G.106 distances to a circle from an exterior point — Code¶
From a point outside a circle, of segments falling on the concave arc the greatest passes through the center and nearer ones are greater, while on the convex arc the least lies between the point and the diameter and nearer ones are less, with at most two equal segments.
"""Programmatic construction for Book III, Proposition 8 (III.8).
From an external point D, straight lines DA (through the centre M, meeting the
convex circumference at G), DE (through K), DF (through L), DC (through H) are
drawn to the circle ABC, together with DB (such that angle DMB = angle KMD)
and DN on the other side of DG.
"""
from __future__ import annotations
import sympy as sp
from geometor.model import Model
def construct_g1() -> Model:
"""Construct the figure 0024.g1 for Book III, Proposition 8."""
model = Model("III.8")
# Circle ABC with centre M at origin and radius 1
M = model.set_point(0, 0, classes=["given"], ID="M")
P_unit = model.set_point(1, 0)
model.construct_circle(M, P_unit)
# External point D along rational unit vector (7/25, 24/25) from M
ux, uy = sp.Rational(7, 25), sp.Rational(24, 25)
d_dist = sp.Rational(32, 15)
D = model.set_point(d_dist * ux, d_dist * uy, classes=["given"], ID="D")
G = model.set_point(ux, uy, ID="G")
A = model.set_point(-ux, -uy, ID="A")
model.construct_line(D, A)
# Points on concave circumference: E, F, C
# Points on convex circumference: K, L, H (collinear with D-E, D-F, D-C)
def secant_points(vx: sp.Expr, vy: sp.Expr):
"""Intersect ray from D in direction (vx, vy) with unit circle at M."""
a = vx**2 + vy**2
b = 2 * (D.x * vx + D.y * vy)
c = D.x**2 + D.y**2 - 1
disc = sp.sqrt(b**2 - 4 * a * c)
t_near = (-b - disc) / (2 * a)
t_far = (-b + disc) / (2 * a)
return (
D.x + t_near * vx,
D.y + t_near * vy,
D.x + t_far * vx,
D.y + t_far * vy,
)
# Secant D-K-E
kx, ky, ex, ey = secant_points(sp.Rational(-2, 5), sp.Integer(-1))
K = model.set_point(kx, ky, ID="K")
E = model.set_point(ex, ey, ID="E")
model.construct_line(D, E)
# Secant D-L-F
lx, ly, fx, fy = secant_points(sp.Rational(-14, 25), sp.Integer(-1))
L = model.set_point(lx, ly, ID="L")
F = model.set_point(fx, fy, ID="F")
model.construct_line(D, F)
# Secant D-H-C
hx, hy, cx, cy = secant_points(sp.Rational(-21, 25), sp.Integer(-1))
H = model.set_point(hx, hy, ID="H")
C = model.set_point(cx, cy, ID="C")
model.construct_line(D, C)
# Point B on convex circumference such that angle DMB = angle KMD
# Reflection of K across line MG (unit vector ux, uy)
dot_K = K.x * ux + K.y * uy
bx = 2 * dot_K * ux - K.x
by = 2 * dot_K * uy - K.y
B = model.set_point(bx, by, ID="B")
# Point N further along the convex circumference on the side of B
N = model.set_point(sp.Rational(3, 5), sp.Rational(4, 5), label="N")
# Collinear segments along D-G-M-A
model.set_segment(D, G)
model.set_segment(G, M)
model.set_segment(M, A)
model.set_segment(D, M)
model.set_segment(G, A)
model.set_segment(D, A)
model.set_section([D, G, M])
model.set_section([G, M, A])
model.set_section([D, M, A])
# Collinear segments along D-K-E, D-L-F, D-H-C
for P_near, P_far in [(K, E), (L, F), (H, C)]:
model.set_segment(D, P_near)
model.set_segment(P_near, P_far)
model.set_segment(D, P_far)
model.set_section([D, P_near, P_far])
# Segments DB and DN
model.set_segment(D, B)
model.set_segment(D, N)
# Radii from centre M
for P_circ in [E, F, C, K, L, H, B, N]:
model.set_segment(M, P_circ)
# Triangles compared in the proof
for P_circ in [E, F, C, K, L, H, B, N]:
model.set_polygon([D, M, P_circ])
return model
if __name__ == "__main__":
construct_g1()