use std::collections::{HashMap, HashSet};
use crate::{
FaceVaryingChannel, FaceVaryingInterpolation, LineageMaps, Mesh, StencilTable, VertexOrigin,
};
fn edge_key(a: u32, b: u32) -> (u32, u32) {
if a <= b { (a, b) } else { (b, a) }
}
pub(crate) type Sparse = Vec<(u32, f32)>;
pub(crate) fn merge(stencil: &mut Sparse, source: &[(u32, f32)], w: f32) {
source.iter().for_each(|&(idx, sw)| {
if let Some(entry) = stencil.iter_mut().find(|(i, _)| *i == idx) {
entry.1 += sw * w;
} else {
stencil.push((idx, sw * w));
}
});
}
pub(crate) fn pack(stencils: &[Sparse]) -> StencilTable {
let mut offsets = Vec::with_capacity(stencils.len() + 1);
let mut indices = Vec::new();
let mut weights = Vec::new();
offsets.push(0u32);
stencils.iter().for_each(|s| {
s.iter().for_each(|&(idx, w)| {
indices.push(idx);
weights.push(w);
});
offsets.push(indices.len() as u32);
});
StencilTable {
offsets,
indices,
weights,
}
}
pub(crate) fn face_offsets(topo: &Mesh) -> Vec<usize> {
std::iter::once(0)
.chain(topo.face_vertex_counts.iter().scan(0usize, |acc, &c| {
*acc += c as usize;
Some(*acc)
}))
.collect()
}
fn fvar_in_face(
parent_mesh: &Mesh,
p_off: &[usize],
channel: &FaceVaryingChannel,
f: usize,
v: u32,
) -> Option<u32> {
let n = parent_mesh.face_vertex_counts[f] as usize;
let s = p_off[f];
parent_mesh.face_vertex_indices[s..s + n]
.iter()
.position(|&u| u == v)
.map(|k| channel.indices[s + k])
}
pub(crate) fn all_linear_via_origin(
parent_mesh: &Mesh,
parent_edge_vertices: &[[u32; 2]],
child_mesh: &Mesh,
child_lineage: &LineageMaps,
channel: &FaceVaryingChannel,
) -> Vec<Sparse> {
let p_off = face_offsets(parent_mesh);
let c_off = face_offsets(child_mesh);
let mut out = Vec::with_capacity(child_mesh.face_vertex_indices.len());
for cf in 0..child_mesh.face_vertex_counts.len() {
let home = child_lineage.face_parent[cf] as usize;
let n = child_mesh.face_vertex_counts[cf] as usize;
let s = c_off[cf];
for k in 0..n {
let cv = child_mesh.face_vertex_indices[s + k];
let stencil = match child_lineage.vertex_origin[cv as usize] {
VertexOrigin::Vertex(v) => {
let fv = fvar_in_face(parent_mesh, &p_off, channel, home, v).unwrap_or(v);
vec![(fv, 1.0)]
}
VertexOrigin::Edge(e) => {
let [a, b] = parent_edge_vertices[e as usize];
let fa = fvar_in_face(parent_mesh, &p_off, channel, home, a);
let fb = fvar_in_face(parent_mesh, &p_off, channel, home, b);
match (fa, fb) {
(Some(fa), Some(fb)) => vec![(fa, 0.5), (fb, 0.5)],
_ => vec![(fa.or(fb).unwrap_or(a), 1.0)],
}
}
VertexOrigin::Face(f) => {
let f = f as usize;
let n2 = parent_mesh.face_vertex_counts[f] as usize;
let s2 = p_off[f];
let w = 1.0 / n2 as f32;
(0..n2).map(|j| (channel.indices[s2 + j], w)).collect()
}
};
out.push(stencil);
}
}
out
}
pub(crate) fn all_linear_copy_corners(
child_mesh: &Mesh,
channel: &FaceVaryingChannel,
) -> Vec<Sparse> {
child_mesh
.face_vertex_indices
.iter()
.map(|&cv| vec![(channel.indices[cv as usize], 1.0)])
.collect()
}
pub(crate) fn detect_seams(
parent_mesh: &Mesh,
parent_edge_vertices: &[[u32; 2]],
channel: &FaceVaryingChannel,
p_off: &[usize],
) -> (Vec<bool>, Vec<bool>) {
let mut edge_faces: HashMap<(u32, u32), Vec<usize>> = HashMap::new();
for fi in 0..parent_mesh.face_vertex_counts.len() {
let n = parent_mesh.face_vertex_counts[fi] as usize;
let s = p_off[fi];
for k in 0..n {
let a = parent_mesh.face_vertex_indices[s + k];
let b = parent_mesh.face_vertex_indices[s + (k + 1) % n];
edge_faces.entry(edge_key(a, b)).or_default().push(fi);
}
}
let edge_seams: Vec<bool> = parent_edge_vertices
.iter()
.map(|&[v0, v1]| match edge_faces.get(&edge_key(v0, v1)) {
Some(fs) if fs.len() >= 2 => {
let f = |fi: usize, v: u32| fvar_in_face(parent_mesh, p_off, channel, fi, v);
f(fs[0], v0) != f(fs[1], v0) || f(fs[0], v1) != f(fs[1], v1)
}
_ => true, })
.collect();
let mut vert_boundary = vec![false; parent_mesh.vertex_count as usize];
for (ei, &seam) in edge_seams.iter().enumerate() {
if seam {
let [a, b] = parent_edge_vertices[ei];
vert_boundary[a as usize] = true;
vert_boundary[b as usize] = true;
}
}
(edge_seams, vert_boundary)
}
struct Islands {
face_island: Vec<u32>,
vert_chart_value: HashMap<(u32, u32), u32>,
}
impl Islands {
fn build(
parent_mesh: &Mesh,
parent_edge_vertices: &[[u32; 2]],
channel: &FaceVaryingChannel,
edge_seams: &[bool],
p_off: &[usize],
) -> Self {
let face_count = parent_mesh.face_vertex_counts.len();
let mut edge_faces: HashMap<(u32, u32), Vec<usize>> = HashMap::new();
for fi in 0..face_count {
let n = parent_mesh.face_vertex_counts[fi] as usize;
let s = p_off[fi];
for k in 0..n {
let a = parent_mesh.face_vertex_indices[s + k];
let b = parent_mesh.face_vertex_indices[s + (k + 1) % n];
edge_faces.entry(edge_key(a, b)).or_default().push(fi);
}
}
let seam_keys: HashSet<(u32, u32)> = parent_edge_vertices
.iter()
.enumerate()
.filter(|&(e, _)| edge_seams[e])
.map(|(_, &[a, b])| edge_key(a, b))
.collect();
let mut uf: Vec<usize> = (0..face_count).collect();
for (key, faces) in &edge_faces {
if faces.len() == 2 && !seam_keys.contains(key) {
let ra = uf_find(&mut uf, faces[0]);
let rb = uf_find(&mut uf, faces[1]);
if ra != rb {
uf[ra] = rb;
}
}
}
let face_island: Vec<u32> = (0..face_count)
.map(|f| uf_find(&mut uf, f) as u32)
.collect();
let mut vert_chart_value = HashMap::new();
for fi in 0..face_count {
let chart = face_island[fi];
let n = parent_mesh.face_vertex_counts[fi] as usize;
let s = p_off[fi];
for k in 0..n {
let v = parent_mesh.face_vertex_indices[s + k];
vert_chart_value
.entry((v, chart))
.or_insert(channel.indices[s + k]);
}
}
Islands {
face_island,
vert_chart_value,
}
}
fn value_of(&self, v: u32, chart: u32) -> Option<u32> {
self.vert_chart_value.get(&(v, chart)).copied()
}
}
fn uf_find(uf: &mut [usize], mut x: usize) -> usize {
while uf[x] != x {
uf[x] = uf[uf[x]];
x = uf[x];
}
x
}
pub(crate) fn smooth_modes(
parent_mesh: &Mesh,
parent_edge_vertices: &[[u32; 2]],
child_mesh: &Mesh,
child_lineage: &LineageMaps,
pos_stencils: &StencilTable,
channel: &FaceVaryingChannel,
mode: FaceVaryingInterpolation,
) -> Vec<Sparse> {
let p_off = face_offsets(parent_mesh);
let (edge_seams, _vert_boundary) =
detect_seams(parent_mesh, parent_edge_vertices, channel, &p_off);
let mut seam_neighbors: Vec<Vec<u32>> = vec![Vec::new(); parent_mesh.vertex_count as usize];
for (e, &seam) in edge_seams.iter().enumerate() {
if seam {
let [a, b] = parent_edge_vertices[e];
seam_neighbors[a as usize].push(b);
seam_neighbors[b as usize].push(a);
}
}
let islands = Islands::build(
parent_mesh,
parent_edge_vertices,
channel,
&edge_seams,
&p_off,
);
let mut value_faces = vec![0u32; channel.value_count as usize];
for &fv in &channel.indices {
value_faces[fv as usize] += 1;
}
let remap = |cv: usize, home: usize, chart: u32| -> Sparse {
let start = pos_stencils.offsets[cv] as usize;
let end = pos_stencils.offsets[cv + 1] as usize;
let mut sten = Sparse::new();
for r in start..end {
let u = pos_stencils.indices[r];
let fu = islands
.value_of(u, chart)
.or_else(|| fvar_in_face(parent_mesh, &p_off, channel, home, u))
.unwrap_or(u);
merge(&mut sten, &[(fu, 1.0)], pos_stencils.weights[r]);
}
sten
};
let c_off = face_offsets(child_mesh);
let mut out = Vec::with_capacity(child_mesh.face_vertex_indices.len());
for cf in 0..child_mesh.face_vertex_counts.len() {
let home = child_lineage.face_parent[cf] as usize;
let chart = islands.face_island[home];
let n = child_mesh.face_vertex_counts[cf] as usize;
let s = c_off[cf];
for k in 0..n {
let cv = child_mesh.face_vertex_indices[s + k] as usize;
let stencil = match child_lineage.vertex_origin[cv] {
VertexOrigin::Edge(e) if edge_seams[e as usize] => {
let [a, b] = parent_edge_vertices[e as usize];
let fa = fvar_in_face(parent_mesh, &p_off, channel, home, a);
let fb = fvar_in_face(parent_mesh, &p_off, channel, home, b);
match (fa, fb) {
(Some(fa), Some(fb)) => vec![(fa, 0.5), (fb, 0.5)],
_ => vec![(fa.or(fb).unwrap_or(a), 1.0)],
}
}
VertexOrigin::Vertex(v) if !seam_neighbors[v as usize].is_empty() => {
seam_vertex_stencil(
parent_mesh,
&p_off,
channel,
&islands,
&seam_neighbors[v as usize],
&value_faces,
home,
chart,
v,
mode,
)
}
VertexOrigin::Face(f) => remap(cv, f as usize, islands.face_island[f as usize]),
_ => remap(cv, home, chart),
};
out.push(stencil);
}
}
out
}
#[allow(clippy::too_many_arguments)]
fn seam_vertex_stencil(
parent_mesh: &Mesh,
p_off: &[usize],
channel: &FaceVaryingChannel,
islands: &Islands,
neighbors: &[u32],
value_faces: &[u32],
home: usize,
chart: u32,
v: u32,
mode: FaceVaryingInterpolation,
) -> Sparse {
let fv_home = fvar_in_face(parent_mesh, p_off, channel, home, v).unwrap_or(v);
let pin = vec![(fv_home, 1.0)];
let crease_curve = || -> Option<Sparse> {
if neighbors.len() != 2 {
return None;
}
let f1 = islands.value_of(neighbors[0], chart)?;
let f2 = islands.value_of(neighbors[1], chart)?;
let mut s = Sparse::new();
merge(&mut s, &[(fv_home, 0.75)], 1.0);
merge(&mut s, &[(f1, 0.125)], 1.0);
merge(&mut s, &[(f2, 0.125)], 1.0);
Some(s)
};
match mode {
FaceVaryingInterpolation::SmoothWithLinearBoundaries | FaceVaryingInterpolation::Linear => {
pin
}
FaceVaryingInterpolation::SmoothWithLinearCorners if value_faces[fv_home as usize] <= 1 => {
pin
}
FaceVaryingInterpolation::Smooth | FaceVaryingInterpolation::SmoothWithLinearCorners => {
crease_curve().unwrap_or(pin)
}
}
}
pub(crate) fn smooth_doo_sabin(
parent_mesh: &Mesh,
child_mesh: &Mesh,
pos_stencils: &StencilTable,
channel: &FaceVaryingChannel,
) -> Vec<Sparse> {
let p_off = face_offsets(parent_mesh);
let mut owner = vec![0usize; *p_off.last().unwrap_or(&0)];
for fi in 0..parent_mesh.face_vertex_counts.len() {
for corner in p_off[fi]..p_off[fi + 1] {
owner[corner] = fi;
}
}
child_mesh
.face_vertex_indices
.iter()
.map(|&cv| {
let cv = cv as usize;
let fi = owner[cv];
let start = pos_stencils.offsets[cv] as usize;
let end = pos_stencils.offsets[cv + 1] as usize;
let mut sten = Sparse::new();
for r in start..end {
let u = pos_stencils.indices[r];
let fu = fvar_in_face(parent_mesh, &p_off, channel, fi, u).unwrap_or(u);
merge(&mut sten, &[(fu, 1.0)], pos_stencils.weights[r]);
}
sten
})
.collect()
}