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
use std::fmt;

use fj_math::{Point, Scalar};

use crate::objects::{Curve, Vertex};

pub fn validate_vertex(
    vertex: &Vertex,
    max_distance: impl Into<Scalar>,
) -> 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 surface and global forms of a curve
    #[error("Mismatch between surface and global forms of curve")]
    Curve(#[from] CurveCoherenceMismatch),

    /// 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 surface and global forms of a curve
///
/// Used in [`CoherenceIssues`].
#[derive(Debug, thiserror::Error)]
pub struct CurveCoherenceMismatch {
    /// The curve coordinate for which a mismatch was found
    pub point_curve: Point<1>,

    /// The curve coordinate, converted to surface coordinates
    pub point_surface: Point<2>,

    /// The surface coordinates, converted to global coordinates
    pub point_surface_as_global: Point<3>,

    /// The curve coordinate, converted to global coordinates
    pub point_global: Point<3>,

    /// The incoherent curve
    pub curve: Curve,
}

impl fmt::Display for CurveCoherenceMismatch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "local: {:?} (converted to surface: {:?}; to global: {:?}), global: {:?},",
            self.point_curve, self.point_surface, self.point_surface_as_global, self.point_global,
        )
    }
}

/// 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,
        )
    }
}