diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index b0a658a2a..de4ca250f 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -8,6 +8,7 @@ use crate::{ geometry::{CurveBoundary, GlobalPath, SurfacePath}, objects::{Curve, Surface}, storage::{Handle, HandleWrapper}, + Core, }; use super::{Approx, ApproxPoint, Tolerance}; @@ -27,14 +28,20 @@ impl Approx self, tolerance: impl Into, cache: &mut Self::Cache, + core: &mut Core, ) -> Self::Approximation { let (curve, surface_path, surface, boundary) = self; match cache.get(curve, boundary) { Some(approx) => approx, None => { - let approx = - approx_curve(&surface_path, surface, boundary, tolerance); + let approx = approx_curve( + &surface_path, + surface, + boundary, + tolerance, + core, + ); cache.insert(curve.clone(), boundary, approx) } @@ -47,6 +54,7 @@ fn approx_curve( surface: &Surface, boundary: CurveBoundary>, tolerance: impl Into, + core: &mut Core, ) -> CurveApprox { // There are different cases of varying complexity. Circles are the hard // part here, as they need to be approximated, while lines don't need to be. @@ -54,60 +62,63 @@ fn approx_curve( // This will probably all be unified eventually, as `SurfacePath` and // `GlobalPath` grow APIs that are better suited to implementing this code // in a more abstract way. - let points = match (path, surface.geometry().u) { - (SurfacePath::Circle(_), GlobalPath::Circle(_)) => { - todo!( + let points = + match (path, surface.geometry().u) { + (SurfacePath::Circle(_), GlobalPath::Circle(_)) => { + todo!( "Approximating a circle on a curved surface not supported yet." ) - } - (SurfacePath::Circle(_), GlobalPath::Line(_)) => { - (path, boundary) - .approx_with_cache(tolerance, &mut ()) - .into_iter() - .map(|(point_curve, point_surface)| { - // We're throwing away `point_surface` here, which is a bit - // weird, as we're recomputing it later (outside of this - // function). - // - // It should be fine though: - // - // 1. We're throwing this version away, so there's no danger - // of inconsistency between this and the later version. - // 2. This version should have been computed using the same - // path and parameters and the later version will be, so - // they should be the same anyway. - // 3. Not all other cases handled in this function have a - // surface point available, so it needs to be computed - // later anyway, in the general case. - + } + (SurfacePath::Circle(_), GlobalPath::Line(_)) => { + (path, boundary) + .approx_with_cache(tolerance, &mut (), core) + .into_iter() + .map(|(point_curve, point_surface)| { + // We're throwing away `point_surface` here, which is a + // bit weird, as we're recomputing it later (outside of + // this function). + // + // It should be fine though: + // + // 1. We're throwing this version away, so there's no + // danger of inconsistency between this and the later + // version. + // 2. This version should have been computed using the + // same path and parameters and the later version + // will be, so they should be the same anyway. + // 3. Not all other cases handled in this function have + // a surface point available, so it needs to be + // computed later anyway, in the general case. + + let point_global = surface + .geometry() + .point_from_surface_coords(point_surface); + (point_curve, point_global) + }) + .collect() + } + (SurfacePath::Line(line), _) => { + let range_u = + CurveBoundary::from(boundary.inner.map(|point_curve| { + [path.point_from_path_coords(point_curve).u] + })); + + let approx_u = (surface.geometry().u, range_u) + .approx_with_cache(tolerance, &mut (), core); + + let mut points = Vec::new(); + for (u, _) in approx_u { + let t = (u.t - line.origin().u) / line.direction().u; + let point_surface = path.point_from_path_coords([t]); let point_global = surface .geometry() .point_from_surface_coords(point_surface); - (point_curve, point_global) - }) - .collect() - } - (SurfacePath::Line(line), _) => { - let range_u = - CurveBoundary::from(boundary.inner.map(|point_curve| { - [path.point_from_path_coords(point_curve).u] - })); - - let approx_u = (surface.geometry().u, range_u) - .approx_with_cache(tolerance, &mut ()); - - let mut points = Vec::new(); - for (u, _) in approx_u { - let t = (u.t - line.origin().u) / line.direction().u; - let point_surface = path.point_from_path_coords([t]); - let point_global = - surface.geometry().point_from_surface_coords(point_surface); - points.push((u, point_global)); - } + points.push((u, point_global)); + } - points - } - }; + points + } + }; let points = points .into_iter() @@ -195,8 +206,8 @@ mod tests { let surface = core.layers.objects.surfaces.xz_plane(); let tolerance = 1.; - let approx = - (&curve, surface_path, surface.deref(), boundary).approx(tolerance); + let approx = (&curve, surface_path, surface.deref(), boundary) + .approx(tolerance, &mut core); assert_eq!(approx.points, vec![]); } @@ -215,8 +226,8 @@ mod tests { }); let tolerance = 1.; - let approx = - (&curve, surface_path, &surface, boundary).approx(tolerance); + let approx = (&curve, surface_path, &surface, boundary) + .approx(tolerance, &mut core); assert_eq!(approx.points, vec![]); } @@ -238,11 +249,11 @@ mod tests { }); let tolerance = 1.; - let approx = - (&curve, surface_path, &surface, boundary).approx(tolerance); + let approx = (&curve, surface_path, &surface, boundary) + .approx(tolerance, &mut core); let expected_approx = (global_path, boundary) - .approx(tolerance) + .approx(tolerance, &mut core) .into_iter() .map(|(point_local, _)| { let point_surface = @@ -266,11 +277,11 @@ mod tests { let surface = core.layers.objects.surfaces.xz_plane(); let tolerance = 1.; - let approx = - (&curve, surface_path, surface.deref(), boundary).approx(tolerance); + let approx = (&curve, surface_path, surface.deref(), boundary) + .approx(tolerance, &mut core); let expected_approx = (&surface_path, boundary) - .approx(tolerance) + .approx(tolerance, &mut core) .into_iter() .map(|(point_local, _)| { let point_surface = diff --git a/crates/fj-core/src/algorithms/approx/cycle.rs b/crates/fj-core/src/algorithms/approx/cycle.rs index 091ff9cb8..e14f884d2 100644 --- a/crates/fj-core/src/algorithms/approx/cycle.rs +++ b/crates/fj-core/src/algorithms/approx/cycle.rs @@ -6,7 +6,10 @@ use std::ops::Deref; use fj_math::Segment; -use crate::objects::{Cycle, Surface}; +use crate::{ + objects::{Cycle, Surface}, + Core, +}; use super::{ edge::{HalfEdgeApprox, HalfEdgeApproxCache}, @@ -21,6 +24,7 @@ impl Approx for (&Cycle, &Surface) { self, tolerance: impl Into, cache: &mut Self::Cache, + core: &mut Core, ) -> Self::Approximation { let (cycle, surface) = self; let tolerance = tolerance.into(); @@ -29,7 +33,8 @@ impl Approx for (&Cycle, &Surface) { .half_edges() .iter() .map(|edge| { - (edge.deref(), surface).approx_with_cache(tolerance, cache) + (edge.deref(), surface) + .approx_with_cache(tolerance, cache, core) }) .collect(); diff --git a/crates/fj-core/src/algorithms/approx/edge.rs b/crates/fj-core/src/algorithms/approx/edge.rs index c9303b25d..961944c0c 100644 --- a/crates/fj-core/src/algorithms/approx/edge.rs +++ b/crates/fj-core/src/algorithms/approx/edge.rs @@ -5,7 +5,10 @@ //! approximations are usually used to build cycle approximations, and this way, //! the caller doesn't have to deal with duplicate vertices. -use crate::objects::{HalfEdge, Surface}; +use crate::{ + objects::{HalfEdge, Surface}, + Core, +}; use super::{ curve::CurveApproxCache, vertex::VertexApproxCache, Approx, ApproxPoint, @@ -20,6 +23,7 @@ impl Approx for (&HalfEdge, &Surface) { self, tolerance: impl Into, cache: &mut Self::Cache, + core: &mut Core, ) -> Self::Approximation { let (edge, surface) = self; let tolerance = tolerance.into(); @@ -42,7 +46,7 @@ impl Approx for (&HalfEdge, &Surface) { let rest = { let approx = (edge.curve(), edge.path(), surface, edge.boundary()) - .approx_with_cache(tolerance, &mut cache.curve); + .approx_with_cache(tolerance, &mut cache.curve, core); approx.points.into_iter().map(|point| { let point_surface = diff --git a/crates/fj-core/src/algorithms/approx/face.rs b/crates/fj-core/src/algorithms/approx/face.rs index 78a1d4357..1c7484d78 100644 --- a/crates/fj-core/src/algorithms/approx/face.rs +++ b/crates/fj-core/src/algorithms/approx/face.rs @@ -8,7 +8,9 @@ use fj_interop::Color; use crate::{ objects::{Face, Handedness, ObjectSet}, + operations::presentation::GetColor, validate::ValidationConfig, + Core, }; use super::{ @@ -24,12 +26,13 @@ impl Approx for &ObjectSet { self, tolerance: impl Into, cache: &mut Self::Cache, + core: &mut Core, ) -> Self::Approximation { let tolerance = tolerance.into(); let approx = self .into_iter() - .map(|face| face.approx_with_cache(tolerance, cache)) + .map(|face| face.approx_with_cache(tolerance, cache, core)) .collect(); let min_distance = ValidationConfig::default().distinct_min_distance; @@ -70,6 +73,7 @@ impl Approx for &Face { self, tolerance: impl Into, cache: &mut Self::Cache, + core: &mut Core, ) -> Self::Approximation { let tolerance = tolerance.into(); @@ -88,19 +92,19 @@ impl Approx for &Face { let exterior = (self.region().exterior().deref(), self.surface().deref()) - .approx_with_cache(tolerance, cache); + .approx_with_cache(tolerance, cache, core); let mut interiors = BTreeSet::new(); for cycle in self.region().interiors() { let cycle = (cycle.deref(), self.surface().deref()) - .approx_with_cache(tolerance, cache); + .approx_with_cache(tolerance, cache, core); interiors.insert(cycle); } FaceApprox { exterior, interiors, - color: self.region().color(), + color: self.region().get_color(core), coord_handedness: self.coord_handedness(), } } diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index 513763c59..ad9ba4bf8 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -19,7 +19,7 @@ use std::{ use fj_math::Point; -use crate::objects::Surface; +use crate::{objects::Surface, Core}; pub use self::tolerance::{InvalidTolerance, Tolerance}; @@ -35,9 +35,13 @@ pub trait Approx: Sized { /// /// `tolerance` defines how far the approximation is allowed to deviate from /// the actual object. - fn approx(self, tolerance: impl Into) -> Self::Approximation { + fn approx( + self, + tolerance: impl Into, + core: &mut Core, + ) -> Self::Approximation { let mut cache = Self::Cache::default(); - self.approx_with_cache(tolerance, &mut cache) + self.approx_with_cache(tolerance, &mut cache, core) } /// Approximate the object, using the provided cache @@ -48,6 +52,7 @@ pub trait Approx: Sized { self, tolerance: impl Into, cache: &mut Self::Cache, + core: &mut Core, ) -> Self::Approximation; } diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index ad55f9eeb..6c27bd298 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -32,7 +32,10 @@ use std::iter; use fj_math::{Circle, Point, Scalar, Sign}; -use crate::geometry::{CurveBoundary, GlobalPath, SurfacePath}; +use crate::{ + geometry::{CurveBoundary, GlobalPath, SurfacePath}, + Core, +}; use super::{Approx, Tolerance}; @@ -44,6 +47,7 @@ impl Approx for (&SurfacePath, CurveBoundary>) { self, tolerance: impl Into, (): &mut Self::Cache, + _core: &mut Core, ) -> Self::Approximation { let (path, range) = self; @@ -64,6 +68,7 @@ impl Approx for (GlobalPath, CurveBoundary>) { self, tolerance: impl Into, (): &mut Self::Cache, + _core: &mut Core, ) -> Self::Approximation { let (path, range) = self; diff --git a/crates/fj-core/src/algorithms/approx/shell.rs b/crates/fj-core/src/algorithms/approx/shell.rs index 2a2fff755..6bfc40da8 100644 --- a/crates/fj-core/src/algorithms/approx/shell.rs +++ b/crates/fj-core/src/algorithms/approx/shell.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; -use crate::objects::Shell; +use crate::{objects::Shell, Core}; use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; @@ -14,7 +14,8 @@ impl Approx for &Shell { self, tolerance: impl Into, cache: &mut Self::Cache, + core: &mut Core, ) -> Self::Approximation { - self.faces().approx_with_cache(tolerance, cache) + self.faces().approx_with_cache(tolerance, cache, core) } } diff --git a/crates/fj-core/src/algorithms/approx/sketch.rs b/crates/fj-core/src/algorithms/approx/sketch.rs index 1a49c0c95..34b16b434 100644 --- a/crates/fj-core/src/algorithms/approx/sketch.rs +++ b/crates/fj-core/src/algorithms/approx/sketch.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; -use crate::objects::Sketch; +use crate::{objects::Sketch, Core}; use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; @@ -14,6 +14,7 @@ impl Approx for &Sketch { self, _tolerance: impl Into, _cache: &mut Self::Cache, + _core: &mut Core, ) -> Self::Approximation { todo!() } diff --git a/crates/fj-core/src/algorithms/approx/solid.rs b/crates/fj-core/src/algorithms/approx/solid.rs index c24d0ed3f..dcceb4e08 100644 --- a/crates/fj-core/src/algorithms/approx/solid.rs +++ b/crates/fj-core/src/algorithms/approx/solid.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; -use crate::objects::Solid; +use crate::{objects::Solid, Core}; use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; @@ -14,12 +14,13 @@ impl Approx for &Solid { self, tolerance: impl Into, cache: &mut Self::Cache, + core: &mut Core, ) -> Self::Approximation { let tolerance = tolerance.into(); self.shells() .iter() - .flat_map(|shell| shell.approx_with_cache(tolerance, cache)) + .flat_map(|shell| shell.approx_with_cache(tolerance, cache, core)) .collect() } } diff --git a/crates/fj-core/src/algorithms/triangulate/mod.rs b/crates/fj-core/src/algorithms/triangulate/mod.rs index 534cca377..0ec1e5ab7 100644 --- a/crates/fj-core/src/algorithms/triangulate/mod.rs +++ b/crates/fj-core/src/algorithms/triangulate/mod.rs @@ -6,6 +6,8 @@ mod polygon; use fj_interop::Mesh; use fj_math::Point; +use crate::Core; + use self::polygon::Polygon; use super::approx::{face::FaceApprox, Approx, Tolerance}; @@ -13,9 +15,9 @@ use super::approx::{face::FaceApprox, Approx, Tolerance}; /// Triangulate a shape pub trait Triangulate: Sized { /// Triangulate the shape - fn triangulate(self) -> Mesh> { + fn triangulate(self, core: &mut Core) -> Mesh> { let mut mesh = Mesh::new(); - self.triangulate_into_mesh(&mut mesh); + self.triangulate_into_mesh(&mut mesh, core); mesh } @@ -23,7 +25,7 @@ pub trait Triangulate: Sized { /// /// This is a low-level method, intended for implementation of /// `Triangulate`. Most callers should prefer [`Triangulate::triangulate`]. - fn triangulate_into_mesh(self, mesh: &mut Mesh>); + fn triangulate_into_mesh(self, mesh: &mut Mesh>, core: &mut Core); } impl Triangulate for (T, Tolerance) @@ -31,19 +33,23 @@ where T: Approx, T::Approximation: IntoIterator, { - fn triangulate_into_mesh(self, mesh: &mut Mesh>) { + fn triangulate_into_mesh(self, mesh: &mut Mesh>, core: &mut Core) { let (approx, tolerance) = self; - let approx = approx.approx(tolerance); + let approx = approx.approx(tolerance, core); for approx in approx { - approx.triangulate_into_mesh(mesh); + approx.triangulate_into_mesh(mesh, core); } } } impl Triangulate for FaceApprox { - fn triangulate_into_mesh(self, mesh: &mut Mesh>) { + fn triangulate_into_mesh( + self, + mesh: &mut Mesh>, + _core: &mut Core, + ) { let face_as_polygon = Polygon::new() .with_exterior( self.exterior @@ -115,7 +121,7 @@ mod tests { let c = Point::from(c).to_xyz(); let d = Point::from(d).to_xyz(); - let triangles = triangulate(face)?; + let triangles = triangulate(face, &mut core)?; assert!(triangles.contains_triangle([a, b, d])); assert!(triangles.contains_triangle([b, c, d])); @@ -153,7 +159,7 @@ mod tests { &mut core, ); - let triangles = triangulate(face)?; + let triangles = triangulate(face, &mut core)?; let a = surface.geometry().point_from_surface_coords(a); let b = surface.geometry().point_from_surface_coords(b); @@ -216,7 +222,7 @@ mod tests { &mut core, ); - let triangles = triangulate(face)?; + let triangles = triangulate(face, &mut core)?; let a = surface.geometry().point_from_surface_coords(a); let b = surface.geometry().point_from_surface_coords(b); @@ -231,8 +237,11 @@ mod tests { Ok(()) } - fn triangulate(face: Face) -> anyhow::Result>> { + fn triangulate( + face: Face, + core: &mut Core, + ) -> anyhow::Result>> { let tolerance = Tolerance::from_scalar(Scalar::ONE)?; - Ok(face.approx(tolerance).triangulate()) + Ok(face.approx(tolerance, core).triangulate(core)) } } diff --git a/crates/fj-core/src/operations/presentation.rs b/crates/fj-core/src/operations/presentation.rs index 84912d436..ff6232f18 100644 --- a/crates/fj-core/src/operations/presentation.rs +++ b/crates/fj-core/src/operations/presentation.rs @@ -8,6 +8,18 @@ use crate::{ Core, }; +/// Get the color of an object +pub trait GetColor { + /// Get the color of the object + fn get_color(&self, core: &mut Core) -> Option; +} + +impl GetColor for Handle { + fn get_color(&self, core: &mut Core) -> Option { + core.layers.presentation.color.get(self).copied() + } +} + /// Set the color of an object pub trait SetColor: IsObject { /// Set the color of the object diff --git a/crates/fj/src/instance.rs b/crates/fj/src/instance.rs index 28e575d64..7278298f8 100644 --- a/crates/fj/src/instance.rs +++ b/crates/fj/src/instance.rs @@ -84,7 +84,7 @@ impl Instance { Some(user_defined_tolerance) => user_defined_tolerance, }; - let mesh = (model, tolerance).triangulate(); + let mesh = (model, tolerance).triangulate(&mut self.core); if let Some(path) = args.export { crate::export::export(&mesh, &path)?;