-- tkz_elements_triangles.lua
-- date 2025/02/26
-- version 3.32
-- Copyright 2025  Alain Matthes
-- This work may be distributed and/or modified under the
-- conditions of the LaTeX Project Public License, either version 1.3
-- of this license or (at your option) any later version.
-- The latest version of this license is in
--   http://www.latex-project.org/lppl.txt
-- and version 1.3 or later is part of all distributions of LaTeX
-- version 2005/12/01 or later.
-- This work has the LPPL maintenance status “maintained”.
-- The Current Maintainer of this work is Alain Matthes.

triangle = {}
function triangle: new (za, zb ,zc)
   local type          = 'triangle'
   local circumcenter  = circum_center_ (za , zb , zc)
   local centroid      = barycenter_ ( {za,1} , {zb,1} , {zc,1} )
   local incenter      = in_center_ (za , zb , zc)
   local orthocenter   = ortho_center_  (za , zb , zc)
   local eulercenter   = euler_center_  (za , zb , zc)
   local spiekercenter = spieker_center_  (za , zb , zc)
   local c             = point.abs(zb-za)
   local a             = point.abs(zc-zb)
   local b             = point.abs(za-zc)
   local alpha         = angle_normalize_(point.arg ((zc-za) / (zb-za)))
   local beta          = angle_normalize_(point.arg ((za-zb) / (zc-zb)))
   local gamma         = angle_normalize_(point.arg ((zb-zc) / (za-zc)))
   local ab            = line:new(za, zb)
   local ca            = line:new(zc, za)
   local bc            = line:new(zb, zc)
   local semiperimeter = (a + b + c) / 2
   local area          = math.sqrt((semiperimeter)*(semiperimeter-a)*(semiperimeter-b)*(semiperimeter-c))
   local inradius                = area / semiperimeter
   local circumradius            = (a * b * c) / (4 * area)
   local tr = { pa               = za, 
                pb               = zb, 
                pc               = zc, 
                type             = type,
                circumcenter     = circumcenter, 
                centroid         = centroid,
                incenter         = incenter, 
                eulercenter      = eulercenter,      
                orthocenter      = orthocenter, 
                spiekercenter    = spiekercenter,
                a                = a, 
                b                = b, 
                c                = c, 
                ab               = ab, 
                ca               = ca, 
                bc               = bc,
                alpha            = alpha, 
                beta             = beta, 
                gamma            = gamma,
                semiperimeter    = semiperimeter,
                area             = area,
                inradius         = inradius,
                circumradius     = circumradius}
    setmetatable(tr, self)
    self.__index = self
    return tr
end
-----------------------
-- points --
-----------------------
function triangle: trilinear (a,b,c)
   return barycenter_ ({self.pa,a*self.a},{self.pb,b*self.b},{self.pc,c*self.c})
end

function triangle: barycentric (a,b,c)
   return barycenter_ ({self.pa,a},{self.pb,b},{self.pc,c})
end

