-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
83 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,137 +1,82 @@ | ||
use std::collections::HashSet; | ||
use crate::mesh::parts::edge::MeshEdge; | ||
use crate::mesh::parts::face::Face; | ||
use crate::mesh::{Mesh, MeshError, MeshResult}; | ||
use egui::ahash::{HashMap, HashMapExt}; | ||
use crate::mesh::parts::vertex::Vertex; | ||
|
||
/// Subdivides the mesh using the Loop subdivision scheme. | ||
/// | ||
/// This method applies the Loop subdivision algorithm to the mesh, | ||
/// which is a technique used to create a smoother mesh by adding new vertices | ||
/// and adjusting the positions of existing vertices. | ||
/// | ||
/// # Returns | ||
/// | ||
/// A `MeshResult` containing the subdivided `Mesh` if successful, or a `MeshError` if an error occurs. | ||
pub fn by_loop(mesh: &Mesh) -> MeshResult<Mesh> { | ||
let trimesh = mesh.triangulate()?; | ||
let start_idx = trimesh.vertices().len(); | ||
let mut new_vertices = vec![]; | ||
let mut new_faces = vec![]; | ||
let mut cache = HashMap::new(); | ||
let mut add_edge = |lhs:usize,rhs:usize|{ | ||
if let Some(idx) = cache.get(&(lhs,rhs)){ | ||
Ok::<usize, MeshError>(*idx) | ||
} | ||
else{ | ||
let v1 = trimesh.get(lhs)?; | ||
let v2 = trimesh.get(rhs)?; | ||
|
||
let next = new_vertices.len() + start_idx; | ||
new_vertices.push((*v1 + *v2) / 2.0); | ||
cache.insert((lhs,rhs),next); | ||
cache.insert((rhs,lhs),next); | ||
Ok(next) | ||
} | ||
}; | ||
for face in trimesh.faces(){ | ||
|
||
pub struct MeshSubdivision<'a> { | ||
mesh: &'a Mesh, | ||
} | ||
|
||
impl<'a> MeshSubdivision<'a> { | ||
pub fn new(mesh: &'a Mesh) -> Self { | ||
MeshSubdivision { mesh } | ||
} | ||
|
||
/// Subdivides the mesh using the Butterfly subdivision scheme. | ||
/// | ||
/// This method applies the Butterfly subdivision algorithm to the mesh, | ||
/// which is a technique used to create a smoother mesh by adding new vertices | ||
/// and adjusting the positions of existing vertices. | ||
/// | ||
/// # Returns | ||
/// | ||
/// A `MeshResult` containing the subdivided `Mesh` if successful, or a `MeshError` if an error occurs. | ||
pub fn by_butterfly(&self) -> MeshResult<Mesh> { | ||
let mesh = self.mesh.triangulate()?; | ||
let mut new_vertices = self.mesh.vertices.clone(); | ||
let mut new_faces = vec![]; | ||
|
||
let table = mesh.try_tables()?; | ||
|
||
let mut cache = HashMap::new(); | ||
|
||
for face in mesh.faces() { | ||
let mut mid_points_extra = vec![]; | ||
let mut edges = face.edges().into_iter().collect::<Vec<_>>(); | ||
|
||
for MeshEdge(lhs, rhs) in edges { | ||
if let Some(idx) = cache.get(&(lhs, rhs)) { | ||
mid_points_extra.push(*idx); | ||
} else { | ||
let v1 = mesh.get(lhs)?; | ||
let v2 = mesh.get(rhs)?; | ||
|
||
let mut mid = (*v1 + *v2) / 2.0; | ||
let adjacent_vs: Vec<usize> = table | ||
.edge_faces(lhs, rhs)? | ||
.iter() | ||
.flat_map(|f| f.flatten()) | ||
.filter(|v| v != &lhs && v != &rhs) | ||
.collect(); | ||
if adjacent_vs.len() >= 2 { | ||
let a1 = adjacent_vs[0]; | ||
let a2 = adjacent_vs[1]; | ||
|
||
let mut b1_vs = Vec::new(); | ||
for face in table.vertex_faces(a1)? { | ||
if !face.flatten().contains(&lhs) && !face.flatten().contains(&rhs) { | ||
b1_vs = face | ||
.flatten() | ||
.iter() | ||
.filter(|v| *v != &a1) | ||
.cloned() | ||
.collect(); | ||
} | ||
} | ||
|
||
let mut b2_vs = Vec::new(); | ||
for face in table.vertex_faces(a2)? { | ||
if !face.flatten().contains(&lhs) && !face.flatten().contains(&rhs) { | ||
b2_vs = face | ||
.flatten() | ||
.iter() | ||
.filter(|v| *v != &a2) | ||
.cloned() | ||
.collect(); | ||
} | ||
} | ||
|
||
if !b1_vs.is_empty() && !b2_vs.is_empty() { | ||
let a1 = mesh.get(a1)?; | ||
let a2 = mesh.get(a2)?; | ||
let b1 = mesh.get(b1_vs[0])?; | ||
let b2 = mesh.get(b2_vs[0])?; | ||
let [a,b,c] = face.flatten()[..] else { | ||
return Err(MeshError::WrongMesh("Invalid number of vertices".to_string())) | ||
}; | ||
let m1 = add_edge(a,b)?; | ||
let m2 = add_edge(b,c)?; | ||
let m3 = add_edge(c,a)?; | ||
|
||
mid = mid + (*a1 + *a2 - *b1 - *b2) * 0.125; | ||
} | ||
} | ||
new_faces.push(Face::new3(a,m1,m3)); | ||
new_faces.push(Face::new3(b,m2,m1)); | ||
new_faces.push(Face::new3(c,m3,m2)); | ||
new_faces.push(Face::new3(m1,m2,m3)); | ||
|
||
let idx = new_vertices.len(); | ||
new_vertices.push(mid); | ||
mid_points_extra.push(idx); | ||
cache.insert((lhs, rhs), idx); | ||
cache.insert((rhs, lhs), idx); | ||
} | ||
} | ||
|
||
mid_points_extra.push(lhs); | ||
mid_points_extra.push(rhs); | ||
} | ||
let mut vertex_neighbors = HashMap::new(); | ||
for (idx,v) in trimesh.vertices().iter().enumerate(){ | ||
vertex_neighbors.insert(idx,HashSet::new()); | ||
} | ||
|
||
let [m_ab, a, b, m_bc, _b, c, m_ca, _c, _a] = mid_points_extra[..] else { | ||
return Err(MeshError::WrongMesh("Invalid number of mid points".to_string())); | ||
}; | ||
for MeshEdge(lhs,rhs) in trimesh.edges().iter(){ | ||
vertex_neighbors.get_mut(&lhs).ok_or(MeshError::idx_vertex(*lhs))?.insert(rhs); | ||
vertex_neighbors.get_mut(&rhs).ok_or(MeshError::idx_vertex(*rhs))?.insert(lhs); | ||
} | ||
|
||
new_faces.push(Face::new3(a, m_ab, m_ca)); | ||
new_faces.push(Face::new3(b, m_bc, m_ab)); | ||
new_faces.push(Face::new3(c, m_ca, m_bc)); | ||
new_faces.push(Face::new3(m_ab, m_bc, m_ca)); | ||
let mut updated_vertices = trimesh.vertices.clone(); | ||
for (idx,neighbours) in vertex_neighbors.iter(){ | ||
let n = neighbours.len() as f32; | ||
let beta = if n > 3.0 { | ||
3.0 / (8.0 * n) | ||
} else { | ||
3.0 / 16.0 | ||
}; | ||
let mut new_vertex = *trimesh.get(*idx)? * (1.0 - n * beta); | ||
let mut coef = Vertex::default(); | ||
for neighbor in neighbours { | ||
coef = coef + *trimesh.get(**neighbor)?; | ||
} | ||
|
||
Ok(Mesh::from_vertices(new_vertices, new_faces, self.mesh.attributes.clone())) | ||
coef = coef * beta; | ||
updated_vertices[*idx] = new_vertex + coef; | ||
} | ||
|
||
/// Subdivides the mesh using the Loop subdivision scheme. | ||
/// | ||
/// This method applies the Loop subdivision algorithm to the mesh, | ||
/// which is a technique used to create a smoother mesh by adding new vertices | ||
/// and adjusting the positions of existing vertices. | ||
/// | ||
/// # Returns | ||
/// | ||
/// A `MeshResult` containing the subdivided `Mesh` if successful, or a `MeshError` if an error occurs. | ||
pub fn by_loop(&self) -> MeshResult<Mesh> { | ||
Ok(self.mesh.clone()) | ||
} | ||
/// Subdivides the mesh using a linear subdivision scheme. | ||
/// | ||
/// This method applies a linear subdivision algorithm to the mesh, | ||
/// which is a technique used to create a smoother mesh by adding new vertices | ||
/// and adjusting the positions of existing vertices. | ||
/// | ||
/// # Returns | ||
/// | ||
/// A `MeshResult` containing the subdivided `Mesh` if successful, or a `MeshError` if an error occurs. | ||
pub fn by_linear(&self) -> MeshResult<Mesh> { | ||
Ok(self.mesh.clone()) | ||
} | ||
Ok(Mesh::from_vertices([updated_vertices, new_vertices].concat(), new_faces, trimesh.attributes.clone())) | ||
} |