mirror of
				https://github.com/hannobraun/Fornjot
				synced 2025-11-04 06:07:19 +00:00 
			
		
		
		
	Merge pull request #465 from hannobraun/tolerance
Add `Tolerance` struct to enforce validity of tolerance values
This commit is contained in:
		
						commit
						c8223e57ef
					
				@ -1,4 +1,7 @@
 | 
				
			|||||||
use std::path::PathBuf;
 | 
					use std::{path::PathBuf, str::FromStr as _};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use fj_kernel::algorithms::Tolerance;
 | 
				
			||||||
 | 
					use fj_math::Scalar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Fornjot - Experimental CAD System
 | 
					/// Fornjot - Experimental CAD System
 | 
				
			||||||
#[derive(clap::Parser)]
 | 
					#[derive(clap::Parser)]
 | 
				
			||||||
@ -16,8 +19,8 @@ pub struct Args {
 | 
				
			|||||||
    pub parameters: Vec<String>,
 | 
					    pub parameters: Vec<String>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Model deviation tolerance
 | 
					    /// Model deviation tolerance
 | 
				
			||||||
    #[clap[short, long]]
 | 
					    #[clap[short, long, parse(try_from_str = parse_tolerance)]]
 | 
				
			||||||
    pub tolerance: Option<f64>,
 | 
					    pub tolerance: Option<Tolerance>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Args {
 | 
					impl Args {
 | 
				
			||||||
@ -29,3 +32,11 @@ impl Args {
 | 
				
			|||||||
        <Self as clap::Parser>::parse()
 | 
					        <Self as clap::Parser>::parse()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn parse_tolerance(input: &str) -> anyhow::Result<Tolerance> {
 | 
				
			||||||
 | 
					    let tolerance = f64::from_str(input)?;
 | 
				
			||||||
 | 
					    let tolerance = Scalar::from_f64(tolerance);
 | 
				
			||||||
 | 
					    let tolerance = Tolerance::from_scalar(tolerance)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(tolerance)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ use std::{collections::HashMap, time::Instant};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use fj_host::Model;
 | 
					use fj_host::Model;
 | 
				
			||||||
use fj_interop::{debug::DebugInfo, mesh::Mesh};
 | 
					use fj_interop::{debug::DebugInfo, mesh::Mesh};
 | 
				
			||||||
use fj_kernel::algorithms::triangulate;
 | 
					use fj_kernel::algorithms::{triangulate, Tolerance};
 | 
				
			||||||
use fj_math::{Aabb, Point, Scalar};
 | 
					use fj_math::{Aabb, Point, Scalar};
 | 
				
			||||||
use fj_operations::ToShape as _;
 | 
					use fj_operations::ToShape as _;
 | 
				
			||||||
use futures::executor::block_on;
 | 
					use futures::executor::block_on;
 | 
				
			||||||
@ -78,7 +78,9 @@ fn main() -> anyhow::Result<()> {
 | 
				
			|||||||
        parameters.insert(key, value);
 | 
					        parameters.insert(key, value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let shape_processor = ShapeProcessor::new(args.tolerance)?;
 | 
					    let shape_processor = ShapeProcessor {
 | 
				
			||||||
 | 
					        tolerance: args.tolerance,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Some(path) = args.export {
 | 
					    if let Some(path) = args.export {
 | 
				
			||||||
        let shape = model.load_once(¶meters)?;
 | 
					        let shape = model.load_once(¶meters)?;
 | 
				
			||||||
@ -238,26 +240,10 @@ fn main() -> anyhow::Result<()> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ShapeProcessor {
 | 
					struct ShapeProcessor {
 | 
				
			||||||
    tolerance: Option<Scalar>,
 | 
					    tolerance: Option<Tolerance>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ShapeProcessor {
 | 
					impl ShapeProcessor {
 | 
				
			||||||
    fn new(tolerance: Option<f64>) -> anyhow::Result<Self> {
 | 
					 | 
				
			||||||
        if let Some(tolerance) = tolerance {
 | 
					 | 
				
			||||||
            if tolerance <= 0. {
 | 
					 | 
				
			||||||
                anyhow::bail!(
 | 
					 | 
				
			||||||
                    "Invalid user defined model deviation tolerance: {}.\n\
 | 
					 | 
				
			||||||
                    Tolerance must be larger than zero",
 | 
					 | 
				
			||||||
                    tolerance
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tolerance = tolerance.map(Scalar::from_f64);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(Self { tolerance })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn process(&self, shape: &fj::Shape) -> ProcessedShape {
 | 
					    fn process(&self, shape: &fj::Shape) -> ProcessedShape {
 | 
				
			||||||
        let aabb = shape.bounding_volume();
 | 
					        let aabb = shape.bounding_volume();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -273,11 +259,8 @@ impl ShapeProcessor {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // `tolerance` must not be zero, or we'll run into trouble.
 | 
					 | 
				
			||||||
                let tolerance = min_extent / Scalar::from_f64(1000.);
 | 
					                let tolerance = min_extent / Scalar::from_f64(1000.);
 | 
				
			||||||
                assert!(tolerance > Scalar::ZERO);
 | 
					                Tolerance::from_scalar(tolerance).unwrap()
 | 
				
			||||||
 | 
					 | 
				
			||||||
                tolerance
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Some(user_defined_tolerance) => user_defined_tolerance,
 | 
					            Some(user_defined_tolerance) => user_defined_tolerance,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ impl FaceApprox {
 | 
				
			|||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// `tolerance` defines how far the approximation is allowed to deviate from
 | 
					    /// `tolerance` defines how far the approximation is allowed to deviate from
 | 
				
			||||||
    /// the actual face.
 | 
					    /// the actual face.
 | 
				
			||||||
    pub fn new(face: &Face, tolerance: Scalar) -> Self {
 | 
					    pub fn new(face: &Face, tolerance: Tolerance) -> Self {
 | 
				
			||||||
        // Curved faces whose curvature is not fully defined by their edges
 | 
					        // Curved faces whose curvature is not fully defined by their edges
 | 
				
			||||||
        // are not supported yet. For that reason, we can fully ignore `face`'s
 | 
					        // are not supported yet. For that reason, we can fully ignore `face`'s
 | 
				
			||||||
        // `surface` field and just pass the edges to `Self::for_edges`.
 | 
					        // `surface` field and just pass the edges to `Self::for_edges`.
 | 
				
			||||||
@ -88,7 +88,7 @@ impl CycleApprox {
 | 
				
			|||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// `tolerance` defines how far the approximation is allowed to deviate from
 | 
					    /// `tolerance` defines how far the approximation is allowed to deviate from
 | 
				
			||||||
    /// the actual face.
 | 
					    /// the actual face.
 | 
				
			||||||
    pub fn new(cycle: &Cycle, tolerance: Scalar) -> Self {
 | 
					    pub fn new(cycle: &Cycle, tolerance: Tolerance) -> Self {
 | 
				
			||||||
        let mut points = Vec::new();
 | 
					        let mut points = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for edge in cycle.edges() {
 | 
					        for edge in cycle.edges() {
 | 
				
			||||||
@ -160,6 +160,61 @@ where
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A tolerance value
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// A tolerance value is used during approximation. It defines the maximum
 | 
				
			||||||
 | 
					/// allowed deviation of the approximation from the actual shape.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The `Tolerance` type enforces that the tolerance value is always larger than
 | 
				
			||||||
 | 
					/// zero, which is an attribute that the approximation code relies on.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// # Failing [`From`]/[`Into`] implementation
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The [`From`]/[`Into`] implementations of tolerance are fallible, which goes
 | 
				
			||||||
 | 
					/// against the explicit mandate of those traits, as stated in their
 | 
				
			||||||
 | 
					/// documentation.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// A fallible [`Into`] provides a lot of convenience in test code. Since said
 | 
				
			||||||
 | 
					/// documentation doesn't provide any actual reasoning for this requirement, I'm
 | 
				
			||||||
 | 
					/// feeling free to just ignore it.
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
 | 
				
			||||||
 | 
					pub struct Tolerance(Scalar);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Tolerance {
 | 
				
			||||||
 | 
					    /// Construct a `Tolerance` from a [`Scalar`]
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns an error, if the passed scalar is not larger than zero.
 | 
				
			||||||
 | 
					    pub fn from_scalar(
 | 
				
			||||||
 | 
					        scalar: impl Into<Scalar>,
 | 
				
			||||||
 | 
					    ) -> Result<Self, InvalidTolerance> {
 | 
				
			||||||
 | 
					        let scalar = scalar.into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if scalar <= Scalar::ZERO {
 | 
				
			||||||
 | 
					            return Err(InvalidTolerance(scalar));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self(scalar))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Return the [`Scalar`] that defines the tolerance
 | 
				
			||||||
 | 
					    pub fn inner(&self) -> Scalar {
 | 
				
			||||||
 | 
					        self.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<S> From<S> for Tolerance
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    S: Into<Scalar>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    fn from(scalar: S) -> Self {
 | 
				
			||||||
 | 
					        Self::from_scalar(scalar).unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, thiserror::Error)]
 | 
				
			||||||
 | 
					#[error("Invalid tolerance ({0}); must be above zero")]
 | 
				
			||||||
 | 
					pub struct InvalidTolerance(Scalar);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
    use fj_math::{Point, Scalar};
 | 
					    use fj_math::{Point, Scalar};
 | 
				
			||||||
@ -171,7 +226,7 @@ mod tests {
 | 
				
			|||||||
        topology::{Face, Vertex},
 | 
					        topology::{Face, Vertex},
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use super::{CycleApprox, FaceApprox};
 | 
					    use super::{CycleApprox, FaceApprox, Tolerance};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn approximate_edge() -> anyhow::Result<()> {
 | 
					    fn approximate_edge() -> anyhow::Result<()> {
 | 
				
			||||||
@ -201,7 +256,7 @@ mod tests {
 | 
				
			|||||||
    fn for_face_closed() -> anyhow::Result<()> {
 | 
					    fn for_face_closed() -> anyhow::Result<()> {
 | 
				
			||||||
        // Test a closed face, i.e. one that is completely encircled by edges.
 | 
					        // Test a closed face, i.e. one that is completely encircled by edges.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let tolerance = Scalar::ONE;
 | 
					        let tolerance = Tolerance::from_scalar(Scalar::ONE).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut shape = Shape::new();
 | 
					        let mut shape = Shape::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ mod sweep;
 | 
				
			|||||||
mod triangulation;
 | 
					mod triangulation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use self::{
 | 
					pub use self::{
 | 
				
			||||||
    approximation::{CycleApprox, FaceApprox},
 | 
					    approximation::{CycleApprox, FaceApprox, Tolerance},
 | 
				
			||||||
    sweep::sweep_shape,
 | 
					    sweep::sweep_shape,
 | 
				
			||||||
    triangulation::triangulate,
 | 
					    triangulation::triangulate,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use fj_math::{Scalar, Transform, Triangle, Vector};
 | 
					use fj_math::{Transform, Triangle, Vector};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    geometry::{Surface, SweptCurve},
 | 
					    geometry::{Surface, SweptCurve},
 | 
				
			||||||
@ -8,13 +8,13 @@ use crate::{
 | 
				
			|||||||
    topology::{Cycle, Edge, Face, Vertex},
 | 
					    topology::{Cycle, Edge, Face, Vertex},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::CycleApprox;
 | 
					use super::{CycleApprox, Tolerance};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Create a new shape by sweeping an existing one
 | 
					/// Create a new shape by sweeping an existing one
 | 
				
			||||||
pub fn sweep_shape(
 | 
					pub fn sweep_shape(
 | 
				
			||||||
    mut source: Shape,
 | 
					    mut source: Shape,
 | 
				
			||||||
    path: Vector<3>,
 | 
					    path: Vector<3>,
 | 
				
			||||||
    tolerance: Scalar,
 | 
					    tolerance: Tolerance,
 | 
				
			||||||
    color: [u8; 4],
 | 
					    color: [u8; 4],
 | 
				
			||||||
) -> Shape {
 | 
					) -> Shape {
 | 
				
			||||||
    let mut target = Shape::new();
 | 
					    let mut target = Shape::new();
 | 
				
			||||||
@ -316,6 +316,7 @@ mod tests {
 | 
				
			|||||||
    use fj_math::{Point, Scalar, Vector};
 | 
					    use fj_math::{Point, Scalar, Vector};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::{
 | 
					    use crate::{
 | 
				
			||||||
 | 
					        algorithms::Tolerance,
 | 
				
			||||||
        geometry::{Surface, SweptCurve},
 | 
					        geometry::{Surface, SweptCurve},
 | 
				
			||||||
        shape::{Handle, Shape},
 | 
					        shape::{Handle, Shape},
 | 
				
			||||||
        topology::{Cycle, Edge, Face},
 | 
					        topology::{Cycle, Edge, Face},
 | 
				
			||||||
@ -325,12 +326,14 @@ mod tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn sweep() -> anyhow::Result<()> {
 | 
					    fn sweep() -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					        let tolerance = Tolerance::from_scalar(Scalar::ONE).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let sketch = Triangle::new([[0., 0., 0.], [1., 0., 0.], [0., 1., 0.]])?;
 | 
					        let sketch = Triangle::new([[0., 0., 0.], [1., 0., 0.], [0., 1., 0.]])?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut swept = sweep_shape(
 | 
					        let mut swept = sweep_shape(
 | 
				
			||||||
            sketch.shape,
 | 
					            sketch.shape,
 | 
				
			||||||
            Vector::from([0., 0., 1.]),
 | 
					            Vector::from([0., 0., 1.]),
 | 
				
			||||||
            Scalar::from_f64(0.),
 | 
					            tolerance,
 | 
				
			||||||
            [255, 0, 0, 255],
 | 
					            [255, 0, 0, 255],
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,18 +3,18 @@ mod polygon;
 | 
				
			|||||||
mod ray;
 | 
					mod ray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use fj_interop::{debug::DebugInfo, mesh::Mesh};
 | 
					use fj_interop::{debug::DebugInfo, mesh::Mesh};
 | 
				
			||||||
use fj_math::{Point, Scalar};
 | 
					use fj_math::Point;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{shape::Shape, topology::Face};
 | 
					use crate::{shape::Shape, topology::Face};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use self::polygon::Polygon;
 | 
					use self::polygon::Polygon;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::FaceApprox;
 | 
					use super::{FaceApprox, Tolerance};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Triangulate a shape
 | 
					/// Triangulate a shape
 | 
				
			||||||
pub fn triangulate(
 | 
					pub fn triangulate(
 | 
				
			||||||
    mut shape: Shape,
 | 
					    mut shape: Shape,
 | 
				
			||||||
    tolerance: Scalar,
 | 
					    tolerance: Tolerance,
 | 
				
			||||||
    debug_info: &mut DebugInfo,
 | 
					    debug_info: &mut DebugInfo,
 | 
				
			||||||
) -> Mesh<Point<3>> {
 | 
					) -> Mesh<Point<3>> {
 | 
				
			||||||
    let mut mesh = Mesh::new();
 | 
					    let mut mesh = Mesh::new();
 | 
				
			||||||
@ -83,7 +83,9 @@ mod tests {
 | 
				
			|||||||
    use fj_interop::{debug::DebugInfo, mesh::Mesh};
 | 
					    use fj_interop::{debug::DebugInfo, mesh::Mesh};
 | 
				
			||||||
    use fj_math::{Point, Scalar};
 | 
					    use fj_math::{Point, Scalar};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::{geometry::Surface, shape::Shape, topology::Face};
 | 
					    use crate::{
 | 
				
			||||||
 | 
					        algorithms::Tolerance, geometry::Surface, shape::Shape, topology::Face,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn simple() -> anyhow::Result<()> {
 | 
					    fn simple() -> anyhow::Result<()> {
 | 
				
			||||||
@ -143,7 +145,7 @@ mod tests {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn triangulate(shape: Shape) -> Mesh<Point<3>> {
 | 
					    fn triangulate(shape: Shape) -> Mesh<Point<3>> {
 | 
				
			||||||
        let tolerance = Scalar::ONE;
 | 
					        let tolerance = Tolerance::from_scalar(Scalar::ONE).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut debug_info = DebugInfo::new();
 | 
					        let mut debug_info = DebugInfo::new();
 | 
				
			||||||
        super::triangulate(shape, tolerance, &mut debug_info)
 | 
					        super::triangulate(shape, tolerance, &mut debug_info)
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,8 @@ use std::f64::consts::PI;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use fj_math::{Point, Scalar, Transform, Vector};
 | 
					use fj_math::{Point, Scalar, Transform, Vector};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::algorithms::Tolerance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A circle
 | 
					/// A circle
 | 
				
			||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
 | 
					#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
 | 
				
			||||||
pub struct Circle {
 | 
					pub struct Circle {
 | 
				
			||||||
@ -80,7 +82,7 @@ impl Circle {
 | 
				
			|||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// `tolerance` specifies how much the approximation is allowed to deviate
 | 
					    /// `tolerance` specifies how much the approximation is allowed to deviate
 | 
				
			||||||
    /// from the circle.
 | 
					    /// from the circle.
 | 
				
			||||||
    pub fn approx(&self, tolerance: Scalar, out: &mut Vec<Point<3>>) {
 | 
					    pub fn approx(&self, tolerance: Tolerance, out: &mut Vec<Point<3>>) {
 | 
				
			||||||
        let radius = self.radius.magnitude();
 | 
					        let radius = self.radius.magnitude();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // To approximate the circle, we use a regular polygon for which
 | 
					        // To approximate the circle, we use a regular polygon for which
 | 
				
			||||||
@ -98,12 +100,11 @@ impl Circle {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn number_of_vertices(tolerance: Scalar, radius: Scalar) -> u64 {
 | 
					    fn number_of_vertices(tolerance: Tolerance, radius: Scalar) -> u64 {
 | 
				
			||||||
        assert!(tolerance > Scalar::ZERO);
 | 
					        if tolerance.inner() > radius / Scalar::TWO {
 | 
				
			||||||
        if tolerance > radius / Scalar::TWO {
 | 
					 | 
				
			||||||
            3
 | 
					            3
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            (Scalar::PI / (Scalar::ONE - (tolerance / radius)).acos())
 | 
					            (Scalar::PI / (Scalar::ONE - (tolerance.inner() / radius)).acos())
 | 
				
			||||||
                .ceil()
 | 
					                .ceil()
 | 
				
			||||||
                .into_u64()
 | 
					                .into_u64()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -116,6 +117,8 @@ mod tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    use fj_math::{Point, Scalar, Vector};
 | 
					    use fj_math::{Point, Scalar, Vector};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use crate::algorithms::Tolerance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use super::Circle;
 | 
					    use super::Circle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
@ -150,7 +153,7 @@ mod tests {
 | 
				
			|||||||
        verify_result(1., 100., 23);
 | 
					        verify_result(1., 100., 23);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn verify_result(
 | 
					        fn verify_result(
 | 
				
			||||||
            tolerance: impl Into<Scalar>,
 | 
					            tolerance: impl Into<Tolerance>,
 | 
				
			||||||
            radius: impl Into<Scalar>,
 | 
					            radius: impl Into<Scalar>,
 | 
				
			||||||
            n: u64,
 | 
					            n: u64,
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
@ -159,9 +162,9 @@ mod tests {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            assert_eq!(n, Circle::number_of_vertices(tolerance, radius));
 | 
					            assert_eq!(n, Circle::number_of_vertices(tolerance, radius));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert!(calculate_error(radius, n) <= tolerance);
 | 
					            assert!(calculate_error(radius, n) <= tolerance.inner());
 | 
				
			||||||
            if n > 3 {
 | 
					            if n > 3 {
 | 
				
			||||||
                assert!(calculate_error(radius, n - 1) >= tolerance);
 | 
					                assert!(calculate_error(radius, n - 1) >= tolerance.inner());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,11 @@
 | 
				
			|||||||
mod circle;
 | 
					mod circle;
 | 
				
			||||||
mod line;
 | 
					mod line;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::algorithms::Tolerance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use self::{circle::Circle, line::Line};
 | 
					pub use self::{circle::Circle, line::Line};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use fj_math::{Point, Scalar, Transform, Vector};
 | 
					use fj_math::{Point, Transform, Vector};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A one-dimensional shape
 | 
					/// A one-dimensional shape
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
@ -96,7 +98,7 @@ impl Curve {
 | 
				
			|||||||
    /// The `approximate_between` methods of the curves then need to make sure
 | 
					    /// The `approximate_between` methods of the curves then need to make sure
 | 
				
			||||||
    /// to only return points in between those vertices, not the vertices
 | 
					    /// to only return points in between those vertices, not the vertices
 | 
				
			||||||
    /// themselves.
 | 
					    /// themselves.
 | 
				
			||||||
    pub fn approx(&self, tolerance: Scalar, out: &mut Vec<Point<3>>) {
 | 
					    pub fn approx(&self, tolerance: Tolerance, out: &mut Vec<Point<3>>) {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::Circle(circle) => circle.approx(tolerance, out),
 | 
					            Self::Circle(circle) => circle.approx(tolerance, out),
 | 
				
			||||||
            Self::Line(_) => {}
 | 
					            Self::Line(_) => {}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
use fj_interop::debug::DebugInfo;
 | 
					use fj_interop::debug::DebugInfo;
 | 
				
			||||||
use fj_kernel::{
 | 
					use fj_kernel::{
 | 
				
			||||||
 | 
					    algorithms::Tolerance,
 | 
				
			||||||
    geometry::Surface,
 | 
					    geometry::Surface,
 | 
				
			||||||
    shape::Shape,
 | 
					    shape::Shape,
 | 
				
			||||||
    topology::{Cycle, Edge, Face},
 | 
					    topology::{Cycle, Edge, Face},
 | 
				
			||||||
@ -9,7 +10,7 @@ use fj_math::{Aabb, Point, Scalar};
 | 
				
			|||||||
use super::ToShape;
 | 
					use super::ToShape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ToShape for fj::Circle {
 | 
					impl ToShape for fj::Circle {
 | 
				
			||||||
    fn to_shape(&self, _: Scalar, _: &mut DebugInfo) -> Shape {
 | 
					    fn to_shape(&self, _: Tolerance, _: &mut DebugInfo) -> Shape {
 | 
				
			||||||
        let mut shape = Shape::new();
 | 
					        let mut shape = Shape::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Circles have just a single round edge with no vertices. So none need
 | 
					        // Circles have just a single round edge with no vertices. So none need
 | 
				
			||||||
 | 
				
			|||||||
@ -2,15 +2,20 @@ use std::collections::HashMap;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use fj_interop::debug::DebugInfo;
 | 
					use fj_interop::debug::DebugInfo;
 | 
				
			||||||
use fj_kernel::{
 | 
					use fj_kernel::{
 | 
				
			||||||
 | 
					    algorithms::Tolerance,
 | 
				
			||||||
    shape::{Handle, Shape},
 | 
					    shape::{Handle, Shape},
 | 
				
			||||||
    topology::{Cycle, Edge, Face, Vertex},
 | 
					    topology::{Cycle, Edge, Face, Vertex},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use fj_math::{Aabb, Scalar};
 | 
					use fj_math::Aabb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::ToShape;
 | 
					use super::ToShape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ToShape for fj::Difference2d {
 | 
					impl ToShape for fj::Difference2d {
 | 
				
			||||||
    fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape {
 | 
					    fn to_shape(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        tolerance: Tolerance,
 | 
				
			||||||
 | 
					        debug_info: &mut DebugInfo,
 | 
				
			||||||
 | 
					    ) -> Shape {
 | 
				
			||||||
        // This method assumes that `b` is fully contained within `a`:
 | 
					        // This method assumes that `b` is fully contained within `a`:
 | 
				
			||||||
        // https://github.com/hannobraun/Fornjot/issues/92
 | 
					        // https://github.com/hannobraun/Fornjot/issues/92
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,15 +2,20 @@ use std::collections::HashMap;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use fj_interop::debug::DebugInfo;
 | 
					use fj_interop::debug::DebugInfo;
 | 
				
			||||||
use fj_kernel::{
 | 
					use fj_kernel::{
 | 
				
			||||||
 | 
					    algorithms::Tolerance,
 | 
				
			||||||
    shape::Shape,
 | 
					    shape::Shape,
 | 
				
			||||||
    topology::{Cycle, Edge, Face, Vertex},
 | 
					    topology::{Cycle, Edge, Face, Vertex},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use fj_math::{Aabb, Scalar};
 | 
					use fj_math::Aabb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::ToShape;
 | 
					use super::ToShape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ToShape for fj::Group {
 | 
					impl ToShape for fj::Group {
 | 
				
			||||||
    fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape {
 | 
					    fn to_shape(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        tolerance: Tolerance,
 | 
				
			||||||
 | 
					        debug_info: &mut DebugInfo,
 | 
				
			||||||
 | 
					    ) -> Shape {
 | 
				
			||||||
        let mut shape = Shape::new();
 | 
					        let mut shape = Shape::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let a = self.a.to_shape(tolerance, debug_info);
 | 
					        let a = self.a.to_shape(tolerance, debug_info);
 | 
				
			||||||
 | 
				
			|||||||
@ -14,13 +14,13 @@ mod sweep;
 | 
				
			|||||||
mod transform;
 | 
					mod transform;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use fj_interop::debug::DebugInfo;
 | 
					use fj_interop::debug::DebugInfo;
 | 
				
			||||||
use fj_kernel::shape::Shape;
 | 
					use fj_kernel::{algorithms::Tolerance, shape::Shape};
 | 
				
			||||||
use fj_math::{Aabb, Scalar};
 | 
					use fj_math::Aabb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Implemented for all operations from the [`fj`] crate
 | 
					/// Implemented for all operations from the [`fj`] crate
 | 
				
			||||||
pub trait ToShape {
 | 
					pub trait ToShape {
 | 
				
			||||||
    /// Compute the boundary representation of the shape
 | 
					    /// Compute the boundary representation of the shape
 | 
				
			||||||
    fn to_shape(&self, tolerance: Scalar, debug: &mut DebugInfo) -> Shape;
 | 
					    fn to_shape(&self, tolerance: Tolerance, debug: &mut DebugInfo) -> Shape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Access the axis-aligned bounding box of a shape
 | 
					    /// Access the axis-aligned bounding box of a shape
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
@ -70,7 +70,7 @@ macro_rules! dispatch {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
dispatch! {
 | 
					dispatch! {
 | 
				
			||||||
    to_shape(
 | 
					    to_shape(
 | 
				
			||||||
        tolerance: Scalar,
 | 
					        tolerance: Tolerance,
 | 
				
			||||||
        debug: &mut DebugInfo,
 | 
					        debug: &mut DebugInfo,
 | 
				
			||||||
    ) -> Shape;
 | 
					    ) -> Shape;
 | 
				
			||||||
    bounding_volume() -> Aabb<3>;
 | 
					    bounding_volume() -> Aabb<3>;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,16 @@
 | 
				
			|||||||
use fj_interop::debug::DebugInfo;
 | 
					use fj_interop::debug::DebugInfo;
 | 
				
			||||||
use fj_kernel::{
 | 
					use fj_kernel::{
 | 
				
			||||||
 | 
					    algorithms::Tolerance,
 | 
				
			||||||
    geometry::Surface,
 | 
					    geometry::Surface,
 | 
				
			||||||
    shape::Shape,
 | 
					    shape::Shape,
 | 
				
			||||||
    topology::{Cycle, Edge, Face, Vertex},
 | 
					    topology::{Cycle, Edge, Face, Vertex},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use fj_math::{Aabb, Point, Scalar};
 | 
					use fj_math::{Aabb, Point};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::ToShape;
 | 
					use super::ToShape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ToShape for fj::Sketch {
 | 
					impl ToShape for fj::Sketch {
 | 
				
			||||||
    fn to_shape(&self, _: Scalar, _: &mut DebugInfo) -> Shape {
 | 
					    fn to_shape(&self, _: Tolerance, _: &mut DebugInfo) -> Shape {
 | 
				
			||||||
        let mut shape = Shape::new();
 | 
					        let mut shape = Shape::new();
 | 
				
			||||||
        let mut vertices = Vec::new();
 | 
					        let mut vertices = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,18 @@
 | 
				
			|||||||
use fj_interop::debug::DebugInfo;
 | 
					use fj_interop::debug::DebugInfo;
 | 
				
			||||||
use fj_kernel::{algorithms::sweep_shape, shape::Shape};
 | 
					use fj_kernel::{
 | 
				
			||||||
use fj_math::{Aabb, Scalar, Vector};
 | 
					    algorithms::{sweep_shape, Tolerance},
 | 
				
			||||||
 | 
					    shape::Shape,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use fj_math::{Aabb, Vector};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::ToShape;
 | 
					use super::ToShape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ToShape for fj::Sweep {
 | 
					impl ToShape for fj::Sweep {
 | 
				
			||||||
    fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape {
 | 
					    fn to_shape(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        tolerance: Tolerance,
 | 
				
			||||||
 | 
					        debug_info: &mut DebugInfo,
 | 
				
			||||||
 | 
					    ) -> Shape {
 | 
				
			||||||
        sweep_shape(
 | 
					        sweep_shape(
 | 
				
			||||||
            self.shape().to_shape(tolerance, debug_info),
 | 
					            self.shape().to_shape(tolerance, debug_info),
 | 
				
			||||||
            Vector::from([0., 0., self.length()]),
 | 
					            Vector::from([0., 0., self.length()]),
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,16 @@
 | 
				
			|||||||
use fj_interop::debug::DebugInfo;
 | 
					use fj_interop::debug::DebugInfo;
 | 
				
			||||||
use fj_kernel::shape::Shape;
 | 
					use fj_kernel::{algorithms::Tolerance, shape::Shape};
 | 
				
			||||||
use fj_math::{Aabb, Scalar, Transform};
 | 
					use fj_math::{Aabb, Transform};
 | 
				
			||||||
use parry3d_f64::math::Isometry;
 | 
					use parry3d_f64::math::Isometry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::ToShape;
 | 
					use super::ToShape;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ToShape for fj::Transform {
 | 
					impl ToShape for fj::Transform {
 | 
				
			||||||
    fn to_shape(&self, tolerance: Scalar, debug_info: &mut DebugInfo) -> Shape {
 | 
					    fn to_shape(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        tolerance: Tolerance,
 | 
				
			||||||
 | 
					        debug_info: &mut DebugInfo,
 | 
				
			||||||
 | 
					    ) -> Shape {
 | 
				
			||||||
        let mut shape = self.shape.to_shape(tolerance, debug_info);
 | 
					        let mut shape = self.shape.to_shape(tolerance, debug_info);
 | 
				
			||||||
        let transform = transform(self);
 | 
					        let transform = transform(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user