use crate::entities::EntityCommon;
use crate::types::Vector3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DimensionType {
Linear = 0,
Aligned = 1,
Angular = 2,
Diameter = 3,
Radius = 4,
Angular3Point = 5,
Ordinate = 6,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AttachmentPointType {
TopLeft = 1,
TopCenter = 2,
TopRight = 3,
MiddleLeft = 4,
MiddleCenter = 5,
MiddleRight = 6,
BottomLeft = 7,
BottomCenter = 8,
BottomRight = 9,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DimensionBase {
pub common: EntityCommon,
pub definition_point: Vector3,
pub text_middle_point: Vector3,
pub insertion_point: Vector3,
pub dimension_type: DimensionType,
pub attachment_point: AttachmentPointType,
pub text: String,
pub user_text: Option<String>,
pub normal: Vector3,
pub text_rotation: f64,
pub horizontal_direction: f64,
pub style_name: String,
pub actual_measurement: f64,
pub version: u8,
pub block_name: String,
pub line_spacing_factor: f64,
}
impl DimensionBase {
pub fn new(dim_type: DimensionType) -> Self {
Self {
common: EntityCommon::default(),
definition_point: Vector3::new(0.0, 0.0, 0.0),
text_middle_point: Vector3::new(0.0, 0.0, 0.0),
insertion_point: Vector3::new(0.0, 0.0, 0.0),
dimension_type: dim_type,
attachment_point: AttachmentPointType::MiddleCenter,
text: String::new(),
user_text: None,
normal: Vector3::new(0.0, 0.0, 1.0),
text_rotation: 0.0,
horizontal_direction: 0.0,
style_name: "Standard".to_string(),
actual_measurement: 0.0,
version: 0,
block_name: String::new(),
line_spacing_factor: 1.0,
}
}
pub fn with_text(mut self, text: impl Into<String>) -> Self {
self.text = text.into();
self
}
pub fn with_style(mut self, style: impl Into<String>) -> Self {
self.style_name = style.into();
self
}
pub fn with_normal(mut self, normal: Vector3) -> Self {
self.normal = normal;
self
}
pub fn is_angular(&self) -> bool {
matches!(
self.dimension_type,
DimensionType::Angular | DimensionType::Angular3Point
)
}
}
impl Default for DimensionBase {
fn default() -> Self {
Self::new(DimensionType::Linear)
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DimensionAligned {
pub base: DimensionBase,
pub first_point: Vector3,
pub second_point: Vector3,
pub definition_point: Vector3,
pub ext_line_rotation: f64,
}
impl DimensionAligned {
pub fn new(first_point: Vector3, second_point: Vector3) -> Self {
let mut base = DimensionBase::new(DimensionType::Aligned);
base.actual_measurement = first_point.distance(&second_point);
Self {
base,
first_point,
second_point,
definition_point: Vector3::ZERO,
ext_line_rotation: 0.0,
}
}
pub fn measurement(&self) -> f64 {
self.first_point.distance(&self.second_point)
}
pub fn set_offset(&mut self, offset: f64) {
let dir = self.second_point - self.first_point;
let perpendicular = Vector3::new(-dir.y, dir.x, 0.0).normalize();
self.definition_point = self.second_point + perpendicular * offset;
}
pub fn offset(&self) -> f64 {
self.second_point.distance(&self.definition_point)
}
}
impl Default for DimensionAligned {
fn default() -> Self {
Self {
base: DimensionBase::new(DimensionType::Aligned),
first_point: Vector3::ZERO,
second_point: Vector3::ZERO,
definition_point: Vector3::ZERO,
ext_line_rotation: 0.0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DimensionLinear {
pub base: DimensionBase,
pub first_point: Vector3,
pub second_point: Vector3,
pub definition_point: Vector3,
pub rotation: f64,
pub ext_line_rotation: f64,
}
impl DimensionLinear {
pub fn new(first_point: Vector3, second_point: Vector3) -> Self {
let mut base = DimensionBase::new(DimensionType::Linear);
base.actual_measurement = first_point.distance(&second_point);
Self {
base,
first_point,
second_point,
definition_point: Vector3::ZERO,
rotation: 0.0,
ext_line_rotation: 0.0,
}
}
pub fn horizontal(first_point: Vector3, second_point: Vector3) -> Self {
Self::new(first_point, second_point)
}
pub fn vertical(first_point: Vector3, second_point: Vector3) -> Self {
let mut dim = Self::new(first_point, second_point);
dim.rotation = std::f64::consts::FRAC_PI_2; dim
}
pub fn rotated(first_point: Vector3, second_point: Vector3, angle: f64) -> Self {
let mut dim = Self::new(first_point, second_point);
dim.rotation = angle;
dim
}
pub fn measurement(&self) -> f64 {
let angle_vec = Vector3::new(self.rotation.cos(), self.rotation.sin(), 0.0);
let diff = self.second_point - self.first_point;
let normalized = diff.normalize();
let dot = angle_vec.dot(&normalized).abs();
self.first_point.distance(&self.second_point) * dot
}
pub fn set_offset(&mut self, offset: f64) {
let axis_y = Vector3::new(-self.rotation.sin(), self.rotation.cos(), 0.0);
self.definition_point = self.second_point + axis_y * offset;
}
}
impl Default for DimensionLinear {
fn default() -> Self {
Self {
base: DimensionBase::new(DimensionType::Linear),
first_point: Vector3::ZERO,
second_point: Vector3::ZERO,
definition_point: Vector3::ZERO,
rotation: 0.0,
ext_line_rotation: 0.0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DimensionRadius {
pub base: DimensionBase,
pub definition_point: Vector3,
pub angle_vertex: Vector3,
pub leader_length: f64,
}
impl DimensionRadius {
pub fn new(center: Vector3, point_on_arc: Vector3) -> Self {
let mut base = DimensionBase::new(DimensionType::Radius);
base.actual_measurement = center.distance(&point_on_arc);
Self {
base,
definition_point: point_on_arc,
angle_vertex: center,
leader_length: 0.0,
}
}
pub fn measurement(&self) -> f64 {
self.definition_point.distance(&self.angle_vertex)
}
}
impl Default for DimensionRadius {
fn default() -> Self {
Self {
base: DimensionBase::new(DimensionType::Radius),
definition_point: Vector3::ZERO,
angle_vertex: Vector3::ZERO,
leader_length: 0.0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DimensionDiameter {
pub base: DimensionBase,
pub definition_point: Vector3,
pub angle_vertex: Vector3,
pub leader_length: f64,
}
impl DimensionDiameter {
pub fn new(center: Vector3, point_on_arc: Vector3) -> Self {
let mut base = DimensionBase::new(DimensionType::Diameter);
base.actual_measurement = center.distance(&point_on_arc) * 2.0;
Self {
base,
definition_point: point_on_arc,
angle_vertex: center,
leader_length: 0.0,
}
}
pub fn measurement(&self) -> f64 {
self.definition_point.distance(&self.angle_vertex) * 2.0
}
pub fn center(&self) -> Vector3 {
(self.angle_vertex + self.definition_point) * 0.5
}
}
impl Default for DimensionDiameter {
fn default() -> Self {
Self {
base: DimensionBase::new(DimensionType::Diameter),
definition_point: Vector3::ZERO,
angle_vertex: Vector3::ZERO,
leader_length: 0.0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DimensionAngular2Ln {
pub base: DimensionBase,
pub dimension_arc: Vector3,
pub first_point: Vector3,
pub second_point: Vector3,
pub angle_vertex: Vector3,
pub definition_point: Vector3,
}
impl DimensionAngular2Ln {
pub fn new(vertex: Vector3, first_point: Vector3, second_point: Vector3) -> Self {
let mut base = DimensionBase::new(DimensionType::Angular);
let v1 = (first_point - vertex).normalize();
let v2 = (second_point - vertex).normalize();
let angle = v1.dot(&v2).acos();
base.actual_measurement = angle.to_degrees();
Self {
base,
dimension_arc: Vector3::ZERO,
first_point,
second_point,
angle_vertex: vertex,
definition_point: Vector3::ZERO,
}
}
pub fn measurement_radians(&self) -> f64 {
let v1 = (self.first_point - self.angle_vertex).normalize();
let v2 = (self.second_point - self.angle_vertex).normalize();
v1.dot(&v2).acos()
}
pub fn measurement_degrees(&self) -> f64 {
self.measurement_radians().to_degrees()
}
}
impl Default for DimensionAngular2Ln {
fn default() -> Self {
Self {
base: DimensionBase::new(DimensionType::Angular),
dimension_arc: Vector3::ZERO,
first_point: Vector3::ZERO,
second_point: Vector3::ZERO,
angle_vertex: Vector3::ZERO,
definition_point: Vector3::ZERO,
}
}
}
pub type DimensionAngular2Line = DimensionAngular2Ln;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DimensionAngular3Pt {
pub base: DimensionBase,
pub definition_point: Vector3,
pub first_point: Vector3,
pub second_point: Vector3,
pub angle_vertex: Vector3,
}
impl DimensionAngular3Pt {
pub fn new(vertex: Vector3, first_point: Vector3, second_point: Vector3) -> Self {
let mut base = DimensionBase::new(DimensionType::Angular3Point);
let v1 = (first_point - vertex).normalize();
let v2 = (second_point - vertex).normalize();
let angle = v1.dot(&v2).acos();
base.actual_measurement = angle.to_degrees();
Self {
base,
definition_point: Vector3::ZERO,
angle_vertex: vertex,
first_point,
second_point,
}
}
pub fn measurement_radians(&self) -> f64 {
let v1 = (self.first_point - self.angle_vertex).normalize();
let v2 = (self.second_point - self.angle_vertex).normalize();
v1.dot(&v2).acos()
}
pub fn measurement_degrees(&self) -> f64 {
self.measurement_radians().to_degrees()
}
}
impl Default for DimensionAngular3Pt {
fn default() -> Self {
Self {
base: DimensionBase::new(DimensionType::Angular3Point),
definition_point: Vector3::ZERO,
first_point: Vector3::ZERO,
second_point: Vector3::ZERO,
angle_vertex: Vector3::ZERO,
}
}
}
pub type DimensionAngular3Point = DimensionAngular3Pt;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DimensionOrdinate {
pub base: DimensionBase,
pub definition_point: Vector3,
pub feature_location: Vector3,
pub leader_endpoint: Vector3,
pub is_ordinate_type_x: bool,
}
impl DimensionOrdinate {
pub fn new(feature_location: Vector3, leader_endpoint: Vector3, is_x_type: bool) -> Self {
let mut base = DimensionBase::new(DimensionType::Ordinate);
base.actual_measurement = if is_x_type {
feature_location.x
} else {
feature_location.y
};
Self {
base,
definition_point: Vector3::ZERO,
feature_location,
leader_endpoint,
is_ordinate_type_x: is_x_type,
}
}
pub fn x_ordinate(feature_location: Vector3, leader_endpoint: Vector3) -> Self {
Self::new(feature_location, leader_endpoint, true)
}
pub fn y_ordinate(feature_location: Vector3, leader_endpoint: Vector3) -> Self {
Self::new(feature_location, leader_endpoint, false)
}
pub fn measurement(&self) -> f64 {
if self.is_ordinate_type_x {
self.feature_location.x
} else {
self.feature_location.y
}
}
}
impl Default for DimensionOrdinate {
fn default() -> Self {
Self {
base: DimensionBase::new(DimensionType::Ordinate),
definition_point: Vector3::ZERO,
feature_location: Vector3::ZERO,
leader_endpoint: Vector3::ZERO,
is_ordinate_type_x: true,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Dimension {
Aligned(DimensionAligned),
Linear(DimensionLinear),
Radius(DimensionRadius),
Diameter(DimensionDiameter),
Angular2Ln(DimensionAngular2Ln),
Angular3Pt(DimensionAngular3Pt),
Ordinate(DimensionOrdinate),
}
impl Dimension {
pub fn base(&self) -> &DimensionBase {
match self {
Dimension::Aligned(d) => &d.base,
Dimension::Linear(d) => &d.base,
Dimension::Radius(d) => &d.base,
Dimension::Diameter(d) => &d.base,
Dimension::Angular2Ln(d) => &d.base,
Dimension::Angular3Pt(d) => &d.base,
Dimension::Ordinate(d) => &d.base,
}
}
pub fn base_mut(&mut self) -> &mut DimensionBase {
match self {
Dimension::Aligned(d) => &mut d.base,
Dimension::Linear(d) => &mut d.base,
Dimension::Radius(d) => &mut d.base,
Dimension::Diameter(d) => &mut d.base,
Dimension::Angular2Ln(d) => &mut d.base,
Dimension::Angular3Pt(d) => &mut d.base,
Dimension::Ordinate(d) => &mut d.base,
}
}
pub fn measurement(&self) -> f64 {
match self {
Dimension::Aligned(d) => d.measurement(),
Dimension::Linear(d) => d.measurement(),
Dimension::Radius(d) => d.measurement(),
Dimension::Diameter(d) => d.measurement(),
Dimension::Angular2Ln(d) => d.measurement_degrees(),
Dimension::Angular3Pt(d) => d.measurement_degrees(),
Dimension::Ordinate(d) => d.measurement(),
}
}
}
impl super::Entity for Dimension {
fn handle(&self) -> crate::types::Handle {
self.base().common.handle
}
fn set_handle(&mut self, handle: crate::types::Handle) {
self.base_mut().common.handle = handle;
}
fn layer(&self) -> &str {
&self.base().common.layer
}
fn set_layer(&mut self, layer: String) {
self.base_mut().common.layer = layer;
}
fn color(&self) -> crate::types::Color {
self.base().common.color
}
fn set_color(&mut self, color: crate::types::Color) {
self.base_mut().common.color = color;
}
fn line_weight(&self) -> crate::types::LineWeight {
self.base().common.line_weight
}
fn set_line_weight(&mut self, weight: crate::types::LineWeight) {
self.base_mut().common.line_weight = weight;
}
fn transparency(&self) -> crate::types::Transparency {
self.base().common.transparency
}
fn set_transparency(&mut self, transparency: crate::types::Transparency) {
self.base_mut().common.transparency = transparency;
}
fn is_invisible(&self) -> bool {
self.base().common.invisible
}
fn set_invisible(&mut self, invisible: bool) {
self.base_mut().common.invisible = invisible;
}
fn bounding_box(&self) -> crate::types::BoundingBox3D {
use crate::types::BoundingBox3D;
match self {
Dimension::Aligned(d) => BoundingBox3D::from_points(&[d.first_point, d.second_point, d.definition_point]).unwrap_or_default(),
Dimension::Linear(d) => BoundingBox3D::from_points(&[d.first_point, d.second_point, d.definition_point]).unwrap_or_default(),
Dimension::Radius(d) => BoundingBox3D::from_points(&[d.angle_vertex, d.definition_point]).unwrap_or_default(),
Dimension::Diameter(d) => BoundingBox3D::from_points(&[d.angle_vertex, d.definition_point]).unwrap_or_default(),
Dimension::Angular2Ln(d) => BoundingBox3D::from_points(&[d.angle_vertex, d.first_point, d.second_point, d.definition_point]).unwrap_or_default(),
Dimension::Angular3Pt(d) => BoundingBox3D::from_points(&[d.angle_vertex, d.first_point, d.second_point, d.definition_point]).unwrap_or_default(),
Dimension::Ordinate(d) => BoundingBox3D::from_points(&[d.feature_location, d.leader_endpoint, d.definition_point]).unwrap_or_default(),
}
}
fn translate(&mut self, offset: Vector3) {
super::translate::translate_dimension(self, offset);
}
fn entity_type(&self) -> &'static str {
match self {
Dimension::Aligned(_) => "DIMENSION_ALIGNED",
Dimension::Linear(_) => "DIMENSION_LINEAR",
Dimension::Radius(_) => "DIMENSION_RADIUS",
Dimension::Diameter(_) => "DIMENSION_DIAMETER",
Dimension::Angular2Ln(_) => "DIMENSION_ANGULAR_2LINE",
Dimension::Angular3Pt(_) => "DIMENSION_ANGULAR_3POINT",
Dimension::Ordinate(_) => "DIMENSION_ORDINATE",
}
}
}