Merge pull request #737 from hannobraun/builder

Simplify builder API
This commit is contained in:
Hanno Braun 2022-06-28 18:58:34 +02:00 committed by GitHub
commit 74d745c644
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 154 additions and 286 deletions

View File

@ -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),

View File

@ -84,7 +84,6 @@ mod tests {
use crate::{
geometry,
objects::{Face, Surface},
shape::Shape,
};
use super::{CycleApprox, FaceApprox, Tolerance};
@ -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.]);
@ -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();
@ -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 {

View File

@ -273,7 +273,6 @@ mod tests {
algorithms::Tolerance,
iter::ObjectIters,
objects::{Face, Surface},
shape::Shape,
};
#[test]
@ -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]);
@ -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 {

View File

@ -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();
@ -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.];
@ -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)?;

View File

@ -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,
}
}
@ -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)
}
}

View File

@ -439,10 +439,7 @@ impl<T> Iterator for Iter<T> {
#[cfg(test)]
mod tests {
use crate::{
objects::{Curve, Cycle, Edge, Face, Surface, Vertex},
shape::Shape,
};
use crate::objects::{Curve, Cycle, Edge, Face, Surface, Vertex};
use super::ObjectIters as _;
@ -460,10 +457,11 @@ mod tests {
#[test]
fn cycle() {
let mut shape = Shape::new();
let cycle = Cycle::builder(Surface::xy_plane(), &mut shape)
.build_polygon([[0., 0.], [1., 0.], [0., 1.]])
.canonical();
let cycle = Cycle::polygon_from_points(
&Surface::xy_plane(),
[[0., 0.], [1., 0.], [0., 1.]],
)
.canonical();
assert_eq!(3, cycle.curve_iter().count());
assert_eq!(1, cycle.cycle_iter().count());
@ -475,10 +473,7 @@ mod tests {
#[test]
fn edge() {
let mut shape = Shape::new();
let edge = Edge::builder(&mut shape)
.build_line_segment_from_points([[0., 0., 0.], [1., 0., 0.]])
.get();
let edge = Edge::line_segment_from_points([[0., 0., 0.], [1., 0., 0.]]);
assert_eq!(1, edge.curve_iter().count());
assert_eq!(0, edge.cycle_iter().count());
@ -490,11 +485,9 @@ mod tests {
#[test]
fn face() {
let mut shape = Shape::new();
let face = Face::builder(Surface::xy_plane(), &mut shape)
let face = Face::builder(Surface::xy_plane())
.with_exterior_polygon([[0., 0.], [1., 0.], [0., 1.]])
.build()
.get();
.build();
assert_eq!(3, face.curve_iter().count());
assert_eq!(1, face.cycle_iter().count());
@ -518,10 +511,7 @@ mod tests {
#[test]
fn vertex() {
let mut shape = Shape::new();
let vertex = Vertex::builder(&mut shape)
.build_from_point([0., 0., 0.])
.get();
let vertex = Vertex::from_point([0., 0., 0.]);
assert_eq!(0, vertex.curve_iter().count());
assert_eq!(0, vertex.cycle_iter().count());

View File

@ -1,9 +1,8 @@
use crate::{
builder::CycleBuilder,
shape::{LocalForm, Shape},
};
use fj_math::{Line, Point};
use super::{Edge, Surface};
use crate::shape::LocalForm;
use super::{Curve, Edge, Surface};
/// A cycle of connected edges
///
@ -29,9 +28,50 @@ impl Cycle<3> {
Self { edges }
}
/// Build a cycle using the [`CycleBuilder`] API
pub fn builder(surface: Surface, shape: &mut Shape) -> CycleBuilder {
CycleBuilder::new(surface, shape)
/// Create a polygon from a list of points
pub fn polygon_from_points(
surface: &Surface,
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| surface.point_from_surface_coords(point));
let edge_canonical =
Edge::line_segment_from_points(points_canonical);
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)
}
/// Access the edges that this cycle refers to

View File

@ -1,11 +1,8 @@
use std::fmt;
use fj_math::Point;
use fj_math::{Circle, Line, Point, Scalar, Vector};
use crate::{
builder::EdgeBuilder,
shape::{LocalForm, Shape},
};
use crate::shape::LocalForm;
use super::{Curve, Vertex};
@ -53,10 +50,62 @@ impl<const D: usize> Edge<D> {
}
}
impl Edge<2> {
/// Create a circle from the given radius
pub fn circle_from_radius(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)
}
}
impl Edge<3> {
/// Build an edge using the [`EdgeBuilder`] API
pub fn builder(shape: &mut Shape) -> EdgeBuilder {
EdgeBuilder::new(shape)
/// Create a line segment from two points
pub fn line_segment_from_points(
vertices: [impl Into<Point<3>>; 2],
) -> Self {
let vertices = vertices.map(|point| {
let point = point.into();
Vertex { point }
});
Self::line_segment_from_vertices(vertices)
}
/// Create a line segment from two vertices
pub fn line_segment_from_vertices([a, b]: [Vertex; 2]) -> Self {
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 {
curve: LocalForm::canonical_only(curve),
vertices: VerticesOfEdge::from_vertices(vertices),
}
}
}

View File

@ -3,10 +3,7 @@ use std::hash::{Hash, Hasher};
use fj_interop::mesh::Color;
use fj_math::Triangle;
use crate::{
builder::FaceBuilder,
shape::{LocalForm, Shape},
};
use crate::{builder::FaceBuilder, shape::LocalForm};
use super::{Cycle, Surface};
@ -57,8 +54,8 @@ impl Face {
})
}
/// Build a face using the [`FaceBuilder`] API
pub fn builder(surface: Surface, shape: &mut Shape) -> FaceBuilder {
FaceBuilder::new(surface, shape)
pub fn builder(surface: Surface) -> FaceBuilder {
FaceBuilder::new(surface)
}
/// Access the boundary representation of the face

View File

@ -2,8 +2,6 @@ use std::hash::Hash;
use fj_math::Point;
use crate::{builder::VertexBuilder, shape::Shape};
/// A vertex
///
/// This struct exists to distinguish between vertices and points at the type
@ -27,8 +25,9 @@ pub struct Vertex {
}
impl Vertex {
/// Build a vertex using the [`VertexBuilder`] API
pub fn builder(shape: &mut Shape) -> VertexBuilder {
VertexBuilder::new(shape)
/// Construct a `Vertex` from a point
pub fn from_point(point: impl Into<Point<3>>) -> Self {
let point = point.into();
Self { point }
}
}

View File

@ -273,36 +273,26 @@ mod tests {
#[test]
fn structural_cycle() {
let mut shape = Shape::new();
let mut other = Shape::new();
// Trying to refer to edge that is not from the same shape. Should fail.
let edge = Edge::builder(&mut other)
.build_line_segment_from_points([[0., 0., 0.], [1., 0., 0.]])
.get();
let edge = Edge::line_segment_from_points([[0., 0., 0.], [1., 0., 0.]]);
shape.insert(Cycle::new(vec![edge.clone()]));
let err =
validate(shape.clone(), &ValidationConfig::default()).unwrap_err();
assert!(err.missing_edge(&edge));
// Referring to edge that *is* from the same shape. Should work.
let edge = Edge::builder(&mut shape)
.build_line_segment_from_points([[0., 0., 0.], [1., 0., 0.]])
.get();
let edge = Edge::line_segment_from_points([[0., 0., 0.], [1., 0., 0.]]);
shape.insert(Cycle::new(vec![edge]));
}
#[test]
fn structural_edge() {
let mut shape = Shape::new();
let mut other = Shape::new();
let curve = Curve::x_axis();
let a = Vertex::builder(&mut other)
.build_from_point([1., 0., 0.])
.get();
let b = Vertex::builder(&mut other)
.build_from_point([2., 0., 0.])
.get();
let a = Vertex::from_point([1., 0., 0.]);
let b = Vertex::from_point([2., 0., 0.]);
let a = LocalForm::new(Point::from([1.]), a);
let b = LocalForm::new(Point::from([2.]), b);
@ -319,12 +309,8 @@ mod tests {
assert!(err.missing_vertex(&b.canonical()));
let curve = Curve::x_axis();
let a = Vertex::builder(&mut shape)
.build_from_point([1., 0., 0.])
.get();
let b = Vertex::builder(&mut shape)
.build_from_point([2., 0., 0.])
.get();
let a = Vertex::from_point([1., 0., 0.]);
let b = Vertex::from_point([2., 0., 0.]);
let a = LocalForm::new(Point::from([1.]), a);
let b = LocalForm::new(Point::from([2.]), b);
@ -339,12 +325,11 @@ mod tests {
#[test]
fn structural_face() {
let mut shape = Shape::new();
let mut other = Shape::new();
let triangle = [[0., 0.], [1., 0.], [0., 1.]];
let surface = Surface::xy_plane();
let cycle = Cycle::builder(surface, &mut other).build_polygon(triangle);
let cycle = Cycle::polygon_from_points(&surface, triangle);
// Nothing has been added to `shape`. Should fail.
shape.insert(Face::new(
@ -359,7 +344,7 @@ mod tests {
assert!(err.missing_cycle(&cycle.canonical()));
let surface = Surface::xy_plane();
let cycle = Cycle::builder(surface, &mut shape).build_polygon(triangle);
let cycle = Cycle::polygon_from_points(&surface, triangle);
// Everything has been added to `shape` now. Should work!
shape.insert(Face::new(

View File

@ -21,8 +21,7 @@ impl ToShape for fj::Circle {
// Circles have just a single round edge with no vertices. So none need
// to be added here.
let edge = Edge::builder(&mut tmp)
.build_circle(Scalar::from_f64(self.radius()));
let edge = Edge::circle_from_radius(Scalar::from_f64(self.radius()));
let cycle_local = Cycle {
edges: vec![edge.clone()],

View File

@ -2,7 +2,6 @@ use fj_interop::debug::DebugInfo;
use fj_kernel::{
algorithms::Tolerance,
objects::{Face, Surface},
shape::Shape,
validation::{validate, Validated, ValidationConfig, ValidationError},
};
use fj_math::{Aabb, Point};
@ -16,16 +15,13 @@ impl ToShape for fj::Sketch {
_: Tolerance,
_: &mut DebugInfo,
) -> Result<Validated<Vec<Face>>, ValidationError> {
let mut tmp = Shape::new();
let surface = Surface::xy_plane();
let points = self.to_points().into_iter().map(Point::from);
let sketch = Face::builder(surface, &mut tmp)
let sketch = Face::builder(surface)
.with_exterior_polygon(points)
.with_color(self.color())
.build()
.get();
.build();
validate(vec![sketch], config)
}