mirror of
https://github.com/hannobraun/Fornjot
synced 2025-09-23 17:48:29 +00:00
Merge pull request #2263 from hannobraun/surface
Fully move surface geometry to geometry layer
This commit is contained in:
commit
839e474875
@ -90,13 +90,18 @@ impl Approx for &Face {
|
||||
// would need to provide its own approximation, as the edges that bound
|
||||
// it have nothing to do with its curvature.
|
||||
|
||||
let exterior =
|
||||
(self.region().exterior().deref(), &self.surface().geometry())
|
||||
.approx_with_cache(tolerance, cache, core);
|
||||
let exterior = (
|
||||
self.region().exterior().deref(),
|
||||
&core.layers.geometry.of_surface(self.surface()),
|
||||
)
|
||||
.approx_with_cache(tolerance, cache, core);
|
||||
|
||||
let mut interiors = BTreeSet::new();
|
||||
for cycle in self.region().interiors() {
|
||||
let cycle = (cycle.deref(), &self.surface().geometry())
|
||||
let cycle = (
|
||||
cycle.deref(),
|
||||
&core.layers.geometry.of_surface(self.surface()),
|
||||
)
|
||||
.approx_with_cache(tolerance, cache, core);
|
||||
interiors.insert(cycle);
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
use fj_math::Aabb;
|
||||
|
||||
use crate::objects::Cycle;
|
||||
use crate::{geometry::Geometry, objects::Cycle};
|
||||
|
||||
impl super::BoundingVolume<2> for Cycle {
|
||||
fn aabb(&self) -> Option<Aabb<2>> {
|
||||
fn aabb(&self, geometry: &Geometry) -> Option<Aabb<2>> {
|
||||
let mut aabb: Option<Aabb<2>> = None;
|
||||
|
||||
for edge in self.half_edges() {
|
||||
let new_aabb = edge.aabb().expect("`Edge` can always compute AABB");
|
||||
let new_aabb =
|
||||
edge.aabb(geometry).expect("`Edge` can always compute AABB");
|
||||
aabb = Some(aabb.map_or(new_aabb, |aabb| aabb.merged(&new_aabb)));
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
use fj_math::{Aabb, Vector};
|
||||
|
||||
use crate::{geometry::SurfacePath, objects::HalfEdge};
|
||||
use crate::{
|
||||
geometry::{Geometry, SurfacePath},
|
||||
objects::HalfEdge,
|
||||
};
|
||||
|
||||
impl super::BoundingVolume<2> for HalfEdge {
|
||||
fn aabb(&self) -> Option<Aabb<2>> {
|
||||
fn aabb(&self, _: &Geometry) -> Option<Aabb<2>> {
|
||||
match self.path() {
|
||||
SurfacePath::Circle(circle) => {
|
||||
// Just calculate the AABB of the whole circle. This is not the
|
||||
|
@ -1,11 +1,14 @@
|
||||
use fj_math::Aabb;
|
||||
|
||||
use crate::{geometry::GlobalPath, objects::Face};
|
||||
use crate::{
|
||||
geometry::{Geometry, GlobalPath},
|
||||
objects::Face,
|
||||
};
|
||||
|
||||
impl super::BoundingVolume<3> for Face {
|
||||
fn aabb(&self) -> Option<Aabb<3>> {
|
||||
self.region().exterior().aabb().map(|aabb2| {
|
||||
let surface = self.surface().geometry();
|
||||
fn aabb(&self, geometry: &Geometry) -> Option<Aabb<3>> {
|
||||
self.region().exterior().aabb(geometry).map(|aabb2| {
|
||||
let surface = geometry.of_surface(self.surface());
|
||||
|
||||
match surface.u {
|
||||
GlobalPath::Circle(circle) => {
|
||||
|
@ -8,10 +8,12 @@ mod solid;
|
||||
|
||||
use fj_math::Aabb;
|
||||
|
||||
use crate::geometry::Geometry;
|
||||
|
||||
/// Compute a bounding volume for an object
|
||||
pub trait BoundingVolume<const D: usize> {
|
||||
/// Compute an axis-aligned bounding box (AABB)
|
||||
///
|
||||
/// Return `None`, if no AABB can be computed (if the object is empty).
|
||||
fn aabb(&self) -> Option<Aabb<D>>;
|
||||
fn aabb(&self, geometry: &Geometry) -> Option<Aabb<D>>;
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
use fj_math::Aabb;
|
||||
|
||||
use crate::objects::Shell;
|
||||
use crate::{geometry::Geometry, objects::Shell};
|
||||
|
||||
impl super::BoundingVolume<3> for Shell {
|
||||
fn aabb(&self) -> Option<Aabb<3>> {
|
||||
fn aabb(&self, geometry: &Geometry) -> Option<Aabb<3>> {
|
||||
let mut aabb: Option<Aabb<3>> = None;
|
||||
|
||||
for face in self.faces() {
|
||||
let new_aabb = face.aabb();
|
||||
let new_aabb = face.aabb(geometry);
|
||||
aabb = aabb.map_or(new_aabb, |aabb| match new_aabb {
|
||||
Some(new_aabb) => Some(aabb.merged(&new_aabb)),
|
||||
None => Some(aabb),
|
||||
|
@ -1,13 +1,13 @@
|
||||
use fj_math::Aabb;
|
||||
|
||||
use crate::objects::Solid;
|
||||
use crate::{geometry::Geometry, objects::Solid};
|
||||
|
||||
impl super::BoundingVolume<3> for Solid {
|
||||
fn aabb(&self) -> Option<Aabb<3>> {
|
||||
fn aabb(&self, geometry: &Geometry) -> Option<Aabb<3>> {
|
||||
let mut aabb: Option<Aabb<3>> = None;
|
||||
|
||||
for shell in self.shells() {
|
||||
let new_aabb = shell.aabb();
|
||||
let new_aabb = shell.aabb(geometry);
|
||||
aabb = aabb.map_or(new_aabb, |aabb| match new_aabb {
|
||||
Some(new_aabb) => Some(aabb.merged(&new_aabb)),
|
||||
None => Some(aabb),
|
||||
|
@ -161,12 +161,36 @@ mod tests {
|
||||
|
||||
let triangles = triangulate(face, &mut core)?;
|
||||
|
||||
let a = surface.geometry().point_from_surface_coords(a);
|
||||
let b = surface.geometry().point_from_surface_coords(b);
|
||||
let e = surface.geometry().point_from_surface_coords(e);
|
||||
let f = surface.geometry().point_from_surface_coords(f);
|
||||
let g = surface.geometry().point_from_surface_coords(g);
|
||||
let h = surface.geometry().point_from_surface_coords(h);
|
||||
let a = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(a);
|
||||
let b = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(b);
|
||||
let e = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(e);
|
||||
let f = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(f);
|
||||
let g = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(g);
|
||||
let h = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(h);
|
||||
|
||||
// Let's test that some correct triangles are present. We don't need to
|
||||
// test them all.
|
||||
@ -224,11 +248,31 @@ mod tests {
|
||||
|
||||
let triangles = triangulate(face, &mut core)?;
|
||||
|
||||
let a = surface.geometry().point_from_surface_coords(a);
|
||||
let b = surface.geometry().point_from_surface_coords(b);
|
||||
let c = surface.geometry().point_from_surface_coords(c);
|
||||
let d = surface.geometry().point_from_surface_coords(d);
|
||||
let e = surface.geometry().point_from_surface_coords(e);
|
||||
let a = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(a);
|
||||
let b = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(b);
|
||||
let c = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(c);
|
||||
let d = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(d);
|
||||
let e = core
|
||||
.layers
|
||||
.geometry
|
||||
.of_surface(&surface)
|
||||
.point_from_surface_coords(e);
|
||||
|
||||
assert!(triangles.contains_triangle([a, b, d]));
|
||||
assert!(triangles.contains_triangle([a, d, e]));
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Layer infrastructure for [`Objects`]
|
||||
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
objects::{AboutToBeStored, AnyObject, Objects},
|
||||
validation::Validation,
|
||||
};
|
||||
@ -14,6 +15,7 @@ impl Layer<Objects> {
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
object: AnyObject<AboutToBeStored>,
|
||||
geometry: &Geometry,
|
||||
validation: &mut Layer<Validation>,
|
||||
) {
|
||||
let mut events = Vec::new();
|
||||
@ -22,6 +24,7 @@ impl Layer<Objects> {
|
||||
for event in events {
|
||||
let event = ValidateObject {
|
||||
object: event.object.into(),
|
||||
geometry,
|
||||
};
|
||||
validation.process(event, &mut Vec::new());
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Layer infrastructure for [`Validation`]
|
||||
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
objects::{AnyObject, Stored},
|
||||
validation::{Validation, ValidationError, ValidationErrors},
|
||||
};
|
||||
@ -15,18 +16,22 @@ impl Layer<Validation> {
|
||||
}
|
||||
|
||||
/// Validate an object
|
||||
pub struct ValidateObject {
|
||||
pub struct ValidateObject<'r> {
|
||||
/// The object to validate
|
||||
pub object: AnyObject<Stored>,
|
||||
|
||||
/// Reference to `Geometry`, which is required for validation
|
||||
pub geometry: &'r Geometry,
|
||||
}
|
||||
|
||||
impl Command<Validation> for ValidateObject {
|
||||
impl Command<Validation> for ValidateObject<'_> {
|
||||
type Result = ();
|
||||
type Event = ValidationFailed;
|
||||
|
||||
fn decide(self, state: &Validation, events: &mut Vec<Self::Event>) {
|
||||
let mut errors = Vec::new();
|
||||
self.object.validate(&state.config, &mut errors);
|
||||
self.object
|
||||
.validate(&state.config, &mut errors, self.geometry);
|
||||
|
||||
for err in errors {
|
||||
events.push(ValidationFailed {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
objects::{
|
||||
Curve, Cycle, Face, HalfEdge, Objects, Region, Shell, Sketch, Solid,
|
||||
Surface, Vertex,
|
||||
@ -38,10 +39,15 @@ macro_rules! any_object {
|
||||
pub fn validate(&self,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
geometry: &Geometry,
|
||||
) {
|
||||
match self {
|
||||
$(
|
||||
Self::$ty(object) => object.validate(config, errors),
|
||||
Self::$ty(object) => object.validate(
|
||||
config,
|
||||
errors,
|
||||
geometry,
|
||||
),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use fj_math::Winding;
|
||||
|
||||
use crate::{
|
||||
objects::{Region, Surface},
|
||||
storage::Handle,
|
||||
storage::{Handle, HandleWrapper},
|
||||
};
|
||||
|
||||
/// A face of a shape
|
||||
@ -31,14 +31,17 @@ use crate::{
|
||||
/// [`Shell`]: crate::objects::Shell
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct Face {
|
||||
surface: Handle<Surface>,
|
||||
surface: HandleWrapper<Surface>,
|
||||
region: Handle<Region>,
|
||||
}
|
||||
|
||||
impl Face {
|
||||
/// Construct an instance of `Face`
|
||||
pub fn new(surface: Handle<Surface>, region: Handle<Region>) -> Self {
|
||||
Self { surface, region }
|
||||
Self {
|
||||
surface: surface.into(),
|
||||
region,
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the surface of the face
|
||||
|
@ -1,19 +1,25 @@
|
||||
use crate::geometry::SurfaceGeometry;
|
||||
|
||||
/// A two-dimensional shape
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct Surface {
|
||||
geometry: SurfaceGeometry,
|
||||
}
|
||||
///
|
||||
///
|
||||
/// ## Equality
|
||||
///
|
||||
/// `Surface` contains no data and exists purely to be referenced via a
|
||||
/// `Handle`, where `Handle::id` can be used to compare different instances of
|
||||
/// it.
|
||||
///
|
||||
/// If `Surface` had `Eq`/`PartialEq` implementations, it containing no data
|
||||
/// would mean that all instances of `Surface` would be considered equal. This
|
||||
/// would be very error-prone.
|
||||
///
|
||||
/// If you need to reference a `Surface` from a struct that needs to derive
|
||||
/// `Eq`/`Ord`/..., you can use `HandleWrapper<Vertex>` to do that. It will
|
||||
/// use `Handle::id` to provide those `Eq`/`Ord`/... implementations.
|
||||
#[derive(Clone, Copy, Debug, Default, Hash)]
|
||||
pub struct Surface {}
|
||||
|
||||
impl Surface {
|
||||
/// Construct an instance of `Surface`
|
||||
pub fn new(geometry: SurfaceGeometry) -> Self {
|
||||
Self { geometry }
|
||||
}
|
||||
|
||||
/// Access the surface's geometry
|
||||
pub fn geometry(&self) -> SurfaceGeometry {
|
||||
self.geometry
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@
|
||||
/// ## Equality
|
||||
///
|
||||
/// `Vertex` contains no data and exists purely to be referenced via a `Handle`,
|
||||
/// where `Handle::id` can be used to compare different instances of `Vertex`.
|
||||
/// where `Handle::id` can be used to compare different instances of it.
|
||||
///
|
||||
/// If `Vertex` had `Eq`/`PartialEq` implementations, it containing no data
|
||||
/// would mean that all instances of `Vertex` would be considered equal. This
|
||||
|
@ -1,9 +1,4 @@
|
||||
use fj_math::Vector;
|
||||
|
||||
use crate::{
|
||||
geometry::{GlobalPath, SurfaceGeometry},
|
||||
storage::{Handle, Store},
|
||||
};
|
||||
use crate::storage::{Handle, Store};
|
||||
|
||||
use super::{
|
||||
Curve, Cycle, Face, HalfEdge, Region, Shell, Sketch, Solid, Surface, Vertex,
|
||||
@ -92,30 +87,13 @@ impl Default for Surfaces {
|
||||
let mut store: Store<Surface> = Store::new();
|
||||
|
||||
let xy_plane = store.reserve();
|
||||
store.insert(
|
||||
xy_plane.clone(),
|
||||
Surface::new(SurfaceGeometry {
|
||||
u: GlobalPath::x_axis(),
|
||||
v: Vector::unit_y(),
|
||||
}),
|
||||
);
|
||||
store.insert(xy_plane.clone(), Surface::new());
|
||||
|
||||
let xz_plane = store.reserve();
|
||||
store.insert(
|
||||
xz_plane.clone(),
|
||||
Surface::new(SurfaceGeometry {
|
||||
u: GlobalPath::x_axis(),
|
||||
v: Vector::unit_z(),
|
||||
}),
|
||||
);
|
||||
store.insert(xz_plane.clone(), Surface::new());
|
||||
|
||||
let yz_plane = store.reserve();
|
||||
store.insert(
|
||||
yz_plane.clone(),
|
||||
Surface::new(SurfaceGeometry {
|
||||
u: GlobalPath::y_axis(),
|
||||
v: Vector::unit_z(),
|
||||
}),
|
||||
);
|
||||
store.insert(yz_plane.clone(), Surface::new());
|
||||
|
||||
Self {
|
||||
store,
|
||||
|
@ -43,15 +43,15 @@ pub trait BuildSurface {
|
||||
v: impl Into<Vector<3>>,
|
||||
core: &mut Core,
|
||||
) -> Handle<Surface> {
|
||||
let geometry = SurfaceGeometry {
|
||||
u: u.into(),
|
||||
v: v.into(),
|
||||
};
|
||||
let surface = Surface::new(geometry).insert(core);
|
||||
let surface = Surface::new().insert(core);
|
||||
|
||||
core.layers
|
||||
.geometry
|
||||
.define_surface(surface.clone(), geometry);
|
||||
core.layers.geometry.define_surface(
|
||||
surface.clone(),
|
||||
SurfaceGeometry {
|
||||
u: u.into(),
|
||||
v: v.into(),
|
||||
},
|
||||
);
|
||||
|
||||
surface
|
||||
}
|
||||
|
@ -99,10 +99,9 @@ impl AddHole for Shell {
|
||||
|
||||
let path = {
|
||||
let point = |location: &HoleLocation| {
|
||||
location
|
||||
.face
|
||||
.surface()
|
||||
.geometry()
|
||||
core.layers
|
||||
.geometry
|
||||
.of_surface(location.face.surface())
|
||||
.point_from_surface_coords(location.position)
|
||||
};
|
||||
|
||||
|
@ -42,6 +42,7 @@ macro_rules! impl_insert {
|
||||
let object = (handle.clone(), self).into();
|
||||
core.layers.objects.insert(
|
||||
object,
|
||||
&core.layers.geometry,
|
||||
&mut core.layers.validation,
|
||||
);
|
||||
handle
|
||||
|
@ -55,7 +55,7 @@ impl SweepRegion for Region {
|
||||
|
||||
let top_exterior = sweep_cycle(
|
||||
self.exterior(),
|
||||
&surface.geometry(),
|
||||
&core.layers.geometry.of_surface(surface),
|
||||
color,
|
||||
&mut faces,
|
||||
path,
|
||||
@ -69,7 +69,7 @@ impl SweepRegion for Region {
|
||||
.map(|bottom_cycle| {
|
||||
sweep_cycle(
|
||||
bottom_cycle,
|
||||
&surface.geometry(),
|
||||
&core.layers.geometry.of_surface(surface),
|
||||
color,
|
||||
&mut faces,
|
||||
path,
|
||||
|
@ -43,14 +43,14 @@ impl SweepSketch for Sketch {
|
||||
assert!(region.exterior().winding().is_ccw());
|
||||
|
||||
let is_negative_sweep = {
|
||||
let u = match surface.geometry().u {
|
||||
let u = match core.layers.geometry.of_surface(&surface).u {
|
||||
GlobalPath::Circle(_) => todo!(
|
||||
"Sweeping sketch from a rounded surfaces is not \
|
||||
supported"
|
||||
),
|
||||
GlobalPath::Line(line) => line.direction(),
|
||||
};
|
||||
let v = surface.geometry().v;
|
||||
let v = core.layers.geometry.of_surface(&surface).v;
|
||||
|
||||
let normal = u.cross(&v);
|
||||
|
||||
|
@ -16,9 +16,10 @@ impl TransformObject for Handle<Surface> {
|
||||
cache
|
||||
.entry(self)
|
||||
.or_insert_with(|| {
|
||||
let geometry = self.geometry().transform(transform);
|
||||
let surface = Surface::new(geometry).insert(core);
|
||||
let surface = Surface::new().insert(core);
|
||||
|
||||
let geometry =
|
||||
core.layers.geometry.of_surface(self).transform(transform);
|
||||
core.layers
|
||||
.geometry
|
||||
.define_surface(surface.clone(), geometry);
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
objects::Curve,
|
||||
validation::{ValidationConfig, ValidationError},
|
||||
};
|
||||
@ -6,5 +7,11 @@ use crate::{
|
||||
use super::Validate;
|
||||
|
||||
impl Validate for Curve {
|
||||
fn validate(&self, _: &ValidationConfig, _: &mut Vec<ValidationError>) {}
|
||||
fn validate(
|
||||
&self,
|
||||
_: &ValidationConfig,
|
||||
_: &mut Vec<ValidationError>,
|
||||
_: &Geometry,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
objects::Cycle,
|
||||
validation::{
|
||||
checks::AdjacentHalfEdgesNotConnected, ValidationCheck,
|
||||
@ -13,6 +14,7 @@ impl Validate for Cycle {
|
||||
&self,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
_: &Geometry,
|
||||
) {
|
||||
errors.extend(
|
||||
AdjacentHalfEdgesNotConnected::check(self, config).map(Into::into),
|
||||
|
@ -1,6 +1,7 @@
|
||||
use fj_math::{Point, Scalar};
|
||||
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
objects::HalfEdge,
|
||||
validation::{ValidationConfig, ValidationError},
|
||||
};
|
||||
@ -12,6 +13,7 @@ impl Validate for HalfEdge {
|
||||
&self,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
_: &Geometry,
|
||||
) {
|
||||
EdgeValidationError::check_vertex_coincidence(self, config, errors);
|
||||
}
|
||||
@ -95,8 +97,9 @@ mod tests {
|
||||
)
|
||||
};
|
||||
|
||||
valid.validate_and_return_first_error()?;
|
||||
valid.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid,
|
||||
ValidationError::Edge(
|
||||
EdgeValidationError::VerticesAreCoincident { .. }
|
||||
|
@ -1,6 +1,7 @@
|
||||
use fj_math::Winding;
|
||||
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
objects::Face,
|
||||
validation::{ValidationConfig, ValidationError},
|
||||
};
|
||||
@ -12,6 +13,7 @@ impl Validate for Face {
|
||||
&self,
|
||||
_: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
_: &Geometry,
|
||||
) {
|
||||
FaceValidationError::check_boundary(self, errors);
|
||||
FaceValidationError::check_interior_winding(self, errors);
|
||||
@ -123,8 +125,9 @@ mod tests {
|
||||
&mut core,
|
||||
);
|
||||
|
||||
valid.validate_and_return_first_error()?;
|
||||
valid.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid,
|
||||
ValidationError::Face(FaceValidationError::MissingBoundary)
|
||||
);
|
||||
@ -181,8 +184,9 @@ mod tests {
|
||||
Face::new(valid.surface().clone(), region)
|
||||
};
|
||||
|
||||
valid.validate_and_return_first_error()?;
|
||||
valid.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid,
|
||||
ValidationError::Face(
|
||||
FaceValidationError::InvalidInteriorWinding { .. }
|
||||
|
@ -73,7 +73,10 @@ mod solid;
|
||||
mod surface;
|
||||
mod vertex;
|
||||
|
||||
use crate::validation::{ValidationConfig, ValidationError};
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
validation::{ValidationConfig, ValidationError},
|
||||
};
|
||||
|
||||
pub use self::{
|
||||
edge::EdgeValidationError, face::FaceValidationError,
|
||||
@ -85,12 +88,13 @@ pub use self::{
|
||||
/// pattern. This is preferred to matching on [`Validate::validate_and_return_first_error`], since usually we don't care about the order.
|
||||
#[macro_export]
|
||||
macro_rules! assert_contains_err {
|
||||
($o:tt,$p:pat) => {
|
||||
($core:expr, $o:expr, $p:pat) => {
|
||||
assert!({
|
||||
let mut errors = Vec::new();
|
||||
$o.validate(
|
||||
&$crate::validation::ValidationConfig::default(),
|
||||
&mut errors,
|
||||
&$core.layers.geometry,
|
||||
);
|
||||
errors.iter().any(|e| matches!(e, $p))
|
||||
})
|
||||
@ -103,9 +107,12 @@ macro_rules! assert_contains_err {
|
||||
pub trait Validate: Sized {
|
||||
/// Validate the object using default config and return on first error
|
||||
#[allow(clippy::result_large_err)]
|
||||
fn validate_and_return_first_error(&self) -> Result<(), ValidationError> {
|
||||
fn validate_and_return_first_error(
|
||||
&self,
|
||||
geometry: &Geometry,
|
||||
) -> Result<(), ValidationError> {
|
||||
let mut errors = Vec::new();
|
||||
self.validate(&ValidationConfig::default(), &mut errors);
|
||||
self.validate(&ValidationConfig::default(), &mut errors, geometry);
|
||||
|
||||
if let Some(err) = errors.into_iter().next() {
|
||||
return Err(err);
|
||||
@ -119,5 +126,6 @@ pub trait Validate: Sized {
|
||||
&self,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
geometry: &Geometry,
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
use crate::objects::Region;
|
||||
use crate::{geometry::Geometry, objects::Region};
|
||||
|
||||
use super::{Validate, ValidationConfig, ValidationError};
|
||||
|
||||
impl Validate for Region {
|
||||
fn validate(&self, _: &ValidationConfig, _: &mut Vec<ValidationError>) {}
|
||||
fn validate(
|
||||
&self,
|
||||
_: &ValidationConfig,
|
||||
_: &mut Vec<ValidationError>,
|
||||
_: &Geometry,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::{collections::BTreeMap, fmt};
|
||||
use fj_math::{Point, Scalar};
|
||||
|
||||
use crate::{
|
||||
geometry::{CurveBoundary, SurfaceGeometry},
|
||||
geometry::{CurveBoundary, Geometry, SurfaceGeometry},
|
||||
objects::{Curve, HalfEdge, Shell, Vertex},
|
||||
queries::{
|
||||
AllHalfEdgesWithSurface, BoundingVerticesOfHalfEdge, SiblingOfHalfEdge,
|
||||
@ -18,10 +18,15 @@ impl Validate for Shell {
|
||||
&self,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
geometry: &Geometry,
|
||||
) {
|
||||
ShellValidationError::check_curve_coordinates(self, config, errors);
|
||||
ShellValidationError::check_curve_coordinates(
|
||||
self, geometry, config, errors,
|
||||
);
|
||||
ShellValidationError::check_half_edge_pairs(self, errors);
|
||||
ShellValidationError::check_half_edge_coincidence(self, config, errors);
|
||||
ShellValidationError::check_half_edge_coincidence(
|
||||
self, geometry, config, errors,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +80,7 @@ impl ShellValidationError {
|
||||
/// Check that local curve definitions that refer to the same curve match
|
||||
fn check_curve_coordinates(
|
||||
shell: &Shell,
|
||||
geometry: &Geometry,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
) {
|
||||
@ -139,17 +145,17 @@ impl ShellValidationError {
|
||||
|
||||
compare_curve_coords(
|
||||
edge_a,
|
||||
&surface_a.geometry(),
|
||||
&geometry.of_surface(surface_a),
|
||||
edge_b,
|
||||
&surface_b.geometry(),
|
||||
&geometry.of_surface(surface_b),
|
||||
config,
|
||||
&mut mismatches,
|
||||
);
|
||||
compare_curve_coords(
|
||||
edge_b,
|
||||
&surface_b.geometry(),
|
||||
&geometry.of_surface(surface_b),
|
||||
edge_a,
|
||||
&surface_a.geometry(),
|
||||
&geometry.of_surface(surface_a),
|
||||
config,
|
||||
&mut mismatches,
|
||||
);
|
||||
@ -208,6 +214,7 @@ impl ShellValidationError {
|
||||
/// Check that non-sibling half-edges are not coincident
|
||||
fn check_half_edge_coincidence(
|
||||
shell: &Shell,
|
||||
geometry: &Geometry,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
) {
|
||||
@ -235,9 +242,9 @@ impl ShellValidationError {
|
||||
// `distinct_min_distance`, that's a problem.
|
||||
if distances(
|
||||
half_edge_a.clone(),
|
||||
&surface_a.geometry(),
|
||||
&geometry.of_surface(surface_a),
|
||||
half_edge_b.clone(),
|
||||
&surface_b.geometry(),
|
||||
&geometry.of_surface(surface_b),
|
||||
)
|
||||
.all(|d| d < config.distinct_min_distance)
|
||||
{
|
||||
@ -441,8 +448,11 @@ mod tests {
|
||||
&mut core,
|
||||
);
|
||||
|
||||
valid.shell.validate_and_return_first_error()?;
|
||||
valid
|
||||
.shell
|
||||
.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid,
|
||||
ValidationError::Shell(
|
||||
ShellValidationError::CurveCoordinateSystemMismatch(..)
|
||||
@ -462,8 +472,11 @@ mod tests {
|
||||
);
|
||||
let invalid = valid.shell.remove_face(&valid.abc.face);
|
||||
|
||||
valid.shell.validate_and_return_first_error()?;
|
||||
valid
|
||||
.shell
|
||||
.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid,
|
||||
ValidationError::Shell(
|
||||
ShellValidationError::HalfEdgeHasNoSibling { .. }
|
||||
@ -508,8 +521,11 @@ mod tests {
|
||||
&mut core,
|
||||
);
|
||||
|
||||
valid.shell.validate_and_return_first_error()?;
|
||||
valid
|
||||
.shell
|
||||
.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid,
|
||||
ValidationError::Shell(
|
||||
ShellValidationError::CoincidentHalfEdgesAreNotSiblings { .. }
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::geometry::Geometry;
|
||||
use crate::{objects::Cycle, storage::Handle};
|
||||
use crate::{objects::Sketch, validate_references};
|
||||
use fj_math::Winding;
|
||||
@ -12,6 +13,7 @@ impl Validate for Sketch {
|
||||
&self,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
_: &Geometry,
|
||||
) {
|
||||
SketchValidationError::check_object_references(self, config, errors);
|
||||
SketchValidationError::check_exterior_cycles(self, config, errors);
|
||||
@ -130,7 +132,7 @@ mod tests {
|
||||
let region = <Region as BuildRegion>::circle([0., 0.], 1., &mut core)
|
||||
.insert(&mut core);
|
||||
let valid_sketch = Sketch::new(vec![region.clone()]).insert(&mut core);
|
||||
valid_sketch.validate_and_return_first_error()?;
|
||||
valid_sketch.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
|
||||
let shared_cycle = region.exterior();
|
||||
let invalid_sketch = Sketch::new(vec![
|
||||
@ -138,6 +140,7 @@ mod tests {
|
||||
Region::new(shared_cycle.clone(), vec![]).insert(&mut core),
|
||||
]);
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_sketch,
|
||||
ValidationError::Sketch(SketchValidationError::MultipleReferences(
|
||||
ReferenceCountError::Cycle { references: _ }
|
||||
@ -157,7 +160,7 @@ mod tests {
|
||||
)
|
||||
.insert(&mut core);
|
||||
let valid_sketch = Sketch::new(vec![region.clone()]).insert(&mut core);
|
||||
valid_sketch.validate_and_return_first_error()?;
|
||||
valid_sketch.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
|
||||
let exterior = region.exterior();
|
||||
let cloned_edges: Vec<_> =
|
||||
@ -169,6 +172,7 @@ mod tests {
|
||||
Region::new(exterior.clone(), vec![interior]).insert(&mut core)
|
||||
]);
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_sketch,
|
||||
ValidationError::Sketch(SketchValidationError::MultipleReferences(
|
||||
ReferenceCountError::HalfEdge { references: _ }
|
||||
@ -190,7 +194,7 @@ mod tests {
|
||||
Sketch::new(vec![
|
||||
Region::new(valid_exterior.clone(), vec![]).insert(&mut core)
|
||||
]);
|
||||
valid_sketch.validate_and_return_first_error()?;
|
||||
valid_sketch.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
|
||||
let invalid_outer_circle = HalfEdge::from_sibling(
|
||||
&valid_outer_circle,
|
||||
@ -204,6 +208,7 @@ mod tests {
|
||||
Region::new(invalid_exterior.clone(), vec![]).insert(&mut core)
|
||||
]);
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_sketch,
|
||||
ValidationError::Sketch(
|
||||
SketchValidationError::ClockwiseExteriorCycle { cycle: _ }
|
||||
@ -235,7 +240,7 @@ mod tests {
|
||||
vec![valid_interior],
|
||||
)
|
||||
.insert(&mut core)]);
|
||||
valid_sketch.validate_and_return_first_error()?;
|
||||
valid_sketch.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
|
||||
let invalid_interior =
|
||||
Cycle::new(vec![inner_circle.clone()]).insert(&mut core);
|
||||
@ -245,6 +250,7 @@ mod tests {
|
||||
)
|
||||
.insert(&mut core)]);
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_sketch,
|
||||
ValidationError::Sketch(
|
||||
SketchValidationError::CounterClockwiseInteriorCycle {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::iter::repeat;
|
||||
|
||||
use crate::{
|
||||
geometry::Geometry,
|
||||
objects::{Solid, Vertex},
|
||||
storage::Handle,
|
||||
validate_references,
|
||||
@ -17,8 +18,9 @@ impl Validate for Solid {
|
||||
&self,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
geometry: &Geometry,
|
||||
) {
|
||||
SolidValidationError::check_vertices(self, config, errors);
|
||||
SolidValidationError::check_vertices(self, geometry, config, errors);
|
||||
SolidValidationError::check_object_references(self, config, errors);
|
||||
}
|
||||
}
|
||||
@ -74,6 +76,7 @@ pub enum SolidValidationError {
|
||||
impl SolidValidationError {
|
||||
fn check_vertices(
|
||||
solid: &Solid,
|
||||
geometry: &Geometry,
|
||||
config: &ValidationConfig,
|
||||
errors: &mut Vec<ValidationError>,
|
||||
) {
|
||||
@ -85,7 +88,7 @@ impl SolidValidationError {
|
||||
face.region()
|
||||
.all_cycles()
|
||||
.flat_map(|cycle| cycle.half_edges().iter().cloned())
|
||||
.zip(repeat(face.surface().geometry()))
|
||||
.zip(repeat(geometry.of_surface(face.surface())))
|
||||
})
|
||||
.map(|(h, s)| {
|
||||
(
|
||||
@ -225,6 +228,7 @@ mod tests {
|
||||
.insert(&mut core);
|
||||
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_solid,
|
||||
ValidationError::Solid(SolidValidationError::MultipleReferences(
|
||||
ReferenceCountError::Face { references: _ }
|
||||
@ -232,7 +236,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let valid_solid = Solid::new(vec![]).insert(&mut core);
|
||||
valid_solid.validate_and_return_first_error()?;
|
||||
valid_solid.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
|
||||
// Ignore remaining validation errors.
|
||||
let _ = core.layers.validation.take_errors();
|
||||
@ -277,6 +281,7 @@ mod tests {
|
||||
.insert(&mut core);
|
||||
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_solid,
|
||||
ValidationError::Solid(SolidValidationError::MultipleReferences(
|
||||
ReferenceCountError::Region { references: _ }
|
||||
@ -284,7 +289,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let valid_solid = Solid::new(vec![]).insert(&mut core);
|
||||
valid_solid.validate_and_return_first_error()?;
|
||||
valid_solid.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
|
||||
// Ignore remaining validation errors.
|
||||
let _ = core.layers.validation.take_errors();
|
||||
@ -326,6 +331,7 @@ mod tests {
|
||||
.insert(&mut core);
|
||||
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_solid,
|
||||
ValidationError::Solid(SolidValidationError::MultipleReferences(
|
||||
ReferenceCountError::Cycle { references: _ }
|
||||
@ -333,7 +339,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let valid_solid = Solid::new(vec![]).insert(&mut core);
|
||||
valid_solid.validate_and_return_first_error()?;
|
||||
valid_solid.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
|
||||
// Ignore remaining validation errors.
|
||||
let _ = core.layers.validation.take_errors();
|
||||
@ -365,6 +371,7 @@ mod tests {
|
||||
.insert(&mut core);
|
||||
|
||||
assert_contains_err!(
|
||||
core,
|
||||
invalid_solid,
|
||||
ValidationError::Solid(SolidValidationError::MultipleReferences(
|
||||
ReferenceCountError::HalfEdge { references: _ }
|
||||
@ -372,7 +379,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let valid_solid = Solid::new(vec![]).insert(&mut core);
|
||||
valid_solid.validate_and_return_first_error()?;
|
||||
valid_solid.validate_and_return_first_error(&core.layers.geometry)?;
|
||||
|
||||
// Ignore remaining validation errors.
|
||||
let _ = core.layers.validation.take_errors();
|
||||
|
@ -1,7 +1,13 @@
|
||||
use crate::objects::Surface;
|
||||
use crate::{geometry::Geometry, objects::Surface};
|
||||
|
||||
use super::{Validate, ValidationConfig, ValidationError};
|
||||
|
||||
impl Validate for Surface {
|
||||
fn validate(&self, _: &ValidationConfig, _: &mut Vec<ValidationError>) {}
|
||||
fn validate(
|
||||
&self,
|
||||
_: &ValidationConfig,
|
||||
_: &mut Vec<ValidationError>,
|
||||
_: &Geometry,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
use crate::objects::Vertex;
|
||||
use crate::{geometry::Geometry, objects::Vertex};
|
||||
|
||||
use super::{Validate, ValidationConfig, ValidationError};
|
||||
|
||||
impl Validate for Vertex {
|
||||
fn validate(&self, _: &ValidationConfig, _: &mut Vec<ValidationError>) {}
|
||||
fn validate(
|
||||
&self,
|
||||
_: &ValidationConfig,
|
||||
_: &mut Vec<ValidationError>,
|
||||
_: &Geometry,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ impl Instance {
|
||||
self.core.layers.validation.take_errors()?;
|
||||
}
|
||||
|
||||
let aabb = model.aabb().unwrap_or(Aabb {
|
||||
let aabb = model.aabb(&self.core.layers.geometry).unwrap_or(Aabb {
|
||||
min: Point::origin(),
|
||||
max: Point::origin(),
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user