diff --git a/crates/fj-kernel/src/algorithms/approx/curves.rs b/crates/fj-kernel/src/algorithms/approx/curves.rs index b2e95d2bb..11b0c6e29 100644 --- a/crates/fj-kernel/src/algorithms/approx/curves.rs +++ b/crates/fj-kernel/src/algorithms/approx/curves.rs @@ -1,8 +1,8 @@ use std::cmp::max; -use fj_math::{Circle, Scalar}; +use fj_math::{Circle, Point, Scalar}; -use crate::{geometry::LocalPoint, objects::Curve}; +use crate::{local::Local, objects::Curve}; use super::Tolerance; @@ -23,7 +23,7 @@ use super::Tolerance; pub fn approx_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(); @@ -53,7 +53,7 @@ pub fn approx_circle( for i in 0..n { let angle = Scalar::PI * 2. / n as f64 * i as f64; let point = circle.point_from_circle_coords([angle]); - out.push(LocalPoint::new([angle], point)); + out.push(Local::new([angle], point)); } } diff --git a/crates/fj-kernel/src/algorithms/approx/cycles.rs b/crates/fj-kernel/src/algorithms/approx/cycles.rs index c28150c3f..1b1b5b139 100644 --- a/crates/fj-kernel/src/algorithms/approx/cycles.rs +++ b/crates/fj-kernel/src/algorithms/approx/cycles.rs @@ -1,6 +1,6 @@ -use fj_math::Segment; +use fj_math::{Point, Segment}; -use crate::{geometry::LocalPoint, objects::Cycle}; +use crate::objects::Cycle; use super::{curves::approx_curve, edges::approx_edge, Tolerance}; @@ -8,7 +8,7 @@ use super::{curves::approx_curve, edges::approx_edge, Tolerance}; #[derive(Debug, Eq, PartialEq, Hash)] pub struct CycleApprox { /// The points that approximate the cycle - pub points: Vec>, + pub points: Vec>, } impl CycleApprox { @@ -27,10 +27,8 @@ impl CycleApprox { points.extend(edge_points); } - let mut points: Vec<_> = points - .into_iter() - .map(|point| LocalPoint::new(point.global(), point.global())) - .collect(); + let mut points: Vec<_> = + points.into_iter().map(|point| point.global()).collect(); points.dedup(); @@ -46,7 +44,6 @@ impl CycleApprox { // up, once `array_windows` is stable. let segment = [segment[0], segment[1]]; - let segment = segment.map(|point| point.global()); segments.push(Segment::from(segment)); } diff --git a/crates/fj-kernel/src/algorithms/approx/edges.rs b/crates/fj-kernel/src/algorithms/approx/edges.rs index 29e0b9de6..e0a290f49 100644 --- a/crates/fj-kernel/src/algorithms/approx/edges.rs +++ b/crates/fj-kernel/src/algorithms/approx/edges.rs @@ -1,6 +1,11 @@ -use crate::{geometry::LocalPoint, objects::VerticesOfEdge}; +use fj_math::Point; -pub fn approx_edge(vertices: VerticesOfEdge, points: &mut Vec>) { +use crate::{local::Local, objects::VerticesOfEdge}; + +pub fn approx_edge( + vertices: VerticesOfEdge, + points: &mut 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. @@ -10,7 +15,7 @@ pub fn approx_edge(vertices: VerticesOfEdge, points: &mut Vec>) { // the same vertex would be understood to refer to very close, but distinct // vertices. let vertices = vertices.convert(|vertex| { - LocalPoint::new(vertex.position(), vertex.global().position()) + Local::new(vertex.position(), vertex.global().position()) }); if let Some([a, b]) = vertices { points.insert(0, a); @@ -32,7 +37,7 @@ mod test { use fj_math::Point; use crate::{ - geometry::LocalPoint, + local::Local, objects::{GlobalVertex, Vertex, VerticesOfEdge}, }; @@ -51,10 +56,10 @@ mod test { Vertex::new(Point::from([1.]), v2), ]); - let a = LocalPoint::new([0.0], a); - let b = LocalPoint::new([0.25], b); - let c = LocalPoint::new([0.75], c); - let d = LocalPoint::new([1.0], d); + let a = Local::new([0.0], a); + let b = Local::new([0.25], b); + let c = Local::new([0.75], c); + let d = Local::new([1.0], d); // Regular edge let mut points = vec![b, c]; diff --git a/crates/fj-kernel/src/algorithms/approx/faces.rs b/crates/fj-kernel/src/algorithms/approx/faces.rs index 12b8be775..7f6664c24 100644 --- a/crates/fj-kernel/src/algorithms/approx/faces.rs +++ b/crates/fj-kernel/src/algorithms/approx/faces.rs @@ -1,6 +1,8 @@ use std::collections::HashSet; -use crate::{geometry::LocalPoint, objects::Face}; +use fj_math::Point; + +use crate::objects::Face; use super::{CycleApprox, Tolerance}; @@ -11,7 +13,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, @@ -81,10 +83,7 @@ mod tests { use fj_math::{Point, Scalar}; use map_macro::set; - use crate::{ - geometry::LocalPoint, - objects::{Face, Surface}, - }; + use crate::objects::{Face, Surface}; use super::{CycleApprox, FaceApprox, Tolerance}; @@ -118,15 +117,6 @@ mod tests { let g = g.to_xyz(); let h = h.to_xyz(); - let a = LocalPoint::new(a, a); - let b = LocalPoint::new(b, b); - let c = LocalPoint::new(c, c); - let d = LocalPoint::new(d, d); - let e = LocalPoint::new(e, e); - let f = LocalPoint::new(f, f); - let g = LocalPoint::new(g, g); - let h = LocalPoint::new(h, h); - let approx = FaceApprox::new(&face, tolerance); let expected = FaceApprox { points: set![a, b, c, d, e, f, g, h], diff --git a/crates/fj-kernel/src/algorithms/reverse.rs b/crates/fj-kernel/src/algorithms/reverse.rs index b48f74ed5..e06d9eeff 100644 --- a/crates/fj-kernel/src/algorithms/reverse.rs +++ b/crates/fj-kernel/src/algorithms/reverse.rs @@ -1,8 +1,8 @@ use fj_math::{Circle, Line, Point, Vector}; use crate::{ + local::Local, objects::{Curve, Cycle, CyclesInFace, Edge, Face}, - shape::LocalForm, }; /// Reverse the direction of a face @@ -31,7 +31,7 @@ fn reverse_local_coordinates_in_cycle( .iter() .map(|edge| { let curve = { - let local = match *edge.curve.local() { + let local = match edge.curve.local() { Curve::Circle(Circle { center, a, b }) => { let center = Point::from([center.u, -center.v]); @@ -49,8 +49,7 @@ fn reverse_local_coordinates_in_cycle( } }; - let canonical = *edge.curve.canonical(); - LocalForm::new(local, canonical) + Local::new(local, edge.curve.global()) }; let vertices = edge.vertices.clone(); diff --git a/crates/fj-kernel/src/algorithms/sweep.rs b/crates/fj-kernel/src/algorithms/sweep.rs index 83f017257..63373fa2a 100644 --- a/crates/fj-kernel/src/algorithms/sweep.rs +++ b/crates/fj-kernel/src/algorithms/sweep.rs @@ -2,10 +2,10 @@ use fj_math::{Point, Scalar, Transform, Triangle, Vector}; use crate::{ iter::ObjectIters, + local::Local, objects::{ Curve, Cycle, Edge, Face, GlobalVertex, Surface, Vertex, VerticesOfEdge, }, - shape::LocalForm, }; use super::{reverse_face, transform::transform_face, CycleApprox, Tolerance}; @@ -144,7 +144,7 @@ fn create_non_continuous_side_face( let global = [a, b].map(|vertex| vertex.1.position()); let global = Curve::line_from_points(global); - LocalForm::new(local, global) + Local::new(local, global) }; let vertices = VerticesOfEdge::from_vertices([ @@ -153,7 +153,7 @@ fn create_non_continuous_side_face( ]); let edge = Edge { - curve: curve.clone(), + curve, vertices: vertices.clone(), }; diff --git a/crates/fj-kernel/src/algorithms/transform.rs b/crates/fj-kernel/src/algorithms/transform.rs index 55974800c..41a9dd28d 100644 --- a/crates/fj-kernel/src/algorithms/transform.rs +++ b/crates/fj-kernel/src/algorithms/transform.rs @@ -1,10 +1,10 @@ use fj_math::Transform; use crate::{ + local::Local, objects::{ Cycle, CyclesInFace, Edge, Face, FaceBRep, GlobalVertex, Vertex, }, - shape::LocalForm, }; /// Transform a shape @@ -53,7 +53,7 @@ pub fn transform_cycles( .edges .iter() .map(|edge| { - let curve_local = *edge.curve.local(); + let curve_local = edge.curve.local(); let curve_canonical = edge.curve().transform(transform); let vertices = edge @@ -62,7 +62,7 @@ pub fn transform_cycles( .map(|vertex| transform_vertex(&vertex, transform)); Edge { - curve: LocalForm::new(curve_local, curve_canonical), + curve: Local::new(curve_local, curve_canonical), vertices, } }) diff --git a/crates/fj-kernel/src/algorithms/triangulate/delaunay.rs b/crates/fj-kernel/src/algorithms/triangulate/delaunay.rs index 06d3dcc8c..5e39cb1b5 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/delaunay.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/delaunay.rs @@ -1,10 +1,10 @@ -use fj_math::{Scalar, Triangle, Winding}; +use fj_math::{Point, Scalar, Triangle, Winding}; use spade::HasPosition; -use crate::geometry::LocalPoint; +use crate::local::Local; /// Create a Delaunay triangulation of all points -pub fn triangulate(points: Vec>) -> Vec<[LocalPoint<2>; 3]> { +pub fn triangulate(points: Vec>>) -> Vec<[Local>; 3]> { use spade::Triangulation as _; let triangulation = spade::DelaunayTriangulation::<_>::bulk_load(points) @@ -29,7 +29,7 @@ pub fn triangulate(points: Vec>) -> Vec<[LocalPoint<2>; 3]> { } // Enables the use of `LocalPoint` in the triangulation. -impl HasPosition for LocalPoint<2> { +impl HasPosition for Local> { type Scalar = Scalar; fn position(&self) -> spade::Point2 { diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 997add704..6e4fb012a 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -31,7 +31,7 @@ pub fn triangulate( .map(|vertex| { // Can't panic, unless the approximation wrongfully // generates points that are not in the surface. - surface.point_to_surface_coords(vertex.global()) + surface.point_to_surface_coords(vertex) }) .collect(); let face_as_polygon = Polygon::new(surface) @@ -39,9 +39,7 @@ pub fn triangulate( |point| { // Can't panic, unless the approximation wrongfully // generates points that are not in the surface. - surface - .point_to_surface_coords(point.global()) - .local() + surface.point_to_surface_coords(point).local() }, )) .with_interiors(approx.interiors.into_iter().map( @@ -50,9 +48,7 @@ pub fn triangulate( // Can't panic, unless the approximation // wrongfully generates points that are not in // the surface. - surface - .point_to_surface_coords(point.global()) - .local() + surface.point_to_surface_coords(point).local() }) }, )); diff --git a/crates/fj-kernel/src/geometry/mod.rs b/crates/fj-kernel/src/geometry/mod.rs deleted file mode 100644 index 4cfa74e4a..000000000 --- a/crates/fj-kernel/src/geometry/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Miscellaneous geometry code - -mod points; - -pub use self::points::LocalPoint; diff --git a/crates/fj-kernel/src/geometry/points.rs b/crates/fj-kernel/src/geometry/points.rs deleted file mode 100644 index 56abca198..000000000 --- a/crates/fj-kernel/src/geometry/points.rs +++ /dev/null @@ -1,55 +0,0 @@ -/// A point that stores a local and a global form -/// -/// The local form of a point is whatever representation is most appropriate in -/// the current context, which might be a curve or surface. The global form is -/// the global 3D form of the same point. -/// -/// The purpose of storing both forms is to be able to losslessly convert the -/// point back to its global form. Even if this conversion can be computed on -/// the fly, such a conversion might not result in the original global form, due -/// to floating point accuracy issues. Hence, such a conversion would not be -/// lossless, which could result in bugs. -/// -/// The `D` parameter defines the dimensionality of the local form. -/// -/// # `LocalPoint` and [`Vertex`] -/// -/// `LocalPoint` is similar to `Vertex`, but there is a key differences: -/// `Vertex` is an object in the boundary representation of a shape, while -/// `LocalPoint` can refer to any point. This distinction is important in the -/// case of approximation, for example, as points might be generated to -/// approximate a curve or surface, without those generated points referring to -/// any vertices. -/// -/// [`Vertex`]: crate::objects::Vertex -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct LocalPoint { - local: fj_math::Point, - global: fj_math::Point<3>, -} - -impl LocalPoint { - /// Construct a new instance - /// - /// Both the local and the global form must be provided. The caller must - /// guarantee that both of them match, i.e. define the same point. - pub fn new( - local: impl Into>, - global: impl Into>, - ) -> Self { - Self { - local: local.into(), - global: global.into(), - } - } - - /// Access the point's local form - pub fn local(&self) -> fj_math::Point { - self.local - } - - /// Access the point's global form - pub fn global(&self) -> fj_math::Point<3> { - self.global - } -} diff --git a/crates/fj-kernel/src/lib.rs b/crates/fj-kernel/src/lib.rs index 4df333908..fed8991f1 100644 --- a/crates/fj-kernel/src/lib.rs +++ b/crates/fj-kernel/src/lib.rs @@ -89,8 +89,7 @@ pub mod algorithms; pub mod builder; -pub mod geometry; pub mod iter; +pub mod local; pub mod objects; -pub mod shape; pub mod validation; diff --git a/crates/fj-kernel/src/local.rs b/crates/fj-kernel/src/local.rs new file mode 100644 index 000000000..234cbe0c4 --- /dev/null +++ b/crates/fj-kernel/src/local.rs @@ -0,0 +1,63 @@ +//! Infrastructure for types that have a local and a global form + +use fj_math::Point; + +use crate::objects::Curve; + +/// A wrapper around the local and global forms of a type +/// +/// The local form is whatever representation of the value that is most +/// appropriate in a given local context, which might be a curve or surface. The +/// global form is the global 3D form of the same value. +/// +/// The purpose of storing both forms is to be able to losslessly convert +/// between them. Even if this conversion can be computed on the fly, it might +/// be lossy due to floating point accuracy issues. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct Local { + local: T, + global: T::GlobalForm, +} + +impl Local { + /// Construct a new instance + /// + /// It is the caller's responsibility to make sure that the local and global + /// forms passed into this constructor match. + pub fn new(local: impl Into, global: impl Into) -> Self { + Self { + local: local.into(), + global: global.into(), + } + } + + /// Access the local form of the value + pub fn local(&self) -> T { + self.local + } + + /// Access the global form of the value + pub fn global(&self) -> T::GlobalForm { + self.global + } +} + +/// Implemented for types that are the local form of a global type +/// +/// See [`Local`] for more information. +pub trait LocalForm: Copy { + /// The global form of the implementing type + type GlobalForm: Copy; +} + +impl LocalForm for Curve<2> { + type GlobalForm = Curve<3>; +} + +impl LocalForm for Point<1> { + type GlobalForm = Point<3>; +} + +impl LocalForm for Point<2> { + type GlobalForm = Point<3>; +} diff --git a/crates/fj-kernel/src/objects/cycle.rs b/crates/fj-kernel/src/objects/cycle.rs index 88bd7ed66..bf05475c2 100644 --- a/crates/fj-kernel/src/objects/cycle.rs +++ b/crates/fj-kernel/src/objects/cycle.rs @@ -1,8 +1,6 @@ -use fj_math::{Line, Point}; +use fj_math::Point; -use crate::shape::LocalForm; - -use super::{Curve, Edge, Surface}; +use super::{Edge, Surface}; /// A cycle of connected edges /// @@ -36,18 +34,7 @@ impl Cycle { // Can be cleaned up, once `array_windows` is stable. let points = [points[0], points[1]]; - let edge_canonical = - Edge::line_segment_from_points(surface, points); - - let edge_local = Edge { - curve: LocalForm::new( - Curve::Line(Line::from_points(points)), - *edge_canonical.curve.canonical(), - ), - vertices: edge_canonical.vertices.clone(), - }; - - edges.push(edge_local); + edges.push(Edge::line_segment_from_points(surface, points)); } Cycle { edges } diff --git a/crates/fj-kernel/src/objects/edge.rs b/crates/fj-kernel/src/objects/edge.rs index e1ce164cb..c3f95c37a 100644 --- a/crates/fj-kernel/src/objects/edge.rs +++ b/crates/fj-kernel/src/objects/edge.rs @@ -2,7 +2,7 @@ use std::fmt; use fj_math::{Circle, Line, Point, Scalar, Vector}; -use crate::shape::LocalForm; +use crate::local::Local; use super::{Curve, GlobalVertex, Surface, Vertex}; @@ -14,7 +14,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: LocalForm, Curve<3>>, + pub curve: Local>, /// Access the vertices that bound the edge on the curve /// @@ -38,7 +38,7 @@ impl Edge { }); Edge { - curve: LocalForm::new(curve_local, curve_canonical), + curve: Local::new(curve_local, curve_canonical), vertices: VerticesOfEdge::none(), } } @@ -71,7 +71,7 @@ impl Edge { }; Self { - curve: LocalForm::new(curve_local, curve_canonical), + curve: Local::new(curve_local, curve_canonical), vertices: VerticesOfEdge::from_vertices(vertices), } } @@ -81,7 +81,7 @@ impl Edge { /// This is a convenience method that saves the caller from dealing with the /// [`Handle`]. pub fn curve(&self) -> Curve<3> { - *self.curve.canonical() + self.curve.global() } /// Access the vertices that the edge refers to diff --git a/crates/fj-kernel/src/objects/surface.rs b/crates/fj-kernel/src/objects/surface.rs index 47af01c92..ae5f90d99 100644 --- a/crates/fj-kernel/src/objects/surface.rs +++ b/crates/fj-kernel/src/objects/surface.rs @@ -1,6 +1,6 @@ use fj_math::{Line, Point, Transform, Vector}; -use crate::geometry::LocalPoint; +use crate::local::Local; use super::Curve; @@ -68,7 +68,7 @@ impl Surface { pub fn point_to_surface_coords( &self, point_3d: impl Into>, - ) -> LocalPoint<2> { + ) -> Local> { let point_3d = point_3d.into(); let point_2d = match self { @@ -77,7 +77,7 @@ impl Surface { } }; - LocalPoint::new(point_2d, point_3d) + Local::new(point_2d, point_3d) } /// Convert a point in surface coordinates to model coordinates diff --git a/crates/fj-kernel/src/shape/local.rs b/crates/fj-kernel/src/shape/local.rs deleted file mode 100644 index 753893a89..000000000 --- a/crates/fj-kernel/src/shape/local.rs +++ /dev/null @@ -1,44 +0,0 @@ -/// A reference to an object, which includes a local form -/// -/// This type is used by topological objects to reference other objects, while -/// also keeping track of a local representation of that object, which is often -/// more appropriate for various tasks. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct LocalForm { - local: Local, - canonical: Canonical, -} - -impl LocalForm { - /// Construct a new instance of `LocalForm` - /// - /// It is the caller's responsibility to make sure that the local and - /// canonical forms passed to this method actually match. - pub fn new(local: Local, canonical: Canonical) -> Self { - Self { local, canonical } - } - - /// Access the local form of the referenced object - pub fn local(&self) -> &Local { - &self.local - } - - /// Access the canonical form of the referenced object - pub fn canonical(&self) -> &Canonical { - &self.canonical - } -} - -impl LocalForm { - /// Construct a new instance of `LocalForm` without a local form - /// - /// It's possible that an object's local and canonical forms are the same. - /// This is a convenience constructor that constructs a `LocalForm` instance - /// for such a situation. - pub fn canonical_only(canonical: Canonical) -> Self - where - Canonical: Clone, - { - Self::new(canonical.clone(), canonical) - } -} diff --git a/crates/fj-kernel/src/shape/mod.rs b/crates/fj-kernel/src/shape/mod.rs deleted file mode 100644 index 741ffdec3..000000000 --- a/crates/fj-kernel/src/shape/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! The API used for creating and manipulating shapes -//! -//! See [`Shape`], which is the main entry point to this API. - -mod local; - -pub use self::local::LocalForm; diff --git a/crates/fj-kernel/src/validation/mod.rs b/crates/fj-kernel/src/validation/mod.rs index ef6f574b6..b34a2d627 100644 --- a/crates/fj-kernel/src/validation/mod.rs +++ b/crates/fj-kernel/src/validation/mod.rs @@ -141,8 +141,8 @@ mod tests { use fj_math::{Point, Scalar}; use crate::{ + local::Local, objects::{Curve, Edge, GlobalVertex, Vertex, VerticesOfEdge}, - shape::LocalForm, validation::{validate, ValidationConfig, ValidationError}, }; @@ -154,7 +154,7 @@ mod tests { let curve = { let curve_local = Curve::line_from_points([[0., 0.], [1., 0.]]); let curve_canonical = Curve::line_from_points([a, b]); - LocalForm::new(curve_local, curve_canonical) + Local::new(curve_local, curve_canonical) }; let a = GlobalVertex::from_position(a); diff --git a/crates/fj-operations/src/difference_2d.rs b/crates/fj-operations/src/difference_2d.rs index 27e6fcf74..a65a9e989 100644 --- a/crates/fj-operations/src/difference_2d.rs +++ b/crates/fj-operations/src/difference_2d.rs @@ -2,8 +2,8 @@ use fj_interop::debug::DebugInfo; use fj_kernel::{ algorithms::Tolerance, iter::ObjectIters, + local::Local, objects::{Cycle, Edge, Face}, - shape::LocalForm, validation::{validate, Validated, ValidationConfig, ValidationError}, }; use fj_math::Aabb; @@ -94,7 +94,7 @@ impl ToShape for fj::Difference2d { fn add_cycle(cycle: Cycle, reverse: bool) -> Cycle { let mut edges = Vec::new(); for edge in cycle.edges { - let curve_local = *edge.curve.local(); + let curve_local = edge.curve.local(); let curve_local = if reverse { curve_local.reverse() } else { @@ -115,7 +115,7 @@ fn add_cycle(cycle: Cycle, reverse: bool) -> Cycle { }; let edge = Edge { - curve: LocalForm::new(curve_local, curve_canonical), + curve: Local::new(curve_local, curve_canonical), vertices: vertices.clone(), };