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
use fj_math::{Point, Scalar};

use crate::{
    geometry::curve::Curve,
    insert::Insert,
    objects::{GlobalEdge, HalfEdge, Objects, Vertex},
    partial::{FullToPartialCache, PartialObject},
    services::Service,
    storage::Handle,
};

/// A partial [`HalfEdge`]
#[derive(Clone, Debug)]
pub struct PartialHalfEdge {
    /// The curve that the half-edge is defined in
    pub curve: Option<MaybeCurve>,

    /// The boundary of the half-edge on the curve
    pub boundary: [Option<Point<1>>; 2],

    /// The surface vertex where the half-edge starts
    pub start_vertex: Handle<Vertex>,

    /// The global form of the half-edge
    pub global_form: Handle<GlobalEdge>,
}

impl PartialHalfEdge {
    /// Compute the surface position where the half-edge starts
    pub fn start_position(&self) -> Option<Point<2>> {
        // Computing the surface position from the curve position is fine.
        // `HalfEdge` "owns" its start position. There is no competing code that
        // could compute the surface position from slightly different data.

        let [start, _] = self.boundary;
        start.and_then(|start| {
            let curve = self.curve?;

            if let MaybeCurve::Defined(curve) = curve {
                return Some(curve.point_from_path_coords(start));
            }

            None
        })
    }
}

impl PartialObject for PartialHalfEdge {
    type Full = HalfEdge;

    fn new(objects: &mut Service<Objects>) -> Self {
        Self {
            curve: None,
            boundary: [None; 2],
            start_vertex: Vertex::new().insert(objects),
            global_form: GlobalEdge::new().insert(objects),
        }
    }

    fn from_full(half_edge: &Self::Full, _: &mut FullToPartialCache) -> Self {
        Self {
            curve: Some(half_edge.curve().into()),
            boundary: half_edge.boundary().map(Some),
            start_vertex: half_edge.start_vertex().clone(),
            global_form: half_edge.global_form().clone(),
        }
    }

    fn build(self, _: &mut Service<Objects>) -> Self::Full {
        let curve = match self.curve.expect("Need path to build curve") {
            MaybeCurve::Defined(path) => path,
            undefined => {
                panic!(
                    "Trying to build curve with undefined path: {undefined:?}"
                )
            }
        };
        let boundary = self.boundary.map(|point| {
            point.expect("Can't build `HalfEdge` without boundary positions")
        });

        HalfEdge::new(curve, boundary, self.start_vertex, self.global_form)
    }
}

/// A possibly undefined curve
#[derive(Clone, Copy, Debug)]
pub enum MaybeCurve {
    /// The curve is fully defined
    Defined(Curve),

    /// The curve is undefined, but we know it is a circle
    UndefinedCircle {
        /// The radius of the undefined circle
        radius: Scalar,
    },

    /// The curve is undefined, but we know it is a line
    UndefinedLine,
}

impl From<Curve> for MaybeCurve {
    fn from(path: Curve) -> Self {
        Self::Defined(path)
    }
}