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. 241
      src/math/mod.rs
  5. 16
      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> { @@ -22,6 +22,10 @@ impl<T: Clone, TRegion: Region<T>> Var<T, TRegion> {
Self::new(value.clone(), TRegion::singleton(value))
}
pub fn val(&self) -> &T {
&self.value
}
pub fn constraints(&self) -> &TRegion {
&self.constraints
}

25
src/main.rs

@ -18,16 +18,21 @@ mod relation; @@ -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<PointRef> = vec![origin.clone(), p1.clone(), p2.clone(), p3.clone()];
let print_points = |points: &Vec<PointRef>| {
println!(
@ -44,7 +49,7 @@ fn main() { @@ -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<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 any_underconstrained = true;
let mut any_constrained = true;
@ -59,6 +64,14 @@ fn main() { @@ -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;

2
src/math/eqn.rs

@ -6,7 +6,7 @@ use crate::math::Scalar; @@ -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<Unknown>;

241
src/math/mod.rs

@ -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
}

16
src/math/ops.rs

@ -99,6 +99,13 @@ impl ops::Div<Unknown> for Expr { @@ -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 {
type Output = Expr;
fn add(self, rhs: Expr) -> Expr {
@ -181,4 +188,11 @@ impl ops::Div<Unknown> for Unknown { @@ -181,4 +188,11 @@ impl ops::Div<Unknown> for Unknown {
fn div(self, rhs: Unknown) -> Expr {
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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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,
}
}

Loading…
Cancel
Save