use arael::refs::Ref;
use arael::vect::vect2d;
use crate::{Point, Line, Arc};
#[derive(Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum DimensionEndpoint {
Point(Ref<Point>),
LineP1(Ref<Line>),
LineP2(Ref<Line>),
ArcCenter(Ref<Arc>),
ArcStart(Ref<Arc>),
ArcEnd(Ref<Arc>),
}
#[derive(Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum DimensionKind {
LineLength(Ref<Line>),
PointPointDistance(DimensionEndpoint, DimensionEndpoint),
PointLineDistance(DimensionEndpoint, Ref<Line>),
ArcRadius(Ref<Arc>),
Angle(Ref<Line>, Ref<Line>, bool),
}
impl DimensionEndpoint {
pub fn references_point(&self, r: Ref<Point>) -> bool {
matches!(self, DimensionEndpoint::Point(p) if *p == r)
}
pub fn references_line(&self, r: Ref<Line>) -> bool {
matches!(self, DimensionEndpoint::LineP1(l) | DimensionEndpoint::LineP2(l) if *l == r)
}
pub fn references_arc(&self, r: Ref<Arc>) -> bool {
matches!(self, DimensionEndpoint::ArcCenter(a) | DimensionEndpoint::ArcStart(a) | DimensionEndpoint::ArcEnd(a) if *a == r)
}
}
impl DimensionKind {
pub fn references_point(&self, r: Ref<Point>) -> bool {
match self {
DimensionKind::PointPointDistance(a, b) => a.references_point(r) || b.references_point(r),
DimensionKind::PointLineDistance(a, _) => a.references_point(r),
_ => false,
}
}
pub fn references_line(&self, r: Ref<Line>) -> bool {
match self {
DimensionKind::LineLength(l) => *l == r,
DimensionKind::PointPointDistance(a, b) => a.references_line(r) || b.references_line(r),
DimensionKind::PointLineDistance(a, l) => a.references_line(r) || *l == r,
DimensionKind::Angle(a, b, _) => *a == r || *b == r,
_ => false,
}
}
pub fn references_arc(&self, r: Ref<Arc>) -> bool {
match self {
DimensionKind::ArcRadius(a) => *a == r,
DimensionKind::PointPointDistance(a, b) => a.references_arc(r) || b.references_arc(r),
DimensionKind::PointLineDistance(a, _) => a.references_arc(r),
_ => false,
}
}
}
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct Dimension {
pub kind: DimensionKind,
pub value: f64,
pub offset: vect2d, pub text_along: f64, pub name: String,
#[serde(default)]
pub expr_str: Option<String>,
#[serde(default)]
pub broken: bool,
}
impl Dimension {
pub fn measured_symbol(&self, sketch: &super::Sketch) -> arael_sym::E {
use arael_sym::symbol;
match &self.kind {
DimensionKind::LineLength(r) => {
let name = &sketch.lines[*r].name;
symbol(&format!("{}.length", name))
}
DimensionKind::ArcRadius(r) => {
let name = &sketch.arcs[*r].name;
symbol(&format!("{}.radius", name))
}
DimensionKind::PointPointDistance(a, b) => {
let pa = dim_endpoint_symbol(a, sketch);
let pb = dim_endpoint_symbol(b, sketch);
let dx = pa.0 - pb.0;
let dy = pa.1 - pb.1;
arael_sym::sqrt(dx.clone() * dx + dy.clone() * dy)
}
DimensionKind::PointLineDistance(pt, line) => {
let (px, py) = dim_endpoint_symbol(pt, sketch);
let ln = &sketch.lines[*line].name;
let p1x = symbol(&format!("{}.p1.x", ln));
let p1y = symbol(&format!("{}.p1.y", ln));
let p2x = symbol(&format!("{}.p2.x", ln));
let p2y = symbol(&format!("{}.p2.y", ln));
let dx = p2x.clone() - p1x.clone();
let dy = p2y.clone() - p1y.clone();
let len = arael_sym::sqrt(dx.clone() * dx.clone() + dy.clone() * dy.clone());
let signed = ((px - p1x) * dy.clone() - (py - p1y) * dx.clone()) / len;
let pt_pos = dim_endpoint_pos(pt, sketch);
let l = &sketch.lines[*line];
let ldx = l.p2.value.x - l.p1.value.x;
let ldy = l.p2.value.y - l.p1.value.y;
let cross = (pt_pos.x - l.p1.value.x) * ldy - (pt_pos.y - l.p1.value.y) * ldx;
if cross >= 0.0 { signed } else { -signed }
}
DimensionKind::Angle(a, b, supplement) => {
let la = &sketch.lines[*a].name;
let lb = &sketch.lines[*b].name;
let dx1 = symbol(&format!("{}.p2.x", la)) - symbol(&format!("{}.p1.x", la));
let dy1 = symbol(&format!("{}.p2.y", la)) - symbol(&format!("{}.p1.y", la));
let dx2 = symbol(&format!("{}.p2.x", lb)) - symbol(&format!("{}.p1.x", lb));
let dy2 = symbol(&format!("{}.p2.y", lb)) - symbol(&format!("{}.p1.y", lb));
let cross = dx1.clone() * dy2.clone() - dy1.clone() * dx2.clone();
let dot = dx1 * dx2 + dy1 * dy2;
let angle = arael_sym::atan2(cross, dot);
let la_line = &sketch.lines[*a];
let lb_line = &sketch.lines[*b];
let cur_dx1 = la_line.p2.value.x - la_line.p1.value.x;
let cur_dy1 = la_line.p2.value.y - la_line.p1.value.y;
let cur_dx2 = lb_line.p2.value.x - lb_line.p1.value.x;
let cur_dy2 = lb_line.p2.value.y - lb_line.p1.value.y;
let cur_cross = cur_dx1 * cur_dy2 - cur_dy1 * cur_dx2;
let cur_dot = cur_dx1 * cur_dx2 + cur_dy1 * cur_dy2;
let cur_angle = cur_cross.atan2(cur_dot);
let deg_factor = arael_sym::constant(180.0 / std::f64::consts::PI);
let signed_deg = angle * deg_factor;
if *supplement {
let sup_sign = if cur_angle >= 0.0 { 1.0 } else { -1.0 };
arael_sym::constant(sup_sign * 180.0) - signed_deg
} else {
if cur_angle >= 0.0 { signed_deg } else { -signed_deg }
}
}
}
}
}
fn dim_endpoint_pos(ep: &DimensionEndpoint, sketch: &super::Sketch) -> vect2d {
match ep {
DimensionEndpoint::Point(r) => sketch.points[*r].pos.value,
DimensionEndpoint::LineP1(r) => sketch.lines[*r].p1.value,
DimensionEndpoint::LineP2(r) => sketch.lines[*r].p2.value,
DimensionEndpoint::ArcCenter(r) => sketch.arcs[*r].center.value,
DimensionEndpoint::ArcStart(r) => {
let a = &sketch.arcs[*r];
vect2d::new(a.center.value.x + a.radius.value * a.start_angle.value.cos(),
a.center.value.y + a.radius.value * a.start_angle.value.sin())
}
DimensionEndpoint::ArcEnd(r) => {
let a = &sketch.arcs[*r];
vect2d::new(a.center.value.x + a.radius.value * a.end_angle.value.cos(),
a.center.value.y + a.radius.value * a.end_angle.value.sin())
}
}
}
fn dim_endpoint_symbol(ep: &DimensionEndpoint, sketch: &super::Sketch) -> (arael_sym::E, arael_sym::E) {
use arael_sym::symbol;
match ep {
DimensionEndpoint::Point(r) => {
let n = &sketch.points[*r].name;
(symbol(&format!("{}.pos.x", n)), symbol(&format!("{}.pos.y", n)))
}
DimensionEndpoint::LineP1(r) => {
let n = &sketch.lines[*r].name;
(symbol(&format!("{}.p1.x", n)), symbol(&format!("{}.p1.y", n)))
}
DimensionEndpoint::LineP2(r) => {
let n = &sketch.lines[*r].name;
(symbol(&format!("{}.p2.x", n)), symbol(&format!("{}.p2.y", n)))
}
DimensionEndpoint::ArcCenter(r) => {
let n = &sketch.arcs[*r].name;
(symbol(&format!("{}.center.x", n)), symbol(&format!("{}.center.y", n)))
}
DimensionEndpoint::ArcStart(r) => {
let n = &sketch.arcs[*r].name;
let cx = symbol(&format!("{}.center.x", n));
let cy = symbol(&format!("{}.center.y", n));
let r = symbol(&format!("{}.radius", n));
let sa = symbol(&format!("{}.start_angle", n));
(cx + r.clone() * arael_sym::cos(sa.clone()), cy + r * arael_sym::sin(sa))
}
DimensionEndpoint::ArcEnd(r) => {
let n = &sketch.arcs[*r].name;
let cx = symbol(&format!("{}.center.x", n));
let cy = symbol(&format!("{}.center.y", n));
let r = symbol(&format!("{}.radius", n));
let ea = symbol(&format!("{}.end_angle", n));
(cx + r.clone() * arael_sym::cos(ea.clone()), cy + r * arael_sym::sin(ea))
}
}
}