use sim_kernel::{Error, Expr, Result};
use sim_lib_scene::{node, sym};
use sim_value::access::field as intent_field;
use crate::num::{as_f64, number};
use crate::plot::{multi_plot_view, response_plot_view};
pub struct Sweep {
param: f64,
samples: usize,
snapshots: Vec<(String, Vec<(f64, f64)>)>,
}
impl Sweep {
pub fn new(param: f64, samples: usize) -> Self {
Self {
param,
samples: samples.max(2),
snapshots: Vec::new(),
}
}
pub fn param(&self) -> f64 {
self.param
}
fn series(&self) -> Vec<(f64, f64)> {
(0..self.samples)
.map(|i| {
let x = i as f64;
(x, self.param * x)
})
.collect()
}
pub fn plot(&self) -> Expr {
let plot = multi_plot_view(&[(format!("param={}", self.param), self.series())]);
node(
"stack",
vec![
("role", sym("sweep")),
("dir", sym("column")),
(
"children",
Expr::List(vec![
node(
"slider",
vec![
("param", sym("slope")),
("min", number(-5.0)),
("max", number(5.0)),
("value", number(self.param)),
],
),
plot,
]),
),
],
)
}
pub fn set_param(&mut self, intent: &Expr) -> Result<Expr> {
match intent_field(intent, "kind") {
Some(Expr::Symbol(kind)) if &*kind.name == "set-param" => {}
_ => return Err(Error::HostError("expected an intent/set-param".to_owned())),
}
let value = intent_field(intent, "value")
.and_then(as_f64)
.ok_or_else(|| Error::HostError("set-param 'value' must be a number".to_owned()))?;
self.param = value;
Ok(self.plot())
}
pub fn snapshot(&mut self) {
self.snapshots
.push((format!("param={}", self.param), self.series()));
}
pub fn snapshot_count(&self) -> usize {
self.snapshots.len()
}
pub fn compare(&self) -> Expr {
let mut series = self.snapshots.clone();
series.push((format!("param={} (current)", self.param), self.series()));
multi_plot_view(&series)
}
}
pub fn response_sweep_view(
lens: &str,
role: &str,
param: SweepParam<'_>,
series_list: &[(String, Vec<(f64, f64)>)],
) -> Expr {
node(
"stack",
vec![
("lens", sym(lens)),
("role", sym(role)),
("dir", sym("column")),
(
"children",
Expr::List(vec![
node(
"slider",
vec![
("param", sym(param.name)),
("min", number(param.min)),
("max", number(param.max)),
("value", number(param.value)),
],
),
response_plot_view(lens, "response-plot", series_list),
]),
),
],
)
}
pub struct SweepParam<'a> {
pub name: &'a str,
pub min: f64,
pub max: f64,
pub value: f64,
}