\subsection{The \emph{luadraw\_spherical} Module} 

This module allows you to draw a number of objects on a sphere (such as circles, spherical triangles, etc) without having to manually manage the visible or invisible parts. Drawing is done in three steps:
\begin{enumerate}
    \item We define the characteristics of the sphere (center, radius, color, etc.)
    \item We define the objects to be added to the scene using dedicated methods.
    \item We display everything with the \cmd{g:Dspherical()} method.
\end{enumerate}
Of course, all 2D and 3D drawing methods remain usable.

\textbf{Usage}: This module adds new graphics methods to the \emph{ld.graph3d} class; it does not return anything. The variables introduced by this module go in the \emph{luadraw} namespace.

\subsubsection{Global Module Functions}

\begin{itemize}
         \item \cmd{ld.sM(x, y, z)}: returns a point on the sphere; this is the point $I$ on the sphere such that the half-line $[O,I)$ ($O$ being the center of the sphere) passes through the point $A$ with Cartesian coordinates $(x,y,z)$. It is the projection of the point $M(x,y,z)$ onto the sphere from the center.
        \item \cmd{ld.sM(theta, phi)}: where \argu{theta} and \argu{phi} are angles in degrees, returns a point on the sphere, whose spherical coordinates are \emph{(R,theta,phi)} where $R$ is the radius of the sphere.
        \item \cmd{ld.toSphere(A)}: returns the projection of point \argu A onto the sphere from the center.
\end{itemize}



\subsubsection{Sphere Definition}

The sphere is defined using the method \cmd{g:Define\_sphere( options )}, where \argu{options} is a table allowing adjustment of each parameter. These are as follows (with their default values):
\begin{itemize}
    \item \opt{center=\val{pt3d.Origin}},
    \item \opt{radius=\val{3}},
    \item \opt{color=\val{"orange"}},
    \item \opt{opacity=\val{1}},
    \item \opt{mode=\val{ld.mBorder}}, sphere display mode (possible values: \val{ld.mWireframe} or \val{ld.mGrid} or \val{ld.mBorder}),
    \item \opt{edgecolor=\val{"lightgray"}},
    \item \opt{edgestyle="\val{solid"}},
    \item \opt{hiddenstyle=\val{ld.Hiddenlinestyle}},
    \item \opt{hiddencolor=\val{"gray"}},
    \item \opt{edgewidth=\val{4}},
    \item \opt{show=\true}, to show or hide the sphere,
    \item \opt{insidelabelcolor=\val{"darkgray"}}: Defines the color of labels whose anchor point is inside the sphere,
    \item \opt{arrowBstyle=\val{"->"}}: Type of arrow at the end of the line,
    \item \opt{arrowAstyle=\val{"<-"}}: Type of arrow at the beginning of the line,
    \item \opt{arrowABstyle=\val{"<->"}}: Very rarely used because most of the time the lines drawn on the sphere must be cut,
    \item \opt{hiddendelayed=\false}: with the value \false the hidden parts are drawn at the end of the instruction \cmd{g:Dspherical()}, with the value \true they are drawn at the very end of the current graph which can be useful if you have added elements after the sphere which hide part of it (However, this behavior can be modified locally with the option \opt{hidden=\true/\false}).
\end{itemize}


The \cmd{g:Clear\_spherical()} method allows you to remove objects that have been added to the scene, and resets the values ​​to default.


\subsubsection{Add a circle: g:DScircle}
\def\writeoptions{The \argu{options} argument is a table whose fields define the options, which are (with their default value)}%

The \cmd{g:DScircle(P, options)} method allows you to add a circle to the sphere. The argument \emph{P} is a table of the form $\{A,n\}$ that represents a plane (passing through $A$ and normal to $n$, two 3D points). The circle is then defined as the intersection of this plane with the sphere. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{out=nil}, if we assign a list variable to this \argu{out} parameter, then the function adds to this list the two points corresponding to the ends of the hidden arc, if any, which allows us to retrieve them without having to calculate them.
\end{itemize}

\subsubsection{Add a great circle: g:DSbigcircle}

