From 556719f52d0350516d7136d6770fd546878454a6 Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Wed, 6 Feb 2019 01:27:47 -0800 Subject: [PATCH] screwing around with equations in relations --- src/entity.rs | 4 + src/main.rs | 25 +++-- src/math/eqn.rs | 2 +- src/math/mod.rs | 241 +++++++++++++++++++++++++++++++++++++++++------- src/math/ops.rs | 16 +++- src/relation.rs | 39 ++++---- 6 files changed, 269 insertions(+), 58 deletions(-) diff --git a/src/entity.rs b/src/entity.rs index 08ce862..c8bd284 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -22,6 +22,10 @@ impl> Var { Self::new(value.clone(), TRegion::singleton(value)) } + pub fn val(&self) -> &T { + &self.value + } + pub fn constraints(&self) -> &TRegion { &self.constraints } diff --git a/src/main.rs b/src/main.rs index 968b04e..3489d95 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,16 +18,21 @@ mod relation; fn main() { use entity::{Point, PointRef, Var}; - use math::Point2; + use math::{Point2, eqn}; use relation::{Relation, ResolveResult}; env_logger::init(); println!("Hello, world!"); - 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_full(Point2::new(2., 2.))); + // let u1 = math::eqn::Unknown(1); + // let u2 = math::eqn::Unknown(2); + let u1 = eqn::Expr::from(1.); + let u2 = eqn::Expr::from(1.); + let origin = Point::new_ref(Var::new_single(Point2::new((0.).into(), (0.).into()))); + // let p1 = Point::new_ref(Var::new_full(Point2::new((1.).into(), (1.).into()))); + let p1 = Point::new_ref(Var::new_single(Point2::new((u1).into(), (u2).into()))); + let p2 = Point::new_ref(Var::new_full(Point2::new((4.).into(), (4.).into()))); + let p3 = Point::new_ref(Var::new_full(Point2::new((2.).into(), (2.).into()))); let mut points: Vec = vec![origin.clone(), p1.clone(), p2.clone(), p3.clone()]; let print_points = |points: &Vec| { println!( @@ -44,7 +49,7 @@ fn main() { let c3 = relation::PointAngle::new_horizontal(p2.clone(), p3.clone()); let c4 = relation::AlignedDistance::new_vertical(p1.clone(), p2.clone(), 12.); let mut relations: Vec> = - vec![/*Box::new(c1),*/ Box::new(c2), Box::new(c3), Box::new(c4)]; + vec![/*Box::new(c1), */Box::new(c2), Box::new(c3), Box::new(c4)]; let mut constrained: Vec> = Vec::new(); let mut any_underconstrained = true; let mut any_constrained = true; @@ -59,6 +64,14 @@ fn main() { "origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}", origin, p1, p2, p3 ); + let mut pos = p2.borrow().pos.val().clone(); + pos.x = pos.x.distribute().simplify(); + pos.y = pos.y.distribute().simplify(); + println!("p2 pos: {}", pos); + let mut pos = p3.borrow().pos.val().clone(); + pos.x = pos.x.distribute().simplify(); + pos.y = pos.y.distribute().simplify(); + println!("p3 pos: {}", pos); match rr { ResolveResult::Underconstrained => { any_underconstrained = true; diff --git a/src/math/eqn.rs b/src/math/eqn.rs index cd463c4..ce57283 100644 --- a/src/math/eqn.rs +++ b/src/math/eqn.rs @@ -6,7 +6,7 @@ use crate::math::Scalar; // an unknown variable with an id #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Unknown(i64); +pub struct Unknown(pub i64); pub type UnknownSet = BTreeSet; diff --git a/src/math/mod.rs b/src/math/mod.rs index e593e7c..99ca5ff 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -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; +// pub type Point2 = nalgebra::Point2; +#[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 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 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 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 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 for Rot2 { + type Output = Vec2; + fn mul(self, rhs: Scalar) -> Vec2 { + Vec2 { + x: self.cos * rhs, + y: self.sin * rhs, + } + } +} + +impl std::ops::Mul 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; -pub type Point2 = nalgebra::Point2; +impl std::ops::Add 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; +impl std::ops::Sub 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 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; pub trait Region { fn full() -> Self; @@ -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 { 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 { // 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 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 { 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 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 { (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 } diff --git a/src/math/ops.rs b/src/math/ops.rs index e48823d..9e2859c 100644 --- a/src/math/ops.rs +++ b/src/math/ops.rs @@ -99,6 +99,13 @@ impl ops::Div for Expr { } } +impl ops::Neg for Expr { + type Output = Expr; + fn neg(self) -> Expr { + Expr::new_neg(self) + } +} + impl ops::Add for Unknown { type Output = Expr; fn add(self, rhs: Expr) -> Expr { @@ -181,4 +188,11 @@ impl ops::Div for Unknown { fn div(self, rhs: Unknown) -> Expr { Expr::new_div(self.into(), rhs.into()) } -} \ No newline at end of file +} + +impl ops::Neg for Unknown { + type Output = Expr; + fn neg(self) -> Expr { + Expr::new_neg(self.into()) + } +} diff --git a/src/relation.rs b/src/relation.rs index b32ea13..3b1ba0b 100644 --- a/src/relation.rs +++ b/src/relation.rs @@ -50,11 +50,11 @@ impl PointAngle { } pub fn new_horizontal(p1: PointRef, p2: PointRef) -> Self { - Self::new(p1, p2, Rot2::from_cos_sin_unchecked(1., 0.)) + Self::new(p1, p2, Rot2::from_cos_sin_unchecked((1.).into(), (0.).into())) } pub fn new_vertical(p1: PointRef, p2: PointRef) -> Self { - Self::new(p1, p2, Rot2::from_cos_sin_unchecked(0., 1.)) + Self::new(p1, p2, Rot2::from_cos_sin_unchecked((0.).into(), (1.).into())) } } @@ -63,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, self.angle, Region1::Full)); + let line = Region2::Line(Line2::new(p1.clone(), self.angle.clone(), Region1::Full)); let new_constraint = p2.pos.constraints().intersect(&line); p2.pos.reconstrain(new_constraint); ResolveResult::from_r2(p2.pos.constraints()) @@ -73,12 +73,13 @@ impl Relation for PointAngle { (Singleton(p1), Singleton(p2)) => { // 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.) { + let r = self.angle.clone().conj() * (p2.clone() - p1.clone()); + println!("angle.cos: {}", r.x); + // if relative_eq!(r.y, 0.) { ResolveResult::Constrained - } else { - ResolveResult::Overconstrained - } + // } else { + // ResolveResult::Overconstrained + // } } (Singleton(p), _) => constrain_line(p, &mut *p2), (_, Singleton(p)) => constrain_line(p, &mut *p1), @@ -124,8 +125,8 @@ impl Relation for AlignedDistance { 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.), + Axis::Horizontal => Rot2::from_cos_sin_unchecked((0.).into(), (1.).into()), + Axis::Vertical => Rot2::from_cos_sin_unchecked((1.).into(), (0.).into()), }; let line = Region2::Line(Line2::new(p1, angle, Region1::Full)); let new_constraint = p2.pos.constraints().intersect(&line); @@ -133,25 +134,25 @@ impl Relation for AlignedDistance { 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), + Axis::Horizontal => Vec2::new(self.distance.into(), (0.).into()), + Axis::Vertical => Vec2::new((0.).into(), self.distance.into()), }; match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) { (Empty, _) | (_, Empty) => ResolveResult::Overconstrained, (Singleton(p1), Singleton(p2)) => { - let r = p2 - p1; + let r = p2.clone() - p1.clone(); let d = match self.axis { Axis::Horizontal => r.x, Axis::Vertical => r.y, }; - if relative_eq!(d, self.distance) { + // if relative_eq!(d, self.distance) { ResolveResult::Constrained - } else { - ResolveResult::Overconstrained - } + // } else { + // ResolveResult::Overconstrained + // } } - (Singleton(pos), _) => constrain_line(pos + offset, &mut *p2), - (_, Singleton(pos)) => constrain_line(pos - offset, &mut *p1), + (Singleton(pos), _) => constrain_line(pos.clone() + offset, &mut *p2), + (_, Singleton(pos)) => constrain_line(pos.clone() - offset, &mut *p1), _ => ResolveResult::Underconstrained, } }