diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-12-29 15:20:47 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-12-29 15:20:47 +0000 |
| commit | e14ffe29676b8450e807a40c8571243760476730 (patch) | |
| tree | f40e40fe0970822ad2a8556ade12dd16016c6f25 /src | |
| parent | add split node (diff) | |
| download | nodetoy-e14ffe29676b8450e807a40c8571243760476730.tar.gz nodetoy-e14ffe29676b8450e807a40c8571243760476730.zip | |
Vector combine
Diffstat (limited to 'src')
| -rw-r--r-- | src/library.rs | 139 | ||||
| -rw-r--r-- | src/main.rs | 140 |
2 files changed, 166 insertions, 113 deletions
diff --git a/src/library.rs b/src/library.rs index 08d75b8..4c3a8c1 100644 --- a/src/library.rs +++ b/src/library.rs @@ -32,12 +32,13 @@ const GEN_U_TYPES: [Type; 4] = [ Vector(UInt, D3), Vector(UInt, D4), ]; -const GEN_B_TYPES: [Type; 4] = [ +const _GEN_B_TYPES: [Type; 4] = [ Scalar(Bool), Vector(Bool, D2), Vector(Bool, D3), Vector(Bool, D4), ]; +const SCALAR_TYPES: [ScalarType; 5] = [Float(Single), Float(Double), Int, UInt, Bool]; const COMPONENT_LABELS: [&str; 4] = ["x", "y", "z", "w"]; pub trait NodeIndex: Sized + fmt::Display { @@ -86,25 +87,22 @@ trait FixedNode: fmt::Debug { } impl<T: FixedNode> ConcreteNode for T { + fn max_inputs(&self) -> usize { + self.all_signatures().map(|s| s.inputs.len()).max().unwrap() + } + // set of possible input type combinations given current connections - fn signatures_matching<I: Iterator<Item = Option<Type>>>( - &self, - connected: &mut I, - ) -> Vec<TypeSignature> { + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature> { self.all_signatures() .filter(|sig| sig.matches_inputs(connected)) .collect() } - fn signature<I: Iterator<Item = Option<Type>>>(&self, connected: &mut I) -> TypeSignature { - let signatures: Vec<_> = self.all_signatures().collect(); - let max_inputs = signatures.iter().map(|s| s.inputs.len()).max().unwrap(); - let connected: Vec<_> = connected.take(max_inputs).collect(); - - signatures + fn signature(&self, connected: &[Option<Type>]) -> TypeSignature { + self.all_signatures() .into_iter() - .find(|sig| sig.matches_inputs(&mut connected.iter().cloned())) - .expect("have to have fallback") + .find(|sig| sig.matches_inputs(connected)) + .unwrap_or_else(|| self.all_signatures().next().expect("need one signature")) } fn compile( @@ -143,11 +141,11 @@ impl<T: OutputOnlyNode> ConcreteNode for T { 1 } - fn signatures_matching(&self, connected: &mut I) -> Vec<TypeSignature> { + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature> { vec![self.signature(connected)] } - fn signature(&self, connected: &mut I) -> TypeSignature { + fn signature(&self, connected: &[Option<Type>]) -> TypeSignature { TypeSignature::new([], [self.output_type()]) } @@ -187,8 +185,8 @@ impl FixedNode for BinArithmetic { .flat_map(|t| { [ TypeSignature::new([t, t], [t]), - TypeSignature::new([t, t.scalar()], [t]), - TypeSignature::new([t.scalar(), t], [t]), + TypeSignature::new([t, t.scalar().into()], [t]), + TypeSignature::new([t.scalar().into(), t], [t]), ] }) .dedup(), @@ -369,7 +367,7 @@ impl FixedNode for Mod { .flatten() .flat_map(|t| { [ - TypeSignature::new([t, t.scalar()], [t]), + TypeSignature::new([t, t.scalar().into()], [t]), TypeSignature::new([t, t], [t]), ] }) @@ -415,7 +413,7 @@ impl FixedNode for MinMax { .flatten() .flat_map(|t| { [ - TypeSignature::new([t, t.scalar()], [t]), + TypeSignature::new([t, t.scalar().into()], [t]), TypeSignature::new([t, t], [t]), ] }) @@ -439,7 +437,7 @@ impl FixedNode for Clamp { .flatten() .flat_map(|t| { [ - TypeSignature::new([t, t.scalar(), t.scalar()], [t]), + TypeSignature::new([t, t.scalar().into(), t.scalar().into()], [t]), TypeSignature::new([t, t, t], [t]), ] }) @@ -469,7 +467,7 @@ impl FixedNode for Mix { }; [ - TypeSignature::new([t, t, t.scalar()], [t]), + TypeSignature::new([t, t, t.scalar().into()], [t]), TypeSignature::new([t, t, t], [t]), TypeSignature::new([t, t, b], [t]), ] @@ -494,7 +492,7 @@ impl FixedNode for Step { .flatten() .flat_map(|t| { [ - TypeSignature::new([t.scalar(), t], [t]), + TypeSignature::new([t.scalar().into(), t], [t]), TypeSignature::new([t, t], [t]), ] }) @@ -518,7 +516,7 @@ impl FixedNode for Smoothstep { .flatten() .flat_map(|t| { [ - TypeSignature::new([t.scalar(), t.scalar(), t], [t]), + TypeSignature::new([t.scalar().into(), t.scalar().into(), t], [t]), TypeSignature::new([t, t, t], [t]), ] }) @@ -540,7 +538,7 @@ impl FixedNode for Length { [GEN_F_TYPES, GEN_D_TYPES] .into_iter() .flatten() - .map(|t| TypeSignature::new([t], [t.scalar()])), + .map(|t| TypeSignature::new([t], [t.scalar().into()])), ) } } @@ -561,7 +559,7 @@ impl FixedNode for VecVecToScalar { [GEN_F_TYPES, GEN_D_TYPES] .into_iter() .flatten() - .map(|t| TypeSignature::new([t, t], [t.scalar()])), + .map(|t| TypeSignature::new([t, t], [t.scalar().into()])), ) } } @@ -903,14 +901,15 @@ impl<T> ConcreteNode for Constant<T> where Constant<T>: ValueEditor, { - fn signatures_matching<I: Iterator<Item = Option<Type>>>( - &self, - connected: &mut I, - ) -> Vec<TypeSignature> { + fn max_inputs(&self) -> usize { + 0 + } + + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature> { vec![self.signature(connected)] } - fn signature<I: Iterator<Item = Option<Type>>>(&self, _connected: &mut I) -> TypeSignature { + fn signature(&self, _connected: &[Option<Type>]) -> TypeSignature { TypeSignature::new([], [Self::TYPE]) } @@ -1020,14 +1019,15 @@ pub enum Input { } impl ConcreteNode for Input { - fn signatures_matching<I: Iterator<Item = Option<Type>>>( - &self, - connected: &mut I, - ) -> Vec<TypeSignature> { + fn max_inputs(&self) -> usize { + 0 + } + + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature> { vec![self.signature(connected)] } - fn signature<I: Iterator<Item = Option<Type>>>(&self, _connected: &mut I) -> TypeSignature { + fn signature(&self, _connected: &[Option<Type>]) -> TypeSignature { TypeSignature::new( [], [match self { @@ -1079,23 +1079,13 @@ pub struct SplitVector; impl FixedNode for SplitVector { fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { - Box::new( + Box::new(SCALAR_TYPES.into_iter().flat_map(|s| { [ - GEN_F_TYPES, - GEN_D_TYPES, - GEN_I_TYPES, - GEN_U_TYPES, - GEN_B_TYPES, + TypeSignature::new([Vector(s, D2)], [Scalar(s); 2]), + TypeSignature::new([Vector(s, D3)], [Scalar(s); 3]), + TypeSignature::new([Vector(s, D4)], [Scalar(s); 4]), ] - .into_iter() - .flatten() - .filter_map(|t| match t { - Vector(s, D2) => Some(TypeSignature::new([t], [Scalar(s); 2])), - Vector(s, D3) => Some(TypeSignature::new([t], [Scalar(s); 3])), - Vector(s, D4) => Some(TypeSignature::new([t], [Scalar(s); 4])), - _ => None, - }), - ) + })) } fn compile( @@ -1122,17 +1112,63 @@ impl fmt::Display for SplitVector { } } +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct CombineVector; + +impl FixedNode for CombineVector { + fn all_signatures(&self) -> Box<dyn Iterator<Item = TypeSignature>> { + Box::new(SCALAR_TYPES.into_iter().flat_map(|s| { + [ + TypeSignature::new([Scalar(s)], [Scalar(s)]), + TypeSignature::new([Vector(s, D2)], [Vector(s, D2)]), + TypeSignature::new([Vector(s, D3)], [Vector(s, D3)]), + TypeSignature::new([Vector(s, D4)], [Vector(s, D4)]), + TypeSignature::new([Scalar(s); 2], [Vector(s, D2)]), + TypeSignature::new([Scalar(s), Vector(s, D2)], [Vector(s, D3)]), + TypeSignature::new([Vector(s, D2), Scalar(s)], [Vector(s, D3)]), + TypeSignature::new([Scalar(s), Vector(s, D3)], [Vector(s, D4)]), + TypeSignature::new([Vector(s, D3), Scalar(s)], [Vector(s, D4)]), + TypeSignature::new([Scalar(s); 3], [Vector(s, D3)]), + TypeSignature::new([Scalar(s), Scalar(s), Vector(s, D2)], [Vector(s, D4)]), + TypeSignature::new([Scalar(s), Vector(s, D2), Scalar(s)], [Vector(s, D4)]), + TypeSignature::new([Vector(s, D2), Scalar(s), Scalar(s)], [Vector(s, D4)]), + TypeSignature::new([Vector(s, D2), Vector(s, D2)], [Vector(s, D4)]), + TypeSignature::new([Scalar(s); 4], [Vector(s, D4)]), + ] + })) + } + + fn compile( + &self, + signature: TypeSignature, + inputs: Vec<String>, + outputs: Vec<String>, + f: &mut dyn fmt::Write, + ) -> fmt::Result { + let out_typ = &signature.outputs[0]; + let out_name = &outputs[0]; + let params = inputs.iter().join(", "); + writeln!(f, "{out_typ} {out_name} = {out_typ}({params});") + } +} +impl fmt::Display for CombineVector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Combine into Vector") + } +} + #[enum_dispatch(ConcreteNode)] #[derive(Copy, Clone, PartialEq, Serialize, Deserialize)] pub enum Conversion { SplitVector(SplitVector), + CombineVector(CombineVector), } impl NodeIndex for Conversion { const TITLE: &'static str = "Conversion"; fn all() -> impl Iterator<Item = Self> { - [Self::from(SplitVector)].into_iter() + [Self::from(SplitVector), Self::from(CombineVector)].into_iter() } } @@ -1140,6 +1176,7 @@ impl fmt::Display for Conversion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::SplitVector(x) => fmt::Display::fmt(x, f), + Self::CombineVector(x) => fmt::Display::fmt(x, f), } } } diff --git a/src/main.rs b/src/main.rs index d8fbe84..e94bcdb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,13 +55,38 @@ enum Type { Matrix(FloatPrecision, Dimension, Dimension), } +impl From<ScalarType> for Type { + fn from(val: ScalarType) -> Self { + Self::Scalar(val) + } +} + impl Type { - fn scalar(&self) -> Type { + fn scalar(&self) -> ScalarType { match self { - Type::Scalar(_) => *self, - Type::Vector(t, _) => Type::Scalar(*t), - Type::Matrix(p, _, _) => Type::Scalar(ScalarType::Float(*p)), + Type::Scalar(s) => *s, + Type::Vector(s, _) => *s, + Type::Matrix(p, _, _) => ScalarType::Float(*p), + } + } +} + +impl Into<PinInfo> for Type { + fn into(self) -> PinInfo { + match self.scalar() { + ScalarType::Float(FloatPrecision::Single) => PinInfo::circle(), + ScalarType::Float(FloatPrecision::Double) => PinInfo::triangle(), + ScalarType::Int => PinInfo::circle(), + ScalarType::UInt => PinInfo::triangle(), + ScalarType::Bool => PinInfo::square(), } + .with_fill(match self { + Type::Scalar(_) => Color32::from_rgb(0xb0, 0x00, 0x00), //red + Type::Vector(_, Dimension::D2) => Color32::from_rgb(0x00, 0xb0, 0x00), //green + Type::Vector(_, Dimension::D3) => Color32::from_rgb(0x00, 0x00, 0xb0), //blue + Type::Vector(_, Dimension::D4) => Color32::from_rgb(0x00, 0xb0, 0xb0), //cyan + Type::Matrix(_, _, _) => Color32::from_rgb(0xb0, 0xb0, 0x00), //yellow + }) } } @@ -123,13 +148,12 @@ impl TypeSignature { #[enum_dispatch] trait ConcreteNode { + fn max_inputs(&self) -> usize; + // set of possible input type combinations given current connections - fn signatures_matching<I: Iterator<Item = Option<Type>>>( - &self, - connected: &mut I, - ) -> Vec<TypeSignature>; + fn signatures_matching(&self, connected: &[Option<Type>]) -> Vec<TypeSignature>; - fn signature<I: Iterator<Item = Option<Type>>>(&self, connected: &mut I) -> TypeSignature; + fn signature(&self, connected: &[Option<Type>]) -> TypeSignature; fn compile( &self, @@ -145,28 +169,35 @@ trait ConcreteNode { } impl TypeSignature { - fn matches_inputs<I: Iterator<Item = Option<Type>>>(&self, connected: &mut I) -> bool { - self.inputs + fn matches_inputs(&self, connected: &[Option<Type>]) -> bool { + let have_inputs = connected .iter() - .zip(connected) - .all(|(expected, connected_input)| { - if let Some(input) = connected_input { - input == *expected - } else { - true - } - }) + .enumerate() + .filter(|(_, t)| matches!(t, Some(_))) + .map(|(i, _)| i + 1) + .max() + .unwrap_or(0); + + if have_inputs > self.inputs.len() { + false + } else { + connected + .iter() + .zip(self.inputs.iter()) + .all(|(connected_input, expected)| { + if let Some(input) = connected_input { + *input == *expected + } else { + true + } + }) + } } } mod library; mod preview; -const STRING_COLOR: Color32 = Color32::from_rgb(0x00, 0xb0, 0x00); -const NUMBER_COLOR: Color32 = Color32::from_rgb(0xb0, 0x00, 0x00); -const IMAGE_COLOR: Color32 = Color32::from_rgb(0xb0, 0x00, 0xb0); -const UNTYPED_COLOR: Color32 = Color32::from_rgb(0xb0, 0xb0, 0xb0); - #[enum_dispatch(ConcreteNode)] #[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)] enum DemoNode { @@ -177,18 +208,16 @@ enum DemoNode { Output(library::Output), } impl DemoNode { - fn get_input_types( - &self, - node: NodeId, - snarl: &Snarl<DemoNode>, - ) -> impl Iterator<Item = Option<Type>> { - (0..).map( - move |input| match &*snarl.in_pin(InPinId { node, input }).remotes { - [] => None, - [out_pin] => Some(DemoViewer::get_out_type(*out_pin, snarl)), - _ => unreachable!("cannot connect to multiple inputs"), - }, - ) + fn get_input_types(&self, node: NodeId, snarl: &Snarl<DemoNode>) -> Vec<Option<Type>> { + (0..snarl[node].max_inputs()) + .map( + move |input| match &*snarl.in_pin(InPinId { node, input }).remotes { + [] => None, + [out_pin] => Some(DemoViewer::get_out_type(*out_pin, snarl)), + _ => unreachable!("cannot connect to multiple inputs"), + }, + ) + .collect() } fn compile_output(pin: OutPinId) -> String { @@ -203,6 +232,10 @@ impl DemoNode { snarl: &Snarl<DemoNode>, f: &mut dyn fmt::Write, ) -> fmt::Result { + let inp = self.get_input_types(node, snarl); + let sig = self.signature(&inp); + info!("compile {self} input types {inp:?} -> sig {sig:?}"); + let signature = self.get_node_signature(node, snarl); let inputs: Result<Vec<String>, fmt::Error> = (0..signature.inputs.len()) @@ -223,8 +256,7 @@ impl DemoNode { } pub fn get_node_signature(&self, node: NodeId, snarl: &Snarl<DemoNode>) -> TypeSignature { - let mut connected = self.get_input_types(node, snarl); - self.signature(&mut connected) + self.signature(&self.get_input_types(node, snarl)) } } @@ -272,7 +304,7 @@ impl DemoViewer { fn get_in_type(pin: InPinId, snarl: &Snarl<DemoNode>) -> Option<Type> { let node = &snarl[pin.node]; let sig = &node.get_node_signature(pin.node, snarl); - Some(sig.inputs[pin.input]) + sig.inputs.get(pin.input).copied() } fn get_out_type(pin: OutPinId, snarl: &Snarl<DemoNode>) -> Type { @@ -316,13 +348,10 @@ impl SnarlViewer<DemoNode> for DemoViewer { #[inline] fn connect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<DemoNode>) { let to_node = &snarl[to.id.node]; - let curr_inputs = to_node.get_node_signature(to.id.node, snarl).outputs.len(); - let mut inputs: Vec<_> = to_node - .get_input_types(to.id.node, snarl) - .take(curr_inputs) - .collect(); + + let mut inputs = to_node.get_input_types(to.id.node, snarl); inputs[to.id.input] = Some(DemoViewer::get_out_type(from.id, snarl)); - let signatures = to_node.signatures_matching(&mut inputs.into_iter()); + let signatures = to_node.signatures_matching(&inputs[..]); if signatures.is_empty() { return; @@ -355,8 +384,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { fn inputs(&mut self, id: NodeId, snarl: &Snarl<DemoNode>) -> usize { let node = &snarl[id]; - let sig = &node.get_node_signature(id, snarl); - sig.inputs.len() + node.max_inputs() } fn outputs(&mut self, id: NodeId, snarl: &Snarl<DemoNode>) -> usize { @@ -369,14 +397,8 @@ impl SnarlViewer<DemoNode> for DemoViewer { #[allow(refining_impl_trait)] fn show_input(&mut self, pin: &InPin, _ui: &mut Ui, snarl: &mut Snarl<DemoNode>) -> PinInfo { match DemoViewer::get_in_type(pin.id, snarl) { - None => PinInfo::circle().with_fill(UNTYPED_COLOR), - Some(ref t) => PinInfo::circle().with_fill(match t { - Type::Scalar(_) => NUMBER_COLOR, - Type::Vector(_, Dimension::D2) => IMAGE_COLOR, - Type::Vector(_, Dimension::D3) => STRING_COLOR, - Type::Vector(_, Dimension::D4) => UNTYPED_COLOR, - Type::Matrix(_, _, _) => UNTYPED_COLOR, - }), + None => PinInfo::star().with_fill(Color32::from_rgb(0xb0, 0xb0, 0xb0)), + Some(t) => t.into(), } } @@ -384,13 +406,7 @@ impl SnarlViewer<DemoNode> for DemoViewer { fn show_output(&mut self, pin: &OutPin, ui: &mut Ui, snarl: &mut Snarl<DemoNode>) -> PinInfo { let typ = DemoViewer::get_out_type(pin.id, snarl); ui.label(format!("{typ}")); - PinInfo::circle().with_fill(match typ { - Type::Scalar(_) => NUMBER_COLOR, - Type::Vector(_, Dimension::D2) => IMAGE_COLOR, - Type::Vector(_, Dimension::D3) => STRING_COLOR, - Type::Vector(_, Dimension::D4) => UNTYPED_COLOR, - Type::Matrix(_, _, _) => UNTYPED_COLOR, - }) + typ.into() } fn has_body(&mut self, _node: &DemoNode) -> bool { |
