Skip to content

Commit bcaad6e

Browse files
committed
more generic approach to colinear points in project_local_point
1 parent 1d117e5 commit bcaad6e

2 files changed

Lines changed: 70 additions & 64 deletions

File tree

src/query/epa/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,22 @@ mod test {
5858
point_dim(100.0, 0.0),
5959
point_dim(160.0, 0.0),
6060
);
61+
// point considered "inside" (on the line)
6162
let res = triangle.project_point_and_get_location(
6263
&Isometry::identity(),
6364
&point_dim(10.0, 0.0),
6465
false,
6566
);
6667
assert!(res.0.is_inside);
6768
res.0.point.iter().for_each(|p| assert!(p.is_finite()));
69+
70+
// point outside
6871
let res = triangle.project_point_and_get_location(
6972
&Isometry::identity(),
7073
&point_dim(10.0, 10.0),
7174
false,
7275
);
73-
// FIXME: This assert is currently failing !
74-
// False negative for inside on edge may be understandable due to floating points imprecisions,
75-
// But false positives for points outside is a problem. (maybe related to triangle orientation?)
76+
7677
assert!(res.0.is_inside == false);
7778
res.0.point.iter().for_each(|p| assert!(p.is_finite()));
7879
}

src/query/point/point_triangle.rs

