Skip to content

Commit

Permalink
Merge pull request #1502 from hannobraun/builder
Browse files Browse the repository at this point in the history
Add mechanisms to make builder API more convenient
  • Loading branch information
hannobraun authored Jan 11, 2023
2 parents d8a6dad + 5d98422 commit cdaceb9
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 25 deletions.
42 changes: 17 additions & 25 deletions crates/fj-kernel/src/builder/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
partial::{Partial, PartialCycle},
};

use super::HalfEdgeBuilder;
use super::{HalfEdgeBuilder, ObjectArgument};

/// Builder API for [`PartialCycle`]
pub trait CycleBuilder {
Expand Down Expand Up @@ -35,10 +35,13 @@ pub trait CycleBuilder {
) -> Partial<HalfEdge>;

/// Update cycle as a polygon from the provided points
fn update_as_polygon_from_points(
fn update_as_polygon_from_points<O, P>(
&mut self,
points: impl IntoIterator<Item = impl Into<Point<2>>>,
) -> Vec<Partial<HalfEdge>>;
points: O,
) -> O::ReturnValue<Partial<HalfEdge>>
where
O: ObjectArgument<P>,
P: Into<Point<2>>;

/// Update cycle as a polygon
///
Expand Down Expand Up @@ -124,19 +127,17 @@ impl CycleBuilder for PartialCycle {
half_edge
}

fn update_as_polygon_from_points(
fn update_as_polygon_from_points<O, P>(
&mut self,
points: impl IntoIterator<Item = impl Into<Point<2>>>,
) -> Vec<Partial<HalfEdge>> {
let mut half_edges = Vec::new();

for point in points {
let half_edge = self.add_half_edge_from_point_to_start(point);
half_edges.push(half_edge);
}

points: O,
) -> O::ReturnValue<Partial<HalfEdge>>
where
O: ObjectArgument<P>,
P: Into<Point<2>>,
{
let half_edges =
points.map(|point| self.add_half_edge_from_point_to_start(point));
self.update_as_polygon();

half_edges
}

Expand All @@ -154,15 +155,6 @@ impl CycleBuilder for PartialCycle {
.surface
.write()
.update_as_plane_from_points(points_global);
let mut edges = self.update_as_polygon_from_points(points_surface);

// None of the following should panic, as we just created a polygon from
// three points, so we should have exactly three edges.
let c = edges.pop().unwrap();
let b = edges.pop().unwrap();
let a = edges.pop().unwrap();
assert!(edges.pop().is_none());

[a, b, c]
self.update_as_polygon_from_points(points_surface)
}
}
53 changes: 53 additions & 0 deletions crates/fj-kernel/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,56 @@ pub use self::{
surface::SurfaceBuilder,
vertex::{GlobalVertexBuilder, SurfaceVertexBuilder, VertexBuilder},
};

/// Pass objects to a builder method
///
/// Many builder methods receive objects as arguments, and many builder
/// arguments return objects back, based on their input. In the general case,
/// the number of objects passed and returned is usually arbitrary, but many
/// callers pass a specific number of objects, and expect the same number of
/// objects back.
///
/// This trait can be used to do exactly that. It is implemented for `Vec` and
/// arrays. When passing a `Vec`, a `Vec` is returned. When passing an array, an
/// array of the same size is returned.
pub trait ObjectArgument<T>: IntoIterator<Item = T> {
/// The value returned, if the implementing type is passed on an argument
///
/// The return value has the same length as the implementing type, but it is
/// not necessarily of the same type. For this reason, this associated type
/// is generic.
type ReturnValue<R>;

/// Create a return value by mapping the implementing type
fn map<F, R>(self, f: F) -> Self::ReturnValue<R>
where
F: FnMut(T) -> R;
}

impl<T> ObjectArgument<T> for Vec<T> {
type ReturnValue<R> = Vec<R>;

fn map<F, R>(self, mut f: F) -> Self::ReturnValue<R>
where
F: FnMut(T) -> R,
{
let mut ret = Vec::new();

for item in self {
ret.push(f(item));
}

ret
}
}

impl<T, const N: usize> ObjectArgument<T> for [T; N] {
type ReturnValue<R> = [R; N];

fn map<F, R>(self, f: F) -> Self::ReturnValue<R>
where
F: FnMut(T) -> R,
{
self.map(f)
}
}

0 comments on commit cdaceb9

Please sign in to comment.