Spaces:
Runtime error
Runtime error
Upload 2 files
Browse files
ag4masses/alphageometry/graph.py
CHANGED
|
@@ -2773,7 +2773,7 @@ class Graph:
|
|
| 2773 |
|
| 2774 |
# not necessary for proofing, but for visualization.
|
| 2775 |
c_args = list(map(lambda x: self.get(x, lambda: int(x)), c.args))
|
| 2776 |
-
self.additionally_draw(c.name, c_args)
|
| 2777 |
|
| 2778 |
for points, bs in cdef.basics:
|
| 2779 |
if points:
|
|
|
|
| 2773 |
|
| 2774 |
# not necessary for proofing, but for visualization.
|
| 2775 |
c_args = list(map(lambda x: self.get(x, lambda: int(x)), c.args))
|
| 2776 |
+
# self.additionally_draw(c.name, c_args)
|
| 2777 |
|
| 2778 |
for points, bs in cdef.basics:
|
| 2779 |
if points:
|
ag4masses/alphageometry/numericals.py
CHANGED
|
@@ -18,6 +18,7 @@ from __future__ import annotations
|
|
| 18 |
|
| 19 |
import math
|
| 20 |
from typing import Any, Optional, Union
|
|
|
|
| 21 |
|
| 22 |
import geometry as gm
|
| 23 |
import matplotlib
|
|
@@ -26,8 +27,10 @@ import matplotlib.colors as mcolors
|
|
| 26 |
import numpy as np
|
| 27 |
from numpy.random import uniform as unif # pylint: disable=g-importing-member
|
| 28 |
import graph as gh
|
|
|
|
|
|
|
| 29 |
|
| 30 |
-
matplotlib.use('
|
| 31 |
|
| 32 |
|
| 33 |
ATOM = 1e-12
|
|
@@ -440,72 +443,72 @@ class Circle:
|
|
| 440 |
|
| 441 |
|
| 442 |
class SemiCircle(Circle):
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
|
| 510 |
|
| 511 |
class HoleCircle(Circle):
|
|
@@ -789,7 +792,7 @@ def check_perp(points: list[Point]) -> bool:
|
|
| 789 |
|
| 790 |
def check_cyclic(points: list[Point]) -> bool:
|
| 791 |
points = list(set(points))
|
| 792 |
-
(a, b, c, *ps)
|
| 793 |
circle = Circle(p1=a, p2=b, p3=c)
|
| 794 |
for d in ps:
|
| 795 |
if not close_enough(d.distance(circle.center), circle.radius):
|
|
@@ -975,7 +978,7 @@ def draw_angle(
|
|
| 975 |
|
| 976 |
|
| 977 |
def naming_position(
|
| 978 |
-
ax: matplotlib.axes.Axes, p: Point, lines: list[Line], circles: list[Circle]
|
| 979 |
) -> tuple[float, float]:
|
| 980 |
"""Figure out a good naming position on the drawing."""
|
| 981 |
_ = ax
|
|
@@ -1034,7 +1037,7 @@ def draw_point(
|
|
| 1034 |
name = name[0] + '_' + name[1:]
|
| 1035 |
|
| 1036 |
ax.annotate(
|
| 1037 |
-
name, naming_position(ax, p, lines, circles), color=color, fontsize=15
|
| 1038 |
)
|
| 1039 |
|
| 1040 |
|
|
@@ -1106,6 +1109,7 @@ def draw_circle(
|
|
| 1106 |
ax: matplotlib.axes.Axes, circle: Circle, color: Any = 'cyan'
|
| 1107 |
) -> Circle:
|
| 1108 |
"""Draw a circle."""
|
|
|
|
| 1109 |
if circle.num is not None:
|
| 1110 |
circle = circle.num
|
| 1111 |
else:
|
|
@@ -1113,11 +1117,14 @@ def draw_circle(
|
|
| 1113 |
if len(points) <= 2:
|
| 1114 |
return
|
| 1115 |
points = [p.num for p in points]
|
|
|
|
| 1116 |
p1, p2, p3 = points[:3]
|
| 1117 |
circle = Circle(p1=p1, p2=p2, p3=p3)
|
| 1118 |
-
|
| 1119 |
-
|
| 1120 |
-
|
|
|
|
|
|
|
| 1121 |
|
| 1122 |
def check_points_semicircle(p1, p2, p3):
|
| 1123 |
"""
|
|
@@ -1175,7 +1182,7 @@ def check_points_semicircle(p1, p2, p3):
|
|
| 1175 |
}
|
| 1176 |
|
| 1177 |
def _draw_semicircle(
|
| 1178 |
-
ax: matplotlib.axes.Axes, P1: Point, P2: Point, P3: Point, color: Any = 'cyan', lw: float = 1.2
|
| 1179 |
) -> None:
|
| 1180 |
"""
|
| 1181 |
Draws a semicircle passing through three points or with one or two points on the diameter.
|
|
@@ -1186,45 +1193,71 @@ def _draw_semicircle(
|
|
| 1186 |
color (Any): Color of the semicircle.
|
| 1187 |
lw (float): Line width of the semicircle.
|
| 1188 |
"""
|
| 1189 |
-
|
| 1190 |
-
if
|
| 1191 |
-
|
| 1192 |
-
|
| 1193 |
-
|
| 1194 |
-
|
| 1195 |
-
|
| 1196 |
-
|
| 1197 |
-
|
| 1198 |
-
|
| 1199 |
-
|
| 1200 |
-
|
| 1201 |
-
|
| 1202 |
-
|
| 1203 |
-
|
| 1204 |
-
|
| 1205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1206 |
|
| 1207 |
-
|
| 1208 |
-
|
| 1209 |
-
|
| 1210 |
-
|
| 1211 |
-
|
| 1212 |
-
|
| 1213 |
-
|
| 1214 |
-
|
| 1215 |
-
|
| 1216 |
-
|
| 1217 |
-
|
| 1218 |
-
|
| 1219 |
-
|
| 1220 |
-
|
| 1221 |
-
|
| 1222 |
-
|
| 1223 |
-
|
| 1224 |
-
|
| 1225 |
-
|
| 1226 |
-
|
| 1227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1228 |
|
| 1229 |
def draw_semicircle(
|
| 1230 |
ax: matplotlib.axes.Axes, semicircle: SemiCircle, color: Any = 'cyan'
|
|
@@ -1296,7 +1329,6 @@ def highlight(
|
|
| 1296 |
_draw_line(ax, c, d, color=color2, lw=2.0)
|
| 1297 |
if name == 'eqangle':
|
| 1298 |
a, b, c, d, e, f, g, h = args
|
| 1299 |
-
|
| 1300 |
x = line_line_intersection(Line(a, b), Line(c, d))
|
| 1301 |
if b.distance(x) > a.distance(x):
|
| 1302 |
a, b = b, a
|
|
@@ -1343,12 +1375,37 @@ def highlight(
|
|
| 1343 |
_draw_line(ax, c, d, color=color2, lw=2.0, alpha=0.5)
|
| 1344 |
_draw_line(ax, m, n, color=color1, lw=2.0, alpha=0.5)
|
| 1345 |
_draw_line(ax, p, q, color=color2, lw=2.0, alpha=0.5)
|
| 1346 |
-
|
| 1347 |
-
|
| 1348 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1349 |
|
| 1350 |
HCOLORS = None
|
| 1351 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1352 |
|
| 1353 |
def _draw(
|
| 1354 |
ax: matplotlib.axes.Axes,
|
|
@@ -1362,6 +1419,7 @@ def _draw(
|
|
| 1362 |
):
|
| 1363 |
"""Draw everything."""
|
| 1364 |
colors = ['red', 'green', 'blue', 'orange', 'magenta', 'purple']
|
|
|
|
| 1365 |
pcolor = 'black'
|
| 1366 |
lcolor = 'black'
|
| 1367 |
ccolor = 'grey'
|
|
@@ -1374,15 +1432,45 @@ def _draw(
|
|
| 1374 |
colors = ['grey']
|
| 1375 |
|
| 1376 |
line_boundaries = []
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1377 |
for l in lines:
|
| 1378 |
p1, p2 = draw_line(ax, l, color=lcolor)
|
| 1379 |
line_boundaries.append((p1, p2))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1380 |
circles = [draw_circle(ax, c, color=ccolor) for c in circles]
|
| 1381 |
semicircles = [draw_semicircle(ax, c, color=ccolor) for c in semicircles]
|
| 1382 |
|
| 1383 |
for p in points:
|
| 1384 |
draw_point(ax, p.num, p.name, line_boundaries, circles, semicircles, color=pcolor)
|
| 1385 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1386 |
if equals:
|
| 1387 |
for i, segs in enumerate(equals['segments']):
|
| 1388 |
color = colors[i % len(colors)]
|
|
@@ -1947,6 +2035,9 @@ def sketch_midp(args: tuple[gm.Point, ...]) -> Point:
|
|
| 1947 |
a, b = args
|
| 1948 |
return (a + b) * 0.5
|
| 1949 |
|
|
|
|
|
|
|
|
|
|
| 1950 |
|
| 1951 |
def sketch_pentagon(args: tuple[gm.Point, ...]) -> tuple[Point, ...]:
|
| 1952 |
points = [Point(1.0, 0.0)]
|
|
|
|
| 18 |
|
| 19 |
import math
|
| 20 |
from typing import Any, Optional, Union
|
| 21 |
+
import random
|
| 22 |
|
| 23 |
import geometry as gm
|
| 24 |
import matplotlib
|
|
|
|
| 27 |
import numpy as np
|
| 28 |
from numpy.random import uniform as unif # pylint: disable=g-importing-member
|
| 29 |
import graph as gh
|
| 30 |
+
from collections import defaultdict
|
| 31 |
+
from itertools import combinations
|
| 32 |
|
| 33 |
+
matplotlib.use('TkAgg')
|
| 34 |
|
| 35 |
|
| 36 |
ATOM = 1e-12
|
|
|
|
| 443 |
|
| 444 |
|
| 445 |
class SemiCircle(Circle):
|
| 446 |
+
"""Numerical semicircle, inherits from Circle."""
|
| 447 |
+
|
| 448 |
+
def __init__(
|
| 449 |
+
self,
|
| 450 |
+
center: Optional[Point] = None,
|
| 451 |
+
radius: Optional[float] = None,
|
| 452 |
+
p1: Optional[Point] = None,
|
| 453 |
+
p2: Optional[Point] = None,
|
| 454 |
+
p3: Optional[Point] = None,
|
| 455 |
+
):
|
| 456 |
+
self.p1 = p1
|
| 457 |
+
self.p2 = p2
|
| 458 |
+
self.p3 = p3
|
| 459 |
+
# Initialize as a Circle
|
| 460 |
+
super().__init__(center, radius, p1, p2, p3)
|
| 461 |
+
# If p1 and p2 define a diameter, set the center and radius accordingly
|
| 462 |
+
if p1 and p2 and not center:
|
| 463 |
+
self.center = Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2)
|
| 464 |
+
self.radius = p1.distance(p2) / 2
|
| 465 |
+
self.r2 = self.radius ** 2
|
| 466 |
+
|
| 467 |
+
# Define the direction or plane for the semicircle (important for sampling and boundaries)
|
| 468 |
+
|
| 469 |
+
def is_within_boundary(self, point: Point) -> bool:
|
| 470 |
+
"""Check if a point is within the boundary of the semicircle."""
|
| 471 |
+
vector_to_point = point - self.center
|
| 472 |
+
angle = math.atan2(vector_to_point.y, vector_to_point.x)
|
| 473 |
+
|
| 474 |
+
# Normalize the angle within [0, 2*pi]
|
| 475 |
+
angle = angle if angle >= 0 else (2 * np.pi + angle)
|
| 476 |
+
|
| 477 |
+
# Check if the point is within the semicircle (half of the circle)
|
| 478 |
+
return -np.pi / 2 <= angle <= np.pi / 2
|
| 479 |
+
|
| 480 |
+
def sample_within(self, points: list[Point], n: int = 5) -> list[Point]:
|
| 481 |
+
"""Sample a point within the semicircle."""
|
| 482 |
+
result = None
|
| 483 |
+
best = -1.0
|
| 484 |
+
for _ in range(n):
|
| 485 |
+
# Generate a random angle between -π/2 and π/2 for the semicircle
|
| 486 |
+
ang = unif(-0.5, 0.5) * np.pi
|
| 487 |
+
x = self.center + Point(np.cos(ang), np.sin(ang)) * self.radius
|
| 488 |
+
|
| 489 |
+
# Check if the sampled point is within the active part of the semicircle
|
| 490 |
+
if not self.is_within_boundary(x):
|
| 491 |
+
continue
|
| 492 |
+
|
| 493 |
+
# Find the minimum distance between the generated point and the provided points
|
| 494 |
+
mind = min([x.distance(p) for p in points])
|
| 495 |
+
if mind > best:
|
| 496 |
+
best = mind
|
| 497 |
+
result = x
|
| 498 |
+
|
| 499 |
+
return [result]
|
| 500 |
+
|
| 501 |
+
def intersect(self, obj: Union[Line, Circle]) -> tuple[Point, ...]:
|
| 502 |
+
"""Find intersection points with a Line or another Circle, constrained to the semicircle."""
|
| 503 |
+
if isinstance(obj, Line):
|
| 504 |
+
intersections = obj.intersect(self)
|
| 505 |
+
elif isinstance(obj, Circle):
|
| 506 |
+
intersections = circle_circle_intersection(self, obj)
|
| 507 |
+
else:
|
| 508 |
+
return tuple()
|
| 509 |
+
|
| 510 |
+
# Filter intersections to only return points within the semicircle
|
| 511 |
+
return tuple(p for p in intersections if self.is_within_boundary(p))
|
| 512 |
|
| 513 |
|
| 514 |
class HoleCircle(Circle):
|
|
|
|
| 792 |
|
| 793 |
def check_cyclic(points: list[Point]) -> bool:
|
| 794 |
points = list(set(points))
|
| 795 |
+
(a, b, c, *ps) = points
|
| 796 |
circle = Circle(p1=a, p2=b, p3=c)
|
| 797 |
for d in ps:
|
| 798 |
if not close_enough(d.distance(circle.center), circle.radius):
|
|
|
|
| 978 |
|
| 979 |
|
| 980 |
def naming_position(
|
| 981 |
+
ax: matplotlib.axes.Axes, p: Point, lines: list[Line], circles: list[Circle], semicircles: list[SemiCircle]
|
| 982 |
) -> tuple[float, float]:
|
| 983 |
"""Figure out a good naming position on the drawing."""
|
| 984 |
_ = ax
|
|
|
|
| 1037 |
name = name[0] + '_' + name[1:]
|
| 1038 |
|
| 1039 |
ax.annotate(
|
| 1040 |
+
name, naming_position(ax, p, lines, circles, semicircles), color=color, fontsize=15
|
| 1041 |
)
|
| 1042 |
|
| 1043 |
|
|
|
|
| 1109 |
ax: matplotlib.axes.Axes, circle: Circle, color: Any = 'cyan'
|
| 1110 |
) -> Circle:
|
| 1111 |
"""Draw a circle."""
|
| 1112 |
+
name_circle = circle.name
|
| 1113 |
if circle.num is not None:
|
| 1114 |
circle = circle.num
|
| 1115 |
else:
|
|
|
|
| 1117 |
if len(points) <= 2:
|
| 1118 |
return
|
| 1119 |
points = [p.num for p in points]
|
| 1120 |
+
print(len(points))
|
| 1121 |
p1, p2, p3 = points[:3]
|
| 1122 |
circle = Circle(p1=p1, p2=p2, p3=p3)
|
| 1123 |
+
if "," in name_circle:
|
| 1124 |
+
_draw_circle(ax, circle, color)
|
| 1125 |
+
return circle
|
| 1126 |
+
else:
|
| 1127 |
+
return circle
|
| 1128 |
|
| 1129 |
def check_points_semicircle(p1, p2, p3):
|
| 1130 |
"""
|
|
|
|
| 1182 |
}
|
| 1183 |
|
| 1184 |
def _draw_semicircle(
|
| 1185 |
+
ax: matplotlib.axes.Axes, P1: Point = None, P2: Point = None, P3: Point = None, color: Any = 'cyan', lw: float = 1.2
|
| 1186 |
) -> None:
|
| 1187 |
"""
|
| 1188 |
Draws a semicircle passing through three points or with one or two points on the diameter.
|
|
|
|
| 1193 |
color (Any): Color of the semicircle.
|
| 1194 |
lw (float): Line width of the semicircle.
|
| 1195 |
"""
|
| 1196 |
+
points = [P for P in (P1, P2, P3) if P is not None] # Filter out None values
|
| 1197 |
+
if len(points) == 2:
|
| 1198 |
+
cx = (P1.x + P2.x) / 2
|
| 1199 |
+
cy = (P1.y + P2.y) / 2
|
| 1200 |
+
radius = np.sqrt((P2.x - P1.x) ** 2 + (P2.y - P1.y) ** 2) / 2
|
| 1201 |
+
|
| 1202 |
+
# Compute angle of the diameter
|
| 1203 |
+
angle_diameter = np.arctan2(P2.y - P1.y, P2.x - P1.x)
|
| 1204 |
+
|
| 1205 |
+
# Randomly determine the orientation of the semicircle
|
| 1206 |
+
offset_angle = np.pi / 2 if random.choice([True, False]) else -np.pi / 2
|
| 1207 |
+
|
| 1208 |
+
# Start and end angles for the semicircle
|
| 1209 |
+
start_angle = angle_diameter + offset_angle
|
| 1210 |
+
end_angle = start_angle + np.pi
|
| 1211 |
+
|
| 1212 |
+
# Generate points for the semicircle
|
| 1213 |
+
t = np.linspace(start_angle, end_angle, 100)
|
| 1214 |
+
x = cx + radius * np.cos(t)
|
| 1215 |
+
y = cy + radius * np.sin(t)
|
| 1216 |
+
|
| 1217 |
+
# Plot the semicircle
|
| 1218 |
+
ax.plot(x, y, color=color, lw=lw)
|
| 1219 |
+
|
| 1220 |
+
if len(points) == 3:
|
| 1221 |
|
| 1222 |
+
result = check_points_semicircle((P1.x, P1.y), (P2.x, P2.y), (P3.x, P3.y))
|
| 1223 |
+
if not result['is_valid']:
|
| 1224 |
+
print("Points are collinear; cannot form a semicircle.")
|
| 1225 |
+
return
|
| 1226 |
+
|
| 1227 |
+
cx, cy = result['center']
|
| 1228 |
+
radius = result['radius']
|
| 1229 |
+
diameter_points = result['diameter_points']
|
| 1230 |
+
|
| 1231 |
+
# If no pair forms a diameter, determine angles for all three points
|
| 1232 |
+
if diameter_points is None:
|
| 1233 |
+
# Calculate angles of all three points relative to the circle's center
|
| 1234 |
+
angles = np.arctan2(
|
| 1235 |
+
[P1.y - cy, P2.y - cy, P3.y - cy],
|
| 1236 |
+
[P1.x - cx, P2.x - cx, P3.x - cx]
|
| 1237 |
+
)
|
| 1238 |
+
angles = (angles + 2 * np.pi) % (2 * np.pi) # Normalize to [0, 2π]
|
| 1239 |
+
|
| 1240 |
+
# Determine the start and end angle for the semicircle
|
| 1241 |
+
start_angle = np.min(angles)
|
| 1242 |
+
end_angle = np.max(angles)
|
| 1243 |
+
if end_angle - start_angle > np.pi:
|
| 1244 |
+
start_angle, end_angle = end_angle, start_angle + 2 * np.pi
|
| 1245 |
+
else:
|
| 1246 |
+
# Use diameter points to define the semicircle angles
|
| 1247 |
+
px, py = diameter_points[0]
|
| 1248 |
+
qx, qy = diameter_points[1]
|
| 1249 |
+
start_angle = np.arctan2(py - cy, px - cx)
|
| 1250 |
+
end_angle = np.arctan2(qy - cy, qx - cx)
|
| 1251 |
+
if end_angle - start_angle > np.pi:
|
| 1252 |
+
start_angle, end_angle = end_angle, start_angle + 2 * np.pi
|
| 1253 |
+
|
| 1254 |
+
# Generate points for the semicircle
|
| 1255 |
+
t = np.linspace(start_angle, end_angle, 100)
|
| 1256 |
+
x = cx + radius * np.cos(t)
|
| 1257 |
+
y = cy + radius * np.sin(t)
|
| 1258 |
+
|
| 1259 |
+
# Plot the semicircle
|
| 1260 |
+
ax.plot(x, y, color=color, lw=lw)
|
| 1261 |
|
| 1262 |
def draw_semicircle(
|
| 1263 |
ax: matplotlib.axes.Axes, semicircle: SemiCircle, color: Any = 'cyan'
|
|
|
|
| 1329 |
_draw_line(ax, c, d, color=color2, lw=2.0)
|
| 1330 |
if name == 'eqangle':
|
| 1331 |
a, b, c, d, e, f, g, h = args
|
|
|
|
| 1332 |
x = line_line_intersection(Line(a, b), Line(c, d))
|
| 1333 |
if b.distance(x) > a.distance(x):
|
| 1334 |
a, b = b, a
|
|
|
|
| 1375 |
_draw_line(ax, c, d, color=color2, lw=2.0, alpha=0.5)
|
| 1376 |
_draw_line(ax, m, n, color=color1, lw=2.0, alpha=0.5)
|
| 1377 |
_draw_line(ax, p, q, color=color2, lw=2.0, alpha=0.5)
|
| 1378 |
+
if name == 'iso_triangle':
|
| 1379 |
+
a, b, c = args
|
| 1380 |
+
_draw_line(ax, c, b, color=color1, lw=2.0)
|
| 1381 |
+
_draw_line(ax, c, a, color=color1, lw=2.0)
|
| 1382 |
+
_draw_line(ax, b, a, color=color2, lw=2.0)
|
| 1383 |
+
if name == 'semicircle':
|
| 1384 |
+
o, a, b, c = args
|
| 1385 |
+
_draw_semicircle(ax, SemiCircle(center=o, p1=a, p2=b, p3=c), color=color1, lw=2.0)
|
| 1386 |
+
|
| 1387 |
+
def convert_point(gm_point: gm.Point) -> Point:
|
| 1388 |
+
return Point(gm_point.num.x, gm_point.num.y)
|
| 1389 |
|
| 1390 |
HCOLORS = None
|
| 1391 |
|
| 1392 |
+
def find_pairs_with_same_distance(line_lengths):
|
| 1393 |
+
# Step 1: Group point pairs by distance
|
| 1394 |
+
distance_groups = defaultdict(list)
|
| 1395 |
+
for (p1, p2), distance in line_lengths.items():
|
| 1396 |
+
distance_groups[distance].append((p1, p2))
|
| 1397 |
+
|
| 1398 |
+
# Step 2: Find combinations of pairs with the same distance
|
| 1399 |
+
result = []
|
| 1400 |
+
for pairs in distance_groups.values():
|
| 1401 |
+
if len(pairs) > 1: # Only consider distances with multiple pairs
|
| 1402 |
+
for i in range(len(pairs)):
|
| 1403 |
+
for j in range(i + 1, len(pairs)):
|
| 1404 |
+
line1 = pairs[i]
|
| 1405 |
+
line2 = pairs[j]
|
| 1406 |
+
result.append((line1, line2)) # Group as ((p1, p2), (p3, p4))
|
| 1407 |
+
|
| 1408 |
+
return result
|
| 1409 |
|
| 1410 |
def _draw(
|
| 1411 |
ax: matplotlib.axes.Axes,
|
|
|
|
| 1419 |
):
|
| 1420 |
"""Draw everything."""
|
| 1421 |
colors = ['red', 'green', 'blue', 'orange', 'magenta', 'purple']
|
| 1422 |
+
colors_highlight = [color for color in mcolors.TABLEAU_COLORS.keys() if color not in colors]
|
| 1423 |
pcolor = 'black'
|
| 1424 |
lcolor = 'black'
|
| 1425 |
ccolor = 'grey'
|
|
|
|
| 1432 |
colors = ['grey']
|
| 1433 |
|
| 1434 |
line_boundaries = []
|
| 1435 |
+
line_lengths = {}
|
| 1436 |
+
# Convert all points
|
| 1437 |
+
for p in points:
|
| 1438 |
+
points_numericals = [convert_point(p) for p in points]
|
| 1439 |
for l in lines:
|
| 1440 |
p1, p2 = draw_line(ax, l, color=lcolor)
|
| 1441 |
line_boundaries.append((p1, p2))
|
| 1442 |
+
points_numericals.append(p1)
|
| 1443 |
+
points_numericals.append(p2)
|
| 1444 |
+
unique_points = []
|
| 1445 |
+
seen = set()
|
| 1446 |
+
for p in points_numericals:
|
| 1447 |
+
if (p.x, p.y) not in seen:
|
| 1448 |
+
unique_points.append(p)
|
| 1449 |
+
seen.add((p.x, p.y))
|
| 1450 |
+
print(len(unique_points))
|
| 1451 |
+
for p1, p2 in combinations(unique_points, 2):
|
| 1452 |
+
line_lengths[(p1, p2)] = round(p1.distance(p2), 9)
|
| 1453 |
circles = [draw_circle(ax, c, color=ccolor) for c in circles]
|
| 1454 |
semicircles = [draw_semicircle(ax, c, color=ccolor) for c in semicircles]
|
| 1455 |
|
| 1456 |
for p in points:
|
| 1457 |
draw_point(ax, p.num, p.name, line_boundaries, circles, semicircles, color=pcolor)
|
| 1458 |
|
| 1459 |
+
same_length_pairs = find_pairs_with_same_distance(line_lengths)
|
| 1460 |
+
print(len(same_length_pairs))
|
| 1461 |
+
|
| 1462 |
+
length_color_map = {} # Dictionary to map length to its color
|
| 1463 |
+
for i, ((p1, p2), (p3, p4)) in enumerate(same_length_pairs):
|
| 1464 |
+
line_length = p1.distance(p2) # Calculate the length of the line
|
| 1465 |
+
if line_length not in length_color_map: # Check if length is already in the dictionary
|
| 1466 |
+
#color = colors_highlight[i % len(colors_highlight)]
|
| 1467 |
+
color = colors_highlight[i % len(colors_highlight) ] # Assign a new color
|
| 1468 |
+
length_color_map[line_length] = color # Store the length and color in the dictionary
|
| 1469 |
+
else:
|
| 1470 |
+
color = length_color_map[line_length] # Use the existing color for this length
|
| 1471 |
+
|
| 1472 |
+
# Call the highlight function with the determined color
|
| 1473 |
+
highlight(ax, 'cong', [p1, p2, p3, p4], lcolor, color, color)
|
| 1474 |
if equals:
|
| 1475 |
for i, segs in enumerate(equals['segments']):
|
| 1476 |
color = colors[i % len(colors)]
|
|
|
|
| 2035 |
a, b = args
|
| 2036 |
return (a + b) * 0.5
|
| 2037 |
|
| 2038 |
+
def sketch_foot(args: tuple[gm.Point, ...]) -> Point:
|
| 2039 |
+
a, b, c = args
|
| 2040 |
+
return a.foot(Line(b, c))
|
| 2041 |
|
| 2042 |
def sketch_pentagon(args: tuple[gm.Point, ...]) -> tuple[Point, ...]:
|
| 2043 |
points = [Point(1.0, 0.0)]
|