1use std::collections::{HashMap, HashSet};
2
3use super::*;
4
5#[derive(Clone)]
6pub struct ConvexHull<
7 FS: OrderedRingStructure + FieldStructure,
8 SP: Borrow<AffineSpace<FS>> + Clone,
9> where
10 FS::Set: Hash,
11{
12 ambient_space: SP,
14 subspace: EmbeddedAffineSubspace<FS, SP, Rc<AffineSpace<FS>>>,
17 facets: Vec<OrientedSimplex<FS, Rc<AffineSpace<FS>>>>,
22 interior: Vec<Simplex<FS, Rc<AffineSpace<FS>>>>,
24 }
37
38impl<FS: OrderedRingStructure + FieldStructure, SP: Borrow<AffineSpace<FS>> + Clone> std::fmt::Debug
39 for ConvexHull<FS, SP>
40where
41 FS::Set: std::hash::Hash + std::fmt::Debug,
42{
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 f.debug_struct("ConvexHull")
45 .field(
46 "embedded_in_dim",
47 &self.subspace.embedded_space().affine_dimension(),
48 )
49 .field("facets", &self.facets)
50 .field("interior", &self.interior)
51 .finish()
52 }
53}
54
55impl<FS: OrderedRingStructure + FieldStructure, SP: Borrow<AffineSpace<FS>> + Clone>
66 ConvexHull<FS, SP>
67where
68 FS::Set: Hash,
69{
70 fn check(&self) -> Result<(), &'static str> {
71 assert_eq!(
72 self.subspace.ambient_space().borrow(),
73 self.ambient_space.borrow()
74 );
75
76 {
77 for facet in &self.facets {
78 if facet.ambient_space() != self.subspace.embedded_space() {
79 return Err("Facet must belong to the embedded subspace");
80 }
81 }
82 for spx in &self.interior {
84 if spx.ambient_space() != self.subspace.embedded_space() {
85 return Err("Interior simplex must belong to the embedded subspace");
86 }
87 if spx.n() != self.subspace.embedded_space().affine_dimension() {
88 return Err("Interior simplex must span the embedded subspace");
89 }
90 }
91 }
92
93 match self.subspace.borrow().embedded_space().affine_dimension() {
94 0 => {
95 if self.facets.len() != 0 {
96 return Err("Empty convex hull should have no facets");
97 }
98 if self.interior
99 != vec![Simplex::new(self.subspace.embedded_space(), vec![]).unwrap()]
100 {
101 return Err(
102 "Empty convex hull should have a single null simplex for its interior",
103 );
104 }
105 }
106 1 => {
107 if self.facets.len() != 1 {
108 return Err("0D convex hull should have one null facet");
109 }
110 if self.interior
111 != vec![
112 Simplex::new(
113 self.subspace.embedded_space(),
114 vec![Vector::construct(
115 self.subspace.embedded_space(),
116 |_| unreachable!(),
117 )],
118 )
119 .unwrap(),
120 ]
121 {
122 return Err("0D convex hull should have one point for its interior");
123 }
124 }
125 _ => {}
126 }
127
128 match self.subspace.borrow().embedded_space().linear_dimension() {
130 Some(_) => {
131 if self.facets.is_empty() {
132 return Err("Facets should be non-empty whenenver the subspace is non-empty");
133 }
134 }
135 None => {}
136 }
137
138 {
140 let mut ridges_count = HashMap::new();
141 for facet in &self.facets {
142 for ridge in facet.simplex().facets() {
143 if !ridges_count.contains_key(&ridge) {
144 ridges_count.insert(ridge.clone(), 0);
145 }
146 *ridges_count.get_mut(&ridge).unwrap() += 1;
147 }
148 }
149 if !ridges_count.into_iter().all(|(_ridge, count)| count == 2) {
150 return Err("Ridges of facets should each be shared between exactly two facets");
151 }
152 }
153
154 {
156 let mut all_pts = HashSet::new();
157 for facet in &self.facets {
158 for pt in facet.simplex().points() {
159 all_pts.insert(pt);
160 }
161 }
162 for spx in &self.interior {
163 for pt in spx.points() {
164 all_pts.insert(pt);
165 }
166 }
167 for facet in &self.facets {
168 for pt in &all_pts {
169 match facet.classify_point(pt) {
170 OrientationSide::Negative => {
171 return Err(
172 "Every point must be on the positive or neutral side of every facet",
173 );
174 }
175 OrientationSide::Neutral | OrientationSide::Positive => {}
176 }
177 }
178 }
179 }
180
181 Ok(())
182 }
183
184 pub fn new_empty(ambient_space: SP) -> Self {
185 let subspace = EmbeddedAffineSubspace::new_empty(ambient_space.clone());
186 Self {
187 ambient_space: ambient_space.clone(),
188 subspace: subspace.clone(),
189 facets: vec![],
190 interior: vec![Simplex::new(subspace.embedded_space(), vec![]).unwrap()],
191 }
192 }
193
194 pub fn new(ambient_space: SP, points: Vec<Vector<FS, SP>>) -> Self {
195 let mut ch = Self::new_empty(ambient_space);
196 for point in points {
197 ch.extend_by_point(point);
198 }
199 ch
200 }
201
202 pub fn affine_span_dimension(&self) -> usize {
203 self.subspace.embedded_space().affine_dimension()
204 }
205
206 pub fn from_simplex(spx: Simplex<FS, SP>) -> Self {
207 let ambient_space = spx.ambient_space();
208 let (subspace, embedded_pts) =
209 EmbeddedAffineSubspace::new_affine_span(ambient_space.clone(), spx.into_points())
210 .unwrap();
211 let embedded_spx = Simplex::new(subspace.embedded_space(), embedded_pts).unwrap();
212 Self {
213 ambient_space,
214 subspace,
215 facets: embedded_spx.oriented_facets(),
216 interior: vec![embedded_spx],
217 }
218 }
219
220 pub fn ambient_space(&self) -> SP {
221 self.subspace.ambient_space()
222 }
223
224 pub fn is_empty(&self) -> bool {
225 self.subspace.borrow().embedded_space().affine_dimension() == 0
226 }
227
228 pub fn defining_points(&self) -> HashSet<Vector<FS, SP>> {
229 match self.subspace.borrow().embedded_space().affine_dimension() {
230 0 => HashSet::new(),
231 1 => {
232 let (root, span) = self.subspace.get_root_and_span().unwrap();
235 debug_assert_eq!(span.len(), 0);
236 HashSet::from([root])
237 }
238 _ => {
239 let mut points = HashSet::new();
240 for facet in &self.facets {
241 for point in facet.simplex().points() {
242 points.insert(self.subspace.embed_point(point));
243 }
244 }
245 points
246 }
247 }
248 }
249
250 pub fn extend_by_point(&mut self, pt: Vector<FS, SP>) {
251 assert_eq!(pt.ambient_space().borrow(), self.ambient_space.borrow());
252 #[cfg(debug_assertions)]
253 self.check().unwrap();
254
255 match self.subspace.unembed_point(&pt) {
261 Some(subsp_pt) => {
262 let mut visible = vec![];
267 let mut hidden = vec![];
268 for facet in &self.facets {
269 match facet.classify_point(&subsp_pt) {
270 OrientationSide::Negative => {
271 visible.push(facet);
272 }
273 OrientationSide::Neutral | OrientationSide::Positive => {
274 hidden.push(facet);
275 }
276 }
277 }
278
279 let mut horizon = HashMap::new(); for facet in &visible {
281 let facet_simplex = facet.simplex();
282 for i in 0..facet_simplex.n() {
283 let (ridge, pt) = (facet_simplex.facet(i), &facet_simplex.points()[i]);
284 match horizon.contains_key(&ridge) {
285 true => {
286 horizon.remove(&ridge);
287 }
288 false => {
289 horizon.insert(ridge, (facet, pt));
290 }
291 }
292 }
293 }
294 #[cfg(debug_assertions)]
295 {
296 let mut horizon_alt = HashSet::new();
297 for facet in &hidden {
298 for ridge in facet.simplex().facets() {
299 match horizon_alt.contains(&ridge) {
300 true => {
301 horizon_alt.remove(&ridge);
302 }
303 false => {
304 horizon_alt.insert(ridge);
305 }
306 }
307 }
308 }
309 assert_eq!(
310 horizon.keys().map(|r| r.clone()).collect::<HashSet<_>>(),
311 horizon_alt
312 );
313 }
314
315 (self.facets, self.interior) = (
323 horizon
324 .iter()
325 .map(|(ridge, (_facet, facet_pt))| {
326 OrientedSimplex::new_with_positive_point(
327 self.subspace.embedded_space(),
328 {
329 let mut points = ridge.points().clone();
330 points.push(subsp_pt.clone());
331 points
332 },
333 *facet_pt,
334 )
335 .unwrap()
336 })
337 .chain(hidden.into_iter().map(|f| f.clone()))
338 .collect::<Vec<_>>(),
339 visible
340 .into_iter()
341 .map(|facet| {
342 Simplex::new(self.subspace.embedded_space(), {
343 let mut points = facet.simplex().points().clone();
344 points.push(subsp_pt.clone());
345 points
346 })
347 .unwrap()
348 })
349 .chain(self.interior.iter().map(|s| s.clone()))
350 .collect::<Vec<_>>(),
351 );
352 }
353 None => {
354 (self.subspace, self.facets, self.interior) = {
355 {
359 let (iota, new_subspace_embedding, pt_in_new_subspace) =
360 self.subspace.extend_dimension_by_point_unsafe(pt.clone());
361
362 let new_subspace = iota.ambient_space();
363 debug_assert_eq!(new_subspace, new_subspace_embedding.embedded_space());
364
365 let mut new_facets = vec![];
368 let mut new_interior = vec![];
369 for old_facet in &self.facets {
370 debug_assert_eq!(
371 old_facet.ambient_space(),
372 self.subspace.embedded_space()
373 );
374 new_facets.push(
375 OrientedSimplex::new_with_positive_point(
376 new_subspace.clone(),
377 {
378 let mut points = vec![];
379 for pt in old_facet.simplex().points() {
380 points.push(iota.embed_point(pt));
381 }
382 points.push(pt_in_new_subspace.clone());
383 points
384 },
385 &{
386 match old_facet.positive_point() {
389 Some(pos_pt) => iota.embed_point(&pos_pt),
390 None => {
391 debug_assert_eq!(
392 self.subspace
393 .embedded_space()
394 .affine_dimension(),
395 1
396 );
397 iota.embed_point(
398 &self
399 .subspace
400 .embedded_space()
401 .origin()
402 .unwrap(),
403 )
404 }
405 }
406 },
407 )
408 .unwrap(),
409 );
410 }
411 for old_interior in &self.interior {
412 debug_assert_eq!(
413 old_interior.ambient_space(),
414 self.subspace.embedded_space()
415 );
416 new_facets.push(
417 OrientedSimplex::new_with_positive_point(
418 new_subspace.clone(),
419 old_interior
420 .points()
421 .iter()
422 .map(|pt| iota.embed_point(pt))
423 .collect(),
424 &pt_in_new_subspace,
425 )
426 .unwrap(),
427 );
428 new_interior.push(
429 Simplex::new(new_subspace.clone(), {
430 let mut points = old_interior
431 .points()
432 .iter()
433 .map(|pt| iota.embed_point(pt))
434 .collect::<Vec<_>>();
435 points.push(pt_in_new_subspace.clone());
436 points
437 })
438 .unwrap(),
439 );
440 }
441
442 (new_subspace_embedding, new_facets, new_interior)
443 }
444 }
445 }
446 };
447
448 #[cfg(debug_assertions)]
449 self.check().unwrap();
450 }
451
452 fn embedded_interior_simplexes(&self) -> Vec<Simplex<FS, SP>> {
453 self.interior
454 .iter()
455 .map(|spx| {
456 Simplex::new(
457 self.ambient_space.clone(),
458 spx.points()
459 .iter()
460 .map(|pt| self.subspace.embed_point(pt))
461 .collect(),
462 )
463 .unwrap()
464 })
465 .collect()
466 }
467
468 fn embedded_facet_simplexes(&self) -> Vec<Simplex<FS, SP>> {
469 self.facets
470 .iter()
471 .map(|ospx| {
472 Simplex::new(
473 self.ambient_space.clone(),
474 ospx.simplex()
475 .points()
476 .iter()
477 .map(|pt| self.subspace.embed_point(pt))
478 .collect(),
479 )
480 .unwrap()
481 })
482 .collect()
483 }
484
485 pub fn as_simplicial_complex(
486 &self,
487 ) -> LabelledSimplicialComplex<FS, SP, InteriorBoundaryLabel> {
488 let boundary_simplexes = self
489 .embedded_facet_simplexes()
490 .into_iter()
491 .map(|spx| spx.sub_simplices_not_null())
492 .flatten()
493 .collect::<HashSet<_>>();
494
495 let all_simplexes = boundary_simplexes
496 .clone()
497 .into_iter()
498 .chain(
499 self.embedded_interior_simplexes()
500 .into_iter()
501 .map(|spx| spx.sub_simplices_not_null())
502 .flatten(),
503 )
504 .collect::<HashSet<_>>();
505
506 LabelledSimplicialComplex::new_labelled(
507 self.ambient_space().clone(),
508 all_simplexes
509 .into_iter()
510 .map(|spx| {
511 let label = match boundary_simplexes.contains(&spx) {
512 false => InteriorBoundaryLabel::Interior,
513 true => InteriorBoundaryLabel::Boundary,
514 };
515 (spx, label)
516 })
517 .collect(),
518 )
519 .unwrap()
520
521 }
543}
544
545#[derive(Clone)]
546struct ConvexHullWireframe<
547 FS: OrderedRingStructure + FieldStructure,
548 SP: Borrow<AffineSpace<FS>> + Clone,
549> {
550 ambient_space: SP,
551 points: Vec<Vector<FS, SP>>,
552 edges: Vec<(Vector<FS, SP>, Vector<FS, SP>)>,
553}
554
555impl<FS: OrderedRingStructure + FieldStructure, SP: Borrow<AffineSpace<FS>> + Clone>
556 ConvexHullWireframe<FS, SP>
557where
558 FS::Set: Hash,
559{
560 fn from_convex_hull(ch: &ConvexHull<FS, SP>) -> Self {
561 let mut outer_points = HashSet::new();
562 let mut outer_edges = HashSet::new();
563 for facet in &ch.facets {
564 for point in facet.simplex().points() {
565 outer_points.insert(point.clone());
566 }
567 for edge in facet.simplex().edges() {
568 outer_edges.insert(edge);
569 }
570 }
571
572 let mut outer_points = outer_points.into_iter().collect::<Vec<_>>();
574 let mut outer_edges = outer_edges
575 .into_iter()
576 .map(|line| {
577 let mut pts = line.into_points().into_iter();
578 (pts.next().unwrap(), pts.next().unwrap())
579 })
580 .collect::<Vec<_>>();
581
582 if ch.subspace.embedded_space().affine_dimension() == 1 {
584 let (_root, span) = ch.subspace.get_root_and_span().unwrap();
585 debug_assert_eq!(span.len(), 0);
586 debug_assert_eq!(outer_points.len(), 0);
587 outer_points.push(Vector::new(ch.subspace.embedded_space(), vec![]));
588 }
589
590 if ch.subspace.embedded_space().affine_dimension() == 2 {
592 debug_assert_eq!(outer_points.len(), 2);
593 debug_assert_eq!(outer_edges.len(), 0);
594 let mut pts = outer_points.iter();
595 outer_edges.push((
596 (*pts.next().unwrap()).clone(),
597 (*pts.next().unwrap()).clone(),
598 ));
599 }
600
601 Self {
602 ambient_space: ch.ambient_space(),
603 points: outer_points
604 .into_iter()
605 .map(|pt| ch.subspace.embed_point(&pt))
606 .collect(),
607 edges: outer_edges
608 .into_iter()
609 .map(|(pt1, pt2)| (ch.subspace.embed_point(&pt1), ch.subspace.embed_point(&pt2)))
610 .collect(),
611 }
612 }
613
614 fn into_convex_hull(self) -> ConvexHull<FS, SP> {
615 ConvexHull::new(self.ambient_space, self.points)
616 }
617
618 pub fn intersect_with_oriented_hyperplane(
619 &self,
620 hyperplane: &OrientedHyperplane<FS, SP>,
621 region: OrientationSide, ) -> Self {
623 let mut positive_points = HashSet::new();
630 let mut middle_points = HashSet::new();
631 let mut negative_points = HashSet::new();
632 for point in &self.points {
633 match hyperplane.classify_point(&point) {
634 OrientationSide::Positive => {
635 positive_points.insert(point.clone());
636 }
637 OrientationSide::Neutral => {
638 middle_points.insert(point.clone());
639 }
640 OrientationSide::Negative => {
641 negative_points.insert(point.clone());
642 }
643 }
644 }
645 let mut positive_edges = HashSet::new();
646 let mut negative_edges = HashSet::new();
647 for (a, b) in &self.edges {
648 match hyperplane.intersect_line(&a, &b) {
649 OrientedHyperplaneIntersectLineSegmentResult::PositivePositive
650 | OrientedHyperplaneIntersectLineSegmentResult::PositiveNeutral
651 | OrientedHyperplaneIntersectLineSegmentResult::NeutralPositive => {
652 positive_edges.insert((a.clone(), b.clone()));
653 }
654 OrientedHyperplaneIntersectLineSegmentResult::NegativeNegative
655 | OrientedHyperplaneIntersectLineSegmentResult::NegativeNeutral
656 | OrientedHyperplaneIntersectLineSegmentResult::NeutralNegative => {
657 negative_edges.insert((a.clone(), b.clone()));
658 }
659
660 OrientedHyperplaneIntersectLineSegmentResult::NeutralNeutral => {
661 }
663 OrientedHyperplaneIntersectLineSegmentResult::PositiveNegative {
664 intersection_point: m,
665 } => {
666 positive_edges.insert((a.clone(), m.clone()));
667 negative_edges.insert((m.clone(), b.clone()));
668 middle_points.insert(m);
669 }
670 OrientedHyperplaneIntersectLineSegmentResult::NegativePositive {
671 intersection_point: m,
672 } => {
673 positive_edges.insert((b.clone(), m.clone()));
674 negative_edges.insert((m.clone(), a.clone()));
675 middle_points.insert(m);
676 }
677 }
678 }
679
680 let ConvexHullWireframe {
681 ambient_space: _,
682 points: middle_points,
683 edges: middle_edges,
684 } = Self::from_convex_hull(&ConvexHull::new(
685 self.ambient_space.clone(),
686 middle_points.into_iter().collect(),
687 ));
688
689 if !positive_points.is_empty() && !negative_points.is_empty() {
690 debug_assert!(!middle_points.is_empty());
691 }
692
693 let mut points: HashSet<Vector<FS, SP>> = middle_points.into_iter().collect();
694 let mut edges: HashSet<(Vector<FS, SP>, Vector<FS, SP>)> =
695 middle_edges.into_iter().collect();
696
697 match region {
698 OrientationSide::Positive => {
699 for pt in positive_points {
700 points.insert(pt);
701 }
702 for edge in positive_edges {
703 edges.insert(edge);
704 }
705 }
706 OrientationSide::Neutral => {}
707 OrientationSide::Negative => {
708 for pt in negative_points {
709 points.insert(pt);
710 }
711 for edge in negative_edges {
712 edges.insert(edge);
713 }
714 }
715 }
716
717 Self {
718 ambient_space: self.ambient_space.clone(),
719 points: points.into_iter().collect(),
720 edges: edges.into_iter().collect(),
721 }
722 }
723}
724
725impl<FS: OrderedRingStructure + FieldStructure, SP: Borrow<AffineSpace<FS>> + Clone>
726 ConvexHull<FS, SP>
727where
728 FS::Set: Hash,
729{
730 pub fn intersect_with_oriented_hyperplane(
731 &self,
732 hyperplane: &OrientedHyperplane<FS, SP>,
733 region: OrientationSide, ) -> Self {
735 ConvexHullWireframe::from_convex_hull(self)
736 .intersect_with_oriented_hyperplane(hyperplane, region)
737 .into_convex_hull()
738 }
739
740 pub fn intersect_mut(&mut self, other: &Self) {
741 let ambient_space = self.ambient_space();
742 assert_eq!(ambient_space.borrow(), other.ambient_space().borrow());
743 match other.subspace.as_hyperplane_intersection() {
744 Some(hyperplanes) => {
745 for hyperplane in hyperplanes {
747 *self = self
748 .intersect_with_oriented_hyperplane(&hyperplane, OrientationSide::Neutral);
749 }
750 let embedded_self_in_other_subspace = &ConvexHull::new(
752 other.subspace.embedded_space(),
753 self.defining_points()
754 .into_iter()
755 .map(|point| other.subspace.unembed_point(&point).unwrap())
756 .collect(),
757 );
758 debug_assert_eq!(
759 embedded_self_in_other_subspace
760 .subspace
761 .embedded_space()
762 .affine_dimension(),
763 self.subspace.embedded_space().affine_dimension()
764 );
765 let mut embedded_self_in_other_subspace =
766 ConvexHullWireframe::from_convex_hull(&embedded_self_in_other_subspace);
767 for facet in &other.facets {
769 embedded_self_in_other_subspace = embedded_self_in_other_subspace
770 .intersect_with_oriented_hyperplane(
771 &facet.clone().into_oriented_hyperplane(),
772 OrientationSide::Positive,
773 );
774 }
775
776 *self = ConvexHull::new(
778 self.ambient_space(),
779 embedded_self_in_other_subspace
780 .into_convex_hull()
781 .defining_points()
782 .into_iter()
783 .map(|point| other.subspace.embed_point(&point))
784 .collect(),
785 );
786 }
787 None => {
788 *self = Self::new_empty(self.ambient_space.clone());
790 }
791 }
792 }
793
794 pub fn intersect(&self, other: &Self) -> Self {
795 let mut self_mut = self.clone();
796 self_mut.intersect_mut(other);
797 self_mut
798 }
799}
800#[cfg(test)]
801mod tests {
802 use algebraeon_nzq::rational::*;
803 use algebraeon_sets::structure::*;
804
805 use super::*;
806
807 #[test]
808 fn construct_convex_hull() {
809 let space = AffineSpace::new_linear(Rational::structure(), 2);
810 let mut ch = ConvexHull::new_empty(&space);
811 ch.extend_by_point(Vector::new(
813 &space,
814 vec![Rational::from(1), Rational::from(1)],
815 ));
816 ch.extend_by_point(Vector::new(
818 &space,
819 vec![Rational::from(1), Rational::from(1)],
820 ));
821 ch.extend_by_point(Vector::new(
823 &space,
824 vec![Rational::from(0), Rational::from(1) / Rational::from(2)],
825 ));
826 ch.extend_by_point(Vector::new(
828 &space,
829 vec![Rational::from(-1), Rational::from(0)],
830 ));
831 ch.extend_by_point(Vector::new(
833 &space,
834 vec![Rational::from(-1), Rational::from(0)],
835 ));
836 ch.extend_by_point(Vector::new(
838 &space,
839 vec![Rational::from(0), Rational::from(1) / Rational::from(2)],
840 ));
841 ch.extend_by_point(Vector::new(
843 &space,
844 vec![
845 Rational::from(1) / Rational::from(2),
846 Rational::from(3) / Rational::from(4),
847 ],
848 ));
849 ch.extend_by_point(Vector::new(
851 &space,
852 vec![Rational::from(2), Rational::from(-1)],
853 ));
854 ch.extend_by_point(Vector::new(
856 &space,
857 vec![Rational::from(0), Rational::from(-2)],
858 ));
859 ch.extend_by_point(Vector::new(
861 &space,
862 vec![Rational::from(2), Rational::from(0)],
863 ));
864 ch.extend_by_point(Vector::new(
866 &space,
867 vec![Rational::from(2), Rational::from(2)],
868 ));
869 }
870
871 #[test]
872 fn convex_hull_intersect_hyperplane() {
873 let space = AffineSpace::new_linear(Rational::structure(), 2);
874 let ch = ConvexHull::new(
875 &space,
876 vec![
877 Vector::new(&space, vec![Rational::from(1), Rational::from(1)]),
878 Vector::new(&space, vec![Rational::from(1), Rational::from(1)]),
879 Vector::new(
880 &space,
881 vec![Rational::from(0), Rational::from(1) / Rational::from(2)],
882 ),
883 Vector::new(&space, vec![Rational::from(-1), Rational::from(0)]),
884 Vector::new(&space, vec![Rational::from(-1), Rational::from(0)]),
885 Vector::new(
886 &space,
887 vec![Rational::from(0), Rational::from(1) / Rational::from(2)],
888 ),
889 Vector::new(
890 &space,
891 vec![
892 Rational::from(1) / Rational::from(2),
893 Rational::from(3) / Rational::from(4),
894 ],
895 ),
896 Vector::new(&space, vec![Rational::from(2), Rational::from(-1)]),
897 Vector::new(&space, vec![Rational::from(0), Rational::from(-2)]),
898 Vector::new(&space, vec![Rational::from(2), Rational::from(0)]),
899 Vector::new(&space, vec![Rational::from(2), Rational::from(2)]),
900 ],
901 );
902
903 let ohsp = OrientedSimplex::new_with_positive_point(
904 &space,
905 vec![
906 Vector::new(&space, vec![Rational::from(1), Rational::from(4)]),
907 Vector::new(&space, vec![Rational::from(1), Rational::from(-4)]),
908 ],
909 &Vector::new(&space, vec![Rational::from(10), Rational::from(0)]),
910 )
911 .unwrap()
912 .into_oriented_hyperplane();
913
914 let smaller_ch = ch.intersect_with_oriented_hyperplane(&ohsp, OrientationSide::Neutral);
915 println!("{:?}", smaller_ch);
916 let smaller_ch = ch.intersect_with_oriented_hyperplane(&ohsp, OrientationSide::Positive);
917 println!("{:?}", smaller_ch);
918 let smaller_ch = ch.intersect_with_oriented_hyperplane(&ohsp, OrientationSide::Negative);
919 println!("{:?}", smaller_ch);
920 }
921
922 #[test]
923 fn convex_hull_intersections() {
924 let space = AffineSpace::new_linear(Rational::structure(), 2);
925 {
927 let ch1 = ConvexHull::new(
928 &space,
929 vec![
930 Vector::new(&space, vec![Rational::from(1), Rational::from(1)]),
931 Vector::new(&space, vec![Rational::from(1), Rational::from(1)]),
932 Vector::new(
933 &space,
934 vec![Rational::from(0), Rational::from(1) / Rational::from(2)],
935 ),
936 Vector::new(&space, vec![Rational::from(-1), Rational::from(0)]),
937 Vector::new(&space, vec![Rational::from(-1), Rational::from(0)]),
938 Vector::new(
939 &space,
940 vec![Rational::from(0), Rational::from(1) / Rational::from(2)],
941 ),
942 Vector::new(
943 &space,
944 vec![
945 Rational::from(1) / Rational::from(2),
946 Rational::from(3) / Rational::from(4),
947 ],
948 ),
949 Vector::new(&space, vec![Rational::from(2), Rational::from(-1)]),
950 Vector::new(&space, vec![Rational::from(0), Rational::from(-2)]),
951 Vector::new(&space, vec![Rational::from(2), Rational::from(0)]),
952 Vector::new(&space, vec![Rational::from(2), Rational::from(2)]),
953 ],
954 );
955 let ch2 = ConvexHull::new(
956 &space,
957 vec![
958 Vector::new(&space, vec![Rational::from(-2), Rational::from(0)]),
959 Vector::new(&space, vec![Rational::from(3), Rational::from(0)]),
960 Vector::new(&space, vec![Rational::from(3), Rational::from(2)]),
961 Vector::new(&space, vec![Rational::from(0), Rational::from(-1)]),
962 ],
963 );
964 let ch3 = ch1.intersect(&ch2);
965 println!("{:?}", ch3);
966 }
967 {
969 let ch1 = ConvexHull::new(
970 &space,
971 vec![
972 Vector::new(&space, vec![Rational::from(1), Rational::from(1)]),
973 Vector::new(&space, vec![Rational::from(1), Rational::from(1)]),
974 Vector::new(
975 &space,
976 vec![Rational::from(0), Rational::from(1) / Rational::from(2)],
977 ),
978 Vector::new(&space, vec![Rational::from(-1), Rational::from(0)]),
979 Vector::new(&space, vec![Rational::from(-1), Rational::from(0)]),
980 Vector::new(
981 &space,
982 vec![Rational::from(0), Rational::from(1) / Rational::from(2)],
983 ),
984 Vector::new(
985 &space,
986 vec![
987 Rational::from(1) / Rational::from(2),
988 Rational::from(3) / Rational::from(4),
989 ],
990 ),
991 Vector::new(&space, vec![Rational::from(2), Rational::from(-1)]),
992 Vector::new(&space, vec![Rational::from(0), Rational::from(-2)]),
993 Vector::new(&space, vec![Rational::from(2), Rational::from(0)]),
994 Vector::new(&space, vec![Rational::from(2), Rational::from(2)]),
995 ],
996 );
997 let ch2 = ConvexHull::new(
998 &space,
999 vec![
1000 Vector::new(&space, vec![Rational::from(3), Rational::from(2)]),
1001 Vector::new(&space, vec![Rational::from(0), Rational::from(-1)]),
1002 ],
1003 );
1004 let ch3 = ch1.intersect(&ch2);
1005 println!("{:?}", ch3);
1006 }
1007 {
1009 let ch1 = ConvexHull::new(
1010 &space,
1011 vec![
1012 Vector::new(&space, vec![Rational::from(-2), Rational::from(-1)]),
1013 Vector::new(&space, vec![Rational::from(-1), Rational::from(1)]),
1014 ],
1015 );
1016 let ch2 = ConvexHull::new(
1017 &space,
1018 vec![
1019 Vector::new(&space, vec![Rational::from(1), Rational::from(1)]),
1020 Vector::new(&space, vec![Rational::from(-1), Rational::from(0)]),
1021 ],
1022 );
1023 let ch3 = ch1.intersect(&ch2);
1024 debug_assert_eq!(ch3.subspace.embedded_space().affine_dimension(), 0);
1025 }
1026 {
1028 let ch1 = ConvexHull::new(
1029 &space,
1030 vec![
1031 Vector::new(&space, vec![Rational::from(2), Rational::from(0)]),
1032 Vector::new(&space, vec![Rational::from(-2), Rational::from(-1)]),
1033 ],
1034 );
1035 let ch2 = ConvexHull::new(
1036 &space,
1037 vec![
1038 Vector::new(&space, vec![Rational::from(0), Rational::from(-1)]),
1039 Vector::new(&space, vec![Rational::from(1), Rational::from(1)]),
1040 ],
1041 );
1042 let ch3 = ch1.intersect(&ch2);
1043 debug_assert_eq!(ch3.subspace.embedded_space().affine_dimension(), 1);
1044 }
1045 }
1046
1047 #[test]
1048 fn convex_hull_from_simplex() {
1049 let space = AffineSpace::new_linear(Rational::structure(), 3);
1050 let p1 = Vector::new(
1051 &space,
1052 vec![Rational::from(4), Rational::from(2), Rational::from(2)],
1053 );
1054 let p2 = Vector::new(
1055 &space,
1056 vec![Rational::from(5), Rational::from(-3), Rational::from(3)],
1057 );
1058 let p3 = Vector::new(
1059 &space,
1060 vec![Rational::from(-5), Rational::from(6), Rational::from(2)],
1061 );
1062 let p4 = Vector::new(
1063 &space,
1064 vec![Rational::from(8), Rational::from(2), Rational::from(-9)],
1065 );
1066
1067 ConvexHull::from_simplex(Simplex::new(&space, vec![]).unwrap())
1068 .check()
1069 .unwrap();
1070 ConvexHull::from_simplex(Simplex::new(&space, vec![p1.clone()]).unwrap())
1071 .check()
1072 .unwrap();
1073 ConvexHull::from_simplex(Simplex::new(&space, vec![p1.clone(), p2.clone()]).unwrap())
1074 .check()
1075 .unwrap();
1076 ConvexHull::from_simplex(
1077 Simplex::new(&space, vec![p1.clone(), p2.clone(), p3.clone()]).unwrap(),
1078 )
1079 .check()
1080 .unwrap();
1081 ConvexHull::from_simplex(
1082 Simplex::new(&space, vec![p1.clone(), p2.clone(), p3.clone(), p4.clone()]).unwrap(),
1083 )
1084 .check()
1085 .unwrap();
1086 }
1087}