use na::{self, RealField, Unit};
use crate::math::{Isometry, Point, Vector};
use crate::query::ContactPreprocessor;
use crate::query::{Contact, ContactPrediction};
use crate::query::{ContactKinematic, ContactManifold, NeighborhoodGeometry};
use crate::shape::{FeatureId, Segment, SegmentPointLocation};
use crate::utils::IsometryOps;
#[derive(Clone, Debug)]
pub struct ConvexPolygonalFeature<N: RealField> {
pub vertices: [Point<N>; 2],
pub nvertices: usize,
pub normal: Option<Unit<Vector<N>>>,
pub feature_id: FeatureId,
pub vertices_id: [FeatureId; 2],
}
impl<N: RealField> ConvexPolygonalFeature<N> {
pub fn new() -> Self {
ConvexPolygonalFeature {
vertices: [Point::origin(); 2],
nvertices: 0,
normal: None,
feature_id: FeatureId::Unknown,
vertices_id: [FeatureId::Unknown, FeatureId::Unknown],
}
}
pub fn clear(&mut self) {
self.nvertices = 0;
self.normal = None;
self.feature_id = FeatureId::Unknown;
}
pub fn transform_by(&mut self, m: &Isometry<N>) {
for p in &mut self.vertices {
*p = m * *p;
}
if let Some(ref mut n) = self.normal {
*n = m * *n;
}
}
pub fn push(&mut self, pt: Point<N>, id: FeatureId) {
self.vertices[self.nvertices] = pt;
self.vertices_id[self.nvertices] = id;
self.nvertices += 1;
}
pub fn nvertices(&self) -> usize {
self.nvertices
}
pub fn vertices(&self) -> &[Point<N>] {
&self.vertices[..self.nvertices]
}
pub fn set_normal(&mut self, normal: Unit<Vector<N>>) {
self.normal = Some(normal)
}
pub fn set_feature_id(&mut self, id: FeatureId) {
self.feature_id = id
}
pub fn project_point(&self, pt: &Point<N>) -> Option<Contact<N>> {
if let Some(n) = self.normal {
let dir = self.vertices[1] - self.vertices[0];
let dpt = *pt - self.vertices[0];
let dot = dir.dot(&dpt);
if dot < N::zero() || dot * dot > dir.norm_squared() {
None
} else {
let dist = n.dot(&dpt);
let proj = *pt + (-n.into_inner() * dist);
Some(Contact::new(proj, *pt, n, -dist))
}
} else {
None
}
}
pub fn clip(
&self,
other: &Self,
normal: &Unit<Vector<N>>,
prediction: &ContactPrediction<N>,
out: &mut Vec<(Contact<N>, FeatureId, FeatureId)>,
) {
if self.nvertices <= 1 || other.nvertices <= 1 {
return;
}
let mut ortho: Vector<N> = na::zero();
ortho[0] = -normal.as_ref()[1];
ortho[1] = normal.as_ref()[0];
let mut seg1 = Segment::new(self.vertices[0], self.vertices[1]);
let mut seg2 = Segment::new(other.vertices[0], other.vertices[1]);
let ref_pt = *seg1.a();
let mut range1 = [
(*seg1.a() - ref_pt).dot(&ortho),
(*seg1.b() - ref_pt).dot(&ortho),
];
let mut range2 = [
(*seg2.a() - ref_pt).dot(&ortho),
(*seg2.b() - ref_pt).dot(&ortho),
];
let mut features1 = [self.vertices_id[0], self.vertices_id[1]];
let mut features2 = [other.vertices_id[0], other.vertices_id[1]];
if range1[1] < range1[0] {
range1.swap(0, 1);
features1.swap(0, 1);
seg1.swap();
}
if range2[1] < range2[0] {
range2.swap(0, 1);
features2.swap(0, 1);
seg2.swap();
}
if range2[0] > range1[1] || range1[0] > range2[1] {
return;
}
let _1: N = na::one();
let length1 = range1[1] - range1[0];
let length2 = range2[1] - range2[0];
if range2[0] > range1[0] {
let bcoord = (range2[0] - range1[0]) / length1;
let p1 = seg1.point_at(&SegmentPointLocation::OnEdge([_1 - bcoord, bcoord]));
let p2 = *seg2.a();
let contact = Contact::new_wo_depth(p1, p2, *normal);
if -contact.depth <= prediction.linear() {
out.push((contact, self.feature_id, features2[0]));
}
} else {
let bcoord = (range1[0] - range2[0]) / length2;
let p1 = *seg1.a();
let p2 = seg2.point_at(&SegmentPointLocation::OnEdge([_1 - bcoord, bcoord]));
let contact = Contact::new_wo_depth(p1, p2, *normal);
if -contact.depth <= prediction.linear() {
out.push((contact, features1[0], other.feature_id));
}
}
if range2[1] < range1[1] {
let bcoord = (range2[1] - range1[0]) / length1;
let p1 = seg1.point_at(&SegmentPointLocation::OnEdge([_1 - bcoord, bcoord]));
let p2 = *seg2.b();
let contact = Contact::new_wo_depth(p1, p2, *normal);
if -contact.depth <= prediction.linear() {
out.push((contact, self.feature_id, features2[1]));
}
} else {
let bcoord = (range1[1] - range2[0]) / length2;
let p1 = *seg1.b();
let p2 = seg2.point_at(&SegmentPointLocation::OnEdge([_1 - bcoord, bcoord]));
let contact = Contact::new_wo_depth(p1, p2, *normal);
if -contact.depth <= prediction.linear() {
out.push((contact, features1[1], other.feature_id));
}
}
}
pub fn add_contact_to_manifold(
&self,
other: &Self,
c: Contact<N>,
m1: &Isometry<N>,
f1: FeatureId,
proc1: Option<&dyn ContactPreprocessor<N>>,
m2: &Isometry<N>,
f2: FeatureId,
proc2: Option<&dyn ContactPreprocessor<N>>,
manifold: &mut ContactManifold<N>,
) {
let mut kinematic = ContactKinematic::new();
let local1 = m1.inverse_transform_point(&c.world1);
let local2 = m2.inverse_transform_point(&c.world2);
match f1 {
FeatureId::Face(..) => kinematic.set_approx1(
f1,
local1,
NeighborhoodGeometry::Plane(
m1.inverse_transform_unit_vector(&self.normal.as_ref().unwrap()),
),
),
FeatureId::Vertex(..) => kinematic.set_approx1(f1, local1, NeighborhoodGeometry::Point),
FeatureId::Unknown => return,
}
match f2 {
FeatureId::Face(..) => {
let approx2 = NeighborhoodGeometry::Plane(
m2.inverse_transform_unit_vector(other.normal.as_ref().unwrap()),
);
kinematic.set_approx2(f2, local2, approx2)
}
FeatureId::Vertex(..) => kinematic.set_approx2(f2, local2, NeighborhoodGeometry::Point),
FeatureId::Unknown => return,
}
let _ = manifold.push(c, kinematic, local1, proc1, proc2);
}
}