fj_core/operations/build/
half_edge.rs

1use fj_interop::ext::ArrayExt;
2use fj_math::{Arc, Point, Scalar};
3
4use crate::{
5    geometry::{CurveBoundary, HalfEdgeGeometry, SurfacePath},
6    objects::{Curve, HalfEdge, Vertex},
7    operations::insert::Insert,
8    storage::Handle,
9    Core,
10};
11
12/// Build a [`HalfEdge`]
13///
14/// See [module-level documentation] for context.
15///
16/// [module-level documentation]: super
17pub trait BuildHalfEdge {
18    /// Create a half-edge that is not joined to a sibling
19    fn unjoined(
20        path: SurfacePath,
21        boundary: impl Into<CurveBoundary<Point<1>>>,
22        core: &mut Core,
23    ) -> HalfEdge {
24        let curve = Curve::new().insert(core);
25        let start_vertex = Vertex::new().insert(core);
26
27        HalfEdge::new(path, boundary, curve, start_vertex)
28    }
29
30    /// Create a half-edge from its sibling
31    fn from_sibling(
32        sibling: &Handle<HalfEdge>,
33        start_vertex: Handle<Vertex>,
34        core: &mut Core,
35    ) -> Handle<HalfEdge> {
36        let half_edge = HalfEdge::new(
37            core.layers.geometry.of_half_edge(sibling).path,
38            sibling.boundary().reverse(),
39            sibling.curve().clone(),
40            start_vertex,
41        )
42        .insert(core);
43
44        core.layers.geometry.define_half_edge(
45            half_edge.clone(),
46            HalfEdgeGeometry {
47                path: core.layers.geometry.of_half_edge(sibling).path,
48            },
49        );
50
51        half_edge
52    }
53
54    /// Create an arc
55    ///
56    /// # Panics
57    ///
58    /// Panics if the given angle is not within the range (-2pi, 2pi) radians.
59    fn arc(
60        start: impl Into<Point<2>>,
61        end: impl Into<Point<2>>,
62        angle_rad: impl Into<Scalar>,
63        core: &mut Core,
64    ) -> Handle<HalfEdge> {
65        let angle_rad = angle_rad.into();
66        if angle_rad <= -Scalar::TAU || angle_rad >= Scalar::TAU {
67            panic!("arc angle must be in the range (-2pi, 2pi) radians");
68        }
69
70        let arc = Arc::from_endpoints_and_angle(start, end, angle_rad);
71
72        let path =
73            SurfacePath::circle_from_center_and_radius(arc.center, arc.radius);
74        let boundary =
75            [arc.start_angle, arc.end_angle].map(|coord| Point::from([coord]));
76
77        let half_edge = HalfEdge::unjoined(path, boundary, core).insert(core);
78        core.layers
79            .geometry
80            .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path });
81
82        half_edge
83    }
84
85    /// Create a circle
86    fn circle(
87        center: impl Into<Point<2>>,
88        radius: impl Into<Scalar>,
89        core: &mut Core,
90    ) -> Handle<HalfEdge> {
91        let path = SurfacePath::circle_from_center_and_radius(center, radius);
92        let boundary =
93            [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord]));
94
95        let half_edge = HalfEdge::unjoined(path, boundary, core).insert(core);
96        core.layers
97            .geometry
98            .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path });
99
100        half_edge
101    }
102
103    /// Create a line segment
104    fn line_segment(
105        points_surface: [impl Into<Point<2>>; 2],
106        boundary: Option<[Point<1>; 2]>,
107        core: &mut Core,
108    ) -> Handle<HalfEdge> {
109        let boundary =
110            boundary.unwrap_or_else(|| [[0.], [1.]].map(Point::from));
111        let path = SurfacePath::line_from_points_with_coords(
112            boundary.zip_ext(points_surface),
113        );
114
115        let half_edge = HalfEdge::unjoined(path, boundary, core).insert(core);
116        core.layers
117            .geometry
118            .define_half_edge(half_edge.clone(), HalfEdgeGeometry { path });
119
120        half_edge
121    }
122}
123
124impl BuildHalfEdge for HalfEdge {}