From 44369cb12bb3454e8f80c2769269e0bdc29d6b22 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sat, 22 Aug 2026 12:29:16 +0000 Subject: [PATCH] Swap the repeat node to its leveled form in place --- node-graph/nodes/repeat/src/repeat_nodes.rs | 63 ++++++++++----------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/node-graph/nodes/repeat/src/repeat_nodes.rs b/node-graph/nodes/repeat/src/repeat_nodes.rs index f0f5992a47..6d6a8041c6 100644 --- a/node-graph/nodes/repeat/src/repeat_nodes.rs +++ b/node-graph/nodes/repeat/src/repeat_nodes.rs @@ -1,51 +1,48 @@ use crate::gcore::Context; use core::f64::consts::TAU; -use core_types::gpoll::Interrupt; +use core_types::context::IndexLink; +use core_types::extent::{ExtentIn, LevelIn, ValueIn}; +use core_types::gpoll::{Extent, GPoll, GraphError, Interrupt}; use core_types::list::List; use core_types::registry::types::{Angle, PixelSize}; -use core_types::{ATTR_TRANSFORM, Color, Ctx, DeriveCtx, InjectVarArgs}; +use core_types::{ATTR_TRANSFORM, Color, Ctx, DeriveCtx, ExtractIndex, InjectVarArgs}; use glam::{DAffine2, DVec2}; use graphic_types::{Graphic, Vector}; use raster_types::{CPU, Raster}; use vector_types::GradientStops; -#[node_macro::node(category("Repeat"))] -fn repeat + Default + Send + Clone + 'static>( - ctx: impl Ctx + DeriveCtx, - #[implementations( - Context -> List, - Context -> List, - Context -> List>, - Context -> List, - Context -> List, - )] - content: impl Node, Output = List>, +/// Each copy evaluates the content within the copy's index pushed in, +/// producing a level of `count` copies. +// Someday this node can have the option to generate infinitely instead of a fixed count (basically `std::iter::repeat`). +#[node_macro::node(category("Repeat"), extent(repeat_extent))] +fn repeat( + ctx: impl Ctx + DeriveCtx + ExtractIndex, + content: impl Node, Output = T>, #[default(1)] #[hard(1..)] count: u32, reverse: bool, -) -> Result, Interrupt> { - // Someday this node can have the option to generate infinitely instead of a fixed count (basically `std::iter::repeat`). - - let count = count as u64; - let spilled = ctx.index_head(); - - let mut result_list = List::new(); - - for index in 0..count { - let index = if reverse { count - index - 1 } else { index }; - - let mark = core_types::record::stack::sp(); - let generated_content = content.eval(&ctx.promoted(&spilled, index))?; - - for generated_row in generated_content.into_iter() { - result_list.push(generated_row); - } - // SAFETY: generated_content is an owned list fully moved into result_list, so no record borrow into this iteration's frames remains. - unsafe { core_types::record::stack::rewind(mark) }; +) -> Result, Interrupt> { + let inner = content.inner_extent(ctx)?; + let (copy, rest) = ctx.split_innermost(inner); + if copy >= count as u64 { + return Err(GraphError::past_end().into()); } + let copy = match reverse { + true => count as u64 - 1 - copy, + false => copy, + }; + let mut frame = IndexLink { index: 0, outer: None }; + content.eval(&ctx.push_level(&mut frame, copy, rest)) +} - Ok(result_list) +/// The pushed level's extent is the copy count; inner levels forward to the +/// content, whose extent is taken uniform across copies (queried at copy 0). +fn repeat_extent(content: ExtentIn<'_>, count: ValueIn<'_, u32>, _reverse: ValueIn<'_, bool>, level: LevelIn) -> GPoll { + match level.pushed() { + true => count.get().map(|count| Extent::Exactly(count as usize)), + false => content.at(level), + } } #[node_macro::node(category("Repeat"))]