use crate::types::{Color, Handle, LineWeight};
use bitflags::bitflags;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum LeaderContentType {
None = 0,
Block = 1,
#[default]
MText = 2,
Tolerance = 3,
}
impl From<i16> for LeaderContentType {
fn from(value: i16) -> Self {
match value {
0 => Self::None,
1 => Self::Block,
2 => Self::MText,
3 => Self::Tolerance,
_ => Self::MText,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum MultiLeaderPathType {
Invisible = 0,
#[default]
StraightLineSegments = 1,
Spline = 2,
}
impl From<i16> for MultiLeaderPathType {
fn from(value: i16) -> Self {
match value {
0 => Self::Invisible,
1 => Self::StraightLineSegments,
2 => Self::Spline,
_ => Self::StraightLineSegments,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum TextAttachmentType {
TopOfTopLine = 0,
#[default]
MiddleOfTopLine = 1,
MiddleOfText = 2,
MiddleOfBottomLine = 3,
BottomOfBottomLine = 4,
BottomLine = 5,
BottomOfTopLineUnderlineBottomLine = 6,
BottomOfTopLineUnderlineTopLine = 7,
BottomOfTopLineUnderlineAll = 8,
CenterOfText = 9,
CenterOfTextOverline = 10,
}
impl From<i16> for TextAttachmentType {
fn from(value: i16) -> Self {
match value {
0 => Self::TopOfTopLine,
1 => Self::MiddleOfTopLine,
2 => Self::MiddleOfText,
3 => Self::MiddleOfBottomLine,
4 => Self::BottomOfBottomLine,
5 => Self::BottomLine,
6 => Self::BottomOfTopLineUnderlineBottomLine,
7 => Self::BottomOfTopLineUnderlineTopLine,
8 => Self::BottomOfTopLineUnderlineAll,
9 => Self::CenterOfText,
10 => Self::CenterOfTextOverline,
_ => Self::MiddleOfTopLine,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum TextAttachmentDirectionType {
#[default]
Horizontal = 0,
Vertical = 1,
}
impl From<i16> for TextAttachmentDirectionType {
fn from(value: i16) -> Self {
match value {
1 => Self::Vertical,
_ => Self::Horizontal,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum TextAlignmentType {
#[default]
Left = 0,
Center = 1,
Right = 2,
}
impl From<i16> for TextAlignmentType {
fn from(value: i16) -> Self {
match value {
0 => Self::Left,
1 => Self::Center,
2 => Self::Right,
_ => Self::Left,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum TextAngleType {
ParallelToLastLeaderLine = 0,
#[default]
Horizontal = 1,
Optimized = 2,
}
impl From<i16> for TextAngleType {
fn from(value: i16) -> Self {
match value {
0 => Self::ParallelToLastLeaderLine,
1 => Self::Horizontal,
2 => Self::Optimized,
_ => Self::Horizontal,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum BlockContentConnectionType {
#[default]
BlockExtents = 0,
BasePoint = 1,
}
impl From<i16> for BlockContentConnectionType {
fn from(value: i16) -> Self {
match value {
1 => Self::BasePoint,
_ => Self::BlockExtents,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum LeaderDrawOrderType {
#[default]
LeaderHeadFirst = 0,
LeaderTailFirst = 1,
}
impl From<i16> for LeaderDrawOrderType {
fn from(value: i16) -> Self {
match value {
1 => Self::LeaderTailFirst,
_ => Self::LeaderHeadFirst,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum MultiLeaderDrawOrderType {
#[default]
ContentFirst = 0,
LeaderFirst = 1,
}
impl From<i16> for MultiLeaderDrawOrderType {
fn from(value: i16) -> Self {
match value {
1 => Self::LeaderFirst,
_ => Self::ContentFirst,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLeaderPropertyOverrideFlags: i32 {
const NONE = 0;
const PATH_TYPE = 0x1;
const LINE_COLOR = 0x2;
const LEADER_LINE_TYPE = 0x4;
const LEADER_LINE_WEIGHT = 0x8;
const ENABLE_LANDING = 0x10;
const LANDING_GAP = 0x20;
const ENABLE_DOGLEG = 0x40;
const LANDING_DISTANCE = 0x80;
const ARROWHEAD = 0x100;
const ARROWHEAD_SIZE = 0x200;
const CONTENT_TYPE = 0x400;
const TEXT_STYLE = 0x800;
const TEXT_LEFT_ATTACHMENT = 0x1000;
const TEXT_ANGLE = 0x2000;
const TEXT_ALIGNMENT = 0x4000;
const TEXT_COLOR = 0x8000;
const TEXT_HEIGHT = 0x10000;
const TEXT_FRAME = 0x20000;
const ENABLE_USE_DEFAULT_MTEXT = 0x40000;
const BLOCK_CONTENT = 0x80000;
const BLOCK_CONTENT_COLOR = 0x100000;
const BLOCK_CONTENT_SCALE = 0x200000;
const BLOCK_CONTENT_ROTATION = 0x400000;
const BLOCK_CONTENT_CONNECTION = 0x800000;
const SCALE_FACTOR = 0x1000000;
const TEXT_RIGHT_ATTACHMENT = 0x2000000;
const TEXT_SWITCH_ALIGNMENT_TYPE = 0x4000000;
const TEXT_ATTACHMENT_DIRECTION = 0x8000000;
const TEXT_TOP_ATTACHMENT = 0x10000000;
const TEXT_BOTTOM_ATTACHMENT = 0x20000000;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LeaderLinePropertyOverrideFlags: i32 {
const NONE = 0;
const PATH_TYPE = 1;
const LINE_COLOR = 2;
const LINE_TYPE = 4;
const LINE_WEIGHT = 8;
const ARROWHEAD_SIZE = 16;
const ARROWHEAD = 32;
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLeaderStyle {
pub handle: Handle,
pub owner_handle: Handle,
pub name: String,
pub description: String,
pub path_type: MultiLeaderPathType,
pub line_color: Color,
pub line_type_handle: Option<Handle>,
pub line_weight: LineWeight,
pub enable_landing: bool,
pub enable_dogleg: bool,
pub landing_distance: f64,
pub landing_gap: f64,
pub arrowhead_handle: Option<Handle>,
pub arrowhead_size: f64,
pub content_type: LeaderContentType,
pub text_style_handle: Option<Handle>,
pub text_left_attachment: TextAttachmentType,
pub text_right_attachment: TextAttachmentType,
pub text_top_attachment: TextAttachmentType,
pub text_bottom_attachment: TextAttachmentType,
pub text_attachment_direction: TextAttachmentDirectionType,
pub text_angle_type: TextAngleType,
pub text_alignment: TextAlignmentType,
pub text_color: Color,
pub text_height: f64,
pub text_frame: bool,
pub text_always_left: bool,
pub default_text: String,
pub block_content_handle: Option<Handle>,
pub block_content_color: Color,
pub block_content_connection: BlockContentConnectionType,
pub block_content_rotation: f64,
pub block_content_scale_x: f64,
pub block_content_scale_y: f64,
pub block_content_scale_z: f64,
pub enable_block_scale: bool,
pub enable_block_rotation: bool,
pub scale_factor: f64,
pub align_space: f64,
pub break_gap_size: f64,
pub max_leader_points: i32,
pub first_segment_angle: f64,
pub second_segment_angle: f64,
pub leader_draw_order: LeaderDrawOrderType,
pub multileader_draw_order: MultiLeaderDrawOrderType,
pub is_annotative: bool,
pub property_changed: bool,
pub unknown_flag_298: bool,
}
impl MultiLeaderStyle {
pub const OBJECT_NAME: &'static str = "MLEADERSTYLE";
pub const SUBCLASS_MARKER: &'static str = "AcDbMLeaderStyle";
pub const STANDARD: &'static str = "Standard";
pub fn new(name: &str) -> Self {
MultiLeaderStyle {
handle: Handle::NULL,
owner_handle: Handle::NULL,
name: name.to_string(),
description: String::new(),
path_type: MultiLeaderPathType::StraightLineSegments,
line_color: Color::ByBlock,
line_type_handle: None,
line_weight: LineWeight::ByBlock,
enable_landing: true,
enable_dogleg: true,
landing_distance: 0.36,
landing_gap: 0.09,
arrowhead_handle: None,
arrowhead_size: 0.18,
content_type: LeaderContentType::MText,
text_style_handle: None,
text_left_attachment: TextAttachmentType::MiddleOfTopLine,
text_right_attachment: TextAttachmentType::MiddleOfTopLine,
text_top_attachment: TextAttachmentType::TopOfTopLine,
text_bottom_attachment: TextAttachmentType::BottomOfBottomLine,
text_attachment_direction: TextAttachmentDirectionType::Horizontal,
text_angle_type: TextAngleType::Horizontal,
text_alignment: TextAlignmentType::Left,
text_color: Color::ByBlock,
text_height: 0.18,
text_frame: false,
text_always_left: false,
default_text: String::new(),
block_content_handle: None,
block_content_color: Color::ByBlock,
block_content_connection: BlockContentConnectionType::BlockExtents,
block_content_rotation: 0.0,
block_content_scale_x: 1.0,
block_content_scale_y: 1.0,
block_content_scale_z: 1.0,
enable_block_scale: false,
enable_block_rotation: false,
scale_factor: 1.0,
align_space: 0.0,
break_gap_size: 0.125,
max_leader_points: 2,
first_segment_angle: 0.0,
second_segment_angle: 0.0,
leader_draw_order: LeaderDrawOrderType::LeaderHeadFirst,
multileader_draw_order: MultiLeaderDrawOrderType::ContentFirst,
is_annotative: false,
property_changed: false,
unknown_flag_298: false,
}
}
pub fn standard() -> Self {
Self::new(Self::STANDARD)
}
pub fn set_block_scale(&mut self, scale: f64) {
self.block_content_scale_x = scale;
self.block_content_scale_y = scale;
self.block_content_scale_z = scale;
}
pub fn uniform_block_scale(&self) -> Option<f64> {
if (self.block_content_scale_x - self.block_content_scale_y).abs() < 1e-10
&& (self.block_content_scale_y - self.block_content_scale_z).abs() < 1e-10
{
Some(self.block_content_scale_x)
} else {
None
}
}
pub fn set_block_rotation_degrees(&mut self, degrees: f64) {
self.block_content_rotation = degrees.to_radians();
}
pub fn block_rotation_degrees(&self) -> f64 {
self.block_content_rotation.to_degrees()
}
pub fn has_text_content(&self) -> bool {
self.content_type == LeaderContentType::MText
}
pub fn has_block_content(&self) -> bool {
self.content_type == LeaderContentType::Block
}
pub fn has_tolerance_content(&self) -> bool {
self.content_type == LeaderContentType::Tolerance
}
pub fn has_visible_leaders(&self) -> bool {
self.path_type != MultiLeaderPathType::Invisible
}
pub fn has_spline_leaders(&self) -> bool {
self.path_type == MultiLeaderPathType::Spline
}
}
impl Default for MultiLeaderStyle {
fn default() -> Self {
Self::standard()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multileaderstyle_creation() {
let style = MultiLeaderStyle::new("TestStyle");
assert_eq!(style.name, "TestStyle");
assert!(style.description.is_empty());
}
#[test]
fn test_standard_style() {
let style = MultiLeaderStyle::standard();
assert_eq!(style.name, "Standard");
}
#[test]
fn test_default_values() {
let style = MultiLeaderStyle::default();
assert_eq!(style.path_type, MultiLeaderPathType::StraightLineSegments);
assert_eq!(style.content_type, LeaderContentType::MText);
assert!(style.enable_landing);
assert!(style.enable_dogleg);
assert!((style.landing_distance - 0.36).abs() < 1e-10);
assert!((style.arrowhead_size - 0.18).abs() < 1e-10);
assert!((style.text_height - 0.18).abs() < 1e-10);
assert!((style.scale_factor - 1.0).abs() < 1e-10);
}
#[test]
fn test_content_type_enum() {
assert_eq!(LeaderContentType::from(0), LeaderContentType::None);
assert_eq!(LeaderContentType::from(1), LeaderContentType::Block);
assert_eq!(LeaderContentType::from(2), LeaderContentType::MText);
assert_eq!(LeaderContentType::from(3), LeaderContentType::Tolerance);
assert_eq!(LeaderContentType::from(99), LeaderContentType::MText);
}
#[test]
fn test_path_type_enum() {
assert_eq!(MultiLeaderPathType::from(0), MultiLeaderPathType::Invisible);
assert_eq!(MultiLeaderPathType::from(1), MultiLeaderPathType::StraightLineSegments);
assert_eq!(MultiLeaderPathType::from(2), MultiLeaderPathType::Spline);
}
#[test]
fn test_text_attachment_type() {
assert_eq!(TextAttachmentType::from(0), TextAttachmentType::TopOfTopLine);
assert_eq!(TextAttachmentType::from(2), TextAttachmentType::MiddleOfText);
assert_eq!(TextAttachmentType::from(9), TextAttachmentType::CenterOfText);
}
#[test]
fn test_set_block_scale() {
let mut style = MultiLeaderStyle::new("Test");
style.set_block_scale(2.5);
assert_eq!(style.block_content_scale_x, 2.5);
assert_eq!(style.block_content_scale_y, 2.5);
assert_eq!(style.block_content_scale_z, 2.5);
assert_eq!(style.uniform_block_scale(), Some(2.5));
}
#[test]
fn test_non_uniform_block_scale() {
let mut style = MultiLeaderStyle::new("Test");
style.block_content_scale_x = 1.0;
style.block_content_scale_y = 2.0;
style.block_content_scale_z = 1.0;
assert_eq!(style.uniform_block_scale(), None);
}
#[test]
fn test_block_rotation_degrees() {
let mut style = MultiLeaderStyle::new("Test");
style.set_block_rotation_degrees(45.0);
assert!((style.block_rotation_degrees() - 45.0).abs() < 1e-10);
}
#[test]
fn test_has_text_content() {
let mut style = MultiLeaderStyle::new("Test");
style.content_type = LeaderContentType::MText;
assert!(style.has_text_content());
assert!(!style.has_block_content());
assert!(!style.has_tolerance_content());
}
#[test]
fn test_has_block_content() {
let mut style = MultiLeaderStyle::new("Test");
style.content_type = LeaderContentType::Block;
assert!(!style.has_text_content());
assert!(style.has_block_content());
}
#[test]
fn test_has_tolerance_content() {
let mut style = MultiLeaderStyle::new("Test");
style.content_type = LeaderContentType::Tolerance;
assert!(style.has_tolerance_content());
}
#[test]
fn test_has_visible_leaders() {
let mut style = MultiLeaderStyle::new("Test");
assert!(style.has_visible_leaders());
style.path_type = MultiLeaderPathType::Invisible;
assert!(!style.has_visible_leaders());
}
#[test]
fn test_has_spline_leaders() {
let mut style = MultiLeaderStyle::new("Test");
assert!(!style.has_spline_leaders());
style.path_type = MultiLeaderPathType::Spline;
assert!(style.has_spline_leaders());
}
#[test]
fn test_property_override_flags() {
let flags = MultiLeaderPropertyOverrideFlags::PATH_TYPE
| MultiLeaderPropertyOverrideFlags::LINE_COLOR
| MultiLeaderPropertyOverrideFlags::TEXT_HEIGHT;
assert!(flags.contains(MultiLeaderPropertyOverrideFlags::PATH_TYPE));
assert!(flags.contains(MultiLeaderPropertyOverrideFlags::LINE_COLOR));
assert!(flags.contains(MultiLeaderPropertyOverrideFlags::TEXT_HEIGHT));
assert!(!flags.contains(MultiLeaderPropertyOverrideFlags::ARROWHEAD));
}
#[test]
fn test_leader_line_override_flags() {
let flags = LeaderLinePropertyOverrideFlags::PATH_TYPE
| LeaderLinePropertyOverrideFlags::ARROWHEAD;
assert!(flags.contains(LeaderLinePropertyOverrideFlags::PATH_TYPE));
assert!(flags.contains(LeaderLinePropertyOverrideFlags::ARROWHEAD));
assert!(!flags.contains(LeaderLinePropertyOverrideFlags::LINE_COLOR));
}
#[test]
fn test_text_alignment_enum() {
assert_eq!(TextAlignmentType::from(0), TextAlignmentType::Left);
assert_eq!(TextAlignmentType::from(1), TextAlignmentType::Center);
assert_eq!(TextAlignmentType::from(2), TextAlignmentType::Right);
}
#[test]
fn test_text_angle_enum() {
assert_eq!(TextAngleType::from(0), TextAngleType::ParallelToLastLeaderLine);
assert_eq!(TextAngleType::from(1), TextAngleType::Horizontal);
assert_eq!(TextAngleType::from(2), TextAngleType::Optimized);
}
#[test]
fn test_block_connection_enum() {
assert_eq!(BlockContentConnectionType::from(0), BlockContentConnectionType::BlockExtents);
assert_eq!(BlockContentConnectionType::from(1), BlockContentConnectionType::BasePoint);
}
#[test]
fn test_draw_order_enums() {
assert_eq!(LeaderDrawOrderType::from(0), LeaderDrawOrderType::LeaderHeadFirst);
assert_eq!(LeaderDrawOrderType::from(1), LeaderDrawOrderType::LeaderTailFirst);
assert_eq!(MultiLeaderDrawOrderType::from(0), MultiLeaderDrawOrderType::ContentFirst);
assert_eq!(MultiLeaderDrawOrderType::from(1), MultiLeaderDrawOrderType::LeaderFirst);
}
}