Skip to content

Commit

Permalink
Merge pull request #579 from hannobraun/curve
Browse files Browse the repository at this point in the history
Make `Curve` generic over its dimensionality
  • Loading branch information
hannobraun authored May 13, 2022
2 parents f2b308a + 1435249 commit b764d2b
Show file tree
Hide file tree
Showing 15 changed files with 86 additions and 75 deletions.
6 changes: 3 additions & 3 deletions crates/fj-kernel/src/algorithms/approx/curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<geometry::Point<1>>,
out: &mut Vec<geometry::Point<1, 3>>,
) {
match curve {
Curve::Circle(curve) => approx_circle(curve, tolerance, out),
Expand All @@ -38,7 +38,7 @@ pub fn approx_curve(
pub fn approx_circle(
circle: &Circle<3>,
tolerance: Tolerance,
out: &mut Vec<geometry::Point<1>>,
out: &mut Vec<geometry::Point<1, 3>>,
) {
let radius = circle.a.magnitude();

Expand Down
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/algorithms/approx/cycles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<geometry::Point<3>>,
pub points: Vec<geometry::Point<3, 3>>,
}

impl CycleApprox {
Expand Down
4 changes: 2 additions & 2 deletions crates/fj-kernel/src/algorithms/approx/edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::{geometry, topology::EdgeVertex};

pub fn approximate_edge(
vertices: Option<[EdgeVertex; 2]>,
mut points: Vec<geometry::Point<1>>,
) -> Vec<geometry::Point<1>> {
mut points: Vec<geometry::Point<1, 3>>,
) -> Vec<geometry::Point<1, 3>> {
// 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.
Expand Down
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/algorithms/approx/faces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<geometry::Point<3>>,
pub points: HashSet<geometry::Point<3, 3>>,

/// Approximation of the exterior cycle
pub exterior: CycleApprox,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Curve> {
pub fn surface_surface(a: &Surface, b: &Surface) -> Option<Curve<3>> {
// Algorithm from Real-Time Collision Detection by Christer Ericson. See
// section 5.4.4, Intersection of Two Planes.

Expand Down
6 changes: 3 additions & 3 deletions crates/fj-kernel/src/algorithms/triangulation/delaunay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::geometry;

/// Create a Delaunay triangulation of all points
pub fn triangulate(
points: Vec<geometry::Point<2>>,
) -> Vec<[geometry::Point<2>; 3]> {
points: Vec<geometry::Point<2, 3>>,
) -> Vec<[geometry::Point<2, 3>; 3]> {
use spade::Triangulation as _;

let triangulation = spade::DelaunayTriangulation::<_>::bulk_load(points)
Expand All @@ -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<Self::Scalar> {
Expand Down
58 changes: 33 additions & 25 deletions crates/fj-kernel/src/geometry/curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<const D: usize> {
/// A circle
Circle(Circle<3>),
Circle(Circle<D>),

/// A line
Line(Line<3>),
Line(Line<D>),
}

impl Curve {
impl Curve<3> {
/// Construct a `Curve` that represents the x-axis
pub fn x_axis() -> Self {
Self::Line(Line {
Expand All @@ -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<const D: usize> Curve<D> {
/// Access the origin of the curve's coordinate system
pub fn origin(&self) -> Point<3> {
pub fn origin(&self) -> Point<D> {
match self {
Self::Circle(curve) => curve.center,
Self::Line(curve) => curve.origin,
Expand All @@ -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.
Expand All @@ -84,23 +90,25 @@ impl Curve {
/// an error.
pub fn point_to_curve_coords(
&self,
point: impl Into<Point<3>>,
) -> geometry::Point<1> {
let point_3d = point.into();
point: impl Into<Point<D>>,
) -> 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<1>>,
) -> Point<3> {
) -> Point<D> {
match self {
Self::Circle(curve) => curve.point_from_circle_coords(point),
Self::Line(curve) => curve.point_from_line_coords(point),
Expand All @@ -111,7 +119,7 @@ impl Curve {
pub fn vector_from_curve_coords(
&self,
point: impl Into<Vector<1>>,
) -> Vector<3> {
) -> Vector<D> {
match self {
Self::Circle(curve) => curve.vector_from_circle_coords(point),
Self::Line(curve) => curve.vector_from_line_coords(point),
Expand Down
53 changes: 28 additions & 25 deletions crates/fj-kernel/src/geometry/points.rs
Original file line number Diff line number Diff line change
@@ -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<const D: usize> {
/// 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<D>,

/// 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<const N: usize, const C: usize> {
native: fj_math::Point<N>,
canonical: fj_math::Point<C>,
}

impl<const D: usize> Point<D> {
impl<const N: usize, const C: usize> Point<N, C> {
/// 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<fj_math::Point<D>>,
canonical: impl Into<fj_math::Point<3>>,
native: impl Into<fj_math::Point<N>>,
canonical: impl Into<fj_math::Point<C>>,
) -> Self {
Self {
native: native.into(),
Expand All @@ -36,12 +39,12 @@ impl<const D: usize> Point<D> {
}

/// Access the point's native form
pub fn native(&self) -> fj_math::Point<D> {
pub fn native(&self) -> fj_math::Point<N> {
self.native
}

/// Access the point's canonical form
pub fn canonical(&self) -> fj_math::Point<3> {
pub fn canonical(&self) -> fj_math::Point<C> {
self.canonical
}
}
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/geometry/surfaces/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl Surface {
pub fn point_to_surface_coords(
&self,
point_3d: impl Into<Point<3>>,
) -> geometry::Point<2> {
) -> geometry::Point<2, 3> {
let point_3d = point_3d.into();

let point_2d = match self {
Expand Down
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/geometry/surfaces/swept.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
4 changes: 2 additions & 2 deletions crates/fj-kernel/src/shape/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Curve> {
pub fn curves(&self) -> Iter<Curve<3>> {
self.stores.curves.iter()
}

Expand Down Expand Up @@ -385,7 +385,7 @@ mod tests {
}
}

fn add_curve(&mut self) -> Handle<Curve> {
fn add_curve(&mut self) -> Handle<Curve<3>> {
self.insert(Curve::x_axis()).unwrap()
}

Expand Down
4 changes: 2 additions & 2 deletions crates/fj-kernel/src/shape/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand All @@ -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 {}
Expand Down
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/shape/stores.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Stores {
}

pub type Points = Store<Point<3>>;
pub type Curves = Store<Curve>;
pub type Curves = Store<Curve<3>>;
pub type Surfaces = Store<Surface>;

pub type Vertices = Store<Vertex>;
Expand Down
6 changes: 3 additions & 3 deletions crates/fj-kernel/src/shape/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down Expand Up @@ -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<Curve>) -> bool {
pub fn missing_curve(&self, curve: &Handle<Curve<3>>) -> bool {
if let Self::Structural(StructuralIssues { missing_curve, .. }) = self {
return missing_curve.as_ref() == Some(curve);
}
Expand Down Expand Up @@ -270,7 +270,7 @@ impl From<StructuralIssues> for ValidationError {
#[derive(Debug, Default)]
pub struct StructuralIssues {
/// Missing curve found in edge validation
pub missing_curve: Option<Handle<Curve>>,
pub missing_curve: Option<Handle<Curve<3>>>,

/// Missing vertices found in edge validation
pub missing_vertices: HashSet<Handle<Vertex>>,
Expand Down
8 changes: 4 additions & 4 deletions crates/fj-kernel/src/topology/edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Curve>,
pub curve: Handle<Curve<3>>,

/// Access the vertices that bound the edge on the curve
///
Expand All @@ -38,7 +38,7 @@ pub struct Edge {
impl Edge {
/// Construct an instance of `Edge`
pub fn new(
curve: Handle<Curve>,
curve: Handle<Curve<3>>,
vertices: Option<[Handle<Vertex>; 2]>,
) -> Self {
let vertices = vertices.map(|vertices| {
Expand All @@ -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()
}

Expand Down Expand Up @@ -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>,
}

0 comments on commit b764d2b

Please sign in to comment.