File: test_532_acis_mesh.py

package info (click to toggle)
ezdxf 1.4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 104,528 kB
  • sloc: python: 182,341; makefile: 116; lisp: 20; ansic: 4
file content (222 lines) | stat: -rw-r--r-- 7,636 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#  Copyright (c) 2022, Manfred Moitzi
#  License: MIT License
from typing import cast
import pytest
import math
from ezdxf.acis.api import mesh_from_body, load, body_from_mesh, vertices_from_body
from ezdxf.acis.mesh import PolyhedronFaceBuilder
from ezdxf.acis import entities, dbg
from ezdxf.render import forms
from ezdxf.math import BoundingBox, Vec3


class TestPrismToMesh:
    """The "prism" is a distorted cube with quadrilateral faces at the
    top and bottom and eight triangles as connecting faces in between.
    """

    @pytest.fixture(scope="class")
    def mesh(self, prism_sat):
        body = load(prism_sat)[0]
        return mesh_from_body(body)[0]

    def test_mesh_has_10_faces(self, mesh):
        assert len(mesh.faces) == 10

    def test_mesh_has_8_unique_vertices(self, mesh):
        assert len(mesh.vertices) == 8

    def test_all_faces_have_at_least_3_vertices(self, mesh):
        assert all(len(set(face)) >= 3 for face in mesh.faces)

    def test_any_face_has_at_least_4_vertices(self, mesh):
        assert any(len(set(face)) == 4 for face in mesh.faces)


class TestPolyhedronFaceBuilder:
    @pytest.fixture
    def builder(self):
        return PolyhedronFaceBuilder(forms.cube())

    def test_six_faces_will_be_created(self, builder):
        """There have to be 6 cube faces."""
        assert len(builder.acis_faces()) == 6

    def test_each_face_gets_it_own_plane(self, builder):
        """Each face has to have its own flat surface plane."""
        planes = {id(face.surface) for face in builder.acis_faces()}
        assert len(planes) == 6
        assert (
            all(face.surface.type == "plane-surface" for face in builder.acis_faces())
            is True
        )

    def test_each_face_has_a_single_loop(self, builder):
        """The PolyhedronFaceBuilder supports only a single loop for each face,
        so divided faces or holes in faces are not supported.
        """
        for face in builder.acis_faces():
            assert face.loop.next_loop.is_none is True

    def test_for_24_unique_coedges(self, builder):
        """Each face has its own coedges which represent the face boundary and
        each coedge references a 'real' edge entity
        """
        coedges = get_coedges(builder.acis_faces())
        assert len(coedges) == 24

    def test_coedge_references_parent_loop(self, builder):
        for coedge in get_coedges(builder.acis_faces()):
            assert coedge.loop.is_none is False

    def test_for_partner_coedges(self, builder):
        """Each coedge has to have a partner coedge in the adjacent face and
        because they are sharing the same 'real' edge they have to have an
        opposite sense.
        """
        for coedge in get_coedges(builder.acis_faces()):
            partner_coedge = coedge.partner_coedge
            assert partner_coedge.is_none is False
            assert coedge.sense is not partner_coedge.sense
            assert partner_coedge.partner_coedge is coedge, "is back linked"

    def test_for_12_unique_edges(self, builder):
        """There have to be 12 'real' cube edges."""
        edges = get_edges(builder.acis_faces())
        assert len(edges) == 12

    def test_edges_have_a_parent_coedge(self, builder):
        for edge in get_edges(builder.acis_faces()):
            assert edge.coedge.type == "coedge"

    def test_each_edges_has_a_unique_straight_line(self, builder):
        """Each edge has to have a straight lines as base curve."""
        edges = get_edges(builder.acis_faces())
        assert all(e.curve.type == "straight-curve" for e in edges) is True
        for e in edges:
            ray = cast(entities.StraightCurve, e.curve)
            assert math.isclose(ray.direction.magnitude, 1.0)
        assert len(edges) == 12

    def test_for_8_unique_vertices(self, builder):
        """Edges do share start- and end vertices which has a reference
        the 'real' point.
        """
        vertices = get_vertices(builder.acis_faces())
        assert len(vertices) == 8

    def test_for_8_unique_points(self, builder):
        """There have to be 8 'real' cube corner points."""
        points = get_points(builder.acis_faces())
        assert len(points) == 8

    def test_build_multiple_times_independent_faces(self, builder):
        """The two builds should not share any data."""
        faces1 = builder.acis_faces()
        faces2 = builder.acis_faces()
        assert set(faces1).isdisjoint(set(faces2))
        assert set(get_coedges(faces1)).isdisjoint(set(get_coedges(faces2)))
        assert set(get_edges(faces1)).isdisjoint(set(get_edges(faces2)))
        assert set(get_vertices(faces1)).isdisjoint(set(get_vertices(faces2)))
        assert set(get_points(faces1)).isdisjoint(set(get_points(faces2)))


