acadrust 0.5.5

A pure Rust library for reading and writing CAD files in DXF format (ASCII and Binary) and DWG format (Binary).
Documentation
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
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
use crate::error::Result;
use crate::io::dxf::writer::stream_writer::{DxfStreamWriter, DxfStreamWriterExt};
use crate::objects::*;
use crate::types::Handle;

use super::SectionWriter;

impl<'a, W: DxfStreamWriter> SectionWriter<'a, W> {
    fn write_assoc_header(&mut self, value: &AssociativeObject) -> Result<()> {
        self.writer.write_string(0, &value.dxf_name)?;
        self.writer.write_handle(5, value.handle)?;
        if !value.reactors.is_empty() {
            self.writer.write_string(102, "{ACAD_REACTORS")?;
            for reactor in &value.reactors {
                self.writer.write_handle(330, *reactor)?;
            }
            self.writer.write_string(102, "}")?;
        }
        if let Some(xdictionary) = value.xdictionary_handle {
            self.writer.write_string(102, "{ACAD_XDICTIONARY")?;
            self.writer.write_handle(360, xdictionary)?;
            self.writer.write_string(102, "}")?;
        }
        self.writer.write_handle(330, value.owner)?;
        Ok(())
    }

    fn write_assoc_eval(&mut self, value: &AssocEvalVariant) -> Result<()> {
        let code = |fallback: i32| {
            if value.code == 0 {
                fallback
            } else {
                value.code as i32
            }
        };
        match &value.value {
            AssocEvalValue::None => {}
            AssocEvalValue::Real(item) => {
                self.writer.write_double(code(40), *item)?;
            }
            AssocEvalValue::Long(item) => {
                self.writer.write_i32(code(90), *item)?;
            }
            AssocEvalValue::Short(item) => {
                self.writer.write_i16(code(70), *item)?;
            }
            AssocEvalValue::Byte(item) => {
                self.writer.write_byte(code(280), *item)?;
            }
            AssocEvalValue::Text(item) => {
                self.writer.write_string(code(1), item)?;
            }
            AssocEvalValue::Handle(item) => {
                self.writer.write_handle(code(330), *item)?;
            }
        }
        Ok(())
    }

    fn write_assoc_value_param(&mut self, value: &AssocValueParam) -> Result<()> {
        self.writer.write_i32(90, value.class_version)?;
        self.writer.write_string(1, &value.name)?;
        self.writer.write_i32(90, value.unit_type)?;
        self.writer.write_i32(90, value.variables.len() as i32)?;
        for variable in &value.variables {
            self.write_assoc_eval(&variable.value)?;
            self.writer.write_handle(330, variable.handle)?;
        }
        self.writer
            .write_handle(330, value.controlled_object_dependency)?;
        Ok(())
    }

    fn write_assoc_dependency(&mut self, value: &AssocDependency) -> Result<()> {
        self.writer.write_subclass("AcDbAssocDependency")?;
        self.writer.write_i16(90, value.class_version)?;
        self.writer.write_i32(90, value.status)?;
        self.writer.write_bool(290, value.is_read_dependency)?;
        self.writer.write_bool(290, value.is_write_dependency)?;
        self.writer.write_bool(290, value.is_attached_to_object)?;
        self.writer
            .write_bool(290, value.is_delegating_to_owning_action)?;
        self.writer.write_i32(90, value.order)?;
        self.writer.write_handle(330, value.dependent_on)?;
        self.writer.write_bool(290, value.name.is_some())?;
        if let Some(name) = &value.name {
            self.writer.write_string(1, name)?;
        }
        self.writer.write_handle(330, value.read_dependency)?;
        self.writer.write_handle(330, value.node)?;
        self.writer.write_handle(360, value.dependency_body)?;
        self.writer.write_i32(90, value.dependency_body_id)?;
        Ok(())
    }

    fn write_constraint_common(&mut self, node: &AssocConstraintNode) -> Result<()> {
        self.writer.write_i32(90, node.node_id)?;
        if self.dxf_version < crate::types::DxfVersion::AC1027 {
            self.writer.write_byte(70, node.status)?;
        }
        self.writer.write_i32(90, node.connections.len() as i32)?;
        for connection in &node.connections {
            self.writer.write_i32(90, *connection)?;
        }
        if self.dxf_version >= crate::types::DxfVersion::AC1027 {
            self.writer.write_byte(70, node.status)?;
        }
        Ok(())
    }

