mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Remove the runtime-named attribute family and its type-erased bridges
This commit is contained in:
@@ -2602,8 +2602,10 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn the_flip_reset_markers_track_the_live_merge_internals() {
|
||||
assert_eq!(FLIP_RESET_NODE_MARKERS, &[graphene_std::graphic::write_attribute::IDENTIFIER.as_str()]);
|
||||
fn the_flip_reset_markers_keep_the_historical_merge_internals_spelling() {
|
||||
// The node itself is removed; the marker matches its spelling in
|
||||
// documents saved before the flip, which must stay stable.
|
||||
assert_eq!(FLIP_RESET_NODE_MARKERS, &["graphic_nodes::graphic::WriteAttributeNode"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
use graphene_std::gradient::GradientStops;
|
||||
use graphene_std::list::{AttributeDyn, AttributeValueDyn, List, ListDyn};
|
||||
|
||||
use graphene_std::list::List;
|
||||
#[cfg(feature = "gpu")]
|
||||
use graphene_std::raster::GPU;
|
||||
use graphene_std::raster::color::Color;
|
||||
use graphene_std::raster::*;
|
||||
|
||||
#[cfg(feature = "gpu")]
|
||||
use graphene_std::SourceId;
|
||||
use graphene_std::raster::{CPU, Raster};
|
||||
use graphene_std::registry::{ConstructionError, EdgeHandle, NodeIOTypes, RegistryEntry};
|
||||
#[cfg(feature = "gpu")]
|
||||
use graphene_std::SourceId;
|
||||
#[cfg(feature = "gpu")]
|
||||
use graphene_std::runtime::RuntimeHandle;
|
||||
use graphene_std::uuid::NodeId;
|
||||
|
||||
use graphene_std::vector::Vector;
|
||||
use graphene_std::{Artboard, Context, Graphic, ProtoNodeIdentifier, concrete};
|
||||
use graphene_std::{Context, Graphic, ProtoNodeIdentifier, concrete};
|
||||
use node_registry_macros::{convert_node, into_node};
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "gpu")]
|
||||
@@ -32,57 +31,6 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> {
|
||||
convert_node!(from: List<Raster<CPU>>, to: List<Graphic>),
|
||||
#[cfg(feature = "gpu")]
|
||||
convert_node!(from: List<Raster<GPU>>, to: List<Graphic>),
|
||||
// Type-erased attribute conversions for the `Attach Attribute` node, so it monomorphizes only over the destination `List` type.
|
||||
convert_node!(from: List<Artboard>, to: AttributeDyn),
|
||||
convert_node!(from: List<Graphic>, to: AttributeDyn),
|
||||
convert_node!(from: List<Vector>, to: AttributeDyn),
|
||||
convert_node!(from: List<Raster<CPU>>, to: AttributeDyn),
|
||||
convert_node!(from: List<Color>, to: AttributeDyn),
|
||||
convert_node!(from: List<GradientStops>, to: AttributeDyn),
|
||||
convert_node!(from: List<f64>, to: AttributeDyn),
|
||||
convert_node!(from: List<bool>, to: AttributeDyn),
|
||||
convert_node!(from: List<String>, to: AttributeDyn),
|
||||
convert_node!(from: List<DAffine2>, to: AttributeDyn),
|
||||
convert_node!(from: List<BlendMode>, to: AttributeDyn),
|
||||
convert_node!(from: List<graphene_std::vector::style::GradientType>, to: AttributeDyn),
|
||||
convert_node!(from: List<graphene_std::vector::style::GradientSpreadMethod>, to: AttributeDyn),
|
||||
convert_node!(from: List<Artboard>, to: ListDyn),
|
||||
convert_node!(from: List<Graphic>, to: ListDyn),
|
||||
convert_node!(from: List<Vector>, to: ListDyn),
|
||||
convert_node!(from: List<Raster<CPU>>, to: ListDyn),
|
||||
#[cfg(feature = "gpu")]
|
||||
convert_node!(from: List<Raster<GPU>>, to: ListDyn),
|
||||
convert_node!(from: List<Color>, to: ListDyn),
|
||||
convert_node!(from: List<GradientStops>, to: ListDyn),
|
||||
convert_node!(from: List<f64>, to: ListDyn),
|
||||
convert_node!(from: List<bool>, to: ListDyn),
|
||||
convert_node!(from: List<String>, to: ListDyn),
|
||||
convert_node!(from: List<u8>, to: ListDyn),
|
||||
convert_node!(from: List<NodeId>, to: ListDyn),
|
||||
convert_node!(from: List<DAffine2>, to: ListDyn),
|
||||
convert_node!(from: List<BlendMode>, to: ListDyn),
|
||||
convert_node!(from: List<graphene_std::vector::style::GradientType>, to: ListDyn),
|
||||
convert_node!(from: List<graphene_std::vector::style::GradientSpreadMethod>, to: ListDyn),
|
||||
// Type-erased attribute value conversions for the `Write Attribute` node, so it monomorphizes only over the destination `List` type.
|
||||
convert_node!(from: f64, to: AttributeValueDyn),
|
||||
convert_node!(from: u32, to: AttributeValueDyn),
|
||||
convert_node!(from: u64, to: AttributeValueDyn),
|
||||
convert_node!(from: bool, to: AttributeValueDyn),
|
||||
convert_node!(from: String, to: AttributeValueDyn),
|
||||
convert_node!(from: DVec2, to: AttributeValueDyn),
|
||||
convert_node!(from: DAffine2, to: AttributeValueDyn),
|
||||
convert_node!(from: Color, to: AttributeValueDyn),
|
||||
convert_node!(from: BlendMode, to: AttributeValueDyn),
|
||||
convert_node!(from: graphene_std::vector::style::GradientType, to: AttributeValueDyn),
|
||||
convert_node!(from: graphene_std::vector::style::GradientSpreadMethod, to: AttributeValueDyn),
|
||||
convert_node!(from: List<String>, to: AttributeValueDyn),
|
||||
convert_node!(from: List<NodeId>, to: AttributeValueDyn),
|
||||
convert_node!(from: List<Color>, to: AttributeValueDyn),
|
||||
convert_node!(from: List<GradientStops>, to: AttributeValueDyn),
|
||||
convert_node!(from: List<Vector>, to: AttributeValueDyn),
|
||||
convert_node!(from: List<Raster<CPU>>, to: AttributeValueDyn),
|
||||
convert_node!(from: List<Raster<GPU>>, to: AttributeValueDyn),
|
||||
convert_node!(from: List<Graphic>, to: AttributeValueDyn),
|
||||
// into_node!(from: List<Raster<CPU>>, to: List<Raster<SRGBA8>>),
|
||||
convert_node!(from: DVec2, to: DVec2),
|
||||
convert_node!(from: List<Vector>, to: List<Vector>),
|
||||
@@ -174,15 +122,17 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> {
|
||||
node_types.extend(
|
||||
graphene_std::graphic::level_to_list_entries()
|
||||
.into_iter()
|
||||
.zip(["List<Graphic>", "List<Vector>", "List<Raster<CPU>>", "List<Raster<GPU>>", "List<Color>", "List<GradientStops>", "List<String>"])
|
||||
.zip([
|
||||
"List<Graphic>",
|
||||
"List<Vector>",
|
||||
"List<Raster<CPU>>",
|
||||
"List<Raster<GPU>>",
|
||||
"List<Color>",
|
||||
"List<GradientStops>",
|
||||
"List<String>",
|
||||
])
|
||||
.map(|(entry, target)| (ProtoNodeIdentifier::with_owned_string(format!("graphene_core::ops::ConvertNode<{target}>")), entry)),
|
||||
);
|
||||
// The same bridge into the attribute family's type-erased list.
|
||||
node_types.extend(
|
||||
graphene_std::graphic::level_to_list_dyn_entries()
|
||||
.into_iter()
|
||||
.map(|entry| (ProtoNodeIdentifier::new("graphene_core::ops::ConvertNode<ListDyn>"), entry)),
|
||||
);
|
||||
// =============
|
||||
// CONVERT NODES
|
||||
// =============
|
||||
@@ -209,7 +159,6 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> {
|
||||
.flatten(),
|
||||
);
|
||||
|
||||
|
||||
let mut map: HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> = HashMap::new();
|
||||
let insert = |map: &mut HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>>, id: ProtoNodeIdentifier, entry: RegistryEntry| {
|
||||
let rows = map.entry(id).or_default();
|
||||
@@ -390,7 +339,6 @@ mod node_registry_macros {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
pub(crate) use convert_node;
|
||||
pub(crate) use into_node;
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@ use core_types::attribute::{Attr, EditorLayerPath, Transform as TransformAttr};
|
||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||
use core_types::extent::{ExtentIn, LevelIn, ListIn, ValueIn};
|
||||
use core_types::gpoll::{Extent, GPoll, GraphError, Interrupt, Level};
|
||||
use core_types::list::{AttributeDyn, AttributeValueDyn, Item, List, ListDyn};
|
||||
use core_types::list::List;
|
||||
use core_types::registry::types::{Angle, SignedInteger};
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, AnyHash, BlendMode, CacheHash, Color, Context, Ctx, DeriveCtx, ExtractIndex, InjectIndex};
|
||||
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, CacheHash, Color, Ctx, DeriveCtx, ExtractIndex, InjectIndex};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphic_types::graphic::{Graphic, IntoGraphicList};
|
||||
use graphic_types::{ATTR_EDITOR_MERGED_LAYERS, Artboard, Vector};
|
||||
use raster_types::{CPU, GPU, Raster};
|
||||
use vector_types::gradient::{GradientSpreadMethod, GradientType};
|
||||
|
||||
use vector_types::{GradientStop, GradientStops, ReferencePoint};
|
||||
|
||||
/// Resolves a signed index over `total` lanes: negatives count from the end,
|
||||
@@ -98,7 +98,8 @@ fn omit_element_extent(list: ExtentIn<'_>, index: ValueIn<'_, f64>, level: Level
|
||||
pub fn extract_element<T: Clone + Default + Send + Sync + CacheHash + 'static>(
|
||||
_: impl Ctx + ExtractIndex + InjectIndex + Copy,
|
||||
/// The `List` of data to extract from.
|
||||
#[implementations(String, f64, NodeId, Color, GradientStops, Vector, Raster<CPU>, Graphic, Artboard)] list: IList<T>,
|
||||
#[implementations(String, f64, NodeId, Color, GradientStops, Vector, Raster<CPU>, Graphic, Artboard)]
|
||||
list: IList<T>,
|
||||
/// The index of the item to retrieve, starting from 0 for the first item. Negative indices count backwards from the end of the list, starting from -1 for the last item.
|
||||
index: SignedInteger,
|
||||
) -> T {
|
||||
@@ -274,7 +275,15 @@ fn mirror<'e>(
|
||||
)>,
|
||||
Interrupt,
|
||||
> {
|
||||
mirror_lane(ctx.arena(), legacy_render_list_of(content), ctx.innermost_index() as usize, relative_to_bounds, offset, angle, keep_original)
|
||||
mirror_lane(
|
||||
ctx.arena(),
|
||||
legacy_render_list_of(content),
|
||||
ctx.innermost_index() as usize,
|
||||
relative_to_bounds,
|
||||
offset,
|
||||
angle,
|
||||
keep_original,
|
||||
)
|
||||
}
|
||||
|
||||
/// The kept originals double the level, counted from the subject's extent
|
||||
@@ -324,7 +333,15 @@ fn mirror_vector<'e>(
|
||||
)>,
|
||||
Interrupt,
|
||||
> {
|
||||
mirror_lane(ctx.arena(), legacy_render_list_of(content), ctx.innermost_index() as usize, relative_to_bounds, offset, angle, keep_original)
|
||||
mirror_lane(
|
||||
ctx.arena(),
|
||||
legacy_render_list_of(content),
|
||||
ctx.innermost_index() as usize,
|
||||
relative_to_bounds,
|
||||
offset,
|
||||
angle,
|
||||
keep_original,
|
||||
)
|
||||
}
|
||||
|
||||
fn mirror_vector_extent(
|
||||
@@ -371,281 +388,6 @@ pub fn stamp_layer_path<'e, T>(ctx: impl Ctx + ExtractArena<'e>, element: T, pat
|
||||
Ok((element, Attr(parked.as_slice())))
|
||||
}
|
||||
|
||||
/// Sets a named attribute on the input `List`, computing one value per item via the value-producing input. That input
|
||||
/// is evaluated once per item, with the item's index and the item itself (as a `List` containing only that item,
|
||||
/// passed as a vararg) provided via context, so the upstream pipeline can return a different value per item that may
|
||||
/// be derived from the item's own data. If the attribute already exists, its values are replaced; if not, it's added.
|
||||
/// The value is type-erased into an `AttributeValueDyn` by an auto-inserted convert node, so this node only
|
||||
/// monomorphizes over `T` instead of the cartesian product `(T, U)`.
|
||||
#[node_macro::node(category("Attributes: Write"))]
|
||||
fn write_attribute<T: AnyHash + Clone + Send + Sync + CacheHash>(
|
||||
ctx: impl Ctx + DeriveCtx,
|
||||
/// The `List` to set the named attribute on (one value per item).
|
||||
#[implementations(
|
||||
List<Artboard>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
List<f64>,
|
||||
List<bool>,
|
||||
List<String>,
|
||||
List<DAffine2>,
|
||||
List<BlendMode>,
|
||||
List<GradientType>,
|
||||
List<GradientSpreadMethod>,
|
||||
)]
|
||||
mut content: List<T>,
|
||||
/// The attribute name (key) to write or replace.
|
||||
name: String,
|
||||
/// The node that produces the attribute value for each item. Called once per item with the item's index in context.
|
||||
#[implementations(Context -> AttributeValueDyn)]
|
||||
value: impl Node<Context<'_>, Output = AttributeValueDyn>,
|
||||
) -> Result<List<T>, Interrupt> {
|
||||
let spilled = ctx.index_head();
|
||||
for index in 0..content.len() {
|
||||
let row = content.clone_item(index).expect("index is within bounds");
|
||||
let item = List::new_from_item(row);
|
||||
let scoped = ctx.push_vararg(&item);
|
||||
let v = value.eval(&scoped.ctx().promoted(&spilled, index as u64))?;
|
||||
content.set_attribute_value_dyn(&name, index, v);
|
||||
}
|
||||
Ok(content)
|
||||
}
|
||||
|
||||
/// Sets a named attribute on the primary list, with each value taken from the corresponding item's element in the source list (paired by index, wrapping if the source has fewer items).
|
||||
/// The source is type-erased into an `AttributeDyn` by an auto-inserted convert node, so this node only monomorphizes over `T` instead of the cartesian product `(T, U)`.
|
||||
#[node_macro::node(category("Attributes: Write"))]
|
||||
fn attach_attribute<T: AnyHash + Clone + Send + Sync + CacheHash>(
|
||||
_: impl Ctx,
|
||||
/// The `List` to attach the new attribute to.
|
||||
#[implementations(
|
||||
List<Artboard>,
|
||||
List<Graphic>,
|
||||
List<Vector>,
|
||||
List<Raster<CPU>>,
|
||||
List<Color>,
|
||||
List<GradientStops>,
|
||||
List<f64>,
|
||||
List<bool>,
|
||||
List<String>,
|
||||
List<DAffine2>,
|
||||
List<BlendMode>,
|
||||
List<GradientType>,
|
||||
List<GradientSpreadMethod>,
|
||||
)]
|
||||
mut content: List<T>,
|
||||
/// The source values to attach.
|
||||
#[expose]
|
||||
source: AttributeDyn,
|
||||
/// The name to assign to the new destination attribute.
|
||||
name: String,
|
||||
) -> List<T> {
|
||||
if source.is_empty() {
|
||||
return content;
|
||||
}
|
||||
content.set_attribute_dyn(name, source);
|
||||
content
|
||||
}
|
||||
|
||||
/// Reads a named `Vector` attribute from the input list, outputting each value as an element of a new `Vector[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_vector(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<Vector> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<Vector>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named numeric attribute (`f64`, `u64`, or `u32`) from the input list, outputting each value as an element of a new `f64[]`. Integer values are converted to `f64`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_number(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<f64> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let value = content
|
||||
.attribute::<f64>(&name, index)
|
||||
.copied()
|
||||
.or_else(|| content.attribute::<u64>(&name, index).map(|v| *v as f64))
|
||||
.or_else(|| content.attribute::<u32>(&name, index).map(|v| *v as f64));
|
||||
let Some(value) = value else { continue };
|
||||
result.push(Item::new_from_element(value));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `bool` attribute from the input list, outputting each value as an element of a new `bool[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_bool(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<bool> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<bool>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `String` attribute from the input list, outputting each value as an element of a new `String[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_string(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<String> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<String>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `DAffine2` transform attribute from the input list, outputting each value as an element of a new `DAffine2[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_transform(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<DAffine2> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<DAffine2>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `Color` attribute from the input list, outputting each value as an element of a new `Color[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_color(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<Color> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<Color>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `BlendMode` attribute from the input list, outputting each value as an element of a new `BlendMode[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_blend_mode(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<BlendMode> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<BlendMode>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `GradientType` attribute from the input list, outputting each value as an element of a new `GradientType[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_gradient_type(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<GradientType> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<GradientType>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `GradientSpreadMethod` attribute from the input list, outputting each value as an element of a new `GradientSpreadMethod[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_spread_method(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<GradientSpreadMethod> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<GradientSpreadMethod>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(*value));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `GradientStops` attribute from the input list, outputting each value as an element of a new `GradientStops[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_gradient_stops(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<GradientStops> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<GradientStops>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `Artboard` attribute from the input list, outputting each value as an element of a new `Artboard[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_artboard(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<Artboard> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<Artboard>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Reads a named `Raster` attribute from the input list, outputting each value as an element of a new `Raster[]`.
|
||||
#[node_macro::node(category("Attributes: Read"))]
|
||||
fn read_attribute_raster(
|
||||
_: impl Ctx,
|
||||
content: ListDyn,
|
||||
/// The attribute name (key) to read.
|
||||
name: String,
|
||||
) -> List<Raster<CPU>> {
|
||||
let mut result = List::with_capacity(content.len());
|
||||
for index in 0..content.len() {
|
||||
let Some(value) = content.attribute::<Raster<CPU>>(&name, index) else { continue };
|
||||
result.push(Item::new_from_element(value.clone()));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Joins two levels of the same type, the base's lanes followed by the new's.
|
||||
#[node_macro::node(category("General"), extent(extend_extent))]
|
||||
pub fn extend<T>(
|
||||
@@ -783,20 +525,6 @@ pub fn level_to_list<T: Clone + Send + Sync + CacheHash + 'static>(
|
||||
graphic_types::graphic::run_to_render_list::<T>(&item).expect("the run holds the row's element type")
|
||||
}
|
||||
|
||||
/// The transitional level bridge into the type-erased list the attribute
|
||||
/// family consumes, as [`level_to_list`].
|
||||
#[node_macro::node(category(""))]
|
||||
pub fn level_to_list_dyn<T: Clone + Send + Sync + CacheHash + 'static>(
|
||||
_: impl Ctx + ExtractIndex + InjectIndex + Copy,
|
||||
#[implementations(Graphic, Vector, Raster<CPU>, Raster<GPU>, Color, GradientStops, String)] value: IList<T>,
|
||||
_converter: (),
|
||||
) -> ListDyn {
|
||||
// SAFETY: a materialized input's frames are arena-resident.
|
||||
let item = unsafe { core_types::record::GroupItem::from_resident(value.batch()) };
|
||||
graphic_types::graphic::run_to_render_list::<T>(&item).expect("the run holds the row's element type").into()
|
||||
}
|
||||
|
||||
pub use _level_to_list_dyn_mod::level_to_list_dyn_entries;
|
||||
pub use _level_to_list_mod::level_to_list_entries;
|
||||
pub use _to_graphic_mod::to_graphic_entries;
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ use core_types::extent::{ExtentIn, LevelIn, ListIn, ValueIn};
|
||||
use core_types::gpoll::{Extent, GPoll, GraphError, Interrupt};
|
||||
use core_types::{ATTR_TRANSFORM, Color, Ctx};
|
||||
use glam::DAffine2;
|
||||
use graphic_types::graphic::Graphic;
|
||||
use graphic_types::Vector;
|
||||
use graphic_types::graphic::Graphic;
|
||||
use raster_types::{CPU, Raster};
|
||||
use vector_types::{GradientStop, GradientStops};
|
||||
|
||||
@@ -335,14 +335,27 @@ mod tests {
|
||||
fn fixture_rows() -> Vec<(Graphic, DAffine2)> {
|
||||
vec![
|
||||
(text("a"), translation(1.)),
|
||||
(group(vec![(text("b"), translation(20.)), (group(vec![(text("c"), translation(300.))]), translation(4000.))]), translation(0.5)),
|
||||
(
|
||||
group(vec![(text("b"), translation(20.)), (group(vec![(text("c"), translation(300.))]), translation(4000.))]),
|
||||
translation(0.5),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
macro_rules! build {
|
||||
($layout:ident, $rows:expr, $fully:expr) => {
|
||||
install(
|
||||
FlattenNode::new(RecordSource::new(GraphicSource { layout: $layout.clone(), rows: $rows }, &$layout, &$layout), ValueNode($fully)),
|
||||
FlattenNode::new(
|
||||
RecordSource::new(
|
||||
GraphicSource {
|
||||
layout: $layout.clone(),
|
||||
rows: $rows,
|
||||
},
|
||||
&$layout,
|
||||
&$layout,
|
||||
),
|
||||
ValueNode($fully),
|
||||
),
|
||||
flatten_layout_meta(),
|
||||
&[Some(&$layout)],
|
||||
)
|
||||
@@ -425,7 +438,14 @@ mod tests {
|
||||
let layout = graphic_layout();
|
||||
let node = install(
|
||||
MapNode::<_, _, Graphic>::new(
|
||||
RecordSource::new(GraphicSource { layout: layout.clone(), rows: ragged_rows() }, &layout, &layout),
|
||||
RecordSource::new(
|
||||
GraphicSource {
|
||||
layout: layout.clone(),
|
||||
rows: ragged_rows(),
|
||||
},
|
||||
&layout,
|
||||
&layout,
|
||||
),
|
||||
PerRowSource { layout: layout.clone() },
|
||||
&layout,
|
||||
),
|
||||
@@ -464,7 +484,14 @@ mod tests {
|
||||
let layout = graphic_layout();
|
||||
let flat = install(
|
||||
FlatMapNode::<_, _, Graphic>::new(
|
||||
RecordSource::new(GraphicSource { layout: layout.clone(), rows: ragged_rows() }, &layout, &layout),
|
||||
RecordSource::new(
|
||||
GraphicSource {
|
||||
layout: layout.clone(),
|
||||
rows: ragged_rows(),
|
||||
},
|
||||
&layout,
|
||||
&layout,
|
||||
),
|
||||
PerRowSource { layout: layout.clone() },
|
||||
&layout,
|
||||
),
|
||||
@@ -473,7 +500,14 @@ mod tests {
|
||||
);
|
||||
let mapped = install(
|
||||
MapNode::<_, _, Graphic>::new(
|
||||
RecordSource::new(GraphicSource { layout: layout.clone(), rows: ragged_rows() }, &layout, &layout),
|
||||
RecordSource::new(
|
||||
GraphicSource {
|
||||
layout: layout.clone(),
|
||||
rows: ragged_rows(),
|
||||
},
|
||||
&layout,
|
||||
&layout,
|
||||
),
|
||||
PerRowSource { layout: layout.clone() },
|
||||
&layout,
|
||||
),
|
||||
@@ -536,7 +570,14 @@ mod tests {
|
||||
let layout = graphic_layout();
|
||||
let node = install(
|
||||
FlatMapNode::<_, _, Graphic>::new(
|
||||
RecordSource::new(GraphicSource { layout: layout.clone(), rows: ragged_rows() }, &layout, &layout),
|
||||
RecordSource::new(
|
||||
GraphicSource {
|
||||
layout: layout.clone(),
|
||||
rows: ragged_rows(),
|
||||
},
|
||||
&layout,
|
||||
&layout,
|
||||
),
|
||||
PerRowSource { layout: layout.clone() },
|
||||
&layout,
|
||||
),
|
||||
@@ -608,7 +649,11 @@ mod tests {
|
||||
};
|
||||
assert_eq!(children.len(), 1);
|
||||
assert_eq!(text_of(children.element(0).unwrap()), "c");
|
||||
assert_eq!(children.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, 0).translation.x, 300., "embedded transforms ride untouched");
|
||||
assert_eq!(
|
||||
children.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, 0).translation.x,
|
||||
300.,
|
||||
"embedded transforms ride untouched"
|
||||
);
|
||||
let transform: DAffine2 = unsafe { rec.read(offset) };
|
||||
assert_eq!(transform.translation.x, 4000.5);
|
||||
unsafe { stack::rewind(mark) };
|
||||
@@ -661,7 +706,7 @@ mod tests {
|
||||
let layout = graphic_layout();
|
||||
let rows = vec![(text("a"), translation(1.)), (text("b"), translation(2.))];
|
||||
let node = install(
|
||||
WrapNode::new(RecordSource::new(GraphicSource { layout: layout.clone(), rows, }, &layout, &layout), &layout),
|
||||
WrapNode::new(RecordSource::new(GraphicSource { layout: layout.clone(), rows }, &layout, &layout), &layout),
|
||||
wrap_layout_meta(),
|
||||
&[Some(&layout)],
|
||||
);
|
||||
@@ -753,12 +798,7 @@ mod tests {
|
||||
|
||||
let layout = Layout::default().with_writes(1, record::element_write_hashed::<Color>(), &[]);
|
||||
let out = Layout::default().with_writes(0, record::element_write_hashed::<GradientStops>(), &[]);
|
||||
let build = |colors: Vec<Color>| {
|
||||
install_flip(
|
||||
ToGradientNode::new(RecordSource::new(ColorSource { layout: layout.clone(), colors }, &layout, &layout), &layout),
|
||||
&out,
|
||||
)
|
||||
};
|
||||
let build = |colors: Vec<Color>| install_flip(ToGradientNode::new(RecordSource::new(ColorSource { layout: layout.clone(), colors }, &layout, &layout), &layout), &out);
|
||||
let stops_of = |colors: Vec<Color>| {
|
||||
let node = build(colors);
|
||||
let GPoll::Final(value) = node.eval(&ctx) else {
|
||||
@@ -788,7 +828,7 @@ mod tests {
|
||||
let layout = graphic_layout();
|
||||
let rows = vec![(text("a"), translation(1.)), (text("b"), translation(2.))];
|
||||
let node = install(
|
||||
WrapNode::new(RecordSource::new(GraphicSource { layout: layout.clone(), rows, }, &layout, &layout), &layout),
|
||||
WrapNode::new(RecordSource::new(GraphicSource { layout: layout.clone(), rows }, &layout, &layout), &layout),
|
||||
wrap_layout_meta(),
|
||||
&[Some(&layout)],
|
||||
);
|
||||
@@ -819,7 +859,7 @@ mod tests {
|
||||
let layout = graphic_layout();
|
||||
let rows = vec![(text("a"), translation(1.)), (text("b"), translation(2.))];
|
||||
let wrapped = install(
|
||||
WrapNode::new(RecordSource::new(GraphicSource { layout: layout.clone(), rows, }, &layout, &layout), &layout),
|
||||
WrapNode::new(RecordSource::new(GraphicSource { layout: layout.clone(), rows }, &layout, &layout), &layout),
|
||||
wrap_layout_meta(),
|
||||
&[Some(&layout)],
|
||||
);
|
||||
|
||||
@@ -10,17 +10,17 @@ use core_types::extent::{ExtentIn, LevelIn, ListIn, ValueIn};
|
||||
use core_types::gpoll::GraphError;
|
||||
use core_types::gpoll::Interrupt;
|
||||
use core_types::gpoll::{Extent, GPoll};
|
||||
use core_types::list::{Item, ItemAttributeValues, List, ListDyn};
|
||||
use core_types::list::{Item, ItemAttributeValues, List};
|
||||
use core_types::registry::types::{Angle, Length, Multiplier, Percentage, PixelLength, Progression, SeedValue};
|
||||
use core_types::transform::Transform;
|
||||
use core_types::uuid::NodeId;
|
||||
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, Color, Ctx, DeriveCtx, ExtractIndex, InjectIndex};
|
||||
use core_types::{ATTR_BLEND_MODE, ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, CacheHash, Color, Ctx, DeriveCtx, ExtractIndex, InjectIndex};
|
||||
use glam::{DAffine2, DMat2, DVec2};
|
||||
use graphic_types::Vector;
|
||||
use graphic_types::graphic::{bake_paint_transforms, graphic_list_at, has_paint_at, is_paint_present, set_paint_attribute_at};
|
||||
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke as StrokeAttr};
|
||||
use graphic_types::raster_types::{CPU, GPU, Raster};
|
||||
use graphic_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_FILL, ATTR_STROKE, Graphic, IntoGraphicList};
|
||||
use graphic_types::{Artboard, Vector};
|
||||
use kurbo::simplify::{SimplifyOptions, simplify_bezpath};
|
||||
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveArclen, PathEl, PathSeg, Shape};
|
||||
use rand::{Rng, SeedableRng};
|
||||
@@ -3540,7 +3540,10 @@ fn point_inside(_: impl Ctx + ExtractIndex + InjectIndex + Copy, source: IList<V
|
||||
// TODO: Return u32, u64, or usize instead of f64 after #1621 is resolved and has allowed us to implement automatic type conversion in the node graph for nodes with generic type inputs.
|
||||
// TODO: (Currently automatic type conversion only works for concrete types, via the Graphene preprocessor and not the full Graphene type system.)
|
||||
#[node_macro::node(category("General"), path(graphene_core::vector))]
|
||||
fn count_elements(_: impl Ctx, content: ListDyn) -> f64 {
|
||||
fn count_elements<T: Clone + Send + Sync + CacheHash + 'static>(
|
||||
_: impl Ctx + ExtractIndex + InjectIndex + Copy,
|
||||
#[implementations(Graphic, Artboard, Vector, Raster<CPU>, Color, GradientStops, String)] content: IList<T>,
|
||||
) -> f64 {
|
||||
content.len() as f64
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user