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 hannobraun#1689, holding off on
validating sketches until we figure out sketch-face relationship: hannobraun#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.