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

Add new builder API #866

Merged
merged 10 commits into from
Jul 22, 2022
14 changes: 4 additions & 10 deletions crates/fj-kernel/src/algorithms/approx/faces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ mod tests {

use crate::{
local::Local,
objects::{Cycle, Face, Surface},
objects::{Face, Surface},
};

use super::{CycleApprox, FaceApprox, Tolerance};
Expand All @@ -107,15 +107,9 @@ mod tests {
let h = Point::from([1., 2.]);

let surface = Surface::xy_plane();
let face = Face::new(surface)
.with_exteriors([Cycle::polygon_from_points(
&surface,
[a, b, c, d],
)])
.with_interiors([Cycle::polygon_from_points(
&surface,
[e, f, g, h],
)]);
let face = Face::build(surface)
.polygon_from_points([a, b, c, d])
.with_hole([e, f, g, h]);

let a = Local::new(a, a.to_xyz());
let b = Local::new(b, b.to_xyz());
Expand Down
8 changes: 4 additions & 4 deletions crates/fj-kernel/src/algorithms/intersection/curve_face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ pub type CurveFaceIntersection = [Scalar; 2];
mod tests {
use fj_math::{Line, Point, Vector};

use crate::objects::{Curve, Cycle, Face, Surface};
use crate::objects::{Curve, Face, Surface};

use super::CurveFaceIntersectionList;

Expand All @@ -211,9 +211,9 @@ mod tests {
];

let surface = Surface::xy_plane();
let face = Face::new(surface)
.with_exteriors([Cycle::polygon_from_points(&surface, exterior)])
.with_interiors([Cycle::polygon_from_points(&surface, interior)]);
let face = Face::build(surface)
.polygon_from_points(exterior)
.with_hole(interior);

let expected =
CurveFaceIntersectionList::from_intervals([[1., 2.], [4., 5.]]);
Expand Down
20 changes: 9 additions & 11 deletions crates/fj-kernel/src/algorithms/reverse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,25 +60,23 @@ fn reverse_local_coordinates_in_cycle<'r>(
mod tests {
use pretty_assertions::assert_eq;

use crate::objects::{Cycle, Face, Surface};
use crate::objects::{Face, Surface};

#[test]
fn reverse_face() {
let surface = Surface::xy_plane();
let original =
Face::new(surface).with_exteriors([Cycle::polygon_from_points(
&surface,
[[0., 0.], [1., 0.], [0., 1.]],
)]);
let original = Face::build(surface).polygon_from_points([
[0., 0.],
[1., 0.],
[0., 1.],
]);

let reversed = super::reverse_face(&original);

let surface = Surface::xy_plane().reverse();
let expected =
Face::new(surface).with_exteriors([Cycle::polygon_from_points(
&surface,
[[0., 0.], [1., 0.], [0., -1.]],
)]);
let expected = Face::build(surface)
.polygon_from_points([[0., 0.], [1., 0.], [0., -1.]])
.into_face();

assert_eq!(expected, reversed);
}
Expand Down
19 changes: 9 additions & 10 deletions crates/fj-kernel/src/algorithms/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ mod tests {
use crate::{
algorithms::Tolerance,
iter::ObjectIters,
objects::{Cycle, Face, Sketch, Surface},
objects::{Face, Sketch, Surface},
};

#[test]
Expand Down Expand Up @@ -295,11 +295,11 @@ mod tests {
let tolerance = Tolerance::from_scalar(Scalar::ONE)?;

let surface = Surface::xy_plane();
let face =
Face::new(surface).with_exteriors([Cycle::polygon_from_points(
&surface,
[[0., 0.], [1., 0.], [0., 1.]],
)]);
let face = Face::build(surface).polygon_from_points([
[0., 0.],
[1., 0.],
[0., 1.],
]);
let sketch = Sketch::from_faces([face]);

let solid =
Expand All @@ -313,10 +313,9 @@ mod tests {
let faces = expected_surfaces.into_iter().map(|surface| {
let surface = Surface::plane_from_points(surface);

Face::new(surface).with_exteriors([Cycle::polygon_from_points(
&surface,
expected_vertices.clone(),
)])
Face::build(surface)
.polygon_from_points(expected_vertices.clone())
.into_face()
});

for face in faces {
Expand Down
34 changes: 12 additions & 22 deletions crates/fj-kernel/src/algorithms/triangulate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ mod tests {

use crate::{
algorithms::Tolerance,
objects::{Cycle, Face, Surface},
objects::{Face, Surface},
};

#[test]
Expand All @@ -78,11 +78,7 @@ mod tests {
let d = [0., 1.];

let surface = Surface::xy_plane();
let face =
Face::new(surface).with_exteriors([Cycle::polygon_from_points(
&surface,
[a, b, c, d],
)]);
let face = Face::build(surface).polygon_from_points([a, b, c, d]);

let a = Point::from(a).to_xyz();
let b = Point::from(b).to_xyz();
Expand Down Expand Up @@ -112,15 +108,9 @@ mod tests {
let h = [1., 2.];

let surface = Surface::xy_plane();
let face = Face::new(surface)
.with_exteriors([Cycle::polygon_from_points(
&surface,
[a, b, c, d],
)])
.with_interiors([Cycle::polygon_from_points(
&surface,
[e, f, g, h],
)]);
let face = Face::build(surface)
.polygon_from_points([a, b, c, d])
.with_hole([e, f, g, h]);

let triangles = triangulate(face)?;

Expand Down Expand Up @@ -168,11 +158,7 @@ mod tests {
let e = Point::from([0., 0.8]);

let surface = Surface::xy_plane();
let face =
Face::new(surface).with_exteriors([Cycle::polygon_from_points(
&surface,
[a, b, c, d, e],
)]);
let face = Face::build(surface).polygon_from_points([a, b, c, d, e]);

let triangles = triangulate(face)?;

Expand All @@ -191,10 +177,14 @@ mod tests {
Ok(())
}

fn triangulate(face: Face) -> anyhow::Result<Mesh<Point<3>>> {
fn triangulate(face: impl Into<Face>) -> anyhow::Result<Mesh<Point<3>>> {
let tolerance = Tolerance::from_scalar(Scalar::ONE)?;

let mut debug_info = DebugInfo::new();
Ok(super::triangulate(vec![face], tolerance, &mut debug_info))
Ok(super::triangulate(
vec![face.into()],
tolerance,
&mut debug_info,
))
}
}
45 changes: 45 additions & 0 deletions crates/fj-kernel/src/builder/cycle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use fj_math::Point;

use crate::objects::{Cycle, Edge, Surface};

/// API for building a [`Cycle`]
pub struct CycleBuilder {
surface: Surface,
}

impl CycleBuilder {
/// Construct an instance of `CycleBuilder`
///
/// Also see [`Cycle::build`].
pub fn new(surface: Surface) -> Self {
Self { surface }
}

/// Create a polygon from a list of points
pub fn polygon_from_points(
&self,
points: impl IntoIterator<Item = impl Into<Point<2>>>,
) -> Cycle {
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]];

edges.push(
Edge::build().line_segment_from_points(&self.surface, points),
);
}

Cycle::new().with_edges(edges)
}
}
64 changes: 64 additions & 0 deletions crates/fj-kernel/src/builder/edge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use fj_math::{Circle, Line, Point, Scalar, Vector};

use crate::{
local::Local,
objects::{Curve, Edge, GlobalVertex, Surface, Vertex, VerticesOfEdge},
};

/// API for building an [`Edge`]
pub struct EdgeBuilder;

impl EdgeBuilder {
/// Create a circle from the given radius
pub fn circle_from_radius(&self, radius: Scalar) -> Edge {
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]),
});

Edge::new(
Local::new(curve_local, curve_canonical),
VerticesOfEdge::none(),
)
}

/// Create a line segment from two points
pub fn line_segment_from_points(
&self,
surface: &Surface,
points: [impl Into<Point<2>>; 2],
) -> Edge {
let points = points.map(Into::into);

let global_vertices = points.map(|position| {
let position = surface.point_from_surface_coords(position);
GlobalVertex::from_position(position)
});

let curve_local = Curve::Line(Line::from_points(points));
let curve_canonical = {
let points =
global_vertices.map(|global_vertex| global_vertex.position());
Curve::Line(Line::from_points(points))
};

let vertices = {
let [a, b] = global_vertices;
[
Vertex::new(Point::from([0.]), a),
Vertex::new(Point::from([1.]), b),
]
};

Edge::new(
Local::new(curve_local, curve_canonical),
VerticesOfEdge::from_vertices(vertices),
)
}
}
72 changes: 72 additions & 0 deletions crates/fj-kernel/src/builder/face.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use std::ops::Deref;

use fj_math::Point;

use crate::objects::{Cycle, Face, Surface};

/// API for building a [`Face`]
pub struct FaceBuilder {
surface: Surface,
}

impl FaceBuilder {
/// Construct an instance of `FaceBuilder`
///
/// Also see [`Face::build`].
pub fn new(surface: Surface) -> Self {
Self { surface }
}

/// Construct a polygon from a list of points
pub fn polygon_from_points(
&self,
points: impl IntoIterator<Item = impl Into<Point<2>>>,
) -> FacePolygon {
let face = Face::new(self.surface)
.with_exteriors([
Cycle::build(self.surface).polygon_from_points(points)
]);

FacePolygon { face }
}
}

/// A polygon
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct FacePolygon {
face: Face,
}

impl FacePolygon {
/// Add a hole to the polygon
pub fn with_hole(
mut self,
points: impl IntoIterator<Item = impl Into<Point<2>>>,
) -> Self {
let surface = *self.face.surface();
self.face = self.face.with_interiors([
Cycle::build(surface).polygon_from_points(points)
]);

self
}

/// Consume the `Polygon` and return the [`Face`] it wraps
pub fn into_face(self) -> Face {
self.face
}
}

impl From<FacePolygon> for Face {
fn from(polygon: FacePolygon) -> Self {
polygon.into_face()
}
}

impl Deref for FacePolygon {
type Target = Face;

fn deref(&self) -> &Self::Target {
&self.face
}
}
13 changes: 13 additions & 0 deletions crates/fj-kernel/src/builder/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! API for building objects

mod cycle;
mod edge;
mod face;
mod solid;

pub use self::{
cycle::CycleBuilder,
edge::EdgeBuilder,
face::{FaceBuilder, FacePolygon},
solid::SolidBuilder,
};
Loading