use core_types::gpoll::{Extent, GPoll, GraphError, Interrupt}; use core_types::list::List; use core_types::{Color, ExtractVarArgs}; use core_types::{Ctx, ExtractIndex, ExtractIndices, ExtractPosition}; use glam::DVec2; use graphic_types::vector_types::Gradient; use graphic_types::{Graphic, Vector}; use raster_types::{CPU, Raster}; /// The mapped row riding as vararg 0, in the production single-item shape. fn vararg_list(ctx: &impl ExtractVarArgs) -> Option<&List> { let arg = ctx.vararg(0).ok()?; (arg as &dyn std::any::Any).downcast_ref::>() } /// Lanes of a leveled vararg source: one per item, none without a row, /// matching the legacy empty-list return. fn vararg_lanes(ctx: &impl ExtractVarArgs, level: u8) -> GPoll { match level { 0 => GPoll::Final(Extent::Exactly(vararg_list::(ctx).map_or(0, List::len))), _ => GPoll::Final(Extent::Exactly(1)), } } fn vararg_element(ctx: &(impl ExtractVarArgs + ExtractIndex)) -> Result { vararg_list::(ctx) .and_then(|list| list.element(ctx.index() as usize)) .cloned() .ok_or_else(|| GraphError::new("vararg row addressed past its items").into()) } // The vararg readers: the mapped row's items as the lanes of a level, elements // only, one node per element type since a reader names its type. macro_rules! vararg_readers { ($($node:ident / $extent:ident / $node_type:ident: $element:ty;)*) => { $( #[node_macro::node(category("Context"), path(graphene_core::vector), extent_raw($extent))] pub fn $node(ctx: impl Ctx + ExtractVarArgs + ExtractIndex, _primary: ()) -> Result, Interrupt> { vararg_element(ctx) } fn $extent(_: &$node_type, ctx: &C, level: u8) -> GPoll { vararg_lanes::<$element>(ctx, level) } )* }; } vararg_readers! { read_graphic / read_graphic_extent / ReadGraphicNode: Graphic<'static>; read_vector / read_vector_extent / ReadVectorNode: Vector; read_raster / read_raster_extent / ReadRasterNode: Raster; read_color / read_color_extent / ReadColorNode: Color; read_gradient / read_gradient_extent / ReadGradientNode: Gradient; read_string / read_string_extent / ReadStringNode: String; read_number / read_number_extent / ReadNumberNode: f64; } #[node_macro::node(category("Context"), path(core_types::vector))] fn read_position( ctx: impl Ctx + ExtractPosition, _primary: (), /// The number of nested loops to traverse outwards (from the innermost loop) to get the position from. The most upstream loop is level 0, and downstream loops add levels. /// /// In programming terms: inside the double loop `i { j { ... } }`, *Loop Level* 0 = `j` and 1 = `i`. After inserting a third loop `k { ... }`, inside it, levels would be 0 = `k`, 1 = `j`, and 2 = `i`. loop_level: u32, ) -> DVec2 { ctx.try_position().and_then(|mut iter| iter.nth(loop_level as usize).or_else(|| iter.last())).unwrap_or(DVec2::ZERO) } // 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.) /// Produces the index of the current iteration of a loop by reading from the evaluation context, which is supplied by downstream nodes such as *Repeat*. /// /// Nested loops can enable 2D or higher-dimensional iteration by using the *Loop Level* parameter to read the index from outer levels of loops. #[node_macro::node(category("Context"), path(core_types::vector))] fn read_index( // `loop_level` is a runtime input, so no level is statically known and the // whole chain has to survive nullification. ctx: impl Ctx + ExtractIndices, _primary: (), /// The number of nested loops to traverse outwards (from the innermost loop) to get the index from. The most upstream loop is level 0, and downstream loops add levels. /// /// In programming terms: inside the double loop `i { j { ... } }`, *Loop Level* 0 = `j` and 1 = `i`. After inserting a third loop `k { ... }`, inside it, levels would be 0 = `k`, 1 = `j`, and 2 = `i`. loop_level: u32, ) -> f64 { // The chain's innermost entry is the consuming input's own lane from the // decompose-and-promote split; the loops the reader counts sit above it. ctx.try_index().and_then(|mut iter| iter.nth(loop_level as usize + 1)).unwrap_or(0) as f64 }