parry3d/query/split/split_segment.rs
1use crate::math::{Point, Real, UnitVector, Vector};
2use crate::query::SplitResult;
3use crate::shape::Segment;
4
5impl Segment {
6 /// Splits this segment along the given canonical axis.
7 ///
8 /// This will split the segment by a plane with a normal with it’s `axis`-th component set to 1.
9 /// The splitting plane is shifted wrt. the origin by the `bias` (i.e. it passes through the point
10 /// equal to `normal * bias`).
11 ///
12 /// # Result
13 /// Returns the result of the split. The first shape returned is the piece lying on the negative
14 /// half-space delimited by the splitting plane. The second shape returned is the piece lying on the
15 /// positive half-space delimited by the splitting plane.
16 pub fn canonical_split(&self, axis: usize, bias: Real, epsilon: Real) -> SplitResult<Self> {
17 // TODO: optimize this.
18 self.local_split(&Vector::ith_axis(axis), bias, epsilon)
19 }
20
21 /// Splits this segment by a plane identified by its normal `local_axis` and
22 /// the `bias` (i.e. the plane passes through the point equal to `normal * bias`).
23 pub fn local_split(
24 &self,
25 local_axis: &UnitVector<Real>,
26 bias: Real,
27 epsilon: Real,
28 ) -> SplitResult<Self> {
29 self.local_split_and_get_intersection(local_axis, bias, epsilon)
30 .0
31 }
32
33 /// Split a segment with a plane.
34 ///
35 /// This returns the result of the splitting operation, as well as
36 /// the intersection point (and barycentric coordinate of this point)
37 /// with the plane. The intersection point is `None` if the plane is
38 /// parallel or near-parallel to the segment.
39 pub fn local_split_and_get_intersection(
40 &self,
41 local_axis: &UnitVector<Real>,
42 bias: Real,
43 epsilon: Real,
44 ) -> (SplitResult<Self>, Option<(Point<Real>, Real)>) {
45 let dir = self.b - self.a;
46 let a = bias - local_axis.dot(&self.a.coords);
47 let b = local_axis.dot(&dir);
48 let bcoord = a / b;
49 let dir_norm = dir.norm();
50
51 if relative_eq!(b, 0.0)
52 || bcoord * dir_norm <= epsilon
53 || bcoord * dir_norm >= dir_norm - epsilon
54 {
55 if a >= 0.0 {
56 (SplitResult::Negative, None)
57 } else {
58 (SplitResult::Positive, None)
59 }
60 } else {
61 let intersection = self.a + dir * bcoord;
62 let s1 = Segment::new(self.a, intersection);
63 let s2 = Segment::new(intersection, self.b);
64 if a >= 0.0 {
65 (SplitResult::Pair(s1, s2), Some((intersection, bcoord)))
66 } else {
67 (SplitResult::Pair(s2, s1), Some((intersection, bcoord)))
68 }
69 }
70 }
71}