use std::fmt; use super::{eqn, Expr, Point2, Rot2, Scalar, Value}; // pub type Vec2 = nalgebra::Vector2; // pub type Point2 = nalgebra::Point2; // pub type Rot2 = nalgebra::UnitComplex; pub trait GenericRegion { fn full() -> Self; fn intersection(self, other: Self) -> Self; fn simplify(self) -> Self; fn evaluate_with(self, eqns: &eqn::Eqns) -> Self; } pub trait Region: GenericRegion { fn singleton(value: T) -> Self; fn nearest(&self, value: &T) -> Option; fn contains(&self, value: &T) -> Option; } #[derive(Clone, Debug)] pub enum Region1 { Empty, Singleton(Value), Range(Value, Value), Intersection(Box, Box), // Union(Box, Box), Full, } impl fmt::Display for Region1 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Region1::*; match self { Empty => write!(f, "Ø"), Singleton(v) => write!(f, "{{ {} }}", v), Range(l, u) => write!(f, "[ {}, {} ]", l, u), Intersection(r1, r2) => write!(f, "{} ∩ {}", r1, r2), Full => write!(f, "ℝ"), } } } impl GenericRegion for Region1 { fn intersection(self, other: Region1) -> Self { Region1::Intersection(Box::new(self), Box::new(other)) } fn full() -> Self { Region1::Full } fn simplify(self) -> Self { use Region1::*; match self { Singleton(n) => Singleton(n.simplify()), Range(l, u) => Range(l.simplify(), u.simplify()), Intersection(r1, r2) => r1.simplify().intersection(r2.simplify()), other => other, } } fn evaluate_with(self, eqns: &eqn::Eqns) -> Self { use Region1::*; match self { Singleton(n) => Singleton(n.evaluate_with(eqns)), Range(l, u) => Range(l.evaluate_with(eqns), u.evaluate_with(eqns)), Intersection(r1, r2) => r1.evaluate_with(eqns).intersection(r2.evaluate_with(eqns)), other => other, } } } impl Region for Region1 { fn singleton(value: Scalar) -> Self { Region1::Singleton(value.into()) } fn contains(&self, n: &Scalar) -> Option { use Expr::Const; use Region1::*; match self { Empty => Some(false), Singleton(n1) => match n1 { Const(c) => Some(relative_eq!(c, n)), _ => None, }, Range(l, u) => match (l, u) { (Const(cl), Const(cu)) => Some(*cl <= *n && *n <= *cu), _ => None, }, Intersection(r1, r2) => r1 .contains(n) .and_then(|c1| r2.contains(n).map(|c2| c1 && c2)), // Union(r1, r2) => r1.contains(n) || r2.contains(n), Full => Some(true), } } fn nearest(&self, s: &Scalar) -> Option { use Expr::Const; use Region1::*; match self { Empty => None, Full => Some(*s), Singleton(n) => match n { Const(c) => Some(*c), _ => None, }, Range(l, u) => match (l, u) { (Const(cl), Const(cu)) => match (cl < s, s < cu) { (true, true) => Some(*s), (true, false) => Some(*cu), (false, true) => Some(*cl), _ => None, }, _ => None, }, Intersection(r1, r2) => unimplemented!(), /*Union(r1, r2) => { let distance = |a: Scalar, b: Scalar| (a - b).abs(); match (r1.nearest(s), r2.nearest(s)) { (None, None) => None, (Some(n), None) | (None, Some(n)) => Some(n), (Some(n1), Some(n2)) => Some({ if distance(*s, n1) <= distance(*s, n2) { n1 } else { n2 } }), } }*/ } } } // line starting at start, point at angle dir, with range extent // ie. start + (cos dir, sin dir) * t for t in extent #[derive(Clone, Debug)] pub struct Line2 { start: Point2, dir: Rot2, extent: Region1, } impl fmt::Display for Line2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{{ = {} + {} * {} }}", self.start, self.dir, self.extent ) } } impl Line2 { pub fn new(start: Point2, dir: Rot2, extent: Region1) -> Self { Self { start, dir, extent } } pub fn evaluate(&self, t: Value) -> Point2 { self.start.clone() + self.dir.clone() * t } pub fn evaluate_extent(&self) -> Option> { match &self.extent { Region1::Singleton(t) => Some(self.evaluate(t.clone())), _ => None, } } pub fn with_extent(self, new_extent: Region1) -> Line2 { Line2 { start: self.start, dir: self.dir, extent: new_extent, } } pub fn nearest(&self, p: &Point2) -> Point2 { // rotate angle 90 degrees let perp_dir = self.dir.clone() + Rot2::cardinal(1); let perp = Line2::new(p.clone(), perp_dir, Region1::Full); match self.intersect(&perp) { Region2::Singleton(np) => np, Region2::Line(l) => l.evaluate_extent().expect("Line2::nearest not found"), _ => panic!("Line2::nearest not found!"), } } pub fn intersect(&self, other: &Line2) -> Region2 { // if the two lines are parallel... let dirs = self.dir.clone() - other.dir.clone(); if relative_eq!(dirs.sin(), 0.) { let starts = self.dir.conj() * (other.start.clone() - self.start.clone()); return if starts.y.simplify().is_zero() { // and they are colinear Region2::Line(self.clone()) } else { // they are parallel and never intersect Region2::Empty }; } // TODO: respect extent let (a, b) = (self, other); let (a_0, a_v, b_0, b_v) = ( a.start.clone(), a.dir.clone(), b.start.clone(), b.dir.clone(), ); let (a_c, a_s, b_c, b_s) = (a_v.cos(), a_v.sin(), b_v.cos(), b_v.sin()); let t_b = (a_0.x.clone() * a_s.clone() - a_0.y.clone() * a_c.clone() - b_0.x.clone() * a_s.clone() + b_0.y.clone() * a_c.clone()) / (a_s.clone() * b_c.clone() - a_c.clone() * b_s.clone()); // Region2::Singleton(b.evaluate(t_b)) trace!("intersect a: {}, b: {}, t_b = {}", a, b, t_b); let res = Region2::Line(b.clone().with_extent(Region1::Singleton(t_b.simplify()))); trace!("intersect a: {}, b: {} = {}", a, b, res); res } pub fn simplify(self) -> Region2 { let new_l = Line2 { start: self.start.simplify(), dir: self.dir, extent: self.extent.simplify(), }; trace!( "line {}: simplify evaluate extent: {:?}", new_l, new_l.evaluate_extent() ); if let Some(p) = new_l.evaluate_extent() { return Region2::Singleton(p.simplify()); } Region2::Line(new_l) } pub fn evaluate_with(self, eqns: &eqn::Eqns) -> Self { Line2 { start: self.start.evaluate_with(eqns), dir: self.dir, extent: self.extent.evaluate_with(eqns), } } } #[derive(Clone, Debug)] pub enum Region2 { Empty, // single point at 0 Singleton(Point2), Line(Line2), // #[allow(dead_code)] // Union(Box, Box), Intersection(Box, Box), Full, } impl fmt::Display for Region2 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Region2::*; match self { Empty => write!(f, "ز"), Singleton(v) => write!(f, "{{ {} }}", v), Line(l) => l.fmt(f), Intersection(r1, r2) => write!(f, "{} ∩ {}", r1, r2), Full => write!(f, "ℝ²"), } } } impl GenericRegion for Region2 { fn full() -> Self { Region2::Full } fn intersection(self, other: Self) -> Self { use Region2::*; match (self, other) { (Empty, _) | (_, Empty) => Empty, (Full, r) | (r, Full) => r, (r1, r2) => Intersection(Box::new(r1), Box::new(r2)), } } fn simplify(self) -> Region2 { use Region2::*; match self { Singleton(n) => Singleton(n.simplify()), Line(l) => l.simplify(), Intersection(r1, r2) => r1.simplify().intersect(r2.simplify()), other => other, } } fn evaluate_with(self, eqns: &eqn::Eqns) -> Self { use Region2::*; match self { Singleton(n) => Singleton(n.evaluate_with(eqns)), Line(l) => Line(l.evaluate_with(eqns)), Intersection(r1, r2) => r1.evaluate_with(eqns).intersection(r2.evaluate_with(eqns)), other => other, } } } impl Region> for Region2 { fn singleton(value: Point2) -> Self { Region2::Singleton(value.into()) } fn contains(&self, p: &Point2) -> Option { self.nearest(p).map(|n| n == *p) } fn nearest(&self, p: &Point2) -> Option> { use Expr::Const; use Region2::*; match self { Empty => None, Full => Some(p.clone()), Singleton(n) => match (&n.x, &n.y) { (Const(cx), Const(cy)) => Some(Point2::new(*cx, *cy)), _ => None, }, Line(line) => { let pv: Point2 = p.clone().into(); let n = line.nearest(&pv).simplify(); trace!("line {} nearest to {}: {}", line, pv, n); match (n.x, n.y) { (Const(cx), Const(cy)) => Some(Point2::new(cx, cy)), _ => None, } } Intersection(r1, r2) => { None // r1.clone().intersect((**r2).clone()).nearest(p) } /*Union(r1, r2) => { use nalgebra::distance; match (r1.nearest(p), r2.nearest(p)) { (None, None) => None, (Some(n), None) | (None, Some(n)) => Some(n), (Some(n1), Some(n2)) => Some({ if distance(p, &n1) <= distance(p, &n2) { n1 } else { n2 } }), } }*/ } } } impl Region> for Region2 { fn singleton(value: Point2) -> Self { Region2::Singleton(value) } fn contains(&self, p: &Point2) -> Option { self.nearest(p) .map(|n| n.simplify() == p.clone().simplify()) } fn nearest(&self, p: &Point2) -> Option> { use Region2::*; match self { Empty => None, Full => Some(p.clone()), Singleton(n) => Some(n.clone()), Line(line) => Some(line.nearest(p)), Intersection(r1, r2) => r1.clone().intersect((**r2).clone()).nearest(p), /*Union(r1, r2) => { use nalgebra::distance; match (r1.nearest(p), r2.nearest(p)) { (None, None) => None, (Some(n), None) | (None, Some(n)) => Some(n), (Some(n1), Some(n2)) => Some({ if distance(p, &n1) <= distance(p, &n2) { n1 } else { n2 } }), } }*/ } } } impl Region2 { /* pub fn union(r1: Region2, r2: Region2) -> Region2 { use Region2::*; match (r1, r2) { (Empty, r) | (r, Empty) => r, (Full, _) | (_, Full) => Full, (r1, r2) => Union(Box::new(r1), Box::new(r2)), } } */ pub fn intersect(self, other: Region2) -> Region2 { use Region2::*; match (self, other) { (Empty, _) | (_, Empty) => Empty, (Full, r) | (r, Full) => r.clone(), (Singleton(n1), Singleton(n2)) => { if n1 == n2 { Singleton(n1) } else { Region2::intersection(Singleton(n1), Singleton(n2)) } } (Singleton(n), o) | (o, Singleton(n)) => { if o.contains(&n).unwrap_or(false) { Singleton(n) } else { Region2::intersection(Singleton(n), o) } } (Intersection(r1, r2), o) | (o, Intersection(r1, r2)) => r1.intersect(*r2).intersect(o), (Line(l1), Line(l2)) => l1.intersect(&l2).simplify(), /*(Union(un1, un2), o) | (o, Union(un1, un2)) => { Self::union(un1.intersect(o), un2.intersect(o)) }*/ (r1, r2) => Intersection(Box::new(r1), Box::new(r2)), } } }