mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-08 15:58:12 +08:00
Reach a compiling workspace on the merged tree
This commit is contained in:
@@ -3803,7 +3803,7 @@ impl DocumentMessageHandler {
|
|||||||
/// Create a network interface with a single export
|
/// Create a network interface with a single export
|
||||||
fn default_document_network_interface() -> NodeNetworkInterface {
|
fn default_document_network_interface() -> NodeNetworkInterface {
|
||||||
let mut network_interface = NodeNetworkInterface::default();
|
let mut network_interface = NodeNetworkInterface::default();
|
||||||
network_interface.add_export(TaggedValue::TypeDefault(concrete!(graphene_std::list::List<graphene_std::Artboard>)), -1, "", &[]);
|
network_interface.add_export(TaggedValue::TypeDefault(graph_craft::descriptor!(graphene_std::list::List<graphene_std::Artboard>)), -1, "", &[]);
|
||||||
network_interface
|
network_interface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -252,7 +252,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the bottom input of the artboard back to artboard
|
// Set the bottom input of the artboard back to artboard
|
||||||
let bottom_input = NodeInput::type_default(concrete!(graphene_std::list::List<Artboard>), true);
|
let bottom_input = NodeInput::type_default(graph_craft::descriptor!(graphene_std::list::List<Artboard>), true);
|
||||||
network_interface.set_input(&InputConnector::primary_input(artboard_layer.to_node()), bottom_input, &[]);
|
network_interface.set_input(&InputConnector::primary_input(artboard_layer.to_node()), bottom_input, &[]);
|
||||||
} else {
|
} else {
|
||||||
// We have some non layers (e.g. just a rectangle node). We disconnect the bottom input and connect it to the left input.
|
// We have some non layers (e.g. just a rectangle node). We disconnect the bottom input and connect it to the left input.
|
||||||
@@ -260,7 +260,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
|||||||
network_interface.set_input(&InputConnector::layer_secondary_input(artboard_layer.to_node()), primary_input, &[]);
|
network_interface.set_input(&InputConnector::layer_secondary_input(artboard_layer.to_node()), primary_input, &[]);
|
||||||
|
|
||||||
// Set the bottom input of the artboard back to artboard
|
// Set the bottom input of the artboard back to artboard
|
||||||
let bottom_input = NodeInput::type_default(concrete!(graphene_std::list::List<Artboard>), true);
|
let bottom_input = NodeInput::type_default(graph_craft::descriptor!(graphene_std::list::List<Artboard>), true);
|
||||||
network_interface.set_input(&InputConnector::primary_input(artboard_layer.to_node()), bottom_input, &[]);
|
network_interface.set_input(&InputConnector::primary_input(artboard_layer.to_node()), bottom_input, &[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ impl DocumentNode {
|
|||||||
/// Normalizes this node's stored types (call argument, `Import` input types, `TypeDefault` value payloads, and any nested network) to their structural form.
|
/// Normalizes this node's stored types (call argument, `Import` input types, `TypeDefault` value payloads, and any nested network) to their structural form.
|
||||||
/// Applied once at ingestion (document migration and clipboard paste) so no name-encoded ranked type enters a live document.
|
/// Applied once at ingestion (document migration and clipboard paste) so no name-encoded ranked type enters a live document.
|
||||||
pub fn normalize_stored_types(&mut self) {
|
pub fn normalize_stored_types(&mut self) {
|
||||||
self.call_argument = self.call_argument.clone().normalize_rank();
|
|
||||||
for input in &mut self.inputs {
|
for input in &mut self.inputs {
|
||||||
normalize_input_stored_type(input);
|
normalize_input_stored_type(input);
|
||||||
}
|
}
|
||||||
@@ -268,8 +267,8 @@ impl NodeInput {
|
|||||||
|
|
||||||
/// Constructs a `NodeInput::Value` whose tagged value is `TaggedValue::TypeDefault(ty)`, recording only the
|
/// Constructs a `NodeInput::Value` whose tagged value is `TaggedValue::TypeDefault(ty)`, recording only the
|
||||||
/// type so the runtime materializes its default rather than baking a placeholder value into the saved document.
|
/// type so the runtime materializes its default rather than baking a placeholder value into the saved document.
|
||||||
pub fn type_default(ty: Type, exposed: bool) -> Self {
|
pub fn type_default(td: core_types::TypeDescriptor, exposed: bool) -> Self {
|
||||||
Self::value(TaggedValue::TypeDefault(ty.normalize_rank()), exposed)
|
Self::value(TaggedValue::TypeDefault(td), exposed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn import(import_type: Type, import_index: usize) -> Self {
|
pub const fn import(import_type: Type, import_index: usize) -> Self {
|
||||||
@@ -758,10 +757,10 @@ impl ScopeChain<'_> {
|
|||||||
/// Normalizes the ranked types an input can store: an `Import`'s type or a value's `TypeDefault` payload.
|
/// Normalizes the ranked types an input can store: an `Import`'s type or a value's `TypeDefault` payload.
|
||||||
fn normalize_input_stored_type(input: &mut NodeInput) {
|
fn normalize_input_stored_type(input: &mut NodeInput) {
|
||||||
match input {
|
match input {
|
||||||
NodeInput::Import { import_type, .. } => *import_type = import_type.clone().normalize_rank(),
|
NodeInput::Import { .. } => {}
|
||||||
NodeInput::Value { tagged_value, .. } => {
|
NodeInput::Value { tagged_value, .. } => {
|
||||||
if let TaggedValue::TypeDefault(ty) = &**tagged_value {
|
if let TaggedValue::TypeDefault(ty) = &**tagged_value {
|
||||||
let normalized = ty.clone().normalize_rank();
|
let normalized = ty.clone();
|
||||||
if normalized != *ty {
|
if normalized != *ty {
|
||||||
*tagged_value = TaggedValue::TypeDefault(normalized).into();
|
*tagged_value = TaggedValue::TypeDefault(normalized).into();
|
||||||
}
|
}
|
||||||
@@ -1061,7 +1060,7 @@ impl NodeNetwork {
|
|||||||
let (tagged_value, exposed) = match previous_export {
|
let (tagged_value, exposed) = match previous_export {
|
||||||
NodeInput::Value { tagged_value, exposed } => (tagged_value, exposed),
|
NodeInput::Value { tagged_value, exposed } => (tagged_value, exposed),
|
||||||
NodeInput::Reflection(reflect) => match reflect {
|
NodeInput::Reflection(reflect) => match reflect {
|
||||||
DocumentNodeMetadata::DocumentNodePath => (TaggedValue::NodeIdPath(path.to_vec()).into(), false),
|
DocumentNodeMetadata::DocumentNodePath => (TaggedValue::NodeIdPath(core_types::list::NodeIdPath::from(path.to_vec())).into(), false),
|
||||||
DocumentNodeMetadata::SourceId => {
|
DocumentNodeMetadata::SourceId => {
|
||||||
let source_id = Self::source_id_for_path(path);
|
let source_id = Self::source_id_for_path(path);
|
||||||
if let Some(context_features) = context_features.as_deref_mut() {
|
if let Some(context_features) = context_features.as_deref_mut() {
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ use super::DocumentNode;
|
|||||||
use crate::application_io::PlatformEditorApi;
|
use crate::application_io::PlatformEditorApi;
|
||||||
use crate::application_io::resource::Resource;
|
use crate::application_io::resource::Resource;
|
||||||
use crate::proto::Any as DAny;
|
use crate::proto::Any as DAny;
|
||||||
use brush_nodes::brush_stroke::Stroke;
|
use brush_nodes::{BrushCache, Stroke};
|
||||||
use core_types::color::SRGBA8;
|
use core_types::color::SRGBA8;
|
||||||
use core_types::context::Context;
|
use core_types::context::Context;
|
||||||
use core_types::gpoll::GPoll;
|
use core_types::gpoll::GPoll;
|
||||||
use core_types::list::List;
|
use core_types::list::{Item, List, NodeIdPath};
|
||||||
use core_types::registry::SourceHandle;
|
use core_types::registry::SourceHandle;
|
||||||
use core_types::transform::Footprint;
|
use core_types::transform::Footprint;
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
@@ -84,7 +84,7 @@ macro_rules! tagged_value {
|
|||||||
/// Stores a type, from which its `Default::default()` value can be obtained, rather than storing an actual type's value.
|
/// Stores a type, from which its `Default::default()` value can be obtained, rather than storing an actual type's value.
|
||||||
/// Example: `TaggedValue::TypeDefault(concrete!(String))` stores the type `String` but no specific string value.
|
/// Example: `TaggedValue::TypeDefault(concrete!(String))` stores the type `String` but no specific string value.
|
||||||
/// (Old documents stored a bare `TypeDescriptor` payload, routed to this shape by `deserialize_tagged_value_with_legacy_migration`.)
|
/// (Old documents stored a bare `TypeDescriptor` payload, routed to this shape by `deserialize_tagged_value_with_legacy_migration`.)
|
||||||
TypeDefault(Type),
|
TypeDefault(TypeDescriptor),
|
||||||
/// Stored compactly as a `Vec<f64>`, materializes as `List<f64>` at runtime via `to_dynany`/`to_any`. Aliases recover legacy on-disk shapes.
|
/// Stored compactly as a `Vec<f64>`, materializes as `List<f64>` at runtime via `to_dynany`/`to_any`. Aliases recover legacy on-disk shapes.
|
||||||
#[serde(deserialize_with = "core_types::misc::migrate_to_f64_array")] // TODO: Eventually remove this document upgrade code
|
#[serde(deserialize_with = "core_types::misc::migrate_to_f64_array")] // TODO: Eventually remove this document upgrade code
|
||||||
#[serde(alias = "F64Table", alias = "VecF64", alias = "VecF32", alias = "F64Array4")]
|
#[serde(alias = "F64Table", alias = "VecF64", alias = "VecF32", alias = "F64Array4")]
|
||||||
@@ -174,7 +174,7 @@ macro_rules! tagged_value {
|
|||||||
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Box::new(<$type_default>::default()); }
|
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Box::new(<$type_default>::default()); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Self::from_type_or_none(&td).to_dynany()
|
Self::from_type_or_none(&Type::Concrete(td.clone())).to_dynany()
|
||||||
}
|
}
|
||||||
Self::F64Array(values) => {
|
Self::F64Array(values) => {
|
||||||
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
||||||
@@ -219,7 +219,7 @@ macro_rules! tagged_value {
|
|||||||
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Arc::new(<$type_default>::default()); }
|
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Arc::new(<$type_default>::default()); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Self::from_type_or_none(&td).to_any()
|
Self::from_type_or_none(&Type::Concrete(td.clone())).to_any()
|
||||||
}
|
}
|
||||||
Self::F64Array(values) => {
|
Self::F64Array(values) => {
|
||||||
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
let list: List<f64> = values.into_iter().map(core_types::list::Item::new_from_element).collect();
|
||||||
@@ -245,13 +245,13 @@ macro_rules! tagged_value {
|
|||||||
Self::DocumentNode(node) => Arc::new(node),
|
Self::DocumentNode(node) => Arc::new(node),
|
||||||
Self::ContextModification(modification) => Arc::new(modification),
|
Self::ContextModification(modification) => Arc::new(modification),
|
||||||
Self::EditorApi(x) => Arc::new(x),
|
Self::EditorApi(x) => Arc::new(x),
|
||||||
Self::ResourceHash(x) => Arc::new(Item::new_from_element(x)),
|
Self::ResourceHash(x) => Arc::new(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the wire [`Type`] of the value inside the tagged value, with ranked types in their structural form.
|
/// Creates the wire [`Type`] of the value inside the tagged value, with ranked types in their structural form.
|
||||||
pub fn ty(&self) -> Type {
|
pub fn ty(&self) -> Type {
|
||||||
let ty = match self {
|
match self {
|
||||||
// ===============
|
// ===============
|
||||||
// MANUAL VARIANTS
|
// MANUAL VARIANTS
|
||||||
// ===============
|
// ===============
|
||||||
@@ -271,6 +271,9 @@ macro_rules! tagged_value {
|
|||||||
Self::Color(_) => concrete!(Color),
|
Self::Color(_) => concrete!(Color),
|
||||||
Self::GradientRamp(_) => concrete!(Gradient),
|
Self::GradientRamp(_) => concrete!(Gradient),
|
||||||
Self::Strokes(_) => concrete!(Stroke),
|
Self::Strokes(_) => concrete!(Stroke),
|
||||||
|
Self::DashPattern(_) => concrete!(DashPattern),
|
||||||
|
Self::BoxCorners(_) => concrete!(BoxCorners),
|
||||||
|
Self::BrushCache(_) => concrete!(BrushCache),
|
||||||
// =======================
|
// =======================
|
||||||
// AUTO-GENERATED VARIANTS
|
// AUTO-GENERATED VARIANTS
|
||||||
// =======================
|
// =======================
|
||||||
@@ -279,7 +282,7 @@ macro_rules! tagged_value {
|
|||||||
// NON-SERIALIZED VARIANTS
|
// NON-SERIALIZED VARIANTS
|
||||||
// =======================
|
// =======================
|
||||||
Self::RenderOutput(_) => concrete!(RenderOutput),
|
Self::RenderOutput(_) => concrete!(RenderOutput),
|
||||||
Self::NodeIdPath(_) => concrete!(Vec<NodeId>),
|
Self::NodeIdPath(_) => concrete!(core_types::list::NodeIdPath),
|
||||||
Self::DocumentNode(_) => concrete!(DocumentNode),
|
Self::DocumentNode(_) => concrete!(DocumentNode),
|
||||||
Self::ContextModification(_) => concrete!(ContextModification),
|
Self::ContextModification(_) => concrete!(ContextModification),
|
||||||
Self::EditorApi(_) => concrete!(Arc<PlatformEditorApi>),
|
Self::EditorApi(_) => concrete!(Arc<PlatformEditorApi>),
|
||||||
@@ -320,9 +323,12 @@ macro_rules! tagged_value {
|
|||||||
Self::Color(_) => leveled::<Color>(),
|
Self::Color(_) => leveled::<Color>(),
|
||||||
Self::GradientRamp(_) => leveled::<Gradient>(),
|
Self::GradientRamp(_) => leveled::<Gradient>(),
|
||||||
Self::Strokes(_) => leveled::<Stroke>(),
|
Self::Strokes(_) => leveled::<Stroke>(),
|
||||||
|
Self::DashPattern(_) => scalar::<DashPattern>(),
|
||||||
|
Self::BoxCorners(_) => scalar::<BoxCorners>(),
|
||||||
|
Self::BrushCache(_) => scalar::<BrushCache>(),
|
||||||
$( Self::$identifier(_) => scalar::<$ty>(), )*
|
$( Self::$identifier(_) => scalar::<$ty>(), )*
|
||||||
Self::RenderOutput(_) => scalar::<RenderOutput>(),
|
Self::RenderOutput(_) => scalar::<RenderOutput>(),
|
||||||
Self::NodeIdPath(_) => scalar::<Vec<NodeId>>(),
|
Self::NodeIdPath(_) => scalar::<core_types::list::NodeIdPath>(),
|
||||||
Self::DocumentNode(_) => scalar::<DocumentNode>(),
|
Self::DocumentNode(_) => scalar::<DocumentNode>(),
|
||||||
Self::ContextModification(_) => scalar::<ContextModification>(),
|
Self::ContextModification(_) => scalar::<ContextModification>(),
|
||||||
Self::EditorApi(_) => scalar::<Arc<PlatformEditorApi>>(),
|
Self::EditorApi(_) => scalar::<Arc<PlatformEditorApi>>(),
|
||||||
@@ -363,6 +369,9 @@ macro_rules! tagged_value {
|
|||||||
Self::Color(color) => Ok(record_value_source(color)),
|
Self::Color(color) => Ok(record_value_source(color)),
|
||||||
Self::GradientRamp(stops) => Ok(leveled_record_value_source(vec![stops])),
|
Self::GradientRamp(stops) => Ok(leveled_record_value_source(vec![stops])),
|
||||||
Self::Strokes(strokes) => Ok(leveled_record_value_source(strokes)),
|
Self::Strokes(strokes) => Ok(leveled_record_value_source(strokes)),
|
||||||
|
Self::DashPattern(lengths) => Ok(record_value_source(DashPattern(lengths.into_iter().map(core_types::list::Item::new_from_element).collect()))),
|
||||||
|
Self::BoxCorners(values) => Ok(record_value_source(BoxCorners(values.into_iter().map(core_types::list::Item::new_from_element).collect()))),
|
||||||
|
Self::BrushCache(cache) => Ok(record_value_source(cache)),
|
||||||
// =======================
|
// =======================
|
||||||
// AUTO-GENERATED VARIANTS
|
// AUTO-GENERATED VARIANTS
|
||||||
// =======================
|
// =======================
|
||||||
@@ -487,15 +496,15 @@ macro_rules! tagged_value {
|
|||||||
if name == core_types::normalize_type_name(std::any::type_name::<()>()) { return Some(TaggedValue::None) }
|
if name == core_types::normalize_type_name(std::any::type_name::<()>()) { return Some(TaggedValue::None) }
|
||||||
// List-wrapped types need a single-item default with the element's default, not an empty list
|
// List-wrapped types need a single-item default with the element's default, not an empty list
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<List<Color>>()) { return Some(TaggedValue::Color(Color::default())) }
|
if name == core_types::normalize_type_name(std::any::type_name::<List<Color>>()) { return Some(TaggedValue::Color(Color::default())) }
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<List<Gradient>>()) { return Some(TaggedValue::Gradient(Gradient::default())) }
|
if name == core_types::normalize_type_name(std::any::type_name::<List<Gradient>>()) { return Some(TaggedValue::GradientRamp(GradientRamp::default())) }
|
||||||
$( if name == core_types::normalize_type_name(std::any::type_name::<$ty>()) { return Some(TaggedValue::$identifier(Default::default())) } )*
|
$( if name == core_types::normalize_type_name(std::any::type_name::<$ty>()) { return Some(TaggedValue::$identifier(Default::default())) } )*
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<List<f64>>()) { return Some(TaggedValue::F64Array(Vec::new())) }
|
if name == core_types::normalize_type_name(std::any::type_name::<List<f64>>()) { return Some(TaggedValue::F64Array(Vec::new())) }
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<List<Stroke>>()) { return Some(TaggedValue::BrushStrokes(Vec::new())) }
|
if name == core_types::normalize_type_name(std::any::type_name::<List<Stroke>>()) { return Some(TaggedValue::Strokes(Vec::new())) }
|
||||||
// Leveled inputs type by their element; each element name maps to the
|
// Leveled inputs type by their element; each element name maps to the
|
||||||
// same tagged default as its legacy list form.
|
// same tagged default as its legacy list form.
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<Color>()) { return Some(TaggedValue::Color(Color::default())) }
|
if name == core_types::normalize_type_name(std::any::type_name::<Color>()) { return Some(TaggedValue::Color(Color::default())) }
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<Gradient>()) { return Some(TaggedValue::Gradient(Gradient::default())) }
|
if name == core_types::normalize_type_name(std::any::type_name::<Gradient>()) { return Some(TaggedValue::GradientRamp(GradientRamp::default())) }
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<Stroke>()) { return Some(TaggedValue::BrushStrokes(Vec::new())) }
|
if name == core_types::normalize_type_name(std::any::type_name::<Stroke>()) { return Some(TaggedValue::Strokes(Vec::new())) }
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<Graphic>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Graphic>))) }
|
if name == core_types::normalize_type_name(std::any::type_name::<Graphic>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Graphic>))) }
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<Artboard>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Artboard>))) }
|
if name == core_types::normalize_type_name(std::any::type_name::<Artboard>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Artboard>))) }
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<Raster<CPU>>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Raster<CPU>>))) }
|
if name == core_types::normalize_type_name(std::any::type_name::<Raster<CPU>>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!(List<Raster<CPU>>))) }
|
||||||
@@ -503,10 +512,10 @@ macro_rules! tagged_value {
|
|||||||
// Types whose `TaggedValue` variant has been removed. They route through `TypeDefault` instead, with `to_dynany`/`to_any` constructing the actual default at execution time.
|
// Types whose `TaggedValue` variant has been removed. They route through `TypeDefault` instead, with `to_dynany`/`to_any` constructing the actual default at execution time.
|
||||||
macro_rules! check {
|
macro_rules! check {
|
||||||
($type_default:ty) => {
|
($type_default:ty) => {
|
||||||
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Some(TaggedValue::TypeDefault(concrete_type.clone())); }
|
if name == core_types::normalize_type_name(std::any::type_name::<$type_default>()) { return Some(TaggedValue::TypeDefault(core_types::descriptor!($type_default))); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
for_each_bare_type_default!(check_bare);
|
for_each_bare_type_default!(check);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Type::Fn(_, output) => TaggedValue::from_type(output),
|
Type::Fn(_, output) => TaggedValue::from_type(output),
|
||||||
@@ -522,7 +531,7 @@ macro_rules! tagged_value {
|
|||||||
}
|
}
|
||||||
macro_rules! check {
|
macro_rules! check {
|
||||||
($type_default:ty) => {
|
($type_default:ty) => {
|
||||||
if **element == concrete!($type_default) { return Some(TaggedValue::TypeDefault(input.clone())); }
|
if **element == concrete!($type_default) { return Some(TaggedValue::TypeDefault(core_types::descriptor!($type_default))); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
for_each_item_type_default!(check);
|
for_each_item_type_default!(check);
|
||||||
@@ -807,12 +816,12 @@ impl TaggedValue {
|
|||||||
|
|
||||||
/// The stored form of a paint input's red-slash "no paint" choice: the `Item<Graphic>` type default, materializing as a `Graphic::None` paint.
|
/// The stored form of a paint input's red-slash "no paint" choice: the `Item<Graphic>` type default, materializing as a `Graphic::None` paint.
|
||||||
pub fn no_paint() -> Self {
|
pub fn no_paint() -> Self {
|
||||||
TaggedValue::TypeDefault(concrete!(Graphic))
|
TaggedValue::TypeDefault(core_types::descriptor!(Graphic))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this is the `Item<Graphic>` type default created by [`Self::no_paint`] (and by disconnecting a paint wire).
|
/// Whether this is the `Item<Graphic>` type default created by [`Self::no_paint`] (and by disconnecting a paint wire).
|
||||||
pub fn is_no_paint(&self) -> bool {
|
pub fn is_no_paint(&self) -> bool {
|
||||||
matches!(self, TaggedValue::TypeDefault(td) if *td == concrete!(Graphic))
|
matches!(self, TaggedValue::TypeDefault(td) if *td == core_types::descriptor!(Graphic))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,14 +829,14 @@ impl TaggedValue {
|
|||||||
///
|
///
|
||||||
/// Routes legacy variant names into modern variants, in typed Rust. Each legacy name is also matched against the historical `#[serde(alias = "...")]` spellings the deleted variant accepted, so old-shape inner payloads are caught:
|
/// Routes legacy variant names into modern variants, in typed Rust. Each legacy name is also matched against the historical `#[serde(alias = "...")]` spellings the deleted variant accepted, so old-shape inner payloads are caught:
|
||||||
///
|
///
|
||||||
/// - `Graphic` (or alias `GraphicGroup`/`Group`) → `TaggedValue::TypeDefault(concrete!(List<Graphic>))`
|
/// - `Graphic` (or alias `GraphicGroup`/`Group`) → `TaggedValue::TypeDefault(core_types::descriptor!(List<Graphic>))`
|
||||||
/// - `Artboard` (or alias `ArtboardGroup`) → `TaggedValue::TypeDefault(concrete!(List<Artboard>))`
|
/// - `Artboard` (or alias `ArtboardGroup`) → `TaggedValue::TypeDefault(core_types::descriptor!(List<Artboard>))`
|
||||||
/// - `Raster` (or alias `ImageFrame`/`RasterData`/`Image`):
|
/// - `Raster` (or alias `ImageFrame`/`RasterData`/`Image`):
|
||||||
/// - non-empty (the legacy `image` proto's input 1, where the inner `Raster<CPU>` serializes as the embedded `Image<Color>`) → `TaggedValue::ImageData(<inner Image<Color>>)`
|
/// - non-empty (the legacy `image` proto's input 1, where the inner `Raster<CPU>` serializes as the embedded `Image<Color>`) → `TaggedValue::ImageData(<inner Image<Color>>)`
|
||||||
/// - empty → `TaggedValue::TypeDefault(concrete!(List<Raster<CPU>>))`
|
/// - empty → `TaggedValue::TypeDefault(core_types::descriptor!(List<Raster<CPU>>))`
|
||||||
/// - `Vector` (or alias `VectorData`):
|
/// - `Vector` (or alias `VectorData`):
|
||||||
/// - non-empty → `TaggedValue::VectorModification(<built from first element>)` (the document_migration's Path pass disambiguates this between SVG-import legacy and a discardable modern baked value via the input's `exposed` flag)
|
/// - non-empty → `TaggedValue::VectorModification(<built from first element>)` (the document_migration's Path pass disambiguates this between SVG-import legacy and a discardable modern baked value via the input's `exposed` flag)
|
||||||
/// - empty → `TaggedValue::TypeDefault(concrete!(List<Vector>))`
|
/// - empty → `TaggedValue::TypeDefault(core_types::descriptor!(List<Vector>))`
|
||||||
/// - `FillChoice` → `TaggedValue::Color` (solid), `TaggedValue::GradientRamp` (gradient), or `TaggedValue::no_paint()` (none)
|
/// - `FillChoice` → `TaggedValue::Color` (solid), `TaggedValue::GradientRamp` (gradient), or `TaggedValue::no_paint()` (none)
|
||||||
/// - `Gradient` (or alias `GradientTable`/`GradientPositions`/`Gradient`) → `TaggedValue::LegacyGradient` (ancient full struct) or `TaggedValue::GradientRamp` (ramp and legacy stops shapes, unwrapped from the legacy table form)
|
/// - `Gradient` (or alias `GradientTable`/`GradientPositions`/`Gradient`) → `TaggedValue::LegacyGradient` (ancient full struct) or `TaggedValue::GradientRamp` (ramp and legacy stops shapes, unwrapped from the legacy table form)
|
||||||
/// - `TypeDefault` with the old bare-`TypeDescriptor` payload → the same variant wrapping a `Type` (name-encoded `List` normalized to structural)
|
/// - `TypeDefault` with the old bare-`TypeDescriptor` payload → the same variant wrapping a `Type` (name-encoded `List` normalized to structural)
|
||||||
@@ -844,8 +853,8 @@ pub fn deserialize_tagged_value_with_legacy_migration<'de, D: serde::Deserialize
|
|||||||
&& let Some((tag, content)) = map.iter().next()
|
&& let Some((tag, content)) = map.iter().next()
|
||||||
{
|
{
|
||||||
match tag.as_str() {
|
match tag.as_str() {
|
||||||
"Graphic" | "GraphicGroup" | "Group" => return Ok(MemoHash::new(TaggedValue::TypeDefault(concrete!(List<Graphic>)))),
|
"Graphic" | "GraphicGroup" | "Group" => return Ok(MemoHash::new(TaggedValue::TypeDefault(core_types::descriptor!(List<Graphic>)))),
|
||||||
"Artboard" | "ArtboardGroup" => return Ok(MemoHash::new(TaggedValue::TypeDefault(concrete!(List<Artboard>)))),
|
"Artboard" | "ArtboardGroup" => return Ok(MemoHash::new(TaggedValue::TypeDefault(core_types::descriptor!(List<Artboard>)))),
|
||||||
"Raster" | "ImageFrame" | "RasterData" | "Image" => {
|
"Raster" | "ImageFrame" | "RasterData" | "Image" => {
|
||||||
let first_element = content
|
let first_element = content
|
||||||
.as_object()
|
.as_object()
|
||||||
@@ -856,7 +865,7 @@ pub fn deserialize_tagged_value_with_legacy_migration<'de, D: serde::Deserialize
|
|||||||
let image: Image<Color> = serde_json::from_value(image_value.clone()).map_err(serde::de::Error::custom)?;
|
let image: Image<Color> = serde_json::from_value(image_value.clone()).map_err(serde::de::Error::custom)?;
|
||||||
return Ok(MemoHash::new(TaggedValue::ImageData(image)));
|
return Ok(MemoHash::new(TaggedValue::ImageData(image)));
|
||||||
}
|
}
|
||||||
return Ok(MemoHash::new(TaggedValue::TypeDefault(concrete!(List<Raster<CPU>>))));
|
return Ok(MemoHash::new(TaggedValue::TypeDefault(core_types::descriptor!(List<Raster<CPU>>))));
|
||||||
}
|
}
|
||||||
"Vector" | "VectorData" => {
|
"Vector" | "VectorData" => {
|
||||||
let vector = graphic_types::migrations::migrate_to_optional_vector(content.clone()).map_err(serde::de::Error::custom)?;
|
let vector = graphic_types::migrations::migrate_to_optional_vector(content.clone()).map_err(serde::de::Error::custom)?;
|
||||||
@@ -864,12 +873,12 @@ pub fn deserialize_tagged_value_with_legacy_migration<'de, D: serde::Deserialize
|
|||||||
let modification = Box::new(VectorModification::create_from_vector(&vector));
|
let modification = Box::new(VectorModification::create_from_vector(&vector));
|
||||||
return Ok(MemoHash::new(TaggedValue::VectorModification(modification)));
|
return Ok(MemoHash::new(TaggedValue::VectorModification(modification)));
|
||||||
}
|
}
|
||||||
return Ok(MemoHash::new(TaggedValue::TypeDefault(concrete!(List<Vector>))));
|
return Ok(MemoHash::new(TaggedValue::TypeDefault(core_types::descriptor!(List<Vector>))));
|
||||||
}
|
}
|
||||||
// The `TypeDefault` payload used to be a bare `TypeDescriptor`; it now carries a `Type`
|
// The `TypeDefault` payload is a bare `TypeDescriptor`: our one wire kind needs no structural rank in it
|
||||||
"TypeDefault" if content.as_object().is_some_and(|c| c.contains_key("name")) => {
|
"TypeDefault" if content.as_object().is_some_and(|c| c.contains_key("name")) => {
|
||||||
let descriptor: TypeDescriptor = serde_json::from_value(content.clone()).map_err(serde::de::Error::custom)?;
|
let descriptor: TypeDescriptor = serde_json::from_value(content.clone()).map_err(serde::de::Error::custom)?;
|
||||||
return Ok(MemoHash::new(TaggedValue::TypeDefault(Type::Concrete(descriptor).normalize_rank())));
|
return Ok(MemoHash::new(TaggedValue::TypeDefault(descriptor)));
|
||||||
}
|
}
|
||||||
// The `Color` tag used to carry `Option<Color>`, where a `null` payload (or an empty legacy color table) was the red-slash "no paint" choice
|
// The `Color` tag used to carry `Option<Color>`, where a `null` payload (or an empty legacy color table) was the red-slash "no paint" choice
|
||||||
"Color" | "ColorTable" | "OptionalColor" | "ColorNotInTable"
|
"Color" | "ColorTable" | "OptionalColor" | "ColorNotInTable"
|
||||||
@@ -1227,7 +1236,7 @@ mod leveled_edges {
|
|||||||
TaggedValue::F64Array(vec![1.]),
|
TaggedValue::F64Array(vec![1.]),
|
||||||
TaggedValue::Bool(true),
|
TaggedValue::Bool(true),
|
||||||
TaggedValue::TypeDefault(descriptor!(List<Vector>)),
|
TaggedValue::TypeDefault(descriptor!(List<Vector>)),
|
||||||
TaggedValue::Gradient(Default::default()),
|
TaggedValue::GradientRamp(Default::default()),
|
||||||
] {
|
] {
|
||||||
let layout = value.value_layout().unwrap();
|
let layout = value.value_layout().unwrap();
|
||||||
let edge = value.to_edge().unwrap();
|
let edge = value.to_edge().unwrap();
|
||||||
|
|||||||
@@ -901,10 +901,6 @@ impl TypingContext {
|
|||||||
self.promotions.get(&node_id)
|
self.promotions.get(&node_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Looks up the sole constructor registered under an adapter identifier, such as an Item -> List promotion adapter.
|
|
||||||
pub fn adapter_constructor(&self, identifier: &ProtoNodeIdentifier) -> Option<NodeConstructor> {
|
|
||||||
self.lookup.get(identifier).and_then(|implementations| implementations.values().next().copied())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the node constructor for a given node id.
|
/// Returns the node constructor for a given node id.
|
||||||
pub fn constructor(&self, node_id: NodeId) -> Option<NodeConstructor> {
|
pub fn constructor(&self, node_id: NodeId) -> Option<NodeConstructor> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::list::{Attribute, AttributeDyn, AttributeValueDyn, Item, List, ListDyn};
|
use crate::list::{Attribute, AttributeDyn, AttributeValueDyn, Item, List, ListDyn};
|
||||||
use crate::math::float_noise::round_away_float_noise;
|
use crate::math::float_noise::round_away_float_noise;
|
||||||
use crate::transform::Footprint;
|
use crate::transform::Footprint;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use graphene_hash::CacheHash;
|
use graphene_hash::CacheHash;
|
||||||
|
|
||||||
/// The [`Convert`] trait allows for conversion between Rust primitive numeric types.
|
/// The [`Convert`] trait allows for conversion between Rust primitive numeric types.
|
||||||
@@ -31,7 +31,7 @@ macro_rules! impl_convert_to_string {
|
|||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
impl_convert_to_string!(f32, u32, u64, i32, i64, bool, DVec2, DAffine2);
|
impl_convert_to_string!(f32, u32, u64, i32, i64, bool, DVec2, DAffine2, String, IVec2, i8, u8, u16, i16, u128, i128, usize, isize);
|
||||||
|
|
||||||
// Denoised so 0.1 + 0.2 reaches the string as "0.3" rather than "0.30000000000000004"
|
// Denoised so 0.1 + 0.2 reaches the string as "0.3" rather than "0.30000000000000004"
|
||||||
impl Convert<String, ()> for f64 {
|
impl Convert<String, ()> for f64 {
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ impl core_types::transform::BakeTransform for Vector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Identity item conversion so `List<Vector>` satisfies the blanket `Convert<List<U>, ()> for List<T>`, letting its
|
||||||
|
// auto-inserted input wrapper be a `ConvertNode` (which also accepts a `DVec2` anchor position) rather than an `IntoNode`.
|
||||||
|
impl core_types::ops::ListConvert<Vector> for Vector {
|
||||||
|
fn convert_item(self) -> Vector {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Vector {
|
impl Vector {
|
||||||
/// Add a path of manipulator groups to this vector path.
|
/// Add a path of manipulator groups to this vector path.
|
||||||
pub fn append_manipulator_groups(&mut self, manipulator_groups: &[ManipulatorGroup], closed: bool, preserve_id: bool) {
|
pub fn append_manipulator_groups(&mut self, manipulator_groups: &[ManipulatorGroup], closed: bool, preserve_id: bool) {
|
||||||
|
|||||||
@@ -83,9 +83,13 @@ fn flip_entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, regular_field
|
|||||||
let mentioned = candidate_params.iter().zip(&kept).any(|(param, kept)| {
|
let mentioned = candidate_params.iter().zip(&kept).any(|(param, kept)| {
|
||||||
*kept
|
*kept
|
||||||
&& match param {
|
&& match param {
|
||||||
GenericParam::Type(type_param) => type_param.bounds.iter().any(|bound| {
|
// Only trait bounds can mention another parameter; a lifetime bound would build an invalid `dyn 'a`.
|
||||||
let bound: Type = syn::parse_quote!(dyn #bound);
|
GenericParam::Type(type_param) => type_param.bounds.iter().any(|bound| match bound {
|
||||||
type_contains_ident(&bound, &ident)
|
syn::TypeParamBound::Trait(trait_bound) => {
|
||||||
|
let bound: Type = syn::parse_quote!(dyn #trait_bound);
|
||||||
|
type_contains_ident(&bound, &ident)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
}),
|
}),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ impl PerPixelAdjustCodegen<'_> {
|
|||||||
let entry_point_name = &self.entry_point_name;
|
let entry_point_name = &self.entry_point_name;
|
||||||
let body = quote! {
|
let body = quote! {
|
||||||
{
|
{
|
||||||
#executor.shader_runtime().run_per_pixel_adjust(&::wgpu_executor::shader_runtime::per_pixel_adjust_runtime::Shaders {
|
#executor.run_per_pixel_adjust(&::wgpu_executor::shader_runtime::per_pixel_adjust_runtime::Shaders {
|
||||||
wgsl_shader: crate::WGSL_SHADER,
|
wgsl_shader: crate::WGSL_SHADER,
|
||||||
fragment_shader_name: super::#entry_point_name,
|
fragment_shader_name: super::#entry_point_name,
|
||||||
has_uniform: #has_uniform,
|
has_uniform: #has_uniform,
|
||||||
|
|||||||
@@ -12,16 +12,16 @@ use core_types::{ATTR_TRANSFORM, Ctx, ExtractFootprint};
|
|||||||
use graphic_types::Graphic;
|
use graphic_types::Graphic;
|
||||||
use pipeline::{BasicBrushPipeline, BasicBrushPipelineArgs};
|
use pipeline::{BasicBrushPipeline, BasicBrushPipelineArgs};
|
||||||
use raster_types::{GPU, Raster};
|
use raster_types::{GPU, Raster};
|
||||||
use wgpu_executor::{WgpuExecutor, WgpuPipelineCache};
|
use core_types::ProtoNodeIdentifier;
|
||||||
|
use wgpu_executor::{WgpuExecutorHandle, WgpuPipelineCache};
|
||||||
|
|
||||||
#[node_macro::node(category("Raster: Brush"))]
|
#[node_macro::node(category("Raster: Brush"))]
|
||||||
pub async fn basic_brush<'a: 'n>(
|
pub fn basic_brush(
|
||||||
ctx: impl Ctx + ExtractFootprint,
|
ctx: impl Ctx + ExtractFootprint,
|
||||||
strokes: List<Graphic>,
|
strokes: List<Graphic<'static>>,
|
||||||
#[widget(ParsedWidgetOverride::Hidden)] cache: Item<BrushCache>,
|
#[widget(ParsedWidgetOverride::Hidden)] cache: BrushCache,
|
||||||
#[scope(basic_brush_pipeline::IDENTIFIER)] pipeline: Item<WgpuPipelineCache>,
|
#[scope(basic_brush_pipeline::IDENTIFIER)] pipeline: WgpuPipelineCache,
|
||||||
) -> List<Raster<GPU>> {
|
) -> List<Raster<GPU>> {
|
||||||
let (cache, pipeline) = (cache.into_element(), pipeline.into_element());
|
|
||||||
let mut stack = vec![strokes.into_iter()];
|
let mut stack = vec![strokes.into_iter()];
|
||||||
let mut strokes = Vec::new();
|
let mut strokes = Vec::new();
|
||||||
while let Some(top) = stack.last_mut() {
|
while let Some(top) = stack.last_mut() {
|
||||||
@@ -56,7 +56,7 @@ pub async fn basic_brush<'a: 'n>(
|
|||||||
strokes: &strokes,
|
strokes: &strokes,
|
||||||
cache: &cache,
|
cache: &cache,
|
||||||
};
|
};
|
||||||
let Some((texture, transform)) = pipeline.run::<BasicBrushPipeline>(&args).await else {
|
let Some((texture, transform)) = pipeline.run::<BasicBrushPipeline>(&args) else {
|
||||||
return List::new();
|
return List::new();
|
||||||
};
|
};
|
||||||
let raster = Raster::<GPU>::new_gpu(texture);
|
let raster = Raster::<GPU>::new_gpu(texture);
|
||||||
@@ -64,11 +64,11 @@ pub async fn basic_brush<'a: 'n>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category(""), inject_scope)]
|
#[node_macro::node(category(""), inject_scope)]
|
||||||
async fn basic_brush_pipeline<'a: 'n>(
|
fn basic_brush_pipeline(
|
||||||
_ctx: impl Ctx,
|
_ctx: impl Ctx,
|
||||||
#[scope(ProtoNodeIdentifier::new("graphene_std::platform_application_io::WgpuExecutorNode"))] executor: Item<&'a WgpuExecutor>,
|
#[scope(ProtoNodeIdentifier::new("graphene_std::platform_application_io::WgpuExecutorNode"))] executor: WgpuExecutorHandle,
|
||||||
#[data] pipeline: WgpuPipelineCache,
|
#[data] pipeline: WgpuPipelineCache,
|
||||||
) -> Item<WgpuPipelineCache> {
|
) -> WgpuPipelineCache {
|
||||||
executor.into_element().pipeline_init::<BasicBrushPipeline>(pipeline);
|
executor.pipeline_init::<BasicBrushPipeline>(pipeline);
|
||||||
Item::new_from_element(pipeline.clone())
|
pipeline.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use core_types::Color;
|
|||||||
use core_types::transform::Footprint;
|
use core_types::transform::Footprint;
|
||||||
use glam::{DAffine2, UVec2};
|
use glam::{DAffine2, UVec2};
|
||||||
use raster_types::Texture;
|
use raster_types::Texture;
|
||||||
use wgpu_executor::{AsyncWgpuPipeline, Buffer, WgpuExecutor};
|
use wgpu_executor::{Buffer, WgpuExecutor, WgpuPipeline};
|
||||||
|
|
||||||
pub(super) const DENSITY_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R16Float;
|
pub(super) const DENSITY_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R16Float;
|
||||||
pub(super) const COMPOSITE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float;
|
pub(super) const COMPOSITE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float;
|
||||||
@@ -71,7 +71,7 @@ pub struct BasicBrushPipelineArgs<'a> {
|
|||||||
pub(super) cache: &'a BrushCache,
|
pub(super) cache: &'a BrushCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncWgpuPipeline for BasicBrushPipeline {
|
impl WgpuPipeline for BasicBrushPipeline {
|
||||||
type Args<'a> = BasicBrushPipelineArgs<'a>;
|
type Args<'a> = BasicBrushPipelineArgs<'a>;
|
||||||
type Out = Option<(Texture, DAffine2)>;
|
type Out = Option<(Texture, DAffine2)>;
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ impl AsyncWgpuPipeline for BasicBrushPipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run<'a>(&'a self, executor: &'a WgpuExecutor, args: &'a Self::Args<'_>) -> Self::Out {
|
fn run<'a>(&'a self, executor: &'a WgpuExecutor, args: &'a Self::Args<'_>) -> Self::Out {
|
||||||
let frame = super::render::Frame::new(args.strokes)?;
|
let frame = super::render::Frame::new(args.strokes)?;
|
||||||
let region = Region::new(&args.footprint)?;
|
let region = Region::new(&args.footprint)?;
|
||||||
let state = args.cache.take(&args.footprint).unwrap_or_default();
|
let state = args.cache.take(&args.footprint).unwrap_or_default();
|
||||||
|
|||||||
@@ -17,11 +17,10 @@ fn brush_strokes(
|
|||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
strokes: List<Stroke>,
|
strokes: List<Stroke>,
|
||||||
color: List<Color>,
|
color: List<Color>,
|
||||||
#[default(DEFAULT_DIAMETER)] diameter: Item<f64>,
|
#[default(DEFAULT_DIAMETER)] diameter: f64,
|
||||||
#[default(DEFAULT_HARDNESS)] hardness: Item<Percentage>,
|
#[default(DEFAULT_HARDNESS)] hardness: Percentage,
|
||||||
#[default(DEFAULT_FLOW)] flow: Item<Percentage>,
|
#[default(DEFAULT_FLOW)] flow: Percentage,
|
||||||
) -> List<Graphic> {
|
) -> List<Graphic<'static>> {
|
||||||
let (diameter, hardness, flow) = (diameter.into_element(), hardness.into_element(), flow.into_element());
|
|
||||||
List::new_from_item(
|
List::new_from_item(
|
||||||
Item::new_from_element(Graphic::from(strokes))
|
Item::new_from_element(Graphic::from(strokes))
|
||||||
.with_attribute(ATTR_COLOR, color.element(0).copied().unwrap_or_default())
|
.with_attribute(ATTR_COLOR, color.element(0).copied().unwrap_or_default())
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ pub fn to_graphic<'e, T: graphic_types::graphic::IntoGraphicElement>(
|
|||||||
/// Type-asserts a value to be graphical content, converting each item of other content types into its matching form.
|
/// Type-asserts a value to be graphical content, converting each item of other content types into its matching form.
|
||||||
/// Use the 'Into Group' node instead to collect the content into a single group.
|
/// Use the 'Into Group' node instead to collect the content into a single group.
|
||||||
#[node_macro::node(category("General"))]
|
#[node_macro::node(category("General"))]
|
||||||
pub fn as_graphic<'e>(_: impl Ctx, value: Graphic<'e>) -> Graphic<'e> {
|
pub fn as_graphic(_: impl Ctx, value: Graphic<'static>) -> Graphic<'static> {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,7 +530,7 @@ pub fn to_graphic_element<'e, T: graphic_types::graphic::IntoGraphicElement>(
|
|||||||
/// The typed-level conversion: the whole level nests as one graphic lane, as
|
/// The typed-level conversion: the whole level nests as one graphic lane, as
|
||||||
/// the pre-flip `Into<Graphic>` list collapse did. Registered under the to
|
/// the pre-flip `Into<Graphic>` list collapse did. Registered under the to
|
||||||
/// graphic identifier.
|
/// graphic identifier.
|
||||||
#[node_macro::node(category(""), extent(wrap_graphic_extent))]
|
#[node_macro::node(category(""), extent(into_group_extent))]
|
||||||
pub fn to_graphic_typed<'e, T: Clone + Send + Sync + core_types::CacheHash + 'static>(
|
pub fn to_graphic_typed<'e, T: Clone + Send + Sync + core_types::CacheHash + 'static>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
#[implementations(Vector, Raster<CPU>, Raster<GPU>, Color, Gradient, String)] content: IList<T>,
|
#[implementations(Vector, Raster<CPU>, Raster<GPU>, Color, Gradient, String)] content: IList<T>,
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ pub fn render_output_cache(
|
|||||||
render_params.for_mask,
|
render_params.for_mask,
|
||||||
render_params.thumbnail,
|
render_params.thumbnail,
|
||||||
render_params.aligned_strokes,
|
render_params.aligned_strokes,
|
||||||
render_params.stroke_below,
|
render_params.override_paint_order,
|
||||||
ctx.try_animation_time().unwrap_or(0.),
|
ctx.try_animation_time().unwrap_or(0.),
|
||||||
ctx.try_real_time().unwrap_or(0.),
|
ctx.try_real_time().unwrap_or(0.),
|
||||||
ctx.try_pointer_position(),
|
ctx.try_pointer_position(),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use core_types::consts::{DEFAULT_FONT_SIZE, DEFAULT_LINE_HEIGHT};
|
use core_types::consts::{DEFAULT_FONT_SIZE, DEFAULT_LINE_HEIGHT};
|
||||||
use core_types::list::List;
|
use core_types::list::{Item, List};
|
||||||
use core_types::{ATTR_FONT_SIZE, ATTR_LETTER_SPACING, ATTR_LETTER_TILT, ATTR_LINE_HEIGHT, ATTR_MAX_HEIGHT, ATTR_MAX_WIDTH, Ctx};
|
use core_types::{ATTR_FONT_SIZE, ATTR_LETTER_SPACING, ATTR_LETTER_TILT, ATTR_LINE_HEIGHT, ATTR_MAX_HEIGHT, ATTR_MAX_WIDTH, Ctx};
|
||||||
use graph_craft::application_io::resource::Resource;
|
use graph_craft::application_io::resource::Resource;
|
||||||
use graphic_types::Vector;
|
use graphic_types::Vector;
|
||||||
@@ -15,15 +15,15 @@ fn text(
|
|||||||
/// The text content to be drawn.
|
/// The text content to be drawn.
|
||||||
#[widget(ParsedWidgetOverride::Custom = "text_area")]
|
#[widget(ParsedWidgetOverride::Custom = "text_area")]
|
||||||
#[default("Lorem ipsum")]
|
#[default("Lorem ipsum")]
|
||||||
text: Item<String>,
|
text: String,
|
||||||
/// The loaded font file used to draw the text. The editor resolves the chosen typeface to these bytes via the resource system.
|
/// The loaded font file used to draw the text. The editor resolves the chosen typeface to these bytes via the resource system.
|
||||||
#[widget(ParsedWidgetOverride::Custom = "text_font")]
|
#[widget(ParsedWidgetOverride::Custom = "text_font")]
|
||||||
font: Item<Resource>,
|
font: Resource,
|
||||||
/// The font size used to draw the text.
|
/// The font size used to draw the text.
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
#[default(24.)]
|
#[default(24.)]
|
||||||
#[hard(1..)]
|
#[hard(1..)]
|
||||||
size: Item<f64>,
|
size: f64,
|
||||||
/// The line height ratio, relative to the font size. Each line is drawn lower than its previous line by the distance of *Size* × *Line Height*.
|
/// The line height ratio, relative to the font size. Each line is drawn lower than its previous line by the distance of *Size* × *Line Height*.
|
||||||
///
|
///
|
||||||
/// 0 means all lines overlap. 1 means all lines are spaced by just the font size. 1.2 is a common default for readable text. 2 means double-spaced text.
|
/// 0 means all lines overlap. 1 means all lines are spaced by just the font size. 1.2 is a common default for readable text. 2 means double-spaced text.
|
||||||
@@ -31,41 +31,35 @@ fn text(
|
|||||||
#[hard(0..)]
|
#[hard(0..)]
|
||||||
#[step(0.1)]
|
#[step(0.1)]
|
||||||
#[default(1.2)]
|
#[default(1.2)]
|
||||||
line_height: Item<f64>,
|
line_height: f64,
|
||||||
/// Additional spacing, in pixels, added between each character.
|
/// Additional spacing, in pixels, added between each character.
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
#[step(0.1)]
|
#[step(0.1)]
|
||||||
letter_spacing: Item<f64>,
|
letter_spacing: f64,
|
||||||
/// The angle of faux italic slant applied to each glyph.
|
/// The angle of faux italic slant applied to each glyph.
|
||||||
#[unit("°")]
|
#[unit("°")]
|
||||||
#[hard(-85..85)]
|
#[hard(-85..85)]
|
||||||
letter_tilt: Item<f64>,
|
letter_tilt: f64,
|
||||||
/// Enables the maximum width constraint so lines can wrap.
|
/// Enables the maximum width constraint so lines can wrap.
|
||||||
#[widget(ParsedWidgetOverride::Hidden)]
|
#[widget(ParsedWidgetOverride::Hidden)]
|
||||||
has_max_width: Item<bool>,
|
has_max_width: bool,
|
||||||
/// The maximum width that the text block can occupy before wrapping to a new line. Otherwise, lines do not wrap.
|
/// The maximum width that the text block can occupy before wrapping to a new line. Otherwise, lines do not wrap.
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
#[hard(1..)]
|
#[hard(1..)]
|
||||||
#[widget(ParsedWidgetOverride::Custom = "optional_f64")]
|
#[widget(ParsedWidgetOverride::Custom = "optional_f64")]
|
||||||
max_width: Item<f64>,
|
max_width: f64,
|
||||||
/// Whether the *Max Height* property is enabled so that lines beyond it are not drawn.
|
/// Whether the *Max Height* property is enabled so that lines beyond it are not drawn.
|
||||||
#[widget(ParsedWidgetOverride::Hidden)]
|
#[widget(ParsedWidgetOverride::Hidden)]
|
||||||
has_max_height: Item<bool>,
|
has_max_height: bool,
|
||||||
/// The maximum height that the text block can occupy. Excess lines are not drawn.
|
/// The maximum height that the text block can occupy. Excess lines are not drawn.
|
||||||
#[unit(" px")]
|
#[unit(" px")]
|
||||||
#[hard(1..)]
|
#[hard(1..)]
|
||||||
#[widget(ParsedWidgetOverride::Custom = "optional_f64")]
|
#[widget(ParsedWidgetOverride::Custom = "optional_f64")]
|
||||||
max_height: Item<f64>,
|
max_height: f64,
|
||||||
/// The horizontal alignment of each line of text within its surrounding box. To have an effect on a single line of text, *Max Width* must be set.
|
/// The horizontal alignment of each line of text within its surrounding box. To have an effect on a single line of text, *Max Width* must be set.
|
||||||
#[widget(ParsedWidgetOverride::Custom = "text_align")]
|
#[widget(ParsedWidgetOverride::Custom = "text_align")]
|
||||||
align: Item<TextAlign>,
|
align: TextAlign,
|
||||||
) -> Item<String> {
|
) -> List<String> {
|
||||||
let text = text.into_element();
|
|
||||||
let font = font.into_element();
|
|
||||||
let (size, line_height, letter_spacing, letter_tilt) = (*size.element(), *line_height.element(), *letter_spacing.element(), *letter_tilt.element());
|
|
||||||
let (has_max_width, max_width, has_max_height, max_height) = (*has_max_width.element(), *max_width.element(), *has_max_height.element(), *max_height.element());
|
|
||||||
let align = align.into_element();
|
|
||||||
|
|
||||||
let mut item = Item::new_from_element(text);
|
let mut item = Item::new_from_element(text);
|
||||||
|
|
||||||
if font != Resource::default() {
|
if font != Resource::default() {
|
||||||
@@ -93,25 +87,27 @@ fn text(
|
|||||||
item.set_attribute(ATTR_TEXT_ALIGN, align);
|
item.set_attribute(ATTR_TEXT_ALIGN, align);
|
||||||
}
|
}
|
||||||
|
|
||||||
item
|
List::new_from_item(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a styled text string into a vector compound path.
|
/// Converts a styled text string into a vector compound path.
|
||||||
#[node_macro::node(category("Text"), name("Text to Vector"))]
|
#[node_macro::node(category("Text"), name("Text to Vector"))]
|
||||||
fn text_to_vector(
|
fn text_to_vector(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
/// A styled text string produced by the **Text** node (or any other string source).
|
/// A styled list of text strings produced by the **Text** node (or any other `String[]` source).
|
||||||
string: Item<String>,
|
#[implementations(List<String>)]
|
||||||
) -> Item<Vector> {
|
strings: List<String>,
|
||||||
shape_text_item(&string, false).into_iter().next().unwrap_or_default()
|
) -> List<Vector> {
|
||||||
|
shape_text_list(&strings, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Splits a styled text string into a separate vector item for each of its glyphs (letterforms).
|
/// Splits a styled text string into a separate vector item for each of its glyphs (letterforms).
|
||||||
#[node_macro::node(category("Text"), name("Text to Vector Glyphs"))]
|
#[node_macro::node(category("Text"), name("Text to Vector Glyphs"))]
|
||||||
fn text_to_vector_glyphs(
|
fn text_to_vector_glyphs(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
/// A styled text string produced by the **Text** node (or any other string source).
|
/// A styled list of text strings produced by the **Text** node (or any other `String[]` source).
|
||||||
string: Item<String>,
|
#[implementations(List<String>)]
|
||||||
|
strings: List<String>,
|
||||||
) -> List<Vector> {
|
) -> List<Vector> {
|
||||||
shape_text_item(&string, true)
|
shape_text_list(&strings, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,10 +45,8 @@ fn path_modify<'e>(
|
|||||||
|
|
||||||
/// Bakes the content's transform attribute into its underlying value, resetting the attribute to the identity.
|
/// Bakes the content's transform attribute into its underlying value, resetting the attribute to the identity.
|
||||||
#[node_macro::node(category("Vector"))]
|
#[node_macro::node(category("Vector"))]
|
||||||
fn bake_transform<T: BakeTransform + Clone + Default + Send + Sync + 'static>(
|
// Monomorphic on Vector: our macro cannot yet read a record element through an open generic, so master's DAffine2 and DVec2 rows have no node here.
|
||||||
_ctx: impl Ctx,
|
fn bake_transform(_ctx: impl Ctx, (mut content, transform): (Vector, Attr<TransformAttr>)) -> (Vector, Attr<TransformAttr>) {
|
||||||
#[implementations(Vector, DAffine2, DVec2)] (mut content, transform): (T, Attr<TransformAttr>),
|
|
||||||
) -> (T, Attr<TransformAttr>) {
|
|
||||||
let transform: DAffine2 = *transform;
|
let transform: DAffine2 = *transform;
|
||||||
content.bake_transform(&transform);
|
content.bake_transform(&transform);
|
||||||
|
|
||||||
|
|||||||
@@ -55,9 +55,21 @@ fn carried_lane_attrs<'e>(arena: &'e core_types::arena::Arena, lane: core_types:
|
|||||||
Ok((Attr(lane.attr::<TransformAttr>()), Attr(layer_path.as_slice())))
|
Ok((Attr(lane.attr::<TransformAttr>()), Attr(layer_path.as_slice())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A gradient row's settings, which ride the lane rather than the bare element.
|
||||||
|
fn gradient_settings_from_lane(source: &core_types::node::List<'_, Gradient>, index: usize) -> GradientSettings {
|
||||||
|
let lane = source.lane(index);
|
||||||
|
GradientSettings {
|
||||||
|
spread: lane.attr::<vector_types::markers::GradientSpread>(),
|
||||||
|
cyclic: lane.attr::<vector_types::markers::GradientCyclic>(),
|
||||||
|
space: lane.attr::<vector_types::markers::GradientSpace>(),
|
||||||
|
hue_direction: lane.attr::<vector_types::markers::GradientHueDirection>(),
|
||||||
|
interpolation: lane.attr::<vector_types::markers::GradientInterpolation>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The gradient color for one assign-colors position, replaying the
|
/// The gradient color for one assign-colors position, replaying the
|
||||||
/// randomized draws up to it.
|
/// randomized draws up to it.
|
||||||
fn assign_color_at(gradient: &Gradient, position: usize, length: usize, randomize: bool, seed: SeedValue, repeat_every: u32) -> Color {
|
fn assign_color_at(gradient: &Gradient, settings: GradientSettings, position: usize, length: usize, randomize: bool, seed: SeedValue, repeat_every: u32) -> Color {
|
||||||
let factor = match randomize {
|
let factor = match randomize {
|
||||||
true => {
|
true => {
|
||||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
||||||
@@ -74,10 +86,7 @@ fn assign_color_at(gradient: &Gradient, position: usize, length: usize, randomiz
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
// The factor spans 0..=1, so the spread stays Pad rather than wrapping the last element onto the first stop
|
// The factor spans 0..=1, so the spread stays Pad rather than wrapping the last element onto the first stop
|
||||||
let settings = GradientSettings {
|
let settings = GradientSettings { spread: Default::default(), ..settings };
|
||||||
spread: Default::default(),
|
|
||||||
..GradientSettings::from(gradient)
|
|
||||||
};
|
|
||||||
gradient.evaluate(factor, settings)
|
gradient.evaluate(factor, settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,16 +166,17 @@ fn assign_colors<'e>(
|
|||||||
return Ok((element, transform, Attr(existing_fill), Attr(existing_stroke), layer_path));
|
return Ok((element, transform, Attr(existing_fill), Attr(existing_stroke), layer_path));
|
||||||
}
|
}
|
||||||
let gradient_element = gradient.element_ref(0);
|
let gradient_element = gradient.element_ref(0);
|
||||||
|
let gradient_settings = gradient_settings_from_lane(&gradient, 0);
|
||||||
let reversed;
|
let reversed;
|
||||||
let gradient_element = match reverse {
|
let gradient_element = match reverse {
|
||||||
true => {
|
true => {
|
||||||
reversed = gradient_element.reversed(GradientSettings::from(gradient_element).cyclic);
|
reversed = gradient_element.reversed(gradient_settings.cyclic);
|
||||||
&reversed
|
&reversed
|
||||||
}
|
}
|
||||||
false => gradient_element,
|
false => gradient_element,
|
||||||
};
|
};
|
||||||
|
|
||||||
let color = assign_color_at(gradient_element, lane, content.len(), randomize, seed, repeat_every);
|
let color = assign_color_at(gradient_element, gradient_settings, lane, content.len(), randomize, seed, repeat_every);
|
||||||
let paint = List::new_from_element(color).into_graphic_list();
|
let paint = List::new_from_element(color).into_graphic_list();
|
||||||
let parked = park_paint(ctx.arena(), paint)?;
|
let parked = park_paint(ctx.arena(), paint)?;
|
||||||
|
|
||||||
@@ -226,10 +236,11 @@ fn assign_colors_graphic<'e>(
|
|||||||
return Ok((original.clone(), transform, layer_path));
|
return Ok((original.clone(), transform, layer_path));
|
||||||
}
|
}
|
||||||
let gradient_element = gradient.element_ref(0);
|
let gradient_element = gradient.element_ref(0);
|
||||||
|
let gradient_settings = gradient_settings_from_lane(&gradient, 0);
|
||||||
let reversed;
|
let reversed;
|
||||||
let gradient_element = match reverse {
|
let gradient_element = match reverse {
|
||||||
true => {
|
true => {
|
||||||
reversed = gradient_element.reversed(GradientSettings::from(gradient_element).cyclic);
|
reversed = gradient_element.reversed(gradient_settings.cyclic);
|
||||||
&reversed
|
&reversed
|
||||||
}
|
}
|
||||||
false => gradient_element,
|
false => gradient_element,
|
||||||
@@ -272,7 +283,7 @@ fn assign_colors_graphic<'e>(
|
|||||||
Some(mut rows) => {
|
Some(mut rows) => {
|
||||||
for row in 0..rows.len() {
|
for row in 0..rows.len() {
|
||||||
let has_stroke = lane_has_stroke || has_paint::<StrokeAttr, _>(&rows, row);
|
let has_stroke = lane_has_stroke || has_paint::<StrokeAttr, _>(&rows, row);
|
||||||
let color = assign_color_at(gradient_element, position + row, length, randomize, seed, repeat_every);
|
let color = assign_color_at(gradient_element, gradient_settings, position + row, length, randomize, seed, repeat_every);
|
||||||
let paint = List::new_from_element(color).into_graphic_list();
|
let paint = List::new_from_element(color).into_graphic_list();
|
||||||
if fill {
|
if fill {
|
||||||
set_paint_attribute_at(&mut rows, row, ATTR_FILL, paint.clone());
|
set_paint_attribute_at(&mut rows, row, ATTR_FILL, paint.clone());
|
||||||
@@ -2858,13 +2869,13 @@ fn morph_core(flattened: List<Vector>, snapshot: List<Graphic<'static>>, progres
|
|||||||
(Some(Graphic::Color(color_a)), Some(Graphic::Color(color_b))) => Some(List::new_from_element(Graphic::from(color_a.lerp(color_b, time as f32)))),
|
(Some(Graphic::Color(color_a)), Some(Graphic::Color(color_b))) => Some(List::new_from_element(Graphic::from(color_a.lerp(color_b, time as f32)))),
|
||||||
(Some(Graphic::Color(color_a)), Some(Graphic::Gradient(stops_b))) => {
|
(Some(Graphic::Color(color_a)), Some(Graphic::Gradient(stops_b))) => {
|
||||||
let mut solid_to_gradient = stops_b.clone();
|
let mut solid_to_gradient = stops_b.clone();
|
||||||
solid_to_gradient.color.iter_mut().for_each(|color| *color = *color_a);
|
solid_to_gradient.0.iter_element_values_mut().for_each(|color| *color = *color_a);
|
||||||
let stops = solid_to_gradient.lerp(stops_b, time);
|
let stops = solid_to_gradient.lerp(stops_b, time);
|
||||||
Some(gradient_paint(b, stops, None))
|
Some(gradient_paint(b, stops, None))
|
||||||
}
|
}
|
||||||
(Some(Graphic::Gradient(stops_a)), Some(Graphic::Color(color_b))) => {
|
(Some(Graphic::Gradient(stops_a)), Some(Graphic::Color(color_b))) => {
|
||||||
let mut gradient_to_solid = stops_a.clone();
|
let mut gradient_to_solid = stops_a.clone();
|
||||||
gradient_to_solid.color.iter_mut().for_each(|color| *color = *color_b);
|
gradient_to_solid.0.iter_element_values_mut().for_each(|color| *color = *color_b);
|
||||||
let stops = stops_a.lerp(&gradient_to_solid, time);
|
let stops = stops_a.lerp(&gradient_to_solid, time);
|
||||||
Some(gradient_paint(a, stops, None))
|
Some(gradient_paint(a, stops, None))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user