1# Copyright (c) 2018-2021 Manfred Moitzi
2# License: MIT License
3import pytest
4import ezdxf
5from ezdxf.entities.dimension import Dimension
6
7
8@pytest.fixture(scope='module')
9def dxf2000():
10    return ezdxf.new('R2000', setup='all')
11
12
13@pytest.fixture(scope='module')
14def dxf2007():
15    return ezdxf.new('R2007', setup='all')
16
17
18def test_dimstyle_standard_exist(dxf2000):
19    assert 'EZDXF' in dxf2000.dimstyles
20
21
22def test_rotated_dimline(dxf2000):
23    msp = dxf2000.modelspace()
24    dxfattribs = {
25        'dimtype': Dimension.LINEAR
26    }
27    dimline = msp.new_entity('DIMENSION', dxfattribs)
28    assert dimline.dimtype == Dimension.LINEAR
29    assert dimline.dxf.defpoint == (0, 0, 0)
30    assert dimline.dxf.insert == (0, 0, 0)
31    assert dimline.dxf.defpoint2 == (0, 0, 0)
32    assert dimline.dxf.defpoint3 == (0, 0, 0)
33    assert dimline.dxf.angle == 0.
34    assert dimline.dxf.oblique_angle == 0.
35
36
37def test_aligned_dimline(dxf2000):
38    msp = dxf2000.modelspace()
39    dxfattribs = {
40        'dimtype': Dimension.ALIGNED
41    }
42    dimline = msp.new_entity('DIMENSION', dxfattribs)
43    assert dimline.dimtype == Dimension.ALIGNED
44    assert dimline.dxf.defpoint == (0, 0, 0)
45    assert dimline.dxf.insert == (0, 0, 0)
46    assert dimline.dxf.defpoint2 == (0, 0, 0)
47    assert dimline.dxf.defpoint3 == (0, 0, 0)
48    assert dimline.dxf.angle == 0.
49    assert dimline.dxf.oblique_angle == 0.
50
51
52def test_angular_dimline(dxf2000):
53    msp = dxf2000.modelspace()
54    dxfattribs = {
55        'dimtype': Dimension.ANGULAR
56    }
57    dimline = msp.new_entity('DIMENSION', dxfattribs)
58    assert dimline.dimtype == Dimension.ANGULAR
59    assert dimline.dxf.defpoint == (0, 0, 0)
60    assert dimline.dxf.defpoint2 == (0, 0, 0)
61    assert dimline.dxf.defpoint3 == (0, 0, 0)
62    assert dimline.dxf.defpoint4 == (0, 0, 0)
63    assert dimline.dxf.defpoint5 == (0, 0, 0)
64
65
66def test_angular_3p_dimline(dxf2000):
67    msp = dxf2000.modelspace()
68    dxfattribs = {
69        'dimtype': Dimension.ANGULAR_3P
70    }
71    dimline = msp.new_entity('DIMENSION', dxfattribs)
72    assert dimline.dimtype == Dimension.ANGULAR_3P
73
74
75def test_radius_dimline(dxf2000):
76    msp = dxf2000.modelspace()
77    dxfattribs = {
78        'dimtype': Dimension.RADIUS
79    }
80    dimline = msp.new_entity('DIMENSION', dxfattribs)
81    assert dimline.dimtype == Dimension.RADIUS
82    assert dimline.dxf.defpoint == (0, 0, 0)
83    assert dimline.dxf.defpoint4 == (0, 0, 0)
84    assert dimline.is_supported_dxf_attrib('leader_length')
85
86
87def test_diameter_dimline(dxf2000):
88    msp = dxf2000.modelspace()
89    dxfattribs = {
90        'dimtype': Dimension.DIAMETER
91    }
92    dimline = msp.new_entity('DIMENSION', dxfattribs)
93    assert dimline.dimtype == Dimension.DIAMETER
94    assert dimline.dxf.defpoint == (0, 0, 0)
95    assert dimline.dxf.defpoint4 == (0, 0, 0)
96    assert dimline.is_supported_dxf_attrib('leader_length')
97
98
99def test_ordinate_dimline(dxf2000):
100    msp = dxf2000.modelspace()
101    dxfattribs = {
102        'dimtype': Dimension.ORDINATE
103    }
104    dimline = msp.new_entity('DIMENSION', dxfattribs)
105    assert dimline.dimtype == Dimension.ORDINATE
106    assert dimline.dxf.defpoint == (0, 0, 0)
107    assert dimline.dxf.defpoint2 == (0, 0, 0)
108    assert dimline.dxf.defpoint3 == (0, 0, 0)
109
110
111def add_linear_dimension(doc):
112    msp = doc.modelspace()
113    override = msp.add_linear_dim(
114        base=(3, 2, 0),
115        p1=(0, 0, 0),
116        p2=(3, 0, 0),
117
118    )
119    override.render()
120    return override.dimension
121
122
123def test_add_horizontal_dimline(dxf2000):
124    dimline = add_linear_dimension(dxf2000)
125    assert dimline.dxf.dimstyle == 'EZDXF'
126    block_name = dimline.dxf.geometry
127    assert block_name.startswith('*D')
128
129    block = dimline.get_geometry_block()
130    assert len(list(block.query('MTEXT'))) == 1
131    assert len(list(block.query('INSERT'))) == 2
132    assert len(
133        list(block.query('LINE'))) == 3  # dimension line + 2 extension lines
134    assert len(list(block.query('POINT'))) == 3  # def points
135
136
137def test_virtual_entities_and_explode(dxf2000):
138    dimline = add_linear_dimension(dxf2000)
139
140    parts = list(dimline.virtual_entities())
141    assert len(parts) == 9
142    geometry = dimline.dxf.geometry
143    parts = dimline.explode()
144    assert len(list(parts.query('MTEXT'))) == 1
145    assert len(list(parts.query('INSERT'))) == 2
146    assert len(
147        list(parts.query('LINE'))) == 3  # dimension line + 2 extension lines
148    assert len(list(parts.query('POINT'))) == 3  # def points
149    assert dimline.is_alive is False
150    assert geometry in dxf2000.blocks, 'Do not destroy anonymous block, may be used by block references.'
151
152
153def test_transformation_of_associated_anonymous_geometry_block(dxf2000):
154    dimline = add_linear_dimension(dxf2000)
155    dx, dy = 1, 1
156    block = dimline.get_geometry_block()
157    original_points = [e.dxf.location for e in block if e.dxftype() == 'POINT']
158    dimline.translate(dx, dy, 0)
159    transformed_points = [e.dxf.location for e in block if
160                          e.dxftype() == 'POINT']
161    for o, t in zip(original_points, transformed_points):
162        assert t.isclose(o + (dx, dy))
163
164
165def test_copy_dimension_with_geometry_block(dxf2000):
166    dimline = add_linear_dimension(dxf2000)
167    vcopy = dimline.copy()
168    assert vcopy.virtual_block_content is not None
169    assert vcopy.dxf.hasattr('geometry') is False
170    block = dimline.get_geometry_block()
171    assert len(vcopy.virtual_block_content) == len(block)
172    for copy, original in zip(vcopy.virtual_block_content, block):
173        assert copy.is_virtual is True
174        assert original.is_virtual is False
175        assert copy.dxftype() == original.dxftype()
176
177
178def test_destroy_virtual_dimension_copy(dxf2000):
179    dimline = add_linear_dimension(dxf2000)
180    dimline.destroy()
181    assert hasattr(dimline, 'virtual_block_content') is False
182
183
184def test_transform_virtual_geometry_block(dxf2000):
185    original = add_linear_dimension(dxf2000)
186
187    original_points = [e.dxf.location for e in original.virtual_entities()
188                       if e.dxftype() == 'POINT']
189    vcopy = original.copy()
190    dx, dy = 1, 1
191    vcopy.translate(dx, dy, 0)
192    transformed_points = [e.dxf.location for e in vcopy.virtual_entities() if
193                          e.dxftype() == 'POINT']
194    assert len(transformed_points) == len(original_points)
195    for o, t in zip(original_points, transformed_points):
196        assert t.isclose(o + (dx, dy))
197
198
199def test_add_virtual_dimension_copy_to_layout(dxf2000):
200    dimline = add_linear_dimension(dxf2000)
201    vcopy = dimline.copy()
202    msp = dxf2000.modelspace()
203    content = vcopy.virtual_block_content
204    msp.add_entity(vcopy)
205    assert vcopy.is_virtual is False
206    assert vcopy.virtual_block_content is None
207    assert all(not e.is_virtual for e in content), \
208        "all entities should be non virtual"
209    db = dxf2000.entitydb
210    assert all(e.dxf.handle in db for e in content), \
211        "all entities should be stored in the entity database"
212    assert all(e.doc is dxf2000 for e in content), \
213        "all entities should be bound to the document"
214
215
216def test_dimstyle_override(dxf2000):
217    msp = dxf2000.modelspace()
218    dimstyle = msp.add_linear_dim(
219        base=(3, 2, 0),
220        p1=(0, 0, 0),
221        p2=(3, 0, 0),
222        dxfattribs={
223            'dimstyle': 'EZDXF',
224        }
225    )
226    dimline = dimstyle.dimension
227    assert dimline.dxf.dimstyle == 'EZDXF'
228    if 'TEST' not in dxf2000.styles:  # text style must exists
229        dxf2000.styles.new('TEST')
230
231    preset = {
232        'dimtxsty': 'TEST',
233        'dimexe': 0.777,
234    }
235    dimstyle.update(preset)
236    assert dimstyle['dimtxsty'] == 'TEST'
237    assert dimstyle['dimexe'] == 0.777
238
239    assert dimstyle['invalid'] is None
240    dimstyle.update({'invalid': 7})
241    # ezdxf 0.10 and later uses internally only resource names not handles for dim style attributes
242    # unknown attributes are ignored
243    dstyle_orig = dimstyle.get_dstyle_dict()
244    assert len(dstyle_orig) == 0
245
246    dimstyle.commit()
247    # ezdxf 0.10 and later uses internally only resource names not handles for dim style attributes
248    dstyle = dimstyle.get_dstyle_dict()
249
250    assert dstyle['dimexe'] == 0.777
251
252    # handle attributes not available, just stored transparent in XDATA
253    assert 'dimtxsty_handle' not in dstyle
254
255    assert dstyle['dimtxsty'] == 'TEST'
256
257
258def test_linetype_override_R2000(dxf2000):
259    msp = dxf2000.modelspace()
260    preset = {
261        'dimltype': 'DOT',
262        'dimltex1': 'DOT2',
263        'dimltex2': 'DOTX2',
264    }
265    dimstyle = msp.add_linear_dim(
266        base=(3, 2, 0),
267        p1=(0, 0, 0),
268        p2=(3, 0, 0),
269        dimstyle='EZDXF',
270        override=preset,
271    )
272    assert dimstyle['dimltype'] == 'DOT'
273    assert dimstyle['dimltex1'] == 'DOT2'
274    assert dimstyle['dimltex2'] == 'DOTX2'
275
276    dimstyle.commit()
277    # ezdxf 0.10 and later uses internally only resource names not handles for dim style attributes
278    dstyle = dimstyle.get_dstyle_dict()
279
280    # handle attributes not available, just stored transparent in XDATA
281    assert 'dimltype_handle' not in dstyle
282    assert 'dimltex1_handle' not in dstyle
283    assert 'dimltex2_handle' not in dstyle
284
285    # line type not supported by DXF R2000
286    assert 'dimltype' not in dstyle
287    assert 'dimltex1' not in dstyle
288    assert 'dimltex2' not in dstyle
289
290
291def test_linetype_override_R2007(dxf2007):
292    msp = dxf2007.modelspace()
293    preset = {
294        'dimltype': 'DOT',
295        'dimltex1': 'DOT2',
296        'dimltex2': 'DOTX2',
297    }
298    dimstyle = msp.add_linear_dim(
299        base=(3, 2, 0),
300        p1=(0, 0, 0),
301        p2=(3, 0, 0),
302        dimstyle='EZDXF',
303        override=preset,
304    )
305    assert dimstyle['dimltype'] == 'DOT'
306    assert dimstyle['dimltex1'] == 'DOT2'
307    assert dimstyle['dimltex2'] == 'DOTX2'
308
309    dimstyle.commit()
310    # ezdxf 0.10 and later uses internally only resource names not handles for dim style attributes
311    dstyle = dimstyle.get_dstyle_dict()
312
313    # handle attributes not available, just stored transparent in XDATA
314    assert 'dimltype_handle' not in dstyle
315    assert 'dimltex1_handle' not in dstyle
316    assert 'dimltex2_handle' not in dstyle
317
318    assert dstyle['dimltype'] == 'DOT'
319    assert dstyle['dimltex1'] == 'DOT2'
320    assert dstyle['dimltex2'] == 'DOTX2'
321
322
323def test_dimstyle_override_arrows(dxf2000):
324    msp = dxf2000.modelspace()
325    arrows = ezdxf.ARROWS
326    blocks = dxf2000.blocks
327
328    arrows.create_block(blocks, arrows.dot_blank)
329    arrows.create_block(blocks, arrows.box)
330    arrows.create_block(blocks, arrows.closed)
331    arrows.create_block(blocks, arrows.closed_filled)
332
333    preset = {
334        'dimblk': arrows.dot_blank,
335        'dimblk1': arrows.box,
336        'dimblk2': arrows.closed,
337        'dimldrblk': arrows.closed_filled,  # virtual attribute
338    }
339    dimstyle = msp.add_linear_dim(
340        base=(3, 2, 0),
341        p1=(0, 0, 0),
342        p2=(3, 0, 0),
343        dimstyle='EZDXF',
344        override=preset,
345    )
346    # still as block names stored
347    assert dimstyle['dimblk'] == arrows.dot_blank
348    assert dimstyle['dimblk1'] == arrows.box
349    assert dimstyle['dimblk2'] == arrows.closed
350    assert dimstyle['dimldrblk'] == arrows.closed_filled
351
352    dstyle_orig = dimstyle.get_dstyle_dict()
353    assert len(dstyle_orig) == 0
354
355    dimstyle.commit()
356    # ezdxf 0.10 and later uses internally only resource names not handles for dim style attributes
357    dstyle = dimstyle.get_dstyle_dict()
358
359    # handle attributes not available, just stored transparent in XDATA
360    assert 'dimblk_handle' not in dstyle
361    assert 'dimblk1_handle' not in dstyle
362    assert 'dimblk2_handle' not in dstyle
363    assert 'dimldrblk_handle' not in dstyle
364
365    assert dstyle['dimblk'] == arrows.dot_blank
366    assert dstyle['dimblk1'] == arrows.box
367    assert dstyle['dimblk2'] == arrows.closed
368    assert dstyle['dimldrblk'] == ''  # special handle for closed filled
369
370    dimstyle.set_arrows(blk=arrows.closed, blk1=arrows.dot_blank,
371                        blk2=arrows.box, ldrblk=arrows.dot_small)
372    assert dimstyle['dimblk'] == arrows.closed
373    assert dimstyle['dimblk1'] == arrows.dot_blank
374    assert dimstyle['dimblk2'] == arrows.box
375    assert dimstyle['dimldrblk'] == arrows.dot_small
376
377    dimstyle.commit()
378    # ezdxf 0.10 and later uses internally only resource names not handles for dim style attributes
379    dstyle = dimstyle.get_dstyle_dict()
380    assert dstyle['dimblk'] == arrows.closed
381    assert dstyle['dimblk1'] == arrows.dot_blank
382    assert dstyle['dimblk2'] == arrows.box
383    # create acad arrows on demand
384    assert dstyle['dimldrblk'] == arrows.dot_small
385