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