Lines changed: 66 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::dbg;
2-
31
use crate::math::{Point, Real, Vector, DIM};
42
use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
53
use crate::shape::{FeatureId, Segment, Triangle, TrianglePointLocation};
@@ -57,60 +55,19 @@ impl PointQueryWithLocation for Triangle {
5755
type Location = TrianglePointLocation;
5856

5957
#[inline]
60-
fn project_local_point_and_get_location(
61-
&self,
62-
pt: &Point<Real>,
63-
solid: bool,
64-
) -> (PointProjection, Self::Location) {
58+
fn triangle(&self, pt: &Point<Real>, solid: bool) -> (PointProjection, Self::Location) {
6559
// To understand the ideas, consider reading the slides below
6660
// https://box2d.org/files/ErinCatto_GJK_GDC2010.pdf
6761

6862
let a = self.a;
6963
let b = self.b;
7064
let c = self.c;
7165

72-
// Additional checks for same point triangles
73-
// TODO: Check for flat triangles (collinear points) ?
74-
if a == b || b == c {
75-
let pos = Segment::new(a, c).project_local_point_and_get_location(pt, solid);
76-
77-
return (
78-
pos.0,
79-
match pos.1 {
80-
crate::shape::SegmentPointLocation::OnVertex(v) => {
81-
TrianglePointLocation::OnVertex(if v == 1 { 2 } else { 0 })
82-
}
83-
crate::shape::SegmentPointLocation::OnEdge(v) => TrianglePointLocation::OnEdge(
84-
// could be 0 or 1, but we're on a flat triangle which is a bit degenerate.
85-
1, v,
86-
),
87-
},
88-
);
89-
}
90-
if a == c {
91-
let pos = Segment::new(a, b).project_local_point_and_get_location(pt, solid);
92-
93-
return (
94-
pos.0,
95-
match pos.1 {
96-
crate::shape::SegmentPointLocation::OnVertex(v) => {
97-
TrianglePointLocation::OnVertex(v)
98-
}
99-
crate::shape::SegmentPointLocation::OnEdge(v) => TrianglePointLocation::OnEdge(
100-
// could be 1 or 2, but we're on a flat triangle which is a bit degenerate.
101-
0, v,
102-
),
103-
},
104-
);
105-
}
106-
10766
let ab = b - a;
10867
let ac = c - a;
10968
let ap = pt - a;
110-
std::dbg!(ab, ac, ap);
11169
let ab_ap = ab.dot(&ap);
11270
let ac_ap = ac.dot(&ap);
113-
std::dbg!(ab_ap, ac_ap);
11471

11572
if ab_ap <= 0.0 && ac_ap <= 0.0 {
11673
// Voronoï region of `a`.
@@ -120,7 +77,6 @@ impl PointQueryWithLocation for Triangle {
12077
let bp = pt - b;
12178
let ab_bp = ab.dot(&bp);
12279
let ac_bp = ac.dot(&bp);
123-
std::dbg!(bp, ab_bp, ac_bp);
12480

12581
if ab_bp >= 0.0 && ac_bp <= ab_bp {
12682
// Voronoï region of `b`.
@@ -130,7 +86,6 @@ impl PointQueryWithLocation for Triangle {
13086
let cp = pt - c;
13187
let ab_cp = ab.dot(&cp);
13288
let ac_cp = ac.dot(&cp);
133-
std::dbg!(cp, ab_cp, ac_cp);
13489

13590
if ac_cp >= 0.0 && ab_cp <= ac_cp {
13691
// Voronoï region of `c`.
@@ -256,23 +211,73 @@ impl PointQueryWithLocation for Triangle {
256211
);
257212
}
258213
ProjectionInfo::OnFace(face_side, va, vb, vc) => {
259-
// Voronoï region of the face.
260-
if DIM != 2 {
261-
// NOTE: in some cases, numerical instability
262-
// may result in the denominator being zero
263-
// when the triangle is nearly degenerate.
264-
if va + vb + vc != 0.0 {
265-
let denom = 1.0 / (va + vb + vc);
266-
let v = vb * denom;
267-
let w = vc * denom;
268-
let bcoords = [1.0 - v - w, v, w];
269-
let res = a + ab * v + ac * w;
270-
214+
let inv_denom = va + vb + vc;
215+
if inv_denom == 0.0 {
216+
// The triangle is degenerate (the three points are collinear).
217+
// So we find the longest segment and project point on it.
218+
219+
// TODO: this can probably be optimized using data we already have ?
220+
let length_sq_ab = ab.magnitude_squared();
221+
let length_sq_ac = ac.magnitude_squared();
222+
let length_sq_bc = bc.magnitude_squared();
223+
224+
if length_sq_ab >= length_sq_ac && length_sq_ab >= length_sq_bc {
225+
// AB is longest edge.
226+
let pos =
227+
Segment::new(a, b).project_local_point_and_get_location(pt, solid);
228+
return (
229+
pos.0,
230+
match pos.1 {
231+
crate::shape::SegmentPointLocation::OnVertex(v) => {
232+
TrianglePointLocation::OnVertex(v)
233+
}
234+
crate::shape::SegmentPointLocation::OnEdge(v) => {
235+
TrianglePointLocation::OnEdge(0, v)
236+
}
237+
},
238+
);
239+
} else if length_sq_ac >= length_sq_ab && length_sq_ac >= length_sq_bc {
240+
// AC is longest edge.
241+
let pos =
242+
Segment::new(a, c).project_local_point_and_get_location(pt, solid);
271243
return (
272-
compute_result(pt, res),
273-
TrianglePointLocation::OnFace(face_side as u32, bcoords),
244+
pos.0,
245+
match pos.1 {
246+
crate::shape::SegmentPointLocation::OnVertex(v) => {
247+
TrianglePointLocation::OnVertex(v)
248+
}
249+
crate::shape::SegmentPointLocation::OnEdge(v) => {
250+
TrianglePointLocation::OnEdge(1, v)
251+
}
252+
},
253+
);
254+
} else {
255+
// BC is longest edge.
256+
let pos =
257+
Segment::new(b, c).project_local_point_and_get_location(pt, solid);
258+
return (
259+
pos.0,
260+
match pos.1 {
261+
crate::shape::SegmentPointLocation::OnVertex(v) => {
262+
TrianglePointLocation::OnVertex(v)
263+
}
264+
crate::shape::SegmentPointLocation::OnEdge(v) => {
265+
TrianglePointLocation::OnEdge(2, v)
266+
}
267+
},
274268
);
275269
}
270+
} else if DIM != 2 {
271+
let denom = 1.0 / inv_denom;
272+
let v = vb * denom;
273+
let w = vc * denom;
274+
let bcoords = [1.0 - v - w, v, w];
275+
let res = a + ab * v + ac * w;
276+
277+
return (
278+
compute_result(pt, res),
279+
TrianglePointLocation::OnFace(face_side as u32, bcoords),
280+
);
276281
}
277282
}
278283
}
@@ -286,11 +291,11 @@ impl PointQueryWithLocation for Triangle {
286291
)
287292
} else {
288293
// We have to project on the closest edge.
294+
289295
// TODO: this might be optimizable.
290296
// TODO: be careful with numerical errors.
291297
let v = ab_ap / (ab_ap - ab_bp); // proj on ab = a + ab * v
292298
let w = ac_ap / (ac_ap - ac_cp); // proj on ac = a + ac * w
293-
dbg!((ac_bp - ab_bp) / (ac_bp - ab_bp + ab_cp - ac_cp));
294299
let u = (ac_bp - ab_bp) / (ac_bp - ab_bp + ab_cp - ac_cp); // proj on bc = b + bc * u
295300

296301
let bc = c - b;

0 commit comments

Comments
 (0)