\section{Transformations calcul matriciel et quelques fonctions mathématiques}

\subsection{Transformations 3D}

Dans les fonctions qui suivent :
\begin{itemize}
    \item l'argument \argu{L} est soit un point 3D, soit un polyèdre, soit une liste de points 3D (facette) soit une liste de listes de points 3D (liste de facettes),
    \item une droite \argu{d} est une liste de deux points 3D $\{A,u\}$ : un point de la droite ($A$) et un vecteur directeur ($u$),
    \item un plan \argu{P} est une liste de deux points 3D $\{A,n\}$ : un point du plan ($A$) et un vecteur normal au plan ($n$).
  \end{itemize}
Le résultat renvoyé est de même type que \argu{L}.
  
\subsubsection{Appliquer une fonction de transformation : ftransform3d}

La fonction \cmd{ld.ftransform3d(L, f)} renvoie l'image de \argu{L} par la fonction \argu{f}, celle-ci  doit être une fonction de $\mathbf R^3$ vers $\mathbf R^3$ (à un point 3D elle associe un point 3D).

\subsubsection{Projections : proj3d, proj3dO, dproj3d}

\begin{itemize}
    \item La fonction \cmd{ld.proj3d(L, P)} renvoie l'image de \argu{L} par la projection orthogonale sur le plan \argu{P}.
    \item La fonction \cmd{ld.proj3dO(L, P, v)} renvoie l'image de \argu{L} par la projection sur le plan \argu{P} parallèlement à la direction du vecteur \argu{v} (point 3D non nul).
    \item La fonction \cmd{ld.dproj3d(L, d)} renvoie l'image de \argu{L} par la projection sur la droite \argu{d}.
\end{itemize}

\subsubsection{Projections sur les axes ou les plans liés aux axes}

\begin{itemize}
    \item La fonction \cmd{ld.pxy(L  \fac{, z0})} renvoie l'mage de \argu{L} par la projection orthogonale sur le plan $z=$\argu{z0} (par défaut $z0=0$).
    \item La fonction \cmd{ld.pyz(L \fac{, x0})} renvoie l'mage de \argu{L} par la projection orthogonale sur le plan $x=$\argu{x0} (par défaut $x0=0$).
    \item La fonction \cmd{ld.pxz(L \fac{, y0})} renvoie l'mage de \argu{L} par la projection orthogonale sur le plan $y=$\argu{y0} (par défaut $y0=0$).
\item La fonction \cmd{ld.px(L)} renvoie l'mage de \argu{L} par la projection orthogonale sur l'axe $Ox$.
\item La fonction \cmd{ld.py(L)} renvoie l'mage de \argu{L} par la projection orthogonale sur l'axe $Oy$.
\item La fonction \cmd{ld.pz(L)} renvoie l'mage de \argu{L} par la projection orthogonale sur l'axe $Oz$.
\end{itemize}

\subsubsection{Symétries : sym3d, sym3dO, dsym3d, psym3d}

\begin{itemize}
    \item La fonction \cmd{ld.sym3d(L, P)} renvoie l'image de \argu{L} par la symétrie orthogonale par rapport au plan \argu{P}.
    \item La fonction \cmd{ld.sym3dO(L, P, v)} renvoie l'image de \argu{L} par la symétrie par rapport au plan \argu{P} et parallèlement à la direction du vecteur \argu{v} (point 3D non nul).
    \item La fonction \cmd{ld.dsym3d(L, d)} renvoie l'image de \argu{L} par la symétrie orthogonale par rapport la droite \argu{d}.
    \item La fonction \cmd{ld.psym3d(L, point)} renvoie l'image de \argu{L} par la symétrie par rapport à \argu{point} (point 3D).
\end{itemize}

\subsubsection{Rotation : rotate3d, rotateaxe3d}

\begin{itemize}
    \item La fonction \cmd{ld.rotate3d(L, angle, d)} renvoie l'image de \argu{L} par la rotation d'axe \argu{d} (orientée par le vecteur directeur qui est $d[2]$), et de \argu{angle} degrés.
    
    \item La fonction \cmd{ld.rotateaxe3d(L, v1, v2 \fac{, center})} renvoie l'image de \argu{L} par une rotation d'axe passant par le point 3D \argu{center} et qui transforme le vecteur \argu{v1} en le vecteur \argu{v2}, ces vecteurs sont normalisés par la fonction. L'argument \argu{center} est facultatif et par défaut c'est le point \varglob{pt3d.Origin}.
