diff --git a/crates/fj-kernel/src/algorithms/approx/curves.rs b/crates/fj-kernel/src/algorithms/approx/curves.rs index fc98d2346..5e8c5bbf5 100644 --- a/crates/fj-kernel/src/algorithms/approx/curves.rs +++ b/crates/fj-kernel/src/algorithms/approx/curves.rs @@ -21,9 +21,9 @@ use super::Tolerance; /// The `approximate_between` methods of the curves then need to make sure to /// only return points in between those vertices, not the vertices themselves. pub fn approx_curve( - curve: &Curve, + curve: &Curve<3>, tolerance: Tolerance, - out: &mut Vec>, + out: &mut Vec>, ) { match curve { Curve::Circle(curve) => approx_circle(curve, tolerance, out), @@ -38,7 +38,7 @@ pub fn approx_curve( pub fn approx_circle( circle: &Circle<3>, tolerance: Tolerance, - out: &mut Vec>, + out: &mut Vec>, ) { let radius = circle.a.magnitude(); diff --git a/crates/fj-kernel/src/algorithms/approx/cycles.rs b/crates/fj-kernel/src/algorithms/approx/cycles.rs index c7ba505a9..77fec5d44 100644 --- a/crates/fj-kernel/src/algorithms/approx/cycles.rs +++ b/crates/fj-kernel/src/algorithms/approx/cycles.rs @@ -8,7 +8,7 @@ use super::{curves::approx_curve, edges::approximate_edge, Tolerance}; #[derive(Debug, Eq, PartialEq, Hash)] pub struct CycleApprox { /// The points that approximate the cycle - pub points: Vec>, + pub points: Vec>, } impl CycleApprox { diff --git a/crates/fj-kernel/src/algorithms/approx/edges.rs b/crates/fj-kernel/src/algorithms/approx/edges.rs index 5cdc22cfd..94f5755ca 100644 --- a/crates/fj-kernel/src/algorithms/approx/edges.rs +++ b/crates/fj-kernel/src/algorithms/approx/edges.rs @@ -2,8 +2,8 @@ use crate::{geometry, topology::EdgeVertex}; pub fn approximate_edge( vertices: Option<[EdgeVertex; 2]>, - mut points: Vec>, -) -> Vec> { + mut points: Vec>, +) -> Vec> { // Insert the exact vertices of this edge into the approximation. This means // we don't rely on the curve approximation to deliver accurate // representations of these vertices, which they might not be able to do. diff --git a/crates/fj-kernel/src/algorithms/approx/faces.rs b/crates/fj-kernel/src/algorithms/approx/faces.rs index 6ff817753..71790940f 100644 --- a/crates/fj-kernel/src/algorithms/approx/faces.rs +++ b/crates/fj-kernel/src/algorithms/approx/faces.rs @@ -11,7 +11,7 @@ pub struct FaceApprox { /// /// These could be actual vertices from the model, points that approximate /// an edge, or points that approximate a face. - pub points: HashSet>, + pub points: HashSet>, /// Approximation of the exterior cycle pub exterior: CycleApprox, diff --git a/crates/fj-kernel/src/algorithms/intersection/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersection/surface_surface.rs index 28e3524b5..916e0b0e3 100644 --- a/crates/fj-kernel/src/algorithms/intersection/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersection/surface_surface.rs @@ -3,7 +3,7 @@ use fj_math::{Line, Point, Scalar, Vector}; use crate::geometry::{Curve, Surface}; /// Test intersection between two surfaces -pub fn surface_surface(a: &Surface, b: &Surface) -> Option { +pub fn surface_surface(a: &Surface, b: &Surface) -> Option> { // Algorithm from Real-Time Collision Detection by Christer Ericson. See // section 5.4.4, Intersection of Two Planes. diff --git a/crates/fj-kernel/src/algorithms/triangulation/delaunay.rs b/crates/fj-kernel/src/algorithms/triangulation/delaunay.rs index 8cabf2f8f..8d7ff0977 100644 --- a/crates/fj-kernel/src/algorithms/triangulation/delaunay.rs +++ b/crates/fj-kernel/src/algorithms/triangulation/delaunay.rs @@ -5,8 +5,8 @@ use crate::geometry; /// Create a Delaunay triangulation of all points pub fn triangulate( - points: Vec>, -) -> Vec<[geometry::Point<2>; 3]> { + points: Vec>, +) -> Vec<[geometry::Point<2, 3>; 3]> { use spade::Triangulation as _; let triangulation = spade::DelaunayTriangulation::<_>::bulk_load(points) @@ -31,7 +31,7 @@ pub fn triangulate( } // Enables the use of `geometry::Point` in the triangulation. -impl HasPosition for geometry::Point<2> { +impl HasPosition for geometry::Point<2, 3> { type Scalar = Scalar; fn position(&self) -> spade::Point2 { diff --git a/crates/fj-kernel/src/geometry/curves.rs b/crates/fj-kernel/src/geometry/curves.rs index a01c2c732..6759a5c38 100644 --- a/crates/fj-kernel/src/geometry/curves.rs +++ b/crates/fj-kernel/src/geometry/curves.rs @@ -11,16 +11,20 @@ use crate::geometry; /// The nomenclature is inspired by Boundary Representation Modelling Techniques /// by Ian Stroud. "Curve" refers to unbounded one-dimensional geometry, while /// while edges are bounded portions of curves. +/// +/// The `D` parameter defines the dimensions in which the curve is defined. +/// Typically, only `2` or `3` make sense, which means the curve is defined on +/// a surface or in a space, respectively. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub enum Curve { +pub enum Curve { /// A circle - Circle(Circle<3>), + Circle(Circle), /// A line - Line(Line<3>), + Line(Line), } -impl Curve { +impl Curve<3> { /// Construct a `Curve` that represents the x-axis pub fn x_axis() -> Self { Self::Line(Line { @@ -45,8 +49,21 @@ impl Curve { }) } + /// Create a new instance that is transformed by `transform` + #[must_use] + pub fn transform(self, transform: &Transform) -> Self { + match self { + Self::Circle(curve) => { + Self::Circle(transform.transform_circle(&curve)) + } + Self::Line(curve) => Self::Line(transform.transform_line(&curve)), + } + } +} + +impl Curve { /// Access the origin of the curve's coordinate system - pub fn origin(&self) -> Point<3> { + pub fn origin(&self) -> Point { match self { Self::Circle(curve) => curve.center, Self::Line(curve) => curve.origin, @@ -62,17 +79,6 @@ impl Curve { } } - /// Create a new instance that is transformed by `transform` - #[must_use] - pub fn transform(self, transform: &Transform) -> Self { - match self { - Self::Circle(curve) => { - Self::Circle(transform.transform_circle(&curve)) - } - Self::Line(curve) => Self::Line(transform.transform_line(&curve)), - } - } - /// Convert a point in model coordinates to curve coordinates /// /// Projects the point onto the curve before computing curve coordinate. @@ -84,23 +90,25 @@ impl Curve { /// an error. pub fn point_to_curve_coords( &self, - point: impl Into>, - ) -> geometry::Point<1> { - let point_3d = point.into(); + point: impl Into>, + ) -> geometry::Point<1, D> { + let point_canonical = point.into(); - let point_1d = match self { - Self::Circle(curve) => curve.point_to_circle_coords(point_3d), - Self::Line(curve) => curve.point_to_line_coords(point_3d), + let point_native = match self { + Self::Circle(curve) => { + curve.point_to_circle_coords(point_canonical) + } + Self::Line(curve) => curve.point_to_line_coords(point_canonical), }; - geometry::Point::new(point_1d, point_3d) + geometry::Point::new(point_native, point_canonical) } /// Convert a point on the curve into model coordinates pub fn point_from_curve_coords( &self, point: impl Into>, - ) -> Point<3> { + ) -> Point { match self { Self::Circle(curve) => curve.point_from_circle_coords(point), Self::Line(curve) => curve.point_from_line_coords(point), @@ -111,7 +119,7 @@ impl Curve { pub fn vector_from_curve_coords( &self, point: impl Into>, - ) -> Vector<3> { + ) -> Vector { match self { Self::Circle(curve) => curve.vector_from_circle_coords(point), Self::Line(curve) => curve.vector_from_line_coords(point), diff --git a/crates/fj-kernel/src/geometry/points.rs b/crates/fj-kernel/src/geometry/points.rs index e98624136..a139604bd 100644 --- a/crates/fj-kernel/src/geometry/points.rs +++ b/crates/fj-kernel/src/geometry/points.rs @@ -1,33 +1,36 @@ -/// A point that can be losslessly converted into its canonical form +/// A point that stores a native and a canonical form /// -/// The canonical form is always the 3D representation. It needs to be provided -/// when constructing the point, along with the point's native form. +/// The native form of a point is whatever representation is most appropriate in +/// the current context. The canonical form is the representation that the +/// native form was created from. +/// +/// Typically, the canonical form is more general and has higher dimensionality +/// (for example, a point in a 3D space), while the native form is more specific +/// and has lower dimensionality (for example, the point in 2D surface +/// coordinates, on surface within that 3D space). +/// +/// The purpose of storing both forms is to be able to losslessly convert the +/// point back to its canonical form. Even if this conversion can be computed on +/// the fly, such a conversion might not result in the original canonical form, +/// due to floating point accuracy issues. Hence, such a conversion would not be +/// lossless, which could result in bugs. +/// +/// The `N` parameter defines the dimensionality of the native form, while the +/// `C` parameter defines the dimensionality of the canonical form. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct Point { - /// This point's native form - /// - /// The native form of the point is its representation in its native - /// coordinate system. This could be a 1-dimensional curve, 2-dimensional - /// surface, or 3-dimensional model coordinate system. - native: fj_math::Point, - - /// The canonical form of the point - /// - /// This is always the 3D representation of the point. Since this is always - /// kept here, unchanged, as the point is converted into other coordinate - /// systems, it allows for a lossless conversion back into 3D coordinates, - /// unaffected by floating point accuracy issues. - canonical: fj_math::Point<3>, +pub struct Point { + native: fj_math::Point, + canonical: fj_math::Point, } -impl Point { +impl Point { /// Construct a new instance /// - /// Both the native and the canonical form must be provide. The caller must - /// guarantee that both of them match. + /// Both the native and the canonical form must be provided. The caller must + /// guarantee that both of them match, i.e. define the same point. pub fn new( - native: impl Into>, - canonical: impl Into>, + native: impl Into>, + canonical: impl Into>, ) -> Self { Self { native: native.into(), @@ -36,12 +39,12 @@ impl Point { } /// Access the point's native form - pub fn native(&self) -> fj_math::Point { + pub fn native(&self) -> fj_math::Point { self.native } /// Access the point's canonical form - pub fn canonical(&self) -> fj_math::Point<3> { + pub fn canonical(&self) -> fj_math::Point { self.canonical } } diff --git a/crates/fj-kernel/src/geometry/surfaces/mod.rs b/crates/fj-kernel/src/geometry/surfaces/mod.rs index c97a9c398..b71a5dc77 100644 --- a/crates/fj-kernel/src/geometry/surfaces/mod.rs +++ b/crates/fj-kernel/src/geometry/surfaces/mod.rs @@ -62,7 +62,7 @@ impl Surface { pub fn point_to_surface_coords( &self, point_3d: impl Into>, - ) -> geometry::Point<2> { + ) -> geometry::Point<2, 3> { let point_3d = point_3d.into(); let point_2d = match self { diff --git a/crates/fj-kernel/src/geometry/surfaces/swept.rs b/crates/fj-kernel/src/geometry/surfaces/swept.rs index f68206734..ac4af14f9 100644 --- a/crates/fj-kernel/src/geometry/surfaces/swept.rs +++ b/crates/fj-kernel/src/geometry/surfaces/swept.rs @@ -6,7 +6,7 @@ use crate::geometry::Curve; #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct SweptCurve { /// The curve that this surface was swept from - pub curve: Curve, + pub curve: Curve<3>, /// The path that the curve was swept along pub path: Vector<3>, diff --git a/crates/fj-kernel/src/shape/api.rs b/crates/fj-kernel/src/shape/api.rs index 77d5a1f62..b94392556 100644 --- a/crates/fj-kernel/src/shape/api.rs +++ b/crates/fj-kernel/src/shape/api.rs @@ -147,7 +147,7 @@ impl Shape { /// Access an iterator over all curves /// /// The caller must not make any assumptions about the order of curves. - pub fn curves(&self) -> Iter { + pub fn curves(&self) -> Iter> { self.stores.curves.iter() } @@ -385,7 +385,7 @@ mod tests { } } - fn add_curve(&mut self) -> Handle { + fn add_curve(&mut self) -> Handle> { self.insert(Curve::x_axis()).unwrap() } diff --git a/crates/fj-kernel/src/shape/object.rs b/crates/fj-kernel/src/shape/object.rs index 75b5fa724..c0eb96458 100644 --- a/crates/fj-kernel/src/shape/object.rs +++ b/crates/fj-kernel/src/shape/object.rs @@ -14,7 +14,7 @@ pub trait Object: } impl private::Sealed for Point<3> {} -impl private::Sealed for Curve {} +impl private::Sealed for Curve<3> {} impl private::Sealed for Surface {} impl private::Sealed for Vertex {} @@ -23,7 +23,7 @@ impl private::Sealed for Cycle {} impl private::Sealed for Face {} impl Object for Point<3> {} -impl Object for Curve {} +impl Object for Curve<3> {} impl Object for Surface {} impl Object for Vertex {} diff --git a/crates/fj-kernel/src/shape/stores.rs b/crates/fj-kernel/src/shape/stores.rs index 905289bea..130dddcf9 100644 --- a/crates/fj-kernel/src/shape/stores.rs +++ b/crates/fj-kernel/src/shape/stores.rs @@ -52,7 +52,7 @@ impl Stores { } pub type Points = Store>; -pub type Curves = Store; +pub type Curves = Store>; pub type Surfaces = Store; pub type Vertices = Store; diff --git a/crates/fj-kernel/src/shape/validate.rs b/crates/fj-kernel/src/shape/validate.rs index 2fba423f5..da9050739 100644 --- a/crates/fj-kernel/src/shape/validate.rs +++ b/crates/fj-kernel/src/shape/validate.rs @@ -23,7 +23,7 @@ impl Validate for Point<3> { } } -impl Validate for Curve { +impl Validate for Curve<3> { fn validate(&self, _: Scalar, _: &Stores) -> Result<(), ValidationError> { Ok(()) } @@ -202,7 +202,7 @@ pub enum ValidationError { impl ValidationError { /// Indicate whether validation found a missing curve #[cfg(test)] - pub fn missing_curve(&self, curve: &Handle) -> bool { + pub fn missing_curve(&self, curve: &Handle>) -> bool { if let Self::Structural(StructuralIssues { missing_curve, .. }) = self { return missing_curve.as_ref() == Some(curve); } @@ -270,7 +270,7 @@ impl From for ValidationError { #[derive(Debug, Default)] pub struct StructuralIssues { /// Missing curve found in edge validation - pub missing_curve: Option>, + pub missing_curve: Option>>, /// Missing vertices found in edge validation pub missing_vertices: HashSet>, diff --git a/crates/fj-kernel/src/topology/edges.rs b/crates/fj-kernel/src/topology/edges.rs index 5c471da1b..03815755e 100644 --- a/crates/fj-kernel/src/topology/edges.rs +++ b/crates/fj-kernel/src/topology/edges.rs @@ -26,7 +26,7 @@ pub struct Edge { /// The edge can be a segment of the curve that is bounded by two vertices, /// or if the curve is continuous (i.e. connects to itself), the edge could /// be defined by the whole curve, and have no bounding vertices. - pub curve: Handle, + pub curve: Handle>, /// Access the vertices that bound the edge on the curve /// @@ -38,7 +38,7 @@ pub struct Edge { impl Edge { /// Construct an instance of `Edge` pub fn new( - curve: Handle, + curve: Handle>, vertices: Option<[Handle; 2]>, ) -> Self { let vertices = vertices.map(|vertices| { @@ -61,7 +61,7 @@ impl Edge { /// /// This is a convenience method that saves the caller from dealing with the /// [`Handle`]. - pub fn curve(&self) -> Curve { + pub fn curve(&self) -> Curve<3> { self.curve.get() } @@ -98,5 +98,5 @@ pub struct EdgeVertex { /// The local representation of the vertex /// /// Represents the vertex in terms of the coordinates of the edge's curve. - pub local: geometry::Point<1>, + pub local: geometry::Point<1, 3>, }