Read a named attribute at each value type it can hold

One read node per value type, mirroring master's read family. A name
means one value type, so there is no coercion between them: master's
number read silently widened integers, and these refuse instead, which
is the same rule the write side already enforces.

Each takes any record wire through the opaque reading input and serves
the name's own default where the attribute is absent, so the value
carries the declared type either way.

The gradient enums join the value-type rows now that a family exists to
read them at.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Dennis Kobert
2026-09-09 12:36:00 +00:00
parent 5baeb8fd46
commit 0a4288eca6
2 changed files with 54 additions and 16 deletions

View File

@@ -15,6 +15,13 @@ core_types::attribute! {
pub EditorClickTarget("editor:click_target"): Option<&crate::Vector>;
}
// The value types a name-generic attribute can name here, so a compile-time
// named write or read reaches this crate's enums like any other plain value.
core_types::named_value! {
for crate::gradient::GradientSpreadMethod;
for crate::gradient::GradientType;
}
pub const ATTR_SPREAD_METHOD: &str = SpreadMethod::NAME;
pub const ATTR_GRADIENT_TYPE: &str = GradientType::NAME;
pub const ATTR_EDITOR_CLICK_TARGET: &str = EditorClickTarget::NAME;

View File

@@ -11,6 +11,7 @@ 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 as GradientTypeValue};
use vector_types::{GradientStop, GradientStops, ReferencePoint};
/// Resolves a signed index over `total` lanes: negatives count from the end,
@@ -387,22 +388,52 @@ pub fn write_attribute<'e, T, V: WireValue>(
Ok((content, Attr(parked)))
}
/// Reads the `f64` attribute `name` names off each lane. An absent attribute
/// reads as the name's own default, so the value is always a number; the name
/// is constant text the compiler folds into an offset when the graph compiles.
///
/// A name written at another value type is a graph error rather than a
/// conversion, so reading a number is never a coercion of one.
#[node_macro::node(category("Attributes: Read"))]
pub fn read_number_attribute<'e, T>(
_: impl Ctx,
/// The content whose lanes carry the attribute; its element is never read.
(content, value): (T, Attr<'e, Named<Name0, f64>>),
/// The attribute name, folded into an offset when the graph compiles.
name: Named<Name0>,
) -> f64 {
let _ = content;
*value
// The attribute reads: one node per value type, since a name means one type
// and there is no coercion between them. Each takes any record wire, never
// looks at its element, and serves the name's own default where the attribute
// is absent, so the value always carries the declared type.
//
// The name is constant text the compiler folds into an offset when the graph
// compiles; a name written at another value type is a graph error rather than
// a conversion.
macro_rules! attribute_reads {
($($(#[$meta:meta])* $node:ident: $row:ty => $value:ty;)*) => {
$(
$(#[$meta])*
#[node_macro::node(category("Attributes: Read"))]
pub fn $node<'e, T>(
_: impl Ctx,
/// The content whose lanes carry the attribute; its element is never read.
(content, value): (T, Attr<'e, Named<Name0, $row>>),
/// The attribute name, folded into an offset when the graph compiles.
name: Named<Name0>,
) -> $value {
let _ = content;
*value
}
)*
};
}
attribute_reads! {
/// Reads a named `f64` attribute, such as `opacity` or `font_size`.
read_number_attribute: f64 => f64;
/// Reads a named `u64` attribute, such as a regex match's `start` or `end`.
read_integer_attribute: u64 => u64;
/// Reads a named `bool` attribute, such as `clipping_mask` or `clip`.
read_bool_attribute: bool => bool;
/// Reads a named `DVec2` attribute, such as an artboard's `location` or `dimensions`.
read_coordinate_attribute: DVec2 => DVec2;
/// Reads a named `DAffine2` attribute, such as `transform`.
read_transform_attribute: DAffine2 => DAffine2;
/// Reads a named `Color` attribute, such as an artboard's `background`.
read_color_attribute: Color => Color;
/// Reads a named `BlendMode` attribute, such as `blend_mode`.
read_blend_mode_attribute: core_types::blending::BlendMode => core_types::blending::BlendMode;
/// Reads a named gradient-shape attribute, such as `gradient_type`.
read_gradient_type_attribute: GradientTypeValue => GradientTypeValue;
/// Reads a named gradient-spread attribute, such as `spread_method`.
read_spread_method_attribute: GradientSpreadMethod => GradientSpreadMethod;
}
/// Joins two levels of the same type, the base's lanes followed by the new's.