Add "Loop Level" to the Position context reader node (#3679)

* Add "Loop Level" to the Position context reader node

* Remove InjectPosition
This commit is contained in:
Keavon Chambers
2026-01-28 02:52:39 -08:00
committed by GitHub
parent ea293575e0
commit 390004897b
6 changed files with 270 additions and 126 deletions

View File

@@ -1,28 +1,15 @@
use core_types::Color;
use core_types::table::{Table, TableRowRef};
use core_types::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, InjectVarArgs, OwnedContextImpl};
use core_types::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractPosition, OwnedContextImpl};
use glam::DVec2;
use graphic_types::Graphic;
use graphic_types::Vector;
use graphic_types::raster_types::{CPU, Raster};
use vector_types::GradientStops;
use log::*;
#[repr(transparent)]
#[derive(dyn_any::DynAny)]
struct HashableDVec2(DVec2);
impl std::hash::Hash for HashableDVec2 {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.x.to_bits().hash(state);
self.0.y.to_bits().hash(state);
}
}
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(core_types::vector))]
async fn instance_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
ctx: impl ExtractAll + CloneVarArgs + Sync + Ctx + InjectVarArgs,
ctx: impl ExtractAll + CloneVarArgs + Sync + Ctx,
points: Table<Vector>,
#[implementations(
Context -> Table<Graphic>,
@@ -40,7 +27,7 @@ async fn instance_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>
let mut iteration = async |index, point| {
let transformed_point = transform.transform_point2(point);
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_vararg(Box::new(HashableDVec2(transformed_point)));
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_position(transformed_point);
let generated_instance = instance.eval(new_ctx.into_context()).await;
for mut generated_row in generated_instance.into_iter() {
@@ -97,13 +84,23 @@ async fn instance_repeat<T: Into<Graphic> + Default + Send + Clone + 'static>(
}
#[node_macro::node(category("Instancing"), path(core_types::vector))]
async fn instance_position(ctx: impl Ctx + ExtractVarArgs) -> DVec2 {
match ctx.vararg(0).map(|dynamic| dynamic.downcast_ref::<HashableDVec2>()) {
Ok(Some(position)) => return position.0,
Ok(_) => warn!("Extracted value of incorrect type"),
Err(e) => warn!("Cannot extract position vararg: {e:?}"),
async fn instance_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 {
let Some(position_iter) = ctx.try_position() else { return DVec2::ZERO };
let mut last = DVec2::ZERO;
for (i, position) in position_iter.enumerate() {
if i == loop_level as usize {
return position;
}
last = position;
}
Default::default()
last
}
// 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.
@@ -160,7 +157,13 @@ mod test {
let owned = OwnedContextImpl::default().into_context();
let rect = RectangleNode::new(
FutureWrapperNode(()),
ExtractXyNode::new(InstancePositionNode {}, FutureWrapperNode(XY::Y)),
ExtractXyNode::new(
InstancePositionNode {
_primary: FutureWrapperNode(()),
loop_level: FutureWrapperNode(0),
},
FutureWrapperNode(XY::Y),
),
FutureWrapperNode(2_f64),
FutureWrapperNode(false),
FutureWrapperNode(0_f64),