WIP: Thumbnails

This commit is contained in:
Adam
2025-07-14 00:25:57 -07:00
parent 8b665d158c
commit fcd379a62f
50 changed files with 1208 additions and 1053 deletions

View File

@@ -1,7 +1,9 @@
use dyn_any::StaticType;
use glam::{DAffine2, UVec2};
use crate::transform::Footprint;
use std::any::Any;
use std::fmt;
use std::panic::Location;
use std::sync::Arc;
@@ -26,6 +28,14 @@ pub trait ExtractRealTime {
fn try_real_time(&self) -> Option<f64>;
}
pub trait ModifyDownstreamTransform: ExtractAll + CloneVarArgs {
fn apply_modification(self, modification: &DAffine2) -> Context;
}
pub trait WithIndex: ExtractAll + CloneVarArgs {
fn with_index(&self, index: usize) -> Context;
}
pub trait ExtractAnimationTime {
fn try_animation_time(&self) -> Option<f64>;
}
@@ -50,7 +60,7 @@ pub trait ExtractAll: ExtractFootprint + ExtractDownstreamTransform + ExtractInd
impl<T: ?Sized + ExtractFootprint + ExtractDownstreamTransform + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs> ExtractAll for T {}
#[derive(Debug, Clone, PartialEq)]
#[derive(Clone, PartialEq)]
#[repr(u8)]
pub enum ContextDependency {
ExtractFootprint = 0b10000000,
@@ -62,7 +72,7 @@ pub enum ContextDependency {
ExtractVarArgs = 0b00000100,
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Clone, PartialEq)]
pub struct ContextDependencies(pub u8);
impl ContextDependencies {
@@ -99,6 +109,34 @@ impl ContextDependencies {
}
}
impl fmt::Debug for ContextDependencies {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut set = Vec::new();
let bits = self.0;
if bits & ContextDependency::ExtractFootprint as u8 != 0 {
set.push("ExtractFootprint");
}
if bits & ContextDependency::ExtractDownstreamTransform as u8 != 0 {
set.push("ExtractDownstreamTransform");
}
if bits & ContextDependency::ExtractRealTime as u8 != 0 {
set.push("ExtractRealTime");
}
if bits & ContextDependency::ExtractAnimationTime as u8 != 0 {
set.push("ExtractAnimationTime");
}
if bits & ContextDependency::ExtractIndex as u8 != 0 {
set.push("ExtractIndex");
}
if bits & ContextDependency::ExtractVarArgs as u8 != 0 {
set.push("ExtractVarArgs");
}
f.debug_list().entries(set).finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VarArgsResult {
IndexOutOfBounds,
@@ -110,7 +148,7 @@ impl Ctx for () {}
impl Ctx for Footprint {}
impl ExtractFootprint for () {
fn try_footprint(&self) -> Option<&Footprint> {
log::error!("tried to extract footprint form (), {}", Location::caller());
log::error!("tried to extract footprint from (), {}", Location::caller());
None
}
}
@@ -129,7 +167,7 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Option<T> {
impl ExtractDownstreamTransform for () {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
log::error!("tried to extract downstream transform form (), {}", Location::caller());
log::error!("tried to extract downstream transform from (), {}", Location::caller());
None
}
}
@@ -146,6 +184,42 @@ impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Option
}
}
impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Arc<T> {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
(**self).try_downstream_transform()
}
}
impl ExtractDownstreamTransform for OwnedContextImpl {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
self.downstream_transform.as_ref()
}
}
impl<T: ExtractAll + CloneVarArgs + Sync> ModifyDownstreamTransform for Option<T> {
fn apply_modification(self, modification: &DAffine2) -> Context {
if let Some(inner) = self {
let mut context = OwnedContextImpl::from(inner);
context.try_apply_downstream_transform(modification);
context.into_context()
} else {
None
}
}
}
impl<T: Ctx + ExtractAll + CloneVarArgs + Sync> WithIndex for Option<T> {
fn with_index(&self, index: usize) -> Context {
if let Some(inner) = self {
let mut context = OwnedContextImpl::from(inner.clone());
context.set_index(index);
context.into_context()
} else {
None
}
}
}
impl<T: ExtractRealTime + Sync> ExtractRealTime for Option<T> {
fn try_real_time(&self) -> Option<f64> {
self.as_ref().and_then(|x| x.try_real_time())
@@ -178,12 +252,6 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Arc<T> {
}
}
impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Arc<T> {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
(**self).try_downstream_transform()
}
}
impl<T: ExtractRealTime + Sync> ExtractRealTime for Arc<T> {
fn try_real_time(&self) -> Option<f64> {
(**self).try_real_time()
@@ -237,12 +305,6 @@ impl ExtractFootprint for OwnedContextImpl {
}
}
impl ExtractDownstreamTransform for OwnedContextImpl {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
self.downstream_transform.as_ref()
}
}
impl ExtractRealTime for OwnedContextImpl {
fn try_real_time(&self) -> Option<f64> {
self.real_time
@@ -395,6 +457,16 @@ impl OwnedContextImpl {
self.parent = None
}
}
pub fn to_editor_context(&self) -> EditorContext {
EditorContext {
footprint: self.footprint,
downstream_transform: self.downstream_transform,
real_time: self.real_time,
animation_time: self.animation_time,
index: self.index,
}
}
}
impl OwnedContextImpl {
@@ -404,9 +476,9 @@ impl OwnedContextImpl {
pub fn set_downstream_transform(&mut self, transform: DAffine2) {
self.downstream_transform = Some(transform);
}
pub fn try_apply_downstream_transform(&mut self, transform: DAffine2) {
pub fn try_apply_downstream_transform(&mut self, transform: &DAffine2) {
if let Some(downstream_transform) = self.downstream_transform {
self.downstream_transform = Some(downstream_transform * transform);
self.downstream_transform = Some(downstream_transform * *transform);
}
}
pub fn set_real_time(&mut self, time: f64) {
@@ -434,14 +506,10 @@ impl OwnedContextImpl {
self.animation_time = Some(animation_time);
self
}
pub fn with_index(mut self, index: usize) -> Self {
if let Some(current_index) = &mut self.index {
current_index.push(index);
} else {
self.index = Some(vec![index]);
}
self
}
// pub fn with_index(mut self, index: usize) -> Self {
// self.index = Some(index);
// self
// }
pub fn into_context(self) -> Option<Arc<Self>> {
Some(Arc::new(self))
}
@@ -465,6 +533,50 @@ impl OwnedContextImpl {
}
}
#[derive(Debug, Clone, Default)]
pub struct EditorContext {
pub footprint: Option<Footprint>,
pub downstream_transform: Option<DAffine2>,
pub real_time: Option<f64>,
pub animation_time: Option<f64>,
pub index: Option<usize>,
// #[serde(skip)]
// pub editor_var_args: Option<(Vec<String>, Vec<Arc<Box<[dyn std::any::Any + 'static + std::panic::UnwindSafe]>>>)>,
}
unsafe impl StaticType for EditorContext {
type Static = EditorContext;
}
impl EditorContext {
pub fn to_owned_context(&self) -> OwnedContextImpl {
let mut context = OwnedContextImpl::default();
if let Some(footprint) = self.footprint {
context.set_footprint(footprint);
}
if let Some(footprint) = self.footprint {
context.set_footprint(footprint);
}
// if let Some(downstream_transform) = self.downstream_transform {
// context.set_downstream_transform(downstream_transform);
// }
if let Some(real_time) = self.real_time {
context.set_real_time(real_time);
}
if let Some(animation_time) = self.animation_time {
context.set_animation_time(animation_time);
}
if let Some(index) = self.index {
context.set_index(index);
}
context
// if let Some(editor_var_args) = self.editor_var_args {
// let (variable_names, values)
// context.set_varargs((variable_names, values))
// }
}
}
// #[derive(Default, Clone, Copy, dyn_any::DynAny)]
// pub struct ContextImpl<'a> {
// pub(crate) footprint: Option<&'a Footprint>,

View File

@@ -4,10 +4,9 @@ use crate::instances::{Instance, Instances};
use crate::math::quad::Quad;
use crate::raster::image::Image;
use crate::raster_types::{CPU, GPU, Raster, RasterDataTable};
use crate::transform::TransformMut;
use crate::uuid::NodeId;
use crate::vector::{VectorData, VectorDataTable};
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
use crate::{ Color, Context, Ctx, ModifyDownstreamTransform};
use dyn_any::DynAny;
use glam::{DAffine2, DVec2, IVec2};
use std::hash::Hash;
@@ -457,7 +456,7 @@ async fn flatten_vector(_: impl Ctx, group: GraphicGroupTable) -> VectorDataTabl
#[node_macro::node(category(""))]
async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
ctx: impl ExtractAll + CloneVarArgs + Ctx,
ctx: impl Ctx + ModifyDownstreamTransform,
#[implementations(
Context -> GraphicGroupTable,
Context -> VectorDataTable,
@@ -471,13 +470,8 @@ async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
background: Color,
clip: bool,
) -> Artboard {
let footprint = ctx.try_footprint().copied();
let mut new_ctx = OwnedContextImpl::from(ctx);
if let Some(mut footprint) = footprint {
footprint.translate(location.as_dvec2());
new_ctx = new_ctx.with_footprint(footprint);
}
let graphic_group = contents.eval(new_ctx.into_context()).await;
let modified_ctx = ctx.apply_modification(&DAffine2::from_translation(location.as_dvec2()));
let graphic_group = contents.eval(modified_ctx).await;
Artboard {
graphic_group: graphic_group.into(),

View File

@@ -32,6 +32,7 @@ pub mod value;
pub mod vector;
pub use crate as graphene_core;
use crate::memo::MonitorIntrospectResult;
pub use blending::*;
pub use context::*;
pub use ctor;
@@ -61,10 +62,14 @@ pub trait Node<'i, Input> {
}
// If check if evaluated is true, then it returns None if the node has not been evaluated since the last introspection
fn introspect(&self, _check_if_evaluated: bool) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
fn introspect(&self) -> MonitorIntrospectResult {
log::warn!("Node::introspect not implemented for {}", std::any::type_name::<Self>());
None
MonitorIntrospectResult::Error
}
fn permanently_enable_cache(&self) {}
fn cache_first_evaluation(&self) {}
}
mod types;

View File

@@ -7,14 +7,37 @@ use std::ops::Deref;
use std::sync::Arc;
use std::sync::Mutex;
#[derive(Debug)]
pub enum MonitorMemoNodeState {
Disabled,
// Stores the first execution, then gets set first execution result, which stores if the value changed
StoreFirstEvaluation,
// Gets set back to disabled on introspection, and stores a boolean for if the value changed since the last introspection
FirstEvaluationResult(bool),
// Acts as a normal cache node, and stores a boolean for if the value changed since the last introspection
Enabled(bool),
}
#[derive(Clone, Debug)]
pub enum MonitorIntrospectResult {
// If trying to inspect a none that cannot be introspected
Error,
Disabled,
// The cache node has not been evaluated since the state was set to StoreFirstEvaluation/Enabled
NotEvaluated,
// If the monitor node was evaluated, then its data must exist, so it is not an option.
// The boolean represents if the data changed since the last introspection
Evaluated((std::sync::Arc<dyn std::any::Any + Send + Sync>, bool)),
}
/// Caches the output of a given Node and acts as a proxy
#[derive(Default)]
pub struct MonitorMemoNode<T, CachedNode> {
// Introspection cache, uses the hash of the nullified context with default var args
// cache: Arc<Mutex<std::collections::HashMap<u64, Arc<T>>>>,
cache: Arc<Mutex<Option<(u64, Arc<T>)>>>,
node: CachedNode,
changed_since_last_eval: Arc<Mutex<bool>>,
hash_on_last_introspection: Arc<Mutex<u64>>,
state: Arc<Mutex<MonitorMemoNodeState>>,
}
impl<'i, I: Hash + 'i + std::fmt::Debug, T: 'static + Clone + Send + Sync, CachedNode: 'i> Node<'i, I> for MonitorMemoNode<T, CachedNode>
where
@@ -26,47 +49,110 @@ where
type Output = DynFuture<'i, T>;
fn eval(&'i self, input: I) -> Self::Output {
let mut hasher = DefaultHasher::new();
input.hash(&mut hasher);
let hash = hasher.finish();
let mut state = self.state.lock().unwrap();
if let Some(data) = self.cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) {
let cloned_data = (*data).clone();
Box::pin(async move { cloned_data })
} else {
let fut = self.node.eval(input);
let cache = self.cache.clone();
*self.changed_since_last_eval.lock().unwrap() = true;
Box::pin(async move {
let value = fut.await;
*cache.lock().unwrap() = Some((hash, Arc::new(value.clone())));
value
})
// log::debug!("Monitor memo node state: {:?}", *state);
if matches!(*state, MonitorMemoNodeState::Disabled | MonitorMemoNodeState::FirstEvaluationResult(_)) {
return Box::pin(self.node.eval(input));
}
let hash = {
let mut hasher = DefaultHasher::new();
input.hash(&mut hasher);
hasher.finish()
};
// log::debug!(
// "Monitor memo node input: {:?}, hash: {:?}, previous_hash: {:?}",
// input,
// hash,
// self.cache.lock().unwrap().as_ref().map(|(h, _)| *h)
// );
let last_hash = *self.hash_on_last_introspection.lock().unwrap();
match &mut *state {
MonitorMemoNodeState::Enabled(changed) => {
*changed = last_hash != hash;
}
MonitorMemoNodeState::StoreFirstEvaluation => {
*state = MonitorMemoNodeState::FirstEvaluationResult(last_hash != hash);
}
_ => {}
}
let cache_guard = self.cache.lock().unwrap();
if let Some((cached_hash, cached_data)) = cache_guard.as_ref() {
if *cached_hash == hash {
let cloned_data = (**cached_data).clone();
return Box::pin(async move { cloned_data });
}
}
drop(cache_guard);
Box::pin(async move {
let value = self.node.eval(input).await;
*self.cache.lock().unwrap() = Some((hash, Arc::new(value.clone())));
value
})
}
fn introspect(&self) -> MonitorIntrospectResult {
let mut state_guard = self.state.lock().unwrap();
match *state_guard {
MonitorMemoNodeState::Disabled => {
// Make sure to set the state to "StoreFirstEvaluation" or "Enabled" before trying to introspect
log::error!("Cannot introspect disabled monitor memo node");
MonitorIntrospectResult::Disabled
}
MonitorMemoNodeState::StoreFirstEvaluation => MonitorIntrospectResult::NotEvaluated,
MonitorMemoNodeState::FirstEvaluationResult(changed_since_last_introspection) => {
let (hash, cache_value) = self
.cache
.lock()
.unwrap()
.as_ref()
.map(|(hash, data)| (*hash, (*data).clone() as Arc<dyn std::any::Any + Send + Sync>))
.expect("Evaluated cache node must store data");
*self.hash_on_last_introspection.lock().unwrap() = hash;
*state_guard = MonitorMemoNodeState::Disabled;
MonitorIntrospectResult::Evaluated((cache_value, changed_since_last_introspection))
}
MonitorMemoNodeState::Enabled(changed_since_last_introspection) => {
let cache = self.cache.lock().unwrap().as_ref().map(|(hash, data)| (*hash, (*data).clone() as Arc<dyn std::any::Any + Send + Sync>));
match cache {
Some((hash, cache_value)) => {
*self.hash_on_last_introspection.lock().unwrap() = hash;
MonitorIntrospectResult::Evaluated((cache_value, changed_since_last_introspection))
}
None => MonitorIntrospectResult::NotEvaluated,
}
}
}
}
// TODO: Consider returning a reference to the entire cache so the frontend reference is automatically updated as the context changes
fn introspect(&self, check_if_evaluated: bool) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
let mut changed = self.changed_since_last_eval.lock().unwrap();
if check_if_evaluated {
if !*changed {
return None;
}
}
*changed = false;
fn permanently_enable_cache(&self) {
*self.state.lock().unwrap() = MonitorMemoNodeState::Enabled(false);
}
let cache_guard = self.cache.lock().unwrap();
let cached = cache_guard.as_ref().expect("Cached data should always be evaluated before introspection");
Some(cached.1.clone() as Arc<dyn std::any::Any + Send + Sync>)
fn cache_first_evaluation(&self) {
if matches!(*self.state.lock().unwrap(), MonitorMemoNodeState::Enabled(_)) {
return;
}
*self.state.lock().unwrap() = MonitorMemoNodeState::StoreFirstEvaluation;
}
}
impl<T, CachedNode> MonitorMemoNode<T, CachedNode> {
pub fn new(node: CachedNode) -> MonitorMemoNode<T, CachedNode> {
pub fn new(node: CachedNode, state: MonitorMemoNodeState) -> MonitorMemoNode<T, CachedNode> {
MonitorMemoNode {
cache: Default::default(),
node,
changed_since_last_eval: Arc::new(Mutex::new(true)),
hash_on_last_introspection: Arc::new(Mutex::new(0)),
state: Arc::new(Mutex::new(state)),
}
}
}

View File

@@ -1,3 +1,4 @@
use crate::memo::MonitorMemoNodeState;
use crate::{ContextDependencies, Node, NodeIO, NodeIOTypes, ProtoNodeIdentifier, Type, WasmNotSend};
use dyn_any::{DynAny, StaticType};
use std::borrow::Cow;
@@ -134,7 +135,7 @@ pub type TypeErasedPinned<'n> = Pin<Box<TypeErasedNode<'n>>>;
pub type SharedNodeContainer = std::sync::Arc<NodeContainer>;
pub type NodeConstructor = fn(Vec<SharedNodeContainer>) -> DynFuture<'static, TypeErasedBox<'static>>;
pub type CacheConstructor = fn(SharedNodeContainer) -> TypeErasedBox<'static>;
pub type CacheConstructor = fn(SharedNodeContainer, MonitorMemoNodeState) -> TypeErasedBox<'static>;
#[derive(Clone)]
pub struct NodeContainer {
@@ -290,8 +291,16 @@ where
}
}
fn introspect(&self, check_if_evaluated: bool) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
self.node.introspect(check_if_evaluated)
fn introspect(&self) -> crate::memo::MonitorIntrospectResult {
self.node.introspect()
}
fn permanently_enable_cache(&self) {
self.node.permanently_enable_cache();
}
fn cache_first_evaluation(&self) {
self.node.cache_first_evaluation();
}
fn reset(&self) {

View File

@@ -110,8 +110,10 @@ impl Footprint {
quality: RenderQuality::Full,
};
pub fn viewport_bounds_in_local_space(&self) -> AxisAlignedBbox {
let inverse = self.transform.inverse();
pub fn viewport_bounds_in_local_space(&self, downstream_transform: &DAffine2) -> AxisAlignedBbox {
// TODO: Check if this is the correct way to apply downstream transforms
let transform = self.transform * *downstream_transform;
let inverse = transform.inverse();
let start = inverse.transform_point2((0., 0.).into());
let end = inverse.transform_point2(self.resolution.as_dvec2());
AxisAlignedBbox { start, end }

View File

@@ -1,14 +1,14 @@
use crate::instances::Instances;
use crate::raster_types::{CPU, GPU, RasterDataTable};
use crate::transform::{ApplyTransform, Footprint, Transform};
use crate::transform::{Transform};
use crate::vector::VectorDataTable;
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, GraphicGroupTable, OwnedContextImpl};
use crate::{ Context, Ctx, GraphicGroupTable, ModifyDownstreamTransform, };
use core::f64;
use glam::{DAffine2, DVec2};
#[node_macro::node(category(""))]
async fn transform<T: 'n + 'static>(
ctx: impl Ctx + CloneVarArgs + ExtractAll,
ctx: impl Ctx + ModifyDownstreamTransform,
#[implementations(
Context -> VectorDataTable,
Context -> GraphicGroupTable,
@@ -23,15 +23,9 @@ async fn transform<T: 'n + 'static>(
) -> Instances<T> {
let matrix = DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., skew.y, skew.x, 1., 0., 0.]);
let footprint = ctx.try_footprint().copied();
let modified_ctx = ctx.apply_modification(&matrix);
let mut ctx = OwnedContextImpl::from(ctx);
if let Some(mut footprint) = footprint {
footprint.apply_transform(&matrix);
ctx = ctx.with_footprint(footprint);
}
let mut transform_target = transform_target.eval(ctx.into_context()).await;
let mut transform_target = transform_target.eval(modified_ctx).await;
for data_transform in transform_target.instance_mut_iter() {
*data_transform.transform = matrix * *data_transform.transform;
@@ -51,39 +45,3 @@ fn replace_transform<Data, TransformInput: Transform>(
}
data
}
#[node_macro::node(category("Debug"))]
async fn boundless_footprint<T: 'n + 'static>(
ctx: impl Ctx + CloneVarArgs + ExtractAll,
#[implementations(
Context -> VectorDataTable,
Context -> GraphicGroupTable,
Context -> RasterDataTable<CPU>,
Context -> RasterDataTable<GPU>,
Context -> String,
Context -> f64,
)]
transform_target: impl Node<Context<'static>, Output = T>,
) -> T {
let ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::BOUNDLESS);
transform_target.eval(ctx.into_context()).await
}
#[node_macro::node(category("Debug"))]
async fn freeze_real_time<T: 'n + 'static>(
ctx: impl Ctx + CloneVarArgs + ExtractAll,
#[implementations(
Context -> VectorDataTable,
Context -> GraphicGroupTable,
Context -> RasterDataTable<CPU>,
Context -> RasterDataTable<GPU>,
Context -> String,
Context -> f64,
)]
transform_target: impl Node<Context<'static>, Output = T>,
) -> T {
let ctx = OwnedContextImpl::from(ctx).with_real_time(0.);
transform_target.eval(ctx.into_context()).await
}

