Skip to content

Commit

Permalink
Move vertex coincidence validation to solid
Browse files Browse the repository at this point in the history
Before it was in shell. This partially addresses #1689, holding off on
validating sketches until we figure out sketch-face relationship: #1691
  • Loading branch information
A-Walrus committed Mar 21, 2023
1 parent 9df79ad commit 83bf61a
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 76 deletions.
1 change: 1 addition & 0 deletions crates/fj-kernel/src/validate/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
11 changes: 9 additions & 2 deletions crates/fj-kernel/src/validate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<Infallible> for ValidationError {
Expand Down
74 changes: 3 additions & 71 deletions crates/fj-kernel/src/validate/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use fj_math::{Point, Scalar};

use crate::{
geometry::surface::SurfaceGeometry,
objects::{HalfEdge, Shell, Vertex},
objects::{HalfEdge, Shell},
storage::{Handle, ObjectId},
};

Expand All @@ -16,7 +16,7 @@ impl Validate for Shell {
config: &ValidationConfig,
errors: &mut Vec<ValidationError>,
) {
ShellValidationError::validate_coincident(self, config, errors);
ShellValidationError::validate_edges_coincident(self, config, errors);
ShellValidationError::validate_watertight(self, config, errors);
}
}
Expand All @@ -43,24 +43,6 @@ pub enum ShellValidationError {
"
)]
IdenticalEdgesNotCoincident(Handle<HalfEdge>, Handle<HalfEdge>),

/// [`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<Vertex>, 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<Vertex>, Point<3>); 2]),
}

/// Sample two edges at various (currently 3) points in 3D along them.
Expand Down Expand Up @@ -106,7 +88,7 @@ fn distances(
}

impl ShellValidationError {
fn validate_coincident(
fn validate_edges_coincident(
shell: &Shell,
config: &ValidationConfig,
errors: &mut Vec<ValidationError>,
Expand Down Expand Up @@ -164,56 +146,6 @@ impl ShellValidationError {
}
}
}

let vertices: Vec<(Point<3>, Handle<Vertex>)> = 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(
Expand Down
93 changes: 90 additions & 3 deletions crates/fj-kernel/src/validate/solid.rs
Original file line number Diff line number Diff line change
@@ -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<ValidationError>,
config: &ValidationConfig,
errors: &mut Vec<ValidationError>,
) {
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<Vertex>, 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<Vertex>, Point<3>); 2]),
}

impl SolidValidationError {
fn check_vertices(
solid: &Solid,
config: &ValidationConfig,
errors: &mut Vec<ValidationError>,
) {
let vertices: Vec<(Point<3>, Handle<Vertex>)> = 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(),
)
}
}
}
}
}
}
}

0 comments on commit 83bf61a

Please sign in to comment.