Initial commit
This commit is contained in:
commit
96606cbac5
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
43
.vscode/launch.json
vendored
Normal file
43
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'cad_rs'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin=cad_rs",
|
||||||
|
"--package=cad_rs"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in executable 'cad_rs'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bin=cad_rs",
|
||||||
|
"--package=cad_rs"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
173
Cargo.lock
generated
Normal file
173
Cargo.lock
generated
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
[[package]]
|
||||||
|
name = "alga"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libm 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "approx"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cad_rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nalgebra 0.16.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloudabi"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matrixmultiply"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nalgebra"
|
||||||
|
version = "0.16.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"alga 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"matrixmultiply 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rawpointer"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum alga 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24bb00eeca59f2986c747b8c2f271d52310ce446be27428fc34705138b155778"
|
||||||
|
"checksum approx 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c57ff1a5b00753647aebbbcf4ea67fa1e711a65ea7a30eb90dbf07de2485aee"
|
||||||
|
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||||
|
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
|
"checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31"
|
||||||
|
"checksum generic-array 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8107dafa78c80c848b71b60133954b4a58609a3a1a5f9af037ecc7f67280f369"
|
||||||
|
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
|
||||||
|
"checksum libm 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "03c0bb6d5ce1b5cc6fd0578ec1cbc18c9d88b5b591a5c7c1d6c6175e266a0819"
|
||||||
|
"checksum matrixmultiply 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "dcad67dcec2d58ff56f6292582377e6921afdf3bfbd533e26fb8900ae575e002"
|
||||||
|
"checksum nalgebra 0.16.13 (registry+https://github.com/rust-lang/crates.io-index)" = "8e0799b53947b9c9048a1537f024f22f54701bbb75274f65955d081a87c0b739"
|
||||||
|
"checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8"
|
||||||
|
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
||||||
|
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
|
||||||
|
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
|
||||||
|
"checksum rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019"
|
||||||
|
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
||||||
|
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||||
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "cad_rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Alex Mikhalev <alexmikhalevalex@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nalgebra = "0.16"
|
386
src/main.rs
Normal file
386
src/main.rs
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
extern crate nalgebra;
|
||||||
|
|
||||||
|
mod math {
|
||||||
|
|
||||||
|
pub type Scalar = f64;
|
||||||
|
|
||||||
|
pub type Vec2 = nalgebra::Vector2<Scalar>;
|
||||||
|
pub type Point2 = nalgebra::Point2<Scalar>;
|
||||||
|
|
||||||
|
pub type Rot2 = nalgebra::UnitComplex<Scalar>;
|
||||||
|
|
||||||
|
pub trait Region<T> {
|
||||||
|
fn full() -> Self;
|
||||||
|
fn singleton(value: T) -> Self;
|
||||||
|
|
||||||
|
fn nearest(&self, value: &T) -> Option<T>;
|
||||||
|
fn contains(&self, value: &T) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Region1 {
|
||||||
|
Empty,
|
||||||
|
Singleton(Scalar),
|
||||||
|
Range(Scalar, Scalar),
|
||||||
|
Union(Box<Region1>, Box<Region1>),
|
||||||
|
Full,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Region<Scalar> for Region1 {
|
||||||
|
fn full() -> Self {
|
||||||
|
Region1::Full
|
||||||
|
}
|
||||||
|
|
||||||
|
fn singleton(value: Scalar) -> Self {
|
||||||
|
Region1::Singleton(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, n: &Scalar) -> bool {
|
||||||
|
use Region1::*;
|
||||||
|
match self {
|
||||||
|
Empty => false,
|
||||||
|
Singleton(n1) => *n1 == *n,
|
||||||
|
Range(l, u) => *l <= *n && *n <= *u,
|
||||||
|
Union(r1, r2) => r1.contains(n) || r2.contains(n),
|
||||||
|
Full => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nearest(&self, n: &Scalar) -> Option<Scalar> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
dir: Rot2,
|
||||||
|
extent: Region1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Line2 {
|
||||||
|
pub fn new(start: Point2, dir: Rot2, extent: Region1) -> Self {
|
||||||
|
Self { start, dir, extent }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(&self, t: Scalar) -> Point2 {
|
||||||
|
self.start + self.dir * Vec2::new(t, 0.)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect(&self, other: &Line2) -> Option<Point2> {
|
||||||
|
// if two lines are parallel
|
||||||
|
// TODO: epsilon?
|
||||||
|
if (self.dir * other.dir).sin_angle() == 0. {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
Some(b.evaluate(t_b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Region2 {
|
||||||
|
Empty,
|
||||||
|
// single point at 0
|
||||||
|
Singleton(Point2),
|
||||||
|
Line(Line2),
|
||||||
|
Union(Box<Region2>, Box<Region2>),
|
||||||
|
Full,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Region<Point2> for Region2 {
|
||||||
|
fn full() -> Self {
|
||||||
|
Region2::Full
|
||||||
|
}
|
||||||
|
|
||||||
|
fn singleton(value: Point2) -> Self {
|
||||||
|
Region2::Singleton(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, p: &Point2) -> bool {
|
||||||
|
use Region2::*;
|
||||||
|
self.nearest(p).map_or(false, |n| n == *p) // TODO: epsilon?
|
||||||
|
|
||||||
|
// match self {
|
||||||
|
// Empty => false,
|
||||||
|
// Singleton(n1) => *n1 == n,
|
||||||
|
// Line(_, _, _) => unimplemented!(),
|
||||||
|
// Union(r1, r2) => r1.contains(n) || r2.contains(n),
|
||||||
|
// Full => true,
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nearest(&self, p: &Point2) -> Option<Point2> {
|
||||||
|
use Region2::*;
|
||||||
|
match self {
|
||||||
|
Empty => None,
|
||||||
|
Full => Some(*p),
|
||||||
|
Singleton(n) => Some(*n),
|
||||||
|
Line(line) => {
|
||||||
|
// rotate angle 90 degrees
|
||||||
|
let perp_dir = line.dir * Rot2::from_cos_sin_unchecked(0., 1.);
|
||||||
|
let perp = Line2::new(*p, perp_dir, Region1::Full);
|
||||||
|
perp.intersect(line)
|
||||||
|
}
|
||||||
|
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 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 {
|
||||||
|
Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Singleton(n), o @ _) | (o @ _, Singleton(n)) => {
|
||||||
|
if o.contains(n) {
|
||||||
|
Singleton(*n)
|
||||||
|
} else {
|
||||||
|
Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Line(l1), Line(l2)) => match l1.intersect(l2) {
|
||||||
|
Some(p) => Singleton(p),
|
||||||
|
None => Empty,
|
||||||
|
},
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mod entity {
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::math::{Line2, Point2, Region, Region1, Region2, Rot2, Scalar};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Var<T: Clone, TRegion: Region<T>> {
|
||||||
|
value: T,
|
||||||
|
constraints: TRegion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone, TRegion: Region<T>> Var<T, TRegion> {
|
||||||
|
pub fn new(value: T, constraints: TRegion) -> Self {
|
||||||
|
Self { value, constraints }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_full(value: T) -> Self {
|
||||||
|
Self::new(value, TRegion::full())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_single(value: T) -> Self {
|
||||||
|
Self::new(value.clone(), TRegion::singleton(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constraints(&self) -> &TRegion {
|
||||||
|
&self.constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reconstrain(&mut self, new_constraints: TRegion) -> bool {
|
||||||
|
self.constraints = new_constraints;
|
||||||
|
if let Some(n) = self.constraints.nearest(&self.value) {
|
||||||
|
self.value = n;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScalarVar = Var<Scalar, Region1>;
|
||||||
|
type PointVar = Var<Point2, Region2>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Point {
|
||||||
|
pub pos: PointVar,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PointRef = Rc<RefCell<Point>>;
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
pub fn new_ref(pos: PointVar) -> PointRef {
|
||||||
|
Rc::new(RefCell::new(Point { pos }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Line {
|
||||||
|
p1: PointRef,
|
||||||
|
p2: PointRef,
|
||||||
|
len: ScalarVar,
|
||||||
|
dir: ScalarVar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod relation {
|
||||||
|
use crate::entity::{Point as PointEntity, PointRef};
|
||||||
|
use crate::math::{Line2, Point2, Region, Region1, Region2, Rot2, Scalar};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum ResolveResult {
|
||||||
|
Underconstrained,
|
||||||
|
Constrained,
|
||||||
|
Overconstrained,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResolveResult {
|
||||||
|
pub fn from_r2(r: &Region2) -> ResolveResult {
|
||||||
|
use Region2::*;
|
||||||
|
match r {
|
||||||
|
Empty => ResolveResult::Overconstrained,
|
||||||
|
Singleton(_) => ResolveResult::Constrained,
|
||||||
|
_ => ResolveResult::Constrained,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Relation {
|
||||||
|
fn resolve(&self) -> ResolveResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Coincident {
|
||||||
|
pub p1: PointRef,
|
||||||
|
pub p2: PointRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Relation for Coincident {
|
||||||
|
fn resolve(&self) -> ResolveResult {
|
||||||
|
use Region2::*;
|
||||||
|
let (mut p1, mut p2) = (self.p1.borrow_mut(), self.p2.borrow_mut());
|
||||||
|
let r = { p1.pos.constraints().intersect(p2.pos.constraints()) };
|
||||||
|
p1.pos.reconstrain(r.clone());
|
||||||
|
p2.pos.reconstrain(r.clone());
|
||||||
|
ResolveResult::from_r2(&r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PointAngle {
|
||||||
|
pub p1: PointRef,
|
||||||
|
pub p2: PointRef,
|
||||||
|
pub angle: Rot2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointAngle {
|
||||||
|
pub fn new(p1: PointRef, p2: PointRef, angle: Rot2) -> Self {
|
||||||
|
Self { p1, p2, angle }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_horizontal(p1: PointRef, p2: PointRef) -> Self {
|
||||||
|
Self::new(p1, p2, Rot2::from_cos_sin_unchecked(1., 0.))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_vertical(p1: PointRef, p2: PointRef) -> Self {
|
||||||
|
Self::new(p1, p2, Rot2::from_cos_sin_unchecked(0., 1.))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Relation for PointAngle {
|
||||||
|
fn resolve(&self) -> ResolveResult {
|
||||||
|
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.clone(), self.angle, Region1::Full));
|
||||||
|
let new_constraint = p2.pos.constraints().intersect(&line);
|
||||||
|
p2.pos.reconstrain(new_constraint);
|
||||||
|
ResolveResult::from_r2(p2.pos.constraints())
|
||||||
|
};
|
||||||
|
match (&mut p1.pos.constraints(), &mut p2.pos.constraints()) {
|
||||||
|
(Empty, _) | (_, Empty) => ResolveResult::Overconstrained,
|
||||||
|
(Singleton(p1), Singleton(p2)) => {
|
||||||
|
if p1.x == p2.x {
|
||||||
|
ResolveResult::Constrained
|
||||||
|
} else {
|
||||||
|
ResolveResult::Overconstrained
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Singleton(p), _) => constrain_line(p, &mut *p2),
|
||||||
|
(_, Singleton(p)) => constrain_line(p, &mut *p1),
|
||||||
|
_ => ResolveResult::Underconstrained,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
use entity::{Point, PointRef, Var};
|
||||||
|
use math::{Point2, Region2};
|
||||||
|
use relation::{Relation, ResolveResult};
|
||||||
|
|
||||||
|
println!("Hello, world!");
|
||||||
|
let origin = Point::new_ref(Var::new(
|
||||||
|
Point2::new(0., 0.),
|
||||||
|
Region2::Singleton(Point2::new(0., 0.)),
|
||||||
|
));
|
||||||
|
let p1 = Point::new_ref(Var::new(Point2::new(1., 1.), Region2::Full));
|
||||||
|
let p2 = Point::new_ref(Var::new(Point2::new(4., 4.), Region2::Full));
|
||||||
|
let p3 = Point::new_ref(Var::new(
|
||||||
|
Point2::new(2., 2.),
|
||||||
|
Region2::Singleton(Point2::new(2., 2.)),
|
||||||
|
));
|
||||||
|
let mut points: Vec<PointRef> = vec![origin.clone(), p1.clone(), p2.clone(), p3.clone()];
|
||||||
|
let print_points = |points: &Vec<PointRef>| {
|
||||||
|
println!(
|
||||||
|
"origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}",
|
||||||
|
points[0], points[1], points[2], points[3],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let c1 = relation::Coincident {
|
||||||
|
p1: origin.clone(),
|
||||||
|
p2: p1.clone(),
|
||||||
|
};
|
||||||
|
let c2 = relation::PointAngle::new_vertical(p1.clone(), p2.clone());
|
||||||
|
let c3 = relation::PointAngle::new_horizontal(p1.clone(), p2.clone());
|
||||||
|
let c4 = relation::PointAngle::new_horizontal(p1.clone(), p3.clone());
|
||||||
|
let mut relations: Vec<Box<dyn Relation>> = vec![Box::new(c1), Box::new(c2), Box::new(c3), Box::new(c4)];
|
||||||
|
let mut has_underconstrained = true;
|
||||||
|
while has_underconstrained {
|
||||||
|
has_underconstrained = false;
|
||||||
|
for r in &relations {
|
||||||
|
let rr = r.resolve();
|
||||||
|
if rr == ResolveResult::Underconstrained {
|
||||||
|
has_underconstrained = true;
|
||||||
|
}
|
||||||
|
println!("resolve result: {:?}", rr);
|
||||||
|
println!(
|
||||||
|
"origin, p1, p2, p3:\n {:?}\n {:?}\n {:?}\n {:?}",
|
||||||
|
origin, p1, p2, p3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user