Browse Source

screwing around with equations in relations

eqn_relations
Alex Mikhalev 6 years ago
parent
commit
556719f52d
  1. 4
      src/entity.rs
  2. 25
      src/main.rs
  3. 2
      src/math/eqn.rs
  4. 239
      src/math/mod.rs
  5. 14
      src/math/ops.rs
  6. 39
      src/relation.rs

4
src/entity.rs

@ -22,6 +22,10 @@ impl<T: Clone, TRegion: Region<T>> Var<T, TRegion> {
Self::new(value.clone(), TRegion::singleton(value)) Self::new(value.clone(), TRegion::singleton(value))
} }
pub fn val(&self) -> &T {
&self.value
}
pub fn constraints(&self) -> &TRegion { pub fn constraints(&self) -> &TRegion {
&self.constraints &self.constraints
} }

25
src/main.rs

@ -18,16 +18,21 @@ mod relation;
fn main() { fn main() {
use entity::{Point, PointRef, Var}; use entity::{Point, PointRef, Var};
use math::Point2; use math::{Point2, eqn};
use relation::{Relation, ResolveResult}; use relation::{Relation, ResolveResult};
env_logger::init(); env_logger::init();
println!("Hello, world!"); println!("Hello, world!");
let origin = Point::new_ref(Var::new_single(Point2::new(0., 0.))); // let u1 = math::eqn::Unknown(1);
let p1 = Point::new_ref(Var::new_full(Point2::new(1., 1.))); // let u2 = math::eqn::Unknown(2);
let p2 = Point::new_ref(Var::new_full(Point2::new(4., 4.))); let u1 = eqn::Expr::from(1.);
let p3 = Point::new_ref(Var::new_full(Point2::new(2., 2.))); 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<PointRef> = vec![origin.clone(), p1.clone(), p2.clone(), p3.clone()]; let mut points: Vec<PointRef> = vec![origin.clone(), p1.clone(), p2.clone(), p3.clone()];
let print_points = |points: &Vec<PointRef>| { let print_points = |points: &Vec<PointRef>| {
println!( println!(
@ -44,7 +49,7 @@ fn main() {
let c3 = relation::PointAngle::new_horizontal(p2.clone(), p3.clone()); let c3 = relation::PointAngle::new_horizontal(p2.clone(), p3.clone());
let c4 = relation::AlignedDistance::new_vertical(p1.clone(), p2.clone(), 12.); let c4 = relation::AlignedDistance::new_vertical(p1.clone(), p2.clone(), 12.);
let mut relations: Vec<Box<dyn Relation>> = let mut relations: Vec<Box<dyn Relation>> =
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<Box<dyn Relation>> = Vec::new(); let mut constrained: Vec<Box<dyn Relation>> = Vec::new();
let mut any_underconstrained = true; let mut any_underconstrained = true;
let mut any_constrained = true; let mut any_constrained = true;
@ -59,6 +64,14 @@ fn main() {
"origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}", "origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}",
origin, p1, p2, p3 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 { match rr {
ResolveResult::Underconstrained => { ResolveResult::Underconstrained => {
any_underconstrained = true; any_underconstrained = true;

2
src/math/eqn.rs

@ -6,7 +6,7 @@ use crate::math::Scalar;
// an unknown variable with an id // an unknown variable with an id
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Unknown(i64); pub struct Unknown(pub i64);
pub type UnknownSet = BTreeSet<Unknown>; pub type UnknownSet = BTreeSet<Unknown>;

239
src/math/mod.rs

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

14
src/math/ops.rs

@ -99,6 +99,13 @@ impl ops::Div<Unknown> for Expr {
} }
} }
impl ops::Neg for Expr {
type Output = Expr;
fn neg(self) -> Expr {
Expr::new_neg(self)
}
}
impl ops::Add<Expr> for Unknown { impl ops::Add<Expr> for Unknown {
type Output = Expr; type Output = Expr;
fn add(self, rhs: Expr) -> Expr { fn add(self, rhs: Expr) -> Expr {
@ -182,3 +189,10 @@ impl ops::Div<Unknown> for Unknown {
Expr::new_div(self.into(), rhs.into()) Expr::new_div(self.into(), rhs.into())
} }
} }
impl ops::Neg for Unknown {
type Output = Expr;
fn neg(self) -> Expr {
Expr::new_neg(self.into())
}
}

39
src/relation.rs

@ -50,11 +50,11 @@ impl PointAngle {
} }
pub fn new_horizontal(p1: PointRef, p2: PointRef) -> Self { 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 { 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::*; use Region2::*;
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut()); let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
let constrain_line = |p1: &Point2, p2: &mut PointEntity| { 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); let new_constraint = p2.pos.constraints().intersect(&line);
p2.pos.reconstrain(new_constraint); p2.pos.reconstrain(new_constraint);
ResolveResult::from_r2(p2.pos.constraints()) ResolveResult::from_r2(p2.pos.constraints())
@ -73,12 +73,13 @@ impl Relation for PointAngle {
(Singleton(p1), Singleton(p2)) => { (Singleton(p1), Singleton(p2)) => {
// if the angle p1 and p2 form is parallel to self.angle, the result // if the angle p1 and p2 form is parallel to self.angle, the result
// will have a y component of 0 // will have a y component of 0
let r = self.angle.to_rotation_matrix().inverse() * (p2 - p1); let r = self.angle.clone().conj() * (p2.clone() - p1.clone());
if relative_eq!(r.y, 0.) { println!("angle.cos: {}", r.x);
// if relative_eq!(r.y, 0.) {
ResolveResult::Constrained ResolveResult::Constrained
} else { // } else {
ResolveResult::Overconstrained // ResolveResult::Overconstrained
} // }
} }
(Singleton(p), _) => constrain_line(p, &mut *p2), (Singleton(p), _) => constrain_line(p, &mut *p2),
(_, Singleton(p)) => constrain_line(p, &mut *p1), (_, 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 (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
let constrain_line = |p1: Point2, p2: &mut PointEntity| { let constrain_line = |p1: Point2, p2: &mut PointEntity| {
let angle = match self.axis { let angle = match self.axis {
Axis::Horizontal => Rot2::from_cos_sin_unchecked(0., 1.), Axis::Horizontal => Rot2::from_cos_sin_unchecked((0.).into(), (1.).into()),
Axis::Vertical => Rot2::from_cos_sin_unchecked(1., 0.), Axis::Vertical => Rot2::from_cos_sin_unchecked((1.).into(), (0.).into()),
}; };
let line = Region2::Line(Line2::new(p1, angle, Region1::Full)); let line = Region2::Line(Line2::new(p1, angle, Region1::Full));
let new_constraint = p2.pos.constraints().intersect(&line); let new_constraint = p2.pos.constraints().intersect(&line);
@ -133,25 +134,25 @@ impl Relation for AlignedDistance {
ResolveResult::from_r2(p2.pos.constraints()) ResolveResult::from_r2(p2.pos.constraints())
}; };
let offset = match self.axis { let offset = match self.axis {
Axis::Horizontal => Vec2::new(self.distance, 0.), Axis::Horizontal => Vec2::new(self.distance.into(), (0.).into()),
Axis::Vertical => Vec2::new(0., self.distance), Axis::Vertical => Vec2::new((0.).into(), self.distance.into()),
}; };
match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) { match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) {
(Empty, _) | (_, Empty) => ResolveResult::Overconstrained, (Empty, _) | (_, Empty) => ResolveResult::Overconstrained,
(Singleton(p1), Singleton(p2)) => { (Singleton(p1), Singleton(p2)) => {
let r = p2 - p1; let r = p2.clone() - p1.clone();
let d = match self.axis { let d = match self.axis {
Axis::Horizontal => r.x, Axis::Horizontal => r.x,
Axis::Vertical => r.y, Axis::Vertical => r.y,
}; };
if relative_eq!(d, self.distance) { // if relative_eq!(d, self.distance) {
ResolveResult::Constrained ResolveResult::Constrained
} else { // } else {
ResolveResult::Overconstrained // ResolveResult::Overconstrained
} // }
} }
(Singleton(pos), _) => constrain_line(pos + offset, &mut *p2), (Singleton(pos), _) => constrain_line(pos.clone() + offset, &mut *p2),
(_, Singleton(pos)) => constrain_line(pos - offset, &mut *p1), (_, Singleton(pos)) => constrain_line(pos.clone() - offset, &mut *p1),
_ => ResolveResult::Underconstrained, _ => ResolveResult::Underconstrained,
} }
} }

Loading…
Cancel
Save