    fn write_constraint_data(&mut self, value: &AssocConstraintNodeData) -> Result<()> {
        match value {
            AssocConstraintNodeData::None => {}
            AssocConstraintNodeData::Geometrical {
                owner_id,
                is_implied,
                is_active,
            } => {
                self.writer.write_i32(90, *owner_id)?;
                self.writer.write_bool(290, *is_implied)?;
                self.writer.write_bool(290, *is_active)?;
            }
            AssocConstraintNodeData::Angle {
                owner_id,
                is_implied,
                is_active,
                value_dependency,
                dimension_dependency,
                sector_type,
            } => {
                self.writer.write_i32(90, *owner_id)?;
                self.writer.write_bool(290, *is_implied)?;
                self.writer.write_bool(290, *is_active)?;
                self.writer.write_handle(340, *value_dependency)?;
                self.writer.write_handle(340, *dimension_dependency)?;
                self.writer.write_byte(280, *sector_type)?;
            }
            AssocConstraintNodeData::Parallel {
                owner_id,
                is_implied,
                is_active,
                datum_line_index,
            } => {
                self.writer.write_i32(90, *owner_id)?;
                self.writer.write_bool(290, *is_implied)?;
                self.writer.write_bool(290, *is_active)?;
                if let Some(datum_line_index) = datum_line_index {
                    self.writer.write_i32(90, *datum_line_index)?;
                }
            }
            AssocConstraintNodeData::Distance {
                owner_id,
                is_implied,
                is_active,
                value_dependency,
                dimension_dependency,
                direction_type,
                distance,
            } => {
                self.writer.write_i32(90, *owner_id)?;
                self.writer.write_bool(290, *is_implied)?;
                self.writer.write_bool(290, *is_active)?;
                self.writer.write_handle(340, *value_dependency)?;
                self.writer.write_handle(340, *dimension_dependency)?;
                self.writer.write_byte(280, *direction_type)?;
                if let Some(distance) = distance {
                    self.writer.write_point3d(10, *distance)?;
                }
            }
            AssocConstraintNodeData::RadiusDiameter {
                owner_id,
                is_implied,
                is_active,
                value_dependency,
                dimension_dependency,
                mode,
            } => {
                self.writer.write_i32(90, *owner_id)?;
                self.writer.write_bool(290, *is_implied)?;
                self.writer.write_bool(290, *is_active)?;
                self.writer.write_handle(340, *value_dependency)?;
                self.writer.write_handle(340, *dimension_dependency)?;
                self.writer.write_byte(280, *mode)?;
            }
            AssocConstraintNodeData::ImplicitPoint {
                geometry_dependency,
                geometry_node_id,
                point,
                point_type,
                point_index,
                curve_id,
            } => {
                self.writer.write_handle(330, *geometry_dependency)?;
                self.writer.write_i32(90, *geometry_node_id)?;
                if let Some(point) = point {
                    self.writer.write_point3d(10, *point)?;
                }
                self.writer.write_byte(280, *point_type)?;
                self.writer.write_i32(90, *point_index)?;
                self.writer.write_i32(90, *curve_id)?;
            }
            AssocConstraintNodeData::Point {
                geometry_dependency,
                geometry_node_id,
                point,
            } => {
                self.writer.write_handle(330, *geometry_dependency)?;
                self.writer.write_i32(90, *geometry_node_id)?;
                if let Some(point) = point {
                    self.writer.write_point3d(10, *point)?;
                }
            }
            AssocConstraintNodeData::Line {
                geometry_dependency,
                geometry_node_id,
                point,
                direction,
            } => {
                self.writer.write_handle(330, *geometry_dependency)?;
                self.writer.write_i32(90, *geometry_node_id)?;
                self.writer.write_point3d(10, *point)?;
                self.writer.write_point3d(10, *direction)?;
            }
            AssocConstraintNodeData::BoundedLine {
                geometry_dependency,
                geometry_node_id,
                point,
                direction,
                is_ray,
                start_point,
                end_point,
            } => {
                self.writer.write_handle(330, *geometry_dependency)?;
                self.writer.write_i32(90, *geometry_node_id)?;
                self.writer.write_point3d(10, *point)?;
                self.writer.write_point3d(10, *direction)?;
                self.writer.write_bool(290, *is_ray)?;
                self.writer.write_point3d(10, *start_point)?;
                self.writer.write_point3d(11, *end_point)?;
            }
            AssocConstraintNodeData::Circle {
                geometry_dependency,
                geometry_node_id,
                center,
                normal,
                direction,
                radius,
                start_parameter,
                end_parameter,
                reserved,
            } => {
                self.writer.write_handle(330, *geometry_dependency)?;
                self.writer.write_i32(90, *geometry_node_id)?;
                self.writer.write_point3d(10, *center)?;
                self.writer.write_point3d(10, *normal)?;
                self.writer.write_point3d(10, *direction)?;
                self.writer.write_double(40, *radius)?;
                self.writer.write_double(40, *start_parameter)?;
                self.writer.write_double(40, *end_parameter)?;
                self.writer.write_double(40, *reserved)?;
            }
            AssocConstraintNodeData::Arc {
                geometry_dependency,
                geometry_node_id,
                center,
                normal,
                direction,
                radius,
                start_parameter,
                end_parameter,
                reserved,
                start_point,
                end_point,
            } => {
                self.writer.write_handle(330, *geometry_dependency)?;
                self.writer.write_i32(90, *geometry_node_id)?;
                self.writer.write_point3d(10, *center)?;
                self.writer.write_point3d(10, *normal)?;
                self.writer.write_point3d(10, *direction)?;
                self.writer.write_double(40, *radius)?;
                self.writer.write_double(40, *start_parameter)?;
                self.writer.write_double(40, *end_parameter)?;
                self.writer.write_double(40, *reserved)?;
                self.writer.write_point3d(10, *start_point)?;
                self.writer.write_point3d(11, *end_point)?;
            }
            AssocConstraintNodeData::Ellipse {
                owner_id,
                is_implied,
                is_active,
                center,
                short_axis,
                axis_ratio,
            } => {
                self.writer.write_i32(90, *owner_id)?;
                self.writer.write_bool(290, *is_implied)?;
                self.writer.write_bool(290, *is_active)?;
                self.writer.write_point3d(10, *center)?;
                self.writer.write_point3d(11, *short_axis)?;
                self.writer.write_double(40, *axis_ratio)?;
            }
            AssocConstraintNodeData::BoundedEllipse {
                owner_id,
                is_implied,
                is_active,
                center,
                short_axis,
                axis_ratio,
                start_point,
                end_point,
            } => {
                self.writer.write_i32(90, *owner_id)?;
                self.writer.write_bool(290, *is_implied)?;
                self.writer.write_bool(290, *is_active)?;
                self.writer.write_point3d(10, *center)?;
                self.writer.write_point3d(11, *short_axis)?;
                self.writer.write_double(40, *axis_ratio)?;
                self.writer.write_point3d(10, *start_point)?;
                self.writer.write_point3d(11, *end_point)?;
            }
        }
        Ok(())
    }

