1use std::sync::{Arc, Mutex};
2
3use egui::ahash::HashMap;
4use mimium_lang::{
5 ast::{Expr, Literal},
6 code, function,
7 interner::{ToSymbol, TypeNodeId},
8 interpreter::Value,
9 log, numeric,
10 pattern::TypedId,
11 plugin::{SysPluginSignature, SystemPlugin, SystemPluginFnType, SystemPluginMacroType},
12 runtime::vm::{Machine, ReturnCode},
13 string_t,
14 types::{PType, Type},
15};
16use plot_window::PlotApp;
17use ringbuf::{
18 HeapProd, HeapRb,
19 traits::{Producer, Split},
20};
21
22use crate::plot_window::FloatParameter;
23pub(crate) mod plot_ui;
24pub mod plot_window;
25
26pub struct GuiToolPlugin {
27 window: Arc<Mutex<PlotApp>>,
28 slider_instances: Vec<Arc<FloatParameter>>,
29 slider_namemap: HashMap<String, usize>,
30
31 probe_instances: Vec<HeapProd<f64>>,
32 probe_namemap: HashMap<String, usize>,
33}
34
35impl Default for GuiToolPlugin {
36 fn default() -> Self {
37 Self {
38 window: Arc::new(Mutex::new(PlotApp::default())),
39 slider_instances: Vec::new(),
40 slider_namemap: HashMap::default(),
41 probe_instances: Vec::new(),
42 probe_namemap: HashMap::default(),
43 }
44 }
45}
46
47impl GuiToolPlugin {
48 fn get_closure_type() -> TypeNodeId {
49 function!(vec![numeric!()], numeric!())
50 }
51 const GET_SLIDER: &'static str = "__get_slider";
52 const PROBE_INTERCEPT: &'static str = "__probe_intercept";
53
54 pub fn make_slider(&mut self, v: &[(Value, TypeNodeId)]) -> Value {
55 assert_eq!(v.len(), 4);
56 let (name, init, min, max, mut window) = match (
57 v[0].0.clone(),
58 v[1].0.clone(),
59 v[2].0.clone(),
60 v[3].0.clone(),
61 self.window.lock(),
62 ) {
63 (
64 Value::String(name),
65 Value::Number(init),
66 Value::Number(min),
67 Value::Number(max),
68 Ok(window),
69 ) => (name, init, min, max, window),
70 _ => {
71 log::error!("invalid argument");
72 return Value::Number(0.0);
73 }
74 };
75 let idx = if let Some(idx) = self.slider_namemap.get(name.as_str()).cloned() {
76 let p = self.slider_instances.get_mut(idx).unwrap();
77 p.set_range(min, max);
78 idx
79 } else {
80 let (p, idx) = window.add_slider(name.as_str(), init, min, max);
81 self.slider_instances.push(p);
82 self.slider_namemap.insert(name.to_string(), idx);
83 idx
84 };
85 Value::Code(
86 Expr::Apply(
87 Expr::Var(Self::GET_SLIDER.to_symbol()).into_id_without_span(),
88 vec![
89 Expr::Literal(Literal::Float(idx.to_string().to_symbol()))
90 .into_id_without_span(),
91 ],
92 )
93 .into_id_without_span(),
94 )
95 }
96
97 pub fn make_probe_macro(&mut self, v: &[(Value, TypeNodeId)]) -> Value {
98 assert_eq!(v.len(), 1);
99 let (name, mut window) = match (v[0].0.clone(), self.window.lock()) {
100 (Value::String(name), Ok(window)) => (name, window),
101 _ => {
102 log::error!("invalid argument for Probe macro type {}", v[0].1);
103 return Value::Code(
104 Expr::Lambda(
105 vec![TypedId::new(
106 "x".to_symbol(),
107 Type::Primitive(PType::Numeric).into_id(),
108 )],
109 None,
110 Expr::Var("x".to_symbol()).into_id_without_span(),
111 )
112 .into_id_without_span(),
113 );
114 }
115 };
116 let probeid = self
117 .probe_namemap
118 .get(name.as_str())
119 .cloned()
120 .unwrap_or_else(|| {
121 let (prod, cons) = HeapRb::<f64>::new(4096).split();
122 window.add_plot(name.as_str(), cons);
123 let idx = self.probe_instances.len();
124 self.probe_instances.push(prod);
125 self.probe_namemap.insert(name.to_string(), idx);
126 idx
127 });
128
129 Value::Code(
131 Expr::Lambda(
132 vec![TypedId::new(
133 "x".to_symbol(),
134 Type::Primitive(PType::Numeric).into_id(),
135 )],
136 None,
137 Expr::Apply(
138 Expr::Var(Self::PROBE_INTERCEPT.to_symbol()).into_id_without_span(),
139 vec![
140 Expr::Var("x".to_symbol()).into_id_without_span(),
141 Expr::Literal(Literal::Float(probeid.to_string().to_symbol()))
142 .into_id_without_span(),
143 ],
144 )
145 .into_id_without_span(),
146 )
147 .into_id_without_span(),
148 )
149 }
150 pub fn get_slider(&mut self, vm: &mut Machine) -> ReturnCode {
151 let slider_idx = Machine::get_as::<f64>(vm.get_stack(0)) as usize;
152
153 match self.slider_instances.get(slider_idx) {
154 Some(s) => {
155 vm.set_stack(0, Machine::to_value(s.get()));
156 }
157 None => {
158 log::error!("invalid slider index");
159 return 0;
160 }
161 };
162
163 1
164 }
165
166 pub fn probe_intercept(&mut self, vm: &mut Machine) -> ReturnCode {
167 let value = Machine::get_as::<f64>(vm.get_stack(0));
168 let probe_idx = Machine::get_as::<f64>(vm.get_stack(1)) as usize;
169
170 match self.probe_instances.get_mut(probe_idx) {
171 Some(prod) => {
172 let _ = prod.try_push(value);
173 }
175 None => {
176 log::error!("invalid probe index: {probe_idx}");
177 }
178 }
179
180 1
181 }
182}
183impl SystemPlugin for GuiToolPlugin {
184 fn try_get_main_loop(&mut self) -> Option<Box<dyn FnOnce()>> {
185 #[cfg(not(target_arch = "wasm32"))]
186 {
187 use crate::plot_window::AsyncPlotApp;
188 let app = Box::new(AsyncPlotApp {
189 window: self.window.clone(),
190 });
191 Some(Box::new(move || {
192 let native_options = eframe::NativeOptions {
193 viewport: egui::ViewportBuilder::default()
194 .with_inner_size([400.0, 300.0])
195 .with_min_inner_size([300.0, 220.0])
196 .with_icon(
197 eframe::icon_data::from_png_bytes(
199 &include_bytes!("../assets/mimium_logo_256.png")[..],
200 )
201 .expect("Failed to load icon"),
202 ),
203 ..Default::default()
204 };
205 let _ =
206 eframe::run_native("mimium guitools", native_options, Box::new(|_cc| Ok(app)))
207 .inspect_err(|e| log::error!("{e}"));
208 }))
209 }
210 #[cfg(target_arch = "wasm32")]
211 None
212 }
213 fn gen_interfaces(&self) -> Vec<SysPluginSignature> {
214 let sliderf: SystemPluginMacroType<Self> = Self::make_slider;
215 let make_slider = SysPluginSignature::new_macro(
216 "Slider",
217 sliderf,
218 function!(
219 vec![string_t!(), numeric!(), numeric!(), numeric!()],
220 code!(numeric!())
221 ),
222 );
223
224 let getsliderf: SystemPluginFnType<Self> = Self::get_slider;
225 let get_slider = SysPluginSignature::new(
226 Self::GET_SLIDER,
227 getsliderf,
228 function!(vec![numeric!()], numeric!()),
229 );
230
231 let probe_macrof: SystemPluginMacroType<Self> = Self::make_probe_macro;
233 let probe_macro = SysPluginSignature::new_macro(
234 "Probe",
235 probe_macrof,
236 function!(
237 vec![string_t!()],
238 Type::Code(function!(vec![numeric!()], numeric!())).into_id()
239 ),
240 );
241 let probe_interceptf: SystemPluginFnType<Self> = Self::probe_intercept;
242 let probe_intercept = SysPluginSignature::new(
243 Self::PROBE_INTERCEPT,
244 probe_interceptf,
245 function!(vec![numeric!(), numeric!()], numeric!()),
246 );
247
248 vec![probe_macro, make_slider, get_slider, probe_intercept]
249 }
250}