diff --git a/crates/fj-core/src/algorithms/approx/edge.rs b/crates/fj-core/src/algorithms/approx/edge.rs index de5f1e9d8..a66d02e78 100644 --- a/crates/fj-core/src/algorithms/approx/edge.rs +++ b/crates/fj-core/src/algorithms/approx/edge.rs @@ -10,7 +10,7 @@ use std::collections::BTreeMap; use fj_math::Point; use crate::{ - geometry::{BoundaryOnCurve, GlobalPath, SurfacePath}, + geometry::{CurveBoundary, GlobalPath, SurfacePath}, objects::{GlobalEdge, HalfEdge, Surface, Vertex}, storage::{Handle, HandleWrapper}, }; @@ -141,7 +141,7 @@ impl HalfEdgeApprox { fn approx_edge( path: &SurfacePath, surface: &Surface, - boundary: BoundaryOnCurve, + boundary: CurveBoundary>, tolerance: impl Into, ) -> GlobalEdgeApprox { // There are different cases of varying complexity. Circles are the hard @@ -185,7 +185,7 @@ fn approx_edge( } (SurfacePath::Line(line), _) => { let range_u = - BoundaryOnCurve::from(boundary.inner.map(|point_curve| { + CurveBoundary::from(boundary.inner.map(|point_curve| { [path.point_from_path_coords(point_curve).u] })); @@ -218,7 +218,7 @@ fn approx_edge( #[derive(Default)] pub struct EdgeCache { edge_approx: BTreeMap< - (HandleWrapper, BoundaryOnCurve), + (HandleWrapper, CurveBoundary>), GlobalEdgeApprox, >, vertex_approx: BTreeMap, Point<3>>, @@ -234,7 +234,7 @@ impl EdgeCache { fn get_edge( &self, handle: Handle, - boundary: BoundaryOnCurve, + boundary: CurveBoundary>, ) -> Option { if let Some(approx) = self.edge_approx.get(&(handle.clone().into(), boundary)) @@ -256,7 +256,7 @@ impl EdgeCache { fn insert_edge( &mut self, handle: Handle, - boundary: BoundaryOnCurve, + boundary: CurveBoundary>, approx: GlobalEdgeApprox, ) -> GlobalEdgeApprox { self.edge_approx @@ -302,7 +302,7 @@ mod tests { use crate::{ algorithms::approx::{Approx, ApproxPoint}, - geometry::{BoundaryOnCurve, GlobalPath, SurfaceGeometry}, + geometry::{CurveBoundary, GlobalPath, SurfaceGeometry}, objects::{HalfEdge, Surface}, operations::BuildHalfEdge, services::Services, @@ -344,7 +344,7 @@ mod tests { let mut services = Services::new(); let path = GlobalPath::circle_from_radius(1.); - let boundary = BoundaryOnCurve::from([[0.], [TAU]]); + let boundary = CurveBoundary::from([[0.], [TAU]]); let surface = Surface::new(SurfaceGeometry { u: path, @@ -384,7 +384,7 @@ mod tests { let approx = (&half_edge, surface.deref()).approx(tolerance); let expected_approx = - (&half_edge.path(), BoundaryOnCurve::from([[0.], [TAU]])) + (&half_edge.path(), CurveBoundary::from([[0.], [TAU]])) .approx(tolerance) .into_iter() .map(|(_, point_surface)| { diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index b06415248..ad55f9eeb 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -32,11 +32,11 @@ use std::iter; use fj_math::{Circle, Point, Scalar, Sign}; -use crate::geometry::{BoundaryOnCurve, GlobalPath, SurfacePath}; +use crate::geometry::{CurveBoundary, GlobalPath, SurfacePath}; use super::{Approx, Tolerance}; -impl Approx for (&SurfacePath, BoundaryOnCurve) { +impl Approx for (&SurfacePath, CurveBoundary>) { type Approximation = Vec<(Point<1>, Point<2>)>; type Cache = (); @@ -56,7 +56,7 @@ impl Approx for (&SurfacePath, BoundaryOnCurve) { } } -impl Approx for (GlobalPath, BoundaryOnCurve) { +impl Approx for (GlobalPath, CurveBoundary>) { type Approximation = Vec<(Point<1>, Point<3>)>; type Cache = (); @@ -82,7 +82,7 @@ impl Approx for (GlobalPath, BoundaryOnCurve) { /// from the circle. fn approx_circle( circle: &Circle, - boundary: impl Into, + boundary: impl Into>>, tolerance: Tolerance, ) -> Vec<(Point<1>, Point)> { let boundary = boundary.into(); @@ -127,7 +127,7 @@ impl PathApproxParams { pub fn points( &self, - boundary: impl Into, + boundary: impl Into>>, ) -> impl Iterator> + '_ { let boundary = boundary.into(); @@ -170,7 +170,7 @@ mod tests { use fj_math::{Circle, Point, Scalar}; - use crate::algorithms::approx::{path::BoundaryOnCurve, Tolerance}; + use crate::algorithms::approx::{path::CurveBoundary, Tolerance}; use super::PathApproxParams; @@ -220,7 +220,7 @@ mod tests { test_path([[TAU - 2.], [0.]], [2., 1.]); fn test_path( - boundary: impl Into, + boundary: impl Into>>, expected_coords: impl IntoIterator>, ) { // Choose radius and tolerance such, that we need 4 vertices to diff --git a/crates/fj-core/src/geometry/boundary.rs b/crates/fj-core/src/geometry/boundary.rs index c38a02563..bb5ed9935 100644 --- a/crates/fj-core/src/geometry/boundary.rs +++ b/crates/fj-core/src/geometry/boundary.rs @@ -1,26 +1,95 @@ +use std::{ + cmp::Ordering, + hash::{Hash, Hasher}, +}; + use fj_math::Point; +use crate::{objects::Vertex, storage::HandleWrapper}; + /// A boundary on a curve -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct BoundaryOnCurve { +/// +/// This struct is generic, because different situations require different +/// representations of a boundary. In some cases, curve coordinates are enough, +/// in other cases, vertices are required, and sometimes you need both. +#[derive(Clone, Copy, Debug)] +pub struct CurveBoundary { /// The raw representation of the boundary - pub inner: [Point<1>; 2], + pub inner: [T::Repr; 2], } -impl BoundaryOnCurve { +impl CurveBoundary { /// Reverse the direction of the boundary + /// + /// Returns a new instance of this struct, which has its direction reversed. + #[must_use] pub fn reverse(self) -> Self { let [a, b] = self.inner; Self { inner: [b, a] } } + + /// Normalize the boundary + /// + /// Returns a new instance of this struct, which has the bounding elements + /// in a defined order. This can be used to compare a boundary while + /// disregarding its direction. + #[must_use] + pub fn normalize(mut self) -> Self { + self.inner.sort(); + self + } +} + +impl Eq for CurveBoundary {} + +impl PartialEq for CurveBoundary { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } } -impl From<[T; 2]> for BoundaryOnCurve +impl From<[S; 2]> for CurveBoundary where - T: Into>, + S: Into, { - fn from(boundary: [T; 2]) -> Self { + fn from(boundary: [S; 2]) -> Self { let inner = boundary.map(Into::into); Self { inner } } } + +impl Hash for CurveBoundary { + fn hash(&self, state: &mut H) { + self.inner.hash(state); + } +} + +impl Ord for CurveBoundary { + fn cmp(&self, other: &Self) -> Ordering { + self.inner.cmp(&other.inner) + } +} + +impl PartialOrd for CurveBoundary { + fn partial_cmp(&self, other: &Self) -> Option { + self.inner.partial_cmp(&other.inner) + } +} + +/// An element of a curve boundary +/// +/// Used for the type parameter of [`CurveBoundary`]. +pub trait CurveBoundaryElement { + /// The representation the curve boundary element + /// + /// This is the actual data stored in [`CurveBoundary`]. + type Repr: Eq + Hash + Ord; +} + +impl CurveBoundaryElement for Point<1> { + type Repr = Self; +} + +impl CurveBoundaryElement for Vertex { + type Repr = HandleWrapper; +} diff --git a/crates/fj-core/src/geometry/bounding_vertices.rs b/crates/fj-core/src/geometry/bounding_vertices.rs deleted file mode 100644 index fd6afb6d4..000000000 --- a/crates/fj-core/src/geometry/bounding_vertices.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{ - objects::Vertex, - storage::{Handle, HandleWrapper}, -}; - -/// The bounding vertices of an edge -#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] -pub struct BoundingVertices { - /// The bounding vertices - pub inner: [HandleWrapper; 2], -} - -impl BoundingVertices { - /// Normalize the bounding vertices - /// - /// Returns a new instance of this struct, which has the vertices in a - /// defined order. This can be used to compare bounding vertices while - /// disregarding their order. - pub fn normalize(mut self) -> Self { - self.inner.sort(); - self - } -} - -impl From<[Handle; 2]> for BoundingVertices { - fn from(vertices: [Handle; 2]) -> Self { - Self { - inner: vertices.map(Into::into), - } - } -} diff --git a/crates/fj-core/src/geometry/mod.rs b/crates/fj-core/src/geometry/mod.rs index 732dbbb7f..0ed948703 100644 --- a/crates/fj-core/src/geometry/mod.rs +++ b/crates/fj-core/src/geometry/mod.rs @@ -1,13 +1,11 @@ //! Types that are tied to objects, but aren't objects themselves mod boundary; -mod bounding_vertices; mod path; mod surface; pub use self::{ - boundary::BoundaryOnCurve, - bounding_vertices::BoundingVertices, + boundary::{CurveBoundary, CurveBoundaryElement}, path::{GlobalPath, SurfacePath}, surface::SurfaceGeometry, }; diff --git a/crates/fj-core/src/objects/kinds/edge.rs b/crates/fj-core/src/objects/kinds/edge.rs index 153f82ac2..419e2b2b6 100644 --- a/crates/fj-core/src/objects/kinds/edge.rs +++ b/crates/fj-core/src/objects/kinds/edge.rs @@ -1,7 +1,7 @@ use fj_math::Point; use crate::{ - geometry::{BoundaryOnCurve, SurfacePath}, + geometry::{CurveBoundary, SurfacePath}, objects::{Curve, Vertex}, storage::{Handle, HandleWrapper}, }; @@ -41,7 +41,7 @@ use crate::{ #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct HalfEdge { path: SurfacePath, - boundary: BoundaryOnCurve, + boundary: CurveBoundary>, curve: HandleWrapper, start_vertex: HandleWrapper, global_form: HandleWrapper, @@ -51,7 +51,7 @@ impl HalfEdge { /// Create an instance of `HalfEdge` pub fn new( path: SurfacePath, - boundary: impl Into, + boundary: impl Into>>, curve: Handle, start_vertex: Handle, global_form: Handle, @@ -71,7 +71,7 @@ impl HalfEdge { } /// Access the boundary points of the half-edge on the curve - pub fn boundary(&self) -> BoundaryOnCurve { + pub fn boundary(&self) -> CurveBoundary> { self.boundary } diff --git a/crates/fj-core/src/operations/build/edge.rs b/crates/fj-core/src/operations/build/edge.rs index 3552ada11..0ffe35cff 100644 --- a/crates/fj-core/src/operations/build/edge.rs +++ b/crates/fj-core/src/operations/build/edge.rs @@ -2,7 +2,7 @@ use fj_interop::ext::ArrayExt; use fj_math::{Arc, Point, Scalar}; use crate::{ - geometry::{BoundaryOnCurve, SurfacePath}, + geometry::{CurveBoundary, SurfacePath}, objects::{Curve, GlobalEdge, HalfEdge, Vertex}, operations::Insert, services::Services, @@ -13,7 +13,7 @@ pub trait BuildHalfEdge { /// Create a half-edge that is not joined to another fn unjoined( path: SurfacePath, - boundary: impl Into, + boundary: impl Into>>, services: &mut Services, ) -> HalfEdge { let curve = Curve::new().insert(services); diff --git a/crates/fj-core/src/operations/join/cycle.rs b/crates/fj-core/src/operations/join/cycle.rs index 316880f52..44e5bbda6 100644 --- a/crates/fj-core/src/operations/join/cycle.rs +++ b/crates/fj-core/src/operations/join/cycle.rs @@ -1,9 +1,10 @@ use std::ops::RangeInclusive; +use fj_math::Point; use itertools::Itertools; use crate::{ - geometry::{BoundaryOnCurve, SurfacePath}, + geometry::{CurveBoundary, SurfacePath}, objects::{Cycle, HalfEdge}, operations::{BuildHalfEdge, Insert, UpdateCycle, UpdateHalfEdge}, services::Services, @@ -17,7 +18,7 @@ pub trait JoinCycle { fn add_joined_edges(&self, edges: Es, services: &mut Services) -> Self where Es: IntoIterator< - Item = (Handle, SurfacePath, BoundaryOnCurve), + Item = (Handle, SurfacePath, CurveBoundary>), >, Es::IntoIter: Clone + ExactSizeIterator; @@ -64,7 +65,7 @@ impl JoinCycle for Cycle { fn add_joined_edges(&self, edges: Es, services: &mut Services) -> Self where Es: IntoIterator< - Item = (Handle, SurfacePath, BoundaryOnCurve), + Item = (Handle, SurfacePath, CurveBoundary>), >, Es::IntoIter: Clone + ExactSizeIterator, { diff --git a/crates/fj-core/src/queries.rs b/crates/fj-core/src/queries.rs index a0116aa36..e4d880b1c 100644 --- a/crates/fj-core/src/queries.rs +++ b/crates/fj-core/src/queries.rs @@ -10,8 +10,8 @@ //! them for various objects that have the information to answer the query. use crate::{ - geometry::BoundingVertices, - objects::{Cycle, Face, HalfEdge, Region, Shell}, + geometry::CurveBoundary, + objects::{Cycle, Face, HalfEdge, Region, Shell, Vertex}, storage::Handle, }; @@ -24,18 +24,18 @@ pub trait BoundingVerticesOfEdge { fn bounding_vertices_of_edge( &self, edge: &Handle, - ) -> Option; + ) -> Option>; } impl BoundingVerticesOfEdge for Cycle { fn bounding_vertices_of_edge( &self, edge: &Handle, - ) -> Option { + ) -> Option> { let start = edge.start_vertex().clone(); let end = self.half_edge_after(edge)?.start_vertex().clone(); - Some(BoundingVertices::from([start, end])) + Some(CurveBoundary::from([start, end])) } } @@ -43,7 +43,7 @@ impl BoundingVerticesOfEdge for Region { fn bounding_vertices_of_edge( &self, edge: &Handle, - ) -> Option { + ) -> Option> { for cycle in self.all_cycles() { if let Some(vertices) = cycle.bounding_vertices_of_edge(edge) { return Some(vertices); @@ -58,7 +58,7 @@ impl BoundingVerticesOfEdge for Face { fn bounding_vertices_of_edge( &self, edge: &Handle, - ) -> Option { + ) -> Option> { self.region().bounding_vertices_of_edge(edge) } } @@ -67,7 +67,7 @@ impl BoundingVerticesOfEdge for Shell { fn bounding_vertices_of_edge( &self, edge: &Handle, - ) -> Option { + ) -> Option> { for face in self.faces() { if let Some(vertices) = face.bounding_vertices_of_edge(edge) { return Some(vertices);