cad_rs/src/math/region.rs
2019-05-22 17:35:04 -07:00

438 lines
15 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::fmt;
use super::{eqn, Expr, Point2, Rot2, Scalar, Value};
// pub type Vec2 = nalgebra::Vector2<Value>;
// pub type Point2 = nalgebra::Point2<Value>;
// pub type Rot2 = nalgebra::UnitComplex<Value>;
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<T>: GenericRegion {
fn singleton(value: T) -> Self;
fn nearest(&self, value: &T) -> Option<T>;
fn contains(&self, value: &T) -> Option<bool>;
}
#[derive(Clone, Debug)]
pub enum Region1 {
Empty,
Singleton(Value),
Range(Value, Value),
Intersection(Box<Region1>, Box<Region1>),
// Union(Box<Region1>, Box<Region1>),
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<Scalar> for Region1 {
fn singleton(value: Scalar) -> Self {
Region1::Singleton(value.into())
}
fn contains(&self, n: &Scalar) -> Option<bool> {
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<Scalar> {
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<Value>,
dir: Rot2,
extent: Region1,
}
impl fmt::Display for Line2 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{{ <x, y> = {} + {} * {} }}",
self.start, self.dir, self.extent
)
}
}
impl Line2 {
pub fn new(start: Point2<Value>, dir: Rot2, extent: Region1) -> Self {
Self { start, dir, extent }
}
pub fn evaluate(&self, t: Value) -> Point2<Value> {
self.start.clone() + self.dir.clone() * t
}
pub fn evaluate_extent(&self) -> Option<Point2<Value>> {
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<Value>) -> Point2<Value> {
// 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<Value>),
Line(Line2),
// #[allow(dead_code)]
// Union(Box<Region2>, Box<Region2>),
Intersection(Box<Region2>, Box<Region2>),
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<Point2<Scalar>> for Region2 {
fn singleton(value: Point2<Scalar>) -> Self {
Region2::Singleton(value.into())
}
fn contains(&self, p: &Point2<Scalar>) -> Option<bool> {
self.nearest(p).map(|n| n == *p)
}
fn nearest(&self, p: &Point2<Scalar>) -> Option<Point2<Scalar>> {
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<Value> = 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<Point2<Value>> for Region2 {
fn singleton(value: Point2<Value>) -> Self {
Region2::Singleton(value)
}
fn contains(&self, p: &Point2<Value>) -> Option<bool> {
self.nearest(p)
.map(|n| n.simplify() == p.clone().simplify())
}
fn nearest(&self, p: &Point2<Value>) -> Option<Point2<Value>> {
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)),
}
}
}