trianglengin 1.0.6__py3-none-any.whl
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.
- tests/__init__.py +0 -0
- tests/conftest.py +108 -0
- tests/core/__init__.py +2 -0
- tests/core/environment/README.md +47 -0
- tests/core/environment/__init__.py +2 -0
- tests/core/environment/test_action_codec.py +50 -0
- tests/core/environment/test_game_state.py +483 -0
- tests/core/environment/test_grid_data.py +205 -0
- tests/core/environment/test_grid_logic.py +362 -0
- tests/core/environment/test_shape_logic.py +171 -0
- tests/core/environment/test_step.py +372 -0
- tests/core/structs/__init__.py +0 -0
- tests/core/structs/test_shape.py +83 -0
- tests/core/structs/test_triangle.py +97 -0
- tests/utils/__init__.py +0 -0
- tests/utils/test_geometry.py +93 -0
- trianglengin/__init__.py +18 -0
- trianglengin/app.py +110 -0
- trianglengin/cli.py +134 -0
- trianglengin/config/__init__.py +9 -0
- trianglengin/config/display_config.py +47 -0
- trianglengin/config/env_config.py +103 -0
- trianglengin/core/__init__.py +8 -0
- trianglengin/core/environment/__init__.py +31 -0
- trianglengin/core/environment/action_codec.py +37 -0
- trianglengin/core/environment/game_state.py +217 -0
- trianglengin/core/environment/grid/README.md +46 -0
- trianglengin/core/environment/grid/__init__.py +18 -0
- trianglengin/core/environment/grid/grid_data.py +140 -0
- trianglengin/core/environment/grid/line_cache.py +189 -0
- trianglengin/core/environment/grid/logic.py +131 -0
- trianglengin/core/environment/logic/__init__.py +3 -0
- trianglengin/core/environment/logic/actions.py +38 -0
- trianglengin/core/environment/logic/step.py +134 -0
- trianglengin/core/environment/shapes/__init__.py +19 -0
- trianglengin/core/environment/shapes/logic.py +84 -0
- trianglengin/core/environment/shapes/templates.py +587 -0
- trianglengin/core/structs/__init__.py +27 -0
- trianglengin/core/structs/constants.py +28 -0
- trianglengin/core/structs/shape.py +61 -0
- trianglengin/core/structs/triangle.py +48 -0
- trianglengin/interaction/README.md +45 -0
- trianglengin/interaction/__init__.py +17 -0
- trianglengin/interaction/debug_mode_handler.py +96 -0
- trianglengin/interaction/event_processor.py +43 -0
- trianglengin/interaction/input_handler.py +82 -0
- trianglengin/interaction/play_mode_handler.py +141 -0
- trianglengin/utils/__init__.py +9 -0
- trianglengin/utils/geometry.py +73 -0
- trianglengin/utils/types.py +10 -0
- trianglengin/visualization/README.md +44 -0
- trianglengin/visualization/__init__.py +61 -0
- trianglengin/visualization/core/README.md +52 -0
- trianglengin/visualization/core/__init__.py +12 -0
- trianglengin/visualization/core/colors.py +117 -0
- trianglengin/visualization/core/coord_mapper.py +73 -0
- trianglengin/visualization/core/fonts.py +55 -0
- trianglengin/visualization/core/layout.py +101 -0
- trianglengin/visualization/core/visualizer.py +232 -0
- trianglengin/visualization/drawing/README.md +45 -0
- trianglengin/visualization/drawing/__init__.py +30 -0
- trianglengin/visualization/drawing/grid.py +156 -0
- trianglengin/visualization/drawing/highlight.py +30 -0
- trianglengin/visualization/drawing/hud.py +39 -0
- trianglengin/visualization/drawing/previews.py +172 -0
- trianglengin/visualization/drawing/shapes.py +36 -0
- trianglengin-1.0.6.dist-info/METADATA +367 -0
- trianglengin-1.0.6.dist-info/RECORD +72 -0
- trianglengin-1.0.6.dist-info/WHEEL +5 -0
- trianglengin-1.0.6.dist-info/entry_points.txt +2 -0
- trianglengin-1.0.6.dist-info/licenses/LICENSE +22 -0
- trianglengin-1.0.6.dist-info/top_level.txt +2 -0
@@ -0,0 +1,587 @@
|
|
1
|
+
# File: trianglengin/core/environment/shapes/templates.py
|
2
|
+
# ==============================================================================
|
3
|
+
# == PREDEFINED SHAPE TEMPLATES ==
|
4
|
+
# == ==
|
5
|
+
# == DO NOT MODIFY THIS LIST MANUALLY unless you are absolutely sure! ==
|
6
|
+
# == These shapes are fundamental to the game's design and balance. ==
|
7
|
+
# == Modifying them can have unintended consequences on gameplay and agent ==
|
8
|
+
# == training. ==
|
9
|
+
# ==============================================================================
|
10
|
+
|
11
|
+
# List of predefined shape templates. Each template is a list of relative triangle coordinates (dr, dc, is_up).
|
12
|
+
# Coordinates are relative to the shape's origin (typically the top-leftmost triangle).
|
13
|
+
# is_up = True for upward-pointing triangle, False for downward-pointing.
|
14
|
+
PREDEFINED_SHAPE_TEMPLATES: list[list[tuple[int, int, bool]]] = [
|
15
|
+
[ # Shape 1
|
16
|
+
(
|
17
|
+
0,
|
18
|
+
0,
|
19
|
+
True,
|
20
|
+
)
|
21
|
+
],
|
22
|
+
[ # Shape 1
|
23
|
+
(
|
24
|
+
0,
|
25
|
+
0,
|
26
|
+
True,
|
27
|
+
)
|
28
|
+
],
|
29
|
+
[ # Shape 2
|
30
|
+
(
|
31
|
+
0,
|
32
|
+
0,
|
33
|
+
True,
|
34
|
+
),
|
35
|
+
(
|
36
|
+
1,
|
37
|
+
0,
|
38
|
+
False,
|
39
|
+
),
|
40
|
+
],
|
41
|
+
[ # Shape 2
|
42
|
+
(
|
43
|
+
0,
|
44
|
+
0,
|
45
|
+
True,
|
46
|
+
),
|
47
|
+
(
|
48
|
+
1,
|
49
|
+
0,
|
50
|
+
False,
|
51
|
+
),
|
52
|
+
],
|
53
|
+
[ # Shape 3
|
54
|
+
(
|
55
|
+
0,
|
56
|
+
0,
|
57
|
+
False,
|
58
|
+
)
|
59
|
+
],
|
60
|
+
[ # Shape 4
|
61
|
+
(
|
62
|
+
0,
|
63
|
+
0,
|
64
|
+
True,
|
65
|
+
),
|
66
|
+
(
|
67
|
+
0,
|
68
|
+
1,
|
69
|
+
False,
|
70
|
+
),
|
71
|
+
],
|
72
|
+
[ # Shape 4
|
73
|
+
(
|
74
|
+
0,
|
75
|
+
0,
|
76
|
+
True,
|
77
|
+
),
|
78
|
+
(
|
79
|
+
0,
|
80
|
+
1,
|
81
|
+
False,
|
82
|
+
),
|
83
|
+
],
|
84
|
+
[ # Shape 5
|
85
|
+
(
|
86
|
+
0,
|
87
|
+
0,
|
88
|
+
False,
|
89
|
+
),
|
90
|
+
(
|
91
|
+
0,
|
92
|
+
1,
|
93
|
+
True,
|
94
|
+
),
|
95
|
+
],
|
96
|
+
[ # Shape 5
|
97
|
+
(
|
98
|
+
0,
|
99
|
+
0,
|
100
|
+
False,
|
101
|
+
),
|
102
|
+
(
|
103
|
+
0,
|
104
|
+
1,
|
105
|
+
True,
|
106
|
+
),
|
107
|
+
],
|
108
|
+
[ # Shape 6
|
109
|
+
(
|
110
|
+
0,
|
111
|
+
0,
|
112
|
+
True,
|
113
|
+
),
|
114
|
+
(
|
115
|
+
0,
|
116
|
+
1,
|
117
|
+
False,
|
118
|
+
),
|
119
|
+
(
|
120
|
+
0,
|
121
|
+
2,
|
122
|
+
True,
|
123
|
+
),
|
124
|
+
],
|
125
|
+
[ # Shape 7
|
126
|
+
(
|
127
|
+
0,
|
128
|
+
0,
|
129
|
+
False,
|
130
|
+
),
|
131
|
+
(
|
132
|
+
0,
|
133
|
+
1,
|
134
|
+
True,
|
135
|
+
),
|
136
|
+
(
|
137
|
+
0,
|
138
|
+
2,
|
139
|
+
False,
|
140
|
+
),
|
141
|
+
],
|
142
|
+
[ # Shape 8
|
143
|
+
(
|
144
|
+
0,
|
145
|
+
0,
|
146
|
+
True,
|
147
|
+
),
|
148
|
+
(
|
149
|
+
0,
|
150
|
+
1,
|
151
|
+
False,
|
152
|
+
),
|
153
|
+
(
|
154
|
+
0,
|
155
|
+
2,
|
156
|
+
True,
|
157
|
+
),
|
158
|
+
(
|
159
|
+
1,
|
160
|
+
0,
|
161
|
+
False,
|
162
|
+
),
|
163
|
+
],
|
164
|
+
[ # Shape 9
|
165
|
+
(
|
166
|
+
0,
|
167
|
+
0,
|
168
|
+
True,
|
169
|
+
),
|
170
|
+
(
|
171
|
+
0,
|
172
|
+
1,
|
173
|
+
False,
|
174
|
+
),
|
175
|
+
(
|
176
|
+
0,
|
177
|
+
2,
|
178
|
+
True,
|
179
|
+
),
|
180
|
+
(
|
181
|
+
1,
|
182
|
+
2,
|
183
|
+
False,
|
184
|
+
),
|
185
|
+
],
|
186
|
+
[ # Shape 10
|
187
|
+
(
|
188
|
+
0,
|
189
|
+
0,
|
190
|
+
False,
|
191
|
+
),
|
192
|
+
(
|
193
|
+
0,
|
194
|
+
1,
|
195
|
+
True,
|
196
|
+
),
|
197
|
+
(
|
198
|
+
1,
|
199
|
+
0,
|
200
|
+
True,
|
201
|
+
),
|
202
|
+
(
|
203
|
+
1,
|
204
|
+
1,
|
205
|
+
False,
|
206
|
+
),
|
207
|
+
],
|
208
|
+
[ # Shape 11
|
209
|
+
(
|
210
|
+
0,
|
211
|
+
0,
|
212
|
+
True,
|
213
|
+
),
|
214
|
+
(
|
215
|
+
0,
|
216
|
+
2,
|
217
|
+
True,
|
218
|
+
),
|
219
|
+
(
|
220
|
+
1,
|
221
|
+
0,
|
222
|
+
False,
|
223
|
+
),
|
224
|
+
(
|
225
|
+
1,
|
226
|
+
1,
|
227
|
+
True,
|
228
|
+
),
|
229
|
+
(
|
230
|
+
1,
|
231
|
+
2,
|
232
|
+
False,
|
233
|
+
),
|
234
|
+
],
|
235
|
+
[ # Shape 12
|
236
|
+
(
|
237
|
+
0,
|
238
|
+
0,
|
239
|
+
True,
|
240
|
+
),
|
241
|
+
(
|
242
|
+
1,
|
243
|
+
-2,
|
244
|
+
False,
|
245
|
+
),
|
246
|
+
(
|
247
|
+
1,
|
248
|
+
-1,
|
249
|
+
True,
|
250
|
+
),
|
251
|
+
(
|
252
|
+
1,
|
253
|
+
0,
|
254
|
+
False,
|
255
|
+
),
|
256
|
+
],
|
257
|
+
[ # Shape 13
|
258
|
+
(
|
259
|
+
0,
|
260
|
+
0,
|
261
|
+
True,
|
262
|
+
),
|
263
|
+
(
|
264
|
+
0,
|
265
|
+
1,
|
266
|
+
False,
|
267
|
+
),
|
268
|
+
(
|
269
|
+
1,
|
270
|
+
0,
|
271
|
+
False,
|
272
|
+
),
|
273
|
+
(
|
274
|
+
1,
|
275
|
+
1,
|
276
|
+
True,
|
277
|
+
),
|
278
|
+
],
|
279
|
+
[ # Shape 14
|
280
|
+
(
|
281
|
+
0,
|
282
|
+
0,
|
283
|
+
True,
|
284
|
+
),
|
285
|
+
(
|
286
|
+
0,
|
287
|
+
1,
|
288
|
+
False,
|
289
|
+
),
|
290
|
+
(
|
291
|
+
1,
|
292
|
+
0,
|
293
|
+
False,
|
294
|
+
),
|
295
|
+
(
|
296
|
+
1,
|
297
|
+
1,
|
298
|
+
True,
|
299
|
+
),
|
300
|
+
(
|
301
|
+
1,
|
302
|
+
2,
|
303
|
+
False,
|
304
|
+
),
|
305
|
+
],
|
306
|
+
[ # Shape 15
|
307
|
+
(
|
308
|
+
0,
|
309
|
+
0,
|
310
|
+
True,
|
311
|
+
),
|
312
|
+
(
|
313
|
+
0,
|
314
|
+
1,
|
315
|
+
False,
|
316
|
+
),
|
317
|
+
(
|
318
|
+
0,
|
319
|
+
2,
|
320
|
+
True,
|
321
|
+
),
|
322
|
+
(
|
323
|
+
1,
|
324
|
+
0,
|
325
|
+
False,
|
326
|
+
),
|
327
|
+
(
|
328
|
+
1,
|
329
|
+
1,
|
330
|
+
True,
|
331
|
+
),
|
332
|
+
],
|
333
|
+
[ # Shape 16
|
334
|
+
(
|
335
|
+
0,
|
336
|
+
0,
|
337
|
+
True,
|
338
|
+
),
|
339
|
+
(
|
340
|
+
0,
|
341
|
+
1,
|
342
|
+
False,
|
343
|
+
),
|
344
|
+
(
|
345
|
+
0,
|
346
|
+
2,
|
347
|
+
True,
|
348
|
+
),
|
349
|
+
(
|
350
|
+
1,
|
351
|
+
0,
|
352
|
+
False,
|
353
|
+
),
|
354
|
+
(
|
355
|
+
1,
|
356
|
+
2,
|
357
|
+
False,
|
358
|
+
),
|
359
|
+
],
|
360
|
+
[ # Shape 17
|
361
|
+
(
|
362
|
+
0,
|
363
|
+
0,
|
364
|
+
True,
|
365
|
+
),
|
366
|
+
(
|
367
|
+
0,
|
368
|
+
1,
|
369
|
+
False,
|
370
|
+
),
|
371
|
+
(
|
372
|
+
0,
|
373
|
+
2,
|
374
|
+
True,
|
375
|
+
),
|
376
|
+
(
|
377
|
+
1,
|
378
|
+
1,
|
379
|
+
True,
|
380
|
+
),
|
381
|
+
(
|
382
|
+
1,
|
383
|
+
2,
|
384
|
+
False,
|
385
|
+
),
|
386
|
+
],
|
387
|
+
[ # Shape 18
|
388
|
+
(
|
389
|
+
0,
|
390
|
+
0,
|
391
|
+
True,
|
392
|
+
),
|
393
|
+
(
|
394
|
+
0,
|
395
|
+
2,
|
396
|
+
True,
|
397
|
+
),
|
398
|
+
(
|
399
|
+
1,
|
400
|
+
0,
|
401
|
+
False,
|
402
|
+
),
|
403
|
+
(
|
404
|
+
1,
|
405
|
+
1,
|
406
|
+
True,
|
407
|
+
),
|
408
|
+
(
|
409
|
+
1,
|
410
|
+
2,
|
411
|
+
False,
|
412
|
+
),
|
413
|
+
],
|
414
|
+
[ # Shape 19
|
415
|
+
(
|
416
|
+
0,
|
417
|
+
0,
|
418
|
+
True,
|
419
|
+
),
|
420
|
+
(
|
421
|
+
0,
|
422
|
+
1,
|
423
|
+
False,
|
424
|
+
),
|
425
|
+
(
|
426
|
+
1,
|
427
|
+
0,
|
428
|
+
False,
|
429
|
+
),
|
430
|
+
(
|
431
|
+
1,
|
432
|
+
1,
|
433
|
+
True,
|
434
|
+
),
|
435
|
+
(
|
436
|
+
1,
|
437
|
+
2,
|
438
|
+
False,
|
439
|
+
),
|
440
|
+
],
|
441
|
+
[ # Shape 20
|
442
|
+
(
|
443
|
+
0,
|
444
|
+
0,
|
445
|
+
False,
|
446
|
+
),
|
447
|
+
(
|
448
|
+
0,
|
449
|
+
1,
|
450
|
+
True,
|
451
|
+
),
|
452
|
+
(
|
453
|
+
1,
|
454
|
+
1,
|
455
|
+
False,
|
456
|
+
),
|
457
|
+
],
|
458
|
+
[ # Shape 21
|
459
|
+
(
|
460
|
+
0,
|
461
|
+
0,
|
462
|
+
True,
|
463
|
+
),
|
464
|
+
(
|
465
|
+
1,
|
466
|
+
-1,
|
467
|
+
True,
|
468
|
+
),
|
469
|
+
(
|
470
|
+
1,
|
471
|
+
0,
|
472
|
+
False,
|
473
|
+
),
|
474
|
+
],
|
475
|
+
[ # Shape 22
|
476
|
+
(
|
477
|
+
0,
|
478
|
+
0,
|
479
|
+
True,
|
480
|
+
),
|
481
|
+
(
|
482
|
+
1,
|
483
|
+
0,
|
484
|
+
False,
|
485
|
+
),
|
486
|
+
(
|
487
|
+
1,
|
488
|
+
1,
|
489
|
+
True,
|
490
|
+
),
|
491
|
+
],
|
492
|
+
[ # Shape 23
|
493
|
+
(
|
494
|
+
0,
|
495
|
+
0,
|
496
|
+
True,
|
497
|
+
),
|
498
|
+
(
|
499
|
+
1,
|
500
|
+
-1,
|
501
|
+
True,
|
502
|
+
),
|
503
|
+
(
|
504
|
+
1,
|
505
|
+
0,
|
506
|
+
False,
|
507
|
+
),
|
508
|
+
(
|
509
|
+
1,
|
510
|
+
1,
|
511
|
+
True,
|
512
|
+
),
|
513
|
+
],
|
514
|
+
[ # Shape 24
|
515
|
+
(
|
516
|
+
0,
|
517
|
+
0,
|
518
|
+
True,
|
519
|
+
),
|
520
|
+
(
|
521
|
+
1,
|
522
|
+
-1,
|
523
|
+
True,
|
524
|
+
),
|
525
|
+
(
|
526
|
+
1,
|
527
|
+
0,
|
528
|
+
False,
|
529
|
+
),
|
530
|
+
],
|
531
|
+
[ # Shape 25
|
532
|
+
(
|
533
|
+
0,
|
534
|
+
0,
|
535
|
+
False,
|
536
|
+
),
|
537
|
+
(
|
538
|
+
0,
|
539
|
+
1,
|
540
|
+
True,
|
541
|
+
),
|
542
|
+
(
|
543
|
+
0,
|
544
|
+
2,
|
545
|
+
False,
|
546
|
+
),
|
547
|
+
(
|
548
|
+
1,
|
549
|
+
1,
|
550
|
+
False,
|
551
|
+
),
|
552
|
+
],
|
553
|
+
[ # Shape 26
|
554
|
+
(
|
555
|
+
0,
|
556
|
+
0,
|
557
|
+
False,
|
558
|
+
),
|
559
|
+
(
|
560
|
+
0,
|
561
|
+
1,
|
562
|
+
True,
|
563
|
+
),
|
564
|
+
(
|
565
|
+
1,
|
566
|
+
1,
|
567
|
+
False,
|
568
|
+
),
|
569
|
+
],
|
570
|
+
[ # Shape 27
|
571
|
+
(
|
572
|
+
0,
|
573
|
+
0,
|
574
|
+
True,
|
575
|
+
),
|
576
|
+
(
|
577
|
+
0,
|
578
|
+
1,
|
579
|
+
False,
|
580
|
+
),
|
581
|
+
(
|
582
|
+
1,
|
583
|
+
0,
|
584
|
+
False,
|
585
|
+
),
|
586
|
+
],
|
587
|
+
]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# File: trianglengin/core/structs/__init__.py
|
2
|
+
"""
|
3
|
+
Module for core data structures used across different parts of the application,
|
4
|
+
like environment, visualization, and features. Helps avoid circular dependencies.
|
5
|
+
"""
|
6
|
+
|
7
|
+
# Correctly export constants from the constants submodule
|
8
|
+
from .constants import (
|
9
|
+
COLOR_ID_MAP,
|
10
|
+
COLOR_TO_ID_MAP,
|
11
|
+
DEBUG_COLOR_ID,
|
12
|
+
NO_COLOR_ID,
|
13
|
+
SHAPE_COLORS,
|
14
|
+
)
|
15
|
+
from .shape import Shape
|
16
|
+
from .triangle import Triangle
|
17
|
+
|
18
|
+
__all__ = [
|
19
|
+
"Triangle",
|
20
|
+
"Shape",
|
21
|
+
# Exported Constants
|
22
|
+
"SHAPE_COLORS",
|
23
|
+
"NO_COLOR_ID",
|
24
|
+
"DEBUG_COLOR_ID",
|
25
|
+
"COLOR_ID_MAP",
|
26
|
+
"COLOR_TO_ID_MAP",
|
27
|
+
]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
SHAPE_COLORS: list[tuple[int, int, int]] = [
|
2
|
+
(220, 40, 40), # 0: Red
|
3
|
+
(60, 60, 220), # 1: Blue
|
4
|
+
(40, 200, 40), # 2: Green
|
5
|
+
(230, 230, 40), # 3: Yellow
|
6
|
+
(240, 150, 20), # 4: Orange
|
7
|
+
(140, 40, 140), # 5: Purple
|
8
|
+
(40, 200, 200), # 6: Cyan
|
9
|
+
(200, 100, 180), # 7: Pink (Example addition)
|
10
|
+
(100, 180, 200), # 8: Light Blue (Example addition)
|
11
|
+
]
|
12
|
+
|
13
|
+
# --- NumPy GridData Color Representation ---
|
14
|
+
# ID for empty cells in the _color_id_np array
|
15
|
+
NO_COLOR_ID: int = -1
|
16
|
+
# ID for debug-toggled cells
|
17
|
+
DEBUG_COLOR_ID: int = -2
|
18
|
+
|
19
|
+
# Mapping from Color ID (int >= 0) to RGB tuple.
|
20
|
+
# Index 0 corresponds to SHAPE_COLORS[0], etc.
|
21
|
+
# This list is used by visualization to get the RGB from the ID.
|
22
|
+
COLOR_ID_MAP: list[tuple[int, int, int]] = SHAPE_COLORS
|
23
|
+
|
24
|
+
# Reverse mapping for efficient lookup during placement (Color Tuple -> ID)
|
25
|
+
# Note: Ensure SHAPE_COLORS have unique tuples.
|
26
|
+
COLOR_TO_ID_MAP: dict[tuple[int, int, int], int] = {
|
27
|
+
color: i for i, color in enumerate(COLOR_ID_MAP)
|
28
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# File: trianglengin/core/structs/shape.py
|
2
|
+
from __future__ import annotations
|
3
|
+
|
4
|
+
import logging
|
5
|
+
from typing import cast
|
6
|
+
|
7
|
+
logger = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
|
10
|
+
class Shape:
|
11
|
+
"""Represents a polyomino-like shape made of triangles."""
|
12
|
+
|
13
|
+
def __init__(
|
14
|
+
self, triangles: list[tuple[int, int, bool]], color: tuple[int, int, int]
|
15
|
+
):
|
16
|
+
# Ensure triangles are tuples and sort them for consistent representation
|
17
|
+
# Sorting is based on (row, col) primarily
|
18
|
+
try:
|
19
|
+
# Explicitly cast inner tuples to the correct type for mypy
|
20
|
+
processed_triangles = [
|
21
|
+
cast("tuple[int, int, bool]", tuple(t)) for t in triangles
|
22
|
+
]
|
23
|
+
self.triangles: list[tuple[int, int, bool]] = sorted(processed_triangles)
|
24
|
+
except Exception as e:
|
25
|
+
logger.error(f"Failed to sort triangles: {triangles}. Error: {e}")
|
26
|
+
# Fallback or re-raise depending on desired behavior
|
27
|
+
self.triangles = [
|
28
|
+
cast("tuple[int, int, bool]", tuple(t)) for t in triangles
|
29
|
+
] # Store as is if sort fails
|
30
|
+
|
31
|
+
self.color: tuple[int, int, int] = color
|
32
|
+
|
33
|
+
def bbox(self) -> tuple[int, int, int, int]:
|
34
|
+
"""Calculates bounding box (min_r, min_c, max_r, max_c) in relative coords."""
|
35
|
+
if not self.triangles:
|
36
|
+
return (0, 0, 0, 0)
|
37
|
+
rows = [t[0] for t in self.triangles]
|
38
|
+
cols = [t[1] for t in self.triangles]
|
39
|
+
return (min(rows), min(cols), max(rows), max(cols))
|
40
|
+
|
41
|
+
def copy(self) -> Shape:
|
42
|
+
"""Creates a shallow copy (triangle list is copied, color is shared)."""
|
43
|
+
new_shape = Shape.__new__(Shape)
|
44
|
+
new_shape.triangles = list(self.triangles)
|
45
|
+
new_shape.color = self.color
|
46
|
+
return new_shape
|
47
|
+
|
48
|
+
def __str__(self) -> str:
|
49
|
+
return f"Shape(Color:{self.color}, Tris:{len(self.triangles)})"
|
50
|
+
|
51
|
+
def __eq__(self, other: object) -> bool:
|
52
|
+
"""Checks for equality based on triangles and color."""
|
53
|
+
if not isinstance(other, Shape):
|
54
|
+
return NotImplemented
|
55
|
+
# Compare sorted lists of tuples
|
56
|
+
return self.triangles == other.triangles and self.color == other.color
|
57
|
+
|
58
|
+
def __hash__(self) -> int:
|
59
|
+
"""Allows shapes to be used in sets/dicts if needed."""
|
60
|
+
# Hash the tuple representation of the sorted list
|
61
|
+
return hash((tuple(self.triangles), self.color))
|