use num_traits::AsPrimitive;
pub fn vtx2normal<Real>(tri2vtx: &[usize], vtx2xyz: &[Real]) -> Vec<Real>
where
Real: num_traits::Float,
{
let mut vtx2nrm = vec![Real::zero(); vtx2xyz.len()];
for node2vtx in tri2vtx.chunks(3) {
let (i0, i1, i2) = (node2vtx[0], node2vtx[1], node2vtx[2]);
let p0 = arrayref::array_ref!(vtx2xyz, i0 * 3, 3);
let p1 = arrayref::array_ref!(vtx2xyz, i1 * 3, 3);
let p2 = arrayref::array_ref!(vtx2xyz, i2 * 3, 3);
let (un, _area) = del_geo_core::tri3::unit_normal_area(p0, p1, p2);
for &i_vtx in &node2vtx[0..3] {
vtx2nrm[i_vtx * 3] = vtx2nrm[i_vtx * 3] + un[0];
vtx2nrm[i_vtx * 3 + 1] = vtx2nrm[i_vtx * 3 + 1] + un[1];
vtx2nrm[i_vtx * 3 + 2] = vtx2nrm[i_vtx * 3 + 2] + un[2];
}
}
for v in vtx2nrm.chunks_mut(3) {
del_geo_core::vec3::normalize_in_place(v.try_into().unwrap());
}
vtx2nrm
}
pub fn vtx2normal_with_mapping<Real>(
tri2vtx: &[usize],
vtx2xyz: &[Real],
vtx2ovtx: &std::collections::HashMap<usize, usize>,
ovtx2nrm: &mut [Real],
) where
Real: num_traits::Float,
{
let num_ovtx = vtx2ovtx.len();
assert_eq!(ovtx2nrm.len() / 3, num_ovtx);
ovtx2nrm.fill(Real::zero());
for node2vtx in tri2vtx.chunks(3) {
let (i0, i1, i2) = (node2vtx[0], node2vtx[1], node2vtx[2]);
let p0 = arrayref::array_ref!(vtx2xyz, i0 * 3, 3);
let p1 = arrayref::array_ref!(vtx2xyz, i1 * 3, 3);
let p2 = arrayref::array_ref!(vtx2xyz, i2 * 3, 3);
let (un, _area) = del_geo_core::tri3::unit_normal_area(p0, p1, p2);
for i_vtx in &node2vtx[0..3] {
let &i_ovtx = vtx2ovtx.get(i_vtx).unwrap();
assert!(i_ovtx < num_ovtx);
ovtx2nrm[i_ovtx * 3] = ovtx2nrm[i_ovtx * 3] + un[0];
ovtx2nrm[i_ovtx * 3 + 1] = ovtx2nrm[i_ovtx * 3 + 1] + un[1];
ovtx2nrm[i_ovtx * 3 + 2] = ovtx2nrm[i_ovtx * 3 + 2] + un[2];
}
}
for v in ovtx2nrm.chunks_mut(3) {
del_geo_core::vec3::normalize_in_place(v.try_into().unwrap());
}
}
pub fn vtx2area<T>(tri2vtx: &[usize], vtx2xyz: &[T]) -> Vec<T>
where
T: num_traits::Float + std::ops::AddAssign + std::ops::MulAssign,
{
let num_vtx = vtx2xyz.len() / 3;
let mut areas = vec![T::zero(); num_vtx];
let one_third = T::one() / (T::one() + T::one() + T::one());
for node2vtx in tri2vtx.chunks(3) {
let (i0, i1, i2) = (node2vtx[0], node2vtx[1], node2vtx[2]);
let p0 = arrayref::array_ref!(vtx2xyz, i0 * 3, 3);
let p1 = arrayref::array_ref!(vtx2xyz, i1 * 3, 3);
let p2 = arrayref::array_ref!(vtx2xyz, i2 * 3, 3);
let a0 = del_geo_core::tri3::area(p0, p1, p2) * one_third;
areas[i0] += a0;
areas[i1] += a0;
areas[i2] += a0;
}
areas
}
#[test]
fn test_vtx2area() {
let (tri2vtx, vtx2xyz) = crate::trimesh3_primitive::sphere_yup(1_f64, 128, 256);
let vtx2area = crate::trimesh3::vtx2area(&tri2vtx, &vtx2xyz);
let total_area: f64 = vtx2area.iter().sum();
assert!((total_area - std::f64::consts::PI * 4.0).abs() < 1.0e-2);
}
pub fn vtx2curvature_gaussian<T>(tri2vtx: &[usize], vtx2xyz: &[T]) -> Vec<T>
where
T: num_traits::Float + std::fmt::Debug + num_traits::FloatConst,
{
let two_pi = T::PI() * (T::one() + T::one());
let num_vtx = vtx2xyz.len() / 3;
let (vtx2idx, idx2tri) = crate::vtx2elem::from_uniform_mesh(tri2vtx, 3, num_vtx);
let mut vtx2curv = vec![T::zero(); num_vtx];
let mut total_area = T::zero();
for i_vtx in 0..num_vtx {
let mut sum_angle = T::zero();
let mut sum_area = T::zero();
for i_tri in &idx2tri[vtx2idx[i_vtx]..vtx2idx[i_vtx + 1]] {
let i_node = crate::tri2vtx::find_node_tri(&tri2vtx[i_tri * 3..i_tri * 3 + 3], i_vtx);
assert_eq!(tri2vtx[i_tri * 3 + i_node], i_vtx);
let i0_vtx = tri2vtx[i_tri * 3 + (i_node + 2) % 3];
let i1_vtx = tri2vtx[i_tri * 3 + i_node];
let i2_vtx = tri2vtx[i_tri * 3 + (i_node + 1) % 3];
let p0 = arrayref::array_ref!(vtx2xyz, i0_vtx * 3, 3);
let p1 = arrayref::array_ref!(vtx2xyz, i1_vtx * 3, 3);
let p2 = arrayref::array_ref!(vtx2xyz, i2_vtx * 3, 3);
let angle = del_geo_core::tri3::angle(p0, p1, p2);
sum_angle = sum_angle + angle;
let area = del_geo_core::tri3::area_for_2nd_node_mixed(p0, p1, p2);
sum_area = sum_area + area;
}
vtx2curv[i_vtx] = (two_pi - sum_angle) / sum_area;
total_area = total_area + sum_area;
}
dbg!(total_area);
vtx2curv
}
#[test]
fn test_vtx2curvature_gaussian() {
let (tri2vtx, vtx2xyz) = crate::trimesh3_primitive::torus_zup(1_f32, 0.3f32, 32, 32);
let vtx2curv = vtx2curvature_gaussian(&tri2vtx, &vtx2xyz);
let vtx2rgb = vtx2curv
.iter()
.flat_map(|&c| [1., (c * 0.2).clamp(-1., 1.) * 0.5 + 0.5, 0.])
.collect::<Vec<f32>>();
crate::io_wavefront_obj::save_tri2vtx_vtx2xyz_vtx2rgb(
"../target/curvature.obj",
&tri2vtx,
&vtx2xyz,
&vtx2rgb,
)
.unwrap()
}
pub fn to_corner_points<Index, Real>(
tri2vtx: &[Index],
vtx2xyz: &[Real],
i_tri: usize,
) -> ([Real; 3], [Real; 3], [Real; 3])
where
Real: Copy,
Index: num_traits::PrimInt + AsPrimitive<usize>,
{
let i0: usize = tri2vtx[i_tri * 3].as_();
let i1: usize = tri2vtx[i_tri * 3 + 1].as_();
let i2: usize = tri2vtx[i_tri * 3 + 2].as_();
(
[vtx2xyz[i0 * 3], vtx2xyz[i0 * 3 + 1], vtx2xyz[i0 * 3 + 2]],
[vtx2xyz[i1 * 3], vtx2xyz[i1 * 3 + 1], vtx2xyz[i1 * 3 + 2]],
[vtx2xyz[i2 * 3], vtx2xyz[i2 * 3 + 1], vtx2xyz[i2 * 3 + 2]],
)
}
pub fn tri2normal<T, INDEX>(tri2vtx: &[INDEX], vtx2xyz: &[T], tri2normal: &mut [T])
where
T: num_traits::Float,
INDEX: AsPrimitive<usize>,
{
assert_eq!(tri2vtx.len(), tri2normal.len());
for (i_tri, node2vtx) in tri2vtx.chunks(3).enumerate() {
let (i0, i1, i2) = (node2vtx[0].as_(), node2vtx[1].as_(), node2vtx[2].as_());
let n = del_geo_core::tri3::normal(
arrayref::array_ref!(vtx2xyz, i0 * 3, 3),
arrayref::array_ref!(vtx2xyz, i1 * 3, 3),
arrayref::array_ref!(vtx2xyz, i2 * 3, 3),
);
tri2normal[i_tri * 3] = n[0];
tri2normal[i_tri * 3 + 1] = n[1];
tri2normal[i_tri * 3 + 2] = n[2];
}
}
pub fn bwd_tri2normal<INDEX>(
tri2vtx: &[INDEX],
vtx2xyz: &[f32],
dw_tri2nrm: &[f32],
dw_vtx2xyz: &mut [f32],
) where
INDEX: num_traits::AsPrimitive<usize>,
{
assert_eq!(vtx2xyz.len(), dw_vtx2xyz.len());
assert_eq!(tri2vtx.len(), dw_tri2nrm.len());
dw_vtx2xyz.fill(0.0);
for (i_tri, node2vtx) in tri2vtx.chunks(3).enumerate() {
let (i0, i1, i2) = (node2vtx[0].as_(), node2vtx[1].as_(), node2vtx[2].as_());
let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, i0);
let p1 = crate::vtx2xyz::to_vec3(vtx2xyz, i1);
let p2 = crate::vtx2xyz::to_vec3(vtx2xyz, i2);
let dw = del_geo_core::tri3::dw_normal(p0, p1, p2);
let dw_nrm = crate::vtx2xyz::to_vec3(dw_tri2nrm, i_tri);
use del_geo_core::vec3;
let q0 = vec3::mult_mat3_col_major(dw_nrm, &dw[0]);
let q1 = vec3::mult_mat3_col_major(dw_nrm, &dw[1]);
let q2 = vec3::mult_mat3_col_major(dw_nrm, &dw[2]);
for i in 0..3 {
dw_vtx2xyz[i0 * 3 + i] += q0[i];
dw_vtx2xyz[i1 * 3 + i] += q1[i];
dw_vtx2xyz[i2 * 3 + i] += q2[i];
}
}
}
pub fn tri2area(tri2vtx: &[usize], vtx2xyz: &[f32]) -> Vec<f32> {
let num_tri = tri2vtx.len() / 3;
let mut tri2area = Vec::<f32>::with_capacity(num_tri);
for i_tri in 0..num_tri {
let area = to_tri3(tri2vtx, vtx2xyz, i_tri).area();
tri2area.push(area);
}
tri2area
}
pub fn extend_avoid_intersection(
tri2vtx: &[usize],
vtx2xyz: &[f64],
q: &[f64; 3],
step: f64,
) -> [f64; 3] {
use del_geo_core::vec3::Vec3;
let mut dq = [0f64; 3];
for node2vtx in tri2vtx.chunks(3) {
let (i0, i1, i2) = (node2vtx[0], node2vtx[1], node2vtx[2]);
let p0: &[f64; 3] = crate::vtx2xyz::to_vec3(vtx2xyz, i0);
let p1: &[f64; 3] = crate::vtx2xyz::to_vec3(vtx2xyz, i1);
let p2: &[f64; 3] = crate::vtx2xyz::to_vec3(vtx2xyz, i2);
let (_, dw) = del_geo_core::tri3::wdw_integral_of_inverse_distance_cubic(p0, p1, p2, q);
dq = dq.sub(&dw);
}
let q = q.add(&dq.normalize().scale(step));
[q[0], q[1], q[2]]
}
pub fn mean_edge_length(tri2vtx: &[usize], vtx2xyz: &[f32]) -> f32 {
let num_tri = tri2vtx.len() / 3;
let mut sum = 0_f32;
for node2vtx in tri2vtx.chunks(3) {
let (i0, i1, i2) = (node2vtx[0], node2vtx[1], node2vtx[2]);
let p0 = arrayref::array_ref![vtx2xyz, i0 * 3, 3];
let p1 = arrayref::array_ref![vtx2xyz, i1 * 3, 3];
let p2 = arrayref::array_ref![vtx2xyz, i2 * 3, 3];
sum += del_geo_core::vec3::distance(p0, p1);
sum += del_geo_core::vec3::distance(p1, p2);
sum += del_geo_core::vec3::distance(p2, p0);
}
sum / (num_tri * 3) as f32
}
pub fn distance_to_point3(tri2vtx: &[usize], vtx2xyz: &[f32], q: &[f32; 3]) -> f32 {
use del_geo_core::vec3::Vec3;
let mut dist_min = f32::MAX;
for node2vtx in tri2vtx.chunks(3) {
let (i0, i1, i2) = (node2vtx[0], node2vtx[1], node2vtx[2]);
let p0 = crate::vtx2xyz::to_vec3(vtx2xyz, i0);
let p1 = crate::vtx2xyz::to_vec3(vtx2xyz, i1);
let p2 = crate::vtx2xyz::to_vec3(vtx2xyz, i2);
let (p012, _r0, _r1) = del_geo_core::tri3::nearest_to_point3(p0, p1, p2, q);
let dist = p012.sub(q).norm();
if dist < dist_min {
dist_min = dist;
}
}
dist_min
}
pub fn distance_to_points3(tri2vtx: &[usize], vtx2xyz: &[f32], hv2xyz: &[f32]) -> f32 {
let mut max_dist = 0f32;
for i_hv in 0..hv2xyz.len() / 3 {
let min_dist = crate::trimesh3::distance_to_point3(
tri2vtx,
vtx2xyz,
hv2xyz[i_hv * 3..i_hv * 3 + 3].try_into().unwrap(),
);
if max_dist < min_dist {
max_dist = min_dist;
}
}
max_dist
}
pub fn area<T>(tri2vtx: &[usize], vtx2xyz: &[T]) -> T
where
T: num_traits::Float,
{
let mut sum_area = T::zero();
for i_tri in 0..tri2vtx.len() / 3 {
sum_area = sum_area + to_tri3(tri2vtx, vtx2xyz, i_tri).area();
}
sum_area
}
pub fn cog_and_area(tri2vtx: &[usize], vtx2xyz: &[f32]) -> Option<([f32; 3], f32)> {
use del_geo_core::vec3;
let mut sum_area = 0f32;
let mut sum_cg = [0f32; 3];
for i_tri in 0..tri2vtx.len() / 3 {
let tri = to_tri3(tri2vtx, vtx2xyz, i_tri);
let area = tri.area();
sum_area += area;
sum_cg = vec3::add(&sum_cg, &vec3::scale(&tri.cog(), area));
}
if sum_area == 0f32 {
return None;
}
let sum_cog = vec3::scale(&sum_cg, 1.0 / sum_area);
Some((sum_cog, sum_area))
}
pub fn to_tri3<'a, Index, Real>(
tri2vtx: &'a [Index],
vtx2xyz: &'a [Real],
i_tri: usize,
) -> del_geo_core::tri3::Tri3<'a, Real>
where
Index: AsPrimitive<usize>,
{
let i0: usize = tri2vtx[i_tri * 3].as_();
let i1: usize = tri2vtx[i_tri * 3 + 1].as_();
let i2: usize = tri2vtx[i_tri * 3 + 2].as_();
del_geo_core::tri3::Tri3 {
p0: arrayref::array_ref!(vtx2xyz, i0 * 3, 3),
p1: arrayref::array_ref!(vtx2xyz, i1 * 3, 3),
p2: arrayref::array_ref!(vtx2xyz, i2 * 3, 3),
}
}