aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-12-29 15:20:47 +0000
committers-ol <s+removethis@s-ol.nu>2025-12-29 15:20:47 +0000
commite14ffe29676b8450e807a40c8571243760476730 (patch)
treef40e40fe0970822ad2a8556ade12dd16016c6f25 /src
parentadd split node (diff)
downloadnodetoy-e14ffe29676b8450e807a40c8571243760476730.tar.gz
nodetoy-e14ffe29676b8450e807a40c8571243760476730.zip
Vector combine
Diffstat (limited to 'src')
-rw-r--r--src/library.rs139
-rw-r--r--src/main.rs140
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 {