use std::rc::Rc;
use dspk_pipeline::Node;
use gpui_kit::{App, Context, Window};
use kitchen_ui::Params;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
pub use kitchen_ui::param_editor::{label_of, trim_number};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct NodeParams(pub Node);
impl Params for NodeParams {
fn choices(field: &str) -> Option<&'static [&'static str]> {
choices(field)
}
fn element_label(field: &str, len: usize, i: usize) -> String {
match (field, len, i) {
("cutoff", 2, 0) => "Low (Hz)".to_string(),
("cutoff", 2, 1) => "High (Hz)".to_string(),
("cutoff", _, _) => "Cutoff (Hz)".to_string(),
_ => format!("{} {}", label_of(field), i + 1),
}
}
fn on_choice(value: &mut Value, field: &str, choice: &str) {
if field != "band" {
return;
}
let cut: Vec<f64> = value["cutoff"].as_array().map(|a| a.iter().filter_map(Value::as_f64).collect()).unwrap_or_default();
let (lo, hi) = (cut.first().copied().unwrap_or(300.0), cut.last().copied().unwrap_or(6000.0));
value["cutoff"] = match choice {
"lowpass" => json!([hi]),
"highpass" => json!([lo]),
_ if cut.len() == 2 => json!(cut),
_ => json!([lo, if hi > lo { hi } else { lo * 10.0 }]),
};
}
}
pub type ParamEditor = kitchen_ui::ParamEditor<NodeParams>;
pub type Commit = Rc<dyn Fn(Node, &mut App) -> Result<(), String>>;
pub fn choices(field: &str) -> Option<&'static [&'static str]> {
Some(match field {
"band" => &["lowpass", "highpass", "bandpass", "bandstop"],
"design" => &["butterworth", "chebyshev1", "bessel"],
"mode" => &["forward_backward", "forward"],
"start" => &["rest", "steady_state"],
"edge" => &["zeros", "odd", "reflect", "nearest"],
_ => return None,
})
}
pub trait EditNode {
fn set_node(&mut self, key: &str, node: &Node, commit: Commit, window: &mut Window, cx: &mut Context<Self>)
where
Self: Sized;
}
impl EditNode for ParamEditor {
fn set_node(&mut self, key: &str, node: &Node, commit: Commit, window: &mut Window, cx: &mut Context<Self>) {
let commit: kitchen_ui::Commit<NodeParams> = Rc::new(move |p: NodeParams, cx| commit(p.0, cx));
self.set_target(key, &NodeParams(node.clone()), commit, window, cx);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_labels_and_numbers() {
assert_eq!(label_of("sigma_samples"), "Sigma samples");
assert_eq!(trim_number(300.0), "300");
assert_eq!(trim_number(0.25), "0.25");
assert_eq!(NodeParams::element_label("cutoff", 2, 1), "High (Hz)");
}
#[test]
fn a_band_change_keeps_the_cutoffs_it_can() {
let mut v = json!({"op": "iir", "band": "bandpass", "cutoff": [300.0, 6000.0]});
NodeParams::on_choice(&mut v, "band", "highpass");
assert_eq!(v["cutoff"], json!([300.0]));
NodeParams::on_choice(&mut v, "band", "bandstop");
assert_eq!(v["cutoff"], json!([300.0, 3000.0]));
}
}