use bevy::prelude::Transform;
use itertools::Itertools;
use super::{edge_ops, face_ops, FaceId, HalfEdgeMesh, StackVec, VertexId};
pub fn transform(mesh:&mut HalfEdgeMesh, vertex:VertexId, transform:Transform) {
let positions = mesh.attributes.get_mut(&super::attributes::AttributeKind::Positions).unwrap().as_vertices_vec3_mut();
positions.insert(vertex, transform.transform_point(positions[vertex]));
}
pub fn delete(mesh:&mut HalfEdgeMesh, vertex:VertexId, is_fill:bool) -> StackVec<Option<FaceId>> {
let fan:StackVec<_> = mesh.goto(vertex).iter_outgoing().map(|t| *t).collect();
let result = fan.iter().map(|&edge| edge_ops::delete(mesh, edge, is_fill)).collect();
mesh.vertices.remove(vertex);
result
}
pub fn chamfer(mesh:&mut HalfEdgeMesh, vertex:VertexId, factor:f32) -> FaceId {
let outgoing:StackVec<_> = mesh.goto(vertex).iter_outgoing().map(|t| *t).collect();
let new_vertices:StackVec<_> = outgoing.iter().map(|&target| edge_ops::split(mesh, target, factor)).collect();
let new_edges:StackVec<_> = new_vertices.iter().circular_tuple_windows().map(|(&start, &end)| face_ops::split(mesh, start, end)).collect();
delete(mesh, vertex, true);
mesh[mesh[new_edges[0]].twin].face.unwrap()
}
pub fn dissolve(mesh:&mut HalfEdgeMesh, vertex:VertexId) {
let outgoing = mesh.goto(vertex).iter_outgoing().map(|e| (*e, *e.twin(), e.face())).collect::<StackVec<_>>();
if outgoing.len() != 2 {
panic!("Tried to dissolve a vertex that deson't connect only 2 edges");
}
let direction = if mesh[outgoing[0].1].next == outgoing[1].0 { 0 } else { 1 };
mesh[outgoing[direction].1].next = mesh[outgoing[(direction + 1) % 2].0].next; mesh[outgoing[direction].1].next = mesh[outgoing[(direction + 1) % 2].0].next;
mesh[outgoing[(direction + 1) % 2].1].next = mesh[outgoing[direction].0].next;
mesh[outgoing[(direction + 1) % 2].1].next = mesh[outgoing[direction].0].next;
if let Some(face) = outgoing[direction].2 {
mesh[face].halfedge = outgoing[(direction + 1) % 2].1;
}
if let Some(face) = outgoing[(direction + 1) % 2].2 {
mesh[face].halfedge = outgoing[direction].1;
}
mesh.halfedges.remove(outgoing[0].0);
mesh.halfedges.remove(outgoing[1].0);
mesh.vertices.remove(vertex);
}
#[cfg(test)]
mod tests {
use bevy::prelude::Vec3;
use slotmap::{KeyData, SecondaryMap};
use smallvec::SmallVec;
use crate::mesh::{attributes::AttributeKind, tests::sample_mesh, FaceId, VertexId};
#[test]
fn test_delete() {
let mut mesh = sample_mesh();
super::delete(&mut mesh, VertexId(KeyData::from_ffi(3)), true);
assert_eq!(mesh.count_islands(), 1);
assert_eq!(mesh.count_face_edges(), 8);
assert_eq!(mesh.face_count(), 1);
assert_eq!(mesh.vertex_count(), 8);
}
#[test]
fn test_chamfer() {
let mut mesh = sample_mesh();
let v = mesh.vertex_keys().collect::<SmallVec<[_;9]>>();
let positions = SecondaryMap::from_iter([
(v[0], -Vec3::X-Vec3::Z), (v[3], -Vec3::Z+0.5*Vec3::Y), (v[5], Vec3::X-Vec3::Z),
(v[1], -Vec3::X+0.5*Vec3::Y), (v[2], Vec3::Y), (v[4], Vec3::X+0.5*Vec3::Y),
(v[6], -Vec3::X+Vec3::Z), (v[7], Vec3::Z+0.5*Vec3::Y), (v[8], Vec3::X+Vec3::Z),
]);
mesh.add_attribute(AttributeKind::Positions, positions);
let new_face = super::chamfer(&mut mesh, v[2], 0.25);
assert!(!mesh.vertices.contains_key(v[2]));
assert!(!mesh.faces.contains_key(FaceId(KeyData::from_ffi(6))));
assert!(!mesh.faces.contains_key(FaceId(KeyData::from_ffi(7))));
assert_eq!(mesh.count_islands(), 1);
assert_eq!(mesh.count_face_edges(), 24);
assert_eq!(new_face, FaceId(KeyData::from_ffi(8)));
let positions = mesh.attribute(&AttributeKind::Positions).unwrap().as_vertices_vec3();
for vertex in mesh.vertex_keys() {
assert!(positions.contains_key(vertex))
}
}
}