    fn write_assoc_action(&mut self, value: &AssocAction) -> Result<()> {
        self.writer.write_subclass("AcDbAssocAction")?;
        self.writer.write_i16(90, value.class_version)?;
        self.writer.write_i32(90, value.geometry_status)?;
        self.writer.write_handle(330, value.owning_network)?;
        self.writer.write_handle(360, value.action_body)?;
        self.writer.write_i32(90, value.action_index)?;
        self.writer.write_i32(90, value.max_dependency_index)?;
        self.writer.write_i32(90, value.dependencies.len() as i32)?;
        for dependency in &value.dependencies {
            self.writer.write_handle(
                if dependency.is_owned { 360 } else { 330 },
                dependency.dependency,
            )?;
        }
        if value.class_version > 1 {
            self.writer.write_i16(90, 0)?;
            self.writer
                .write_i32(90, value.owned_parameters.len() as i32)?;
            for parameter in &value.owned_parameters {
                self.writer.write_handle(360, *parameter)?;
            }
            self.writer.write_i16(90, 0)?;
            self.writer.write_i32(90, value.values.len() as i32)?;
            for item in &value.values {
                self.write_assoc_value_param(item)?;
            }
        }
        Ok(())
    }

    fn write_assoc_action_param(&mut self, value: &AssocActionParam) -> Result<()> {
        self.writer.write_subclass("AcDbAssocActionParam")?;
        self.writer.write_i16(90, value.is_r2013)?;
        if self.dxf_version >= crate::types::DxfVersion::AC1027 {
            self.writer.write_i32(90, value.version)?;
        }
        self.writer.write_string(1, &value.name)?;
        Ok(())
    }

    fn write_assoc_action_body(&mut self, value: &AssocActionBody) -> Result<()> {
        self.writer.write_subclass("AcDbAssocActionBody")?;
        self.writer.write_i32(90, value.version)
    }

    fn write_assoc_parameter_body(&mut self, value: &AssocParamBasedActionBody) -> Result<()> {
        if value.version == 0
            && value.minor == 0
            && value.dependencies.is_empty()
            && value.values.is_empty()
            && value.dependency.is_null()
        {
            return Ok(());
        }
        self.writer
            .write_subclass("AcDbAssocParamBasedActionBody")?;
        self.writer.write_i32(90, value.version)?;
        self.writer.write_i32(90, value.minor)?;
        self.writer.write_i32(90, value.dependencies.len() as i32)?;
        for dependency in &value.dependencies {
            self.writer.write_handle(360, *dependency)?;
        }
        self.writer.write_i32(90, value.marker)?;
        self.writer.write_i32(90, value.values.len() as i32)?;
        if value.values.is_empty() {
            self.writer.write_i32(90, value.empty_value_marker)?;
            self.writer.write_handle(330, value.dependency)?;
        }
        for item in &value.values {
            self.write_assoc_value_param(item)?;
        }
        Ok(())
    }