The method \cmd{g:DSbigcircle(AB, options)} adds a great circle to the sphere. The argument \argu{AB} is a table of the form $\{A,B\}$ where $A$ and $B$ are two distinct points on the sphere. The great circle is then the circle centered at the center of the sphere, and passing through $A$ and $B$. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{out=nil}, if we assign a list variable to this \argu{out} parameter, then the function adds to this list the two points corresponding to the ends of the hidden arc, if any, which allows us to retrieve them without having to calculate them.
\end{itemize}

\subsubsection{Add a great circle arc: g:DSarc}

The method \cmd{g:DSarc(AB, sens, options)} allows you to add a great circle arc to the sphere. The argument \argu{AB} is a table of the form $\{A,B\}$ where $A$ and $B$ are two distinct points on the sphere. The great circle arc is then drawn from $A$ to $B$. The argument \argu{sens} is equal to $1$ or $-1$ to indicate the direction of the arc. When $A$ and $B$ are not diametrically opposed, the plane $OAB$ (where $O$ is the center of the sphere) is oriented with $\vec{OA}\wedge\vec{OB}$. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{arrows=0}, three possible values: $0$ (no arrow), $1$ (one arrow at $B$), $2$ (arrow at $A$ and $B$).
    \item \opt{normal=nil}, allows you to specify a normal vector to the $OAB$ plane when these three points are aligned.
\end{itemize}

\subsubsection{Add an angle: g:DSangle}

The method \cmd{g:DSangle(B, A, C, r, sens, options)}, where \argu{A}, \argu{B}, and \argu{C} are three points on the sphere, allows you to draw a great circle arc on the sphere to represent the angle $(\vec{AB},\vec{AC})$ with a radius of \argu{r}. The argument \argu{sens} is $1$ or $-1$ to indicate the direction of the arc; the plane $ABC$ is oriented with $\vec{AB}\wedge\vec{AC}$. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{arrows=0}, three possible values: $0$ (no arrow), $1$ (one arrow at $B$), $2$ (arrow at $A$ and $B$).
    \item \opt{normal=nil}, allows you to specify a normal vector to the $OAB$ plane when these three points are aligned.
\end{itemize}

\subsubsection{Add a spherical facet: g:DSfacet}

The method \cmd{g:DSfacet(F, options)}, where \argu{F} is a list of points on the sphere, allows you to draw the facet represented by \argu{F}, the edges being great circle arcs. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{fill=""}, string representing the fill color (none by default),
    \item \opt{fillopacity=0.3}, opacity of the fill color.
\end{itemize}

\subsubsection{Add a spherical curve: g:DScurve}

The method \cmd{g:DScurve(L, options)}, where \argu{L} is a list of points on the sphere, allows you to draw the curve represented by \argu{L}. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{out=nil}. If we assign a table-type variable to this \opt{out} option, then the function adds the points corresponding to the ends of the hidden parts to this list.
\end{itemize}

We will now deal with objects that are not necessarily on the sphere, but that may pass through it, or be inside it, or outside it.

\subsubsection{ Add a segment: g:DSseg}

The \cmd{g:DSseg(AB, options)} method allows you to add a segment. The argument \argu{AB} is a table of the form $\{A,B\}$ where $A$ and $B$ are two points in space. The function handles interactions with the sphere. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{arrows=0}, three possible values: $0$ (no arrow), $1$ (one arrow in $B$), $2$ (arrow in $A$ and $B$).
\end{itemize}

\subsubsection{Add a line: g:DSline}

The \cmd{g:DSline(d, options)} method allows you to add a line. The argument \argu{d} is a table of the form $\{A,u\}$ where $A$ is a point on the line and $u$ is a direction vector (two 3D points). The function handles interactions with the sphere. The drawn segment is obtained by intersecting the line with the 3D window; it may be empty if the window is too narrow. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{arrows=0}, three possible values: $0$ (no arrow), $1$ (one arrow in $B$), $2$ (arrow in $A$ and $B$).
    \item \opt{scale=1}, allows you to change the size of the plotted segment.
\end{itemize}

    
\subsubsection{ Add a polygonal line: g:DSpolyline}

