Skip to content

Commit

Permalink
Merge pull request #1814 from hannobraun/operations
Browse files Browse the repository at this point in the history
Use richer face representation in `Tetrahedron`
  • Loading branch information
hannobraun authored May 4, 2023
2 parents d7b4fca + a924143 commit 98c7434
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 44 deletions.
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/algorithms/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub trait TransformObject: Sized {

impl<T> TransformObject for Handle<T>
where
T: Clone + Insert + TransformObject + 'static,
T: Clone + Insert<Inserted = Handle<T>> + TransformObject + 'static,
{
fn transform_with_cache(
self,
Expand Down
6 changes: 3 additions & 3 deletions crates/fj-kernel/src/operations/build/face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use fj_math::Point;

use crate::{
objects::{Cycle, Face, HalfEdge, Surface, Vertex},
operations::Insert,
operations::{Insert, IsInserted, IsInsertedNo},
services::Services,
storage::Handle,
};
Expand Down Expand Up @@ -62,9 +62,9 @@ impl BuildFace for Face {}
/// Currently code that deals with `Polygon` might assume that the polygon has
/// no holes. Unless you create a `Polygon` yourself, or modify a `Polygon`'s
/// `face` field to have interior cycles, this should not affect you.
pub struct Polygon<const D: usize> {
pub struct Polygon<const D: usize, I: IsInserted = IsInsertedNo> {
/// The face that forms the polygon
pub face: Face,
pub face: I::T<Face>,

/// The edges of the polygon
pub edges: [Handle<HalfEdge>; D],
Expand Down
65 changes: 30 additions & 35 deletions crates/fj-kernel/src/operations/build/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ use fj_math::Point;

use crate::{
objects::{Face, Shell},
operations::{Insert, JoinCycle, UpdateFace},
operations::{Insert, IsInsertedYes, JoinCycle, UpdateFace},
services::Services,
storage::Handle,
};

use super::BuildFace;
use super::{BuildFace, Polygon};

/// Build a [`Shell`]
pub trait BuildShell {
Expand Down Expand Up @@ -35,39 +34,35 @@ pub trait BuildShell {
) -> Tetrahedron {
let [a, b, c, d] = points.map(Into::into);

let abc = Face::triangle([a, b, c], services).face;
let abc = Face::triangle([a, b, c], services);
let bad =
Face::triangle([b, a, d], services)
.face
.update_exterior(|cycle| {
cycle
.join_to(abc.exterior(), 0..=0, 0..=0, services)
.insert(services)
});
Face::triangle([b, a, d], services).update_exterior(|cycle| {
cycle
.join_to(abc.face.exterior(), 0..=0, 0..=0, services)
.insert(services)
});
let dac =
Face::triangle([d, a, c], services)
.face
.update_exterior(|cycle| {
cycle
.join_to(abc.exterior(), 1..=1, 2..=2, services)
.join_to(bad.exterior(), 0..=0, 1..=1, services)
.insert(services)
});
Face::triangle([d, a, c], services).update_exterior(|cycle| {
cycle
.join_to(abc.face.exterior(), 1..=1, 2..=2, services)
.join_to(bad.face.exterior(), 0..=0, 1..=1, services)
.insert(services)
});
let cbd =
Face::triangle([c, b, d], services)
.face
.update_exterior(|cycle| {
cycle
.join_to(abc.exterior(), 0..=0, 1..=1, services)
.join_to(bad.exterior(), 1..=1, 2..=2, services)
.join_to(dac.exterior(), 2..=2, 2..=2, services)
.insert(services)
});
Face::triangle([c, b, d], services).update_exterior(|cycle| {
cycle
.join_to(abc.face.exterior(), 0..=0, 1..=1, services)
.join_to(bad.face.exterior(), 1..=1, 2..=2, services)
.join_to(dac.face.exterior(), 2..=2, 2..=2, services)
.insert(services)
});

let faces = [abc, bad, dac, cbd].map(|face| face.insert(services));
let shell = Shell::new(faces.clone());
let triangles =
[abc, bad, dac, cbd].map(|triangle| triangle.insert(services));
let shell =
Shell::new(triangles.iter().map(|triangle| triangle.face.clone()));

let [abc, bad, dac, cbd] = faces;
let [abc, bad, dac, cbd] = triangles;

Tetrahedron {
shell,
Expand All @@ -93,14 +88,14 @@ pub struct Tetrahedron {
pub shell: Shell,

/// The face formed by the points `a`, `b`, and `c`.
pub abc: Handle<Face>,
pub abc: Polygon<3, IsInsertedYes>,

/// The face formed by the points `b`, `a`, and `d`.
pub bad: Handle<Face>,
pub bad: Polygon<3, IsInsertedYes>,

/// The face formed by the points `d`, `a`, and `c`.
pub dac: Handle<Face>,
pub dac: Polygon<3, IsInsertedYes>,

/// The face formed by the points `c`, `b`, and `d`.
pub cbd: Handle<Face>,
pub cbd: Polygon<3, IsInsertedYes>,
}
53 changes: 51 additions & 2 deletions crates/fj-kernel/src/operations/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,30 @@ use crate::{
storage::Handle,
};

use super::Polygon;

/// Insert an object into its respective store
///
/// This is the only primitive operation that is directly understood by
/// `Service<Objects>`. All other operations are built on top of it.
pub trait Insert: Sized {
/// The type of `Self`, once it has been inserted
///
/// Usually this is just `Handle<Self>`, but there are some more complex
/// cases where this type needs to be customized.
type Inserted;

/// Insert the object into its respective store
fn insert(self, services: &mut Services) -> Handle<Self>;
fn insert(self, services: &mut Services) -> Self::Inserted;
}

macro_rules! impl_insert {
($($ty:ty, $store:ident;)*) => {
$(
impl Insert for $ty {
fn insert(self, services: &mut Services) -> Handle<Self> {
type Inserted = Handle<Self>;

fn insert(self, services: &mut Services) -> Self::Inserted {
let handle = services.objects.$store.reserve();
let object = (handle.clone(), self).into();
services.insert_object(object);
Expand All @@ -42,3 +52,42 @@ impl_insert!(
Surface, surfaces;
Vertex, vertices;
);

/// Indicate whether an object has been inserted
///
/// Intended to be used as a type parameter bound for structs that need to track
/// whether their contents have been inserted or not.
pub trait IsInserted {
/// The type of the object for which the insertion status is tracked
type T<T>;
}

/// Indicate that an object has been inserted
///
/// See [`IsInserted`].
pub struct IsInsertedYes;

impl IsInserted for IsInsertedYes {
type T<T> = Handle<T>;
}

/// Indicate that an object has not been inserted
///
/// See [`IsInserted`].
pub struct IsInsertedNo;

impl IsInserted for IsInsertedNo {
type T<T> = T;
}

impl<const D: usize> Insert for Polygon<D, IsInsertedNo> {
type Inserted = Polygon<D, IsInsertedYes>;

fn insert(self, services: &mut Services) -> Self::Inserted {
Polygon {
face: self.face.insert(services),
edges: self.edges,
vertices: self.vertices,
}
}
}
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub use self::{
BuildCycle, BuildFace, BuildHalfEdge, BuildShell, BuildSurface,
Polygon, Tetrahedron,
},
insert::Insert,
insert::{Insert, IsInserted, IsInsertedNo, IsInsertedYes},
join::JoinCycle,
update::{UpdateCycle, UpdateFace, UpdateHalfEdge, UpdateShell},
};
41 changes: 41 additions & 0 deletions crates/fj-kernel/src/operations/update/face.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::array;

use crate::{
objects::{Cycle, Face},
operations::Polygon,
storage::Handle,
};

Expand Down Expand Up @@ -47,3 +50,41 @@ impl UpdateFace for Face {
)
}
}

impl<const D: usize> UpdateFace for Polygon<D> {
fn update_exterior(
&self,
f: impl FnOnce(&Handle<Cycle>) -> Handle<Cycle>,
) -> Self {
let face = self.face.update_exterior(f);
let edges = array::from_fn(|i| {
face.exterior()
.nth_half_edge(i)
.expect("Operation should not have changed length of cycle")
.clone()
});
let vertices = array::from_fn(|i| {
// The duplicated code here is unfortunate, but unless we get a
// stable `array::each_ref` and something like `array::unzip`, I'm
// not sure how to avoid it.
face.exterior()
.nth_half_edge(i)
.expect("Operation should not have changed length of cycle")
.start_vertex()
.clone()
});

Polygon {
face,
edges,
vertices,
}
}

fn add_interiors(
&self,
_: impl IntoIterator<Item = Handle<Cycle>>,
) -> Self {
panic!("Adding interiors to `Polygon` is not supported.")
}
}
4 changes: 2 additions & 2 deletions crates/fj-kernel/src/validate/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ mod tests {
[[0., 0., 0.], [0., 1., 0.], [1., 0., 0.], [0., 0., 1.]],
&mut services,
);
let invalid = valid.shell.update_face(&valid.abc, |face| {
let invalid = valid.shell.update_face(&valid.abc.face, |face| {
face.update_exterior(|cycle| {
cycle
.update_nth_half_edge(0, |half_edge| {
Expand Down Expand Up @@ -243,7 +243,7 @@ mod tests {
[[0., 0., 0.], [0., 1., 0.], [1., 0., 0.], [0., 0., 1.]],
&mut services,
);
let invalid = valid.shell.remove_face(&valid.abc);
let invalid = valid.shell.remove_face(&valid.abc.face);

valid.shell.validate_and_return_first_error()?;
assert_contains_err!(
Expand Down

0 comments on commit 98c7434

Please sign in to comment.