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
use fj_interop::mesh::Color;
use fj_math::Vector;

use crate::{
    objects::{Cycle, Face, Surface},
    operations::{
        build::BuildCycle, join::JoinCycle, sweep::half_edge::SweepHalfEdge,
    },
    services::Services,
};

use super::SweepCache;

/// # Sweep a [`Cycle`]
///
/// See [module documentation] for more information.
///
/// [module documentation]: super
pub trait SweepCycle {
    /// # Sweep the [`Cycle`]
    ///
    /// Sweep the cycle into a set of connected faces. Each half-edge in the
    /// cycle is swept into a face, meaning all resulting faces form a connected
    /// set of side walls.
    ///
    /// Requires the surface that the half-edges of the cycle are defined in,
    /// and optionally the color of the created faces.
    ///
    /// There is no face at the "top" (the end of the sweep path), as we don't
    /// have enough information here to create that. We just have a single
    /// cycle, and don't know whether that is supposed to be the only cycle
    /// within a potential top face, or whether it's an exterior or interior
    /// one.
    ///
    /// For the same reason, there also is no "bottom" face. Additionally,
    /// whether a bottom face is even desirable depends on the context this
    /// operation is called in, and therefore falls outside of its scope.
    fn sweep_cycle(
        &self,
        surface: &Surface,
        color: Option<Color>,
        path: impl Into<Vector<3>>,
        cache: &mut SweepCache,
        services: &mut Services,
    ) -> SweptCycle;
}

impl SweepCycle for Cycle {
    fn sweep_cycle(
        &self,
        surface: &Surface,
        color: Option<Color>,
        path: impl Into<Vector<3>>,
        cache: &mut SweepCache,
        services: &mut Services,
    ) -> SweptCycle {
        let path = path.into();

        let mut faces = Vec::new();
        let mut top_edges = Vec::new();

        for bottom_half_edge_pair in self.half_edges().pairs() {
            let (bottom_half_edge, bottom_half_edge_next) =
                bottom_half_edge_pair;

            let (side_face, top_edge) = bottom_half_edge.sweep_half_edge(
                bottom_half_edge_next.start_vertex().clone(),
                surface,
                color,
                path,
                cache,
                services,
            );

            faces.push(side_face);

            top_edges.push((
                top_edge,
                bottom_half_edge.path(),
                bottom_half_edge.boundary(),
            ));
        }

        let top_cycle = Cycle::empty().add_joined_edges(top_edges, services);

        SweptCycle { faces, top_cycle }
    }
}

/// The result of sweeping a [`Cycle`]
///
/// See [`SweepCycle`].
pub struct SweptCycle {
    /// The faces created by sweeping each half-edge of the cycle
    ///
    /// See [`SweepCycle::sweep_cycle`] for more information.
    pub faces: Vec<Face>,

    /// A cycle made up of the "top" half-edges of the resulting faces
    ///
    /// "Top" here refers to the place that the sweep path points to from the
    /// original cycle. Essentially, this is a translated (along the sweep path)
    /// and reversed version of the original cycle.
    pub top_cycle: Cycle,
}