use crate::geometry::{Point, Vector2, Arc, Line};
use crate::data_structure::{Entity, EntityType, EntityGeometry};
use serde::{Serialize, Deserialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AngularDimension {
pub geometry: DimensionGeometry,
pub arc: Arc,
pub extension_line1: Line,
pub extension_line2: Line,
pub text_location: Point,
pub arc_center: Point,
pub arc_radius: f64,
pub start_angle: f64,
pub end_angle: f64,
pub is_2_line: bool,
pub is_3_point: bool,
}
impl AngularDimension {
pub fn new_from_arc(
center: Point,
arc: Arc,
style: DimensionStyle,
) -> Self {
let mut geometry = DimensionGeometry::new(DimensionType::Angular, style.clone());
geometry.definition_points = vec![
center,
Point::new(
center.x + arc.radius * arc.start_angle.cos(),
center.y + arc.radius * arc.start_angle.sin(),
0.0,
),
Point::new(
center.x + arc.radius * arc.end_angle.cos(),
center.y + arc.radius * arc.end_angle.sin(),
0.0,
),
];
let ext_length = style.extension_line_extension * 2.0;
let v1 = Vector2::new(
geometry.definition_points[1].x - center.x,
geometry.definition_points[1].y - center.y,
).normalize();
let v2 = Vector2::new(
geometry.definition_points[2].x - center.x,
geometry.definition_points[2].y - center.y,
).normalize();
let ext1_end = Point::new(
center.x + v1.x * ext_length,
center.y + v1.y * ext_length,
0.0,
);
let extension_line1 = Line::new(center, ext1_end);
let ext2_end = Point::new(
center.x + v2.x * ext_length,
center.y + v2.y * ext_length,
0.0,
);
let extension_line2 = Line::new(center, ext2_end);
let arc_radius = style.arrow_size * 8.0;
let mid_angle = (arc.start_angle + arc.end_angle) / 2.0;
let text_location = Point::new(
center.x + mid_angle.cos() * (arc_radius + style.text_height + style.dimension_line_gap),
center.y + mid_angle.sin() * (arc_radius + style.text_height + style.dimension_line_gap),
0.0,
);
geometry.calculate_measurement();
Self {
geometry,
arc: Arc::new(center, arc_radius, arc.start_angle, arc.end_angle),
extension_line1,
extension_line2,
text_location,
arc_center: center,
arc_radius,
start_angle: arc.start_angle,
end_angle: arc.end_angle,
is_2_line: false,
is_3_point: true,
}
}
pub fn new_from_3_point(
center: Point,
p1: Point,
p2: Point,
style: DimensionStyle,
) -> Self {
let mut geometry = DimensionGeometry::new(DimensionType::Angular, style.clone());
geometry.definition_points = vec![center, p1, p2];
let radius = center.distance_to(&p1).max(center.distance_to(&p2));
let v1 = (p1 - center).to_vector2();
let v2 = (p2 - center).to_vector2();
let angle1 = v1.angle();
let angle2 = v2.angle();
let arc_radius = style.arrow_size * 8.0;
let ext_length = style.extension_line_extension * 2.0;
let ext1_end = Point::new(
center.x + v1.normalize().x * ext_length,
center.y + v1.normalize().y * ext_length,
0.0,
);
let extension_line1 = Line::new(center, ext1_end);
let ext2_end = Point::new(
center.x + v2.normalize().x * ext_length,
center.y + v2.normalize().y * ext_length,
0.0,
);
let extension_line2 = Line::new(center, ext2_end);
let start_angle = angle1.min(angle2);
let end_angle = angle1.max(angle2);
let mid_angle = (start_angle + end_angle) / 2.0;
let text_location = Point::new(
center.x + mid_angle.cos() * (arc_radius + style.text_height + style.dimension_line_gap),
center.y + mid_angle.sin() * (arc_radius + style.text_height + style.dimension_line_gap),
0.0,
);
geometry.calculate_measurement();
Self {
geometry,
arc: Arc::new(center, arc_radius, start_angle, end_angle),
extension_line1,
extension_line2,
text_location,
arc_center: center,
arc_radius,
start_angle,
end_angle,
is_2_line: false,
is_3_point: true,
}
}
pub fn new_from_2_line(
p1: Point,
p2: Point,
center: Point,
style: DimensionStyle,
) -> Self {
let mut geometry = DimensionGeometry::new(DimensionType::Angular, style.clone());
geometry.definition_points = vec![center, p1, p2];
let arc_radius = style.arrow_size * 8.0;
let ext_length = style.extension_line_extension * 2.0;
let v1 = (p1 - center).to_vector2().normalize();
let v2 = (p2 - center).to_vector2().normalize();
let ext1_end = Point::new(
p1.x + v1.x * ext_length,
p1.y + v1.y * ext_length,
0.0,
);
let extension_line1 = Line::new(p1, ext1_end);
let ext2_end = Point::new(
p2.x + v2.x * ext_length,
p2.y + v2.y * ext_length,
0.0,
);
let extension_line2 = Line::new(p2, ext2_end);
let angle1 = v1.angle();
let angle2 = v2.angle();
let start_angle = angle1.min(angle2);
let end_angle = angle1.max(angle2);
let mid_angle = (start_angle + end_angle) / 2.0;
let text_location = Point::new(
center.x + mid_angle.cos() * (arc_radius + style.text_height + style.dimension_line_gap),
center.y + mid_angle.sin() * (arc_radius + style.text_height + style.dimension_line_gap),
0.0,
);
geometry.calculate_measurement();
Self {
geometry,
arc: Arc::new(center, arc_radius, start_angle, end_angle),
extension_line1,
extension_line2,
text_location,
arc_center: center,
arc_radius,
start_angle,
end_angle,
is_2_line: true,
is_3_point: false,
}
}
pub fn flip(&mut self) {
std::mem::swap(&mut self.start_angle, &mut self.end_angle);
std::mem::swap(&mut self.extension_line1, &mut self.extension_line2);
self.geometry.calculate_measurement();
}
pub fn set_text_location(&mut self, location: Point) {
self.text_location = location;
self.geometry.user_text_location = Some(location);
}
}
impl From<AngularDimension> for Entity {
fn from(dim: AngularDimension) -> Self {
Entity::new(
EntityType::Dimension,
EntityGeometry::AngularDimension(dim),
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArcLengthDimension {
pub geometry: DimensionGeometry,
pub arc: Arc,
pub extension_line1: Line,
pub extension_line2: Line,
pub text_location: Point,
}
impl ArcLengthDimension {
pub fn new(arc: Arc, style: DimensionStyle) -> Self {
let mut geometry = DimensionGeometry::new(DimensionType::ArcLength, style.clone());
geometry.definition_points = vec![
arc.center,
Point::new(
arc.center.x + arc.radius * arc.start_angle.cos(),
arc.center.y + arc.radius * arc.start_angle.sin(),
0.0,
),
Point::new(
arc.center.x + arc.radius * arc.end_angle.cos(),
arc.center.y + arc.radius * arc.end_angle.sin(),
0.0,
),
];
let arc_length = arc.radius * (arc.end_angle - arc.start_angle).abs();
geometry.measurement = arc_length;
geometry.actual_measurement = arc_length;
geometry.update_text();
let radius = style.arrow_size * 8.0;
let mid_angle = (arc.start_angle + arc.end_angle) / 2.0;
let text_location = Point::new(
arc.center.x + mid_angle.cos() * (radius + style.text_height + style.dimension_line_gap),
arc.center.y + mid_angle.sin() * (radius + style.text_height + style.dimension_line_gap),
0.0,
);
let ext_length = style.extension_line_extension * 2.0;
let v1 = Vector2::new(
geometry.definition_points[1].x - arc.center.x,
geometry.definition_points[1].y - arc.center.y,
).normalize();
let v2 = Vector2::new(
geometry.definition_points[2].x - arc.center.x,
geometry.definition_points[2].y - arc.center.y,
).normalize();
let ext1_end = Point::new(
geometry.definition_points[1].x + v1.x * ext_length,
geometry.definition_points[1].y + v1.y * ext_length,
0.0,
);
let extension_line1 = Line::new(geometry.definition_points[1], ext1_end);
let ext2_end = Point::new(
geometry.definition_points[2].x + v2.x * ext_length,
geometry.definition_points[2].y + v2.y * ext_length,
0.0,
);
let extension_line2 = Line::new(geometry.definition_points[2], ext2_end);
Self {
geometry,
arc: Arc::new(arc.center, radius, arc.start_angle, arc.end_angle),
extension_line1,
extension_line2,
text_location,
}
}
}
impl From<ArcLengthDimension> for Entity {
fn from(dim: ArcLengthDimension) -> Self {
Entity::new(
EntityType::Dimension,
EntityGeometry::ArcLengthDimension(dim),
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum AngularUnit {
Degrees,
DegreesMinutesSeconds,
Gradians,
Radians,
}
impl Default for AngularUnit {
fn default() -> Self {
AngularUnit::Degrees
}
}
impl AngularUnit {
pub fn format_angle(&self, angle_radians: f64) -> String {
match self {
AngularUnit::Degrees => {
let degrees = angle_radians.to_degrees();
format!("{:.2}°", degrees)
}
AngularUnit::DegreesMinutesSeconds => {
let degrees = angle_radians.to_degrees();
let d = degrees.floor() as i32;
let minutes = ((degrees - d as f64) * 60.0).floor() as i32;
let seconds = ((degrees - d as f64 - minutes as f64 / 60.0) * 3600.0).round();
format!("{}°{}'{:.0}\"", d, minutes, seconds)
}
AngularUnit::Gradians => {
let gradians = angle_radians.to_degrees() * 10.0 / 9.0;
format!("{:.2}g", gradians)
}
AngularUnit::Radians => {
format!("{:.4} rad", angle_radians)
}
}
}
}