-- File: tkz_elements_triangle.lua -- Copyright (c) 2026 Alain Matthes -- SPDX-License-Identifier: LPPL-1.3c -- Maintainer: Alain Matthes triangle = {} triangle.__index = triangle function triangle:new(za, zb, zc) local eps = tkz.epsilon -- ===== Guardrails against degeneration ===== -- sall vertices combined if point.abs(za - zb) <= eps or point.abs(zb - zc) <= eps or point.abs(zc - za) <= eps then error("tkz-elements: triangle: degenerate triangle (two vertices are identical)", 2) end -- aligned vertices if tkz.is_linear(za, zb, zc) then error("tkz-elements: triangle: degenerate triangle (the vertices are collinear)", 2) end -- ===== Side lengths ===== local c = point.abs(zb - za) local a = point.abs(zc - zb) local b = point.abs(za - zc) -- ===== Line objects ===== local ab = line:new(za, zb) local bc = line:new(zb, zc) local ca = line:new(zc, za) -- ===== Angles ===== local alpha = math.abs(get_angle_(za, zb, zc)) local beta = math.abs(get_angle_(zb, zc, za)) local gamma = math.abs(get_angle_(zc, za, zb)) local alpha_ = angle:new(za, zb, zc) local beta_ = angle:new(zb, zc, za) local gamma_ = angle:new(zc, za, zb) 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 orientation = find_orientation_(za, zb, zc) local semiperimeter = (a + b + c) / 2 local cross = (zb - za) ^ (zc - za) local area = 0.5 * math.abs((zb - za) ^ (zc - za)) local inradius = area / semiperimeter local circumradius = (a * b * c) / (4 * area) local ma = midpoint_(zb, zc) local mb = midpoint_(zc, za) local mc = midpoint_(za, zb) local ha = projection_(zb, zc, za) local hb = projection_(zc, za, zb) local hc = projection_(za, zb, zc) local ia = find_foot_bisector_(za, zb, zc) local ib = find_foot_bisector_(zb, zc, za) local ic = find_foot_bisector_(zc, za, zb) 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, ma = ma, mb = mb, mc = mc, ha = ha, hb = hb, hc = hc, ia = ia, ib = ib, ic = ic, alpha = alpha, beta = beta, gamma = gamma, alpha_ = alpha_, beta_ = beta_, gamma_ = gamma_, cross = cross, semiperimeter = semiperimeter, area = area, inradius = inradius, circumradius = circumradius, orientation = orientation, } setmetatable(tr, self) return tr end setmetatable(triangle, { __call = function(cls, ...) return cls:new(...) end, }) function triangle:get(i) if i == nil then return self.pa, self.pb, self.pc elseif i == 1 then return self.pa elseif i == 2 then return self.pb elseif i == 3 then return self.pc else return nil end end ----------------------- -- Result -> boolean ----------------------- -- added strict function triangle:position(p, EPS) EPS = EPS or tkz.epsilon local x, y, z = barycentric_coordinates_(self.pa, self.pb, self.pc, p) local in_ = (x > EPS and y > EPS and z > EPS) local out_ = (x < -EPS or y < -EPS or z < -EPS) if in_ then return "IN" elseif out_ then return "OUT" else return "ON" -- border zone (tolerance) end end -- strict = true: the boundary is excluded function triangle:in_out(pt, strict) if strict == true then return in_out_strict_(self.pa, self.pb, self.pc, pt) end return in_out_(self.pa, self.pb, self.pc, pt) end function triangle:on_triangle(pt, EPS) EPS = EPS or tkz.epsilon local a, b, c = self.pa, self.pb, self.pc local x, y, z = barycentric_coordinates_(a, b, c, pt) -- boundary cases local onx = math.abs(x) <= EPS local ony = math.abs(y) <= EPS local onz = math.abs(z) <= EPS if onx or ony or onz then return true end return false end function triangle:check_equilateral(EPS) return check_equilateral_(self.pa, self.pb, self.pc, EPS) end triangle.is_equilateral = triangle.check_equilateral function triangle:check_acutangle(EPS) return check_acutangle_(self.pa, self.pb, self.pc, EPS) end triangle.is_acute = triangle.check_acutangle function triangle:check_isosceles(EPS) return check_isosceles_(self.pa, self.pb, self.pc, EPS) end triangle.is_isosceles = triangle.check_isosceles ----------------------- -- Result -> real ----------------------- function triangle:area() -- obsolete return area_(self.pa, self.pb, self.pc) end function triangle:barycentric_coordinates(pt) local xA, yA = self.pa:get() local xB, yB = self.pb:get() local xC, yC = self.pc:get() local xP, yP = pt:get() local orient2d_ABC = oriented_area2_(xA, yA, xB, yB, xC, yC) local l_A = oriented_area2_(xP, yP, xB, yB, xC, yC) / orient2d_ABC local l_B = oriented_area2_(xA, yA, xP, yP, xC, yC) / orient2d_ABC local l_C = oriented_area2_(xA, yA, xB, yB, xP, yP) / orient2d_ABC return l_A, l_B, l_C end function triangle:trilinear_coordinates(pt) local la, lb, lc = self:barycentric_coordinates(pt) local ta, tb, tc = la / self.a, lb / self.b, lc / self.c -- normalize local N = ta + tb + tc return ta / N, tb / N, tc / N end function triangle:trilinear_to_d(x, y, z) return trilinear_to_d_(x, y, z, self.a, self.b, self.c) end -- get_angle(arg): measure (radians) of the angle at a vertex; -- arg = 0, 1, 2 (vertex A, B, C) or a vertex given as a point. function triangle:get_angle(arg) local i = resolve_triangle_index_(self, arg) -- 0, 1, 2 if i == 0 then return self.alpha end -- angle at A if i == 1 then return self.beta end -- angle at B return self.gamma -- angle at C end -- angle(i): the angle object (class angle) at a vertex; -- i = 1, 2, 3 (as for get(i)) or a vertex given as a point. function triangle:angle(i) local angles = { self.alpha_, self.beta_, self.gamma_ } if type(i) == "number" then if i ~= 1 and i ~= 2 and i ~= 3 then error("tkz-elements: triangle:angle: the index must be 1, 2 or 3, got " .. tostring(i), 2) end return angles[i] elseif type(i) == "table" and i.type == "point" then for k, v in ipairs({ self.pa, self.pb, self.pc }) do if point.abs(i - v) <= tkz.epsilon then return angles[k] end end error("tkz-elements: triangle:angle: the point is not a vertex of the triangle", 2) end error("tkz-elements: triangle:angle: expected 1, 2, 3 or a vertex, got " .. tostring(i), 2) end function triangle:parameter(p) local a, b, c = self.a, self.b, self.c -- a=BC, b=CA, c=AB local A, B, C = self.pa, self.pb, self.pc local AB, BC, CA = self.ab, self.bc, self.ca local ptotal = a + b + c if AB:on_segment(p) then return length_(A, p) / ptotal elseif BC:on_segment(p) then return (c + length_(B, p)) / ptotal elseif CA:on_segment(p) then return (c + a + length_(C, p)) / ptotal else error("tkz-elements: triangle:parameter: the point is not on the boundary of the triangle", 2) end end ----------------------- -- Result -> point ----------------------- function triangle:barycenter(ka, kb, kc) return barycenter_({ self.pa, ka }, { self.pb, kb }, { self.pc, kc }) end triangle.barycentric = triangle.barycenter 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:point(t) local p = self.a + self.b + self.c -- perimeter local t1 = self.c / p -- end of AB local t2 = (self.c + self.a) / p -- end of AB + BC if t <= t1 then return self.ab:point(t / t1) elseif t <= t2 then return self.bc:point((t - t1) / (t2 - t1)) else return self.ca:point((t - t2) / (1 - t2)) end end function triangle:random(inside) inside = (inside == "inside") if inside then local x = self.bc:random() local L = line:new(self.pa, x) return L:random() else return self:point(math.random()) end end 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:kimberling(n) local cos, sin, tan, pi = math.cos, math.sin, math.tan, math.pi 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 == 9 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 == 39 then return self:trilinear(a * (b ^ 2 + c ^ 2), b * (c ^ 2 + a ^ 2), c * (a ^ 2 + b ^ 2)) --brocard midpoint elseif n == 55 then return self:trilinear(a * (b + c - a), b * (c + a - b), c * (a + b - c)) elseif n == 56 then return self:trilinear(a / (b + c - a), b / (c + a - b), c / (a + b - c)) 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 elseif n == 175 then local ra, rb, rc = radius_excircle_(self.pa, self.pb, self.pc) return self:barycentric(a - ra, b - rb, c - rc) elseif n == 176 then ra, rb, rc = radius_excircle_(self.pa, self.pb, self.pc) return self:barycentric(a + ra, b + rb, c + rc) elseif n == 181 then return self:trilinear((a * (b + c) ^ 2) / (b + c - a), (b * (c + a) ^ 2) / (c + a - b), (c * (a + b) ^ 2) / (a + b - c))--apollonius point elseif n == 213 then return self:trilinear((b + c) * a ^ 2, (c + a) * b ^ 2, (a + b) * c ^ 2) elseif n == 264 then return self:barycentric(1 / sin(2 * A), 1 / sin(2 * B), 1 / sin(2 * C)) elseif n == 371 then return self:trilinear(cos(A - pi / 4), cos(B - pi / 4), cos(C - pi / 4)) elseif n == 598 then return self:trilinear( b * c / (a ^ 2 - 2 * b ^ 2 - 2 * c ^ 2), c * a / (b ^ 2 - 2 * c ^ 2 - 2 * a ^ 2), a * b / (c ^ 2 - 2 * a ^ 2 - 2 * b ^ 2) ) -- to get lemoine triangle else error("tkz-elements: triangle:kimberling: X(" .. tostring(n) .. ") is not available", 2) 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:excenter(pt) if pt == self.pa then return self:trilinear(-1, 1, 1) elseif pt == self.pb then return self:trilinear(1, -1, 1) elseif pt == self.pc then return self:trilinear(1, 1, -1) else error("tkz-elements: triangle:excenter: the argument must be a vertex of the triangle", 2) end end function triangle:projections(p) local p1 = projection_(self.pb, self.pc, p) local p2 = projection_(self.pa, self.pc, p) local p3 = projection_(self.pa, self.pb, p) return p1, p2, p3 end function triangle:reflections(p) local p1 = symmetry_axial_(self.pa, self.pb, p) local p2 = symmetry_axial_(self.pb, self.pc, p) local p3 = symmetry_axial_(self.pc, self.pa, p) return p2, p3, p1 end function triangle:parallelogram() local x = self.pc + self.pa - self.pb return x 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: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:euler_points() local H = self.orthocenter return midpoint_(H, self.pa), midpoint_(H, self.pb), midpoint_(H, self.pc) end function triangle:orthic_points() return orthic_tr_(self.pa, self.pb, self.pc) end function triangle:medial_points() return medial_tr_(self.pa, self.pb, self.pc) end function triangle:tangential_points() return tangential_tr_(self.pa, self.pb, 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 local 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:first_lemoine_points() local lc = self:lemoine_point() local p1 = intersection_ll_(lc, ll_from_(lc, self.pa, self.pb), self.pa, self.pc) local p2 = intersection_ll_(lc, ll_from_(lc, self.pb, self.pc), self.pb, self.pa) local p3 = intersection_ll_(lc, ll_from_(lc, self.pc, self.pa), self.pc, self.pb) local p5 = intersection_ll_(lc, ll_from_(lc, self.pa, self.pb), self.pb, self.pc) local p6 = intersection_ll_(lc, ll_from_(lc, self.pb, self.pc), self.pa, self.pc) local p4 = intersection_ll_(lc, ll_from_(lc, self.pc, self.pa), self.pa, self.pb) return p1, p2, p3, p4, p5, p6 end function triangle:second_lemoine_points() local lc = self:lemoine_point() local ha, hb, hc = orthic_tr_(self.pa, self.pb, self.pc) local p1 = intersection_ll_(lc, ll_from_(lc, ha, hb), self.pa, self.pc) local p2 = intersection_ll_(lc, ll_from_(lc, hb, hc), self.pb, self.pa) local p3 = intersection_ll_(lc, ll_from_(lc, hc, ha), self.pc, self.pb) local p5 = intersection_ll_(lc, ll_from_(lc, ha, hb), self.pb, self.pc) local p6 = intersection_ll_(lc, ll_from_(lc, hb, hc), self.pa, self.pc) local p4 = intersection_ll_(lc, ll_from_(lc, hc, ha), self.pa, self.pb) return p1, p2, p3, p4, p5, p6 end function triangle:soddy_points() return soddy_center_(self.pa, self.pb, self.pc) end -- soddy centers function triangle:soddy_center(outer) outer = (outer == "outer") local index = outer and 175 or 176 return self:kimberling(index) 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:conway_center() return self.incenter 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 function triangle:kenmotu_point() return self:kimberling(371) end function triangle:kenmotu_points() local ck = self:kenmotu_circle() local p1, p2 = intersection_lc_(self.pa, self.pb, ck.center, ck.through) local p3, p4 = intersection_lc_(self.pb, self.pc, ck.center, ck.through) local p5, p6 = intersection_lc_(self.pc, self.pa, ck.center, ck.through) return p1, p2, p3, p4, p5, p6 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:adams_points() local i = self:kimberling(1) local g = self:kimberling(7) local l = projection_(self.pa, self.pc, i) local n = projection_(self.pa, self.pb, i) local m = projection_(self.pb, self.pc, i) local x = ll_from_(g, m, n) local y = ll_from_(g, n, l) local z = ll_from_(g, m, l) local t1 = intersection_ll_(x, g, self.pa, self.pb) local t4 = intersection_ll_(x, g, self.pb, self.pc) local t2 = intersection_ll_(y, g, self.pa, self.pb) local t5 = intersection_ll_(y, g, self.pa, self.pc) local t3 = intersection_ll_(z, g, self.pb, self.pc) local t6 = intersection_ll_(z, g, self.pa, self.pc) return t1, t2, t3, t4, t5, t6 end function triangle:lamoen_points() local ma, mb, mc = medial_tr_(self.pa, self.pb, self.pc) local g = centroid_(self.pa, self.pb, self.pc) local p1 = circum_center_(self.pa, mc, g) local p2 = circum_center_(self.pb, ma, g) local p3 = circum_center_(self.pb, mc, g) local p4 = circum_center_(self.pc, mb, g) local p5 = circum_center_(self.pc, ma, g) local p6 = circum_center_(self.pa, mb, g) return p1, p2, p3, p4, p5, p6 end function triangle:yiu_centers() local ap = symmetry_axial_(self.pb, self.pc, self.pa) local bp = symmetry_axial_(self.pa, self.pc, self.pb) local cp = symmetry_axial_(self.pa, self.pb, self.pc) local oa = circum_center_(self.pa, bp, cp) local ob = circum_center_(self.pb, ap, cp) local oc = circum_center_(self.pc, bp, ap) return oa, ob, oc end function triangle:first_brocard_point() return self:trilinear(self.c / self.b, self.a / self.c, self.b / self.a) end function triangle:second_brocard_point() return self:trilinear(self.b / self.c, self.c / self.a, self.a / self.b) end function triangle:brocard_midpoint() return self:kimberling(39) end function triangle:macbeath_point() return self:kimberling(264) end function triangle:poncelet_point(pt) local p, err = poncelet_point_(self.pa, self.pb, self.pc, pt) if not p then error("tkz-elements: triangle:poncelet_point: " .. err, 2) end return p end function triangle:orthopole(l, EPS) EPS = EPS or tkz.epsilon -- garde-fous if tkz.is_linear(self.pa, self.pb, self.pc) then error("tkz-elements: triangle:orthopole: degenerate triangle", 2) end local p, q = l:get() if point.abs(p - q) <= EPS then error("tkz-elements: triangle:orthopole: degenerate line", 2) end -- projections de A,B sur l (on n'a pas besoin de C ici) local ap = projection_(p, q, self.pa) local bp = projection_(p, q, self.pb) -- feet on the sides CA and BC local a1, a2 = self.bc:get() -- line (BC) local c1, c2 = self.ca:get() -- line (CA) if point.abs(a2 - a1) <= EPS or point.abs(c2 - c1) <= EPS then error("tkz-elements: triangle:orthopole: degenerate side of the triangle", 2) end local app = projection_(a1, a2, ap) -- foot of ap on (BC) local bpp = projection_(c1, c2, bp) -- foot of bp on (CA) -- orthopole = intersection of the lines (ap, app) and (bp, bpp) return intersection(line:new(ap, app), line:new(bp, bpp)) end function triangle:isodynamic_points() -- Special case: equilateral triangle if self:is_equilateral() then -- The centroid G = (A + B + C)/3 is used local A, B, C = self.pa, self.pb, self.pc local G = (A + B + C) / 3 -- The same point is returned twice, to keep the same signature return G, G end -- General case local a, b, c = self.a, self.b, self.c local ab, bc = self.ab, self.bc, self.ca -- self.ca is not used here -- Apollonius circles for the ratios b/a on AB and c/b on BC local Cab = ab:apollonius(b / a) local Cbc = bc:apollonius(c / b) -- Their intersection gives the two isodynamic points return intersection(Cab, Cbc) end triangle.neuberg_points = triangle.isodynamic_points -- Points of intersection of the Apollonius circle (b/a, side AB) -- with the line (AB), the point on the segment [AB] first. function triangle:apollonius_points(side, EPS) EPS = EPS or tkz.epsilon local a, b, c = self.a, self.b, self.c -- a = BC, b = CA local A, B, C = self.pa, self.pb, self.pc local ab, bc, ca = self.ab, self.bc, self.ca local x, y, X, Y, L, k if side == "ab" then L = self.ab k = b / a x, y = a, b X, Y = A, B -- AC / BC elseif side == "bc" then L = self.bc k = c / b x, y = b, c X, Y = B, C -- AB / CA elseif side == "ca" then L = self.ca k = a / c x, y = c, a X, Y = C, A -- BC / AB else error("tkz-elements: triangle:apollonius_points: invalid side " .. tostring(side) .. " (use \"ab\", \"bc\" or \"ca\")", 2) end if math.abs(x - y) <= EPS then local M = (X + Y) / 2 return M, M end return L:harmonic(k) end function triangle:the_apollonius_center() local lbrocard = self:brocard_axis() local n = self.eulercenter local s = self.spiekercenter local lns = line(n,s) return intersection(lbrocard, lns) end function triangle:apollonius_point() return kimberling(181) end function triangle:isotomic(p) local pta, ptb, ptc = cevian_(self.pa, self.pb, self.pc, p) local pta_s = 2*self.ma - pta local ptb_s = 2*self.mb - ptb return intersection_ll_(self.pa,pta_s,self.pb,ptb_s) end function triangle:anticomplement(p) return self.centroid:homothety(-2,p) end function triangle:complement(p) return self.centroid:homothety(-1/2,p) end ------------------- -- Result -> line ------------------- function triangle:symmedian_line(arg) local pts = { self.pa, self.pb, self.pc } local i = resolve_triangle_index_(self, arg) if i < 0 or i > 2 then return nil end local A = pts[i + 1] local B = pts[(i + 1) % 3 + 1] local C = pts[(i + 2) % 3 + 1] local L = self:lemoine_point() local P = intersection_ll_(A, L, B, C) return line:new(A, P) end function triangle:altitude(arg) local pts = { self.pa, self.pb, self.pc } local i = resolve_triangle_index_(self, arg) if i < 0 or i > 2 then return nil end local A = pts[i + 1] local B = pts[(i + 1) % 3 + 1] local C = pts[(i + 2) % 3 + 1] return line:new(A, projection_(B, C, A)) end function triangle:bisector(arg) local i = resolve_triangle_index_(self, arg) if i < 0 or i > 2 then return nil end local pts = { self.pa, self.pb, self.pc } local I = self.incenter local A = pts[i + 1] local B = pts[(i + 1) % 3 + 1] local C = pts[(i + 2) % 3 + 1] local P = intersection_ll_(A, I, B, C) return line:new(A, P) end function triangle:bisector_ext(arg) local i = resolve_triangle_index_(self, arg) if i < 0 or i > 2 then return nil end local pts = { self.pa, self.pb, self.pc } local A = pts[i + 1] local B = pts[(i + 1) % 3 + 1] local C = pts[(i + 2) % 3 + 1] return line:new(A, bisector_ext_(A, B, C)) end function triangle:mediator(arg) local i = resolve_triangle_index_(self, arg) if i < 0 or i > 2 then return nil end local pts = { self.pa, self.pb, self.pc } local B = pts[(i + 1) % 3 + 1] local C = pts[(i + 2) % 3 + 1] return line:new(B, C):mediator() end function triangle:trisector(arg) local pts = { self.pa, self.pb, self.pc } local i = resolve_triangle_index_(self, arg) if i < 0 or i > 2 then return nil end local A = pts[i + 1] local B = pts[(i + 1) % 3 + 1] local C = pts[(i + 2) % 3 + 1] local an = get_angle_(A, B, C) local pti = report_(A, B, 1) local ptj = rotation_(A, an / 3, pti) local ptk = rotation_(A, an / 3, ptj) return line(A, ptj), line(A, ptk) end function triangle:antiparallel(pt, arg) local a, b, c = self.pa, self.pb, self.pc local I = self.incenter local i = resolve_triangle_index_(self, arg) local S = { a, b, c } local A = S[i + 1] -- starting vertex local B = S[(i + 1) % 3 + 1] -- premier sommet adjacent local C = S[(i + 2) % 3 + 1] -- second sommet adjacent -- u, v are the reflections of B and C about the bisector at I local u = symmetry_axial_(A, I, B) local v = symmetry_axial_(A, I, C) local w = ll_from_(pt, u, v) return line:new(intersection_ll_(pt, w, B, A), intersection_ll_(pt, w, C, A)) end function triangle:orthic_axis() local x, y, z = orthic_axis_(self.pa, self.pb, self.pc) return line:new(x, z) end -- N,H,G,O -- check_equilateral_ (a,b,c) function triangle:euler_line() if check_equilateral_(self.pa, self.pb, self.pc) then error("tkz-elements: triangle:euler_line: no Euler line for an equilateral triangle", 2) 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 -- 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:lemoine_axis() -- to be checked local C = self:circum_circle() local pl = self:lemoine_point() return C:polar(pl) 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:soddy_line() return line:new(self:soddy_center(), self:soddy_center("outer")) end ----------------------- --- Result -> circles -- ----------------------- function triangle:apollonius_circle(side, EPS) EPS = EPS or tkz.epsilon -- lengths local a, b, c = self.a, self.b, self.c local L, k if side == "ab" then L = self.ab k = b / a -- AC / BC elseif side == "bc" then L = self.bc k = c / b -- AB / CA elseif side == "ca" then L = self.ca k = a / c -- BC / AB else error("tkz-elements: triangle:apollonius_circle: invalid side " .. tostring(side) .. " (use \"ab\", \"bc\" or \"ca\")", 2) end -- case k = 1 (approximately): handled on purpose if math.abs(k - 1) <= EPS then return L:bisector() else return L:apollonius(k) end end function triangle:three_apollonius_circles() local a, b, c = self.a, self.b, self.c local ab, bc, ca = self.ab, self.bc, self.ca local Cab = ab:apollonius(b / a) local Cbc = bc:apollonius(c / b) local Cca = ca:apollonius(a / c) return Cab, Cbc, Cca end function triangle:three_tangent_circles() local i = self.incenter local a, b, c = self:projections(i) return circle:new(self.pa, c), circle:new(self.pb, a), circle:new(self.pc, b) end function triangle:euler_circle() return circle:new(euler_center_(self.pa, self.pb, self.pc), midpoint_(self.pb, self.pc)) end --aliase triangle.feuerbach = triangle.euler_circle function triangle:circum_circle() return circle:new(circum_center_(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 -- see excenter ?? function triangle:ex_circle(arg) local pts = { self.pa, self.pb, self.pc } local i = resolve_triangle_index_(self, arg) if i < 0 or i > 2 then return nil end local A = pts[i + 1] local B = pts[(i + 1) % 3 + 1] local C = pts[(i + 2) % 3 + 1] local O = ex_center_(A, B, C) -- excenter opposite A local T = projection_(B, C, O) -- point of tangency on (BC) return circle:new(O, T) end function triangle:radical_circle() local Ca, Cb, Cc Ca = self:ex_circle(self.pa) Cb = self:ex_circle(self.pb) Cc = self:ex_circle(self.pc) return Ca:radical_circle(Cb, Cc) 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:cevian_circle(p) local pta, ptb, ptc = cevian_(self.pa, self.pb, self.pc, p) return circle:new(circum_center_(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_center_(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: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: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_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_center_(d, e, f), d) end function triangle:adams_circle() local i = self:kimberling(1) local g = self:kimberling(7) local m = projection_(self.pb, self.pc, i) local n = projection_(self.pa, self.pb, i) local x = ll_from_(g, m, n) local t = intersection_ll_(x, g, self.pa, self.pb) return circle:new(i, t) end function triangle:lamoen_circle() local ma, mb, mc = medial_tr_(self.pa, self.pb, self.pc) local g = centroid_(self.pa, self.pb, self.pc) local p1 = circum_center_(self.pa, mb, g) local p2 = circum_center_(self.pb, ma, g) local p3 = circum_center_(self.pc, ma, g) local c = circum_center_(p1, p2, p3) return circle:new(c, p1) end -- soddy circles function triangle:soddy_circle(outer) outer = (outer == "outer") local index = outer and 175 or 176 local c = self:kimberling(index) local ra = (self.b + self.c - self.a) / 2 local ta = report_(self.pa, self.pb, ra, self.pa) local x, y = intersection_lc_(self.pa, c, self.pa, ta) local t if outer then t = (length_(x, c) > length_(y, c)) and x or y else t = (length_(x, c) < length_(y, c)) and x or y end return circle:new(c, t) end function triangle:yiu_circles() local x, y, z = self:yiu_centers() return circle:new(x, self.pa), circle:new(y, self.pb), circle:new(z, self.pc) end function triangle:kenmotu_circle() local a, b, c = self.a, self.b, self.c local area = self.area local rho = (math.sqrt(2) * a * b * c) / (4 * area + (a ^ 2 + b ^ 2 + c ^ 2)) local center = self:kimberling(371) return circle:new(through(center, rho)) end -- Circle tangent to the sides originating from p and to the circle passing through the other two vertices. -- argument the vertex of the chosen triangle, the center of the circle passing through the other two vertices. function triangle:c_c(p, C) local a, b, c local i = resolve_triangle_index_(self, p) if i == 0 then a, b, c = self.pa, self.pb, self.pc elseif i == 1 then a, b, c = self.pb, self.pc, self.pa elseif i == 2 then a, b, c = self.pc, self.pa, self.pb end local Lm = self:mediator(p) local n = intersection(Lm, C, {near = p}) local i = self.incenter local q = line:new(c, i):reflection(n) local L = line:new(c, q):parallel_from(i) local Lac = line:new(a, c) local e = intersection(L, Lac) local Lia = line:new(i, a) local Le = Lac:orthogonal_from(e) return circle(intersection(Lia, Le), e) end triangle.thebault = triangle.c_c function triangle:thebault_circles(p) local pa, pb, pc = self.pa, self.pb, self.pc local a, b, c -- reordered vertices local side -- side containing p (line object) -------------------------------------------------- -- 1. Side containing p, and (a,b,c) -- a = vertex opposite the side containing p -------------------------------------------------- if self.bc:on_segment(p) then -- p on BC -> opposite vertex A a, b, c = pa, pb, pc side = self.bc elseif self.ca:on_segment(p) then -- p on CA -> opposite vertex B a, b, c = pb, pc, pa side = self.ca elseif self.ab:on_segment(p) then -- p on AB -> opposite vertex C a, b, c = pc, pa, pb side = self.ab else error("tkz-elements: triangle:thebault_circles: the point is not on a side of the triangle", 2) end -------------------------------------------------- -- 2. Construction of the centers -------------------------------------------------- local i = self.incenter -- incenter of the triangle ABC local ic = in_center_(p, a, c) -- incenter of the triangle (p,a,c) local ib = in_center_(p, a, b) -- incenter of the triangle (p,a,b) -- projections of p on (ic i) and (ib i) local pc_ = projection_(p, ic, i) local pb_ = projection_(p, ib, i) -- intersections with (b c) local tc = intersection_ll_(i, pc_, b, c) local tb = intersection_ll_(i, pb_, b, c) -- perpendiculars to (b c) through tc and tb local xc = ortho_from_(tc, b, c) local xb = ortho_from_(tb, b, c) -- centers of the two circles local center_c = intersection_ll_(p, ic, tc, xc) local center_b = intersection_ll_(p, ib, tb, xb) if not center_b or not center_c then error("tkz-elements: triangle:thebault_circles: the centers of the circles cannot be constructed", 2) end -------------------------------------------------- -- 3. Points of tangency on the side, and circles -------------------------------------------------- -- side:projection(pt1, pt2) returns the projections local tb_tan, tc_tan = side:projection(center_b, center_c) local Cb = circle:new(center_b, tb_tan) local Cc = circle:new(center_c, tc_tan) return Cb, Cc end function triangle:mixtilinear_incircle(arg) local i = resolve_triangle_index_(self, arg) local incenter = self.incenter local pts = { self.pa, self.pb, self.pc } local segs = { self.ab, self.bc, self.ca } local A = pts[i + 1] local B = pts[(i + 1) % 3 + 1] local C = pts[(i + 2) % 3 + 1] local AB = segs[i + 1] local BC = segs[(i + 1) % 3 + 1] local CA = segs[(i + 2) % 3 + 1] local l = line:new(A, incenter) local lp = l:orthogonal_from(incenter) local P1 = intersection(lp, CA) local P2 = intersection(lp, AB) local l1 = CA:orthogonal_from(P1) local l2 = AB:orthogonal_from(P2) local W = intersection(l1, l2) return circle(W, P1) end function triangle:feuerbach_apollonius(EPS) EPS = EPS or tkz.epsilon -- 1. Feuerbach points local Ea, Eb, Ec = self:feuerbach():get() -- -- 2. Excenter Ja local C_JaEa = self:ex_circle() -- 4. Spieker center local S = self.spiekercenter -- 5. Orthogonal circle local Cortho = C_JaEa:orthogonal_from(S) -- 6. Euler circle local Ceuler = self:euler_circle() -- 7. Inversion of the circle local InvEuler = Cortho:inversion(Ceuler) local O = InvEuler.center -- 8. Inverted points local xa, xb, xc = Cortho:inversion(Ea, Eb, Ec) return circle:new(O, xa) end function triangle:feuerbach_apollonius_k181(EPS) EPS = EPS or tkz.epsilon local Ea, Eb, Ec = self:feuerbach():get() --Feuerbach points local A = self:kimberling(181)-- Apollonius point local S = self.spiekercenter local xa = intersection_ll_(self.pa,A,S,Ea) local xb = intersection_ll_(self.pb,A,S,Eb) local xc = intersection_ll_(self.pc,A,S,Ec) return circle:new(circum_circle_(xa, xb, xc)) end function triangle:the_apollonius_circle() local ce = self:the_apollonius_center() local apo = self:kimberling(181) local ll = line(apo, self.pa) local cxa = self:ex_circle() local u,v = intersection(ll, cxa,{near = self.pa}) return circle:new(ce, v) 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 triangle.contact = triangle.intouch triangle.gergonne = triangle.intouch function triangle:extouch() return triangle:new(extouch_tr_(self.pa, self.pb, self.pc)) end triangle.nagel = triangle.extouch 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 triangle.similar = 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 triangle.symmedial = triangle.symmedian function triangle:lemoine() local X = self:kimberling(598) return triangle:new(cevian_(self.pa, self.pb, self.pc, X)) end function triangle:macbeath() local X = self:kimberling(264) return triangle:new(cevian_(self.pa, self.pb, self.pc, X)) end function triangle:euler() return triangle:new(euler_points_(self.pa, self.pb, self.pc)) end function triangle:pedal(pt) return triangle:new(self:projections(pt)) end function triangle:yiu() return triangle:new(self:yiu_centers()) end function triangle:reflection() return triangle:new( symmetry_axial_(self.pb, self.pc, self.pa), symmetry_axial_(self.pa, self.pc, self.pb), symmetry_axial_(self.pa, self.pb, self.pc) ) end function triangle:projection() return triangle:new( projection_(self.pb, self.pc, self.pa), projection_(self.pa, self.pc, self.pb), projection_(self.pa, self.pb, self.pc) ) end function triangle:circumcevian(pt) local circum = self:circum_circle() local x = intersection_lc_(self.pa, pt, circum.center, circum.through) local y = intersection_lc_(self.pb, pt, circum.center, circum.through) local z = intersection_lc_(self.pc, pt, circum.center, circum.through) return triangle:new(x, y, z) end function triangle:morley() local la1, la2 = self:trisector(self.pa) local lb1, lb2 = self:trisector(self.pb) local lc1, lc2 = self:trisector(self.pc) local D = intersection(la1, lb2) local E = intersection(lb1, lc2) local F = intersection(lc1, la2) return triangle:new(D, E, F) end function triangle:napoleon(swap) swap = (swap == "swap") if swap then local a = self.ab:equilateral().centroid local b = self.bc:equilateral().centroid local c = self.ca:equilateral().centroid return triangle:new(a, b, c) else local a = self.ab:equilateral("swap").centroid local b = self.bc:equilateral("swap").centroid local c = self.ca:equilateral("swap").centroid return triangle:new(a, b, c) end end function triangle:soddy(outer) local ci = self:soddy_circle(outer) local a, b, c = self.pa, self.pb, self.pc outer = (outer == "outer") local ce = ci.center local ti = ci.through local sa = intersection_lc_(ce, a, ce, ti) local sb = intersection_lc_(ce, b, ce, ti) local sc = intersection_lc_(ce, c, ce, ti) return triangle:new(sa, sb, sc) end ------------------- -- Result -> square ------------------- -- Method for inscribing a square in a triangle with vertex rotation function triangle:square_inscribed(permutation) -- Set a default value for rotation if not supplied permutation = permutation or 0 permutation = permutation + 1 local vertices = { self.pa, self.pb, self.pc } local i = (permutation - 1) % 3 + 1 local j = i % 3 + 1 local k = j % 3 + 1 local a = vertices[i] local b = vertices[j] local c = vertices[k] return square:new(square_inscribed_(a, b, c)) 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 = 0.5 * length_(x, y) return conic:new(EL_bifocal(self.orthocenter, self.circumcenter, a)) end end -- kiepert hyperbola 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 = tkz.bisector(center, Lsa.pa, Lsb.pa) -- new frame system local minor = axis:ortho_from(center) local sys = occs:new(minor, center) local x, y = sys:coordinates(self.pa) 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 error("tkz-elements: triangle:kiepert_parabola: undefined for an isosceles triangle (no X(110))", 2) else local L = self:euler_line() local F = self:kimberling(110) return conic:new(F, L, 1) end end --lemoine inellipse -- bifocal needs the two foci and a point on the ellipse, or a function triangle:lemoine_inellipse() local G = self.centroid local L = self:lemoine_point() local Xa = self:lemoine().pa return conic:new(EL_bifocal(G, L, Xa)) end --brocard inellipse function triangle:brocard_inellipse() local B1 = self:first_brocard_point() local B2 = self:second_brocard_point() local Ka = self:symmedian().pa return conic:new(EL_bifocal(B2, B1, Ka)) end -- orthic inellipse function triangle:macbeath_inellipse() if self:check_acutangle() then local O = self.circumcenter local H = self.orthocenter local Xa = self:macbeath().pa return conic:new(EL_bifocal(O, H, Xa)) else error("tkz-elements: triangle:macbeath_inellipse: the triangle must not have an obtuse angle", 2) end end function triangle:mandart_inellipse() local T = self local M = T:mittenpunkt_point() local Xa, Yb, Zc = T:extouch():get() local X, Y = M:symmetry(Xa, Yb) local coefficients = search_ellipse_(X, Y, Xa, Yb, Zc) local center, ra, rb, angle = ellipse_axes_angle(coefficients) return conic:new(EL_radii(center, ra, rb, angle)) end function triangle:orthic_inellipse() if self:check_acutangle() then local T = self local K = T:symmedian_point() local Ha, Hb, Hc = T:orthic():get() local Sa, Sb = K:symmetry(Ha, Hb) local coefficients = search_ellipse_(Ha, Hb, Hc, Sa, Sb) local center, ra, rb, angle = ellipse_axes_angle(coefficients) return conic:new(EL_radii(center, ra, rb, angle)) else error("tkz-elements: triangle:orthic_inellipse: the triangle must not have an obtuse angle", 2) end end function triangle:path(p1, p2) local list = {} -- List of the sides and vertices local sides = { { seg = self.ab, A = self.pa, B = self.pb }, { seg = self.bc, A = self.pb, B = self.pc }, { seg = self.ca, A = self.pc, B = self.pa }, } -- Indices of the segments containing p1 and p2 local function locate(p) for i, s in ipairs(sides) do if s.seg:on_segment(p) then return i end end error("tkz-elements: triangle:path: the point is not on the boundary of the triangle", 3) end local i1 = locate(p1) local i2 = locate(p2) -- Simple case: same segment if i1 == i2 then table.insert(list, "(" .. checknumber_(p1.re) .. "," .. checknumber_(p1.im) .. ")") table.insert(list, "(" .. checknumber_(p2.re) .. "," .. checknumber_(p2.im) .. ")") return path:new(list) end -- Sinon, commencer par p1 table.insert(list, "(" .. checknumber_(p1.re) .. "," .. checknumber_(p1.im) .. ")") -- Intermediate vertices, counterclockwise local i = i1 repeat local s = sides[i] table.insert(list, "(" .. checknumber_(s.B.re) .. "," .. checknumber_(s.B.im) .. ")") i = i % 3 + 1 until i == i2 -- Terminer par p2 table.insert(list, "(" .. checknumber_(p2.re) .. "," .. checknumber_(p2.im) .. ")") return path:new(list) end --- CONTACT PROBLEM APOLLONIUS -- Circle tangent to the three lines of the triangle: -- returns (in this order): incircle, excircle at pa, excircle at pb, excircle at pc. -- Optional: which in { nil/"all", "in", "pa", "pb", "pc", "ex_a", "ex_b", "ex_c" } function triangle:LLL(which) which = which or "all" -- --- selector local C_in = self:in_circle() local C_ea = self:ex_circle(0) -- pa local C_eb = self:ex_circle(1) -- pb local C_ec = self:ex_circle(2) -- pc if which == "all" or which == nil then return C_in, C_ea, C_eb, C_ec end -- alias pratiques if which == "in" then return C_in end if which == "pa" or which == "ex_a" then return C_ea end if which == "pb" or which == "ex_b" then return C_eb end if which == "pc" or which == "ex_c" then return C_ec end error("tkz-elements: triangle:LLL: unknown selector " .. tostring(which) .. " (use \"all\", \"in\", \"pa\", \"pb\" or \"pc\")", 2) end triangle.c_lll = triangle.LLL -- code for an inellipse in a triangle local function side_of_point_(pa, pb, pc, pt, eps) eps = eps or tkz.epsilon if line_on_(pa, pb, pt, eps) then return "ab" end if line_on_(pb, pc, pt, eps) then return "bc" end if line_on_(pc, pa, pt, eps) then return "ca" end return nil end function triangle:cevian_point(p, q, eps) eps = eps or tkz.epsilon local sp = side_of_point_(self.pa, self.pb, self.pc, p, eps) local sq = side_of_point_(self.pa, self.pb, self.pc, q, eps) if not sp or not sq or sp == sq then error("tkz-elements: triangle:cevian_point: the points must lie on two distinct sides of the triangle", 2) end local br if (sp == "ab" and sq == "ca") or (sp == "ca" and sq == "ab") then br = intersection(line(self.pc, p), line(self.pb, q)) elseif (sp == "ab" and sq == "bc") or (sp == "bc" and sq == "ab") then br = intersection(line(self.pc, p), line(self.pa, q)) else br = intersection(line(self.pb, p), line(self.pa, q)) end if br == nil then error("tkz-elements: triangle:cevian_point: the cevian point cannot be constructed", 2) end local x, y, z = cevian_(self.pa, self.pb, self.pc, br) return br, x, y, z end local function ellipse_from_brianchon_(T, br, eps) eps = eps or tkz.epsilon if br == nil then error("tkz-elements: triangle:ellipse_inscribed: the Brianchon point is required", 2) end local a, b, c = cevian_(T.pa, T.pb, T.pc, br) local k = T:isotomic(br) local o = T:complement(k) local u, v = o:symmetry(a, b) return conic:new(EL_five_points(a, b, c, u, v)) end function triangle:ellipse_inscribed(spec, eps) eps = eps or tkz.epsilon if type(spec) ~= "table" then error("tkz-elements: triangle:ellipse_inscribed: a specification table is expected", 2) end if spec.brianchon ~= nil then return ellipse_from_brianchon_(self, spec.brianchon, eps) end if spec.center ~= nil then local o = spec.center local k = self:anticomplement(o) local br = self:isotomic(k) return ellipse_from_brianchon_(self, br, eps) end if spec.focus ~= nil then local f1 = spec.focus local f2 = self:isogonal(f1) local o = midpoint_(f1, f2) local k = self:anticomplement(o) local br = self:isotomic(k) return ellipse_from_brianchon_(self, br, eps) end error("tkz-elements: triangle:ellipse_inscribed: the table must contain brianchon, center or focus", 2) end return triangle