    fn write_assoc_surface(&mut self, value: &AssocSurfaceActionBody) -> Result<()> {
        self.write_assoc_action_body(&value.action_body)?;
        self.write_assoc_parameter_body(&value.parameter_body)?;
        self.writer.write_subclass("AcDbAssocSurfaceActionBody")?;
        self.writer.write_i32(90, value.surface_body.version)?;
        self.writer
            .write_handle(330, value.surface_body.dependency)?;
        self.writer
            .write_bool(290, value.surface_body.is_semi_associative)?;
        self.writer.write_i32(90, value.surface_body.marker)?;
        self.writer
            .write_bool(290, value.surface_body.is_semi_override)?;
        self.writer.write_i16(70, value.surface_body.grip_status)?;
        self.writer
            .write_subclass("AcDbAssocPathBasedSurfaceActionBody")?;
        self.writer.write_i32(90, value.path_status)?;
        let section = match value.kind {
            AssocSurfaceActionKind::Plane => "AcDbAssocPlaneSurfaceActionBody",
            AssocSurfaceActionKind::Extend => "AcDbAssocExtendSurfaceActionBody",
            AssocSurfaceActionKind::Extruded => "AcDbAssocExtrudedSurfaceActionBody",
            AssocSurfaceActionKind::Lofted => "AcDbAssocLoftedSurfaceActionBody",
            AssocSurfaceActionKind::Network => "AcDbAssocNetworkSurfaceActionBody",
            AssocSurfaceActionKind::Offset => "AcDbAssocOffsetSurfaceActionBody",
            AssocSurfaceActionKind::Revolved => "AcDbAssocRevolvedSurfaceActionBody",
            AssocSurfaceActionKind::Trim => "AcDbAssocTrimSurfaceActionBody",
            AssocSurfaceActionKind::Blend => "AcDbAssocBlendSurfaceActionBody",
            AssocSurfaceActionKind::Patch => "AcDbAssocPatchSurfaceActionBody",
            AssocSurfaceActionKind::Fillet => "AcDbAssocFilletSurfaceActionBody",
            AssocSurfaceActionKind::Swept => "AcDbAssocSweptSurfaceActionBody",
            AssocSurfaceActionKind::EdgeChamfer => "AcDbAssocEdgeChamferActionBody",
            AssocSurfaceActionKind::EdgeFillet => "AcDbAssocEdgeFilletActionBody",
        };
        self.writer.write_subclass(section)?;
        if !matches!(
            value.kind,
            AssocSurfaceActionKind::EdgeChamfer | AssocSurfaceActionKind::EdgeFillet
        ) {
            self.writer.write_i32(90, value.class_version)?;
        }
        match value.kind {
            AssocSurfaceActionKind::Extend => self.writer.write_byte(280, value.option)?,
            AssocSurfaceActionKind::Offset => self.writer.write_bool(290, value.flags[0])?,
            AssocSurfaceActionKind::Trim => {
                self.writer.write_bool(290, value.flags[0])?;
                self.writer.write_bool(290, value.flags[1])?;
                self.writer.write_double(40, value.distance)?;
            }
            AssocSurfaceActionKind::Blend => {
                self.writer.write_bool(290, value.flags[0])?;
                self.writer.write_bool(291, value.flags[1])?;
                self.writer.write_bool(292, value.flags[2])?;
                self.writer.write_i16(72, value.status)?;
                self.writer.write_bool(293, value.flags[3])?;
                self.writer.write_bool(294, value.flags[4])?;
                self.writer.write_i16(73, value.secondary_status)?;
            }
            AssocSurfaceActionKind::Fillet => {
                self.writer.write_i16(70, value.status)?;
                self.writer.write_point2d(10, value.first_point)?;
                self.writer.write_point2d(10, value.second_point)?;
            }
            _ => {}
        }
        Ok(())
    }

    fn write_assoc_annotation_base(&mut self, value: &AssocAnnotationBase) -> Result<()> {
        self.write_assoc_action_body(&value.action_body)?;
        self.writer.write_i16(90, value.version)?;
        self.writer.write_handle(330, value.dependency)?;
        Ok(())
    }