The \cmd{g:DSpolyline(L, options)} method allows you to add a polygonal line. The argument \argu{L} is a list of points in space, or a list of lists of points in space. The function handles interactions with the sphere. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{arrows=0}, three possible values: $0$ (no arrow), $1$ (one arrow in $B$), $2$ (arrow in $A$ and $B$).
    \item \opt{close=false}, indicates whether the line should be closed.
\end{itemize}

\subsubsection{Add a plane: g:DSplane}

The \cmd{g:DSplane(P, options)} method allows you to add the contour of a plane. The argument \argu{P} is a table of the form $\{A,n\}$, where $A$ is a point on the plane and $n$ is a normal vector. The function draws a parallelogram representing the plane \argu{P}, processing the interactions with the sphere. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{scale=1}, allows you to change the size of the parallelogram,
    \item \opt{angle=0}, angle in degrees, allows you to rotate the parallelogram around the perpendicular line passing through the center of the sphere.
    \item \opt{trace=true}, allows you to draw, or not, the intersection of the plane with the sphere when it is not empty.
\end{itemize}

\subsubsection{Add a label: g:DSlabel}

The \cmd{g:DSlabel(text1, anchor1, options1, text2, anchor2, options2, \ldots)} method allows you to add one or more labels using the same principle as the \cmd{g:Dlabel3d()} method, except that here the function handles cases where the anchor point is inside the sphere, behind the sphere, or in front of the sphere. When it is inside, the label color is given by the sphere option \opt{insidelabelcolor}, which defaults to \val{"darkgray"}.

\subsubsection{Add points: g:DSdots and g:DSstars}

The \cmd{g:DSdots(dots, options)} method allows you to add points to the scene. The \argu{dots} argument is a list of 3D points. The function draws points by managing interactions with the sphere. \writeoptions:
\begin{itemize}
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{mark\_options=""}, a string that will be passed directly to the \drawcmd instruction.
\end{itemize}

If a point is inside the sphere, or on the hidden face, the point's color is given by sphere option \opt{insidelabelcolor}, which defaults to \val{"darkgray"}.

The \cmd{g:DSstars(dots, options)} method allows you to add points \textbf{onto} the sphere. The \argu{dots} argument is a list of 3D points that will be projected onto the sphere. The function draws these points as an asterisk. \writeoptions:
\begin{itemize}
    \item \opt{style=<current line style>},
    \item \opt{color=<current line color>},
    \item \opt{width=<current line thickness in tenths of a point>},
    \item \opt{opacity=<current line opacity>},
    \item \opt{hidden=ld.Hiddenlines},
    \item \opt{scale=1}, allows you to change the size,
    \item \opt{circled=false}, allows you to add a circle around the star,
    \item \opt{fill=""}, string representing a color. When not empty, the asterisk is replaced by a circled hexagonal facet and filled with the color specified by this option.
\end{itemize}

The points on the hidden face of the sphere have the color given by the sphere option \opt{insidelabelcolor}, which defaults to \val{"darkgray"}.

\subsubsection{Inverse Stereography: g:DSinvstereo\_curve and g:DSinvstereo\_polyline}

The method \cmd{g:DSinvstereo\_curve(L, options)}, where \argu{L} is a 3D polygonal line representing a curve drawn on a plane with equation $z=$cte, draws the image of \argu{L} on the sphere by inverse stereography, the pole being the point \emph{C+r*vecK}, where $C$ is the center of the sphere and $r$ is the radius.

The method \cmd{g:DSinvstereo\_polyline(L, options)}, where \argu{L} is a 3D polygonal line drawn on a plane with equation $z=$cte, draws the image of \argu{L} on the sphere by inverse stereography, the pole being the point \emph{C+r*vecK}, where $C$ is the center of the sphere and $r$ is the radius.

In both cases, the \argu{options} are the same as for the \cmd{g:DScurve()} method.

\subsubsection{Examples}

\begin{demo}{Cube in a Sphere}
\begin{luadraw}{name=cube_in_sphere}
local ld = luadraw
local cpx, pt3d = ld.cpx, ld.pt3d
local Origin, vecI, vecJ, vecK, M = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M

