geometryai 0.0.5__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.5
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,3 @@
1
+ __version__ = "0.0.5"
2
+
3
+ from .core import draw_triangle, given_equal_line, prove_congruent_triangle, check_equal_angle, check_equal_line, process, cpct, show, join, split_line
@@ -0,0 +1,367 @@
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=600, margin=40):
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
+ def transform(x, y):
65
+ sx = (x - min_x) / (max_x - min_x or 1)
66
+ sy = (y - min_y) / (max_y - min_y or 1)
67
+ px = margin + sx * (size - 2 * margin)
68
+ py = size - (margin + sy * (size - 2 * margin))
69
+ return px, py
70
+ img = Image.new("RGB", (size, size), "white")
71
+ draw = ImageDraw.Draw(img)
72
+ for i, j in edges:
73
+ p1 = transform(*pts[i])
74
+ p2 = transform(*pts[j])
75
+ draw.line([p1, p2], fill="black", width=2)
76
+ try:
77
+ font = ImageFont.truetype("arial.ttf", 22)
78
+ except:
79
+ font = ImageFont.load_default()
80
+ r = 4
81
+ label_offset = (6, -6)
82
+ for idx, (x, y) in enumerate(pts):
83
+ px, py = transform(x, y)
84
+ draw.ellipse((px-r, py-r, px+r, py+r), fill="red")
85
+ label = chr(ord("A") + idx)
86
+ draw.text(
87
+ (px + label_offset[0], py + label_offset[1]),
88
+ label,
89
+ fill="blue",
90
+ font=font
91
+ )
92
+ return img
93
+
94
+ def merge_category(cat, mergefx):
95
+ n = len(cat)
96
+ used = [False] * n
97
+ out = []
98
+ for i in range(n):
99
+ if used[i]:
100
+ continue
101
+ merged = []
102
+ for j in range(i, n):
103
+ if not used[j] and mergefx(cat[i], cat[j]):
104
+ merged += cat[j]
105
+ used[j] = True
106
+ out.append(merged)
107
+ return [list(set(item)) for item in out]
108
+
109
+ def polygon_area(points):
110
+ n = len(points)
111
+ area = 0
112
+ for i in range(n - 1):
113
+ area += points[i][0] * points[i + 1][1] - points[i][1] * points[i + 1][0]
114
+ area += points[-1][0] * points[0][1] - points[-1][1] * points[0][0]
115
+ return abs(area) / 2
116
+ def intersection(p1, p2, p3, p4):
117
+ x1, y1 = p1
118
+ x2, y2 = p2
119
+ x3, y3 = p3
120
+ x4, y4 = p4
121
+ A1 = y2 - y1
122
+ B1 = x1 - x2
123
+ C1 = A1 * x1 + B1 * y1
124
+ A2 = y4 - y3
125
+ B2 = x3 - x4
126
+ C2 = A2 * x3 + B2 * y3
127
+ det = A1 * B2 - A2 * B1
128
+ if det == 0:
129
+ return None
130
+ x = (C1 * B2 - C2 * B1) / det
131
+ y = (A1 * C2 - A2 * C1) / det
132
+ return (x, y)
133
+
134
+ def line_sort(line):
135
+ if isinstance(line, str):
136
+ return tuple(sorted([ord(item)-ord("A") for item in line]))
137
+ return tuple(sorted(list(line)))
138
+ class Space:
139
+ def __init__(self):
140
+ self.point_location = []
141
+ self.line_info = []
142
+ self.angle_list = {}
143
+ self.command = []
144
+ self.line = []
145
+ self.ray = []
146
+ self.graph = None
147
+ self.line_eq = []
148
+ self.angle_eq = []
149
+ self.tri_eq = []
150
+ def standard_angle(self, angle):
151
+
152
+ if isinstance(angle, str):
153
+ angle = tuple([ord(item)-ord("A") for item in angle])
154
+ if angle[0] > angle[2]:
155
+ angle = (angle[2],angle[1],angle[0])
156
+ for key in self.angle_list.keys():
157
+ if key == angle or angle in self.angle_list[key]:
158
+ return key
159
+ return None
160
+ def straight_line(self, point_list):
161
+ return polygon_area([self.point_location[x] for x in point_list]) == 0
162
+ def sort_collinear(self, point_list):
163
+ p = min([self.point_location[x][1] for x in point_list])
164
+ p2 = [x for x in point_list if self.point_location[x][1] == p]
165
+ p3 = list(sorted(p2, key=lambda x: self.point_location[x][0]))[0]
166
+ m, n = self.point_location[p3]
167
+ return list(sorted(point_list, key=lambda x: (self.point_location[x][0]-m)**2 + (self.point_location[x][1]-n)**2))
168
+ def calc_angle_list(self):
169
+ lst = self.graph.consecutive_triplets_at()
170
+ lst = list(set([item if item[0]<item[2] else (item[2],item[1],item[0]) for item in lst]))
171
+ lst = [item for item in lst if not self.straight_line(list(item))]
172
+ for item2 in lst:
173
+ a = list(item2[:2])
174
+ b = list(item2[1:])
175
+ self.angle_list[item2] = []
176
+ for item in itertools.permutations(list(set(range(len(self.point_location))) - {item2[1]}),2):
177
+ if (self.straight_line(a+[item[0]]) and self.straight_line(b+[item[1]])) or\
178
+ (self.straight_line(b+[item[0]]) and self.straight_line(a+[item[1]])):
179
+ x = (item[0], item2[1], item[1])
180
+ y = (item[1], item2[1], item[0])
181
+ self.angle_list[item2]+= [x,y]
182
+ def give_connect(self):
183
+ out = []
184
+ for item in self.line_info:
185
+ for i in range(len(item)-1):
186
+ out.append(item[i:i+2])
187
+ return out
188
+ def line_eq_fx(self, line1, line2):
189
+ line1 = line_sort(line1)
190
+ line2 = line_sort(line2)
191
+ for item in self.line_info:
192
+ if line1[0] in item and line1[1] in item and line2[0] in item and line2[1] in item:
193
+ return True
194
+ for item in self.line_eq:
195
+ if line1 in item and line2 in item:
196
+ return True
197
+ return False
198
+ def angle_eq_fx(self, angle1, angle2):
199
+ angle1 = self.standard_angle(angle1)
200
+ angle2 = self.standard_angle(angle2)
201
+ for item in self.angle_eq:
202
+ if angle1 in item and angle2 in item:
203
+ return True
204
+ return False
205
+ def valid_line(self, line):
206
+ line = line_sort(line)
207
+ return any((line[0] in item and line[1] in item) for item in self.line_info)
208
+ def show_diagram(self):
209
+ draw_geometry(self.point_location, self.give_connect()).show()
210
+ def calc_line_info(self):
211
+ line = [self.line_info[x] for x in self.line]
212
+ ray = [(self.line_info[x[0]], x[1]) for x in self.ray]
213
+ self.line = []
214
+ self.ray = []
215
+ cat = []
216
+ for index in range(2):
217
+ for item in itertools.combinations(list(range(len(self.point_location))), 3):
218
+ if self.straight_line(list(item)):
219
+ cat.append(list(item))
220
+ for item in self.line_info:
221
+ if len(item) == 2 and all(item[0] not in item2 or item[1] not in item2 for item2 in cat):
222
+ cat.append(list(item))
223
+ def mergefx(a, b):
224
+ return self.straight_line(list(set(a+b)))
225
+
226
+ cat = merge_category(cat, mergefx)
227
+ p = []
228
+
229
+ for item in itertools.combinations(cat, 2):
230
+ p2 = intersection(*[self.point_location[item2] for item2 in item[0][:2]+item[1][:2]])
231
+ p.append(p2)
232
+ p = list(set(p))
233
+ if self.command != [] and index == 0:
234
+ for i in range(len(self.command)-1,-1,-1):
235
+ for item in p:
236
+ if any(item2[0]==item[0] and item2[1]==item[1] for item2 in self.point_location):
237
+ continue
238
+ self.point_location.append(item)
239
+ lst = [self.command[i][0], len(self.point_location)-1, self.command[i][1]]
240
+ 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]):
241
+ pass
242
+ else:
243
+ self.point_location.pop(-1)
244
+ #self.command.pop(-1)
245
+ cat = []
246
+ else:
247
+ break
248
+ self.line_info = [self.sort_collinear(item) for item in cat]
249
+ for item in line:
250
+ for i in range(len(self.line_info)):
251
+ if self.straight_line(list(self.line_info[i])+item):
252
+ self.line.append(i)
253
+ for item in ray:
254
+ for i in range(len(self.line_info)):
255
+ if self.straight_line(list(self.line_info[i])+item[0]):
256
+ self.ray.append((i, item[1]))
257
+ self.graph = Graph(self)
258
+ def default_merge(a, b):
259
+ return (set(a)&set(b)) != {}
260
+ space = Space()
261
+ def draw_triangle():
262
+ global space
263
+ space.point_location = [
264
+ (Fraction(0), Fraction(0)), # A
265
+ (Fraction(4), Fraction(1)), # B
266
+ (Fraction(1), Fraction(3)), # C
267
+ ]
268
+ space.line_info = [[0,1],[1,2],[2,0]]
269
+ def given_equal_line(line1, line2):
270
+ global space
271
+ line1 = line_sort(line1)
272
+ line2 = line_sort(line2)
273
+ space.line_eq.append([line1, line2])
274
+ space.line_eq = merge_category(space.line_eq, default_merge)
275
+ def cpct():
276
+ global space
277
+ for item in space.tri_eq:
278
+ for item2 in itertools.combinations(item, 2):
279
+ m2 = list(zip(*item2))
280
+ for item3 in itertools.permutations(m2):
281
+ angle1, angle2 = (item3[0][0], item3[1][0], item3[2][0]), (item3[0][1], item3[1][1], item3[2][1])
282
+ angle1, angle2 = space.standard_angle(angle1), space.standard_angle(angle2)
283
+
284
+ if angle1 is None or angle2 is None or angle1 == angle2:
285
+ continue
286
+ space.angle_eq.append([angle1, angle2])
287
+ for item3 in itertools.combinations(m2, 2):
288
+ line1, line2 = item3
289
+ line1, line2 = line_sort(line1), line_sort(line2)
290
+ if not space.valid_line(line1) or not space.valid_line(line2) or line1 == line2:
291
+ continue
292
+ space.line_eq.append([line1, line2])
293
+ space.line_eq = merge_category(space.line_eq, default_merge)
294
+ space.angle_eq = merge_category(space.angle_eq, default_merge)
295
+ def sss_rule(a1, a2, a3, b1, b2, b3):
296
+ global space
297
+ a1, a2, a3, b1, b2, b3 = [[item] for item in [a1, a2, a3, b1, b2, b3]]
298
+ line = [
299
+ line_sort(a1 + a2),
300
+ line_sort(b1 + b2),
301
+ line_sort(a2 + a3),
302
+ line_sort(b2 + b3),
303
+ line_sort(a1 + a3),
304
+ line_sort(b1 + b3),
305
+ ]
306
+
307
+ for item in line:
308
+ if not space.valid_line(item):
309
+ return False
310
+
311
+ return (
312
+ space.line_eq_fx(line[0], line[1])
313
+ and space.line_eq_fx(line[2], line[3])
314
+ and space.line_eq_fx(line[4], line[5])
315
+ )
316
+ def tri_sort(tri):
317
+ return tuple([ord(item)-ord("A") for item in tri])
318
+ def check_equal_angle(a, b):
319
+ global space
320
+ return space.angle_eq_fx(a, b)
321
+ def check_equal_line(a, b):
322
+ global space
323
+ return space.line_eq_fx(a, b)
324
+ def prove_congruent_triangle(tri1, tri2=None):
325
+ global space
326
+ if tri2 is None:
327
+ tri2 = tri1
328
+ list1 = list(itertools.permutations(list(tri_sort(tri1))))
329
+ list2 = list(itertools.permutations(list(tri_sort(tri2))))
330
+ for x in list1:
331
+ for y in list2:
332
+ a = list(x)
333
+ b = list(y)
334
+ for item in [a+b,b+a]:
335
+ for rule in [sss_rule]:
336
+ out = rule(*item)
337
+ if out:
338
+ space.tri_eq.append([x, y])
339
+ space.tri_eq = merge_category(space.tri_eq, default_merge)
340
+ def process():
341
+ global space
342
+ space.calc_line_info()
343
+ space.calc_angle_list()
344
+ def split_line(line):
345
+ global space
346
+ line = line_sort(line)
347
+ a, b = space.point_location[line[0]], space.point_location[line[1]]
348
+ px = (a[0]+b[0])/2
349
+ py = (a[1]+b[1])/2
350
+ space.point_location.append((px, py))
351
+ for i in range(len(space.line_info)-1,-1,-1):
352
+ if line[0] in space.line_info[i] and line[1] in space.line_info[i]:
353
+ space.line_info[i] = space.line_info[:min(line)]+[len(space.point_location)-1]+space.line_info[max(line):]
354
+ def extended_line(line):
355
+ global space
356
+ line = line_sort(line)
357
+ for i in range(len(space.line_info)):
358
+ if line[0] in space.line_info[i] and line[1] in space.line_info[i]:
359
+ self.line.append(i)
360
+ def join(line):
361
+ global space
362
+ line = line_sort(line)
363
+ space.line_info.append(line)
364
+ space.command.append(line)
365
+ def show():
366
+ global space
367
+ space.show_diagram()
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: geometryai
3
+ Version: 0.0.5
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.5",
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
+ )