jagua_rs/geometry/fail_fast/
sp_surrogate.rs1use crate::geometry::Transformation;
2use crate::geometry::convex_hull;
3use crate::geometry::fail_fast::pole;
4use crate::geometry::geo_traits::{Transformable, TransformableFrom};
5use crate::geometry::primitives::Circle;
6use crate::geometry::primitives::SPolygon;
7use itertools::Itertools;
8use serde::{Deserialize, Serialize};
9
10use anyhow::{Result, ensure};
11
12#[derive(Clone, Debug)]
13pub struct SPSurrogate {
17 pub poles: Vec<Circle>,
19 pub convex_hull_indices: Vec<usize>,
22 pub convex_hull_area: f32,
24 pub config: SPSurrogateConfig,
26 n_ff_poles: usize,
28 hull_reversed: bool,
31}
32
33impl SPSurrogate {
34 pub fn new(simple_poly: &SPolygon, config: SPSurrogateConfig) -> Result<Self> {
37 ensure!(
38 (0.0..=1.0).contains(&config.ff_pole_area_ratio),
39 "fail-fast pole area ratio must be between 0.0 and 1.0"
40 );
41 let convex_hull_indices = convex_hull::convex_hull_indices(simple_poly);
42 let convex_hull_points = convex_hull_indices
43 .iter()
44 .map(|&i| simple_poly.vertices[i])
45 .collect_vec();
46 let convex_hull_area = SPolygon::calculate_area(&convex_hull_points);
47 let poles = pole::generate_surrogate_poles(simple_poly, &config.n_pole_limits)?;
48 let n_ff_poles = if config.ff_pole_area_ratio == 0.0 {
49 0
50 } else {
51 let target_area = simple_poly.area * config.ff_pole_area_ratio;
52 let mut covered_area = 0.0;
53 poles
54 .iter()
55 .position(|pole| {
56 covered_area += pole.area();
57 covered_area >= target_area
58 })
59 .map_or(poles.len(), |i| i + 1)
60 };
61
62 Ok(Self {
63 poles,
64 convex_hull_indices,
65 convex_hull_area,
66 config,
67 n_ff_poles,
68 hull_reversed: false,
69 })
70 }
71
72 #[must_use]
74 pub fn ff_poles(&self) -> &[Circle] {
75 &self.poles[..self.n_ff_poles]
76 }
77
78 pub(crate) fn reverse_vertex_order(&mut self, n_vertices: usize) {
80 self.convex_hull_indices.reverse();
81 for i in &mut self.convex_hull_indices {
82 *i = n_vertices - 1 - *i;
83 }
84 self.hull_reversed = !self.hull_reversed;
85 }
86
87 pub(crate) fn hull_reversed(&self) -> bool {
89 self.hull_reversed
90 }
91}
92
93impl Transformable for SPSurrogate {
94 fn transform(&mut self, t: &Transformation) -> &mut Self {
95 let Self {
97 convex_hull_indices: _,
98 poles,
99 convex_hull_area: _,
100 config: _,
101 n_ff_poles: _,
102 hull_reversed: _,
103 } = self;
104
105 for c in poles.iter_mut() {
107 c.transform(t);
108 }
109
110 self
111 }
112}
113
114impl TransformableFrom for SPSurrogate {
115 fn transform_from(&mut self, reference: &Self, t: &Transformation) -> &mut Self {
116 debug_assert_eq!(self.poles.len(), reference.poles.len());
117
118 let Self {
120 convex_hull_indices: _,
121 poles,
122 convex_hull_area: _,
123 config: _,
124 n_ff_poles: _,
125 hull_reversed: _,
126 } = self;
127
128 for (pole, ref_pole) in poles.iter_mut().zip(reference.poles.iter()) {
129 pole.transform_from(ref_pole, t);
130 }
131
132 self
133 }
134}
135
136const N_POLE_LIMITS: usize = 3;
138
139#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
141pub struct SPSurrogateConfig {
142 pub n_pole_limits: [(usize, f32); N_POLE_LIMITS],
148 pub ff_pole_area_ratio: f32,
154}
155
156impl SPSurrogateConfig {
157 #[must_use]
158 pub fn none() -> Self {
159 Self {
160 n_pole_limits: [(0, 0.0); N_POLE_LIMITS],
161 ff_pole_area_ratio: 0.0,
162 }
163 }
164}