acadrust 0.6.0

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
944
945
946
947
948
949
950
951
952
953
954
955
use crate::io::dwg::dwg_reference_type::DwgReferenceType;
use crate::objects::*;
use crate::types::Handle;

use super::DwgObjectWriter;

impl<'a> DwgObjectWriter<'a> {
    fn write_assoc_handle(&mut self, kind: DwgReferenceType, value: Handle) {
        self.writer.write_handle(kind, value.value());
    }

    fn write_assoc_handles(&mut self, kind: DwgReferenceType, values: &[Handle]) {
        for value in values {
            self.write_assoc_handle(kind, *value);
        }
    }

    fn write_assoc_eval(&mut self, value: &AssocEvalVariant) {
        self.writer.write_bit_short(value.code);
        match &value.value {
            AssocEvalValue::None => {}
            AssocEvalValue::Real(value) => self.writer.write_bit_double(*value),
            AssocEvalValue::Long(value) => self.writer.write_bit_long(*value),
            AssocEvalValue::Short(value) => self.writer.write_bit_short(*value),
            AssocEvalValue::Byte(value) => self.writer.write_byte(*value),
            AssocEvalValue::Text(value) => self.writer.write_variable_text(value),
            AssocEvalValue::Handle(value) => {
                self.write_assoc_handle(DwgReferenceType::HardPointer, *value)
            }
        }
    }

    fn write_assoc_value_param(&mut self, value: &AssocValueParam) {
        self.writer.write_bit_long(value.class_version);
        self.writer.write_variable_text(&value.name);
        self.writer.write_bit_long(value.unit_type);
        self.writer.write_bit_long(value.variables.len() as i32);
        for variable in &value.variables {
            self.write_assoc_eval(&variable.value);
            self.write_assoc_handle(DwgReferenceType::SoftPointer, variable.handle);
        }
        self.write_assoc_handle(
            DwgReferenceType::SoftPointer,
            value.controlled_object_dependency,
        );
    }

    fn write_assoc_values(&mut self, values: &[AssocValueParam]) {
        for value in values {
            self.write_assoc_value_param(value);
        }
    }

    fn write_assoc_dependency(&mut self, value: &AssocDependency) {
        self.writer.write_bit_short(value.class_version);
        self.writer.write_bit_long(value.status);
        self.writer.write_bit(value.is_read_dependency);
        self.writer.write_bit(value.is_write_dependency);
        self.writer.write_bit(value.is_attached_to_object);
        self.writer.write_bit(value.is_delegating_to_owning_action);
        self.writer.write_bit_long(value.order);
        self.write_assoc_handle(DwgReferenceType::SoftPointer, value.dependent_on);
        self.writer.write_bit(value.name.is_some());
        if let Some(name) = &value.name {
            self.writer.write_variable_text(name);
        }
        self.write_assoc_handle(DwgReferenceType::SoftPointer, value.read_dependency);
        self.write_assoc_handle(DwgReferenceType::SoftPointer, value.node);
        self.write_assoc_handle(DwgReferenceType::HardOwnership, value.dependency_body);
        self.writer.write_bit_long(value.dependency_body_id);
    }

    fn write_assoc_action(&mut self, value: &AssocAction) {
        self.writer.write_bit_short(value.class_version);
        self.writer.write_bit_long(value.geometry_status);
        self.write_assoc_handle(DwgReferenceType::SoftPointer, value.owning_network);
        self.write_assoc_handle(DwgReferenceType::HardOwnership, value.action_body);
        self.writer.write_bit_long(value.action_index);
        self.writer.write_bit_long(value.max_dependency_index);
        self.writer.write_bit_long(value.dependencies.len() as i32);
        for dependency in &value.dependencies {
            self.writer.write_bit(dependency.is_owned);
            self.write_assoc_handle(
                if dependency.is_owned {
                    DwgReferenceType::HardOwnership
                } else {
                    DwgReferenceType::SoftPointer
                },
                dependency.dependency,
            );
        }
        if value.class_version > 1 {
            self.writer.write_bit_short(0);
            self.writer
                .write_bit_long(value.owned_parameters.len() as i32);
            self.write_assoc_handles(DwgReferenceType::HardOwnership, &value.owned_parameters);
            self.writer.write_bit_short(0);
            self.writer.write_bit_long(value.values.len() as i32);
            self.write_assoc_values(&value.values);
        }
    }

