use crate::entities::{Entity, EntityCommon};
use crate::types::{BoundingBox3D, Color, Handle, LineWeight, Transparency, Vector3};
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::None,
}
}
}
#[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,
MiddleOfTopLine = 1,
#[default]
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::MiddleOfText,
}
}
}
#[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 {
0 => Self::BlockExtents,
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 TextAttachmentDirectionType {
#[default]
Horizontal = 0,
Vertical = 1,
}
impl From<i16> for TextAttachmentDirectionType {
fn from(value: i16) -> Self {
match value {
0 => Self::Horizontal,
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 TextAttachmentPointType {
Left = 1,
#[default]
Center = 2,
Right = 3,
}
impl From<i16> for TextAttachmentPointType {
fn from(value: i16) -> Self {
match value {
1 => Self::Left,
2 => Self::Center,
3 => Self::Right,
_ => Self::Center,
}
}
}
#[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 FlowDirectionType {
#[default]
Horizontal = 1,
Vertical = 3,
ByStyle = 5,
}
impl From<i16> for FlowDirectionType {
fn from(value: i16) -> Self {
match value {
1 => Self::Horizontal,
3 => Self::Vertical,
5 => Self::ByStyle,
_ => Self::Horizontal,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum LineSpacingStyle {
#[default]
AtLeast = 1,
Exactly = 2,
}
impl From<i16> for LineSpacingStyle {
fn from(value: i16) -> Self {
match value {
1 => Self::AtLeast,
2 => Self::Exactly,
_ => Self::AtLeast,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLeaderPropertyOverrideFlags: u32 {
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: u32 {
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, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StartEndPointPair {
pub start_point: Vector3,
pub end_point: Vector3,
}
impl StartEndPointPair {
pub fn new(start_point: Vector3, end_point: Vector3) -> Self {
Self {
start_point,
end_point,
}
}
}
impl Default for StartEndPointPair {
fn default() -> Self {
Self {
start_point: Vector3::ZERO,
end_point: Vector3::ZERO,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LeaderLine {
pub index: i32,
pub segment_index: i32,
pub points: Vec<Vector3>,
pub break_points: Vec<StartEndPointPair>,
pub break_info_count: i32,
pub path_type: MultiLeaderPathType,
pub line_color: Color,
pub line_type_handle: Option<Handle>,
pub line_weight: LineWeight,
pub arrowhead_handle: Option<Handle>,
pub arrowhead_size: f64,
pub override_flags: LeaderLinePropertyOverrideFlags,
}
impl LeaderLine {
pub fn new(index: i32) -> Self {
Self {
index,
segment_index: 0,
points: Vec::new(),
break_points: Vec::new(),
break_info_count: 0,
path_type: MultiLeaderPathType::StraightLineSegments,
line_color: Color::ByBlock,
line_type_handle: None,
line_weight: LineWeight::ByLayer,
arrowhead_handle: None,
arrowhead_size: 0.18,
override_flags: LeaderLinePropertyOverrideFlags::NONE,
}
}
pub fn from_points(index: i32, points: Vec<Vector3>) -> Self {
let mut line = Self::new(index);
line.points = points;
line
}
pub fn add_point(&mut self, point: Vector3) {
self.points.push(point);
}
pub fn point_count(&self) -> usize {
self.points.len()
}
pub fn start_point(&self) -> Option<Vector3> {
self.points.first().copied()
}
pub fn end_point(&self) -> Option<Vector3> {
self.points.last().copied()
}
pub fn length(&self) -> f64 {
if self.points.len() < 2 {
return 0.0;
}
self.points
.windows(2)
.map(|w| (w[1] - w[0]).length())
.sum()
}
}
impl Default for LeaderLine {
fn default() -> Self {
Self::new(0)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LeaderRoot {
pub leader_index: i32,
pub content_valid: bool,
pub unknown: bool,
pub connection_point: Vector3,
pub direction: Vector3,
pub break_points: Vec<StartEndPointPair>,
pub lines: Vec<LeaderLine>,
pub landing_distance: f64,
pub text_attachment_direction: TextAttachmentDirectionType,
}
impl LeaderRoot {
pub fn new(index: i32) -> Self {
Self {
leader_index: index,
content_valid: true,
unknown: true,
connection_point: Vector3::ZERO,
direction: Vector3::new(1.0, 0.0, 0.0),
break_points: Vec::new(),
lines: Vec::new(),
landing_distance: 0.36,
text_attachment_direction: TextAttachmentDirectionType::Horizontal,
}
}
pub fn add_line(&mut self, line: LeaderLine) {
self.lines.push(line);
}
pub fn create_line(&mut self, points: Vec<Vector3>) -> &mut LeaderLine {
let index = self.lines.len() as i32;
let line = LeaderLine::from_points(index, points);
self.lines.push(line);
self.lines.last_mut().unwrap()
}
pub fn line_count(&self) -> usize {
self.lines.len()
}
}
impl Default for LeaderRoot {
fn default() -> Self {
Self::new(0)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlockAttribute {
pub attribute_definition_handle: Option<Handle>,
pub index: i16,
pub width: f64,
pub text: String,
}
impl BlockAttribute {
pub fn new(text: &str) -> Self {
Self {
attribute_definition_handle: None,
index: 0,
width: 0.0,
text: text.to_string(),
}
}
}
impl Default for BlockAttribute {
fn default() -> Self {
Self::new("")
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLeaderAnnotContext {
pub leader_roots: Vec<LeaderRoot>,
pub scale_factor: f64,
pub content_base_point: Vector3,
pub has_text_contents: bool,
pub text_string: String,
pub text_normal: Vector3,
pub text_location: Vector3,
pub text_direction: Vector3,
pub text_rotation: f64,
pub text_height: f64,
pub text_width: f64,
pub text_boundary_height: f64,
pub line_spacing_factor: f64,
pub line_spacing_style: LineSpacingStyle,
pub text_color: Color,
pub text_attachment_point: TextAttachmentPointType,
pub text_flow_direction: FlowDirectionType,
pub text_alignment: TextAlignmentType,
pub text_left_attachment: TextAttachmentType,
pub text_right_attachment: TextAttachmentType,
pub text_top_attachment: TextAttachmentType,
pub text_bottom_attachment: TextAttachmentType,
pub text_height_automatic: bool,
pub word_break: bool,
pub text_style_handle: Option<Handle>,
pub has_block_contents: bool,
pub block_content_handle: Option<Handle>,
pub block_content_normal: Vector3,
pub block_content_location: Vector3,
pub block_content_scale: Vector3,
pub block_rotation: f64,
pub block_content_color: Color,
pub block_connection_type: BlockContentConnectionType,
pub column_type: i16,
pub column_width: f64,
pub column_gutter: f64,
pub column_flow_reversed: bool,
pub column_sizes: Vec<f64>,
pub background_fill_enabled: bool,
pub background_mask_fill_on: bool,
pub background_fill_color: Color,
pub background_scale_factor: f64,
pub background_transparency: i32,
pub base_point: Vector3,
pub base_direction: Vector3,
pub base_vertical: Vector3,
pub normal_reversed: bool,
pub arrowhead_size: f64,
pub landing_gap: f64,
pub transform_matrix: [f64; 16],
pub scale_handle: Option<Handle>,
}
impl MultiLeaderAnnotContext {
pub fn new() -> Self {
let mut transform_matrix = [0.0; 16];
transform_matrix[0] = 1.0;
transform_matrix[5] = 1.0;
transform_matrix[10] = 1.0;
transform_matrix[15] = 1.0;
Self {
leader_roots: Vec::new(),
scale_factor: 1.0,
content_base_point: Vector3::ZERO,
has_text_contents: false,
text_string: String::new(),
text_normal: Vector3::new(0.0, 0.0, 1.0),
text_location: Vector3::ZERO,
text_direction: Vector3::new(1.0, 0.0, 0.0),
text_rotation: 0.0,
text_height: 0.18,
text_width: 0.0,
text_boundary_height: 0.0,
line_spacing_factor: 1.0,
line_spacing_style: LineSpacingStyle::AtLeast,
text_color: Color::ByBlock,
text_attachment_point: TextAttachmentPointType::Center,
text_flow_direction: FlowDirectionType::Horizontal,
text_alignment: TextAlignmentType::Left,
text_left_attachment: TextAttachmentType::MiddleOfText,
text_right_attachment: TextAttachmentType::MiddleOfText,
text_top_attachment: TextAttachmentType::CenterOfText,
text_bottom_attachment: TextAttachmentType::CenterOfText,
text_height_automatic: false,
word_break: true,
text_style_handle: None,
has_block_contents: false,
block_content_handle: None,
block_content_normal: Vector3::new(0.0, 0.0, 1.0),
block_content_location: Vector3::ZERO,
block_content_scale: Vector3::new(1.0, 1.0, 1.0),
block_rotation: 0.0,
block_content_color: Color::ByBlock,
block_connection_type: BlockContentConnectionType::BlockExtents,
column_type: 0,
column_width: 0.0,
column_gutter: 0.0,
column_flow_reversed: false,
column_sizes: Vec::new(),
background_fill_enabled: false,
background_mask_fill_on: false,
background_fill_color: Color::ByBlock,
background_scale_factor: 1.5,
background_transparency: 0,
base_point: Vector3::ZERO,
base_direction: Vector3::new(1.0, 0.0, 0.0),
base_vertical: Vector3::new(0.0, 1.0, 0.0),
normal_reversed: false,
arrowhead_size: 0.18,
landing_gap: 0.09,
transform_matrix,
scale_handle: None,
}
}
pub fn add_leader_root(&mut self) -> &mut LeaderRoot {
let index = self.leader_roots.len() as i32;
self.leader_roots.push(LeaderRoot::new(index));
self.leader_roots.last_mut().unwrap()
}
pub fn leader_root_count(&self) -> usize {
self.leader_roots.len()
}
pub fn set_text_content(&mut self, text: &str, location: Vector3) {
self.has_text_contents = true;
self.text_string = text.to_string();
self.text_location = location;
self.content_base_point = location;
}
pub fn set_block_content(&mut self, block_handle: Handle, location: Vector3) {
self.has_block_contents = true;
self.block_content_handle = Some(block_handle);
self.block_content_location = location;
self.content_base_point = location;
}
}
impl Default for MultiLeaderAnnotContext {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLeader {
pub common: EntityCommon,
pub style_handle: Option<Handle>,
pub content_type: LeaderContentType,
pub context: MultiLeaderAnnotContext,
pub block_attributes: Vec<BlockAttribute>,
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 dogleg_length: f64,
pub arrowhead_handle: Option<Handle>,
pub arrowhead_size: f64,
pub text_style_handle: Option<Handle>,
pub text_color: Color,
pub text_frame: bool,
pub text_height: f64,
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_attachment_point: TextAttachmentPointType,
pub text_alignment: TextAlignmentType,
pub text_angle_type: TextAngleType,
pub text_direction_negative: bool,
pub text_align_in_ipe: i16,
pub block_content_handle: Option<Handle>,
pub block_content_color: Color,
pub block_connection_type: BlockContentConnectionType,
pub block_rotation: f64,
pub block_scale: Vector3,
pub scale_factor: f64,
pub property_override_flags: MultiLeaderPropertyOverrideFlags,
pub enable_annotation_scale: bool,
pub extend_leader_to_text: bool,
}
impl MultiLeader {
pub fn new() -> Self {
Self {
common: EntityCommon::default(),
style_handle: None,
content_type: LeaderContentType::MText,
context: MultiLeaderAnnotContext::new(),
block_attributes: Vec::new(),
path_type: MultiLeaderPathType::StraightLineSegments,
line_color: Color::ByBlock,
line_type_handle: None,
line_weight: LineWeight::ByLayer,
enable_landing: true,
enable_dogleg: true,
dogleg_length: 0.36,
arrowhead_handle: None,
arrowhead_size: 0.18,
text_style_handle: None,
text_color: Color::ByBlock,
text_frame: false,
text_height: 0.18,
text_left_attachment: TextAttachmentType::MiddleOfText,
text_right_attachment: TextAttachmentType::MiddleOfText,
text_top_attachment: TextAttachmentType::CenterOfText,
text_bottom_attachment: TextAttachmentType::CenterOfText,
text_attachment_direction: TextAttachmentDirectionType::Horizontal,
text_attachment_point: TextAttachmentPointType::Center,
text_alignment: TextAlignmentType::Left,
text_angle_type: TextAngleType::Horizontal,
text_direction_negative: false,
text_align_in_ipe: 0,
block_content_handle: None,
block_content_color: Color::ByBlock,
block_connection_type: BlockContentConnectionType::BlockExtents,
block_rotation: 0.0,
block_scale: Vector3::new(1.0, 1.0, 1.0),
scale_factor: 1.0,
property_override_flags: MultiLeaderPropertyOverrideFlags::NONE,
enable_annotation_scale: true,
extend_leader_to_text: false,
}
}
pub fn with_text(text: &str, text_location: Vector3, leader_points: Vec<Vector3>) -> Self {
let mut mleader = Self::new();
mleader.set_text_content(text, text_location);
let root = mleader.add_leader_root();
root.connection_point = text_location;
if !leader_points.is_empty() {
root.create_line(leader_points);
}
mleader
}
pub fn set_text_content(&mut self, text: &str, location: Vector3) {
self.content_type = LeaderContentType::MText;
self.context.set_text_content(text, location);
}
pub fn set_block_content(&mut self, block_handle: Handle, location: Vector3) {
self.content_type = LeaderContentType::Block;
self.block_content_handle = Some(block_handle);
self.context.set_block_content(block_handle, location);
}
pub fn add_leader_root(&mut self) -> &mut LeaderRoot {
self.context.add_leader_root()
}
pub fn leader_root_count(&self) -> usize {
self.context.leader_root_count()
}
pub fn total_leader_line_count(&self) -> usize {
self.context
.leader_roots
.iter()
.map(|r| r.line_count())
.sum()
}
pub fn text(&self) -> Option<&str> {
if self.content_type == LeaderContentType::MText && self.context.has_text_contents {
Some(&self.context.text_string)
} else {
None
}
}
pub fn set_text(&mut self, text: &str) {
if self.content_type == LeaderContentType::MText {
self.context.text_string = text.to_string();
self.context.has_text_contents = true;
}
}
pub fn translate(&mut self, offset: Vector3) {
self.context.content_base_point = self.context.content_base_point + offset;
self.context.text_location = self.context.text_location + offset;
self.context.block_content_location = self.context.block_content_location + offset;
self.context.base_point = self.context.base_point + offset;
for root in &mut self.context.leader_roots {
root.connection_point = root.connection_point + offset;
for bp in &mut root.break_points {
bp.start_point = bp.start_point + offset;
bp.end_point = bp.end_point + offset;
}
for line in &mut root.lines {
for point in &mut line.points {
*point = *point + offset;
}
for bp in &mut line.break_points {
bp.start_point = bp.start_point + offset;
bp.end_point = bp.end_point + offset;
}
}
}
}
pub fn bounding_box(&self) -> Option<(Vector3, Vector3)> {
let mut points: Vec<Vector3> = Vec::new();
points.push(self.context.content_base_point);
for root in &self.context.leader_roots {
points.push(root.connection_point);
for line in &root.lines {
points.extend(&line.points);
}
}
if points.is_empty() {
return None;
}
let mut min = points[0];
let mut max = points[0];
for p in &points[1..] {
min.x = min.x.min(p.x);
min.y = min.y.min(p.y);
min.z = min.z.min(p.z);
max.x = max.x.max(p.x);
max.y = max.y.max(p.y);
max.z = max.z.max(p.z);
}
Some((min, max))
}
}
impl Default for MultiLeader {
fn default() -> Self {
Self::new()
}
}
impl Entity for MultiLeader {
fn handle(&self) -> Handle {
self.common.handle
}
fn set_handle(&mut self, handle: Handle) {
self.common.handle = handle;
}
fn layer(&self) -> &str {
&self.common.layer
}
fn set_layer(&mut self, layer: String) {
self.common.layer = layer;
}
fn color(&self) -> Color {
self.common.color
}
fn set_color(&mut self, color: Color) {
self.common.color = color;
}
fn line_weight(&self) -> LineWeight {
self.common.line_weight
}
fn set_line_weight(&mut self, line_weight: LineWeight) {
self.common.line_weight = line_weight;
}
fn transparency(&self) -> Transparency {
self.common.transparency
}
fn set_transparency(&mut self, transparency: Transparency) {
self.common.transparency = transparency;
}
fn is_invisible(&self) -> bool {
self.common.invisible
}
fn set_invisible(&mut self, invisible: bool) {
self.common.invisible = invisible;
}
fn bounding_box(&self) -> BoundingBox3D {
let mut min = Vector3::new(f64::MAX, f64::MAX, f64::MAX);
let mut max = Vector3::new(f64::MIN, f64::MIN, f64::MIN);
let loc = &self.context.content_base_point;
min.x = min.x.min(loc.x);
min.y = min.y.min(loc.y);
min.z = min.z.min(loc.z);
max.x = max.x.max(loc.x);
max.y = max.y.max(loc.y);
max.z = max.z.max(loc.z);
for root in &self.context.leader_roots {
for line in &root.lines {
for pt in &line.points {
min.x = min.x.min(pt.x);
min.y = min.y.min(pt.y);
min.z = min.z.min(pt.z);
max.x = max.x.max(pt.x);
max.y = max.y.max(pt.y);
max.z = max.z.max(pt.z);
}
}
}
BoundingBox3D::new(min, max)
}
fn translate(&mut self, offset: Vector3) {
super::translate::translate_multileader(self, offset);
}
fn entity_type(&self) -> &'static str {
"MULTILEADER"
}
fn apply_transform(&mut self, transform: &crate::types::Transform) {
super::transform::transform_multileader(self, transform);
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MultiLeaderBuilder {
multileader: MultiLeader,
}
impl MultiLeaderBuilder {
pub fn new() -> Self {
Self {
multileader: MultiLeader::new(),
}
}
pub fn text(mut self, text: &str, location: Vector3) -> Self {
self.multileader.set_text_content(text, location);
self
}
pub fn block(mut self, block_handle: Handle, location: Vector3) -> Self {
self.multileader.set_block_content(block_handle, location);
self
}
pub fn no_content(mut self) -> Self {
self.multileader.content_type = LeaderContentType::None;
self
}
pub fn leader_line(mut self, points: Vec<Vector3>) -> Self {
if self.multileader.context.leader_roots.is_empty() {
self.multileader.add_leader_root();
}
let root = self.multileader.context.leader_roots.last_mut().unwrap();
root.create_line(points);
self
}
pub fn new_root(mut self) -> Self {
self.multileader.add_leader_root();
self
}
pub fn path_type(mut self, path_type: MultiLeaderPathType) -> Self {
self.multileader.path_type = path_type;
self
}
pub fn arrowhead_size(mut self, size: f64) -> Self {
self.multileader.arrowhead_size = size;
self.multileader.context.arrowhead_size = size;
self
}
pub fn text_height(mut self, height: f64) -> Self {
self.multileader.text_height = height;
self.multileader.context.text_height = height;
self
}
pub fn text_frame(mut self, frame: bool) -> Self {
self.multileader.text_frame = frame;
self
}
pub fn line_color(mut self, color: Color) -> Self {
self.multileader.line_color = color;
self
}
pub fn text_color(mut self, color: Color) -> Self {
self.multileader.text_color = color;
self.multileader.context.text_color = color;
self
}
pub fn landing(mut self, enable: bool) -> Self {
self.multileader.enable_landing = enable;
self
}
pub fn dogleg_length(mut self, length: f64) -> Self {
self.multileader.dogleg_length = length;
self
}
pub fn scale(mut self, scale: f64) -> Self {
self.multileader.scale_factor = scale;
self.multileader.context.scale_factor = scale;
self
}
pub fn build(self) -> MultiLeader {
self.multileader
}
}
impl Default for MultiLeaderBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multileader_creation() {
let mleader = MultiLeader::new();
assert_eq!(mleader.content_type, LeaderContentType::MText);
assert!(mleader.enable_landing);
assert!(mleader.enable_dogleg);
assert_eq!(mleader.leader_root_count(), 0);
}
#[test]
fn test_multileader_with_text() {
let points = vec![
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(5.0, 5.0, 0.0),
];
let mleader = MultiLeader::with_text("Note", Vector3::new(10.0, 10.0, 0.0), points);
assert_eq!(mleader.content_type, LeaderContentType::MText);
assert_eq!(mleader.text(), Some("Note"));
assert_eq!(mleader.leader_root_count(), 1);
assert_eq!(mleader.total_leader_line_count(), 1);
}
#[test]
fn test_multileader_add_leader_root() {
let mut mleader = MultiLeader::new();
let root = mleader.add_leader_root();
root.create_line(vec![Vector3::ZERO, Vector3::new(1.0, 1.0, 0.0)]);
root.create_line(vec![Vector3::new(0.0, 1.0, 0.0), Vector3::new(1.0, 1.0, 0.0)]);
assert_eq!(mleader.leader_root_count(), 1);
assert_eq!(mleader.total_leader_line_count(), 2);
}
#[test]
fn test_leader_line() {
let mut line = LeaderLine::new(0);
line.add_point(Vector3::new(0.0, 0.0, 0.0));
line.add_point(Vector3::new(3.0, 4.0, 0.0));
assert_eq!(line.point_count(), 2);
assert_eq!(line.start_point(), Some(Vector3::new(0.0, 0.0, 0.0)));
assert_eq!(line.end_point(), Some(Vector3::new(3.0, 4.0, 0.0)));
assert!((line.length() - 5.0).abs() < 1e-10);
}
#[test]
fn test_multileader_translate() {
let mut mleader = MultiLeader::with_text(
"Note",
Vector3::new(10.0, 10.0, 0.0),
vec![Vector3::ZERO, Vector3::new(5.0, 5.0, 0.0)],
);
mleader.translate(Vector3::new(5.0, 5.0, 0.0));
assert_eq!(mleader.context.text_location, Vector3::new(15.0, 15.0, 0.0));
let root = &mleader.context.leader_roots[0];
let line = &root.lines[0];
assert_eq!(line.points[0], Vector3::new(5.0, 5.0, 0.0));
assert_eq!(line.points[1], Vector3::new(10.0, 10.0, 0.0));
}
#[test]
fn test_multileader_bounding_box() {
let mleader = MultiLeader::with_text(
"Note",
Vector3::new(10.0, 10.0, 0.0),
vec![
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(5.0, 5.0, 0.0),
],
);
let bbox = mleader.bounding_box().unwrap();
assert_eq!(bbox.0, Vector3::new(0.0, 0.0, 0.0));
assert_eq!(bbox.1, Vector3::new(10.0, 10.0, 0.0));
}
#[test]
fn test_content_types() {
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);
}
#[test]
fn test_path_types() {
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_types() {
assert_eq!(TextAttachmentType::from(0), TextAttachmentType::TopOfTopLine);
assert_eq!(TextAttachmentType::from(2), TextAttachmentType::MiddleOfText);
assert_eq!(TextAttachmentType::from(9), TextAttachmentType::CenterOfText);
}
#[test]
fn test_builder() {
let mleader = MultiLeaderBuilder::new()
.text("Note", Vector3::new(10.0, 10.0, 0.0))
.leader_line(vec![Vector3::ZERO, Vector3::new(5.0, 5.0, 0.0)])
.arrowhead_size(0.25)
.text_height(0.2)
.text_frame(true)
.scale(2.0)
.build();
assert_eq!(mleader.text(), Some("Note"));
assert_eq!(mleader.arrowhead_size, 0.25);
assert_eq!(mleader.text_height, 0.2);
assert!(mleader.text_frame);
assert_eq!(mleader.scale_factor, 2.0);
}
#[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::LINE_COLOR
| LeaderLinePropertyOverrideFlags::ARROWHEAD;
assert!(flags.contains(LeaderLinePropertyOverrideFlags::LINE_COLOR));
assert!(flags.contains(LeaderLinePropertyOverrideFlags::ARROWHEAD));
assert!(!flags.contains(LeaderLinePropertyOverrideFlags::PATH_TYPE));
}
#[test]
fn test_block_attribute() {
let attr = BlockAttribute::new("Value");
assert_eq!(attr.text, "Value");
assert_eq!(attr.index, 0);
assert_eq!(attr.width, 0.0);
}
#[test]
fn test_annot_context_defaults() {
let ctx = MultiLeaderAnnotContext::new();
assert_eq!(ctx.scale_factor, 1.0);
assert_eq!(ctx.text_height, 0.18);
assert!(!ctx.has_text_contents);
assert!(!ctx.has_block_contents);
assert_eq!(ctx.leader_roots.len(), 0);
}
}