View File

@@ -1,12 +1,12 @@
use crate::instances::{InstanceRef, Instances};
use crate::raster_types::{CPU, RasterDataTable};
use crate::vector::VectorDataTable;
use crate::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, GraphicElement, GraphicGroupTable, OwnedContextImpl};
use crate::{ Context, Ctx, ExtractIndex, ExtractVarArgs, GraphicElement, GraphicGroupTable, WithIndex};
use glam::DVec2;
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + 'static>(
ctx: impl ExtractAll + CloneVarArgs + Sync + Ctx,
ctx: impl Ctx + WithIndex,
points: VectorDataTable,
#[implementations(
Context -> GraphicGroupTable,
@@ -22,8 +22,7 @@ async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + '
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(("Transformed point", Box::new(transformed_point)));
let generated_instance = instance.eval(new_ctx.into_context()).await;
let generated_instance = instance.eval(ctx.with_index(index)).await;
for mut instanced in generated_instance.instance_iter() {
instanced.transform.translation = transformed_point;
@@ -48,7 +47,7 @@ async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + '
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
async fn instance_repeat<T: Into<GraphicElement> + Default + Send + Clone + 'static>(
ctx: impl ExtractAll + CloneVarArgs + Ctx,
ctx: impl Ctx + WithIndex,
#[implementations(
Context -> GraphicGroupTable,
Context -> VectorDataTable,
@@ -65,8 +64,7 @@ async fn instance_repeat<T: Into<GraphicElement> + Default + Send + Clone + 'sta
for index in 0..count {
let index = if reverse { count - index - 1 } else { index };
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index);
let generated_instance = instance.eval(new_ctx.into_context()).await;
let generated_instance = instance.eval(ctx.with_index(index)).await;
for instanced in generated_instance.instance_iter() {
result_table.push(instanced);

View File

@@ -8,19 +8,14 @@ use crate::bounds::BoundingBox;
use crate::instances::{Instance, InstanceMut, Instances};
use crate::raster_types::{CPU, GPU, RasterDataTable};
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, PixelSize, SeedValue};
use crate::transform::{Footprint, ReferencePoint, Transform};
use crate::vector::PointDomain;
use crate::vector::algorithms::bezpath_algorithms::{eval_pathseg_euclidean, is_linear};
use crate::transform::{ReferencePoint, Transform};
use crate::vector::algorithms::merge_by_distance::MergeByDistanceExt;
use crate::vector::misc::{MergeByDistanceAlgorithm, PointSpacingType};
use crate::vector::misc::{handles_to_segment, segment_to_handles};
use crate::vector::style::{PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
use crate::vector::{FillId, RegionId};
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicElement, GraphicGroupTable, OwnedContextImpl};
use bezier_rs::{BezierHandles, Join, ManipulatorGroup, Subpath};
use core::f64::consts::PI;
use core::hash::{Hash, Hasher};
use crate::vector::{FillId, PointDomain, RegionId};
use crate::{Color, Ctx, GraphicElement, GraphicGroupTable};
use bezier_rs::{Join, ManipulatorGroup, Subpath};
use glam::{DAffine2, DVec2};
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, ParamCurve, PathEl, PathSeg, Shape};
use rand::{Rng, SeedableRng};
@@ -2063,10 +2058,7 @@ async fn path_length(_: impl Ctx, source: VectorDataTable) -> f64 {
}
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<Context<'static>, Output = VectorDataTable>) -> f64 {
let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context();
let vector_data = vector_data.eval(new_ctx).await;
async fn area(_ctx: impl Ctx, vector_data: VectorDataTable) -> f64 {
vector_data
.instance_ref_iter()
.map(|vector_data_instance| {
@@ -2077,10 +2069,7 @@ async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<
}
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl Node<Context<'static>, Output = VectorDataTable>, centroid_type: CentroidType) -> DVec2 {
let new_ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::default()).into_context();
let vector_data = vector_data.eval(new_ctx).await;
async fn centroid(_ctx: impl Ctx, vector_data: VectorDataTable, centroid_type: CentroidType) -> DVec2 {
if vector_data.is_empty() {
return DVec2::ZERO;
}