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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! Approximation of objects

pub mod curve;
pub mod cycle;
pub mod edge;
pub mod face;
pub mod path;
pub mod shell;
pub mod sketch;
pub mod solid;
pub mod tolerance;
pub mod vertex;

use std::{
    cmp::Ordering,
    fmt::Debug,
    hash::{Hash, Hasher},
};

use fj_math::Point;

use crate::Core;

pub use self::tolerance::{InvalidTolerance, Tolerance};

/// Approximate an object
pub trait Approx: Sized {
    /// The approximation of the object
    type Approximation;

    /// The cache used to cache approximation results
    type Cache: Default;

    /// Approximate the object
    ///
    /// `tolerance` defines how far the approximation is allowed to deviate from
    /// the actual object.
    fn approx(
        self,
        tolerance: impl Into<Tolerance>,
        core: &mut Core,
    ) -> Self::Approximation {
        let mut cache = Self::Cache::default();
        self.approx_with_cache(tolerance, &mut cache, core)
    }

    /// Approximate the object, using the provided cache
    ///
    /// This is a lower-level method that allows some degree of control over
    /// caching. Callers might consider using [`Approx::approx`] instead.
    fn approx_with_cache(
        self,
        tolerance: impl Into<Tolerance>,
        cache: &mut Self::Cache,
        core: &mut Core,
    ) -> Self::Approximation;
}

/// A point from an approximation, with local and global forms
#[derive(Clone, Copy, Debug)]
pub struct ApproxPoint<const D: usize> {
    /// The local form of the point
    pub local_form: Point<D>,

    /// The global form of the points
    pub global_form: Point<3>,
}

impl<const D: usize> ApproxPoint<D> {
    /// Create an instance of `ApproxPoint`
    pub fn new(
        local_form: impl Into<Point<D>>,
        global_form: impl Into<Point<3>>,
    ) -> Self {
        Self {
            local_form: local_form.into(),
            global_form: global_form.into(),
        }
    }
}

impl<const D: usize> Eq for ApproxPoint<D> {}

impl<const D: usize> PartialEq for ApproxPoint<D> {
    fn eq(&self, other: &Self) -> bool {
        self.local_form == other.local_form
            && self.global_form == other.global_form
    }
}

impl<const D: usize> Hash for ApproxPoint<D> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.local_form.hash(state);
        self.global_form.hash(state);
    }
}

impl<const D: usize> Ord for ApproxPoint<D> {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.local_form.cmp(&other.local_form) {
            Ordering::Equal => {}
            ord => return ord,
        }
        self.global_form.cmp(&other.global_form)
    }
}

impl<const D: usize> PartialOrd for ApproxPoint<D> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}