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