fj_core/algorithms/approx/
cycle.rs

1//! Cycle approximation
2//!
3//! See [`CycleApprox`].
4
5use fj_math::Segment;
6
7use crate::{geometry::SurfaceGeometry, objects::Cycle, Core};
8
9use super::{
10    edge::{HalfEdgeApprox, HalfEdgeApproxCache},
11    Approx, ApproxPoint, Tolerance,
12};
13
14impl Approx for (&Cycle, &SurfaceGeometry) {
15    type Approximation = CycleApprox;
16    type Cache = HalfEdgeApproxCache;
17
18    fn approx_with_cache(
19        self,
20        tolerance: impl Into<Tolerance>,
21        cache: &mut Self::Cache,
22        core: &mut Core,
23    ) -> Self::Approximation {
24        let (cycle, surface) = self;
25        let tolerance = tolerance.into();
26
27        let half_edges = cycle
28            .half_edges()
29            .iter()
30            .map(|half_edge| {
31                (half_edge, surface).approx_with_cache(tolerance, cache, core)
32            })
33            .collect();
34
35        CycleApprox { half_edges }
36    }
37}
38
39/// An approximation of a [`Cycle`]
40#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
41pub struct CycleApprox {
42    /// The approximated half-edges that make up the approximated cycle
43    pub half_edges: Vec<HalfEdgeApprox>,
44}
45
46impl CycleApprox {
47    /// Compute the points that approximate the cycle
48    pub fn points(&self) -> Vec<ApproxPoint<2>> {
49        let mut points = Vec::new();
50
51        for approx in &self.half_edges {
52            points.extend(approx.points.iter().copied());
53        }
54
55        if let Some(point) = points.first() {
56            points.push(*point);
57        }
58
59        points
60    }
61
62    /// Construct the segments that approximate the cycle
63    pub fn segments(&self) -> Vec<Segment<3>> {
64        let mut segments = Vec::new();
65
66        for segment in self.points().windows(2) {
67            // This can't panic, as we passed `2` to `windows`. Can be cleaned
68            // up, once `array_windows` is stable.
69            let segment = [&segment[0], &segment[1]];
70
71            segments
72                .push(Segment::from(segment.map(|point| point.global_form)));
73        }
74
75        segments
76    }
77}