use crate::core::cell::Cell;
use crate::core::collections::{MAX_PRACTICAL_DIMENSION_SIZE, SmallBuffer};
use crate::core::traits::DataType;
use crate::core::triangulation_data_structure::{Tds, VertexKey};
use crate::geometry::point::Point;
use crate::geometry::traits::coordinate::CoordinateScalar;
use slotmap::Key;
pub fn sorted_cell_points<T, U, V, const D: usize>(
tds: &Tds<T, U, V, D>,
cell: &Cell<T, U, V, D>,
) -> Option<SmallBuffer<Point<T, D>, MAX_PRACTICAL_DIMENSION_SIZE>>
where
T: CoordinateScalar,
U: DataType,
V: DataType,
{
let mut keys: SmallBuffer<VertexKey, MAX_PRACTICAL_DIMENSION_SIZE> =
cell.vertices().iter().copied().collect();
keys.sort_unstable_by_key(|vk| vk.data().as_ffi());
let mut points = SmallBuffer::with_capacity(keys.len());
for &vk in &keys {
points.push(*tds.get_vertex_by_key(vk)?.point());
}
Some(points)
}
pub fn sorted_facet_points_with_extra<T, U, V, const D: usize>(
tds: &Tds<T, U, V, D>,
facet_keys: &[VertexKey],
extra: Point<T, D>,
) -> Option<SmallBuffer<Point<T, D>, MAX_PRACTICAL_DIMENSION_SIZE>>
where
T: CoordinateScalar,
U: DataType,
V: DataType,
{
let mut sorted_keys: SmallBuffer<VertexKey, MAX_PRACTICAL_DIMENSION_SIZE> =
facet_keys.iter().copied().collect();
sorted_keys.sort_unstable_by_key(|vk| vk.data().as_ffi());
let mut points = SmallBuffer::with_capacity(sorted_keys.len() + 1);
for &vk in &sorted_keys {
points.push(*tds.get_vertex_by_key(vk)?.point());
}
points.push(extra);
Some(points)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::traits::coordinate::Coordinate;
fn build_tds_with_points<const D: usize>(
coords: &[[f64; D]],
) -> (Tds<f64, (), (), D>, Vec<VertexKey>) {
let mut tds = Tds::<f64, (), (), D>::empty();
let mut keys = Vec::with_capacity(coords.len());
for c in coords {
let v = crate::core::vertex::VertexBuilder::<_, (), _>::default()
.point(Point::new(*c))
.build()
.expect("vertex build should succeed");
let vk = tds
.insert_vertex_with_mapping(v)
.expect("insert should succeed");
keys.push(vk);
}
(tds, keys)
}
#[test]
fn test_sorted_cell_points_produces_canonical_order() {
let (mut tds, keys) = build_tds_with_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
let cell = Cell::new(keys.clone(), None::<()>).expect("cell should be valid");
let cell_key = tds
.insert_cell_with_mapping(cell)
.expect("insert should succeed");
let cell_ref = tds.get_cell(cell_key).unwrap();
let points = sorted_cell_points(&tds, cell_ref).expect("should resolve all vertices");
let mut sorted_keys = keys;
sorted_keys.sort_unstable_by_key(|vk| vk.data().as_ffi());
for (i, &vk) in sorted_keys.iter().enumerate() {
let expected = *tds.get_vertex_by_key(vk).unwrap().point();
assert_eq!(points[i], expected);
}
}
#[test]
fn test_sorted_cell_points_permutation_invariant() {
let (mut tds, keys) = build_tds_with_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
let cell_a =
Cell::new(vec![keys[0], keys[1], keys[2]], None::<()>).expect("cell should be valid");
let cell_b =
Cell::new(vec![keys[2], keys[0], keys[1]], None::<()>).expect("cell should be valid");
let key_a = tds
.insert_cell_with_mapping(cell_a)
.expect("insert should succeed");
let key_b = tds
.insert_cell_with_mapping(cell_b)
.expect("insert should succeed");
let points_a = sorted_cell_points(&tds, tds.get_cell(key_a).unwrap()).unwrap();
let points_b = sorted_cell_points(&tds, tds.get_cell(key_b).unwrap()).unwrap();
assert_eq!(points_a.as_slice(), points_b.as_slice());
}
#[test]
fn test_sorted_facet_points_with_extra_appends_at_end() {
let (tds, keys) = build_tds_with_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
let facet_keys = &[keys[0], keys[1]];
let extra = Point::new([0.5, 0.5]);
let points =
sorted_facet_points_with_extra(&tds, facet_keys, extra).expect("should resolve");
assert_eq!(points.len(), 3);
assert_eq!(points[2], extra);
}
#[test]
fn test_sorted_facet_points_with_extra_permutation_invariant() {
let (tds, keys) = build_tds_with_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
let extra = Point::new([0.5, 0.5]);
let points_a = sorted_facet_points_with_extra(&tds, &[keys[0], keys[1]], extra).unwrap();
let points_b = sorted_facet_points_with_extra(&tds, &[keys[1], keys[0]], extra).unwrap();
assert_eq!(points_a.as_slice(), points_b.as_slice());
}
#[test]
fn test_canonical_insphere_permutation_invariant_2d() {
use crate::geometry::kernel::{AdaptiveKernel, Kernel};
let (mut tds, keys) = build_tds_with_points(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
let test_point = Point::new([1.0, 1.0]);
let kernel = AdaptiveKernel::<f64>::new();
let permutations: [[usize; 3]; 6] = [
[0, 1, 2],
[0, 2, 1],
[1, 0, 2],
[1, 2, 0],
[2, 0, 1],
[2, 1, 0],
];
let mut signs = Vec::new();
for perm in &permutations {
let cell = Cell::new(
vec![keys[perm[0]], keys[perm[1]], keys[perm[2]]],
None::<()>,
)
.expect("cell should be valid");
let cell_key = tds
.insert_cell_with_mapping(cell)
.expect("insert should succeed");
let sorted = sorted_cell_points(&tds, tds.get_cell(cell_key).unwrap()).unwrap();
let sign = kernel.in_sphere(&sorted, &test_point).unwrap();
signs.push(sign);
}
assert!(
signs.iter().all(|&s| s == signs[0]),
"canonical sorting must make insphere permutation-invariant: {signs:?}"
);
}
#[test]
fn test_canonical_insphere_permutation_invariant_3d() {
use crate::geometry::kernel::{AdaptiveKernel, Kernel};
let (mut tds, keys) = build_tds_with_points(&[
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
]);
let test_point = Point::new([1.0, 1.0, 1.0]);
let kernel = AdaptiveKernel::<f64>::new();
#[rustfmt::skip]
let perms: [[usize; 4]; 24] = [
[0,1,2,3], [0,1,3,2], [0,2,1,3], [0,2,3,1], [0,3,1,2], [0,3,2,1],
[1,0,2,3], [1,0,3,2], [1,2,0,3], [1,2,3,0], [1,3,0,2], [1,3,2,0],
[2,0,1,3], [2,0,3,1], [2,1,0,3], [2,1,3,0], [2,3,0,1], [2,3,1,0],
[3,0,1,2], [3,0,2,1], [3,1,0,2], [3,1,2,0], [3,2,0,1], [3,2,1,0],
];
let mut signs = Vec::new();
for perm in &perms {
let cell = Cell::new(
vec![keys[perm[0]], keys[perm[1]], keys[perm[2]], keys[perm[3]]],
None::<()>,
)
.expect("cell should be valid");
let cell_key = tds
.insert_cell_with_mapping(cell)
.expect("insert should succeed");
let sorted = sorted_cell_points(&tds, tds.get_cell(cell_key).unwrap()).unwrap();
let sign = kernel.in_sphere(&sorted, &test_point).unwrap();
signs.push(sign);
}
assert!(
signs.iter().all(|&s| s == signs[0]),
"canonical sorting must make 3D insphere permutation-invariant: {signs:?}"
);
}
}