1use std;
4use std::collections::{BTreeSet, VecDeque};
5use std::sync::Arc;
6use approx;
7
8use crate::*;
9use super::*;
10
11use geometry::mesh::VertexEdgeTriangleMesh;
12use geometry::mesh::edge_triangle::{self, Edge, Triangle, TriangleKey};
13
14#[derive(Clone, Debug, Eq, PartialEq)]
16#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
17pub struct Hull2 <S> {
18 points : Vec <Point2 <S>>
21}
22
23#[derive(Clone, Debug, Eq, PartialEq)]
25#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
26pub struct Hull3 <S> {
27 points : Vec <Point3 <S>>
28}
29
30impl <S> Hull2 <S> {
31 pub fn from_points (points : &[Point2 <S>]) -> Option <Self> where S : Real {
41 Self::from_points_indices (points).map (|indices|{
42 let points = indices.iter().map (|i| points[*i as usize]).collect();
43 Hull2 { points }
44 })
45 }
46
47 #[inline]
49 pub fn points (&self) -> &[Point2 <S>] {
50 &self.points
51 }
52
53 #[inline]
54 pub fn numcast <T> (&self) -> Option <Hull2 <T>> where
55 S : num::NumCast + num::ToPrimitive + Copy,
56 T : num::NumCast
57 {
58 let points = self.points.iter().map (|p| p.numcast())
59 .collect::<Option <Vec <Point2 <T>>>>()?;
60 Some (Hull2 { points })
61 }
62
63 #[expect(clippy::missing_asserts_for_indexing)]
64 fn from_points_indices (points : &[Point2 <S>]) -> Option <Vec <u32>> where S : Real {
65 use std::cmp::Ordering;
68 match points.len() {
69 0 => return None,
70 1 => return Some (vec![0]),
71 2 => {
72 let v = if points[0] == points[1] {
73 vec![0]
74 } else {
75 vec![0, 1]
76 };
77 return Some (v)
78 }
79 _ => {} }
81 let mut indexed_points = points.iter().copied().enumerate()
82 .collect::<Vec <(usize, Point2 <S>)>>();
83 let start_index = indexed_points.iter().min_by (|a, b|{
85 let cmp = a.1.0.y.partial_cmp (&b.1.0.y).unwrap();
86 if cmp == Ordering::Equal {
87 a.1.0.x.partial_cmp (&b.1.0.x).unwrap()
88 } else {
89 cmp
90 }
91 }).unwrap().0;
92 let start_point = indexed_points[start_index].1;
93 indexed_points.sort_by (|a, b| {
95 if a.0 == start_index {
96 return Ordering::Less
97 }
98 if b.0 == start_index {
99 return Ordering::Greater
100 }
101 let angle_a = (a.1.0.y - start_point.0.y)
102 .atan2 (a.1.0.x - start_point.0.x);
103 let angle_b = (b.1.0.y - start_point.0.y)
104 .atan2 (b.1.0.x - start_point.0.x);
105 let angle_cmp = angle_a.partial_cmp (&angle_b).unwrap();
106 if angle_cmp == Ordering::Equal {
107 let dist_a = (a.1.0.x - start_point.0.x).powi (2) +
109 (a.1.0.y - start_point.0.y).powi (2);
110 let dist_b = (b.1.0.x - start_point.0.x).powi (2) +
111 (b.1.0.y - start_point.0.y).powi (2);
112 dist_a.partial_cmp (&dist_b).unwrap()
113 } else {
114 angle_cmp
115 }
116 });
117 let mut stack : Vec <u32> = Vec::new();
119 for (point_index, point) in indexed_points {
120 while stack.len() >= 2 {
121 let top = stack[stack.len() - 1];
122 let second = stack[stack.len() -2];
123 let p1 = points[second as usize];
124 let p2 = points[top as usize];
125 let p3 = point;
126 let det = Matrix2 {
127 cols: vector2 (p2 - p1, p3 - p1)
128 }.determinant();
129 if det <= S::zero() {
130 stack.pop();
131 } else {
132 break
133 }
134 }
135 #[expect(clippy::cast_possible_truncation)]
136 stack.push (point_index as u32)
137 }
138 Some (stack)
139 }
140}
141impl <S> Primitive <S, Point2 <S>> for Hull2 <S> where
142 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes
143{
144 fn translate (&mut self, displacement : Vector2 <S>) {
145 self.points.iter_mut().for_each (|p| *p += displacement);
146 }
147 fn scale (&mut self, scale : Positive <S>) {
148 self.points.iter_mut().for_each (|p| p.0 *= *scale);
149 }
150 fn support (&self, direction : NonZero2 <S>) -> (Point2 <S>, S) {
151 support2 (self.points(), direction)
152 }
153}
154impl <S : Real> From <simplex2::Segment <S>> for Hull2 <S> {
155 fn from (segment : simplex2::Segment <S>) -> Self {
156 Hull2::from_points (segment.points().as_slice()).unwrap()
157 }
158}
159impl <S : Real> From <simplex2::Triangle <S>> for Hull2 <S> {
160 fn from (triangle : simplex2::Triangle <S>) -> Self {
161 Hull2::from_points (triangle.points().as_slice()).unwrap()
162 }
163}
164impl <S : Real> From <Simplex2 <S>> for Hull2 <S> {
165 fn from (simplex : Simplex2 <S>) -> Self {
166 match simplex {
167 Simplex2::Segment (segment) => Hull2::from_points (segment.points().as_slice()),
168 Simplex2::Triangle (triangle) => Hull2::from_points (triangle.points().as_slice())
169 }.unwrap()
170 }
171}
172
173impl <S> Hull3 <S> {
174 pub fn from_points (points : &[Point3 <S>]) -> Option <Self> where
175 S : Real + approx::RelativeEq <Epsilon = S>
176 {
177 Self::from_points_with_mesh (points).map (|x| x.0)
178 }
179
180 pub fn from_points_with_mesh (points : &[Point3 <S>])
181 -> Option <(Self, Arc <VertexEdgeTriangleMesh>)>
182 where S : Real + approx::RelativeEq <Epsilon = S> {
183 let point_hull = |points : Vec <Point3 <S>>| {
188 debug_assert_eq!(points.len(), 1);
189 ( Hull3 { points },
190 Arc::new (VertexEdgeTriangleMesh::with_vertex (edge_triangle::Vertex::new (0)))
191 )
192 };
193 let line_hull = |points : Vec <Point3 <S>>| {
194 debug_assert_eq!(points.len(), 2);
195 ( Hull3 { points },
196 Arc::new (VertexEdgeTriangleMesh::with_edge (Edge::new (0, 1)))
197 )
198 };
199 match points.len() {
200 0 => return None,
201 n if n < 3 => {
202 let mut points = points.to_vec();
203 points.dedup();
204 match points.len() {
205 1 => return Some (point_hull (points)),
206 2 => return Some (line_hull (points)),
207 _ => unreachable!()
208 }
209 }
210 _ => {}
211 }
212 let get_point = |i : u32| points[i as usize];
213 let collect_points = |indices : &[u32]|
214 indices.iter().copied().map (get_point).collect();
215 let sorted = {
216 #[expect(clippy::cast_possible_truncation)]
217 let mut sorted = (0..points.len() as u32).collect::<Vec<_>>();
218 sorted.sort_by (|a, b|
219 Point3::partial_cmp (&get_point (*a), &get_point (*b)).unwrap());
220 sorted.dedup_by_key (|i| get_point(*i));
221 sorted
222 };
223 let mut hull = Vec::with_capacity (sorted.len());
224 hull.push (sorted[0]); let mut current = 1;
226 let mut dimension = 0;
227 for i in &sorted[current..] {
229 if get_point (hull[0]) != get_point (*i) {
230 dimension = 1;
231 break
232 }
233 current += 1;
234 }
235 debug_assert_eq!(hull.len(), 1);
236 if dimension == 0 {
237 let points = collect_points (hull.as_slice());
238 return Some (point_hull (points))
239 }
240 hull.push (sorted[current]); current += 1;
243 for i in &sorted[current..] {
244 if !colinear_3d (get_point (hull[0]), get_point (hull[1]), get_point (*i)) {
245 dimension = 2;
246 break
247 }
248 hull.push (sorted[current]);
249 current += 1;
250 }
251 if hull.len() > 2 {
252 hull.sort_by (|a, b|
254 Point3::partial_cmp (&get_point (*a), &get_point (*b)).unwrap());
255 hull.drain (1..hull.len()-1);
256 }
257 debug_assert_eq!(hull.len(), 2);
258 if dimension == 1 {
259 let points = collect_points (hull.as_slice());
260 return Some (line_hull (points))
261 }
262 hull.push (sorted[current]); current += 1;
265 while current < sorted.len() {
266 if !coplanar_3d (
267 get_point (hull[0]), get_point (hull[1]), get_point (hull[2]),
268 get_point (sorted[current])
269 ) {
270 let p = get_point (sorted[current]);
280 let mut actually_coplanar = false;
281 'outer: for i in 0..hull.len() {
283 for j in i+1..hull.len() {
284 if colinear_3d (get_point (hull[i]), get_point (hull[j]), p) {
285 actually_coplanar = true;
286 break 'outer;
287 }
288 }
289 }
290 if !actually_coplanar {
291 let mut best_norm_sq = S::zero();
294 let mut best : Option <(usize, usize, usize)> = None;
295 for i in 0..hull.len() {
296 for j in i+1..hull.len() {
297 for k in j+1..hull.len() {
298 let a = get_point (hull[i]);
299 let b = get_point (hull[j]);
300 let c = get_point (hull[k]);
301 let n = (b - a).cross (c - a);
302 let n_sq = n.magnitude_squared();
303 if n_sq > best_norm_sq {
304 best_norm_sq = n_sq;
305 best = Some ((i, j, k));
306 }
307 }
308 }
309 }
310 if let Some ((i, j, k)) = best
311 && coplanar_3d (
312 get_point (hull[i]), get_point (hull[j]), get_point (hull[k]), p
313 )
314 {
315 actually_coplanar = true;
316 }
317 }
318 if !actually_coplanar {
319 dimension = 3;
320 break
321 }
322 }
324 hull.push (sorted[current]);
325 current += 1;
326 }
327 if hull.len() > 3 {
328 let (i_ref, j_ref, k_ref) = {
334 let mut best_norm_sq = S::zero();
335 let mut best = (0, 1, 2);
336 for i in 0..hull.len() {
337 for j in i+1..hull.len() {
338 for k in j+1..hull.len() {
339 let a = get_point (hull[i]);
340 let b = get_point (hull[j]);
341 let c = get_point (hull[k]);
342 let n = (b - a).cross (c - a);
343 let n_sq = n.magnitude_squared();
344 if n_sq > best_norm_sq {
345 best_norm_sq = n_sq;
346 best = (i, j, k);
347 }
348 }
349 }
350 }
351 best
352 };
353 let v0 = get_point (hull[i_ref]);
356 let v1 = get_point (hull[j_ref]);
357 let v2 = get_point (hull[k_ref]);
358 let diff1 = v1 - v0;
359 let diff2 = v2 - v0;
360 let mut normal = diff1.cross (diff2);
361 let signs = normal.sigvec();
362 let c;
363 debug_assert_eq!(signs.len(), 3);
364 normal = normal.map (|s| s.abs());
365 if normal.x > normal.y {
366 if normal.x > normal.z {
367 if signs[0] > S::zero() {
368 c = [1, 2];
369 } else {
370 c = [2, 1];
371 }
372 } else {
373 if signs[2] > S::zero() {
374 c = [0, 1];
375 } else {
376 c = [1, 0];
377 }
378 }
379 } else {
380 if normal.y > normal.z {
381 if signs[1] > S::zero() {
382 c = [2, 0];
383 } else {
384 c = [0, 2];
385 }
386 } else {
387 if signs[2] > S::zero() {
388 c = [0, 1];
389 } else {
390 c = [1, 0];
391 }
392 }
393 }
394 let projections = hull.iter().copied()
395 .map (|i| point2 (get_point (i).0[c[0]], get_point (i).0[c[1]]))
396 .collect::<Vec <_>>();
397 let hull2_indices = Hull2::from_points_indices (projections.as_slice()).unwrap();
398 hull = hull2_indices.iter().map (|i| hull[*i as usize]).collect::<Vec <_>>();
399 }
400 if dimension == 2 {
401 let points = collect_points (hull.as_slice());
402 debug_assert!(points.len() >= 3);
406 let mut mesh = VertexEdgeTriangleMesh::default();
407 #[expect(clippy::cast_possible_truncation)]
408 for i in 1u32..points.len() as u32 - 1 {
409 mesh.insert (0, i, i+1);
410 }
411 return Some ((Hull3 { points }, Arc::new (mesh)))
412 }
413 let plane_side = |a, b, c, d| {
415 let [s0, s1, s2, s3] = [a, b, c, d].map (get_point);
416 plane_side_3d (s0, s1, s2, s3)
417 };
418 let sign = plane_side (hull[0], hull[1], hull[2], sorted[current]);
419 let mut mesh = VertexEdgeTriangleMesh::default();
420 let mut h0 = hull[0];
421 let mut h1;
422 if sign > S::zero() {
423 for i in 1..hull.len()-1 {
424 h1 = hull[i];
425 let h2 = hull[i+1];
426 let inserted = mesh.insert (h0, h2, h1);
427 debug_assert!(inserted.is_some());
428 }
429 h0 = sorted[current];
430 let mut i1 = hull.len() - 1;
431 let mut i2 = 0;
432 while i2 < hull.len() {
433 h1 = hull[i1];
434 let h2 = hull[i2];
435 let inserted = mesh.insert (h0, h1, h2);
436 debug_assert!(inserted.is_some());
437 i1 = i2;
439 i2 += 1;
440 }
441 } else {
442 for i in 1..hull.len()-1 {
443 h1 = hull[i];
444 let h2 = hull[i+1];
445 let inserted = mesh.insert (h0, h1, h2);
446 debug_assert!(inserted.is_some());
447 }
448 h0 = sorted[current];
449 let mut i1 = hull.len() - 1;
450 let mut i2 = 0;
451 while i2 < hull.len() {
452 h1 = hull[i1];
453 let h2 = hull[i2];
454 let inserted = mesh.insert (h0, h2, h1);
455 debug_assert!(inserted.is_some());
456 i1 = i2;
458 i2 += 1;
459 }
460 }
461 let mut terminator : Vec <[u32; 2]> = Vec::new();
462 let mut to_remove : Vec <TriangleKey> = Vec::new();
463 current += 1;
464 while current < sorted.len() {
465 let vertex = mesh.get_vertex (h0).unwrap();
466 h1 = sorted[current];
467 let mut visible = VecDeque::<TriangleKey>::new();
468 let mut visited = BTreeSet::<TriangleKey>::new();
469 for triangle_key in vertex.adjacent_triangles().iter() {
470 let triangle = mesh.get_triangle (triangle_key).unwrap();
471 let sign = plane_side (
472 triangle.vertices()[0], triangle.vertices()[1], triangle.vertices()[2], h1);
473 if sign > S::zero() {
474 visible.push_back (*triangle_key);
475 visited.insert (*triangle_key);
476 break
477 }
478 }
479 debug_assert!(visible.len() > 0);
480 debug_assert!(terminator.is_empty());
481 debug_assert!(to_remove.is_empty());
483 while visible.len() > 0 {
489 let triangle_key = visible.pop_front().unwrap();
490 let triangle = mesh.get_triangle (&triangle_key).copied().unwrap();
491 to_remove.push (triangle_key);
492 for (i, adjacent_key) in triangle.adjacent_triangles().iter().enumerate() {
493 if let Some (key) = adjacent_key {
494 let adjacent = mesh.get_triangle (key).unwrap();
495 if plane_side (
496 adjacent.vertices()[0], adjacent.vertices()[1], adjacent.vertices()[2], h1
497 ) <= S::zero() {
498 terminator.push (
499 [triangle.vertices()[i], triangle.vertices()[(i + 1) % 3]]);
500 } else if visited.insert (*key) {
501 visible.push_back (*key);
502 }
503 }
504 }
505 }
510 let any_degenerate = terminator.iter().any (|edge|
521 colinear_3d (get_point (edge[0]), get_point (edge[1]), get_point (h1)));
522 if any_degenerate {
523 terminator.clear();
524 to_remove.clear();
525 current += 1;
526 continue
527 }
528 #[expect(clippy::iter_with_drain)]
531 for triangle_key in to_remove.drain(..) {
532 let triangle = *mesh.get_triangle (&triangle_key).unwrap();
533 let removed = mesh.remove (
535 triangle.vertices()[0], triangle.vertices()[1], triangle.vertices()[2]);
536 debug_assert!(removed);
537 }
538 #[expect(clippy::iter_with_drain)]
540 for edge in terminator.drain(..) {
541 let inserted = mesh.insert (edge[0], edge[1], h1);
542 debug_assert!(inserted.is_some());
543 }
544 h0 = h1;
545 current += 1;
546 }
547 let points = mesh.vertices().keys().copied().map (get_point).collect();
548 mesh.reindex();
549 Some ((Hull3 { points }, Arc::new (mesh)))
550 }
551 #[inline]
552 pub fn points (&self) -> &[Point3 <S>] {
553 &self.points
554 }
555 #[inline]
556 pub fn edge (&self, edge : &Edge) -> Segment3 <S> where S : OrderedRing {
557 Segment3::from_array (edge.vertices().map(|i| self.points[i as usize])).unwrap()
558 }
559 #[inline]
560 pub fn triangle (&self, triangle : &Triangle) -> Triangle3 <S> where
561 S : OrderedField + Sqrt + approx::RelativeEq <Epsilon = S>
562 {
563 Triangle3::from_array (triangle.vertices().map(|i| self.points[i as usize])).unwrap()
564 }
565 #[inline]
566 pub fn numcast <T> (&self) -> Option <Hull3 <T>> where
567 S : num::NumCast + num::ToPrimitive + Copy,
568 T : num::NumCast
569 {
570 let points = self.points.iter().map (|p| p.numcast())
571 .collect::<Option <Vec <Point3 <T>>>>()?;
572 Some (Hull3 { points })
573 }
574
575}
576impl <S> Primitive <S, Point3 <S>> for Hull3 <S> where
577 S : Real + num::real::Real + MaybeSerDes
578{
579 fn translate (&mut self, displacement : Vector3 <S>) {
580 self.points.iter_mut().for_each (|p| *p += displacement);
581 }
582 fn scale (&mut self, scale : Positive <S>) {
583 self.points.iter_mut().for_each (|p| p.0 *= *scale);
584 }
585 fn support (&self, direction : NonZero3 <S>) -> (Point3 <S>, S) {
586 support3 (self.points(), direction)
587 }
588}
589impl <S> From <simplex3::Segment <S>> for Hull3 <S> where
590 S : Real + approx::RelativeEq <Epsilon = S>
591{
592 fn from (segment : simplex3::Segment <S>) -> Self {
593 Hull3::from_points (segment.points().as_slice()).unwrap()
594 }
595}
596impl <S> From <simplex3::Triangle <S>> for Hull3 <S> where
597 S : Real + approx::RelativeEq <Epsilon = S>
598{
599 fn from (triangle : simplex3::Triangle <S>) -> Self {
600 Hull3::from_points (triangle.points().as_slice()).unwrap()
601 }
602}
603impl <S> From <simplex3::Tetrahedron <S>> for Hull3 <S> where
604 S : Real + approx::RelativeEq <Epsilon = S>
605{
606 fn from (tetrahedron : simplex3::Tetrahedron <S>) -> Self {
607 Hull3::from_points (tetrahedron.points().as_slice()).unwrap()
608 }
609}
610impl <S> From <Simplex3 <S>> for Hull3 <S> where
611 S : Real + approx::RelativeEq <Epsilon = S>
612{
613 fn from (simplex : Simplex3 <S>) -> Self {
614 match simplex {
615 Simplex3::Segment (segment) =>
616 Hull3::from_points (segment.points().as_slice()),
617 Simplex3::Triangle (triangle) =>
618 Hull3::from_points (triangle.points().as_slice()),
619 Simplex3::Tetrahedron (tetrahedron) =>
620 Hull3::from_points (tetrahedron.points().as_slice())
621 }.unwrap()
622 }
623}
624
625#[cfg(test)]
626mod tests {
627 use super::*;
628
629 #[test]
630 fn hull2() {
631 use crate::*;
632 let points : Vec <Point2 <f32>> = [
634 [ 1.0, 1.0],
635 [ 1.0, 1.0]
636 ].map (Point2::from).into_iter().collect();
637 let hull = Hull2::from_points (points.as_slice()).unwrap();
638 assert_eq!(hull.points(), &[[1.0, 1.0].into()]);
639 let points : Vec <Point2 <f32>> = [
641 [ 0.0, 0.0],
642 [ 1.0, 3.0],
643 [ 2.0, -3.0],
644 [-3.0, -1.0]
645 ].map (Point2::from).into_iter().collect();
646 let hull = Hull2::from_points (points.as_slice()).unwrap();
647 let points : Vec <Point2 <f32>> = [
649 [ 2.0, -3.0],
650 [ 1.0, 3.0],
651 [-3.0, -1.0]
652 ].map (Point2::from).into_iter().collect();
653 assert_eq!(hull.points(), points);
654 let points : Vec <Point2 <f32>> = [
656 [ 0.0, 2.0],
657 [-2.0, -2.0],
658 [ 0.0, -2.0],
659 [ 2.0, -2.0]
660 ].map (Point2::from).into_iter().collect();
661 let hull = Hull2::from_points (points.as_slice()).unwrap();
662 let points : Vec <Point2 <f32>> = [
664 [-2.0, -2.0],
665 [ 2.0, -2.0],
666 [ 0.0, 2.0]
667 ].map (Point2::from).into_iter().collect();
668 assert_eq!(hull.points(), points);
669 let points : Vec <Point2 <f32>> = [
671 [ 0.0, 6.0],
673 [ 1.0, 5.0],
674 [ 2.0, 4.0],
675 [ 3.0, 3.0],
676 [ 3.0, 1.0],
677 [ 3.0, -1.0],
678 [ 3.0, -3.0],
679 [ 1.0, -3.0],
680 [-1.0, -3.0],
681 [-3.0, -3.0],
682 [-3.0, -1.0],
683 [-3.0, 1.0],
684 [-3.0, 3.0],
685 [-2.0, 4.0],
686 [-1.0, 5.0],
687 [-1.0, 2.0],
689 [ 2.0, -1.0],
690 [ 0.0, 3.0],
691 [-2.0, -2.0]
692 ].map (Point2::from).into_iter().collect();
693 let hull = Hull2::from_points (points.as_slice()).unwrap();
694 let points : Vec <Point2 <f32>> = [
696 [-3.0, -3.0],
697 [ 3.0, -3.0],
698 [ 3.0, 3.0],
699 [ 0.0, 6.0],
700 [-3.0, 3.0]
701 ].map (Point2::from).into_iter().collect();
702 assert_eq!(hull.points(), points);
703 }
704
705 #[test]
706 fn hull3() {
707 use rand::{RngExt, SeedableRng};
708 use rand_xorshift::XorShiftRng;
709 use rand_distr::Distribution;
710 let points = [
712 [-1.0, -4.0, -1.0],
713 [-1.0, -4.0, -1.0]
714 ].map (Point3::<f32>::from);
715 let hull = Hull3::from_points (points.as_slice()).unwrap();
716 assert_eq!(hull.points(), &[
717 [-1.0, -4.0, -1.0]
718 ].map (Into::into));
719 let points = [
721 [-1.0, -4.0, -1.0],
722 [ 0.0, 0.0, 0.0],
723 [ 1.0, 4.0, 1.0]
724 ].map (Point3::<f32>::from);
725 let hull = Hull3::from_points (points.as_slice()).unwrap();
726 assert_eq!(hull.points(), &[
727 [-1.0, -4.0, -1.0],
728 [ 1.0, 4.0, 1.0]
729 ].map (Into::into));
730 let points = [
732 [-1.0, -4.0, 0.0],
733 [ 2.0, 2.0, 0.0],
734 [-1.0, -1.0, 0.0],
735 [-4.0, -1.0, 0.0],
736 [ 0.0, 2.0, 0.0]
737 ].map (Point3::<f32>::from);
738 let hull = Hull3::from_points (points.as_slice()).unwrap();
739 assert_eq!(hull.points(), &[
740 [-1.0, -4.0, 0.0],
741 [ 2.0, 2.0, 0.0],
742 [ 0.0, 2.0, 0.0],
743 [-4.0, -1.0, 0.0]
744 ].map (Into::into));
745 let points = [
747 [-1.0, -4.0, -1.0],
748 [ 2.0, 2.0, 2.0],
749 [-4.0, -1.0, 2.0],
750 [ 0.0, 2.0, -3.0]
751 ].map (Point3::<f32>::from);
752 let hull = Hull3::from_points (points.as_slice()).unwrap();
753 assert_eq!(hull.points(), &[
754 [-1.0, -4.0, -1.0],
755 [ 2.0, 2.0, 2.0],
756 [-4.0, -1.0, 2.0],
757 [ 0.0, 2.0, -3.0]
758 ].map (Into::into));
759 let points = [
760 [ 1.0, -1.0, -1.0],
761 [ 1.0, -1.0, 1.0],
762 [ 1.0, 1.0, -1.0],
763 [ 1.0, 1.0, 1.0],
764 [-1.0, -1.0, -1.0],
765 [-1.0, -1.0, 1.0],
766 [-1.0, 1.0, -1.0],
767 [-1.0, 1.0, 1.0]
768 ].map (Point3::<f32>::from);
769 let hull = Hull3::from_points (points.as_slice()).unwrap();
770 assert_eq!(hull.points(), points.as_slice());
771 let points = [
773 [-1.0, -4.0, -1.0],
774 [ 2.0, 2.0, 2.0],
775 [-4.0, -1.0, 2.0],
776 [ 0.0, 2.0, -3.0],
777 [ 0.1, 0.2, 0.3],
778 [-0.1, -0.2, -0.3],
779 [-0.1, 0.2, 0.3]
780 ].map (Point3::<f32>::from);
781 let hull = Hull3::from_points (points.as_slice()).unwrap();
782 assert_eq!(hull.points(), &[
783 [-1.0, -4.0, -1.0],
784 [ 2.0, 2.0, 2.0],
785 [-4.0, -1.0, 2.0],
786 [ 0.0, 2.0, -3.0]
787 ].map (Into::into));
788 let mut rng = XorShiftRng::seed_from_u64 (0);
792 let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
793 let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
794 for _ in 0..500 {
795 let _aabb = Aabb3::with_minmax (
796 [-5.0, -5.0, -5.0].into(),
797 [ 5.0, 5.0, 5.0].into()).unwrap();
798 let mut points = std::iter::repeat_with (
799 || point3 (randf (&mut rng), randf (&mut rng), randf (&mut rng))
800 ).take (3).collect::<Vec<_>>();
802 let npoints = rng.random_range (4..50);
803 let mut rand_vec =
804 || vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
805 for _ in 0..npoints {
806 points.push (points.last().unwrap() + rand_vec());
807 }
809 let (hull, mesh) = Hull3::from_points_with_mesh (&points).unwrap();
810 for triangle in mesh.triangles().values() {
811 let _triangle = hull.triangle (triangle);
812 }
813 }
814 }
815}