File: test_531_acis_entities.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 (403 lines) | stat: -rw-r--r-- 11,954 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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
#  Copyright (c) 2022-2024, Manfred Moitzi
#  License: MIT License

import pytest
from ezdxf.acis.api import load, export_sat, export_sab, ExportError
from ezdxf.acis import sat, sab, entities, hdr, const, mesh
from ezdxf.math import Matrix44
import math


def test_load_any_format(any_cube):
    bodies = load(any_cube)
    assert len(bodies) == 1


@pytest.fixture(scope="module")
def body(any_cube):
    return load(any_cube)[0]


class TestBody:
    def test_type_type(self, body):
        assert body.type == "body"

    def test_has_transform_attribute(self, body):
        assert body.transform.is_none is False

    def test_transform_attribute_was_loaded(self, body):
        m = body.transform.matrix
        assert m.get_row(3) == (388.5, 388.5, 388.5, 1.0)

    def test_has_wire_attribute(self, body):
        assert body.wire.is_none is True

    def test_get_all_lumps_as_a_list(self, body):
        bodies = body.lumps()
        assert len(bodies) == 1
        assert isinstance(bodies[0], entities.Lump)

    def test_append_lumps(self):
        body = entities.Body()
        assert len(body.lumps()) == 0
        body.append_lump(entities.Lump())
        assert len(body.lumps()) == 1
        body.append_lump(entities.Lump())
        assert len(body.lumps()) == 2


class TestLump:
    def test_lump_type(self, body):
        assert body.lump.type == "lump"

    def test_back_pointer_to_body(self, body):
        assert body.lump.body is body

    def test_has_no_next_lump(self, body):
        assert body.lump.next_lump.is_none is True

    def test_has_attribute_to_first_shell(self, body):
        assert body.lump.shell.is_none is False

    def test_get_all_shells_as_a_list(self, body):
        shells = body.lump.shells()
        assert len(shells) == 1
        assert isinstance(shells[0], entities.Shell)

    def test_append_shells(self):
        body = entities.Body()
        lump = entities.Lump()
        body.append_lump(lump)
        assert len(lump.shells()) == 0
        lump.append_shell(entities.Shell())
        assert len(lump.shells()) == 1
        lump.append_shell(entities.Shell())
        assert len(lump.shells()) == 2


class TestShell:
    @pytest.fixture(scope="class")
    def shell(self, body):
        return body.lump.shell

    def test_shell_type(self, shell):
        assert shell.type == "shell"

    def test_back_pointer_to_lump(self, shell):
        assert shell.lump.shell is shell

    def test_has_no_next_shell(self, shell):
        assert shell.next_shell.is_none is True

    def test_get_all_faces_as_a_list(self, shell):
        faces = shell.faces()
        assert len(faces) == 6
        assert isinstance(faces[0], entities.Face)

    def test_append_faces(self):
        body = entities.Body()
        lump = entities.Lump()
        shell = entities.Shell()
        body.append_lump(lump)
        lump.append_shell(shell)

        assert len(shell.faces()) == 0
        shell.append_face(entities.Face())
        assert len(shell.faces()) == 1
        shell.append_face(entities.Face())
        assert len(shell.faces()) == 2


class TestFace:
    @pytest.fixture(scope="class")
    def face(self, body):
        return body.lump.shell.face

    def test_face_type(self, face):
        assert face.type == "face"

    def test_back_pointer_to_shell(self, body, face):
        assert face.shell is body.lump.shell

    def test_has_attribute_surface(self, face):
        assert face.surface.type == "plane-surface"

    def test_face_features(self, face):
        assert face.sense is False  # forward
        assert face.double_sided is False  # single
        assert face.containment is False

    def test_traverse_all_six_cube_faces(self, face):
        count = 1
        while not face.next_face.is_none:
            count += 1
            face = face.next_face
        assert count == 6


