Skip to content

Commit

Permalink
Merge pull request #1510 from hannobraun/builder
Browse files Browse the repository at this point in the history
Expand builder API
  • Loading branch information
hannobraun authored Jan 13, 2023
2 parents 257f636 + bdc5a2a commit 98d9c00
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 11 deletions.
26 changes: 26 additions & 0 deletions crates/fj-interop/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,32 @@ impl<T> ArrayExt<T, 2> for [T; 2] {
}
}

impl<T> ArrayExt<T, 3> for [T; 3] {
fn each_ref_ext(&self) -> [&T; 3] {
let [a, b, c] = self;
[a, b, c]
}

fn each_mut_ext(&mut self) -> [&mut T; 3] {
let [a, b, c] = self;
[a, b, c]
}

fn try_map_ext<F, U, E>(self, f: F) -> Result<[U; 3], E>
where
F: FnMut(T) -> Result<U, E>,
{
let [a, b, c] = self.map(f);
Ok([a?, b?, c?])
}

fn zip_ext<U>(self, rhs: [U; 3]) -> [(T, U); 3] {
let [a, b, c] = self;
let [q, r, s] = rhs;
[(a, q), (b, r), (c, s)]
}
}

impl<T> ArrayExt<T, 4> for [T; 4] {
fn each_ref_ext(&self) -> [&T; 4] {
let [a, b, c, d] = self;
Expand Down
6 changes: 3 additions & 3 deletions crates/fj-kernel/src/builder/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl CurveBuilder for PartialCurve {
radius: impl Into<Scalar>,
) -> SurfacePath {
let path = SurfacePath::circle_from_radius(radius);
self.path = Some(path);
self.path = Some(path.into());
path
}

Expand All @@ -70,7 +70,7 @@ impl CurveBuilder for PartialCurve {
radius: impl Into<Scalar>,
) -> SurfacePath {
let path = SurfacePath::circle_from_center_and_radius(center, radius);
self.path = Some(path);
self.path = Some(path.into());
path
}

Expand All @@ -79,7 +79,7 @@ impl CurveBuilder for PartialCurve {
points: [impl Into<Point<2>>; 2],
) -> SurfacePath {
let (path, _) = SurfacePath::line_from_points(points);
self.path = Some(path);
self.path = Some(path.into());
path
}
}
53 changes: 52 additions & 1 deletion crates/fj-kernel/src/builder/cycle.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use fj_interop::ext::ArrayExt;
use fj_math::Point;

use crate::{
Expand Down Expand Up @@ -34,6 +35,19 @@ pub trait CycleBuilder {
point: impl Into<Point<2>>,
) -> Partial<HalfEdge>;

/// Add a new half-edge that starts at the provided point
///
/// Opens the cycle between the last and first edge, updates the last edge
/// to go the provided point, and adds a new half-edge from the provided
/// point the the first edge.
///
/// If the cycle doesn't have any edges yet, the new edge connects to
/// itself, starting and ending at the provided point.
fn add_half_edge_from_global_point_to_start(
&mut self,
point: impl Into<Point<3>>,
) -> Partial<HalfEdge>;

/// Update cycle as a polygon from the provided points
fn update_as_polygon_from_points<O, P>(
&mut self,
Expand Down Expand Up @@ -127,6 +141,25 @@ impl CycleBuilder for PartialCycle {
half_edge
}

fn add_half_edge_from_global_point_to_start(
&mut self,
point: impl Into<Point<3>>,
) -> Partial<HalfEdge> {
let mut half_edge = self.add_half_edge();

half_edge
.write()
.back_mut()
.write()
.surface_form
.write()
.global_form
.write()
.position = Some(point.into());

half_edge
}

fn update_as_polygon_from_points<O, P>(
&mut self,
points: O,
Expand All @@ -151,10 +184,28 @@ impl CycleBuilder for PartialCycle {
&mut self,
points_global: [impl Into<Point<3>>; 3],
) -> [Partial<HalfEdge>; 3] {
let points_global = points_global.map(Into::into);

let points_surface = self
.surface
.write()
.update_as_plane_from_points(points_global);
self.update_as_polygon_from_points(points_surface)

let half_edges = self.update_as_polygon_from_points(points_surface);

for (mut half_edge, point) in half_edges.clone().zip_ext(points_global)
{
half_edge
.write()
.back_mut()
.write()
.surface_form
.write()
.global_form
.write()
.position = Some(point);
}

half_edges
}
}
39 changes: 37 additions & 2 deletions crates/fj-kernel/src/builder/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use fj_interop::ext::ArrayExt;
use fj_math::{Point, Scalar};

use crate::{
objects::{GlobalEdge, Surface},
partial::{Partial, PartialGlobalEdge, PartialHalfEdge},
objects::{GlobalEdge, HalfEdge, Surface},
partial::{MaybeSurfacePath, Partial, PartialGlobalEdge, PartialHalfEdge},
};

use super::{CurveBuilder, VertexBuilder};
Expand Down Expand Up @@ -46,6 +46,12 @@ pub trait HalfEdgeBuilder {
/// Updates the global form referenced by this half-edge, and also returns
/// it.
fn infer_global_form(&mut self) -> Partial<GlobalEdge>;

/// Update this edge from another
///
/// Infers as much information about this edge from the other, under the
/// assumption that the other edge is on a different surface.
fn update_from_other_edge(&mut self, other: &Partial<HalfEdge>);
}

impl HalfEdgeBuilder for PartialHalfEdge {
Expand Down Expand Up @@ -177,6 +183,35 @@ impl HalfEdgeBuilder for PartialHalfEdge {

self.global_form.clone()
}

fn update_from_other_edge(&mut self, other: &Partial<HalfEdge>) {
let global_curve = other.read().curve().read().global_form.clone();
self.curve().write().global_form = global_curve.clone();
self.global_form.write().curve = global_curve;

self.curve().write().path = other
.read()
.curve()
.read()
.path
.as_ref()
.map(MaybeSurfacePath::to_undefined);

for (this, other) in self
.vertices
.iter_mut()
.zip(other.read().vertices.iter().rev())
{
this.write().position = other.read().position;
this.write()
.surface_form
.write()
.global_form
.write()
.position =
other.read().surface_form.read().global_form.read().position;
}
}
}

/// Builder API for [`PartialGlobalEdge`]
Expand Down
127 changes: 126 additions & 1 deletion crates/fj-kernel/src/builder/face.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,99 @@
use std::collections::VecDeque;

use fj_interop::ext::ArrayExt;

use crate::{
objects::Cycle,
objects::{Cycle, HalfEdge, Surface},
partial::{Partial, PartialCycle, PartialFace},
};

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

/// Builder API for [`PartialFace`]
pub trait FaceBuilder {
/// Connect the face to another face at the provided half-edges
///
/// Assumes that the provided half-edges, once translated into local
/// equivalents of this face, will not form a cycle.
///
/// Returns the local equivalents of the provided half-edges and, as the
/// last entry, an additional half-edge that closes the cycle.
fn connect_to_open_edges<O>(
&mut self,
edges: O,
) -> O::SizePlusOne<Partial<HalfEdge>>
where
O: ObjectArgument<Partial<HalfEdge>>;

/// Connect the face to another face at the provided half-edges
///
/// Assumes that the provided half-edges, once translated into local
/// equivalents of this face, form a cycle.
///
/// Returns the local equivalents of the provided half-edges.
fn connect_to_closed_edges<O>(
&mut self,
edges: O,
) -> O::SameSize<Partial<HalfEdge>>
where
O: ObjectArgument<Partial<HalfEdge>>;

/// Add an interior cycle
fn add_interior(&mut self) -> Partial<Cycle>;

/// Update the face's surface as a plane
///
/// The plane geometry is inferred from three of the face's vertices. Also
/// infers any undefined `SurfaceVertex` positions.
///
/// # Panics
///
/// Assumes that the face exterior has exactly three vertices to use. Panics
/// otherwise. This is a temporary limitation, not a fundamental one. It
/// could be overcome with some more work.
fn update_surface_as_plane(&mut self) -> Partial<Surface>;
}

impl FaceBuilder for PartialFace {
fn connect_to_open_edges<O>(
&mut self,
edges: O,
) -> O::SizePlusOne<Partial<HalfEdge>>
where
O: ObjectArgument<Partial<HalfEdge>>,
{
// We need to create the additional half-edge last, but at the same time
// need to provide it to the `map_plus_one` method first. Really no
// choice but to create them all in one go, as we do here.
let mut half_edges = VecDeque::new();
for _ in 0..edges.num_objects() {
half_edges.push_back(self.exterior.write().add_half_edge());
}
let additional_half_edge = self.exterior.write().add_half_edge();

edges.map_plus_one(additional_half_edge, |other| {
let mut this = half_edges.pop_front().expect(
"Pushed correct number of half-edges; should be able to pop",
);
this.write().update_from_other_edge(&other);
this
})
}

fn connect_to_closed_edges<O>(
&mut self,
edges: O,
) -> O::SameSize<Partial<HalfEdge>>
where
O: ObjectArgument<Partial<HalfEdge>>,
{
edges.map(|other| {
let mut this = self.exterior.write().add_half_edge();
this.write().update_from_other_edge(&other);
this
})
}

fn add_interior(&mut self) -> Partial<Cycle> {
let cycle = Partial::from_partial(PartialCycle {
surface: self.exterior.read().surface.clone(),
Expand All @@ -18,4 +102,45 @@ impl FaceBuilder for PartialFace {
self.interiors.push(cycle.clone());
cycle
}

fn update_surface_as_plane(&mut self) -> Partial<Surface> {
let mut exterior = self.exterior.write();
let mut vertices = {
exterior.half_edges.iter().map(|half_edge| {
half_edge.read().back().read().surface_form.clone()
})
};

let vertices = {
let array = [
vertices.next().expect("Expected exactly three vertices"),
vertices.next().expect("Expected exactly three vertices"),
vertices.next().expect("Expected exactly three vertices"),
];

assert!(
vertices.next().is_none(),
"Expected exactly three vertices"
);

array
};
let points = vertices.each_ref_ext().map(|vertex| {
vertex
.read()
.global_form
.read()
.position
.expect("Need global position to infer plane")
});

let points_surface =
exterior.surface.write().update_as_plane_from_points(points);

for (mut surface_vertex, point) in vertices.zip_ext(points_surface) {
surface_vertex.write().position = Some(point);
}

exterior.surface.clone()
}
}
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/partial/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod wrapper;

pub use self::{
objects::{
curve::{PartialCurve, PartialGlobalCurve},
curve::{MaybeSurfacePath, PartialCurve, PartialGlobalCurve},
cycle::PartialCycle,
edge::{PartialGlobalEdge, PartialHalfEdge},
face::PartialFace,
Expand Down
Loading

0 comments on commit 98d9c00

Please sign in to comment.