pub fn convex_hull2<Real>(vtx2xy: &[[Real; 2]]) -> Vec<usize>
where
Real: num_traits::Float + std::fmt::Debug,
{
use del_geo_core::vec2::Vec2;
let p0_idx = {
let mut p0_idx;
p0_idx = 0;
let mut p0 = vtx2xy[0];
for (i, xy) in vtx2xy.iter().enumerate().skip(1) {
if xy[1] > p0[1] {
continue;
}
if xy[1] == p0[1] && xy[0] > p0[0] {
continue;
}
p0_idx = i;
p0 = *xy;
}
p0_idx
};
assert!(p0_idx < vtx2xy.len());
let mut idxcos: Vec<(usize, Real)> = vec![];
{
let x_axis = [Real::one(), Real::zero()];
for i in 0..vtx2xy.len() {
if i == p0_idx {
continue;
}
let dir = vtx2xy[i].sub(&vtx2xy[p0_idx]).normalize();
idxcos.push((i, -x_axis.dot(&dir))); }
}
{
idxcos.sort_by(|a, b| {
if a.1 != b.1 {
a.1.partial_cmp(&b.1).unwrap()
} else {
dbg!("hogehoge");
let dist_a = del_geo_core::edge2::length(&vtx2xy[a.0], &vtx2xy[p0_idx]);
let dist_b = del_geo_core::edge2::length(&vtx2xy[b.0], &vtx2xy[p0_idx]);
dist_a.partial_cmp(&dist_b).unwrap()
}
});
}
let mut point_idxs: Vec<usize> = vec![p0_idx, idxcos[0].0];
let mut stack_top = 1;
for itr in idxcos.iter().skip(1) {
let p3_idx = itr.0;
loop {
assert!(stack_top > 0);
let p1_idx = point_idxs[stack_top - 1];
let p2_idx = point_idxs[stack_top];
let p1p2 = vtx2xy[p2_idx].sub(&vtx2xy[p1_idx]);
let p1p3 = vtx2xy[p3_idx].sub(&vtx2xy[p1_idx]);
if p1p2[0] * p1p3[1] - p1p2[1] * p1p3[0] <= Real::zero() {
point_idxs.pop(); stack_top -= 1;
} else {
break;
}
}
point_idxs.push(p3_idx);
stack_top += 1;
}
point_idxs
}
#[test]
fn test_convex_hull2() {
use del_geo_core::vec2::Vec2;
use rand::SeedableRng;
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(0);
for _iter in 0..100 {
let vtx2xy: Vec<_> = (0..10)
.map(|_| del_geo_core::aabb2::sample::<_, f32>(&[-2., -2., 2., 2.], &mut rng))
.collect();
let polyloop2vtx = convex_hull2(&vtx2xy);
{
let num_points_polygon = polyloop2vtx.len();
for ip1 in 0..num_points_polygon {
let ip0 = (ip1 + num_points_polygon - 1) % num_points_polygon;
let ip2 = (ip1 + 1) % num_points_polygon;
assert_ne!(ip0, ip1);
assert_ne!(ip1, ip2);
let p1p2 = vtx2xy[polyloop2vtx[ip1]].sub(&vtx2xy[polyloop2vtx[ip0]]);
let p2p3 = vtx2xy[polyloop2vtx[ip2]].sub(&vtx2xy[polyloop2vtx[ip1]]);
let sin_val = p1p2.cross(&p2p3);
let v0 = p1p2.norm() * p2p3.norm();
assert!(sin_val > 1.0e-10 * v0);
}
}
{
let boundary_point_idx =
std::collections::BTreeSet::<usize>::from_iter(polyloop2vtx.clone());
let polygon: Vec<[_; 2]> = polyloop2vtx.iter().map(|&i_vtx| vtx2xy[i_vtx]).collect();
for i_vtx in 0..vtx2xy.len() {
if boundary_point_idx.get(&i_vtx).is_some() {
continue;
}
let p = &vtx2xy[i_vtx];
use slice_of_array::SliceFlatExt;
let polygon = polygon.flat();
let wn = crate::polyloop2::winding_number(polygon, p);
assert!((wn - 1.0).abs() < 1.0e-5);
}
}
}
}