class TestPolyhedronFaceBuilder:
    @pytest.fixture(scope="class")
    def cube(self):
        from ezdxf.render.forms import cube
        return mesh.PolyhedronFaceBuilder(cube())

    def test_creates_six_faces(self, cube):
        faces = list(cube.acis_faces())
        assert len(faces) == 6

    def test_each_face_defines_a_plane_surface(self, cube):
        for face in cube.acis_faces():
            assert face.surface.type == "plane-surface"


class TestPlane:
    @pytest.fixture(scope="class")
    def plane(self, body):
        return body.lump.shell.face.surface

    def test_plane_type(self, plane):
        assert plane.type == "plane-surface"

    def test_plane_location(self, plane):
        assert plane.origin.isclose((0, 0, 388.5))

    def test_plane_normal(self, plane):
        assert plane.normal.isclose((0, 0, 1))

    def test_plane_u_dir(self, plane):
        assert plane.u_dir.isclose((1, 0, 0))

    def test_plane_has_infinite_bounds(self, plane):
        assert math.isinf(plane.u_bounds[0])
        assert math.isinf(plane.u_bounds[1])
        assert math.isinf(plane.v_bounds[0])
        assert math.isinf(plane.v_bounds[1])


class TestLoop:
    @pytest.fixture(scope="class")
    def loop(self, body):
        return body.lump.shell.face.loop

    def test_loop_type(self, loop):
        assert loop.type == "loop"

    def test_cube_face_has_only_one_loop(self, loop):
        assert loop.next_loop.is_none is True

    def test_loop_references_the_parent_face(self, loop):
        assert loop.face.loop is loop

    def test_set_closed_coedges(self):
        loop = entities.Loop()
        coedges = [entities.Coedge() for _ in range(3)]
        loop.set_coedges(coedges, close=True)

        result = loop.coedges()
        assert result == coedges

    def test_set_open_coedges(self):
        loop = entities.Loop()
        coedges = [entities.Coedge() for _ in range(3)]
        loop.set_coedges(coedges, close=False)
        assert loop.coedge.prev_coedge.is_none is True

        result = loop.coedges()
        assert result == coedges


class TestCoedge:
    @pytest.fixture(scope="class")
    def coedge(self, body):
        return body.lump.shell.face.loop.coedge

    def test_co_edge_type(self, coedge):
        assert coedge.type == "coedge"

    def test_co_edges_are_organized_as_a_forward_linked_list(self, coedge):
        next = coedge.next_coedge
        co_edges = [next]
        while next is not coedge:
            next = next.next_coedge
            co_edges.append(next)
        assert len(co_edges) == 4

    def test_co_edges_are_organized_as_a_reverse_linked_list(self, coedge):
        prev = coedge.prev_coedge
        co_edges = [prev]
        while prev is not coedge:
            prev = prev.prev_coedge
            co_edges.append(prev)
        assert len(co_edges) == 4

    def test_partner_co_edge_count_is_two(self, coedge):
        assert len(coedge.partner_coedges()) == 1

    def test_co_edges_have_partner_co_edges_other_faces(self, coedge):
        assert coedge.partner_coedge.partner_coedge is coedge

    def test_sense_of_co_edge_is_forward(self, coedge):
        assert coedge.sense is False

    def test_co_edge_references_the_parent_loop(self, coedge):
        assert coedge.loop.coedge is coedge


def test_default_partner_co_edge_count_is_two():
    coedge = entities.Coedge()
    assert len(coedge.partner_coedges()) == 0


class TestEdge:
    @pytest.fixture(scope="class")
    def edge(self, body):
        return body.lump.shell.face.loop.coedge.edge

    def test_edge_type(self, edge):
        assert edge.type == "edge"

    def test_edge_has_a_start_vertex(self, edge):
        assert edge.start_vertex.is_none is False

    def test_edge_has_an_end_vertex(self, edge):
        assert edge.end_vertex.is_none is False

    # start- and end parameter do not exist in ACIS-400

    def test_sense_of_edge_is_forward(self, edge):
        assert edge.sense is False

    def test_underlying_curve_of_edge(self, edge):
        assert edge.curve.type == "straight-curve"

    def test_edge_is_referenced_by_two_parent_co_edges(self, edge):
        parent = edge.coedge
        assert parent.edge is edge
        assert parent.partner_coedge.edge is edge