    fn write_assoc_annotation(&mut self, value: &AssocAnnotationActionBody) -> Result<()> {
        if value.kind == AssocAnnotationKind::RestoreEntityState {
            self.write_assoc_action_body(&value.action_body)?;
            self.writer
                .write_subclass("AcDbAssocRestoreEntityStateActionBody")?;
            self.writer.write_i32(90, value.class_version)?;
            self.writer.write_handle(330, value.entity)?;
            return Ok(());
        }
        self.write_assoc_annotation_base(&value.annotation)?;
        let section = match value.kind {
            AssocAnnotationKind::MLeader => "AcDbAssocMLeaderActionBody",
            AssocAnnotationKind::AlignedDimension => "ACDBASSOCALIGNEDDIMACTIONBODY",
            AssocAnnotationKind::ThreePointAngularDimension => "Assoc3PointAngularDimActionBody",
            AssocAnnotationKind::OrdinateDimension => "AssocOrdinatedDimActionBody",
            AssocAnnotationKind::RotatedDimension => "AssocRotatedDimActionBody",
            AssocAnnotationKind::RestoreEntityState => unreachable!(),
        };
        self.writer.write_subclass(section)?;
        self.writer.write_i32(90, value.class_version)?;
        match value.kind {
            AssocAnnotationKind::MLeader => {
                self.writer.write_i32(90, value.actions.len() as i32)?;
                for action in &value.actions {
                    self.writer.write_i32(90, action.dependency_id)?;
                    self.writer.write_handle(330, action.dependency)?;
                }
            }
            AssocAnnotationKind::AlignedDimension
            | AssocAnnotationKind::OrdinateDimension
            | AssocAnnotationKind::RotatedDimension => {
                self.writer.write_handle(330, value.read_node)?;
                self.writer.write_handle(330, value.dimension_node)?;
            }
            AssocAnnotationKind::ThreePointAngularDimension => {
                self.writer.write_handle(330, value.read_node)?;
                self.writer.write_handle(330, value.dimension_node)?;
                self.writer.write_handle(330, value.dependency)?;
            }
            AssocAnnotationKind::RestoreEntityState => {}
        }
        Ok(())
    }

    fn write_assoc_single_dependency(
        &mut self,
        value: &AssocSingleDependencyActionParam,
        leaf: &str,
    ) -> Result<()> {
        self.write_assoc_action_param(&value.action_param)?;
        self.writer
            .write_subclass("AcDbAssocSingleDependencyActionParam")?;
        self.writer.write_i32(90, value.dependency_class_version)?;
        self.writer.write_handle(330, value.dependency)?;
        self.writer.write_subclass(leaf)?;
        self.writer.write_i32(90, value.class_version)?;
        Ok(())
    }

    fn write_assoc_compound(&mut self, value: &AssocCompoundActionParam) -> Result<()> {
        self.write_assoc_action_param(&value.action_param)?;
        self.writer.write_subclass("AcDbAssocCompoundActionParam")?;
        self.writer.write_i16(90, value.class_version)?;
        self.writer.write_i16(90, value.status)?;
        self.writer.write_i32(90, value.parameters.len() as i32)?;
        for parameter in &value.parameters {
            self.writer.write_handle(360, *parameter)?;
        }
        if let Some(child) = &value.child_parameter {
            self.writer.write_i16(90, child.status)?;
            self.writer.write_i32(90, child.id)?;
            self.writer.write_handle(330, child.parameter)?;
            if child.id != 0 {
                self.writer.write_handle(330, child.secondary_parameter)?;
                self.writer.write_i32(90, child.marker)?;
                self.writer.write_handle(330, child.tertiary_parameter)?;
            }
        }
        Ok(())
    }

    fn write_assoc_array_body(&mut self, value: &AssocArrayActionBody) -> Result<()> {
        self.write_assoc_action_body(&value.action_body)?;
        self.write_assoc_parameter_body(&value.parameter_body)?;
        self.writer.write_subclass("AcDbAssocArrayActionBody")?;
        self.writer.write_i32(90, value.version)?;
        self.writer.write_string(1, &value.parameter_block)?;
        for item in value.transform {
            self.writer.write_double(40, item)?;
        }
        Ok(())
    }

    fn write_assoc_array_parameters(&mut self, value: &AssocArrayParameters) -> Result<()> {
        self.writer
            .write_subclass("AcDbAssocArrayCommonParameters")?;
        self.writer.write_i32(90, value.version)?;
        self.writer.write_i32(90, value.items.len() as i32)?;
        self.writer.write_string(1, &value.class_name)?;
        for item in &value.items {
            self.writer.write_i32(90, item.class_version)?;
            for location in item.location {
                self.writer.write_i32(90, location)?;
            }
            self.writer.write_i32(90, item.flags)?;
            if item.uses_default_transform {
                self.writer.write_point3d(11, item.x_direction)?;
            } else {
                for matrix_value in item.transform {
                    self.writer.write_double(40, matrix_value)?;
                }
            }
            if let Some(matrix) = item.relative_transform {
                for matrix_value in matrix {
                    self.writer.write_double(40, matrix_value)?;
                }
            }
            if let Some(first) = item.first_handle {
                self.writer.write_handle(330, first)?;
            }
            if item.flags & 0x10 != 0 {
                self.writer
                    .write_handle(330, item.second_handle.unwrap_or(Handle::NULL))?;
            }
        }
        self.writer.write_i32(90, value.item_count)?;
        self.writer.write_i32(90, value.row_count)?;
        self.writer.write_i32(90, value.level_count)?;
        Ok(())
    }

