-- tkz_elements_triangles.lua -- date 2025/01/06 -- version 3.10 -- Copyright 2024 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*inradius*semiperimeter) local o = { 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(o, self) self.__index = self return o 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 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 () local H H = ortho_center_ ( self.pa , self.pb , self.pc ) 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, H -- 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 = ortho_center_(self.pa, self.pb, self.pc) -- 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 = (self.a + self.b + self.c) 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,a2,b1,b2,c1,c2 a1 = report_ (self.pb,self.pa,length(self.pb,self.pc),self.pa) a2 = report_ (self.pc,self.pa,length(self.pb,self.pc),self.pa) b1 = report_ (self.pa,self.pb,length(self.pa,self.pc),self.pb) b2 = report_ (self.pc,self.pb,length(self.pa,self.pc),self.pb) c1 = report_ (self.pb,self.pc,length(self.pb,self.pa),self.pc) c2 = report_ (self.pa,self.pc,length(self.pb,self.pa),self.pc) return a1,a2,b1,b2,c1,c2 end ------------------- -- Result -> line ------------------- -- N,H,G,O -- check_equilateral_ (a,b,c) function triangle: euler_line () if not check_equilateral_(self.pa,self.pb,self.pc) then return line : new (self.orthocenter,self.circumcenter) end 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 function triangle: altitude (n) local a,b,c,o,p a = self.pa b = self.pb c = self.pc o = ortho_center_ (a,b,c) 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 = in_center_ (a,b,c) 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 = in_center_ (a,b,c) 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 ----------------------- --- 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() local o o = in_center_ ( self.pa , self.pb , self.pc) return circle : new (o, projection_ (self.pb , self.pc,o) ) 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 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 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 i,t i = in_center_ (self.pa,self.pb,self.pc) t = report_ (self.pb,self.pa,length(self.pb,self.pc),self.pa) return circle : new (i,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 ------------------- -- 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 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 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 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) local x,y,z x = projection_ (self.pb,self.pc,pt) y = projection_ (self.pa,self.pc,pt) z = projection_ (self.pa,self.pb,pt) return triangle : new (x,y,z) end function triangle: similar () return triangle : new (similar_ (self.pa,self.pb,self.pc) ) end ------------------- -- Result -> ellipse ------------------- function triangle: steiner_inellipse () return steiner_ (self.pa,self.pb,self.pc) end function triangle: steiner_circumellipse () local e e = steiner_ (self.pa,self.pb,self.pc) return ellipse: radii (e.center,2*e.Rx,2*e.Ry,e.slope ) end function triangle: euler_ellipse () if self : check_acutangle () then local a,b a,b = intersection_lc_ (self.orthocenter,self.circumcenter, self.eulercenter,self.ab.mid) return ellipse: foci (self.orthocenter,self.circumcenter,a) 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,y,z x = projection_ (self.pb,self.pc,p) y = projection_ (self.pa,self.pc,p) 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 () 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