1use crate::{Cell, CellDir, Matrix, NodeId, ParamId, SAtom};
26use std::collections::HashMap;
27
28#[derive(Debug, Clone)]
29struct MatrixChainLink {
30 cell: Cell,
31 dir: CellDir,
32 params: Vec<(ParamId, SAtom)>,
33}
34
35#[derive(Debug, Clone)]
57pub struct MatrixCellChain {
58 chain: Vec<MatrixChainLink>,
59 error: Option<ChainError>,
60 dir: CellDir,
61 param_idx: usize,
62}
63
64#[derive(Debug, Clone)]
66pub enum ChainError {
67 UnknownNodeId(String),
68 UnknownOutput(NodeId, String),
69 UnknownInput(NodeId, String),
70}
71
72impl MatrixCellChain {
73 pub fn new(dir: CellDir) -> Self {
77 Self { dir, chain: vec![], error: None, param_idx: 0 }
78 }
79
80 fn output_dir(&self) -> CellDir {
81 if self.dir.is_output() {
82 self.dir
83 } else {
84 self.dir.flip()
85 }
86 }
87
88 fn input_dir(&self) -> CellDir {
89 if self.dir.is_input() {
90 self.dir
91 } else {
92 self.dir.flip()
93 }
94 }
95
96 pub fn params_for_idx(&mut self, idx: usize) -> &mut Self {
98 self.param_idx = idx;
99 if self.param_idx >= self.chain.len() {
100 self.param_idx = self.chain.len();
101 }
102
103 self
104 }
105
106 pub fn set_denorm(&mut self, param: &str, denorm: f32) -> &mut Self {
112 let link = self.chain.get_mut(self.param_idx).expect("Correct parameter idx");
113
114 if let Some(pid) = link.cell.node_id().inp_param(param) {
115 link.params.push((pid, SAtom::param(pid.norm(denorm as f32))));
116 } else {
117 self.error = Some(ChainError::UnknownInput(link.cell.node_id(), param.to_string()));
118 }
119
120 self
121 }
122
123 pub fn set_norm(&mut self, param: &str, norm: f32) -> &mut Self {
129 let link = self.chain.get_mut(self.param_idx).expect("Correct parameter idx");
130
131 if let Some(pid) = link.cell.node_id().inp_param(param) {
132 link.params.push((pid, SAtom::param(norm as f32)));
133 } else {
134 self.error = Some(ChainError::UnknownInput(link.cell.node_id(), param.to_string()));
135 }
136
137 self
138 }
139
140 pub fn set_atom(&mut self, param: &str, at: SAtom) -> &mut Self {
146 let link = self.chain.get_mut(self.param_idx).expect("Correct parameter idx");
147
148 if let Some(pid) = link.cell.node_id().inp_param(param) {
149 link.params.push((pid, at));
150 } else {
151 self.error = Some(ChainError::UnknownInput(link.cell.node_id(), param.to_string()));
152 }
153
154 self
155 }
156
157 pub fn spawn_cell_from_node_id_name(&mut self, node_id_name: &str) -> Option<Cell> {
159 let node_id = NodeId::from_str(node_id_name);
160 if node_id == NodeId::Nop && node_id_name != "nop" {
161 return None;
162 }
163
164 Some(Cell::empty(node_id))
165 }
166
167 pub fn add_link(&mut self, cell: Cell) {
171 self.chain.push(MatrixChainLink { dir: self.dir, cell, params: vec![] });
172 self.param_idx = self.chain.len() - 1;
173 }
174
175 pub fn node(&mut self, node_id: &str) -> &mut Self {
178 if let Some(cell) = self.spawn_cell_from_node_id_name(node_id) {
179 self.add_link(cell);
180 } else {
181 self.error = Some(ChainError::UnknownNodeId(node_id.to_string()));
182 }
183
184 self
185 }
186
187 pub fn node_out(&mut self, node_id: &str, out: &str) -> &mut Self {
189 if let Some(mut cell) = self.spawn_cell_from_node_id_name(node_id) {
190 if let Err(()) = cell.set_output_by_name(out, self.output_dir()) {
191 self.error = Some(ChainError::UnknownOutput(cell.node_id(), out.to_string()));
192 }
193
194 self.add_link(cell);
195 } else {
196 self.error = Some(ChainError::UnknownNodeId(node_id.to_string()));
197 }
198
199 self
200 }
201
202 pub fn node_inp(&mut self, node_id: &str, inp: &str) -> &mut Self {
204 if let Some(mut cell) = self.spawn_cell_from_node_id_name(node_id) {
205 if let Err(()) = cell.set_input_by_name(inp, self.input_dir()) {
206 self.error = Some(ChainError::UnknownInput(cell.node_id(), inp.to_string()));
207 }
208
209 self.add_link(cell);
210 } else {
211 self.error = Some(ChainError::UnknownNodeId(node_id.to_string()));
212 }
213
214 self
215 }
216
217 pub fn node_io(&mut self, node_id: &str, inp: &str, out: &str) -> &mut Self {
219 if let Some(mut cell) = self.spawn_cell_from_node_id_name(node_id) {
220 if let Err(()) = cell.set_input_by_name(inp, self.input_dir()) {
221 self.error = Some(ChainError::UnknownInput(cell.node_id(), inp.to_string()));
222 }
223
224 if let Err(()) = cell.set_output_by_name(out, self.output_dir()) {
225 self.error = Some(ChainError::UnknownOutput(cell.node_id(), out.to_string()));
226 }
227
228 self.add_link(cell);
229 } else {
230 self.error = Some(ChainError::UnknownNodeId(node_id.to_string()));
231 }
232
233 self
234 }
235
236 pub fn place(
241 &mut self,
242 matrix: &mut Matrix,
243 at_x: usize,
244 at_y: usize,
245 ) -> Result<(), ChainError> {
246 if let Some(err) = self.error.take() {
247 return Err(err);
248 }
249
250 let mut last_unused = HashMap::new();
251
252 let mut pos = (at_x, at_y);
253
254 for link in self.chain.iter() {
255 let (x, y) = pos;
256
257 let mut cell = link.cell;
258
259 let node_id = cell.node_id();
260 let node_name = node_id.name();
261
262 let node_id = if let Some(i) = last_unused.get(node_name).cloned() {
263 last_unused.insert(node_name.to_string(), i + 1);
264 node_id.to_instance(i + 1)
265 } else {
266 let node_id = matrix.get_unused_instance_node_id(node_id);
267 last_unused.insert(node_name.to_string(), node_id.instance());
268 node_id
269 };
270
271 cell.set_node_id_keep_ios(node_id);
272
273 matrix.place(x, y, cell);
274
275 let offs = link.dir.as_offs(pos.0);
276 pos.0 = (pos.0 as i32 + offs.0) as usize;
277 pos.1 = (pos.1 as i32 + offs.1) as usize;
278 }
279
280 for link in self.chain.iter() {
281 for (pid, at) in link.params.iter() {
282 matrix.set_param(*pid, at.clone());
283 }
284 }
285
286 Ok(())
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn check_matrix_chain_builder_1() {
296 use crate::nodes::new_node_engine;
297
298 let (node_conf, _node_exec) = new_node_engine();
299 let mut matrix = Matrix::new(node_conf, 7, 7);
300
301 let mut chain = MatrixCellChain::new(CellDir::B);
302
303 chain
304 .node_out("sin", "sig")
305 .set_denorm("freq", 220.0)
306 .node_io("amp", "inp", "sig")
307 .set_denorm("att", 0.5)
308 .node_inp("out", "ch1");
309
310 chain.params_for_idx(0).set_atom("det", SAtom::param(0.1));
311
312 chain.place(&mut matrix, 2, 2).expect("no error in this case");
313
314 matrix.sync().expect("Sync ok");
315
316 let cell_sin = matrix.get(2, 2).unwrap();
317 assert_eq!(cell_sin.node_id(), NodeId::Sin(0));
318
319 let cell_amp = matrix.get(2, 3).unwrap();
320 assert_eq!(cell_amp.node_id(), NodeId::Amp(0));
321
322 let cell_out = matrix.get(2, 4).unwrap();
323 assert_eq!(cell_out.node_id(), NodeId::Out(0));
324
325 assert_eq!(
326 format!("{:?}", matrix.get_param(&NodeId::Sin(0).inp_param("freq").unwrap()).unwrap()),
327 "Param(-0.1)"
328 );
329 assert_eq!(
330 format!("{:?}", matrix.get_param(&NodeId::Sin(0).inp_param("det").unwrap()).unwrap()),
331 "Param(0.1)"
332 );
333 assert_eq!(
334 format!("{:?}", matrix.get_param(&NodeId::Amp(0).inp_param("att").unwrap()).unwrap()),
335 "Param(0.5)"
336 );
337 }
338}