use alga::linear::FiniteDimInnerSpace;
use na::{self, Point2, RealField, Unit};
use std::iter;
use crate::math::{Isometry, Point, Vector};
use crate::query::ContactPreprocessor;
use crate::query::{
self, Contact, ContactKinematic, ContactManifold, ContactPrediction, NeighborhoodGeometry,
};
use crate::shape::{FeatureId, Segment, SegmentPointLocation};
use crate::utils::{self, IsometryOps};
#[derive(Clone)]
pub struct ClippingCache<N: RealField> {
poly1: Vec<Point2<N>>,
poly2: Vec<Point2<N>>,
}
impl<N: RealField> ClippingCache<N> {
pub fn new() -> Self {
ClippingCache {
poly1: Vec::with_capacity(4),
poly2: Vec::with_capacity(4),
}
}
pub fn clear(&mut self) {
self.poly1.clear();
self.poly2.clear();
}
}
#[derive(Clone, Debug)]
pub struct ConvexPolygonalFeature<N: RealField> {
pub vertices: Vec<Point<N>>,
pub edge_normals: Vec<Vector<N>>,
pub normal: Option<Unit<Vector<N>>>,
pub feature_id: FeatureId,
pub vertices_id: Vec<FeatureId>,
pub edges_id: Vec<FeatureId>,
}
impl<N: RealField> ConvexPolygonalFeature<N> {
pub fn new() -> Self {
ConvexPolygonalFeature {
vertices: Vec::new(),
edge_normals: Vec::new(),
normal: None,
feature_id: FeatureId::Unknown,
vertices_id: Vec::new(),
edges_id: Vec::new(),
}
}
pub fn with_size(n: usize) -> Self {
ConvexPolygonalFeature {
vertices: iter::repeat(Point::origin()).take(n).collect(),
edge_normals: iter::repeat(Vector::zeros()).take(n).collect(),
normal: None,
feature_id: FeatureId::Unknown,
vertices_id: iter::repeat(FeatureId::Unknown).take(n).collect(),
edges_id: iter::repeat(FeatureId::Unknown).take(n).collect(),
}
}
pub fn clear(&mut self) {
self.vertices.clear();
self.edge_normals.clear();
self.vertices_id.clear();
self.edges_id.clear();
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;
}
for n in &mut self.edge_normals {
*n = m * *n;
}
if let Some(ref mut n) = self.normal {
*n = m * *n;
}
}
pub fn push(&mut self, pt: Point<N>, id: FeatureId) {
self.vertices.push(pt);
self.vertices_id.push(id);
}
pub fn nvertices(&self) -> usize {
self.vertices.len()
}
pub fn vertices(&self) -> &[Point<N>] {
&self.vertices[..]
}
pub fn nedges(&self) -> usize {
match self.vertices.len() {
1 => 0,
2 => 1,
l => l,
}
}
pub fn edge(&self, edge_id: FeatureId) -> Option<Segment<N>> {
for i1 in 0..self.vertices.len() {
if self.edges_id[i1] == edge_id {
let i2 = (i1 + 1) % self.vertices.len();
return Some(Segment::new(self.vertices[i1], self.vertices[i2]));
}
}
None
}
pub fn push_scaled_edge_normal(&mut self, normal: Vector<N>) {
if let Some(normal) = normal.try_normalize(N::default_epsilon()) {
self.edge_normals.push(normal)
} else {
self.edge_normals.push(na::zero())
}
}
pub fn push_edge_normal(&mut self, normal: Unit<Vector<N>>) {
self.edge_normals.push(normal.into_inner())
}
pub fn recompute_edge_normals(&mut self) {
self.edge_normals.clear();
for i1 in 0..self.vertices.len() {
let i2 = (i1 + 1) % self.vertices.len();
let dpt = self.vertices[i2] - self.vertices[i1];
let scaled_normal = dpt.cross(
self.normal
.as_ref()
.expect("The face normal must be set before computing edge normals."),
);
self.push_scaled_edge_normal(scaled_normal)
}
}
pub fn project_point(&self, pt: &Point<N>) -> Option<Contact<N>> {
if let Some(n) = self.normal {
let dpt = *pt - self.vertices[0];
let dist = n.dot(&dpt);
let proj = *pt + (-n.into_inner() * dist);
for i in 0..self.edge_normals.len() {
let dpt = proj - self.vertices[i];
if dpt.dot(&self.edge_normals[i]) > na::zero() {
return None;
}
}
Some(Contact::new(proj, *pt, n, -dist))
} else {
None
}
}
pub fn set_normal(&mut self, normal: Unit<Vector<N>>) {
self.normal = Some(normal)
}
pub fn push_edge_feature_id(&mut self, id: FeatureId) {
self.edges_id.push(id)
}
pub fn set_feature_id(&mut self, id: FeatureId) {
self.feature_id = id
}
pub fn clip(
&self,
other: &Self,
normal: &Unit<Vector<N>>,
prediction: &ContactPrediction<N>,
cache: &mut ClippingCache<N>,
out: &mut Vec<(Contact<N>, FeatureId, FeatureId)>,
) {
cache.clear();
if self.vertices.len() <= 2 && other.vertices.len() <= 2 {
return;
}
let mut basis = [na::zero(), na::zero()];
let mut basis_i = 0;
Vector::orthonormal_subspace_basis(&[normal.into_inner()], |dir| {
basis[basis_i] = *dir;
basis_i += 1;
true
});
let ref_pt = self.vertices[0];
for pt in &self.vertices {
let dpt = *pt - ref_pt;
let coords = Point2::new(basis[0].dot(&dpt), basis[1].dot(&dpt));
cache.poly1.push(coords);
}
for pt in &other.vertices {
let dpt = *pt - ref_pt;
let coords = Point2::new(basis[0].dot(&dpt), basis[1].dot(&dpt));
cache.poly2.push(coords);
}
if cache.poly2.len() > 2 {
for i in 0..cache.poly1.len() {
let pt = &cache.poly1[i];
if utils::point_in_poly2d(pt, &cache.poly2) {
let origin = ref_pt + basis[0] * pt.x + basis[1] * pt.y;
let n2 = other.normal.as_ref().unwrap().into_inner();
let p2 = &other.vertices[0];
if let Some(toi2) =
query::line_toi_with_plane(p2, &n2, &origin, &normal.into_inner())
{
let world2 = origin + normal.into_inner() * toi2;
let world1 = self.vertices[i];
let f2 = other.feature_id;
let f1 = self.vertices_id[i];
let contact = Contact::new_wo_depth(world1, world2, *normal);
if -contact.depth <= prediction.linear() {
out.push((contact, f1, f2));
}
}
}
}
}
if cache.poly1.len() > 2 {
for i in 0..cache.poly2.len() {
let pt = &cache.poly2[i];
if utils::point_in_poly2d(pt, &cache.poly1) {
let origin = ref_pt + basis[0] * pt.x + basis[1] * pt.y;
let n1 = self.normal.as_ref().unwrap().into_inner();
let p1 = &self.vertices[0];
if let Some(toi1) =
query::line_toi_with_plane(p1, &n1, &origin, &normal.into_inner())
{
let world1 = origin + normal.into_inner() * toi1;
let world2 = other.vertices[i];
let f1 = self.feature_id;
let f2 = other.vertices_id[i];
let contact = Contact::new_wo_depth(world1, world2, *normal);
if -contact.depth <= prediction.linear() {
out.push((contact, f1, f2));
}
}
}
}
}
let nedges1 = self.nedges();
let nedges2 = other.nedges();
for i1 in 0..nedges1 {
let j1 = (i1 + 1) % cache.poly1.len();
let seg1 = (&cache.poly1[i1], &cache.poly1[j1]);
for i2 in 0..nedges2 {
let j2 = (i2 + 1) % cache.poly2.len();
let seg2 = (&cache.poly2[i2], &cache.poly2[j2]);
if let (SegmentPointLocation::OnEdge(e1), SegmentPointLocation::OnEdge(e2)) =
query::closest_points_segment_segment_with_locations_nD(seg1, seg2)
{
let original1 = Segment::new(self.vertices[i1], self.vertices[j1]);
let original2 = Segment::new(other.vertices[i2], other.vertices[j2]);
let world1 = original1.point_at(&SegmentPointLocation::OnEdge(e1));
let world2 = original2.point_at(&SegmentPointLocation::OnEdge(e2));
let f1 = self.edges_id[i1];
let f2 = other.edges_id[i2];
let contact = Contact::new_wo_depth(world1, world2, *normal);
if -contact.depth <= prediction.linear() {
out.push((contact, f1, f2));
}
}
}
}
}
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::Edge(..) => {
let e1 = self.edge(f1).expect("Invalid edge id.");
if let Some(dir1) = e1.direction() {
let local_dir1 = m1.inverse_transform_unit_vector(&dir1);
let approx1 = NeighborhoodGeometry::Line(local_dir1);
kinematic.set_approx1(f1, local1, approx1)
} else {
return;
}
}
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::Edge(..) => {
let e2 = other.edge(f2).expect("Invalid edge id.");
if let Some(dir2) = e2.direction() {
let local_dir2 = m2.inverse_transform_unit_vector(&dir2);
let approx2 = NeighborhoodGeometry::Line(local_dir2);
kinematic.set_approx2(f2, local2, approx2)
} else {
return;
}
}
FeatureId::Vertex(..) => kinematic.set_approx2(f2, local2, NeighborhoodGeometry::Point),
FeatureId::Unknown => return,
}
let _ = manifold.push(c, kinematic, local1, proc1, proc2);
}
}