local g = ld.graph3d:new{window={-9,9,-4,5},viewdir={25,70},size={16,8}}
require 'luadraw_spherical'
g:Linewidth(6); ld.Hiddenlinestyle = "dashed"
local a = 4
local O = Origin
local cube = ld.parallelep(O,a*vecI,a*vecJ,a*vecK)
local G = pt3d.isobar3d(cube.vertices)
cube = ld.shift3d(cube,-G) -- to center the cube at the origin
local R = pt3d.abs(cube.vertices[1])

local dessin = function()
    g:DSpolyline({{O,5*vecI},{O,5*vecJ},{O,5*vecK}},{arrows=1, width=8}) -- axes
    g:DSplane({a/2*vecK,vecK},{color="blue",scale=0.9,angle=20}); 
    g:DScircle({-a/2*vecK,vecK},{color="blue"})
    g:DSpolyline( ld.facetedges(cube) ); g:DSlabel("$O$",O,{pos="W"})
    g:Dspherical()
end

g:Saveattr(); g:Viewport(-9,0,-4,5); g:Coordsystem(-5,5,-5,5)
ld.Hiddenlines = true; g:Define_sphere({radius=R, arrowBstyle = "-stealth"})
dessin()
g:Dlabel3d("$x$",5*vecI,{pos="SW"},"$y$",5*vecJ,{pos="E"},"$z$",5*vecK,{pos="N"})
g:Dlabel("Hiddenlines=true",0.5-4.5*cpx.I,{})
g:Restoreattr()

g:Saveattr(); g:Viewport(0,9,-4,5); g:Coordsystem(-5,5,-5,5)
ld.Hiddenlines = false; g:Define_sphere({radius=R,opacity=0.7, arrowBstyle = "-stealth"} )
dessin()
g:Dlabel3d("$x$",5*vecI,{pos="SW"},"$y$",5*vecJ,{pos="E"},"$z$",5*vecK,{pos="N"})
g:Dlabel("Hiddenlines=false, opacity=0.7",0.5-4.5*cpx.I,{})
g:Restoreattr()
g:Show()
\end{luadraw}
\end{demo}

\paragraph{Spherical curve}

\begin{demo}{Viviani window}
\begin{luadraw}{name=courbe_spherique}
local ld = luadraw
local cpx, pt3d = ld.cpx, ld.pt3d
local Origin, vecI, vecJ, vecK, M, Ms = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M, pt3d.Ms

local g = ld.graph3d:new{window={-4.5,4.5,-4.5,4.5},viewdir={30,60},margin={0,0,0,0},size={10,10}}
require 'luadraw_spherical'
g:Linewidth(6); ld.Hiddenlinestyle = "dotted"
ld.Hiddenlines = false; 
local C = ld.cylinder(M(1.5,0,-3.5),1.5,M(1.5,0,3.5),35,true)
local L = ld.parametric3d( function(t) return Ms(3,t-math.pi/2,t) end, -math.pi,math.pi) -- the curve
g:Define_sphere({arrowBstyle = "-stealth"})
g:DSpolyline(ld.facetedges(C),{color="gray"}) -- drawing cylinder
g:DSpolyline({{-5*vecI,5*vecI},{-5*vecJ,5*vecJ},{-5*vecK,5*vecK}},{arrows=1}) --axes
ld.Hiddenlines=true; g:DScurve(L,{width=12,color="blue"}) -- curve with hidden part
g:Dspherical()
g:Show()
\end{luadraw}
\end{demo}

To avoid compromising the readability of the drawing, the hidden parts have not been displayed except for the curve.

\paragraph{A spherical tiling}

\begin{demo}{A spherical tiling}
\begin{luadraw}{name=pavage_spherique}
local ld = luadraw
local Origin = ld.pt3d.Origin

local g = ld.graph3d:new{window={-3,3,-3,3},viewdir={30,60},size={10,10}}
require 'luadraw_spherical'
local poly = require "luadraw_polyhedrons"
g:Linewidth(6); ld.Hiddenlines = true; ld.Hiddenlinestyle = "dotted"
local P = ld.poly2facet( poly.octahedron(Origin, ld.sM(30,10)) )
local colors = {"Crimson","ForestGreen","Gold","SteelBlue","SlateGray","Brown","Orange","Navy"}
g:Define_sphere()
for k,F in ipairs(P) do
    g:DSfacet(F,{fill=colors[k],style="noline",fillopacity=0.7})  --facets without edges
