Skip to main content

acadrust/entities/
multileader.rs

1//! MultiLeader entity implementation.
2//!
3//! The MultiLeader (MLEADER) entity is an advanced annotation object that can have
4//! multiple leader lines connecting to text (MText), block, or tolerance content.
5
6use crate::entities::{Entity, EntityCommon};
7use crate::types::{BoundingBox3D, Color, Handle, LineWeight, Transparency, Vector3};
8
9use bitflags::bitflags;
10
11// ============================================================================
12// Enums
13// ============================================================================
14
15/// Content type for multileader annotation.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[repr(i16)]
19pub enum LeaderContentType {
20    /// No content (leader only).
21    None = 0,
22    /// Content is a block reference.
23    Block = 1,
24    /// Content is multiline text.
25    #[default]
26    MText = 2,
27    /// Content is a tolerance frame.
28    Tolerance = 3,
29}
30
31impl From<i16> for LeaderContentType {
32    fn from(value: i16) -> Self {
33        match value {
34            0 => Self::None,
35            1 => Self::Block,
36            2 => Self::MText,
37            3 => Self::Tolerance,
38            _ => Self::None,
39        }
40    }
41}
42
43/// Leader path type (line style).
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46#[repr(i16)]
47pub enum MultiLeaderPathType {
48    /// Leader is invisible.
49    Invisible = 0,
50    /// Straight line segments (polyline).
51    #[default]
52    StraightLineSegments = 1,
53    /// Spline curve.
54    Spline = 2,
55}
56
57impl From<i16> for MultiLeaderPathType {
58    fn from(value: i16) -> Self {
59        match value {
60            0 => Self::Invisible,
61            1 => Self::StraightLineSegments,
62            2 => Self::Spline,
63            _ => Self::StraightLineSegments,
64        }
65    }
66}
67
68/// Text attachment point relative to landing.
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71#[repr(i16)]
72pub enum TextAttachmentType {
73    /// Top of top line.
74    TopOfTopLine = 0,
75    /// Middle of top line.
76    MiddleOfTopLine = 1,
77    /// Middle of text (vertical center).
78    #[default]
79    MiddleOfText = 2,
80    /// Middle of bottom line.
81    MiddleOfBottomLine = 3,
82    /// Bottom of bottom line.
83    BottomOfBottomLine = 4,
84    /// Bottom line.
85    BottomLine = 5,
86    /// Bottom of top line, underline bottom line.
87    BottomOfTopLineUnderlineBottomLine = 6,
88    /// Bottom of top line, underline top line.
89    BottomOfTopLineUnderlineTopLine = 7,
90    /// Bottom of top line, underline all.
91    BottomOfTopLineUnderlineAll = 8,
92    /// Center of text (for vertical attachment).
93    CenterOfText = 9,
94    /// Center of text with overline (for vertical attachment).
95    CenterOfTextOverline = 10,
96}
97
98impl From<i16> for TextAttachmentType {
99    fn from(value: i16) -> Self {
100        match value {
101            0 => Self::TopOfTopLine,
102            1 => Self::MiddleOfTopLine,
103            2 => Self::MiddleOfText,
104            3 => Self::MiddleOfBottomLine,
105            4 => Self::BottomOfBottomLine,
106            5 => Self::BottomLine,
107            6 => Self::BottomOfTopLineUnderlineBottomLine,
108            7 => Self::BottomOfTopLineUnderlineTopLine,
109            8 => Self::BottomOfTopLineUnderlineAll,
110            9 => Self::CenterOfText,
111            10 => Self::CenterOfTextOverline,
112            _ => Self::MiddleOfText,
113        }
114    }
115}
116
117/// Text angle type for leader content.
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
119#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
120#[repr(i16)]
121pub enum TextAngleType {
122    /// Text angle parallel to last leader line segment.
123    ParallelToLastLeaderLine = 0,
124    /// Text is always horizontal.
125    #[default]
126    Horizontal = 1,
127    /// Like parallel, but rotated 180° if needed for readability.
128    Optimized = 2,
129}
130
131impl From<i16> for TextAngleType {
132    fn from(value: i16) -> Self {
133        match value {
134            0 => Self::ParallelToLastLeaderLine,
135            1 => Self::Horizontal,
136            2 => Self::Optimized,
137            _ => Self::Horizontal,
138        }
139    }
140}
141
142/// Block content connection type.
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
144#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
145#[repr(i16)]
146pub enum BlockContentConnectionType {
147    /// Connect to block extents.
148    #[default]
149    BlockExtents = 0,
150    /// Connect to block base point.
151    BasePoint = 1,
152}
153
154impl From<i16> for BlockContentConnectionType {
155    fn from(value: i16) -> Self {
156        match value {
157            0 => Self::BlockExtents,
158            1 => Self::BasePoint,
159            _ => Self::BlockExtents,
160        }
161    }
162}
163
164/// Text attachment direction (horizontal or vertical).
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
166#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
167#[repr(i16)]
168pub enum TextAttachmentDirectionType {
169    /// Leaders attach to left/right of content.
170    #[default]
171    Horizontal = 0,
172    /// Leaders attach to top/bottom of content.
173    Vertical = 1,
174}
175
176impl From<i16> for TextAttachmentDirectionType {
177    fn from(value: i16) -> Self {
178        match value {
179            0 => Self::Horizontal,
180            1 => Self::Vertical,
181            _ => Self::Horizontal,
182        }
183    }
184}
185
186/// Text attachment point type (left/center/right).
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
188#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
189#[repr(i16)]
190pub enum TextAttachmentPointType {
191    /// Attach to left.
192    Left = 1,
193    /// Attach to center.
194    #[default]
195    Center = 2,
196    /// Attach to right.
197    Right = 3,
198}
199
200impl From<i16> for TextAttachmentPointType {
201    fn from(value: i16) -> Self {
202        match value {
203            1 => Self::Left,
204            2 => Self::Center,
205            3 => Self::Right,
206            _ => Self::Center,
207        }
208    }
209}
210
211/// Text alignment type.
212#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
213#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
214#[repr(i16)]
215pub enum TextAlignmentType {
216    /// Left alignment.
217    #[default]
218    Left = 0,
219    /// Center alignment.
220    Center = 1,
221    /// Right alignment.
222    Right = 2,
223}
224
225impl From<i16> for TextAlignmentType {
226    fn from(value: i16) -> Self {
227        match value {
228            0 => Self::Left,
229            1 => Self::Center,
230            2 => Self::Right,
231            _ => Self::Left,
232        }
233    }
234}
235
236/// Flow direction for text columns.
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
238#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
239#[repr(i16)]
240pub enum FlowDirectionType {
241    /// Horizontal flow.
242    #[default]
243    Horizontal = 1,
244    /// Vertical flow.
245    Vertical = 3,
246    /// Use style setting.
247    ByStyle = 5,
248}
249
250impl From<i16> for FlowDirectionType {
251    fn from(value: i16) -> Self {
252        match value {
253            1 => Self::Horizontal,
254            3 => Self::Vertical,
255            5 => Self::ByStyle,
256            _ => Self::Horizontal,
257        }
258    }
259}
260
261/// Line spacing style.
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
263#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
264#[repr(i16)]
265pub enum LineSpacingStyle {
266    /// At least the specified spacing.
267    #[default]
268    AtLeast = 1,
269    /// Exactly the specified spacing.
270    Exactly = 2,
271}
272
273impl From<i16> for LineSpacingStyle {
274    fn from(value: i16) -> Self {
275        match value {
276            1 => Self::AtLeast,
277            2 => Self::Exactly,
278            _ => Self::AtLeast,
279        }
280    }
281}
282
283// ============================================================================
284// Bitflags
285// ============================================================================
286
287bitflags! {
288    /// Property override flags for MultiLeader.
289    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
290    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
291    pub struct MultiLeaderPropertyOverrideFlags: u32 {
292        /// No overrides.
293        const NONE = 0;
294        /// Override path type.
295        const PATH_TYPE = 0x1;
296        /// Override line color.
297        const LINE_COLOR = 0x2;
298        /// Override leader line type.
299        const LEADER_LINE_TYPE = 0x4;
300        /// Override leader line weight.
301        const LEADER_LINE_WEIGHT = 0x8;
302        /// Override enable landing.
303        const ENABLE_LANDING = 0x10;
304        /// Override landing gap.
305        const LANDING_GAP = 0x20;
306        /// Override enable dogleg.
307        const ENABLE_DOGLEG = 0x40;
308        /// Override landing distance.
309        const LANDING_DISTANCE = 0x80;
310        /// Override arrowhead.
311        const ARROWHEAD = 0x100;
312        /// Override arrowhead size.
313        const ARROWHEAD_SIZE = 0x200;
314        /// Override content type.
315        const CONTENT_TYPE = 0x400;
316        /// Override text style.
317        const TEXT_STYLE = 0x800;
318        /// Override text left attachment.
319        const TEXT_LEFT_ATTACHMENT = 0x1000;
320        /// Override text angle.
321        const TEXT_ANGLE = 0x2000;
322        /// Override text alignment.
323        const TEXT_ALIGNMENT = 0x4000;
324        /// Override text color.
325        const TEXT_COLOR = 0x8000;
326        /// Override text height.
327        const TEXT_HEIGHT = 0x10000;
328        /// Override text frame.
329        const TEXT_FRAME = 0x20000;
330        /// Override use default MText.
331        const ENABLE_USE_DEFAULT_MTEXT = 0x40000;
332        /// Override block content.
333        const BLOCK_CONTENT = 0x80000;
334        /// Override block content color.
335        const BLOCK_CONTENT_COLOR = 0x100000;
336        /// Override block content scale.
337        const BLOCK_CONTENT_SCALE = 0x200000;
338        /// Override block content rotation.
339        const BLOCK_CONTENT_ROTATION = 0x400000;
340        /// Override block content connection.
341        const BLOCK_CONTENT_CONNECTION = 0x800000;
342        /// Override scale factor.
343        const SCALE_FACTOR = 0x1000000;
344        /// Override text right attachment.
345        const TEXT_RIGHT_ATTACHMENT = 0x2000000;
346        /// Override text switch alignment type.
347        const TEXT_SWITCH_ALIGNMENT_TYPE = 0x4000000;
348        /// Override text attachment direction.
349        const TEXT_ATTACHMENT_DIRECTION = 0x8000000;
350        /// Override text top attachment.
351        const TEXT_TOP_ATTACHMENT = 0x10000000;
352        /// Override text bottom attachment.
353        const TEXT_BOTTOM_ATTACHMENT = 0x20000000;
354    }
355}
356
357bitflags! {
358    /// Property override flags for individual leader lines.
359    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
360    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
361    pub struct LeaderLinePropertyOverrideFlags: u32 {
362        /// No overrides.
363        const NONE = 0;
364        /// Override path type.
365        const PATH_TYPE = 1;
366        /// Override line color.
367        const LINE_COLOR = 2;
368        /// Override line type.
369        const LINE_TYPE = 4;
370        /// Override line weight.
371        const LINE_WEIGHT = 8;
372        /// Override arrowhead size.
373        const ARROWHEAD_SIZE = 16;
374        /// Override arrowhead.
375        const ARROWHEAD = 32;
376    }
377}
378
379// ============================================================================
380// Support Structures
381// ============================================================================
382
383/// Start/end point pair for leader line breaks.
384#[derive(Debug, Clone, Copy, PartialEq)]
385#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
386pub struct StartEndPointPair {
387    /// Break start point.
388    pub start_point: Vector3,
389    /// Break end point.
390    pub end_point: Vector3,
391}
392
393impl StartEndPointPair {
394    /// Creates a new start/end point pair.
395    pub fn new(start_point: Vector3, end_point: Vector3) -> Self {
396        Self {
397            start_point,
398            end_point,
399        }
400    }
401}
402
403impl Default for StartEndPointPair {
404    fn default() -> Self {
405        Self {
406            start_point: Vector3::ZERO,
407            end_point: Vector3::ZERO,
408        }
409    }
410}
411
412/// A single leader line with vertices.
413#[derive(Debug, Clone, PartialEq)]
414#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
415pub struct LeaderLine {
416    /// Index of this leader line.
417    pub index: i32,
418    /// Segment index.
419    pub segment_index: i32,
420    /// Points (vertices) along the leader line.
421    pub points: Vec<Vector3>,
422    /// Break start/end point pairs.
423    pub break_points: Vec<StartEndPointPair>,
424    /// Number of break info entries.
425    pub break_info_count: i32,
426    /// Path type (straight, spline, invisible).
427    pub path_type: MultiLeaderPathType,
428    /// Line color override.
429    pub line_color: Color,
430    /// Line type handle.
431    pub line_type_handle: Option<Handle>,
432    /// Line weight override.
433    pub line_weight: LineWeight,
434    /// Arrowhead block handle.
435    pub arrowhead_handle: Option<Handle>,
436    /// Arrowhead size.
437    pub arrowhead_size: f64,
438    /// Property override flags.
439    pub override_flags: LeaderLinePropertyOverrideFlags,
440}
441
442impl LeaderLine {
443    /// Creates a new leader line with the given index.
444    pub fn new(index: i32) -> Self {
445        Self {
446            index,
447            segment_index: 0,
448            points: Vec::new(),
449            break_points: Vec::new(),
450            break_info_count: 0,
451            path_type: MultiLeaderPathType::StraightLineSegments,
452            line_color: Color::ByBlock,
453            line_type_handle: None,
454            line_weight: LineWeight::ByLayer,
455            arrowhead_handle: None,
456            arrowhead_size: 0.18,
457            override_flags: LeaderLinePropertyOverrideFlags::NONE,
458        }
459    }
460
461    /// Creates a leader line from a list of points.
462    pub fn from_points(index: i32, points: Vec<Vector3>) -> Self {
463        let mut line = Self::new(index);
464        line.points = points;
465        line
466    }
467
468    /// Adds a point to the leader line.
469    pub fn add_point(&mut self, point: Vector3) {
470        self.points.push(point);
471    }
472
473    /// Returns the number of points.
474    pub fn point_count(&self) -> usize {
475        self.points.len()
476    }
477
478    /// Gets the start point (first point).
479    pub fn start_point(&self) -> Option<Vector3> {
480        self.points.first().copied()
481    }
482
483    /// Gets the end point (last point).
484    pub fn end_point(&self) -> Option<Vector3> {
485        self.points.last().copied()
486    }
487
488    /// Calculates the total length of the leader line.
489    pub fn length(&self) -> f64 {
490        if self.points.len() < 2 {
491            return 0.0;
492        }
493        self.points
494            .windows(2)
495            .map(|w| (w[1] - w[0]).length())
496            .sum()
497    }
498}
499
500impl Default for LeaderLine {
501    fn default() -> Self {
502        Self::new(0)
503    }
504}
505
506/// A leader root containing one or more leader lines.
507#[derive(Debug, Clone, PartialEq)]
508#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
509pub struct LeaderRoot {
510    /// Index of this leader root.
511    pub leader_index: i32,
512    /// Whether content is valid.
513    pub content_valid: bool,
514    /// Unknown flag (ODA writes true).
515    pub unknown: bool,
516    /// Connection point at content.
517    pub connection_point: Vector3,
518    /// Direction from connection point.
519    pub direction: Vector3,
520    /// Break start/end point pairs.
521    pub break_points: Vec<StartEndPointPair>,
522    /// Leader lines in this root.
523    pub lines: Vec<LeaderLine>,
524    /// Landing distance (dogleg length).
525    pub landing_distance: f64,
526    /// Text attachment direction (R2010+).
527    pub text_attachment_direction: TextAttachmentDirectionType,
528}
529
530impl LeaderRoot {
531    /// Creates a new leader root with the given index.
532    pub fn new(index: i32) -> Self {
533        Self {
534            leader_index: index,
535            content_valid: true,
536            unknown: true,
537            connection_point: Vector3::ZERO,
538            direction: Vector3::new(1.0, 0.0, 0.0),
539            break_points: Vec::new(),
540            lines: Vec::new(),
541            landing_distance: 0.36,
542            text_attachment_direction: TextAttachmentDirectionType::Horizontal,
543        }
544    }
545
546    /// Adds a leader line to this root.
547    pub fn add_line(&mut self, line: LeaderLine) {
548        self.lines.push(line);
549    }
550
551    /// Creates a leader line and adds it to this root.
552    pub fn create_line(&mut self, points: Vec<Vector3>) -> &mut LeaderLine {
553        let index = self.lines.len() as i32;
554        let line = LeaderLine::from_points(index, points);
555        self.lines.push(line);
556        self.lines.last_mut().unwrap()
557    }
558
559    /// Returns the number of leader lines.
560    pub fn line_count(&self) -> usize {
561        self.lines.len()
562    }
563}
564
565impl Default for LeaderRoot {
566    fn default() -> Self {
567        Self::new(0)
568    }
569}
570
571/// Block attribute for block content.
572#[derive(Debug, Clone, PartialEq)]
573#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
574pub struct BlockAttribute {
575    /// Handle to the attribute definition.
576    pub attribute_definition_handle: Option<Handle>,
577    /// Attribute index.
578    pub index: i16,
579    /// Attribute width.
580    pub width: f64,
581    /// Attribute text value.
582    pub text: String,
583}
584
585impl BlockAttribute {
586    /// Creates a new block attribute.
587    pub fn new(text: &str) -> Self {
588        Self {
589            attribute_definition_handle: None,
590            index: 0,
591            width: 0.0,
592            text: text.to_string(),
593        }
594    }
595}
596
597impl Default for BlockAttribute {
598    fn default() -> Self {
599        Self::new("")
600    }
601}
602
603// ============================================================================
604// MultiLeader Context Data
605// ============================================================================
606
607/// Context data for MultiLeader annotation (geometry and content).
608#[derive(Debug, Clone, PartialEq)]
609#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
610pub struct MultiLeaderAnnotContext {
611    /// Leader roots (each can have multiple leader lines).
612    pub leader_roots: Vec<LeaderRoot>,
613
614    // Scale and positioning
615    /// Overall scale factor.
616    pub scale_factor: f64,
617    /// Content base point.
618    pub content_base_point: Vector3,
619
620    // Text content properties
621    /// Whether there is text content.
622    pub has_text_contents: bool,
623    /// Text string (may contain MTEXT markup).
624    pub text_string: String,
625    /// Text normal vector.
626    pub text_normal: Vector3,
627    /// Text location.
628    pub text_location: Vector3,
629    /// Text direction.
630    pub text_direction: Vector3,
631    /// Text rotation in radians.
632    pub text_rotation: f64,
633    /// Text height (scaled).
634    pub text_height: f64,
635    /// Text width boundary.
636    pub text_width: f64,
637    /// Text height boundary.
638    pub text_boundary_height: f64,
639    /// Line spacing factor.
640    pub line_spacing_factor: f64,
641    /// Line spacing style.
642    pub line_spacing_style: LineSpacingStyle,
643    /// Text color.
644    pub text_color: Color,
645    /// Text attachment point.
646    pub text_attachment_point: TextAttachmentPointType,
647    /// Text flow direction.
648    pub text_flow_direction: FlowDirectionType,
649    /// Text alignment.
650    pub text_alignment: TextAlignmentType,
651    /// Text left attachment type.
652    pub text_left_attachment: TextAttachmentType,
653    /// Text right attachment type.
654    pub text_right_attachment: TextAttachmentType,
655    /// Text top attachment type.
656    pub text_top_attachment: TextAttachmentType,
657    /// Text bottom attachment type.
658    pub text_bottom_attachment: TextAttachmentType,
659    /// Whether text height is automatic.
660    pub text_height_automatic: bool,
661    /// Word break enabled.
662    pub word_break: bool,
663    /// Text style handle.
664    pub text_style_handle: Option<Handle>,
665
666    // Block content properties
667    /// Whether there is block content.
668    pub has_block_contents: bool,
669    /// Block content handle.
670    pub block_content_handle: Option<Handle>,
671    /// Block content normal.
672    pub block_content_normal: Vector3,
673    /// Block content location.
674    pub block_content_location: Vector3,
675    /// Block content scale.
676    pub block_content_scale: Vector3,
677    /// Block rotation in radians.
678    pub block_rotation: f64,
679    /// Block content color.
680    pub block_content_color: Color,
681    /// Block connection type.
682    pub block_connection_type: BlockContentConnectionType,
683
684    // Column properties
685    /// Column type.
686    pub column_type: i16,
687    /// Column width.
688    pub column_width: f64,
689    /// Column gutter.
690    pub column_gutter: f64,
691    /// Column flow reversed.
692    pub column_flow_reversed: bool,
693    /// Column sizes.
694    pub column_sizes: Vec<f64>,
695
696    // Background fill
697    /// Background fill enabled.
698    pub background_fill_enabled: bool,
699    /// Background mask fill on.
700    pub background_mask_fill_on: bool,
701    /// Background fill color.
702    pub background_fill_color: Color,
703    /// Background scale factor.
704    pub background_scale_factor: f64,
705    /// Background transparency.
706    pub background_transparency: i32,
707
708    // Transformation
709    /// Base point.
710    pub base_point: Vector3,
711    /// Base direction vector.
712    pub base_direction: Vector3,
713    /// Base vertical vector.
714    pub base_vertical: Vector3,
715    /// Normal reversed.
716    pub normal_reversed: bool,
717
718    // Arrowhead
719    /// Arrowhead size (scaled).
720    pub arrowhead_size: f64,
721    /// Landing gap.
722    pub landing_gap: f64,
723
724    // Transformation matrix (4x4)
725    /// Transformation matrix (16 values, column-major).
726    pub transform_matrix: [f64; 16],
727
728    /// Scale object handle.
729    pub scale_handle: Option<Handle>,
730}
731
732impl MultiLeaderAnnotContext {
733    /// Creates a new annotation context with defaults.
734    pub fn new() -> Self {
735        // Identity matrix
736        let mut transform_matrix = [0.0; 16];
737        transform_matrix[0] = 1.0;
738        transform_matrix[5] = 1.0;
739        transform_matrix[10] = 1.0;
740        transform_matrix[15] = 1.0;
741
742        Self {
743            leader_roots: Vec::new(),
744            scale_factor: 1.0,
745            content_base_point: Vector3::ZERO,
746            has_text_contents: false,
747            text_string: String::new(),
748            text_normal: Vector3::new(0.0, 0.0, 1.0),
749            text_location: Vector3::ZERO,
750            text_direction: Vector3::new(1.0, 0.0, 0.0),
751            text_rotation: 0.0,
752            text_height: 0.18,
753            text_width: 0.0,
754            text_boundary_height: 0.0,
755            line_spacing_factor: 1.0,
756            line_spacing_style: LineSpacingStyle::AtLeast,
757            text_color: Color::ByBlock,
758            text_attachment_point: TextAttachmentPointType::Center,
759            text_flow_direction: FlowDirectionType::Horizontal,
760            text_alignment: TextAlignmentType::Left,
761            text_left_attachment: TextAttachmentType::MiddleOfText,
762            text_right_attachment: TextAttachmentType::MiddleOfText,
763            text_top_attachment: TextAttachmentType::CenterOfText,
764            text_bottom_attachment: TextAttachmentType::CenterOfText,
765            text_height_automatic: false,
766            word_break: true,
767            text_style_handle: None,
768            has_block_contents: false,
769            block_content_handle: None,
770            block_content_normal: Vector3::new(0.0, 0.0, 1.0),
771            block_content_location: Vector3::ZERO,
772            block_content_scale: Vector3::new(1.0, 1.0, 1.0),
773            block_rotation: 0.0,
774            block_content_color: Color::ByBlock,
775            block_connection_type: BlockContentConnectionType::BlockExtents,
776            column_type: 0,
777            column_width: 0.0,
778            column_gutter: 0.0,
779            column_flow_reversed: false,
780            column_sizes: Vec::new(),
781            background_fill_enabled: false,
782            background_mask_fill_on: false,
783            background_fill_color: Color::ByBlock,
784            background_scale_factor: 1.5,
785            background_transparency: 0,
786            base_point: Vector3::ZERO,
787            base_direction: Vector3::new(1.0, 0.0, 0.0),
788            base_vertical: Vector3::new(0.0, 1.0, 0.0),
789            normal_reversed: false,
790            arrowhead_size: 0.18,
791            landing_gap: 0.09,
792            transform_matrix,
793            scale_handle: None,
794        }
795    }
796
797    /// Adds a leader root and returns a mutable reference.
798    pub fn add_leader_root(&mut self) -> &mut LeaderRoot {
799        let index = self.leader_roots.len() as i32;
800        self.leader_roots.push(LeaderRoot::new(index));
801        self.leader_roots.last_mut().unwrap()
802    }
803
804    /// Gets the number of leader roots.
805    pub fn leader_root_count(&self) -> usize {
806        self.leader_roots.len()
807    }
808
809    /// Sets text content.
810    pub fn set_text_content(&mut self, text: &str, location: Vector3) {
811        self.has_text_contents = true;
812        self.text_string = text.to_string();
813        self.text_location = location;
814        self.content_base_point = location;
815    }
816
817    /// Sets block content.
818    pub fn set_block_content(&mut self, block_handle: Handle, location: Vector3) {
819        self.has_block_contents = true;
820        self.block_content_handle = Some(block_handle);
821        self.block_content_location = location;
822        self.content_base_point = location;
823    }
824}
825
826impl Default for MultiLeaderAnnotContext {
827    fn default() -> Self {
828        Self::new()
829    }
830}
831
832// ============================================================================
833// MultiLeader Entity
834// ============================================================================
835
836/// MultiLeader (MLEADER) entity.
837///
838/// An advanced annotation object with multiple leader lines connecting to
839/// text (MText), block, or tolerance content.
840///
841/// # Example
842///
843/// ```ignore
844/// use acadrust::entities::MultiLeader;
845/// use acadrust::types::Vector3;
846///
847/// // Create a multileader with text content
848/// let mut mleader = MultiLeader::new();
849/// mleader.set_text_content("Note", Vector3::new(10.0, 10.0, 0.0));
850///
851/// // Add a leader from (0,0) to (5,5) to (10,10)
852/// let root = mleader.add_leader_root();
853/// root.create_line(vec![
854///     Vector3::new(0.0, 0.0, 0.0),
855///     Vector3::new(5.0, 5.0, 0.0),
856/// ]);
857/// ```
858#[derive(Debug, Clone, PartialEq)]
859#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
860pub struct MultiLeader {
861    /// Common entity data.
862    pub common: EntityCommon,
863
864    // Style reference
865    /// Handle to MultiLeader style.
866    pub style_handle: Option<Handle>,
867
868    // Content settings
869    /// Content type (None, Block, MText, Tolerance).
870    pub content_type: LeaderContentType,
871    /// Context data (geometry and content).
872    pub context: MultiLeaderAnnotContext,
873    /// Block attributes (for block content).
874    pub block_attributes: Vec<BlockAttribute>,
875
876    // Leader line settings
877    /// Path type (straight, spline, invisible).
878    pub path_type: MultiLeaderPathType,
879    /// Leader line color.
880    pub line_color: Color,
881    /// Leader line type handle.
882    pub line_type_handle: Option<Handle>,
883    /// Leader line weight.
884    pub line_weight: LineWeight,
885    /// Enable landing.
886    pub enable_landing: bool,
887    /// Enable dogleg.
888    pub enable_dogleg: bool,
889    /// Dogleg length.
890    pub dogleg_length: f64,
891    /// Arrowhead block handle.
892    pub arrowhead_handle: Option<Handle>,
893    /// Arrowhead size.
894    pub arrowhead_size: f64,
895
896    // Text settings
897    /// Text style handle.
898    pub text_style_handle: Option<Handle>,
899    /// Text color.
900    pub text_color: Color,
901    /// Draw text with frame.
902    pub text_frame: bool,
903    /// Text height.
904    pub text_height: f64,
905    /// Text left attachment type.
906    pub text_left_attachment: TextAttachmentType,
907    /// Text right attachment type.
908    pub text_right_attachment: TextAttachmentType,
909    /// Text top attachment type.
910    pub text_top_attachment: TextAttachmentType,
911    /// Text bottom attachment type.
912    pub text_bottom_attachment: TextAttachmentType,
913    /// Text attachment direction.
914    pub text_attachment_direction: TextAttachmentDirectionType,
915    /// Text attachment point.
916    pub text_attachment_point: TextAttachmentPointType,
917    /// Text alignment.
918    pub text_alignment: TextAlignmentType,
919    /// Text angle type.
920    pub text_angle_type: TextAngleType,
921    /// Text direction negative.
922    pub text_direction_negative: bool,
923    /// Text align in IPE.
924    pub text_align_in_ipe: i16,
925
926    // Block settings
927    /// Block content handle.
928    pub block_content_handle: Option<Handle>,
929    /// Block content color.
930    pub block_content_color: Color,
931    /// Block content connection type.
932    pub block_connection_type: BlockContentConnectionType,
933    /// Block rotation in radians.
934    pub block_rotation: f64,
935    /// Block scale.
936    pub block_scale: Vector3,
937
938    // General settings
939    /// Scale factor.
940    pub scale_factor: f64,
941    /// Property override flags.
942    pub property_override_flags: MultiLeaderPropertyOverrideFlags,
943    /// Enable annotation scale.
944    pub enable_annotation_scale: bool,
945    /// Extend leader to text.
946    pub extend_leader_to_text: bool,
947}
948
949impl MultiLeader {
950    /// Creates a new MultiLeader with default settings.
951    pub fn new() -> Self {
952        Self {
953            common: EntityCommon::default(),
954            style_handle: None,
955            content_type: LeaderContentType::MText,
956            context: MultiLeaderAnnotContext::new(),
957            block_attributes: Vec::new(),
958            path_type: MultiLeaderPathType::StraightLineSegments,
959            line_color: Color::ByBlock,
960            line_type_handle: None,
961            line_weight: LineWeight::ByLayer,
962            enable_landing: true,
963            enable_dogleg: true,
964            dogleg_length: 0.36,
965            arrowhead_handle: None,
966            arrowhead_size: 0.18,
967            text_style_handle: None,
968            text_color: Color::ByBlock,
969            text_frame: false,
970            text_height: 0.18,
971            text_left_attachment: TextAttachmentType::MiddleOfText,
972            text_right_attachment: TextAttachmentType::MiddleOfText,
973            text_top_attachment: TextAttachmentType::CenterOfText,
974            text_bottom_attachment: TextAttachmentType::CenterOfText,
975            text_attachment_direction: TextAttachmentDirectionType::Horizontal,
976            text_attachment_point: TextAttachmentPointType::Center,
977            text_alignment: TextAlignmentType::Left,
978            text_angle_type: TextAngleType::Horizontal,
979            text_direction_negative: false,
980            text_align_in_ipe: 0,
981            block_content_handle: None,
982            block_content_color: Color::ByBlock,
983            block_connection_type: BlockContentConnectionType::BlockExtents,
984            block_rotation: 0.0,
985            block_scale: Vector3::new(1.0, 1.0, 1.0),
986            scale_factor: 1.0,
987            property_override_flags: MultiLeaderPropertyOverrideFlags::NONE,
988            enable_annotation_scale: true,
989            extend_leader_to_text: false,
990        }
991    }
992
993    /// Creates a MultiLeader with text content.
994    pub fn with_text(text: &str, text_location: Vector3, leader_points: Vec<Vector3>) -> Self {
995        let mut mleader = Self::new();
996        mleader.set_text_content(text, text_location);
997
998        // Add leader root and line
999        let root = mleader.add_leader_root();
1000        root.connection_point = text_location;
1001        if !leader_points.is_empty() {
1002            root.create_line(leader_points);
1003        }
1004
1005        mleader
1006    }
1007
1008    /// Sets text content.
1009    pub fn set_text_content(&mut self, text: &str, location: Vector3) {
1010        self.content_type = LeaderContentType::MText;
1011        self.context.set_text_content(text, location);
1012    }
1013
1014    /// Sets block content.
1015    pub fn set_block_content(&mut self, block_handle: Handle, location: Vector3) {
1016        self.content_type = LeaderContentType::Block;
1017        self.block_content_handle = Some(block_handle);
1018        self.context.set_block_content(block_handle, location);
1019    }
1020
1021    /// Adds a leader root.
1022    pub fn add_leader_root(&mut self) -> &mut LeaderRoot {
1023        self.context.add_leader_root()
1024    }
1025
1026    /// Gets the number of leader roots.
1027    pub fn leader_root_count(&self) -> usize {
1028        self.context.leader_root_count()
1029    }
1030
1031    /// Gets the total number of leader lines across all roots.
1032    pub fn total_leader_line_count(&self) -> usize {
1033        self.context
1034            .leader_roots
1035            .iter()
1036            .map(|r| r.line_count())
1037            .sum()
1038    }
1039
1040    /// Gets the content text (if text content).
1041    pub fn text(&self) -> Option<&str> {
1042        if self.content_type == LeaderContentType::MText && self.context.has_text_contents {
1043            Some(&self.context.text_string)
1044        } else {
1045            None
1046        }
1047    }
1048
1049    /// Sets the text content string.
1050    pub fn set_text(&mut self, text: &str) {
1051        if self.content_type == LeaderContentType::MText {
1052            self.context.text_string = text.to_string();
1053            self.context.has_text_contents = true;
1054        }
1055    }
1056
1057    /// Translates the multileader by the given offset.
1058    pub fn translate(&mut self, offset: Vector3) {
1059        // Translate context points
1060        self.context.content_base_point = self.context.content_base_point + offset;
1061        self.context.text_location = self.context.text_location + offset;
1062        self.context.block_content_location = self.context.block_content_location + offset;
1063        self.context.base_point = self.context.base_point + offset;
1064
1065        // Translate all leader roots and lines
1066        for root in &mut self.context.leader_roots {
1067            root.connection_point = root.connection_point + offset;
1068            for bp in &mut root.break_points {
1069                bp.start_point = bp.start_point + offset;
1070                bp.end_point = bp.end_point + offset;
1071            }
1072            for line in &mut root.lines {
1073                for point in &mut line.points {
1074                    *point = *point + offset;
1075                }
1076                for bp in &mut line.break_points {
1077                    bp.start_point = bp.start_point + offset;
1078                    bp.end_point = bp.end_point + offset;
1079                }
1080            }
1081        }
1082    }
1083
1084    /// Returns the bounding box of all leader line points and content.
1085    pub fn bounding_box(&self) -> Option<(Vector3, Vector3)> {
1086        let mut points: Vec<Vector3> = Vec::new();
1087
1088        // Add content point
1089        points.push(self.context.content_base_point);
1090
1091        // Add all leader line points
1092        for root in &self.context.leader_roots {
1093            points.push(root.connection_point);
1094            for line in &root.lines {
1095                points.extend(&line.points);
1096            }
1097        }
1098
1099        if points.is_empty() {
1100            return None;
1101        }
1102
1103        let mut min = points[0];
1104        let mut max = points[0];
1105
1106        for p in &points[1..] {
1107            min.x = min.x.min(p.x);
1108            min.y = min.y.min(p.y);
1109            min.z = min.z.min(p.z);
1110            max.x = max.x.max(p.x);
1111            max.y = max.y.max(p.y);
1112            max.z = max.z.max(p.z);
1113        }
1114
1115        Some((min, max))
1116    }
1117}
1118
1119impl Default for MultiLeader {
1120    fn default() -> Self {
1121        Self::new()
1122    }
1123}
1124
1125impl Entity for MultiLeader {
1126    fn handle(&self) -> Handle {
1127        self.common.handle
1128    }
1129
1130    fn set_handle(&mut self, handle: Handle) {
1131        self.common.handle = handle;
1132    }
1133
1134    fn layer(&self) -> &str {
1135        &self.common.layer
1136    }
1137
1138    fn set_layer(&mut self, layer: String) {
1139        self.common.layer = layer;
1140    }
1141
1142    fn color(&self) -> Color {
1143        self.common.color
1144    }
1145
1146    fn set_color(&mut self, color: Color) {
1147        self.common.color = color;
1148    }
1149
1150    fn line_weight(&self) -> LineWeight {
1151        self.common.line_weight
1152    }
1153
1154    fn set_line_weight(&mut self, line_weight: LineWeight) {
1155        self.common.line_weight = line_weight;
1156    }
1157
1158    fn transparency(&self) -> Transparency {
1159        self.common.transparency
1160    }
1161
1162    fn set_transparency(&mut self, transparency: Transparency) {
1163        self.common.transparency = transparency;
1164    }
1165
1166    fn is_invisible(&self) -> bool {
1167        self.common.invisible
1168    }
1169
1170    fn set_invisible(&mut self, invisible: bool) {
1171        self.common.invisible = invisible;
1172    }
1173
1174    fn bounding_box(&self) -> BoundingBox3D {
1175        // Calculate bounding box from all leader line points and content location
1176        let mut min = Vector3::new(f64::MAX, f64::MAX, f64::MAX);
1177        let mut max = Vector3::new(f64::MIN, f64::MIN, f64::MIN);
1178        
1179        // Include content location
1180        let loc = &self.context.content_base_point;
1181        min.x = min.x.min(loc.x);
1182        min.y = min.y.min(loc.y);
1183        min.z = min.z.min(loc.z);
1184        max.x = max.x.max(loc.x);
1185        max.y = max.y.max(loc.y);
1186        max.z = max.z.max(loc.z);
1187        
1188        // Include all leader line points
1189        for root in &self.context.leader_roots {
1190            for line in &root.lines {
1191                for pt in &line.points {
1192                    min.x = min.x.min(pt.x);
1193                    min.y = min.y.min(pt.y);
1194                    min.z = min.z.min(pt.z);
1195                    max.x = max.x.max(pt.x);
1196                    max.y = max.y.max(pt.y);
1197                    max.z = max.z.max(pt.z);
1198                }
1199            }
1200        }
1201        
1202        BoundingBox3D::new(min, max)
1203    }
1204
1205    fn translate(&mut self, offset: Vector3) {
1206        super::translate::translate_multileader(self, offset);
1207    }
1208
1209    fn entity_type(&self) -> &'static str {
1210        "MULTILEADER"
1211    }
1212    
1213    fn apply_transform(&mut self, transform: &crate::types::Transform) {
1214        super::transform::transform_multileader(self, transform);
1215    }
1216}
1217
1218// ============================================================================
1219// Builder
1220// ============================================================================
1221
1222/// Builder for MultiLeader entities.
1223#[derive(Debug, Clone)]
1224#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1225pub struct MultiLeaderBuilder {
1226    multileader: MultiLeader,
1227}
1228
1229impl MultiLeaderBuilder {
1230    /// Creates a new builder.
1231    pub fn new() -> Self {
1232        Self {
1233            multileader: MultiLeader::new(),
1234        }
1235    }
1236
1237    /// Sets text content.
1238    pub fn text(mut self, text: &str, location: Vector3) -> Self {
1239        self.multileader.set_text_content(text, location);
1240        self
1241    }
1242
1243    /// Sets block content.
1244    pub fn block(mut self, block_handle: Handle, location: Vector3) -> Self {
1245        self.multileader.set_block_content(block_handle, location);
1246        self
1247    }
1248
1249    /// Sets content type to None.
1250    pub fn no_content(mut self) -> Self {
1251        self.multileader.content_type = LeaderContentType::None;
1252        self
1253    }
1254
1255    /// Adds a leader line.
1256    pub fn leader_line(mut self, points: Vec<Vector3>) -> Self {
1257        if self.multileader.context.leader_roots.is_empty() {
1258            self.multileader.add_leader_root();
1259        }
1260        let root = self.multileader.context.leader_roots.last_mut().unwrap();
1261        root.create_line(points);
1262        self
1263    }
1264
1265    /// Adds a new leader root.
1266    pub fn new_root(mut self) -> Self {
1267        self.multileader.add_leader_root();
1268        self
1269    }
1270
1271    /// Sets the path type.
1272    pub fn path_type(mut self, path_type: MultiLeaderPathType) -> Self {
1273        self.multileader.path_type = path_type;
1274        self
1275    }
1276
1277    /// Sets the arrowhead size.
1278    pub fn arrowhead_size(mut self, size: f64) -> Self {
1279        self.multileader.arrowhead_size = size;
1280        self.multileader.context.arrowhead_size = size;
1281        self
1282    }
1283
1284    /// Sets the text height.
1285    pub fn text_height(mut self, height: f64) -> Self {
1286        self.multileader.text_height = height;
1287        self.multileader.context.text_height = height;
1288        self
1289    }
1290
1291    /// Sets whether to draw text frame.
1292    pub fn text_frame(mut self, frame: bool) -> Self {
1293        self.multileader.text_frame = frame;
1294        self
1295    }
1296
1297    /// Sets the line color.
1298    pub fn line_color(mut self, color: Color) -> Self {
1299        self.multileader.line_color = color;
1300        self
1301    }
1302
1303    /// Sets the text color.
1304    pub fn text_color(mut self, color: Color) -> Self {
1305        self.multileader.text_color = color;
1306        self.multileader.context.text_color = color;
1307        self
1308    }
1309
1310    /// Enables or disables landing (dogleg).
1311    pub fn landing(mut self, enable: bool) -> Self {
1312        self.multileader.enable_landing = enable;
1313        self
1314    }
1315
1316    /// Sets the dogleg length.
1317    pub fn dogleg_length(mut self, length: f64) -> Self {
1318        self.multileader.dogleg_length = length;
1319        self
1320    }
1321
1322    /// Sets the scale factor.
1323    pub fn scale(mut self, scale: f64) -> Self {
1324        self.multileader.scale_factor = scale;
1325        self.multileader.context.scale_factor = scale;
1326        self
1327    }
1328
1329    /// Builds the MultiLeader.
1330    pub fn build(self) -> MultiLeader {
1331        self.multileader
1332    }
1333}
1334
1335impl Default for MultiLeaderBuilder {
1336    fn default() -> Self {
1337        Self::new()
1338    }
1339}
1340
1341// ============================================================================
1342// Tests
1343// ============================================================================
1344
1345#[cfg(test)]
1346mod tests {
1347    use super::*;
1348
1349    #[test]
1350    fn test_multileader_creation() {
1351        let mleader = MultiLeader::new();
1352        assert_eq!(mleader.content_type, LeaderContentType::MText);
1353        assert!(mleader.enable_landing);
1354        assert!(mleader.enable_dogleg);
1355        assert_eq!(mleader.leader_root_count(), 0);
1356    }
1357
1358    #[test]
1359    fn test_multileader_with_text() {
1360        let points = vec![
1361            Vector3::new(0.0, 0.0, 0.0),
1362            Vector3::new(5.0, 5.0, 0.0),
1363        ];
1364        let mleader = MultiLeader::with_text("Note", Vector3::new(10.0, 10.0, 0.0), points);
1365
1366        assert_eq!(mleader.content_type, LeaderContentType::MText);
1367        assert_eq!(mleader.text(), Some("Note"));
1368        assert_eq!(mleader.leader_root_count(), 1);
1369        assert_eq!(mleader.total_leader_line_count(), 1);
1370    }
1371
1372    #[test]
1373    fn test_multileader_add_leader_root() {
1374        let mut mleader = MultiLeader::new();
1375        let root = mleader.add_leader_root();
1376        root.create_line(vec![Vector3::ZERO, Vector3::new(1.0, 1.0, 0.0)]);
1377        root.create_line(vec![Vector3::new(0.0, 1.0, 0.0), Vector3::new(1.0, 1.0, 0.0)]);
1378
1379        assert_eq!(mleader.leader_root_count(), 1);
1380        assert_eq!(mleader.total_leader_line_count(), 2);
1381    }
1382
1383    #[test]
1384    fn test_leader_line() {
1385        let mut line = LeaderLine::new(0);
1386        line.add_point(Vector3::new(0.0, 0.0, 0.0));
1387        line.add_point(Vector3::new(3.0, 4.0, 0.0));
1388
1389        assert_eq!(line.point_count(), 2);
1390        assert_eq!(line.start_point(), Some(Vector3::new(0.0, 0.0, 0.0)));
1391        assert_eq!(line.end_point(), Some(Vector3::new(3.0, 4.0, 0.0)));
1392        assert!((line.length() - 5.0).abs() < 1e-10);
1393    }
1394
1395    #[test]
1396    fn test_multileader_translate() {
1397        let mut mleader = MultiLeader::with_text(
1398            "Note",
1399            Vector3::new(10.0, 10.0, 0.0),
1400            vec![Vector3::ZERO, Vector3::new(5.0, 5.0, 0.0)],
1401        );
1402
1403        mleader.translate(Vector3::new(5.0, 5.0, 0.0));
1404
1405        assert_eq!(mleader.context.text_location, Vector3::new(15.0, 15.0, 0.0));
1406        let root = &mleader.context.leader_roots[0];
1407        let line = &root.lines[0];
1408        assert_eq!(line.points[0], Vector3::new(5.0, 5.0, 0.0));
1409        assert_eq!(line.points[1], Vector3::new(10.0, 10.0, 0.0));
1410    }
1411
1412    #[test]
1413    fn test_multileader_bounding_box() {
1414        let mleader = MultiLeader::with_text(
1415            "Note",
1416            Vector3::new(10.0, 10.0, 0.0),
1417            vec![
1418                Vector3::new(0.0, 0.0, 0.0),
1419                Vector3::new(5.0, 5.0, 0.0),
1420            ],
1421        );
1422
1423        let bbox = mleader.bounding_box().unwrap();
1424        assert_eq!(bbox.0, Vector3::new(0.0, 0.0, 0.0));
1425        assert_eq!(bbox.1, Vector3::new(10.0, 10.0, 0.0));
1426    }
1427
1428    #[test]
1429    fn test_content_types() {
1430        assert_eq!(LeaderContentType::from(0), LeaderContentType::None);
1431        assert_eq!(LeaderContentType::from(1), LeaderContentType::Block);
1432        assert_eq!(LeaderContentType::from(2), LeaderContentType::MText);
1433        assert_eq!(LeaderContentType::from(3), LeaderContentType::Tolerance);
1434    }
1435
1436    #[test]
1437    fn test_path_types() {
1438        assert_eq!(MultiLeaderPathType::from(0), MultiLeaderPathType::Invisible);
1439        assert_eq!(MultiLeaderPathType::from(1), MultiLeaderPathType::StraightLineSegments);
1440        assert_eq!(MultiLeaderPathType::from(2), MultiLeaderPathType::Spline);
1441    }
1442
1443    #[test]
1444    fn test_text_attachment_types() {
1445        assert_eq!(TextAttachmentType::from(0), TextAttachmentType::TopOfTopLine);
1446        assert_eq!(TextAttachmentType::from(2), TextAttachmentType::MiddleOfText);
1447        assert_eq!(TextAttachmentType::from(9), TextAttachmentType::CenterOfText);
1448    }
1449
1450    #[test]
1451    fn test_builder() {
1452        let mleader = MultiLeaderBuilder::new()
1453            .text("Note", Vector3::new(10.0, 10.0, 0.0))
1454            .leader_line(vec![Vector3::ZERO, Vector3::new(5.0, 5.0, 0.0)])
1455            .arrowhead_size(0.25)
1456            .text_height(0.2)
1457            .text_frame(true)
1458            .scale(2.0)
1459            .build();
1460
1461        assert_eq!(mleader.text(), Some("Note"));
1462        assert_eq!(mleader.arrowhead_size, 0.25);
1463        assert_eq!(mleader.text_height, 0.2);
1464        assert!(mleader.text_frame);
1465        assert_eq!(mleader.scale_factor, 2.0);
1466    }
1467
1468    #[test]
1469    fn test_property_override_flags() {
1470        let flags = MultiLeaderPropertyOverrideFlags::PATH_TYPE
1471            | MultiLeaderPropertyOverrideFlags::LINE_COLOR
1472            | MultiLeaderPropertyOverrideFlags::TEXT_HEIGHT;
1473
1474        assert!(flags.contains(MultiLeaderPropertyOverrideFlags::PATH_TYPE));
1475        assert!(flags.contains(MultiLeaderPropertyOverrideFlags::LINE_COLOR));
1476        assert!(flags.contains(MultiLeaderPropertyOverrideFlags::TEXT_HEIGHT));
1477        assert!(!flags.contains(MultiLeaderPropertyOverrideFlags::ARROWHEAD));
1478    }
1479
1480    #[test]
1481    fn test_leader_line_override_flags() {
1482        let flags = LeaderLinePropertyOverrideFlags::LINE_COLOR
1483            | LeaderLinePropertyOverrideFlags::ARROWHEAD;
1484
1485        assert!(flags.contains(LeaderLinePropertyOverrideFlags::LINE_COLOR));
1486        assert!(flags.contains(LeaderLinePropertyOverrideFlags::ARROWHEAD));
1487        assert!(!flags.contains(LeaderLinePropertyOverrideFlags::PATH_TYPE));
1488    }
1489
1490    #[test]
1491    fn test_block_attribute() {
1492        let attr = BlockAttribute::new("Value");
1493        assert_eq!(attr.text, "Value");
1494        assert_eq!(attr.index, 0);
1495        assert_eq!(attr.width, 0.0);
1496    }
1497
1498    #[test]
1499    fn test_annot_context_defaults() {
1500        let ctx = MultiLeaderAnnotContext::new();
1501        assert_eq!(ctx.scale_factor, 1.0);
1502        assert_eq!(ctx.text_height, 0.18);
1503        assert!(!ctx.has_text_contents);
1504        assert!(!ctx.has_block_contents);
1505        assert_eq!(ctx.leader_roots.len(), 0);
1506    }
1507}
1508