    fn write_dimension_association_dxf(&mut self, value: &AssocDimensionAssociation) -> Result<()> {
        self.writer.write_subclass("AcDbDimAssoc")?;
        self.writer.write_handle(330, value.dimension)?;
        self.writer.write_i32(90, value.associativity)?;
        self.writer.write_bool(70, value.trans_space)?;
        self.writer.write_byte(71, value.rotated_type)?;
        for slot in 0..4 {
            if value.associativity & (1 << slot) == 0 {
                continue;
            }
            let fallback = AssocDimensionReference::default();
            let stored = value
                .references
                .get(slot)
                .map(Vec::as_slice)
                .unwrap_or_default();
            let references = if stored.is_empty() {
                std::slice::from_ref(&fallback)
            } else {
                stored
            };
            for (index, reference) in references.iter().enumerate() {
                self.writer.write_string(1, &reference.class_name)?;
                self.writer.write_byte(72, reference.osnap_type)?;
                for xref in &reference.xrefs {
                    self.writer.write_handle(331, *xref)?;
                }
                if reference.osnap_type != 0 {
                    self.writer.write_i32(73, reference.main_subent_type)?;
                    self.writer.write_i32(91, reference.main_gs_marker)?;
                    for path in &reference.xref_paths {
                        self.writer.write_string(301, path)?;
                    }
                }
                self.writer.write_double(40, reference.osnap_distance)?;
                self.writer.write_point3d(10, reference.osnap_point)?;
                if reference.osnap_type == 6 || reference.osnap_type == 11 {
                    for object in &reference.intersection_objects {
                        self.writer.write_handle(332, *object)?;
                    }
                    self.writer
                        .write_i32(74, reference.intersection_subent_type)?;
                    self.writer
                        .write_i32(92, reference.intersection_gs_marker)?;
                    for path in &reference.intersection_xref_paths {
                        self.writer.write_string(302, path)?;
                    }
                }
                self.writer.write_bool(75, index + 1 < references.len())?;
            }
        }
        Ok(())
    }

    fn write_static_pers_subent_manager_dxf(&mut self, value: &PersSubentManager) -> Result<()> {
        self.writer.write_subclass("AcDbPersSubentManager")?;
        self.writer.write_i32(90, value.class_version)?;
        self.writer.write_i32(90, value.marker_zero)?;
        self.writer.write_i32(90, value.marker_two)?;
        self.writer.write_i32(90, value.associative_step_count)?;
        self.writer.write_i32(90, value.associative_subent_count)?;
        self.writer.write_i32(90, value.steps.len() as i32)?;
        for step in &value.steps {
            self.writer.write_i32(90, *step)?;
        }
        if value.associative_subent_count != 0 || !value.subents.is_empty() {
            self.writer.write_i32(90, value.subents.len() as i32)?;
            for subent in &value.subents {
                self.writer.write_i32(90, *subent)?;
            }
        }
        Ok(())
    }

