#[path = "subdivision_partition.rs"]
mod subdivision_partition;
use subdivision_partition::{BaryIndices, Partition, lerp_vec4, next3};
use crate::impl_mesh::ManifoldImpl;
use crate::linalg::{BVec4, IVec3, IVec4, Mat3, Mat3x4, Vec3, Vec4};
use crate::types::{next_halfedge, Barycentric, Halfedge, TmpEdge, TriRef};
fn create_tmp_edges(halfedge: &[Halfedge]) -> Vec<TmpEdge> {
let mut edges: Vec<TmpEdge> = Vec::with_capacity(halfedge.len());
for (idx, half) in halfedge.iter().enumerate() {
if half.is_forward() {
edges.push(TmpEdge::new(half.start_vert, half.end_vert, idx as i32));
}
}
debug_assert!(
edges.len() == halfedge.len() / 2,
"Not oriented! edges={} halfedges={}",
edges.len(),
halfedge.len()
);
edges
}
impl ManifoldImpl {
pub fn get_neighbor(&self, tri: i32) -> i32 {
let mut neighbor: i32 = -1;
for i in 0..3 {
if self.is_marked_inside_quad((3 * tri + i) as usize) {
neighbor = if neighbor == -1 { i } else { -2 };
}
}
neighbor
}
pub fn get_halfedges_quad(&self, tri: i32) -> IVec4 {
let mut halfedges = IVec4::new(-1, -1, -1, -1);
for i in 0..3 {
halfedges[i] = 3 * tri + i as i32;
}
let neighbor = self.get_neighbor(tri);
if neighbor >= 0 {
let pair = self.halfedge[(3 * tri + neighbor) as usize].paired_halfedge;
if pair / 3 < tri {
return IVec4::new(-1, -1, -1, -1); }
halfedges[2] = next_halfedge(halfedges[neighbor as usize]);
halfedges[3] = next_halfedge(halfedges[2]);
halfedges[0] = next_halfedge(pair);
halfedges[1] = next_halfedge(halfedges[0]);
}
halfedges
}
fn get_indices(&self, halfedge: i32) -> BaryIndices {
let mut tri = halfedge / 3;
let mut idx = halfedge % 3;
let neighbor = self.get_neighbor(tri);
if idx == neighbor {
return BaryIndices { tri: -1, start4: -1, end4: -1 };
}
if neighbor < 0 {
BaryIndices { tri, start4: idx, end4: next3(idx) }
} else {
let pair = self.halfedge[(3 * tri + neighbor) as usize].paired_halfedge;
if pair / 3 < tri {
tri = pair / 3;
idx = if next3(neighbor) == idx { 0 } else { 1 };
} else {
idx = if next3(neighbor) == idx { 2 } else { 3 };
}
BaryIndices { tri, start4: idx, end4: (idx + 1) % 4 }
}
}
fn fill_retained_verts(&self, vert_bary: &mut [Barycentric]) {
let num_tri = self.halfedge.len() / 3;
for tri in 0..num_tri {
for i in 0..3 {
let indices = self.get_indices((3 * tri + i) as i32);
if indices.start4 < 0 {
continue; }
let mut uvw = Vec4::splat(0.0);
uvw[indices.start4 as usize] = 1.0;
vert_bary[self.halfedge[3 * tri + i].start_vert as usize] = Barycentric {
tri: indices.tri,
uvw,
};
}
}
}
pub fn subdivide(
&mut self,
edge_divisions: &dyn Fn(Vec3, Vec4, Vec4) -> i32,
keep_interior: bool,
) -> Vec<Barycentric> {
let edges = create_tmp_edges(&self.halfedge);
let num_vert = self.num_vert();
let num_edge = edges.len();
let num_tri = self.num_tri();
let mut half2edge = vec![0i32; 2 * num_edge];
for (edge, tmp) in edges.iter().enumerate() {
let idx = tmp.halfedge_idx as usize;
half2edge[idx] = edge as i32;
half2edge[self.halfedge[idx].paired_halfedge as usize] = edge as i32;
}
let face_halfedges: Vec<IVec4> = (0..num_tri)
.map(|tri| self.get_halfedges_quad(tri as i32))
.collect();
let mut edge_added = vec![0i32; num_edge];
for i in 0..num_edge {
let edge = &edges[i];
let h_idx = edge.halfedge_idx as usize;
if self.is_marked_inside_quad(h_idx) {
edge_added[i] = 0;
continue;
}
let vec = self.vert_pos[edge.first as usize] - self.vert_pos[edge.second as usize];
let tangent0 = if self.halfedge_tangent.is_empty() {
Vec4::splat(0.0)
} else {
self.halfedge_tangent[h_idx]
};
let tangent1 = if self.halfedge_tangent.is_empty() {
Vec4::splat(0.0)
} else {
self.halfedge_tangent[self.halfedge[h_idx].paired_halfedge as usize]
};
edge_added[i] = edge_divisions(vec, tangent0, tangent1);
}
if keep_interior {
let orig_edge_added = edge_added.clone();
for i in 0..num_edge {
let edge = &edges[i];
let h_idx = edge.halfedge_idx as usize;
if self.is_marked_inside_quad(h_idx) {
continue;
}
let this_added = orig_edge_added[i];
let added_fn = |mut h: i32| -> i32 {
let mut longest = 0;
let mut total = 0;
for _ in 0..3 {
let added = orig_edge_added[half2edge[h as usize] as usize];
longest = longest.max(added);
total += added;
h = next_halfedge(h);
if self.is_marked_inside_quad(h as usize) {
longest = 0;
total = 1;
break;
}
}
let min_extra = (longest as f64 * 0.2) as i32 + 1;
let extra = 2 * longest + min_extra - total;
if longest == 0 {
return 0;
}
if extra > 0 {
(extra * (longest - this_added)) / longest
} else {
0
}
};
let a1 = added_fn(h_idx as i32);
let a2 = added_fn(self.halfedge[h_idx].paired_halfedge);
edge_added[i] = orig_edge_added[i] + a1.max(a2);
}
}
let mut edge_offset = vec![0i32; num_edge];
let mut acc = num_vert as i32;
for i in 0..num_edge {
edge_offset[i] = acc;
acc += edge_added[i];
}
let total_edge_verts = acc - num_vert as i32;
let mut vert_bary = vec![
Barycentric {
tri: 0,
uvw: Vec4::splat(0.0)
};
acc as usize
];
self.fill_retained_verts(&mut vert_bary);
for i in 0..num_edge {
let n = edge_added[i];
let offset = edge_offset[i];
let indices = self.get_indices(edges[i].halfedge_idx);
if indices.tri < 0 {
continue; }
let frac = 1.0 / (n as f64 + 1.0);
for j in 0..n {
let mut uvw = Vec4::splat(0.0);
uvw[indices.end4 as usize] = (j + 1) as f64 * frac;
uvw[indices.start4 as usize] = 1.0 - uvw[indices.end4 as usize];
vert_bary[(offset + j) as usize] = Barycentric { tri: indices.tri, uvw };
}
}
let sub_tris: Vec<Partition> = (0..num_tri)
.map(|tri| {
let halfedges = face_halfedges[tri];
let mut divisions = IVec4::default();
for i in 0..4 {
if halfedges[i] >= 0 {
divisions[i] = edge_added[half2edge[halfedges[i] as usize] as usize] + 1;
}
}
Partition::get_partition(divisions)
})
.collect();
let mut tri_offset = vec![0i32; num_tri];
{
let mut acc = 0i32;
for tri in 0..num_tri {
tri_offset[tri] = acc;
acc += sub_tris[tri].tri_vert.len() as i32;
}
}
let mut interior_offset = vec![0i32; num_tri];
{
let mut acc = vert_bary.len() as i32;
for tri in 0..num_tri {
interior_offset[tri] = acc;
acc += sub_tris[tri].num_interior();
}
}
let total_new_tris = if num_tri > 0 {
tri_offset[num_tri - 1] + sub_tris[num_tri - 1].tri_vert.len() as i32
} else {
0
};
let total_new_verts = if num_tri > 0 {
interior_offset[num_tri - 1] + sub_tris[num_tri - 1].num_interior()
} else {
vert_bary.len() as i32
};
let mut tri_verts = vec![IVec3::default(); total_new_tris as usize];
vert_bary.resize(
total_new_verts as usize,
Barycentric { tri: 0, uvw: Vec4::splat(0.0) },
);
let mut tri_ref_out = vec![TriRef::default(); total_new_tris as usize];
let mut face_normal_out = vec![Vec3::splat(0.0); total_new_tris as usize];
for tri in 0..num_tri {
let halfedges = face_halfedges[tri];
if halfedges[0] < 0 {
continue;
}
let mut tri3 = IVec4::default();
let mut edge_offs = IVec4::default();
let mut edge_fwd = BVec4::splat(false);
for i in 0..4 {
if halfedges[i] < 0 {
tri3[i] = -1;
continue;
}
let he = &self.halfedge[halfedges[i] as usize];
tri3[i] = he.start_vert;
edge_offs[i] = edge_offset[half2edge[halfedges[i] as usize] as usize];
edge_fwd[i] = he.is_forward();
}
let new_tris = sub_tris[tri].reindex(
tri3,
edge_offs,
edge_fwd,
interior_offset[tri],
);
let start = tri_offset[tri] as usize;
for (j, t) in new_tris.iter().enumerate() {
tri_verts[start + j] = *t;
tri_ref_out[start + j] = self.mesh_relation.tri_ref[tri];
face_normal_out[start + j] = self.face_normal[tri];
}
let idx = sub_tris[tri].idx;
let v_idx = if halfedges[3] >= 0 || idx[1] == next3(idx[0]) {
idx
} else {
IVec4::new(idx[2], idx[0], idx[1], idx[3])
};
let mut r_idx = IVec4::default();
for i in 0..4 {
r_idx[v_idx[i] as usize] = i as i32;
}
let sub_bary = &sub_tris[tri].vert_bary;
let int_off = sub_tris[tri].interior_offset() as usize;
for (j, bary) in sub_bary[int_off..].iter().enumerate() {
vert_bary[interior_offset[tri] as usize + j] = Barycentric {
tri: tri as i32,
uvw: Vec4::new(
bary[r_idx[0] as usize],
bary[r_idx[1] as usize],
bary[r_idx[2] as usize],
bary[r_idx[3] as usize],
),
};
}
}
self.mesh_relation.tri_ref = tri_ref_out;
self.face_normal = face_normal_out;
let mut new_vert_pos = vec![Vec3::splat(0.0); vert_bary.len()];
for (vert, bary) in vert_bary.iter().enumerate() {
let halfedges = face_halfedges[bary.tri as usize];
if halfedges[3] < 0 {
let tri_pos = Mat3::from_cols(
self.vert_pos[self.halfedge[halfedges[0] as usize].start_vert as usize],
self.vert_pos[self.halfedge[halfedges[1] as usize].start_vert as usize],
self.vert_pos[self.halfedge[halfedges[2] as usize].start_vert as usize],
);
new_vert_pos[vert] = tri_pos * bary.uvw.xyz();
} else {
let quad_pos = Mat3x4::from_cols(
self.vert_pos[self.halfedge[halfedges[0] as usize].start_vert as usize],
self.vert_pos[self.halfedge[halfedges[1] as usize].start_vert as usize],
self.vert_pos[self.halfedge[halfedges[2] as usize].start_vert as usize],
self.vert_pos[self.halfedge[halfedges[3] as usize].start_vert as usize],
);
new_vert_pos[vert] = quad_pos * bary.uvw;
}
}
self.vert_pos = new_vert_pos;
if self.num_prop > 0 {
let num_prop_vert = self.num_prop_vert();
let added_verts = self.num_vert() - num_vert;
let prop_offset = num_prop_vert as i32 - num_vert as i32;
let num_prop = self.num_prop as usize;
let mut prop =
vec![0.0f64; num_prop * (num_prop_vert + added_verts + total_edge_verts as usize)];
for (i, &v) in self.properties.iter().enumerate() {
prop[i] = v;
}
for i in 0..added_verts {
let vert = num_prop_vert + i;
let bary = &vert_bary[num_vert + i];
let halfedges = face_halfedges[bary.tri as usize];
for p in 0..num_prop {
if halfedges[3] < 0 {
let mut tri_prop = Vec3::splat(0.0);
for k in 0..3 {
tri_prop[k] = self.properties
[self.halfedge[3 * bary.tri as usize + k].prop_vert as usize
* num_prop
+ p];
}
prop[vert * num_prop + p] =
tri_prop.x * bary.uvw.x + tri_prop.y * bary.uvw.y + tri_prop.z * bary.uvw.z;
} else {
let mut quad_prop = Vec4::splat(0.0);
for k in 0..4 {
quad_prop[k] = self.properties
[self.halfedge[halfedges[k] as usize].prop_vert as usize
* num_prop
+ p];
}
prop[vert * num_prop + p] = quad_prop.x * bary.uvw.x
+ quad_prop.y * bary.uvw.y
+ quad_prop.z * bary.uvw.z
+ quad_prop.w * bary.uvw.w;
}
}
}
for i in 0..num_edge {
let n = edge_added[i];
let offset = edge_offset[i] as usize + prop_offset as usize + added_verts;
let frac = 1.0 / (n as f64 + 1.0);
let halfedge_idx =
self.halfedge[edges[i].halfedge_idx as usize].paired_halfedge as usize;
let prop0 = self.halfedge[halfedge_idx].prop_vert as usize;
let prop1 =
self.halfedge[next_halfedge(halfedge_idx as i32) as usize].prop_vert as usize;
for j in 0..n as usize {
for p in 0..num_prop {
let t = (j + 1) as f64 * frac;
prop[(offset + j) * num_prop + p] = self.properties[prop0 * num_prop + p]
+ (self.properties[prop1 * num_prop + p]
- self.properties[prop0 * num_prop + p])
* t;
}
}
}
let mut tri_prop_out = vec![IVec3::default(); total_new_tris as usize];
for tri in 0..num_tri {
let halfedges = face_halfedges[tri];
if halfedges[0] < 0 {
continue;
}
let mut tri3 = IVec4::default();
let mut edge_offs = IVec4::default();
let mut edge_fwd = BVec4::splat(true);
for i in 0..4 {
if halfedges[i] < 0 {
tri3[i] = -1;
continue;
}
let he = &self.halfedge[halfedges[i] as usize];
tri3[i] = he.prop_vert;
edge_offs[i] = edge_offset[half2edge[halfedges[i] as usize] as usize];
if !he.is_forward() {
let paired = he.paired_halfedge;
if self.halfedge[paired as usize].prop_vert
!= self.halfedge[next_halfedge(halfedges[i]) as usize].prop_vert
|| self.halfedge[next_halfedge(paired) as usize].prop_vert
!= he.prop_vert
{
edge_offs[i] += added_verts as i32;
} else {
edge_fwd[i] = false;
}
}
}
let prop_edge_offs = IVec4::new(
edge_offs[0] + prop_offset,
edge_offs[1] + prop_offset,
edge_offs[2] + prop_offset,
edge_offs[3] + prop_offset,
);
let new_tris = sub_tris[tri].reindex(
tri3,
prop_edge_offs,
edge_fwd,
interior_offset[tri] + prop_offset,
);
let start = tri_offset[tri] as usize;
for (j, t) in new_tris.iter().enumerate() {
tri_prop_out[start + j] = *t;
}
}
self.properties = prop;
self.create_halfedges(&tri_prop_out, &tri_verts);
} else {
self.create_halfedges(&tri_verts, &[]);
}
vert_bary
}
}
pub fn subdivide_impl(mesh: &ManifoldImpl, levels: usize) -> ManifoldImpl {
if levels == 0 || mesh.is_empty() {
return mesh.clone();
}
let mut current = mesh.clone();
for _ in 0..levels {
current.subdivide(&|_vec, _t0, _t1| 1, false);
current.calculate_bbox();
current.set_epsilon(-1.0, false);
}
current
}
#[cfg(test)]
#[path = "subdivision_tests.rs"]
mod tests;