geometryai 0.0.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: geometryai
3
+ Version: 0.0.9
4
+ Summary: solve euclid geometry
5
+ Author: Your Name
6
+ Author-email: you@example.com
7
+ Requires-Python: >=3.8
8
+ Dynamic: author
9
+ Dynamic: author-email
10
+ Dynamic: requires-python
11
+ Dynamic: summary
@@ -0,0 +1,3 @@
1
+ # GeometryAI
2
+
3
+ Dummy package for testing pip installation.
@@ -0,0 +1,4 @@
1
+ __version__ = "0.0.9"
2
+
3
+ from .core import draw_triangle, given_equal_line, prove_congruent_triangle,\
4
+ check_equal_angle, check_equal_line, process, cpct, show, join, split_line, norm_angle, draw_perpendicular
@@ -0,0 +1,482 @@
1
+ import itertools
2
+ from fractions import Fraction
3
+ from PIL import Image, ImageDraw, ImageFont
4
+ class Graph:
5
+ def __init__(self, space):
6
+ self.n = len(space.point_location)
7
+ self.adj = {i: set() for i in range(self.n)}
8
+ self._build(space.give_connect())
9
+ def _build(self, edges):
10
+ for u, v in edges:
11
+ self.adj[u].add(v)
12
+ self.adj[v].add(u)
13
+ def all_cycles(self):
14
+ cycles = []
15
+ visited = [False] * self.n
16
+ def dfs(start, current, parent, path):
17
+ visited[current] = True
18
+ path.append(current)
19
+ for nxt in self.adj[current]:
20
+ if nxt == parent:
21
+ continue
22
+ if nxt == start and len(path) > 2:
23
+ cycles.append(path.copy())
24
+ elif not visited[nxt]:
25
+ dfs(start, nxt, current, path)
26
+ path.pop()
27
+ visited[current] = False
28
+ for v in range(self.n):
29
+ dfs(v, v, -1, [])
30
+ return cycles
31
+ def _canonical_cycle(self, cycle):
32
+ cycle = cycle[:-1] if cycle[0] == cycle[-1] else cycle
33
+ n = len(cycle)
34
+ rotations = []
35
+ for i in range(n):
36
+ r = cycle[i:] + cycle[:i]
37
+ rotations.append(tuple(r))
38
+ rotations.append(tuple(reversed(r)))
39
+ return min(rotations)
40
+ def simple_cycles(self):
41
+ raw = self.all_cycles()
42
+ unique = set()
43
+ for cycle in raw:
44
+ if len(set(cycle)) != len(cycle):
45
+ continue
46
+ unique.add(self._canonical_cycle(cycle))
47
+ return [list(c) for c in unique]
48
+ def consecutive_triplets_at(self):
49
+ triplets = []
50
+ for v in self.adj.keys():
51
+ nbrs = list(self.adj[v])
52
+ if len(nbrs) >=2:
53
+ for i in range(len(nbrs)):
54
+ for j in range(len(nbrs)):
55
+ if i != j:
56
+ triplets.append((nbrs[i], v, nbrs[j]))
57
+ return triplets
58
+ def draw_geometry(points, edges, size, margin):
59
+ pts = [(float(x), float(y)) for x, y in points]
60
+ xs = [x for x, y in pts]
61
+ ys = [y for x, y in pts]
62
+ min_x, max_x = min(xs), max(xs)
63
+ min_y, max_y = min(ys), max(ys)
64
+ width = max_x - min_x or 1
65
+ height = max_y - min_y or 1
66
+ if size is None:
67
+ size = 300
68
+ if margin is None:
69
+ margin = 40
70
+ scale = min(
71
+ (size - 2 * margin) / width,
72
+ (size - 2 * margin) / height
73
+ )
74
+ def transform(x, y):
75
+ px = margin + (x - min_x) * scale
76
+ py = size - (margin + (y - min_y) * scale)
77
+ return px, py
78
+
79
+ img = Image.new("RGB", (size, size), "white")
80
+ draw = ImageDraw.Draw(img)
81
+ for i, j in edges:
82
+ p1 = transform(*pts[i])
83
+ p2 = transform(*pts[j])
84
+ draw.line([p1, p2], fill="black", width=2)
85
+ try:
86
+ font = ImageFont.truetype("arial.ttf", 22)
87
+ except:
88
+ font = ImageFont.load_default()
89
+ r = 4
90
+ label_offset = (6, -6)
91
+ for idx, (x, y) in enumerate(pts):
92
+ px, py = transform(x, y)
93
+ draw.ellipse((px-r, py-r, px+r, py+r), fill="red")
94
+ label = chr(ord("A") + idx)
95
+ draw.text(
96
+ (px + label_offset[0], py + label_offset[1]),
97
+ label,
98
+ fill="blue",
99
+ font=font
100
+ )
101
+ img.save("output.png")
102
+ return img
103
+ def merge_category(cat, mergefx):
104
+ n = len(cat)
105
+ used = [False] * n
106
+ out = []
107
+ for i in range(n):
108
+ if used[i]:
109
+ continue
110
+ merged = []
111
+ for j in range(i, n):
112
+ if not used[j] and mergefx(cat[i], cat[j]):
113
+ merged += cat[j]
114
+ used[j] = True
115
+ out.append(merged)
116
+ return [list(set(item)) for item in out]
117
+
118
+ def are_collinear(points):
119
+ """
120
+ Returns True if all points are collinear, False otherwise.
121
+ points: list of (x, y), x and y can be Fraction or int
122
+ """
123
+ n = len(points)
124
+ if n <= 2:
125
+ return True
126
+ x0, y0 = points[0]
127
+ x1, y1 = points[1]
128
+ dx = x1 - x0
129
+ dy = y1 - y0
130
+ for i in range(2, n):
131
+ xi, yi = points[i]
132
+ if (xi - x0) * dy != (yi - y0) * dx:
133
+ return False
134
+ return True
135
+ def intersection(p1, p2, p3, p4):
136
+ x1, y1 = p1
137
+ x2, y2 = p2
138
+ x3, y3 = p3
139
+ x4, y4 = p4
140
+ A1 = y2 - y1
141
+ B1 = x1 - x2
142
+ C1 = A1 * x1 + B1 * y1
143
+ A2 = y4 - y3
144
+ B2 = x3 - x4
145
+ C2 = A2 * x3 + B2 * y3
146
+ det = A1 * B2 - A2 * B1
147
+ if det == 0:
148
+ return None
149
+ x = (C1 * B2 - C2 * B1) / det
150
+ y = (A1 * C2 - A2 * C1) / det
151
+ return (x, y)
152
+
153
+ def point_sort(point):
154
+ if isinstance(point, str):
155
+ return ord(point)-ord("A")
156
+ return point
157
+ def line_sort(line):
158
+ if isinstance(line, str):
159
+ return tuple(sorted([ord(item)-ord("A") for item in line]))
160
+ return tuple(sorted(list(line)))
161
+ class Space:
162
+ def __init__(self):
163
+ self.point_location = []
164
+ self.line_info = []
165
+ self.angle_list = {}
166
+ self.command = []
167
+ self.line = []
168
+ self.ray = []
169
+ self.graph = None
170
+ self.line_eq = []
171
+ self.angle_eq = []
172
+ self.tri_eq = []
173
+ self.perpendicular_angle = []
174
+ self.perpendicular = []
175
+ def standard_angle(self, angle):
176
+ if isinstance(angle, str):
177
+ angle = tuple([ord(item)-ord("A") for item in angle])
178
+ if angle[0] > angle[2]:
179
+ angle = (angle[2],angle[1],angle[0])
180
+ if isinstance(angle, list):
181
+ angle = tuple(angle)
182
+ for key in self.angle_list.keys():
183
+ if key == angle or angle in self.angle_list[key]:
184
+ return key
185
+ return None
186
+ def perpen_angle(self):
187
+ self.perpendicular = [[list(item2) for item2 in item] for item in self.perpendicular]
188
+ for item in itertools.combinations(self.line_info, 2):
189
+ item = list(item)
190
+ if all(not self.straight_line(list(set(item[0]+item2[0]))) or not self.straight_line(list(set(item[1]+item2[1]))) for item2 in self.perpendicular)\
191
+ and all(not self.straight_line(list(set(item[0]+item2[1]))) or not self.straight_line(list(set(item[1]+item2[0]))) for item2 in self.perpendicular):
192
+ continue
193
+ if len(set(item[0])&set(item[1]))==1:
194
+ c = list(set(item[0])&set(item[1]))[0]
195
+ a = item[0].index(c)
196
+ b = item[1].index(c)
197
+ m, n = [], []
198
+ for i in range(2):
199
+ d = [a,b][i]
200
+ if d>0:
201
+ [m,n][i].append(item[i][d-1])
202
+ if d<len(item[i])-1:
203
+ [m,n][i].append(item[i][d+1])
204
+ for item2 in itertools.product(m,n):
205
+ angle = space.standard_angle((item2[0],c,item2[1]))
206
+ if angle not in self.perpendicular_angle:
207
+ self.perpendicular_angle.append(angle)
208
+ space.angle_eq.append(self.perpendicular_angle)
209
+ space.angle_eq = merge_category(space.angle_eq, default_merge)
210
+ def straight_line(self, point_list):
211
+ return are_collinear([self.point_location[x] for x in point_list])
212
+ def sort_collinear(self, point_list):
213
+ p = min([self.point_location[x][1] for x in point_list])
214
+ p2 = [x for x in point_list if self.point_location[x][1] == p]
215
+ p3 = list(sorted(p2, key=lambda x: self.point_location[x][0]))[0]
216
+ m, n = self.point_location[p3]
217
+ return list(sorted(point_list, key=lambda x: (self.point_location[x][0]-m)**2 + (self.point_location[x][1]-n)**2))
218
+ def calc_angle_list(self):
219
+ lst = self.graph.consecutive_triplets_at()
220
+ lst = list(set([item if item[0]<item[2] else (item[2],item[1],item[0]) for item in lst]))
221
+ lst = [item for item in lst if not self.straight_line(list(item))]
222
+ for item2 in lst:
223
+ self.angle_list[item2] = []
224
+ for item in itertools.permutations(self.line_info, 2):
225
+ if item2[0] in item[0] and item2[2] in item[1] and item2[1] in item[0] and item2[1] in item[1]:
226
+ h = []
227
+ for i in range(2):
228
+ m = item[i][:item[i].index(item2[1])]
229
+ n = item[i][item[i].index(item2[1])+1:]
230
+ if item2[0] in m or item2[2] in m:
231
+ h.append(m)
232
+ elif item2[0] in n or item2[2] in n:
233
+ h.append(n)
234
+ for item3 in itertools.product(*h):
235
+ x = (item3[0], item2[1], item3[1])
236
+ y = (item3[1], item2[1], item3[0])
237
+ self.angle_list[item2] += [x,y]
238
+ def give_connect(self):
239
+ out = []
240
+ for item in self.line_info:
241
+ for i in range(len(item)-1):
242
+ out.append(item[i:i+2])
243
+ return out
244
+ def line_eq_fx(self, line1, line2):
245
+ line1 = line_sort(line1)
246
+ line2 = line_sort(line2)
247
+ if line1 == line2:
248
+ return True
249
+ for item in self.line_eq:
250
+ if line1 in item and line2 in item:
251
+ return True
252
+ return False
253
+ def angle_eq_fx(self, angle1, angle2):
254
+ angle1 = self.standard_angle(angle1)
255
+ angle2 = self.standard_angle(angle2)
256
+ if angle1 == angle2:
257
+ return True
258
+ for item in self.angle_eq:
259
+ if angle1 in item and angle2 in item:
260
+ return True
261
+ return False
262
+ def valid_line(self, line):
263
+ line = line_sort(line)
264
+ return any((line[0] in item and line[1] in item) for item in self.line_info)
265
+ def show_diagram(self, size):
266
+ out = draw_geometry(self.point_location, self.give_connect(), size, None)
267
+ try:
268
+ from IPython.display import display
269
+ display(out)
270
+ out.show()
271
+ except:
272
+ print("error displaying image")
273
+ def calc_line_info(self):
274
+ line = [self.line_info[x] for x in self.line]
275
+ ray = [(self.line_info[x[0]], x[1]) for x in self.ray]
276
+ self.line = []
277
+ self.ray = []
278
+ cat = []
279
+ for index in range(2):
280
+ for item in itertools.combinations(list(range(len(self.point_location))), 3):
281
+ if self.straight_line(list(item)):
282
+ cat.append(list(item))
283
+ for item in self.line_info:
284
+ if len(item) == 2 and all(item[0] not in item2 or item[1] not in item2 for item2 in cat):
285
+ cat.append(list(item))
286
+ def mergefx(a, b):
287
+ return self.straight_line(list(set(a+b)))
288
+ cat = merge_category(cat, mergefx)
289
+ p = []
290
+
291
+ for item in itertools.combinations(cat, 2):
292
+ p2 = intersection(*[self.point_location[item2] for item2 in item[0][:2]+item[1][:2]])
293
+ p.append(p2)
294
+ p = list(set(p))
295
+ if self.command != [] and index == 0:
296
+ for i in range(len(self.command)-1,-1,-1):
297
+ for item in p:
298
+ if any(item2[0]==item[0] and item2[1]==item[1] for item2 in self.point_location):
299
+ continue
300
+ self.point_location.append(item)
301
+ lst = [self.command[i][0], len(self.point_location)-1, self.command[i][1]]
302
+ if self.straight_line(lst) and (any(lst[0] in item2 and lst[1] in item2 for item2 in self.line) or self.sort_collinear(lst)[1] == lst[1]):
303
+ pass
304
+ else:
305
+ self.point_location.pop(-1)
306
+ cat = []
307
+ else:
308
+ break
309
+ self.line_info = [self.sort_collinear(item) for item in cat]
310
+ for item in line:
311
+ for i in range(len(self.line_info)):
312
+ if self.straight_line(list(self.line_info[i])+item):
313
+ self.line.append(i)
314
+ for item in ray:
315
+ for i in range(len(self.line_info)):
316
+ if self.straight_line(list(self.line_info[i])+item[0]):
317
+ self.ray.append((i, item[1]))
318
+ self.graph = Graph(self)
319
+ def default_merge(a, b):
320
+ return (set(a)&set(b)) != {}
321
+ space = Space()
322
+ def draw_triangle():
323
+ global space
324
+ space.point_location = [
325
+ (Fraction(0), Fraction(0)),
326
+ (Fraction(4), Fraction(1)),
327
+ (Fraction(1), Fraction(3)),
328
+ ]
329
+ space.line_info = [[0,1],[1,2],[2,0]]
330
+ def given_equal_line(line1, line2):
331
+ global space
332
+ line1 = line_sort(line1)
333
+ line2 = line_sort(line2)
334
+ space.line_eq.append([line1, line2])
335
+ space.line_eq = merge_category(space.line_eq, default_merge)
336
+ def cpct():
337
+ global space
338
+ for item in space.tri_eq:
339
+ for item2 in itertools.combinations(item, 2):
340
+ m2 = list(zip(*item2))
341
+ for item3 in itertools.permutations(m2):
342
+ angle1, angle2 = (item3[0][0], item3[1][0], item3[2][0]), (item3[0][1], item3[1][1], item3[2][1])
343
+ angle1, angle2 = space.standard_angle(angle1), space.standard_angle(angle2)
344
+
345
+ if angle1 is None or angle2 is None or angle1 == angle2:
346
+ continue
347
+ space.angle_eq.append([angle1, angle2])
348
+ for item3 in itertools.combinations(m2, 2):
349
+ line1, line2 = item3
350
+ line1, line2 = line_sort(line1), line_sort(line2)
351
+ if not space.valid_line(line1) or not space.valid_line(line2) or line1 == line2:
352
+ continue
353
+ space.line_eq.append([line1, line2])
354
+ space.line_eq = merge_category(space.line_eq, default_merge)
355
+ space.angle_eq = merge_category(space.angle_eq, default_merge)
356
+ def sss_rule(a1, a2, a3, b1, b2, b3):
357
+ global space
358
+ a1, a2, a3, b1, b2, b3 = [[item] for item in [a1, a2, a3, b1, b2, b3]]
359
+ line = [
360
+ line_sort(a1 + a2),
361
+ line_sort(b1 + b2),
362
+ line_sort(a2 + a3),
363
+ line_sort(b2 + b3),
364
+ line_sort(a1 + a3),
365
+ line_sort(b1 + b3),
366
+ ]
367
+
368
+ for item in line:
369
+ if not space.valid_line(item):
370
+ return False
371
+
372
+ return (
373
+ space.line_eq_fx(line[0], line[1])
374
+ and space.line_eq_fx(line[2], line[3])
375
+ and space.line_eq_fx(line[4], line[5])
376
+ )
377
+ def rhs_rule(a1, a2, a3, b1, b2, b3):
378
+ global space
379
+ a1, a2, a3, b1, b2, b3 = [[item] for item in [a1, a2, a3, b1, b2, b3]]
380
+ line = [
381
+ line_sort(a1 + a2),
382
+ line_sort(b1 + b2),
383
+ line_sort(a1 + a3),
384
+ line_sort(b1 + b3),
385
+ ]
386
+ angle = [space.standard_angle(a1 + a2 + a3), space.standard_angle(b1 + b2 + b3)]
387
+
388
+ for item in line:
389
+ if not space.valid_line(item):
390
+ return False
391
+
392
+ for item in angle:
393
+ if item is None:
394
+ return False
395
+
396
+ return (
397
+ space.line_eq_fx(line[0], line[1])
398
+ and space.angle_eq_fx(angle[0], angle[1])
399
+ and space.line_eq_fx(line[2], line[3])
400
+ and angle[0] in space.perpendicular_angle
401
+ )
402
+ def tri_sort(tri):
403
+ return tuple([ord(item)-ord("A") for item in tri])
404
+ def check_equal_angle(a, b):
405
+ global space
406
+ return space.angle_eq_fx(a, b)
407
+ def check_equal_line(a, b):
408
+ global space
409
+ return space.line_eq_fx(a, b)
410
+ def prove_congruent_triangle(tri1, tri2=None):
411
+ global space
412
+ if tri2 is None:
413
+ tri2 = tri1
414
+ list1 = list(itertools.permutations(list(tri_sort(tri1))))
415
+ list2 = list(itertools.permutations(list(tri_sort(tri2))))
416
+ for x in list1:
417
+ for y in list2:
418
+ a = list(x)
419
+ b = list(y)
420
+ for item in [a+b,b+a]:
421
+ for rule in [sss_rule, rhs_rule]:
422
+ out = rule(*item)
423
+ if out:
424
+ space.tri_eq.append([x, y])
425
+ space.tri_eq = merge_category(space.tri_eq, default_merge)
426
+ def process():
427
+ global space
428
+ space.calc_line_info()
429
+ space.calc_angle_list()
430
+ space.perpen_angle()
431
+ def split_line(line, p=None):
432
+ global space
433
+ line = line_sort(line)
434
+ a, b = space.point_location[line[0]], space.point_location[line[1]]
435
+ px = (a[0]+b[0])/2
436
+ py = (a[1]+b[1])/2
437
+ if p is not None:
438
+ px, py = p
439
+ space.point_location.append((px, py))
440
+ r = len(space.point_location)-1
441
+ for i in range(len(space.line_info)-1,-1,-1):
442
+ if line[0] in space.line_info[i] and line[1] in space.line_info[i]:
443
+
444
+ space.line_info[i] = space.line_info[i][:min(line)]+[len(space.point_location)-1]+space.line_info[i][max(line):]
445
+
446
+ return r
447
+ def extended_line(line):
448
+ global space
449
+ line = line_sort(line)
450
+ for i in range(len(space.line_info)):
451
+ if line[0] in space.line_info[i] and line[1] in space.line_info[i]:
452
+ self.line.append(i)
453
+ def join(line):
454
+ global space
455
+ line = line_sort(line)
456
+ space.line_info.append(line)
457
+ space.command.append(line)
458
+ def foot_of_perpendicular(P, A, B):
459
+ x0, y0 = P
460
+ x1, y1 = A
461
+ x2, y2 = B
462
+ dx = x2 - x1
463
+ dy = y2 - y1
464
+ t = ((x0 - x1) * dx + (y0 - y1) * dy) / (dx*dx + dy*dy)
465
+ x = x1 + t * dx
466
+ y = y1 + t * dy
467
+ return (x, y)
468
+ def draw_perpendicular(point, line):
469
+ global space
470
+ point = point_sort(point)
471
+ line = line_sort(line)
472
+ out = foot_of_perpendicular(space.point_location[point], space.point_location[line[0]], space.point_location[line[1]])
473
+ out2 = split_line(line, out)
474
+ m = line_sort((point, out2))
475
+ join(m)
476
+ space.perpendicular.append([m,line])
477
+ def show(size=None):
478
+ global space
479
+ space.show_diagram(size)
480
+ def norm_angle(angle):
481
+ global space
482
+ return space.standard_angle(angle)
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: geometryai
3
+ Version: 0.0.9
4
+ Summary: solve euclid geometry
5
+ Author: Your Name
6
+ Author-email: you@example.com
7
+ Requires-Python: >=3.8
8
+ Dynamic: author
9
+ Dynamic: author-email
10
+ Dynamic: requires-python
11
+ Dynamic: summary
@@ -0,0 +1,8 @@
1
+ README.md
2
+ setup.py
3
+ geometryai/__init__.py
4
+ geometryai/core.py
5
+ geometryai.egg-info/PKG-INFO
6
+ geometryai.egg-info/SOURCES.txt
7
+ geometryai.egg-info/dependency_links.txt
8
+ geometryai.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ geometryai
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,11 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="geometryai",
5
+ version="0.0.9",
6
+ description="solve euclid geometry",
7
+ author="Your Name",
8
+ author_email="you@example.com",
9
+ packages=find_packages(),
10
+ python_requires=">=3.8",
11
+ )