|
|
|
@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
|
|
|
|
|
use crate::entity::{Point as PointEntity, PointRef}; |
|
|
|
|
use crate::math::{Line2, Point2, Region1, Region2, Rot2}; |
|
|
|
|
use crate::math::{Line2, Vec2, Point2, Region1, Region2, Rot2, Scalar}; |
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)] |
|
|
|
|
pub enum ResolveResult { |
|
|
|
@ -86,3 +86,73 @@ impl Relation for PointAngle {
@@ -86,3 +86,73 @@ impl Relation for PointAngle {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub enum Axis { |
|
|
|
|
Vertical, |
|
|
|
|
Horizontal, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub struct AlignedDistance { |
|
|
|
|
pub p1: PointRef, |
|
|
|
|
pub p2: PointRef, |
|
|
|
|
pub axis: Axis, |
|
|
|
|
pub distance: Scalar, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl AlignedDistance { |
|
|
|
|
pub fn new(p1: PointRef, p2: PointRef, axis: Axis, distance: Scalar) -> Self { |
|
|
|
|
Self { |
|
|
|
|
p1, |
|
|
|
|
p2, |
|
|
|
|
axis, |
|
|
|
|
distance, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn new_vertical(p1: PointRef, p2: PointRef, distance: Scalar) -> Self { |
|
|
|
|
Self::new(p1, p2, Axis::Vertical, distance) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn new_horizontal(p1: PointRef, p2: PointRef, distance: Scalar) -> Self { |
|
|
|
|
Self::new(p1, p2, Axis::Horizontal, distance) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Relation for AlignedDistance { |
|
|
|
|
fn resolve(&self) -> ResolveResult { |
|
|
|
|
use Region2::*; |
|
|
|
|
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut()); |
|
|
|
|
let constrain_line = |p1: Point2, p2: &mut PointEntity| { |
|
|
|
|
let angle = match self.axis { |
|
|
|
|
Axis::Horizontal => Rot2::from_cos_sin_unchecked(0., 1.), |
|
|
|
|
Axis::Vertical => Rot2::from_cos_sin_unchecked(1., 0.), |
|
|
|
|
}; |
|
|
|
|
let line = Region2::Line(Line2::new(p1, angle, Region1::Full)); |
|
|
|
|
let new_constraint = p2.pos.constraints().intersect(&line); |
|
|
|
|
p2.pos.reconstrain(new_constraint); |
|
|
|
|
ResolveResult::from_r2(p2.pos.constraints()) |
|
|
|
|
}; |
|
|
|
|
let offset = match self.axis { |
|
|
|
|
Axis::Horizontal => Vec2::new(self.distance, 0.), |
|
|
|
|
Axis::Vertical => Vec2::new(0., self.distance), |
|
|
|
|
}; |
|
|
|
|
match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) { |
|
|
|
|
(Empty, _) | (_, Empty) => ResolveResult::Overconstrained, |
|
|
|
|
(Singleton(p1), Singleton(p2)) => { |
|
|
|
|
let r = p2 - p1; |
|
|
|
|
let d = match self.axis { |
|
|
|
|
Axis::Horizontal => r.x, |
|
|
|
|
Axis::Vertical => r.y, |
|
|
|
|
}; |
|
|
|
|
if relative_eq!(d, self.distance) { |
|
|
|
|
ResolveResult::Constrained |
|
|
|
|
} else { |
|
|
|
|
ResolveResult::Overconstrained |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
(Singleton(pos), _) => constrain_line(pos + offset, &mut *p2), |
|
|
|
|
(_, Singleton(pos)) => constrain_line(pos - offset, &mut *p1), |
|
|
|
|
_ => ResolveResult::Underconstrained, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|