From 3cc5581e9c012cd06e6a1c1b3c65e29c82c5d7e1 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 27 Oct 2022 12:22:46 +0200 Subject: [PATCH 1/7] Add validation code for `SurfaceVertex` --- crates/fj-kernel/src/validate/mod.rs | 6 ++ crates/fj-kernel/src/validate/vertex.rs | 76 ++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/crates/fj-kernel/src/validate/mod.rs b/crates/fj-kernel/src/validate/mod.rs index 27ced8c48..bb6d220e1 100644 --- a/crates/fj-kernel/src/validate/mod.rs +++ b/crates/fj-kernel/src/validate/mod.rs @@ -37,6 +37,8 @@ use fj_math::Scalar; use crate::iter::ObjectIters; +use self::vertex::SurfaceVertexPositionMismatch; + /// Validate an object pub trait Validate: Sized { /// Validate the object using default configuration @@ -187,6 +189,10 @@ pub enum ValidationError { /// Uniqueness validation failed #[error("Uniqueness validation failed")] Uniqueness(#[from] UniquenessIssues), + + /// `SurfaceVertex` position didn't match `GlobalVertex` + #[error(transparent)] + SurfaceVertexPositionMismatch(#[from] SurfaceVertexPositionMismatch), } impl From for ValidationError { diff --git a/crates/fj-kernel/src/validate/vertex.rs b/crates/fj-kernel/src/validate/vertex.rs index 3058f447f..27eccd3dc 100644 --- a/crates/fj-kernel/src/validate/vertex.rs +++ b/crates/fj-kernel/src/validate/vertex.rs @@ -1,5 +1,7 @@ use std::convert::Infallible; +use fj_math::{Point, Scalar}; + use crate::objects::{GlobalVertex, SurfaceVertex, Vertex}; use super::{Validate2, ValidationConfig}; @@ -16,16 +18,53 @@ impl Validate2 for Vertex { } impl Validate2 for SurfaceVertex { - type Error = Infallible; + type Error = SurfaceVertexPositionMismatch; fn validate_with_config( &self, - _: &ValidationConfig, + config: &ValidationConfig, ) -> Result<(), Self::Error> { + let surface_position_as_global = + self.surface().point_from_surface_coords(self.position()); + let global_position = self.global_form().position(); + + let distance = surface_position_as_global.distance_to(&global_position); + + if distance > config.identical_max_distance { + return Err(SurfaceVertexPositionMismatch { + surface_vertex: self.clone(), + global_vertex: self.global_form().clone_object(), + surface_position_as_global, + distance, + }); + } + Ok(()) } } +#[derive(Debug, thiserror::Error)] +#[error( + "`SurfaceVertex` position doesn't match position of its global form\n\ + `SurfaceVertex`: {surface_vertex:#?}\n\ + `GlobalVertex`: {global_vertex:#?}\n\ + `SurfaceVertex` position as global: {surface_position_as_global:?}\n\ + Distance between the positions: {distance}" +)] +pub struct SurfaceVertexPositionMismatch { + /// The surface vertex + pub surface_vertex: SurfaceVertex, + + /// The mismatched global vertex + pub global_vertex: GlobalVertex, + + /// The surface position converted into a global position + pub surface_position_as_global: Point<3>, + + /// The distance between the positions + pub distance: Scalar, +} + impl Validate2 for GlobalVertex { type Error = Infallible; @@ -36,3 +75,36 @@ impl Validate2 for GlobalVertex { Ok(()) } } + +#[cfg(test)] +mod tests { + use crate::{ + objects::{GlobalVertex, Objects, SurfaceVertex}, + validate::Validate2, + }; + + #[test] + fn surface_vertex_position_mismatch() -> anyhow::Result<()> { + let objects = Objects::new(); + + let valid = SurfaceVertex::new( + [0., 0.], + objects.surfaces.xy_plane(), + objects + .global_vertices + .insert(GlobalVertex::from_position([0., 0., 0.]))?, + ); + let invalid = SurfaceVertex::new( + [0., 0.], + objects.surfaces.xy_plane(), + objects + .global_vertices + .insert(GlobalVertex::from_position([1., 0., 0.]))?, + ); + + assert!(valid.validate().is_ok()); + assert!(invalid.validate().is_err()); + + Ok(()) + } +} From 642dbef04c64d4f5be64b2c8baeeb010b4479f83 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 27 Oct 2022 14:00:40 +0200 Subject: [PATCH 2/7] Remove test It's in the way of my next commit, as it starts failing when I add more validation code under the new infrastructure. It covers validation code that is about to be made redundant anyway, so this is no loss. --- crates/fj-kernel/src/validate/mod.rs | 80 +--------------------------- 1 file changed, 1 insertion(+), 79 deletions(-) diff --git a/crates/fj-kernel/src/validate/mod.rs b/crates/fj-kernel/src/validate/mod.rs index bb6d220e1..e48912dab 100644 --- a/crates/fj-kernel/src/validate/mod.rs +++ b/crates/fj-kernel/src/validate/mod.rs @@ -203,91 +203,13 @@ impl From for ValidationError { #[cfg(test)] mod tests { - use fj_interop::ext::ArrayExt; use fj_math::{Point, Scalar}; use crate::{ - objects::{ - Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects, - SurfaceVertex, Vertex, - }, - partial::HasPartial, - path::SurfacePath, + objects::{GlobalVertex, Objects}, validate::{Validate, ValidationConfig, ValidationError}, }; - #[test] - fn coherence_edge() -> anyhow::Result<()> { - let objects = Objects::new(); - - let surface = objects.surfaces.xy_plane(); - - let points_surface = [[0., 0.], [1., 0.]]; - let points_global = [[0., 0., 0.], [1., 0., 0.]]; - - let curve = { - let path = SurfacePath::line_from_points(points_surface); - let global_form = objects.global_curves.insert(GlobalCurve)?; - - objects.curves.insert(Curve::new( - surface.clone(), - path, - global_form, - ))? - }; - - let vertices_global = points_global.try_map_ext(|point| { - objects - .global_vertices - .insert(GlobalVertex::from_position(point)) - })?; - - let [a_surface, b_surface] = points_surface - .zip_ext(vertices_global) - .try_map_ext(|(point_surface, vertex_global)| { - objects.surface_vertices.insert(SurfaceVertex::new( - point_surface, - surface.clone(), - vertex_global, - )) - })?; - - let deviation = Scalar::from_f64(0.25); - - let a = objects.vertices.insert(Vertex::new( - Point::from([Scalar::ZERO + deviation]), - curve.clone(), - a_surface, - ))?; - let b = objects.vertices.insert(Vertex::new( - Point::from([Scalar::ONE]), - curve.clone(), - b_surface, - ))?; - let vertices = [a, b]; - - let global_edge = GlobalEdge::partial() - .from_curve_and_vertices(&curve, &vertices) - .build(&objects)?; - let half_edge = objects - .half_edges - .insert(HalfEdge::new(vertices, global_edge)); - - let result = - half_edge.clone().validate_with_config(&ValidationConfig { - identical_max_distance: deviation * 2., - ..ValidationConfig::default() - }); - assert!(result.is_ok()); - - let result = half_edge.validate_with_config(&ValidationConfig { - identical_max_distance: deviation / 2., - ..ValidationConfig::default() - }); - assert!(result.is_err()); - Ok(()) - } - #[test] fn uniqueness_vertex() -> anyhow::Result<()> { let objects = Objects::new(); From c00aa09ebe8b6340fe1760e3c4e85b5ffb2ab37a Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 27 Oct 2022 14:02:00 +0200 Subject: [PATCH 3/7] Add validation code for `Vertex` --- crates/fj-kernel/src/validate/mod.rs | 6 +- crates/fj-kernel/src/validate/vertex.rs | 77 ++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/crates/fj-kernel/src/validate/mod.rs b/crates/fj-kernel/src/validate/mod.rs index e48912dab..5df123f70 100644 --- a/crates/fj-kernel/src/validate/mod.rs +++ b/crates/fj-kernel/src/validate/mod.rs @@ -37,7 +37,7 @@ use fj_math::Scalar; use crate::iter::ObjectIters; -use self::vertex::SurfaceVertexPositionMismatch; +use self::vertex::{SurfaceVertexPositionMismatch, VertexPositionMismatch}; /// Validate an object pub trait Validate: Sized { @@ -193,6 +193,10 @@ pub enum ValidationError { /// `SurfaceVertex` position didn't match `GlobalVertex` #[error(transparent)] SurfaceVertexPositionMismatch(#[from] SurfaceVertexPositionMismatch), + + /// `Vertex` position didn't match `SurfaceVertex` + #[error(transparent)] + VertexPositionMismatch(#[from] VertexPositionMismatch), } impl From for ValidationError { diff --git a/crates/fj-kernel/src/validate/vertex.rs b/crates/fj-kernel/src/validate/vertex.rs index 27eccd3dc..0e5ea0c7c 100644 --- a/crates/fj-kernel/src/validate/vertex.rs +++ b/crates/fj-kernel/src/validate/vertex.rs @@ -7,16 +7,53 @@ use crate::objects::{GlobalVertex, SurfaceVertex, Vertex}; use super::{Validate2, ValidationConfig}; impl Validate2 for Vertex { - type Error = Infallible; + type Error = VertexPositionMismatch; fn validate_with_config( &self, - _: &ValidationConfig, + config: &ValidationConfig, ) -> Result<(), Self::Error> { + let curve_position_as_surface = + self.curve().path().point_from_path_coords(self.position()); + let surface_position = self.surface_form().position(); + + let distance = curve_position_as_surface.distance_to(&surface_position); + + if distance > config.identical_max_distance { + return Err(VertexPositionMismatch { + vertex: self.clone(), + surface_vertex: self.surface_form().clone_object(), + curve_position_as_surface, + distance, + }); + } + Ok(()) } } +#[derive(Debug, thiserror::Error)] +#[error( + "`Vertex` position doesn't match position of its surface form\n\ + `Vertex`: {vertex:#?}\n\ + `SurfaceVertex`: {surface_vertex:#?}\n\ + `Vertex` position as surface: {curve_position_as_surface:?}\n\ + Distance between the positions: {distance}" +)] +pub struct VertexPositionMismatch { + /// The vertex + pub vertex: Vertex, + + /// The mismatched surface vertex + pub surface_vertex: SurfaceVertex, + + /// The curve position converted into a surface position + pub curve_position_as_surface: Point<2>, + + /// The distance between the positions + pub distance: Scalar, +} + impl Validate2 for SurfaceVertex { type Error = SurfaceVertexPositionMismatch; @@ -79,10 +116,44 @@ impl Validate2 for GlobalVertex { #[cfg(test)] mod tests { use crate::{ - objects::{GlobalVertex, Objects, SurfaceVertex}, + objects::{Curve, GlobalVertex, Objects, SurfaceVertex, Vertex}, + partial::HasPartial, validate::Validate2, }; + #[test] + fn vertex_position_mismatch() -> anyhow::Result<()> { + let objects = Objects::new(); + + let valid = Vertex::new( + [0.], + Curve::partial() + .with_surface(Some(objects.surfaces.xy_plane())) + .as_u_axis() + .build(&objects)?, + SurfaceVertex::partial() + .with_surface(Some(objects.surfaces.xy_plane())) + .with_position(Some([0., 0.])) + .build(&objects)?, + ); + let invalid = Vertex::new( + [0.], + Curve::partial() + .with_surface(Some(objects.surfaces.xy_plane())) + .as_u_axis() + .build(&objects)?, + SurfaceVertex::partial() + .with_surface(Some(objects.surfaces.xy_plane())) + .with_position(Some([1., 0.])) + .build(&objects)?, + ); + + assert!(valid.validate().is_ok()); + assert!(invalid.validate().is_err()); + + Ok(()) + } + #[test] fn surface_vertex_position_mismatch() -> anyhow::Result<()> { let objects = Objects::new(); From 5d197e47697f3a0e67d4f579bdc0fb2ea8fb8a18 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 27 Oct 2022 14:02:26 +0200 Subject: [PATCH 4/7] Remove redundant validation code --- crates/fj-kernel/src/validate/coherence.rs | 69 ---------------------- crates/fj-kernel/src/validate/mod.rs | 13 +--- 2 files changed, 1 insertion(+), 81 deletions(-) delete mode 100644 crates/fj-kernel/src/validate/coherence.rs diff --git a/crates/fj-kernel/src/validate/coherence.rs b/crates/fj-kernel/src/validate/coherence.rs deleted file mode 100644 index dc7842d4b..000000000 --- a/crates/fj-kernel/src/validate/coherence.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::fmt; - -use fj_math::{Point, Scalar}; - -use crate::objects::Vertex; - -pub fn validate_vertex( - vertex: &Vertex, - max_distance: impl Into, -) -> Result<(), CoherenceIssues> { - let max_distance = max_distance.into(); - - // Validate that the local and global forms of the vertex match. As a side - // effect, this also happens to validate that the global form of the vertex - // lies on the curve. - - let local = vertex.position(); - let local_as_surface = vertex.curve().path().point_from_path_coords(local); - let local_as_global = vertex - .curve() - .surface() - .point_from_surface_coords(local_as_surface); - let global = vertex.global_form().position(); - let distance = (local_as_global - global).magnitude(); - - if distance > max_distance { - Err(VertexCoherenceMismatch { - local, - local_as_global, - global, - })? - } - - Ok(()) -} - -/// Issues in coherence validation -#[allow(clippy::large_enum_variant)] -#[derive(Debug, thiserror::Error)] -pub enum CoherenceIssues { - /// Mismatch between the local and global coordinates of a vertex - #[error("Mismatch between local and global coordinates of vertex")] - Vertex(#[from] VertexCoherenceMismatch), -} - -/// A mismatch between the local and global forms of a vertex -/// -/// Used in [`CoherenceIssues`]. -#[derive(Debug, Default, thiserror::Error)] -pub struct VertexCoherenceMismatch { - /// The local form of the object - pub local: Point<1>, - - /// The local form of the object, converted into the global form - pub local_as_global: Point<3>, - - /// The global form of the object - pub global: Point<3>, -} - -impl fmt::Display for VertexCoherenceMismatch { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "local: {:?} (converted to global: {:?}), global: {:?},", - self.local, self.local_as_global, self.global, - ) - } -} diff --git a/crates/fj-kernel/src/validate/mod.rs b/crates/fj-kernel/src/validate/mod.rs index 5df123f70..ba65e91e4 100644 --- a/crates/fj-kernel/src/validate/mod.rs +++ b/crates/fj-kernel/src/validate/mod.rs @@ -14,7 +14,6 @@ //! Please note that not all of these validation categories are fully //! implemented, as of this writing. -mod coherence; mod curve; mod cycle; mod edge; @@ -26,10 +25,7 @@ mod surface; mod uniqueness; mod vertex; -pub use self::{ - coherence::{CoherenceIssues, VertexCoherenceMismatch}, - uniqueness::UniquenessIssues, -}; +pub use self::uniqueness::UniquenessIssues; use std::{collections::HashSet, convert::Infallible, ops::Deref}; @@ -96,9 +92,6 @@ where global_vertices.insert(*global_vertex); } - for vertex in self.vertex_iter() { - coherence::validate_vertex(vertex, config.identical_max_distance)?; - } Ok(Validated(self)) } @@ -178,10 +171,6 @@ impl Deref for Validated { #[allow(clippy::large_enum_variant)] #[derive(Debug, thiserror::Error)] pub enum ValidationError { - /// Coherence validation failed - #[error("Coherence validation failed")] - Coherence(#[from] CoherenceIssues), - /// Geometric validation failed #[error("Geometric validation failed")] Geometric, From d3ceccf73790f417f9b4bab8b3800af0f2a437fd Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 27 Oct 2022 14:20:47 +0200 Subject: [PATCH 5/7] Add `VertexValidationError` --- crates/fj-kernel/src/validate/mod.rs | 4 +-- crates/fj-kernel/src/validate/vertex.rs | 44 +++++++++++++------------ 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/crates/fj-kernel/src/validate/mod.rs b/crates/fj-kernel/src/validate/mod.rs index ba65e91e4..10b7239d4 100644 --- a/crates/fj-kernel/src/validate/mod.rs +++ b/crates/fj-kernel/src/validate/mod.rs @@ -33,7 +33,7 @@ use fj_math::Scalar; use crate::iter::ObjectIters; -use self::vertex::{SurfaceVertexPositionMismatch, VertexPositionMismatch}; +use self::vertex::{SurfaceVertexPositionMismatch, VertexValidationError}; /// Validate an object pub trait Validate: Sized { @@ -185,7 +185,7 @@ pub enum ValidationError { /// `Vertex` position didn't match `SurfaceVertex` #[error(transparent)] - VertexPositionMismatch(#[from] VertexPositionMismatch), + Vertex(#[from] VertexValidationError), } impl From for ValidationError { diff --git a/crates/fj-kernel/src/validate/vertex.rs b/crates/fj-kernel/src/validate/vertex.rs index 0e5ea0c7c..5a5d5838c 100644 --- a/crates/fj-kernel/src/validate/vertex.rs +++ b/crates/fj-kernel/src/validate/vertex.rs @@ -7,7 +7,7 @@ use crate::objects::{GlobalVertex, SurfaceVertex, Vertex}; use super::{Validate2, ValidationConfig}; impl Validate2 for Vertex { - type Error = VertexPositionMismatch; + type Error = VertexValidationError; fn validate_with_config( &self, @@ -20,7 +20,7 @@ impl Validate2 for Vertex { let distance = curve_position_as_surface.distance_to(&surface_position); if distance > config.identical_max_distance { - return Err(VertexPositionMismatch { + return Err(VertexValidationError::PositionMismatch { vertex: self.clone(), surface_vertex: self.surface_form().clone_object(), curve_position_as_surface, @@ -33,25 +33,27 @@ impl Validate2 for Vertex { } #[derive(Debug, thiserror::Error)] -#[error( - "`Vertex` position doesn't match position of its surface form\n\ - `Vertex`: {vertex:#?}\n\ - `SurfaceVertex`: {surface_vertex:#?}\n\ - `Vertex` position as surface: {curve_position_as_surface:?}\n\ - Distance between the positions: {distance}" -)] -pub struct VertexPositionMismatch { - /// The vertex - pub vertex: Vertex, - - /// The mismatched surface vertex - pub surface_vertex: SurfaceVertex, - - /// The curve position converted into a surface position - pub curve_position_as_surface: Point<2>, - - /// The distance between the positions - pub distance: Scalar, +pub enum VertexValidationError { + #[error( + "`Vertex` position doesn't match position of its surface form\n\ + `Vertex`: {vertex:#?}\n\ + `SurfaceVertex`: {surface_vertex:#?}\n\ + `Vertex` position as surface: {curve_position_as_surface:?}\n\ + Distance between the positions: {distance}" + )] + PositionMismatch { + /// The vertex + vertex: Vertex, + + /// The mismatched surface vertex + surface_vertex: SurfaceVertex, + + /// The curve position converted into a surface position + curve_position_as_surface: Point<2>, + + /// The distance between the positions + distance: Scalar, + }, } impl Validate2 for SurfaceVertex { From 22fe7dc3fc8a7f2ab02ae3474c402ddf8ff149c5 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 27 Oct 2022 14:32:34 +0200 Subject: [PATCH 6/7] Add missing re-exports --- crates/fj-kernel/src/validate/mod.rs | 7 ++++--- crates/fj-kernel/src/validate/vertex.rs | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/fj-kernel/src/validate/mod.rs b/crates/fj-kernel/src/validate/mod.rs index 10b7239d4..8bace5737 100644 --- a/crates/fj-kernel/src/validate/mod.rs +++ b/crates/fj-kernel/src/validate/mod.rs @@ -25,7 +25,10 @@ mod surface; mod uniqueness; mod vertex; -pub use self::uniqueness::UniquenessIssues; +pub use self::{ + uniqueness::UniquenessIssues, + vertex::{SurfaceVertexPositionMismatch, VertexValidationError}, +}; use std::{collections::HashSet, convert::Infallible, ops::Deref}; @@ -33,8 +36,6 @@ use fj_math::Scalar; use crate::iter::ObjectIters; -use self::vertex::{SurfaceVertexPositionMismatch, VertexValidationError}; - /// Validate an object pub trait Validate: Sized { /// Validate the object using default configuration diff --git a/crates/fj-kernel/src/validate/vertex.rs b/crates/fj-kernel/src/validate/vertex.rs index 5a5d5838c..1e42cdbfe 100644 --- a/crates/fj-kernel/src/validate/vertex.rs +++ b/crates/fj-kernel/src/validate/vertex.rs @@ -32,8 +32,10 @@ impl Validate2 for Vertex { } } +/// [`Vertex`] validation failed #[derive(Debug, thiserror::Error)] pub enum VertexValidationError { + /// Mismatch between position of the vertex and position of its surface form #[error( "`Vertex` position doesn't match position of its surface form\n\ `Vertex`: {vertex:#?}\n\ @@ -82,6 +84,7 @@ impl Validate2 for SurfaceVertex { } } +/// Mismatch between position of surface vertex and position of its global form #[derive(Debug, thiserror::Error)] #[error( "`SurfaceVertex` position doesn't match position of its global form\n\ From 3babce3c9b15f76bb4f2b723a4eda24079a62416 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Thu, 27 Oct 2022 14:36:17 +0200 Subject: [PATCH 7/7] Move panicky validation to new infrastructure --- crates/fj-kernel/src/objects/vertex.rs | 7 --- crates/fj-kernel/src/validate/vertex.rs | 65 ++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/crates/fj-kernel/src/objects/vertex.rs b/crates/fj-kernel/src/objects/vertex.rs index b7febcfc1..62a6db6a4 100644 --- a/crates/fj-kernel/src/objects/vertex.rs +++ b/crates/fj-kernel/src/objects/vertex.rs @@ -1,5 +1,4 @@ use fj_math::Point; -use pretty_assertions::assert_eq; use crate::storage::Handle; @@ -29,12 +28,6 @@ impl Vertex { ) -> Self { let position = position.into(); - assert_eq!( - curve.surface().id(), - surface_form.surface().id(), - "Surface form of vertex must be defined on same surface as curve", - ); - Self { position, curve, diff --git a/crates/fj-kernel/src/validate/vertex.rs b/crates/fj-kernel/src/validate/vertex.rs index 1e42cdbfe..5a4086239 100644 --- a/crates/fj-kernel/src/validate/vertex.rs +++ b/crates/fj-kernel/src/validate/vertex.rs @@ -1,8 +1,11 @@ -use std::convert::Infallible; +use std::{convert::Infallible, ops::Deref}; use fj_math::{Point, Scalar}; -use crate::objects::{GlobalVertex, SurfaceVertex, Vertex}; +use crate::{ + objects::{GlobalVertex, Surface, SurfaceVertex, Vertex}, + storage::Handle, +}; use super::{Validate2, ValidationConfig}; @@ -13,6 +16,15 @@ impl Validate2 for Vertex { &self, config: &ValidationConfig, ) -> Result<(), Self::Error> { + let curve_surface = self.curve().surface(); + let surface_form_surface = self.surface_form().surface(); + if curve_surface.id() != surface_form_surface.id() { + return Err(VertexValidationError::SurfaceMismatch { + curve_surface: curve_surface.clone(), + surface_form_surface: surface_form_surface.clone(), + }); + } + let curve_position_as_surface = self.curve().path().point_from_path_coords(self.position()); let surface_position = self.surface_form().position(); @@ -35,6 +47,22 @@ impl Validate2 for Vertex { /// [`Vertex`] validation failed #[derive(Debug, thiserror::Error)] pub enum VertexValidationError { + /// Mismatch between the surface's of the curve and surface form + #[error( + "Surface form of vertex must be defined on same surface as curve\n\ + `Surface` of curve: {curve_surface:?} -> {:?}\n\ + `Surface` of surface form: {surface_form_surface:?} -> {:?}", + .curve_surface.deref(), + .surface_form_surface.deref(), + )] + SurfaceMismatch { + /// The surface of the vertex' curve + curve_surface: Handle, + + /// The surface of the vertex' surface form + surface_form_surface: Handle, + }, + /// Mismatch between position of the vertex and position of its surface form #[error( "`Vertex` position doesn't match position of its surface form\n\ @@ -126,6 +154,39 @@ mod tests { validate::Validate2, }; + #[test] + fn vertex_surface_mismatch() -> anyhow::Result<()> { + let objects = Objects::new(); + + let valid = Vertex::new( + [0.], + Curve::partial() + .with_surface(Some(objects.surfaces.xy_plane())) + .as_u_axis() + .build(&objects)?, + SurfaceVertex::partial() + .with_surface(Some(objects.surfaces.xy_plane())) + .with_position(Some([0., 0.])) + .build(&objects)?, + ); + let invalid = Vertex::new( + [0.], + Curve::partial() + .with_surface(Some(objects.surfaces.xy_plane())) + .as_u_axis() + .build(&objects)?, + SurfaceVertex::partial() + .with_surface(Some(objects.surfaces.xz_plane())) + .with_position(Some([0., 0.])) + .build(&objects)?, + ); + + assert!(valid.validate().is_ok()); + assert!(invalid.validate().is_err()); + + Ok(()) + } + #[test] fn vertex_position_mismatch() -> anyhow::Result<()> { let objects = Objects::new();