sim_lib_view_math/
sweep.rs1use sim_kernel::{Error, Expr, Result};
9use sim_lib_scene::{node, sym};
10use sim_value::access::field as intent_field;
11
12use crate::num::{as_f64, number};
13use crate::plot::{multi_plot_view, response_plot_view};
14
15pub struct Sweep {
17 param: f64,
18 samples: usize,
19 snapshots: Vec<(String, Vec<(f64, f64)>)>,
20}
21
22impl Sweep {
23 pub fn new(param: f64, samples: usize) -> Self {
25 Self {
26 param,
27 samples: samples.max(2),
28 snapshots: Vec::new(),
29 }
30 }
31
32 pub fn param(&self) -> f64 {
34 self.param
35 }
36
37 fn series(&self) -> Vec<(f64, f64)> {
38 (0..self.samples)
39 .map(|i| {
40 let x = i as f64;
41 (x, self.param * x)
42 })
43 .collect()
44 }
45
46 pub fn plot(&self) -> Expr {
49 let plot = multi_plot_view(&[(format!("param={}", self.param), self.series())]);
50 node(
51 "stack",
52 vec![
53 ("role", sym("sweep")),
54 ("dir", sym("column")),
55 (
56 "children",
57 Expr::List(vec![
58 node(
59 "slider",
60 vec![
61 ("param", sym("slope")),
62 ("min", number(-5.0)),
63 ("max", number(5.0)),
64 ("value", number(self.param)),
65 ],
66 ),
67 plot,
68 ]),
69 ),
70 ],
71 )
72 }
73
74 pub fn set_param(&mut self, intent: &Expr) -> Result<Expr> {
77 match intent_field(intent, "kind") {
78 Some(Expr::Symbol(kind)) if &*kind.name == "set-param" => {}
79 _ => return Err(Error::HostError("expected an intent/set-param".to_owned())),
80 }
81 let value = intent_field(intent, "value")
82 .and_then(as_f64)
83 .ok_or_else(|| Error::HostError("set-param 'value' must be a number".to_owned()))?;
84 self.param = value;
85 Ok(self.plot())
86 }
87
88 pub fn snapshot(&mut self) {
90 self.snapshots
91 .push((format!("param={}", self.param), self.series()));
92 }
93
94 pub fn snapshot_count(&self) -> usize {
96 self.snapshots.len()
97 }
98
99 pub fn compare(&self) -> Expr {
101 let mut series = self.snapshots.clone();
102 series.push((format!("param={} (current)", self.param), self.series()));
103 multi_plot_view(&series)
104 }
105}
106
107pub fn response_sweep_view(
109 lens: &str,
110 role: &str,
111 param: SweepParam<'_>,
112 series_list: &[(String, Vec<(f64, f64)>)],
113) -> Expr {
114 node(
115 "stack",
116 vec![
117 ("lens", sym(lens)),
118 ("role", sym(role)),
119 ("dir", sym("column")),
120 (
121 "children",
122 Expr::List(vec![
123 node(
124 "slider",
125 vec![
126 ("param", sym(param.name)),
127 ("min", number(param.min)),
128 ("max", number(param.max)),
129 ("value", number(param.value)),
130 ],
131 ),
132 response_plot_view(lens, "response-plot", series_list),
133 ]),
134 ),
135 ],
136 )
137}
138
139pub struct SweepParam<'a> {
141 pub name: &'a str,
143 pub min: f64,
145 pub max: f64,
147 pub value: f64,
149}