\end{itemize}


\subsubsection{Homothétie : scale3d}

La fonction \cmd{ld.scale3d(L, k \fac{, center})} renvoie l'image de \argu{L} par l'homothétie de centre le point 3D \argu{center}, et de rapport \argu{k}. L'argument \argu{center} est facultatif et par défaut c'est le point \varglob{pt3d.Origin}.

\subsubsection{Inversion : inv3d}

La fonction \cmd{ld.inv3d(L, radius \fac{, center})} renvoie l'image de \argu{L} par l'inversion par rapport à la sphère de centre \argu{center}, et de rayon \argu{radius}.  L'argument \argu{center} est facultatif et par défaut c'est le point \varglob{Origin}.

\subsubsection{Stéréographie : projstereo et inv\_projstereo}

Fonction \cmd{ld.projstereo(L, S, N, h)}: l'argument \argu{L} désigne un point 3D ou une liste de points 3D ou une liste de listes de points 3D, appartenant tous à la sphère \argu{S}, où \argu{S}=$\{C,r\}$ ($C$ est le centre de la sphère, et $r$ le rayon). L'argument \argu{N} désigne un point de la sphère qui sera le pôle de la projection. L'argument \argu{h} est un réel qui définit le plan de la projection, ce plan est perpendiculaire à l'axe $(CN)$, et passe par le point $I=C+h \frac{\vec{CN}}{CN}$ (avec $h=0$ c'est le plan équatorial, avec $h=-r$ c'est le plan tangent à la sphère au pôle opposé). La fonction renvoie l'image de \argu{L} par la projection stéréographique par rapport à la sphère \argu{S} avec \argu{N} comme pôle, et sur le plan $\{I,N-C\}$.

Fonction inverse \cmd{ld.inv\_projstereo(L, S, N)} : \argu{S}$=\{C,r\}$ est la sphère de centre $C$ et de rayon $r$, \argu{N} est un point de la sphère \argu{S} (pôle), et \argu{L} est un point 3D ou une liste de points 3D ou une liste de listes de points 3D appartenant tous à un même plan orthogonal à l'axe $(CN)$. La fonction renvoie l'image de \argu{L} par l'inverse de la projection stéréographique par rapport à \argu{S} et de pôle \argu{N}.


\subsubsection{Translation : shift3d}

La fonction \cmd{ld.shift3d(L, v)} renvoie l'image de \argu{L} par la translation de vecteur \argu{v} (point 3D).

\subsection{Calcul matriciel}

Si $f$ est une application affine de l'espace $\mathbf R^3$, on appellera matrice de $f$ la liste (table) :
\codeln{ \{f(pt3d.Origin),Lf(pt3d.vecI),Lf(pt3d.vecJ),Lf(pt3d.vecK)\} }
où $Lf$ désigne la partie linéaire de $f$ (on a \code{Lf(pt3d.vecI)=f(pt3d.vecI)-f(pt3d.Origin)}, etc). La matrice identité est notée \varglob{ld.ID3d} dans \luadrawenv, elle correspond simplement à la liste \code{\{pt3d.Origin,pt3d.vecI,pt3d.vecJ,pt3d.vecK\}}.

\subsubsection{applymatrix3d et applyLmatrix3d}

\begin{itemize}
    \item La fonction \cmd{ld.applymatrix3d(A, M)} applique la matrice \argu{M} au point 3D \argu{A} et renvoie le résultat (ce qui revient à calculer $f(A)$ si $M$ est la matrice de $f$). Si \argu{A} n'est pas un point 3D, la fonction renvoie \argu{A}.
    
    \item La fonction \cmd{ld.applyLmatrix3d(A, M)} applique la partie linéaire la matrice \argu{M} au point 3D \argu{A} et renvoie le résultat (ce qui revient à calculer $Lf(A)$ si $M$ est la matrice de $f$). Si \argu{A} n'est pas un point 3D, la fonction renvoie \argu{A}.
\end{itemize}

\subsubsection{composematrix3d}
La fonction \cmd{ld.composematrix3d(M1, M2)} effectue le produit matriciel \argu{M1}$\times$\argu{M2} et renvoie le résultat.

\subsubsection{invmatrix3d}
La fonction \cmd{ld.invmatrix3d(M)} calcule et renvoie l'inverse de la matrice \argu{M} lorsque cela est possible.

