use sorted_vec::partial::ReverseSortedVec;
use crate::object;
use crate::math::*;
use super::Proximity;
#[derive(Clone, Copy, Debug, PartialEq)]
struct SimplexMinkowski {
pub points : [PointMinkowski; 4],
pub npoints : u8
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
struct PointMinkowski {
pub point_a : Point3 <f64>,
pub point_b : Point3 <f64>
}
impl SimplexMinkowski {
const fn push (&mut self, point : PointMinkowski) {
self.points[self.npoints as usize] = point;
self.npoints += 1;
}
}
impl PointMinkowski {
fn point (self) -> Point3 <f64> {
Point3 (self.point_a - self.point_b)
}
fn support <A, B> (direction : NonZero3 <f64>, object_a : &A, object_b : &B)
-> (Self, f64)
where A : object::Bounded, B : object::Bounded {
let (point_a, _) = object_a.to_primitive().support (direction);
let (point_b, _) = object_b.to_primitive().support (-direction);
let minkowski = PointMinkowski { point_a, point_b };
(minkowski, minkowski.point().0.dot (*direction))
}
}
impl Proximity {
pub fn query_gjk <A, B> (object_a : &A, object_b : &B) -> Self where
A : object::Bounded, B : object::Bounded
{
const TOLERANCE : f64 = 0.000_000_000_01;
const TOLERANCE_SQUARED : f64 = TOLERANCE * TOLERANCE;
let mut search_dir = NonZero3::unchecked (vector3 (1.0, 1.0, 1.0));
let (support, _) = PointMinkowski::support (search_dir, object_a, object_b);
let mut near = support.point();
let mut distance_squared = near.0.magnitude_squared();
let mut lowest_distance_sq = distance_squared;
let mut simplex = SimplexMinkowski {
points: [support,
PointMinkowski::default(), PointMinkowski::default(), PointMinkowski::default()],
npoints: 1
};
search_dir = -NonZero3::unchecked (support.point().0);
let mut lowest_unique_count = 0;
let mut last_distance_sq = f64::MAX;
let noprogress_limit = 3;
let mut noprogress_count = 0;
let mut unique_count = 0;
loop {
let (support, _) = PointMinkowski::support (search_dir, object_a, object_b);
let vnew = support.point() - near;
let dot_new = vnew.dot (*search_dir);
if dot_new <= TOLERANCE {
break
}
simplex.push (support);
let mut overlap = false;
if simplex.npoints == 4 {
let [a, b, c, d] = simplex.points.map (PointMinkowski::point);
let ab = b - a;
let ac = c - a;
let ad = d - a;
let da = a - d;
let db = b - d;
let dc = c - d;
let ab_x_ac = ab.cross (ac);
let ab_x_ac_dot_ad = ab_x_ac.dot (ad);
let nd = -d.0;
let dab;
let dac;
#[expect(clippy::useless_let_if_seq)]
let dbc;
if ab_x_ac_dot_ad > 0.0 {
dab = db.cross (da);
dac = da.cross (dc);
dbc = dc.cross (db);
} else {
dab = da.cross (db);
dac = dc.cross (da);
dbc = db.cross (dc);
}
let check1 = dab.dot (nd) > 0.000000001;
let check2 = dac.dot (nd) > 0.000000001;
let check3 = dbc.dot (nd) > 0.000000001;
if check1 && check2 && check3 {
overlap = true
} else {
let line = geometry::Segment3::noisy (Point3::<f64>::origin(), near).into();
if let Some (triangle) = geometry::Triangle3::new (d, a, b)
&& geometry::intersect::line_triangle (line, triangle).is_some()
{
simplex.points[2] = simplex.points[3];
} else {
if let Some (triangle) = geometry::Triangle3::new (d, a, c)
&& geometry::intersect::line_triangle (line, triangle).is_some()
{
simplex.points[1] = simplex.points[3];
} else {
if let Some (triangle) = geometry::Triangle3::new (d, b, c)
&& geometry::intersect::line_triangle (line, triangle).is_some()
{
simplex.points[0] = simplex.points[3];
} else {
unreachable!("should have intersected new simplex")
}
}
}
simplex.npoints = 3;
}
}
if !overlap {
match simplex.npoints {
1 => {
near = simplex.points[0].point();
distance_squared = near.0.magnitude_squared();
if distance_squared < TOLERANCE_SQUARED {
overlap = true
} else {
search_dir = NonZero3::new (-near.0).unwrap()
}
}
2 => {
let t;
(t, near) = geometry::Segment3::unchecked (
simplex.points[0].point(), simplex.points[1].point()
).nearest_point (Point3::origin());
distance_squared = near.0.magnitude_squared();
if distance_squared < TOLERANCE_SQUARED {
overlap = true
} else if *t == 1.0 {
simplex.points[0] = simplex.points[1];
simplex.npoints -= 1;
search_dir = NonZero3::unchecked (-simplex.points[0].point().0)
} else {
search_dir = NonZero3::unchecked (-near.0)
}
}
3 => {
let (s, t);
([s, t], near) = geometry::Triangle3::unchecked (
simplex.points[0].point(),
simplex.points[1].point(),
simplex.points[2].point()
).nearest_point (Point3::origin());
distance_squared = near.0.magnitude_squared();
if distance_squared < TOLERANCE_SQUARED {
overlap = true
} else {
search_dir = NonZero3::unchecked (-near.0);
if *s == 0.0 && *t == 0.0 {
simplex.npoints = 1;
} else if *s == 1.0 && *t == 0.0 {
simplex.points[0] = simplex.points[1];
simplex.npoints = 1;
} else if *s == 0.0 && *t == 1.0 {
simplex.points[0] = simplex.points[2];
simplex.npoints = 1;
} else if (*s + *t - 1.0).abs() < TOLERANCE {
simplex.points[0] = simplex.points[1];
simplex.points[1] = simplex.points[2];
simplex.npoints = 2;
} else if *s < TOLERANCE {
simplex.points[1] = simplex.points[2];
simplex.npoints = 2;
} else if *t < TOLERANCE {
simplex.npoints = 2;
} else {
debug_assert!(*s > 0.0);
debug_assert!(*s < 1.0);
debug_assert!(*t > 0.0);
debug_assert!(*t < 1.0);
debug_assert!(*s + *t < 1.0);
}
}
}
_ => unreachable!()
}
}
if overlap {
#[derive(PartialEq)]
struct PolytopeFace {
pub distance_squared : NonNegative <f64>,
pub distance_vector : Vector3 <f64>,
pub nearest_a : Point3 <f64>,
pub nearest_b : Point3 <f64>,
pub points : [PointMinkowski; 3],
pub normal : NonZero3 <f64>,
pub exterior : bool
}
impl PartialOrd for PolytopeFace {
fn partial_cmp (&self, other : &Self) -> Option <std::cmp::Ordering> {
self.distance_squared.partial_cmp (&other.distance_squared)
}
}
let mut node_list = ReverseSortedVec::<PolytopeFace>::new();
let add_face = |
node_list : &mut ReverseSortedVec <PolytopeFace>,
p1 : PointMinkowski, p2 : PointMinkowski, p3 : PointMinkowski
| {
let triangle = geometry::Triangle3::noisy (p1.point(), p2.point(), p3.point());
let ([s, t], nearest) = triangle.nearest_point (Point3::origin());
let nearest_a = p1.point_a +
(p2.point_a - p1.point_a) * *s + (p3.point_a - p1.point_a) * *t;
let nearest_b = p1.point_b +
(p2.point_b - p1.point_b) * *s + (p3.point_b - p1.point_b) * *t;
let normal = {
let mut normal = triangle.normal();
if normal.dot (triangle.point_a().0) < 0.0 {
normal = -normal
}
normal
};
let node = PolytopeFace {
distance_squared: nearest.0.norm_squared(),
distance_vector: -nearest.0,
points: [p1, p2, p3],
exterior: false,
nearest_a,
nearest_b,
normal
};
node_list.insert (node);
};
let a = simplex.points[0];
let b = simplex.points[1];
let c = simplex.points[2];
let d = simplex.points[3];
match simplex.npoints {
3 => {
let e1 = simplex.points[1].point() - simplex.points[0].point();
let e2 = simplex.points[2].point() - simplex.points[0].point();
let n1 = NonZero3::noisy (e1.cross (e2));
let n2 = -n1;
let (p1, dot) = PointMinkowski::support (n1, object_a, object_b);
if dot <= TOLERANCE {
let half_axis = *n1 * TOLERANCE * 0.5;
return Proximity {
distance: -TOLERANCE.sqrt(),
normal: n1.normalize(),
midpoint: p1.point_a + half_axis,
half_axis
}
}
add_face (&mut node_list, a, b, p1);
add_face (&mut node_list, a, c, p1);
add_face (&mut node_list, b, c, p1);
let (p2, dot) = PointMinkowski::support (n2, object_a, object_b);
if dot <= TOLERANCE {
let half_axis = *n2 * TOLERANCE * 0.5;
return Proximity {
distance: -TOLERANCE.sqrt(),
normal: n2.normalize(),
midpoint: p2.point_a + half_axis,
half_axis
}
}
add_face (&mut node_list, a, b, p2);
add_face (&mut node_list, a, c, p2);
add_face (&mut node_list, b, c, p2);
}
4 => {
add_face (&mut node_list, a, b, c);
add_face (&mut node_list, a, b, d);
add_face (&mut node_list, a, c, d);
add_face (&mut node_list, b, c, d);
}
_ => unreachable!()
}
let mut edges = vec![];
let mut edge_in_list = vec![];
let mut last_iter_distance = f64::MIN;
let exterior_node_proximity = |node : PolytopeFace| {
let half_axis = node.distance_vector * 0.5;
Proximity {
half_axis,
distance: -*node.distance_squared.sqrt(),
normal: half_axis.normalize(), midpoint: node.nearest_a + half_axis
}
};
let mut iter = 0;
let noprogress_limit = 3;
let mut noprogress_count = 0;
loop {
let mut node = node_list.pop().unwrap();
if approx::relative_eq!(*node.distance_squared, last_iter_distance,
epsilon = 0.000000001
) {
noprogress_count += 1;
if noprogress_count == noprogress_limit {
log::trace!("expanding polytope search no progress after {iter} iterations");
return exterior_node_proximity (node)
}
} else {
noprogress_count = 0;
}
last_iter_distance = *node.distance_squared;
let (pa, _) = PointMinkowski::support (node.normal, object_a, object_b);
if node.points.contains (&pa) {
node.exterior = true;
} else {
let bpa = pa.point() - node.points[0].point();
let normal_unit = node.normal.normalize();
if normal_unit.dot (bpa) < TOLERANCE {
node.exterior = true;
}
}
if node.exterior {
return exterior_node_proximity (node)
} else {
edges.clear();
edge_in_list.clear();
edges.extend ([
[node.points[0], node.points[1]],
[node.points[0], node.points[2]],
[node.points[1], node.points[2]]
]);
edge_in_list.extend ([true, true, true]);
node_list.retain (|node|{
let p0_pa = pa.point() - node.points[0].point();
if TOLERANCE < node.normal.dot (p0_pa) {
let mut add_edge = |i : usize, j : usize| {
let mut found = false;
for (k, edge) in edges.iter().enumerate()
.filter (|(k, _)| edge_in_list[*k])
{
if node.points[i].point() == edge[0].point()
&& node.points[j].point() == edge[1].point()
|| node.points[i].point() == edge[1].point()
&& node.points[j].point() == edge[0].point()
{
found = true;
edge_in_list[k] = false; break
}
}
if !found {
edge_in_list.push (true);
edges.push ([node.points[i], node.points[j]]);
}
};
add_edge (0, 1); add_edge (0, 2); add_edge (1, 2); false } else {
true }
});
for (_, edge) in edges.iter().enumerate()
.filter (|(i, _)| edge_in_list[*i])
{
if geometry::Triangle3::new (pa.point(), edge[0].point(), edge[1].point())
.is_none()
{
continue
}
add_face (&mut node_list, pa, edge[0], edge[1]);
}
}
iter += 1;
}
} if simplex.points[0].point_a == simplex.points[1].point_a
&& simplex.points[1].point_a == simplex.points[2].point_a
{
unique_count += 1;
} else if simplex.points[0].point_a == simplex.points[1].point_a
|| simplex.points[0].point_a == simplex.points[2].point_a
|| simplex.points[1].point_a == simplex.points[2].point_a
{
unique_count += 2;
} else {
debug_assert!(
simplex.points[0].point_a != simplex.points[1].point_a &&
simplex.points[0].point_a != simplex.points[2].point_a &&
simplex.points[1].point_a != simplex.points[2].point_a);
unique_count += 3;
}
if simplex.points[0].point_b == simplex.points[1].point_b
&& simplex.points[1].point_b == simplex.points[2].point_b
{
unique_count += 1;
} else if simplex.points[0].point_b == simplex.points[1].point_b
|| simplex.points[0].point_b == simplex.points[2].point_b
|| simplex.points[1].point_b == simplex.points[2].point_b
{
unique_count += 2;
} else {
debug_assert!(
simplex.points[0].point_b != simplex.points[1].point_b &&
simplex.points[0].point_b != simplex.points[2].point_b &&
simplex.points[1].point_b != simplex.points[2].point_b);
unique_count += 3;
}
if distance_squared < lowest_distance_sq ||
distance_squared == lowest_distance_sq && unique_count < lowest_unique_count
{
lowest_distance_sq = distance_squared;
lowest_unique_count = unique_count;
} else if distance_squared >= last_distance_sq {
if noprogress_count < noprogress_limit {
noprogress_count += 1;
} else {
break
}
}
last_distance_sq = distance_squared;
}
let mut dupe_a_01 = false;
let mut dupe_a_02 = false;
let mut dupe_a_12 = false;
let mut dupe_b_01 = false;
let mut dupe_b_02 = false;
let mut dupe_b_12 = false;
if simplex.npoints >= 2 {
dupe_a_01 = simplex.points[0].point_a == simplex.points[1].point_a;
dupe_b_01 = simplex.points[0].point_b == simplex.points[1].point_b;
}
if simplex.npoints >= 3 {
dupe_a_02 = simplex.points[0].point_a == simplex.points[2].point_a;
dupe_a_12 = simplex.points[1].point_a == simplex.points[2].point_a;
dupe_b_02 = simplex.points[0].point_b == simplex.points[2].point_b;
dupe_b_12 = simplex.points[1].point_b == simplex.points[2].point_b;
}
debug_assert!(!(simplex.npoints == 2 && unique_count == 2),
"only one unique point: not possible");
match simplex.npoints {
1 => {
let nearest_a = simplex.points[0].point_a;
let nearest_b = simplex.points[0].point_b;
let a_to_b = nearest_b - nearest_a;
let half_axis = a_to_b * 0.5;
let midpoint = nearest_a + half_axis;
let normal = if !a_to_b.is_approx_zero() {
Unit3::normalize (-a_to_b)
} else {
Unit3::normalize (object_a.position().0 - object_b.position().0)
};
let distance = a_to_b.magnitude();
Proximity { distance, half_axis, midpoint, normal }
}
2 => {
let point0_a = simplex.points[0].point_a;
let point1_a = simplex.points[1].point_a;
let point0_b = simplex.points[0].point_b;
let point1_b = simplex.points[1].point_b;
if !dupe_a_01 && !dupe_b_01 {
let edge_a = geometry::Segment3::noisy (point0_a, point1_a);
let edge_b = geometry::Segment3::noisy (point0_b, point1_b);
Proximity::query_segment_segment (&edge_a, &edge_b)
} else {
if dupe_a_01 {
debug_assert!(!dupe_b_01);
debug_assert_eq!(point0_a, point1_a);
let point_a = point0_a;
let edge_b = geometry::Segment3::noisy (point0_b, point1_b);
Proximity::query_segment_point (&edge_b, &point_a).flip()
} else {
debug_assert!(dupe_b_01);
let point_b = point0_b;
let edge_a = geometry::Segment3::noisy (point0_a, point1_a);
Proximity::query_segment_point (&edge_a, &point_b)
}
}
}
3 => {
let is_edge = |
p0 : &mut Point3 <f64>, p1 : &mut Point3 <f64>, p2 : &Point3 <f64>
| {
let p01 = *p1 - *p0;
let p02 = *p2 - *p0;
let p01_cross_p02 = p01.cross (p02);
if p01_cross_p02.is_approx_zero() {
let p12 = *p2 - *p1;
let dot_p01 = p01.magnitude_squared();
let dot_p02 = p02.magnitude_squared();
let dot_p12 = p12.magnitude_squared();
if dot_p01 > dot_p02 && dot_p01 > dot_p12 {
} else if dot_p02 > dot_p01 && dot_p02 > dot_p12 {
*p1 = *p2;
} else {
debug_assert!(dot_p12 > dot_p01 && dot_p12 > dot_p02);
*p0 = *p1;
*p1 = *p2;
}
true
} else {
false
}
};
debug_assert!(!((dupe_a_01 && dupe_a_12) && (dupe_b_01 && dupe_b_12)));
if (!dupe_a_01 && !dupe_a_02 && !dupe_a_12) &&
(!dupe_b_01 && !dupe_b_02 && !dupe_b_12)
{
let mut a0 = simplex.points[0].point_a;
let mut a1 = simplex.points[1].point_a;
let a2 = simplex.points[2].point_a;
let mut b0 = simplex.points[0].point_b;
let mut b1 = simplex.points[1].point_b;
let b2 = simplex.points[2].point_b;
let edge_a = is_edge (&mut a0, &mut a1, &a2);
let edge_b = is_edge (&mut b0, &mut b1, &b2);
if edge_a && edge_b {
let edge_a = geometry::Segment3::noisy (a0, a1);
let edge_b = geometry::Segment3::noisy (b0, b1);
Proximity::query_segment_segment (&edge_a, &edge_b)
} else if edge_a && !edge_b {
let edge_a = geometry::Segment3::noisy (a0, a1);
let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
Proximity::query_triangle_segment (&triangle_b, &edge_a).flip()
} else if !edge_a && edge_b {
let edge_b = geometry::Segment3::noisy (b0, b1);
let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
Proximity::query_triangle_segment (&triangle_a, &edge_b)
} else {
debug_assert!(!edge_a);
debug_assert!(!edge_b);
let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
Proximity::query_triangle_triangle (&triangle_a, &triangle_b)
}
} else if (dupe_a_01 != dupe_a_02 || dupe_a_01 != dupe_a_12)
&& (!dupe_b_01 && !dupe_b_02 && !dupe_b_12)
{
let a0 = simplex.points[0].point_a;
let mut a1 = simplex.points[1].point_a;
let mut b0 = simplex.points[0].point_b;
let mut b1 = simplex.points[1].point_b;
let b2 = simplex.points[2].point_b;
if dupe_a_01 || dupe_a_12 {
a1 = simplex.points[2].point_a;
}
let edge_b = is_edge (&mut b0, &mut b1, &b2);
let edge_a = geometry::Segment3::noisy (a0, a1);
if edge_b {
let edge_b = geometry::Segment3::noisy (b0, b1);
Proximity::query_segment_segment (&edge_a, &edge_b)
} else {
let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
Proximity::query_triangle_segment (&triangle_b, &edge_a).flip()
}
} else if (dupe_b_01 != dupe_b_02 || dupe_b_01 != dupe_b_12)
&& (!dupe_a_01 && !dupe_a_02 && !dupe_a_12)
{
let mut a0 = simplex.points[0].point_a;
let mut a1 = simplex.points[1].point_a;
let a2 = simplex.points[2].point_a;
let b0 = simplex.points[0].point_b;
let b1 = if dupe_b_01 || dupe_b_12 {
simplex.points[2].point_b
} else {
simplex.points[1].point_b
};
let edge_a = is_edge (&mut a0, &mut a1, &a2);
if edge_a {
let edge_a = geometry::Segment3::noisy (a0, a1);
let edge_b = geometry::Segment3::noisy (b0, b1);
Proximity::query_segment_segment (&edge_a, &edge_b)
} else {
let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
let edge_b = geometry::Segment3::noisy (b0, b1);
Proximity::query_triangle_segment (&triangle_a, &edge_b)
}
} else {
if (dupe_a_01 != dupe_a_02 || dupe_a_01 != dupe_a_12)
&& !(dupe_a_01 && dupe_a_02 && dupe_a_12) {
debug_assert!(dupe_b_01 != dupe_b_02 || dupe_b_01 != dupe_b_12);
debug_assert!(!(dupe_b_01 && dupe_b_02 && dupe_b_12));
debug_assert!(!(dupe_a_01 && dupe_b_01));
debug_assert!(!(dupe_a_02 && dupe_b_02));
debug_assert!(!(dupe_a_12 && dupe_b_12));
if (dupe_a_01 != dupe_a_02) && (dupe_b_01 != dupe_b_02) {
if dupe_a_02 {
debug_assert!(dupe_b_01);
} else {
debug_assert!(dupe_a_01);
debug_assert!(dupe_b_02);
simplex.points.swap(1, 2);
}
} else if (dupe_a_01 != dupe_a_12) && (dupe_b_01 != dupe_b_12) {
let temp = simplex.points[0];
simplex.points[0] = simplex.points[1];
if dupe_a_01 {
debug_assert!(dupe_b_12);
simplex.points[1] = simplex.points[2];
simplex.points[2] = temp;
} else {
debug_assert!(dupe_a_12);
debug_assert!(dupe_b_01);
simplex.points[1] = temp;
}
} else {
debug_assert!(dupe_a_02 != dupe_a_12);
debug_assert!(dupe_b_02 != dupe_b_12);
let temp = simplex.points[0];
simplex.points[0] = simplex.points[2];
if dupe_a_02 {
debug_assert!(dupe_b_12);
simplex.points[2] = temp;
} else {
debug_assert!(dupe_a_12);
debug_assert!(dupe_b_02);
simplex.points[2] = simplex.points[1];
simplex.points[1] = temp;
}
}
let a0 = simplex.points[0].point_a;
let a1 = simplex.points[1].point_a;
let b0 = simplex.points[0].point_b;
let b2 = simplex.points[2].point_b;
let edge_a = geometry::Segment3::noisy (a0, a1);
let edge_b = geometry::Segment3::noisy (b0, b2);
Proximity::query_segment_segment (&edge_a, &edge_b)
} else {
debug_assert!((dupe_a_01 && dupe_a_12) != (dupe_b_01 && dupe_b_12));
if dupe_a_01 && dupe_a_12 {
debug_assert!(dupe_a_02); let point_a = simplex.points[0].point_a;
let b0 = simplex.points[0].point_b;
let b1 = simplex.points[1].point_b;
let b2 = simplex.points[2].point_b;
let triangle_b = geometry::Triangle3::noisy (b0, b1, b2);
Proximity::query_triangle_point (&triangle_b, &point_a).flip()
} else {
debug_assert!(dupe_b_01 && dupe_b_12);
debug_assert!(dupe_b_02); let point_b = simplex.points[0].point_b;
let a0 = simplex.points[0].point_a;
let a1 = simplex.points[1].point_a;
let a2 = simplex.points[2].point_a;
let triangle_a = geometry::Triangle3::noisy (a0, a1, a2);
Proximity::query_triangle_point (&triangle_a, &point_b)
}
}
}
}
_ => unreachable!()
}
}
}
#[cfg(test)]
mod tests {
use approx;
use gl_utils::{mesh, Mesh};
use rand;
use rand_distr;
use rand_xorshift::XorShiftRng;
use crate::math::geometry::*;
use crate::component;
use super::*;
#[test]
fn query_gjk() {
use rand::SeedableRng;
use rand_distr::Distribution;
let tetrahedron = |p1 : [f64; 3], p2 : [f64; 3], p3 : [f64; 3], p4 : [f64; 3]|
-> (component::Position, component::Bound)
{
( component::Position::origin(),
Hull3::from_points_with_mesh (
&simplex3::Tetrahedron::<f64>::noisy (
p1.into(), p2.into(), p3.into(), p4.into()
).points()
).unwrap().into()
)
};
let a = tetrahedron (
[-2.0, 2.0, -1.0],
[ 2.0, 2.0, -1.0],
[ 0.0, -2.0, -1.0],
[ 0.0, 0.0, 2.0]);
let b = tetrahedron (
[-2.0, 2.0, 3.5],
[ 2.0, 2.0, 3.5],
[ 0.0, -2.0, 3.5],
[ 0.0, 0.0, 6.5]);
let proximity = Proximity::query_gjk (&a, &b);
assert_eq!(proximity.distance, 1.5);
assert_eq!(proximity.half_axis, [0.0, 0.0, 0.75].into());
assert_eq!(proximity.midpoint, [0.0, 0.0, 2.75].into());
assert_eq!(proximity.normal, -Unit3::axis_z());
let a = tetrahedron (
[-2.0, 2.0, -1.0],
[ 2.0, 2.0, -1.0],
[ 0.0, -2.0, -1.0],
[ 0.0, 0.0, 2.0]);
let b = tetrahedron (
[-2.0, 2.0, 1.75],
[ 2.0, 2.0, 1.75],
[ 0.0, -2.0, 1.75],
[ 0.0, 0.0, 4.75]);
let proximity = Proximity::query_gjk (&a, &b);
assert_eq!(proximity.distance, -0.25);
assert_eq!(proximity.half_axis, [0.0, 0.0, -0.125].into());
assert_eq!(proximity.midpoint, [0.0, 0.0, 1.875].into());
assert_eq!(proximity.normal, -Unit3::axis_z());
let a = tetrahedron (
[ 0.0, 0.0, 3.5],
[-2.0, 2.0, 0.5],
[ 2.0, 2.0, 0.5],
[ 0.0, -2.0, 0.5]);
let b = tetrahedron (
[-2.0, 2.0, 1.0],
[ 2.0, 2.0, 1.0],
[ 0.0, -2.0, 1.0],
[ 0.0, 0.0, -2.0]);
let proximity = Proximity::query_gjk (&a, &b);
assert_eq!(proximity.distance, -0.5);
approx::assert_abs_diff_eq!(proximity.half_axis, [0.0, 0.0, 0.25].into());
assert_eq!(proximity.midpoint, [0.0, 2.0/3.0, 0.75].into());
approx::assert_abs_diff_eq!(*proximity.normal, *Unit3::axis_z(),
epsilon = f64::EPSILON * 4.0);
let hull = |points : &[Point3 <f64>]| -> (component::Position, component::Bound) {
( component::Position::origin(),
Hull3::from_points_with_mesh (points).unwrap().into()
)
};
let a = hull (&[
[ 0.0, 0.0, 3.5],
[ 0.0, 0.0, 0.5],
[-2.0, 2.0, 0.5],
[ 2.0, 2.0, 0.5],
[ 0.0, -2.0, 0.5]].map (Into::into));
let b = hull (&[
[-2.0, 2.0, 1.0],
[ 2.0, 2.0, 1.0],
[ 0.0, -2.0, 1.0],
[ 0.0, 0.0, 1.0],
[ 0.0, 0.0, -2.0]].map (Into::into));
let proximity = Proximity::query_gjk (&a, &b);
assert_eq!(proximity.distance, -0.5);
approx::assert_abs_diff_eq!(proximity.half_axis, [0.0, 0.0, 0.25].into());
assert_eq!(proximity.midpoint, [0.0, 2.0/3.0, 0.75].into());
approx::assert_abs_diff_eq!(*proximity.normal, *Unit3::axis_z(),
epsilon = f64::EPSILON * 4.0);
{
const NPOINTS : usize = 4;
let mut rng = XorShiftRng::seed_from_u64 (0);
let cauchy = rand_distr::Cauchy::new (0.0, 1.0).unwrap();
let randf = |rng : &mut XorShiftRng| cauchy.sample (rng);
let aabb = Aabb3::with_minmax (
[-5.0, -5.0, -5.0].into(),
[ 5.0, 5.0, 5.0].into()).unwrap();
let rand_point = |rng : &mut XorShiftRng|
aabb.clamp (point3 (randf (rng), randf (rng), randf (rng)));
let object = |hull : shape::Hull <f64>|
(component::Position::origin(), component::Bound::from (hull));
#[expect(unused_variables)]
let write_gltf = |
(hull, mesh) : &shape::Hull <f64>,
filename : &str
| {
let mut m = mesh::Triangles3dBuilder::empty();
for triangle in mesh.triangles().values() {
let triangle = hull.triangle (triangle);
m.push_face (triangle.points());
}
Mesh::from (m.build()).write_gltf (filename);
};
for _i in 0..1000 {
let a = {
let points = std::iter::repeat_with (|| rand_point (&mut rng)).take (NPOINTS)
.collect::<Vec<_>>();
let hull = Hull3::from_points_with_mesh (&points).unwrap();
object (hull)
};
let b = {
let points = std::iter::repeat_with (|| rand_point (&mut rng)).take (NPOINTS)
.collect::<Vec<_>>();
let hull = Hull3::from_points_with_mesh (&points).unwrap();
object (hull)
};
let _proximity = Proximity::query_gjk (&a, &b);
}
}
}
}