node_engine/
input.rs

1use anyhow::{anyhow, Result};
2
3#[cfg(feature = "egui")]
4use crate::ui::*;
5use crate::*;
6
7#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
8pub enum InputKey {
9  Idx(u32),
10  Name(String),
11}
12
13impl From<InputId> for InputKey {
14  fn from(id: InputId) -> Self {
15    Self::Idx(id.idx)
16  }
17}
18
19impl From<u32> for InputKey {
20  fn from(idx: u32) -> Self {
21    Self::Idx(idx)
22  }
23}
24
25impl From<String> for InputKey {
26  fn from(name: String) -> Self {
27    Self::Name(name)
28  }
29}
30
31impl From<&String> for InputKey {
32  fn from(name: &String) -> Self {
33    Self::Name(name.clone())
34  }
35}
36
37impl From<&str> for InputKey {
38  fn from(name: &str) -> Self {
39    Self::Name(name.to_string())
40  }
41}
42
43#[derive(Clone, Debug)]
44pub enum Input {
45  Disconnect,
46  Connect(OutputId, Option<DataType>),
47  Value(Value),
48}
49
50impl<T: Into<Value>> From<T> for Input {
51  fn from(v: T) -> Self {
52    Self::Value(v.into())
53  }
54}
55
56impl From<NodeId> for Input {
57  fn from(n: NodeId) -> Self {
58    Self::Connect(OutputId::new(n, 0), None)
59  }
60}
61
62#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
63pub struct InputTyped<T, const N: u32> {
64  value: T,
65  connected: Option<(OutputId, Option<DataType>)>,
66}
67
68impl<T: ValueType, const N: u32> InputTyped<T, N> {
69  pub fn new(value: T) -> Self {
70    Self {
71      value,
72      connected: None,
73    }
74  }
75
76  pub fn is_connected(&self) -> bool {
77    self.connected.is_some()
78  }
79
80  pub fn is_dynamic(&self) -> bool {
81    self.value.is_dynamic()
82  }
83
84  pub fn as_input(&self) -> Input {
85    match &self.connected {
86      Some((id, dt)) => Input::Connect(*id, *dt),
87      None => Input::Value(self.value.to_value()),
88    }
89  }
90
91  pub fn resolve(
92    &self,
93    concrete_type: &mut NodeConcreteType,
94    graph: &NodeGraph,
95    compile: &mut NodeGraphCompile,
96  ) -> Result<CompiledValue> {
97    let mut value = match &self.connected {
98      Some((id, _)) => {
99        let value = compile.resolve_output(graph, *id)?;
100        if self.is_dynamic() {
101          // Collect info about dynamic inputs.
102          concrete_type.add_input_type(value.dt);
103        }
104        value
105      }
106      None => self.value.compile()?,
107    };
108    // Make sure the value is in our type.
109    value.convert(self.value.data_type())?;
110    Ok(value)
111  }
112
113  pub fn compile(
114    &self,
115    graph: &NodeGraph,
116    compile: &mut NodeGraphCompile,
117  ) -> Result<CompiledValue> {
118    let mut value = match &self.connected {
119      Some((id, _)) => compile.resolve_output(graph, *id)?,
120      None => self.value.compile()?,
121    };
122    // Make sure the value is in our type.
123    value.convert(self.value.data_type())?;
124    Ok(value)
125  }
126
127  pub fn set_input(&mut self, input: Input) -> Result<Option<OutputId>> {
128    let old = self.connected.take().map(|(id, _)| id);
129    match input {
130      Input::Disconnect => (),
131      Input::Value(val) => {
132        self.value.set_value(val)?;
133      }
134      Input::Connect(id, dt) => {
135        if let Some(output_dt) = dt {
136          if !self.value.data_type().is_compatible(&output_dt) {
137            return Err(anyhow!("Incompatible output"));
138          }
139        }
140        self.connected = Some((id, dt));
141      }
142    }
143    Ok(old)
144  }
145
146  #[cfg(feature = "egui")]
147  pub fn ui(
148    &mut self,
149    concrete_type: &mut NodeConcreteType,
150    def: &InputDefinition,
151    ui: &mut egui::Ui,
152    id: NodeId,
153    details: bool,
154  ) -> bool {
155    let mut changed = false;
156    ui.horizontal(|ui| {
157      if details {
158        ui.collapsing(&def.name, |ui| {
159          changed = self.value.ui(ui);
160        });
161      } else {
162        match self.connected {
163          Some((output_id, mut dt)) => {
164            if self.is_dynamic() {
165              dt = NodeGraphMeta::get(ui).and_then(|g| g.resolve_output(&output_id));
166              if let Some(dt) = dt {
167                concrete_type.add_input_type(dt);
168              }
169            }
170            let mut socket = NodeSocket::input(id, N, true, def);
171            if let Some(dt) = dt {
172              socket.set_data_type(dt);
173            }
174            ui.add(socket);
175            ui.label(&def.name);
176          }
177          None => {
178            ui.add(NodeSocket::input(id, N, false, def));
179            ui.collapsing(&def.name, |ui| {
180              changed = self.value.ui(ui);
181            });
182          }
183        }
184      }
185    });
186    changed
187  }
188}
189
190impl<T: ValueType + Clone + Default, const N: u32> InputTyped<T, N> {
191  pub fn eval(&self, graph: &NodeGraph, execution: &mut NodeGraphExecution) -> Result<T> {
192    match &self.connected {
193      Some((OutputId { node: id, .. }, _)) => {
194        let mut val = T::default();
195        val.set_value(execution.eval_node(graph, *id)?)?;
196        Ok(val)
197      }
198      None => Ok(self.value.clone()),
199    }
200  }
201}