diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index f774d1add..29b657983 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -95,7 +95,7 @@ impl Sweep for (HalfEdge, Color) { let global = bottom_edge .curve() .global_form() - .clone_object() + .clone() .translate(path, stores); // Please note that creating a line here is correct, even if the diff --git a/crates/fj-kernel/src/algorithms/transform.rs b/crates/fj-kernel/src/algorithms/transform.rs deleted file mode 100644 index 8166f8ea7..000000000 --- a/crates/fj-kernel/src/algorithms/transform.rs +++ /dev/null @@ -1,249 +0,0 @@ -//! Transforming objects - -use fj_math::{Transform, Vector}; - -use crate::{ - objects::{ - Curve, Cycle, Face, Faces, GlobalCurve, GlobalEdge, GlobalVertex, - HalfEdge, Shell, Sketch, Solid, Surface, SurfaceVertex, Vertex, - }, - path::GlobalPath, - stores::{Handle, Stores}, -}; - -/// Transform an object -/// -/// # Implementation Note -/// -/// So far, a general `transform` method is available, along some convenience -/// methods for more specific transformations. -/// -/// More convenience methods can be added as required. The only reason this -/// hasn't been done so far, is that no one has put in the work yet. -pub trait TransformObject: Sized { - /// The type of the transformed object - /// - /// # Implementation Note - /// - /// This type is temporary, while we transition to a global object store. It - /// should be removed, once that transition is complete. - type Transformed; - - /// Transform the object - #[must_use] - fn transform( - self, - transform: &Transform, - stores: &Stores, - ) -> Self::Transformed; - - /// Translate the object - #[must_use] - fn translate( - self, - offset: impl Into>, - stores: &Stores, - ) -> Self::Transformed { - self.transform(&Transform::translation(offset), stores) - } - - /// Rotate the object - #[must_use] - fn rotate( - self, - axis_angle: impl Into>, - stores: &Stores, - ) -> Self::Transformed { - self.transform(&Transform::rotation(axis_angle), stores) - } -} - -impl TransformObject for Curve { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - let surface = self.surface().transform(transform, stores); - let global = self.global_form().transform(transform, stores); - - // Don't need to transform `self.kind`, as that's in local form. - Curve::new(surface, self.path(), global) - } -} - -impl TransformObject for Cycle { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - Self::new( - self.surface().transform(transform, stores), - self.into_half_edges() - .map(|edge| edge.transform(transform, stores)), - ) - } -} - -impl TransformObject for Face { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - let exterior = self.exterior().clone().transform(transform, stores); - let interiors = self - .interiors() - .map(|cycle| cycle.clone().transform(transform, stores)); - - let color = self.color(); - - Face::from_exterior(exterior) - .with_interiors(interiors) - .with_color(color) - } -} - -impl TransformObject for Faces { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - let mut faces = Faces::new(); - faces.extend( - self.into_iter() - .map(|face| face.transform(transform, stores)), - ); - faces - } -} - -impl TransformObject for GlobalCurve { - type Transformed = Handle; - - fn transform(self, transform: &Transform, stores: &Stores) -> Handle { - stores.global_curves.insert(GlobalCurve::from_path( - self.path().transform(transform, stores), - )) - } -} - -impl TransformObject for GlobalEdge { - type Transformed = Self; - - fn transform( - self, - transform: &Transform, - stores: &Stores, - ) -> Self::Transformed { - let curve = self.curve().clone().transform(transform, stores); - let vertices = self - .vertices() - .map(|vertex| vertex.transform(transform, stores)); - - Self::new(curve, vertices) - } -} - -impl TransformObject for GlobalPath { - type Transformed = Self; - - fn transform(self, transform: &Transform, _: &Stores) -> Self { - match self { - Self::Circle(curve) => { - Self::Circle(transform.transform_circle(&curve)) - } - Self::Line(curve) => Self::Line(transform.transform_line(&curve)), - } - } -} - -impl TransformObject for GlobalVertex { - type Transformed = Self; - - fn transform(self, transform: &Transform, _: &Stores) -> Self { - let position = transform.transform_point(&self.position()); - Self::from_position(position) - } -} - -impl TransformObject for HalfEdge { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - let curve = self.curve().clone().transform(transform, stores); - let vertices = self - .vertices() - .clone() - .map(|vertex| vertex.transform(transform, stores)); - let global_form = - self.global_form().clone().transform(transform, stores); - - Self::new(curve, vertices, global_form) - } -} - -impl TransformObject for Shell { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - let faces = self - .into_faces() - .into_iter() - .map(|face| face.transform(transform, stores)); - Self::new().with_faces(faces) - } -} - -impl TransformObject for Sketch { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - let faces = self - .into_faces() - .into_iter() - .map(|face| face.transform(transform, stores)); - Self::new().with_faces(faces) - } -} - -impl TransformObject for Solid { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - let faces = self - .into_shells() - .map(|shell| shell.transform(transform, stores)); - Self::new().with_shells(faces) - } -} - -impl TransformObject for Surface { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - Self::new( - self.u().transform(transform, stores), - transform.transform_vector(&self.v()), - ) - } -} - -impl TransformObject for SurfaceVertex { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - Self::new( - self.position(), - self.surface().transform(transform, stores), - self.global_form().transform(transform, stores), - ) - } -} - -impl TransformObject for Vertex { - type Transformed = Self; - - fn transform(self, transform: &Transform, stores: &Stores) -> Self { - Self::new( - self.position(), - self.curve().clone().transform(transform, stores), - self.surface_form().transform(transform, stores), - self.global_form().transform(transform, stores), - ) - } -} diff --git a/crates/fj-kernel/src/algorithms/transform/curve.rs b/crates/fj-kernel/src/algorithms/transform/curve.rs new file mode 100644 index 000000000..dcba67312 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/curve.rs @@ -0,0 +1,28 @@ +use fj_math::Transform; + +use crate::{ + objects::{Curve, GlobalCurve}, + stores::{Handle, Stores}, +}; + +use super::TransformObject; + +impl TransformObject for Curve { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let surface = self.surface().transform(transform, stores); + let global_form = + self.global_form().clone().transform(transform, stores); + + // Don't need to transform `self.path`, as that's defined in surface + // coordinates, and thus transforming `surface` takes care of it. + Self::new(surface, self.path(), global_form) + } +} + +impl TransformObject for Handle { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + stores.global_curves.insert(GlobalCurve::from_path( + self.path().transform(transform, stores), + )) + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/cycle.rs b/crates/fj-kernel/src/algorithms/transform/cycle.rs new file mode 100644 index 000000000..cfcd0975b --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/cycle.rs @@ -0,0 +1,15 @@ +use fj_math::Transform; + +use crate::{objects::Cycle, stores::Stores}; + +use super::TransformObject; + +impl TransformObject for Cycle { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + Self::new( + self.surface().transform(transform, stores), + self.into_half_edges() + .map(|edge| edge.transform(transform, stores)), + ) + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/edge.rs b/crates/fj-kernel/src/algorithms/transform/edge.rs new file mode 100644 index 000000000..a900f0791 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/edge.rs @@ -0,0 +1,33 @@ +use fj_math::Transform; + +use crate::{ + objects::{GlobalEdge, HalfEdge}, + stores::Stores, +}; + +use super::TransformObject; + +impl TransformObject for HalfEdge { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let curve = self.curve().clone().transform(transform, stores); + let vertices = self + .vertices() + .clone() + .map(|vertex| vertex.transform(transform, stores)); + let global_form = + self.global_form().clone().transform(transform, stores); + + Self::new(curve, vertices, global_form) + } +} + +impl TransformObject for GlobalEdge { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let curve = self.curve().clone().transform(transform, stores); + let vertices = self + .vertices() + .map(|vertex| vertex.transform(transform, stores)); + + Self::new(curve, vertices) + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/face.rs b/crates/fj-kernel/src/algorithms/transform/face.rs new file mode 100644 index 000000000..608dbd9b9 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/face.rs @@ -0,0 +1,34 @@ +use fj_math::Transform; + +use crate::{ + objects::{Face, Faces}, + stores::Stores, +}; + +use super::TransformObject; + +impl TransformObject for Face { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let exterior = self.exterior().clone().transform(transform, stores); + let interiors = self + .interiors() + .map(|cycle| cycle.clone().transform(transform, stores)); + + let color = self.color(); + + Face::from_exterior(exterior) + .with_interiors(interiors) + .with_color(color) + } +} + +impl TransformObject for Faces { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let mut faces = Faces::new(); + faces.extend( + self.into_iter() + .map(|face| face.transform(transform, stores)), + ); + faces + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/mod.rs b/crates/fj-kernel/src/algorithms/transform/mod.rs new file mode 100644 index 000000000..c8ce24269 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/mod.rs @@ -0,0 +1,47 @@ +//! API for transforming objects + +mod curve; +mod cycle; +mod edge; +mod face; +mod path; +mod shell; +mod sketch; +mod solid; +mod surface; +mod vertex; + +use fj_math::{Transform, Vector}; + +use crate::stores::Stores; + +/// Transform an object +/// +/// # Implementation Note +/// +/// So far, a general `transform` method is available, along some convenience +/// methods for more specific transformations. +/// +/// More convenience methods can be added as required. The only reason this +/// hasn't been done so far, is that no one has put in the work yet. +pub trait TransformObject: Sized { + /// Transform the object + #[must_use] + fn transform(self, transform: &Transform, stores: &Stores) -> Self; + + /// Translate the object + /// + /// Convenience wrapper around [`TransformObject::transform`]. + #[must_use] + fn translate(self, offset: impl Into>, stores: &Stores) -> Self { + self.transform(&Transform::translation(offset), stores) + } + + /// Rotate the object + /// + /// Convenience wrapper around [`TransformObject::transform`]. + #[must_use] + fn rotate(self, axis_angle: impl Into>, stores: &Stores) -> Self { + self.transform(&Transform::rotation(axis_angle), stores) + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/path.rs b/crates/fj-kernel/src/algorithms/transform/path.rs new file mode 100644 index 000000000..b4c998654 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/path.rs @@ -0,0 +1,16 @@ +use fj_math::Transform; + +use crate::{path::GlobalPath, stores::Stores}; + +use super::TransformObject; + +impl TransformObject for GlobalPath { + fn transform(self, transform: &Transform, _: &Stores) -> 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/transform/shell.rs b/crates/fj-kernel/src/algorithms/transform/shell.rs new file mode 100644 index 000000000..d2ebadad9 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/shell.rs @@ -0,0 +1,15 @@ +use fj_math::Transform; + +use crate::{objects::Shell, stores::Stores}; + +use super::TransformObject; + +impl TransformObject for Shell { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let faces = self + .into_faces() + .into_iter() + .map(|face| face.transform(transform, stores)); + Self::new().with_faces(faces) + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/sketch.rs b/crates/fj-kernel/src/algorithms/transform/sketch.rs new file mode 100644 index 000000000..4a48bb23d --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/sketch.rs @@ -0,0 +1,15 @@ +use fj_math::Transform; + +use crate::{objects::Sketch, stores::Stores}; + +use super::TransformObject; + +impl TransformObject for Sketch { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let faces = self + .into_faces() + .into_iter() + .map(|face| face.transform(transform, stores)); + Self::new().with_faces(faces) + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/solid.rs b/crates/fj-kernel/src/algorithms/transform/solid.rs new file mode 100644 index 000000000..558e3a1e0 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/solid.rs @@ -0,0 +1,14 @@ +use fj_math::Transform; + +use crate::{objects::Solid, stores::Stores}; + +use super::TransformObject; + +impl TransformObject for Solid { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let faces = self + .into_shells() + .map(|shell| shell.transform(transform, stores)); + Self::new().with_shells(faces) + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/surface.rs b/crates/fj-kernel/src/algorithms/transform/surface.rs new file mode 100644 index 000000000..823431256 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/surface.rs @@ -0,0 +1,14 @@ +use fj_math::Transform; + +use crate::{objects::Surface, stores::Stores}; + +use super::TransformObject; + +impl TransformObject for Surface { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + Self::new( + self.u().transform(transform, stores), + transform.transform_vector(&self.v()), + ) + } +} diff --git a/crates/fj-kernel/src/algorithms/transform/vertex.rs b/crates/fj-kernel/src/algorithms/transform/vertex.rs new file mode 100644 index 000000000..89d2507b6 --- /dev/null +++ b/crates/fj-kernel/src/algorithms/transform/vertex.rs @@ -0,0 +1,38 @@ +use fj_math::Transform; + +use crate::{ + objects::{GlobalVertex, SurfaceVertex, Vertex}, + stores::Stores, +}; + +use super::TransformObject; + +impl TransformObject for Vertex { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let curve = self.curve().clone().transform(transform, stores); + let surface_form = self.surface_form().transform(transform, stores); + let global_form = self.global_form().transform(transform, stores); + + // Don't need to transform `self.position`, as that is in curve + // coordinates and thus transforming the curve takes care of it. + Self::new(self.position(), curve, surface_form, global_form) + } +} + +impl TransformObject for SurfaceVertex { + fn transform(self, transform: &Transform, stores: &Stores) -> Self { + let surface = self.surface().transform(transform, stores); + let global_form = self.global_form().transform(transform, stores); + + // Don't need to transform `self.position`, as that is in surface + // coordinates and thus transforming the surface takes care of it. + Self::new(self.position(), surface, global_form) + } +} + +impl TransformObject for GlobalVertex { + fn transform(self, transform: &Transform, _: &Stores) -> Self { + let position = transform.transform_point(&self.position()); + Self::from_position(position) + } +}