    pub(super) fn write_associative_object_dxf(
        &mut self,
        object: &AssociativeObject,
    ) -> Result<()> {
        self.write_assoc_header(object)?;
        match &object.data {
            AssociativeData::Unknown => {}
            AssociativeData::Dependency(value) => self.write_assoc_dependency(value)?,
            AssociativeData::ValueDependency(value) => {
                self.write_assoc_dependency(&value.dependency)?;
                self.writer.write_subclass("AcDbAssocValueDependency")?;
                self.writer.write_i32(90, value.class_version)?;
                self.writer.write_string(1, &value.name)?;
                self.write_assoc_eval(&value.value)?;
            }
            AssociativeData::GeomDependency(value) => {
                self.write_assoc_dependency(&value.dependency)?;
                self.writer.write_subclass("AcDbAssocGeomDependency")?;
                self.writer.write_i16(90, value.class_version)?;
                self.writer.write_bool(290, value.enabled)?;
                self.writer.write_subclass("AcDbAssocPersSubentId")?;
                self.writer
                    .write_string(1, &value.persistent_subent.class_name)?;
                self.writer
                    .write_bool(290, value.persistent_subent.dependent_on_compound_object)?;
            }
            AssociativeData::SurfaceActionBody(value) => self.write_assoc_surface(value)?,
            AssociativeData::Action(value) => self.write_assoc_action(value)?,
            AssociativeData::Network(value) => {
                self.write_assoc_action(&value.action)?;
                self.writer.write_subclass("AcDbAssocNetwork")?;
                self.writer.write_i16(90, value.network_version)?;
                self.writer.write_i32(90, value.network_action_index)?;
                self.writer.write_i32(90, value.actions.len() as i32)?;
                for action in &value.actions {
                    self.writer
                        .write_handle(if action.is_owned { 360 } else { 330 }, action.dependency)?;
                }
                self.writer
                    .write_i32(90, value.owned_actions.len() as i32)?;
                for action in &value.owned_actions {
                    self.writer.write_handle(330, *action)?;
                }
            }
            AssociativeData::AnnotationActionBody(value) => self.write_assoc_annotation(value)?,
            AssociativeData::PersSubentManager(value) => {
                self.writer.write_subclass("AcDbAssocPersSubentManager")?;
                self.writer.write_i32(90, value.class_version)?;
                for marker in value.markers {
                    self.writer.write_i32(90, marker)?;
                }
                self.writer.write_i32(90, value.steps.len() as i32)?;
                for step in &value.steps {
                    self.writer.write_i32(90, *step)?;
                }
                self.writer.write_i32(90, value.subent_count)?;
                for item in &value.subent_data {
                    self.writer.write_i32(90, *item)?;
                }
                self.writer.write_bool(290, value.final_flag)?;
            }
            AssociativeData::EdgeActionParam(value) => {
                self.write_assoc_single_dependency(
                    &value.single_dependency,
                    "AcDbAssocEdgeActionParam",
                )?;
                self.writer.write_handle(330, value.parameter)?;
                self.writer.write_bool(290, value.has_action)?;
                self.writer.write_i32(90, value.action_type)?;
            }
            AssociativeData::ConstraintGroup(value) => {
                self.write_assoc_action(&value.action)?;
                self.writer.write_subclass("AcDbAssoc2dConstraintGroup")?;
                self.writer.write_i32(90, value.version)?;
                self.writer.write_bool(70, value.flag)?;
                for point in value.work_plane {
                    self.writer.write_point3d(10, point)?;
                }
                self.writer.write_handle(360, value.dependency)?;
                self.writer.write_i32(90, value.actions.len() as i32)?;
                for action in &value.actions {
                    self.writer.write_handle(360, *action)?;
                }
                let root = value.nodes.iter().find(|node| node.class_name.is_empty());
                let registered: Vec<&AssocConstraintNode> = value
                    .nodes
                    .iter()
                    .filter(|node| !node.class_name.is_empty())
                    .collect();
                self.writer.write_i32(90, registered.len() as i32 + 1)?;
                if let Some(root) = root {
                    self.writer.write_i32(90, root.node_id)?;
                    self.writer.write_i32(90, root.connections.len() as i32)?;
                    for connection in &root.connections {
                        self.writer.write_i32(90, *connection)?;
                    }
                    self.writer.write_bool(290, root.status != 0)?;
                } else {
                    self.writer.write_i32(90, 0)?;
                    self.writer.write_i32(90, 0)?;
                    self.writer.write_bool(290, false)?;
                }
                self.writer.write_i32(90, registered.len() as i32)?;
                for node in &registered {
                    self.writer.write_string(1, &node.class_name)?;
                    self.writer.write_i32(90, node.node_id)?;
                }
                for node in registered {
                    self.write_constraint_common(node)?;
                    self.write_constraint_data(&node.data)?;
                }
            }
            AssociativeData::Variable(value) => {
                self.write_assoc_action(&value.action)?;
                self.writer.write_subclass("AcDbAssocVariable")?;
                self.writer.write_i32(90, value.class_version)?;
                self.writer.write_string(1, &value.name)?;
                self.writer.write_string(1, &value.expression)?;
                self.writer.write_string(1, &value.evaluator)?;
                self.writer.write_string(1, &value.description)?;
                self.write_assoc_eval(&value.value)?;
                self.writer.write_bool(290, value.has_cached_value)?;
                if value.has_cached_value {
                    self.writer.write_string(1, &value.cached_value)?;
                }
                self.writer.write_bool(290, value.flag)?;
                self.writer.write_i32(90, value.reserved)?;
            }
            AssociativeData::ActionParam(value) => self.write_assoc_action_param(value)?,
            AssociativeData::CompoundActionParam(value)
            | AssociativeData::PointRefActionParam(value) => self.write_assoc_compound(value)?,
            AssociativeData::OsnapPointRefActionParam(value) => {
                self.write_assoc_compound(&value.compound)?;
                self.writer
                    .write_subclass("ACDBASSOCOSNAPPOINTREFACTIONPARAM")?;
                self.writer.write_i16(90, value.status)?;
                self.writer.write_byte(90, value.osnap_mode)?;
                self.writer.write_double(40, value.parameter)?;
            }
            AssociativeData::ObjectActionParam(value) => {
                self.write_assoc_single_dependency(value, "AcDbAssocObjectActionParam")?
            }
            AssociativeData::PathActionParam(value) => {
                self.write_assoc_compound(&value.compound)?;
                self.writer.write_subclass("AcDbAssocPathActionParam")?;
                self.writer.write_i32(90, value.version)?;
            }
            AssociativeData::DimDependencyBody(value) => {
                self.writer.write_subclass("AcDbAssocDependencyBody")?;
                self.writer.write_i16(90, value.dependency_body_version)?;
                self.writer
                    .write_subclass("AcDbImpAssocDimDependencyBodyBase")?;
                self.writer.write_i16(90, value.base_version)?;
                self.writer.write_string(1, &value.name)?;
                self.writer.write_subclass("AcDbAssocDimDependencyBody")?;
                self.writer.write_i16(90, value.class_version)?;
            }
            AssociativeData::FaceActionParam(value) => {
                self.write_assoc_single_dependency(
                    &value.single_dependency,
                    "AcDbAssocFaceActionParam",
                )?;
                self.writer.write_i32(90, value.index)?;
            }
            AssociativeData::VertexActionParam(value) => {
                self.write_assoc_single_dependency(
                    &value.single_dependency,
                    "AcDbAssocVertexActionParam",
                )?;
                self.writer.write_point3d(10, value.point)?;
            }
            AssociativeData::AsmBodyActionParam(value) => {
                self.write_assoc_single_dependency(
                    &value.single_dependency,
                    "AcDbAssocAsmbodyActionParam",
                )?;
                self.writer.write_subclass("AcDbModelerGeometry")?;
                self.write_acis_data(&value.acis_data)?;
            }
            AssociativeData::ArrayParameters(value) => {
                self.write_assoc_array_parameters(value)?;
                self.writer.write_subclass(&object.cpp_class_name)?;
            }
            AssociativeData::ArrayActionBody(value) => self.write_assoc_array_body(value)?,
            AssociativeData::ArrayModifyActionBody(value) => {
                self.write_assoc_array_body(&value.body)?;
                self.writer
                    .write_subclass("AcDbAssocArrayModifyActionBody")?;
                self.writer.write_i16(70, value.status)?;
                self.writer
                    .write_i32(90, value.item_locations.len() as i32)?;
                for location in &value.item_locations {
                    for item in location {
                        self.writer.write_i32(90, *item)?;
                    }
                }
            }
            AssociativeData::DimensionAssociation(value) => {
                self.write_dimension_association_dxf(value)?
            }
            AssociativeData::PersSubentManagerStatic(value) => {
                self.write_static_pers_subent_manager_dxf(value)?
            }
            AssociativeData::ViewRepActionBody(value) => {
                self.write_assoc_action_body(&value.action_body)?;
                self.writer.write_subclass("AcDbAssocViewRepActionBody")?;
                self.writer.write_i16(70, value.class_version)?;
                self.writer.write_handle(360, value.view_rep)?;
                self.writer.write_i32(90, value.view_type)?;
                self.writer.write_double(40, value.rotation)?;
            }
            AssociativeData::ViewObjectActionParam(value) => {
                self.write_assoc_single_dependency(
                    &value.single_dependency,
                    "AcDbAssocObjectActionParam",
                )?;
                let section = match value.kind {
                    AssocViewObjectActionParamKind::ViewBorder => "AcDbAssocViewBorderActionParam",
                    AssocViewObjectActionParamKind::ViewRep => "AcDbAssocViewRepActionParam",
                    AssocViewObjectActionParamKind::ViewSymbol => "AcDbAssocViewSymbolActionParam",
                    AssocViewObjectActionParamKind::ViewStyle => "AcDbAssocViewStyleActionParam",
                };
                self.writer.write_subclass(section)?;
                self.writer.write_i16(70, value.class_version)?;
            }
            AssociativeData::ViewRepHatchManager(value) => {
                self.write_assoc_compound(&value.compound)?;
                self.writer.write_subclass("AcDbAssocViewRepHatchManager")?;
                self.writer.write_i16(70, value.class_version)?;
                self.writer.write_i32(90, value.items.len() as i32)?;
                for item in &value.items {
                    self.writer.write_i64(160, item.first_id)?;
                    self.writer.write_i64(160, item.second_id)?;
                    self.writer.write_i32(90, item.status)?;
                    self.writer.write_handle(330, item.parameter)?;
                }
            }
            AssociativeData::ViewRepHatchActionParam(value) => {
                self.write_assoc_single_dependency(
                    &value.single_dependency,
                    "AcDbAssocObjectActionParam",
                )?;
                self.writer
                    .write_subclass("AcDbAssocViewRepHatchActionParam")?;
                self.writer.write_i16(70, value.class_version)?;
                self.writer.write_point3d(210, value.normal)?;
                self.writer.write_i32(90, value.hatch_index)?;
                self.writer.write_i32(90, value.flags)?;
            }
            AssociativeData::ViewLabelActionParam(value) => {
                self.write_assoc_single_dependency(
                    &value.single_dependency,
                    "AcDbAssocObjectActionParam",
                )?;
                self.writer
                    .write_subclass("AcDbAssocViewLabelActionParam")?;
                self.writer.write_i16(70, value.class_version)?;
                self.writer.write_i16(70, value.label_version)?;
                self.writer.write_double(210, value.offset.x)?;
                self.writer.write_double(220, value.offset.y)?;
                self.writer.write_byte(280, value.flag)?;
            }
        }
        Ok(())
    }
}