1# Copyright (c) 2018-2020 Manfred Moitzi
2# License: MIT License
3import pytest
4import ezdxf
5from ezdxf.layouts import VirtualLayout
6from ezdxf import colors
7from ezdxf.lldxf import const
8from ezdxf.lldxf.tags import Tags
9from ezdxf.lldxf.extendedtags import ExtendedTags
10from ezdxf.math import Matrix44
11
12# noinspection PyProtectedMember
13from ezdxf.entities.mleader import (
14    LeaderLine, Leader, compile_context_tags, MultiLeaderContext, MultiLeader,
15    BlockData,
16)
17from ezdxf.lldxf.tagwriter import TagCollector, basic_tags_from_text
18
19
20@pytest.fixture
21def msp():
22    return VirtualLayout()
23
24
25# todo: real MLEADER tests
26def test_generic_mleader(msp):
27    mleader = msp.new_entity('MLEADER', {})
28    assert mleader.dxftype() == 'MLEADER'
29    assert mleader.dxf.style_handle is None
30
31
32def test_synonym_multileader(msp):
33    mleader = msp.new_entity('MULTILEADER', {})
34    assert mleader.dxftype() == 'MULTILEADER'
35    assert mleader.dxf.style_handle is None
36
37
38# todo: real MLEADERSTYLE tests
39def test_standard_mleader_style():
40    doc = ezdxf.new('R2007')
41    mleader_style = doc.mleader_styles.get('Standard')
42    assert mleader_style.dxftype() == 'MLEADERSTYLE'
43    assert mleader_style.dxf.content_type == 2
44
45
46class TestLeaderLine:
47    @pytest.fixture(scope='class')
48    def tags(self):
49        return Tags.from_text(LEADER_LINE_1)
50
51    def test_parse(self, tags):
52        line = LeaderLine.load(tags)
53        assert len(line.vertices) == 1
54        assert len(line.breaks) == 3
55        assert line.index == 0
56        assert line.color == colors.BY_BLOCK_RAW_VALUE
57
58    def test_export_dxf(self, tags):
59        expected = basic_tags_from_text(LEADER_LINE_1)
60        line = LeaderLine.load(tags)
61        collector = TagCollector()
62        line.export_dxf(collector)
63        assert collector.tags == expected
64
65
66LEADER_LINE_1 = """304
67LEADER_LINE{
68 10
69181.0
70 20
71176.0
72 30
730.0
74 90
750
76 11
77204.0
78 21
79159.0
80 31
810.0
82 12
83206.0
84 22
85158.0
86 32
870.0
88 91
890
90 92
91-1056964608
92305
93}
94"""
95
96
97class TestLeader:
98    @pytest.fixture(scope='class')
99    def tags(self):
100        return Tags.from_text(LEADER_1)
101
102    def test_parse(self, tags):
103        ctx = compile_context_tags(tags, 303)
104        leader = Leader.load(ctx)
105        assert len(leader.lines) == 1
106        assert leader.has_last_leader_line == 1
107        assert leader.has_dogleg_vector == 1
108        assert leader.last_leader_point == (213.9, 199.1, 0)
109        assert leader.dogleg_vector == (1, 0, 0)
110        assert len(leader.breaks) == 2
111        assert leader.dogleg_length == 8.0
112        assert leader.index == 0
113
114    def test_export_dxf(self, tags):
115        expected = basic_tags_from_text(LEADER_1)
116        ctx = compile_context_tags(tags, 303)
117        leader = Leader.load(ctx)
118        collector = TagCollector()
119        leader.export_dxf(collector)
120        assert collector.tags == expected
121
122
123LEADER_1 = """302
124LEADER{
125290
1261
127291
1281
12910
130213.9
13120
132199.1
13330
1340.0
13511
1361.0
13721
1380.0
13931
1400.0
14112
142215.2
14322
144199.1
14532
1460.0
14713
148219.0
14923
150199.1
15133
1520.0
15390
1540
15540
1568.0
157304
158LEADER_LINE{
15910
160195.8
16120
162176.1
16330
1640.0
16591
1660
16792
168-1056964608
169305
170}
171271
1720
173303
174}
175"""
176
177
178class MLeaderTesting:
179    @pytest.fixture(scope='class')
180    def tags(self, text):
181        tags = Tags.from_text(text)
182        return MultiLeader.extract_context_data(tags)
183
184    @pytest.fixture(scope='class')
185    def ctx(self, tags):
186        return MultiLeaderContext.load(compile_context_tags(tags, 301))
187
188    @pytest.fixture(scope='class')
189    def mleader(self, text):
190        return MultiLeader.load(ExtendedTags.from_text(text))
191
192    def test_context_attribs_definition(self, ctx):
193        for name in ctx.ATTRIBS.values():
194            assert hasattr(ctx, name) is True
195
196    def test_mleader_export_dxf(self, text, mleader):
197        expected = basic_tags_from_text(text)
198        collector = TagCollector(dxfversion=const.DXF2010)
199        mleader.export_dxf(collector)
200        assert collector.tags == expected
201
202
203class TestMTextContext(MLeaderTesting):
204    @pytest.fixture(scope='class')
205    def text(self):
206        return MTEXT_MLEADER_R2010
207
208    def test_mtext_data_attribs_definition(self, ctx):
209        mtext = ctx.mtext
210        for name in mtext.ATTRIBS.values():
211            assert hasattr(mtext, name) is True
212
213    def test_load_mtext_context(self, ctx):
214        # Leader() class is tested in TestLeader():
215        assert len(ctx.leaders) == 2
216        assert ctx.scale == 1
217        assert ctx.base_point == (187.4, 185, 0)
218        assert ctx.text_height == 5
219        assert ctx.arrowhead_size == 3
220        assert ctx.landing_gap_size == 2.5
221        assert ctx.left_attachment == 1
222        assert ctx.right_attachment == 1
223        assert ctx.attachment_type == 0
224        assert ctx.mtext is not None  # see test_mtext_data()
225        assert ctx.block is None
226        assert ctx.plane_origin == (1, 2, 3)
227        assert ctx.plane_x_axis == (0, 1, 0)
228        assert ctx.plane_y_axis == (1, 0, 0)
229        assert ctx.plane_normal_reversed == 1
230        assert ctx.top_attachment == 8
231        assert ctx.bottom_attachment == 8
232
233    def test_mtext_data(self, ctx):
234        mtext = ctx.mtext
235        assert mtext.default_content == 'MTEXT-DATA-CONTENT'
236        assert mtext.normal_direction == (1, 0, 0)
237        assert mtext.style_handle == 'FEFE'  # handle of TextStyle() table entry
238        assert mtext.location == (236.6, 187.0, 0)
239        assert mtext.direction == (0, 1, 0)
240        assert mtext.rotation == 0.2  # in radians!
241        assert mtext.boundary_width == 104.6
242        assert mtext.line_space_factor == 1.5
243        assert mtext.line_space_style == 1
244        assert mtext.color == colors.BY_BLOCK_RAW_VALUE
245        assert mtext.alignment == 3
246        assert mtext.flow_direction == 1
247        assert mtext.bg_color == -939524096  # use window background color?
248        assert mtext.bg_scale_factor == 2
249        assert mtext.bg_transparency == 0
250        assert mtext.has_bg_color == 0
251        assert mtext.has_bg_fill == 0
252        assert mtext.column_type == 0
253        assert mtext.use_auto_height == 0
254        assert mtext.column_width == 0.0
255        assert mtext.column_gutter_width == 0.0
256        assert mtext.column_flow_reversed == 0
257        assert len(mtext.column_sizes) == 0
258        assert mtext.use_word_break == 0
259
260
261MTEXT_MLEADER_R2010 = """0
262MULTILEADER
2635
26498
265330
2661F
267100
268AcDbEntity
2698
2700
271100
272AcDbMLeader
273270
2742
275300
276CONTEXT_DATA{
27740
2781.0
27910
280187.4
28120
282185.0
28330
2840.0
28541
2865.0
287140
2883.0
289145
2902.5
291174
2921
293175
2941
295176
2962
297177
2980
299290
3001
301304
302MTEXT-DATA-CONTENT
30311
3041.0
30521
3060.0
30731
3080.0
309340
310FEFE
31112
312236.6
31322
314187.0
31532
3160.0
31713
3180.0
31923
3201.0
32133
3220.0
32342
3240.2
32543
326104.6
32744
3280.0
32945
3301.5
331170
3321
33390
334-1056964608
335171
3363
337172
3381
33991
340-939524096
341141
3422.0
34392
3440
345291
3460
347292
3480
349173
3500
351293
3520
353142
3540.0
355143
3560.0
357294
3580
359295
3600
361296
3620
363110
3641.0
365120
3662.0
367130
3683.0
369111
3700.0
371121
3721.0
373131
3740.0
375112
3761.0
377122
3780.0
379132
3800.0
381297
3821
383302
384LEADER{
385290
3861
387291
3881
38910
390246.6
39120
392185.0
39330
3940.0
39511
396-1.0
39721
3980.0
39931
4000.0
40190
4020
40340
4048.0
405304
406LEADER_LINE{
40710
408287.3
40920
410220.5
41130
4120.0
41391
4140
41592
416-1056964608
417305
418}
419271
4200
421303
422}
423302
424LEADER{
425290
4261
427291
4281
42910
430179.4
43120
432185.0
43330
4340.0
43511
4361.0
43721
4380.0
43931
4400.0
44190
4421
44340
4448.0
445304
446LEADER_LINE{
44710
448146.5
44920
450149.0
45130
4520.0
45391
4541
45592
456-1056964608
457305
458}
459271
4600
461303
462}
463272
4648
465273
4668
467301
468}
469340
4706D
47190
472330752
473170
4741
47591
476-1056964608
477341
47814
479171
480-2
481290
4821
483291
4841
48541
4868.0
48742
4884.0
489172
4902
491343
49211
493173
4941
49595
4961
497174
4981
499175
5000
50192
502-1056964608
503292
5040
50593
506-1056964608
50710
5081.0
50920
5101.0
51130
5121.0
51343
5140.0
515176
5160
517293
5180
519294
5200
521178
5220
523179
5241
52545
5261.0
527271
5280
529272
5309
531273
5329
533"""
534
535
536class TestBlockContext(MLeaderTesting):
537    @pytest.fixture(scope='class')
538    def text(self):
539        return BLOCK_MLEADER_R2010
540
541    def test_block_data_attribs_definition(self, ctx):
542        block = ctx.block
543        for name in block.ATTRIBS.values():
544            assert hasattr(block, name) is True
545
546    def test_load_block_context(self, ctx):
547        # Leader() class is tested in TestLeader():
548        assert len(ctx.leaders) == 1
549        assert ctx.scale == 1
550        assert ctx.base_point == (8.42, 0.70, 0)
551        assert ctx.text_height == 5
552        assert ctx.arrowhead_size == 3
553        assert ctx.landing_gap_size == 2.5
554        assert ctx.left_attachment == 1
555        assert ctx.right_attachment == 1
556        assert ctx.attachment_type == 0
557        assert ctx.mtext is None
558        assert ctx.block is not None  # see test_block_data()
559        assert ctx.plane_origin == (1, 2, 3)
560        assert ctx.plane_x_axis == (0, 1, 0)
561        assert ctx.plane_y_axis == (1, 0, 0)
562        assert ctx.plane_normal_reversed == 1
563        assert ctx.top_attachment == 8
564        assert ctx.bottom_attachment == 8
565
566    def test_block_data(self, ctx):
567        block = ctx.block
568        assert block.block_record_handle == 'FEFE'
569        assert block.normal_direction == (0, 0, 1)
570        assert block.location == (18.42, 0.70, 0)
571        assert block.scale == (1.0, 2.0, 3.0)
572        assert block.rotation == 0.2
573        assert block.color == colors.BY_BLOCK_RAW_VALUE
574
575    def test_get_transformation_matrix(self, ctx):
576        # The transformation matrix is stored in transposed order
577        # of ezdxf.math.Matrix44()!
578        assert ctx.block._matrix == [
579            1, 0, 0, 18.42,
580            0, 1, 0, 0.70,
581            0, 0, 1, 0,
582            0, 0, 0, 1,
583        ]
584        assert ctx.block.matrix44.get_row(3) == (18.42, 0.70, 0, 1)
585
586    def test_set_transformation_matrix(self):
587        m = Matrix44()
588        m.set_row(3, (4, 3, 2, 1))
589        block = BlockData()
590        block.matrix44 = m
591        # The transformation matrix is stored in transposed order
592        # of ezdxf.math.Matrix44()!
593        assert block._matrix == [
594            1, 0, 0, 4,
595            0, 1, 0, 3,
596            0, 0, 1, 2,
597            0, 0, 0, 1,
598        ]
599
600
601BLOCK_MLEADER_R2010 = """  0
602MULTILEADER
6035
604B5
605330
6061F
607100
608AcDbEntity
6098
6100
611100
612AcDbMLeader
613270
6142
615300
616CONTEXT_DATA{
61740
6181.0
61910
6208.42
62120
6220.70
62330
6240.0
62541
6265.0
627140
6283.0
629145
6302.5
631174
6321
633175
6341
635176
6360
637177
6380
639290
6400
641296
6421
643341
644FEFE
64514
6460.0
64724
6480.0
64934
6501.0
65115
65218.42
65325
6540.70
65535
6560.0
65716
6581.0
65926
6602.0
66136
6623.0
66346
6640.2
66593
666-1056964608
66747
6681.0
66947
6700.0
67147
6720.0
67347
67418.42
67547
6760.0
67747
6781.0
67947
6800.0
68147
6820.70
68347
6840.0
68547
6860.0
68747
6881.0
68947
6900.0
69147
6920.0
69347
6940.0
69547
6960.0
69747
6981.0
699110
7001.0
701120
7022.0
703130
7043.0
705111
7060.0
707121
7081.0
709131
7100.0
711112
7121.0
713122
7140.0
715132
7160.0
717297
7181
719302
720LEADER{
721290
7221
723291
7241
72510
7269.42
72720
7280.70
72930
7300.0
73111
7321.0
73321
7340.0
73531
7360.0
73790
7380
73940
7408.0
741304
742LEADER_LINE{
74310
7441.15
74520
746-10.40
74730
7480.0
74991
7500
75192
752-1056964608
753305
754}
755271
7560
757303
758}
759272
7608
761273
7628
763301
764}
765340
7666D
76790
7686816768
769170
7701
77191
772-1056964608
773341
77414
775171
776-2
777290
7781
779291
7801
78141
7828.0
78342
7844.0
785172
7861
787343
78811
789173
7901
79195
7921
793174
7941
795175
7960
79792
798-1056964608
799292
8000
801344
80294
80393
804-1056964608
80510
8061.0
80720
8081.0
80930
8101.0
81143
8120.0
813176
8140
815293
8160
817330
818A3
819177
8201
82144
8220.0
823302
824B
825294
8260
827178
8280
829179
8301
83145
8321.0
833271
8340
835272
8369
837273
8389
839"""
840