\subsubsection{matrix3dof}

La fonction \cmd{ld.matrix3dof(f)} calcule et renvoie la matrice de \argu{f}, qui doit être une application affine de l'espace $\mathbf R^3$ (à un point 3D elle associe un point 3D).


\subsubsection{mtransform3d et mLtransform3d}

\begin{itemize}
    \item La fonction \cmd{ld.mtransform3d(L, M)} applique la matrice \argu{M} à la liste \argu{L} et renvoie le résultat. \argu{L} doit être une liste de points 3D (une facette) ou une liste de listes de points 3D (liste de facettes).
    \item La fonction \cmd{ld.mLtransform3d(L, M)} applique la partie linéaire la matrice \argu{M} à la liste \argu{L} et renvoie le résultat. \argu{L} doit être une liste de points 3D (une facette) ou une liste de listes de points 3D (liste de facettes).
\end{itemize}

\subsection{Matrice associée au graphe 3D}

Lorsque l'on crée un graphe dans l'environnement \luadrawenv, par exemple :
\begin{Luacode}
local ld = luadraw
local g = ld.graph3d:new{size={10,10}}
\end{Luacode}

l'objet \emph{g} créé possède une matrice 3D de transformation qui est initialement l'identité. Toutes les méthodes graphiques appliquent automatiquement la matrice 3D de transformation du graphe. Pour manipuler cette matrice, on dispose des méthodes qui suivent.

\subsubsection{g:Composematrix3d()}

La méthode \cmd{g:Composematrix3d(M)} multiplie la matrice 3D du graphe \emph g par la matrice \argu{M} (avec \argu{M} à droite) et le résultat est affecté à la matrice 3D du graphe. L'argument \argu{M} doit donc être une matrice 3D.

\subsubsection{g:Det3d()}

La méthode \cmd{g:Det3d()} envoie $1$ lorsque la matrice 3D de transformation a un déterminant positif, et $-1$ dans le cas contraire. Cette information est utile lorsqu'on a besoin de savoir si l'orientation de l'espace a été changée ou non.

\subsubsection{g:IDmatrix3d()}

La méthode \cmd{g:IDmatrix3d()} réaffecte l'identité à la matrice 3D du graphe \emph g.

\subsubsection{g:Mtransform3d()}

La méthode \cmd{g:Mtransform3d(L)} applique la matrice du graphe 3D de \emph g à \argu{L} et renvoie le résultat, l'argument \argu L doit être une liste de points 3D (une facette) ou une liste de listes de points 3D (liste de facettes).

\subsubsection{g:MLtransform3d()}

La méthode \cmd{g:MLtransform3d(L)} applique la partie linéaire de la matrice 3D du graphe \emph g à \argu{L} et renvoie le résultat. L'argument \argu L doit être une liste de points 3D (une facette) ou une liste de listes de points 3D (liste de facettes).

\subsubsection{g:Rotate3d()}

La méthode \cmd{g:Rotate3d(angle, axe)} modifie la matrice 3D de transformation du graphe \emph g en la composant avec la matrice de la rotation d'angle \argu{angle} (en degrés) et d'axe \argu{axe}. 

\subsubsection{g:Scale3d()}

La méthode \cmd{g:Scale3d(factor \fac{, center})} modifie la matrice 3D de transformation du graphe \emph g en la composant avec la matrice de l'homothétie de rapport \argu{factor} et de centre \argu{center}. L'argument \argu{center} est un point 3D qui vaut \varglob{pt3d.Origin} par défaut.


\subsubsection{g:Setmatrix3d()}

La méthode \cmd{g:Setmatrix3d(M)} permet d'affecter la matrice \argu M à la matrice 3D de transformation du graphe \emph g.

\subsubsection{g:Shift3d()}

La méthode \cmd{g:Shift3d(v)} modifie la matrice 3D de transformation du graphe \emph g en la composant avec la matrice de la translation de vecteur \argu{v} qui doit être un point 3D.

\subsection{Fonctions mathématiques supplémentaires}

\subsubsection{clippolyline3d()}

La fonction \cmd{ld.clippolyline3d(L, poly \fac{, exterior, close})} clippe la ligne polygonale 3D \argu{L} avec le polyèdre \textbf{convexe} \argu{poly}, si l'argument facultatif \argu{exterior} vaut \true, alors c'est la partie extérieure au polyèdre qui est renvoyée (\false par défaut), si l'argument facultatif \argu{close} vaut \true, alors la ligne polygonale est refermée (\false par défaut). \argu{L} est une liste de points 3D ou une liste de listes de points 3D.

