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

Simplify builder API #737

Merged
merged 13 commits into from
Jun 28, 2022
8 changes: 3 additions & 5 deletions crates/fj-kernel/src/algorithms/approx/edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,18 @@ mod test {
use crate::{
geometry,
objects::{Vertex, VerticesOfEdge},
shape::{LocalForm, Shape},
shape::LocalForm,
};

#[test]
fn approx_edge() {
let mut shape = Shape::new();

let a = Point::from([1., 2., 3.]);
let b = Point::from([2., 3., 5.]);
let c = Point::from([3., 5., 8.]);
let d = Point::from([5., 8., 13.]);

let v1 = Vertex::builder(&mut shape).build_from_point(a).get();
let v2 = Vertex::builder(&mut shape).build_from_point(d).get();
let v1 = Vertex::from_point(a);
let v2 = Vertex::from_point(d);

let vertices = VerticesOfEdge::from_vertices([
LocalForm::new(Point::from([0.]), v1),
Expand Down
7 changes: 2 additions & 5 deletions crates/fj-kernel/src/algorithms/approx/faces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ mod tests {
use crate::{
geometry,
objects::{Face, Surface},
shape::Shape,
};

use super::{CycleApprox, FaceApprox, Tolerance};
Expand All @@ -95,8 +94,6 @@ mod tests {

let tolerance = Tolerance::from_scalar(Scalar::ONE)?;

let mut shape = Shape::new();

let a = Point::from([0., 0.]);
let b = Point::from([3., 0.]);
let c = Point::from([3., 3.]);
Expand All @@ -107,7 +104,7 @@ mod tests {
let g = Point::from([2., 2.]);
let h = Point::from([1., 2.]);

let face = Face::builder(Surface::xy_plane(), &mut shape)
let face = Face::builder(Surface::xy_plane())
.with_exterior_polygon([a, b, c, d])
.with_interior_polygon([e, f, g, h])
.build();
Expand All @@ -130,7 +127,7 @@ mod tests {
let g = geometry::Point::new(g, g);
let h = geometry::Point::new(h, h);

let approx = FaceApprox::new(&face.get(), tolerance);
let approx = FaceApprox::new(&face, tolerance);
let expected = FaceApprox {
points: set![a, b, c, d, e, f, g, h],
exterior: CycleApprox {
Expand Down
12 changes: 3 additions & 9 deletions crates/fj-kernel/src/algorithms/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ mod tests {
algorithms::Tolerance,
iter::ObjectIters,
objects::{Face, Surface},
shape::Shape,
};

#[test]
Expand Down Expand Up @@ -362,12 +361,9 @@ mod tests {
) -> anyhow::Result<()> {
let tolerance = Tolerance::from_scalar(Scalar::ONE)?;

let mut shape = Shape::new();

let sketch = Face::builder(Surface::xy_plane(), &mut shape)
let sketch = Face::builder(Surface::xy_plane())
.with_exterior_polygon([[0., 0.], [1., 0.], [0., 1.]])
.build()
.get();
.build();

let solid =
super::sweep(vec![sketch], direction, tolerance, [255, 0, 0, 255]);
Expand All @@ -377,14 +373,12 @@ mod tests {
.map(|vertex| vertex.into())
.collect();

let mut shape = Shape::new();
let faces = expected_surfaces.into_iter().map(|surface| {
let surface = Surface::plane_from_points(surface);

Face::builder(surface, &mut shape)
Face::builder(surface)
.with_exterior_polygon(expected_vertices.clone())
.build()
.get()
});

for face in faces {
Expand Down
15 changes: 4 additions & 11 deletions crates/fj-kernel/src/algorithms/triangulation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,18 @@ mod tests {
use crate::{
algorithms::Tolerance,
objects::{Face, Surface},
shape::Shape,
};

#[test]
fn simple() -> anyhow::Result<()> {
let mut shape = Shape::new();

let a = [0., 0.];
let b = [2., 0.];
let c = [2., 2.];
let d = [0., 1.];

let face = Face::builder(Surface::xy_plane(), &mut shape)
let face = Face::builder(Surface::xy_plane())
.with_exterior_polygon([a, b, c, d])
.build()
.get();
.build();

let a = Point::from(a).to_xyz();
let b = Point::from(b).to_xyz();
Expand All @@ -123,8 +119,6 @@ mod tests {

#[test]
fn simple_hole() -> anyhow::Result<()> {
let mut shape = Shape::new();

let a = [0., 0.];
let b = [4., 0.];
let c = [4., 4.];
Expand All @@ -135,11 +129,10 @@ mod tests {
let g = [3., 3.];
let h = [1., 2.];

let face = Face::builder(Surface::xy_plane(), &mut shape)
let face = Face::builder(Surface::xy_plane())
.with_exterior_polygon([a, b, c, d])
.with_interior_polygon([e, f, g, h])
.build()
.get();
.build();

let triangles = triangulate(face)?;

Expand Down
187 changes: 9 additions & 178 deletions crates/fj-kernel/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,191 +1,26 @@
//! Convenient API to build objects

use fj_math::{Circle, Line, Point, Scalar, Vector};
use fj_math::Point;

use crate::{
objects::{Curve, Cycle, Edge, Face, Surface, Vertex, VerticesOfEdge},
shape::{Handle, LocalForm, Shape},
};

/// API for building a [`Vertex`]
#[must_use]
pub struct VertexBuilder<'r> {
shape: &'r mut Shape,
}

impl<'r> VertexBuilder<'r> {
/// Construct a new instance of `VertexBuilder`
pub fn new(shape: &'r mut Shape) -> Self {
Self { shape }
}

/// Build a [`Vertex`] from a point
///
/// If an identical point or vertex are already part of the shape, those
/// objects are re-used.
pub fn build_from_point(
self,
point: impl Into<Point<3>>,
) -> Handle<Vertex> {
let point = point.into();
self.shape.get_handle_or_insert(Vertex { point })
}
}

/// API for building an [`Edge`]
#[must_use]
pub struct EdgeBuilder<'r> {
shape: &'r mut Shape,
}

impl<'r> EdgeBuilder<'r> {
/// Construct a new instance of `EdgeBuilder`
pub fn new(shape: &'r mut Shape) -> Self {
Self { shape }
}

/// Build a circle from a radius
pub fn build_circle(self, radius: Scalar) -> LocalForm<Edge<2>, Edge<3>> {
let curve_local = Curve::Circle(Circle {
center: Point::origin(),
a: Vector::from([radius, Scalar::ZERO]),
b: Vector::from([Scalar::ZERO, radius]),
});
let curve_canonical = Curve::Circle(Circle {
center: Point::origin(),
a: Vector::from([radius, Scalar::ZERO, Scalar::ZERO]),
b: Vector::from([Scalar::ZERO, radius, Scalar::ZERO]),
});

let edge_local = Edge {
curve: LocalForm::new(curve_local, curve_canonical),
vertices: VerticesOfEdge::none(),
};
let edge_canonical = Edge {
curve: LocalForm::canonical_only(curve_canonical),
vertices: VerticesOfEdge::none(),
};

LocalForm::new(edge_local, edge_canonical)
}

/// Build a line segment from two points
pub fn build_line_segment_from_points(
self,
vertices: [impl Into<Point<3>>; 2],
) -> Handle<Edge<3>> {
let vertices = vertices.map(|point| {
let point = point.into();
Vertex { point }
});

self.build_line_segment_from_vertices(vertices)
}

/// Build a line segment from two vertices
pub fn build_line_segment_from_vertices(
self,
[a, b]: [Vertex; 2],
) -> Handle<Edge<3>> {
let curve = {
let points = [a, b].map(|vertex| vertex.point);
Curve::Line(Line::from_points(points))
};

let vertices = [
LocalForm::new(Point::from([0.]), a),
LocalForm::new(Point::from([1.]), b),
];

self.shape.get_handle_or_insert(Edge {
curve: LocalForm::canonical_only(curve),
vertices: VerticesOfEdge::from_vertices(vertices),
})
}
}

/// API for building a [`Cycle`]
#[must_use]
pub struct CycleBuilder<'r> {
surface: Surface,
shape: &'r mut Shape,
}

impl<'r> CycleBuilder<'r> {
/// Construct a new instance of `CycleBuilder`
pub fn new(surface: Surface, shape: &'r mut Shape) -> Self {
Self { surface, shape }
}

/// Build a polygon from a list of points
pub fn build_polygon(
self,
points: impl IntoIterator<Item = impl Into<Point<2>>>,
) -> LocalForm<Cycle<2>, Cycle<3>> {
let mut points: Vec<_> = points.into_iter().map(Into::into).collect();

// A polygon is closed, so we need to add the first point at the end
// again, for the next step.
if let Some(point) = points.first().cloned() {
points.push(point);
}

let mut edges = Vec::new();
for points in points.windows(2) {
// Can't panic, as we passed `2` to `windows`.
//
// Can be cleaned up, once `array_windows` is stable.
let points = [points[0], points[1]];

let points_canonical = points
.map(|point| self.surface.point_from_surface_coords(point));
let edge_canonical = Edge::builder(self.shape)
.build_line_segment_from_points(points_canonical)
.get();

let edge_local = Edge {
curve: LocalForm::new(
Curve::Line(Line::from_points(points)),
edge_canonical.curve.canonical(),
),
vertices: edge_canonical.vertices.clone(),
};

edges.push(LocalForm::new(edge_local, edge_canonical));
}

let local = Cycle {
edges: edges.clone(),
};

let edges_canonical = edges.into_iter().map(|edge| edge.canonical());
let canonical = Cycle::new(edges_canonical);

LocalForm::new(local, canonical)
}
}
use crate::objects::{Cycle, Face, Surface};

/// API for building a [`Face`]
#[must_use]
pub struct FaceBuilder<'r> {
pub struct FaceBuilder {
surface: Surface,
exterior: Option<Vec<Point<2>>>,
interiors: Vec<Vec<Point<2>>>,
color: Option<[u8; 4]>,

shape: &'r mut Shape,
}

impl<'r> FaceBuilder<'r> {
impl FaceBuilder {
/// Construct a new instance of `FaceBuilder`
pub fn new(surface: Surface, shape: &'r mut Shape) -> Self {
pub fn new(surface: Surface) -> Self {
Self {
surface,
exterior: None,
interiors: Vec::new(),
color: None,

shape,
}
}

Expand Down Expand Up @@ -222,27 +57,23 @@ impl<'r> FaceBuilder<'r> {
}

/// Build the face
pub fn build(self) -> Handle<Face> {
pub fn build(self) -> Face {
let surface = self.surface;

let mut exteriors = Vec::new();
if let Some(points) = self.exterior {
let cycle =
Cycle::builder(self.surface, self.shape).build_polygon(points);
let cycle = Cycle::polygon_from_points(&self.surface, points);
exteriors.push(cycle);
}

let mut interiors = Vec::new();
for points in self.interiors {
let cycle =
Cycle::builder(self.surface, self.shape).build_polygon(points);
let cycle = Cycle::polygon_from_points(&self.surface, points);
interiors.push(cycle);
}

let color = self.color.unwrap_or([255, 0, 0, 255]);

self.shape.get_handle_or_insert(Face::new(
surface, exteriors, interiors, color,
))
Face::new(surface, exteriors, interiors, color)
}
}
Loading