Skip to main content

acadrust/objects/
multileader_style.rs

1//! MultiLeaderStyle object implementation.
2//!
3//! Defines the visual properties and behavior for MultiLeader entities.
4
5use crate::types::{Color, Handle, LineWeight};
6
7use bitflags::bitflags;
8
9// ============================================================================
10// Enums
11// ============================================================================
12
13/// Content type for multileader.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[repr(i16)]
17pub enum LeaderContentType {
18    /// No content.
19    None = 0,
20    /// Block content.
21    Block = 1,
22    /// MText content (default).
23    #[default]
24    MText = 2,
25    /// Tolerance content.
26    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/// Path type for leader lines.
42#[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 leader lines.
47    Invisible = 0,
48    /// Straight line segments (default).
49    #[default]
50    StraightLineSegments = 1,
51    /// Spline curve.
52    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/// Text attachment type for horizontal attachment.
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69#[repr(i16)]
70pub enum TextAttachmentType {
71    /// Top of top line.
72    TopOfTopLine = 0,
73    /// Middle of top line.
74    #[default]
75    MiddleOfTopLine = 1,
76    /// Middle of text.
77    MiddleOfText = 2,
78    /// Middle of bottom line.
79    MiddleOfBottomLine = 3,
80    /// Bottom of bottom line.
81    BottomOfBottomLine = 4,
82    /// Bottom line.
83    BottomLine = 5,
84    /// Bottom of top line, underline bottom line.
85    BottomOfTopLineUnderlineBottomLine = 6,
86    /// Bottom of top line, underline top line.
87    BottomOfTopLineUnderlineTopLine = 7,
88    /// Bottom of top line, underline all.
89    BottomOfTopLineUnderlineAll = 8,
90    /// Center of text (vertical).
91    CenterOfText = 9,
92    /// Center of text with overline (vertical).
93    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/// Text attachment direction.
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118#[repr(i16)]
119pub enum TextAttachmentDirectionType {
120    /// Horizontal attachment.
121    #[default]
122    Horizontal = 0,
123    /// Vertical attachment.
124    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/// Text alignment type.
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
138#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
139#[repr(i16)]
140pub enum TextAlignmentType {
141    /// Left aligned.
142    #[default]
143    Left = 0,
144    /// Center aligned.
145    Center = 1,
146    /// Right aligned.
147    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/// Text angle type for leader text.
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164#[repr(i16)]
165pub enum TextAngleType {
166    /// Parallel to last leader line segment.
167    ParallelToLastLeaderLine = 0,
168    /// Always horizontal (default).
169    #[default]
170    Horizontal = 1,
171    /// Optimized for readability.
172    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/// Block content connection type.
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
188#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
189#[repr(i16)]
190pub enum BlockContentConnectionType {
191    /// Connect to block extents.
192    #[default]
193    BlockExtents = 0,
194    /// Connect to block base point.
195    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/// Leader draw order type.
208#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
209#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
210#[repr(i16)]
211pub enum LeaderDrawOrderType {
212    /// Draw leader head first.
213    #[default]
214    LeaderHeadFirst = 0,
215    /// Draw leader tail first.
216    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/// MultiLeader draw order type.
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
230#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
231#[repr(i16)]
232pub enum MultiLeaderDrawOrderType {
233    /// Draw content first.
234    #[default]
235    ContentFirst = 0,
236    /// Draw leader first.
237    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
249// ============================================================================
250// Flags
251// ============================================================================
252
253bitflags! {
254    /// Property override flags for MultiLeader.
255    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
256    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
257    pub struct MultiLeaderPropertyOverrideFlags: i32 {
258        /// No overrides.
259        const NONE = 0;
260        /// Path type override.
261        const PATH_TYPE = 0x1;
262        /// Line color override.
263        const LINE_COLOR = 0x2;
264        /// Leader line type override.
265        const LEADER_LINE_TYPE = 0x4;
266        /// Leader line weight override.
267        const LEADER_LINE_WEIGHT = 0x8;
268        /// Enable landing override.
269        const ENABLE_LANDING = 0x10;
270        /// Landing gap override.
271        const LANDING_GAP = 0x20;
272        /// Enable dogleg override.
273        const ENABLE_DOGLEG = 0x40;
274        /// Landing distance override.
275        const LANDING_DISTANCE = 0x80;
276        /// Arrowhead override.
277        const ARROWHEAD = 0x100;
278        /// Arrowhead size override.
279        const ARROWHEAD_SIZE = 0x200;
280        /// Content type override.
281        const CONTENT_TYPE = 0x400;
282        /// Text style override.
283        const TEXT_STYLE = 0x800;
284        /// Text left attachment override.
285        const TEXT_LEFT_ATTACHMENT = 0x1000;
286        /// Text angle override.
287        const TEXT_ANGLE = 0x2000;
288        /// Text alignment override.
289        const TEXT_ALIGNMENT = 0x4000;
290        /// Text color override.
291        const TEXT_COLOR = 0x8000;
292        /// Text height override.
293        const TEXT_HEIGHT = 0x10000;
294        /// Text frame override.
295        const TEXT_FRAME = 0x20000;
296        /// Enable use default mtext override.
297        const ENABLE_USE_DEFAULT_MTEXT = 0x40000;
298        /// Block content override.
299        const BLOCK_CONTENT = 0x80000;
300        /// Block content color override.
301        const BLOCK_CONTENT_COLOR = 0x100000;
302        /// Block content scale override.
303        const BLOCK_CONTENT_SCALE = 0x200000;
304        /// Block content rotation override.
305        const BLOCK_CONTENT_ROTATION = 0x400000;
306        /// Block content connection override.
307        const BLOCK_CONTENT_CONNECTION = 0x800000;
308        /// Scale factor override.
309        const SCALE_FACTOR = 0x1000000;
310        /// Text right attachment override.
311        const TEXT_RIGHT_ATTACHMENT = 0x2000000;
312        /// Text switch alignment type override.
313        const TEXT_SWITCH_ALIGNMENT_TYPE = 0x4000000;
314        /// Text attachment direction override.
315        const TEXT_ATTACHMENT_DIRECTION = 0x8000000;
316        /// Text top attachment override.
317        const TEXT_TOP_ATTACHMENT = 0x10000000;
318        /// Text bottom attachment override.
319        const TEXT_BOTTOM_ATTACHMENT = 0x20000000;
320    }
321}
322
323bitflags! {
324    /// Property override flags for leader lines.
325    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
326    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
327    pub struct LeaderLinePropertyOverrideFlags: i32 {
328        /// No overrides.
329        const NONE = 0;
330        /// Path type override.
331        const PATH_TYPE = 1;
332        /// Line color override.
333        const LINE_COLOR = 2;
334        /// Line type override.
335        const LINE_TYPE = 4;
336        /// Line weight override.
337        const LINE_WEIGHT = 8;
338        /// Arrowhead size override.
339        const ARROWHEAD_SIZE = 16;
340        /// Arrowhead override.
341        const ARROWHEAD = 32;
342    }
343}
344
345// ============================================================================
346// MultiLeaderStyle
347// ============================================================================
348
349/// MultiLeader style object.
350///
351/// Defines the visual properties and behavior for MultiLeader entities.
352///
353/// # DXF Information
354/// - Object type: MLEADERSTYLE
355/// - Subclass marker: AcDbMLeaderStyle
356///
357/// # Example
358///
359/// ```ignore
360/// use acadrust::objects::MultiLeaderStyle;
361///
362/// let mut style = MultiLeaderStyle::new("MyStyle");
363/// style.text_height = 0.25;
364/// style.arrowhead_size = 0.25;
365/// style.enable_landing = true;
366/// ```
367#[derive(Debug, Clone, PartialEq)]
368#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
369pub struct MultiLeaderStyle {
370    /// Object handle.
371    pub handle: Handle,
372
373    /// Owner handle.
374    pub owner_handle: Handle,
375
376    /// Style name.
377    /// DXF code: 3
378    pub name: String,
379
380    /// Style description.
381    /// DXF code: 300
382    pub description: String,
383
384    // ========== Leader Line Properties ==========
385    /// Leader path type.
386    /// DXF code: 173
387    pub path_type: MultiLeaderPathType,
388
389    /// Leader line color.
390    /// DXF code: 91
391    pub line_color: Color,
392
393    /// Leader line type handle.
394    /// DXF code: 340
395    pub line_type_handle: Option<Handle>,
396
397    /// Leader line weight.
398    /// DXF code: 92
399    pub line_weight: LineWeight,
400
401    /// Enable landing (dogleg).
402    /// DXF code: 290
403    pub enable_landing: bool,
404
405    /// Enable dogleg.
406    /// DXF code: 291
407    pub enable_dogleg: bool,
408
409    /// Landing/dogleg length.
410    /// DXF code: 43
411    pub landing_distance: f64,
412
413    /// Gap between leader and content.
414    /// DXF code: 42
415    pub landing_gap: f64,
416
417    // ========== Arrowhead Properties ==========
418    /// Arrowhead block handle.
419    /// DXF code: 341
420    pub arrowhead_handle: Option<Handle>,
421
422    /// Arrowhead size.
423    /// DXF code: 44
424    pub arrowhead_size: f64,
425
426    // ========== Content Properties ==========
427    /// Content type.
428    /// DXF code: 170
429    pub content_type: LeaderContentType,
430
431    // ========== Text Properties ==========
432    /// Text style handle.
433    /// DXF code: 342
434    pub text_style_handle: Option<Handle>,
435
436    /// Text left attachment type.
437    /// DXF code: 174
438    pub text_left_attachment: TextAttachmentType,
439
440    /// Text right attachment type.
441    /// DXF code: 178
442    pub text_right_attachment: TextAttachmentType,
443
444    /// Text top attachment type.
445    /// DXF code: 273
446    pub text_top_attachment: TextAttachmentType,
447
448    /// Text bottom attachment type.
449    /// DXF code: 272
450    pub text_bottom_attachment: TextAttachmentType,
451
452    /// Text attachment direction.
453    /// DXF code: 271
454    pub text_attachment_direction: TextAttachmentDirectionType,
455
456    /// Text angle type.
457    /// DXF code: 175
458    pub text_angle_type: TextAngleType,
459
460    /// Text alignment.
461    /// DXF code: 176
462    pub text_alignment: TextAlignmentType,
463
464    /// Text color.
465    /// DXF code: 93
466    pub text_color: Color,
467
468    /// Text height.
469    /// DXF code: 45
470    pub text_height: f64,
471
472    /// Draw text frame.
473    /// DXF code: 292
474    pub text_frame: bool,
475
476    /// Text always left aligned.
477    /// DXF code: 297
478    pub text_always_left: bool,
479
480    /// Default text contents.
481    /// DXF code: 304
482    pub default_text: String,
483
484    // ========== Block Properties ==========
485    /// Block content handle.
486    /// DXF code: 343
487    pub block_content_handle: Option<Handle>,
488
489    /// Block content color.
490    /// DXF code: 94
491    pub block_content_color: Color,
492
493    /// Block content connection type.
494    /// DXF code: 177
495    pub block_content_connection: BlockContentConnectionType,
496
497    /// Block content rotation.
498    /// DXF code: 141
499    pub block_content_rotation: f64,
500
501    /// Block content X scale.
502    /// DXF code: 47
503    pub block_content_scale_x: f64,
504
505    /// Block content Y scale.
506    /// DXF code: 49
507    pub block_content_scale_y: f64,
508
509    /// Block content Z scale.
510    /// DXF code: 140
511    pub block_content_scale_z: f64,
512
513    /// Enable block scale.
514    /// DXF code: 293
515    pub enable_block_scale: bool,
516
517    /// Enable block rotation.
518    /// DXF code: 294
519    pub enable_block_rotation: bool,
520
521    // ========== Scale and Constraints ==========
522    /// Overall scale factor.
523    /// DXF code: 142
524    pub scale_factor: f64,
525
526    /// Align space.
527    /// DXF code: 46
528    pub align_space: f64,
529
530    /// Break gap size.
531    /// DXF code: 143
532    pub break_gap_size: f64,
533
534    /// Max leader segment points.
535    /// DXF code: 90
536    pub max_leader_points: i32,
537
538    /// First segment angle constraint.
539    /// DXF code: 40
540    pub first_segment_angle: f64,
541
542    /// Second segment angle constraint.
543    /// DXF code: 41
544    pub second_segment_angle: f64,
545
546    // ========== Draw Order ==========
547    /// Leader draw order.
548    /// DXF code: 172
549    pub leader_draw_order: LeaderDrawOrderType,
550
551    /// MultiLeader draw order.
552    /// DXF code: 171
553    pub multileader_draw_order: MultiLeaderDrawOrderType,
554
555    // ========== Flags ==========
556    /// Is annotative.
557    /// DXF code: 296
558    pub is_annotative: bool,
559
560    /// Property changed flag.
561    /// DXF code: 295
562    pub property_changed: bool,
563
564    /// R2013+ undocumented flag.
565    /// DXF code: 298
566    pub unknown_flag_298: bool,
567}
568
569impl MultiLeaderStyle {
570    /// Object type name.
571    pub const OBJECT_NAME: &'static str = "MLEADERSTYLE";
572
573    /// Subclass marker.
574    pub const SUBCLASS_MARKER: &'static str = "AcDbMLeaderStyle";
575
576    /// Default style name.
577    pub const STANDARD: &'static str = "Standard";
578
579    /// Creates a new MultiLeaderStyle with default values.
580    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            // Leader line
588            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
598            arrowhead_handle: None,
599            arrowhead_size: 0.18,
600
601            // Content
602            content_type: LeaderContentType::MText,
603
604            // Text
605            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
620            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 and constraints
631            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            // Draw order
639            leader_draw_order: LeaderDrawOrderType::LeaderHeadFirst,
640            multileader_draw_order: MultiLeaderDrawOrderType::ContentFirst,
641
642            // Flags
643            is_annotative: false,
644            property_changed: false,
645            unknown_flag_298: false,
646        }
647    }
648
649    /// Creates the standard MultiLeaderStyle.
650    pub fn standard() -> Self {
651        Self::new(Self::STANDARD)
652    }
653
654    /// Sets all block content scale factors uniformly.
655    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    /// Gets the uniform block scale if all factors are equal.
662    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    /// Sets the block content rotation in degrees.
673    pub fn set_block_rotation_degrees(&mut self, degrees: f64) {
674        self.block_content_rotation = degrees.to_radians();
675    }
676
677    /// Gets the block content rotation in degrees.
678    pub fn block_rotation_degrees(&self) -> f64 {
679        self.block_content_rotation.to_degrees()
680    }
681
682    /// Returns true if this style uses text content.
683    pub fn has_text_content(&self) -> bool {
684        self.content_type == LeaderContentType::MText
685    }
686
687    /// Returns true if this style uses block content.
688    pub fn has_block_content(&self) -> bool {
689        self.content_type == LeaderContentType::Block
690    }
691
692    /// Returns true if this style uses tolerance content.
693    pub fn has_tolerance_content(&self) -> bool {
694        self.content_type == LeaderContentType::Tolerance
695    }
696
697    /// Returns true if leader lines are visible.
698    pub fn has_visible_leaders(&self) -> bool {
699        self.path_type != MultiLeaderPathType::Invisible
700    }
701
702    /// Returns true if leaders use spline curves.
703    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// ============================================================================
715// Tests
716// ============================================================================
717
718#[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