1pub mod marching_squares;
26pub mod marching_cubes;
27pub mod line;
28pub mod contour;
29
30use std::{cmp, collections::HashMap, ops::Index};
31
32use approx::relative_eq;
33use nalgebra::partial_max;
34use num::traits::{real::Real, Num, Pow, Zero};
35
36use wasm_bindgen::prelude::wasm_bindgen;
38
39#[cfg(target_family = "wasm")]
40use js_sys::{Array, ArrayBuffer, Float64Array};
41
42#[cfg(target_family = "wasm")]
43use wasm_bindgen::JsValue;
44
45use crate::console;
46
47#[derive(Debug, Clone, Copy, cmp::PartialEq)]
48pub struct Point<T = f32, const N: usize = 2>
49where
50 T: Num + cmp::PartialOrd + Copy,
51{
52 data: [T; N],
53}
54
55impl<T, const N: usize> Point<T, N>
56where
57 T: Num + cmp::PartialOrd + Copy,
58{
59 pub fn new(data: [T; N]) -> Point<T, N> {
60 Point { data }
61 }
62}
63
64#[cfg(target_family = "wasm")]
65impl<T, const N: usize> Point<T, N>
66where
67 T: Num + cmp::PartialOrd + Copy + Into<f64>,
68{
69 pub fn to_array(&self) -> Array {
70 let mut v = Array::new();
71 for i in 0..N {
72 v.push(&JsValue::from(Into::<f64>::into(self[i])));
73 }
74 v
75 }
76}
77
78pub enum PointLineSegmentRelation {
79 On,
80 OnRayAB,
81 OnRayBA,
82 Appart,
83}
84
85impl<T> Point<T, 2>
86where
87 T: num::Num + cmp::PartialOrd + Copy + Into<f64> + approx::AbsDiffEq,
88{
89 pub fn relative_to(
90 &self,
91 line_segment: &LineSegment<T, 2>,
92 epsilon: f64,
93 ) -> PointLineSegmentRelation {
94 let x0 = line_segment.p0[0];
95 let y0 = line_segment.p0[1];
96 let x1 = line_segment.p1[0];
97 let y1 = line_segment.p1[1];
98 let dx = x0 - x1;
99 let dy = y0 - y1;
100
101 if dx == T::zero() {
102 if approx::abs_diff_eq!(self[0], x0) {
104 if y0 < y1 {
105 if self[1] > y1 {
106 return PointLineSegmentRelation::OnRayAB;
107 } else if self[1] < y0 {
108 return PointLineSegmentRelation::OnRayBA;
109 } else {
110 return PointLineSegmentRelation::On;
111 }
112 } else if y0 > y1 {
113 if self[1] > y0 {
114 return PointLineSegmentRelation::OnRayBA;
115 } else if self[1] < y1 {
116 return PointLineSegmentRelation::OnRayAB;
117 } else {
118 return PointLineSegmentRelation::On;
119 }
120 } else {
121 unreachable!()
122 }
123 }
124 }
125
126 if dy == T::zero() {
127 if approx::abs_diff_eq!(self[1], y0) {
129 if x0 < x1 {
130 if self[0] > x1 {
131 return PointLineSegmentRelation::OnRayAB;
132 } else if self[0] < x0 {
133 return PointLineSegmentRelation::OnRayBA;
134 } else {
135 return PointLineSegmentRelation::On;
136 }
137 } else if x0 > x1 {
138 if self[0] > x0 {
139 return PointLineSegmentRelation::OnRayBA;
140 } else if self[0] < x1 {
141 return PointLineSegmentRelation::OnRayAB;
142 } else {
143 return PointLineSegmentRelation::On;
144 }
145 } else {
146 unreachable!()
147 }
148 }
149 }
150
151 let y: f64 = y0.into() + dy.into() / dx.into() * (self[0].into() - x0.into());
156 if approx::abs_diff_eq!(y, self[1].into(), epsilon = epsilon) {
157 if x0 < x1 {
158 if self[0] > x1 {
159 return PointLineSegmentRelation::OnRayAB;
160 } else if self[0] < x0 {
161 return PointLineSegmentRelation::OnRayBA;
162 } else {
163 return PointLineSegmentRelation::On;
164 }
165 } else if x0 > x1 {
166 if self[0] > x0 {
167 return PointLineSegmentRelation::OnRayBA;
168 } else if self[0] < x1 {
169 return PointLineSegmentRelation::OnRayAB;
170 } else {
171 return PointLineSegmentRelation::On;
172 }
173 } else {
174 unreachable!()
175 }
176 }
177 return PointLineSegmentRelation::Appart;
178 }
179
180 pub fn is_on(&self, line_segment: &LineSegment<T, 2>, epsilon: f64) -> bool {
181 if let PointLineSegmentRelation::On = self.relative_to(line_segment, epsilon) {
182 return true;
183 }
184 return false;
185 }
186}
187
188type Point2D<T> = Point<T, 2>;
189type Point3D<T> = Point<T, 3>;
190
191impl<T, const N: usize> Index<usize> for Point<T, N>
192where
193 T: Num + cmp::PartialOrd + Copy,
194{
195 type Output = T;
196
197 fn index(&self, index: usize) -> &Self::Output {
198 &self.data[index]
199 }
200}
201
202struct PointInPlace<'a, T = f32, const N: usize = 2>
203where
204 T: Num + cmp::PartialOrd + Copy,
205{
206 data: &'a [T; N],
207}
208
209impl<'a, T, const N: usize> PointInPlace<'a, T, N>
210where
211 T: Num + cmp::PartialOrd + Copy,
212{
213 pub fn new(data: &'a [T; N]) -> PointInPlace<'a, T, N> {
214 PointInPlace { data }
215 }
216}
217
218impl<'a, T, const N: usize> Index<usize> for PointInPlace<'a, T, N>
219where
220 T: Num + cmp::PartialOrd + Copy,
221{
222 type Output = T;
223
224 fn index(&self, index: usize) -> &Self::Output {
225 &self.data[index]
226 }
227}
228
229#[derive(Debug, Clone, Copy, cmp::PartialEq)]
230pub struct LineSegment<T = f32, const N: usize = 2>
231where
232 T: Num + cmp::PartialOrd + Copy,
233{
234 p0: Point<T, N>,
235 p1: Point<T, N>,
236}
237
238impl<T, const N: usize> LineSegment<T, N>
239where
240 T: Num + cmp::PartialOrd + Copy,
241{
242 pub fn new(p0: Point<T, N>, p1: Point<T, N>) -> LineSegment<T, N> {
243 LineSegment { p0, p1 }
244 }
245}
246
247impl<T, const N: usize> LineSegment<T, N>
248where
249 T: Num + cmp::PartialOrd + Copy + Into<f64>,
250{
251 #[cfg(target_family = "wasm")]
252 pub fn to_array(&self) -> Array {
253 let mut v = Array::new();
254 v.push(&self.p0.to_array());
255 v.push(&self.p1.to_array());
256 v
257 }
258}
259
260fn is_intersect_2d_impl<T>(p0: &[T; 2], p1: &[T; 2], p2: &[T; 2], p3: &[T; 2], epsilon: f64) -> bool
261where
262 T: num::Num + cmp::PartialOrd + Copy + Into<f64> + approx::AbsDiffEq,
263{
264 let x0 = p0[0];
265 let y0 = p0[1];
266 let x1 = p1[0];
267 let y1 = p1[1];
268 let x2 = p2[0];
269 let y2 = p2[1];
270 let x3 = p3[0];
271 let y3 = p3[1];
272 let dx0 = x0 - x1;
273 let dy0 = y0 - y1;
274 let dx1 = x2 - x3;
275 let dy1 = y2 - y3;
276
277 if approx::abs_diff_eq!(p0[0], p1[0]) && approx::abs_diff_eq!(p2[0], p3[0]) {
278 if approx::abs_diff_ne!(x0, x2) {
280 return false;
281 } else {
282 return !(partial_ord_max(y0, y1) < partial_ord_min(y2, y3)
283 || partial_ord_min(y0, y1) > partial_ord_max(y2, y3));
284 }
285 }
286
287 if approx::abs_diff_eq!(p0[1], p1[1]) && approx::abs_diff_eq!(p2[1], p3[1]) {
288 return !(partial_ord_max(x0, x1) < partial_ord_min(x2, x3)
290 || partial_ord_min(x0, x1) > partial_ord_max(x2, x3));
291 }
292
293 let y0_: f64 = y2.into() + dy1.into() / dx1.into() * (x0.into() - x2.into());
294 let y1_: f64 = y2.into() + dy1.into() / dx1.into() * (x1.into() - x2.into());
295 let y2_: f64 = y0.into() + dy0.into() / dx0.into() * (x2.into() - x0.into());
296 let y3_: f64 = y0.into() + dy0.into() / dx0.into() * (x3.into() - x0.into());
297 if (y0.into() > y0_ && y1.into() > y1_) || (y0.into() < y0_ && y1.into() < y1_) {
298 return false;
299 }
300 if (y2.into() > y2_ && y3.into() > y3_) || (y2.into() < y2_ && y3.into() < y3_) {
301 return false;
302 }
303 return true;
304}
305
306impl<T> LineSegment<T, 2>
307where
308 T: num::Num + cmp::PartialOrd + Copy + Into<f64> + approx::AbsDiffEq,
309{
310 pub fn is_intersect(&self, rhs: &LineSegment<T, 2>, epsilon: f64) -> bool {
311 is_intersect_2d_impl(
312 &self.p0.data,
313 &self.p1.data,
314 &rhs.p0.data,
315 &rhs.p1.data,
316 epsilon,
317 )
318 }
319}
320
321fn partial_ord_max<T>(a: T, b: T) -> T
322where
323 T: PartialOrd,
324{
325 if a > b {
326 a
327 } else {
328 b
329 }
330}
331
332fn partial_ord_min<T>(a: T, b: T) -> T
333where
334 T: PartialOrd,
335{
336 if a < b {
337 a
338 } else {
339 b
340 }
341}
342
343#[derive(Debug)]
344struct LineSegmentInPlace<'a, T = f32, const N: usize = 2>
345where
346 T: Num + cmp::PartialOrd + Copy,
347{
348 p0: &'a Point<T, N>,
349 p1: &'a Point<T, N>,
350}
351
352impl<'a, T, const N: usize> LineSegmentInPlace<'a, T, N>
353where
354 T: Num + cmp::PartialOrd + Copy,
355{
356 pub fn new(p0: &'a Point<T, N>, p1: &'a Point<T, N>) -> LineSegmentInPlace<'a, T, N> {
357 LineSegmentInPlace { p0, p1 }
358 }
359}
360
361impl<'a, T> LineSegmentInPlace<'a, T, 2>
362where
363 T: Num + cmp::PartialOrd + Copy + Into<f64> + approx::AbsDiffEq,
364{
365 pub fn is_intersect(&self, rhs: &LineSegmentInPlace<'a, T, 2>, epsilon: f64) -> bool {
366 is_intersect_2d_impl(
367 &self.p0.data,
368 &self.p1.data,
369 &rhs.p0.data,
370 &rhs.p1.data,
371 epsilon,
372 )
373 }
374}
375
376pub trait Distance {
377 type Output;
378 fn distance(&self, rhs: &Self) -> Self::Output;
379}
380
381impl<T, const N: usize> Distance for Point<T, N>
382where
383 T: Real,
384{
385 type Output = T;
386 fn distance(&self, rhs: &Self) -> Self::Output {
387 let sum: T = T::zero();
388 for i in 0..N {
389 sum == sum + (self[i] - rhs[i]).powi(2);
390 }
391 sum.sqrt()
392 }
393}
394
395struct Base<T = f32, const N: usize = 2> {
396 data: [[T; N]; N],
398}
399
400#[inline]
401pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
402 if relative_eq!(t, 0.) || t < 0. {
403 return a;
404 } else if relative_eq!(t, 1.) || t > 1. {
405 return b;
406 } else {
407 a + (b - a) * t
408 }
409}
410
411#[derive(Debug, Clone)]
412pub struct LineSegments<T = f32, const N: usize = 2>
413where
414 T: Copy + Num + cmp::PartialOrd,
415{
416 data: Vec<LineSegment<T, N>>,
417}
418
419impl<T, const N: usize> LineSegments<T, N>
420where
421 T: Copy + Num + cmp::PartialOrd,
422{
423 pub fn new() -> LineSegments<T, N> {
424 LineSegments {
425 data: Vec::<LineSegment<T, N>>::new(),
426 }
427 }
428
429 pub fn push(&mut self, line_segment: LineSegment<T, N>) {
430 self.data.push(line_segment);
431 }
432}
433
434
435#[cfg(test)]
436mod test {
437 use approx::{assert_abs_diff_eq, assert_relative_eq};
438
439 use super::*;
440 macro_rules! setup_line_segment {
441 ($p0: expr, $p1: expr, $p2: expr, $p3: expr, $epsilon: expr, $t: expr) => {
442 let p0 = Point2D::new($p0);
443 let p1 = Point2D::new($p1);
444 let p2 = Point2D::new($p2);
445 let p3 = Point2D::new($p3);
446 let line_segment0 = LineSegment::new(p0, p1);
447 let line_segment1 = LineSegment::new(p2, p3);
448 assert_eq!(line_segment0.is_intersect(&line_segment1, $epsilon), $t);
449 };
450 }
451 #[test]
452 fn test_line_segments() {
453 let p0 = Point2D::new([0, 0]);
454 let p1 = Point2D::<i32>::new([30, 30]);
456 let p2 = Point2D::<i32>::new([10, 15]);
457 let p3 = Point2D::<i32>::new([20, 21]);
458 let line_segment0 = LineSegment::new(p0, p1);
459 assert_eq!(p2.is_on(&line_segment0, 0.5), false);
460
461 let line_segment1 = LineSegment::new(p2, p3);
462 assert_eq!(line_segment0.is_intersect(&line_segment1, 0.5), false);
463
464 let p0 = Point2D::<f32>::new([0., 0.]);
465 let p1 = Point2D::<f32>::new([30., 30.]);
466 let p2 = Point2D::<f32>::new([15., 15.]);
467 let line_segment0 = LineSegment::new(p0, p1);
468 assert_eq!(p2.is_on(&line_segment0, 0.5), true);
469
470 setup_line_segment!([0, 0], [30, 30], [10, 15], [20, 21], 0.5, false);
471 setup_line_segment!([0., 0.], [30., 30.], [10., 15.], [20., 19.], 0.5, true);
472 setup_line_segment!([-10., -15.], [30., 25.], [10., 5.], [10., 19.], 0.1, true);
473 setup_line_segment!(
474 [-10., -15.],
475 [30., 25.],
476 [10., 5.01],
477 [10., 109.],
478 0.001,
479 false
480 );
481 setup_line_segment!([10., 0.], [10., 30.], [10., 30.001], [10., 50.], 0.5, false);
482 setup_line_segment!([10., 0.], [10., 30.], [10., 15.], [10., 19.], 0.5, true);
483
484 let p0 = Point2D::<f32>::new([1., 0.]);
485 let p1 = Point2D::<f32>::new([1., 30.]);
486 let p2 = Point2D::<f32>::new([1., 15.]);
487 let line_segment0 = LineSegment::new(p0, p1);
488 assert_eq!(p2.is_on(&line_segment0, 0.5), true);
489
490 let p0 = Point2D::<f32>::new([1., 0.]);
491 let p1 = Point2D::<f32>::new([1., 30.]);
492 let p2 = Point2D::<f32>::new([1., 30.001]);
493 let line_segment0 = LineSegment::new(p0, p1);
494 assert_eq!(p2.is_on(&line_segment0, 0.5), false);
495 }
496
497
498
499
500}