use crate::collision::{Capsule, Circle, Segment};
use crate::geometry::{compute_polygon_aabb, make_box, make_rounded_box};
use crate::manifold::*;
use crate::math_functions::{
inv_mul_world_transforms, make_world_transform, offset_pos, Transform, Vec2, POS_ZERO,
ROT_IDENTITY, TRANSFORM_IDENTITY, VEC2_ZERO, WORLD_TRANSFORM_IDENTITY,
};
fn ensure_small(value: f32, tolerance: f32) {
assert!(
!(value < -tolerance || tolerance < value),
"|{value}| > tolerance {tolerance}"
);
}
fn v(x: f32, y: f32) -> Vec2 {
Vec2 { x, y }
}
#[test]
fn large_world_manifold() {
let box_a = make_box(0.5, 0.5);
let box_b = make_box(0.5, 0.5);
let sep = v(0.9, 0.0);
let xf_ao = WORLD_TRANSFORM_IDENTITY;
let xf_bo = crate::math_functions::WorldTransform {
p: offset_pos(POS_ZERO, sep),
q: ROT_IDENTITY,
};
let m_origin = collide_polygons(&box_a, &box_b, inv_mul_world_transforms(xf_ao, xf_bo));
assert_eq!(m_origin.point_count, 2);
ensure_small(m_origin.points[0].separation + 0.1, 0.01);
ensure_small(m_origin.points[1].separation + 0.1, 0.01);
#[cfg(feature = "double-precision")]
{
let base = offset_pos(POS_ZERO, v(1.0e7, 1.0e7));
let xf_al = crate::math_functions::WorldTransform {
p: base,
q: ROT_IDENTITY,
};
let xf_bl = crate::math_functions::WorldTransform {
p: offset_pos(base, sep),
q: ROT_IDENTITY,
};
let m_large = collide_polygons(&box_a, &box_b, inv_mul_world_transforms(xf_al, xf_bl));
assert_eq!(m_large.point_count, m_origin.point_count);
ensure_small(m_large.normal.x - m_origin.normal.x, 1e-4);
ensure_small(m_large.normal.y - m_origin.normal.y, 1e-4);
for i in 0..m_large.point_count as usize {
ensure_small(
m_large.points[i].separation - m_origin.points[i].separation,
1e-4,
);
ensure_small(m_large.points[i].point.x - m_origin.points[i].point.x, 1e-4);
ensure_small(m_large.points[i].point.y - m_origin.points[i].point.y, 1e-4);
}
}
let _ = make_world_transform(TRANSFORM_IDENTITY);
}
#[test]
fn large_world_aabb() {
let box_ = make_rounded_box(0.5, 0.5, 0.1);
let aabb_origin = compute_polygon_aabb(&box_, WORLD_TRANSFORM_IDENTITY);
ensure_small(aabb_origin.lower_bound.x + 0.6, f32::EPSILON);
ensure_small(aabb_origin.lower_bound.y + 0.6, f32::EPSILON);
ensure_small(aabb_origin.upper_bound.x - 0.6, f32::EPSILON);
ensure_small(aabb_origin.upper_bound.y - 0.6, f32::EPSILON);
#[cfg(feature = "double-precision")]
{
use crate::collision::ShapeGeometry;
use crate::geometry::compute_fat_shape_aabb;
let d = 1.0e7_f64;
let xf_large = crate::math_functions::WorldTransform {
p: offset_pos(POS_ZERO, v(1.0e7, 1.0e7)),
q: ROT_IDENTITY,
};
let tight = compute_polygon_aabb(&box_, xf_large);
assert!((tight.lower_bound.x as f64) <= d - 0.6);
assert!((tight.lower_bound.y as f64) <= d - 0.6);
assert!((tight.upper_bound.x as f64) >= d + 0.6);
assert!((tight.upper_bound.y as f64) >= d + 0.6);
let extra = 0.05_f32;
let geometry = ShapeGeometry::Polygon(box_);
let fat = compute_fat_shape_aabb(&geometry, xf_large, extra);
assert!((fat.lower_bound.x as f64) <= d - 0.6 - (extra as f64));
assert!((fat.lower_bound.y as f64) <= d - 0.6 - (extra as f64));
assert!((fat.upper_bound.x as f64) >= d + 0.6 + (extra as f64));
assert!((fat.upper_bound.y as f64) >= d + 0.6 + (extra as f64));
}
}
#[test]
fn circle_manifolds() {
let a = Circle {
center: VEC2_ZERO,
radius: 1.0,
};
let b = Circle {
center: VEC2_ZERO,
radius: 1.0,
};
let xf = Transform {
p: v(1.5, 0.0),
q: ROT_IDENTITY,
};
let m = collide_circles(&a, &b, xf);
assert_eq!(m.point_count, 1);
ensure_small(m.normal.x - 1.0, f32::EPSILON);
ensure_small(m.normal.y, f32::EPSILON);
ensure_small(m.points[0].separation + 0.5, 1e-6);
ensure_small(m.points[0].point.x - 0.75, 1e-6);
let far = Transform {
p: v(10.0, 0.0),
q: ROT_IDENTITY,
};
assert_eq!(collide_circles(&a, &b, far).point_count, 0);
let box_a = make_box(1.0, 1.0);
let circle_above = Circle {
center: VEC2_ZERO,
radius: 0.5,
};
let xf_above = Transform {
p: v(0.0, 1.45),
q: ROT_IDENTITY,
};
let m = collide_polygon_and_circle(&box_a, &circle_above, xf_above);
assert_eq!(m.point_count, 1);
ensure_small(m.normal.x, 1e-6);
ensure_small(m.normal.y - 1.0, 1e-6);
ensure_small(m.points[0].separation + 0.05, 1e-5);
}
#[test]
fn capsule_manifolds() {
let a = Capsule {
center1: v(-1.0, 0.0),
center2: v(1.0, 0.0),
radius: 0.5,
};
let b = Capsule {
center1: v(-1.0, 0.0),
center2: v(1.0, 0.0),
radius: 0.5,
};
let xf = Transform {
p: v(0.0, 0.9),
q: ROT_IDENTITY,
};
let m = collide_capsules(&a, &b, xf);
assert_eq!(m.point_count, 2);
ensure_small(m.normal.x, 1e-6);
ensure_small(m.normal.y - 1.0, 1e-6);
ensure_small(m.points[0].separation + 0.1, 1e-5);
ensure_small(m.points[1].separation + 0.1, 1e-5);
let seg = Segment {
point1: v(-2.0, 0.0),
point2: v(2.0, 0.0),
};
let xf_seg = Transform {
p: v(0.0, 0.4),
q: ROT_IDENTITY,
};
let m = collide_segment_and_capsule(&seg, &b, xf_seg);
assert_eq!(m.point_count, 2);
ensure_small(m.normal.y - 1.0, 1e-6);
ensure_small(m.points[0].separation + 0.1, 1e-5);
}
#[test]
fn polygon_capsule_and_segment_manifolds() {
let box_a = make_box(1.0, 1.0);
let cap = Capsule {
center1: v(-0.5, 0.0),
center2: v(0.5, 0.0),
radius: 0.25,
};
let xf = Transform {
p: v(0.0, 1.2),
q: ROT_IDENTITY,
};
let m = collide_polygon_and_capsule(&box_a, &cap, xf);
assert_eq!(m.point_count, 2);
ensure_small(m.normal.y - 1.0, 1e-5);
ensure_small(m.points[0].separation + 0.05, 1e-5);
let ground = Segment {
point1: v(-5.0, 0.0),
point2: v(5.0, 0.0),
};
let xf_box = Transform {
p: v(0.0, 0.95),
q: ROT_IDENTITY,
};
let m = collide_segment_and_polygon(&ground, &box_a, xf_box);
assert_eq!(m.point_count, 2);
ensure_small(m.normal.y - 1.0, 1e-5);
ensure_small(m.points[0].separation + 0.05, 1e-5);
}
#[test]
fn chain_segment_manifolds() {
use crate::collision::ChainSegment;
use crate::distance::SimplexCache;
let chain = ChainSegment {
ghost1: v(-2.0, 0.0),
segment: Segment {
point1: v(-1.0, 0.0),
point2: v(1.0, 0.0),
},
ghost2: v(2.0, 0.0),
chain_id: 0,
};
let circle = Circle {
center: VEC2_ZERO,
radius: 0.5,
};
let below = Transform {
p: v(0.0, -0.45),
q: ROT_IDENTITY,
};
let m = collide_chain_segment_and_circle(&chain, &circle, below);
assert_eq!(m.point_count, 1);
ensure_small(m.normal.y + 1.0, 1e-6);
ensure_small(m.points[0].separation + 0.05, 1e-5);
let above = Transform {
p: v(0.0, 0.45),
q: ROT_IDENTITY,
};
assert_eq!(
collide_chain_segment_and_circle(&chain, &circle, above).point_count,
0
);
let box_b = make_box(0.5, 0.5);
let mut cache = SimplexCache::default();
let m = collide_chain_segment_and_polygon(
&chain,
&box_b,
Transform {
p: v(0.0, -0.45),
q: ROT_IDENTITY,
},
&mut cache,
);
assert_eq!(m.point_count, 2);
ensure_small(m.normal.y + 1.0, 1e-5);
let mut cache2 = SimplexCache::default();
let m = collide_chain_segment_and_polygon(
&chain,
&box_b,
Transform {
p: v(0.0, 0.45),
q: ROT_IDENTITY,
},
&mut cache2,
);
assert_eq!(m.point_count, 0);
}