use std::ops::Mul;
use linear_isomorphic::{InnerSpace, RealField, VectorSpace};
use nalgebra::{Matrix4, Vector4};
use super::queue::PQueue;
use crate::*;
use crate::{MeshStorage, PolyHedron, VertData};
pub fn quadric_simplify<S, C: MeshStorage, const MANIFOLD: bool>(
mesh: &mut PolyHedron<C, MANIFOLD>,
mut simplify_count: usize,
) where
VertData<C>: linear_isomorphic::InnerSpace<S>,
S: linear_isomorphic::RealField
+ Mul<VertData<C>, Output = VertData<C>>
+ ordered_float::FloatCore
+ nalgebra::ComplexField,
{
let (mut quadrics, mut queue) = initialize_vertex_quadrics(mesh);
while !queue.is_empty() && simplify_count > 0
{
let (eid, _) = queue.pop().unwrap();
let hedge_handle = mesh.half_edge_handle(eid);
if hedge_handle.id().is_void()
|| hedge_handle.is_in_boundary()
|| hedge_handle
.source()
.iter_star_half_edges()
.any(|e| e.is_in_boundary())
|| hedge_handle
.dest()
.iter_star_half_edges()
.any(|e| e.is_in_boundary())
|| hedge_handle.iterate_butterfly().any(|e| e.is_in_boundary())
|| !hedge_handle.can_collapse()
{
continue;
}
simplify_count -= 1;
for e in hedge_handle.source().iter_star_half_edges()
{
let id = e.id();
queue.remove(id);
}
for e in hedge_handle.dest().iter_star_half_edges()
{
let id = e.id();
queue.remove(id);
}
let (_cost, point) = compute_edge_weight(hedge_handle.replicate(), &quadrics);
let new_quadric = quadrics[hedge_handle.source().id().to_index()]
+ quadrics[hedge_handle.dest().id().to_index()];
let v = hedge_handle.collapse();
let mut v_handle = mesh.vert_handle(v);
quadrics[v_handle.id().to_index()] = new_quadric;
let mut pos = VertData::<C>::default();
pos[0] = point.x;
pos[1] = point.y;
pos[2] = point.z;
v_handle.update_data(|v| *v = pos.clone());
for e in v_handle.iter_star_half_edges()
{
debug_assert!(!e.id().is_void());
let (cost, _) = compute_edge_weight(e.replicate(), &quadrics);
queue.push(e.id(), cost);
}
}
}
fn initialize_vertex_quadrics<S, C: MeshStorage, const MANIFOLD: bool>(
mesh: &PolyHedron<C, MANIFOLD>,
) -> (Vec<Matrix4<S>>, PQueue<HalfEdgeId, S>)
where
VertData<C>: linear_isomorphic::InnerSpace<S>,
S: linear_isomorphic::RealField
+ Mul<VertData<C>, Output = VertData<C>>
+ ordered_float::FloatCore
+ nalgebra::ComplexField,
{
let mut vertex_quadrics = vec![Matrix4::<S>::zeros(); mesh.vert_count()];
for face in mesh.iter_faces()
{
let normal = face.naive_normal().normalized();
let position = face.half_edge().source().read_data().clone();
let d = position.dot(&normal);
let q = Vector4::<S>::new(normal[0], normal[1], normal[2], -d);
let quadric = q * q.transpose();
for v in face.vertices()
{
vertex_quadrics[v.id().to_index()] += quadric;
}
}
let mut queue = PQueue::new();
for edge in mesh.iter_edges()
{
let eid = edge.id();
let (cost, _) = compute_edge_weight(edge, &vertex_quadrics);
queue.push(eid, cost);
}
(vertex_quadrics, queue)
}
fn compute_edge_weight<S, C: MeshStorage, const MANIFOLD: bool>(
edge: HalfEdgeHandle<C, MANIFOLD>,
quadrics: &Vec<Matrix4<S>>,
) -> (S, Vector4<S>)
where
S: RealField + nalgebra::ComplexField,
VertData<C>: VectorSpace<Scalar = S>,
{
debug_assert!(!edge.id().is_void());
let cost_matrix =
quadrics[edge.source().id().0 as usize] + quadrics[edge.dest().id().0 as usize];
let intermediate = match cost_matrix.try_inverse()
{
None =>
{
let half = S::from(0.5).unwrap();
(edge.dest().read_data().clone() + edge.source().read_data().clone()) * half
}
Some(inverse) =>
{
let b = Vector4::<S>::new(
S::from(0.).unwrap(),
S::from(0.).unwrap(),
S::from(0.).unwrap(),
S::from(1.).unwrap(),
);
let sol = inverse * b;
let mut res = VertData::<C>::default();
res[0] = sol.x / sol.w;
res[1] = sol.y / sol.w;
res[2] = sol.z / sol.w;
res
}
};
let new_point = Vector4::<S>::new(
intermediate[0],
intermediate[1],
intermediate[2],
S::from(1.0).unwrap(),
);
(
-(new_point.transpose() * cost_matrix * new_point)[(0, 0)],
new_point,
)
}
#[cfg(test)]
mod tests
{
use wavefront_loader::ObjData;
use super::*;
use crate::verification::verify_correctness;
use crate::BasicPolyHedron;
#[test]
fn test_quadric_simplify()
{
let ObjData {
vertices,
vertex_face_indices,
..
} = ObjData::from_disk_file("../../../Assets/armadillo.obj");
let mut mesh = BasicPolyHedron::<_, false>::new(
vertices.clone(),
(),
vertex_face_indices
.iter()
.map(|l| l.iter().map(|i| *i as usize)),
);
verify_correctness(&mesh).unwrap();
let count = mesh.vert_count() as f64 * (1.0 - 0.1);
quadric_simplify(&mut mesh, count as usize);
verify_correctness(&mesh).unwrap();
let (vs, fs) = mesh.face_list();
ObjData::export(&(&vs, &fs), "tmp/simple_armadillo.obj");
}
}