1use std::collections::VecDeque;
4
5use indexmap::IndexMap;
6
7use crate::node::Node;
8use crate::port::PortKind;
9
10pub type NodeId = String;
13
14pub type NodeIx = usize;
17
18#[derive(Debug, thiserror::Error)]
19pub enum BuildError {
20 #[error("unknown node reference `{from}` -> `{to}`")]
21 UnknownRef { from: NodeId, to: NodeId },
22
23 #[error("node `{node}` has no input port named `{port}`")]
24 UnknownPort { node: NodeId, port: String },
25
26 #[error("port `{node}.{port}` already connected")]
27 DuplicateEdge { node: NodeId, port: String },
28
29 #[error("node `{node}` cannot serve these input kinds: {msg}")]
30 UnsupportedKinds { node: NodeId, msg: String },
31
32 #[error(
33 "type mismatch on `{node}.{port}`: expected one of [{}], source `{src}` produces {got}",
34 accepts.iter().map(|k| k.to_string()).collect::<Vec<_>>().join(", ")
35 )]
36 TypeMismatch {
37 node: NodeId,
38 port: String,
39 src: NodeId,
40 accepts: Vec<PortKind>,
41 got: PortKind,
42 },
43
44 #[error("required port `{node}.{port}` is not connected")]
45 MissingInput { node: NodeId, port: String },
46
47 #[error("cycle detected involving node `{0}`")]
48 Cycle(NodeId),
49
50 #[error("output node `{0}` is not in the graph")]
51 UnknownOutput(NodeId),
52
53 #[error("graph has no output node")]
54 NoOutput,
55
56 #[error(
57 "output node `{node}` produces `{got}`, but the document output must produce `raster` (canvas-padded). Pipe a sprite through `place`, `tiling`, or `stamp` first."
58 )]
59 OutputKindMismatch { node: NodeId, got: PortKind },
60
61 #[error("required pad ({required}) on node `{node}` exceeds limit ({limit})")]
62 PadExceeded {
63 node: NodeId,
64 required: u32,
65 limit: u32,
66 },
67}
68
69#[derive(Debug, Clone, Copy)]
71pub struct Edge {
72 pub src: NodeIx,
73 pub dst: NodeIx,
74 pub dst_port: usize,
75}
76
77pub struct Graph {
80 nodes: IndexMap<NodeId, Box<dyn Node>>,
81 incoming: Vec<Vec<Option<NodeIx>>>,
84 outgoing: Vec<Vec<NodeIx>>,
86 outgoing_unique: Vec<Vec<NodeIx>>,
90 indegree: Vec<usize>,
92 output: NodeIx,
94 topo: Vec<NodeIx>,
96 output_kinds: Vec<PortKind>,
101}
102
103pub const MAX_PAD: u32 = 256;
106
107pub struct GraphBuilder {
110 nodes: IndexMap<NodeId, Box<dyn Node>>,
111 edges: Vec<EdgeSpec>,
113 output: Option<NodeId>,
114}
115
116struct EdgeSpec {
117 src: NodeId,
118 dst: NodeId,
119 dst_port: String,
120}
121
122impl GraphBuilder {
123 pub fn new() -> Self {
124 Self {
125 nodes: IndexMap::new(),
126 edges: Vec::new(),
127 output: None,
128 }
129 }
130
131 pub fn add_node(&mut self, id: impl Into<NodeId>, node: Box<dyn Node>) -> &mut Self {
132 self.nodes.insert(id.into(), node);
133 self
134 }
135
136 pub fn connect(
137 &mut self,
138 src: impl Into<NodeId>,
139 dst: impl Into<NodeId>,
140 dst_port: impl Into<String>,
141 ) -> &mut Self {
142 self.edges.push(EdgeSpec {
143 src: src.into(),
144 dst: dst.into(),
145 dst_port: dst_port.into(),
146 });
147 self
148 }
149
150 pub fn set_output(&mut self, id: impl Into<NodeId>) -> &mut Self {
151 self.output = Some(id.into());
152 self
153 }
154
155 pub fn build(self) -> Result<Graph, BuildError> {
156 let n = self.nodes.len();
157 let mut incoming: Vec<Vec<Option<NodeIx>>> = self
158 .nodes
159 .values()
160 .map(|node| vec![None; node.inputs().len()])
161 .collect();
162 let mut outgoing: Vec<Vec<NodeIx>> = vec![Vec::new(); n];
163
164 let ix_of = |id: &str| -> Option<NodeIx> { self.nodes.get_index_of(id) };
165
166 for edge in &self.edges {
169 let src_ix = ix_of(&edge.src).ok_or_else(|| BuildError::UnknownRef {
170 from: edge.src.clone(),
171 to: edge.dst.clone(),
172 })?;
173 let dst_ix = ix_of(&edge.dst).ok_or_else(|| BuildError::UnknownRef {
174 from: edge.src.clone(),
175 to: edge.dst.clone(),
176 })?;
177
178 let (_, dst_node) = self
179 .nodes
180 .get_index(dst_ix)
181 .expect("dst_ix came from ix_of and is in range");
182 let port_ix = dst_node
183 .inputs()
184 .iter()
185 .position(|p| p.name == edge.dst_port)
186 .ok_or_else(|| BuildError::UnknownPort {
187 node: edge.dst.clone(),
188 port: edge.dst_port.clone(),
189 })?;
190
191 if incoming[dst_ix][port_ix].is_some() {
192 return Err(BuildError::DuplicateEdge {
193 node: edge.dst.clone(),
194 port: edge.dst_port.clone(),
195 });
196 }
197
198 incoming[dst_ix][port_ix] = Some(src_ix);
199 outgoing[src_ix].push(dst_ix);
200 }
201
202 for (ix, (id, node)) in self.nodes.iter().enumerate() {
204 for (port_ix, port) in node.inputs().iter().enumerate() {
205 if !port.optional && incoming[ix][port_ix].is_none() {
206 return Err(BuildError::MissingInput {
207 node: id.clone(),
208 port: port.name.to_string(),
209 });
210 }
211 }
212 }
213
214 let output_id = self.output.ok_or(BuildError::NoOutput)?;
215 let output_ix = ix_of(&output_id).ok_or(BuildError::UnknownOutput(output_id.clone()))?;
216
217 let topo = topo_sort(n, &incoming, &self.nodes, output_ix)?;
218
219 let mut output_kinds: Vec<PortKind> = vec![PortKind::Raster; n];
223 for &ix in &topo {
224 let (id, node) = self.nodes.get_index(ix).expect("ix from topo is in range");
225 let specs = node.inputs();
226 let mut input_kinds: Vec<Option<PortKind>> = Vec::with_capacity(specs.len());
227 for (port_ix, spec) in specs.iter().enumerate() {
228 match incoming[ix][port_ix] {
229 Some(src_ix) => {
230 let src_kind = output_kinds[src_ix];
231 if !spec.accepts_kind(src_kind) {
232 let (src_id, _) = self
233 .nodes
234 .get_index(src_ix)
235 .expect("src_ix from incoming is in range");
236 return Err(BuildError::TypeMismatch {
237 node: id.clone(),
238 port: spec.name.to_string(),
239 src: src_id.clone(),
240 accepts: spec.accepts.to_vec(),
241 got: src_kind,
242 });
243 }
244 input_kinds.push(Some(src_kind));
245 }
246 None => input_kinds.push(None),
247 }
248 }
249 if let Err(msg) = node.validate_kinds(&input_kinds) {
250 return Err(BuildError::UnsupportedKinds {
251 node: id.clone(),
252 msg,
253 });
254 }
255 output_kinds[ix] = node.output(&input_kinds);
256 }
257
258 let output_kind = output_kinds[output_ix];
262 if output_kind != PortKind::Raster {
263 return Err(BuildError::OutputKindMismatch {
264 node: output_id.clone(),
265 got: output_kind,
266 });
267 }
268
269 let mut outgoing_unique = outgoing.clone();
273 for dsts in &mut outgoing_unique {
274 dsts.sort_unstable();
275 dsts.dedup();
276 }
277 let indegree: Vec<usize> = incoming
278 .iter()
279 .map(|ports| {
280 let mut srcs: Vec<NodeIx> = ports.iter().filter_map(|p| *p).collect();
281 srcs.sort_unstable();
282 srcs.dedup();
283 srcs.len()
284 })
285 .collect();
286
287 Ok(Graph {
288 nodes: self.nodes,
289 incoming,
290 outgoing,
291 outgoing_unique,
292 indegree,
293 output: output_ix,
294 topo,
295 output_kinds,
296 })
297 }
298}
299
300impl Default for GraphBuilder {
301 fn default() -> Self {
302 Self::new()
303 }
304}
305
306fn topo_sort(
321 n: usize,
322 incoming: &[Vec<Option<NodeIx>>],
323 nodes: &IndexMap<NodeId, Box<dyn Node>>,
324 output: NodeIx,
325) -> Result<Vec<NodeIx>, BuildError> {
326 let kahn = kahn_order(n, incoming, nodes)?;
327
328 let mut order = Vec::with_capacity(n);
332 let mut seen = vec![false; n];
333 let mut stack: Vec<(NodeIx, usize)> = vec![(output, 0)];
334 seen[output] = true;
335 while let Some((ix, port)) = stack.pop() {
336 match incoming[ix].get(port) {
340 Some(&edge) => {
341 stack.push((ix, port + 1));
342 if let Some(src) = edge {
343 if !seen[src] {
344 seen[src] = true;
345 stack.push((src, 0));
346 }
347 }
348 }
349 None => order.push(ix),
350 }
351 }
352 order.extend(kahn.into_iter().filter(|&ix| !seen[ix]));
355 Ok(order)
356}
357
358fn kahn_order(
359 n: usize,
360 incoming: &[Vec<Option<NodeIx>>],
361 nodes: &IndexMap<NodeId, Box<dyn Node>>,
362) -> Result<Vec<NodeIx>, BuildError> {
363 let mut indegree: Vec<usize> = incoming
365 .iter()
366 .map(|ports| {
367 let mut srcs: Vec<NodeIx> = ports.iter().filter_map(|p| *p).collect();
368 srcs.sort_unstable();
369 srcs.dedup();
370 srcs.len()
371 })
372 .collect();
373
374 let mut rev: Vec<Vec<NodeIx>> = vec![Vec::new(); n];
376 for (dst, ports) in incoming.iter().enumerate() {
377 let mut srcs: Vec<NodeIx> = ports.iter().filter_map(|p| *p).collect();
378 srcs.sort_unstable();
379 srcs.dedup();
380 for src in srcs {
381 rev[src].push(dst);
382 }
383 }
384
385 let mut queue: VecDeque<NodeIx> = (0..n).filter(|&i| indegree[i] == 0).collect();
386 let mut order = Vec::with_capacity(n);
387 while let Some(ix) = queue.pop_front() {
388 order.push(ix);
389 for &dst in &rev[ix] {
390 indegree[dst] -= 1;
391 if indegree[dst] == 0 {
392 queue.push_back(dst);
393 }
394 }
395 }
396
397 if order.len() != n {
398 let bad = (0..n)
402 .find(|&i| indegree[i] != 0)
403 .expect("order.len() != n implies some indegree is non-zero");
404 let (id, _) = nodes.get_index(bad).expect("bad < n is within nodes range");
405 return Err(BuildError::Cycle(id.clone()));
406 }
407 Ok(order)
408}
409
410impl std::fmt::Debug for Graph {
411 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412 let ids: Vec<&str> = self.nodes.keys().map(String::as_str).collect();
413 f.debug_struct("Graph")
414 .field("nodes", &ids)
415 .field("output", &self.node_id(self.output))
416 .field(
417 "topo",
418 &self
419 .topo
420 .iter()
421 .map(|&i| self.node_id(i))
422 .collect::<Vec<_>>(),
423 )
424 .finish()
425 }
426}
427
428impl Graph {
429 pub fn len(&self) -> usize {
431 self.nodes.len()
432 }
433
434 pub fn is_empty(&self) -> bool {
435 self.nodes.is_empty()
436 }
437
438 pub fn output(&self) -> NodeIx {
439 self.output
440 }
441
442 pub fn topo_order(&self) -> &[NodeIx] {
444 &self.topo
445 }
446
447 pub fn asset_inputs(&self) -> std::collections::BTreeSet<String> {
454 self.nodes.values().flat_map(|n| n.asset_inputs()).collect()
455 }
456
457 pub fn node(&self, ix: NodeIx) -> &dyn Node {
458 self.nodes
459 .get_index(ix)
460 .expect("NodeIx is always within self.nodes range")
461 .1
462 .as_ref()
463 }
464
465 pub fn node_id(&self, ix: NodeIx) -> &str {
466 self.nodes
467 .get_index(ix)
468 .expect("NodeIx is always within self.nodes range")
469 .0
470 }
471
472 pub fn index_of(&self, id: &str) -> Option<NodeIx> {
474 self.nodes.get_index_of(id)
475 }
476
477 pub fn upstream(&self, ix: NodeIx) -> impl Iterator<Item = NodeIx> + '_ {
479 let mut srcs: Vec<NodeIx> = self.incoming[ix].iter().filter_map(|p| *p).collect();
480 srcs.sort_unstable();
481 srcs.dedup();
482 srcs.into_iter()
483 }
484
485 pub fn downstream(&self, ix: NodeIx) -> &[NodeIx] {
488 &self.outgoing[ix]
489 }
490
491 pub fn downstream_unique(&self, ix: NodeIx) -> &[NodeIx] {
493 &self.outgoing_unique[ix]
494 }
495
496 pub fn indegree(&self, ix: NodeIx) -> usize {
498 self.indegree[ix]
499 }
500
501 pub fn incoming(&self, ix: NodeIx, port_ix: usize) -> Option<NodeIx> {
503 self.incoming[ix][port_ix]
504 }
505
506 pub fn output_kind(&self, ix: NodeIx) -> PortKind {
509 self.output_kinds[ix]
510 }
511
512 pub fn required_pad(&self) -> Result<u32, BuildError> {
533 Ok(self.compute_pad(0)?.into_iter().max().unwrap_or(0))
534 }
535
536 pub fn influence_pads(&self, assets: &dyn crate::eval::AssetLoader) -> Vec<u32> {
544 let mut influence = vec![0u32; self.len()];
545 for &ix in self.topo.iter().rev() {
546 let brush = self
550 .upstream(ix)
551 .find_map(|src| self.node(src).ink_reach(assets));
552 let ctx = crate::node::InfluenceCtx {
553 downstream: influence[ix],
554 brush,
555 assets,
556 };
557 let up = self.node(ix).influence_pad(&ctx);
558 for src in self.upstream(ix) {
559 influence[src] = influence[src].max(up);
560 }
561 }
562 influence
563 }
564
565 pub fn compute_pad(&self, doc_pad: u32) -> Result<Vec<u32>, BuildError> {
574 let mut required = vec![0u32; self.len()];
575 required[self.output] = doc_pad;
576 for &ix in self.topo.iter().rev() {
577 let down = required[ix];
578 let up = self.node(ix).required_pad(down);
579 if up > MAX_PAD {
580 return Err(BuildError::PadExceeded {
581 node: self.node_id(ix).to_string(),
582 required: up,
583 limit: MAX_PAD,
584 });
585 }
586 for src in self.upstream(ix) {
587 required[src] = required[src].max(up);
588 }
589 }
590 Ok(required)
591 }
592}