1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! API for processing shapes

use fj_interop::{debug::DebugInfo, processed_shape::ProcessedShape};
use fj_kernel::{
    algorithms::{
        approx::{InvalidTolerance, Tolerance},
        triangulate::Triangulate,
    },
    objects::Objects,
    validate::ValidationError,
};
use fj_math::Scalar;

use crate::Shape as _;

/// Processes an [`fj::Shape`] into a [`ProcessedShape`]
pub struct ShapeProcessor {
    /// The tolerance value used for creating the triangle mesh
    pub tolerance: Option<Tolerance>,
}

impl ShapeProcessor {
    /// Process an [`fj::Shape`] into [`ProcessedShape`]
    pub fn process(&self, shape: &fj::Shape) -> Result<ProcessedShape, Error> {
        let aabb = shape.bounding_volume();

        let tolerance = match self.tolerance {
            None => {
                // Compute a reasonable default for the tolerance value. To do
                // this, we just look at the smallest non-zero extent of the
                // bounding box and divide that by some value.
                let mut min_extent = Scalar::MAX;
                for extent in aabb.size().components {
                    if extent > Scalar::ZERO && extent < min_extent {
                        min_extent = extent;
                    }
                }

                let tolerance = min_extent / Scalar::from_f64(1000.);
                Tolerance::from_scalar(tolerance)?
            }
            Some(user_defined_tolerance) => user_defined_tolerance,
        };

        let objects = Objects::new();
        let mut debug_info = DebugInfo::new();
        let shape = shape.compute_brep(&objects, &mut debug_info)?;
        let mesh = (&shape, tolerance).triangulate();

        Ok(ProcessedShape {
            aabb,
            mesh,
            debug_info,
        })
    }
}

/// A shape processing error
#[allow(clippy::large_enum_variant)]
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// Error converting to shape
    #[error("Error converting to shape")]
    ToShape(#[from] ValidationError),

    /// Model has zero size
    #[error("Model has zero size")]
    Extent(#[from] InvalidTolerance),
}