From 83bf61afd2a78e376a146f2930d6b87b24b734b3 Mon Sep 17 00:00:00 2001 From: A-Walrus Date: Tue, 21 Mar 2023 08:01:44 +0200 Subject: [PATCH] Move vertex coincidence validation to solid Before it was in shell. This partially addresses #1689, holding off on validating sketches until we figure out sketch-face relationship: #1691 --- crates/fj-kernel/src/validate/cycle.rs | 1 + crates/fj-kernel/src/validate/mod.rs | 11 ++- crates/fj-kernel/src/validate/shell.rs | 74 +------------------- crates/fj-kernel/src/validate/solid.rs | 93 +++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 76 deletions(-) diff --git a/crates/fj-kernel/src/validate/cycle.rs b/crates/fj-kernel/src/validate/cycle.rs index 5821519a10..7b638f438f 100644 --- a/crates/fj-kernel/src/validate/cycle.rs +++ b/crates/fj-kernel/src/validate/cycle.rs @@ -42,6 +42,7 @@ pub enum CycleValidationError { /// The half-edge half_edges: Box<(HalfEdge, HalfEdge)>, }, + /// [`Cycle`]'s should have at least one `HalfEdge` #[error("Expected at least one `HalfEdge`\n")] NotEnoughHalfEdges, } diff --git a/crates/fj-kernel/src/validate/mod.rs b/crates/fj-kernel/src/validate/mod.rs index 30f6b97a98..7d155d3d31 100644 --- a/crates/fj-kernel/src/validate/mod.rs +++ b/crates/fj-kernel/src/validate/mod.rs @@ -9,8 +9,11 @@ mod solid; mod surface; mod vertex; -use self::{cycle::CycleValidationError, shell::ShellValidationError}; -pub use self::{edge::HalfEdgeValidationError, face::FaceValidationError}; +pub use self::{ + cycle::CycleValidationError, edge::HalfEdgeValidationError, + face::FaceValidationError, shell::ShellValidationError, + solid::SolidValidationError, +}; use std::convert::Infallible; @@ -109,6 +112,10 @@ pub enum ValidationError { /// `Shell` validation error #[error("`Shell` validation error:\n{0}")] Shell(#[from] ShellValidationError), + + /// `Solid` validation error + #[error("`Solid` validation error:\n{0}")] + Solid(#[from] SolidValidationError), } impl From for ValidationError { diff --git a/crates/fj-kernel/src/validate/shell.rs b/crates/fj-kernel/src/validate/shell.rs index f6b21813df..3f1bc690ad 100644 --- a/crates/fj-kernel/src/validate/shell.rs +++ b/crates/fj-kernel/src/validate/shell.rs @@ -4,7 +4,7 @@ use fj_math::{Point, Scalar}; use crate::{ geometry::surface::SurfaceGeometry, - objects::{HalfEdge, Shell, Vertex}, + objects::{HalfEdge, Shell}, storage::{Handle, ObjectId}, }; @@ -16,7 +16,7 @@ impl Validate for Shell { config: &ValidationConfig, errors: &mut Vec, ) { - ShellValidationError::validate_coincident(self, config, errors); + ShellValidationError::validate_edges_coincident(self, config, errors); ShellValidationError::validate_watertight(self, config, errors); } } @@ -43,24 +43,6 @@ pub enum ShellValidationError { " )] IdenticalEdgesNotCoincident(Handle, Handle), - - /// [`Shell`] contains vertices that are coincident, but not identical - #[error( - "Shell contains Vertices that are coinciendent but not identical\n - Vertex 1: {:#?} {:#?} - Vertex 2: {:#?} {:#?} - ", .0[0].0, .0[0].1,.0[1].0,.0[1].1 - )] - DistinctVertsCoincide([(Handle, Point<3>); 2]), - - /// [`Shell`] contains vertices that are identical, but do not coincide - #[error( - "Shell contains Vertices that are identical but do not coincide\n - Vertex 1: {:#?} {:#?} - Vertex 2: {:#?} {:#?} - ", .0[0].0, .0[0].1,.0[1].0,.0[1].1 - )] - IdenticalVertsNotCoincident([(Handle, Point<3>); 2]), } /// Sample two edges at various (currently 3) points in 3D along them. @@ -106,7 +88,7 @@ fn distances( } impl ShellValidationError { - fn validate_coincident( + fn validate_edges_coincident( shell: &Shell, config: &ValidationConfig, errors: &mut Vec, @@ -164,56 +146,6 @@ impl ShellValidationError { } } } - - let vertices: Vec<(Point<3>, Handle)> = shell - .faces() - .into_iter() - .flat_map(|face| { - face.all_cycles() - .flat_map(|cycle| cycle.half_edges().cloned()) - .zip(repeat(face.surface().geometry())) - }) - .map(|(h, s)| { - ( - s.point_from_surface_coords(h.start_position()), - h.start_vertex().clone(), - ) - }) - .collect(); - - // This is O(N^2) which isn't great, but we can't use a HashMap since we - // need to deal with float inaccuracies. Maybe we could use some smarter - // data-structure like an octree. - for a in &vertices { - for b in &vertices { - match a.1.id() == b.1.id() { - true => { - if a.0.distance_to(&b.0) > config.identical_max_distance - { - errors.push( - Self::IdenticalVertsNotCoincident([ - (a.1.clone(), a.0), - (b.1.clone(), b.0), - ]) - .into(), - ) - } - } - false => { - if a.0.distance_to(&b.0) < config.distinct_min_distance - { - errors.push( - Self::DistinctVertsCoincide([ - (a.1.clone(), a.0), - (b.1.clone(), b.0), - ]) - .into(), - ) - } - } - } - } - } } fn validate_watertight( diff --git a/crates/fj-kernel/src/validate/solid.rs b/crates/fj-kernel/src/validate/solid.rs index e2982bd6a2..b5fe04cc24 100644 --- a/crates/fj-kernel/src/validate/solid.rs +++ b/crates/fj-kernel/src/validate/solid.rs @@ -1,12 +1,99 @@ -use crate::objects::Solid; +use std::iter::repeat; + +use crate::{ + objects::{Solid, Vertex}, + storage::Handle, +}; +use fj_math::Point; use super::{Validate, ValidationConfig, ValidationError}; impl Validate for Solid { fn validate_with_config( &self, - _: &ValidationConfig, - _: &mut Vec, + config: &ValidationConfig, + errors: &mut Vec, ) { + SolidValidationError::check_vertices(self, config, errors) + } +} + +/// [`Solid`] validation failed +#[derive(Clone, Debug, thiserror::Error)] +pub enum SolidValidationError { + /// [`Solid`] contains vertices that are coincident, but not identical + #[error( + "Solid contains Vertices that are coinciendent but not identical\n + Vertex 1: {:#?} {:#?} + Vertex 2: {:#?} {:#?} + ", .0[0].0, .0[0].1,.0[1].0,.0[1].1 + )] + DistinctVertsCoincide([(Handle, Point<3>); 2]), + + /// [`Solid`] contains vertices that are identical, but do not coincide + #[error( + "Solid contains Vertices that are identical but do not coincide\n + Vertex 1: {:#?} {:#?} + Vertex 2: {:#?} {:#?} + ", .0[0].0, .0[0].1,.0[1].0,.0[1].1 + )] + IdenticalVertsNotCoincident([(Handle, Point<3>); 2]), +} + +impl SolidValidationError { + fn check_vertices( + solid: &Solid, + config: &ValidationConfig, + errors: &mut Vec, + ) { + let vertices: Vec<(Point<3>, Handle)> = solid + .shells() + .flat_map(|s| s.faces()) + .flat_map(|face| { + face.all_cycles() + .flat_map(|cycle| cycle.half_edges().cloned()) + .zip(repeat(face.surface().geometry())) + }) + .map(|(h, s)| { + ( + s.point_from_surface_coords(h.start_position()), + h.start_vertex().clone(), + ) + }) + .collect(); + + // This is O(N^2) which isn't great, but we can't use a HashMap since we + // need to deal with float inaccuracies. Maybe we could use some smarter + // data-structure like an octree. + for a in &vertices { + for b in &vertices { + match a.1.id() == b.1.id() { + true => { + if a.0.distance_to(&b.0) > config.identical_max_distance + { + errors.push( + Self::IdenticalVertsNotCoincident([ + (a.1.clone(), a.0), + (b.1.clone(), b.0), + ]) + .into(), + ) + } + } + false => { + if a.0.distance_to(&b.0) < config.distinct_min_distance + { + errors.push( + Self::DistinctVertsCoincide([ + (a.1.clone(), a.0), + (b.1.clone(), b.0), + ]) + .into(), + ) + } + } + } + } + } } }