diff --git a/fj-app/src/camera.rs b/fj-app/src/camera.rs index 76bb8148a..069a5cf86 100644 --- a/fj-app/src/camera.rs +++ b/fj-app/src/camera.rs @@ -1,6 +1,6 @@ use std::f64::consts::FRAC_PI_2; -use fj_interop::mesh::Triangle; +use fj_interop::mesh::Mesh; use fj_math::{Aabb, Scalar}; use nalgebra::{Point, TAffine, Transform, Translation, Vector}; use parry3d_f64::query::{Ray, RayCast as _}; @@ -131,7 +131,7 @@ impl Camera { &self, window: &Window, cursor: Option>, - triangles: &[Triangle], + mesh: &Mesh>, ) -> FocusPoint { let cursor = match cursor { Some(cursor) => cursor, @@ -147,7 +147,7 @@ impl Camera { let mut min_t = None; - for triangle in triangles { + for triangle in mesh.triangles() { let t = triangle.inner.to_parry().cast_local_ray( &ray, f64::INFINITY, diff --git a/fj-app/src/graphics/vertices.rs b/fj-app/src/graphics/vertices.rs index aa0cf1529..43a2c8610 100644 --- a/fj-app/src/graphics/vertices.rs +++ b/fj-app/src/graphics/vertices.rs @@ -1,7 +1,7 @@ use bytemuck::{Pod, Zeroable}; use fj_interop::{ debug::DebugInfo, - mesh::{Index, Mesh, Triangle}, + mesh::{Index, Mesh}, }; use nalgebra::{vector, Point}; @@ -66,22 +66,22 @@ impl Vertices { } } -impl From<&Vec> for Vertices { - fn from(triangles: &Vec) -> Self { - let mut mesh = Mesh::new(); +impl From<&Mesh>> for Vertices { + fn from(mesh: &Mesh>) -> Self { + let mut m = Mesh::new(); - for triangle in triangles { + for triangle in mesh.triangles() { let [a, b, c] = triangle.inner.points(); let normal = (b - a).cross(&(c - a)).normalize(); let color = triangle.color; - mesh.push((a, normal, color)); - mesh.push((b, normal, color)); - mesh.push((c, normal, color)); + m.push_vertex((a, normal, color)); + m.push_vertex((b, normal, color)); + m.push_vertex((c, normal, color)); } - let vertices = mesh + let vertices = m .vertices() .map(|(vertex, normal, color)| Vertex { position: vertex.into(), @@ -90,7 +90,7 @@ impl From<&Vec> for Vertices { }) .collect(); - let indices = mesh.indices().collect(); + let indices = m.indices().collect(); Self { vertices, indices } } diff --git a/fj-app/src/input/handler.rs b/fj-app/src/input/handler.rs index 8d5d98cfc..42eca7824 100644 --- a/fj-app/src/input/handler.rs +++ b/fj-app/src/input/handler.rs @@ -1,6 +1,7 @@ use std::time::Instant; -use fj_interop::mesh::Triangle; +use fj_interop::mesh::Mesh; +use fj_math::Point; use winit::{ dpi::PhysicalPosition, event::{ @@ -121,9 +122,9 @@ impl Handler { now: Instant, camera: &mut Camera, window: &Window, - triangles: &[Triangle], + mesh: &Mesh>, ) { - let focus_point = camera.focus_point(window, self.cursor, triangles); + let focus_point = camera.focus_point(window, self.cursor, mesh); self.zoom.discard_old_events(now); self.zoom.update_speed(now, delta_t, focus_point, camera); diff --git a/fj-app/src/main.rs b/fj-app/src/main.rs index e0841a77e..37f81303d 100644 --- a/fj-app/src/main.rs +++ b/fj-app/src/main.rs @@ -9,10 +9,9 @@ use std::path::PathBuf; use std::{collections::HashMap, time::Instant}; use fj_host::Model; -use fj_interop::mesh::Triangle; use fj_interop::{debug::DebugInfo, mesh::Mesh}; use fj_kernel::algorithms::triangulate; -use fj_math::{Aabb, Scalar}; +use fj_math::{Aabb, Point, Scalar}; use fj_operations::ToShape as _; use futures::executor::block_on; use tracing::{trace, warn}; @@ -85,18 +84,10 @@ fn main() -> anyhow::Result<()> { let shape = model.load_once(¶meters)?; let shape = shape_processor.process(&shape); - let mut mesh_maker = Mesh::new(); - - for triangle in shape.triangles { - for vertex in triangle.inner.points() { - mesh_maker.push(vertex); - } - } - let vertices = - mesh_maker.vertices().map(|vertex| vertex.into()).collect(); + shape.mesh.vertices().map(|vertex| vertex.into()).collect(); - let indices: Vec<_> = mesh_maker.indices().collect(); + let indices: Vec<_> = shape.mesh.indices().collect(); let triangles = indices .chunks(3) .map(|triangle| { @@ -187,7 +178,7 @@ fn main() -> anyhow::Result<()> { let focus_point = camera.focus_point( &window, input_handler.cursor(), - &shape.triangles, + &shape.mesh, ); input_handler.handle_mouse_input( @@ -213,7 +204,7 @@ fn main() -> anyhow::Result<()> { now, camera, &window, - &shape.triangles, + &shape.mesh, ); } @@ -292,17 +283,15 @@ impl ShapeProcessor { }; let mut debug_info = DebugInfo::new(); - let mut triangles = Vec::new(); - triangulate( + let mesh = triangulate( shape.to_shape(tolerance, &mut debug_info), tolerance, - &mut triangles, &mut debug_info, ); ProcessedShape { aabb, - triangles, + mesh, debug_info, } } @@ -310,14 +299,14 @@ impl ShapeProcessor { struct ProcessedShape { aabb: Aabb<3>, - triangles: Vec, + mesh: Mesh>, debug_info: DebugInfo, } impl ProcessedShape { fn update_geometry(&self, renderer: &mut Renderer) { renderer.update_geometry( - (&self.triangles).into(), + (&self.mesh).into(), (&self.debug_info).into(), self.aabb, ); diff --git a/fj-interop/src/mesh.rs b/fj-interop/src/mesh.rs index c596f8da8..b65874fa9 100644 --- a/fj-interop/src/mesh.rs +++ b/fj-interop/src/mesh.rs @@ -2,12 +2,15 @@ use std::{collections::HashMap, hash::Hash}; +use fj_math::Point; + /// A triangle mesh pub struct Mesh { vertices: Vec, indices: Vec, indices_by_vertex: HashMap, + triangles: Vec, } impl Mesh @@ -20,7 +23,7 @@ where } /// Add a vertex to the mesh - pub fn push(&mut self, vertex: V) { + pub fn push_vertex(&mut self, vertex: V) { let index = *self.indices_by_vertex.entry(vertex).or_insert_with(|| { let index = self.vertices.len(); @@ -31,6 +34,25 @@ where self.indices.push(index); } + /// Determine whether the mesh contains the provided triangle + /// + /// Returns true, if a triangle with any combination of the points of the + /// provided triangle is part of the mesh. + pub fn contains_triangle( + &self, + triangle: impl Into>, + ) -> bool { + let triangle = triangle.into().normalize(); + + for t in &self.triangles { + if triangle == t.inner.normalize() { + return true; + } + } + + false + } + /// Access the vertices of the mesh pub fn vertices(&self) -> impl Iterator + '_ { self.vertices.iter().copied() @@ -40,6 +62,28 @@ where pub fn indices(&self) -> impl Iterator + '_ { self.indices.iter().copied() } + + /// Access the triangles of the mesh + pub fn triangles(&self) -> impl Iterator + '_ { + self.triangles.iter().copied() + } +} + +impl Mesh> { + /// Add a triangle to the mesh + pub fn push_triangle( + &mut self, + triangle: impl Into>, + color: Color, + ) { + let triangle = triangle.into(); + + for point in triangle.points() { + self.push_vertex(point); + } + + self.triangles.push(Triangle::new(triangle, color)); + } } // This needs to be a manual implementation. Deriving `Default` would require @@ -50,6 +94,7 @@ impl Default for Mesh { vertices: Default::default(), indices: Default::default(), indices_by_vertex: Default::default(), + triangles: Default::default(), } } } diff --git a/fj-kernel/src/algorithms/triangulation/mod.rs b/fj-kernel/src/algorithms/triangulation/mod.rs index ccd678036..2355ade7d 100644 --- a/fj-kernel/src/algorithms/triangulation/mod.rs +++ b/fj-kernel/src/algorithms/triangulation/mod.rs @@ -2,8 +2,8 @@ mod delaunay; mod polygon; mod ray; -use fj_interop::{debug::DebugInfo, mesh::Triangle}; -use fj_math::Scalar; +use fj_interop::{debug::DebugInfo, mesh::Mesh}; +use fj_math::{Point, Scalar}; use crate::{shape::Shape, topology::Face}; @@ -15,9 +15,10 @@ use super::FaceApprox; pub fn triangulate( mut shape: Shape, tolerance: Scalar, - out: &mut Vec, debug_info: &mut DebugInfo, -) { +) -> Mesh> { + let mut mesh = Mesh::new(); + for face in shape.topology().faces() { let face = face.get(); match &face { @@ -61,20 +62,26 @@ pub fn triangulate( ) }); - out.extend(triangles.into_iter().map(|triangle| { + for triangle in triangles { let points = triangle.map(|point| point.canonical()); - Triangle::new(points, *color) - })); + mesh.push_triangle(points, *color); + } + } + Face::Triangles(triangles) => { + for triangle in triangles { + mesh.push_triangle(triangle.inner, triangle.color); + } } - Face::Triangles(triangles) => out.extend(triangles), } } + + mesh } #[cfg(test)] mod tests { - use fj_interop::{debug::DebugInfo, mesh::Triangle}; - use fj_math::Scalar; + use fj_interop::{debug::DebugInfo, mesh::Mesh}; + use fj_math::{Point, Scalar}; use crate::{geometry::Surface, shape::Shape, topology::Face}; @@ -92,10 +99,10 @@ mod tests { .build()?; let triangles = triangulate(shape); - assert!(triangles.contains([a, b, d])); - assert!(triangles.contains([b, c, d])); - assert!(!triangles.contains([a, b, c])); - assert!(!triangles.contains([a, c, d])); + assert!(triangles.contains_triangle([a, b, d])); + assert!(triangles.contains_triangle([b, c, d])); + assert!(!triangles.contains_triangle([a, b, c])); + assert!(!triangles.contains_triangle([a, c, d])); Ok(()) } @@ -123,44 +130,22 @@ mod tests { // Should contain some triangles from the polygon. Don't need to test // them all. - assert!(triangles.contains([a, e, h])); - assert!(triangles.contains([a, d, h])); + assert!(triangles.contains_triangle([a, e, h])); + assert!(triangles.contains_triangle([a, d, h])); // Shouldn't contain any possible triangle from the hole. - assert!(!triangles.contains([e, f, g])); - assert!(!triangles.contains([e, g, h])); - assert!(!triangles.contains([e, f, h])); - assert!(!triangles.contains([f, g, h])); + assert!(!triangles.contains_triangle([e, f, g])); + assert!(!triangles.contains_triangle([e, g, h])); + assert!(!triangles.contains_triangle([e, f, h])); + assert!(!triangles.contains_triangle([f, g, h])); Ok(()) } - fn triangulate(shape: Shape) -> Triangles { + fn triangulate(shape: Shape) -> Mesh> { let tolerance = Scalar::ONE; - let mut triangles = Vec::new(); let mut debug_info = DebugInfo::new(); - - super::triangulate(shape, tolerance, &mut triangles, &mut debug_info); - - for triangle in &mut triangles { - *triangle = Triangle { - inner: triangle.inner.normalize(), - ..*triangle - }; - } - - Triangles(triangles) - } - - #[derive(Debug)] - struct Triangles(Vec); - - impl Triangles { - fn contains(&self, triangle: impl Into>) -> bool { - let triangle = - Triangle::new(triangle.into().normalize(), [255, 0, 0, 255]); - self.0.contains(&triangle) - } + super::triangulate(shape, tolerance, &mut debug_info) } }