class TestVertex:
    @pytest.fixture(scope="class")
    def vertex(self, body):
        return body.lump.shell.face.loop.coedge.edge.start_vertex

    def test_vertex_type(self, vertex):
        assert vertex.type == "vertex"

    def test_vertex_references_parent_edge(self, vertex):
        assert vertex.edge.start_vertex is vertex


class TestPoint:
    @pytest.fixture(scope="class")
    def point(self, body):
        return body.lump.shell.face.loop.coedge.edge.start_vertex.point

    def test_point_type(self, point):
        assert point.type == "point"

    def test_point_location(self, point):
        assert point.location.isclose((388.5, -388.5, 388.5))

    def test_get_all_points(self, body):
        face = body.lump.shell.face
        vertices = set()
        while not face.is_none:
            first_coedge = face.loop.coedge
            coedge = first_coedge
            while True:
                vertices.add(coedge.edge.start_vertex.point.location)
                coedge = coedge.next_coedge
                if coedge is first_coedge:
                    break
            face = face.next_face
        assert len(vertices) == 8


@pytest.fixture(scope="module")
def prism700(prism_sat):
    return load(prism_sat)


class TestExportSat:
    def test_export_rejects_unsupported_acis_versions(self, prism700):
        with pytest.raises(ExportError):
            export_sat(prism700, version=400)

    def test_export_acis_700(self, prism700):
        data = export_sat(prism700)
        assert len(data) == 117  # includes header
        assert data[-1] == "End-of-ACIS-data "  # an extra space at the end!


class TestExportSab21800:
    def test_export_rejects_unsupported_acis_versions(self, prism700):
        with pytest.raises(ExportError):
            export_sab(prism700, version=400)

    def test_reload_records_from_acis_export(self, prism700):
        data = export_sab(prism700)
        decoder = sab.Decoder(data)
        header = decoder.read_header()
        assert header.version == 21800
        records = list(decoder.read_records())
        # 115 = excludes header
        assert len(records) == 115
        assert records[-1][0].value == "End-of-ASM-data"


def test_load_mesh_from_exported_sat_data(prism700):
    bodies = load(export_sat(prism700))
    m = mesh.mesh_from_body(bodies[0])[0]
    assert len(m.vertices) == 8
    assert len(m.faces) == 10


def test_load_mesh_from_exported_sab_data(prism700):
    bodies = load(export_sab(prism700))
    m = mesh.mesh_from_body(bodies[0])[0]
    assert len(m.vertices) == 8
    assert len(m.faces) == 10


class TestExportTransform:
    @pytest.fixture(scope="class")
    def header(self):
        header = hdr.AcisHeader()
        header.version = 700
        return header

    @pytest.fixture(scope="class")
    def sat_exporter(self, header):
        return sat.SatExporter(header)

    @pytest.fixture(scope="class")
    def sab_exporter(self, header):
        return sab.SabExporter(header)

    def test_export_sat_identity_matrix(self, sat_exporter):
        data = []
        exporter = sat.SatDataExporter(sat_exporter, data)
        t = entities.Transform()
        t.matrix = Matrix44()
        t.export(exporter)
        assert (
            " ".join(data)
            == "1 0 0 0 1 0 0 0 1 0 0 0 1 no_rotate no_reflect no_shear"
        )

    def test_export_sab_identity_matrix(self, sab_exporter):
        data = []
        exporter = sab.SabDataExporter(sab_exporter, data)
        t = entities.Transform()
        t.matrix = Matrix44()
        t.export(exporter)
        assert data[0] == (
            const.Tags.LITERAL_STR,
            # The last space is important!
            "1 0 0 0 1 0 0 0 1 0 0 0 1 no_rotate no_reflect no_shear ",
        )


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