halfedge 0.3.0__tar.gz → 0.4.0__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.
- {halfedge-0.3.0 → halfedge-0.4.0}/.github/workflows/pypi-project.yml +21 -21
- {halfedge-0.3.0/src/halfedge.egg-info → halfedge-0.4.0}/PKG-INFO +1 -1
- {halfedge-0.3.0 → halfedge-0.4.0}/pyproject.toml +2 -2
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge/__init__.py +6 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge/type_attrib.py +223 -1
- {halfedge-0.3.0 → halfedge-0.4.0/src/halfedge.egg-info}/PKG-INFO +1 -1
- {halfedge-0.3.0 → halfedge-0.4.0}/.gitignore +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/.pre-commit-config.yaml +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/README.md +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/setup.cfg +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge/half_edge_constructors.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge/half_edge_elements.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge/half_edge_object.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge/half_edge_querries.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge/py.typed +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge/validations.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge.egg-info/SOURCES.txt +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge.egg-info/dependency_links.txt +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge.egg-info/requires.txt +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/src/halfedge.egg-info/top_level.txt +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/tests/__init__.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/tests/conftest.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/tests/test_classes.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/tests/test_constructors.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/tests/test_elements.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/tests/test_object_pickups.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/tests/test_operations.py +0 -0
- {halfedge-0.3.0 → halfedge-0.4.0}/tests/test_validations.py +0 -0
|
@@ -38,27 +38,27 @@ jobs:
|
|
|
38
38
|
run: |
|
|
39
39
|
pytest
|
|
40
40
|
|
|
41
|
-
# If the tests pass, try to bump the version number. If no bump is warranted,
|
|
42
|
-
# pass silently.
|
|
43
|
-
bump_version:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
41
|
+
# # If the tests pass, try to bump the version number. If no bump is warranted,
|
|
42
|
+
# # pass silently.
|
|
43
|
+
# bump_version:
|
|
44
|
+
# runs-on: ubuntu-latest
|
|
45
|
+
# name: "Bump version and create changelog with commitizen"
|
|
46
|
+
# continue-on-error: true
|
|
47
|
+
# needs: [tests]
|
|
48
|
+
# if: github.ref == 'refs/heads/dev'
|
|
49
|
+
# steps:
|
|
50
|
+
# - name: Check out
|
|
51
|
+
# uses: actions/checkout@v4
|
|
52
|
+
# with:
|
|
53
|
+
# fetch-depth: 0
|
|
54
|
+
# token: "${{ secrets.COMMITIZEN_BUMP }}"
|
|
55
|
+
# - id: cz
|
|
56
|
+
# name: Create bump and changelog
|
|
57
|
+
# uses: commitizen-tools/commitizen-action@master
|
|
58
|
+
# with:
|
|
59
|
+
# github_token: ${{ secrets.COMMITIZEN_BUMP }}
|
|
60
|
+
# - name: Print Version
|
|
61
|
+
# run: echo "Bumped to version ${{ steps.cz.outputs.version }}"
|
|
62
62
|
|
|
63
63
|
# Deploy on test.pypi when branch is dev and commit message starts with 'bump'
|
|
64
64
|
deploy-on-testpypi:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "halfedge"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "A typical half-edge data structure with some padding"
|
|
5
5
|
authors = [{ name = "Shay Hill", email = "shay_public@hotmail.com" }]
|
|
6
6
|
license = {text = "MIT"}
|
|
@@ -25,7 +25,7 @@ build-backend = "setuptools.build_meta"
|
|
|
25
25
|
|
|
26
26
|
[tool.commitizen]
|
|
27
27
|
name = "cz_conventional_commits"
|
|
28
|
-
version = "0.
|
|
28
|
+
version = "0.4.0"
|
|
29
29
|
tag_format = "$version"
|
|
30
30
|
major-version-zero = true
|
|
31
31
|
version_files = ["pyproject.toml:^version"]
|
|
@@ -6,10 +6,13 @@ from halfedge.half_edge_object import HalfEdges
|
|
|
6
6
|
from halfedge.type_attrib import (
|
|
7
7
|
Attrib,
|
|
8
8
|
ContagionAttrib,
|
|
9
|
+
EdgeAttrib,
|
|
10
|
+
FaceAttrib,
|
|
9
11
|
IncompatibleAttrib,
|
|
10
12
|
NumericAttrib,
|
|
11
13
|
Vector2Attrib,
|
|
12
14
|
Vector3Attrib,
|
|
15
|
+
VertAttrib,
|
|
13
16
|
)
|
|
14
17
|
|
|
15
18
|
__all__ = [
|
|
@@ -17,7 +20,9 @@ __all__ = [
|
|
|
17
20
|
"BlindHalfEdges",
|
|
18
21
|
"ContagionAttrib",
|
|
19
22
|
"Edge",
|
|
23
|
+
"EdgeAttrib",
|
|
20
24
|
"Face",
|
|
25
|
+
"FaceAttrib",
|
|
21
26
|
"HalfEdges",
|
|
22
27
|
"IncompatibleAttrib",
|
|
23
28
|
"MeshElementBase",
|
|
@@ -25,4 +30,5 @@ __all__ = [
|
|
|
25
30
|
"Vector2Attrib",
|
|
26
31
|
"Vector3Attrib",
|
|
27
32
|
"Vert",
|
|
33
|
+
"VertAttrib",
|
|
28
34
|
]
|
|
@@ -67,7 +67,7 @@ from paragraphs import par
|
|
|
67
67
|
|
|
68
68
|
if TYPE_CHECKING:
|
|
69
69
|
from halfedge.half_edge_constructors import BlindHalfEdges
|
|
70
|
-
from halfedge.half_edge_elements import MeshElementBase
|
|
70
|
+
from halfedge.half_edge_elements import Edge, Face, MeshElementBase, Vert
|
|
71
71
|
|
|
72
72
|
_T = TypeVar("_T")
|
|
73
73
|
|
|
@@ -312,6 +312,228 @@ class Attrib(Generic[_T]):
|
|
|
312
312
|
_TAttrib = TypeVar("_TAttrib", bound=Attrib[Any])
|
|
313
313
|
|
|
314
314
|
|
|
315
|
+
class VertAttrib(Generic[_T]):
|
|
316
|
+
"""Base class for Vert attributes."""
|
|
317
|
+
|
|
318
|
+
__slots__ = ("_value", "vert")
|
|
319
|
+
|
|
320
|
+
def __new__(
|
|
321
|
+
cls: type[_TVertAttrib], value: _T | None = None, vert: Vert | None = None
|
|
322
|
+
) -> _TVertAttrib:
|
|
323
|
+
"""Raise an exception if the attribute is not subclassed."""
|
|
324
|
+
del value
|
|
325
|
+
del vert
|
|
326
|
+
if cls is VertAttrib:
|
|
327
|
+
msg = "VertAttrib is an abstract class and cannot be instantiated."
|
|
328
|
+
raise TypeError(msg)
|
|
329
|
+
return object.__new__(cls)
|
|
330
|
+
|
|
331
|
+
def __init__(self, value: _T | None = None, vert: Vert | None = None) -> None:
|
|
332
|
+
"""Set value and vert."""
|
|
333
|
+
self._value = value
|
|
334
|
+
self.vert = vert
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def value(self) -> _T:
|
|
338
|
+
"""Return value if set, else try to infer a value.
|
|
339
|
+
|
|
340
|
+
:return: Value of the attribute
|
|
341
|
+
:raises AttributeError: If no value is set and _infer_value fails
|
|
342
|
+
"""
|
|
343
|
+
if self._value is not None:
|
|
344
|
+
return self._value
|
|
345
|
+
with suppress(NotImplementedError, ValueError):
|
|
346
|
+
value = self._infer_value()
|
|
347
|
+
self._value = value
|
|
348
|
+
return self._value
|
|
349
|
+
msg = "no value set and failed to infer from 'self.vert'"
|
|
350
|
+
raise AttributeError(msg)
|
|
351
|
+
|
|
352
|
+
def copy_to_vert(self: VertAttrib[_T], vert: Vert) -> VertAttrib[_T]:
|
|
353
|
+
"""Return a new instance with the same value, assigned to a new vert.
|
|
354
|
+
|
|
355
|
+
:param vert: New vert
|
|
356
|
+
:return: VertAttrib instance
|
|
357
|
+
"""
|
|
358
|
+
return type(self)(self._value, vert)
|
|
359
|
+
|
|
360
|
+
@classmethod
|
|
361
|
+
def merge(cls, *merge_from: _TVertAttrib | None) -> _TVertAttrib | None:
|
|
362
|
+
"""Get value of self from self._merge_from.
|
|
363
|
+
|
|
364
|
+
:param merge_from: VertAttrib instances to merge (all of the same class)
|
|
365
|
+
:return: VertAttrib instance or None
|
|
366
|
+
"""
|
|
367
|
+
_ = merge_from
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
def split(self: _TVertAttrib) -> _TVertAttrib | None:
|
|
371
|
+
"""Define how attribute will be passed when dividing self.vert.
|
|
372
|
+
|
|
373
|
+
:return: VertAttrib instance or None
|
|
374
|
+
"""
|
|
375
|
+
return None
|
|
376
|
+
|
|
377
|
+
def _infer_value(self) -> _T:
|
|
378
|
+
"""Get value of self from self._vert."""
|
|
379
|
+
msg = par(
|
|
380
|
+
f"""'{type(self).__name__}' has no provision for inferring a value from
|
|
381
|
+
'self.vert'"""
|
|
382
|
+
)
|
|
383
|
+
raise AttributeError(msg)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
_TVertAttrib = TypeVar("_TVertAttrib", bound=VertAttrib[Any])
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class EdgeAttrib(Generic[_T]):
|
|
390
|
+
"""Base class for Edge attributes."""
|
|
391
|
+
|
|
392
|
+
__slots__ = ("_value", "edge")
|
|
393
|
+
|
|
394
|
+
def __new__(
|
|
395
|
+
cls: type[_TEdgeAttrib], value: _T | None = None, edge: Edge | None = None
|
|
396
|
+
) -> _TEdgeAttrib:
|
|
397
|
+
"""Raise an exception if the attribute is not subclassed."""
|
|
398
|
+
del value
|
|
399
|
+
del edge
|
|
400
|
+
if cls is EdgeAttrib:
|
|
401
|
+
msg = "EdgeAttrib is an abstract class and cannot be instantiated."
|
|
402
|
+
raise TypeError(msg)
|
|
403
|
+
return object.__new__(cls)
|
|
404
|
+
|
|
405
|
+
def __init__(self, value: _T | None = None, edge: Edge | None = None) -> None:
|
|
406
|
+
"""Set value and edge."""
|
|
407
|
+
self._value = value
|
|
408
|
+
self.edge = edge
|
|
409
|
+
|
|
410
|
+
@property
|
|
411
|
+
def value(self) -> _T:
|
|
412
|
+
"""Return value if set, else try to infer a value.
|
|
413
|
+
|
|
414
|
+
:return: Value of the attribute
|
|
415
|
+
:raises AttributeError: If no value is set and _infer_value fails
|
|
416
|
+
"""
|
|
417
|
+
if self._value is not None:
|
|
418
|
+
return self._value
|
|
419
|
+
with suppress(NotImplementedError, ValueError):
|
|
420
|
+
value = self._infer_value()
|
|
421
|
+
self._value = value
|
|
422
|
+
return self._value
|
|
423
|
+
msg = "no value set and failed to infer from 'self.edge'"
|
|
424
|
+
raise AttributeError(msg)
|
|
425
|
+
|
|
426
|
+
def copy_to_edge(self: EdgeAttrib[_T], edge: Edge) -> EdgeAttrib[_T]:
|
|
427
|
+
"""Return a new instance with the same value, assigned to a new edge.
|
|
428
|
+
|
|
429
|
+
:param edge: New edge
|
|
430
|
+
:return: EdgeAttrib instance
|
|
431
|
+
"""
|
|
432
|
+
return type(self)(self._value, edge)
|
|
433
|
+
|
|
434
|
+
@classmethod
|
|
435
|
+
def merge(cls, *merge_from: _TEdgeAttrib | None) -> _TEdgeAttrib | None:
|
|
436
|
+
"""Get value of self from self._merge_from.
|
|
437
|
+
|
|
438
|
+
:param merge_from: EdgeAttrib instances to merge (all of the same class)
|
|
439
|
+
:return: EdgeAttrib instance or None
|
|
440
|
+
"""
|
|
441
|
+
_ = merge_from
|
|
442
|
+
return None
|
|
443
|
+
|
|
444
|
+
def split(self: _TEdgeAttrib) -> _TEdgeAttrib | None:
|
|
445
|
+
"""Define how attribute will be passed when dividing self.edge.
|
|
446
|
+
|
|
447
|
+
:return: EdgeAttrib instance or None
|
|
448
|
+
"""
|
|
449
|
+
return None
|
|
450
|
+
|
|
451
|
+
def _infer_value(self) -> _T:
|
|
452
|
+
"""Get value of self from self._edge."""
|
|
453
|
+
msg = par(
|
|
454
|
+
f"""'{type(self).__name__}' has no provision for inferring a value from
|
|
455
|
+
'self.edge'"""
|
|
456
|
+
)
|
|
457
|
+
raise AttributeError(msg)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
_TEdgeAttrib = TypeVar("_TEdgeAttrib", bound=EdgeAttrib[Any])
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class FaceAttrib(Generic[_T]):
|
|
464
|
+
"""Base class for Face attributes."""
|
|
465
|
+
|
|
466
|
+
__slots__ = ("_value", "face")
|
|
467
|
+
|
|
468
|
+
def __new__(
|
|
469
|
+
cls: type[_TFaceAttrib], value: _T | None = None, face: Face | None = None
|
|
470
|
+
) -> _TFaceAttrib:
|
|
471
|
+
"""Raise an exception if the attribute is not subclassed."""
|
|
472
|
+
del value
|
|
473
|
+
del face
|
|
474
|
+
if cls is FaceAttrib:
|
|
475
|
+
msg = "FaceAttrib is an abstract class and cannot be instantiated."
|
|
476
|
+
raise TypeError(msg)
|
|
477
|
+
return object.__new__(cls)
|
|
478
|
+
|
|
479
|
+
def __init__(self, value: _T | None = None, face: Face | None = None) -> None:
|
|
480
|
+
"""Set value and face."""
|
|
481
|
+
self._value = value
|
|
482
|
+
self.face = face
|
|
483
|
+
|
|
484
|
+
@property
|
|
485
|
+
def value(self) -> _T:
|
|
486
|
+
"""Return value if set, else try to infer a value.
|
|
487
|
+
|
|
488
|
+
:return: Value of the attribute
|
|
489
|
+
:raises AttributeError: If no value is set and _infer_value fails
|
|
490
|
+
"""
|
|
491
|
+
if self._value is not None:
|
|
492
|
+
return self._value
|
|
493
|
+
with suppress(NotImplementedError, ValueError):
|
|
494
|
+
value = self._infer_value()
|
|
495
|
+
self._value = value
|
|
496
|
+
return self._value
|
|
497
|
+
msg = "no value set and failed to infer from 'self.face'"
|
|
498
|
+
raise AttributeError(msg)
|
|
499
|
+
|
|
500
|
+
def copy_to_face(self: FaceAttrib[_T], face: Face) -> FaceAttrib[_T]:
|
|
501
|
+
"""Return a new instance with the same value, assigned to a new face.
|
|
502
|
+
|
|
503
|
+
:param face: New face
|
|
504
|
+
:return: FaceAttrib instance
|
|
505
|
+
"""
|
|
506
|
+
return type(self)(self._value, face)
|
|
507
|
+
|
|
508
|
+
@classmethod
|
|
509
|
+
def merge(cls, *merge_from: _TFaceAttrib | None) -> _TFaceAttrib | None:
|
|
510
|
+
"""Get value of self from self._merge_from.
|
|
511
|
+
|
|
512
|
+
:param merge_from: FaceAttrib instances to merge (all of the same class)
|
|
513
|
+
:return: FaceAttrib instance or None
|
|
514
|
+
"""
|
|
515
|
+
_ = merge_from
|
|
516
|
+
return None
|
|
517
|
+
|
|
518
|
+
def split(self: _TFaceAttrib) -> _TFaceAttrib | None:
|
|
519
|
+
"""Define how attribute will be passed when dividing self.face.
|
|
520
|
+
|
|
521
|
+
:return: FaceAttrib instance or None
|
|
522
|
+
"""
|
|
523
|
+
return None
|
|
524
|
+
|
|
525
|
+
def _infer_value(self) -> _T:
|
|
526
|
+
"""Get value of self from self._face."""
|
|
527
|
+
msg = par(
|
|
528
|
+
f"""'{type(self).__name__}' has no provision for inferring a value from
|
|
529
|
+
'self.face'"""
|
|
530
|
+
)
|
|
531
|
+
raise AttributeError(msg)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
_TFaceAttrib = TypeVar("_TFaceAttrib", bound=FaceAttrib[Any])
|
|
535
|
+
|
|
536
|
+
|
|
315
537
|
class ContagionAttrib(Attrib[Literal[True]]):
|
|
316
538
|
"""Spread value when combining with anything.
|
|
317
539
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|