use anyhow::{anyhow, Result}; use smallvec::smallvec; use crate::graph_types::*; use crate::id_types::*; use crate::SVec; impl Graph { pub fn new() -> Self { Self::default() } pub fn add_node(&mut self, d: NodeDescriptor) -> NodeId { let node_id = self.nodes.insert_with_key(|node_id| { Node { id: node_id, label: d.label, op_name: d.op_name, // These will get filled in later inputs: Vec::default(), outputs: Vec::default(), is_executable: d.is_executable, } }); use InputParamKind::*; let inputs: Vec<(String, InputId)> = d .inputs .into_iter() .map(|(input_name, input)| { let input_id = self.inputs.insert_with_key(|id| match input { InputDescriptor::Vector { default } => InputParam { id, typ: DataType::Vector, value: InputParamValue::Vector(default), metadata: smallvec![], kind: ConnectionOrConstant, node: node_id, }, InputDescriptor::Mesh => InputParam { id, typ: DataType::Mesh, value: InputParamValue::None, metadata: smallvec![], kind: ConnectionOnly, node: node_id, }, InputDescriptor::Selection => InputParam { id, typ: DataType::Selection, value: InputParamValue::Selection { text: "".into(), selection: Some(vec![]), }, metadata: smallvec![], kind: ConnectionOrConstant, node: node_id, }, InputDescriptor::Scalar { default, min, max } => InputParam { id, typ: DataType::Scalar, value: InputParamValue::Scalar(default), metadata: smallvec![InputParamMetadata::MinMaxScalar { min, max }], kind: ConnectionOrConstant, node: node_id, }, InputDescriptor::Enum { values } => InputParam { id, typ: DataType::Enum, value: InputParamValue::Enum { values, selection: None, }, metadata: smallvec![], kind: ConstantOnly, node: node_id, }, InputDescriptor::NewFile => InputParam { id, typ: DataType::NewFile, value: InputParamValue::NewFile { path: None }, metadata: smallvec![], kind: ConstantOnly, node: node_id, }, }); (input_name, input_id) }) .collect(); let outputs: Vec<(String, OutputId)> = d .outputs .into_iter() .map(|(output_name, output)| { let output_id = self.outputs.insert_with_key(|id| OutputParam { node: node_id, id, typ: output.0, }); (output_name, output_id) }) .collect(); self[node_id].inputs = inputs; self[node_id].outputs = outputs; node_id } pub fn remove_node(&mut self, node_id: NodeId) { self.connections .retain(|i, o| !(self.outputs[*o].node == node_id || self.inputs[*i].node == node_id)); let inputs: SVec<_> = self[node_id].input_ids().collect(); for input in inputs { self.inputs.remove(input); } let outputs: SVec<_> = self[node_id].output_ids().collect(); for output in outputs { self.outputs.remove(output); } self.nodes.remove(node_id); } pub fn remove_connection(&mut self, input_id: InputId) -> Option { self.connections.remove(&input_id) } pub fn iter_nodes(&self) -> impl Iterator + '_ { self.nodes.iter().map(|(id, _)| id) } pub fn add_connection(&mut self, output: OutputId, input: InputId) { self.connections.insert(input, output); } pub fn iter_connections(&self) -> impl Iterator + '_ { self.connections.iter().map(|(o, i)| (*o, *i)) } pub fn connection(&self, input: InputId) -> Option { self.connections.get(&input).copied() } pub fn any_param_type(&self, param: AnyParameterId) -> Result { match param { AnyParameterId::Input(input) => self.inputs.get(input).map(|x| x.typ), AnyParameterId::Output(output) => self.outputs.get(output).map(|x| x.typ), } .ok_or_else(|| anyhow!("Invalid parameter id: {:?}", param)) } pub fn get_input(&self, input: InputId) -> &InputParam { &self.inputs[input] } pub fn get_output(&self, output: OutputId) -> &OutputParam { &self.outputs[output] } } impl Node { pub fn inputs<'a>(&'a self, graph: &'a Graph) -> impl Iterator + 'a { self.input_ids().map(|id| graph.get_input(id)) } pub fn outputs<'a>(&'a self, graph: &'a Graph) -> impl Iterator + 'a { self.output_ids().map(|id| graph.get_output(id)) } pub fn input_ids(&self) -> impl Iterator + '_ { self.inputs.iter().map(|(_name, id)| *id) } pub fn output_ids(&self) -> impl Iterator + '_ { self.outputs.iter().map(|(_name, id)| *id) } pub fn get_input(&self, name: &str) -> Result { self.inputs .iter() .find(|(param_name, _id)| param_name == name) .map(|x| x.1) .ok_or_else(|| anyhow!("Node {:?} has no parameter named {}", self.id, name)) } pub fn get_output(&self, name: &str) -> Result { self.outputs .iter() .find(|(param_name, _id)| param_name == name) .map(|x| x.1) .ok_or_else(|| anyhow!("Node {:?} has no parameter named {}", self.id, name)) } /// Can this node be enabled on the UI? I.e. does it output a mesh? pub fn can_be_enabled(&self, graph: &Graph) -> bool { self.outputs(graph) .any(|output| output.typ == DataType::Mesh) } /// Executable nodes are used to produce side effects, like exporting files. pub fn is_executable(&self) -> bool { self.is_executable } }