runmat_meshing_size/adaptive/
markers.rs1use 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}