Merge pull request #308 from hannobraun/shape

Integrate `Faces` into `Shape`
This commit is contained in:
Hanno Braun 2022-03-08 14:26:34 +01:00 committed by GitHub
commit 399247ddba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 147 additions and 102 deletions

View File

@ -1,8 +1,5 @@
use crate::{ use crate::{
kernel::{ kernel::{shape::Shape, topology::faces::Face},
shape::Shape,
topology::faces::{Face, Faces},
},
math::{Scalar, Transform, Vector}, math::{Scalar, Transform, Vector},
}; };
@ -22,9 +19,9 @@ pub fn sweep_shape(
let mut top_faces = Vec::new(); let mut top_faces = Vec::new();
let mut side_faces = Vec::new(); let mut side_faces = Vec::new();
for face in &original.faces.0 { for face in original.faces().all() {
bottom_faces.push(face.clone()); bottom_faces.push(face.clone());
top_faces.push(transform_face(face, &translation, &mut shape)); top_faces.push(transform_face(&face, &translation, &mut shape));
} }
for cycle in original.cycles().all() { for cycle in original.cycles().all() {
@ -55,12 +52,15 @@ pub fn sweep_shape(
side_faces.push(Face::Triangles(side_face)); side_faces.push(Face::Triangles(side_face));
} }
let mut faces = Vec::new(); for face in bottom_faces {
faces.extend(bottom_faces); shape.faces().add((*face).clone());
faces.extend(top_faces); }
faces.extend(side_faces); for face in top_faces {
shape.faces().add(face);
shape.faces = Faces(faces); }
for face in side_faces {
shape.faces().add(face);
}
shape shape
} }
@ -70,7 +70,7 @@ mod tests {
use crate::{ use crate::{
kernel::{ kernel::{
geometry::{surfaces::Swept, Surface}, geometry::{surfaces::Swept, Surface},
shape::Shape, shape::{handle::Handle, Shape},
topology::{ topology::{
edges::{Cycle, Edge}, edges::{Cycle, Edge},
faces::Face, faces::Face,
@ -85,7 +85,7 @@ mod tests {
fn sweep() { fn sweep() {
let sketch = Triangle::new([[0., 0., 0.], [1., 0., 0.], [0., 1., 0.]]); let sketch = Triangle::new([[0., 0., 0.], [1., 0., 0.], [0., 1., 0.]]);
let swept = sweep_shape( let mut swept = sweep_shape(
sketch.shape, sketch.shape,
Vector::from([0., 0., 1.]), Vector::from([0., 0., 1.]),
Scalar::from_f64(0.), Scalar::from_f64(0.),
@ -95,8 +95,8 @@ mod tests {
let top_face = let top_face =
Triangle::new([[0., 0., 1.], [1., 0., 1.], [0., 1., 1.]]).face; Triangle::new([[0., 0., 1.], [1., 0., 1.], [0., 1., 1.]]).face;
assert!(swept.faces.0.contains(&bottom_face)); assert!(swept.faces().contains(&bottom_face));
assert!(swept.faces.0.contains(&top_face)); assert!(swept.faces().contains(&top_face));
// Side faces are not tested, as those use triangle representation. The // Side faces are not tested, as those use triangle representation. The
// plan is to start testing them, as they are transitioned to b-rep. // plan is to start testing them, as they are transitioned to b-rep.
@ -104,7 +104,7 @@ mod tests {
pub struct Triangle { pub struct Triangle {
shape: Shape, shape: Shape,
face: Face, face: Handle<Face>,
} }
impl Triangle { impl Triangle {
@ -134,9 +134,9 @@ mod tests {
}], }],
}; };
shape.faces.0.push(abc.clone()); let face = shape.faces().add(abc);
Self { shape, face: abc } Self { shape, face }
} }
} }
} }

View File

@ -19,12 +19,12 @@ use crate::{
/// ///
/// Addressing the shortcomings in this method probably doesn't make sense, /// Addressing the shortcomings in this method probably doesn't make sense,
/// except as a side effect of addressing the shortcomings of `Shape`. /// except as a side effect of addressing the shortcomings of `Shape`.
pub fn transform_shape(original: &Shape, transform: &Transform) -> Shape { pub fn transform_shape(mut original: Shape, transform: &Transform) -> Shape {
let mut transformed = Shape::new(); let mut transformed = Shape::new();
for face in &original.faces.0 { for face in original.faces().all() {
let face = transform_face(face, transform, &mut transformed); let face = transform_face(&face, transform, &mut transformed);
transformed.faces.0.push(face); transformed.faces().add(face);
} }
transformed transformed

View File

@ -29,7 +29,7 @@ impl Cycles<'_> {
} }
/// Access an iterator over all cycles /// Access an iterator over all cycles
pub fn all(&self) -> impl Iterator<Item = Storage<Cycle>> + '_ { pub fn all(&self) -> impl Iterator<Item = Handle<Cycle>> + '_ {
self.cycles.iter().cloned() self.cycles.iter().map(|storage| storage.handle())
} }
} }

49
src/kernel/shape/faces.rs Normal file
View File

@ -0,0 +1,49 @@
use crate::{
debug::DebugInfo,
kernel::topology::faces::Face,
math::{Scalar, Triangle},
};
use super::{
handle::{Handle, Storage},
FacesInner,
};
/// The faces of a shape
pub struct Faces<'r> {
pub(super) faces: &'r mut FacesInner,
}
impl Faces<'_> {
/// Add a face to the shape
pub fn add(&mut self, face: Face) -> Handle<Face> {
let storage = Storage::new(face);
let handle = storage.handle();
self.faces.push(storage);
handle
}
/// Check whether the shape contains a specific face
#[cfg(test)]
pub fn contains(&self, face: &Face) -> bool {
self.faces.contains(&Storage::new(face.clone()))
}
/// Access an iterator over all faces
pub fn all(&self) -> impl Iterator<Item = Handle<Face>> + '_ {
self.faces.iter().map(|storage| storage.handle())
}
pub fn triangles(
&self,
tolerance: Scalar,
out: &mut Vec<Triangle<3>>,
debug_info: &mut DebugInfo,
) {
for face in &*self.faces {
face.triangles(tolerance, out, debug_info);
}
}
}

