use std::collections::HashMap;
use crate::geometry::{edge_length, face_area, face_normal, vertex_normal};
use crate::ids::{FaceId, HalfEdgeId, VertexId};
use crate::linalg::vec3::Vec3;
use crate::storage::MeshStorage;
use crate::traversal::VertexAdjacentVerts;
#[derive(Debug)]
pub struct MeshCache {
face_normals: HashMap<FaceId, Vec3>,
face_areas: HashMap<FaceId, f64>,
vertex_normals: HashMap<VertexId, Vec3>,
vertex_valences: HashMap<VertexId, usize>,
edge_lengths: HashMap<HalfEdgeId, f64>,
}
impl MeshCache {
pub fn new() -> Self {
Self {
face_normals: HashMap::new(),
face_areas: HashMap::new(),
vertex_normals: HashMap::new(),
vertex_valences: HashMap::new(),
edge_lengths: HashMap::new(),
}
}
pub fn face_normal(&mut self, mesh: &MeshStorage, f: FaceId) -> Vec3 {
if let Some(&n) = self.face_normals.get(&f) {
return n;
}
let n = face_normal(mesh, f).unwrap_or([0.0; 3]);
self.face_normals.insert(f, n);
n
}
pub fn face_area(&mut self, mesh: &MeshStorage, f: FaceId) -> f64 {
if let Some(&a) = self.face_areas.get(&f) {
return a;
}
let a = face_area(mesh, f).unwrap_or(0.0);
self.face_areas.insert(f, a);
a
}
pub fn vertex_normal(&mut self, mesh: &MeshStorage, v: VertexId) -> Vec3 {
if let Some(&n) = self.vertex_normals.get(&v) {
return n;
}
let n = vertex_normal(mesh, v).unwrap_or([0.0; 3]);
self.vertex_normals.insert(v, n);
n
}
pub fn vertex_valence(&mut self, mesh: &MeshStorage, v: VertexId) -> usize {
if let Some(&val) = self.vertex_valences.get(&v) {
return val;
}
let val = VertexAdjacentVerts::new(mesh, v).count();
self.vertex_valences.insert(v, val);
val
}
pub fn edge_length(&mut self, mesh: &MeshStorage, he: HalfEdgeId) -> f64 {
if let Some(&l) = self.edge_lengths.get(&he) {
return l;
}
let l = edge_length(mesh, he).unwrap_or(0.0);
self.edge_lengths.insert(he, l);
l
}
pub fn invalidate(&mut self) {
self.face_normals.clear();
self.face_areas.clear();
self.vertex_normals.clear();
self.vertex_valences.clear();
self.edge_lengths.clear();
}
pub fn invalidate_face(&mut self, f: FaceId) {
self.face_normals.remove(&f);
self.face_areas.remove(&f);
}
pub fn invalidate_vertex(&mut self, v: VertexId) {
self.vertex_normals.remove(&v);
self.vertex_valences.remove(&v);
}
pub fn invalidate_edge(&mut self, he: HalfEdgeId) {
self.edge_lengths.remove(&he);
}
pub fn face_normal_count(&self) -> usize {
self.face_normals.len()
}
pub fn face_area_count(&self) -> usize {
self.face_areas.len()
}
pub fn vertex_normal_count(&self) -> usize {
self.vertex_normals.len()
}
pub fn vertex_valence_count(&self) -> usize {
self.vertex_valences.len()
}
pub fn edge_length_count(&self) -> usize {
self.edge_lengths.len()
}
}
impl Default for MeshCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::{
edge_length as geo_edge_length, face_area as geo_face_area, face_normal as geo_face_normal,
vertex_normal as geo_vertex_normal,
};
use crate::io::build_mesh_from_vertices_and_faces;
use crate::test_util::build_icosphere;
use crate::traversal::VertexAdjacentVerts;
fn build_triangle() -> MeshStorage {
let verts = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let faces = vec![[0u32, 1, 2]];
build_mesh_from_vertices_and_faces(&verts, &faces).unwrap()
}
#[test]
fn face_normal_matches_direct() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
let cached = cache.face_normal(&mesh, f);
let direct = geo_face_normal(&mesh, f).unwrap();
for i in 0..3 {
assert!((cached[i] - direct[i]).abs() < 1e-12);
}
}
#[test]
fn face_area_matches_direct() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
let cached = cache.face_area(&mesh, f);
let direct = geo_face_area(&mesh, f).unwrap();
assert!((cached - direct).abs() < 1e-12);
}
#[test]
fn vertex_normal_matches_direct() {
let mesh = build_triangle();
let v = mesh.vertex_ids().next().unwrap();
let mut cache = MeshCache::new();
let cached = cache.vertex_normal(&mesh, v);
let direct = geo_vertex_normal(&mesh, v).unwrap();
for i in 0..3 {
assert!((cached[i] - direct[i]).abs() < 1e-12);
}
}
#[test]
fn vertex_valence_matches_direct() {
let mesh = build_triangle();
let v = mesh.vertex_ids().next().unwrap();
let mut cache = MeshCache::new();
let cached = cache.vertex_valence(&mesh, v);
let direct = VertexAdjacentVerts::new(&mesh, v).count();
assert_eq!(cached, direct);
assert_eq!(cached, 2);
}
#[test]
fn edge_length_matches_direct() {
let mesh = build_triangle();
let he = mesh.halfedge_ids().next().unwrap();
let mut cache = MeshCache::new();
let cached = cache.edge_length(&mesh, he);
let direct = geo_edge_length(&mesh, he).unwrap();
assert!((cached - direct).abs() < 1e-12);
}
#[test]
fn cache_stores_after_first_call() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
assert_eq!(cache.face_normal_count(), 0);
assert_eq!(cache.face_area_count(), 0);
let _ = cache.face_normal(&mesh, f);
assert_eq!(cache.face_normal_count(), 1);
let _ = cache.face_area(&mesh, f);
assert_eq!(cache.face_area_count(), 1);
let _ = cache.face_normal(&mesh, f);
let _ = cache.face_area(&mesh, f);
assert_eq!(cache.face_normal_count(), 1);
assert_eq!(cache.face_area_count(), 1);
}
#[test]
fn cache_returns_same_value_on_repeat() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
let n1 = cache.face_normal(&mesh, f);
let n2 = cache.face_normal(&mesh, f);
assert_eq!(n1, n2);
}
#[test]
fn cache_hit_does_not_increase_count() {
let mesh = build_icosphere(1);
let faces: Vec<_> = mesh.face_ids().collect();
let mut cache = MeshCache::new();
for &f in &faces {
cache.face_normal(&mesh, f);
}
assert_eq!(cache.face_normal_count(), faces.len());
for &f in &faces {
cache.face_normal(&mesh, f);
}
assert_eq!(cache.face_normal_count(), faces.len());
}
#[test]
fn invalidate_all_clears_everything() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let v = mesh.vertex_ids().next().unwrap();
let he = mesh.halfedge_ids().next().unwrap();
let mut cache = MeshCache::new();
cache.face_normal(&mesh, f);
cache.face_area(&mesh, f);
cache.vertex_normal(&mesh, v);
cache.vertex_valence(&mesh, v);
cache.edge_length(&mesh, he);
assert!(cache.face_normal_count() > 0);
assert!(cache.face_area_count() > 0);
assert!(cache.vertex_normal_count() > 0);
assert!(cache.vertex_valence_count() > 0);
assert!(cache.edge_length_count() > 0);
cache.invalidate();
assert_eq!(cache.face_normal_count(), 0);
assert_eq!(cache.face_area_count(), 0);
assert_eq!(cache.vertex_normal_count(), 0);
assert_eq!(cache.vertex_valence_count(), 0);
assert_eq!(cache.edge_length_count(), 0);
}
#[test]
fn invalidate_face_clears_only_that_face() {
let mesh = build_icosphere(1);
let faces: Vec<_> = mesh.face_ids().collect();
let f0 = faces[0];
let f1 = faces[1];
let mut cache = MeshCache::new();
cache.face_normal(&mesh, f0);
cache.face_normal(&mesh, f1);
assert_eq!(cache.face_normal_count(), 2);
cache.invalidate_face(f0);
assert_eq!(cache.face_normal_count(), 1);
let _ = cache.face_normal(&mesh, f1);
assert_eq!(cache.face_normal_count(), 1);
}
#[test]
fn invalidate_face_clears_area_too() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
cache.face_normal(&mesh, f);
cache.face_area(&mesh, f);
assert_eq!(cache.face_normal_count(), 1);
assert_eq!(cache.face_area_count(), 1);
cache.invalidate_face(f);
assert_eq!(cache.face_normal_count(), 0);
assert_eq!(cache.face_area_count(), 0);
}
#[test]
fn invalidate_vertex_clears_only_that_vertex() {
let mesh = build_icosphere(1);
let verts: Vec<_> = mesh.vertex_ids().collect();
let v0 = verts[0];
let v1 = verts[1];
let mut cache = MeshCache::new();
cache.vertex_normal(&mesh, v0);
cache.vertex_normal(&mesh, v1);
cache.vertex_valence(&mesh, v0);
cache.vertex_valence(&mesh, v1);
assert_eq!(cache.vertex_normal_count(), 2);
assert_eq!(cache.vertex_valence_count(), 2);
cache.invalidate_vertex(v0);
assert_eq!(cache.vertex_normal_count(), 1);
assert_eq!(cache.vertex_valence_count(), 1);
}
#[test]
fn invalidate_edge_clears_only_that_edge() {
let mesh = build_icosphere(1);
let hes: Vec<_> = mesh.halfedge_ids().collect();
let he0 = hes[0];
let he1 = hes[1];
let mut cache = MeshCache::new();
cache.edge_length(&mesh, he0);
cache.edge_length(&mesh, he1);
assert_eq!(cache.edge_length_count(), 2);
cache.invalidate_edge(he0);
assert_eq!(cache.edge_length_count(), 1);
}
#[test]
fn invalidate_then_recompute_returns_same_value() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
let n1 = cache.face_normal(&mesh, f);
cache.invalidate_face(f);
let n2 = cache.face_normal(&mesh, f);
assert_eq!(n1, n2);
}
#[test]
fn triangle_face_normal_points_up() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
let n = cache.face_normal(&mesh, f);
assert!(n[0].abs() < 1e-12);
assert!(n[1].abs() < 1e-12);
assert!((n[2] - 1.0).abs() < 1e-12);
}
#[test]
fn triangle_face_area_is_half() {
let mesh = build_triangle();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
let a = cache.face_area(&mesh, f);
assert!((a - 0.5).abs() < 1e-12);
}
#[test]
fn triangle_vertex_valence_is_two() {
let mesh = build_triangle();
for v in mesh.vertex_ids() {
let mut cache = MeshCache::new();
assert_eq!(cache.vertex_valence(&mesh, v), 2);
}
}
#[test]
fn triangle_vertex_normal_points_up() {
let mesh = build_triangle();
for v in mesh.vertex_ids() {
let mut cache = MeshCache::new();
let n = cache.vertex_normal(&mesh, v);
assert!((n[2] - 1.0).abs() < 1e-12);
}
}
#[test]
fn icosphere_all_face_normals_match_direct() {
let mesh = build_icosphere(2);
let mut cache = MeshCache::new();
for f in mesh.face_ids() {
let cached = cache.face_normal(&mesh, f);
let direct = geo_face_normal(&mesh, f).unwrap();
for i in 0..3 {
assert!(
(cached[i] - direct[i]).abs() < 1e-12,
"面 {:?} 法向不匹配",
f
);
}
}
assert_eq!(cache.face_normal_count(), mesh.face_count());
}
#[test]
fn icosphere_all_face_areas_match_direct() {
let mesh = build_icosphere(2);
let mut cache = MeshCache::new();
for f in mesh.face_ids() {
let cached = cache.face_area(&mesh, f);
let direct = geo_face_area(&mesh, f).unwrap();
assert!((cached - direct).abs() < 1e-12);
}
assert_eq!(cache.face_area_count(), mesh.face_count());
}
#[test]
fn icosphere_all_vertex_normals_match_direct() {
let mesh = build_icosphere(2);
let mut cache = MeshCache::new();
for v in mesh.vertex_ids() {
let cached = cache.vertex_normal(&mesh, v);
if let Some(direct) = geo_vertex_normal(&mesh, v) {
for i in 0..3 {
assert!((cached[i] - direct[i]).abs() < 1e-12);
}
}
}
}
#[test]
fn icosphere_all_vertex_valences_match_direct() {
let mesh = build_icosphere(2);
let mut cache = MeshCache::new();
for v in mesh.vertex_ids() {
let cached = cache.vertex_valence(&mesh, v);
let direct = VertexAdjacentVerts::new(&mesh, v).count();
assert_eq!(cached, direct);
}
assert_eq!(cache.vertex_valence_count(), mesh.vertex_count());
}
#[test]
fn icosphere_all_edge_lengths_match_direct() {
let mesh = build_icosphere(2);
let mut cache = MeshCache::new();
for he in mesh.halfedge_ids() {
let cached = cache.edge_length(&mesh, he);
let direct = geo_edge_length(&mesh, he).unwrap();
assert!((cached - direct).abs() < 1e-12);
}
assert_eq!(cache.edge_length_count(), mesh.halfedge_count());
}
#[test]
fn icosphere_cache_survives_repeated_queries() {
let mesh = build_icosphere(2);
let mut cache = MeshCache::new();
for f in mesh.face_ids() {
cache.face_normal(&mesh, f);
}
let count_after_first = cache.face_normal_count();
for f in mesh.face_ids() {
cache.face_normal(&mesh, f);
}
assert_eq!(cache.face_normal_count(), count_after_first);
}
#[test]
fn default_creates_empty_cache() {
let cache = MeshCache::default();
assert_eq!(cache.face_normal_count(), 0);
assert_eq!(cache.face_area_count(), 0);
assert_eq!(cache.vertex_normal_count(), 0);
assert_eq!(cache.vertex_valence_count(), 0);
assert_eq!(cache.edge_length_count(), 0);
}
#[test]
fn degenerate_face_returns_zero_normal() {
let verts = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [2.0, 0.0, 0.0]];
let faces = vec![[0u32, 1, 2]];
let mesh = build_mesh_from_vertices_and_faces(&verts, &faces).unwrap();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
let n = cache.face_normal(&mesh, f);
assert_eq!(n, [0.0, 0.0, 0.0]);
let a = cache.face_area(&mesh, f);
assert!(a.abs() < 1e-12);
}
#[test]
fn degenerate_face_cached_zero_on_repeat() {
let verts = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [2.0, 0.0, 0.0]];
let faces = vec![[0u32, 1, 2]];
let mesh = build_mesh_from_vertices_and_faces(&verts, &faces).unwrap();
let f = mesh.face_ids().next().unwrap();
let mut cache = MeshCache::new();
let n1 = cache.face_normal(&mesh, f);
assert_eq!(n1, [0.0, 0.0, 0.0]);
assert_eq!(cache.face_normal_count(), 1);
let n2 = cache.face_normal(&mesh, f);
assert_eq!(n2, [0.0, 0.0, 0.0]);
assert_eq!(cache.face_normal_count(), 1);
}
}