diff --git a/node-graph/gcore/src/context_modification.rs b/node-graph/gcore/src/context_modification.rs index f22ea1ec43..48bdf9f741 100644 --- a/node-graph/gcore/src/context_modification.rs +++ b/node-graph/gcore/src/context_modification.rs @@ -31,6 +31,7 @@ async fn context_modification( Context -> Vec, Context -> Vec, Context -> Vec, + Context -> Vec, Context -> Table, Context -> Table, Context -> Table>, diff --git a/node-graph/gcore/src/graphic.rs b/node-graph/gcore/src/graphic.rs index d6774c8c67..31a0f6979a 100644 --- a/node-graph/gcore/src/graphic.rs +++ b/node-graph/gcore/src/graphic.rs @@ -451,6 +451,7 @@ fn index( Vec, Vec, Vec, + Vec, Table, Table, Table, diff --git a/node-graph/gcore/src/logic.rs b/node-graph/gcore/src/logic.rs index 84842e0000..a7c434a072 100644 --- a/node-graph/gcore/src/logic.rs +++ b/node-graph/gcore/src/logic.rs @@ -69,6 +69,17 @@ fn string_length(_: impl Ctx, string: String) -> f64 { string.chars().count() as f64 } +#[node_macro::node(category("Text"))] +fn string_split(_: impl Ctx, string: String, #[default("\\n")] delimeter: String, #[default(true)] delimeter_escaping: bool) -> Vec { + let delimeter = if delimeter_escaping { + delimeter.replace("\\n", "\n").replace("\\r", "\r").replace("\\t", "\t").replace("\\0", "\0").replace("\\\\", "\\") + } else { + delimeter + }; + + string.split(&delimeter).map(str::to_string).collect() +} + /// Evaluates either the "If True" or "If False" input branch based on whether the input condition is true or false. #[node_macro::node(category("Math: Logic"))] async fn switch( diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index 309fa615da..6217778ee6 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -1954,11 +1954,39 @@ fn point_inside(_: impl Ctx, source: Table, point: DVec2) -> bool { source.into_iter().any(|row| row.element.check_point_inside_shape(row.transform, point)) } +trait Count { + fn count(&self) -> usize; +} +impl Count for Table { + fn count(&self) -> usize { + self.len() + } +} +impl Count for Vec { + fn count(&self) -> usize { + self.len() + } +} + // TODO: Return u32, u64, or usize instead of f64 after #1621 is resolved and has allowed us to implement automatic type conversion in the node graph for nodes with generic type inputs. // TODO: (Currently automatic type conversion only works for concrete types, via the Graphene preprocessor and not the full Graphene type system.) #[node_macro::node(category("General"), path(graphene_core::vector))] -async fn count_elements(_: impl Ctx, #[implementations(Table, Table, Table>, Table>, Table, Table)] source: Table) -> f64 { - source.len() as f64 +async fn count_elements( + _: impl Ctx, + #[implementations( + Table, + Table, + Table>, + Table>, + Table, + Table, + Vec, + Vec, + Vec, + )] + source: I, +) -> f64 { + source.count() as f64 } #[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))] diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 5a285f5298..ab5fde8110 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -108,6 +108,7 @@ fn node_registry() -> HashMap, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Option]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec]), + async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => [f64; 4]]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Graphic]), @@ -165,6 +166,7 @@ fn node_registry() -> HashMap, input: Context, fn_params: [Context => Vec]), async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Vec]), async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Vec]), + async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Vec]), #[cfg(feature = "gpu")] async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Arc]), async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => WindowHandle]), diff --git a/node-graph/node-macro/src/codegen.rs b/node-graph/node-macro/src/codegen.rs index ad1e079bcd..d0733e1bb6 100644 --- a/node-graph/node-macro/src/codegen.rs +++ b/node-graph/node-macro/src/codegen.rs @@ -88,7 +88,15 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn .iter() .map(|field| match &field.ty { ParsedFieldType::Regular(RegularParsedField { value_source, .. }) => match value_source { - ParsedValueSource::Default(data) => quote!(RegistryValueSource::Default(stringify!(#data))), + ParsedValueSource::Default(data) => { + // Check if the data is a string literal by parsing the token stream + let data_str = data.to_string(); + if data_str.starts_with('"') && data_str.ends_with('"') && data_str.len() >= 2 { + quote!(RegistryValueSource::Default(#data)) + } else { + quote!(RegistryValueSource::Default(stringify!(#data))) + } + } ParsedValueSource::Scope(data) => quote!(RegistryValueSource::Scope(#data)), _ => quote!(RegistryValueSource::None), },