    fn write_assoc_action_param(&mut self, value: &AssocActionParam) {
        self.writer.write_bit_short(value.is_r2013);
        if self.version.r2013_plus(self.dxf_version) {
            self.writer.write_bit_long(value.version);
        }
        self.writer.write_variable_text(&value.name);
    }

    fn write_assoc_action_body(&mut self, value: &AssocActionBody) {
        self.writer.write_bit_long(value.version);
    }

    fn write_assoc_parameter_body(&mut self, value: &AssocParamBasedActionBody) {
        if self.version.r2013_plus(self.dxf_version) {
            return;
        }
        self.writer.write_bit_long(value.version);
        self.writer.write_bit_long(value.minor);
        self.writer.write_bit_long(value.dependencies.len() as i32);
        self.write_assoc_handles(DwgReferenceType::SoftPointer, &value.dependencies);
        self.writer.write_bit_long(value.marker);
        self.writer.write_bit_long(value.values.len() as i32);
        if value.values.is_empty() {
            self.writer.write_bit_long(value.empty_value_marker);
            self.write_assoc_handle(DwgReferenceType::HardPointer, value.dependency);
        }
        self.write_assoc_values(&value.values);
    }

    fn write_assoc_surface(&mut self, value: &AssocSurfaceActionBody) {
        self.write_assoc_action_body(&value.action_body);
        self.write_assoc_parameter_body(&value.parameter_body);
        self.writer.write_bit_long(value.surface_body.version);
        self.write_assoc_handle(DwgReferenceType::HardPointer, value.surface_body.dependency);
        self.writer
            .write_bit(value.surface_body.is_semi_associative);
        self.writer.write_bit_long(value.surface_body.marker);
        self.writer.write_bit(value.surface_body.is_semi_override);
        self.writer.write_bit_short(value.surface_body.grip_status);
        self.writer.write_bit_long(value.path_status);
        match value.kind {
            AssocSurfaceActionKind::Network
            | AssocSurfaceActionKind::Patch
            | AssocSurfaceActionKind::EdgeChamfer
            | AssocSurfaceActionKind::EdgeFillet => {}
            _ => self.writer.write_bit_long(value.class_version),
        }
        match value.kind {
            AssocSurfaceActionKind::Extend => self.writer.write_byte(value.option),
            AssocSurfaceActionKind::Offset => self.writer.write_bit(value.flags[0]),
            AssocSurfaceActionKind::Trim => {
                self.writer.write_bit(value.flags[0]);
                self.writer.write_bit(value.flags[1]);
                self.writer.write_bit_double(value.distance);
            }
            AssocSurfaceActionKind::Blend => {
                self.writer.write_bit(value.flags[0]);
                self.writer.write_bit(value.flags[1]);
                self.writer.write_bit(value.flags[2]);
                self.writer.write_bit_short(value.status);
                self.writer.write_bit(value.flags[3]);
                self.writer.write_bit(value.flags[4]);
                self.writer.write_bit_short(value.secondary_status);
            }
            AssocSurfaceActionKind::Fillet => {
                self.writer.write_bit_short(value.status);
                self.writer.write_2raw_double(value.first_point);
                self.writer.write_2raw_double(value.second_point);
            }
            _ => {}
        }
    }

    fn write_assoc_annotation_base(&mut self, value: &AssocAnnotationBase) {
        if self.version.r2010_plus() {
            self.writer.write_bit_short(value.version);
            self.write_assoc_handle(DwgReferenceType::HardPointer, value.dependency);
        } else {
            self.write_assoc_action_body(&value.action_body);
            self.write_assoc_parameter_body(&value.parameter_body);
        }
    }

