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