function triangle: bevan_point ()
   return circum_center_ ( excentral_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: mittenpunkt_point ()
   return lemoine_point_ ( excentral_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: gergonne_point ()
   return gergonne_point_ ( self.pa , self.pb , self.pc)
end

function triangle: nagel_point ()
return   nagel_point_ ( self.pa , self.pb , self.pc)
end

function triangle: feuerbach_point ()
return   feuerbach_point_ ( self.pa , self.pb , self.pc)
end

function triangle: lemoine_point()
   return lemoine_point_ ( self.pa , self.pb , self.pc)
end

function triangle: symmedian_point()
   return lemoine_point_ ( self.pa , self.pb , self.pc)
end

triangle.lemoine_point = triangle.symmedian_point
triangle.grebe_point = triangle.symmedian_point

function triangle: spieker_center()
return spieker_center_ ( self.pa , self.pb , self.pc )
end

function triangle: barycenter (ka,kb,kc)
 return   barycenter_ ({self.pa,ka},{self.pb,kb},{self.pc,kc})
end

function triangle: base (u,v) -- (ab,ac) base coord u,v
    return barycenter_ ({self.pa,(1-u-v)},{self.pb,u},{self.pc,v})
end

function triangle: euler_points ()
   H = self.orthocenter
   return midpoint_ ( H,self.pa ), midpoint_ ( H,self.pb ), midpoint_ ( H,self.pc )
end

function triangle:nine_points()
    local ma, mb, mc, ha, hb, hc
    -- Calculate the medial triangle
    ma, mb, mc = medial_tr_(self.pa, self.pb, self.pc)
    -- Calculate the orthic triangle
    ha, hb, hc = orthic_tr_(self.pa, self.pb, self.pc)
    -- Calculate the orthocenter
    H = self.orthocenter   
    -- Return the points of the nine-point circle
    return ma, mb, mc, ha, hb, hc,
           midpoint_(H, self.pa), midpoint_(H, self.pb), midpoint_(H, self.pc)
end


function triangle : point (t)
  local p = 2* self.semiperimeter
  local t1 = self.a / p
  local t2 = (self.a + self.b) / p
   if t<= t1 then  
      return self.ab : point (t/t1) 
   elseif t <= t2 then
      return self.bc : point ((t*p-self.a)/self.b)
   else
      return self.ca : point ((t*p-self.c-self.b)/self.a)
   end
end

function triangle :  soddy_center ()
   return soddy_center_ (self.pa,self.pb,self.pc)
end

function triangle :  conway_points ()
   local  a1 =      report_ (self.pb,self.pa,length(self.pb,self.pc),self.pa)
   local  a2 =      report_ (self.pc,self.pa,length(self.pb,self.pc),self.pa)
   local  b1 =      report_ (self.pa,self.pb,length(self.pa,self.pc),self.pb)
   local  b2 =      report_ (self.pc,self.pb,length(self.pa,self.pc),self.pb)
   local  c1 =      report_ (self.pb,self.pc,length(self.pb,self.pa),self.pc)
   local  c2 =      report_ (self.pa,self.pc,length(self.pb,self.pa),self.pc)
     return a1,a2,b1,b2,c1,c2
  end
  
  function triangle : kimberling (n)
    local cos,sin,tan = math.cos,math.sin,math.tan
    local A,B,C = self.alpha,self.beta,self.gamma
    local a,b,c = self.a,self.b,self.c
    local pi = math.pi
        if n == 1 then return self : trilinear(1,1,1)     -- incenter
    elseif n == 2 then return self : barycentric (1,1,1)  -- centroid
    elseif n == 3 then return self : trilinear (cos(A),cos(B),cos(C))  --circumcenter
    elseif n == 4 then return self : barycentric (tan(A),tan(B),tan(C)) -- orthocenter
    elseif n == 5 then return self : trilinear(cos(B - C),cos(C - A),cos(A - B))  --nine
    elseif n == 6 then return self : trilinear (a,b,c)   -- lemoine
    elseif n == 7 then return self : barycentric (1/(b+c-a),1/(a+c-b),1/(a+b-c))-- gergonne
    elseif n == 8 then return self : barycentric(b+c-a,a+c-b,a+b-c) -- nagel
    elseif n == 0 then return self : barycentric(a*(b+c-a),b*(a+c-b),c*(a+b-c)) -- mittenpunkt 
    elseif n == 10 then return self : barycentric(b+c,a+c,a+b) -- spieker 
    elseif n == 11 then return self : trilinear(1-cos(B - C),1-cos(C - A),1-cos(A - B))   -- feuerbach  
    elseif n == 13 then return self : trilinear(1/sin(A+pi/3),1/sin(B+pi/3),1/sin(C+pi/3))   -- first fermat
    elseif n == 14 then return self : trilinear(1/sin(A-pi/3),1/sin(B-pi/3),1/sin(C-pi/3))   -- second fermat
    elseif n == 19 then return self : trilinear(tan(A),tan(B),tan(C)) -- clawson
    elseif n == 20 then return self : barycentric(tan(B)+tan(C)-tan(A),tan(A)+tan(C)-tan(B),tan(A)+tan(B)-tan(C)) --de Longchamps
    elseif n == 110 then return self : trilinear( a/(b^2 - c^2),
      b/(c^2 - a^2),
      c/(a^2 - b^2)) -- kiepert parabola
    elseif n == 115 then return self : barycentric( (b^2 - c^2)^2,(c^2 - a^2)^2,(a^2 - b^2)^2) -- kiepert hyperbola
    end
  end
  
  function triangle : isogonal (pt)
    local pa  = self.bc : reflection (pt)
    local pb  = self.ca : reflection (pt)
    local pc  = self.ab : reflection (pt)
    return circum_center_ (pa,pb,pc)   
  end
  
  function triangle: first_fermat_point ()
    local x = equilateral_tr_(self.pb,self.pa)
    local y = equilateral_tr_(self.pa,self.pc)
    return intersection_ll_(x,self.pc,y,self.pb)
  end

  function triangle: second_fermat_point ()
    local x = equilateral_tr_(self.pa,self.pb)
    local y = equilateral_tr_(self.pc,self.pa)
    return intersection_ll_(x,self.pc,y,self.pb)
  end
 
  function triangle : orthic_axis_points () 
    return orthic_axis_(self.pa,self.pb,self.pc)
  end 
-------------------
-- Result -> line
-------------------
-- N,H,G,O -- check_equilateral_ (a,b,c)
function triangle: euler_line ()
  if  check_equilateral_(self.pa,self.pb,self.pc) 
     then 
        tex.error("An error has occurred", {"No euler line with equilateral triangle"})
     else
       local a,b,c = orthic_axis_(self.pa,self.pb,self.pc)
       local x = projection_(a,c,self.eulercenter)
        return  line : new (self.eulercenter,x)
     end
end

function triangle : fermat_axis ()
  local x = self: first_fermat_point ()
   local y = self: second_fermat_point ()
   return line : new (x,y)
end

function triangle : brocard_axis ()
  return line : new (self.circumcenter,self: lemoine_point())  
end

function triangle : simson_line (pt) -- pt on circumcircle
  local x = self.ab : projection (pt)
  local y = self.bc : projection (pt)
  return   line : new (x,y)
end

function triangle: symmedian_line (n)
   local a = self.pa
   local b = self.pb
   local c = self.pc
   local l = self : lemoine_point ()
   if n==1 then 
           return line : new (b,intersection_ll_ (b,l,a,c)) 
    elseif n==2 then
           return line : new (c,intersection_ll_ (c,l,a,b))
     else
           return line : new (a,intersection_ll_ (a,l,b,c))
    end
end

-- with pt on the circumcircle
function triangle:steiner_line (pt)
  local u = symmetry_axial_(self.pa,self.pb,pt)
  local v = symmetry_axial_(self.pa,self.pc,pt)
  return line : new (u,v) 
end


function triangle: altitude (n)
   local a,b,c,o,p
   a = self.pa
   b = self.pb
   c = self.pc
   o = self.orthocenter
   if n==1 then 
        p = projection_ (a,c,b)
           return line : new (b,p)
    elseif n==2 then
        p = projection_ (a,b,c)
           return line : new (c,p)
     else
        p = projection_ (b,c,a)
           return  line : new (a,p) 
    end
end

function triangle: bisector (n)
    local  a = self.pa
    local  b = self.pb
    local  c = self.pc
    local  i = self.incenter
      if n==1 then
              return line : new (b,intersection_ll_ (b,i,a,c))
       elseif n==2 then
              return line : new (c,intersection_ll_ (c,i,a,b))
        else
              return  line : new (a,intersection_ll_ (a,i,b,c))
       end
end

function triangle: bisector_ext(n)   -- n =1 swap n=2 2 swap
 local  a = self.pa
 local  b = self.pb
 local  c = self.pc
 if n==1 then -- ac
         return line : new (b,bisector_ext_ (b,c,a))
  elseif n==2 then -- ab
     
         return line : new (c,bisector_ext_ (c,a,b))
   else -- bc
         return line : new (a,bisector_ext_ (a,b,c))
  end
end

function triangle: antiparallel(pt,n)   -- n = 1 swap ; n= 2 2 swap
local a,b,c,i,u,v,w
   a = self.pa
   b = self.pb
   c = self.pc
   i = self.incenter
 if n==1 then
    u = symmetry_axial_ (b,i,a)
    v = symmetry_axial_ (b,i,c)
    w = ll_from_ ( pt , u , v )
    intersection_ll_ (pt,w,a,b)
        return line : new (intersection_ll_ (pt,w,c,b),intersection_ll_ (pt,w,a,b))
  elseif n==2 then
     u = symmetry_axial_ (c,i,a)
     v = symmetry_axial_ (c,i,b)
     w = ll_from_ ( pt , u , v )
     intersection_ll_ (pt,w,a,c)
         return line : new (intersection_ll_ (pt,w,b,c),intersection_ll_ (pt,w,a,c))
   else
      u = symmetry_axial_ (a,i,b)
      v = symmetry_axial_ (a,i,c)
      w = ll_from_ ( pt , u , v )
      intersection_ll_ (pt,w,b,c)
          return line : new (intersection_ll_ (pt,w,c,a),intersection_ll_ (pt,w,b,a))
  end
end

function triangle : lemoine_axis () -- revoir car problème
  local C = self : circum_circle ()
  local pl = self : lemoine_point()
  return C : polar (pl)
end

function triangle : orthic_axis () 
  local x,y,z = orthic_axis_(self.pa,self.pb,self.pc)
  return line : new (x,z)
end

-----------------------
--- Result -> circles --
-----------------------
function triangle:euler_circle()
   return circle : new (euler_center_ ( self.pa , self.pb , self.pc),midpoint_( self.pb , self.pc))
end

function triangle:circum_circle()
return   circle : new (circum_circle_ ( self.pa , self.pb , self.pc), self.pa )
end

function triangle:in_circle()
return  circle : new  (self.incenter, projection_ (self.pb , self.pc,self.incenter) )
end

function triangle:ex_circle (n)   -- n =1 swap n=2 2 swap
   local a,b,c,o
   a = self.pa
   b = self.pb
   c = self.pc
 if n==1 then 
    o = ex_center_ (b,c,a) 
     return circle : new (o ,   projection_ (c,a,o))
  elseif n==2 then
     o = ex_center_ (c,a,b) 
      return circle : new (o ,   projection_ (a,b,o))
   else
      o = ex_center_ (a,b,c) 
     return  circle : new (o ,   projection_ (b,c,o)) 
  end
end

function triangle: first_lemoine_circle()
local lc,oc
   lc = self: lemoine_point()
   oc = self.circumcenter
return circle : new( midpoint_ (lc,oc),intersection_ll_ (lc,ll_from_ (lc,self.pa,self.pb),self.pa,self.pc))
end

function triangle: second_lemoine_circle()
local lc,a,b,c,r,th
   lc = self: lemoine_point()
    a = point.abs(self.pc-self.pb)
    b = point.abs(self.pa-self.pc)
    c = point.abs(self.pb-self.pa)
    r = ( a*b*c) / (a*a+b*b+c*c)
    th = lc + point(r,0)
  return   circle : new (lc, th )
end

function triangle: spieker_circle()
local ma,mb,mc,sp
      ma,mb,mc =  medial_tr_ ( self.pa , self.pb , self.pc)
      sp       = in_center_ (ma,mb,mc)
return circle : new (sp,projection_ (ma,mb,sp))
end


function triangle :  soddy_circle ()
  local s,i = soddy_center_ (self.pa,self.pb,self.pc)
   return circle : new ( s , i )
end

function triangle :  cevian_circle (p)
    local pta,ptb,ptc = cevian_  (self.pa,self.pb,self.pc,p)
     return circle : new (circum_circle_ (pta,ptb,ptc),pta)
  end

function triangle :  symmedial_circle ()
  local pta,ptb,ptc,p
   p = lemoine_point_ ( self.pa , self.pb , self.pc)
   pta,ptb,ptc = cevian_  (self.pa,self.pb,self.pc,p)
   return circle : new (circum_circle_ (pta,ptb,ptc),pta)
end
  
function triangle :  conway_circle ()
  local t
   t =      report_ (self.pb,self.pa,length(self.pb,self.pc),self.pa)
   return circle : new (self.incenter,t)
end

function triangle :  pedal_circle (pt)
  local x,y,z,c
  x = projection_ (self.pb,self.pc,pt)
  y = projection_ (self.pa,self.pc,pt)
  z = projection_ (self.pa,self.pb,pt)
  c = circum_center_ (x,y,z)
  return circle : new (c,x)
end    

function triangle: bevan_circle ()
   local o,r,s,t
   o = circum_center_ ( excentral_tr_ ( self.pa , self.pb , self.pc))
   r,s,t = excentral_tr_ ( self.pa , self.pb , self.pc)
return  circle : new  (o, r)
end

function triangle:taylor_points()
  local a,b,c = orthic_tr_(self.pa,self.pb,self.pc)
  return 
         projection_(self.pa, self.pc, a),
         projection_(self.pa, self.pb, a),
         projection_(self.pb, self.pa, b),
         projection_(self.pb, self.pc, b),
         projection_(self.pc, self.pb, c),
         projection_(self.pc, self.pa, c)
end

function triangle : taylor_circle ()
  local a,b,c = orthic_tr_(self.pa,self.pb,self.pc)
   local d = projection_(self.pa, self.pb, a)
   local e = projection_(self.pb, self.pc, b)
   local f = projection_(self.pc, self.pa, c)
   return  circle : new ( circum_circle_ (d,e,f),d)
end
-------------------
-- Result -> triangle
-------------------
function triangle: orthic()
return   triangle : new (orthic_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: medial()
return    triangle : new (medial_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: incentral()
    return   triangle : new (incentral_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: excentral()
    return   triangle : new (excentral_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: intouch()
    return  triangle : new  (intouch_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: contact()
    return  triangle : new  (intouch_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: extouch()
    return  triangle : new  (extouch_tr_ ( self.pa , self.pb , self.pc))
end

function triangle: feuerbach()
    return   triangle : new (feuerbach_tr_ (self.pa , self.pb , self.pc))
end

function triangle: anti ()
     return  triangle : new (anti_tr_  (self.pa,self.pb,self.pc))
end

triangle.anticomplementary = triangle.anti

function triangle: tangential ()
     return triangle : new  (tangential_tr_  (self.pa,self.pb,self.pc))
end

function triangle: cevian (p)
   return triangle : new  (cevian_  (self.pa,self.pb,self.pc,p))
end

function triangle: symmedian ()
   local p = lemoine_point_ ( self.pa , self.pb , self.pc)
   return triangle : new  (cevian_  (self.pa,self.pb,self.pc,p))
end

function triangle: symmedial ()
   local p = lemoine_point_ ( self.pa , self.pb , self.pc)
   return triangle : new  (cevian_  (self.pa,self.pb,self.pc,p))
end

function triangle: euler ()
   return triangle : new  (euler_points_ (self.pa,self.pb,self.pc) )
end

function triangle: pedal (pt)
   return triangle : new  (self : projection (pt))
end

function triangle: similar ()
   return triangle : new  (similar_ (self.pa,self.pb,self.pc) )
end
-------------------
-- Result -> conic
-------------------
function triangle: steiner_inellipse ()
   return steiner_ (self.pa,self.pb,self.pc)
end

function triangle: steiner_circumellipse ()
   local e =  steiner_ (self.pa,self.pb,self.pc)
   local v =  report_(e.center,e.vertex,e.a,e.vertex)
   local cv = report_(e.center,e.covertex,e.b,e.covertex)
   return conic : new ( EL_points (e.center,v,cv))
end

function triangle: euler_ellipse ()
   if  self : check_acutangle ()
   then
    local  x,y  = intersection_lc_ (self.orthocenter,self.circumcenter,
                        self.eulercenter,self.ab.mid)
    local a =.5 * length(x,y)
  return conic : new ( EL_bifocal (self.orthocenter,self.circumcenter,a))
   end
end

--  kiepert ellipse
function triangle : kiepert_hyperbola ()
    -- center
    local center    = self : kimberling (115)
    local O         = self.circumcenter
    local G         = self.centroid
    --  Brocard axis
    local L         = self : brocard_axis ()
    local M,N       = intersection_lc_ (L.pa,L.pb,O,self.pa)
     -- simson lines
    local Lsa       = self : simson_line (M)
    local Lsb       = self : simson_line (N)
    local axis      = bisector (center,Lsa.pa,Lsb.pa)
   -- new frame system
    local U         = report_(axis.pa,axis.pb,-1,center)
    local V         = rotation_ (center,math.pi/2,U)
    -- 
    local  minor = axis : ortho_from (center)
    local sys       = occs : new (minor  ,center)
    local x,y       = sys : coordinates (self.pa)
     -- x=-x
     -- y=-y
   -- local x,y       = newcoordinates (self.pa,center,U,V)
    local a         = math.sqrt(x^2 - y^2)
    local c         = math.sqrt(2) * a
    local Fa        = report_(center,axis.pb,-c,    center)
    local Fb        = report_(center,axis.pb, c,    center)
    local K         = report_(center,axis.pb,-a^2/c,center)
    local directrix = axis : ortho_from (K)
    return conic : new (Fa,directrix,math.sqrt(2))
  end
  --  kiepert parabola
  function triangle : kiepert_parabola ()
    if (self.a == self.b) or (self.a == self.c) or (self.c == self.b)
    then 
      tex.error("An error has occurred", {"No X(110)"})
    else
      L = self : euler_line ()
      F = self : kimberling (110)
      return conic : new (F,L,1)
    end
  end
-------------------
-- Result -> miscellaneous
-------------------
function triangle: get_angle (n)
   local a,b,c
   a = self.pa
   b = self.pb
   c = self.pc
   if n==1 then
          return  point.arg  ((a-b)/(c-b))
    elseif n==2 then
          return  point.arg  ((b-c)/(a-c))
     else
          return  point.arg  ((c-a)/(b-a))
    end
end

function triangle: projection (p)
  local  x = projection_ (self.pb,self.pc,p)
  local  y = projection_ (self.pa,self.pc,p)
  local  z = projection_ (self.pa,self.pb,p)
  return   x,y,z
end

function triangle: parallelogram ()
    local x = self.pc + self.pa - self.pb
  return  x
end

function triangle: area () -- obsolete
    return area_(self.pa,self.pb,self.pc)
end

function triangle: barycentric_coordinates (pt)
   return barycentric_coordinates_ (self.pa,self.pb,self.pc,pt)
end

function triangle: in_out (pt)
 return in_out_ (self.pa,self.pb,self.pc,pt)
end

function triangle: check_equilateral ()
 return check_equilateral_ (self.pa,self.pb,self.pc)
end

function triangle: check_acutangle()
    local asq = self.a * self.a
    local bsq = self.b * self.b
    local csq = self.c * self.c
    if asq + bsq > csq and bsq + csq > asq and csq + asq > bsq then
        return true
    else
        return false
    end
end

-- Circle tangent to two straight lines passing through a given point
function triangle:c_ll_p(p)
  
  -- Compute the bisector of the triangle 
  local lbi = bisector(self.pa, self.pb,self.pc)

  if lbi:in_out(p) then
    -- Orthogonal projection of p onto the bisector
    local lp = lbi:ortho_from(p)

    -- Intersection of line from p to its projection with self.pa and self.pb
    local i = intersection_ll_(p, lp.pb, self.pa, self.pb)

    -- Intersection points of the line with the circle defined by (i, p)
    local t1, t2 = intersection_lc_(self.pa, self.pb, i, p)

    -- Create the main line and find orthogonal projections from t1 and t2
    local lab = line:new(self.pa, self.pb)
    local x = lab:ortho_from(t1).pb
    local y = lab:ortho_from(t2).pb

    -- Return two circles based on the orthogonal projections and points t1, t2
    return circle:new(intersection_ll_(x, t1, self.pa, p), t1),
           circle:new(intersection_ll_(y, t2, self.pa, p), t2)
  else
     local lab = line:new(self.pa, self.pb)
    -- Reflection of p across the bisector
    local q = lbi : reflection (p)

    -- Compute circles from the Wallis construction
    local c1, c2 = lab:c_l_pp(p, q)

    -- Return two circles with centers and points on their circumference
    return  c1,c2
  end
end


return triangle