-- luadraw_transformations.lua (chargé par luadraw_calc.lua) -- date 2026/05/29 -- version 3.1 -- Copyright 2026 Patrick Fradin -- This work may be distributed and/or modified under the -- conditions of the LaTeX Project Public License. -- The latest version of this license is in -- https://www.ctan.org/license/lppl local ld = luadraw local cpx = ld.cpx local Z = cpx.Z local toComplex = cpx.toComplex local isComplex = cpx.isComplex local notDef = ld.notDef function ld.ftransform(L,f) -- image de L par la fonction f (l'image du complexe cpx.Jump est lui-même) -- f doit être une fonction de la variable complexe -- L est un complexe ou une liste de complexes ou une liste de listes de complexes if (L == nil) then return end if (type(L) == "number") or isComplex(L) then if z == cpx.Jump then return cpx.Jump else return f(L) end end local res, u = {} if (type(L) ~= "table") or (#L == 0) then return end if (type(L[1]) == "number") or isComplex(L[1]) then -- liste de réels/complexes for _,z in ipairs(L) do if z == cpx.Jump then u = cpx.Jump else u = f(z) end if u ~= nil then table.insert(res,u) end end if #res > 0 then return res end else -- L est une liste de listes for _, cp in ipairs(L) do local aux = {} for _, z in ipairs(cp) do if z == cpx.Jump then u = cpx.Jump else u = f(z) end if u ~= nil then table.insert(aux,u) end end if #aux > 0 then table.insert(res, aux) end end if #res > 0 then return res end end end function ld.proj(L,d) -- image de L par la projection orthogonale sur la droite d = {point, vecteur directeur} -- L est un complexe ou une liste de complexes ou une liste de listes de complexes if (d == nil) or (type(d) ~= "table") or (#d ~= 2) then return end local B, u = d[1], d[2] B = toComplex(B); u = toComplex(u) if (B ==nil) or (u == nil) or ((u.re == 0) and (u.im == 0)) then return end local D = cpx.dot(u,u) local proj1 = function(A) -- image d'un seul point -- image d'un seul point A = toComplex(A) if (A == nil) then return end local t = cpx.dot(A-B,u) / D if t ~= nil then local C = B+t*u if notDef(C.re) or notDef(C.im) then return else return C end end end return ld.ftransform(L,proj1) end function ld.projO(L,d,v) -- image de L par la projection oblique sur la droite d = {point, vecteur directeur} parallèlement au vecteur v -- L est un complexe ou une liste de complexes ou une liste de listes de complexes if (d == nil) or (type(d) ~= "table") or (#d ~= 2) then return end local B, u = d[1], d[2] B = toComplex(B); u = toComplex(u); v = toComplex(v) if (B ==nil) or (u == nil) or (v == nil) or ((u.re == 0) and (u.im == 0)) or ((v.re == 0) and (v.im == 0)) then return end local D = cpx.det(u,v) local projO1 = function(A) -- image d'un point A = toComplex(A) if (A == nil) then return end local t = cpx.det(A-B,v) / D if t ~= nil then return B+t*u end end return ld.ftransform(L,projO1) end function ld.sym(L,d) -- image de L par la symétrie orthogonale d'axe la droite d = {point, vecteur directeur} -- L est un complexe ou une liste de complexes ou une liste de listes de complexes if (d == nil) or (type(d) ~= "table") or (#d ~= 2) then return end local B, u = d[1], d[2] B = toComplex(B); u = toComplex(u) if (B ==nil) or (u == nil) or ((u.re == 0) and (u.im == 0)) then return end local D = cpx.dot(u,u) local sym1 = function(A) -- image d'un seul point A = toComplex(A) if (A == nil) then return end local t = cpx.dot(A-B,u) / D if t ~= nil then local C = B+t*u -- projeté de A sur d if notDef(C.re) or notDef(C.im) then return else return 2*C-A end end end return ld.ftransform(L,sym1) end function ld.symO(L,d,v) -- image de L par la symétrie oblique d'axe la droite d = {point, vecteur directeur} parallèlement au vecteur v -- L est un complexe ou une liste de complexes ou une liste de listes de complexes if (d == nil) or (type(d) ~= "table") or (#d ~= 2) then return end local B, u = d[1], d[2] B = toComplex(B); u = toComplex(u); v = toComplex(v) if (B ==nil) or (u == nil) or (v == nil) or ((u.re == 0) and (u.im == 0)) or ((v.re == 0) and (v.im == 0)) then return end local D = cpx.det(u,v) local symO1 = function(A) -- image d'un point A = toComplex(A) if (A == nil) then return end local t = cpx.det(A-B,v) / D if t ~= nil then return 2*(B+t*u)-A end end return ld.ftransform(L,symO1) end function ld.symG(L,d,v) -- image de L par la symétrie glissée d'axe la droite d = {point, vecteur directeur} et de vecteur v -- L est un complexe ou une liste de complexes ou une liste de listes de complexes if (d == nil) or (type(d) ~= "table") or (#d ~= 2) then return end local B, u = d[1], d[2] B = toComplex(B); u = toComplex(u); v = toComplex(v) if (B ==nil) or (u == nil) or (v == nil) or ((u.re == 0) and (u.im == 0)) or ((v.re == 0) and (v.im == 0)) then return end local D = cpx.dot(u,u) local symG1 = function(A) A = toComplex(A) if (A == nil) then return end local t = cpx.dot(A-B,u) / D if t ~= nil then local C = B+t*u -- projeté de A sur d if notDef(C.re) or notDef(C.im) then return else return 2*C-A+v end end end return ld.ftransform(L,symG1) end function ld.simil(L,factor,angle,center) -- image de L par une similitude (angle en degrés) -- L est un complexe ou une liste de complexes ou une liste de listes de complexes center = center or 0 center = toComplex(center) if (center == nil) or (factor == nil) or (type(factor) ~= "number") or (angle == nil) or (type(angle) ~= "number") then return end angle = angle * math.pi / 180 local simil1 = function(A) -- image d'un point A = toComplex(A) if (A == nil) then return end return cpx.exp(cpx.I*angle)*factor*(A-center) + center end return ld.ftransform(L,simil1) end function ld.rotate(L,angle,center) -- image de L par une rotation (angle en degrés) -- L est un complexe ou une liste de complexes ou une liste de listes de complexes return ld.simil(L,1,angle,center) end function ld.shift(L,u) -- image de L par la translation de vecteur u -- L est un complexe ou une liste de complexes ou une liste de listes de complexes u = toComplex(u) if u == nil then return end local shift1 = function(A) -- image d'un point A = toComplex(A) if (A == nil) then return end return A + u end return ld.ftransform(L,shift1) end function ld.hom(L,factor,center) -- image de L par une homothétie -- L est un complexe ou une liste de complexes ou une liste de listes de complexes return ld.simil(L,factor,0,center) end function ld.affin(L,d,v,k) -- image de L par l'affinité de base la droite d, parallèlement au vecteur v (non nul) et de rapport k -- L est un complexe ou une liste de complexes ou une liste de listes de complexes if (d == nil) or (type(d) ~= "table") or (#d ~= 2) then return end if (k == nil) or (type(k) ~= "number") then return end local B, u = d[1], d[2] B = toComplex(B); u = toComplex(u); v = toComplex(v) if (B == nil) or (u == nil) or (v == nil) or ((u.re == 0) and (u.im == 0)) or ((v.re == 0) and (v.im == 0)) then return end local D = cpx.det(u,v) local affin1 = function(A) -- image d'un point A = toComplex(A) if (A == nil) then return end local t = cpx.det(A-B,v) / D if t ~= nil then local C = B+t*u -- projeté oblique de A sur d return k*(A-C)+C end end return ld.ftransform(L,affin1) end function ld.inv(L, rayon, centre) -- image de L par l'inversion -- L est un complexe ou une liste de complexes ou une liste de listes de complexes centre = centre or 0 centre = toComplex(centre) if (rayon == nil) or (type(rayon) ~= "number") or (rayon <= 0) then return end local inv1 = function(A) A = toComplex(A) if (A == nil) then return end if A == centre then return else return centre + rayon*rayon / cpx.bar(A-centre) end end return ld.ftransform(L,inv1) end