Skip to main content

runmat_meshing_size/adaptive/
markers.rs

1use serde::{Deserialize, Serialize};
2
3use super::SizingFieldUpdate;
4use crate::field::SizingSample;
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct RefinementMarker {
8    pub entity_id: String,
9    pub weight: f64,
10    pub reason: String,
11}
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub struct RefinementIndicatorSample {
15    pub entity_id: String,
16    pub position_m: [f64; 3],
17    pub indicator_value: f64,
18    pub current_size_m: f64,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
22pub struct RefinementMarkerOptions {
23    pub max_markers: usize,
24    pub min_relative_value: f64,
25    pub target_size_scale: f64,
26}
27
28impl Default for RefinementMarkerOptions {
29    fn default() -> Self {
30        Self {
31            max_markers: 64,
32            min_relative_value: 0.25,
33            target_size_scale: 0.5,
34        }
35    }
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub enum RefinementMarkerError {
40    InvalidMaxMarkers,
41    InvalidMinRelativeValue,
42    InvalidTargetSizeScale,
43}
44
45pub fn build_refinement_markers_from_samples(
46    samples: &[RefinementIndicatorSample],
47    reason: &str,
48    options: RefinementMarkerOptions,
49) -> Result<(Vec<RefinementMarker>, SizingFieldUpdate), RefinementMarkerError> {
50    if options.max_markers == 0 {
51        return Err(RefinementMarkerError::InvalidMaxMarkers);
52    }
53    if !options.min_relative_value.is_finite() || !(0.0..=1.0).contains(&options.min_relative_value)
54    {
55        return Err(RefinementMarkerError::InvalidMinRelativeValue);
56    }
57    if !options.target_size_scale.is_finite()
58        || options.target_size_scale <= 0.0
59        || options.target_size_scale >= 1.0
60    {
61        return Err(RefinementMarkerError::InvalidTargetSizeScale);
62    }
63
64    let mut finite_samples = samples
65        .iter()
66        .filter(|sample| {
67            sample.indicator_value.is_finite()
68                && sample.indicator_value > 0.0
69                && sample.current_size_m.is_finite()
70                && sample.current_size_m > 0.0
71                && sample.position_m.iter().all(|value| value.is_finite())
72        })
73        .collect::<Vec<_>>();
74    let Some(max_value) = finite_samples
75        .iter()
76        .map(|sample| sample.indicator_value)
77        .reduce(f64::max)
78    else {
79        return Ok((Vec::new(), SizingFieldUpdate::default()));
80    };
81
82    finite_samples
83        .retain(|sample| sample.indicator_value / max_value >= options.min_relative_value);
84    finite_samples.sort_by(|left, right| {
85        right
86            .indicator_value
87            .total_cmp(&left.indicator_value)
88            .then_with(|| left.entity_id.cmp(&right.entity_id))
89    });
90    finite_samples.truncate(options.max_markers);
91
92    let markers = finite_samples
93        .iter()
94        .map(|sample| RefinementMarker {
95            entity_id: sample.entity_id.clone(),
96            weight: sample.indicator_value / max_value,
97            reason: reason.to_string(),
98        })
99        .collect::<Vec<_>>();
100    let sizing_update = SizingFieldUpdate {
101        samples: finite_samples
102            .into_iter()
103            .map(|sample| SizingSample {
104                position_m: sample.position_m,
105                target_size_m: sample.current_size_m * options.target_size_scale,
106                reason: Some(reason.to_string()),
107            })
108            .collect(),
109        min_size_m: None,
110        max_size_m: None,
111    };
112
113    Ok((markers, sizing_update))
114}