use crate::geometry::ContactManifold;
use crate::math::Real;
use crate::utils::OrthonormalBasis;
pub(crate) fn reduce_manifold_naive(
manifold: &ContactManifold,
selected: &mut [usize; 4],
num_selected: &mut usize,
prediction_distance: Real,
) {
if manifold.points.len() <= 4 {
return;
}
*selected = [usize::MAX; 4];
let mut deepest_dist = Real::MAX;
for (i, pt) in manifold.points.iter().enumerate() {
if pt.dist < deepest_dist {
deepest_dist = pt.dist;
selected[0] = i;
}
}
if selected[0] == usize::MAX {
*num_selected = 0;
return;
}
let selected_a = manifold.points[selected[0]].local_p1;
let mut furthest_dist = -Real::MAX;
for (i, pt) in manifold.points.iter().enumerate() {
let dist = (pt.local_p1 - selected_a).length_squared();
if i != selected[0] && pt.dist <= prediction_distance && dist > furthest_dist {
furthest_dist = dist;
selected[1] = i;
}
}
if selected[1] == usize::MAX {
*num_selected = 1;
return;
}
let selected_b = manifold.points[selected[1]].local_p1;
if selected_a == selected_b {
*num_selected = 1;
return;
}
let selected_ab = selected_b - selected_a;
let tangent = selected_ab.cross(manifold.local_n1);
let mut min_dot = Real::MAX;
let mut max_dot = -Real::MAX;
for (i, pt) in manifold.points.iter().enumerate() {
if i == selected[0] || i == selected[1] || pt.dist > prediction_distance {
continue;
}
let dot = (pt.local_p1 - selected_a).dot(tangent);
if dot < min_dot {
min_dot = dot;
selected[2] = i;
}
if dot > max_dot {
max_dot = dot;
selected[3] = i;
}
}
if selected[2] == usize::MAX {
*num_selected = 2;
} else if selected[2] == selected[3] {
*num_selected = 3;
} else {
*num_selected = 4;
}
}
#[allow(dead_code)]
pub(crate) fn reduce_manifold_bepu_like(
manifold: &ContactManifold,
selected: &mut [usize; 4],
num_selected: &mut usize,
) {
if manifold.points.len() <= 4 {
return;
}
let mut best_score = -Real::MAX;
const EXTREMITY_SCALE: Real = 1e-2;
const EXTREMITY_DIR_X: Real = 0.7946898;
const EXTREMITY_DIR_Y: Real = 0.6070158;
let tangents = manifold.local_n1.orthonormal_basis();
for (i, pt) in manifold.points.iter().enumerate() {
let tx1 = pt.local_p1.dot(tangents[0]);
let ty1 = pt.local_p1.dot(tangents[1]);
let extremity = (tx1 * EXTREMITY_DIR_X + ty1 * EXTREMITY_DIR_Y).abs();
let score = if pt.dist >= 0.0 {
-pt.dist } else {
-pt.dist + extremity * EXTREMITY_SCALE
};
if score > best_score {
best_score = score;
selected[0] = i;
}
}
let contact0_pos = manifold.points[selected[0]].local_p1;
let mut max_distance_squared = 0.0;
for (i, pt) in manifold.points.iter().enumerate() {
let offset = pt.local_p1 - contact0_pos;
let offset_x = offset.dot(tangents[0]);
let offset_y = offset.dot(tangents[1]);
let distance_squared = offset_x * offset_x + offset_y * offset_y;
if distance_squared > max_distance_squared {
max_distance_squared = distance_squared;
selected[1] = i;
}
}
let epsilon = 1e-6;
if max_distance_squared <= epsilon {
*num_selected = 1;
} else {
*num_selected = 2;
selected[2] = usize::MAX;
selected[3] = usize::MAX;
let contact1_pos = manifold.points[selected[1]].local_p1;
let edge_offset = contact1_pos - contact0_pos;
let edge_offset_x = edge_offset.dot(tangents[0]);
let edge_offset_y = edge_offset.dot(tangents[1]);
let mut min_signed_area = 0.0;
let mut max_signed_area = 0.0;
for (i, pt) in manifold.points.iter().enumerate() {
let candidate_offset = pt.local_p1 - contact0_pos;
let candidate_offset_x = candidate_offset.dot(tangents[0]);
let candidate_offset_y = candidate_offset.dot(tangents[1]);
let mut signed_area =
candidate_offset_x * edge_offset_y - candidate_offset_y * edge_offset_x;
if pt.dist >= 0.0 {
signed_area *= 0.25;
}
if signed_area < min_signed_area {
min_signed_area = signed_area;
selected[2] = i;
}
if signed_area > max_signed_area {
max_signed_area = signed_area;
selected[3] = i;
}
}
let area_epsilon = max_distance_squared * max_distance_squared * 1e-6;
if min_signed_area * min_signed_area <= area_epsilon {
selected[2] = usize::MAX;
}
if max_signed_area * max_signed_area <= area_epsilon {
selected[3] = usize::MAX;
}
let keep2 = selected[2] != usize::MAX;
let keep3 = selected[3] != usize::MAX;
*num_selected += keep2 as usize + keep3 as usize;
if !keep2 {
selected[2] = selected[3];
}
}
}