end
for _, A in ipairs(ld.facetedges(P)) do
    g:DSarc(A,1,{width=8}) -- each edge is an arc of a great circle
end
g:Dspherical()
g:Show()
\end{luadraw}
\end{demo}

For this spherical tiling, we chose a regular octahedron with a center identical to that of the sphere and with one vertex on the sphere (and therefore all vertices are on the sphere).

\paragraph{Tangents to the sphere from a point}

\begin{demo}{Tangents to the sphere from a point}
\begin{luadraw}{name=tangent_to_sphere}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, M = pt3d.Origin, pt3d.M

local g = ld.graph3d:new{window={-4,5.5,-4,4},viewdir={30,60},size={10,10}}
require 'luadraw_spherical'
ld.Hiddenlines=true; g:Linewidth(6)
local O, I = Origin, M(0,6,0)
local S,S1 = {O, 3}, {(I+O)/2,pt3d.abs(I-O)/2}
-- the circle of tangency is the intersection between spheres S and S1
local C,r,n = ld.interSS(S,S1) 
local L = ld.circle3d(C,r,n)[1] -- list of 3D points on the circle
local dots, lines = {}, {}
-- draw
g:Define_sphere({opacity=1})
g:DScircle({C,n},{color="red"})
for k = 1, math.floor(#L/4) do
    local A = L[4*(k-1)+1]
    table.insert(dots,A)
    table.insert(lines,{I, 2*A-I})
end
g:DSpolyline(lines ,{color="gray"})
g:DSstars(dots) -- drawing points on the sphere
g:DSdots({O,I});  -- points in the scene
g:DSlabel("$I$",I,{pos="S",node_options="red"},"$O$",O,{})
g:Dspherical()
g:Dseg3d({O,dots[1]},"gray,dashed"); g:Dangle3d(O,dots[1],I,0.2,"gray")
g:Show() 
\end{luadraw}
\end{demo}

\paragraph{Inverse Stereography}

\begin{demo}{\emph{DSinvstereo\_curve} and \emph{DSinvstereo\_polyline} methods}
\begin{luadraw}{name=stereographic_curve}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, M, vecJ, vecK = pt3d.Origin, pt3d.M, pt3d.vecJ, pt3d.vecK

local g = ld.graph3d:new{window3d={-5,5,-2,2,-2,2},window={-4.25,4.25,-2.5,2},size={10,10}, viewdir={40,70}}
ld.Hiddenlines = true; ld.Hiddenlinestyle="dashed"; g:Linewidth(6)
require 'luadraw_spherical'
local C, R = Origin, 1
local a = -R
local P = ld.planeEq(0,0,1,-a)
local L = {M(2,0,a), M(2,2.5,a), M(-1,2,a)}
local L2 = ld.circle3d(M(2.25,-1,a),0.5,vecK)[1]
local A, B = (L[2]+L[3])/2, L2[20]
local a,b = table.unpack( ld.inv_projstereo({A,B},{C,R},C+R*vecK) )
g:Dplane(P,vecJ,6,6,15,"draw=none,fill=Beige")
g:Define_sphere( {center=C,radius=R, color="SlateGray!30", show=true} )
g:DSpolyline(L,{color="blue",close=true}); g:DSinvstereo_polyline(L,{color="red",width=8,close=true})
g:DSpolyline(L2,{color="Navy"}); g:DSinvstereo_curve(L2,{color="Brown",width=6})
g:DSplane(P,{scale=1.5})
g:DSpolyline({{C+R*vecK,A},{C+R*vecK,B}}, {color="ForestGreen",width=8})
g:DSpolyline({{-vecK,2*vecK}}, {arrows=1})
g:DSstars({C+R*vecK,a,b}, {scale=0.75})
g:Dspherical()
g:Dballdots3d({A,B},"ForestGreen",0.75)
g:Show()
\end{luadraw}
\end{demo}
