1use crate::types::{Color, Handle, LineWeight};
6
7use bitflags::bitflags;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[repr(i16)]
17pub enum LeaderContentType {
18 None = 0,
20 Block = 1,
22 #[default]
24 MText = 2,
25 Tolerance = 3,
27}
28
29impl From<i16> for LeaderContentType {
30 fn from(value: i16) -> Self {
31 match value {
32 0 => Self::None,
33 1 => Self::Block,
34 2 => Self::MText,
35 3 => Self::Tolerance,
36 _ => Self::MText,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44#[repr(i16)]
45pub enum MultiLeaderPathType {
46 Invisible = 0,
48 #[default]
50 StraightLineSegments = 1,
51 Spline = 2,
53}
54
55impl From<i16> for MultiLeaderPathType {
56 fn from(value: i16) -> Self {
57 match value {
58 0 => Self::Invisible,
59 1 => Self::StraightLineSegments,
60 2 => Self::Spline,
61 _ => Self::StraightLineSegments,
62 }
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69#[repr(i16)]
70pub enum TextAttachmentType {
71 TopOfTopLine = 0,
73 #[default]
75 MiddleOfTopLine = 1,
76 MiddleOfText = 2,
78 MiddleOfBottomLine = 3,
80 BottomOfBottomLine = 4,
82 BottomLine = 5,
84 BottomOfTopLineUnderlineBottomLine = 6,
86 BottomOfTopLineUnderlineTopLine = 7,
88 BottomOfTopLineUnderlineAll = 8,
90 CenterOfText = 9,
92 CenterOfTextOverline = 10,
94}
95
96impl From<i16> for TextAttachmentType {
97 fn from(value: i16) -> Self {
98 match value {
99 0 => Self::TopOfTopLine,
100 1 => Self::MiddleOfTopLine,
101 2 => Self::MiddleOfText,
102 3 => Self::MiddleOfBottomLine,
103 4 => Self::BottomOfBottomLine,
104 5 => Self::BottomLine,
105 6 => Self::BottomOfTopLineUnderlineBottomLine,
106 7 => Self::BottomOfTopLineUnderlineTopLine,
107 8 => Self::BottomOfTopLineUnderlineAll,
108 9 => Self::CenterOfText,
109 10 => Self::CenterOfTextOverline,
110 _ => Self::MiddleOfTopLine,
111 }
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118#[repr(i16)]
119pub enum TextAttachmentDirectionType {
120 #[default]
122 Horizontal = 0,
123 Vertical = 1,
125}
126
127impl From<i16> for TextAttachmentDirectionType {
128 fn from(value: i16) -> Self {
129 match value {
130 1 => Self::Vertical,
131 _ => Self::Horizontal,
132 }
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
138#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
139#[repr(i16)]
140pub enum TextAlignmentType {
141 #[default]
143 Left = 0,
144 Center = 1,
146 Right = 2,
148}
149
150impl From<i16> for TextAlignmentType {
151 fn from(value: i16) -> Self {
152 match value {
153 0 => Self::Left,
154 1 => Self::Center,
155 2 => Self::Right,
156 _ => Self::Left,
157 }
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164#[repr(i16)]
165pub enum TextAngleType {
166 ParallelToLastLeaderLine = 0,
168 #[default]
170 Horizontal = 1,
171 Optimized = 2,
173}
174
175impl From<i16> for TextAngleType {
176 fn from(value: i16) -> Self {
177 match value {
178 0 => Self::ParallelToLastLeaderLine,
179 1 => Self::Horizontal,
180 2 => Self::Optimized,
181 _ => Self::Horizontal,
182 }
183 }
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
188#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
189#[repr(i16)]
190pub enum BlockContentConnectionType {
191 #[default]
193 BlockExtents = 0,
194 BasePoint = 1,
196}
197
198impl From<i16> for BlockContentConnectionType {
199 fn from(value: i16) -> Self {
200 match value {
201 1 => Self::BasePoint,
202 _ => Self::BlockExtents,
203 }
204 }
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
210#[repr(i16)]
211pub enum LeaderDrawOrderType {
212 #[default]
214 LeaderHeadFirst = 0,
215 LeaderTailFirst = 1,
217}
218
219impl From<i16> for LeaderDrawOrderType {
220 fn from(value: i16) -> Self {
221 match value {
222 1 => Self::LeaderTailFirst,
223 _ => Self::LeaderHeadFirst,
224 }
225 }
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
230#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
231#[repr(i16)]
232pub enum MultiLeaderDrawOrderType {
233 #[default]
235 ContentFirst = 0,
236 LeaderFirst = 1,
238}
239
240impl From<i16> for MultiLeaderDrawOrderType {
241 fn from(value: i16) -> Self {
242 match value {
243 1 => Self::LeaderFirst,
244 _ => Self::ContentFirst,
245 }
246 }
247}
248
249bitflags! {
254 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
256 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
257 pub struct MultiLeaderPropertyOverrideFlags: i32 {
258 const NONE = 0;
260 const PATH_TYPE = 0x1;
262 const LINE_COLOR = 0x2;
264 const LEADER_LINE_TYPE = 0x4;
266 const LEADER_LINE_WEIGHT = 0x8;
268 const ENABLE_LANDING = 0x10;
270 const LANDING_GAP = 0x20;
272 const ENABLE_DOGLEG = 0x40;
274 const LANDING_DISTANCE = 0x80;
276 const ARROWHEAD = 0x100;
278 const ARROWHEAD_SIZE = 0x200;
280 const CONTENT_TYPE = 0x400;
282 const TEXT_STYLE = 0x800;
284 const TEXT_LEFT_ATTACHMENT = 0x1000;
286 const TEXT_ANGLE = 0x2000;
288 const TEXT_ALIGNMENT = 0x4000;
290 const TEXT_COLOR = 0x8000;
292 const TEXT_HEIGHT = 0x10000;
294 const TEXT_FRAME = 0x20000;
296 const ENABLE_USE_DEFAULT_MTEXT = 0x40000;
298 const BLOCK_CONTENT = 0x80000;
300 const BLOCK_CONTENT_COLOR = 0x100000;
302 const BLOCK_CONTENT_SCALE = 0x200000;
304 const BLOCK_CONTENT_ROTATION = 0x400000;
306 const BLOCK_CONTENT_CONNECTION = 0x800000;
308 const SCALE_FACTOR = 0x1000000;
310 const TEXT_RIGHT_ATTACHMENT = 0x2000000;
312 const TEXT_SWITCH_ALIGNMENT_TYPE = 0x4000000;
314 const TEXT_ATTACHMENT_DIRECTION = 0x8000000;
316 const TEXT_TOP_ATTACHMENT = 0x10000000;
318 const TEXT_BOTTOM_ATTACHMENT = 0x20000000;
320 }
321}
322
323bitflags! {
324 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
326 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
327 pub struct LeaderLinePropertyOverrideFlags: i32 {
328 const NONE = 0;
330 const PATH_TYPE = 1;
332 const LINE_COLOR = 2;
334 const LINE_TYPE = 4;
336 const LINE_WEIGHT = 8;
338 const ARROWHEAD_SIZE = 16;
340 const ARROWHEAD = 32;
342 }
343}
344
345#[derive(Debug, Clone, PartialEq)]
368#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
369pub struct MultiLeaderStyle {
370 pub handle: Handle,
372
373 pub owner_handle: Handle,
375
376 pub name: String,
379
380 pub description: String,
383
384 pub path_type: MultiLeaderPathType,
388
389 pub line_color: Color,
392
393 pub line_type_handle: Option<Handle>,
396
397 pub line_weight: LineWeight,
400
401 pub enable_landing: bool,
404
405 pub enable_dogleg: bool,
408
409 pub landing_distance: f64,
412
413 pub landing_gap: f64,
416
417 pub arrowhead_handle: Option<Handle>,
421
422 pub arrowhead_size: f64,
425
426 pub content_type: LeaderContentType,
430
431 pub text_style_handle: Option<Handle>,
435
436 pub text_left_attachment: TextAttachmentType,
439
440 pub text_right_attachment: TextAttachmentType,
443
444 pub text_top_attachment: TextAttachmentType,
447
448 pub text_bottom_attachment: TextAttachmentType,
451
452 pub text_attachment_direction: TextAttachmentDirectionType,
455
456 pub text_angle_type: TextAngleType,
459
460 pub text_alignment: TextAlignmentType,
463
464 pub text_color: Color,
467
468 pub text_height: f64,
471
472 pub text_frame: bool,
475
476 pub text_always_left: bool,
479
480 pub default_text: String,
483
484 pub block_content_handle: Option<Handle>,
488
489 pub block_content_color: Color,
492
493 pub block_content_connection: BlockContentConnectionType,
496
497 pub block_content_rotation: f64,
500
501 pub block_content_scale_x: f64,
504
505 pub block_content_scale_y: f64,
508
509 pub block_content_scale_z: f64,
512
513 pub enable_block_scale: bool,
516
517 pub enable_block_rotation: bool,
520
521 pub scale_factor: f64,
525
526 pub align_space: f64,
529
530 pub break_gap_size: f64,
533
534 pub max_leader_points: i32,
537
538 pub first_segment_angle: f64,
541
542 pub second_segment_angle: f64,
545
546 pub leader_draw_order: LeaderDrawOrderType,
550
551 pub multileader_draw_order: MultiLeaderDrawOrderType,
554
555 pub is_annotative: bool,
559
560 pub property_changed: bool,
563
564 pub unknown_flag_298: bool,
567}
568
569impl MultiLeaderStyle {
570 pub const OBJECT_NAME: &'static str = "MLEADERSTYLE";
572
573 pub const SUBCLASS_MARKER: &'static str = "AcDbMLeaderStyle";
575
576 pub const STANDARD: &'static str = "Standard";
578
579 pub fn new(name: &str) -> Self {
581 MultiLeaderStyle {
582 handle: Handle::NULL,
583 owner_handle: Handle::NULL,
584 name: name.to_string(),
585 description: String::new(),
586
587 path_type: MultiLeaderPathType::StraightLineSegments,
589 line_color: Color::ByBlock,
590 line_type_handle: None,
591 line_weight: LineWeight::ByBlock,
592 enable_landing: true,
593 enable_dogleg: true,
594 landing_distance: 0.36,
595 landing_gap: 0.09,
596
597 arrowhead_handle: None,
599 arrowhead_size: 0.18,
600
601 content_type: LeaderContentType::MText,
603
604 text_style_handle: None,
606 text_left_attachment: TextAttachmentType::MiddleOfTopLine,
607 text_right_attachment: TextAttachmentType::MiddleOfTopLine,
608 text_top_attachment: TextAttachmentType::TopOfTopLine,
609 text_bottom_attachment: TextAttachmentType::BottomOfBottomLine,
610 text_attachment_direction: TextAttachmentDirectionType::Horizontal,
611 text_angle_type: TextAngleType::Horizontal,
612 text_alignment: TextAlignmentType::Left,
613 text_color: Color::ByBlock,
614 text_height: 0.18,
615 text_frame: false,
616 text_always_left: false,
617 default_text: String::new(),
618
619 block_content_handle: None,
621 block_content_color: Color::ByBlock,
622 block_content_connection: BlockContentConnectionType::BlockExtents,
623 block_content_rotation: 0.0,
624 block_content_scale_x: 1.0,
625 block_content_scale_y: 1.0,
626 block_content_scale_z: 1.0,
627 enable_block_scale: false,
628 enable_block_rotation: false,
629
630 scale_factor: 1.0,
632 align_space: 0.0,
633 break_gap_size: 0.125,
634 max_leader_points: 2,
635 first_segment_angle: 0.0,
636 second_segment_angle: 0.0,
637
638 leader_draw_order: LeaderDrawOrderType::LeaderHeadFirst,
640 multileader_draw_order: MultiLeaderDrawOrderType::ContentFirst,
641
642 is_annotative: false,
644 property_changed: false,
645 unknown_flag_298: false,
646 }
647 }
648
649 pub fn standard() -> Self {
651 Self::new(Self::STANDARD)
652 }
653
654 pub fn set_block_scale(&mut self, scale: f64) {
656 self.block_content_scale_x = scale;
657 self.block_content_scale_y = scale;
658 self.block_content_scale_z = scale;
659 }
660
661 pub fn uniform_block_scale(&self) -> Option<f64> {
663 if (self.block_content_scale_x - self.block_content_scale_y).abs() < 1e-10
664 && (self.block_content_scale_y - self.block_content_scale_z).abs() < 1e-10
665 {
666 Some(self.block_content_scale_x)
667 } else {
668 None
669 }
670 }
671
672 pub fn set_block_rotation_degrees(&mut self, degrees: f64) {
674 self.block_content_rotation = degrees.to_radians();
675 }
676
677 pub fn block_rotation_degrees(&self) -> f64 {
679 self.block_content_rotation.to_degrees()
680 }
681
682 pub fn has_text_content(&self) -> bool {
684 self.content_type == LeaderContentType::MText
685 }
686
687 pub fn has_block_content(&self) -> bool {
689 self.content_type == LeaderContentType::Block
690 }
691
692 pub fn has_tolerance_content(&self) -> bool {
694 self.content_type == LeaderContentType::Tolerance
695 }
696
697 pub fn has_visible_leaders(&self) -> bool {
699 self.path_type != MultiLeaderPathType::Invisible
700 }
701
702 pub fn has_spline_leaders(&self) -> bool {
704 self.path_type == MultiLeaderPathType::Spline
705 }
706}
707
708impl Default for MultiLeaderStyle {
709 fn default() -> Self {
710 Self::standard()
711 }
712}
713
714#[cfg(test)]
719mod tests {
720 use super::*;
721
722 #[test]
723 fn test_multileaderstyle_creation() {
724 let style = MultiLeaderStyle::new("TestStyle");
725 assert_eq!(style.name, "TestStyle");
726 assert!(style.description.is_empty());
727 }
728
729 #[test]
730 fn test_standard_style() {
731 let style = MultiLeaderStyle::standard();
732 assert_eq!(style.name, "Standard");
733 }
734
735 #[test]
736 fn test_default_values() {
737 let style = MultiLeaderStyle::default();
738 assert_eq!(style.path_type, MultiLeaderPathType::StraightLineSegments);
739 assert_eq!(style.content_type, LeaderContentType::MText);
740 assert!(style.enable_landing);
741 assert!(style.enable_dogleg);
742 assert!((style.landing_distance - 0.36).abs() < 1e-10);
743 assert!((style.arrowhead_size - 0.18).abs() < 1e-10);
744 assert!((style.text_height - 0.18).abs() < 1e-10);
745 assert!((style.scale_factor - 1.0).abs() < 1e-10);
746 }
747
748 #[test]
749 fn test_content_type_enum() {
750 assert_eq!(LeaderContentType::from(0), LeaderContentType::None);
751 assert_eq!(LeaderContentType::from(1), LeaderContentType::Block);
752 assert_eq!(LeaderContentType::from(2), LeaderContentType::MText);
753 assert_eq!(LeaderContentType::from(3), LeaderContentType::Tolerance);
754 assert_eq!(LeaderContentType::from(99), LeaderContentType::MText);
755 }
756
757 #[test]
758 fn test_path_type_enum() {
759 assert_eq!(MultiLeaderPathType::from(0), MultiLeaderPathType::Invisible);
760 assert_eq!(MultiLeaderPathType::from(1), MultiLeaderPathType::StraightLineSegments);
761 assert_eq!(MultiLeaderPathType::from(2), MultiLeaderPathType::Spline);
762 }
763
764 #[test]
765 fn test_text_attachment_type() {
766 assert_eq!(TextAttachmentType::from(0), TextAttachmentType::TopOfTopLine);
767 assert_eq!(TextAttachmentType::from(2), TextAttachmentType::MiddleOfText);
768 assert_eq!(TextAttachmentType::from(9), TextAttachmentType::CenterOfText);
769 }
770
771 #[test]
772 fn test_set_block_scale() {
773 let mut style = MultiLeaderStyle::new("Test");
774 style.set_block_scale(2.5);
775
776 assert_eq!(style.block_content_scale_x, 2.5);
777 assert_eq!(style.block_content_scale_y, 2.5);
778 assert_eq!(style.block_content_scale_z, 2.5);
779 assert_eq!(style.uniform_block_scale(), Some(2.5));
780 }
781
782 #[test]
783 fn test_non_uniform_block_scale() {
784 let mut style = MultiLeaderStyle::new("Test");
785 style.block_content_scale_x = 1.0;
786 style.block_content_scale_y = 2.0;
787 style.block_content_scale_z = 1.0;
788
789 assert_eq!(style.uniform_block_scale(), None);
790 }
791
792 #[test]
793 fn test_block_rotation_degrees() {
794 let mut style = MultiLeaderStyle::new("Test");
795 style.set_block_rotation_degrees(45.0);
796
797 assert!((style.block_rotation_degrees() - 45.0).abs() < 1e-10);
798 }
799
800 #[test]
801 fn test_has_text_content() {
802 let mut style = MultiLeaderStyle::new("Test");
803 style.content_type = LeaderContentType::MText;
804 assert!(style.has_text_content());
805 assert!(!style.has_block_content());
806 assert!(!style.has_tolerance_content());
807 }
808
809 #[test]
810 fn test_has_block_content() {
811 let mut style = MultiLeaderStyle::new("Test");
812 style.content_type = LeaderContentType::Block;
813 assert!(!style.has_text_content());
814 assert!(style.has_block_content());
815 }
816
817 #[test]
818 fn test_has_tolerance_content() {
819 let mut style = MultiLeaderStyle::new("Test");
820 style.content_type = LeaderContentType::Tolerance;
821 assert!(style.has_tolerance_content());
822 }
823
824 #[test]
825 fn test_has_visible_leaders() {
826 let mut style = MultiLeaderStyle::new("Test");
827 assert!(style.has_visible_leaders());
828
829 style.path_type = MultiLeaderPathType::Invisible;
830 assert!(!style.has_visible_leaders());
831 }
832
833 #[test]
834 fn test_has_spline_leaders() {
835 let mut style = MultiLeaderStyle::new("Test");
836 assert!(!style.has_spline_leaders());
837
838 style.path_type = MultiLeaderPathType::Spline;
839 assert!(style.has_spline_leaders());
840 }
841
842 #[test]
843 fn test_property_override_flags() {
844 let flags = MultiLeaderPropertyOverrideFlags::PATH_TYPE
845 | MultiLeaderPropertyOverrideFlags::LINE_COLOR
846 | MultiLeaderPropertyOverrideFlags::TEXT_HEIGHT;
847
848 assert!(flags.contains(MultiLeaderPropertyOverrideFlags::PATH_TYPE));
849 assert!(flags.contains(MultiLeaderPropertyOverrideFlags::LINE_COLOR));
850 assert!(flags.contains(MultiLeaderPropertyOverrideFlags::TEXT_HEIGHT));
851 assert!(!flags.contains(MultiLeaderPropertyOverrideFlags::ARROWHEAD));
852 }
853
854 #[test]
855 fn test_leader_line_override_flags() {
856 let flags = LeaderLinePropertyOverrideFlags::PATH_TYPE
857 | LeaderLinePropertyOverrideFlags::ARROWHEAD;
858
859 assert!(flags.contains(LeaderLinePropertyOverrideFlags::PATH_TYPE));
860 assert!(flags.contains(LeaderLinePropertyOverrideFlags::ARROWHEAD));
861 assert!(!flags.contains(LeaderLinePropertyOverrideFlags::LINE_COLOR));
862 }
863
864 #[test]
865 fn test_text_alignment_enum() {
866 assert_eq!(TextAlignmentType::from(0), TextAlignmentType::Left);
867 assert_eq!(TextAlignmentType::from(1), TextAlignmentType::Center);
868 assert_eq!(TextAlignmentType::from(2), TextAlignmentType::Right);
869 }
870
871 #[test]
872 fn test_text_angle_enum() {
873 assert_eq!(TextAngleType::from(0), TextAngleType::ParallelToLastLeaderLine);
874 assert_eq!(TextAngleType::from(1), TextAngleType::Horizontal);
875 assert_eq!(TextAngleType::from(2), TextAngleType::Optimized);
876 }
877
878 #[test]
879 fn test_block_connection_enum() {
880 assert_eq!(BlockContentConnectionType::from(0), BlockContentConnectionType::BlockExtents);
881 assert_eq!(BlockContentConnectionType::from(1), BlockContentConnectionType::BasePoint);
882 }
883
884 #[test]
885 fn test_draw_order_enums() {
886 assert_eq!(LeaderDrawOrderType::from(0), LeaderDrawOrderType::LeaderHeadFirst);
887 assert_eq!(LeaderDrawOrderType::from(1), LeaderDrawOrderType::LeaderTailFirst);
888
889 assert_eq!(MultiLeaderDrawOrderType::from(0), MultiLeaderDrawOrderType::ContentFirst);
890 assert_eq!(MultiLeaderDrawOrderType::from(1), MultiLeaderDrawOrderType::LeaderFirst);
891 }
892}
893