diff --git a/src/entity.rs b/src/entity.rs index d210e76..6c93e02 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; use std::rc::Rc; -use crate::math::{Line2, Point2, Region, Region1, Region2, Rot2, Scalar}; +use crate::math::{Point2, Region, Region1, Region2, Scalar}; #[derive(Clone, Copy, Debug)] pub struct Var> { diff --git a/src/main.rs b/src/main.rs index 40ae4a8..cbeb8f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,26 +2,20 @@ extern crate nalgebra; #[macro_use] extern crate approx; -mod math; -mod entity; -mod relation; +mod entity; +mod math; +mod relation; fn main() { use entity::{Point, PointRef, Var}; - use math::{Point2, Region2}; + use math::Point2; use relation::{Relation, ResolveResult}; println!("Hello, world!"); - let origin = Point::new_ref(Var::new( - Point2::new(0., 0.), - Region2::Singleton(Point2::new(0., 0.)), - )); - let p1 = Point::new_ref(Var::new(Point2::new(1., 1.), Region2::Full)); - let p2 = Point::new_ref(Var::new(Point2::new(4., 4.), Region2::Full)); - let p3 = Point::new_ref(Var::new( - Point2::new(2., 2.), - Region2::Singleton(Point2::new(2., 2.)), - )); + let origin = Point::new_ref(Var::new_single(Point2::new(0., 0.))); + let p1 = Point::new_ref(Var::new_full(Point2::new(1., 1.))); + let p2 = Point::new_ref(Var::new_full(Point2::new(4., 4.))); + let p3 = Point::new_ref(Var::new_single(Point2::new(2., 2.))); let mut points: Vec = vec![origin.clone(), p1.clone(), p2.clone(), p3.clone()]; let print_points = |points: &Vec| { println!( @@ -29,6 +23,7 @@ fn main() { points[0], points[1], points[2], points[3], ); }; + print_points(&points); let c1 = relation::Coincident { p1: origin.clone(), p2: p1.clone(), @@ -36,7 +31,8 @@ fn main() { let c2 = relation::PointAngle::new_vertical(p1.clone(), p2.clone()); let c3 = relation::PointAngle::new_horizontal(p1.clone(), p2.clone()); let c4 = relation::PointAngle::new_horizontal(p1.clone(), p3.clone()); - let mut relations: Vec> = vec![Box::new(c1), Box::new(c2), Box::new(c3), Box::new(c4)]; + let mut relations: Vec> = + vec![Box::new(c1), Box::new(c2), Box::new(c3), Box::new(c4)]; let mut has_underconstrained = true; while has_underconstrained { has_underconstrained = false; @@ -52,5 +48,4 @@ fn main() { ); } } - } diff --git a/src/math.rs b/src/math.rs index b7dc393..37d8c8e 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,7 +1,5 @@ pub type Scalar = f64; -pub const EPSILON: Scalar = std::f64::EPSILON * 100.; - pub type Vec2 = nalgebra::Vector2; pub type Point2 = nalgebra::Point2; @@ -37,7 +35,7 @@ impl Region for Region1 { use Region1::*; match self { Empty => false, - Singleton(n1) => *n1 == *n, + Singleton(n1) => relative_eq!(n1, n), Range(l, u) => *l <= *n && *n <= *u, Union(r1, r2) => r1.contains(n) || r2.contains(n), Full => true, @@ -69,8 +67,7 @@ impl Line2 { pub fn intersect(&self, other: &Line2) -> Option { // if two lines are parallel - // TODO: epsilon? - if (self.dir * other.dir).sin_angle() == 0. { + if relative_eq!((self.dir / other.dir).sin_angle(), 0.) { return None; } // TODO: respect extent @@ -82,8 +79,7 @@ impl Line2 { b_v.cos_angle(), b_v.sin_angle(), ); - let t_b = - (a_0.x * a_s - a_0.y * a_c + a_0.x * a_s + b_0.y * a_c) / (a_s * b_c - a_c * b_s); + let t_b = (a_0.x * a_s - a_0.y * a_c + a_0.x * a_s + b_0.y * a_c) / (a_s * b_c - a_c * b_s); Some(b.evaluate(t_b)) } } @@ -94,6 +90,7 @@ pub enum Region2 { // single point at 0 Singleton(Point2), Line(Line2), + #[allow(dead_code)] Union(Box, Box), Full, } @@ -108,16 +105,7 @@ impl Region for Region2 { } fn contains(&self, p: &Point2) -> bool { - use Region2::*; - self.nearest(p).map_or(false, |n| n == *p) // TODO: epsilon? - - // match self { - // Empty => false, - // Singleton(n1) => *n1 == n, - // Line(_, _, _) => unimplemented!(), - // Union(r1, r2) => r1.contains(n) || r2.contains(n), - // Full => true, - // } + self.nearest(p).map_or(false, |n| relative_eq!(n, p)) } fn nearest(&self, p: &Point2) -> Option { @@ -155,7 +143,7 @@ impl Region2 { use Region2::*; match (self, other) { (Empty, _) | (_, Empty) => Empty, - (Full, r @ _) | (r @ _, Full) => r.clone(), + (Full, r) | (r, Full) => r.clone(), (Singleton(n1), Singleton(n2)) => { if n1 == n2 { Singleton(*n1) @@ -163,7 +151,7 @@ impl Region2 { Empty } } - (Singleton(n), o @ _) | (o @ _, Singleton(n)) => { + (Singleton(n), o) | (o, Singleton(n)) => { if o.contains(n) { Singleton(*n) } else { @@ -177,4 +165,4 @@ impl Region2 { _ => unimplemented!(), } } -} \ No newline at end of file +} diff --git a/src/relation.rs b/src/relation.rs index 65e176c..2fedde3 100644 --- a/src/relation.rs +++ b/src/relation.rs @@ -1,5 +1,5 @@ use crate::entity::{Point as PointEntity, PointRef}; -use crate::math::{Line2, Point2, Region, Region1, Region2, Rot2, Scalar}; +use crate::math::{Line2, Point2, Region1, Region2, Rot2}; #[derive(Clone, Copy, Debug, PartialEq)] pub enum ResolveResult { @@ -30,7 +30,6 @@ pub struct Coincident { impl Relation for Coincident { fn resolve(&self) -> ResolveResult { - use Region2::*; let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut()); let r = { p1.pos.constraints().intersect(p2.pos.constraints()) }; p1.pos.reconstrain(r.clone()); @@ -64,7 +63,7 @@ impl Relation for PointAngle { use Region2::*; let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut()); let constrain_line = |p1: &Point2, p2: &mut PointEntity| { - let line = Region2::Line(Line2::new(p1.clone(), self.angle, Region1::Full)); + let line = Region2::Line(Line2::new(*p1, self.angle, Region1::Full)); let new_constraint = p2.pos.constraints().intersect(&line); p2.pos.reconstrain(new_constraint); ResolveResult::from_r2(p2.pos.constraints()) @@ -72,7 +71,10 @@ impl Relation for PointAngle { match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) { (Empty, _) | (_, Empty) => ResolveResult::Overconstrained, (Singleton(p1), Singleton(p2)) => { - if p1.x == p2.x { + // if the angle p1 and p2 form is parallel to self.angle, the result + // will have a y component of 0 + let r = self.angle.to_rotation_matrix().inverse() * (p2 - p1); + if relative_eq!(r.y, 0.) { ResolveResult::Constrained } else { ResolveResult::Overconstrained @@ -83,4 +85,4 @@ impl Relation for PointAngle { _ => ResolveResult::Underconstrained, } } -} \ No newline at end of file +}