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

Expand builder API #1510

Merged
merged 8 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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