|
use super::DocumentNode; |
|
use crate::proto::{Any as DAny, FutureAny}; |
|
use crate::wasm_application_io::WasmEditorApi; |
|
use dyn_any::DynAny; |
|
pub use dyn_any::StaticType; |
|
pub use glam::{DAffine2, DVec2, IVec2, UVec2}; |
|
use graphene_application_io::SurfaceFrame; |
|
use graphene_brush::brush_cache::BrushCache; |
|
use graphene_brush::brush_stroke::BrushStroke; |
|
use graphene_core::raster_types::CPU; |
|
use graphene_core::transform::ReferencePoint; |
|
use graphene_core::uuid::NodeId; |
|
use graphene_core::vector::style::Fill; |
|
use graphene_core::{Color, MemoHash, Node, Type}; |
|
use graphene_svg_renderer::RenderMetadata; |
|
use std::fmt::Display; |
|
use std::hash::Hash; |
|
use std::marker::PhantomData; |
|
use std::str::FromStr; |
|
pub use std::sync::Arc; |
|
|
|
pub struct TaggedValueTypeError; |
|
|
|
|
|
macro_rules! tagged_value { |
|
($ ($( #[$meta:meta] )* $identifier:ident ($ty:ty) ),* $(,)?) => { |
|
|
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] |
|
#[allow(clippy::large_enum_variant)] |
|
pub enum TaggedValue { |
|
None, |
|
$( $(#[$meta] ) *$identifier( $ty ), )* |
|
RenderOutput(RenderOutput), |
|
SurfaceFrame(SurfaceFrame), |
|
#[serde(skip)] |
|
EditorApi(Arc<WasmEditorApi>) |
|
} |
|
|
|
|
|
#[allow(clippy::derived_hash_with_manual_eq)] |
|
impl Hash for TaggedValue { |
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
|
core::mem::discriminant(self).hash(state); |
|
match self { |
|
Self::None => {} |
|
$( Self::$identifier(x) => {x.hash(state)}),* |
|
Self::RenderOutput(x) => x.hash(state), |
|
Self::SurfaceFrame(x) => x.hash(state), |
|
Self::EditorApi(x) => x.hash(state), |
|
} |
|
} |
|
} |
|
impl<'a> TaggedValue { |
|
|
|
pub fn to_dynany(self) -> DAny<'a> { |
|
match self { |
|
Self::None => Box::new(()), |
|
$( Self::$identifier(x) => Box::new(x), )* |
|
Self::RenderOutput(x) => Box::new(x), |
|
Self::SurfaceFrame(x) => Box::new(x), |
|
Self::EditorApi(x) => Box::new(x), |
|
} |
|
} |
|
|
|
pub fn to_any(self) -> Arc<dyn std::any::Any + Send + Sync + 'static> { |
|
match self { |
|
Self::None => Arc::new(()), |
|
$( Self::$identifier(x) => Arc::new(x), )* |
|
Self::RenderOutput(x) => Arc::new(x), |
|
Self::SurfaceFrame(x) => Arc::new(x), |
|
Self::EditorApi(x) => Arc::new(x), |
|
} |
|
} |
|
|
|
pub fn ty(&self) -> Type { |
|
match self { |
|
Self::None => concrete!(()), |
|
$( Self::$identifier(_) => concrete!($ty), )* |
|
Self::RenderOutput(_) => concrete!(RenderOutput), |
|
Self::SurfaceFrame(_) => concrete!(SurfaceFrame), |
|
Self::EditorApi(_) => concrete!(&WasmEditorApi) |
|
} |
|
} |
|
|
|
pub fn try_from_any(input: Box<dyn DynAny<'a> + 'a>) -> Result<Self, String> { |
|
use dyn_any::downcast; |
|
use std::any::TypeId; |
|
|
|
match DynAny::type_id(input.as_ref()) { |
|
x if x == TypeId::of::<()>() => Ok(TaggedValue::None), |
|
$( x if x == TypeId::of::<$ty>() => Ok(TaggedValue::$identifier(*downcast(input).unwrap())), )* |
|
x if x == TypeId::of::<RenderOutput>() => Ok(TaggedValue::RenderOutput(*downcast(input).unwrap())), |
|
x if x == TypeId::of::<SurfaceFrame>() => Ok(TaggedValue::SurfaceFrame(*downcast(input).unwrap())), |
|
|
|
|
|
_ => Err(format!("Cannot convert {:?} to TaggedValue", DynAny::type_name(input.as_ref()))), |
|
} |
|
} |
|
|
|
pub fn try_from_std_any_ref(input: &(dyn std::any::Any)) -> Result<Self, String> { |
|
use std::any::TypeId; |
|
|
|
match input.type_id() { |
|
x if x == TypeId::of::<()>() => Ok(TaggedValue::None), |
|
$( x if x == TypeId::of::<$ty>() => Ok(TaggedValue::$identifier(<$ty as Clone>::clone(input.downcast_ref().unwrap()))), )* |
|
x if x == TypeId::of::<RenderOutput>() => Ok(TaggedValue::RenderOutput(RenderOutput::clone(input.downcast_ref().unwrap()))), |
|
x if x == TypeId::of::<SurfaceFrame>() => Ok(TaggedValue::SurfaceFrame(SurfaceFrame::clone(input.downcast_ref().unwrap()))), |
|
_ => Err(format!("Cannot convert {:?} to TaggedValue",std::any::type_name_of_val(input))), |
|
} |
|
} |
|
pub fn from_type(input: &Type) -> Option<Self> { |
|
match input { |
|
Type::Generic(_) => None, |
|
Type::Concrete(concrete_type) => { |
|
let internal_id = concrete_type.id?; |
|
use std::any::TypeId; |
|
|
|
|
|
Some(match internal_id { |
|
x if x == TypeId::of::<()>() => TaggedValue::None, |
|
$( x if x == TypeId::of::<$ty>() => TaggedValue::$identifier(Default::default()), )* |
|
_ => return None, |
|
}) |
|
} |
|
Type::Fn(_, output) => TaggedValue::from_type(output), |
|
Type::Future(output) => { |
|
TaggedValue::from_type(output) |
|
} |
|
} |
|
} |
|
pub fn from_type_or_none(input: &Type) -> Self { |
|
Self::from_type(input).unwrap_or(TaggedValue::None) |
|
} |
|
} |
|
|
|
$( |
|
impl From<$ty> for TaggedValue { |
|
fn from(value: $ty) -> Self { |
|
Self::$identifier(value) |
|
} |
|
} |
|
)* |
|
|
|
$( |
|
impl<'a> TryFrom<&'a TaggedValue> for &'a $ty { |
|
type Error = TaggedValueTypeError; |
|
fn try_from(value: &'a TaggedValue) -> Result<Self, Self::Error> { |
|
match value{ |
|
TaggedValue::$identifier(value) => Ok(value), |
|
_ => Err(TaggedValueTypeError), |
|
} |
|
} |
|
} |
|
)* |
|
}; |
|
} |
|
|
|
tagged_value! { |
|
|
|
|
|
|
|
#[serde(alias = "F32")] |
|
F64(f64), |
|
U32(u32), |
|
U64(u64), |
|
Bool(bool), |
|
String(String), |
|
UVec2(UVec2), |
|
IVec2(IVec2), |
|
DVec2(DVec2), |
|
DAffine2(DAffine2), |
|
OptionalF64(Option<f64>), |
|
OptionalDVec2(Option<DVec2>), |
|
|
|
|
|
|
|
#[serde(alias = "VecF32")] |
|
VecF64(Vec<f64>), |
|
VecU64(Vec<u64>), |
|
VecDVec2(Vec<DVec2>), |
|
F64Array4([f64; 4]), |
|
NodePath(Vec<NodeId>), |
|
#[serde(alias = "ManipulatorGroupIds")] |
|
PointIds(Vec<graphene_core::vector::PointId>), |
|
|
|
|
|
|
|
GraphicElement(graphene_core::GraphicElement), |
|
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::vector::migrate_vector_data"))] |
|
VectorData(graphene_core::vector::VectorDataTable), |
|
#[cfg_attr(target_arch = "wasm32", serde(alias = "ImageFrame", deserialize_with = "graphene_core::raster::image::migrate_image_frame"))] |
|
RasterData(graphene_core::raster_types::RasterDataTable<CPU>), |
|
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::migrate_graphic_group"))] |
|
GraphicGroup(graphene_core::GraphicGroupTable), |
|
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::migrate_artboard_group"))] |
|
ArtboardGroup(graphene_core::ArtboardGroupTable), |
|
|
|
|
|
|
|
Artboard(graphene_core::Artboard), |
|
Image(graphene_core::raster::Image<Color>), |
|
Color(graphene_core::raster::color::Color), |
|
OptionalColor(Option<graphene_core::raster::color::Color>), |
|
Palette(Vec<Color>), |
|
Subpaths(Vec<bezier_rs::Subpath<graphene_core::vector::PointId>>), |
|
Fill(graphene_core::vector::style::Fill), |
|
Stroke(graphene_core::vector::style::Stroke), |
|
Gradient(graphene_core::vector::style::Gradient), |
|
#[serde(alias = "GradientPositions")] |
|
GradientStops(graphene_core::vector::style::GradientStops), |
|
Font(graphene_core::text::Font), |
|
BrushStrokes(Vec<BrushStroke>), |
|
BrushCache(BrushCache), |
|
DocumentNode(DocumentNode), |
|
Curve(graphene_raster_nodes::curve::Curve), |
|
Footprint(graphene_core::transform::Footprint), |
|
VectorModification(Box<graphene_core::vector::VectorModification>), |
|
FontCache(Arc<graphene_core::text::FontCache>), |
|
|
|
|
|
|
|
BlendMode(graphene_core::blending::BlendMode), |
|
LuminanceCalculation(graphene_raster_nodes::adjustments::LuminanceCalculation), |
|
XY(graphene_core::extract_xy::XY), |
|
RedGreenBlue(graphene_raster_nodes::adjustments::RedGreenBlue), |
|
RedGreenBlueAlpha(graphene_raster_nodes::adjustments::RedGreenBlueAlpha), |
|
RealTimeMode(graphene_core::animation::RealTimeMode), |
|
NoiseType(graphene_raster_nodes::adjustments::NoiseType), |
|
FractalType(graphene_raster_nodes::adjustments::FractalType), |
|
CellularDistanceFunction(graphene_raster_nodes::adjustments::CellularDistanceFunction), |
|
CellularReturnType(graphene_raster_nodes::adjustments::CellularReturnType), |
|
DomainWarpType(graphene_raster_nodes::adjustments::DomainWarpType), |
|
RelativeAbsolute(graphene_raster_nodes::adjustments::RelativeAbsolute), |
|
SelectiveColorChoice(graphene_raster_nodes::adjustments::SelectiveColorChoice), |
|
GridType(graphene_core::vector::misc::GridType), |
|
ArcType(graphene_core::vector::misc::ArcType), |
|
MergeByDistanceAlgorithm(graphene_core::vector::misc::MergeByDistanceAlgorithm), |
|
PointSpacingType(graphene_core::vector::misc::PointSpacingType), |
|
#[serde(alias = "LineCap")] |
|
StrokeCap(graphene_core::vector::style::StrokeCap), |
|
#[serde(alias = "LineJoin")] |
|
StrokeJoin(graphene_core::vector::style::StrokeJoin), |
|
StrokeAlign(graphene_core::vector::style::StrokeAlign), |
|
PaintOrder(graphene_core::vector::style::PaintOrder), |
|
FillType(graphene_core::vector::style::FillType), |
|
FillChoice(graphene_core::vector::style::FillChoice), |
|
GradientType(graphene_core::vector::style::GradientType), |
|
ReferencePoint(graphene_core::transform::ReferencePoint), |
|
CentroidType(graphene_core::vector::misc::CentroidType), |
|
BooleanOperation(graphene_path_bool::BooleanOperation), |
|
} |
|
|
|
impl TaggedValue { |
|
pub fn to_primitive_string(&self) -> String { |
|
match self { |
|
TaggedValue::None => "()".to_string(), |
|
TaggedValue::String(x) => format!("\"{x}\""), |
|
TaggedValue::U32(x) => x.to_string() + "_u32", |
|
TaggedValue::U64(x) => x.to_string() + "_u64", |
|
TaggedValue::F64(x) => x.to_string() + "_f64", |
|
TaggedValue::Bool(x) => x.to_string(), |
|
TaggedValue::BlendMode(x) => "BlendMode::".to_string() + &x.to_string(), |
|
TaggedValue::Color(x) => format!("Color {x:?}"), |
|
_ => panic!("Cannot convert to primitive string"), |
|
} |
|
} |
|
|
|
pub fn from_primitive_string(string: &str, ty: &Type) -> Option<Self> { |
|
fn to_dvec2(input: &str) -> Option<DVec2> { |
|
let mut split = input.split(','); |
|
let x = split.next()?.trim().parse().ok()?; |
|
let y = split.next()?.trim().parse().ok()?; |
|
Some(DVec2::new(x, y)) |
|
} |
|
|
|
fn to_color(input: &str) -> Option<Color> { |
|
|
|
if input.starts_with('"') && input.ends_with('"') { |
|
let color = input.trim().trim_matches('"').trim().trim_start_matches('#'); |
|
match color.len() { |
|
6 => return Color::from_rgb_str(color), |
|
8 => return Color::from_rgba_str(color), |
|
_ => { |
|
log::error!("Invalid default value color string: {}", input); |
|
return None; |
|
} |
|
} |
|
} |
|
|
|
|
|
let mut choices = input.split("::"); |
|
let (first, second) = (choices.next()?.trim(), choices.next()?.trim()); |
|
if first == "Color" { |
|
return Some(match second { |
|
"BLACK" => Color::BLACK, |
|
"WHITE" => Color::WHITE, |
|
"RED" => Color::RED, |
|
"GREEN" => Color::GREEN, |
|
"BLUE" => Color::BLUE, |
|
"YELLOW" => Color::YELLOW, |
|
"CYAN" => Color::CYAN, |
|
"MAGENTA" => Color::MAGENTA, |
|
"TRANSPARENT" => Color::TRANSPARENT, |
|
_ => { |
|
log::error!("Invalid default value color constant: {}", input); |
|
return None; |
|
} |
|
}); |
|
} |
|
|
|
log::error!("Invalid default value color: {}", input); |
|
None |
|
} |
|
|
|
fn to_reference_point(input: &str) -> Option<ReferencePoint> { |
|
let mut choices = input.split("::"); |
|
let (first, second) = (choices.next()?.trim(), choices.next()?.trim()); |
|
if first == "ReferencePoint" { |
|
return Some(match second { |
|
"None" => ReferencePoint::None, |
|
"TopLeft" => ReferencePoint::TopLeft, |
|
"TopCenter" => ReferencePoint::TopCenter, |
|
"TopRight" => ReferencePoint::TopRight, |
|
"CenterLeft" => ReferencePoint::CenterLeft, |
|
"Center" => ReferencePoint::Center, |
|
"CenterRight" => ReferencePoint::CenterRight, |
|
"BottomLeft" => ReferencePoint::BottomLeft, |
|
"BottomCenter" => ReferencePoint::BottomCenter, |
|
"BottomRight" => ReferencePoint::BottomRight, |
|
_ => { |
|
log::error!("Invalid ReferencePoint default type variant: {}", input); |
|
return None; |
|
} |
|
}); |
|
} |
|
|
|
log::error!("Invalid ReferencePoint default type: {}", input); |
|
None |
|
} |
|
|
|
match ty { |
|
Type::Generic(_) => None, |
|
Type::Concrete(concrete_type) => { |
|
let internal_id = concrete_type.id?; |
|
use std::any::TypeId; |
|
|
|
|
|
let ty = match internal_id { |
|
x if x == TypeId::of::<()>() => TaggedValue::None, |
|
x if x == TypeId::of::<String>() => TaggedValue::String(string.into()), |
|
x if x == TypeId::of::<f64>() => FromStr::from_str(string).map(TaggedValue::F64).ok()?, |
|
x if x == TypeId::of::<u64>() => FromStr::from_str(string).map(TaggedValue::U64).ok()?, |
|
x if x == TypeId::of::<u32>() => FromStr::from_str(string).map(TaggedValue::U32).ok()?, |
|
x if x == TypeId::of::<DVec2>() => to_dvec2(string).map(TaggedValue::DVec2)?, |
|
x if x == TypeId::of::<bool>() => FromStr::from_str(string).map(TaggedValue::Bool).ok()?, |
|
x if x == TypeId::of::<Color>() => to_color(string).map(TaggedValue::Color)?, |
|
x if x == TypeId::of::<Option<Color>>() => to_color(string).map(|color| TaggedValue::OptionalColor(Some(color)))?, |
|
x if x == TypeId::of::<Fill>() => to_color(string).map(|color| TaggedValue::Fill(Fill::solid(color)))?, |
|
x if x == TypeId::of::<ReferencePoint>() => to_reference_point(string).map(TaggedValue::ReferencePoint)?, |
|
_ => return None, |
|
}; |
|
Some(ty) |
|
} |
|
Type::Fn(_, output) => TaggedValue::from_primitive_string(string, output), |
|
Type::Future(fut) => TaggedValue::from_primitive_string(string, fut), |
|
} |
|
} |
|
|
|
pub fn to_u32(&self) -> u32 { |
|
match self { |
|
TaggedValue::U32(x) => *x, |
|
_ => panic!("Passed value is not of type u32"), |
|
} |
|
} |
|
} |
|
|
|
impl Display for TaggedValue { |
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
match self { |
|
TaggedValue::String(x) => f.write_str(x), |
|
TaggedValue::U32(x) => f.write_fmt(format_args!("{x}")), |
|
TaggedValue::U64(x) => f.write_fmt(format_args!("{x}")), |
|
TaggedValue::F64(x) => f.write_fmt(format_args!("{x}")), |
|
TaggedValue::Bool(x) => f.write_fmt(format_args!("{x}")), |
|
_ => panic!("Cannot convert to string"), |
|
} |
|
} |
|
} |
|
|
|
pub struct UpcastNode { |
|
value: MemoHash<TaggedValue>, |
|
} |
|
impl<'input> Node<'input, DAny<'input>> for UpcastNode { |
|
type Output = FutureAny<'input>; |
|
|
|
fn eval(&'input self, _: DAny<'input>) -> Self::Output { |
|
Box::pin(async move { self.value.clone().into_inner().to_dynany() }) |
|
} |
|
} |
|
impl UpcastNode { |
|
pub fn new(value: MemoHash<TaggedValue>) -> Self { |
|
Self { value } |
|
} |
|
} |
|
#[derive(Default, Debug, Clone, Copy)] |
|
pub struct UpcastAsRefNode<T: AsRef<U> + Sync + Send, U: Sync + Send>(pub T, PhantomData<U>); |
|
|
|
impl<'i, T: 'i + AsRef<U> + Sync + Send, U: 'i + StaticType + Sync + Send> Node<'i, DAny<'i>> for UpcastAsRefNode<T, U> { |
|
type Output = FutureAny<'i>; |
|
#[inline(always)] |
|
fn eval(&'i self, _: DAny<'i>) -> Self::Output { |
|
Box::pin(async move { Box::new(self.0.as_ref()) as DAny<'i> }) |
|
} |
|
} |
|
|
|
impl<T: AsRef<U> + Sync + Send, U: Sync + Send> UpcastAsRefNode<T, U> { |
|
pub const fn new(value: T) -> UpcastAsRefNode<T, U> { |
|
UpcastAsRefNode(value, PhantomData) |
|
} |
|
} |
|
|
|
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)] |
|
pub struct RenderOutput { |
|
pub data: RenderOutputType, |
|
pub metadata: RenderMetadata, |
|
} |
|
|
|
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, Hash, serde::Serialize, serde::Deserialize)] |
|
pub enum RenderOutputType { |
|
CanvasFrame(SurfaceFrame), |
|
Svg(String), |
|
Image(Vec<u8>), |
|
} |
|
|
|
impl Hash for RenderOutput { |
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
|
self.data.hash(state) |
|
} |
|
} |
|
|
|
|
|
trait FakeHash { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H); |
|
} |
|
mod fake_hash { |
|
use super::*; |
|
impl FakeHash for f64 { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
|
self.to_bits().hash(state) |
|
} |
|
} |
|
impl FakeHash for DVec2 { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
|
self.to_array().iter().for_each(|x| x.to_bits().hash(state)) |
|
} |
|
} |
|
impl FakeHash for DAffine2 { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
|
self.to_cols_array().iter().for_each(|x| x.to_bits().hash(state)) |
|
} |
|
} |
|
impl<X: FakeHash> FakeHash for Option<X> { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
|
if let Some(x) = self { |
|
1.hash(state); |
|
x.hash(state); |
|
} else { |
|
0.hash(state); |
|
} |
|
} |
|
} |
|
impl<X: FakeHash> FakeHash for Vec<X> { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
|
self.len().hash(state); |
|
self.iter().for_each(|x| x.hash(state)) |
|
} |
|
} |
|
impl<T: FakeHash, const N: usize> FakeHash for [T; N] { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
|
self.iter().for_each(|x| x.hash(state)) |
|
} |
|
} |
|
impl FakeHash for (f64, Color) { |
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { |
|
self.0.to_bits().hash(state); |
|
self.1.hash(state) |
|
} |
|
} |
|
} |
|
|