use enum_dispatch::enum_dispatch;
use crate::catmull_clark::stencils::{
CcLevelData, base_level_data as cc_base_level_data,
refine_topology_once as cc_refine_topology_once,
vertex_stencils_from_level as cc_vertex_stencils_from_level,
};
use crate::doo_sabin::stencils::{
DooSabinLevelData, base_level_data as doo_sabin_base_level_data,
refine_topology_once as doo_sabin_refine_topology_once,
vertex_stencils_from_level as doo_sabin_vertex_stencils_from_level,
};
use crate::loop_subdivision::stencils::{
LoopLevelData, base_level_data as loop_base_level_data,
refine_topology_once as loop_refine_topology_once,
vertex_stencils_from_level as loop_vertex_stencils_from_level,
};
use crate::sqrt3::stencils::{
Sqrt3LevelData, base_level_data as sqrt3_base_level_data,
refine_topology_once as sqrt3_refine_topology_once,
vertex_stencils_from_level as sqrt3_vertex_stencils_from_level,
};
use crate::{
Adjacency, FaceVaryingChannel, FaceVaryingInterpolation, KernelError, LineageMaps, Mesh,
RefinementResult, Scheme, SchemeOptions, StencilTable, UniformRefine,
};
#[enum_dispatch]
pub(crate) trait LevelDataCommon {
fn mesh(&self) -> &Mesh;
fn lineage(&self) -> &LineageMaps;
fn face_selected(&self) -> &[bool];
fn adjacency(&self) -> &Adjacency;
}
#[enum_dispatch(LevelDataCommon)]
pub(crate) enum LevelData {
Cc(CcLevelData),
Loop(LoopLevelData),
Sqrt3(Sqrt3LevelData),
DooSabin(DooSabinLevelData),
}
pub struct Refiner {
topology: Mesh,
scheme: Scheme,
options: SchemeOptions,
}
#[must_use]
pub struct Refinement {
levels: Vec<LevelData>,
scheme: Scheme,
options: SchemeOptions,
edge_polylines: Option<Vec<Vec<u32>>>,
}
#[non_exhaustive]
pub struct RefinedFinalParts {
pub topology: Mesh,
pub lineage: LineageMaps,
pub adjacency: Adjacency,
pub selected_faces: Option<Vec<bool>>,
pub edge_polylines: Option<Vec<Vec<u32>>>,
}
impl Refinement {
#[must_use]
pub fn scheme(&self) -> Scheme {
self.scheme
}
pub fn vertex_stencils(&self) -> Vec<StencilTable> {
let parent_count = self.levels.len().saturating_sub(1);
(0..parent_count)
.map(|k| match &self.levels[k] {
LevelData::Cc(parent) => cc_vertex_stencils_from_level(parent, &self.options),
LevelData::Loop(parent) => loop_vertex_stencils_from_level(parent, &self.options),
LevelData::Sqrt3(parent) => sqrt3_vertex_stencils_from_level(parent, &self.options),
LevelData::DooSabin(parent) => {
doo_sabin_vertex_stencils_from_level(parent, &self.options)
}
})
.collect()
}
pub fn face_varying_stencils(
&self,
channel: &FaceVaryingChannel,
mode: FaceVaryingInterpolation,
) -> Result<Vec<StencilTable>, KernelError> {
let level_count = self.levels.len().saturating_sub(1);
let mut tables = Vec::with_capacity(level_count);
let mut current_fvar = channel.clone();
for i in 0..level_count {
let table = match (&self.levels[i], &self.levels[i + 1]) {
(LevelData::Cc(parent), LevelData::Cc(child)) => {
crate::catmull_clark::face_varying::fvar_stencils_once(
parent,
child,
¤t_fvar,
mode,
&self.options,
)?
}
(LevelData::Loop(parent), LevelData::Loop(child)) => {
crate::loop_subdivision::face_varying::fvar_stencils_once(
parent,
child,
¤t_fvar,
mode,
&self.options,
)?
}
(LevelData::Sqrt3(parent), LevelData::Sqrt3(child)) => {
crate::sqrt3::face_varying::fvar_stencils_once(
parent,
child,
¤t_fvar,
mode,
&self.options,
)?
}
(LevelData::DooSabin(parent), LevelData::DooSabin(child)) => {
crate::doo_sabin::face_varying::fvar_stencils_once(
parent,
child,
¤t_fvar,
mode,
&self.options,
)?
}
_ => unreachable!("a refinement chain holds one scheme's level data throughout"),
};
tables.push(table);
current_fvar = identity_channel(self.levels[i + 1].mesh());
}
Ok(tables)
}
pub fn final_topology(&self) -> &Mesh {
self.levels.last().expect("at least one level").mesh()
}
pub fn lineage(&self) -> &LineageMaps {
self.levels.last().expect("at least one level").lineage()
}
pub fn adjacency(&self) -> &Adjacency {
self.levels.last().expect("at least one level").adjacency()
}
pub fn edge_polylines(&self) -> Option<&[Vec<u32>]> {
self.edge_polylines.as_deref()
}
pub fn selected_faces(&self) -> Option<&[bool]> {
if self.levels.len() <= 1 {
return None;
}
Some(
self.levels
.last()
.expect("at least one level")
.face_selected(),
)
}
pub fn refinement_steps(&self) -> usize {
self.levels.len().saturating_sub(1)
}
pub fn level_lineage(&self, step: usize) -> Option<&LineageMaps> {
self.levels.get(step + 1).map(|level| level.lineage())
}
pub fn into_final_parts(mut self) -> RefinedFinalParts {
let last = self.levels.pop().expect("at least one level");
let refinement_ran = !self.levels.is_empty();
match last {
LevelData::Cc(level) => RefinedFinalParts {
topology: level.mesh,
lineage: level.lineage,
adjacency: level.adjacency,
selected_faces: refinement_ran.then_some(level.face_selected),
edge_polylines: self.edge_polylines,
},
LevelData::Loop(level) => RefinedFinalParts {
topology: level.mesh,
lineage: level.lineage,
adjacency: level.adjacency,
selected_faces: refinement_ran.then_some(level.face_selected),
edge_polylines: self.edge_polylines,
},
LevelData::Sqrt3(level) => RefinedFinalParts {
topology: level.mesh,
lineage: level.lineage,
adjacency: level.adjacency,
selected_faces: refinement_ran.then_some(level.face_selected),
edge_polylines: self.edge_polylines,
},
LevelData::DooSabin(level) => RefinedFinalParts {
topology: level.mesh,
lineage: level.lineage,
adjacency: level.adjacency,
selected_faces: refinement_ran.then_some(level.face_selected),
edge_polylines: self.edge_polylines,
},
}
}
}
impl Refiner {
pub fn new(
topology: Mesh,
scheme: Scheme,
options: SchemeOptions,
) -> Result<Self, KernelError> {
topology.validate()?;
Ok(Self {
topology,
scheme,
options,
})
}
pub fn topology(&self) -> &Mesh {
&self.topology
}
pub fn scheme(&self) -> Scheme {
self.scheme
}
pub fn options(&self) -> &SchemeOptions {
&self.options
}
pub fn refine_topology(&self, req: &UniformRefine) -> Result<Refinement, KernelError> {
match self.scheme {
Scheme::CatmullClark => self.refine_topology_cc(req),
Scheme::Loop => self.refine_topology_loop(req),
Scheme::Sqrt3 => self.refine_topology_sqrt3(req),
Scheme::DooSabin => self.refine_topology_doo_sabin(req),
}
}
fn active_selection(&self, req: &UniformRefine) -> Result<Vec<bool>, KernelError> {
let initial_face_count = self.topology.face_vertex_counts.len();
req.selected_faces
.as_ref()
.map(|m| {
(m.len() == initial_face_count).then(|| m.clone()).ok_or(
KernelError::InvalidTopology(
"selected-face mask length does not match face count",
),
)
})
.transpose()
.map(|opt| opt.unwrap_or_else(|| vec![true; initial_face_count]))
}
fn refine_topology_cc(&self, req: &UniformRefine) -> Result<Refinement, KernelError> {
let active_sel = self.active_selection(req)?;
let base = cc_base_level_data(&self.topology, active_sel, req.selection_boundary_crease)?;
let mut levels: Vec<LevelData> = Vec::with_capacity(req.levels.get() as usize + 1);
levels.push(LevelData::Cc(base));
let mut polylines = req.edge_polylines.then(|| {
self.topology
.edge_vertices
.iter()
.map(|&[v0, v1]| vec![v0, v1])
.collect::<Vec<_>>()
});
for _ in 0..req.levels.get() {
let parent = match levels.last().unwrap() {
LevelData::Cc(p) => p,
_ => unreachable!("CC refine push-chain only touches CC variants"),
};
let child =
cc_refine_topology_once(parent, &self.options, req.selection_boundary_crease)?;
if let Some(ref mut polys) = polylines {
Self::refine_polylines(polys, &child.lineage, &parent.mesh);
}
levels.push(LevelData::Cc(child));
}
Ok(Refinement {
levels,
scheme: self.scheme,
options: self.options,
edge_polylines: polylines,
})
}
fn refine_topology_loop(&self, req: &UniformRefine) -> Result<Refinement, KernelError> {
let active_sel = self.active_selection(req)?;
let base = loop_base_level_data(
&self.topology,
active_sel,
&self.options,
req.selection_boundary_crease,
)?;
let mut levels: Vec<LevelData> = Vec::with_capacity(req.levels.get() as usize + 1);
levels.push(LevelData::Loop(base));
let mut polylines = req.edge_polylines.then(|| {
self.topology
.edge_vertices
.iter()
.map(|&[v0, v1]| vec![v0, v1])
.collect::<Vec<_>>()
});
for _ in 0..req.levels.get() {
let parent = match levels.last().unwrap() {
LevelData::Loop(p) => p,
_ => unreachable!("Loop refine push-chain only touches Loop variants"),
};
let child =
loop_refine_topology_once(parent, &self.options, req.selection_boundary_crease)?;
if let Some(ref mut polys) = polylines {
Self::refine_polylines(polys, &child.lineage, &parent.mesh);
}
levels.push(LevelData::Loop(child));
}
Ok(Refinement {
levels,
scheme: self.scheme,
options: self.options,
edge_polylines: polylines,
})
}
fn refine_topology_sqrt3(&self, req: &UniformRefine) -> Result<Refinement, KernelError> {
let active_sel = self.active_selection(req)?;
let base =
sqrt3_base_level_data(&self.topology, active_sel, req.selection_boundary_crease)?;
let mut levels: Vec<LevelData> = Vec::with_capacity(req.levels.get() as usize + 1);
levels.push(LevelData::Sqrt3(base));
let mut polylines = req.edge_polylines.then(|| {
self.topology
.edge_vertices
.iter()
.map(|&[v0, v1]| vec![v0, v1])
.collect::<Vec<_>>()
});
for _ in 0..req.levels.get() {
let parent = match levels.last().unwrap() {
LevelData::Sqrt3(p) => p,
_ => unreachable!("Sqrt3 refine push-chain only touches Sqrt3 variants"),
};
let child =
sqrt3_refine_topology_once(parent, &self.options, req.selection_boundary_crease)?;
if let Some(ref mut polys) = polylines {
Self::refine_polylines(polys, &child.lineage, &parent.mesh);
}
levels.push(LevelData::Sqrt3(child));
}
Ok(Refinement {
levels,
scheme: self.scheme,
options: self.options,
edge_polylines: polylines,
})
}
fn refine_topology_doo_sabin(&self, req: &UniformRefine) -> Result<Refinement, KernelError> {
let active_sel = self.active_selection(req)?;
let base =
doo_sabin_base_level_data(&self.topology, active_sel, req.selection_boundary_crease)?;
let mut levels: Vec<LevelData> = Vec::with_capacity(req.levels.get() as usize + 1);
levels.push(LevelData::DooSabin(base));
let polylines: Option<Vec<Vec<u32>>> = None;
for _ in 0..req.levels.get() {
let parent = match levels.last().unwrap() {
LevelData::DooSabin(p) => p,
_ => unreachable!("DooSabin refine push-chain only touches DooSabin variants"),
};
let child = doo_sabin_refine_topology_once(
parent,
&self.options,
req.selection_boundary_crease,
)?;
levels.push(LevelData::DooSabin(child));
}
Ok(Refinement {
levels,
scheme: self.scheme,
options: self.options,
edge_polylines: polylines,
})
}
pub fn refine_uniform(&self, req: &UniformRefine) -> Result<RefinementResult, KernelError> {
let refined = self.refine_topology(req)?;
let level_stencils = refined.vertex_stencils();
let face_root = {
let base_faces = self.topology.face_vertex_counts.len() as u32;
let mut root: Vec<u32> = (0..base_faces).collect();
for step in 0..refined.refinement_steps() {
let lineage = refined
.level_lineage(step)
.expect("refinement_steps bounds level_lineage");
root = lineage
.face_parent
.iter()
.map(|&parent| root[parent as usize])
.collect();
}
root
};
Ok(RefinementResult {
topology: refined.final_topology().clone(),
level_stencils,
lineage: refined.lineage().clone(),
face_root,
selected_faces: refined.selected_faces().map(|s| s.to_vec()),
edge_polylines: refined.edge_polylines().map(|p| p.to_vec()),
adjacency: refined.adjacency().clone(),
scheme: self.scheme,
options: self.options,
})
}
fn refine_polylines(polylines: &mut [Vec<u32>], lineage: &LineageMaps, parent_topo: &Mesh) {
use crate::output::VertexOrigin;
let edge_point_for_parent: Vec<Option<u32>> = {
let edge_count = parent_topo.edge_vertices.len();
let mut map = vec![None; edge_count];
lineage
.vertex_origin
.iter()
.enumerate()
.for_each(|(vi, origin)| {
if let VertexOrigin::Edge(parent_ei) = *origin {
map[parent_ei as usize] = Some(vi as u32);
}
});
map
};
let edge_key = |a: u32, b: u32| if a <= b { (a, b) } else { (b, a) };
let edge_key_to_idx: rustc_hash::FxHashMap<(u32, u32), usize> = parent_topo
.edge_vertices
.iter()
.enumerate()
.map(|(ei, &[v0, v1])| (edge_key(v0, v1), ei))
.collect();
let vertex_point_for_parent: Vec<Option<u32>> = {
let vert_count = parent_topo.vertex_count as usize;
let mut map = vec![None; vert_count];
lineage
.vertex_origin
.iter()
.enumerate()
.for_each(|(vi, origin)| {
if let VertexOrigin::Vertex(parent_vi) = *origin {
map[parent_vi as usize] = Some(vi as u32);
}
});
map
};
polylines.iter_mut().for_each(|poly| {
let mut new_poly = Vec::with_capacity(poly.len() * 2);
poly.windows(2).for_each(|pair| {
let a = pair[0];
let b = pair[1];
let ra = vertex_point_for_parent
.get(a as usize)
.copied()
.flatten()
.unwrap_or(a);
new_poly.push(ra);
let key = edge_key(a, b);
if let Some(&ei) = edge_key_to_idx.get(&key) {
if let Some(ep) = edge_point_for_parent[ei] {
new_poly.push(ep);
}
}
});
if let Some(&last) = poly.last() {
let rl = vertex_point_for_parent
.get(last as usize)
.copied()
.flatten()
.unwrap_or(last);
new_poly.push(rl);
}
*poly = new_poly;
});
}
pub fn face_varying_stencils(
&self,
req: &UniformRefine,
channel: &FaceVaryingChannel,
mode: FaceVaryingInterpolation,
) -> Result<Vec<StencilTable>, KernelError> {
match self.scheme {
Scheme::CatmullClark => self.face_varying_stencils_cc(req, channel, mode),
Scheme::Loop => self.face_varying_stencils_loop(req, channel, mode),
Scheme::Sqrt3 => self.face_varying_stencils_sqrt3(req, channel, mode),
Scheme::DooSabin => self.face_varying_stencils_doo_sabin(req, channel, mode),
}
}
fn face_varying_stencils_loop(
&self,
req: &UniformRefine,
channel: &FaceVaryingChannel,
mode: FaceVaryingInterpolation,
) -> Result<Vec<StencilTable>, KernelError> {
use crate::loop_subdivision::face_varying::fvar_stencils_once;
let active_sel = self.active_selection(req)?;
let mut parent = loop_base_level_data(
&self.topology,
active_sel,
&self.options,
req.selection_boundary_crease,
)?;
let mut current_fvar = channel.clone();
let mut tables = Vec::with_capacity(req.levels.get() as usize);
for _ in 0..req.levels.get() {
let child =
loop_refine_topology_once(&parent, &self.options, req.selection_boundary_crease)?;
tables.push(fvar_stencils_once(
&parent,
&child,
¤t_fvar,
mode,
&self.options,
)?);
current_fvar = identity_channel(&child.mesh);
parent = child;
}
Ok(tables)
}
fn face_varying_stencils_sqrt3(
&self,
req: &UniformRefine,
channel: &FaceVaryingChannel,
mode: FaceVaryingInterpolation,
) -> Result<Vec<StencilTable>, KernelError> {
use crate::sqrt3::face_varying::fvar_stencils_once;
let active_sel = self.active_selection(req)?;
let mut parent =
sqrt3_base_level_data(&self.topology, active_sel, req.selection_boundary_crease)?;
let mut current_fvar = channel.clone();
let mut tables = Vec::with_capacity(req.levels.get() as usize);
for _ in 0..req.levels.get() {
let child =
sqrt3_refine_topology_once(&parent, &self.options, req.selection_boundary_crease)?;
tables.push(fvar_stencils_once(
&parent,
&child,
¤t_fvar,
mode,
&self.options,
)?);
current_fvar = identity_channel(&child.mesh);
parent = child;
}
Ok(tables)
}
fn face_varying_stencils_doo_sabin(
&self,
req: &UniformRefine,
channel: &FaceVaryingChannel,
mode: FaceVaryingInterpolation,
) -> Result<Vec<StencilTable>, KernelError> {
use crate::doo_sabin::face_varying::fvar_stencils_once;
let active_sel = self.active_selection(req)?;
let mut parent =
doo_sabin_base_level_data(&self.topology, active_sel, req.selection_boundary_crease)?;
let mut current_fvar = channel.clone();
let mut tables = Vec::with_capacity(req.levels.get() as usize);
for _ in 0..req.levels.get() {
let child = doo_sabin_refine_topology_once(
&parent,
&self.options,
req.selection_boundary_crease,
)?;
tables.push(fvar_stencils_once(
&parent,
&child,
¤t_fvar,
mode,
&self.options,
)?);
current_fvar = identity_channel(&child.mesh);
parent = child;
}
Ok(tables)
}
fn face_varying_stencils_cc(
&self,
req: &UniformRefine,
channel: &FaceVaryingChannel,
mode: FaceVaryingInterpolation,
) -> Result<Vec<StencilTable>, KernelError> {
use crate::catmull_clark::face_varying::fvar_stencils_once;
let active_sel = self.active_selection(req)?;
let mut current_level =
cc_base_level_data(&self.topology, active_sel, req.selection_boundary_crease)?;
let mut current_fvar = channel.clone();
let mut tables = Vec::with_capacity(req.levels.get() as usize);
for _ in 0..req.levels.get() {
let child = cc_refine_topology_once(
¤t_level,
&self.options,
req.selection_boundary_crease,
)?;
tables.push(fvar_stencils_once(
¤t_level,
&child,
¤t_fvar,
mode,
&self.options,
)?);
current_fvar = identity_channel(&child.mesh);
current_level = child;
}
Ok(tables)
}
}
fn identity_channel(mesh: &Mesh) -> FaceVaryingChannel {
let n: u32 = mesh.face_vertex_counts.iter().sum();
FaceVaryingChannel {
indices: (0..n).collect(),
value_count: n,
}
}