halfedge 0.6.0__py3-none-any.whl → 0.8.1__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.
- halfedge/half_edge_constructors.py +3 -1
- halfedge/half_edge_elements.py +21 -1
- halfedge/half_edge_object.py +7 -0
- halfedge/type_attrib.py +5 -5
- halfedge/validations.py +3 -1
- {halfedge-0.6.0.dist-info → halfedge-0.8.1.dist-info}/METADATA +50 -34
- halfedge-0.8.1.dist-info/RECORD +12 -0
- {halfedge-0.6.0.dist-info → halfedge-0.8.1.dist-info}/WHEEL +1 -1
- halfedge-0.6.0.dist-info/RECORD +0 -12
- {halfedge-0.6.0.dist-info → halfedge-0.8.1.dist-info}/top_level.txt +0 -0
|
@@ -23,13 +23,15 @@ then passing that raw data to mesh_from_vr would create a mesh with 6 faces and
|
|
|
23
23
|
from __future__ import annotations
|
|
24
24
|
|
|
25
25
|
from contextlib import suppress
|
|
26
|
-
from typing import TYPE_CHECKING, Any,
|
|
26
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
27
27
|
|
|
28
28
|
from paragraphs import par
|
|
29
29
|
|
|
30
30
|
from halfedge.half_edge_elements import Edge, Face, ManifoldMeshError, Vert
|
|
31
31
|
|
|
32
32
|
if TYPE_CHECKING:
|
|
33
|
+
from collections.abc import Iterable, Sequence
|
|
34
|
+
|
|
33
35
|
from halfedge.type_attrib import Attrib, StaticAttrib
|
|
34
36
|
|
|
35
37
|
|
halfedge/half_edge_elements.py
CHANGED
|
@@ -84,11 +84,31 @@ class MeshElementBase:
|
|
|
84
84
|
"""
|
|
85
85
|
self.sn = next(self._sn_generator)
|
|
86
86
|
self.attrib: dict[str, Attrib[Any]] = {}
|
|
87
|
-
self.
|
|
87
|
+
self._mesh = mesh
|
|
88
88
|
|
|
89
89
|
for attribute in attributes:
|
|
90
90
|
self.set_attrib(attribute)
|
|
91
91
|
|
|
92
|
+
@property
|
|
93
|
+
def mesh(self) -> BlindHalfEdges:
|
|
94
|
+
"""Return the mesh instance.
|
|
95
|
+
|
|
96
|
+
:return: the mesh instance
|
|
97
|
+
:raise AttributeError: if mesh not set for self
|
|
98
|
+
"""
|
|
99
|
+
if self._mesh is not None:
|
|
100
|
+
return self._mesh
|
|
101
|
+
msg = "mesh not set for self"
|
|
102
|
+
raise AttributeError(msg)
|
|
103
|
+
|
|
104
|
+
@mesh.setter
|
|
105
|
+
def mesh(self, mesh: BlindHalfEdges) -> None:
|
|
106
|
+
"""Set the mesh instance.
|
|
107
|
+
|
|
108
|
+
:param mesh: the mesh instance
|
|
109
|
+
"""
|
|
110
|
+
self._mesh = mesh
|
|
111
|
+
|
|
92
112
|
def set_attrib(self, attrib: Attrib[Any]) -> None:
|
|
93
113
|
"""Set an attribute.
|
|
94
114
|
|
halfedge/half_edge_object.py
CHANGED
|
@@ -79,6 +79,13 @@ class HalfEdges(StaticHalfEdges):
|
|
|
79
79
|
Able to infer from:
|
|
80
80
|
* both verts on same face: that face
|
|
81
81
|
* empty mesh: a new Hole
|
|
82
|
+
|
|
83
|
+
This is only called when inserting an edge into an existing face (or empty
|
|
84
|
+
mesh), so the orig and dest should always be connected to only one face (or
|
|
85
|
+
nothing, which is handled in this method).
|
|
86
|
+
|
|
87
|
+
Once the face is split, the two verts will form an edge that connects to two
|
|
88
|
+
faces.
|
|
82
89
|
"""
|
|
83
90
|
if not self.edges:
|
|
84
91
|
return self.new_hole()
|
halfedge/type_attrib.py
CHANGED
|
@@ -61,7 +61,7 @@ When assigned to a Vert instance, these will be stored in the Vert instance's
|
|
|
61
61
|
from __future__ import annotations
|
|
62
62
|
|
|
63
63
|
from contextlib import suppress
|
|
64
|
-
from typing import TYPE_CHECKING, Any, Generic, Literal,
|
|
64
|
+
from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar
|
|
65
65
|
|
|
66
66
|
from paragraphs import par
|
|
67
67
|
|
|
@@ -79,7 +79,7 @@ class StaticAttrib(Generic[_T]):
|
|
|
79
79
|
merged or split.
|
|
80
80
|
"""
|
|
81
81
|
|
|
82
|
-
__slots__ = ("
|
|
82
|
+
__slots__ = ("_mesh", "_value")
|
|
83
83
|
|
|
84
84
|
def __new__(
|
|
85
85
|
cls: type[_TStaticAttrib],
|
|
@@ -193,7 +193,7 @@ class Attrib(Generic[_T]):
|
|
|
193
193
|
every case.
|
|
194
194
|
"""
|
|
195
195
|
|
|
196
|
-
__slots__ = ("
|
|
196
|
+
__slots__ = ("_element", "_value")
|
|
197
197
|
|
|
198
198
|
def __new__(
|
|
199
199
|
cls: type[_TAttrib],
|
|
@@ -469,7 +469,7 @@ class NumericAttrib(Attrib[_T]):
|
|
|
469
469
|
return type(have_values[0])(sum(values) / len(values))
|
|
470
470
|
|
|
471
471
|
|
|
472
|
-
class Vector2Attrib(Attrib[
|
|
472
|
+
class Vector2Attrib(Attrib[tuple[float, float]]):
|
|
473
473
|
"""Average merge_from values as xy tuples."""
|
|
474
474
|
|
|
475
475
|
def __new__(
|
|
@@ -501,7 +501,7 @@ class Vector2Attrib(Attrib[Tuple[float, float]]):
|
|
|
501
501
|
return type(have_values[0])((sum_x / num, sum_y / num))
|
|
502
502
|
|
|
503
503
|
|
|
504
|
-
class Vector3Attrib(Attrib[
|
|
504
|
+
class Vector3Attrib(Attrib[tuple[float, float, float]]):
|
|
505
505
|
"""Average merge_from values as xyz tuples."""
|
|
506
506
|
|
|
507
507
|
def __new__(
|
halfedge/validations.py
CHANGED
|
@@ -10,11 +10,13 @@ created: 181127
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
from itertools import chain
|
|
13
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
|
14
14
|
|
|
15
15
|
from halfedge.half_edge_elements import Edge, Face, ManifoldMeshError
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Iterator
|
|
19
|
+
|
|
18
20
|
from halfedge.half_edge_querries import StaticHalfEdges
|
|
19
21
|
|
|
20
22
|
_T = TypeVar("_T")
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: halfedge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: A typical half-edge data structure with some padding
|
|
5
5
|
Author-email: Shay Hill <shay_public@hotmail.com>
|
|
6
6
|
License: MIT
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: paragraphs
|
|
10
10
|
Provides-Extra: dev
|
|
11
|
-
Requires-Dist: commitizen
|
|
12
|
-
Requires-Dist: coverage
|
|
13
|
-
Requires-Dist: pre-commit
|
|
14
|
-
Requires-Dist: pylint
|
|
15
|
-
Requires-Dist: pytest
|
|
16
|
-
Requires-Dist: tox
|
|
11
|
+
Requires-Dist: commitizen; extra == "dev"
|
|
12
|
+
Requires-Dist: coverage; extra == "dev"
|
|
13
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
14
|
+
Requires-Dist: pylint; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest; extra == "dev"
|
|
16
|
+
Requires-Dist: tox; extra == "dev"
|
|
17
17
|
|
|
18
18
|
# A typical halfedges data structure with some padding
|
|
19
19
|
|
|
@@ -35,11 +35,11 @@ mesh.remove_edge(edge)
|
|
|
35
35
|
|
|
36
36
|
## Particulars
|
|
37
37
|
|
|
38
|
-
The idea (for
|
|
38
|
+
The idea (for 19 years and counting) has been to create an interface that is neither too complex, too verbose, nor too magical. I've been all over the place as to where that line should be. This is my current thinking.
|
|
39
39
|
|
|
40
40
|
### Reflection
|
|
41
41
|
|
|
42
|
-
If you set `edge_a.orig = vert_a`, then the `Edge.vert` setter will *automagically* set `vert_a.edge = edge_a`. This is true for any setter that might otherwise break the mesh. Sometimes, this reflection will happen when it isn't strictly necessary. Imagine you have `face_a` with three edges: `edge_a`, `edge_b` and `edge_c`. `face_a` has a pointer to `edge_a`, but it could point to any of the three edges and still be correct per the requirements of the halfedge data structure. If you directly set `edge_b.face = face_a`, everything would still be correct (`face_a.edge` would still be `edge_a` and that would still be correct), but the `Edge.edge` setter will nevertheless set `face_a.edge = edge_b`.
|
|
42
|
+
If you set `edge_a.orig = vert_a`, then the `Edge.vert` setter will *automagically* set `vert_a.edge = edge_a`. This is true for any setter that might otherwise break the mesh. Sometimes, this reflection will happen when it isn't strictly necessary. Imagine you have `face_a` with three edges: `edge_a`, `edge_b` and `edge_c`. `face_a` has a pointer to `edge_a`, but it could point to any of the three edges and still be correct per the requirements of the halfedge data structure. If you directly set `edge_b.face = face_a`, everything would still be correct (`face_a.edge` would still be `edge_a` and that would still be correct), but the `Edge.edge` setter will nevertheless "reflectively" set `face_a.edge = edge_b`.
|
|
43
43
|
|
|
44
44
|
### id
|
|
45
45
|
|
|
@@ -64,50 +64,66 @@ Face(
|
|
|
64
64
|
|
|
65
65
|
The `is_hole` `__init__` kwarg is shorthand for
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
```python
|
|
68
|
+
class IsHole(ContagionAttribute):
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
face = Face()
|
|
72
|
+
face.add_attrib(IsHole())
|
|
73
|
+
```
|
|
69
74
|
|
|
70
|
-
vert = Vert()
|
|
71
|
-
vert.add_attrib(IsHole())
|
|
72
75
|
|
|
73
76
|
The `face_instance.is_hole` property getter is shorthand for
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
```python
|
|
79
|
+
vert.get_attrib(IsHole())
|
|
80
|
+
```
|
|
76
81
|
|
|
77
82
|
More on `Attrib` classes below and in `type_attrib.py`.
|
|
78
83
|
|
|
79
84
|
### Element Attributes
|
|
80
85
|
|
|
81
|
-
By halfedge convention
|
|
86
|
+
By halfedge convention
|
|
82
87
|
|
|
83
|
-
|
|
88
|
+
- each Vert instance holds a pointer to one Edge instance;
|
|
89
|
+
- each Face instance holds a pointer to one Edge instance; and
|
|
90
|
+
- each Edge instance holds four pointers (orig, pair, face, next).
|
|
84
91
|
|
|
85
|
-
|
|
92
|
+
These describe the geometry of a mesh, but there may be other attributes you would like to assign to these instances. For example, each Face instance might have a color. There is no objectively correct way to define a face color, nor to merge colors when two faces are merged, nor to split a color when faces are split. Do a red and a blue face behave like paint and combine to make a purple face? Or do they behave like DNA to make a red *or* blue face depending on which is dominant? This library will not guess.
|
|
86
93
|
|
|
87
|
-
|
|
88
|
-
pass
|
|
94
|
+
For each such attribute, you will need to define `merge` and `split` methods to explicate how the attribute 1) combines when elements are merged; and 2) behaves when an element is split. Do this by creating a new descendent of `Attrib` or one of `Attrib`'s children defined in `type_attrib.py`. See the docstring in that file for more information.
|
|
89
95
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
```python
|
|
97
|
+
# `Vector2Attrib` is a child of `Attrib` defined in `type_attrib.py`.
|
|
98
|
+
# It defines suitable (YMMV) `merge` (average) and `split` (fail) methods for
|
|
99
|
+
# an (x, y) coordinate.
|
|
100
|
+
class Coordinate(Vector2Attrib):
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
vert = Vert()
|
|
104
|
+
vert.add_attrib(Coordinate((1, 2)))
|
|
105
|
+
assert vert.get_attrib(Coordinate).value == (1, 2)
|
|
106
|
+
```
|
|
93
107
|
|
|
94
|
-
|
|
108
|
+
You cannot assign or access these attributes with `vert.attribute`. Instead assign with `vert.add_attrib(attrib_instance)`. Retrieve the value with `vert.get_attrib(attrib_class)`. Everything will be keyed to the class name, so you will need a new ElemAttribBase descendant for each attribute type.
|
|
95
109
|
|
|
96
|
-
|
|
97
|
-
assert vert.get_attrib(Coordinate).value == (1, 2)
|
|
110
|
+
These element attributes can also be passed at `__init__`
|
|
98
111
|
|
|
99
|
-
|
|
112
|
+
```python
|
|
113
|
+
vert = Vert(Coordinate(1, 2))
|
|
114
|
+
assert vert.get_attrib(Coordinate).value == (1, 2)
|
|
115
|
+
```
|
|
100
116
|
|
|
101
117
|
### You Should Know
|
|
102
118
|
|
|
103
119
|
A canonical half-edge data structure stores:
|
|
104
120
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
121
|
+
- a set of verts (redundant)
|
|
122
|
+
- for each vert, a pointer to an edge
|
|
123
|
+
- a set of edges
|
|
124
|
+
- for each edge, pointers to vert, pair, face, next
|
|
125
|
+
- a set of faces (redundant)
|
|
126
|
+
- for each face, a pointer to an edge
|
|
111
127
|
|
|
112
128
|
This implementation only stores a set of edges. Sets of verts and faces are generated by iterating through references in edge instances. This makes for slower code, but does not violate DRY and makes for dramatically cleaner code.
|
|
113
129
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
halfedge/__init__.py,sha256=O5tfw3JIBJH83JlpwdYoTNHFsmcg959rAJd6RU51E4s,661
|
|
2
|
+
halfedge/half_edge_constructors.py,sha256=0V9xsjPgoA0zKuqUSSLJI1WKhLDJAXHzVWwH2AHZcq4,8686
|
|
3
|
+
halfedge/half_edge_elements.py,sha256=oX4ti6f6L1gcYsoP-KGNwdF4-cRpTJeBoMGMTJuIN14,18554
|
|
4
|
+
halfedge/half_edge_object.py,sha256=A70CVmV44zDi46gbtGHj8dw4Ltshqs56MRn5-78eM20,28771
|
|
5
|
+
halfedge/half_edge_querries.py,sha256=q5xVOFdpw7t6fhs-Z8yF5j3RlG4DOrOgoLbBsAfkm4A,5179
|
|
6
|
+
halfedge/py.typed,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
|
|
7
|
+
halfedge/type_attrib.py,sha256=NP8h5DmkSeq_v-wfueUumFeUKY94XqDoTQR9Lu8n094,20413
|
|
8
|
+
halfedge/validations.py,sha256=8dDUf76t1_63_e0V5tRETHJDoYUUmjnSFgsULwSSKP4,4544
|
|
9
|
+
halfedge-0.8.1.dist-info/METADATA,sha256=HITY6W4YbbBLTaGm68Iizy9tDtaMxmDyXj5gyYp4PKo,6996
|
|
10
|
+
halfedge-0.8.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
halfedge-0.8.1.dist-info/top_level.txt,sha256=iqphKHiIR4DfFMs14-gkFOWTXVkRWfGmDZmCYHhuoFM,9
|
|
12
|
+
halfedge-0.8.1.dist-info/RECORD,,
|
halfedge-0.6.0.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
halfedge/__init__.py,sha256=O5tfw3JIBJH83JlpwdYoTNHFsmcg959rAJd6RU51E4s,661
|
|
2
|
-
halfedge/half_edge_constructors.py,sha256=ToGcOYW2b848ynlY59xRNd7ibZuu3OqQpauPCl-N1YE,8654
|
|
3
|
-
halfedge/half_edge_elements.py,sha256=6PkPjGxe_0FYSWXi5MY8Yp4an7yYMWir8MPZn8QlUOc,18042
|
|
4
|
-
halfedge/half_edge_object.py,sha256=Cc1t_hy2tF52O9iD5tvZN6_w4H7Wt9hwDGFLdBqUxr4,28451
|
|
5
|
-
halfedge/half_edge_querries.py,sha256=q5xVOFdpw7t6fhs-Z8yF5j3RlG4DOrOgoLbBsAfkm4A,5179
|
|
6
|
-
halfedge/py.typed,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
|
|
7
|
-
halfedge/type_attrib.py,sha256=1W06nlyTSc0Q6f6PnQec_VNNRLVSgdNStoBlJmMgJNQ,20420
|
|
8
|
-
halfedge/validations.py,sha256=FhUv_J7XcaAduyudYGmi7ZL1tEqwJoCxKZwJU2zQolg,4512
|
|
9
|
-
halfedge-0.6.0.dist-info/METADATA,sha256=G5Ee4xaYWSU7gN4BXPuSjquALcOBy3-xWAsxxvmBa-c,7411
|
|
10
|
-
halfedge-0.6.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
11
|
-
halfedge-0.6.0.dist-info/top_level.txt,sha256=iqphKHiIR4DfFMs14-gkFOWTXVkRWfGmDZmCYHhuoFM,9
|
|
12
|
-
halfedge-0.6.0.dist-info/RECORD,,
|
|
File without changes
|