use crate::Mesh;
use crate::SchemeOptions;
use crate::sharpness::{
auto_corner_sharpness, merge, sharp_vertex_stencil_osd, sharpness_to_blend,
};
use super::super::topology::Topology;
use super::sparse::Sparse;
pub(super) fn face_vertex_stencils(
topology: &Topology,
topo_mesh: &Mesh,
face_selected: &[bool],
effective_edge_creases: &[f32],
options: &SchemeOptions,
) -> Vec<Sparse> {
let face_count = topology.faces.len();
let vertex_count = topo_mesh.vertex_count as usize;
let sharp_vertex_data =
compute_sharp_vertex_data(vertex_count, topology, options, effective_edge_creases);
let total_verts: usize = (0..face_count).map(|fi| topology.faces.row_len(fi)).sum();
let mut all_stencils: Vec<Sparse> = Vec::with_capacity(total_verts);
for fi in 0..face_count {
let fv = topology.faces.row(fi);
let n = fv.len();
let n_f32 = n as f32;
for i in 0..n {
let vi = fv[i] as usize;
let smooth_stencil: Sparse = if face_selected[fi] {
(0..n)
.map(|j| {
let weight = if i == j {
(n_f32 + 5.0) / (4.0 * n_f32)
} else {
let angle =
std::f32::consts::TAU * ((i as f32 - j as f32).abs()) / n_f32;
(3.0 + 2.0 * angle.cos()) / (4.0 * n_f32)
};
(fv[j], weight)
})
.collect()
} else {
vec![(fv[i], 1.0)]
};
let stencil = if let Some(sharp) = &sharp_vertex_data[vi] {
blend_toward_sharp(&smooth_stencil, &sharp.stencil, sharp.blend_factor)
} else {
let corner =
effective_corner(vi, topo_mesh, options, effective_edge_creases, topology);
if corner > 0.0 {
let identity: Sparse = vec![(fv[i], 1.0)];
let blend = sharpness_to_blend(corner);
let mut result = Sparse::new();
merge(&mut result, &smooth_stencil, 1.0 - blend);
merge(&mut result, &identity, blend);
result
} else {
smooth_stencil
}
};
all_stencils.push(stencil);
}
}
all_stencils
}
struct SharpVertexData {
stencil: Sparse,
blend_factor: f32,
}
fn compute_sharp_vertex_data(
vertex_count: usize,
topology: &Topology,
options: &SchemeOptions,
effective_edge_creases: &[f32],
) -> Vec<Option<SharpVertexData>> {
(0..vertex_count)
.map(|vi| {
let sharp_neighbors: Vec<(u32, f32)> = topology
.vertex_edges
.row(vi)
.iter()
.filter_map(|&ei| {
let crease = effective_edge_creases[ei as usize];
if crease > 0.0 {
let [a, b] = topology.edge_vertices[ei as usize];
let other = if a as usize == vi { b } else { a };
Some((other, crease))
} else {
None
}
})
.collect();
let fully_sharp_count = sharp_neighbors.iter().filter(|(_, c)| *c >= 1.0).count();
if fully_sharp_count < 2 {
return None;
}
let smooth: Sparse = vec![(vi as u32, 1.0)];
let neighbors: Vec<u32> = sharp_neighbors.iter().map(|&(n, _)| n).collect();
let creases: Vec<f32> = sharp_neighbors.iter().map(|&(_, c)| c).collect();
let sharp_stencil =
sharp_vertex_stencil_osd(vi as u32, &smooth, &smooth, 0.0, &neighbors, &creases);
let corner = effective_corner_raw(vi, topology, options, effective_edge_creases);
let final_stencil = if corner > 0.0 {
let identity: Sparse = vec![(vi as u32, 1.0)];
let blend = sharpness_to_blend(corner);
let mut result = Sparse::new();
merge(&mut result, &sharp_stencil, 1.0 - blend);
merge(&mut result, &identity, blend);
result
} else {
sharp_stencil
};
let blend_factor = sharp_neighbors
.iter()
.map(|(_, c)| *c)
.fold(f32::INFINITY, f32::min)
.min(1.0);
Some(SharpVertexData {
stencil: final_stencil,
blend_factor,
})
})
.collect()
}
fn effective_corner(
vi: usize,
topo: &Mesh,
options: &SchemeOptions,
effective_edge_creases: &[f32],
topology: &Topology,
) -> f32 {
effective_corner_raw(vi, topology, options, effective_edge_creases).max(topo.vertex_corners[vi])
}
fn effective_corner_raw(
vi: usize,
topology: &Topology,
options: &SchemeOptions,
effective_edge_creases: &[f32],
) -> f32 {
if !options.auto_corner {
return 0.0;
}
let incident_creases: Vec<f32> = topology
.vertex_edges
.row(vi)
.iter()
.map(|&ei| effective_edge_creases[ei as usize])
.collect();
auto_corner_sharpness(&incident_creases)
}
fn blend_toward_sharp(smooth: &Sparse, sharp: &Sparse, blend_factor: f32) -> Sparse {
let mut result = Sparse::new();
merge(&mut result, smooth, 1.0 - blend_factor);
merge(&mut result, sharp, blend_factor);
result
}