View File

@ -1,21 +1,19 @@
pub mod cycles; pub mod cycles;
pub mod edges; pub mod edges;
pub mod faces;
pub mod handle; pub mod handle;
pub mod vertices; pub mod vertices;
use crate::math::Scalar; use crate::math::Scalar;
use super::topology::{edges::Cycle, faces::Faces, vertices::Vertex}; use super::topology::{edges::Cycle, faces::Face, vertices::Vertex};
use self::{cycles::Cycles, edges::Edges, handle::Storage, vertices::Vertices}; use self::{
cycles::Cycles, edges::Edges, faces::Faces, handle::Storage,
vertices::Vertices,
};
/// The boundary representation of a shape /// The boundary representation of a shape
///
/// # Implementation note
///
/// The goal for `Shape` is to enforce full self-consistency, through the API it
/// provides. Steps have been made in that direction, but right now, the API is
/// still full of holes, forcing callers to just be careful for the time being.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Shape { pub struct Shape {
/// The minimum distance between two vertices /// The minimum distance between two vertices
@ -25,8 +23,7 @@ pub struct Shape {
vertices: VerticesInner, vertices: VerticesInner,
cycles: CyclesInner, cycles: CyclesInner,
faces: FacesInner,
pub faces: Faces,
} }
impl Shape { impl Shape {
@ -40,7 +37,7 @@ impl Shape {
vertices: VerticesInner::new(), vertices: VerticesInner::new(),
cycles: CyclesInner::new(), cycles: CyclesInner::new(),
faces: Faces(Vec::new()), faces: FacesInner::new(),
} }
} }
@ -78,7 +75,15 @@ impl Shape {
cycles: &mut self.cycles, cycles: &mut self.cycles,
} }
} }
/// Access the shape's faces
pub fn faces(&mut self) -> Faces {
Faces {
faces: &mut self.faces,
}
}
} }
type VerticesInner = Vec<Storage<Vertex>>; type VerticesInner = Vec<Storage<Vertex>>;
type CyclesInner = Vec<Storage<Cycle>>; type CyclesInner = Vec<Storage<Cycle>>;
type FacesInner = Vec<Storage<Face>>;

