diff --git a/crates/fj-kernel/src/algorithms/mod.rs b/crates/fj-kernel/src/algorithms/mod.rs index 559f22709..01928fca2 100644 --- a/crates/fj-kernel/src/algorithms/mod.rs +++ b/crates/fj-kernel/src/algorithms/mod.rs @@ -13,6 +13,6 @@ pub mod intersection; pub use self::{ approx::{CycleApprox, FaceApprox, InvalidTolerance, Tolerance}, sweep::sweep, - transform::transform_shape, + transform::transform, triangulation::triangulate, }; diff --git a/crates/fj-kernel/src/algorithms/sweep.rs b/crates/fj-kernel/src/algorithms/sweep.rs index 93e343f74..0ee491af3 100644 --- a/crates/fj-kernel/src/algorithms/sweep.rs +++ b/crates/fj-kernel/src/algorithms/sweep.rs @@ -8,7 +8,7 @@ use crate::{ shape::{LocalForm, Shape}, }; -use super::{CycleApprox, Tolerance}; +use super::{transform::transform_cycles, CycleApprox, Tolerance}; /// Create a solid by sweeping a sketch pub fn sweep( @@ -101,9 +101,8 @@ fn create_top_face( surface = surface.transform(&translation); - let mut tmp = Shape::new(); - exteriors = transform_cycle(&exteriors, &translation, &mut tmp); - interiors = transform_cycle(&interiors, &translation, &mut tmp); + exteriors = transform_cycles(&exteriors, &translation); + interiors = transform_cycles(&interiors, &translation); if is_sweep_along_negative_direction { surface = surface.reverse(); @@ -112,6 +111,7 @@ fn create_top_face( interiors = reverse_local_coordinates_in_cycle(&interiors); }; + let mut tmp = Shape::new(); let surface = tmp.insert(surface); let face = Face::new( @@ -149,84 +149,6 @@ fn reverse_local_coordinates_in_cycle(cycles: &CyclesInFace) -> CyclesInFace { CyclesInFace::new(cycles) } -fn transform_cycle( - cycles: &CyclesInFace, - transform: &Transform, - target: &mut Shape, -) -> CyclesInFace { - let cycles = cycles.as_local_form().map(|cycle| { - let edges_local = cycle - .local() - .edges - .iter() - .map(|edge| { - let curve_local = *edge.local().curve.local(); - let curve_canonical = target - .merge(edge.canonical().get().curve().transform(transform)); - - let vertices = edge.canonical().get().vertices.map(|vertex| { - let point = vertex.canonical().get().point; - let point = transform.transform_point(&point); - - let local = *vertex.local(); - let canonical = target.merge(Vertex { point }); - - LocalForm::new(local, canonical) - }); - - let edge_local = Edge { - curve: LocalForm::new(curve_local, curve_canonical.clone()), - vertices: vertices.clone(), - }; - let edge_canonical = target.merge(Edge { - curve: LocalForm::canonical_only(curve_canonical), - vertices, - }); - - LocalForm::new(edge_local, edge_canonical) - }) - .collect(); - let edges_canonical = cycle - .canonical() - .get() - .edges - .iter() - .map(|edge| { - let edge = edge.canonical().get(); - - let curve = { - let curve = edge.curve().transform(transform); - - let curve = target.merge(curve); - LocalForm::canonical_only(curve) - }; - let vertices = edge.vertices.map(|vertex| { - let point = vertex.canonical().get().point; - let point = transform.transform_point(&point); - - let local = *vertex.local(); - let canonical = target.merge(Vertex { point }); - - LocalForm::new(local, canonical) - }); - - let edge = target.merge(Edge { curve, vertices }); - LocalForm::canonical_only(edge) - }) - .collect(); - - let cycle_local = Cycle { edges: edges_local }; - - let cycle_canonical = target.merge(Cycle { - edges: edges_canonical, - }); - - LocalForm::new(cycle_local, cycle_canonical) - }); - - CyclesInFace::new(cycles) -} - fn create_non_continuous_side_face( path: Vector<3>, is_sweep_along_negative_direction: bool, diff --git a/crates/fj-kernel/src/algorithms/transform.rs b/crates/fj-kernel/src/algorithms/transform.rs index 3da41920b..bd148a9ae 100644 --- a/crates/fj-kernel/src/algorithms/transform.rs +++ b/crates/fj-kernel/src/algorithms/transform.rs @@ -1,30 +1,125 @@ use fj_math::Transform; use crate::{ - objects::{Curve, Face, Surface, Vertex}, - shape::Shape, + objects::{Cycle, CyclesInFace, Edge, Face, FaceBRep, Vertex}, + shape::{LocalForm, Shape}, }; -/// Transform the geometry of the shape -/// -/// Since the topological types refer to geometry, and don't contain any -/// geometry themselves, this transforms the whole shape. -pub fn transform_shape(shape: &mut Shape, transform: &Transform) { - shape - .update() - .update_all(|vertex: &mut Vertex| { - vertex.point = transform.transform_point(&vertex.point) - }) - .update_all(|curve: &mut Curve<3>| *curve = curve.transform(transform)) - .update_all(|surface: &mut Surface| { - *surface = surface.transform(transform) - }) - .update_all(|mut face: &mut Face| { - use std::ops::DerefMut as _; - if let Face::Triangles(triangles) = face.deref_mut() { - for (triangle, _) in triangles { - *triangle = transform.transform_triangle(triangle); - } +/// Transform a shape +pub fn transform(faces: &[Face], transform: &Transform) -> Vec { + let mut target = Vec::new(); + + for face in faces { + let face = match face { + Face::Face(face) => { + let mut tmp = Shape::new(); + let surface = face.surface.get().transform(transform); + let surface = tmp.insert(surface); + + let exteriors = transform_cycles(&face.exteriors, transform); + let interiors = transform_cycles(&face.interiors, transform); + + let color = face.color; + + Face::Face(FaceBRep { + surface, + exteriors, + interiors, + color, + }) } - }); + Face::Triangles(triangles) => { + let mut target = Vec::new(); + + for &(triangle, color) in triangles { + let triangle = transform.transform_triangle(&triangle); + target.push((triangle, color)); + } + + Face::Triangles(target) + } + }; + target.push(face); + } + + target +} + +pub fn transform_cycles( + cycles: &CyclesInFace, + transform: &Transform, +) -> CyclesInFace { + let mut tmp = Shape::new(); + + let cycles = cycles.as_local_form().map(|cycle| { + let edges_local = cycle + .local() + .edges + .iter() + .map(|edge| { + let curve_local = *edge.local().curve.local(); + let curve_canonical = tmp + .merge(edge.canonical().get().curve().transform(transform)); + + let vertices = edge.canonical().get().vertices.map(|vertex| { + let point = vertex.canonical().get().point; + let point = transform.transform_point(&point); + + let local = *vertex.local(); + let canonical = tmp.merge(Vertex { point }); + + LocalForm::new(local, canonical) + }); + + let edge_local = Edge { + curve: LocalForm::new(curve_local, curve_canonical.clone()), + vertices: vertices.clone(), + }; + let edge_canonical = tmp.merge(Edge { + curve: LocalForm::canonical_only(curve_canonical), + vertices, + }); + + LocalForm::new(edge_local, edge_canonical) + }) + .collect(); + let edges_canonical = cycle + .canonical() + .get() + .edges + .iter() + .map(|edge| { + let edge = edge.canonical().get(); + + let curve = { + let curve = edge.curve().transform(transform); + + let curve = tmp.merge(curve); + LocalForm::canonical_only(curve) + }; + let vertices = edge.vertices.map(|vertex| { + let point = vertex.canonical().get().point; + let point = transform.transform_point(&point); + + let local = *vertex.local(); + let canonical = tmp.merge(Vertex { point }); + + LocalForm::new(local, canonical) + }); + + let edge = tmp.merge(Edge { curve, vertices }); + LocalForm::canonical_only(edge) + }) + .collect(); + + let cycle_local = Cycle { edges: edges_local }; + + let cycle_canonical = tmp.merge(Cycle { + edges: edges_canonical, + }); + + LocalForm::new(cycle_local, cycle_canonical) + }); + + CyclesInFace::new(cycles) } diff --git a/crates/fj-operations/src/transform.rs b/crates/fj-operations/src/transform.rs index bfd1a207c..39eb98380 100644 --- a/crates/fj-operations/src/transform.rs +++ b/crates/fj-operations/src/transform.rs @@ -1,6 +1,7 @@ use fj_interop::debug::DebugInfo; use fj_kernel::{ - algorithms::{transform_shape, Tolerance}, + algorithms::{transform, Tolerance}, + iter::ObjectIters, shape::Shape, validation::{validate, Validated, ValidationConfig, ValidationError}, }; @@ -16,22 +17,27 @@ impl ToShape for fj::Transform { debug_info: &mut DebugInfo, ) -> Result, ValidationError> { let shape = self.shape.to_shape(config, tolerance, debug_info)?; - let mut shape = shape.into_inner(); + let shape = shape.into_inner(); - let transform = transform(self); + let shape = shape.face_iter().collect::>(); + let faces = transform(&shape, &make_transform(self)); - transform_shape(&mut shape, &transform); - let shape = validate(shape, config)?; + let mut target = Shape::new(); + for face in faces { + target.merge(face); + } + + let shape = validate(target, config)?; Ok(shape) } fn bounding_volume(&self) -> Aabb<3> { - transform(self).transform_aabb(&self.shape.bounding_volume()) + make_transform(self).transform_aabb(&self.shape.bounding_volume()) } } -fn transform(transform: &fj::Transform) -> Transform { +fn make_transform(transform: &fj::Transform) -> Transform { let axis = Vector::from(transform.axis).normalize(); Transform::translation(transform.offset) * Transform::rotation(axis * transform.angle.rad())