\textbf{Remarque} : le résultat n'est pas toujours satisfaisant pour la partie extérieure.

\paragraph{Cas particulier} : clipper une ligne polygonale 3D \argu L avec la fenêtre 3D courante peut se faire avec cette fonction de la manière suivante :
\codeln{L=ld.clippolyline3d(L,g:Box3d())}

En effet, la méthode \cmd{g:Box3d()} renvoie la fenêtre 3D courante sous forme d'un parallélépipède.


\subsubsection{clipline3d()}

La fonction \cmd{ld.clipline3d(line, poly)} clippe la droite \argu{line} avec le polyèdre \textbf{convexe} \argu{poly}, la fonction renvoie la partie de la droite intérieure au polyèdre. L'argument \argu{line} est une table de la forme $\{A,u\}$ où $A$ est un point de la droite et $u$ un vecteur directeur (deux points 3D).

\paragraph{Cas particulier} : clipper une droite \argu d avec la fenêtre 3D courante peut se faire avec cette fonction de la manière suivante :
\codeln{d=ld.clipline3d(d,g:Box3d())}

En effet, la méthode \cmd{g:Box3d()} renvoie la fenêtre 3D courante sous forme d'un parallélépipède (\argu d devient alors un segment).

\subsubsection{cutpolyline3d()}

La fonction \cmd{ld.cutpolyline3d(L, plane \fac{, close})} coupe la ligne polygonale 3D \argu{L} avec le plan \argu{plane}, si l'argument facultatif \emph{argu} vaut \true, alors la ligne est refermée (\false par défaut). \argu{L} est une liste de points 3D ou une liste de listes de points 3D, \argu{plane} est une table de la forme $\{A,n\}$ où $A$ est un point du plan et $n$ un vecteur normal (deux points 3D).

Le fonction renvoie trois choses :
\begin{itemize}
    \item la partie de \argu{L} qui est dans le demi-espace contenant le vecteur $n$,
    \item suivie de la partie de \argu{L} qui est dans l'autre demi-espace,
    \item suivie de la liste des points d'intersection.
\end{itemize}

\subsubsection{getbounds3d()}

La fonction \cmd{ld.getbounds3d(L)} renvoie sous forme d'une séquence les limites \emph{xmin, xmax, ymin, ymax, zmin, zmax} de la ligne polygonale 3D \argu{L} (liste de points 3D ou une liste de listes de points 3D).

\subsubsection{interDP()}

La fonction \cmd{ld.interDP(d, P)} calcule et renvoie (si elle existe) l'intersection entre la droite \argu d et le plan \argu P.

\subsubsection{interPP()}

La fonction \cmd{ld.interPP(P1, P2)} calcule et renvoie (si elle existe) l'intersection entre les plans \argu{P1} et \argu{P2}.

\subsubsection{interDD()}

La fonction \cmd{ld.interDD(D1, D2 \fac{, epsilon})} calcule et renvoie (si elle existe) l'intersection entre les droites \argu{D1} et \argu{D1}. L'argument \argu{epsilon} vaut $10^{-10}$ par défaut (sert à tester si un certain flottant est nul).

\subsubsection{interCS()}

La fonction \cmd{ld.interCS(C, S)} calcule et renvoie (si elle existe) l'intersection entre le cercle \argu{C}$=\{A,r,n\}$ ($A$ est le centre du cercle, $r$ le rayon et $n$ un vecteur normal au plan du cercle), et la sphère \argu{S}$=\{B,R\}$ ($B$ est le centre de la sphère et $R$ le rayon). La fonction renvoie soit \nil (intersection vide), soit un seul point, soit deux points (séquence).

\subsubsection{interDS()}

La fonction \cmd{ld.interDS(d, S)} calcule et renvoie (si elle existe) l'intersection entre la droite \argu{d} et la sphère \argu{S} qui est une table de la forme $\{C,r\}$ avec $C$ le centre (point 3D) et $r$ le rayon de la sphère. La fonction renvoie soit \nil (intersection vide), soit un seul point, soit deux points.

\subsubsection{interPS()}

