mirror of https://github.com/hannobraun/Fornjot
Merge pull request #1331 from hannobraun/partial
Integrate `Face` into partial object API
This commit is contained in:
commit
d9bef00c12
|
@ -150,7 +150,7 @@ where
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
builder::CurveBuilder,
|
||||
builder::{CurveBuilder, FaceBuilder},
|
||||
objects::{Curve, Face, Objects},
|
||||
partial::HasPartial,
|
||||
};
|
||||
|
@ -183,11 +183,11 @@ mod tests {
|
|||
[ 1., -1.],
|
||||
];
|
||||
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points(exterior)
|
||||
.with_interior_polygon_from_points(interior)
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
|
||||
let expected =
|
||||
CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]);
|
||||
|
|
|
@ -67,7 +67,7 @@ mod tests {
|
|||
|
||||
use crate::{
|
||||
algorithms::intersect::CurveFaceIntersection,
|
||||
builder::CurveBuilder,
|
||||
builder::{CurveBuilder, FaceBuilder},
|
||||
objects::{Curve, Face, Objects},
|
||||
partial::HasPartial,
|
||||
};
|
||||
|
@ -86,12 +86,12 @@ mod tests {
|
|||
[1., 2.],
|
||||
];
|
||||
let [a, b] = [objects.surfaces.xy_plane(), objects.surfaces.xz_plane()]
|
||||
.map(|surface| {
|
||||
Face::builder(&objects)
|
||||
.try_map_ext(|surface| {
|
||||
Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points(points)
|
||||
.build()
|
||||
});
|
||||
.build(&objects)
|
||||
})?;
|
||||
|
||||
let intersection = FaceFaceIntersection::compute([&a, &b], &objects)?;
|
||||
|
||||
|
@ -113,12 +113,12 @@ mod tests {
|
|||
];
|
||||
let surfaces =
|
||||
[objects.surfaces.xy_plane(), objects.surfaces.xz_plane()];
|
||||
let [a, b] = surfaces.clone().map(|surface| {
|
||||
Face::builder(&objects)
|
||||
let [a, b] = surfaces.clone().try_map_ext(|surface| {
|
||||
Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points(points)
|
||||
.build()
|
||||
});
|
||||
.build(&objects)
|
||||
})?;
|
||||
|
||||
let intersection = FaceFaceIntersection::compute([&a, &b], &objects)?;
|
||||
|
||||
|
|
|
@ -136,34 +136,38 @@ mod tests {
|
|||
|
||||
use crate::{
|
||||
algorithms::intersect::{face_point::FacePointIntersection, Intersect},
|
||||
builder::FaceBuilder,
|
||||
iter::ObjectIters,
|
||||
objects::{Face, Objects},
|
||||
partial::HasPartial,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn point_is_outside_face() {
|
||||
fn point_is_outside_face() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([[0., 0.], [1., 1.], [0., 2.]])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let point = Point::from([2., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
assert_eq!(intersection, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_vertex_while_passing_outside() {
|
||||
fn ray_hits_vertex_while_passing_outside() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([[0., 0.], [2., 1.], [0., 2.]])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let point = Point::from([1., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
|
@ -171,17 +175,19 @@ mod tests {
|
|||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_vertex_at_cycle_seam() {
|
||||
fn ray_hits_vertex_at_cycle_seam() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([[4., 2.], [0., 4.], [0., 0.]])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let point = Point::from([1., 2.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
|
@ -189,14 +195,16 @@ mod tests {
|
|||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_vertex_while_staying_inside() {
|
||||
fn ray_hits_vertex_while_staying_inside() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[0., 0.],
|
||||
|
@ -204,7 +212,7 @@ mod tests {
|
|||
[3., 0.],
|
||||
[3., 4.],
|
||||
])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let point = Point::from([1., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
|
@ -212,14 +220,17 @@ mod tests {
|
|||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_parallel_edge_and_leaves_face_at_vertex() {
|
||||
fn ray_hits_parallel_edge_and_leaves_face_at_vertex() -> anyhow::Result<()>
|
||||
{
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[0., 0.],
|
||||
|
@ -227,7 +238,7 @@ mod tests {
|
|||
[3., 1.],
|
||||
[0., 2.],
|
||||
])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let point = Point::from([1., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
|
@ -235,14 +246,17 @@ mod tests {
|
|||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ray_hits_parallel_edge_and_does_not_leave_face_there() {
|
||||
fn ray_hits_parallel_edge_and_does_not_leave_face_there(
|
||||
) -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[0., 0.],
|
||||
|
@ -251,7 +265,7 @@ mod tests {
|
|||
[4., 0.],
|
||||
[4., 5.],
|
||||
])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let point = Point::from([1., 1.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
|
@ -259,17 +273,19 @@ mod tests {
|
|||
intersection,
|
||||
Some(FacePointIntersection::PointIsInsideFace)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn point_is_coincident_with_edge() {
|
||||
fn point_is_coincident_with_edge() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([[0., 0.], [2., 0.], [0., 1.]])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let point = Point::from([1., 0.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
|
@ -286,17 +302,19 @@ mod tests {
|
|||
intersection,
|
||||
Some(FacePointIntersection::PointIsOnEdge(edge.clone()))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn point_is_coincident_with_vertex() {
|
||||
fn point_is_coincident_with_vertex() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let point = Point::from([1., 0.]);
|
||||
|
||||
let intersection = (&face, &point).intersect();
|
||||
|
@ -311,5 +329,7 @@ mod tests {
|
|||
intersection,
|
||||
Some(FacePointIntersection::PointIsOnVertex(vertex.clone()))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,8 +152,10 @@ mod tests {
|
|||
},
|
||||
transform::TransformObject,
|
||||
},
|
||||
builder::FaceBuilder,
|
||||
iter::ObjectIters,
|
||||
objects::{Face, Objects},
|
||||
partial::HasPartial,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
@ -163,7 +165,7 @@ mod tests {
|
|||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let surface = objects.surfaces.yz_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[-1., -1.],
|
||||
|
@ -171,7 +173,7 @@ mod tests {
|
|||
[1., 1.],
|
||||
[-1., 1.],
|
||||
])
|
||||
.build()
|
||||
.build(&objects)?
|
||||
.translate([-1., 0., 0.], &objects)?;
|
||||
|
||||
assert_eq!((&ray, &face).intersect(), None);
|
||||
|
@ -185,7 +187,7 @@ mod tests {
|
|||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let surface = objects.surfaces.yz_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[-1., -1.],
|
||||
|
@ -193,7 +195,7 @@ mod tests {
|
|||
[1., 1.],
|
||||
[-1., 1.],
|
||||
])
|
||||
.build()
|
||||
.build(&objects)?
|
||||
.translate([1., 0., 0.], &objects)?;
|
||||
|
||||
assert_eq!(
|
||||
|
@ -210,7 +212,7 @@ mod tests {
|
|||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let surface = objects.surfaces.yz_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[-1., -1.],
|
||||
|
@ -218,7 +220,7 @@ mod tests {
|
|||
[1., 1.],
|
||||
[-1., 1.],
|
||||
])
|
||||
.build()
|
||||
.build(&objects)?
|
||||
.translate([0., 0., 2.], &objects)?;
|
||||
|
||||
assert_eq!((&ray, &face).intersect(), None);
|
||||
|
@ -232,7 +234,7 @@ mod tests {
|
|||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let surface = objects.surfaces.yz_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[-1., -1.],
|
||||
|
@ -240,7 +242,7 @@ mod tests {
|
|||
[1., 1.],
|
||||
[-1., 1.],
|
||||
])
|
||||
.build()
|
||||
.build(&objects)?
|
||||
.translate([1., 1., 0.], &objects)?;
|
||||
|
||||
let edge = face
|
||||
|
@ -265,7 +267,7 @@ mod tests {
|
|||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let surface = objects.surfaces.yz_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[-1., -1.],
|
||||
|
@ -273,7 +275,7 @@ mod tests {
|
|||
[1., 1.],
|
||||
[-1., 1.],
|
||||
])
|
||||
.build()
|
||||
.build(&objects)?
|
||||
.translate([1., 1., 1.], &objects)?;
|
||||
|
||||
let vertex = face
|
||||
|
@ -290,13 +292,13 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn ray_is_parallel_to_surface_and_hits() {
|
||||
fn ray_is_parallel_to_surface_and_hits() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[-1., -1.],
|
||||
|
@ -304,12 +306,14 @@ mod tests {
|
|||
[1., 1.],
|
||||
[-1., 1.],
|
||||
])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
|
||||
assert_eq!(
|
||||
(&ray, &face).intersect(),
|
||||
Some(RayFaceIntersection::RayHitsFaceAndAreParallel)
|
||||
)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -319,7 +323,7 @@ mod tests {
|
|||
let ray = HorizontalRayToTheRight::from([0., 0., 0.]);
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[-1., -1.],
|
||||
|
@ -327,7 +331,7 @@ mod tests {
|
|||
[1., 1.],
|
||||
[-1., 1.],
|
||||
])
|
||||
.build()
|
||||
.build(&objects)?
|
||||
.translate([0., 0., 1.], &objects)?;
|
||||
|
||||
assert_eq!((&ray, &face).intersect(), None);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
objects::{Face, Objects},
|
||||
partial::HasPartial,
|
||||
storage::Handle,
|
||||
validate::ValidationError,
|
||||
};
|
||||
|
@ -14,10 +15,10 @@ impl Reverse for Handle<Face> {
|
|||
.map(|cycle| cycle.clone().reverse(objects))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(Face::builder(objects)
|
||||
Face::partial()
|
||||
.with_exterior(exterior)
|
||||
.with_interiors(interiors)
|
||||
.with_color(self.color())
|
||||
.build())
|
||||
.build(objects)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
Curve, Cycle, Face, GlobalEdge, HalfEdge, Objects, SurfaceVertex,
|
||||
Vertex,
|
||||
},
|
||||
partial::HasPartial,
|
||||
path::SurfacePath,
|
||||
storage::Handle,
|
||||
validate::ValidationError,
|
||||
|
@ -175,10 +176,10 @@ impl Sweep for (Handle<HalfEdge>, Color) {
|
|||
objects.cycles.insert(Cycle::new(edges))?
|
||||
};
|
||||
|
||||
Ok(Face::builder(objects)
|
||||
Face::partial()
|
||||
.with_exterior(cycle)
|
||||
.with_color(color)
|
||||
.build())
|
||||
.build(objects)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,7 +257,7 @@ mod tests {
|
|||
.cycles
|
||||
.insert(Cycle::new([bottom, side_up, top, side_down]))?;
|
||||
|
||||
Face::builder(&objects).with_exterior(cycle).build()
|
||||
Face::partial().with_exterior(cycle).build(&objects)?
|
||||
};
|
||||
|
||||
assert_eq!(face, expected_face);
|
||||
|
|
|
@ -84,7 +84,7 @@ mod tests {
|
|||
|
||||
use crate::{
|
||||
algorithms::{reverse::Reverse, transform::TransformObject},
|
||||
builder::HalfEdgeBuilder,
|
||||
builder::{FaceBuilder, HalfEdgeBuilder},
|
||||
objects::{Face, HalfEdge, Objects, Sketch},
|
||||
partial::HasPartial,
|
||||
};
|
||||
|
@ -107,15 +107,15 @@ mod tests {
|
|||
.build()
|
||||
.sweep(UP, &objects)?;
|
||||
|
||||
let bottom = Face::builder(&objects)
|
||||
let bottom = Face::partial()
|
||||
.with_surface(surface.clone())
|
||||
.with_exterior_polygon_from_points(TRIANGLE)
|
||||
.build()
|
||||
.build(&objects)?
|
||||
.reverse(&objects)?;
|
||||
let top = Face::builder(&objects)
|
||||
let top = Face::partial()
|
||||
.with_surface(surface.translate(UP, &objects)?)
|
||||
.with_exterior_polygon_from_points(TRIANGLE)
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
|
||||
assert!(solid.find_face(&bottom).is_some());
|
||||
assert!(solid.find_face(&top).is_some());
|
||||
|
@ -151,15 +151,15 @@ mod tests {
|
|||
.build()
|
||||
.sweep(DOWN, &objects)?;
|
||||
|
||||
let bottom = Face::builder(&objects)
|
||||
let bottom = Face::partial()
|
||||
.with_surface(surface.clone().translate(DOWN, &objects)?)
|
||||
.with_exterior_polygon_from_points(TRIANGLE)
|
||||
.build()
|
||||
.build(&objects)?
|
||||
.reverse(&objects)?;
|
||||
let top = Face::builder(&objects)
|
||||
let top = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points(TRIANGLE)
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
|
||||
assert!(solid.find_face(&bottom).is_some());
|
||||
assert!(solid.find_face(&top).is_some());
|
||||
|
|
|
@ -2,44 +2,51 @@ use fj_math::Transform;
|
|||
|
||||
use crate::{
|
||||
objects::{Face, FaceSet, Objects},
|
||||
partial::HasPartial,
|
||||
storage::Handle,
|
||||
partial::{HasPartial, PartialFace},
|
||||
validate::ValidationError,
|
||||
};
|
||||
|
||||
use super::TransformObject;
|
||||
|
||||
impl TransformObject for Handle<Face> {
|
||||
impl TransformObject for PartialFace {
|
||||
fn transform(
|
||||
self,
|
||||
transform: &Transform,
|
||||
objects: &Objects,
|
||||
) -> Result<Self, ValidationError> {
|
||||
let surface = self.surface().clone().transform(transform, objects)?;
|
||||
let surface = self
|
||||
.surface()
|
||||
.map(|surface| surface.transform(transform, objects))
|
||||
.transpose()?;
|
||||
let exterior = self
|
||||
.exterior()
|
||||
.to_partial()
|
||||
.into_partial()
|
||||
.transform(transform, objects)?
|
||||
.with_surface(Some(surface.clone()))
|
||||
.build(objects)?;
|
||||
.with_surface(surface.clone());
|
||||
let interiors = self
|
||||
.interiors()
|
||||
.map(|cycle| -> Result<_, ValidationError> {
|
||||
cycle
|
||||
.to_partial()
|
||||
.into_partial()
|
||||
.transform(transform, objects)?
|
||||
.with_surface(Some(surface.clone()))
|
||||
.with_surface(surface.clone())
|
||||
.build(objects)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let color = self.color();
|
||||
|
||||
Ok(Face::builder(objects)
|
||||
let mut face = Face::partial()
|
||||
.with_exterior(exterior)
|
||||
.with_interiors(interiors)
|
||||
.with_color(color)
|
||||
.build())
|
||||
.with_interiors(interiors);
|
||||
if let Some(surface) = surface {
|
||||
face = face.with_surface(surface);
|
||||
}
|
||||
if let Some(color) = color {
|
||||
face = face.with_color(color);
|
||||
}
|
||||
|
||||
Ok(face)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,9 @@ mod tests {
|
|||
|
||||
use crate::{
|
||||
algorithms::approx::{Approx, Tolerance},
|
||||
builder::FaceBuilder,
|
||||
objects::{Face, Objects},
|
||||
partial::HasPartial,
|
||||
storage::Handle,
|
||||
};
|
||||
|
||||
|
@ -100,10 +102,10 @@ mod tests {
|
|||
let d = [0., 1.];
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([a, b, c, d])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
|
||||
let a = Point::from(a).to_xyz();
|
||||
let b = Point::from(b).to_xyz();
|
||||
|
@ -135,11 +137,11 @@ mod tests {
|
|||
let h = [3., 1.];
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface.clone())
|
||||
.with_exterior_polygon_from_points([a, b, c, d])
|
||||
.with_interior_polygon_from_points([e, f, g, h])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
|
||||
let triangles = triangulate(face)?;
|
||||
|
||||
|
@ -196,10 +198,10 @@ mod tests {
|
|||
let e = [0., 0.8];
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface.clone())
|
||||
.with_exterior_polygon_from_points([a, b, c, d, e])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
|
||||
let triangles = triangulate(face)?;
|
||||
|
||||
|
|
|
@ -1,115 +1,49 @@
|
|||
use fj_interop::mesh::Color;
|
||||
use fj_math::Point;
|
||||
|
||||
use crate::{
|
||||
objects::{Cycle, Face, Objects, Surface},
|
||||
partial::HasPartial,
|
||||
storage::Handle,
|
||||
objects::Cycle,
|
||||
partial::{HasPartial, PartialFace},
|
||||
};
|
||||
|
||||
use super::CycleBuilder;
|
||||
|
||||
/// API for building a [`Face`]
|
||||
///
|
||||
/// Also see [`Face::builder`].
|
||||
pub struct FaceBuilder<'a> {
|
||||
/// The stores that the created objects are put in
|
||||
pub objects: &'a Objects,
|
||||
/// Builder API for [`PartialFace`]
|
||||
pub trait FaceBuilder {
|
||||
/// Update the [`PartialFace`] with an exterior polygon
|
||||
fn with_exterior_polygon_from_points(
|
||||
self,
|
||||
points: impl IntoIterator<Item = impl Into<Point<2>>>,
|
||||
) -> Self;
|
||||
|
||||
/// The surface that the [`Face`] is defined in
|
||||
pub surface: Option<Handle<Surface>>,
|
||||
|
||||
/// The exterior cycle that bounds the [`Face`] on the outside
|
||||
///
|
||||
/// Must be provided by the caller, directly or using one of the `with_`
|
||||
/// methods, before [`FaceBuilder::build`] is called.
|
||||
pub exterior: Option<Handle<Cycle>>,
|
||||
|
||||
/// The interior cycles that form holes in the [`Face`]
|
||||
pub interiors: Vec<Handle<Cycle>>,
|
||||
|
||||
/// The color of the [`Face`]
|
||||
pub color: Option<Color>,
|
||||
/// Update the [`PartialFace`] with an interior polygon
|
||||
fn with_interior_polygon_from_points(
|
||||
self,
|
||||
points: impl IntoIterator<Item = impl Into<Point<2>>>,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
impl<'a> FaceBuilder<'a> {
|
||||
/// Build the [`Face`] with the provided surface
|
||||
pub fn with_surface(mut self, surface: Handle<Surface>) -> Self {
|
||||
self.surface = Some(surface);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with the provided exterior
|
||||
pub fn with_exterior(mut self, exterior: Handle<Cycle>) -> Self {
|
||||
self.exterior = Some(exterior);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with an exterior polygon from the provided points
|
||||
pub fn with_exterior_polygon_from_points(
|
||||
mut self,
|
||||
impl FaceBuilder for PartialFace {
|
||||
fn with_exterior_polygon_from_points(
|
||||
self,
|
||||
points: impl IntoIterator<Item = impl Into<Point<2>>>,
|
||||
) -> Self {
|
||||
let surface = self
|
||||
.surface
|
||||
.as_ref()
|
||||
.expect("Need surface to create polygon");
|
||||
let surface = self.surface().expect("Need surface to create polygon");
|
||||
|
||||
self.exterior = Some(
|
||||
self.with_exterior(
|
||||
Cycle::partial()
|
||||
.with_poly_chain_from_points(surface.clone(), points)
|
||||
.close_with_line_segment()
|
||||
.build(self.objects)
|
||||
.unwrap(),
|
||||
);
|
||||
self
|
||||
.with_poly_chain_from_points(surface, points)
|
||||
.close_with_line_segment(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with the provided interior polygons
|
||||
pub fn with_interiors(
|
||||
mut self,
|
||||
interiors: impl IntoIterator<Item = Handle<Cycle>>,
|
||||
) -> Self {
|
||||
self.interiors.extend(interiors);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with an interior polygon from the provided points
|
||||
pub fn with_interior_polygon_from_points(
|
||||
mut self,
|
||||
fn with_interior_polygon_from_points(
|
||||
self,
|
||||
points: impl IntoIterator<Item = impl Into<Point<2>>>,
|
||||
) -> Self {
|
||||
let surface = self
|
||||
.surface
|
||||
.as_ref()
|
||||
.expect("Need surface to build polygon.");
|
||||
let surface = self.surface().expect("Need surface to build polygon.");
|
||||
|
||||
self.interiors.push(
|
||||
Cycle::partial()
|
||||
.with_poly_chain_from_points(surface.clone(), points)
|
||||
.close_with_line_segment()
|
||||
.build(self.objects)
|
||||
.unwrap(),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with the provided color
|
||||
pub fn with_color(mut self, color: Color) -> Self {
|
||||
self.color = Some(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Construct a polygon from a list of points
|
||||
pub fn build(self) -> Handle<Face> {
|
||||
let exterior = self
|
||||
.exterior
|
||||
.expect("Can't build `Face` without exterior cycle");
|
||||
let color = self.color.unwrap_or_default();
|
||||
|
||||
self.objects
|
||||
.faces
|
||||
.insert(Face::new(exterior, self.interiors, color))
|
||||
.unwrap()
|
||||
self.with_interiors([Cycle::partial()
|
||||
.with_poly_chain_from_points(surface, points)
|
||||
.close_with_line_segment()])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
// These are the old-style builders that need to be transferred to the partial
|
||||
// object API. Issue:
|
||||
// https://github.com/hannobraun/Fornjot/issues/1147
|
||||
mod face;
|
||||
mod shell;
|
||||
mod sketch;
|
||||
mod solid;
|
||||
|
@ -12,6 +11,7 @@ mod solid;
|
|||
mod curve;
|
||||
mod cycle;
|
||||
mod edge;
|
||||
mod face;
|
||||
mod vertex;
|
||||
|
||||
pub use self::{
|
||||
|
|
|
@ -5,7 +5,7 @@ use fj_math::Scalar;
|
|||
|
||||
use crate::{
|
||||
algorithms::transform::TransformObject,
|
||||
builder::HalfEdgeBuilder,
|
||||
builder::{FaceBuilder, HalfEdgeBuilder},
|
||||
objects::{
|
||||
Curve, Cycle, Face, FaceSet, HalfEdge, Objects, Shell, Surface,
|
||||
SurfaceVertex, Vertex,
|
||||
|
@ -54,7 +54,7 @@ impl<'a> ShellBuilder<'a> {
|
|||
.translate([Z, Z, -h], self.objects)
|
||||
.unwrap();
|
||||
|
||||
Face::builder(self.objects)
|
||||
Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([
|
||||
[-h, -h],
|
||||
|
@ -62,7 +62,8 @@ impl<'a> ShellBuilder<'a> {
|
|||
[h, h],
|
||||
[-h, h],
|
||||
])
|
||||
.build()
|
||||
.build(self.objects)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let (sides, top_edges) = {
|
||||
|
@ -193,7 +194,10 @@ impl<'a> ShellBuilder<'a> {
|
|||
.build(self.objects)
|
||||
.unwrap();
|
||||
|
||||
Face::builder(self.objects).with_exterior(cycle).build()
|
||||
Face::partial()
|
||||
.with_exterior(cycle)
|
||||
.build(self.objects)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
(sides, tops)
|
||||
|
@ -259,11 +263,12 @@ impl<'a> ShellBuilder<'a> {
|
|||
);
|
||||
}
|
||||
|
||||
Face::builder(self.objects)
|
||||
Face::partial()
|
||||
.with_exterior(
|
||||
self.objects.cycles.insert(Cycle::new(edges)).unwrap(),
|
||||
)
|
||||
.build()
|
||||
.build(self.objects)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
self.faces.extend([bottom]);
|
||||
|
|
|
@ -2,9 +2,12 @@ use fj_math::Point;
|
|||
|
||||
use crate::{
|
||||
objects::{Face, FaceSet, Objects, Sketch, Surface},
|
||||
partial::HasPartial,
|
||||
storage::Handle,
|
||||
};
|
||||
|
||||
use super::FaceBuilder;
|
||||
|
||||
/// API for building a [`Sketch`]
|
||||
///
|
||||
/// Also see [`Sketch::builder`].
|
||||
|
@ -44,10 +47,11 @@ impl<'a> SketchBuilder<'a> {
|
|||
.surface
|
||||
.as_ref()
|
||||
.expect("Can't build `Sketch` without `Surface`");
|
||||
self.faces.extend([Face::builder(self.objects)
|
||||
self.faces.extend([Face::partial()
|
||||
.with_surface(surface.clone())
|
||||
.with_exterior_polygon_from_points(points)
|
||||
.build()]);
|
||||
.build(self.objects)
|
||||
.unwrap()]);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -360,7 +360,7 @@ impl<T> Iterator for Iter<T> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
builder::{CurveBuilder, CycleBuilder, HalfEdgeBuilder},
|
||||
builder::{CurveBuilder, CycleBuilder, FaceBuilder, HalfEdgeBuilder},
|
||||
objects::{
|
||||
Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Objects,
|
||||
Shell, Sketch, Solid, SurfaceVertex, Vertex,
|
||||
|
@ -424,10 +424,10 @@ mod tests {
|
|||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let object = Face::builder(&objects)
|
||||
let object = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]])
|
||||
.build();
|
||||
.build(&objects);
|
||||
|
||||
assert_eq!(3, object.curve_iter().count());
|
||||
assert_eq!(1, object.cycle_iter().count());
|
||||
|
@ -528,14 +528,14 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn sketch() {
|
||||
fn sketch() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let face = Face::builder(&objects)
|
||||
let face = Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points([[0., 0.], [1., 0.], [0., 1.]])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let object = Sketch::builder(&objects).with_faces([face]).build();
|
||||
|
||||
assert_eq!(3, object.curve_iter().count());
|
||||
|
@ -549,6 +549,8 @@ mod tests {
|
|||
assert_eq!(0, object.solid_iter().count());
|
||||
assert_eq!(1, object.surface_iter().count());
|
||||
assert_eq!(6, object.vertex_iter().count());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -3,9 +3,9 @@ use std::collections::{btree_set, BTreeSet};
|
|||
use fj_interop::mesh::Color;
|
||||
use fj_math::Winding;
|
||||
|
||||
use crate::{builder::FaceBuilder, storage::Handle};
|
||||
use crate::storage::Handle;
|
||||
|
||||
use super::{Cycle, Objects, Surface};
|
||||
use super::{Cycle, Surface};
|
||||
|
||||
/// A face of a shape
|
||||
///
|
||||
|
@ -39,17 +39,6 @@ pub struct Face {
|
|||
}
|
||||
|
||||
impl Face {
|
||||
/// Build a `Face` using [`FaceBuilder`]
|
||||
pub fn builder(objects: &Objects) -> FaceBuilder {
|
||||
FaceBuilder {
|
||||
objects,
|
||||
surface: None,
|
||||
exterior: None,
|
||||
interiors: Vec::new(),
|
||||
color: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a new instance of `Face`
|
||||
pub fn new(
|
||||
exterior: Handle<Cycle>,
|
||||
|
|
|
@ -45,6 +45,7 @@ pub use self::{
|
|||
curve::{PartialCurve, PartialGlobalCurve},
|
||||
cycle::PartialCycle,
|
||||
edge::{PartialGlobalEdge, PartialHalfEdge},
|
||||
face::PartialFace,
|
||||
vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex},
|
||||
},
|
||||
traits::{HasPartial, Partial},
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
use fj_interop::mesh::Color;
|
||||
|
||||
use crate::{
|
||||
objects::{Cycle, Face, Objects, Surface},
|
||||
partial::{util::merge_options, MaybePartial},
|
||||
storage::Handle,
|
||||
validate::ValidationError,
|
||||
};
|
||||
|
||||
/// A partial [`Face`]
|
||||
///
|
||||
/// See [`crate::partial`] for more information.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PartialFace {
|
||||
surface: Option<Handle<Surface>>,
|
||||
exterior: MaybePartial<Cycle>,
|
||||
interiors: Vec<MaybePartial<Cycle>>,
|
||||
color: Option<Color>,
|
||||
}
|
||||
|
||||
impl PartialFace {
|
||||
/// Access th surface that the [`Face`] is defined in
|
||||
pub fn surface(&self) -> Option<Handle<Surface>> {
|
||||
self.surface.clone()
|
||||
}
|
||||
|
||||
/// Access the [`Face`]'s exterior cycle
|
||||
pub fn exterior(&self) -> MaybePartial<Cycle> {
|
||||
self.exterior.clone()
|
||||
}
|
||||
|
||||
/// Access the [`Face`]'s interior cycles
|
||||
pub fn interiors(&self) -> impl Iterator<Item = MaybePartial<Cycle>> + '_ {
|
||||
self.interiors.iter().cloned()
|
||||
}
|
||||
|
||||
/// Access the color of the [`Face`]
|
||||
pub fn color(&self) -> Option<Color> {
|
||||
self.color
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with the provided surface
|
||||
pub fn with_surface(mut self, surface: Handle<Surface>) -> Self {
|
||||
self.surface = Some(surface);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with the provided exterior
|
||||
pub fn with_exterior(
|
||||
mut self,
|
||||
exterior: impl Into<MaybePartial<Cycle>>,
|
||||
) -> Self {
|
||||
self.exterior = exterior.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with the provided interior polygons
|
||||
pub fn with_interiors(
|
||||
mut self,
|
||||
interiors: impl IntoIterator<Item = impl Into<MaybePartial<Cycle>>>,
|
||||
) -> Self {
|
||||
let interiors = interiors.into_iter().map(Into::into);
|
||||
self.interiors.extend(interiors);
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the [`Face`] with the provided color
|
||||
pub fn with_color(mut self, color: Color) -> Self {
|
||||
self.color = Some(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
let mut interiors = self.interiors;
|
||||
interiors.extend(other.interiors);
|
||||
|
||||
Self {
|
||||
surface: merge_options(self.surface, other.surface),
|
||||
exterior: self.exterior.merge_with(other.exterior),
|
||||
interiors,
|
||||
color: merge_options(self.color, other.color),
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a polygon from a list of points
|
||||
pub fn build(
|
||||
self,
|
||||
objects: &Objects,
|
||||
) -> Result<Handle<Face>, ValidationError> {
|
||||
let exterior = self.exterior.into_full(objects)?;
|
||||
let interiors = self
|
||||
.interiors
|
||||
.into_iter()
|
||||
.map(|cycle| cycle.into_full(objects))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let color = self.color.unwrap_or_default();
|
||||
|
||||
Ok(objects
|
||||
.faces
|
||||
.insert(Face::new(exterior, interiors, color))?)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Face> for PartialFace {
|
||||
fn from(face: &Face) -> Self {
|
||||
Self {
|
||||
surface: Some(face.surface().clone()),
|
||||
exterior: face.exterior().clone().into(),
|
||||
interiors: face.interiors().cloned().map(Into::into).collect(),
|
||||
color: Some(face.color()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,19 @@
|
|||
pub mod curve;
|
||||
pub mod cycle;
|
||||
pub mod edge;
|
||||
pub mod face;
|
||||
pub mod vertex;
|
||||
|
||||
use crate::{
|
||||
objects::{
|
||||
Curve, Cycle, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
|
||||
SurfaceVertex, Vertex,
|
||||
Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge,
|
||||
Objects, SurfaceVertex, Vertex,
|
||||
},
|
||||
storage::Handle,
|
||||
};
|
||||
|
||||
use super::{
|
||||
HasPartial, MaybePartial, Partial, PartialCurve, PartialCycle,
|
||||
HasPartial, MaybePartial, Partial, PartialCurve, PartialCycle, PartialFace,
|
||||
PartialGlobalCurve, PartialGlobalEdge, PartialGlobalVertex,
|
||||
PartialHalfEdge, PartialSurfaceVertex, PartialVertex,
|
||||
};
|
||||
|
@ -53,6 +54,7 @@ macro_rules! impl_traits {
|
|||
impl_traits!(
|
||||
Curve, PartialCurve;
|
||||
Cycle, PartialCycle;
|
||||
Face, PartialFace;
|
||||
GlobalCurve, PartialGlobalCurve;
|
||||
GlobalEdge, PartialGlobalEdge;
|
||||
GlobalVertex, PartialGlobalVertex;
|
||||
|
|
|
@ -105,7 +105,7 @@ impl FaceValidationError {
|
|||
mod tests {
|
||||
use crate::{
|
||||
algorithms::reverse::Reverse,
|
||||
builder::CycleBuilder,
|
||||
builder::{CycleBuilder, FaceBuilder},
|
||||
objects::{Cycle, Face, Objects},
|
||||
partial::HasPartial,
|
||||
validate::Validate,
|
||||
|
@ -115,11 +115,11 @@ mod tests {
|
|||
fn face_surface_mismatch() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let valid = Face::builder(&objects)
|
||||
let valid = Face::partial()
|
||||
.with_surface(objects.surfaces.xy_plane())
|
||||
.with_exterior_polygon_from_points([[0., 0.], [3., 0.], [0., 3.]])
|
||||
.with_interior_polygon_from_points([[1., 1.], [1., 2.], [2., 1.]])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let invalid = {
|
||||
let interiors = [Cycle::partial()
|
||||
.with_poly_chain_from_points(
|
||||
|
@ -142,11 +142,11 @@ mod tests {
|
|||
fn face_invalid_interior_winding() -> anyhow::Result<()> {
|
||||
let objects = Objects::new();
|
||||
|
||||
let valid = Face::builder(&objects)
|
||||
let valid = Face::partial()
|
||||
.with_surface(objects.surfaces.xy_plane())
|
||||
.with_exterior_polygon_from_points([[0., 0.], [3., 0.], [0., 3.]])
|
||||
.with_interior_polygon_from_points([[1., 1.], [1., 2.], [2., 1.]])
|
||||
.build();
|
||||
.build(&objects)?;
|
||||
let invalid = {
|
||||
let interiors = valid
|
||||
.interiors()
|
||||
|
|
|
@ -5,6 +5,7 @@ use fj_kernel::{
|
|||
algorithms::reverse::Reverse,
|
||||
iter::ObjectIters,
|
||||
objects::{Face, Objects, Sketch},
|
||||
partial::HasPartial,
|
||||
validate::ValidationError,
|
||||
};
|
||||
use fj_math::Aabb;
|
||||
|
@ -78,11 +79,11 @@ impl Shape for fj::Difference2d {
|
|||
);
|
||||
|
||||
faces.push(
|
||||
Face::builder(objects)
|
||||
Face::partial()
|
||||
.with_exterior(exterior)
|
||||
.with_interiors(interiors)
|
||||
.with_color(Color(self.color()))
|
||||
.build(),
|
||||
.build(objects)?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::ops::Deref;
|
|||
|
||||
use fj_interop::{debug::DebugInfo, mesh::Color};
|
||||
use fj_kernel::{
|
||||
builder::HalfEdgeBuilder,
|
||||
builder::{FaceBuilder, HalfEdgeBuilder},
|
||||
objects::{Cycle, Face, HalfEdge, Objects, Sketch},
|
||||
partial::HasPartial,
|
||||
validate::ValidationError,
|
||||
|
@ -32,20 +32,20 @@ impl Shape for fj::Sketch {
|
|||
.build(objects)?;
|
||||
let cycle = objects.cycles.insert(Cycle::new([half_edge]))?;
|
||||
|
||||
Face::builder(objects)
|
||||
Face::partial()
|
||||
.with_exterior(cycle)
|
||||
.with_color(Color(self.color()))
|
||||
.build()
|
||||
.build(objects)?
|
||||
}
|
||||
fj::Chain::PolyChain(poly_chain) => {
|
||||
let points =
|
||||
poly_chain.to_points().into_iter().map(Point::from);
|
||||
|
||||
Face::builder(objects)
|
||||
Face::partial()
|
||||
.with_surface(surface)
|
||||
.with_exterior_polygon_from_points(points)
|
||||
.with_color(Color(self.color()))
|
||||
.build()
|
||||
.build(objects)?
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue