diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index ba4169ffa..19c2465c4 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -13,7 +13,10 @@ use std::cmp::max; use fj_math::{Circle, Point, Scalar}; -use crate::objects::{Curve, CurveKind, GlobalCurve, Vertex}; +use crate::{ + objects::{Curve, GlobalCurve, Vertex}, + path::GlobalPath, +}; use super::{Approx, ApproxPoint, Tolerance}; @@ -28,7 +31,7 @@ impl Approx for (&Curve, RangeOnCurve) { .into_iter() .map(|point| { let point_surface = - curve.kind().point_from_curve_coords(point.local_form); + curve.path().point_from_path_coords(point.local_form); ApproxPoint::new(point_surface, point.global_form) .with_source((*curve, point.local_form)) }) @@ -44,9 +47,11 @@ impl Approx for (&GlobalCurve, RangeOnCurve) { fn approx(self, tolerance: Tolerance) -> Self::Approximation { let (curve, range) = self; - match curve.kind() { - CurveKind::Circle(curve) => approx_circle(curve, range, tolerance), - CurveKind::Line(_) => vec![], + match curve.path() { + GlobalPath::Circle(circle) => { + approx_circle(&circle, range, tolerance) + } + GlobalPath::Line(_) => vec![], } } } diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs index 4c7d19a96..417643799 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs @@ -1,6 +1,9 @@ use fj_math::{Point, Segment}; -use crate::objects::{Curve, CurveKind, HalfEdge}; +use crate::{ + objects::{Curve, HalfEdge}, + path::SurfacePath, +}; use super::LineSegmentIntersection; @@ -29,14 +32,14 @@ impl CurveEdgeIntersection { /// computed. Panics, if a different type of [`Curve`] or [`HalfEdge`] is /// passed. pub fn compute(curve: &Curve, half_edge: &HalfEdge) -> Option { - let curve_as_line = match curve.kind() { - CurveKind::Line(line) => line, + let curve_as_line = match curve.path() { + SurfacePath::Line(line) => line, _ => todo!("Curve-edge intersection only supports lines"), }; let edge_as_segment = { - let edge_curve_as_line = match half_edge.curve().kind() { - CurveKind::Line(line) => line, + let edge_curve_as_line = match half_edge.curve().path() { + SurfacePath::Line(line) => line, _ => { todo!("Curve-edge intersection only supports line segments") } @@ -50,7 +53,7 @@ impl CurveEdgeIntersection { }; let intersection = - LineSegmentIntersection::compute(curve_as_line, &edge_as_segment)?; + LineSegmentIntersection::compute(&curve_as_line, &edge_as_segment)?; let intersection = match intersection { LineSegmentIntersection::Point { point_on_line } => Self::Point { diff --git a/crates/fj-kernel/src/algorithms/intersect/ray_edge.rs b/crates/fj-kernel/src/algorithms/intersect/ray_edge.rs index 4ac3b07b5..bbc26f1a8 100644 --- a/crates/fj-kernel/src/algorithms/intersect/ray_edge.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_edge.rs @@ -4,7 +4,8 @@ use fj_math::Segment; use crate::{ algorithms::intersect::{HorizontalRayToTheRight, Intersect}, - objects::{CurveKind, HalfEdge}, + objects::HalfEdge, + path::SurfacePath, }; use super::ray_segment::RaySegmentIntersection; @@ -15,9 +16,9 @@ impl Intersect for (&HorizontalRayToTheRight<2>, &HalfEdge) { fn intersect(self) -> Option { let (ray, edge) = self; - let line = match edge.curve().kind() { - CurveKind::Line(line) => line, - CurveKind::Circle(_) => { + let line = match edge.curve().path() { + SurfacePath::Line(line) => line, + SurfacePath::Circle(_) => { todo!("Casting rays against circles is not supported yet") } }; diff --git a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs index 1e3ca504b..e6686cb57 100644 --- a/crates/fj-kernel/src/algorithms/intersect/ray_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/ray_face.rs @@ -4,7 +4,8 @@ use fj_math::{Point, Scalar, Vector}; use crate::{ algorithms::intersect::face_point::FacePointIntersection, - objects::{CurveKind, Face, HalfEdge, Vertex}, + objects::{Face, HalfEdge, Vertex}, + path::GlobalPath, }; use super::{HorizontalRayToTheRight, Intersect}; @@ -17,10 +18,10 @@ impl Intersect for (&HorizontalRayToTheRight<3>, &Face) { let (plane_origin, plane_direction_1, plane_direction_2) = match face.surface().u() { - CurveKind::Circle(_) => todo!( + GlobalPath::Circle(_) => todo!( "Casting a ray against a swept circle is not supported yet" ), - CurveKind::Line(line) => { + GlobalPath::Line(line) => { (line.origin(), line.direction(), face.surface().v()) } }; diff --git a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs index 9b230fed7..c3b39a27a 100644 --- a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs @@ -1,6 +1,9 @@ use fj_math::{Line, Point, Scalar, Vector}; -use crate::objects::{Curve, CurveKind, GlobalCurve, Surface}; +use crate::{ + objects::{Curve, GlobalCurve, Surface}, + path::{GlobalPath, SurfacePath}, +}; /// The intersection between two surfaces #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -49,11 +52,11 @@ impl SurfaceSurfaceIntersection { let curves = planes_parametric.map(|(surface, plane)| { let local = project_line_into_plane(&line, &plane); - let global = CurveKind::Line(Line::from_origin_and_direction( + let global = GlobalPath::Line(Line::from_origin_and_direction( origin, direction, )); - Curve::new(surface, local, GlobalCurve::from_kind(global)) + Curve::new(surface, local, GlobalCurve::from_path(global)) }); Some(Self { @@ -74,7 +77,7 @@ impl PlaneParametric { pub fn extract_from_surface(surface: &Surface) -> Self { let (line, path) = { let line = match surface.u() { - CurveKind::Line(line) => line, + GlobalPath::Line(line) => line, _ => todo!( "Only plane-plane intersection is currently supported." ), @@ -120,7 +123,7 @@ impl PlaneConstantNormal { fn project_line_into_plane( line: &Line<3>, plane: &PlaneParametric, -) -> CurveKind<2> { +) -> SurfacePath { let line_origin_relative_to_plane = line.origin() - plane.origin; let line_origin_in_plane = Vector::from([ plane @@ -143,7 +146,7 @@ fn project_line_into_plane( line_direction_in_plane, ); - CurveKind::Line(line) + SurfacePath::Line(line) } #[cfg(test)] diff --git a/crates/fj-kernel/src/algorithms/sweep/curve.rs b/crates/fj-kernel/src/algorithms/sweep/curve.rs index cd7178e1d..b5a5882bf 100644 --- a/crates/fj-kernel/src/algorithms/sweep/curve.rs +++ b/crates/fj-kernel/src/algorithms/sweep/curve.rs @@ -16,6 +16,6 @@ impl Sweep for GlobalCurve { type Swept = Surface; fn sweep(self, path: impl Into>) -> Self::Swept { - Surface::new(*self.kind(), path.into()) + Surface::new(self.path(), path.into()) } } diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 26c062fc3..0e9355866 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -4,9 +4,9 @@ use fj_math::{Line, Scalar, Vector}; use crate::{ algorithms::{reverse::Reverse, transform::TransformObject}, objects::{ - Curve, CurveKind, Cycle, Face, GlobalEdge, HalfEdge, SurfaceVertex, - Vertex, + Curve, Cycle, Face, GlobalEdge, HalfEdge, SurfaceVertex, Vertex, }, + path::SurfacePath, }; use super::Sweep; @@ -32,13 +32,14 @@ impl Sweep for (HalfEdge, Color) { let curve = { // Please note that creating a line here is correct, even if the - // global curve is a circle. Projected into the side surface, it is - // going to be a line either way. - let kind = CurveKind::Line(Line::from_points_with_line_coords( - points_curve_and_surface, - )); + // global curve is a circle. Projected into the side surface, it + // is going to be a line either way. + let path = + SurfacePath::Line(Line::from_points_with_line_coords( + points_curve_and_surface, + )); - Curve::new(surface, kind, *edge.curve().global_form()) + Curve::new(surface, path, *edge.curve().global_form()) }; let vertices = { @@ -93,11 +94,12 @@ impl Sweep for (HalfEdge, Color) { // Please note that creating a line here is correct, even if the // global curve is a circle. Projected into the side surface, it // is going to be a line either way. - let kind = CurveKind::Line(Line::from_points_with_line_coords( - points_curve_and_surface, - )); + let path = + SurfacePath::Line(Line::from_points_with_line_coords( + points_curve_and_surface, + )); - Curve::new(surface, kind, global) + Curve::new(surface, path, global) }; let global = GlobalEdge::new(*curve.global_form(), global_vertices); diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index e7391ab0e..af252266d 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -2,7 +2,8 @@ use fj_math::{Scalar, Vector}; use crate::{ algorithms::{reverse::Reverse, transform::TransformObject}, - objects::{CurveKind, Face, Shell}, + objects::{Face, Shell}, + path::GlobalPath, }; use super::Sweep; @@ -17,11 +18,11 @@ impl Sweep for Face { let is_negative_sweep = { let a = match self.surface().u() { - CurveKind::Circle(_) => todo!( + GlobalPath::Circle(_) => todo!( "Sweeping from faces defined in round surfaces is not \ supported" ), - CurveKind::Line(line) => line.direction(), + GlobalPath::Line(line) => line.direction(), }; let b = self.surface().v(); diff --git a/crates/fj-kernel/src/algorithms/sweep/vertex.rs b/crates/fj-kernel/src/algorithms/sweep/vertex.rs index 63165586e..22a031113 100644 --- a/crates/fj-kernel/src/algorithms/sweep/vertex.rs +++ b/crates/fj-kernel/src/algorithms/sweep/vertex.rs @@ -1,8 +1,11 @@ use fj_math::{Line, Point, Scalar, Vector}; -use crate::objects::{ - Curve, CurveKind, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface, - SurfaceVertex, Vertex, +use crate::{ + objects::{ + Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface, + SurfaceVertex, Vertex, + }, + path::SurfacePath, }; use super::Sweep; @@ -46,7 +49,7 @@ impl Sweep for (Vertex, Surface) { // // Let's make sure that these requirements are met. { - assert_eq!(vertex.curve().global_form().kind(), &surface.u()); + assert_eq!(vertex.curve().global_form().path(), surface.u()); assert_eq!(path, surface.v()); } @@ -78,7 +81,7 @@ impl Sweep for (Vertex, Surface) { // `Edge` is straight-forward. let curve = { let line = Line::from_points(points_surface); - Curve::new(surface, CurveKind::Line(line), *edge_global.curve()) + Curve::new(surface, SurfacePath::Line(line), *edge_global.curve()) }; // And now the vertices. Again, nothing wild here. diff --git a/crates/fj-kernel/src/algorithms/transform.rs b/crates/fj-kernel/src/algorithms/transform.rs index fefd8d11e..30394d341 100644 --- a/crates/fj-kernel/src/algorithms/transform.rs +++ b/crates/fj-kernel/src/algorithms/transform.rs @@ -2,9 +2,12 @@ use fj_math::{Transform, Vector}; -use crate::objects::{ - Curve, Cycle, Face, Faces, GlobalCurve, GlobalVertex, HalfEdge, Shell, - Sketch, Solid, Surface, SurfaceVertex, Vertex, +use crate::{ + objects::{ + Curve, Cycle, Face, Faces, GlobalCurve, GlobalVertex, HalfEdge, Shell, + Sketch, Solid, Surface, SurfaceVertex, Vertex, + }, + path::GlobalPath, }; /// Transform an object @@ -40,7 +43,7 @@ impl TransformObject for Curve { let global = self.global_form().transform(transform); // Don't need to transform `self.kind`, as that's in local form. - Curve::new(surface, *self.kind(), global) + Curve::new(surface, self.path(), global) } } @@ -80,8 +83,18 @@ impl TransformObject for Faces { impl TransformObject for GlobalCurve { fn transform(self, transform: &Transform) -> Self { - let kind = self.kind().transform(transform); - GlobalCurve::from_kind(kind) + GlobalCurve::from_path(self.path().transform(transform)) + } +} + +impl TransformObject for GlobalPath { + 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)), + } } } diff --git a/crates/fj-kernel/src/algorithms/validate/coherence.rs b/crates/fj-kernel/src/algorithms/validate/coherence.rs index bae4d679a..e2cc725b5 100644 --- a/crates/fj-kernel/src/algorithms/validate/coherence.rs +++ b/crates/fj-kernel/src/algorithms/validate/coherence.rs @@ -13,13 +13,13 @@ pub fn validate_curve( let points_curve = [-2., -1., 0., 1., 2.].map(|point| Point::from([point])); for point_curve in points_curve { - let point_surface = curve.kind().point_from_curve_coords(point_curve); + let point_surface = curve.path().point_from_path_coords(point_curve); let point_surface_as_global = curve.surface().point_from_surface_coords(point_surface); let point_global = curve .global_form() - .kind() - .point_from_curve_coords(point_curve); + .path() + .point_from_path_coords(point_curve); let distance = (point_surface_as_global - point_global).magnitude(); @@ -51,8 +51,8 @@ pub fn validate_vertex( let local_as_global = vertex .curve() .global_form() - .kind() - .point_from_curve_coords(local); + .path() + .point_from_path_coords(local); let global = vertex.global_form().position(); let distance = (local_as_global - global).magnitude(); diff --git a/crates/fj-kernel/src/algorithms/validate/mod.rs b/crates/fj-kernel/src/algorithms/validate/mod.rs index e4f5380b5..4d667fb06 100644 --- a/crates/fj-kernel/src/algorithms/validate/mod.rs +++ b/crates/fj-kernel/src/algorithms/validate/mod.rs @@ -161,20 +161,22 @@ mod tests { use crate::{ algorithms::validate::{Validate, ValidationConfig, ValidationError}, objects::{ - Curve, CurveKind, GlobalCurve, GlobalVertex, HalfEdge, Surface, - SurfaceVertex, Vertex, + Curve, GlobalCurve, GlobalVertex, HalfEdge, Surface, SurfaceVertex, + Vertex, }, + path::{GlobalPath, SurfacePath}, }; #[test] fn coherence_curve() { let line_global = Line::from_points([[0., 0., 0.], [1., 0., 0.]]); - let global_curve = GlobalCurve::from_kind(CurveKind::Line(line_global)); + let global_curve = + GlobalCurve::from_path(GlobalPath::Line(line_global)); let line_surface = Line::from_points([[0., 0.], [2., 0.]]); let curve = Curve::new( Surface::xy_plane(), - CurveKind::Line(line_surface), + SurfacePath::Line(line_surface), global_curve, ); @@ -190,11 +192,11 @@ mod tests { let points_global = [[0., 0., 0.], [1., 0., 0.]]; let curve = { - let curve_local = CurveKind::line_from_points(points_surface); - let curve_global = GlobalCurve::from_kind( - CurveKind::line_from_points(points_global), + let path = SurfacePath::line_from_points(points_surface); + let curve_global = GlobalCurve::from_path( + GlobalPath::line_from_points(points_global), ); - Curve::new(surface, curve_local, curve_global) + Curve::new(surface, path, curve_global) }; let [a_global, b_global] = diff --git a/crates/fj-kernel/src/builder/curve.rs b/crates/fj-kernel/src/builder/curve.rs index 600b1481d..b51858ae9 100644 --- a/crates/fj-kernel/src/builder/curve.rs +++ b/crates/fj-kernel/src/builder/curve.rs @@ -1,6 +1,9 @@ use fj_math::{Line, Point, Vector}; -use crate::objects::{Curve, CurveKind, GlobalCurve, Surface}; +use crate::{ + objects::{Curve, GlobalCurve, Surface}, + path::{GlobalPath, SurfacePath}, +}; /// API for building a [`Curve`] pub struct CurveBuilder { @@ -42,8 +45,8 @@ impl CurveBuilder { Curve::new( self.surface, - CurveKind::Line(local), - GlobalCurve::from_kind(CurveKind::Line(global)), + SurfacePath::Line(local), + GlobalCurve::from_path(GlobalPath::Line(global)), ) } } @@ -54,17 +57,17 @@ pub struct GlobalCurveBuilder; impl GlobalCurveBuilder { /// Create a line that represents the x-axis pub fn x_axis(&self) -> GlobalCurve { - GlobalCurve::from_kind(CurveKind::x_axis()) + GlobalCurve::from_path(GlobalPath::x_axis()) } /// Create a line that represents the y-axis pub fn y_axis(&self) -> GlobalCurve { - GlobalCurve::from_kind(CurveKind::y_axis()) + GlobalCurve::from_path(GlobalPath::y_axis()) } /// Create a line that represents the z-axis pub fn z_axis(&self) -> GlobalCurve { - GlobalCurve::from_kind(CurveKind::z_axis()) + GlobalCurve::from_path(GlobalPath::z_axis()) } /// Create a line from the given points @@ -73,6 +76,6 @@ impl GlobalCurveBuilder { points: [impl Into>; 2], ) -> GlobalCurve { let line = Line::from_points(points); - GlobalCurve::from_kind(CurveKind::Line(line)) + GlobalCurve::from_path(GlobalPath::Line(line)) } } diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs index 7034f621f..d2340292e 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/builder/edge.rs @@ -1,8 +1,11 @@ use fj_math::{Circle, Line, Point, Scalar, Vector}; -use crate::objects::{ - Curve, CurveKind, GlobalCurve, GlobalVertex, HalfEdge, Surface, - SurfaceVertex, Vertex, +use crate::{ + objects::{ + Curve, GlobalCurve, GlobalVertex, HalfEdge, Surface, SurfaceVertex, + Vertex, + }, + path::{GlobalPath, SurfacePath}, }; /// API for building an [`HalfEdge`] @@ -21,19 +24,19 @@ impl HalfEdgeBuilder { /// Build a circle from the given radius pub fn circle_from_radius(&self, radius: Scalar) -> HalfEdge { let curve = { - let local = CurveKind::Circle(Circle::new( + let path = SurfacePath::Circle(Circle::new( Point::origin(), Vector::from([radius, Scalar::ZERO]), Vector::from([Scalar::ZERO, radius]), )); let global = - GlobalCurve::from_kind(CurveKind::Circle(Circle::new( + GlobalCurve::from_path(GlobalPath::Circle(Circle::new( Point::origin(), Vector::from([radius, Scalar::ZERO, Scalar::ZERO]), Vector::from([Scalar::ZERO, radius, Scalar::ZERO]), ))); - Curve::new(self.surface, local, global) + Curve::new(self.surface, path, global) }; let vertices = { @@ -41,12 +44,12 @@ impl HalfEdgeBuilder { [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); let global_vertex = GlobalVertex::from_position( - curve.global_form().kind().point_from_curve_coords(a_curve), + curve.global_form().path().point_from_path_coords(a_curve), ); let surface_vertices = [a_curve, b_curve].map(|point_curve| { let point_surface = - curve.kind().point_from_curve_coords(point_curve); + curve.path().point_from_path_coords(point_curve); SurfaceVertex::new(point_surface, self.surface, global_vertex) }); @@ -97,15 +100,16 @@ impl HalfEdgeBuilder { }; let curve = { - let curve_local = CurveKind::Line(Line::from_points(points)); + let path = SurfacePath::Line(Line::from_points(points)); let curve_global = { let points = global_vertices .map(|global_vertex| global_vertex.position()); - let kind = CurveKind::Line(Line::from_points(points)); - GlobalCurve::from_kind(kind) + GlobalCurve::from_path(GlobalPath::Line(Line::from_points( + points, + ))) }; - Curve::new(self.surface, curve_local, curve_global) + Curve::new(self.surface, path, curve_global) }; let vertices = { diff --git a/crates/fj-kernel/src/builder/vertex.rs b/crates/fj-kernel/src/builder/vertex.rs index b5a323610..8da304b4f 100644 --- a/crates/fj-kernel/src/builder/vertex.rs +++ b/crates/fj-kernel/src/builder/vertex.rs @@ -23,11 +23,11 @@ impl VertexBuilder { let global_form = GlobalVertex::from_position( self.curve .global_form() - .kind() - .point_from_curve_coords(point), + .path() + .point_from_path_coords(point), ); let surface_form = SurfaceVertex::new( - self.curve.kind().point_from_curve_coords(point), + self.curve.path().point_from_path_coords(point), surface, global_form, ); diff --git a/crates/fj-kernel/src/lib.rs b/crates/fj-kernel/src/lib.rs index 33a837d82..59a16b390 100644 --- a/crates/fj-kernel/src/lib.rs +++ b/crates/fj-kernel/src/lib.rs @@ -91,3 +91,4 @@ pub mod algorithms; pub mod builder; pub mod iter; pub mod objects; +pub mod path; diff --git a/crates/fj-kernel/src/objects/curve.rs b/crates/fj-kernel/src/objects/curve.rs index 6e3d7a6dc..362f4d8d6 100644 --- a/crates/fj-kernel/src/objects/curve.rs +++ b/crates/fj-kernel/src/objects/curve.rs @@ -1,14 +1,15 @@ -use fj_math::{Circle, Line, Point, Transform, Vector}; - -use crate::builder::{CurveBuilder, GlobalCurveBuilder}; +use crate::{ + builder::{CurveBuilder, GlobalCurveBuilder}, + path::{GlobalPath, SurfacePath}, +}; use super::Surface; /// A curve, defined in local surface coordinates #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Curve { + path: SurfacePath, surface: Surface, - kind: CurveKind<2>, global: GlobalCurve, } @@ -21,26 +22,26 @@ impl Curve { /// Construct a new instance of `Curve` pub fn new( surface: Surface, - kind: CurveKind<2>, + path: SurfacePath, global: GlobalCurve, ) -> Self { Self { surface, - kind, + path, global, } } + /// Access the path that defines this curve + pub fn path(&self) -> SurfacePath { + self.path + } + /// Access the surface that this curve is defined in pub fn surface(&self) -> &Surface { &self.surface } - /// Access the kind of this curve - pub fn kind(&self) -> &CurveKind<2> { - &self.kind - } - /// Access the global form of this curve pub fn global_form(&self) -> &GlobalCurve { &self.global @@ -50,7 +51,7 @@ impl Curve { /// A curve, defined in global (3D) coordinates #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct GlobalCurve { - kind: CurveKind<3>, + path: GlobalPath, } impl GlobalCurve { @@ -59,111 +60,13 @@ impl GlobalCurve { GlobalCurveBuilder } - /// Construct a `GlobalCurve` from a [`CurveKind<3>`] - pub fn from_kind(kind: CurveKind<3>) -> Self { - Self { kind } - } - - /// Access the kind of this curve - pub fn kind(&self) -> &CurveKind<3> { - &self.kind - } -} - -/// A one-dimensional shape -/// -/// The word "curve" is used as an umbrella term for all one-dimensional shapes, -/// and doesn't imply that those shapes need to be curved. Straight lines are -/// included. -/// -/// 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 CurveKind { - /// A circle - Circle(Circle), - - /// A line - Line(Line), -} - -impl CurveKind { - /// Construct a line from two points - pub fn line_from_points(points: [impl Into>; 2]) -> Self { - Self::Line(Line::from_points(points)) + /// Construct a `GlobalCurve` from the path that defines it + pub fn from_path(path: GlobalPath) -> Self { + Self { path } } - /// Access the origin of the curve's coordinate system - pub fn origin(&self) -> Point { - match self { - Self::Circle(curve) => curve.center(), - Self::Line(curve) => curve.origin(), - } - } - - /// Convert a point on the curve into model coordinates - pub fn point_from_curve_coords( - &self, - point: impl Into>, - ) -> Point { - match self { - Self::Circle(curve) => curve.point_from_circle_coords(point), - Self::Line(curve) => curve.point_from_line_coords(point), - } - } - - /// Convert a vector on the curve into model coordinates - pub fn vector_from_curve_coords( - &self, - point: impl Into>, - ) -> Vector { - match self { - Self::Circle(curve) => curve.vector_from_circle_coords(point), - Self::Line(curve) => curve.vector_from_line_coords(point), - } - } -} - -impl CurveKind<3> { - /// Construct a `Curve` that represents the x-axis - pub fn x_axis() -> Self { - Self::Line(Line::from_origin_and_direction( - Point::origin(), - Vector::unit_x(), - )) - } - - /// Construct a `Curve` that represents the y-axis - pub fn y_axis() -> Self { - Self::Line(Line::from_origin_and_direction( - Point::origin(), - Vector::unit_y(), - )) - } - - /// Construct a `Curve` that represents the z-axis - pub fn z_axis() -> Self { - Self::Line(Line::from_origin_and_direction( - Point::origin(), - Vector::unit_z(), - )) - } - - /// Transform the surface - #[must_use] - pub fn transform(self, transform: &Transform) -> Self { - match self { - CurveKind::Circle(curve) => { - CurveKind::Circle(transform.transform_circle(&curve)) - } - CurveKind::Line(curve) => { - CurveKind::Line(transform.transform_line(&curve)) - } - } + /// Access the path that defines this curve + pub fn path(&self) -> GlobalPath { + self.path } } diff --git a/crates/fj-kernel/src/objects/cycle.rs b/crates/fj-kernel/src/objects/cycle.rs index d3d47ed08..7d56dfb9d 100644 --- a/crates/fj-kernel/src/objects/cycle.rs +++ b/crates/fj-kernel/src/objects/cycle.rs @@ -1,7 +1,7 @@ use fj_math::{Scalar, Winding}; use pretty_assertions::assert_eq; -use crate::builder::CycleBuilder; +use crate::{builder::CycleBuilder, path::SurfacePath}; use super::{HalfEdge, Surface}; @@ -104,9 +104,9 @@ impl Cycle { let [a, b] = first.vertices(); let edge_direction_positive = a.position() < b.position(); - let circle = match first.curve().kind() { - super::CurveKind::Circle(circle) => circle, - super::CurveKind::Line(_) => unreachable!( + let circle = match first.curve().path() { + SurfacePath::Circle(circle) => circle, + SurfacePath::Line(_) => unreachable!( "Invalid cycle: less than 3 edges, but not all are circles" ), }; diff --git a/crates/fj-kernel/src/objects/mod.rs b/crates/fj-kernel/src/objects/mod.rs index a437140d9..ab37f651e 100644 --- a/crates/fj-kernel/src/objects/mod.rs +++ b/crates/fj-kernel/src/objects/mod.rs @@ -15,7 +15,7 @@ mod surface; mod vertex; pub use self::{ - curve::{Curve, CurveKind, GlobalCurve}, + curve::{Curve, GlobalCurve}, cycle::Cycle, edge::{GlobalEdge, HalfEdge}, face::{Face, Faces, Handedness}, diff --git a/crates/fj-kernel/src/objects/surface.rs b/crates/fj-kernel/src/objects/surface.rs index bfd197ac0..8741a26e4 100644 --- a/crates/fj-kernel/src/objects/surface.rs +++ b/crates/fj-kernel/src/objects/surface.rs @@ -1,24 +1,24 @@ use fj_math::{Line, Point, Vector}; -use super::CurveKind; +use crate::path::GlobalPath; /// A two-dimensional shape #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Surface { - u: CurveKind<3>, + u: GlobalPath, v: Vector<3>, } impl Surface { /// Construct a `Surface` from two paths that define its coordinate system - pub fn new(u: CurveKind<3>, v: Vector<3>) -> Self { + pub fn new(u: GlobalPath, v: Vector<3>) -> Self { Self { u, v } } /// Construct a `Surface` that represents the xy-plane pub fn xy_plane() -> Self { Self { - u: CurveKind::x_axis(), + u: GlobalPath::x_axis(), v: Vector::unit_y(), } } @@ -26,7 +26,7 @@ impl Surface { /// Construct a `Surface` that represents the xz-plane pub fn xz_plane() -> Self { Self { - u: CurveKind::x_axis(), + u: GlobalPath::x_axis(), v: Vector::unit_z(), } } @@ -34,7 +34,7 @@ impl Surface { /// Construct a `Surface` that represents the yz-plane pub fn yz_plane() -> Self { Self { - u: CurveKind::y_axis(), + u: GlobalPath::y_axis(), v: Vector::unit_z(), } } @@ -43,14 +43,14 @@ impl Surface { pub fn plane_from_points(points: [impl Into>; 3]) -> Self { let [a, b, c] = points.map(Into::into); - let u = CurveKind::Line(Line::from_points([a, b])); + let u = GlobalPath::Line(Line::from_points([a, b])); let v = c - a; Self { u, v } } /// Access the path that defines the u-coordinate of this surface - pub fn u(&self) -> CurveKind<3> { + pub fn u(&self) -> GlobalPath { self.u } @@ -65,7 +65,7 @@ impl Surface { point: impl Into>, ) -> Point<3> { let point = point.into(); - self.u.point_from_curve_coords([point.u]) + self.u.point_from_path_coords([point.u]) + self.path_to_line().vector_from_line_coords([point.v]) } @@ -75,7 +75,7 @@ impl Surface { vector: impl Into>, ) -> Vector<3> { let vector = vector.into(); - self.u.vector_from_curve_coords([vector.u]) + self.u.vector_from_path_coords([vector.u]) + self.path_to_line().vector_from_line_coords([vector.v]) } @@ -89,14 +89,14 @@ mod tests { use fj_math::{Line, Point, Vector}; use pretty_assertions::assert_eq; - use crate::objects::CurveKind; + use crate::path::GlobalPath; use super::Surface; #[test] fn point_from_surface_coords() { let swept = Surface { - u: CurveKind::Line(Line::from_origin_and_direction( + u: GlobalPath::Line(Line::from_origin_and_direction( Point::from([1., 1., 1.]), Vector::from([0., 2., 0.]), )), @@ -112,7 +112,7 @@ mod tests { #[test] fn vector_from_surface_coords() { let swept = Surface { - u: CurveKind::Line(Line::from_origin_and_direction( + u: GlobalPath::Line(Line::from_origin_and_direction( Point::from([1., 0., 0.]), Vector::from([0., 2., 0.]), )), diff --git a/crates/fj-kernel/src/path.rs b/crates/fj-kernel/src/path.rs new file mode 100644 index 000000000..de796d118 --- /dev/null +++ b/crates/fj-kernel/src/path.rs @@ -0,0 +1,124 @@ +//! Paths through 2D and 3D space +//! +//! See [`SurfacePath`] and [`GlobalPath`]. +//! +//! # Implementation Note +//! +//! This is a bit of an in-between module. It is closely associated with curves +//! ([`Curve`]/[`GlobalCurve`]) and [`Surface`]s, but paths are not really +//! objects themselves, as logically speaking, they are owned and not referenced +//! (practically speaking, all objects are owned and not referenced, but that is +//! an implementation detail; see [#1021] for context on where things are +//! going). +//! +//! On the other hand, the types in this module don't follow the general style +//! of types in `fj-math`. +//! +//! We'll see how it shakes out. Maybe it will stay its own thing, maybe it will +//! move to `fj-math`, maybe something else entirely will happen. +//! +//! [`Curve`]: crate::objects::Curve +//! [`GlobalCurve`]: crate::objects::GlobalCurve +//! [`Surface`]: crate::objects::Surface +//! [#1021]: https://github.com/hannobraun/Fornjot/issues/1021 + +use fj_math::{Circle, Line, Point, Vector}; + +/// A path through surface (2D) space +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum SurfacePath { + /// A circle + Circle(Circle<2>), + + /// A line + Line(Line<2>), +} + +impl SurfacePath { + /// Construct a line from two points + pub fn line_from_points(points: [impl Into>; 2]) -> Self { + Self::Line(Line::from_points(points)) + } + + /// Convert a point on the path into global coordinates + pub fn point_from_path_coords( + &self, + point: impl Into>, + ) -> Point<2> { + match self { + Self::Circle(circle) => circle.point_from_circle_coords(point), + Self::Line(line) => line.point_from_line_coords(point), + } + } +} + +/// A path through global (3D) space +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum GlobalPath { + /// A circle + Circle(Circle<3>), + + /// A line + Line(Line<3>), +} + +impl GlobalPath { + /// Construct a line from two points + pub fn line_from_points(points: [impl Into>; 2]) -> Self { + Self::Line(Line::from_points(points)) + } + + /// Construct a `GlobalPath` that represents the x-axis + pub fn x_axis() -> Self { + Self::Line(Line::from_origin_and_direction( + Point::origin(), + Vector::unit_x(), + )) + } + + /// Construct a `GlobalPath` that represents the y-axis + pub fn y_axis() -> Self { + Self::Line(Line::from_origin_and_direction( + Point::origin(), + Vector::unit_y(), + )) + } + + /// Construct a `GlobalPath` that represents the z-axis + pub fn z_axis() -> Self { + Self::Line(Line::from_origin_and_direction( + Point::origin(), + Vector::unit_z(), + )) + } + + /// Access the origin of the path's coordinate system + pub fn origin(&self) -> Point<3> { + match self { + Self::Circle(circle) => circle.center() + circle.a(), + Self::Line(line) => line.origin(), + } + } + + /// Convert a point on the path into global coordinates + pub fn point_from_path_coords( + &self, + point: impl Into>, + ) -> Point<3> { + match self { + Self::Circle(circle) => circle.point_from_circle_coords(point), + Self::Line(line) => line.point_from_line_coords(point), + } + } + + /// Convert a vector on the path into global coordinates + pub fn vector_from_path_coords( + &self, + vector: impl Into>, + ) -> Vector<3> { + match self { + Self::Circle(circle) => circle.vector_from_circle_coords(vector), + Self::Line(line) => line.vector_from_line_coords(vector), + } + } +}