|
|
|
@ -5,15 +5,194 @@ pub use eqn::Unknown;
@@ -5,15 +5,194 @@ pub use eqn::Unknown;
|
|
|
|
|
pub use ops::*; |
|
|
|
|
|
|
|
|
|
pub type Scalar = f64; |
|
|
|
|
pub enum Value { |
|
|
|
|
Known(Scalar), |
|
|
|
|
Unkn(Unknown), |
|
|
|
|
// #[derive(Clone, Copy, PartialEq, Debug)]
|
|
|
|
|
// pub enum Value {
|
|
|
|
|
// Known(Scalar),
|
|
|
|
|
// Unkn(Unknown),
|
|
|
|
|
// }
|
|
|
|
|
pub type Value = eqn::Expr; |
|
|
|
|
|
|
|
|
|
// pub type Vec2 = nalgebra::Vector2<Value>;
|
|
|
|
|
// pub type Point2 = nalgebra::Point2<Value>;
|
|
|
|
|
#[derive(Clone, PartialEq, Debug)] |
|
|
|
|
pub struct Vec2 { |
|
|
|
|
pub x: Value, |
|
|
|
|
pub y: Value, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Vec2 { |
|
|
|
|
pub fn new(x: Value, y: Value) -> Self { |
|
|
|
|
Self { x, y } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl std::ops::Add<Vec2> for Vec2 { |
|
|
|
|
type Output = Vec2; |
|
|
|
|
fn add(self, rhs: Vec2) -> Vec2 { |
|
|
|
|
Vec2 { |
|
|
|
|
x: self.x + rhs.x, |
|
|
|
|
y: self.y + rhs.y, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Debug)] |
|
|
|
|
pub struct Point2 { |
|
|
|
|
pub x: Value, |
|
|
|
|
pub y: Value, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Point2 { |
|
|
|
|
pub fn new(x: Value, y: Value) -> Point2 { |
|
|
|
|
Point2 { x, y } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl std::ops::Add<Vec2> for Point2 { |
|
|
|
|
type Output = Point2; |
|
|
|
|
fn add(self, rhs: Vec2) -> Point2 { |
|
|
|
|
Point2 { |
|
|
|
|
x: self.x + rhs.x, |
|
|
|
|
y: self.y + rhs.y, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl std::ops::Sub<Vec2> for Point2 { |
|
|
|
|
type Output = Point2; |
|
|
|
|
fn sub(self, rhs: Vec2) -> Point2 { |
|
|
|
|
Point2 { |
|
|
|
|
x: self.x - rhs.x, |
|
|
|
|
y: self.y - rhs.y, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl std::ops::Sub<Point2> for Point2 { |
|
|
|
|
type Output = Vec2; |
|
|
|
|
fn sub(self, rhs: Point2) -> Vec2 { |
|
|
|
|
Vec2 { |
|
|
|
|
x: self.x - rhs.x, |
|
|
|
|
y: self.y - rhs.y, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
use std::fmt; |
|
|
|
|
|
|
|
|
|
impl fmt::Display for Point2 { |
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
|
|
|
write!(f, "({}, {})", self.x, self.y) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Debug)] |
|
|
|
|
pub struct Rot2 { |
|
|
|
|
cos: Value, |
|
|
|
|
sin: Value, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Rot2 { |
|
|
|
|
pub fn from_cos_sin_unchecked(cos: Value, sin: Value) -> Self { |
|
|
|
|
Self { cos, sin } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn from_angle(angle: Scalar) -> Self { |
|
|
|
|
Self { |
|
|
|
|
cos: angle.cos().into(), |
|
|
|
|
sin: angle.sin().into(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn cardinal(index: i64) -> Self { |
|
|
|
|
match index % 4 { |
|
|
|
|
0 => Rot2 { |
|
|
|
|
cos: (1.).into(), |
|
|
|
|
sin: (0.).into(), |
|
|
|
|
}, |
|
|
|
|
1 => Rot2 { |
|
|
|
|
cos: (0.).into(), |
|
|
|
|
sin: (1.).into(), |
|
|
|
|
}, |
|
|
|
|
2 => Rot2 { |
|
|
|
|
cos: (-1.).into(), |
|
|
|
|
sin: (0.).into(), |
|
|
|
|
}, |
|
|
|
|
3 => Rot2 { |
|
|
|
|
cos: (0.).into(), |
|
|
|
|
sin: (-1.).into(), |
|
|
|
|
}, |
|
|
|
|
_ => unreachable!(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn cos(&self) -> &Value { |
|
|
|
|
&self.cos |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn sin(&self) -> &Value { |
|
|
|
|
&self.sin |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn conj(self) -> Self { |
|
|
|
|
Self { |
|
|
|
|
cos: self.cos, |
|
|
|
|
sin: -self.sin, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl std::ops::Mul<Scalar> for Rot2 { |
|
|
|
|
type Output = Vec2; |
|
|
|
|
fn mul(self, rhs: Scalar) -> Vec2 { |
|
|
|
|
Vec2 { |
|
|
|
|
x: self.cos * rhs, |
|
|
|
|
y: self.sin * rhs, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl std::ops::Mul<Value> for Rot2 { |
|
|
|
|
type Output = Vec2; |
|
|
|
|
fn mul(self, rhs: Value) -> Vec2 { |
|
|
|
|
Vec2 { |
|
|
|
|
x: self.cos * rhs.clone(), |
|
|
|
|
y: self.sin * rhs, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub type Vec2 = nalgebra::Vector2<Scalar>; |
|
|
|
|
pub type Point2 = nalgebra::Point2<Scalar>; |
|
|
|
|
impl std::ops::Add<Rot2> for Rot2 { |
|
|
|
|
type Output = Rot2; |
|
|
|
|
fn add(self, rhs: Rot2) -> Rot2 { |
|
|
|
|
Rot2 { |
|
|
|
|
cos: self.cos.clone() * rhs.cos.clone() - self.sin.clone() * rhs.sin.clone(), |
|
|
|
|
sin: self.cos * rhs.sin + self.sin * rhs.cos, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub type Rot2 = nalgebra::UnitComplex<Scalar>; |
|
|
|
|
impl std::ops::Sub<Rot2> for Rot2 { |
|
|
|
|
type Output = Rot2; |
|
|
|
|
fn sub(self, rhs: Rot2) -> Rot2 { |
|
|
|
|
Rot2 { |
|
|
|
|
cos: self.cos.clone() * rhs.cos.clone() + self.sin.clone() * rhs.sin.clone(), |
|
|
|
|
sin: self.sin * rhs.cos - self.cos * rhs.sin, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl std::ops::Mul<Vec2> for Rot2 { |
|
|
|
|
type Output = Vec2; |
|
|
|
|
fn mul(self, rhs: Vec2) -> Vec2 { |
|
|
|
|
Vec2 { |
|
|
|
|
x: self.cos.clone() * rhs.x.clone() - self.sin.clone() * rhs.y.clone(), |
|
|
|
|
y: self.cos * rhs.y + self.sin * rhs.x, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// pub type Rot2 = nalgebra::UnitComplex<Value>;
|
|
|
|
|
|
|
|
|
|
pub trait Region<T> { |
|
|
|
|
fn full() -> Self; |
|
|
|
@ -96,14 +275,14 @@ impl Line2 {
@@ -96,14 +275,14 @@ impl Line2 {
|
|
|
|
|
Self { start, dir, extent } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn evaluate(&self, t: Scalar) -> Point2 { |
|
|
|
|
self.start + self.dir * Vec2::new(t, 0.) |
|
|
|
|
pub fn evaluate(&self, t: Value) -> Point2 { |
|
|
|
|
self.start.clone() + self.dir.clone() * t |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn nearest(&self, p: &Point2) -> Point2 { |
|
|
|
|
// rotate angle 90 degrees
|
|
|
|
|
let perp_dir = self.dir * Rot2::from_cos_sin_unchecked(0., 1.); |
|
|
|
|
let perp = Line2::new(*p, perp_dir, Region1::Full); |
|
|
|
|
let perp_dir = self.dir.clone() + Rot2::cardinal(1); |
|
|
|
|
let perp = Line2::new(p.clone(), perp_dir, Region1::Full); |
|
|
|
|
if let Region2::Singleton(np) = self.intersect(&perp) { |
|
|
|
|
np |
|
|
|
|
} else { |
|
|
|
@ -113,8 +292,9 @@ impl Line2 {
@@ -113,8 +292,9 @@ impl Line2 {
|
|
|
|
|
|
|
|
|
|
pub fn intersect(&self, other: &Line2) -> Region2 { |
|
|
|
|
// if the two lines are parallel...
|
|
|
|
|
let dirs = self.dir / other.dir; |
|
|
|
|
if relative_eq!(dirs.sin_angle(), 0.) { |
|
|
|
|
let dirs = self.dir.clone() - other.dir.clone(); |
|
|
|
|
/* |
|
|
|
|
if relative_eq!(dirs.sin(), 0.) { |
|
|
|
|
let starts = self.dir.to_rotation_matrix().inverse() * (other.start - self.start); |
|
|
|
|
return if relative_eq!(starts.y, 0.) { |
|
|
|
|
// and they are colinear
|
|
|
|
@ -123,17 +303,15 @@ impl Line2 {
@@ -123,17 +303,15 @@ impl Line2 {
|
|
|
|
|
// 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, a.dir, b.start, b.dir); |
|
|
|
|
let (a_c, a_s, b_c, b_s) = ( |
|
|
|
|
a_v.cos_angle(), |
|
|
|
|
a_v.sin_angle(), |
|
|
|
|
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 (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() |
|
|
|
|
+ a_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)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -159,15 +337,16 @@ impl Region<Point2> for Region2 {
@@ -159,15 +337,16 @@ impl Region<Point2> for Region2 {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn contains(&self, p: &Point2) -> bool { |
|
|
|
|
self.nearest(p).map_or(false, |n| relative_eq!(n, p)) |
|
|
|
|
self.nearest(p) |
|
|
|
|
.map_or(false, |n| true /*relative_eq!(n, p)*/) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn nearest(&self, p: &Point2) -> Option<Point2> { |
|
|
|
|
use Region2::*; |
|
|
|
|
match self { |
|
|
|
|
Empty => None, |
|
|
|
|
Full => Some(*p), |
|
|
|
|
Singleton(n) => Some(*n), |
|
|
|
|
Full => Some(p.clone()), |
|
|
|
|
Singleton(n) => Some(n.clone()), |
|
|
|
|
Line(line) => Some(line.nearest(p)), |
|
|
|
|
Union(r1, r2) => { |
|
|
|
|
use nalgebra::distance; |
|
|
|
@ -175,11 +354,11 @@ impl Region<Point2> for Region2 {
@@ -175,11 +354,11 @@ impl Region<Point2> for Region2 {
|
|
|
|
|
(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 |
|
|
|
|
} |
|
|
|
|
// if distance(p, &n1) <= distance(p, &n2) {
|
|
|
|
|
n1 |
|
|
|
|
// } else {
|
|
|
|
|
// n2
|
|
|
|
|
// }
|
|
|
|
|
}), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -204,14 +383,14 @@ impl Region2 {
@@ -204,14 +383,14 @@ impl Region2 {
|
|
|
|
|
(Full, r) | (r, Full) => r.clone(), |
|
|
|
|
(Singleton(n1), Singleton(n2)) => { |
|
|
|
|
if n1 == n2 { |
|
|
|
|
Singleton(*n1) |
|
|
|
|
Singleton(n1.clone()) |
|
|
|
|
} else { |
|
|
|
|
Empty |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
(Singleton(n), o) | (o, Singleton(n)) => { |
|
|
|
|
if o.contains(n) { |
|
|
|
|
Singleton(*n) |
|
|
|
|
Singleton(n.clone()) |
|
|
|
|
} else { |
|
|
|
|
Empty |
|
|
|
|
} |
|
|
|
|