\section{Introduction}

Rappel, nous utilisons les raccourcis suivants :

\begin{Luacode}
local ld = luadraw -- alias sur l'espace de nommage
local cpx = ld.cpx -- raccourci pour la classe cpx
local pt3d = ld.pt3d -- raccourci pour la classe pt3d
\end{Luacode}

\subsection{Prérequis}

\begin{itemize}
\item Ce chapitre présente l'utilisation du package \luadrawenv avec l'option globale \emph{3d} :
\verb|\usepackage[3d]{luadraw}|.
\item Le paquet charge le module \emph{luadraw\_graph2d.lua} qui définit la classe \emph{ld.graph}, et fournit l'environnement \luadrawenv qui permet de faire des graphiques en Lua. Tout ce qui est dit dans le précédent chapitre (Dessin 2D) s'applique donc, et est supposé connu ici.
\item L'option globale \emph{3d} permet en plus le chargement du module \emph{luadraw\_graph3d.lua}. Celui-ci définit en plus la classe \emph{ld.graph3d} (qui s'appuie sur la classe \emph{ld.graph}) pour des dessins en 3D. 
\end{itemize}

\subsection{Quelques rappels}

\begin{itemize}
    \item Autre option globale du paquet : \opt{noexec}. Lorsque cette option globale est mentionnée la valeur par défaut de l'option \opt{exec} pour l'environnement \luadrawenv sera \false (et non plus \true).

    \item Lorsqu'un graphique est terminé il est exporté au format TikZ, donc ce paquet charge également le paquet \emph{tikz} ainsi que les librairies :
    \begin{itemize}
        \item\emph{patterns}
        \item\emph{plotmarks}
        \item\emph{arrows.meta}
        \item\emph{decorations.markings}
        \end{itemize}
        
    \item Les graphiques sont créés dans un environnement \luadrawenv, celui-ci appelle \emph{luacode}, c'est donc du \textbf{langage Lua} qu'il faut utiliser dans cet environnement.

    \item Sauvegarde du fichier \emph{*.tkz} : le graphique est exporté au format TikZ dans un fichier (avec l'extension \emph{tkz}), par défaut celui-ci est sauvegardé dans le dossier \emph{\_luadraw} qui est un sous-dossier du dossier courant (contenant le document maître), mais il est possible d'imposer un chemin vers un autre sous-dossier avec l'option globale \opt{cachedir=\ldots}.
    
    \item Les options de l'environnement sont :
    \begin{itemize}
    \item \opt{name=\ldots} : permet de donner un nom au fichier TikZ produit, on donne un nom sans extension (celle-ci sera automatiquement ajoutée, c'est \emph{.tkz}). Si cette option est omise, alors il y a un nom par défaut, qui est le nom du fichier maître suivi d'un numéro.
    
    \item \opt{exec=true/false} : permet d'exécuter ou non le code Lua compris dans l'environnement. Par défaut cette option vaut \true, \textbf{SAUF} si l'option globale \opt{noexec} a été mentionnée dans le préambule avec la déclaration du paquet. Lorsqu'un graphique complexe qui demande beaucoup de calculs est au point, il peut être intéressant de lui ajouter l'option \opt{exec=false}, cela évitera les recalculs de ce même graphique pour les compilations à venir.
    
    \item \opt{auto=true/false} : permet d'inclure ou non automatiquement le fichier TikZ en lieu et place de l'environnement \luadrawenv lorsque l'option \opt{exec} est à \false. Par défaut l'option \opt{auto} vaut \true.
    \end{itemize}
\end{itemize}


\subsection{Création d'un graphe 3D}

\begin{TeXcode}
\begin{luadraw}{ name=<filename>, exec=true/false, auto=true/false }
local ld = luadraw
-- création d'un nouveau graphique en lui donnant un nom local
local g = ld.graph3d:new{ window3d={x1,x2,y1,y2,z1,z2 [,xscale,yscale,zscale]}, adjust2d=true/false, viewdir={30,60}, window={x1,x2,y1,y2 [,xscale,yscale]}, margin={top,right,bottom,left}, size={largeur,hauteur,ratio}, bg="color", border=true/false }
-- construction du graphique g
    instructions graphiques en langage Lua ...
-- affichage du graphique g et sauvegarde dans le fichier <filename>.tkz
g:Show()
-- ou bien sauvegarde uniquement dans le fichier <filename>.tkz
g:Save()
\end{luadraw}
\end{TeXcode}

\noindent\textbf{Important}: dans toute la suite de ce chapitre, les points de l'espace sont appelés \emph{points 3D}, ce sont des triplets de $\mathbf R^3$. Dans l'environnement \luadrawenv, le point 3D $(x,y,z)$ sera noté \cmd{pt3d.M(x, y, z)} (\cmd{pt3d.M} est une fonction de la classe \emph{pt3d} qui crée et renvoie un point 3D).

La création se fait dans un environnement \luadrawenv, c'est à la première ligne à l'intérieur de l'environnement qu'est faite cette création en nommant le graphique :

\begin{Luacode}
local ld = luadraw
local g = ld.graph3d:new{ window3d={x1,x2,y1,y2,z1,z2 [,xscale,yscale,zscale]}, adjust2d=true/false, viewdir={30,60}, window={x1,x2,y1,y2 [,xscale,yscale]}, margin={left,right,top,bottom}, size={largeur,hauteur,ratio}, bg="color", border=true/false }
\end{Luacode}

La classe \emph{ld.graph3d} est définie dans le paquet \luadrawenv grâce à l'option globale \opt{3d}. On instancie cette classe en invoquant son constructeur et en donnant un nom (ici c'est \emph{g}), on le fait en local de sorte que le graphique \emph{g} ainsi créé, n'existera plus une fois sorti de l'environnement (sinon \emph{g} resterait en mémoire jusqu'à la fin du document).

\begin{itemize}
 \item L'option \opt{window3d=\{x1,x2,y1,y2,z1,z2 \fac{,xscale,yscale,zscale}\}} définit le pavé de $\mathbf R^3$ correspondant au graphique : c'est $[x_1;x_2]\times[y_1;y_2]\times[z_1;z_2]$, ainsi que l'échelle sur les trois axes : \emph{xscale}, \emph{yscale} et \emph{zscale}, celles-ci  sont facultatives et valent $1$ par défaut. Le pavé par défaut est $[-5;5]\times[-5;5]\times[-5;5]$.
 
 \textbf{Attention} : les trois échelles déterminent la matrice 3D initiale du graphe, lorsque l'une d'elle est différente de $1$, cette matrice n'est pas l'identité. Si vous devez changer la matrice du graphe par la suite, il faut utiliser la méthode \cmd{g:Composematrix3d()} et non pas \cmd{g:Setmatrix3d()}, et penser à sauvegarder auparavant la matrice initiale avec la méthode \cmd{g:Savematrix()}, on peut ensuite la restaurer avec la méthode \cmd{g:Restorematrix()}.
 
 \item L'option \opt{adjust2d} indique si la fenêtre 2D qui va contenir la projection du dessin 3D, doit être déterminée automatiquement (\false par défaut). Cette fenêtre 2D correspond à l'option \opt{window}.
 
 \item L'option \opt{viewdir} est une table qui définit le mode de projection et les deux angles de vue (en degrés). Par défaut \opt{viewdir=\{"ortho",30,60\}} (projection orthographique). La figure suivante montre à quoi correspondent ces deux angles.
 
\begin{center}
\begin{luadraw}{name=viewdir}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, vecI, vecJ, vecK = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK 
local g = ld.graph3d:new{ size={8,8} }
local i, M = ld.cpx.I, ld.pt3d.M
local O, A = Origin, M(4,4,4)
local B, C, D, E = ld.pxy(A), ld.px(A), ld.py(A), ld.pz(A) --projeté de A sur le plan xOy et sur les axes
g:Dpolyline3d( {{O,A},{-5*vecI,5*vecI},{-5*vecJ,5*vecJ},{-5*vecK,5*vecK}}, "->") -- axes
g:Dpolyline3d( {{E,A,B,O}, {C,B,D}}, "dashed")
g:Dpath3d( {C,O,B,2.5,1,"ca",O,"l","cl"}, "draw=none,fill=cyan,fill opacity=0.8") --secteur angulaire
g:Darc3d(C,O,B,2.5,1,"->") -- arc de cercle pour theta
g:Dpath3d( {E,O,A,2.5,1,"ca",O,"l","cl"}, "draw=none,fill=cyan,fill opacity=0.8") --secteur angulaire
g:Darc3d(E,O,A,2.5,1,"->") -- arc de cercle pour phi
g:Dballdots3d(O) -- le point origine sous forme d'une petite sphère
g:Labelsize("footnotesize")
g:Dlabel3d(
  "$x$", 5.25*vecI,{}, "$y$", 5.25*vecJ,{}, "$z$", 5.25*vecK,{},
  "vers observateur", A, {pos="E"},
  "$O$", O, {pos="NW"},
  "$\\theta$", (B+C)/2, {pos="N", dist=0.15},
  "$\\varphi$", (A+E)/2, {pos="S",dist=0.25})
g:Dlabel("viewdir=\\{'ortho',$\\theta,\\varphi$\\} (en degrés)",-5*i,{pos="N"}) -- label 2D
g:Show()
\end{luadraw}
\captionof{figure}{Angles de vue}\label{viewdir}
\end{center}

\item Les autres options sont celles de la classe \emph{ld.graph}, elles ont été décrites dans le chapitre 1.
\end{itemize}


\paragraph{Construction du graphique.}

\begin{itemize}
    \item L'objet instancié possède toutes les méthodes de la classe \emph{ld.graph}, plus des méthodes spécifiques à la 3D.
    \item La classe \emph{ld.graph3d} amène aussi un certain nombre de fonctions mathématiques propres à la 3D.
\end{itemize}


\subsection{Modes de projection affine}

\begin{itemize}
    \item Projections orthogonales :
        \begin{itemize}
            \item \opt{viewdir=\{"ortho",theta,phi\}}, ou \opt{viewdir=\{theta,phi\}} : projection orthographique (projection orthogonale sur l'écran), c'est la projection par défaut avec theta=30 et phi=60 (degrés),
            \item \opt{viewdir="xOy"} : projection orthogonale sur le plan $xy$,
            \item \opt{viewdir="xOz"} : projection orthogonale sur le plan $xz$,
            \item \opt{viewdir="yOz"} : projection orthogonale sur le plan $yz$.
        \end{itemize}
    \item Projections non orthogonales :
        \begin{itemize}
            \item \opt{viewdir=\{"yz",k,alpha\}} : perspective cavalière sur le plan $yz$,
            \item \opt{viewdir=\{"xz",k,alpha\}} : perspective cavalière sur le plan $xz$,
            \item \opt{viewdir=\{"xy",k,alpha\}} : perspective cavalière sur le plan $xy$,
            \item \opt{viewdir="iso"} : perspective isométrique.
        \end{itemize}
        Les trois perspectives cavalières sont définies à l'aide de deux paramètres : un nombre positif \emph{k} et un angle en degré \emph{alpha}, qui sont mis en évidence dans la figure suivante.
\end{itemize}

\begin{center}
\begin{luadraw}{name=perpectives}
local k = 0.65
local alpha = 60
local r = 3
local ld, cpx, pt3d = luadraw, luadraw.cpx, luadraw.pt3d
local Origin, vecI, vecJ, vecK = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK 
local Z, Zp, deg = cpx.Z, cpx.Zp, ld.deg
local g = ld.graph3d:new{
    window3d = {-4.5,4.5,-4.5,4.5,-4.5,4.5},
    window ={-5,5,-5,5},
    viewdir = {"yz",k,alpha},
    size={10,10},
}
g:Labelsize("footnotesize")
local draw = function()
    g:Dscene3d( g:addAxes(Origin, {arrows=1}) )
    g:Darc(1,0,Zp(1,alpha*deg),(r+0.5)*k,1,"->"); g:Ddots(r*k)
    g:Dlabel("$\\alpha$",Zp((r+1)*k,30*deg),{pos="NE"}, "$k$", r*k,{pos="SE"})
    g:Dcircle(0,r*k)
end
g:Dline(0,1); g:Dline(0,cpx.I)
-- en haut à gauche
g:Saveattr(); g:Viewport(-5,0,0,5); g:Coordsystem(-4.5,5.5,-5,5)
draw()
g:Ddots3d(r*vecI); g:Dlabel3d("$x=1$",r*vecI,{pos="W",dist=0.1}); 
g:Dlabel("perspective sur le plan $yz$",Z(0.5,-5),{pos="N",node_options="fill=white"})
g:Restoreattr()
-- en haut à droite
g:Saveattr(); g:Viewport(0,5,0,5); g:Coordsystem(-4.75,5.5,-5,5)
g:Setviewdir({"xz",k,alpha})
draw()
g:Ddots3d(r*vecJ); g:Dlabel3d("$y=1$",r*vecJ,{pos="NW"}); 
g:Dlabel("perspective sur le plan $xz$",Z(0.5,-5),{pos="N",node_options="fill=white"})
g:Restoreattr()
-- en bas à gauche
g:Saveattr(); g:Viewport(-5,0,-5,0); g:Coordsystem(-4.5,5.5,-5,5.5)
g:Setviewdir({"xy",k,alpha})
draw()
g:Ddots3d(r*vecK); g:Dlabel3d("$z=1$",r*vecK,{pos="W"}); 
g:Dlabel("perspective sur le plan $xy$",Z(0.5,-5),{pos="N",node_options="fill=white"})
g:Restoreattr()
-- en bas à droite
g:Saveattr(); g:Viewport(0,5,-5,0); g:Coordsystem(-4.5,5.5,-5,5.5)
g:Setviewdir("iso")
g:Dscene3d( g:addAxes(Origin, {arrows=1}) ); g:Dcircle(0, cpx.abs(g:Proj3d(r*vecI)))
g:Ddots3d({r*vecI,r*vecJ,r*vecK}); 
g:Dlabel3d("$z=1$",r*vecK,{pos="NW"}, "$x=1$",r*vecI,{pos="NW"},"$y=1$",r*vecJ,{pos="NE"});
g:Dlabel("perspective isométrique",Z(0.5,-5),{pos="N",node_options="fill=white"})
g:Restoreattr()
g:Show()
\end{luadraw}
\captionof{figure}{Modes de projection affine}
\end{center}

Toutes les valeurs qui peuvent être transmises à l'option \opt{viewdir} lors la création, peuvent également être utilisées dans la méthode \cmd{g:Setviewdir()} en cours de graphique.

\subsection{Projection centrale}

Depuis la version 2.4, \luadrawenv propose également la projection centrale. À la différence des modes précédents, \textbf{cette projection n'est pas affine}, et d'autre part elle n'est pas définie pour tous les points de l'espace, ce qui peut conduire à des erreurs, cela demande donc de la réflexion et des ajustements. Cette projection est définie par :

\begin{itemize}
    \item Une caméra, qui est un point de l'espace mémorisé dans une variable globale appelée \varglob{ld.camera} et qui ne  doit pas être modifiée directement.
    \item Une cible, qui est un point de l'espace mémorisé dans une variable globale appelée \varglob{ld.target} et qui ne  doit pas être modifiée directement.
\end{itemize}

Le plan passant par le point \varglob{ld.target} et orthogonal à l'axe \varglob{ld.target} - \varglob{ld.camera} est le plan de la projection, il représente l'écran. Comme pour les modes précédents, la projection centrale est accessible par l'option \opt{viewdir} ou la méthode \cmd{g:Setviewdir} :
\cmdln{ viewdir = \{"central" \fac{, camera, target}\}}
ou bien 
\cmdln{viewdir = \{"central" \fac{, theta, phi, d, target}\}}

Dans le premier cas on donne les valeurs de \argu{camera} et \argu{target} (points 3D, par défaut \argu{target} est l'origine). Dans le second cas, les trois arguments \argu{theta}, \argu{phi}, et \argu{d}  servent à positionner la caméra conformément au schéma suivant :

\begin{center}
\begin{luadraw}{name=central_perspective}
local ld, cpx, pt3d = luadraw, luadraw.cpx, luadraw.pt3d
local Origin, vecI, vecJ, vecK, M = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M

local g = ld.graph3d:new{window3d={-5,3,-5,3,-5,5}, window={-5,10,-7,7}, size={12,12},
    margin={0,0,0,0}, bbox=false, viewdir={"central",-60,65,35} }
    
g:Writeln("\\tikzset{->-/.style= {decoration={markings, mark=at position #1 with {\\arrow{stealth}}}, postaction={decorate}}}")
g:Labelsize("footnotesize")
local dcamera = function(pos,dir)
    local a, b, c, d, e = 0.25, 0.25, 0.1, 0.2, 0.1
    local u = cpx.normalize(dir)
    local v = cpx.I*u
    local chem, dep = {}, pos+e*u-b*v
    chem = {dep,dep+2*a*u,dep+2*a*u+2*b*v,dep+2*b*v,0.15,"cla", dep+(b-c)*v,"m",pos-d*v,pos+d*v,dep+(b+c)*v,"l"}
    g:Dpath(chem)
end
local d = 10
local N = pt3d.normalize(M(2,1,1.5))
local v = pt3d.normalize(pt3d.prod(N,vecJ))
local u = pt3d.prod(v,N)
local Cam = d*N
local A = ld.pxy(Cam)
local B, C = M(0,2,2.5), M(3,-1,4)
local E = (B+C)/2+vecK
local F = pt3d.isobar3d({B,C,E})+1.51*vecJ
local T = ld.tetra(B,E-B,C-B,F-B)
local B1, C1, E1, F1 = ld.proj3dO(B, {Origin,N}, Cam-B), ld.proj3dO(C, {Origin,N}, Cam-C), ld.proj3dO(E, {Origin,N}, Cam-E), ld.proj3dO(F, {Origin,N}, Cam-F)
local x, y = 12,8
local D = Origin -x*u/2-y*v/2.5
local plan = {D,D+x*u,D+x*u+y*v,D+y*v}  
g:Dscene3d(
    g:addFacet(plan, {color="white", contrast=0.125, edge=true}),
    g:addPoly(T, {color="white", contrast=0.25, edge=true, hidden=true, hiddenstyle="dashed"}),
    g:addPolyline( {{(Cam+B)/2,B1},{C,C1},{E,E1},{F,F1}}, {width=2,color="gray", hidden=true}),
    g:addPolyline({{-5*vecK,5*vecK},{-5*vecI,4*vecI}}, {arrows=1, hidden=true})
)
g:Dballdots3d(Origin,nil,1.5)
g:Ddots3d({B1,C1, E1, F1},"gray")
g:Dpolyline3d({Origin, A, Cam},true,"dashed")
g:Dangle3d(Cam,A,Origin)
g:Dangle3d(Cam,Origin,v,0.3,"line width=0.8pt")
g:Darc3d(vecI,Origin,A,2.25,1,'-stealth'); g:Darc3d(vecK,Origin,Cam,2.25,1,'-stealth')
g:Dpolyline3d({{Cam,(B+Cam)/2},{Cam,C},{Cam,E},{Cam,F}},true,"->-=0.35,line width=0.1pt,gray")
g:Dpolyline3d({{B1,C1,E1,F1},{C1,F1}},true,"gray")
g:Dpolyline3d({B1,E1},true,"dotted,gray")
g:Ddots3d({B, C, E, F})
g:Dlabel3d("screen plane",D,{pos="NE",dir={u,v}})
g:Dlabel3d("target",Origin, {pos="S",dist=0.1}, "camera",Cam,{pos="SE"}, "$A$",B,{pos="N"},"$B$",C,{pos="S"},"$C$",E,{pos="N"},
"$D$",F,{},"$A'$",B1,{node_options="gray",dist=0}, "$B'$",C1,{pos="NW"}, "$C'$",E1,{pos="N"},"$D'$",F1,{}, "$\\theta$", 2.75*vecI,{pos="N",node_options="black"},"$\\varphi$", 1.3*(vecK+N), {}, "$z$",5*vecK,{},"$x$",4.5*vecI,{pos="center"})
local O = M(-3,-2,-4)
g:Ddots3d(O); g:Dpolyline3d({{O,O+vecI},{O,O+vecJ},{O,O+vecK}},'->')
g:Dlabel3d("$x$",O+1.25*vecI,{},"$y$",O+1.25*vecJ,{},"$z$",O+1.25*vecK,{},"Origin",O,{pos="S"})
u = g:Proj3dV(Cam)
g:Dpolyline3d( {Cam/2-0.4*vecK-Cam/3.5, Cam/2-0.4*vecK+Cam/3.5}, 'stealth-stealth')
g:Dlabel("$d$ = distance target - camera", g:Proj3d(Cam/2-0.4*vecK),{dir={u, cpx.I*u}, node_options="fill=white"})
dcamera(u,u)
g:Show()
\end{luadraw}
\captionof{figure}{Projection centrale}
\end{center}

Les valeurs par défaut sont : \argu{theta}=$30$ (degrés), \argu{phi}=$60$, \argu{d}=$15$, \argu{target}=\varglob{pt3d.Origin} (la variable globale \varglob{pt3d.Origin} est le point 3D $(0,0,0)$). 
