use crate::geometry::{Contact, ContactManifold};
use crate::math::{Isometry, Point, Vector};
use crate::utils::WBasis;
use na::Point2;
#[derive(Debug)]
#[allow(dead_code)]
pub(crate) enum CuboidFeature {
Face(CuboidFeatureFace),
Edge(CuboidFeatureEdge),
Vertex(CuboidFeatureVertex),
}
#[derive(Debug)]
pub(crate) struct CuboidFeatureVertex {
pub vertex: Point<f32>,
pub vid: u8,
}
impl CuboidFeatureVertex {
pub fn transform_by(&mut self, iso: &Isometry<f32>) {
self.vertex = iso * self.vertex;
}
}
#[derive(Debug)]
pub(crate) struct CuboidFeatureEdge {
pub vertices: [Point<f32>; 2],
pub vids: [u8; 2],
pub eid: u8,
}
impl CuboidFeatureEdge {
pub fn transform_by(&mut self, iso: &Isometry<f32>) {
self.vertices[0] = iso * self.vertices[0];
self.vertices[1] = iso * self.vertices[1];
}
}
#[derive(Debug)]
pub(crate) struct CuboidFeatureFace {
pub vertices: [Point<f32>; 4],
pub vids: [u8; 4],
pub eids: [u8; 4],
pub fid: u8,
}
impl CuboidFeatureFace {
pub fn transform_by(&mut self, iso: &Isometry<f32>) {
self.vertices[0] = iso * self.vertices[0];
self.vertices[1] = iso * self.vertices[1];
self.vertices[2] = iso * self.vertices[2];
self.vertices[3] = iso * self.vertices[3];
}
}
impl CuboidFeature {
pub fn transform_by(&mut self, iso: &Isometry<f32>) {
match self {
CuboidFeature::Face(face) => face.transform_by(iso),
CuboidFeature::Edge(edge) => edge.transform_by(iso),
CuboidFeature::Vertex(vertex) => vertex.transform_by(iso),
}
}
pub fn face_vertex_contacts(
face1: &CuboidFeatureFace,
sep_axis1: &Vector<f32>,
vertex2: &CuboidFeatureVertex,
pos21: &Isometry<f32>,
manifold: &mut ContactManifold,
) {
let normal1 =
(face1.vertices[0] - face1.vertices[1]).cross(&(face1.vertices[2] - face1.vertices[1]));
let denom = -normal1.dot(&sep_axis1);
let dist = (face1.vertices[0] - vertex2.vertex).dot(&normal1) / denom;
let local_p2 = vertex2.vertex;
let local_p1 = vertex2.vertex - dist * sep_axis1;
manifold.points.push(Contact {
local_p1,
local_p2: pos21 * local_p2,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: face1.fid,
fid2: vertex2.vid,
dist,
});
}
pub fn face_edge_contacts(
prediction_distance: f32,
face1: &CuboidFeatureFace,
sep_axis1: &Vector<f32>,
edge2: &CuboidFeatureEdge,
pos21: &Isometry<f32>,
manifold: &mut ContactManifold,
flipped: bool,
) {
let basis = sep_axis1.orthonormal_basis();
let projected_face1 = [
Point2::new(
face1.vertices[0].coords.dot(&basis[0]),
face1.vertices[0].coords.dot(&basis[1]),
),
Point2::new(
face1.vertices[1].coords.dot(&basis[0]),
face1.vertices[1].coords.dot(&basis[1]),
),
Point2::new(
face1.vertices[2].coords.dot(&basis[0]),
face1.vertices[2].coords.dot(&basis[1]),
),
Point2::new(
face1.vertices[3].coords.dot(&basis[0]),
face1.vertices[3].coords.dot(&basis[1]),
),
];
let projected_edge2 = [
Point2::new(
edge2.vertices[0].coords.dot(&basis[0]),
edge2.vertices[0].coords.dot(&basis[1]),
),
Point2::new(
edge2.vertices[1].coords.dot(&basis[0]),
edge2.vertices[1].coords.dot(&basis[1]),
),
];
for i in 0..4 {
let projected_edge1 = [projected_face1[i], projected_face1[(i + 1) % 4]];
if let Some(bcoords) = closest_points_line2d(projected_edge1, projected_edge2) {
if bcoords.0 > 0.0 && bcoords.0 < 1.0 && bcoords.1 > 0.0 && bcoords.1 < 1.0 {
let edge1 = [face1.vertices[i], face1.vertices[(i + 1) % 4]];
let local_p1 = edge1[0] * (1.0 - bcoords.0) + edge1[1].coords * bcoords.0;
let local_p2 = edge2.vertices[0] * (1.0 - bcoords.1)
+ edge2.vertices[1].coords * bcoords.1;
let dist = (local_p2 - local_p1).dot(&sep_axis1);
if dist < prediction_distance {
if flipped {
manifold.points.push(Contact {
local_p1: local_p2,
local_p2: pos21 * local_p1,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: edge2.eid,
fid2: face1.eids[i],
dist,
});
} else {
manifold.points.push(Contact {
local_p1,
local_p2: pos21 * local_p2,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: face1.eids[i],
fid2: edge2.eid,
dist,
});
}
}
}
}
}
let normal1 =
(face1.vertices[2] - face1.vertices[1]).cross(&(face1.vertices[0] - face1.vertices[1]));
'point_loop2: for i in 0..2 {
let p2 = projected_edge2[i];
let sign = (projected_face1[0] - projected_face1[3]).perp(&(p2 - projected_face1[3]));
for j in 0..3 {
let new_sign =
(projected_face1[j + 1] - projected_face1[j]).perp(&(p2 - projected_face1[j]));
if new_sign * sign < 0.0 {
continue 'point_loop2;
}
}
let denom = -normal1.dot(&sep_axis1);
let dist = (face1.vertices[0] - edge2.vertices[i]).dot(&normal1) / denom;
let local_p2 = edge2.vertices[i];
let local_p1 = edge2.vertices[i] - dist * sep_axis1;
if dist < prediction_distance {
if flipped {
manifold.points.push(Contact {
local_p1: local_p2,
local_p2: pos21 * local_p1,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: edge2.vids[i],
fid2: face1.fid,
dist,
});
} else {
manifold.points.push(Contact {
local_p1,
local_p2: pos21 * local_p2,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: face1.fid,
fid2: edge2.vids[i],
dist,
});
}
}
}
}
pub fn edge_edge_contacts(
edge1: &CuboidFeatureEdge,
sep_axis1: &Vector<f32>,
edge2: &CuboidFeatureEdge,
pos21: &Isometry<f32>,
manifold: &mut ContactManifold,
) {
let basis = sep_axis1.orthonormal_basis();
let projected_edge1 = [
Point2::new(
edge1.vertices[0].coords.dot(&basis[0]),
edge1.vertices[0].coords.dot(&basis[1]),
),
Point2::new(
edge1.vertices[1].coords.dot(&basis[0]),
edge1.vertices[1].coords.dot(&basis[1]),
),
];
let projected_edge2 = [
Point2::new(
edge2.vertices[0].coords.dot(&basis[0]),
edge2.vertices[0].coords.dot(&basis[1]),
),
Point2::new(
edge2.vertices[1].coords.dot(&basis[0]),
edge2.vertices[1].coords.dot(&basis[1]),
),
];
if let Some(bcoords) = closest_points_line2d(projected_edge1, projected_edge2) {
let local_p1 =
edge1.vertices[0] * (1.0 - bcoords.0) + edge1.vertices[1].coords * bcoords.0;
let local_p2 =
edge2.vertices[0] * (1.0 - bcoords.1) + edge2.vertices[1].coords * bcoords.1;
let dist = (local_p2 - local_p1).dot(&sep_axis1);
manifold.points.push(Contact {
local_p1,
local_p2: pos21 * local_p2,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: edge1.eid,
fid2: edge2.eid,
dist,
});
}
}
pub fn face_face_contacts(
_prediction_distance: f32,
face1: &CuboidFeatureFace,
sep_axis1: &Vector<f32>,
face2: &CuboidFeatureFace,
pos21: &Isometry<f32>,
manifold: &mut ContactManifold,
) {
let basis = sep_axis1.orthonormal_basis();
let projected_face1 = [
Point2::new(
face1.vertices[0].coords.dot(&basis[0]),
face1.vertices[0].coords.dot(&basis[1]),
),
Point2::new(
face1.vertices[1].coords.dot(&basis[0]),
face1.vertices[1].coords.dot(&basis[1]),
),
Point2::new(
face1.vertices[2].coords.dot(&basis[0]),
face1.vertices[2].coords.dot(&basis[1]),
),
Point2::new(
face1.vertices[3].coords.dot(&basis[0]),
face1.vertices[3].coords.dot(&basis[1]),
),
];
let projected_face2 = [
Point2::new(
face2.vertices[0].coords.dot(&basis[0]),
face2.vertices[0].coords.dot(&basis[1]),
),
Point2::new(
face2.vertices[1].coords.dot(&basis[0]),
face2.vertices[1].coords.dot(&basis[1]),
),
Point2::new(
face2.vertices[2].coords.dot(&basis[0]),
face2.vertices[2].coords.dot(&basis[1]),
),
Point2::new(
face2.vertices[3].coords.dot(&basis[0]),
face2.vertices[3].coords.dot(&basis[1]),
),
];
let normal1 =
(face1.vertices[2] - face1.vertices[1]).cross(&(face1.vertices[0] - face1.vertices[1]));
let normal2 =
(face2.vertices[2] - face2.vertices[1]).cross(&(face2.vertices[0] - face2.vertices[1]));
'point_loop1: for i in 0..4 {
let p1 = projected_face1[i];
let sign = (projected_face2[0] - projected_face2[3]).perp(&(p1 - projected_face2[3]));
for j in 0..3 {
let new_sign =
(projected_face2[j + 1] - projected_face2[j]).perp(&(p1 - projected_face2[j]));
if new_sign * sign < 0.0 {
continue 'point_loop1;
}
}
let denom = normal2.dot(&sep_axis1);
let dist = (face2.vertices[0] - face1.vertices[i]).dot(&normal2) / denom;
let local_p1 = face1.vertices[i];
let local_p2 = face1.vertices[i] + dist * sep_axis1;
if true {
manifold.points.push(Contact {
local_p1,
local_p2: pos21 * local_p2,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: face1.vids[i],
fid2: face2.fid,
dist,
});
}
}
let is_clockwise1 = (projected_face1[0] - projected_face1[1])
.perp(&(projected_face1[2] - projected_face1[1]))
>= 0.0;
let mut vertex_class2 = [0u8; 4];
for i in 0..4 {
let p2 = projected_face2[i];
let sign = (projected_face1[0] - projected_face1[3]).perp(&(p2 - projected_face1[3]));
vertex_class2[i] |= ((sign >= 0.0) as u8) << 3;
for j in 0..3 {
let sign =
(projected_face1[j + 1] - projected_face1[j]).perp(&(p2 - projected_face1[j]));
vertex_class2[i] |= ((sign >= 0.0) as u8) << j;
}
if !is_clockwise1 {
vertex_class2[i] = (!vertex_class2[i]) & 0b01111;
}
if vertex_class2[i] == 0 {
let denom = -normal1.dot(&sep_axis1);
let dist = (face1.vertices[0] - face2.vertices[i]).dot(&normal1) / denom;
let local_p2 = face2.vertices[i];
let local_p1 = face2.vertices[i] - dist * sep_axis1;
if true {
manifold.points.push(Contact {
local_p1,
local_p2: pos21 * local_p2,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: face1.fid,
fid2: face2.vids[i],
dist,
});
}
}
}
for j in 0..4 {
let projected_edge2 = [projected_face2[j], projected_face2[(j + 1) % 4]];
if (vertex_class2[j] & vertex_class2[(j + 1) % 4]) != 0 {
continue;
}
let edge_class2 = vertex_class2[j] | vertex_class2[(j + 1) % 4];
for i in 0..4 {
if (edge_class2 & (1 << i)) != 0 {
let projected_edge1 = [projected_face1[i], projected_face1[(i + 1) % 4]];
if let Some(bcoords) = closest_points_line2d(projected_edge1, projected_edge2) {
if bcoords.0 > 0.0 && bcoords.0 < 1.0 && bcoords.1 > 0.0 && bcoords.1 < 1.0
{
let edge1 = (face1.vertices[i], face1.vertices[(i + 1) % 4]);
let edge2 = (face2.vertices[j], face2.vertices[(j + 1) % 4]);
let local_p1 = edge1.0 * (1.0 - bcoords.0) + edge1.1.coords * bcoords.0;
let local_p2 = edge2.0 * (1.0 - bcoords.1) + edge2.1.coords * bcoords.1;
let dist = (local_p2 - local_p1).dot(&sep_axis1);
if true {
manifold.points.push(Contact {
local_p1,
local_p2: pos21 * local_p2,
impulse: 0.0,
tangent_impulse: Contact::zero_tangent_impulse(),
fid1: face1.eids[i],
fid2: face2.eids[j],
dist,
});
}
}
}
}
}
}
}
}
fn closest_points_line2d(edge1: [Point2<f32>; 2], edge2: [Point2<f32>; 2]) -> Option<(f32, f32)> {
use approx::AbsDiffEq;
let dir1 = edge1[1] - edge1[0];
let dir2 = edge2[1] - edge2[0];
let r = edge1[0] - edge2[0];
let a = dir1.norm_squared();
let e = dir2.norm_squared();
let f = dir2.dot(&r);
let eps = f32::default_epsilon();
if a <= eps && e <= eps {
Some((0.0, 0.0))
} else if a <= eps {
Some((0.0, f / e))
} else {
let c = dir1.dot(&r);
if e <= eps {
Some((-c / a, 0.0))
} else {
let b = dir1.dot(&dir2);
let ae = a * e;
let bb = b * b;
let denom = ae - bb;
let parallel = denom <= eps || approx::ulps_eq!(ae, bb);
let s = if !parallel {
(b * f - c * e) / denom
} else {
0.0
};
if parallel {
None
} else {
Some((s, (b * s + f) / e))
}
}
}
}