View File

@ -5,7 +5,7 @@ use crate::{
shape::Shape, shape::Shape,
topology::{ topology::{
edges::{Cycle, Edge}, edges::{Cycle, Edge},
faces::{Face, Faces}, faces::Face,
}, },
}, },
math::{Aabb, Point, Scalar}, math::{Aabb, Point, Scalar},
@ -25,14 +25,15 @@ impl ToShape for fj::Circle {
.add(Edge::circle(Scalar::from_f64(self.radius))); .add(Edge::circle(Scalar::from_f64(self.radius)));
shape.cycles().add(Cycle { edges: vec![edge] }); shape.cycles().add(Cycle { edges: vec![edge] });
shape.faces = Faces(vec![Face::Face { let cycles = shape
cycles: shape .cycles()
.cycles() .all()
.all() .map(|handle| (*handle).clone())
.map(|handle| (*handle).clone()) .collect();
.collect(), shape.faces().add(Face::Face {
cycles,
surface: Surface::x_y_plane(), surface: Surface::x_y_plane(),
}]); });
shape shape
} }

View File

@ -2,10 +2,7 @@ use crate::{
debug::DebugInfo, debug::DebugInfo,
kernel::{ kernel::{
shape::Shape, shape::Shape,
topology::{ topology::{edges::Cycle, faces::Face},
edges::Cycle,
faces::{Face, Faces},
},
}, },
math::{Aabb, Scalar}, math::{Aabb, Scalar},
}; };
@ -41,10 +38,15 @@ impl ToShape for fj::Difference2d {
); );
} }
shape.faces = { {
let (a, b) = if a.faces.0.len() == 1 && b.faces.0.len() == 1 { let (a, b) = if a.faces().all().count() == 1
&& b.faces().all().count() == 1
{
// Can't panic. We just checked that length of `a` and `b` is 1. // Can't panic. We just checked that length of `a` and `b` is 1.
(a.faces.0.pop().unwrap(), b.faces.0.pop().unwrap()) (
a.faces().all().next().unwrap(),
b.faces().all().next().unwrap(),
)
} else { } else {
// See issue: // See issue:
// https://github.com/hannobraun/Fornjot/issues/95 // https://github.com/hannobraun/Fornjot/issues/95
@ -54,22 +56,24 @@ impl ToShape for fj::Difference2d {
); );
}; };
let (a, b, surface_a, surface_b) = match (a, b) { let (a, b, surface_a, surface_b) =
( match ((*a).clone(), (*b).clone()) {
Face::Face { (
cycles: a, Face::Face {
surface: surface_a, cycles: a,
}, surface: surface_a,
Face::Face { },
cycles: b, Face::Face {
surface: surface_b, cycles: b,
}, surface: surface_b,
) => (a, b, surface_a, surface_b), },
_ => { ) => (a, b, surface_a, surface_b),
// None of the 2D types still use the triangles representation. _ => {
unreachable!() // None of the 2D types still use triangle
} // representation.
}; unreachable!()
}
};
assert!( assert!(
surface_a == surface_b, surface_a == surface_b,
@ -82,7 +86,7 @@ impl ToShape for fj::Difference2d {
let mut cycles = a; let mut cycles = a;
cycles.extend(b); cycles.extend(b);
Faces(vec![Face::Face { cycles, surface }]) shape.faces().add(Face::Face { cycles, surface });
}; };
shape shape

View File

@ -5,7 +5,7 @@ use crate::{
shape::Shape, shape::Shape,
topology::{ topology::{
edges::{Cycle, Edge}, edges::{Cycle, Edge},
faces::{Face, Faces}, faces::Face,
}, },
}, },
math::{Aabb, Point, Scalar}, math::{Aabb, Point, Scalar},
@ -55,7 +55,7 @@ impl ToShape for fj::Sketch {
.collect(), .collect(),
surface: Surface::x_y_plane(), surface: Surface::x_y_plane(),
}; };
shape.faces = Faces(vec![face]); shape.faces().add(face);
shape shape
} }

