Merge pull request #2263 from hannobraun/surface

Fully move surface geometry to geometry layer
This commit is contained in:
Hanno Braun 2024-03-13 14:59:20 +01:00 committed by GitHub
commit 839e474875
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 265 additions and 134 deletions

View File

@ -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);
}

View File

@ -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)));
}

View File

@ -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

View File

@ -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) => {

View File

@ -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>>;
}

View File

@ -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),

View File

@ -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),

View File

@ -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]));

View File

@ -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());
}

View File

@ -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 {

View File

@ -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,
),
)*
}
}

View File

@ -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

View File

@ -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()
}
}

View File

@ -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

View File

@ -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,

View File

@ -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
}

View File

@ -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)
};

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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,
) {
}
}

View File

@ -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),

View File

@ -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 { .. }

View File

@ -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 { .. }

View File

@ -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,
);
}

View File

@ -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,
) {
}
}

View File

@ -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 { .. }

View File

@ -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 {

View File

@ -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();

View File

@ -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,
) {
}
}

View File

@ -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,
) {
}
}

View File

@ -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(),
});