def get_coedges(faces):
    coedges = set()
    for face in faces:
        for coedge in face.loop.coedges():
            coedges.add(coedge)
    return coedges


def get_edges(faces):
    edges = set()
    for face in faces:
        for coedge in face.loop.coedges():
            edges.add(coedge.edge)
    return edges


def get_vertices(faces):
    vertices = set()
    for face in faces:
        for coedge in face.loop.coedges():
            vertices.add(coedge.edge.start_vertex)
            vertices.add(coedge.edge.end_vertex)
    return vertices


def get_points(faces):
    points = set()
    for face in faces:
        for coedge in face.loop.coedges():
            points.add(coedge.edge.start_vertex.point)
            points.add(coedge.edge.end_vertex.point)
    return points


def test_rebuild_mesh_from_acis_body():
    """Convert a cube-mesh into an ACIS body and the ACIS body back to a
    MeshBuilder instance.
    """
    cube = forms.cube()
    body = body_from_mesh(cube)
    cube2 = mesh_from_body(body)[0]
    assert set(cube.vertices) == set(cube2.vertices)
    assert faces_set(cube) == faces_set(cube2)


def faces_set(mesh):
    return set([tuple(face) for face in mesh.faces_as_vertices()])


class TestTransformCenterOfMeshToOriginAtConversionToAcisBody:
    @pytest.fixture(scope="class")
    def body(self):
        cube = forms.cube(center=False)
        return body_from_mesh(cube)

    def test_transformation_matrix_for_translation(self, body):
        matrix = body.transform.matrix
        translation = Vec3(matrix.get_row(3)[:3])
        assert translation.isclose((0.5, 0.5, 0.5))

    def test_if_acis_points_are_centered_around_the_origin(self, body):
        debugger = dbg.AcisDebugger(body)
        vertices = [p.location for p in debugger.filter_type("point")]
        bbox = BoundingBox(vertices)
        assert bbox.extmin.isclose((-0.5, -0.5, -0.5))
        assert bbox.extmax.isclose((0.5, 0.5, 0.5))


def test_non_manifold_detection():
    cube = forms.cube(center=False)
    # duplicate last face
    cube.faces.append(cube.faces[-1])
    body = body_from_mesh(cube)
    debugger = dbg.AcisDebugger(body)
    assert debugger.is_manifold() is False


def test_body_from_menger_sponge_is_manifold():
    from ezdxf.addons import MengerSponge

    sponge = MengerSponge().mesh()
    body = body_from_mesh(sponge)
    debugger = dbg.AcisDebugger(body)
    assert debugger.is_manifold() is True


def test_vertices_from_body():
    cube = forms.cube()
    body = body_from_mesh(cube)
    vertices = vertices_from_body(body)
    extents = BoundingBox(vertices)

    assert extents.extmin.isclose((-0.5, -0.5, -0.5))
    assert extents.extmax.isclose((0.5, 0.5, 0.5))
    assert len(vertices) == 6 * 4 * 2  # 6 faces x 4 edges x 2 vertices


if __name__ == "__main__":
    pytest.main([__file__])