View File

@ -12,7 +12,7 @@ impl ToShape for fj::Transform {
fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape { fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape {
let shape = self.shape.to_shape(tolerance, debug_info); let shape = self.shape.to_shape(tolerance, debug_info);
let transform = transform(self); let transform = transform(self);
transform_shape(&shape, &transform) transform_shape(shape, &transform)
} }
fn bounding_volume(&self) -> Aabb<3> { fn bounding_volume(&self) -> Aabb<3> {

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
debug::DebugInfo, debug::DebugInfo,
kernel::{shape::Shape, topology::faces::Faces}, kernel::shape::Shape,
math::{Aabb, Scalar}, math::{Aabb, Scalar},
}; };
@ -10,19 +10,20 @@ impl ToShape for fj::Union {
fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape { fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape {
let mut shape = Shape::new(); let mut shape = Shape::new();
let a = self.a.to_shape(tolerance, debug_info).faces; let mut a = self.a.to_shape(tolerance, debug_info);
let b = self.b.to_shape(tolerance, debug_info).faces; let mut b = self.b.to_shape(tolerance, debug_info);
// This doesn't create a true union, as it doesn't eliminate, merge, or // This doesn't create a true union, as it doesn't eliminate, merge, or
// split faces. // split faces.
// //
// See issue: // See issue:
// https://github.com/hannobraun/Fornjot/issues/42 // https://github.com/hannobraun/Fornjot/issues/42
let mut faces = Vec::new(); for face in a.faces().all() {
faces.extend(a.0); shape.faces().add((*face).clone());
faces.extend(b.0); }
for face in b.faces().all() {
shape.faces = Faces(faces); shape.faces().add((*face).clone());
}
shape shape
} }

View File

@ -16,23 +16,6 @@ use crate::{
use super::edges::Cycle; use super::edges::Cycle;
/// The faces of a shape
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Faces(pub Vec<Face>);
impl Faces {
pub fn triangles(
&self,
tolerance: Scalar,
out: &mut Vec<Triangle<3>>,
debug_info: &mut DebugInfo,
) {
for face in &self.0 {
face.triangles(tolerance, out, debug_info);
}
}
}
/// A face of a shape /// A face of a shape
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum Face { pub enum Face {

View File

@ -100,10 +100,11 @@ fn main() -> anyhow::Result<()> {
}; };
let mut debug_info = DebugInfo::new(); let mut debug_info = DebugInfo::new();
let faces = shape.to_shape(tolerance, &mut debug_info).faces;
let mut triangles = Vec::new(); let mut triangles = Vec::new();
faces.triangles(tolerance, &mut triangles, &mut debug_info); shape
.to_shape(tolerance, &mut debug_info)
.faces()
.triangles(tolerance, &mut triangles, &mut debug_info);
if let Some(path) = args.export { if let Some(path) = args.export {
let mut mesh_maker = MeshMaker::new(); let mut mesh_maker = MeshMaker::new();
@ -226,10 +227,11 @@ fn main() -> anyhow::Result<()> {
debug_info.clear(); debug_info.clear();
triangles.clear(); triangles.clear();
let faces = shape.to_shape(tolerance, &mut debug_info).faces;
aabb = shape.bounding_volume(); aabb = shape.bounding_volume();
faces.triangles(tolerance, &mut triangles, &mut debug_info); shape
.to_shape(tolerance, &mut debug_info)
.faces()
.triangles(tolerance, &mut triangles, &mut debug_info);
renderer.update_geometry( renderer.update_geometry(
(&triangles).into(), (&triangles).into(),