Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decouple transform algorithm from Shape #733

Merged
merged 11 commits into from
Jun 28, 2022
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/algorithms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ pub mod intersection;
pub use self::{
approx::{CycleApprox, FaceApprox, InvalidTolerance, Tolerance},
sweep::sweep,
transform::transform_shape,
transform::transform,
triangulation::triangulate,
};
86 changes: 4 additions & 82 deletions crates/fj-kernel/src/algorithms/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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();
Expand All @@ -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(
Expand Down Expand Up @@ -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,
Expand Down
137 changes: 116 additions & 21 deletions crates/fj-kernel/src/algorithms/transform.rs
Original file line number Diff line number Diff line change
@@ -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<Face> {
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)
}
20 changes: 13 additions & 7 deletions crates/fj-operations/src/transform.rs
Original file line number Diff line number Diff line change
@@ -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},
};
Expand All @@ -16,22 +17,27 @@ impl ToShape for fj::Transform {
debug_info: &mut DebugInfo,
) -> Result<Validated<Shape>, 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::<Vec<_>>();
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())
Expand Down