    fn write_assoc_annotation(&mut self, value: &AssocAnnotationActionBody) {
        if value.kind == AssocAnnotationKind::RestoreEntityState {
            self.write_assoc_action_body(&value.action_body);
            self.writer.write_bit_long(value.class_version);
            self.write_assoc_handle(DwgReferenceType::HardPointer, value.entity);
            return;
        }
        self.write_assoc_annotation_base(&value.annotation);
        match value.kind {
            AssocAnnotationKind::ThreePointAngularDimension
            | AssocAnnotationKind::RotatedDimension => {
                self.writer.write_bit_short(value.class_version as i16)
            }
            _ => self.writer.write_bit_long(value.class_version),
        }
        match value.kind {
            AssocAnnotationKind::MLeader => {
                self.writer.write_bit_long(value.actions.len() as i32);
                for action in &value.actions {
                    self.writer.write_bit_long(action.dependency_id);
                    self.write_assoc_handle(DwgReferenceType::HardPointer, action.dependency);
                }
            }
            AssocAnnotationKind::AlignedDimension => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, value.read_node);
                self.write_assoc_handle(DwgReferenceType::SoftPointer, value.dimension_node);
            }
            AssocAnnotationKind::ThreePointAngularDimension => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, value.read_node);
                self.write_assoc_handle(DwgReferenceType::SoftPointer, value.dimension_node);
                self.write_assoc_handle(DwgReferenceType::HardPointer, value.dependency);
            }
            AssocAnnotationKind::OrdinateDimension | AssocAnnotationKind::RotatedDimension => {
                self.write_assoc_handle(DwgReferenceType::HardPointer, value.read_node);
                self.write_assoc_handle(DwgReferenceType::HardPointer, value.dimension_node);
            }
            AssocAnnotationKind::RestoreEntityState => {}
        }
    }

    fn write_assoc_single_dependency(&mut self, value: &AssocSingleDependencyActionParam) {
        self.write_assoc_action_param(&value.action_param);
        self.writer.write_bit_long(value.dependency_class_version);
        self.write_assoc_handle(DwgReferenceType::SoftPointer, value.dependency);
        self.writer.write_bit_long(value.class_version);
    }

    fn write_assoc_compound(&mut self, value: &AssocCompoundActionParam) {
        self.write_assoc_action_param(&value.action_param);
        self.writer.write_bit_short(value.class_version);
        self.writer.write_bit_short(value.status);
        self.writer.write_bit_long(value.parameters.len() as i32);
        self.write_assoc_handles(DwgReferenceType::SoftPointer, &value.parameters);
        if let Some(child) = &value.child_parameter {
            self.writer.write_bit_short(child.status);
            self.writer.write_bit_long(child.id);
            self.write_assoc_handle(DwgReferenceType::HardOwnership, child.parameter);
            if child.id != 0 {
                self.write_assoc_handle(DwgReferenceType::HardOwnership, child.secondary_parameter);
                self.writer.write_bit_long(child.marker);
                self.write_assoc_handle(DwgReferenceType::HardOwnership, child.tertiary_parameter);
            }
        }
    }

    fn write_assoc_array_body(&mut self, value: &AssocArrayActionBody) {
        self.write_assoc_action_body(&value.action_body);
        self.write_assoc_parameter_body(&value.parameter_body);
        self.writer.write_bit_long(value.version);
        self.writer.write_variable_text(&value.parameter_block);
        for item in value.transform {
            self.writer.write_bit_double(item);
        }
    }

    fn write_assoc_array_parameters(&mut self, value: &AssocArrayParameters) {
        self.writer.write_bit_long(value.version);
        self.writer.write_bit_long(value.items.len() as i32);
        self.writer.write_variable_text(&value.class_name);
        for item in &value.items {
            self.writer.write_bit_long(item.class_version);
            for location in item.location {
                self.writer.write_bit_long(location);
            }
            self.writer.write_bit_long(item.flags);
            if item.uses_default_transform {
                self.writer.write_3bit_double(item.x_direction);
            } else {
                for matrix_value in item.transform {
                    self.writer.write_bit_double(matrix_value);
                }
            }
            if let Some(matrix) = item.relative_transform {
                for matrix_value in matrix {
                    self.writer.write_bit_double(matrix_value);
                }
            }
            if let Some(first) = item.first_handle {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, first);
            }
            if item.flags & 0x10 != 0 {
                self.write_assoc_handle(
                    DwgReferenceType::SoftPointer,
                    item.second_handle.unwrap_or(Handle::NULL),
                );
            }
        }
        self.writer.write_bit_long(value.item_count);
        self.writer.write_bit_long(value.row_count);
        self.writer.write_bit_long(value.level_count);
    }

    fn write_dimension_association(&mut self, value: &AssocDimensionAssociation) {
        self.writer.write_bit_long(value.associativity);
        self.writer.write_bit(value.trans_space);
        self.writer.write_byte(value.rotated_type);
        self.write_assoc_handle(DwgReferenceType::SoftPointer, value.dimension);
        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_variable_text(&reference.class_name);
                self.writer.write_byte(reference.osnap_type);
                self.writer.write_bit_long(reference.xrefs.len() as i32);
                self.write_assoc_handles(DwgReferenceType::SoftPointer, &reference.xrefs);
                if reference.osnap_type != 0 {
                    self.writer.write_bit_long(reference.main_subent_type);
                    self.writer.write_bit_long(reference.main_gs_marker);
                    self.writer
                        .write_bit_long(reference.xref_paths.len() as i32);
                    for path in &reference.xref_paths {
                        self.writer.write_variable_text(path);
                    }
                }
                self.writer.write_bit_double(reference.osnap_distance);
                self.writer.write_3bit_double(reference.osnap_point);
                if reference.osnap_type == 6 || reference.osnap_type == 11 {
                    self.writer
                        .write_bit_long(reference.intersection_objects.len() as i32);
                    self.write_assoc_handles(
                        DwgReferenceType::HardPointer,
                        &reference.intersection_objects,
                    );
                    self.writer
                        .write_bit_long(reference.intersection_subent_type);
                    self.writer.write_bit_long(reference.intersection_gs_marker);
                    self.writer
                        .write_bit_long(reference.intersection_xref_paths.len() as i32);
                    for path in &reference.intersection_xref_paths {
                        self.writer.write_variable_text(path);
                    }
                }
                self.writer.write_bit(index + 1 < references.len());
            }
        }
    }

    fn write_static_pers_subent_manager(&mut self, value: &PersSubentManager) {
        self.writer.write_bit_long(value.class_version);
        self.writer.write_bit_long(value.marker_zero);
        self.writer.write_bit_long(value.marker_two);
        self.writer.write_bit_long(value.associative_step_count);
        self.writer.write_bit_long(value.associative_subent_count);
        self.writer.write_bit_long(value.steps.len() as i32);
        for step in &value.steps {
            self.writer.write_bit_long(*step);
        }
        if value.associative_subent_count != 0 || !value.subents.is_empty() {
            self.writer.write_bit_long(value.subents.len() as i32);
            for subent in &value.subents {
                self.writer.write_bit_long(*subent);
            }
        }
    }

    fn write_constraint_node_common(&mut self, node: &AssocConstraintNode) {
        self.writer.write_bit_long(node.node_id);
        if !self.version.r2013_plus(self.dxf_version) {
            self.writer.write_byte(node.status);
        }
        self.writer.write_bit_long(node.connections.len() as i32);
        for connection in &node.connections {
            self.writer.write_bit_long(*connection);
        }
        if self.version.r2013_plus(self.dxf_version) {
            self.writer.write_byte(node.status);
        }
    }

    fn write_geometrical_constraint(&mut self, owner_id: i32, is_implied: bool, is_active: bool) {
        self.writer.write_bit_long(owner_id);
        self.writer.write_bit(is_implied);
        self.writer.write_bit(is_active);
    }

    fn write_explicit_constraint(
        &mut self,
        owner_id: i32,
        is_implied: bool,
        is_active: bool,
        value_dependency: Handle,
        dimension_dependency: Handle,
    ) {
        self.write_geometrical_constraint(owner_id, is_implied, is_active);
        self.write_assoc_handle(DwgReferenceType::HardPointer, value_dependency);
        self.write_assoc_handle(DwgReferenceType::HardPointer, dimension_dependency);
    }

    fn write_constraint_node_data(&mut self, data: &AssocConstraintNodeData) {
        match data {
            AssocConstraintNodeData::None => {}
            AssocConstraintNodeData::Geometrical {
                owner_id,
                is_implied,
                is_active,
            } => self.write_geometrical_constraint(*owner_id, *is_implied, *is_active),
            AssocConstraintNodeData::Composite {
                owner_id,
                is_implied,
                is_active,
                owned_constraint_ids,
            } => {
                self.write_geometrical_constraint(*owner_id, *is_implied, *is_active);
                self.writer
                    .write_bit_long(owned_constraint_ids.len() as i32);
                for constraint_id in owned_constraint_ids {
                    self.writer.write_bit_long(*constraint_id);
                }
            }
            AssocConstraintNodeData::HelpParameter { value, reserved } => {
                self.writer.write_bit_double(*value);
                self.writer.write_bit(*reserved);
            }
            AssocConstraintNodeData::Angle {
                owner_id,
                is_implied,
                is_active,
                value_dependency,
                dimension_dependency,
                sector_type,
            } => {
                self.write_explicit_constraint(
                    *owner_id,
                    *is_implied,
                    *is_active,
                    *value_dependency,
                    *dimension_dependency,
                );
                self.writer.write_byte(*sector_type);
            }
            AssocConstraintNodeData::Parallel {
                owner_id,
                is_implied,
                is_active,
                datum_line_index,
            } => {
                self.write_geometrical_constraint(*owner_id, *is_implied, *is_active);
                if let Some(datum_line_index) = datum_line_index {
                    self.writer.write_bit_long(*datum_line_index);
                }
            }
            AssocConstraintNodeData::Distance {
                owner_id,
                is_implied,
                is_active,
                value_dependency,
                dimension_dependency,
                direction_type,
                distance,
            } => {
                self.write_explicit_constraint(
                    *owner_id,
                    *is_implied,
                    *is_active,
                    *value_dependency,
                    *dimension_dependency,
                );
                self.writer.write_byte(*direction_type);
                if *direction_type != 0 {
                    self.writer
                        .write_3bit_double(distance.unwrap_or(crate::types::Vector3::ZERO));
                }
            }
            AssocConstraintNodeData::RadiusDiameter {
                owner_id,
                is_implied,
                is_active,
                value_dependency,
                dimension_dependency,
                mode,
            } => {
                self.write_explicit_constraint(
                    *owner_id,
                    *is_implied,
                    *is_active,
                    *value_dependency,
                    *dimension_dependency,
                );
                self.writer.write_byte(*mode);
            }
            AssocConstraintNodeData::ImplicitPoint {
                geometry_dependency,
                geometry_node_id,
                point,
                point_type,
                point_index,
                curve_id,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                if !geometry_dependency.is_null() {
                    self.writer
                        .write_3bit_double(point.unwrap_or(crate::types::Vector3::ZERO));
                }
                self.writer.write_byte(*point_type);
                self.writer.write_bit_long(*point_index);
                self.writer.write_bit_long(*curve_id);
            }
            AssocConstraintNodeData::Point {
                geometry_dependency,
                geometry_node_id,
                point,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                if !geometry_dependency.is_null() {
                    self.writer
                        .write_3bit_double(point.unwrap_or(crate::types::Vector3::ZERO));
                }
            }
            AssocConstraintNodeData::RigidSet {
                geometry_dependency,
                geometry_node_id,
                reserved,
                transform,
                geometry_ids,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                self.writer.write_bit(*reserved);
                for value in transform {
                    self.writer.write_bit_double(*value);
                }
                self.writer.write_bit_long(geometry_ids.len() as i32);
                for geometry_id in geometry_ids {
                    self.writer.write_bit_long(*geometry_id);
                }
            }
            AssocConstraintNodeData::Line {
                geometry_dependency,
                geometry_node_id,
                point,
                direction,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                self.writer.write_3bit_double(*point);
                self.writer.write_3bit_double(*direction);
            }
            AssocConstraintNodeData::BoundedLine {
                geometry_dependency,
                geometry_node_id,
                point,
                direction,
                is_ray,
                start_point,
                end_point,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                self.writer.write_3bit_double(*point);
                self.writer.write_3bit_double(*direction);
                self.writer.write_bit(*is_ray);
                self.writer.write_3bit_double(*start_point);
                self.writer.write_3bit_double(*end_point);
            }
            AssocConstraintNodeData::Circle {
                geometry_dependency,
                geometry_node_id,
                center,
                normal,
                direction,
                radius,
                start_parameter,
                end_parameter,
                reserved,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                self.writer.write_3bit_double(*center);
                self.writer.write_3bit_double(*normal);
                self.writer.write_3bit_double(*direction);
                self.writer.write_bit_double(*radius);
                self.writer.write_bit_double(*start_parameter);
                self.writer.write_bit_double(*end_parameter);
                self.writer.write_bit_double(*reserved);
            }
            AssocConstraintNodeData::Arc {
                geometry_dependency,
                geometry_node_id,
                center,
                normal,
                direction,
                radius,
                start_parameter,
                end_parameter,
                reserved,
                start_point,
                end_point,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                self.writer.write_3bit_double(*center);
                self.writer.write_3bit_double(*normal);
                self.writer.write_3bit_double(*direction);
                self.writer.write_bit_double(*radius);
                self.writer.write_bit_double(*start_parameter);
                self.writer.write_bit_double(*end_parameter);
                self.writer.write_bit_double(*reserved);
                self.writer.write_3bit_double(*start_point);
                self.writer.write_3bit_double(*end_point);
            }
            AssocConstraintNodeData::Ellipse {
                geometry_dependency,
                geometry_node_id,
                center,
                major_axis,
                axis_ratio,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                self.writer.write_3bit_double(*center);
                self.writer.write_3bit_double(*major_axis);
                self.writer.write_bit_double(*axis_ratio);
            }
            AssocConstraintNodeData::BoundedEllipse {
                geometry_dependency,
                geometry_node_id,
                center,
                major_axis,
                axis_ratio,
                start_point,
                end_point,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                self.writer.write_3bit_double(*center);
                self.writer.write_3bit_double(*major_axis);
                self.writer.write_bit_double(*axis_ratio);
                self.writer.write_3bit_double(*start_point);
                self.writer.write_3bit_double(*end_point);
            }
            AssocConstraintNodeData::Spline {
                geometry_dependency,
                geometry_node_id,
                rational,
                periodic,
                degree,
                knot_tolerance,
                knot_physical_length,
                knot_grow_length,
                knots,
                weight_physical_length,
                weight_grow_length,
                weights,
                control_point_physical_length,
                control_point_grow_length,
                control_points,
                implicit_point_ids,
            } => {
                self.write_assoc_handle(DwgReferenceType::SoftPointer, *geometry_dependency);
                self.writer.write_bit_long(*geometry_node_id);
                self.writer.write_bit(*rational);
                self.writer.write_bit(*periodic);
                self.writer.write_bit_long(*degree);
                self.writer.write_bit_double(*knot_tolerance);
                self.writer.write_bit_long(knots.len() as i32);
                self.writer.write_bit_long(*knot_physical_length);
                self.writer.write_bit_long(*knot_grow_length);
                for knot in knots {
                    self.writer.write_bit_double(*knot);
                }
                self.writer.write_bit_long(weights.len() as i32);
                self.writer.write_bit_long(*weight_physical_length);
                self.writer.write_bit_long(*weight_grow_length);
                for weight in weights {
                    self.writer.write_bit_double(*weight);
                }
                self.writer.write_bit_long(control_points.len() as i32);
                self.writer.write_bit_long(*control_point_physical_length);
                self.writer.write_bit_long(*control_point_grow_length);
                for point in control_points {
                    self.writer.write_3bit_double(*point);
                }
                self.writer.write_bit_long(implicit_point_ids.len() as i32);
                for point_id in implicit_point_ids {
                    self.writer.write_bit_long(*point_id);
                }
            }
        }
    }

    pub(super) fn write_associative_object(&mut self, object: &AssociativeObject) {
        let canonical = associative_canonical_name(&object.dxf_name);
        let prefixed = format!("ACDB{canonical}");
        let type_code = self
            .document
            .classes
            .get_by_name(&object.dxf_name)
            .or_else(|| self.document.classes.get_by_name(&prefixed))
            .map(|class| class.class_number)
            .unwrap_or(500);
        if matches!(&object.data, AssociativeData::ViewRepActionBody(_)) {
            self.write_common_non_entity_data_relative_owner(
                type_code,
                object.handle,
                object.owner,
                &object.reactors,
                &object.xdictionary_handle,
            );
        } else {
            self.write_common_non_entity_data(
                type_code,
                object.handle,
                object.owner,
                &object.reactors,
                &object.xdictionary_handle,
            );
        }
        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_bit_long(value.class_version);
                self.writer.write_variable_text(&value.name);
                self.write_assoc_eval(&value.value);
            }
            AssociativeData::GeomDependency(value) => {
                self.write_assoc_dependency(&value.dependency);
                self.writer.write_bit_short(value.class_version);
                self.writer.write_bit(value.enabled);
                self.writer
                    .write_variable_text(&value.persistent_subent.class_name);
                self.writer
                    .write_bit(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_bit_short(value.network_version);
                self.writer.write_bit_long(value.network_action_index);
                self.writer.write_bit_long(value.actions.len() as i32);
                for action in &value.actions {
                    self.writer.write_bit(action.is_owned);
                    self.write_assoc_handle(
                        if action.is_owned {
                            DwgReferenceType::HardOwnership
                        } else {
                            DwgReferenceType::SoftPointer
                        },
                        action.dependency,
                    );
                }
                self.writer.write_bit_long(value.owned_actions.len() as i32);
                self.write_assoc_handles(DwgReferenceType::SoftPointer, &value.owned_actions);
            }
            AssociativeData::AnnotationActionBody(value) => self.write_assoc_annotation(value),
            AssociativeData::PersSubentManager(value) => {
                self.writer.write_bit_long(value.class_version);
                for marker in value.markers {
                    self.writer.write_bit_long(marker);
                }
                self.writer.write_bit_long(value.steps.len() as i32);
                for step in &value.steps {
                    self.writer.write_bit_long(*step);
                }
                self.writer.write_bit_long(value.subent_count);
                for item in &value.subent_data {
                    self.writer.write_bit_long(*item);
                }
                self.writer.write_bit(value.final_flag);
            }
            AssociativeData::EdgeActionParam(value) => {
                self.write_assoc_single_dependency(&value.single_dependency);
                self.write_assoc_handle(DwgReferenceType::HardOwnership, value.parameter);
                self.writer.write_bit(value.has_action);
                self.writer.write_bit_long(value.action_type);
            }
            AssociativeData::ConstraintGroup(value) => {
                self.write_assoc_action(&value.action);
                self.writer.write_bit_long(value.version);
                self.writer.write_bit(value.flag);
                for point in value.work_plane {
                    self.writer.write_3bit_double(point);
                }
                self.write_assoc_handle(DwgReferenceType::HardOwnership, value.dependency);
                self.writer.write_bit_long(value.actions.len() as i32);
                self.write_assoc_handles(DwgReferenceType::HardOwnership, &value.actions);
                if let Some(first) = value.nodes.first() {
                    let registered: Vec<&AssocConstraintNode> = value
                        .nodes
                        .iter()
                        .skip(1)
                        .filter(|node| !node.class_name.is_empty())
                        .collect();
                    self.writer.write_bit_long(registered.len() as i32);
                    self.writer.write_bit_long(first.node_id);
                    self.writer.write_bit_long(first.connections.len() as i32);
                    for connection in &first.connections {
                        self.writer.write_bit_long(*connection);
                    }
                    self.writer.write_bit(first.status != 0);
                    let mut class_types: Vec<&str> = Vec::new();
                    for node in &registered {
                        if !class_types
                            .iter()
                            .any(|name| name.eq_ignore_ascii_case(&node.class_name))
                        {
                            class_types.push(&node.class_name);
                        }
                    }
                    self.writer.write_bit_long(class_types.len() as i32);
                    for class_name in &class_types {
                        self.writer.write_variable_text(class_name);
                    }
                    self.writer.write_bit_long(registered.len() as i32);
                    for node in &registered {
                        self.writer.write_bit(node.registry_flag);
                        let class_index = class_types
                            .iter()
                            .position(|name| name.eq_ignore_ascii_case(&node.class_name))
                            .map(|index| index as i32 + 1)
                            .unwrap_or(0);
                        self.writer.write_bit_long(class_index);
                        self.writer.write_bit_long(node.node_id);
                    }
                    for node in registered {
                        self.write_constraint_node_common(node);
                        self.write_constraint_node_data(&node.data);
                    }
                } else {
                    self.writer.write_bit_long(0);
                }
            }
            AssociativeData::Variable(value) => {
                self.write_assoc_action(&value.action);
                self.writer.write_bit_long(value.class_version);
                self.writer.write_variable_text(&value.name);
                self.writer.write_variable_text(&value.expression);
                self.writer.write_variable_text(&value.evaluator);
                self.writer.write_variable_text(&value.description);
                self.write_assoc_eval(&value.value);
                self.writer.write_bit(value.has_cached_value);
                if value.has_cached_value {
                    self.writer.write_variable_text(&value.cached_value);
                }
                self.writer.write_bit(value.flag);
                self.writer.write_bit_long(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_bit_short(value.status);
                self.writer.write_byte(value.osnap_mode);
                self.writer.write_bit_double(value.parameter);
            }
            AssociativeData::ObjectActionParam(value) => self.write_assoc_single_dependency(value),
            AssociativeData::PathActionParam(value) => {
                self.write_assoc_compound(&value.compound);
                self.writer.write_bit_long(value.version);
            }
            AssociativeData::DimDependencyBody(value) => {
                self.writer.write_bit_short(value.dependency_body_version);
                self.writer.write_bit_short(value.base_version);
                self.writer.write_variable_text(&value.name);
                self.writer.write_bit_short(value.class_version);
            }
            AssociativeData::FaceActionParam(value) => {
                self.write_assoc_single_dependency(&value.single_dependency);
                self.writer.write_bit_long(value.index);
            }
            AssociativeData::VertexActionParam(value) => {
                self.write_assoc_single_dependency(&value.single_dependency);
                self.writer.write_3bit_double(value.point);
            }
            AssociativeData::AsmBodyActionParam(value) => {
                self.write_assoc_single_dependency(&value.single_dependency);
                self.write_acis_data(
                    value.point_of_reference,
                    &value.acis_data,
                    &value.wires,
                    &value.silhouettes,
                );
                if value.acis_data.is_binary {
                    self.write_assoc_handle(DwgReferenceType::SoftPointer, value.history);
                }
            }
            AssociativeData::ArrayParameters(value) => self.write_assoc_array_parameters(value),
            AssociativeData::ArrayActionBody(value) => self.write_assoc_array_body(value),
            AssociativeData::ArrayModifyActionBody(value) => {
                self.write_assoc_array_body(&value.body);
                self.writer.write_bit_short(value.status);
                self.writer
                    .write_bit_long(value.item_locations.len() as i32);
                for location in &value.item_locations {
                    for item in location {
                        self.writer.write_bit_long(*item);
                    }
                }
            }
            AssociativeData::DimensionAssociation(value) => self.write_dimension_association(value),
            AssociativeData::PersSubentManagerStatic(value) => {
                self.write_static_pers_subent_manager(value)
            }
            AssociativeData::ViewRepActionBody(value) => {
                self.write_assoc_action_body(&value.action_body);
                self.writer.write_bit_short(value.class_version);
                self.write_assoc_handle(DwgReferenceType::HardOwnership, value.view_rep);
                self.writer.write_bit_long(value.view_type);
                self.writer.write_bit_double(value.rotation);
            }
            AssociativeData::ViewObjectActionParam(value) => {
                self.write_assoc_single_dependency(&value.single_dependency);
                self.writer.write_bit_short(value.class_version);
            }
            AssociativeData::ViewRepHatchManager(value) => {
                self.write_assoc_compound(&value.compound);
                self.writer.write_bit_short(value.class_version);
                self.writer.write_bit_long(value.items.len() as i32);
                for item in &value.items {
                    self.writer.write_bit_long_long(item.first_id);
                    self.writer.write_bit_long_long(item.second_id);
                    self.writer.write_bit_long(item.status);
                    self.write_assoc_handle(DwgReferenceType::SoftPointer, item.parameter);
                }
            }
            AssociativeData::ViewRepHatchActionParam(value) => {
                self.write_assoc_single_dependency(&value.single_dependency);
                self.writer.write_bit_short(value.class_version);
                self.writer.write_3bit_double(value.normal);
                self.writer.write_bit_long(value.hatch_index);
                self.writer.write_bit_long(value.flags);
            }
            AssociativeData::ViewLabelActionParam(value) => {
                self.write_assoc_single_dependency(&value.single_dependency);
                self.writer.write_bit_short(value.class_version);
                self.writer.write_bit_short(value.label_version);
                self.writer.write_2raw_double(value.offset);
                self.writer.write_byte(value.flag);
            }
        }
        self.register_object(object.handle);
    }
}