Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate approximations before using them for triangulation #1058

Merged
merged 11 commits into from
Sep 8, 2022
50 changes: 49 additions & 1 deletion crates/fj-kernel/src/algorithms/approx/face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,58 @@ use std::collections::BTreeSet;

use fj_interop::mesh::Color;

use crate::objects::Face;
use crate::{
algorithms::validate::ValidationConfig,
objects::{Face, Faces},
};

use super::{cycle::CycleApprox, Approx, ApproxPoint, Tolerance};

impl Approx for &Faces {
type Approximation = BTreeSet<FaceApprox>;

fn approx(self, tolerance: Tolerance) -> Self::Approximation {
let approx = self
.into_iter()
.map(|face| face.approx(tolerance))
.collect();

let min_distance = ValidationConfig::default().distinct_min_distance;
let mut all_points: BTreeSet<ApproxPoint<2>> = BTreeSet::new();

for approx in &approx {
let approx: &FaceApprox = approx;

for point in &approx.points() {
for p in &all_points {
let distance =
(p.global_form - point.global_form).magnitude();

if p.global_form != point.global_form
&& distance < min_distance
{
let a = p;
let b = point;

panic!(
"Invalid approximation: \
Distinct points are too close \
(a: {:?}, b: {:?}, distance: {distance})\n\
source of `a`: {:#?}\n\
source of `b`: {:#?}\n",
a.global_form, b.global_form, a.source, b.source
);
}
}

all_points.insert(point.clone());
}
}

approx
}
}

impl Approx for &Face {
type Approximation = FaceApprox;

Expand Down
3 changes: 3 additions & 0 deletions crates/fj-kernel/src/algorithms/approx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ pub mod curve;
pub mod cycle;
pub mod edge;
pub mod face;
pub mod shell;
pub mod sketch;
pub mod solid;
pub mod tolerance;

use std::{
Expand Down
15 changes: 15 additions & 0 deletions crates/fj-kernel/src/algorithms/approx/shell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Shell approximation

use std::collections::BTreeSet;

use crate::objects::Shell;

use super::{face::FaceApprox, Approx, Tolerance};

impl Approx for &Shell {
type Approximation = BTreeSet<FaceApprox>;

fn approx(self, tolerance: Tolerance) -> Self::Approximation {
self.faces().approx(tolerance)
}
}
15 changes: 15 additions & 0 deletions crates/fj-kernel/src/algorithms/approx/sketch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Sketch approximation

use std::collections::BTreeSet;

use crate::objects::Sketch;

use super::{face::FaceApprox, Approx, Tolerance};

impl Approx for &Sketch {
type Approximation = BTreeSet<FaceApprox>;

fn approx(self, tolerance: Tolerance) -> Self::Approximation {
self.faces().approx(tolerance)
}
}
17 changes: 17 additions & 0 deletions crates/fj-kernel/src/algorithms/approx/solid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! Solid approximation

use std::collections::BTreeSet;

use crate::objects::Solid;

use super::{face::FaceApprox, Approx, Tolerance};

impl Approx for &Solid {
type Approximation = BTreeSet<FaceApprox>;

fn approx(self, tolerance: Tolerance) -> Self::Approximation {
self.shells()
.flat_map(|shell| shell.approx(tolerance))
.collect()
}
}
57 changes: 21 additions & 36 deletions crates/fj-kernel/src/algorithms/triangulate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,19 @@
mod delaunay;
mod polygon;

use fj_interop::{debug::DebugInfo, mesh::Mesh};
use fj_interop::mesh::Mesh;
use fj_math::Point;

use crate::objects::Face;

use self::{delaunay::TriangulationPoint, polygon::Polygon};

use super::approx::{Approx, Tolerance};
use super::approx::{face::FaceApprox, Approx, Tolerance};

/// Triangulate a shape
pub trait Triangulate: Sized {
/// Triangulate the shape
fn triangulate(
self,
tolerance: impl Into<Tolerance>,
debug_info: &mut DebugInfo,
) -> Mesh<Point<3>> {
fn triangulate(self, tolerance: impl Into<Tolerance>) -> Mesh<Point<3>> {
let mut mesh = Mesh::new();
self.triangulate_into_mesh(tolerance, &mut mesh, debug_info);
self.triangulate_into_mesh(tolerance, &mut mesh);
mesh
}

Expand All @@ -33,80 +27,73 @@ pub trait Triangulate: Sized {
self,
tolerance: impl Into<Tolerance>,
mesh: &mut Mesh<Point<3>>,
debug_info: &mut DebugInfo,
);
}

impl<T> Triangulate for T
where
T: IntoIterator<Item = Face>,
T: Approx,
T::Approximation: IntoIterator<Item = FaceApprox>,
{
fn triangulate_into_mesh(
self,
tolerance: impl Into<Tolerance>,
mesh: &mut Mesh<Point<3>>,
debug_info: &mut DebugInfo,
) {
let tolerance = tolerance.into();
let approx = self.approx(tolerance);

for face in self {
face.triangulate_into_mesh(tolerance, mesh, debug_info);
for approx in approx {
approx.triangulate_into_mesh(tolerance, mesh);
}
}
}

impl Triangulate for Face {
impl Triangulate for FaceApprox {
fn triangulate_into_mesh(
self,
tolerance: impl Into<Tolerance>,
_: impl Into<Tolerance>,
mesh: &mut Mesh<Point<3>>,
debug_info: &mut DebugInfo,
) {
let surface = self.surface();
let approx = self.approx(tolerance.into());

let points: Vec<_> = approx
let points: Vec<_> = self
.points()
.into_iter()
.map(|point| TriangulationPoint {
point_surface: point.local_form,
point_global: point.global_form,
})
.collect();
let face_as_polygon = Polygon::new(*surface)
let face_as_polygon = Polygon::new()
.with_exterior(
approx
.exterior
self.exterior
.points()
.into_iter()
.map(|point| point.local_form),
)
.with_interiors(approx.interiors.into_iter().map(|interior| {
.with_interiors(self.interiors.into_iter().map(|interior| {
interior.points().into_iter().map(|point| point.local_form)
}));

let mut triangles = delaunay::triangulate(points);
triangles.retain(|triangle| {
face_as_polygon.contains_triangle(
triangle.map(|point| point.point_surface),
debug_info,
)
face_as_polygon
.contains_triangle(triangle.map(|point| point.point_surface))
});

for triangle in triangles {
let points = triangle.map(|point| point.point_global);
mesh.push_triangle(points, self.color());
mesh.push_triangle(points, self.color);
}
}
}

#[cfg(test)]
mod tests {
use fj_interop::{debug::DebugInfo, mesh::Mesh};
use fj_interop::mesh::Mesh;
use fj_math::{Point, Scalar};

use crate::{
algorithms::approx::Tolerance,
algorithms::approx::{Approx, Tolerance},
objects::{Face, Surface},
};

Expand Down Expand Up @@ -221,8 +208,6 @@ mod tests {

fn triangulate(face: impl Into<Face>) -> anyhow::Result<Mesh<Point<3>>> {
let tolerance = Tolerance::from_scalar(Scalar::ONE)?;

let mut debug_info = DebugInfo::new();
Ok(vec![face.into()].triangulate(tolerance, &mut debug_info))
Ok(face.into().approx(tolerance).triangulate(tolerance))
}
}
Loading