La fonction \cmd{ld.interPS(P, S)} calcule et renvoie (si elle existe) l'intersection entre le plan \argu{P} et la sphère \argu{S}$=\{C,r\}$ avec $C$ le centre (point 3D) et $r$ le rayon de la sphère. La fonction renvoie soit \nil (intersection vide), soit une séquence de la forme $I,r,n$, où $I$ est un point 3D représentant le centre d'un cercle, $r$ son rayon et $n$ un vecteur normal au plan du cercle, ce cercle est l'intersection cherchée. 

\subsubsection{interSS()}

La fonction \cmd{ld.interSS(S1, S2)} calcule et renvoie (si elle existe) l'intersection entre la sphère \argu{S1}$=\{C1,r1\}$ et \argu{S2}$=\{C2,r2\}$. La fonction renvoie soit \nil (intersection vide), ou bien une séquence de la forme $I,r,n$, où $I$ est un point 3D représentant le centre d'un cercle, $r$ son rayon et $n$ un vecteur normal au plan du cercle, ce cercle est l'intersection cherchée. 

\subsubsection{interSSS()}

La fonction \cmd{ld.interSSS(S1, S2, S3)} calcule et renvoie (si elle existe) l'intersection entre les sphères \argu{S1}$=\{C1,r1\}$, \argu{S2}$=\{C2,r2\}$ et \argu{S3}$=\{C3,r3\}$. La fonction renvoie soit \nil (intersection vide), soit un seul point, soit deux points (séquence).


\subsubsection{merge3d()}

La fonction \cmd{ld.merge3d(L \fac{, epsilon})} recolle si c'est possible, les composantes connexes de \argu{L} qui doit être une liste de listes de points 3D, la fonction renvoie le résultat. L'argument \argu{epsilon} vaut par défaut $10^{-10}$, il est utilisé lors des comparaisons.

\subsubsection{split\_points\_by\_visibility()}

La fonction \cmd{ld.split\_points\_by\_visibility(L, visible\_function)} où \argu{L} est une liste de points 3D, ou une liste de listes de points 3D, et où \argu{visible\_function} est une fonction telle que \code{visible\_function(A)} retourne \true si le point 3D $A$ est visible, \false sinon, permet de trier les points de \argu{L} suivant qu'ils sont visibles ou non. La fonction renvoie une séquence de deux tables : \emph{visible\_points}, \emph{hidden\_points}.

\begin{demo}{Une courbe sur un cylindre}
\begin{luadraw}{name=curve_on_cylinder}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, vecI, vecJ, vecK, M, Mc = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M, pt3d.Mc

local g = ld.graph3d:new{adjust2d=true,bbox=false,size={10,10}, viewdir="central"}
g:Labelsize("footnotesize")
ld.Hiddenlines = true; ld.Hiddenlinestyle = "dashed"
local curve_on_cylinder = function(curve,cylinder)
-- curve is a 3D polyline on a cylinder,
-- cylinder = {A,r,V,B}
    local A,r,V,B = table.unpack(cylinder)
    if B == nil then B = V; V = B-A end
    local U = B-A
    local visible_function
    if ld.projection_mode == "central" then
        visible_function = function(N)
            local I = ld.dproj3d(N,{A,U})
            local M1, M2 = ld.interCS({I,r,U},{ (I+ld.camera)/2, pt3d.abs(I-ld.camera)/2})
            local alpha = pt3d.angle3d(M1-I,ld.camera-I)
            return pt3d.angle3d(N-I,ld.camera-I) <= alpha
        end
    else
        visible_function = function(N)
            local I = ld.dproj3d(N,{A,U})
            return (pt3d.dot(N-I,g.Normal) >= 0)
        end
    end
    return ld.split_points_by_visibility(curve,visible_function)
end
-- test
local A, r, B = -5*vecJ, 4, 5*vecJ -- cylinder
local p = function(t) return Mc(r,t,t/3) end
local Curve = ld.rotate3d( ld.parametric3d(p,-4*math.pi,4*math.pi),90,{Origin,vecI})
local Vi, Hi = curve_on_cylinder(Curve,{A,r,B})
local curve_color = "DarkGreen"
g:Dboxaxes3d({grid=true,gridcolor="gray",fillcolor="LightGray"})
g:Dcylinder(A,r,B,{color="orange"})
g:Dpolyline3d(Vi,curve_color)
g:Dpolyline3d(Hi,curve_color..","..ld.Hiddenlinestyle)
g:Show()
\end{luadraw}
\end{demo}
