diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/circle.rs similarity index 64% rename from crates/fj-core/src/algorithms/approx/path.rs rename to crates/fj-core/src/algorithms/approx/circle.rs index 827a00ff8..e1cb7b93d 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/circle.rs @@ -1,91 +1,50 @@ -//! # Path approximation -//! -//! Since paths are infinite (even circles have an infinite coordinate space, -//! even though they connect to themselves in global coordinates), a range must -//! be provided to approximate them. The approximation then returns points -//! within that range. -//! -//! The boundaries of the range are not included in the approximation. This is -//! done, to give the caller (who knows the boundary anyway) more options on how -//! to further process the approximation. -//! -//! ## Determinism -//! -//! Path approximation is carefully designed to produce a deterministic result -//! for the combination of a given path and a given tolerance, regardless of -//! what the range is. This is done to prevent invalid meshes from being -//! generated. -//! -//! In specific terms, this means there is an infinite set of points that -//! approximates a path, and that set is deterministic for a given combination -//! of path and tolerance. The range that defines where the path is approximated -//! only influences the result in two ways: -//! -//! 1. It controls which points from the infinite set are actually computed. -//! 2. It defines the order in which the computed points are returned. -//! -//! As a result, path approximation is guaranteed to generate points that can -//! fit together in a valid mesh, no matter which ranges of a path are being -//! approximated, and how many times. - use std::iter; use fj_math::{Circle, Point, Scalar, Sign}; -use crate::geometry::{CurveBoundary, Geometry, GlobalPath, SurfacePath}; - -use super::{Approx, Tolerance}; - -impl Approx for (&SurfacePath, CurveBoundary>) { - type Approximation = Vec<(Point<1>, Point<2>)>; - type Cache = (); - - fn approx_with_cache( - self, - tolerance: impl Into, - (): &mut Self::Cache, - _: &Geometry, - ) -> Self::Approximation { - let (path, range) = self; - - match path { - SurfacePath::Circle(circle) => { - approx_circle(circle, range, tolerance.into()) - } - SurfacePath::Line(_) => vec![], - } - } -} - -impl Approx for (GlobalPath, CurveBoundary>) { - type Approximation = Vec<(Point<1>, Point<3>)>; - type Cache = (); +use crate::geometry::CurveBoundary; - fn approx_with_cache( - self, - tolerance: impl Into, - (): &mut Self::Cache, - _: &Geometry, - ) -> Self::Approximation { - let (path, range) = self; - - match path { - GlobalPath::Circle(circle) => { - approx_circle(&circle, range, tolerance.into()) - } - GlobalPath::Line(_) => vec![], - } - } -} +use super::Tolerance; -/// Approximate a circle +/// # Approximate a circle +/// +/// ## Arguments +/// +/// Besides a circle, this method takes two arguments: +/// +/// - The `boundary` within which the circle should be approximated. +/// - The `tolerance` that specifies how much the approximation is allowed to +/// deviate from the actual circle. +/// +/// ## Return Value +/// +/// The approximation returns points within the provided boundary. The boundary +/// points themselves are not included in the approximation. This gives the +/// caller (who knows the boundary anyway) more options for how to further +/// process the approximation. +/// +/// ## Determinism +/// +/// Circle approximation is carefully designed to produce a deterministic result +/// for the combination of a given circle and tolerance, regardless of the +/// boundary. This is done to prevent invalid meshes from being generated. +/// +/// In specific terms, this means there is an infinite set of points that +/// approximates a circle (infinite, since the circle's local coordinate space +/// is infinite). That set is deterministic for a given combination of circle +/// and tolerance. The boundary that defines where the circle is approximated +/// only influences the result in two ways: +/// +/// 1. It controls which points from the infinite set are actually computed. +/// 2. It defines the order in which the computed points are returned. /// -/// `tolerance` specifies how much the approximation is allowed to deviate -/// from the circle. -fn approx_circle( +/// As a result, circle approximation is guaranteed to generate points that can +/// fit together in a valid mesh, no matter which ranges of a path are being +/// approximated, and how many times. +pub fn approx_circle( circle: &Circle, boundary: impl Into>>, - tolerance: Tolerance, + tolerance: impl Into, ) -> Vec<(Point<1>, Point)> { let boundary = boundary.into(); @@ -172,7 +131,7 @@ mod tests { use fj_math::{Circle, Point, Scalar}; - use crate::algorithms::approx::{path::CurveBoundary, Tolerance}; + use crate::{algorithms::approx::Tolerance, geometry::CurveBoundary}; use super::PathApproxParams; diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 82bff3dc3..426fd161f 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -1,50 +1,43 @@ -//! Curve approximation - use std::collections::BTreeMap; -use fj_math::Point; +use fj_math::{Circle, Line, Point}; use crate::{ - geometry::{ - CurveBoundary, Geometry, GlobalPath, HalfEdgeGeom, SurfaceGeom, - SurfacePath, - }, + geometry::{CurveBoundary, Geometry, GlobalPath, SurfaceGeom, SurfacePath}, storage::Handle, topology::{Curve, Surface}, }; -use super::{Approx, ApproxPoint, Tolerance}; - -impl Approx for (&Handle, &HalfEdgeGeom, &Handle) { - type Approximation = CurveApprox; - type Cache = CurveApproxCache; - - fn approx_with_cache( - self, - tolerance: impl Into, - cache: &mut Self::Cache, - geometry: &Geometry, - ) -> Self::Approximation { - let (curve, half_edge, surface) = self; - - match cache.get(curve, half_edge.boundary) { - Some(approx) => approx, - None => { - let approx = approx_curve( - &geometry - .of_curve(curve) - .unwrap() - .local_on(surface) - .unwrap() - .path, - geometry.of_surface(surface), - half_edge.boundary, - tolerance, - geometry, - ); - - cache.insert(curve.clone(), half_edge.boundary, approx) - } +use super::{circle::approx_circle, line::approx_line, ApproxPoint, Tolerance}; + +/// Approximate the provided curve +/// +/// The approximation is cached, and cached approximations are used, where +/// possible. +pub fn approx_curve_with_cache( + curve: &Handle, + surface: &Handle, + boundary: CurveBoundary>, + tolerance: impl Into, + cache: &mut CurveApproxCache, + geometry: &Geometry, +) -> CurveApprox { + match cache.get(curve, boundary) { + Some(approx) => approx, + None => { + let approx = approx_curve( + &geometry + .of_curve(curve) + .unwrap() + .local_on(surface) + .unwrap() + .path, + geometry.of_surface(surface), + boundary, + tolerance, + ); + + cache.insert(curve.clone(), boundary, approx) } } } @@ -54,82 +47,92 @@ fn approx_curve( surface: &SurfaceGeom, boundary: CurveBoundary>, tolerance: impl Into, - geometry: &Geometry, ) -> 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. - // - // 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.u) { (SurfacePath::Circle(_), GlobalPath::Circle(_)) => { - todo!( - "Approximating a circle on a curved surface not supported yet." - ) + approx_circle_on_curved_surface() } - (SurfacePath::Circle(_), GlobalPath::Line(_)) => { - (path, boundary) - .approx_with_cache(tolerance, &mut (), geometry) - .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.point_from_surface_coords(point_surface); - (point_curve, point_global) - }) - .collect() + (SurfacePath::Circle(circle), GlobalPath::Line(_)) => { + approx_circle_on_straight_surface( + circle, boundary, surface, tolerance, + ) } (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.u, range_u).approx_with_cache( - tolerance, - &mut (), - geometry, - ); - - 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.point_from_surface_coords(point_surface); - points.push((u, point_global)); - } - - points + approx_line_on_any_surface(line, boundary, surface, tolerance) } }; - let points = points + CurveApprox { points } +} + +fn approx_circle_on_curved_surface() -> Vec> { + todo!("Approximating a circle on a curved surface is not supported yet.") +} + +fn approx_circle_on_straight_surface( + circle: &Circle<2>, + boundary: CurveBoundary>, + surface: &SurfaceGeom, + tolerance: impl Into, +) -> Vec> { + approx_circle(circle, boundary, tolerance) .into_iter() - .map(|(point_curve, point_global)| { + .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.point_from_surface_coords(point_surface); ApproxPoint::new(point_curve, point_global) }) - .collect(); - CurveApprox { points } + .collect() } -/// Approximation of [`Curve`], within a specific boundary +fn approx_line_on_any_surface( + line: &Line<2>, + boundary: CurveBoundary>, + surface: &SurfaceGeom, + tolerance: impl Into, +) -> Vec> { + let range_u = CurveBoundary::from( + boundary + .inner + .map(|point_curve| [line.point_from_line_coords(point_curve).u]), + ); + + let approx_u = match surface.u { + GlobalPath::Circle(circle) => { + approx_circle(&circle, range_u, tolerance) + } + GlobalPath::Line(line) => approx_line(&line), + }; + + let mut points = Vec::new(); + for (u, _) in approx_u { + let t = (u.t - line.origin().u) / line.direction().u; + let point_surface = line.point_from_line_coords([t]); + let point_global = surface.point_from_surface_coords(point_surface); + points.push(ApproxPoint::new(u, point_global)); + } + + points +} + +/// Approximation of a [`Curve`], within a specific boundary +/// +/// The approximation of the curve only includes points _within_ the boundary, +/// not those _on_ the boundary. Those boundary points are part of half-edge +/// approximation, which uses and includes curve approximation. #[derive(Clone)] pub struct CurveApprox { /// The points that approximate the curve within the boundary @@ -183,54 +186,46 @@ impl CurveApproxCache { mod tests { use std::f64::consts::TAU; + use fj_math::{Circle, Point, Vector}; use pretty_assertions::assert_eq; use crate::{ - algorithms::approx::{Approx, ApproxPoint}, - geometry::{CurveBoundary, GlobalPath, HalfEdgeGeom, SurfacePath}, - operations::build::{BuildCurve, BuildSurface}, - topology::{Curve, Surface}, + algorithms::approx::{ + circle::approx_circle, curve::approx_curve, ApproxPoint, + }, + geometry::{CurveBoundary, GlobalPath, SurfaceGeom, SurfacePath}, + operations::build::BuildSurface, + topology::Surface, Core, }; #[test] fn approx_line_on_flat_surface() { - let mut core = Core::new(); + let core = Core::new(); - let surface = core.layers.topology.surfaces.xz_plane(); + let surface = core.layers.geometry.xz_plane(); let (path, boundary) = SurfacePath::line_from_points([[1., 1.], [2., 1.]]); - let curve = - Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from(boundary); - let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &half_edge, &surface) - .approx(tolerance, &core.layers.geometry); + let approx = approx_curve(&path, surface, boundary, tolerance); assert_eq!(approx.points, vec![]); } #[test] fn approx_line_on_curved_surface_but_not_along_curve() { - let mut core = Core::new(); - - let surface = Surface::from_uv( - GlobalPath::circle_from_radius(1.), - [0., 0., 1.], - &mut core, - ); + let surface = SurfaceGeom { + u: GlobalPath::circle_from_radius(1.), + v: Vector::from([0., 0., 1.]), + }; let (path, boundary) = SurfacePath::line_from_points([[1., 1.], [2., 1.]]); - let curve = - Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from(boundary); - let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &half_edge, &surface) - .approx(tolerance, &core.layers.geometry); + let approx = approx_curve(&path, &surface, boundary, tolerance); assert_eq!(approx.points, vec![]); } @@ -239,23 +234,23 @@ mod tests { fn approx_line_on_curved_surface_along_curve() { let mut core = Core::new(); - let global_path = GlobalPath::circle_from_radius(1.); - let surface = Surface::from_uv(global_path, [0., 0., 1.], &mut core); + let circle = Circle::from_center_and_radius(Point::origin(), 1.); + let global_path = GlobalPath::Circle(circle); + let surface_geom = SurfaceGeom { + u: global_path, + v: Vector::from([0., 0., 1.]), + }; + let surface = Surface::from_geometry(surface_geom, &mut core); let path = SurfacePath::line_from_points_with_coords([ ([0.], [0., 1.]), ([TAU], [TAU, 1.]), ]); - let curve = - Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from([[0.], [TAU]]); - let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &half_edge, &surface) - .approx(tolerance, &core.layers.geometry); + let approx = approx_curve(&path, &surface_geom, boundary, tolerance); - let expected_approx = (global_path, boundary) - .approx(tolerance, &core.layers.geometry) + let expected_approx = approx_circle(&circle, boundary, tolerance) .into_iter() .map(|(point_local, _)| { let point_surface = path.point_from_path_coords(point_local); @@ -274,19 +269,16 @@ mod tests { fn approx_circle_on_flat_surface() { let mut core = Core::new(); - let surface = core.layers.topology.surfaces.xz_plane(); - let path = SurfacePath::circle_from_center_and_radius([0., 0.], 1.); - let curve = - Curve::from_path_and_surface(path, surface.clone(), &mut core); + let surface_geom = *core.layers.geometry.xz_plane(); + let surface = Surface::from_geometry(surface_geom, &mut core); + let circle = Circle::from_center_and_radius([0., 0.], 1.); + let path = SurfacePath::Circle(circle); let boundary = CurveBoundary::from([[0.], [TAU]]); - let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &half_edge, &surface) - .approx(tolerance, &core.layers.geometry); + let approx = approx_curve(&path, &surface_geom, boundary, tolerance); - let expected_approx = (&path, boundary) - .approx(tolerance, &core.layers.geometry) + let expected_approx = approx_circle(&circle, boundary, tolerance) .into_iter() .map(|(point_local, _)| { let point_surface = path.point_from_path_coords(point_local); diff --git a/crates/fj-core/src/algorithms/approx/cycle.rs b/crates/fj-core/src/algorithms/approx/cycle.rs index 23f356b9f..256487eee 100644 --- a/crates/fj-core/src/algorithms/approx/cycle.rs +++ b/crates/fj-core/src/algorithms/approx/cycle.rs @@ -11,7 +11,7 @@ use crate::{ }; use super::{ - edge::{HalfEdgeApprox, HalfEdgeApproxCache}, + half_edge::{HalfEdgeApprox, HalfEdgeApproxCache}, Approx, ApproxPoint, Tolerance, }; diff --git a/crates/fj-core/src/algorithms/approx/edge.rs b/crates/fj-core/src/algorithms/approx/edge.rs deleted file mode 100644 index bc9ea427b..000000000 --- a/crates/fj-core/src/algorithms/approx/edge.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Edge approximation -//! -//! The approximation of a curve is its first vertex, combined with the -//! approximation of its curve. The second vertex is left out, as edge -//! approximations are usually used to build cycle approximations, and this way, -//! the caller doesn't have to deal with duplicate vertices. - -use crate::{ - geometry::Geometry, - storage::Handle, - topology::{HalfEdge, Surface}, -}; - -use super::{ - curve::CurveApproxCache, vertex::VertexApproxCache, Approx, ApproxPoint, - Tolerance, -}; - -impl Approx for (&Handle, &Handle) { - type Approximation = HalfEdgeApprox; - type Cache = HalfEdgeApproxCache; - - fn approx_with_cache( - self, - tolerance: impl Into, - cache: &mut Self::Cache, - geometry: &Geometry, - ) -> Self::Approximation { - let (half_edge, surface) = self; - let tolerance = tolerance.into(); - - let start_position_surface = - geometry.of_half_edge(half_edge).start_position( - &geometry - .of_curve(half_edge.curve()) - .unwrap() - .local_on(surface) - .unwrap() - .path, - ); - let start_position = - match cache.start_position.get(half_edge.start_vertex()) { - Some(position) => position, - None => { - let position_global = geometry - .of_surface(surface) - .point_from_surface_coords(start_position_surface); - cache.start_position.insert( - half_edge.start_vertex().clone(), - position_global, - ) - } - }; - - let first = ApproxPoint::new(start_position_surface, start_position); - - let rest = { - let approx = - (half_edge.curve(), geometry.of_half_edge(half_edge), surface) - .approx_with_cache(tolerance, &mut cache.curve, geometry); - - approx.points.into_iter().map(|point| { - let point_surface = geometry - .of_curve(half_edge.curve()) - .unwrap() - .local_on(surface) - .unwrap() - .path - .point_from_path_coords(point.local_form); - - ApproxPoint::new(point_surface, point.global_form) - }) - }; - - let mut points = vec![first]; - points.extend(rest); - - HalfEdgeApprox { points } - } -} - -/// An approximation of a [`HalfEdge`] -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct HalfEdgeApprox { - /// The points that approximate the half-edge - pub points: Vec>, -} - -/// Cache for half-edge approximations -#[derive(Default)] -pub struct HalfEdgeApproxCache { - start_position: VertexApproxCache, - curve: CurveApproxCache, -} diff --git a/crates/fj-core/src/algorithms/approx/face.rs b/crates/fj-core/src/algorithms/approx/face.rs index 1f939888a..aa0b809fa 100644 --- a/crates/fj-core/src/algorithms/approx/face.rs +++ b/crates/fj-core/src/algorithms/approx/face.rs @@ -12,7 +12,7 @@ use crate::{ }; use super::{ - cycle::CycleApprox, edge::HalfEdgeApproxCache, Approx, ApproxPoint, + cycle::CycleApprox, half_edge::HalfEdgeApproxCache, Approx, ApproxPoint, Tolerance, }; diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs new file mode 100644 index 000000000..2c8a07bcf --- /dev/null +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -0,0 +1,89 @@ +//! Half-edge approximation +//! +//! See [`HalfEdgeApprox`]. + +use std::iter; + +use crate::{ + geometry::Geometry, + storage::Handle, + topology::{HalfEdge, Surface}, +}; + +use super::{ + curve::{approx_curve_with_cache, CurveApproxCache}, + vertex::{approx_vertex, VertexApproxCache}, + Approx, ApproxPoint, Tolerance, +}; + +impl Approx for (&Handle, &Handle) { + type Approximation = HalfEdgeApprox; + type Cache = HalfEdgeApproxCache; + + fn approx_with_cache( + self, + tolerance: impl Into, + cache: &mut Self::Cache, + geometry: &Geometry, + ) -> Self::Approximation { + let (half_edge, surface) = self; + let tolerance = tolerance.into(); + + let boundary = geometry.of_half_edge(half_edge).boundary; + let [start_position_curve, _] = boundary.inner; + + let start = approx_vertex( + half_edge.start_vertex().clone(), + half_edge.curve(), + surface, + start_position_curve, + &mut cache.start_position, + geometry, + ); + + let rest = approx_curve_with_cache( + half_edge.curve(), + surface, + boundary, + tolerance, + &mut cache.curve, + geometry, + ); + + let points = iter::once(start) + .chain(rest.points) + .map(|point| { + let point_surface = geometry + .of_curve(half_edge.curve()) + .unwrap() + .local_on(surface) + .unwrap() + .path + .point_from_path_coords(point.local_form); + + ApproxPoint::new(point_surface, point.global_form) + }) + .collect(); + + HalfEdgeApprox { points } + } +} + +/// An approximation of a [`HalfEdge`] +/// +/// The approximation of a half-edge is its first vertex, combined with the +/// approximation of its curve. The second vertex is left out, as half-edge +/// approximations are usually used to build cycle approximations, and this way, +/// the caller doesn't have to deal with duplicate vertices. +#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct HalfEdgeApprox { + /// The points that approximate the half-edge + pub points: Vec>, +} + +/// Cache for half-edge approximations +#[derive(Default)] +pub struct HalfEdgeApproxCache { + start_position: VertexApproxCache, + curve: CurveApproxCache, +} diff --git a/crates/fj-core/src/algorithms/approx/line.rs b/crates/fj-core/src/algorithms/approx/line.rs new file mode 100644 index 000000000..e0d2e1a68 --- /dev/null +++ b/crates/fj-core/src/algorithms/approx/line.rs @@ -0,0 +1,16 @@ +use fj_math::{Line, Point}; + +/// Approximate a line +/// +/// Since curve approximations don't include the approximation boundary itself, +/// and a line does not require any other points to be fully defined, this +/// method always returns no points. +/// +/// The method still exists, to make the code that approximates lines, and thus +/// this piece of documentation, easy to find for anyone who's looking. +pub fn approx_line( + line: &Line, +) -> Vec<(Point<1>, Point)> { + let _ = line; + Vec::new() +} diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index 46b9b6001..c88042866 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -1,15 +1,17 @@ //! Approximation of objects -pub mod curve; pub mod cycle; -pub mod edge; pub mod face; -pub mod path; +pub mod half_edge; pub mod shell; pub mod sketch; pub mod solid; pub mod tolerance; -pub mod vertex; + +mod circle; +mod curve; +mod line; +mod vertex; use std::{ cmp::Ordering, diff --git a/crates/fj-core/src/algorithms/approx/shell.rs b/crates/fj-core/src/algorithms/approx/shell.rs index ed3fc77ad..2bdde91b6 100644 --- a/crates/fj-core/src/algorithms/approx/shell.rs +++ b/crates/fj-core/src/algorithms/approx/shell.rs @@ -4,7 +4,9 @@ use std::collections::BTreeSet; use crate::{geometry::Geometry, topology::Shell}; -use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; +use super::{ + face::FaceApprox, half_edge::HalfEdgeApproxCache, Approx, Tolerance, +}; impl Approx for &Shell { type Approximation = BTreeSet; diff --git a/crates/fj-core/src/algorithms/approx/sketch.rs b/crates/fj-core/src/algorithms/approx/sketch.rs index 9c8804df3..192d5ac56 100644 --- a/crates/fj-core/src/algorithms/approx/sketch.rs +++ b/crates/fj-core/src/algorithms/approx/sketch.rs @@ -4,7 +4,9 @@ use std::collections::BTreeSet; use crate::{geometry::Geometry, topology::Sketch}; -use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; +use super::{ + face::FaceApprox, half_edge::HalfEdgeApproxCache, Approx, Tolerance, +}; impl Approx for &Sketch { type Approximation = BTreeSet; diff --git a/crates/fj-core/src/algorithms/approx/solid.rs b/crates/fj-core/src/algorithms/approx/solid.rs index 8f3cc326c..3592100c2 100644 --- a/crates/fj-core/src/algorithms/approx/solid.rs +++ b/crates/fj-core/src/algorithms/approx/solid.rs @@ -4,7 +4,9 @@ use std::collections::BTreeSet; use crate::{geometry::Geometry, topology::Solid}; -use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; +use super::{ + face::FaceApprox, half_edge::HalfEdgeApproxCache, Approx, Tolerance, +}; impl Approx for &Solid { type Approximation = BTreeSet; diff --git a/crates/fj-core/src/algorithms/approx/vertex.rs b/crates/fj-core/src/algorithms/approx/vertex.rs index ca641eb4d..92530dfb0 100644 --- a/crates/fj-core/src/algorithms/approx/vertex.rs +++ b/crates/fj-core/src/algorithms/approx/vertex.rs @@ -1,10 +1,44 @@ -//! Vertex approximation - use std::collections::BTreeMap; use fj_math::Point; -use crate::{storage::Handle, topology::Vertex}; +use crate::{ + geometry::Geometry, + storage::Handle, + topology::{Curve, Surface, Vertex}, +}; + +use super::ApproxPoint; + +/// # Approximate a vertex position +pub fn approx_vertex( + vertex: Handle, + curve: &Handle, + surface: &Handle, + position_curve: Point<1>, + cache: &mut VertexApproxCache, + geometry: &Geometry, +) -> ApproxPoint<1> { + let position_surface = geometry + .of_curve(curve) + .unwrap() + .local_on(surface) + .unwrap() + .path + .point_from_path_coords(position_curve); + + let position_global = match cache.get(&vertex) { + Some(position) => position, + None => { + let position_global = geometry + .of_surface(surface) + .point_from_surface_coords(position_surface); + cache.insert(vertex, position_global) + } + }; + + ApproxPoint::new(position_curve, position_global) +} /// Cache for vertex approximations #[derive(Default)]