mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-18 18:18:08 +08:00
WIP: Thumbnails
This commit is contained in:
@@ -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>,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user