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