PC Music Generator - a Virtual Modular Synthesizer
1use eframe::{
2 egui::Ui,
3 epaint::{
4 vec2,
5 Rect,
6 },
7};
8
9use egui::{
10 Context,
11 InnerResponse,
12 Rounding,
13 Stroke,
14};
15use emath::Pos2;
16use itertools::Itertools;
17use quadtree_rs::{
18 area::AreaBuilder,
19 point::Point,
20 Quadtree,
21};
22use slotmap::SecondaryMap;
23use wmidi::MidiMessage;
24
25use crate::{
26 devices::description::DeviceKind,
27 graph::{
28 modules::ModuleResponse,
29 Connector,
30 CtlGraph,
31 Graph,
32 InputId,
33 ModuleId,
34 OutputId,
35 },
36 widgets::connector::draw_catenary,
37 STQueue,
38};
39
40use self::sizing::*;
41
42pub mod sizing;
43
44#[derive(Clone, Copy)]
45enum ConnAttempt {
46 None,
47 In(InputId),
48 Out(OutputId),
49}
50
51pub struct Stack {
52 pub graph: Graph,
53 end: Option<InputId>,
54 pub events: STQueue<StackResponse>,
55 attempting_connection: ConnAttempt,
56 qt: Quadtree<u8, ModuleId>,
57}
58
59impl Stack {
60 pub fn new(events: STQueue<StackResponse>) -> Self {
61 Self {
62 graph: Default::default(),
63 end: None,
64 events,
65 attempting_connection: ConnAttempt::None,
66 qt: Quadtree::new(3),
67 }
68 }
69
70 pub fn with_module(&mut self, id: ModuleId) -> Option<ModuleId> {
71 let sz = self.graph.modules[id].size;
72
73 let mut ab = AreaBuilder::default();
74 ab.dimensions(sz.size_in_units());
75
76 let a = (0..self.qt.width())
77 .cartesian_product(0..self.qt.height())
78 .map(|(x, y)| {
79 let (x, y) = (x as _, y as _);
80 ab.anchor(Point { x, y });
81 ab.build().unwrap()
82 })
83 .filter(|c| self.qt.query(*c).count() == 0)
84 .min_by(|a, b| {
85 let Point { x: ax, y: ay } = a.anchor();
86 let Point { x: bx, y: by } = b.anchor();
87 let (ax, ay, bx, by) = (ax as f32, ay as f32, bx as f32, by as f32);
88 (ax.powi(2) + ay.powi(2))
89 .sqrt()
90 .total_cmp(&(bx.powi(2) + by.powi(2)).sqrt())
91 });
92
93 if let Some(a) = a {
94 let devs = &self.graph.devices;
95
96 for (did, kind) in devs {
97 if matches!(kind, DeviceKind::Output) {
98 self.end = self
99 .graph
100 .dev_ins
101 .get(did)
102 .and_then(|ins| ins.first())
103 .copied();
104 }
105 }
106 // TODO: Handle insertion of multiple outputs
107
108 self.qt.insert(a, id);
109
110 None
111 } else {
112 Some(id)
113 }
114 }
115
116 pub fn show(&mut self, ctx: &Context, ui: &mut Ui) {
117 let rect = ui.available_rect_before_wrap();
118 let top = rect.left_top();
119 let rects: SecondaryMap<_, _> = self
120 .qt
121 .iter()
122 .map(|e| {
123 let a = e.area();
124 let Point { x, y } = a.anchor();
125 let tl = top + vec2(x as f32 * U1_WIDTH, y as f32 * U1_HEIGHT);
126 let sz = vec2(a.width() as f32 * U1_WIDTH, a.height() as f32 * U1_HEIGHT);
127 (*e.value_ref(), Rect::from_min_size(tl, sz))
128 })
129 .collect();
130
131 let mut trigger_rebuild = false;
132 let mut control_change = None;
133
134 for (mid, r) in &rects {
135 let p = ui.painter();
136 let theme = self.graph[mid].theme;
137 p.rect(
138 *r,
139 Rounding::ZERO,
140 theme.background_color,
141 Stroke {
142 width: 2.0,
143 color: theme.background_accent_color,
144 },
145 );
146
147 ui.put(*r, |ui: &mut Ui| {
148 let InnerResponse { inner, response } = self.graph[mid].show(ui);
149
150 match (self.attempting_connection, inner) {
151 (_, ModuleResponse::None) => {}
152 (ConnAttempt::None, ModuleResponse::AttemptConnection(Connector::Out(id))) => {
153 self.attempting_connection = ConnAttempt::Out(id);
154 }
155 (ConnAttempt::None, ModuleResponse::AttemptConnection(Connector::In(id))) => {
156 self.attempting_connection = ConnAttempt::In(id);
157 }
158 (ConnAttempt::In(_), ModuleResponse::AttemptConnection(Connector::In(_))) => {}
159 (
160 ConnAttempt::In(inid),
161 ModuleResponse::AttemptConnection(Connector::Out(outid)),
162 ) => {
163 self.graph.cables.insert(inid, outid);
164 self.attempting_connection = ConnAttempt::None;
165 trigger_rebuild = true;
166 }
167 (
168 ConnAttempt::Out(outid),
169 ModuleResponse::AttemptConnection(Connector::In(inid)),
170 ) => {
171 self.graph.cables.insert(inid, outid);
172 self.attempting_connection = ConnAttempt::None;
173 trigger_rebuild = true;
174 }
175 (ConnAttempt::Out(_), ModuleResponse::AttemptConnection(Connector::Out(_))) => {
176 }
177 (_, ModuleResponse::Changed(conn, v)) => control_change = Some((conn, v)),
178 (
179 ConnAttempt::In(_) | ConnAttempt::Out(_),
180 ModuleResponse::AttemptDisconnect(_),
181 ) => self.attempting_connection = ConnAttempt::None,
182 (_, ModuleResponse::AttemptDisconnect(c)) => {
183 match c {
184 Connector::In(i) => {
185 self.graph.cables.remove(i);
186 }
187 Connector::Out(o) => self.graph.cables.retain(|_, co| *co != o),
188 };
189 trigger_rebuild = true;
190 }
191 }
192
193 response
194 });
195 }
196
197 self.draw_wires(&rects, ctx, ui);
198
199 if trigger_rebuild {
200 if let Some(end) = &self.end {
201 self.events
202 .put(StackResponse::Rebuild(self.graph.walk_to(*end)));
203
204 for (mid, _) in rects {
205 let module = &self.graph[mid];
206 for (knob, &conn) in &module.values {
207 let value = module.visuals[knob].value();
208 self.events.put(StackResponse::ControlChange(conn, value));
209 }
210 }
211 }
212 } else if let Some((c, v)) = control_change {
213 self.events.put(StackResponse::ControlChange(c, v));
214 }
215 }
216
217 fn draw_wires(&mut self, rects: &SecondaryMap<ModuleId, Rect>, ctx: &Context, ui: &mut Ui) {
218 match self.attempting_connection {
219 ConnAttempt::None => {}
220 ConnAttempt::Out(start) => {
221 let start = self.get_output_pos(start, rects);
222 if let Some(end) = ctx.pointer_latest_pos() {
223 draw_catenary(start, end, ui.painter());
224 }
225 }
226 ConnAttempt::In(start) => {
227 let start = self.get_input_pos(start, rects);
228 if let Some(end) = ctx.pointer_latest_pos() {
229 draw_catenary(start, end, ui.painter());
230 }
231 }
232 }
233
234 for (inp, &out) in &self.graph.cables {
235 let start = self.get_input_pos(inp, rects);
236
237 let end = self.get_output_pos(out, rects);
238
239 draw_catenary(start, end, ui.painter());
240 }
241 }
242
243 fn get_output_pos(&self, out: OutputId, rects: &SecondaryMap<ModuleId, Rect>) -> Pos2 {
244 let (mid, module, vid) = self
245 .graph
246 .modules
247 .iter()
248 .find_map(|(mid, m)| m.outs.get(out).map(|o| (mid, m, *o)))
249 .unwrap();
250 let mod_tl = rects[mid].min;
251 let widget = &module.visuals[vid];
252
253 mod_tl + (widget.pos().to_vec2() + widget.size() / 2.0)
254 }
255
256 fn get_input_pos(&self, inp: InputId, rects: &SecondaryMap<ModuleId, Rect>) -> Pos2 {
257 let (mid, module, vid) = self
258 .graph
259 .modules
260 .iter()
261 .find_map(|(mid, m)| m.ins.get(inp).map(|o| (mid, m, *o)))
262 .unwrap();
263 let mod_tl = rects[mid].min;
264 let widget = &module.visuals[vid];
265
266 mod_tl + (widget.pos().to_vec2() + widget.size() / 2.0)
267 }
268}
269
270pub enum StackResponse {
271 Rebuild(CtlGraph),
272 ControlChange(Connector, f32),
273 MidiChange(STQueue<(u64, MidiMessage<'static>)>),
274}
275
276impl std::fmt::Debug for StackResponse {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 match self {
279 Self::Rebuild(arg0) => f.debug_tuple("Rebuild").field(arg0).finish(),
280 Self::ControlChange(arg0, arg1) => f
281 .debug_tuple("ControlChange")
282 .field(arg0)
283 .field(arg1)
284 .finish(),
285 Self::MidiChange(_) => f.debug_tuple("MidiChange").finish(),
286 }
287 }
288}