use std::cmp::Ordering;
use glam_det::nums::{bool32x4, Num, Signed};
use glam_det::{Dot, Isometry3, Point3, Vec3};
use smallvec::SmallVec;
use crate::convex_contact_manifold::{
Convex4ContactManifoldWide, Face, ManifoldCandidate, ManifoldCandidateWide,
};
use crate::shapes::GENERAL_FACE_VERTICES_RESTRICTIONS;
pub type ManifoldCandidateScalarSmallVec =
SmallVec<[ManifoldCandidate; GENERAL_FACE_VERTICES_RESTRICTIONS * 2]>;
pub struct CandidateScalarsReducer<'a> {
candidates: &'a mut ManifoldCandidateScalarSmallVec,
manifold_wide: &'a mut Convex4ContactManifoldWide,
slot_index: usize,
transform_b: Isometry3,
}
impl<'a> CandidateScalarsReducer<'a> {
pub fn new(
candidates: &'a mut ManifoldCandidateScalarSmallVec,
manifold_wide: &'a mut Convex4ContactManifoldWide,
slot_index: usize,
transform_b: Isometry3,
) -> Self {
assert!(!candidates.is_empty());
Self {
candidates,
manifold_wide,
slot_index,
transform_b,
}
}
pub fn reduce(
&mut self,
dot_axis: Vec3,
face_center_a: Point3,
face_b: &Face,
epsilon_scale: f32,
min_depth: f32,
) {
assert!(!self.candidates.is_empty());
let face_center_a_to_face_center_b = face_b.origin - face_center_a;
let base_dot = dot_axis.dot(face_center_a_to_face_center_b);
let x_dot = dot_axis.dot(face_b.tangent_x);
let y_dot = dot_axis.dot(face_b.tangent_y);
self.candidates.retain(
#[inline]
|candidate| {
let depth = base_dot + x_dot * candidate.x + y_dot * candidate.y;
if depth <= min_depth {
false
} else {
candidate.depth = depth;
true
}
},
);
if self.candidates.len() <= 4 {
for (i, candidate) in self.candidates.iter().enumerate() {
self.manifold_wide.write_slot_with_candidate_scalar(
*candidate,
self.slot_index,
i,
face_b,
self.transform_b,
);
}
return;
}
let extremity_scale = epsilon_scale * 1e-2f32;
let extremity_x = extremity_scale * 0.6_f32;
let extremity_y = extremity_scale * 0.51_f32;
let valid_iter = self.candidates.iter().enumerate();
let (best_index0, &candidate0, _) = valid_iter
.map(
#[inline]
|(i, candidate)| {
let extremity = candidate.x * extremity_x + candidate.y * extremity_y;
let mut score = candidate.depth;
if candidate.depth > 0_f32 {
score += extremity.absf();
}
(i, candidate, score)
},
)
.max_by(|(_, _, left), (_, _, right)| left.partial_cmp(right).unwrap())
.unwrap();
self.manifold_wide.write_slot_with_candidate_scalar(
candidate0,
self.slot_index,
0,
face_b,
self.transform_b,
);
remove_candidate_at(self.candidates, best_index0);
let valid_iter = self.candidates.iter().enumerate();
let (best_index1, &candidate1, max_distance_squared) = valid_iter
.map(
#[inline]
|(i, candidate)| {
let dx = candidate.x - candidate0.x;
let dy = candidate.y - candidate0.y;
let distance_squared = dx * dx + dy * dy;
(i, candidate, distance_squared)
},
)
.max_by(|(_, _, left), (_, _, right)| {
if left > right {
Ordering::Greater
} else {
Ordering::Less
}
})
.unwrap();
if max_distance_squared < 1e-6f32 * epsilon_scale * epsilon_scale {
return;
}
self.manifold_wide.write_slot_with_candidate_scalar(
candidate1,
self.slot_index,
1,
face_b,
self.transform_b,
);
remove_candidate_at(self.candidates, best_index1);
let edge_offset_x = candidate1.x - candidate0.x;
let edge_offset_y = candidate1.y - candidate0.y;
let mut min_signed_area = 0f32;
let mut max_signed_area = 0f32;
let mut best_index2 = 0usize;
let mut best_index3 = 0usize;
for i in 0..self.candidates.len() {
let candidate = self.candidates[i];
let offset_x = candidate.x - candidate0.x;
let offset_y = candidate.y - candidate0.y;
let mut signed_area = offset_x * edge_offset_y - offset_y * edge_offset_x;
if candidate.depth < 0f32 {
signed_area *= 0.25f32;
}
if signed_area < min_signed_area {
min_signed_area = signed_area;
best_index2 = i;
}
if signed_area > max_signed_area {
max_signed_area = signed_area;
best_index3 = i;
}
}
let area_epsilon = 1e-6f32 * max_distance_squared * max_distance_squared;
if min_signed_area * min_signed_area > area_epsilon {
let candidate2 = *self.candidates.get(best_index2).unwrap();
self.manifold_wide.write_slot_with_candidate_scalar(
candidate2,
self.slot_index,
2,
face_b,
self.transform_b,
);
}
if max_signed_area * max_signed_area > area_epsilon {
let candidate3 = *self.candidates.get(best_index3).unwrap();
self.manifold_wide.write_slot_with_candidate_scalar(
candidate3,
self.slot_index,
3,
face_b,
self.transform_b,
);
}
}
}
fn remove_candidate_at(candidates: &mut ManifoldCandidateScalarSmallVec, index: usize) {
let last_index = candidates.len() - 1;
if index < last_index {
candidates[index] = candidates[last_index];
}
candidates.pop();
}
#[inline]
pub fn conditional_select(
cond: bool32x4,
left: &ManifoldCandidateWide,
right: &ManifoldCandidateWide,
) -> ManifoldCandidateWide {
ManifoldCandidateWide {
x: left.x.select(cond, right.x),
y: left.y.select(cond, right.y),
depth: left.depth.select(cond, right.depth),
feature_id: left.feature_id.select(cond, right.feature_id),
}
}