Centralize attribute strings in consts and rename "editor:layer" to "editor:layer_path" (#4076)
* Rename "editor:layer" to "editor:layer_path" and centralize it in a const * Centralize "editor:merged_layers" in a const * Centralize all other attributes in consts * Rename consts with ATTR_ prefix * Format
This commit is contained in:
parent
5774ec215d
commit
0847d7b0ab
|
|
@ -993,7 +993,7 @@ fn table_node_id_path_layout_with_breadcrumb(path: &Table<NodeId>, data: &mut La
|
||||||
/// Mirrors [`dispatch_value_widget`] but routes to [`TableRowLayout::layout_with_breadcrumb`].
|
/// Mirrors [`dispatch_value_widget`] but routes to [`TableRowLayout::layout_with_breadcrumb`].
|
||||||
/// Returns `None` for unrecognized types.
|
/// Returns `None` for unrecognized types.
|
||||||
fn drilldown_attribute_layout(any: &dyn Any, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
|
fn drilldown_attribute_layout(any: &dyn Any, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
|
||||||
// `Table<NodeId>` is interpreted as a path (e.g. the `editor:layer` attribute), so each item's NodeId value
|
// `Table<NodeId>` is interpreted as a path (e.g. the `editor:layer_path` attribute), so each item's NodeId value
|
||||||
// resolves against the prefix made up of preceding items. Handled before the generic `Table<T>` blanket impl.
|
// resolves against the prefix made up of preceding items. Handled before the generic `Table<T>` blanket impl.
|
||||||
if let Some(path) = any.downcast_ref::<Table<NodeId>>() {
|
if let Some(path) = any.downcast_ref::<Table<NodeId>>() {
|
||||||
return Some(table_node_id_path_layout_with_breadcrumb(path, data));
|
return Some(table_node_id_path_layout_with_breadcrumb(path, data));
|
||||||
|
|
|
||||||
|
|
@ -239,12 +239,12 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::path_of_subgraph::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::path_of_subgraph::IDENTIFIER),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Stamp each item of the content with the parent layer's NodeId via the `editor:layer` attribute,
|
// Stamp each item of the content with the parent layer's NodeId via the `editor:layer_path` attribute,
|
||||||
// so editor tools (e.g. selection, click target routing) can trace data back to its owning layer.
|
// so editor tools (e.g. selection, click target routing) can trace data back to its owning layer.
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::node(NodeId(1), 0),
|
NodeInput::node(NodeId(1), 0),
|
||||||
NodeInput::value(TaggedValue::String(String::from("editor:layer")), false),
|
NodeInput::value(TaggedValue::String(graphene_std::ATTR_EDITOR_LAYER_PATH.to_string()), false),
|
||||||
NodeInput::node(NodeId(2), 0),
|
NodeInput::node(NodeId(2), 0),
|
||||||
],
|
],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::write_attribute::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::write_attribute::IDENTIFIER),
|
||||||
|
|
@ -374,12 +374,12 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::path_of_subgraph::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::path_of_subgraph::IDENTIFIER),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
// Stamp each item of the content with the parent layer's NodeId via the `editor:layer` attribute,
|
// Stamp each item of the content with the parent layer's NodeId via the `editor:layer_path` attribute,
|
||||||
// so editor tools (e.g. selection, click target routing) can trace data back to its owning layer.
|
// so editor tools (e.g. selection, click target routing) can trace data back to its owning layer.
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::node(NodeId(0), 0),
|
NodeInput::node(NodeId(0), 0),
|
||||||
NodeInput::value(TaggedValue::String(String::from("editor:layer")), false),
|
NodeInput::value(TaggedValue::String(graphene_std::ATTR_EDITOR_LAYER_PATH.to_string()), false),
|
||||||
NodeInput::node(NodeId(1), 0),
|
NodeInput::node(NodeId(1), 0),
|
||||||
],
|
],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::write_attribute::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(graphic::write_attribute::IDENTIFIER),
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use crate::messages::prelude::ViewportMessageHandler;
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
use core::f64::consts::{FRAC_PI_2, PI, TAU};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_std::ATTR_TRANSFORM;
|
||||||
use graphene_std::math::quad::Quad;
|
use graphene_std::math::quad::Quad;
|
||||||
use graphene_std::subpath::{self, Subpath};
|
use graphene_std::subpath::{self, Subpath};
|
||||||
use graphene_std::table::Table;
|
use graphene_std::table::Table;
|
||||||
|
|
@ -1170,7 +1171,7 @@ impl OverlayContextInternal {
|
||||||
// Use the existing bezier_to_path infrastructure to convert Vector to BezPath
|
// Use the existing bezier_to_path infrastructure to convert Vector to BezPath
|
||||||
let mut path = BezPath::new();
|
let mut path = BezPath::new();
|
||||||
let mut last_point = None;
|
let mut last_point = None;
|
||||||
let transform: DAffine2 = text_table.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = text_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
|
|
||||||
let Some(element) = text_table.element(index) else { continue };
|
let Some(element) = text_table.element(index) else { continue };
|
||||||
for (_, bezier, start_id, end_id) in element.segment_iter() {
|
for (_, bezier, start_id, end_id) in element.segment_iter() {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pub use num_traits;
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
pub use table::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_END, ATTR_NAME, ATTR_START, ATTR_TRANSFORM, ATTR_TYPE};
|
||||||
#[cfg(feature = "wasm")]
|
#[cfg(feature = "wasm")]
|
||||||
pub use tsify;
|
pub use tsify;
|
||||||
pub use types::Cow;
|
pub use types::Cow;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,45 @@ use dyn_any::{StaticType, StaticTypeSized};
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// Standard attribute keys used across the data flow
|
||||||
|
// =====================================================================
|
||||||
|
|
||||||
|
/// Attribute key for a row's `DAffine2` transformation, applied when rendering and when accumulating
|
||||||
|
/// transforms through nested compositions.
|
||||||
|
pub const ATTR_TRANSFORM: &str = "transform";
|
||||||
|
|
||||||
|
/// Attribute key for a row's `AlphaBlending` (blend mode + opacity + fill + clip), composed
|
||||||
|
/// multiplicatively through nested compositions.
|
||||||
|
pub const ATTR_ALPHA_BLENDING: &str = "alpha_blending";
|
||||||
|
|
||||||
|
/// Attribute key under which each row of an editor-aware layer stores a `Table<NodeId>` describing the
|
||||||
|
/// path (from the root document network) to the layer node that owns the row. Editor tools use this to
|
||||||
|
/// route clicks/selection back to the originating layer at any nesting depth.
|
||||||
|
pub const ATTR_EDITOR_LAYER_PATH: &str = "editor:layer_path";
|
||||||
|
|
||||||
|
/// Attribute key under which a row stores a `Table<Graphic>` snapshot of the upstream content that fed
|
||||||
|
/// into a destructive merge (Boolean Operation, Flatten Path, Morph, Rasterize, etc.). The renderer
|
||||||
|
/// recurses into this snapshot during metadata collection so the editor can still surface click targets
|
||||||
|
/// for the original child layers after their content has been collapsed into a single output.
|
||||||
|
pub const ATTR_EDITOR_MERGED_LAYERS: &str = "editor:merged_layers";
|
||||||
|
|
||||||
|
/// Attribute key for the byte offset where a regex match begins in the input string, set by the
|
||||||
|
/// `regex_find_all` and `regex_capture` text nodes.
|
||||||
|
pub const ATTR_START: &str = "start";
|
||||||
|
|
||||||
|
/// Attribute key for the byte offset where a regex match ends in the input string, set by the
|
||||||
|
/// `regex_find_all` and `regex_capture` text nodes.
|
||||||
|
pub const ATTR_END: &str = "end";
|
||||||
|
|
||||||
|
/// Attribute key for a regex named-capture-group's name (empty for unnamed groups), set by the
|
||||||
|
/// `regex_capture` text node.
|
||||||
|
pub const ATTR_NAME: &str = "name";
|
||||||
|
|
||||||
|
/// Attribute key for a JSON value's type (`"string"`, `"number"`, `"object"`, etc.), set by the
|
||||||
|
/// `json_query_all` text node alongside each extracted value.
|
||||||
|
pub const ATTR_TYPE: &str = "type";
|
||||||
|
|
||||||
// =====================
|
// =====================
|
||||||
// TRAIT: AttributeValue
|
// TRAIT: AttributeValue
|
||||||
// =====================
|
// =====================
|
||||||
|
|
@ -747,7 +786,7 @@ impl<T: BoundingBox> BoundingBox for Table<T> {
|
||||||
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
|
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
|
||||||
let mut combined_bounds = None;
|
let mut combined_bounds = None;
|
||||||
|
|
||||||
for (element, row_transform) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<DAffine2>("transform")) {
|
for (element, row_transform) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<DAffine2>(ATTR_TRANSFORM)) {
|
||||||
match element.bounding_box(transform * row_transform, include_stroke) {
|
match element.bounding_box(transform * row_transform, include_stroke) {
|
||||||
RenderBoundingBox::None => continue,
|
RenderBoundingBox::None => continue,
|
||||||
RenderBoundingBox::Infinite => return RenderBoundingBox::Infinite,
|
RenderBoundingBox::Infinite => return RenderBoundingBox::Infinite,
|
||||||
|
|
@ -793,10 +832,10 @@ impl<T: graphene_hash::CacheHash> graphene_hash::CacheHash for Table<T> {
|
||||||
for element in self.iter_element_values() {
|
for element in self.iter_element_values() {
|
||||||
element.cache_hash(state);
|
element.cache_hash(state);
|
||||||
}
|
}
|
||||||
for transform in self.iter_attribute_values_or_default::<DAffine2>("transform") {
|
for transform in self.iter_attribute_values_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
graphene_hash::CacheHash::cache_hash(&transform, state);
|
graphene_hash::CacheHash::cache_hash(&transform, state);
|
||||||
}
|
}
|
||||||
for alpha_blending in self.iter_attribute_values_or_default::<crate::AlphaBlending>("alpha_blending") {
|
for alpha_blending in self.iter_attribute_values_or_default::<crate::AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
alpha_blending.cache_hash(state);
|
alpha_blending.cache_hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -811,14 +850,14 @@ impl<T: PartialEq> PartialEq for Table<T> {
|
||||||
impl<T> ApplyTransform for Table<T> {
|
impl<T> ApplyTransform for Table<T> {
|
||||||
/// Right-multiplies the modification into each row's transform attribute.
|
/// Right-multiplies the modification into each row's transform attribute.
|
||||||
fn apply_transform(&mut self, modification: &DAffine2) {
|
fn apply_transform(&mut self, modification: &DAffine2) {
|
||||||
for transform in self.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
for transform in self.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
*transform *= *modification;
|
*transform *= *modification;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Left-multiplies the modification into each row's transform attribute.
|
/// Left-multiplies the modification into each row's transform attribute.
|
||||||
fn left_apply_transform(&mut self, modification: &DAffine2) {
|
fn left_apply_transform(&mut self, modification: &DAffine2) {
|
||||||
for transform in self.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
for transform in self.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
*transform = *modification * *transform;
|
*transform = *modification * *transform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::graphic::Graphic;
|
use crate::graphic::Graphic;
|
||||||
use core_types::Color;
|
|
||||||
use core_types::blending::AlphaBlending;
|
use core_types::blending::AlphaBlending;
|
||||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||||
use core_types::math::quad::Quad;
|
use core_types::math::quad::Quad;
|
||||||
|
|
@ -7,6 +6,7 @@ use core_types::render_complexity::RenderComplexity;
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
use core_types::transform::Transform;
|
use core_types::transform::Transform;
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
|
use core_types::{ATTR_TRANSFORM, Color};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use graphene_hash::CacheHash;
|
use graphene_hash::CacheHash;
|
||||||
|
|
@ -52,7 +52,7 @@ impl BoundingBox for Artboard {
|
||||||
|
|
||||||
let mut combined_bounds = None;
|
let mut combined_bounds = None;
|
||||||
|
|
||||||
for (element, row_transform) in self.content.iter_element_values().zip(self.content.iter_attribute_values_or_default::<DAffine2>("transform")) {
|
for (element, row_transform) in self.content.iter_element_values().zip(self.content.iter_attribute_values_or_default::<DAffine2>(ATTR_TRANSFORM)) {
|
||||||
match element.bounding_box(transform * row_transform, include_stroke) {
|
match element.bounding_box(transform * row_transform, include_stroke) {
|
||||||
RenderBoundingBox::None => continue,
|
RenderBoundingBox::None => continue,
|
||||||
RenderBoundingBox::Infinite => return RenderBoundingBox::Infinite,
|
RenderBoundingBox::Infinite => return RenderBoundingBox::Infinite,
|
||||||
|
|
@ -113,7 +113,7 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
|
||||||
alpha_blending: Vec<AlphaBlending>,
|
alpha_blending: Vec<AlphaBlending>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||||
Ok(match ArtboardFormat::deserialize(deserializer)? {
|
Ok(match ArtboardFormat::deserialize(deserializer)? {
|
||||||
ArtboardFormat::ArtboardGroup(artboard_group) => artboard_group.artboards.into_iter().map(|(artboard, _)| TableRow::new_from_element(artboard)).collect(),
|
ArtboardFormat::ArtboardGroup(artboard_group) => artboard_group.artboards.into_iter().map(|(artboard, _)| TableRow::new_from_element(artboard)).collect(),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use core_types::Color;
|
|
||||||
use core_types::blending::AlphaBlending;
|
use core_types::blending::AlphaBlending;
|
||||||
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||||
use core_types::graphene_hash::CacheHash;
|
use core_types::graphene_hash::CacheHash;
|
||||||
|
|
@ -6,6 +5,7 @@ use core_types::ops::TableConvert;
|
||||||
use core_types::render_complexity::RenderComplexity;
|
use core_types::render_complexity::RenderComplexity;
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
|
use core_types::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, Color};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use raster_types::{CPU, GPU, Raster};
|
use raster_types::{CPU, GPU, Raster};
|
||||||
|
|
@ -141,19 +141,19 @@ fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic
|
||||||
|
|
||||||
fn flatten_recursive<T>(output: &mut Table<T>, current_graphic_table: Table<Graphic>, extract_variant: fn(Graphic) -> Option<Table<T>>) {
|
fn flatten_recursive<T>(output: &mut Table<T>, current_graphic_table: Table<Graphic>, extract_variant: fn(Graphic) -> Option<Table<T>>) {
|
||||||
for current_graphic_row in current_graphic_table.into_iter() {
|
for current_graphic_row in current_graphic_table.into_iter() {
|
||||||
let layer_path: Table<NodeId> = current_graphic_row.attribute_cloned_or_default("editor:layer");
|
let layer_path: Table<NodeId> = current_graphic_row.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH);
|
||||||
let current_transform: DAffine2 = current_graphic_row.attribute_cloned_or_default("transform");
|
let current_transform: DAffine2 = current_graphic_row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let current_alpha_blending: AlphaBlending = current_graphic_row.attribute_cloned_or_default("alpha_blending");
|
let current_alpha_blending: AlphaBlending = current_graphic_row.attribute_cloned_or_default(ATTR_ALPHA_BLENDING);
|
||||||
|
|
||||||
match current_graphic_row.into_element() {
|
match current_graphic_row.into_element() {
|
||||||
// Recurse into nested `Table<Graphic>` items, composing the parent's transform onto each child
|
// Recurse into nested `Table<Graphic>` items, composing the parent's transform onto each child
|
||||||
Graphic::Graphic(mut sub_table) => {
|
Graphic::Graphic(mut sub_table) => {
|
||||||
for index in 0..sub_table.len() {
|
for index in 0..sub_table.len() {
|
||||||
let child_transform: DAffine2 = sub_table.attribute_cloned_or_default("transform", index);
|
let child_transform: DAffine2 = sub_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let child_alpha_blending: AlphaBlending = sub_table.attribute_cloned_or_default("alpha_blending", index);
|
let child_alpha_blending: AlphaBlending = sub_table.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
|
|
||||||
sub_table.set_attribute("transform", index, current_transform * child_transform);
|
sub_table.set_attribute(ATTR_TRANSFORM, index, current_transform * child_transform);
|
||||||
sub_table.set_attribute("alpha_blending", index, compose_alpha_blending(current_alpha_blending, child_alpha_blending));
|
sub_table.set_attribute(ATTR_ALPHA_BLENDING, index, compose_alpha_blending(current_alpha_blending, child_alpha_blending));
|
||||||
}
|
}
|
||||||
|
|
||||||
flatten_recursive(output, sub_table, extract_variant);
|
flatten_recursive(output, sub_table, extract_variant);
|
||||||
|
|
@ -162,13 +162,13 @@ fn flatten_graphic_table<T>(content: Table<Graphic>, extract_variant: fn(Graphic
|
||||||
other => {
|
other => {
|
||||||
if let Some(typed_table) = extract_variant(other) {
|
if let Some(typed_table) = extract_variant(other) {
|
||||||
for row in typed_table.into_iter() {
|
for row in typed_table.into_iter() {
|
||||||
let row_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let row_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let row_alpha_blending: AlphaBlending = row.attribute_cloned_or_default("alpha_blending");
|
let row_alpha_blending: AlphaBlending = row.attribute_cloned_or_default(ATTR_ALPHA_BLENDING);
|
||||||
let (element, mut attributes) = row.into_parts();
|
let (element, mut attributes) = row.into_parts();
|
||||||
|
|
||||||
attributes.insert("transform", current_transform * row_transform);
|
attributes.insert(ATTR_TRANSFORM, current_transform * row_transform);
|
||||||
attributes.insert("alpha_blending", compose_alpha_blending(current_alpha_blending, row_alpha_blending));
|
attributes.insert(ATTR_ALPHA_BLENDING, compose_alpha_blending(current_alpha_blending, row_alpha_blending));
|
||||||
attributes.insert("editor:layer", layer_path.clone());
|
attributes.insert(ATTR_EDITOR_LAYER_PATH, layer_path.clone());
|
||||||
|
|
||||||
output.push(TableRow::from_parts(element, attributes));
|
output.push(TableRow::from_parts(element, attributes));
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +321,7 @@ impl Graphic {
|
||||||
|
|
||||||
pub fn had_clip_enabled(&self) -> bool {
|
pub fn had_clip_enabled(&self) -> bool {
|
||||||
fn all_clipped<T>(table: &Table<T>) -> bool {
|
fn all_clipped<T>(table: &Table<T>) -> bool {
|
||||||
table.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending").all(|a| a.clip)
|
table.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING).all(|a| a.clip)
|
||||||
}
|
}
|
||||||
match self {
|
match self {
|
||||||
Graphic::Vector(table) => all_clipped(table),
|
Graphic::Vector(table) => all_clipped(table),
|
||||||
|
|
@ -337,7 +337,7 @@ impl Graphic {
|
||||||
match self {
|
match self {
|
||||||
Graphic::Vector(vector) => vector
|
Graphic::Vector(vector) => vector
|
||||||
.iter_element_values()
|
.iter_element_values()
|
||||||
.zip(vector.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending"))
|
.zip(vector.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING))
|
||||||
.all(|(element, alpha_blending)| {
|
.all(|(element, alpha_blending)| {
|
||||||
(alpha_blending.opacity > 1. - f32::EPSILON) && element.style.fill().is_opaque() && element.style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
(alpha_blending.opacity > 1. - f32::EPSILON) && element.style.fill().is_opaque() && element.style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
|
||||||
}),
|
}),
|
||||||
|
|
@ -504,7 +504,7 @@ pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Res
|
||||||
Table(serde_json::Value),
|
Table(serde_json::Value),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||||
Ok(match GraphicFormat::deserialize(deserializer)? {
|
Ok(match GraphicFormat::deserialize(deserializer)? {
|
||||||
GraphicFormat::OldGraphicGroup(old) => old.elements.into_iter().map(|(graphic, _)| TableRow::new_from_element(graphic)).collect(),
|
GraphicFormat::OldGraphicGroup(old) => old.elements.into_iter().map(|(graphic, _)| TableRow::new_from_element(graphic)).collect(),
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ pub mod migrations {
|
||||||
|
|
||||||
Ok(match VectorFormat::deserialize(deserializer)? {
|
Ok(match VectorFormat::deserialize(deserializer)? {
|
||||||
VectorFormat::Vector(vector) => Table::new_from_element(vector),
|
VectorFormat::Vector(vector) => Table::new_from_element(vector),
|
||||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||||
VectorFormat::OldVectorData(old) => Table::new_from_element(Vector {
|
VectorFormat::OldVectorData(old) => Table::new_from_element(Vector {
|
||||||
style: old.style,
|
style: old.style,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::raster_types::{CPU, Raster};
|
use crate::raster_types::{CPU, Raster};
|
||||||
use crate::{Bitmap, BitmapMut};
|
use crate::{Bitmap, BitmapMut};
|
||||||
use core_types::AlphaBlending;
|
|
||||||
use core_types::Color;
|
|
||||||
use core_types::color::float_to_srgb_u8;
|
use core_types::color::float_to_srgb_u8;
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
|
use core_types::{ATTR_ALPHA_BLENDING, ATTR_TRANSFORM, AlphaBlending, Color};
|
||||||
// use crate::vector::Vector; // TODO: Check if Vector is actually used, if so handle differently
|
// use crate::vector::Vector; // TODO: Check if Vector is actually used, if so handle differently
|
||||||
use core_types::color::*;
|
use core_types::color::*;
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
|
|
@ -319,7 +318,7 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||||
Table::new_from_element(Raster::new_cpu(table.element(0).unwrap().clone()))
|
Table::new_from_element(Raster::new_cpu(table.element(0).unwrap().clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||||
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
// to recover the elements. Per-item attribute values are populated at runtime by the node graph.
|
||||||
fn old_table_to_new_table<T>(old_table: OldTable<T>) -> Table<T> {
|
fn old_table_to_new_table<T>(old_table: OldTable<T>) -> Table<T> {
|
||||||
old_table.element.into_iter().map(TableRow::new_from_element).collect()
|
old_table.element.into_iter().map(TableRow::new_from_element).collect()
|
||||||
|
|
@ -339,8 +338,8 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||||
FormatVersions::Image(image) => Table::new_from_element(Raster::new_cpu(image)),
|
FormatVersions::Image(image) => Table::new_from_element(Raster::new_cpu(image)),
|
||||||
FormatVersions::OldImageFrame(OldImageFrame { image, transform, alpha_blending }) => {
|
FormatVersions::OldImageFrame(OldImageFrame { image, transform, alpha_blending }) => {
|
||||||
let mut image_frame_table = Table::new_from_element(Raster::new_cpu(image));
|
let mut image_frame_table = Table::new_from_element(Raster::new_cpu(image));
|
||||||
image_frame_table.set_attribute("transform", 0, transform);
|
image_frame_table.set_attribute(ATTR_TRANSFORM, 0, transform);
|
||||||
image_frame_table.set_attribute("alpha_blending", 0, alpha_blending);
|
image_frame_table.set_attribute(ATTR_ALPHA_BLENDING, 0, alpha_blending);
|
||||||
image_frame_table
|
image_frame_table
|
||||||
}
|
}
|
||||||
FormatVersions::OlderImageFrameTable(old_table) => from_image_frame_table(older_table_to_new_table(old_table)),
|
FormatVersions::OlderImageFrameTable(old_table) => from_image_frame_table(older_table_to_new_table(old_table)),
|
||||||
|
|
@ -430,7 +429,7 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
|
||||||
RasterTableRow(TableRow<Raster<CPU>>),
|
RasterTableRow(TableRow<Raster<CPU>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes (transform, alpha_blending, editor:layer) are not serialized, so migration only needs
|
// Attributes (transform, alpha_blending, editor:layer_path) are not serialized, so migration only needs
|
||||||
// to recover the element. Per-item attribute values are populated at runtime by the node graph.
|
// to recover the element. Per-item attribute values are populated at runtime by the node graph.
|
||||||
Ok(match FormatVersions::deserialize(deserializer)? {
|
Ok(match FormatVersions::deserialize(deserializer)? {
|
||||||
FormatVersions::Image(image) => TableRow::new_from_element(Raster::new_cpu(image)),
|
FormatVersions::Image(image) => TableRow::new_from_element(Raster::new_cpu(image)),
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use core_types::render_complexity::RenderComplexity;
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
use core_types::transform::{Footprint, Transform};
|
use core_types::transform::{Footprint, Transform};
|
||||||
use core_types::uuid::{NodeId, generate_uuid};
|
use core_types::uuid::{NodeId, generate_uuid};
|
||||||
|
use core_types::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_hash::CacheHashWrapper;
|
use graphene_hash::CacheHashWrapper;
|
||||||
|
|
@ -412,9 +413,9 @@ impl Render for Graphic {
|
||||||
metadata.upstream_footprints.insert(element_id, footprint);
|
metadata.upstream_footprints.insert(element_id, footprint);
|
||||||
// TODO: Find a way to handle more than the first item
|
// TODO: Find a way to handle more than the first item
|
||||||
if !table.is_empty() {
|
if !table.is_empty() {
|
||||||
let layer_path: Table<NodeId> = table.attribute_cloned_or_default("editor:layer", 0);
|
let layer_path: Table<NodeId> = table.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, 0);
|
||||||
let layer = layer_path.iter_element_values().next_back().copied();
|
let layer = layer_path.iter_element_values().next_back().copied();
|
||||||
let transform: DAffine2 = table.attribute_cloned_or_default("transform", 0);
|
let transform: DAffine2 = table.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||||
|
|
||||||
metadata.first_element_source_id.insert(element_id, layer);
|
metadata.first_element_source_id.insert(element_id, layer);
|
||||||
metadata.local_transforms.insert(element_id, transform);
|
metadata.local_transforms.insert(element_id, transform);
|
||||||
|
|
@ -425,7 +426,7 @@ impl Render for Graphic {
|
||||||
|
|
||||||
// TODO: Find a way to handle more than the first item
|
// TODO: Find a way to handle more than the first item
|
||||||
if !table.is_empty() {
|
if !table.is_empty() {
|
||||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default(ATTR_TRANSFORM, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Graphic::RasterGPU(table) => {
|
Graphic::RasterGPU(table) => {
|
||||||
|
|
@ -433,7 +434,7 @@ impl Render for Graphic {
|
||||||
|
|
||||||
// TODO: Find a way to handle more than the first item
|
// TODO: Find a way to handle more than the first item
|
||||||
if !table.is_empty() {
|
if !table.is_empty() {
|
||||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default(ATTR_TRANSFORM, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Graphic::Color(table) => {
|
Graphic::Color(table) => {
|
||||||
|
|
@ -441,7 +442,7 @@ impl Render for Graphic {
|
||||||
|
|
||||||
// TODO: Find a way to handle more than the first item
|
// TODO: Find a way to handle more than the first item
|
||||||
if !table.is_empty() {
|
if !table.is_empty() {
|
||||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default(ATTR_TRANSFORM, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Graphic::Gradient(table) => {
|
Graphic::Gradient(table) => {
|
||||||
|
|
@ -449,7 +450,7 @@ impl Render for Graphic {
|
||||||
|
|
||||||
// TODO: Find a way to handle more than the first item
|
// TODO: Find a way to handle more than the first item
|
||||||
if !table.is_empty() {
|
if !table.is_empty() {
|
||||||
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default("transform", 0));
|
metadata.local_transforms.insert(element_id, table.attribute_cloned_or_default(ATTR_TRANSFORM, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -551,7 +552,7 @@ impl Render for Artboard {
|
||||||
|attributes| {
|
|attributes| {
|
||||||
let matrix = format_transform_matrix(self.transform());
|
let matrix = format_transform_matrix(self.transform());
|
||||||
if !matrix.is_empty() {
|
if !matrix.is_empty() {
|
||||||
attributes.push("transform", matrix);
|
attributes.push(ATTR_TRANSFORM, matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.clip {
|
if self.clip {
|
||||||
|
|
@ -656,7 +657,7 @@ impl Render for Table<Artboard> {
|
||||||
|
|
||||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option<NodeId>) {
|
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option<NodeId>) {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default("editor:layer", index);
|
let layer_path: Table<NodeId> = self.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||||
let layer = layer_path.iter_element_values().next_back().copied();
|
let layer = layer_path.iter_element_values().next_back().copied();
|
||||||
self.element(index).unwrap().collect_metadata(metadata, footprint, layer);
|
self.element(index).unwrap().collect_metadata(metadata, footprint, layer);
|
||||||
}
|
}
|
||||||
|
|
@ -678,8 +679,8 @@ impl Render for Table<Graphic> {
|
||||||
let mut mask_state = None;
|
let mut mask_state = None;
|
||||||
|
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
let element = self.element(index).unwrap();
|
let element = self.element(index).unwrap();
|
||||||
|
|
||||||
render.parent_tag(
|
render.parent_tag(
|
||||||
|
|
@ -687,7 +688,7 @@ impl Render for Table<Graphic> {
|
||||||
|attributes| {
|
|attributes| {
|
||||||
let matrix = format_transform_matrix(transform);
|
let matrix = format_transform_matrix(transform);
|
||||||
if !matrix.is_empty() {
|
if !matrix.is_empty() {
|
||||||
attributes.push("transform", matrix);
|
attributes.push(ATTR_TRANSFORM, matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||||
|
|
@ -732,9 +733,9 @@ impl Render for Table<Graphic> {
|
||||||
let mut mask_element_and_transform = None;
|
let mut mask_element_and_transform = None;
|
||||||
|
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let transform = transform * row_transform;
|
let transform = transform * row_transform;
|
||||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
let element = self.element(index).unwrap();
|
let element = self.element(index).unwrap();
|
||||||
|
|
||||||
let mut layer = false;
|
let mut layer = false;
|
||||||
|
|
@ -806,8 +807,8 @@ impl Render for Table<Graphic> {
|
||||||
|
|
||||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
|
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default("editor:layer", index);
|
let layer_path: Table<NodeId> = self.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||||
let layer = layer_path.iter_element_values().next_back().copied();
|
let layer = layer_path.iter_element_values().next_back().copied();
|
||||||
let element = self.element(index).unwrap();
|
let element = self.element(index).unwrap();
|
||||||
|
|
||||||
|
|
@ -817,7 +818,7 @@ impl Render for Table<Graphic> {
|
||||||
if let Some(element_id) = layer {
|
if let Some(element_id) = layer {
|
||||||
element.collect_metadata(metadata, footprint, Some(element_id));
|
element.collect_metadata(metadata, footprint, Some(element_id));
|
||||||
} else {
|
} else {
|
||||||
// Recurse through anonymous wrapper items to reach nested content with editor:layer tags
|
// Recurse through anonymous wrapper items to reach nested content with editor:layer_path tags
|
||||||
element.collect_metadata(metadata, footprint, None);
|
element.collect_metadata(metadata, footprint, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -826,7 +827,7 @@ impl Render for Table<Graphic> {
|
||||||
let mut all_upstream_click_targets = Vec::new();
|
let mut all_upstream_click_targets = Vec::new();
|
||||||
|
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let element = self.element(index).unwrap();
|
let element = self.element(index).unwrap();
|
||||||
let mut new_click_targets = Vec::new();
|
let mut new_click_targets = Vec::new();
|
||||||
element.add_upstream_click_targets(&mut new_click_targets);
|
element.add_upstream_click_targets(&mut new_click_targets);
|
||||||
|
|
@ -844,7 +845,7 @@ impl Render for Table<Graphic> {
|
||||||
|
|
||||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let element = self.element(index).unwrap();
|
let element = self.element(index).unwrap();
|
||||||
let mut new_click_targets = Vec::new();
|
let mut new_click_targets = Vec::new();
|
||||||
|
|
||||||
|
|
@ -863,7 +864,7 @@ impl Render for Table<Graphic> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_ids_from_hash(&mut self, _reference: Option<NodeId>) {
|
fn new_ids_from_hash(&mut self, _reference: Option<NodeId>) {
|
||||||
let (elements, layers) = self.element_and_attribute_slices_mut::<Table<NodeId>>("editor:layer");
|
let (elements, layers) = self.element_and_attribute_slices_mut::<Table<NodeId>>(ATTR_EDITOR_LAYER_PATH);
|
||||||
for (element, layer) in elements.iter_mut().zip(layers.iter()) {
|
for (element, layer) in elements.iter_mut().zip(layers.iter()) {
|
||||||
element.new_ids_from_hash(layer.iter_element_values().next_back().copied());
|
element.new_ids_from_hash(layer.iter_element_values().next_back().copied());
|
||||||
}
|
}
|
||||||
|
|
@ -874,8 +875,8 @@ impl Render for Table<Vector> {
|
||||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let Some(vector) = self.element(index) else { continue };
|
let Some(vector) = self.element(index) else { continue };
|
||||||
let multiplied_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let multiplied_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
|
|
||||||
// Only consider strokes with non-zero weight, since default strokes with zero weight would prevent assigning the correct stroke transform
|
// Only consider strokes with non-zero weight, since default strokes with zero weight would prevent assigning the correct stroke transform
|
||||||
let has_real_stroke = vector.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
let has_real_stroke = vector.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
||||||
|
|
@ -915,7 +916,7 @@ impl Render for Table<Vector> {
|
||||||
attributes.push("d", path.clone());
|
attributes.push("d", path.clone());
|
||||||
let matrix = format_transform_matrix(element_transform);
|
let matrix = format_transform_matrix(element_transform);
|
||||||
if !matrix.is_empty() {
|
if !matrix.is_empty() {
|
||||||
attributes.push("transform", matrix);
|
attributes.push(ATTR_TRANSFORM, matrix);
|
||||||
}
|
}
|
||||||
let mut style = vector.style.clone();
|
let mut style = vector.style.clone();
|
||||||
style.clear_stroke();
|
style.clear_stroke();
|
||||||
|
|
@ -940,8 +941,8 @@ impl Render for Table<Vector> {
|
||||||
|
|
||||||
let vector_row = Table::new_from_row(
|
let vector_row = Table::new_from_row(
|
||||||
TableRow::new_from_element(cloned_vector)
|
TableRow::new_from_element(cloned_vector)
|
||||||
.with_attribute("transform", multiplied_transform)
|
.with_attribute(ATTR_TRANSFORM, multiplied_transform)
|
||||||
.with_attribute("alpha_blending", alpha_blending),
|
.with_attribute(ATTR_ALPHA_BLENDING, alpha_blending),
|
||||||
);
|
);
|
||||||
|
|
||||||
(id, mask_type, vector_row)
|
(id, mask_type, vector_row)
|
||||||
|
|
@ -957,7 +958,7 @@ impl Render for Table<Vector> {
|
||||||
attributes.push("d", face_d.clone());
|
attributes.push("d", face_d.clone());
|
||||||
let matrix = format_transform_matrix(element_transform);
|
let matrix = format_transform_matrix(element_transform);
|
||||||
if !matrix.is_empty() {
|
if !matrix.is_empty() {
|
||||||
attributes.push("transform", matrix);
|
attributes.push(ATTR_TRANSFORM, matrix);
|
||||||
}
|
}
|
||||||
let mut style = vector.style.clone();
|
let mut style = vector.style.clone();
|
||||||
style.clear_stroke();
|
style.clear_stroke();
|
||||||
|
|
@ -978,7 +979,7 @@ impl Render for Table<Vector> {
|
||||||
attributes.push("d", path.clone());
|
attributes.push("d", path.clone());
|
||||||
let matrix = format_transform_matrix(element_transform);
|
let matrix = format_transform_matrix(element_transform);
|
||||||
if !matrix.is_empty() {
|
if !matrix.is_empty() {
|
||||||
attributes.push("transform", matrix);
|
attributes.push(ATTR_TRANSFORM, matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
let defs = &mut attributes.0.svg_defs;
|
let defs = &mut attributes.0.svg_defs;
|
||||||
|
|
@ -1043,7 +1044,7 @@ impl Render for Table<Vector> {
|
||||||
attributes.push("d", path);
|
attributes.push("d", path);
|
||||||
let matrix = format_transform_matrix(element_transform);
|
let matrix = format_transform_matrix(element_transform);
|
||||||
if !matrix.is_empty() {
|
if !matrix.is_empty() {
|
||||||
attributes.push("transform", matrix);
|
attributes.push(ATTR_TRANSFORM, matrix);
|
||||||
}
|
}
|
||||||
let mut style = vector.style.clone();
|
let mut style = vector.style.clone();
|
||||||
style.clear_stroke();
|
style.clear_stroke();
|
||||||
|
|
@ -1068,8 +1069,8 @@ impl Render for Table<Vector> {
|
||||||
use graphic_types::vector_types::vector;
|
use graphic_types::vector_types::vector;
|
||||||
|
|
||||||
let Some(element) = self.element(index) else { continue };
|
let Some(element) = self.element(index) else { continue };
|
||||||
let row_transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let row_transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
let multiplied_transform = parent_transform * row_transform;
|
let multiplied_transform = parent_transform * row_transform;
|
||||||
let has_real_stroke = element.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
let has_real_stroke = element.style.stroke().filter(|stroke| stroke.weight() > 0.);
|
||||||
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform.matrix2.determinant() != 0.);
|
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform.matrix2.determinant() != 0.);
|
||||||
|
|
@ -1257,8 +1258,8 @@ impl Render for Table<Vector> {
|
||||||
|
|
||||||
let vector_table = Table::new_from_row(
|
let vector_table = Table::new_from_row(
|
||||||
TableRow::new_from_element(cloned_element)
|
TableRow::new_from_element(cloned_element)
|
||||||
.with_attribute("transform", row_transform)
|
.with_attribute(ATTR_TRANSFORM, row_transform)
|
||||||
.with_attribute("alpha_blending", alpha_blending),
|
.with_attribute(ATTR_ALPHA_BLENDING, alpha_blending),
|
||||||
);
|
);
|
||||||
|
|
||||||
let bounds = element.bounding_box_with_transform(multiplied_transform).unwrap_or(layer_bounds);
|
let bounds = element.bounding_box_with_transform(multiplied_transform).unwrap_or(layer_bounds);
|
||||||
|
|
@ -1329,12 +1330,12 @@ impl Render for Table<Vector> {
|
||||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, caller_element_id: Option<NodeId>) {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let Some(vector) = self.element(index) else { continue };
|
let Some(vector) = self.element(index) else { continue };
|
||||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let layer_path: Table<NodeId> = self.attribute_cloned_or_default("editor:layer", index);
|
let layer_path: Table<NodeId> = self.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||||
let layer = layer_path.iter_element_values().next_back().copied();
|
let layer = layer_path.iter_element_values().next_back().copied();
|
||||||
|
|
||||||
if let Some(element_id) = caller_element_id.or(layer) {
|
if let Some(element_id) = caller_element_id.or(layer) {
|
||||||
// When recovering element_id from the item's editor:layer tag (because the caller
|
// When recovering element_id from the item's editor:layer_path tag (because the caller
|
||||||
// passed None), also store the transform metadata that Graphic::collect_metadata
|
// passed None), also store the transform metadata that Graphic::collect_metadata
|
||||||
// normally provides but skipped due to the None element_id.
|
// normally provides but skipped due to the None element_id.
|
||||||
if caller_element_id.is_none() {
|
if caller_element_id.is_none() {
|
||||||
|
|
@ -1378,7 +1379,7 @@ impl Render for Table<Vector> {
|
||||||
// If this item carries a snapshot of upstream graphic content (e.g. it was produced by Boolean Operation,
|
// If this item carries a snapshot of upstream graphic content (e.g. it was produced by Boolean Operation,
|
||||||
// Flatten Path, Morph, or any other destructive merge), recurse into that snapshot so the editor can
|
// Flatten Path, Morph, or any other destructive merge), recurse into that snapshot so the editor can
|
||||||
// surface the original child layers' click targets.
|
// surface the original child layers' click targets.
|
||||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>("editor:merged_layers", index);
|
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, index);
|
||||||
if !upstream_nested_layers.is_empty() {
|
if !upstream_nested_layers.is_empty() {
|
||||||
let mut upstream_footprint = footprint;
|
let mut upstream_footprint = footprint;
|
||||||
upstream_footprint.transform *= transform;
|
upstream_footprint.transform *= transform;
|
||||||
|
|
@ -1390,7 +1391,7 @@ impl Render for Table<Vector> {
|
||||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let Some(vector) = self.element(index) else { continue };
|
let Some(vector) = self.element(index) else { continue };
|
||||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
|
|
||||||
let stroke_width = vector.style.stroke().as_ref().map_or(0., Stroke::effective_width);
|
let stroke_width = vector.style.stroke().as_ref().map_or(0., Stroke::effective_width);
|
||||||
let filled = vector.style.fill() != &Fill::None;
|
let filled = vector.style.fill() != &Fill::None;
|
||||||
|
|
@ -1435,8 +1436,8 @@ impl Render for Table<Raster<CPU>> {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let Some(image) = self.element(index) else { continue };
|
let Some(image) = self.element(index) else { continue };
|
||||||
|
|
||||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
|
|
||||||
if image.data.is_empty() {
|
if image.data.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1457,7 +1458,7 @@ impl Render for Table<Raster<CPU>> {
|
||||||
let matrix = DAffine2::from_scale_angle_translation(transform_values.0, transform_values.1, transform_values.2);
|
let matrix = DAffine2::from_scale_angle_translation(transform_values.0, transform_values.1, transform_values.2);
|
||||||
let matrix = format_transform_matrix(matrix);
|
let matrix = format_transform_matrix(matrix);
|
||||||
if !matrix.is_empty() {
|
if !matrix.is_empty() {
|
||||||
attributes.push("transform", matrix);
|
attributes.push(ATTR_TRANSFORM, matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes.push("width", size.x.to_string());
|
attributes.push("width", size.x.to_string());
|
||||||
|
|
@ -1500,7 +1501,7 @@ impl Render for Table<Raster<CPU>> {
|
||||||
attributes.push("href", base64_string);
|
attributes.push("href", base64_string);
|
||||||
let matrix = format_transform_matrix(transform);
|
let matrix = format_transform_matrix(transform);
|
||||||
if !matrix.is_empty() {
|
if !matrix.is_empty() {
|
||||||
attributes.push("transform", matrix);
|
attributes.push(ATTR_TRANSFORM, matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||||
|
|
@ -1522,7 +1523,7 @@ impl Render for Table<Raster<CPU>> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
||||||
|
|
||||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||||
|
|
@ -1537,7 +1538,7 @@ impl Render for Table<Raster<CPU>> {
|
||||||
layer = true;
|
layer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let transform_attribute: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let transform_attribute: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
|
|
||||||
if let RenderMode::Outline = render_params.render_mode {
|
if let RenderMode::Outline = render_params.render_mode {
|
||||||
let outline_transform: DAffine2 = transform * transform_attribute;
|
let outline_transform: DAffine2 = transform * transform_attribute;
|
||||||
|
|
@ -1577,7 +1578,7 @@ impl Render for Table<Raster<CPU>> {
|
||||||
metadata.upstream_footprints.insert(element_id, footprint);
|
metadata.upstream_footprints.insert(element_id, footprint);
|
||||||
// TODO: Find a way to handle more than one item of the `Table<Raster<...>>`
|
// TODO: Find a way to handle more than one item of the `Table<Raster<...>>`
|
||||||
if !self.is_empty() {
|
if !self.is_empty() {
|
||||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", 0);
|
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||||
metadata.local_transforms.insert(element_id, transform);
|
metadata.local_transforms.insert(element_id, transform);
|
||||||
|
|
||||||
// If this raster carries a snapshot of upstream graphic content (e.g. it was produced by Rasterize,
|
// If this raster carries a snapshot of upstream graphic content (e.g. it was produced by Rasterize,
|
||||||
|
|
@ -1586,7 +1587,7 @@ impl Render for Table<Raster<CPU>> {
|
||||||
// The snapshot was captured before Rasterize shifted its input transforms to align with the rasterization
|
// The snapshot was captured before Rasterize shifted its input transforms to align with the rasterization
|
||||||
// area, so the children are already in the coordinate space matching `footprint` here — we must NOT
|
// area, so the children are already in the coordinate space matching `footprint` here — we must NOT
|
||||||
// multiply in `transform` (which is the rasterization area, not a layer-stack transform).
|
// multiply in `transform` (which is the rasterization area, not a layer-stack transform).
|
||||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>("editor:merged_layers", 0);
|
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, 0);
|
||||||
if !upstream_nested_layers.is_empty() {
|
if !upstream_nested_layers.is_empty() {
|
||||||
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
||||||
}
|
}
|
||||||
|
|
@ -1609,7 +1610,7 @@ impl Render for Table<Raster<GPU>> {
|
||||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let Some(raster) = self.element(index) else { continue };
|
let Some(raster) = self.element(index) else { continue };
|
||||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
let blend_mode = match render_params.render_mode {
|
let blend_mode = match render_params.render_mode {
|
||||||
RenderMode::Outline => peniko::Mix::Normal,
|
RenderMode::Outline => peniko::Mix::Normal,
|
||||||
_ => alpha_blending.blend_mode.to_peniko(),
|
_ => alpha_blending.blend_mode.to_peniko(),
|
||||||
|
|
@ -1626,7 +1627,7 @@ impl Render for Table<Raster<GPU>> {
|
||||||
layer = true;
|
layer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let transform_attribute: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let transform_attribute: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
|
|
||||||
if let RenderMode::Outline = render_params.render_mode {
|
if let RenderMode::Outline = render_params.render_mode {
|
||||||
let outline_transform = transform * transform_attribute;
|
let outline_transform = transform * transform_attribute;
|
||||||
|
|
@ -1667,7 +1668,7 @@ impl Render for Table<Raster<GPU>> {
|
||||||
metadata.upstream_footprints.insert(element_id, footprint);
|
metadata.upstream_footprints.insert(element_id, footprint);
|
||||||
// TODO: Find a way to handle more than one item of the `Table<Raster<...>>`
|
// TODO: Find a way to handle more than one item of the `Table<Raster<...>>`
|
||||||
if !self.is_empty() {
|
if !self.is_empty() {
|
||||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", 0);
|
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||||
metadata.local_transforms.insert(element_id, transform);
|
metadata.local_transforms.insert(element_id, transform);
|
||||||
|
|
||||||
// If this raster carries a snapshot of upstream graphic content (e.g. it was produced by Rasterize,
|
// If this raster carries a snapshot of upstream graphic content (e.g. it was produced by Rasterize,
|
||||||
|
|
@ -1676,7 +1677,7 @@ impl Render for Table<Raster<GPU>> {
|
||||||
// The snapshot was captured before Rasterize shifted its input transforms to align with the rasterization
|
// The snapshot was captured before Rasterize shifted its input transforms to align with the rasterization
|
||||||
// area, so the children are already in the coordinate space matching `footprint` here — we must NOT
|
// area, so the children are already in the coordinate space matching `footprint` here — we must NOT
|
||||||
// multiply in `transform` (which is the rasterization area, not a layer-stack transform).
|
// multiply in `transform` (which is the rasterization area, not a layer-stack transform).
|
||||||
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>("editor:merged_layers", 0);
|
let upstream_nested_layers = self.attribute_cloned_or_default::<Table<Graphic>>(ATTR_EDITOR_MERGED_LAYERS, 0);
|
||||||
if !upstream_nested_layers.is_empty() {
|
if !upstream_nested_layers.is_empty() {
|
||||||
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
upstream_nested_layers.collect_metadata(metadata, footprint, None);
|
||||||
}
|
}
|
||||||
|
|
@ -1697,7 +1698,7 @@ impl Render for Table<Raster<GPU>> {
|
||||||
// later replace with the current viewport transform before each render.
|
// later replace with the current viewport transform before each render.
|
||||||
impl Render for Table<Color> {
|
impl Render for Table<Color> {
|
||||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||||
for (color, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending")) {
|
for (color, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING)) {
|
||||||
render.leaf_tag("polyline", |attributes| {
|
render.leaf_tag("polyline", |attributes| {
|
||||||
// Chrome doesn't like drawing centered rectangles bigger than ~20 million so we draw a polyline quad instead
|
// Chrome doesn't like drawing centered rectangles bigger than ~20 million so we draw a polyline quad instead
|
||||||
let max = u64::MAX;
|
let max = u64::MAX;
|
||||||
|
|
@ -1723,7 +1724,7 @@ impl Render for Table<Color> {
|
||||||
fn render_to_vello(&self, scene: &mut Scene, _parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_to_vello(&self, scene: &mut Scene, _parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
||||||
use vello::peniko;
|
use vello::peniko;
|
||||||
|
|
||||||
for (color, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending")) {
|
for (color, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING)) {
|
||||||
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
||||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||||
|
|
||||||
|
|
@ -1752,8 +1753,8 @@ impl Render for Table<GradientStops> {
|
||||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||||
for index in 0..self.len() {
|
for index in 0..self.len() {
|
||||||
let Some(gradient) = self.element(index) else { continue };
|
let Some(gradient) = self.element(index) else { continue };
|
||||||
let transform: DAffine2 = self.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default("alpha_blending", index);
|
let alpha_blending: AlphaBlending = self.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, index);
|
||||||
render.leaf_tag("rect", |attributes| {
|
render.leaf_tag("rect", |attributes| {
|
||||||
// Chrome doesn't like drawing centered rectangles bigger than ~20 million so we draw a polyline quad instead
|
// Chrome doesn't like drawing centered rectangles bigger than ~20 million so we draw a polyline quad instead
|
||||||
let max = u64::MAX;
|
let max = u64::MAX;
|
||||||
|
|
@ -1820,7 +1821,7 @@ impl Render for Table<GradientStops> {
|
||||||
fn render_to_vello(&self, scene: &mut Scene, _parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_to_vello(&self, scene: &mut Scene, _parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
||||||
use vello::peniko;
|
use vello::peniko;
|
||||||
|
|
||||||
for (gradient, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>("alpha_blending")) {
|
for (gradient, alpha_blending) in self.iter_element_values().zip(self.iter_attribute_values_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING)) {
|
||||||
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
let blend_mode = alpha_blending.blend_mode.to_peniko();
|
||||||
let opacity = alpha_blending.opacity(render_params.for_mask);
|
let opacity = alpha_blending.opacity(render_params.for_mask);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//! Contains stylistic options for SVG elements.
|
//! Contains stylistic options for SVG elements.
|
||||||
|
|
||||||
pub use crate::gradient::*;
|
pub use crate::gradient::*;
|
||||||
|
use core_types::ATTR_ALPHA_BLENDING;
|
||||||
use core_types::AlphaBlending;
|
use core_types::AlphaBlending;
|
||||||
use core_types::Color;
|
use core_types::Color;
|
||||||
use core_types::color::Alpha;
|
use core_types::color::Alpha;
|
||||||
|
|
@ -135,7 +136,7 @@ impl From<Option<Color>> for Fill {
|
||||||
|
|
||||||
impl From<Table<Color>> for Fill {
|
impl From<Table<Color>> for Fill {
|
||||||
fn from(color: Table<Color>) -> Fill {
|
fn from(color: Table<Color>) -> Fill {
|
||||||
let alpha = color.attribute_cloned_or_default::<AlphaBlending>("alpha_blending", 0).opacity;
|
let alpha = color.attribute_cloned_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING, 0).opacity;
|
||||||
let color = color.element(0).copied();
|
let color = color.element(0).copied();
|
||||||
Fill::solid_or_none(color.map(|c| c.with_alpha(c.alpha() * alpha)))
|
Fill::solid_or_none(color.map(|c| c.with_alpha(c.alpha() * alpha)))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use core_types::AlphaBlending;
|
use core_types::AlphaBlending;
|
||||||
use core_types::registry::types::Percentage;
|
use core_types::registry::types::Percentage;
|
||||||
use core_types::table::Table;
|
use core_types::table::Table;
|
||||||
use core_types::{BlendMode, Color, Ctx};
|
use core_types::{ATTR_ALPHA_BLENDING, BlendMode, Color, Ctx};
|
||||||
use graphic_types::Graphic;
|
use graphic_types::Graphic;
|
||||||
use graphic_types::Vector;
|
use graphic_types::Vector;
|
||||||
use graphic_types::raster_types::{CPU, Raster};
|
use graphic_types::raster_types::{CPU, Raster};
|
||||||
|
|
@ -18,35 +18,35 @@ impl MultiplyAlpha for Color {
|
||||||
}
|
}
|
||||||
impl MultiplyAlpha for Table<Vector> {
|
impl MultiplyAlpha for Table<Vector> {
|
||||||
fn multiply_alpha(&mut self, factor: f64) {
|
fn multiply_alpha(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.opacity *= factor as f32;
|
a.opacity *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MultiplyAlpha for Table<Graphic> {
|
impl MultiplyAlpha for Table<Graphic> {
|
||||||
fn multiply_alpha(&mut self, factor: f64) {
|
fn multiply_alpha(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.opacity *= factor as f32;
|
a.opacity *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MultiplyAlpha for Table<Raster<CPU>> {
|
impl MultiplyAlpha for Table<Raster<CPU>> {
|
||||||
fn multiply_alpha(&mut self, factor: f64) {
|
fn multiply_alpha(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.opacity *= factor as f32;
|
a.opacity *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MultiplyAlpha for Table<Color> {
|
impl MultiplyAlpha for Table<Color> {
|
||||||
fn multiply_alpha(&mut self, factor: f64) {
|
fn multiply_alpha(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.opacity *= factor as f32;
|
a.opacity *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MultiplyAlpha for Table<GradientStops> {
|
impl MultiplyAlpha for Table<GradientStops> {
|
||||||
fn multiply_alpha(&mut self, factor: f64) {
|
fn multiply_alpha(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.opacity *= factor as f32;
|
a.opacity *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62,35 +62,35 @@ impl MultiplyFill for Color {
|
||||||
}
|
}
|
||||||
impl MultiplyFill for Table<Vector> {
|
impl MultiplyFill for Table<Vector> {
|
||||||
fn multiply_fill(&mut self, factor: f64) {
|
fn multiply_fill(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.fill *= factor as f32;
|
a.fill *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MultiplyFill for Table<Graphic> {
|
impl MultiplyFill for Table<Graphic> {
|
||||||
fn multiply_fill(&mut self, factor: f64) {
|
fn multiply_fill(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.fill *= factor as f32;
|
a.fill *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MultiplyFill for Table<Raster<CPU>> {
|
impl MultiplyFill for Table<Raster<CPU>> {
|
||||||
fn multiply_fill(&mut self, factor: f64) {
|
fn multiply_fill(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.fill *= factor as f32;
|
a.fill *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MultiplyFill for Table<Color> {
|
impl MultiplyFill for Table<Color> {
|
||||||
fn multiply_fill(&mut self, factor: f64) {
|
fn multiply_fill(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.fill *= factor as f32;
|
a.fill *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MultiplyFill for Table<GradientStops> {
|
impl MultiplyFill for Table<GradientStops> {
|
||||||
fn multiply_fill(&mut self, factor: f64) {
|
fn multiply_fill(&mut self, factor: f64) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.fill *= factor as f32;
|
a.fill *= factor as f32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,35 +102,35 @@ trait SetBlendMode {
|
||||||
|
|
||||||
impl SetBlendMode for Table<Vector> {
|
impl SetBlendMode for Table<Vector> {
|
||||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.blend_mode = blend_mode;
|
a.blend_mode = blend_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SetBlendMode for Table<Graphic> {
|
impl SetBlendMode for Table<Graphic> {
|
||||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.blend_mode = blend_mode;
|
a.blend_mode = blend_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SetBlendMode for Table<Raster<CPU>> {
|
impl SetBlendMode for Table<Raster<CPU>> {
|
||||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.blend_mode = blend_mode;
|
a.blend_mode = blend_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SetBlendMode for Table<Color> {
|
impl SetBlendMode for Table<Color> {
|
||||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.blend_mode = blend_mode;
|
a.blend_mode = blend_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SetBlendMode for Table<GradientStops> {
|
impl SetBlendMode for Table<GradientStops> {
|
||||||
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
fn set_blend_mode(&mut self, blend_mode: BlendMode) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.blend_mode = blend_mode;
|
a.blend_mode = blend_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -142,35 +142,35 @@ trait SetClip {
|
||||||
|
|
||||||
impl SetClip for Table<Vector> {
|
impl SetClip for Table<Vector> {
|
||||||
fn set_clip(&mut self, clip: bool) {
|
fn set_clip(&mut self, clip: bool) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.clip = clip;
|
a.clip = clip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SetClip for Table<Graphic> {
|
impl SetClip for Table<Graphic> {
|
||||||
fn set_clip(&mut self, clip: bool) {
|
fn set_clip(&mut self, clip: bool) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.clip = clip;
|
a.clip = clip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SetClip for Table<Raster<CPU>> {
|
impl SetClip for Table<Raster<CPU>> {
|
||||||
fn set_clip(&mut self, clip: bool) {
|
fn set_clip(&mut self, clip: bool) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.clip = clip;
|
a.clip = clip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SetClip for Table<Color> {
|
impl SetClip for Table<Color> {
|
||||||
fn set_clip(&mut self, clip: bool) {
|
fn set_clip(&mut self, clip: bool) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.clip = clip;
|
a.clip = clip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SetClip for Table<GradientStops> {
|
impl SetClip for Table<GradientStops> {
|
||||||
fn set_clip(&mut self, clip: bool) {
|
fn set_clip(&mut self, clip: bool) {
|
||||||
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>("alpha_blending") {
|
for a in self.iter_attribute_values_mut_or_default::<AlphaBlending>(ATTR_ALPHA_BLENDING) {
|
||||||
a.clip = clip;
|
a.clip = clip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use core_types::table::{Table, TableRow};
|
||||||
use core_types::transform::Transform;
|
use core_types::transform::Transform;
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
use core_types::value::ClonedNode;
|
use core_types::value::ClonedNode;
|
||||||
|
use core_types::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM};
|
||||||
use core_types::{AlphaBlending, Ctx, Node};
|
use core_types::{AlphaBlending, Ctx, Node};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use raster_nodes::blending_nodes::blend_colors;
|
use raster_nodes::blending_nodes::blend_colors;
|
||||||
|
|
@ -90,7 +91,7 @@ where
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (elements, transforms) = target.element_and_attribute_slices_mut::<DAffine2>("transform");
|
let (elements, transforms) = target.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
||||||
for (element, transform_attribute) in elements.iter_mut().zip(transforms.iter()) {
|
for (element, transform_attribute) in elements.iter_mut().zip(transforms.iter()) {
|
||||||
let target_width = element.width;
|
let target_width = element.width;
|
||||||
let target_height = element.height;
|
let target_height = element.height;
|
||||||
|
|
@ -280,7 +281,7 @@ async fn brush(
|
||||||
let has_erase_or_restore_strokes = strokes.iter_element_values().any(|s| matches!(s.style.blend_mode, BlendMode::Erase | BlendMode::Restore));
|
let has_erase_or_restore_strokes = strokes.iter_element_values().any(|s| matches!(s.style.blend_mode, BlendMode::Erase | BlendMode::Restore));
|
||||||
if has_erase_or_restore_strokes {
|
if has_erase_or_restore_strokes {
|
||||||
let opaque_image = Image::new(bbox.size().x as u32, bbox.size().y as u32, Color::WHITE);
|
let opaque_image = Image::new(bbox.size().x as u32, bbox.size().y as u32, Color::WHITE);
|
||||||
let mut erase_restore_mask = TableRow::new_from_element(Raster::new_cpu(opaque_image)).with_attribute("transform", background_bounds);
|
let mut erase_restore_mask = TableRow::new_from_element(Raster::new_cpu(opaque_image)).with_attribute(ATTR_TRANSFORM, background_bounds);
|
||||||
|
|
||||||
for stroke in strokes.into_iter().map(|row| row.into_element()) {
|
for stroke in strokes.into_iter().map(|row| row.into_element()) {
|
||||||
let mut brush_texture = cache.get_cached_brush(&stroke.style);
|
let mut brush_texture = cache.get_cached_brush(&stroke.style);
|
||||||
|
|
@ -312,14 +313,14 @@ async fn brush(
|
||||||
actual_image = blend_image_closure(erase_restore_mask, actual_image, |a, b| blend_params.eval((a, b)));
|
actual_image = blend_image_closure(erase_restore_mask, actual_image, |a, b| blend_params.eval((a, b)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let transform: DAffine2 = actual_image.attribute_cloned_or_default("transform");
|
let transform: DAffine2 = actual_image.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let alpha_blending: AlphaBlending = actual_image.attribute_cloned_or_default("alpha_blending");
|
let alpha_blending: AlphaBlending = actual_image.attribute_cloned_or_default(ATTR_ALPHA_BLENDING);
|
||||||
let layer: Table<NodeId> = actual_image.attribute_cloned_or_default("editor:layer");
|
let layer: Table<NodeId> = actual_image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH);
|
||||||
|
|
||||||
*image.element_mut(0).unwrap() = actual_image.into_element();
|
*image.element_mut(0).unwrap() = actual_image.into_element();
|
||||||
image.set_attribute("transform", 0, transform);
|
image.set_attribute(ATTR_TRANSFORM, 0, transform);
|
||||||
image.set_attribute("alpha_blending", 0, alpha_blending);
|
image.set_attribute(ATTR_ALPHA_BLENDING, 0, alpha_blending);
|
||||||
image.set_attribute("editor:layer", 0, layer);
|
image.set_attribute(ATTR_EDITOR_LAYER_PATH, 0, layer);
|
||||||
|
|
||||||
image
|
image
|
||||||
}
|
}
|
||||||
|
|
@ -329,8 +330,8 @@ pub fn blend_image_closure(foreground: TableRow<Raster<CPU>>, mut background: Ta
|
||||||
let background_size = DVec2::new(background.element().width as f64, background.element().height as f64);
|
let background_size = DVec2::new(background.element().width as f64, background.element().height as f64);
|
||||||
|
|
||||||
// Transforms a point from the background image to the foreground image
|
// Transforms a point from the background image to the foreground image
|
||||||
let foreground_transform: DAffine2 = foreground.attribute_cloned_or_default("transform");
|
let foreground_transform: DAffine2 = foreground.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let background_transform: DAffine2 = background.attribute_cloned_or_default("transform");
|
let background_transform: DAffine2 = background.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let background_to_foreground = DAffine2::from_scale(foreground_size) * foreground_transform.inverse() * background_transform * DAffine2::from_scale(1. / background_size);
|
let background_to_foreground = DAffine2::from_scale(foreground_size) * foreground_transform.inverse() * background_transform * DAffine2::from_scale(1. / background_size);
|
||||||
|
|
||||||
// Footprint of the foreground image (0, 0)..(1, 1) in the background image space
|
// Footprint of the foreground image (0, 0)..(1, 1) in the background image space
|
||||||
|
|
@ -361,7 +362,7 @@ pub fn blend_stamp_closure(foreground: BrushStampGenerator<Color>, mut backgroun
|
||||||
let background_size = DVec2::new(background.element().width as f64, background.element().height as f64);
|
let background_size = DVec2::new(background.element().width as f64, background.element().height as f64);
|
||||||
|
|
||||||
// Transforms a point from the background image to the foreground image
|
// Transforms a point from the background image to the foreground image
|
||||||
let background_transform: DAffine2 = background.attribute_cloned_or_default("transform");
|
let background_transform: DAffine2 = background.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let background_to_foreground = background_transform * DAffine2::from_scale(1. / background_size);
|
let background_to_foreground = background_transform * DAffine2::from_scale(1. / background_size);
|
||||||
|
|
||||||
// Footprint of the foreground image (0, 0)..(1, 1) in the background image space
|
// Footprint of the foreground image (0, 0)..(1, 1) in the background image space
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::brush_stroke::BrushStroke;
|
use crate::brush_stroke::BrushStroke;
|
||||||
use crate::brush_stroke::BrushStyle;
|
use crate::brush_stroke::BrushStyle;
|
||||||
|
use core_types::ATTR_TRANSFORM;
|
||||||
use core_types::graphene_hash::CacheHashWrapper;
|
use core_types::graphene_hash::CacheHashWrapper;
|
||||||
use core_types::table::TableRow;
|
use core_types::table::TableRow;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
|
|
@ -65,7 +66,7 @@ impl BrushCacheImpl {
|
||||||
|
|
||||||
// Check if the first non-blended stroke is an extension of the last one.
|
// Check if the first non-blended stroke is an extension of the last one.
|
||||||
// Transform is set to ZERO (not the default IDENTITY) as a sentinel to mark this item as uninitialized.
|
// Transform is set to ZERO (not the default IDENTITY) as a sentinel to mark this item as uninitialized.
|
||||||
let mut first_stroke_texture = TableRow::new_from_element(Raster::<CPU>::default()).with_attribute("transform", glam::DAffine2::ZERO);
|
let mut first_stroke_texture = TableRow::new_from_element(Raster::<CPU>::default()).with_attribute(ATTR_TRANSFORM, glam::DAffine2::ZERO);
|
||||||
let mut first_stroke_point_skip = 0;
|
let mut first_stroke_point_skip = 0;
|
||||||
let strokes = input[num_blended_strokes..].to_vec();
|
let strokes = input[num_blended_strokes..].to_vec();
|
||||||
if !strokes.is_empty() && self.prev_input.len() > num_blended_strokes {
|
if !strokes.is_empty() && self.prev_input.len() > num_blended_strokes {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use core_types::bounds::{BoundingBox, RenderBoundingBox};
|
||||||
use core_types::registry::types::{Angle, SignedInteger};
|
use core_types::registry::types::{Angle, SignedInteger};
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
use core_types::{AnyHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
|
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, AnyHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphic_types::graphic::{Graphic, IntoGraphicTable};
|
use graphic_types::graphic::{Graphic, IntoGraphicTable};
|
||||||
use graphic_types::{Artboard, Vector};
|
use graphic_types::{Artboard, Vector};
|
||||||
|
|
@ -196,8 +196,8 @@ where
|
||||||
|
|
||||||
// Create and add mirrored items
|
// Create and add mirrored items
|
||||||
for mut row in content.into_iter() {
|
for mut row in content.into_iter() {
|
||||||
let current_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let current_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
row.set_attribute("transform", reflected_transform * current_transform);
|
row.set_attribute(ATTR_TRANSFORM, reflected_transform * current_transform);
|
||||||
result_table.push(row);
|
result_table.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,7 +207,7 @@ where
|
||||||
/// Returns the path identifying the subgraph (network) that contains this proto node — i.e. the input `node_path`
|
/// Returns the path identifying the subgraph (network) that contains this proto node — i.e. the input `node_path`
|
||||||
/// with its own trailing entry dropped. The terminating element of the returned path is the document node whose
|
/// with its own trailing entry dropped. The terminating element of the returned path is the document node whose
|
||||||
/// encapsulated network we live in, so the path doubles as a unique reference to that node at any nesting depth.
|
/// encapsulated network we live in, so the path doubles as a unique reference to that node at any nesting depth.
|
||||||
/// Used as the value source for stamping the `editor:layer` attribute on each item of a layer's output, which lets
|
/// Used as the value source for stamping the `editor:layer_path` attribute on each item of a layer's output, which lets
|
||||||
/// editor tools (e.g. selection, click target routing) trace data back to its owning layer regardless of whether
|
/// editor tools (e.g. selection, click target routing) trace data back to its owning layer regardless of whether
|
||||||
/// the layer is at the root document network or nested inside a custom subgraph.
|
/// the layer is at the root document network or nested inside a custom subgraph.
|
||||||
#[node_macro::node(name("Path of Subgraph"), category(""))]
|
#[node_macro::node(name("Path of Subgraph"), category(""))]
|
||||||
|
|
@ -296,7 +296,7 @@ pub async fn legacy_layer_extend<T: 'n + Send + Clone>(
|
||||||
|
|
||||||
let mut base = base;
|
let mut base = base;
|
||||||
for mut row in new.into_iter() {
|
for mut row in new.into_iter() {
|
||||||
row.set_attribute("editor:layer", layer);
|
row.set_attribute(ATTR_EDITOR_LAYER_PATH, layer);
|
||||||
base.push(row);
|
base.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,7 +348,7 @@ pub async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten
|
||||||
for index in 0..current_graphic_table.len() {
|
for index in 0..current_graphic_table.len() {
|
||||||
let Some(current_element) = current_graphic_table.element(index) else { continue };
|
let Some(current_element) = current_graphic_table.element(index) else { continue };
|
||||||
let current_element = current_element.clone();
|
let current_element = current_element.clone();
|
||||||
let current_transform: DAffine2 = current_graphic_table.attribute_cloned_or_default("transform", index);
|
let current_transform: DAffine2 = current_graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
|
|
||||||
let recurse = fully_flatten || recursion_depth == 0;
|
let recurse = fully_flatten || recursion_depth == 0;
|
||||||
|
|
||||||
|
|
@ -356,7 +356,7 @@ pub async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten
|
||||||
// If we're allowed to recurse, flatten any graphics we encounter
|
// If we're allowed to recurse, flatten any graphics we encounter
|
||||||
Graphic::Graphic(mut current_element) if recurse => {
|
Graphic::Graphic(mut current_element) if recurse => {
|
||||||
// Apply the parent graphic's transform to all child elements
|
// Apply the parent graphic's transform to all child elements
|
||||||
for graphic_transform in current_element.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
for graphic_transform in current_element.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
*graphic_transform = current_transform * *graphic_transform;
|
*graphic_transform = current_transform * *graphic_transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ use base64::Engine;
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
use canvas_utils::{Canvas, CanvasHandle};
|
use canvas_utils::{Canvas, CanvasHandle};
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
use core_types::WasmNotSend;
|
|
||||||
#[cfg(target_family = "wasm")]
|
|
||||||
use core_types::math::bbox::Bbox;
|
use core_types::math::bbox::Bbox;
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
use core_types::transform::Footprint;
|
use core_types::transform::Footprint;
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
use core_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM, WasmNotSend};
|
||||||
use core_types::{Color, Ctx};
|
use core_types::{Color, Ctx};
|
||||||
pub use graph_craft::application_io::*;
|
pub use graph_craft::application_io::*;
|
||||||
pub use graph_craft::document::value::RenderOutputType;
|
pub use graph_craft::document::value::RenderOutputType;
|
||||||
|
|
@ -210,7 +210,7 @@ where
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
for transform in data.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
for transform in data.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
*transform = DAffine2::from_translation(-aabb.start) * *transform;
|
*transform = DAffine2::from_translation(-aabb.start) * *transform;
|
||||||
}
|
}
|
||||||
data.render_svg(&mut render, &render_params);
|
data.render_svg(&mut render, &render_params);
|
||||||
|
|
@ -237,7 +237,7 @@ where
|
||||||
let image = Image::from_image_data(&rasterized.data().0, resolution.x as u32, resolution.y as u32);
|
let image = Image::from_image_data(&rasterized.data().0, resolution.x as u32, resolution.y as u32);
|
||||||
Table::new_from_row(
|
Table::new_from_row(
|
||||||
TableRow::new_from_element(Raster::new_cpu(image))
|
TableRow::new_from_element(Raster::new_cpu(image))
|
||||||
.with_attribute("transform", footprint.transform)
|
.with_attribute(ATTR_TRANSFORM, footprint.transform)
|
||||||
.with_attribute("editor:merged_layers", upstream_graphic_table),
|
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, upstream_graphic_table),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
use core_types::{AlphaBlending, Color, Ctx};
|
use core_types::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM, AlphaBlending, Color, Ctx};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphic_types::vector_types::subpath::{ManipulatorGroup, Subpath};
|
use graphic_types::vector_types::subpath::{ManipulatorGroup, Subpath};
|
||||||
use graphic_types::vector_types::vector::PointId;
|
use graphic_types::vector_types::vector::PointId;
|
||||||
|
|
@ -40,8 +40,8 @@ async fn boolean_operation<I: graphic_types::IntoGraphicTable + 'n + Send + Clon
|
||||||
|
|
||||||
// Replace the transformation matrix with a mutation of the vector points themselves
|
// Replace the transformation matrix with a mutation of the vector points themselves
|
||||||
if result_vector_table.element_mut(0).is_some() {
|
if result_vector_table.element_mut(0).is_some() {
|
||||||
let transform: DAffine2 = result_vector_table.attribute_cloned_or_default("transform", 0);
|
let transform: DAffine2 = result_vector_table.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||||
result_vector_table.set_attribute("transform", 0, DAffine2::IDENTITY);
|
result_vector_table.set_attribute(ATTR_TRANSFORM, 0, DAffine2::IDENTITY);
|
||||||
|
|
||||||
let result_vector = result_vector_table.element_mut(0).unwrap();
|
let result_vector = result_vector_table.element_mut(0).unwrap();
|
||||||
Vector::transform(result_vector, transform);
|
Vector::transform(result_vector, transform);
|
||||||
|
|
@ -49,10 +49,10 @@ async fn boolean_operation<I: graphic_types::IntoGraphicTable + 'n + Send + Clon
|
||||||
|
|
||||||
// Snapshot the input layers as the `editor:merged_layers` attribute so the renderer can recurse into them
|
// Snapshot the input layers as the `editor:merged_layers` attribute so the renderer can recurse into them
|
||||||
// for editor click-target preservation.
|
// for editor click-target preservation.
|
||||||
result_vector_table.set_attribute("editor:merged_layers", 0, content.clone());
|
result_vector_table.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, content.clone());
|
||||||
|
|
||||||
// Clean up the boolean operation result by merging duplicated points
|
// Clean up the boolean operation result by merging duplicated points
|
||||||
let merge_transform: DAffine2 = result_vector_table.attribute_cloned_or_default("transform", 0);
|
let merge_transform: DAffine2 = result_vector_table.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||||
result_vector_table.element_mut(0).unwrap().merge_by_distance_spatial(merge_transform, 0.0001);
|
result_vector_table.element_mut(0).unwrap().merge_by_distance_spatial(merge_transform, 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,7 +126,7 @@ fn boolean_operation_on_vector_table(vector: &Table<Vector>, boolean_operation:
|
||||||
let mut row = if let Some(index) = copy_from_index {
|
let mut row = if let Some(index) = copy_from_index {
|
||||||
let mut attributes = vector.clone_row_attributes(index);
|
let mut attributes = vector.clone_row_attributes(index);
|
||||||
// The boolean op bakes input transforms into the output geometry, so the result item carries no transform of its own
|
// The boolean op bakes input transforms into the output geometry, so the result item carries no transform of its own
|
||||||
attributes.insert("transform", DAffine2::IDENTITY);
|
attributes.insert(ATTR_TRANSFORM, DAffine2::IDENTITY);
|
||||||
let copy_from = vector.element(index).unwrap();
|
let copy_from = vector.element(index).unwrap();
|
||||||
let element = Vector {
|
let element = Vector {
|
||||||
style: copy_from.style.clone(),
|
style: copy_from.style.clone(),
|
||||||
|
|
@ -139,7 +139,7 @@ fn boolean_operation_on_vector_table(vector: &Table<Vector>, boolean_operation:
|
||||||
|
|
||||||
for index in 0..vector.len() {
|
for index in 0..vector.len() {
|
||||||
let element = vector.element(index).unwrap();
|
let element = vector.element(index).unwrap();
|
||||||
paths.push(to_bez_path(element, vector.attribute_cloned_or_default("transform", index)));
|
paths.push(to_bez_path(element, vector.attribute_cloned_or_default(ATTR_TRANSFORM, index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let top = match Topology::<WindingNumber>::from_paths(paths.iter().enumerate().map(|(idx, path)| (path, (idx, paths.len()))), EPSILON) {
|
let top = match Topology::<WindingNumber>::from_paths(paths.iter().enumerate().map(|(idx, path)| (path, (idx, paths.len()))), EPSILON) {
|
||||||
|
|
@ -167,18 +167,18 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||||
match graphic.clone() {
|
match graphic.clone() {
|
||||||
Graphic::Vector(vector) => {
|
Graphic::Vector(vector) => {
|
||||||
// Apply the parent graphic's transform to each element of the `Table<Vector>`
|
// Apply the parent graphic's transform to each element of the `Table<Vector>`
|
||||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default("transform", index);
|
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
vector
|
vector
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut sub_vector| {
|
.map(|mut sub_vector| {
|
||||||
let current_transform: DAffine2 = sub_vector.attribute_cloned_or_default("transform");
|
let current_transform: DAffine2 = sub_vector.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
*sub_vector.attribute_mut_or_insert_default("transform") = parent_transform * current_transform;
|
*sub_vector.attribute_mut_or_insert_default(ATTR_TRANSFORM) = parent_transform * current_transform;
|
||||||
sub_vector
|
sub_vector
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
Graphic::RasterCPU(image) => {
|
Graphic::RasterCPU(image) => {
|
||||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default("transform", index);
|
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let make_row = |transform, layer, alpha_blending| {
|
let make_row = |transform, layer, alpha_blending| {
|
||||||
let mut subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
|
let mut subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
|
||||||
subpath.apply_transform(transform);
|
subpath.apply_transform(transform);
|
||||||
|
|
@ -187,8 +187,8 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||||
element.style.set_fill(Fill::Solid(Color::BLACK));
|
element.style.set_fill(Fill::Solid(Color::BLACK));
|
||||||
|
|
||||||
TableRow::new_from_element(element)
|
TableRow::new_from_element(element)
|
||||||
.with_attribute("alpha_blending", alpha_blending)
|
.with_attribute(ATTR_ALPHA_BLENDING, alpha_blending)
|
||||||
.with_attribute("editor:layer", layer)
|
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply the parent graphic's transform to each raster element, preserving each item's layer
|
// Apply the parent graphic's transform to each raster element, preserving each item's layer
|
||||||
|
|
@ -196,15 +196,15 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||||
// back to the originating raster layer
|
// back to the originating raster layer
|
||||||
(0..image.len())
|
(0..image.len())
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let row_transform: DAffine2 = image.attribute_cloned_or_default("transform", i);
|
let row_transform: DAffine2 = image.attribute_cloned_or_default(ATTR_TRANSFORM, i);
|
||||||
let layer: Table<NodeId> = image.attribute_cloned_or_default("editor:layer", i);
|
let layer: Table<NodeId> = image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, i);
|
||||||
let alpha_blending: AlphaBlending = image.attribute_cloned_or_default("alpha_blending", i);
|
let alpha_blending: AlphaBlending = image.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, i);
|
||||||
make_row(parent_transform * row_transform, layer, alpha_blending)
|
make_row(parent_transform * row_transform, layer, alpha_blending)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
Graphic::RasterGPU(image) => {
|
Graphic::RasterGPU(image) => {
|
||||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default("transform", index);
|
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let make_row = |transform, layer, alpha_blending| {
|
let make_row = |transform, layer, alpha_blending| {
|
||||||
let mut subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
|
let mut subpath = Subpath::new_rectangle(DVec2::ZERO, DVec2::ONE);
|
||||||
subpath.apply_transform(transform);
|
subpath.apply_transform(transform);
|
||||||
|
|
@ -213,8 +213,8 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||||
element.style.set_fill(Fill::Solid(Color::BLACK));
|
element.style.set_fill(Fill::Solid(Color::BLACK));
|
||||||
|
|
||||||
TableRow::new_from_element(element)
|
TableRow::new_from_element(element)
|
||||||
.with_attribute("alpha_blending", alpha_blending)
|
.with_attribute(ATTR_ALPHA_BLENDING, alpha_blending)
|
||||||
.with_attribute("editor:layer", layer)
|
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply the parent graphic's transform to each raster element, preserving each item's layer
|
// Apply the parent graphic's transform to each raster element, preserving each item's layer
|
||||||
|
|
@ -222,17 +222,17 @@ fn flatten_vector(graphic_table: &Table<Graphic>) -> Table<Vector> {
|
||||||
// back to the originating raster layer
|
// back to the originating raster layer
|
||||||
(0..image.len())
|
(0..image.len())
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let row_transform: DAffine2 = image.attribute_cloned_or_default("transform", i);
|
let row_transform: DAffine2 = image.attribute_cloned_or_default(ATTR_TRANSFORM, i);
|
||||||
let layer: Table<NodeId> = image.attribute_cloned_or_default("editor:layer", i);
|
let layer: Table<NodeId> = image.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, i);
|
||||||
let alpha_blending: AlphaBlending = image.attribute_cloned_or_default("alpha_blending", i);
|
let alpha_blending: AlphaBlending = image.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, i);
|
||||||
make_row(parent_transform * row_transform, layer, alpha_blending)
|
make_row(parent_transform * row_transform, layer, alpha_blending)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
Graphic::Graphic(mut graphic) => {
|
Graphic::Graphic(mut graphic) => {
|
||||||
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default("transform", index);
|
let parent_transform: DAffine2 = graphic_table.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
// Apply the parent graphic's transform to each element of the inner `Table`
|
// Apply the parent graphic's transform to each element of the inner `Table`
|
||||||
for transform in graphic.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
for transform in graphic.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
*transform = parent_transform * *transform;
|
*transform = parent_transform * *transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use core_types::context::{Ctx, ExtractFootprint};
|
||||||
use core_types::math::bbox::Bbox;
|
use core_types::math::bbox::Bbox;
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
use core_types::transform::Transform;
|
use core_types::transform::Transform;
|
||||||
|
use core_types::{ATTR_ALPHA_BLENDING, ATTR_TRANSFORM};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use fastnoise_lite;
|
use fastnoise_lite;
|
||||||
use glam::{DAffine2, DVec2, Vec2};
|
use glam::{DAffine2, DVec2, Vec2};
|
||||||
|
|
@ -34,7 +35,7 @@ pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: Tabl
|
||||||
image_frame
|
image_frame
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|row| {
|
.filter_map(|row| {
|
||||||
let image_frame_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let image_frame_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let (image, mut attributes) = row.into_parts();
|
let (image, mut attributes) = row.into_parts();
|
||||||
|
|
||||||
// Resize the image using the image crate
|
// Resize the image using the image crate
|
||||||
|
|
@ -86,7 +87,7 @@ pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: Tabl
|
||||||
// we need to adjust the offset if we truncate the offset calculation
|
// we need to adjust the offset if we truncate the offset calculation
|
||||||
|
|
||||||
let new_transform = image_frame_transform * DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
let new_transform = image_frame_transform * DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
||||||
attributes.insert("transform", new_transform);
|
attributes.insert(ATTR_TRANSFORM, new_transform);
|
||||||
|
|
||||||
Some(TableRow::from_parts(Raster::new_cpu(image), attributes))
|
Some(TableRow::from_parts(Raster::new_cpu(image), attributes))
|
||||||
})
|
})
|
||||||
|
|
@ -194,7 +195,7 @@ pub fn mask(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|mut row| {
|
.filter_map(|mut row| {
|
||||||
let image_size = DVec2::new(row.element().width as f64, row.element().height as f64);
|
let image_size = DVec2::new(row.element().width as f64, row.element().height as f64);
|
||||||
let stencil_transform: DAffine2 = stencil.attribute_cloned_or_default("transform");
|
let stencil_transform: DAffine2 = stencil.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let mask_size = stencil_transform.scale_magnitudes();
|
let mask_size = stencil_transform.scale_magnitudes();
|
||||||
|
|
||||||
if mask_size == DVec2::ZERO {
|
if mask_size == DVec2::ZERO {
|
||||||
|
|
@ -202,7 +203,7 @@ pub fn mask(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transforms a point from the background image to the foreground image
|
// Transforms a point from the background image to the foreground image
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let bg_to_fg = transform_attribute * DAffine2::from_scale(1. / image_size);
|
let bg_to_fg = transform_attribute * DAffine2::from_scale(1. / image_size);
|
||||||
let stencil_transform_inverse = stencil_transform.inverse();
|
let stencil_transform_inverse = stencil_transform.inverse();
|
||||||
|
|
||||||
|
|
@ -230,7 +231,7 @@ pub fn extend_image_to_bounds(_: impl Ctx, image: Table<Raster<CPU>>, bounds: DA
|
||||||
image
|
image
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut row| {
|
.map(|mut row| {
|
||||||
let row_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let row_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let image_aabb = Bbox::unit().affine_transform(row_transform).to_axis_aligned_bbox();
|
let image_aabb = Bbox::unit().affine_transform(row_transform).to_axis_aligned_bbox();
|
||||||
let bounds_aabb = Bbox::unit().affine_transform(bounds.transform()).to_axis_aligned_bbox();
|
let bounds_aabb = Bbox::unit().affine_transform(bounds.transform()).to_axis_aligned_bbox();
|
||||||
if image_aabb.contains(bounds_aabb.start) && image_aabb.contains(bounds_aabb.end) {
|
if image_aabb.contains(bounds_aabb.start) && image_aabb.contains(bounds_aabb.end) {
|
||||||
|
|
@ -267,7 +268,7 @@ pub fn extend_image_to_bounds(_: impl Ctx, image: Table<Raster<CPU>>, bounds: DA
|
||||||
let new_texture_to_layer_space = row_transform * DAffine2::from_scale(1. / orig_image_scale) * DAffine2::from_translation(new_start) * DAffine2::from_scale(new_scale);
|
let new_texture_to_layer_space = row_transform * DAffine2::from_scale(1. / orig_image_scale) * DAffine2::from_translation(new_start) * DAffine2::from_scale(new_scale);
|
||||||
|
|
||||||
*row.element_mut() = Raster::new_cpu(new_image);
|
*row.element_mut() = Raster::new_cpu(new_image);
|
||||||
row.set_attribute("transform", new_texture_to_layer_space);
|
row.set_attribute(ATTR_TRANSFORM, new_texture_to_layer_space);
|
||||||
row
|
row
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
@ -282,8 +283,8 @@ pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Table<Color>) -> Tab
|
||||||
let image = Image::new(width, height, color);
|
let image = Image::new(width, height, color);
|
||||||
|
|
||||||
let mut result_table = Table::new_from_element(Raster::new_cpu(image));
|
let mut result_table = Table::new_from_element(Raster::new_cpu(image));
|
||||||
result_table.set_attribute("transform", 0, transform);
|
result_table.set_attribute(ATTR_TRANSFORM, 0, transform);
|
||||||
result_table.set_attribute("alpha_blending", 0, AlphaBlending::default());
|
result_table.set_attribute(ATTR_ALPHA_BLENDING, 0, AlphaBlending::default());
|
||||||
|
|
||||||
// Callers of empty_image can safely unwrap on returned `Table`
|
// Callers of empty_image can safely unwrap on returned `Table`
|
||||||
result_table
|
result_table
|
||||||
|
|
@ -378,7 +379,7 @@ pub fn noise_pattern(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Table::new_from_row(TableRow::new_from_element(Raster::new_cpu(image)).with_attribute("transform", transform));
|
return Table::new_from_row(TableRow::new_from_element(Raster::new_cpu(image)).with_attribute(ATTR_TRANSFORM, transform));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
noise.set_noise_type(Some(noise_type));
|
noise.set_noise_type(Some(noise_type));
|
||||||
|
|
@ -436,7 +437,7 @@ pub fn noise_pattern(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Table::new_from_row(TableRow::new_from_element(Raster::new_cpu(image)).with_attribute("transform", transform))
|
Table::new_from_row(TableRow::new_from_element(Raster::new_cpu(image)).with_attribute(ATTR_TRANSFORM, transform))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Raster: Pattern"))]
|
#[node_macro::node(category("Raster: Pattern"))]
|
||||||
|
|
@ -481,7 +482,7 @@ pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> Table<Raster<CPU>> {
|
||||||
data,
|
data,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}))
|
}))
|
||||||
.with_attribute("transform", DAffine2::from_translation(offset) * DAffine2::from_scale(size)),
|
.with_attribute(ATTR_TRANSFORM, DAffine2::from_translation(offset) * DAffine2::from_scale(size)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::gcore::Context;
|
||||||
use core::f64::consts::TAU;
|
use core::f64::consts::TAU;
|
||||||
use core_types::registry::types::{Angle, PixelSize};
|
use core_types::registry::types::{Angle, PixelSize};
|
||||||
use core_types::table::Table;
|
use core_types::table::Table;
|
||||||
use core_types::{CloneVarArgs, Color, Ctx, ExtractAll, InjectVarArgs, OwnedContextImpl};
|
use core_types::{ATTR_TRANSFORM, CloneVarArgs, Color, Ctx, ExtractAll, InjectVarArgs, OwnedContextImpl};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphic_types::{Graphic, Vector};
|
use graphic_types::{Graphic, Vector};
|
||||||
use raster_types::{CPU, Raster};
|
use raster_types::{CPU, Raster};
|
||||||
|
|
@ -80,10 +80,10 @@ pub async fn repeat_array<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||||
for row_index in 0..generated_content.len() {
|
for row_index in 0..generated_content.len() {
|
||||||
let Some(mut row) = generated_content.clone_row(row_index) else { continue };
|
let Some(mut row) = generated_content.clone_row(row_index) else { continue };
|
||||||
|
|
||||||
let local_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let local_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let local_translation = DAffine2::from_translation(local_transform.translation);
|
let local_translation = DAffine2::from_translation(local_transform.translation);
|
||||||
let local_matrix = DAffine2::from_mat2(local_transform.matrix2);
|
let local_matrix = DAffine2::from_mat2(local_transform.matrix2);
|
||||||
*row.attribute_mut_or_insert_default("transform") = local_translation * transform * local_matrix;
|
*row.attribute_mut_or_insert_default(ATTR_TRANSFORM) = local_translation * transform * local_matrix;
|
||||||
|
|
||||||
result_table.push(row);
|
result_table.push(row);
|
||||||
}
|
}
|
||||||
|
|
@ -126,10 +126,10 @@ async fn repeat_radial<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||||
for row_index in 0..generated_content.len() {
|
for row_index in 0..generated_content.len() {
|
||||||
let Some(mut row) = generated_content.clone_row(row_index) else { continue };
|
let Some(mut row) = generated_content.clone_row(row_index) else { continue };
|
||||||
|
|
||||||
let local_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let local_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let local_translation = DAffine2::from_translation(local_transform.translation);
|
let local_translation = DAffine2::from_translation(local_transform.translation);
|
||||||
let local_matrix = DAffine2::from_mat2(local_transform.matrix2);
|
let local_matrix = DAffine2::from_mat2(local_transform.matrix2);
|
||||||
*row.attribute_mut_or_insert_default("transform") = local_translation * transform * local_matrix;
|
*row.attribute_mut_or_insert_default(ATTR_TRANSFORM) = local_translation * transform * local_matrix;
|
||||||
|
|
||||||
result_table.push(row);
|
result_table.push(row);
|
||||||
}
|
}
|
||||||
|
|
@ -156,7 +156,7 @@ async fn repeat_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||||
|
|
||||||
for points_index in 0..points.len() {
|
for points_index in 0..points.len() {
|
||||||
let Some(points_element) = points.element(points_index) else { continue };
|
let Some(points_element) = points.element(points_index) else { continue };
|
||||||
let transform: DAffine2 = points.attribute_cloned_or_default("transform", points_index);
|
let transform: DAffine2 = points.attribute_cloned_or_default(ATTR_TRANSFORM, points_index);
|
||||||
|
|
||||||
let mut iteration = async |index, point| {
|
let mut iteration = async |index, point| {
|
||||||
let transformed_point = transform.transform_point2(point);
|
let transformed_point = transform.transform_point2(point);
|
||||||
|
|
@ -165,7 +165,7 @@ async fn repeat_on_points<T: Into<Graphic> + Default + Send + Clone + 'static>(
|
||||||
let generated_content = content.eval(new_ctx.into_context()).await;
|
let generated_content = content.eval(new_ctx.into_context()).await;
|
||||||
|
|
||||||
for mut generated_row in generated_content.into_iter() {
|
for mut generated_row in generated_content.into_iter() {
|
||||||
generated_row.attribute_mut_or_insert_default::<DAffine2>("transform").translation = transformed_point;
|
generated_row.attribute_mut_or_insert_default::<DAffine2>(ATTR_TRANSFORM).translation = transformed_point;
|
||||||
result_table.push(generated_row);
|
result_table.push(generated_row);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -237,7 +237,7 @@ mod test {
|
||||||
let bounds = generated
|
let bounds = generated
|
||||||
.element(index)
|
.element(index)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.bounding_box_with_transform(generated.attribute_cloned_or_default("transform", index))
|
.bounding_box_with_transform(generated.attribute_cloned_or_default(ATTR_TRANSFORM, index))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(position.abs_diff_eq((bounds[0] + bounds[1]) / 2., 1e-10));
|
assert!(position.abs_diff_eq((bounds[0] + bounds[1]) / 2., 1e-10));
|
||||||
assert_eq!((bounds[1] - bounds[0]).x, position.y);
|
assert_eq!((bounds[1] - bounds[0]).x, position.y);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use core_types::Ctx;
|
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
|
use core_types::{ATTR_TYPE, Ctx};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::unescape_string;
|
use crate::unescape_string;
|
||||||
|
|
@ -248,7 +248,10 @@ fn query_json_all(
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
resolve_all(&value, &segments, !unquote_strings, &mut results);
|
resolve_all(&value, &segments, !unquote_strings, &mut results);
|
||||||
|
|
||||||
results.into_iter().map(|(text, ty)| TableRow::new_from_element(text).with_attribute("type", ty.to_string())).collect()
|
results
|
||||||
|
.into_iter()
|
||||||
|
.map(|(text, ty)| TableRow::new_from_element(text).with_attribute(ATTR_TYPE, ty.to_string()))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A parsed segment of a JSON access path.
|
/// A parsed segment of a JSON access path.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use core_types::ATTR_TRANSFORM;
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use parley::GlyphRun;
|
use parley::GlyphRun;
|
||||||
|
|
@ -52,7 +53,7 @@ impl PathBuilder {
|
||||||
|
|
||||||
if per_glyph_items {
|
if per_glyph_items {
|
||||||
self.vector_table
|
self.vector_table
|
||||||
.push(TableRow::new_from_element(Vector::from_subpaths(core::mem::take(&mut self.glyph_subpaths), false)).with_attribute("transform", DAffine2::from_translation(glyph_offset)));
|
.push(TableRow::new_from_element(Vector::from_subpaths(core::mem::take(&mut self.glyph_subpaths), false)).with_attribute(ATTR_TRANSFORM, DAffine2::from_translation(glyph_offset)));
|
||||||
} else {
|
} else {
|
||||||
for subpath in self.glyph_subpaths.drain(..) {
|
for subpath in self.glyph_subpaths.drain(..) {
|
||||||
// Unwrapping here is ok because `self.vector_table` is initialized with a single `Table<Vector>` item
|
// Unwrapping here is ok because `self.vector_table` is initialized with a single `Table<Vector>` item
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use core_types::Ctx;
|
|
||||||
use core_types::registry::types::SignedInteger;
|
use core_types::registry::types::SignedInteger;
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
|
use core_types::{ATTR_END, ATTR_NAME, ATTR_START, Ctx};
|
||||||
|
|
||||||
/// Checks whether the string contains a match for the given regular expression pattern. Optionally restricts the match to only the start and/or end of the string.
|
/// Checks whether the string contains a match for the given regular expression pattern. Optionally restricts the match to only the start and/or end of the string.
|
||||||
#[node_macro::node(category("Text: Regex"))]
|
#[node_macro::node(category("Text: Regex"))]
|
||||||
|
|
@ -143,7 +143,10 @@ fn regex_find(
|
||||||
let start = captured.map_or(0_u64, |m| m.start() as u64);
|
let start = captured.map_or(0_u64, |m| m.start() as u64);
|
||||||
let end = captured.map_or(0_u64, |m| m.end() as u64);
|
let end = captured.map_or(0_u64, |m| m.end() as u64);
|
||||||
let name = capture_names.get(i).cloned().flatten().unwrap_or_default();
|
let name = capture_names.get(i).cloned().flatten().unwrap_or_default();
|
||||||
TableRow::new_from_element(text).with_attribute("start", start).with_attribute("end", end).with_attribute("name", name)
|
TableRow::new_from_element(text)
|
||||||
|
.with_attribute(ATTR_START, start)
|
||||||
|
.with_attribute(ATTR_END, end)
|
||||||
|
.with_attribute(ATTR_NAME, name)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
@ -185,8 +188,8 @@ fn regex_find_all(
|
||||||
.filter_map(|m| m.ok())
|
.filter_map(|m| m.ok())
|
||||||
.map(|m| {
|
.map(|m| {
|
||||||
TableRow::new_from_element(m.as_str().to_string())
|
TableRow::new_from_element(m.as_str().to_string())
|
||||||
.with_attribute("start", m.start() as u64)
|
.with_attribute(ATTR_START, m.start() as u64)
|
||||||
.with_attribute("end", m.end() as u64)
|
.with_attribute(ATTR_END, m.end() as u64)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use core::f64;
|
||||||
use core_types::color::Color;
|
use core_types::color::Color;
|
||||||
use core_types::table::Table;
|
use core_types::table::Table;
|
||||||
use core_types::transform::{ApplyTransform, ScaleType, Transform};
|
use core_types::transform::{ApplyTransform, ScaleType, Transform};
|
||||||
use core_types::{CloneVarArgs, Context, Ctx, ExtractAll, InjectFootprint, ModifyFootprint, OwnedContextImpl};
|
use core_types::{ATTR_TRANSFORM, CloneVarArgs, Context, Ctx, ExtractAll, InjectFootprint, ModifyFootprint, OwnedContextImpl};
|
||||||
use glam::{DAffine2, DMat2, DVec2};
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
use graphic_types::Graphic;
|
use graphic_types::Graphic;
|
||||||
use graphic_types::Vector;
|
use graphic_types::Vector;
|
||||||
|
|
@ -66,7 +66,7 @@ fn reset_transform<T>(
|
||||||
reset_rotation: bool,
|
reset_rotation: bool,
|
||||||
reset_scale: bool,
|
reset_scale: bool,
|
||||||
) -> Table<T> {
|
) -> Table<T> {
|
||||||
for row_transform in content.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
for row_transform in content.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
if reset_translation {
|
if reset_translation {
|
||||||
row_transform.translation = DVec2::ZERO;
|
row_transform.translation = DVec2::ZERO;
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +102,7 @@ fn replace_transform<T>(
|
||||||
mut content: Table<T>,
|
mut content: Table<T>,
|
||||||
transform: DAffine2,
|
transform: DAffine2,
|
||||||
) -> Table<T> {
|
) -> Table<T> {
|
||||||
for row_transform in content.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
for row_transform in content.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
*row_transform = transform.transform();
|
*row_transform = transform.transform();
|
||||||
}
|
}
|
||||||
content
|
content
|
||||||
|
|
@ -123,7 +123,7 @@ async fn extract_transform<T>(
|
||||||
)]
|
)]
|
||||||
content: Table<T>,
|
content: Table<T>,
|
||||||
) -> DAffine2 {
|
) -> DAffine2 {
|
||||||
content.attribute_cloned_or_default::<DAffine2>("transform", 0)
|
content.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produces the inverse of the input transform, which is the transform that undoes the effect of the original transform.
|
/// Produces the inverse of the input transform, which is the transform that undoes the effect of the original transform.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use core_types::Ctx;
|
|
||||||
use core_types::table::Table;
|
use core_types::table::Table;
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
|
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM, Ctx};
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use graphic_types::Vector;
|
use graphic_types::Vector;
|
||||||
use vector_types::vector::VectorModification;
|
use vector_types::vector::VectorModification;
|
||||||
|
|
@ -21,8 +21,8 @@ async fn path_modify(_ctx: impl Ctx, mut vector: Table<Vector>, modification: Bo
|
||||||
let len = node_path.len();
|
let len = node_path.len();
|
||||||
node_path.into_iter().take(len.saturating_sub(1)).collect()
|
node_path.into_iter().take(len.saturating_sub(1)).collect()
|
||||||
};
|
};
|
||||||
let existing: Table<NodeId> = vector.attribute_cloned_or_default("editor:layer", 0);
|
let existing: Table<NodeId> = vector.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, 0);
|
||||||
vector.set_attribute("editor:layer", 0, if existing.is_empty() { subgraph_path } else { existing });
|
vector.set_attribute(ATTR_EDITOR_LAYER_PATH, 0, if existing.is_empty() { subgraph_path } else { existing });
|
||||||
|
|
||||||
if vector.len() > 1 {
|
if vector.len() > 1 {
|
||||||
warn!("The path modify ran on {} vector items. Only the first can be modified.", vector.len());
|
warn!("The path modify ran on {} vector items. Only the first can be modified.", vector.len());
|
||||||
|
|
@ -33,7 +33,7 @@ async fn path_modify(_ctx: impl Ctx, mut vector: Table<Vector>, modification: Bo
|
||||||
/// Applies the vector path's local transformation to its geometry and resets the transform to the identity.
|
/// Applies the vector path's local transformation to its geometry and resets the transform to the identity.
|
||||||
#[node_macro::node(category("Vector"))]
|
#[node_macro::node(category("Vector"))]
|
||||||
async fn apply_transform(_ctx: impl Ctx, mut vector: Table<Vector>) -> Table<Vector> {
|
async fn apply_transform(_ctx: impl Ctx, mut vector: Table<Vector>) -> Table<Vector> {
|
||||||
let (elements, transforms) = vector.element_and_attribute_slices_mut::<DAffine2>("transform");
|
let (elements, transforms) = vector.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
||||||
for (element, transform) in elements.iter_mut().zip(transforms.iter_mut()) {
|
for (element, transform) in elements.iter_mut().zip(transforms.iter_mut()) {
|
||||||
for (_, point) in element.point_domain.positions_mut() {
|
for (_, point) in element.point_domain.positions_mut() {
|
||||||
*point = transform.transform_point2(*point);
|
*point = transform.transform_point2(*point);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use core_types::registry::types::{Angle, Length, Multiplier, Percentage, PixelLe
|
||||||
use core_types::table::{Table, TableRow};
|
use core_types::table::{Table, TableRow};
|
||||||
use core_types::transform::{Footprint, Transform};
|
use core_types::transform::{Footprint, Transform};
|
||||||
use core_types::uuid::NodeId;
|
use core_types::uuid::NodeId;
|
||||||
use core_types::{CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
|
use core_types::{ATTR_ALPHA_BLENDING, ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM, CloneVarArgs, Color, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||||
use glam::{DAffine2, DMat2, DVec2};
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
use graphic_types::Vector;
|
use graphic_types::Vector;
|
||||||
use graphic_types::raster_types::{CPU, GPU, Raster};
|
use graphic_types::raster_types::{CPU, GPU, Raster};
|
||||||
|
|
@ -41,7 +41,7 @@ impl VectorTableIterMut for Table<Graphic> {
|
||||||
fn for_each_vector_mut(&mut self, mut f: impl FnMut(&mut Vector, DAffine2)) {
|
fn for_each_vector_mut(&mut self, mut f: impl FnMut(&mut Vector, DAffine2)) {
|
||||||
for graphic in self.iter_element_values_mut() {
|
for graphic in self.iter_element_values_mut() {
|
||||||
let Some(vector_table) = graphic.as_vector_mut() else { continue };
|
let Some(vector_table) = graphic.as_vector_mut() else { continue };
|
||||||
let (elements, transforms) = vector_table.element_and_attribute_slices_mut::<DAffine2>("transform");
|
let (elements, transforms) = vector_table.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
||||||
for (vector, transform) in elements.iter_mut().zip(transforms.iter()) {
|
for (vector, transform) in elements.iter_mut().zip(transforms.iter()) {
|
||||||
f(vector, *transform);
|
f(vector, *transform);
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@ impl VectorTableIterMut for Table<Graphic> {
|
||||||
|
|
||||||
impl VectorTableIterMut for Table<Vector> {
|
impl VectorTableIterMut for Table<Vector> {
|
||||||
fn for_each_vector_mut(&mut self, mut f: impl FnMut(&mut Vector, DAffine2)) {
|
fn for_each_vector_mut(&mut self, mut f: impl FnMut(&mut Vector, DAffine2)) {
|
||||||
let (elements, transforms) = self.element_and_attribute_slices_mut::<DAffine2>("transform");
|
let (elements, transforms) = self.element_and_attribute_slices_mut::<DAffine2>(ATTR_TRANSFORM);
|
||||||
for (vector, transform) in elements.iter_mut().zip(transforms.iter()) {
|
for (vector, transform) in elements.iter_mut().zip(transforms.iter()) {
|
||||||
f(vector, *transform);
|
f(vector, *transform);
|
||||||
}
|
}
|
||||||
|
|
@ -288,7 +288,7 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||||
let do_scale = random_scale_difference.abs() > 1e-6;
|
let do_scale = random_scale_difference.abs() > 1e-6;
|
||||||
let do_rotation = random_rotation.abs() > 1e-6;
|
let do_rotation = random_rotation.abs() > 1e-6;
|
||||||
|
|
||||||
let points_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let points_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
for &point in row.element().point_domain.positions() {
|
for &point in row.element().point_domain.positions() {
|
||||||
let translation = points_transform.transform_point2(point);
|
let translation = points_transform.transform_point2(point);
|
||||||
|
|
||||||
|
|
@ -317,8 +317,8 @@ async fn copy_to_points<I: 'n + Send + Clone>(
|
||||||
|
|
||||||
for row_index in 0..content.len() {
|
for row_index in 0..content.len() {
|
||||||
let Some(mut row) = content.clone_row(row_index) else { continue };
|
let Some(mut row) = content.clone_row(row_index) else { continue };
|
||||||
let row_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let row_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
row.set_attribute("transform", transform * row_transform);
|
row.set_attribute(ATTR_TRANSFORM, transform * row_transform);
|
||||||
|
|
||||||
result_table.push(row);
|
result_table.push(row);
|
||||||
}
|
}
|
||||||
|
|
@ -349,7 +349,7 @@ async fn round_corners(
|
||||||
) -> Table<Vector> {
|
) -> Table<Vector> {
|
||||||
(0..source.len())
|
(0..source.len())
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let source_transform: DAffine2 = source.attribute_cloned_or_default("transform", index);
|
let source_transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let source_transform_inverse = source_transform.inverse();
|
let source_transform_inverse = source_transform.inverse();
|
||||||
let attributes = source.clone_row_attributes(index);
|
let attributes = source.clone_row_attributes(index);
|
||||||
let source = source.element(index).unwrap();
|
let source = source.element(index).unwrap();
|
||||||
|
|
@ -455,7 +455,7 @@ pub fn merge_by_distance(
|
||||||
MergeByDistanceAlgorithm::Spatial => content
|
MergeByDistanceAlgorithm::Spatial => content
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut row| {
|
.map(|mut row| {
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
row.element_mut().merge_by_distance_spatial(transform, distance);
|
row.element_mut().merge_by_distance_spatial(transform, distance);
|
||||||
row
|
row
|
||||||
})
|
})
|
||||||
|
|
@ -678,12 +678,12 @@ async fn extrude(_: impl Ctx, mut source: Table<Vector>, direction: DVec2, joini
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(core_types::vector))]
|
||||||
async fn box_warp(_: impl Ctx, content: Table<Vector>, #[expose] rectangle: Table<Vector>) -> Table<Vector> {
|
async fn box_warp(_: impl Ctx, content: Table<Vector>, #[expose] rectangle: Table<Vector>) -> Table<Vector> {
|
||||||
let Some(target) = rectangle.element(0).cloned() else { return content };
|
let Some(target) = rectangle.element(0).cloned() else { return content };
|
||||||
let target_transform: DAffine2 = rectangle.attribute_cloned_or_default("transform", 0);
|
let target_transform: DAffine2 = rectangle.attribute_cloned_or_default(ATTR_TRANSFORM, 0);
|
||||||
|
|
||||||
content
|
content
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut row| {
|
.map(|mut row| {
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let vector = std::mem::take(row.element_mut());
|
let vector = std::mem::take(row.element_mut());
|
||||||
|
|
||||||
// Get the bounding box of the source vector geometry
|
// Get the bounding box of the source vector geometry
|
||||||
|
|
@ -743,7 +743,7 @@ async fn box_warp(_: impl Ctx, content: Table<Vector>, #[expose] rectangle: Tabl
|
||||||
|
|
||||||
// Add this to the `Table` and reset the transform since we've applied it directly to the points
|
// Add this to the `Table` and reset the transform since we've applied it directly to the points
|
||||||
*row.element_mut() = result;
|
*row.element_mut() = result;
|
||||||
row.set_attribute("transform", DAffine2::IDENTITY);
|
row.set_attribute(ATTR_TRANSFORM, DAffine2::IDENTITY);
|
||||||
row
|
row
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
@ -852,8 +852,8 @@ where
|
||||||
RowsOrColumns::Rows => DVec2::new(strip.along_position, strip.cross_position),
|
RowsOrColumns::Rows => DVec2::new(strip.along_position, strip.cross_position),
|
||||||
RowsOrColumns::Columns => DVec2::new(strip.cross_position, strip.along_position),
|
RowsOrColumns::Columns => DVec2::new(strip.cross_position, strip.along_position),
|
||||||
};
|
};
|
||||||
let row_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let row_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
row.set_attribute("transform", DAffine2::from_translation(target_position - top_left) * row_transform);
|
row.set_attribute(ATTR_TRANSFORM, DAffine2::from_translation(target_position - top_left) * row_transform);
|
||||||
|
|
||||||
strip.along_position += along + separation;
|
strip.along_position += along + separation;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -864,8 +864,8 @@ where
|
||||||
RowsOrColumns::Rows => DVec2::new(0., new_cross),
|
RowsOrColumns::Rows => DVec2::new(0., new_cross),
|
||||||
RowsOrColumns::Columns => DVec2::new(new_cross, 0.),
|
RowsOrColumns::Columns => DVec2::new(new_cross, 0.),
|
||||||
};
|
};
|
||||||
let row_transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let row_transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
row.set_attribute("transform", DAffine2::from_translation(target_position - top_left) * row_transform);
|
row.set_attribute(ATTR_TRANSFORM, DAffine2::from_translation(target_position - top_left) * row_transform);
|
||||||
|
|
||||||
strips.push(Strip {
|
strips.push(Strip {
|
||||||
along_position: along + separation,
|
along_position: along + separation,
|
||||||
|
|
@ -896,7 +896,7 @@ async fn auto_tangents(
|
||||||
) -> Table<Vector> {
|
) -> Table<Vector> {
|
||||||
(0..source.len())
|
(0..source.len())
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let transform: DAffine2 = source.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let attributes = source.clone_row_attributes(index);
|
let attributes = source.clone_row_attributes(index);
|
||||||
let source = source.element(index).unwrap();
|
let source = source.element(index).unwrap();
|
||||||
|
|
||||||
|
|
@ -1063,7 +1063,7 @@ async fn bounding_box(_: impl Ctx, content: Table<Vector>) -> Table<Vector> {
|
||||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||||
async fn dimensions(_: impl Ctx, content: Table<Vector>) -> DVec2 {
|
async fn dimensions(_: impl Ctx, content: Table<Vector>) -> DVec2 {
|
||||||
(0..content.len())
|
(0..content.len())
|
||||||
.filter_map(|index| content.element(index).unwrap().bounding_box_with_transform(content.attribute_cloned_or_default("transform", index)))
|
.filter_map(|index| content.element(index).unwrap().bounding_box_with_transform(content.attribute_cloned_or_default(ATTR_TRANSFORM, index)))
|
||||||
.reduce(|[acc_top_left, acc_bottom_right], [top_left, bottom_right]| [acc_top_left.min(top_left), acc_bottom_right.max(bottom_right)])
|
.reduce(|[acc_top_left, acc_bottom_right], [top_left, bottom_right]| [acc_top_left.min(top_left), acc_bottom_right.max(bottom_right)])
|
||||||
.map(|[top_left, bottom_right]| bottom_right - top_left)
|
.map(|[top_left, bottom_right]| bottom_right - top_left)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
|
|
@ -1115,7 +1115,7 @@ async fn offset_path(_: impl Ctx, content: Table<Vector>, distance: f64, join: S
|
||||||
content
|
content
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut row| {
|
.map(|mut row| {
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let transform = Affine::new(transform_attribute.to_cols_array());
|
let transform = Affine::new(transform_attribute.to_cols_array());
|
||||||
let vector = std::mem::take(row.element_mut());
|
let vector = std::mem::take(row.element_mut());
|
||||||
|
|
||||||
|
|
@ -1296,14 +1296,14 @@ pub async fn flatten_path<T: IntoGraphicTable + 'n + Send>(_: impl Ctx, #[implem
|
||||||
// Concatenate every vector element's subpaths into the single output compound path
|
// Concatenate every vector element's subpaths into the single output compound path
|
||||||
for index in 0..flattened.len() {
|
for index in 0..flattened.len() {
|
||||||
let Some(element) = flattened.element(index) else { continue };
|
let Some(element) = flattened.element(index) else { continue };
|
||||||
let layer_path: Table<NodeId> = flattened.attribute_cloned_or_default("editor:layer", index);
|
let layer_path: Table<NodeId> = flattened.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
|
||||||
let node_id = layer_path.iter_element_values().next_back().map(|node_id| node_id.0).unwrap_or_default();
|
let node_id = layer_path.iter_element_values().next_back().map(|node_id| node_id.0).unwrap_or_default();
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
(index, node_id).hash(&mut hasher);
|
(index, node_id).hash(&mut hasher);
|
||||||
let collision_hash_seed = hasher.finish();
|
let collision_hash_seed = hasher.finish();
|
||||||
|
|
||||||
output.concat(element, flattened.attribute_cloned_or_default("transform", index), collision_hash_seed);
|
output.concat(element, flattened.attribute_cloned_or_default(ATTR_TRANSFORM, index), collision_hash_seed);
|
||||||
|
|
||||||
// TODO: Make this instead use the first encountered style
|
// TODO: Make this instead use the first encountered style
|
||||||
// Use the last encountered style as the output style
|
// Use the last encountered style as the output style
|
||||||
|
|
@ -1313,13 +1313,13 @@ pub async fn flatten_path<T: IntoGraphicTable + 'n + Send>(_: impl Ctx, #[implem
|
||||||
// Preserve a reference to the original upstream `Table<Graphic>` so the renderer can recurse into it
|
// Preserve a reference to the original upstream `Table<Graphic>` so the renderer can recurse into it
|
||||||
// when collecting metadata, exposing the original child layers' click targets to editor tools.
|
// when collecting metadata, exposing the original child layers' click targets to editor tools.
|
||||||
// This is the same mechanism Boolean Operation uses to keep its inputs editable after the merge.
|
// This is the same mechanism Boolean Operation uses to keep its inputs editable after the merge.
|
||||||
output_table.set_attribute("editor:merged_layers", 0, graphic_table);
|
output_table.set_attribute(ATTR_EDITOR_MERGED_LAYERS, 0, graphic_table);
|
||||||
|
|
||||||
// Adopt the last input item's layer so the editor can also bucket clicks under a contributing child layer
|
// Adopt the last input item's layer so the editor can also bucket clicks under a contributing child layer
|
||||||
if !flattened.is_empty() {
|
if !flattened.is_empty() {
|
||||||
let primary = flattened.len() - 1;
|
let primary = flattened.len() - 1;
|
||||||
let layer_path: Table<NodeId> = flattened.attribute_cloned_or_default("editor:layer", primary);
|
let layer_path: Table<NodeId> = flattened.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, primary);
|
||||||
output_table.set_attribute("editor:layer", 0, layer_path);
|
output_table.set_attribute(ATTR_EDITOR_LAYER_PATH, 0, layer_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
output_table
|
output_table
|
||||||
|
|
@ -1365,12 +1365,12 @@ async fn sample_polyline(
|
||||||
style: std::mem::take(&mut row.element_mut().style),
|
style: std::mem::take(&mut row.element_mut().style),
|
||||||
};
|
};
|
||||||
// Transfer the stroke transform from the input vector content to the result.
|
// Transfer the stroke transform from the input vector content to the result.
|
||||||
result.style.set_stroke_transform(row.attribute_cloned_or_default("transform"));
|
result.style.set_stroke_transform(row.attribute_cloned_or_default(ATTR_TRANSFORM));
|
||||||
|
|
||||||
for local_bezpath in row.element().stroke_bezpath_iter() {
|
for local_bezpath in row.element().stroke_bezpath_iter() {
|
||||||
// Apply the transform to compute sample locations in world space (for correct distance-based spacing)
|
// Apply the transform to compute sample locations in world space (for correct distance-based spacing)
|
||||||
let mut world_bezpath = local_bezpath.clone();
|
let mut world_bezpath = local_bezpath.clone();
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
world_bezpath.apply_affine(Affine::new(transform_attribute.to_cols_array()));
|
world_bezpath.apply_affine(Affine::new(transform_attribute.to_cols_array()));
|
||||||
|
|
||||||
// Per-segment perimeter lengths (transform-baked) for distance-based spacing
|
// Per-segment perimeter lengths (transform-baked) for distance-based spacing
|
||||||
|
|
@ -1431,7 +1431,7 @@ async fn simplify(
|
||||||
content
|
content
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut row| {
|
.map(|mut row| {
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let transform = Affine::new(transform_attribute.to_cols_array());
|
let transform = Affine::new(transform_attribute.to_cols_array());
|
||||||
let inverse_transform = transform.inverse();
|
let inverse_transform = transform.inverse();
|
||||||
|
|
||||||
|
|
@ -1527,7 +1527,7 @@ async fn decimate(
|
||||||
content
|
content
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut row| {
|
.map(|mut row| {
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let transform = Affine::new(transform_attribute.to_cols_array());
|
let transform = Affine::new(transform_attribute.to_cols_array());
|
||||||
let inverse_transform = transform.inverse();
|
let inverse_transform = transform.inverse();
|
||||||
|
|
||||||
|
|
@ -1710,7 +1710,7 @@ async fn position_on_path(
|
||||||
|
|
||||||
let mut bezpaths: Vec<_> = (0..content.len())
|
let mut bezpaths: Vec<_> = (0..content.len())
|
||||||
.flat_map(|index| {
|
.flat_map(|index| {
|
||||||
let transform: DAffine2 = content.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
content.element(index).unwrap().stroke_bezpath_iter().map(move |bezpath| (bezpath, transform)).collect::<Vec<_>>()
|
content.element(index).unwrap().stroke_bezpath_iter().map(move |bezpath| (bezpath, transform)).collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -1750,7 +1750,7 @@ async fn tangent_on_path(
|
||||||
|
|
||||||
let mut bezpaths: Vec<_> = (0..content.len())
|
let mut bezpaths: Vec<_> = (0..content.len())
|
||||||
.flat_map(|index| {
|
.flat_map(|index| {
|
||||||
let transform: DAffine2 = content.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
content.element(index).unwrap().stroke_bezpath_iter().map(move |bezpath| (bezpath, transform)).collect::<Vec<_>>()
|
content.element(index).unwrap().stroke_bezpath_iter().map(move |bezpath| (bezpath, transform)).collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -1948,7 +1948,7 @@ async fn jitter_points(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut row| {
|
.map(|mut row| {
|
||||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let inverse_linear = inverse_linear_or_repair(transform_attribute.matrix2);
|
let inverse_linear = inverse_linear_or_repair(transform_attribute.matrix2);
|
||||||
|
|
||||||
let deltas: Vec<_> = (0..row.element().point_domain.positions().len())
|
let deltas: Vec<_> = (0..row.element().point_domain.positions().len())
|
||||||
|
|
@ -1969,7 +1969,7 @@ async fn jitter_points(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
apply_point_deltas(row.element_mut(), &deltas, transform);
|
apply_point_deltas(row.element_mut(), &deltas, transform);
|
||||||
|
|
||||||
row
|
row
|
||||||
|
|
@ -1992,7 +1992,7 @@ async fn offset_points(
|
||||||
content
|
content
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut row| {
|
.map(|mut row| {
|
||||||
let transform_attribute: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform_attribute: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let inverse_linear = inverse_linear_or_repair(transform_attribute.matrix2);
|
let inverse_linear = inverse_linear_or_repair(transform_attribute.matrix2);
|
||||||
|
|
||||||
let deltas: Vec<_> = (0..row.element().point_domain.positions().len())
|
let deltas: Vec<_> = (0..row.element().point_domain.positions().len())
|
||||||
|
|
@ -2005,7 +2005,7 @@ async fn offset_points(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
apply_point_deltas(row.element_mut(), &deltas, transform);
|
apply_point_deltas(row.element_mut(), &deltas, transform);
|
||||||
|
|
||||||
row
|
row
|
||||||
|
|
@ -2146,7 +2146,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||||
let default_polyline = || {
|
let default_polyline = || {
|
||||||
let mut default_path = BezPath::new();
|
let mut default_path = BezPath::new();
|
||||||
for index in 0..content.len() {
|
for index in 0..content.len() {
|
||||||
let transform_attribute: DAffine2 = content.attribute_cloned_or_default("transform", index);
|
let transform_attribute: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let origin = transform_attribute.translation;
|
let origin = transform_attribute.translation;
|
||||||
let point = kurbo::Point::new(origin.x, origin.y);
|
let point = kurbo::Point::new(origin.x, origin.y);
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
|
|
@ -2164,7 +2164,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||||
// User-provided path: collect all subpaths with transforms applied
|
// User-provided path: collect all subpaths with transforms applied
|
||||||
let paths: Vec<BezPath> = (0..path.len())
|
let paths: Vec<BezPath> = (0..path.len())
|
||||||
.flat_map(|index| {
|
.flat_map(|index| {
|
||||||
let transform: DAffine2 = path.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = path.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
path.element(index)
|
path.element(index)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.stroke_bezpath_iter()
|
.stroke_bezpath_iter()
|
||||||
|
|
@ -2238,8 +2238,8 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||||
if content.element(source_index).is_none() || content.element(target_index).is_none() {
|
if content.element(source_index).is_none() || content.element(target_index).is_none() {
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
let source_transform: DAffine2 = content.attribute_cloned_or_default("transform", source_index);
|
let source_transform: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, source_index);
|
||||||
let target_transform: DAffine2 = content.attribute_cloned_or_default("transform", target_index);
|
let target_transform: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, target_index);
|
||||||
let (s_angle, s_scale, s_skew) = source_transform.decompose_rotation_scale_skew();
|
let (s_angle, s_scale, s_skew) = source_transform.decompose_rotation_scale_skew();
|
||||||
let (t_angle, t_scale, t_skew) = target_transform.decompose_rotation_scale_skew();
|
let (t_angle, t_scale, t_skew) = target_transform.decompose_rotation_scale_skew();
|
||||||
|
|
||||||
|
|
@ -2311,8 +2311,8 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lerp styles
|
// Lerp styles
|
||||||
let source_alpha_blending: AlphaBlending = content.attribute_cloned_or_default("alpha_blending", source_index);
|
let source_alpha_blending: AlphaBlending = content.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, source_index);
|
||||||
let target_alpha_blending: AlphaBlending = content.attribute_cloned_or_default("alpha_blending", target_index);
|
let target_alpha_blending: AlphaBlending = content.attribute_cloned_or_default(ATTR_ALPHA_BLENDING, target_index);
|
||||||
let vector_alpha_blending = source_alpha_blending.lerp(&target_alpha_blending, time as f32);
|
let vector_alpha_blending = source_alpha_blending.lerp(&target_alpha_blending, time as f32);
|
||||||
|
|
||||||
// Evaluate the spatial position on the control path for the translation component.
|
// Evaluate the spatial position on the control path for the translation component.
|
||||||
|
|
@ -2330,8 +2330,8 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||||
// This decomposition must match the one used in Stroke::lerp so the renderer's stroke_transform.inverse()
|
// This decomposition must match the one used in Stroke::lerp so the renderer's stroke_transform.inverse()
|
||||||
// correctly cancels the element transform, keeping the stroke uniform when Stroke is after Transform.
|
// correctly cancels the element transform, keeping the stroke uniform when Stroke is after Transform.
|
||||||
let lerped_transform = {
|
let lerped_transform = {
|
||||||
let source_transform: DAffine2 = content.attribute_cloned_or_default("transform", source_index);
|
let source_transform: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, source_index);
|
||||||
let target_transform: DAffine2 = content.attribute_cloned_or_default("transform", target_index);
|
let target_transform: DAffine2 = content.attribute_cloned_or_default(ATTR_TRANSFORM, target_index);
|
||||||
let (s_angle, s_scale, s_skew) = source_transform.decompose_rotation_scale_skew();
|
let (s_angle, s_scale, s_skew) = source_transform.decompose_rotation_scale_skew();
|
||||||
let (t_angle, t_scale, t_skew) = target_transform.decompose_rotation_scale_skew();
|
let (t_angle, t_scale, t_skew) = target_transform.decompose_rotation_scale_skew();
|
||||||
|
|
||||||
|
|
@ -2360,7 +2360,7 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||||
// in which case we skip pre-compensation to avoid propagating NaN through merged_layers transforms.
|
// in which case we skip pre-compensation to avoid propagating NaN through merged_layers transforms.
|
||||||
if lerped_transform.matrix2.determinant().abs() > f64::EPSILON {
|
if lerped_transform.matrix2.determinant().abs() > f64::EPSILON {
|
||||||
let lerped_inverse = lerped_transform.inverse();
|
let lerped_inverse = lerped_transform.inverse();
|
||||||
for transform in graphic_table_content.iter_attribute_values_mut_or_default::<DAffine2>("transform") {
|
for transform in graphic_table_content.iter_attribute_values_mut_or_default::<DAffine2>(ATTR_TRANSFORM) {
|
||||||
*transform = lerped_inverse * *transform;
|
*transform = lerped_inverse * *transform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2372,8 +2372,8 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||||
let endpoint_element = content.element(endpoint_index).unwrap();
|
let endpoint_element = content.element(endpoint_index).unwrap();
|
||||||
|
|
||||||
let mut attributes = content.clone_row_attributes(endpoint_index);
|
let mut attributes = content.clone_row_attributes(endpoint_index);
|
||||||
attributes.insert("transform", lerped_transform);
|
attributes.insert(ATTR_TRANSFORM, lerped_transform);
|
||||||
attributes.insert("editor:merged_layers", graphic_table_content);
|
attributes.insert(ATTR_EDITOR_MERGED_LAYERS, graphic_table_content);
|
||||||
|
|
||||||
return Table::new_from_row(TableRow::from_parts(endpoint_element.clone(), attributes));
|
return Table::new_from_row(TableRow::from_parts(endpoint_element.clone(), attributes));
|
||||||
}
|
}
|
||||||
|
|
@ -2529,14 +2529,14 @@ async fn morph<I: IntoGraphicTable + 'n + Send + Clone>(
|
||||||
// The result is a synthesis of source and target, so adopt whichever endpoint the result is closer to as
|
// The result is a synthesis of source and target, so adopt whichever endpoint the result is closer to as
|
||||||
// the click-target identity (so the editor can route clicks back to one of the contributing layers)
|
// the click-target identity (so the editor can route clicks back to one of the contributing layers)
|
||||||
let primary_index = if time < 0.5 { source_index } else { target_index };
|
let primary_index = if time < 0.5 { source_index } else { target_index };
|
||||||
let layer_path: Table<NodeId> = content.attribute_cloned_or_default("editor:layer", primary_index);
|
let layer_path: Table<NodeId> = content.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, primary_index);
|
||||||
|
|
||||||
Table::new_from_row(
|
Table::new_from_row(
|
||||||
TableRow::new_from_element(vector)
|
TableRow::new_from_element(vector)
|
||||||
.with_attribute("transform", lerped_transform)
|
.with_attribute(ATTR_TRANSFORM, lerped_transform)
|
||||||
.with_attribute("alpha_blending", vector_alpha_blending)
|
.with_attribute(ATTR_ALPHA_BLENDING, vector_alpha_blending)
|
||||||
.with_attribute("editor:layer", layer_path)
|
.with_attribute(ATTR_EDITOR_LAYER_PATH, layer_path)
|
||||||
.with_attribute("editor:merged_layers", graphic_table_content),
|
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, graphic_table_content),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2815,7 +2815,7 @@ fn bevel(_: impl Ctx, source: Table<Vector>, #[default(10.)] distance: Length) -
|
||||||
source
|
source
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
let (element, attributes) = row.into_parts();
|
let (element, attributes) = row.into_parts();
|
||||||
|
|
||||||
TableRow::from_parts(bevel_algorithm(element, transform, distance), attributes)
|
TableRow::from_parts(bevel_algorithm(element, transform, distance), attributes)
|
||||||
|
|
@ -2837,7 +2837,7 @@ fn close_path(_: impl Ctx, source: Table<Vector>) -> Table<Vector> {
|
||||||
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(core_types::vector))]
|
||||||
fn point_inside(_: impl Ctx, source: Table<Vector>, point: DVec2) -> bool {
|
fn point_inside(_: impl Ctx, source: Table<Vector>, point: DVec2) -> bool {
|
||||||
source.into_iter().any(|row| {
|
source.into_iter().any(|row| {
|
||||||
let transform: DAffine2 = row.attribute_cloned_or_default("transform");
|
let transform: DAffine2 = row.attribute_cloned_or_default(ATTR_TRANSFORM);
|
||||||
row.element().check_point_inside_shape(transform, point)
|
row.element().check_point_inside_shape(transform, point)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -2918,7 +2918,7 @@ async fn index_points(
|
||||||
async fn path_length(_: impl Ctx, source: Table<Vector>) -> f64 {
|
async fn path_length(_: impl Ctx, source: Table<Vector>) -> f64 {
|
||||||
(0..source.len())
|
(0..source.len())
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let transform: DAffine2 = source.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = source.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
|
|
||||||
source
|
source
|
||||||
.element(index)
|
.element(index)
|
||||||
|
|
@ -2940,7 +2940,7 @@ async fn area(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<Cont
|
||||||
|
|
||||||
(0..vector.len())
|
(0..vector.len())
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let transform: DAffine2 = vector.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = vector.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let area_scale = transform.matrix2.determinant().abs();
|
let area_scale = transform.matrix2.determinant().abs();
|
||||||
vector.element(index).unwrap().stroke_bezpath_iter().map(|subpath| subpath.area() * area_scale).sum::<f64>()
|
vector.element(index).unwrap().stroke_bezpath_iter().map(|subpath| subpath.area() * area_scale).sum::<f64>()
|
||||||
})
|
})
|
||||||
|
|
@ -2969,7 +2969,7 @@ async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<
|
||||||
CentroidType::Length => subpath.length_centroid_and_length(None, true),
|
CentroidType::Length => subpath.length_centroid_and_length(None, true),
|
||||||
};
|
};
|
||||||
if let Some((subpath_centroid, area_or_length)) = partial {
|
if let Some((subpath_centroid, area_or_length)) = partial {
|
||||||
let transform: DAffine2 = vector.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = vector.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
let subpath_centroid = transform.transform_point2(subpath_centroid);
|
let subpath_centroid = transform.transform_point2(subpath_centroid);
|
||||||
|
|
||||||
sum += area_or_length;
|
sum += area_or_length;
|
||||||
|
|
@ -2987,7 +2987,7 @@ async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, content: impl Node<
|
||||||
|
|
||||||
let summed_positions = (0..vector.len())
|
let summed_positions = (0..vector.len())
|
||||||
.flat_map(|index| {
|
.flat_map(|index| {
|
||||||
let transform: DAffine2 = vector.attribute_cloned_or_default("transform", index);
|
let transform: DAffine2 = vector.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||||
vector
|
vector
|
||||||
.element(index)
|
.element(index)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
@ -3032,7 +3032,7 @@ mod test {
|
||||||
fn create_vector_row(bezpath: BezPath, transform: DAffine2) -> TableRow<Vector> {
|
fn create_vector_row(bezpath: BezPath, transform: DAffine2) -> TableRow<Vector> {
|
||||||
let mut row = Vector::default();
|
let mut row = Vector::default();
|
||||||
row.append_bezpath(bezpath);
|
row.append_bezpath(bezpath);
|
||||||
TableRow::new_from_element(row).with_attribute("transform", transform)
|
TableRow::new_from_element(row).with_attribute(ATTR_TRANSFORM, transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
@ -3054,7 +3054,7 @@ mod test {
|
||||||
// Test a rectangular path with non-zero rotation
|
// Test a rectangular path with non-zero rotation
|
||||||
let square = Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY));
|
let square = Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY));
|
||||||
let mut square = Table::new_from_element(square);
|
let mut square = Table::new_from_element(square);
|
||||||
square.with_attribute_mut_or_default("transform", 0, |t: &mut DAffine2| *t *= DAffine2::from_angle(std::f64::consts::FRAC_PI_4));
|
square.with_attribute_mut_or_default(ATTR_TRANSFORM, 0, |t: &mut DAffine2| *t *= DAffine2::from_angle(std::f64::consts::FRAC_PI_4));
|
||||||
let bounding_box = BoundingBoxNode { content: FutureWrapperNode(square) }.eval(Footprint::default()).await;
|
let bounding_box = BoundingBoxNode { content: FutureWrapperNode(square) }.eval(Footprint::default()).await;
|
||||||
let bounding_box = bounding_box.element(0).unwrap();
|
let bounding_box = bounding_box.element(0).unwrap();
|
||||||
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
assert_eq!(bounding_box.region_manipulator_groups().count(), 1);
|
||||||
|
|
@ -3157,7 +3157,7 @@ mod test {
|
||||||
async fn morph() {
|
async fn morph() {
|
||||||
let mut rectangles = vector_node_from_bezpath(Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY));
|
let mut rectangles = vector_node_from_bezpath(Rect::new(0., 0., 100., 100.).to_path(DEFAULT_ACCURACY));
|
||||||
let mut second_rectangle = rectangles.clone_row(0).unwrap();
|
let mut second_rectangle = rectangles.clone_row(0).unwrap();
|
||||||
*second_rectangle.attribute_mut_or_insert_default::<DAffine2>("transform") *= DAffine2::from_translation((-100., -100.).into());
|
*second_rectangle.attribute_mut_or_insert_default::<DAffine2>(ATTR_TRANSFORM) *= DAffine2::from_translation((-100., -100.).into());
|
||||||
rectangles.push(second_rectangle);
|
rectangles.push(second_rectangle);
|
||||||
|
|
||||||
let morphed = super::morph(Footprint::default(), rectangles, 0.5, false, InterpolationDistribution::default(), Table::default()).await;
|
let morphed = super::morph(Footprint::default(), rectangles, 0.5, false, InterpolationDistribution::default(), Table::default()).await;
|
||||||
|
|
@ -3168,7 +3168,7 @@ mod test {
|
||||||
vec![DVec2::new(0., 0.), DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)]
|
vec![DVec2::new(0., 0.), DVec2::new(100., 0.), DVec2::new(100., 100.), DVec2::new(0., 100.)]
|
||||||
);
|
);
|
||||||
// The interpolated transform carries the midpoint translation (approximate due to arc-length parameterization)
|
// The interpolated transform carries the midpoint translation (approximate due to arc-length parameterization)
|
||||||
assert!((morphed.attribute_cloned_or_default::<DAffine2>("transform", 0).translation - DVec2::new(-50., -50.)).length() < 1e-3);
|
assert!((morphed.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, 0).translation - DVec2::new(-50., -50.)).length() < 1e-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
|
|
@ -3244,7 +3244,7 @@ mod test {
|
||||||
let vector = Vector::from_bezpath(source);
|
let vector = Vector::from_bezpath(source);
|
||||||
let mut vector_table = Table::new_from_element(vector.clone());
|
let mut vector_table = Table::new_from_element(vector.clone());
|
||||||
|
|
||||||
vector_table.set_attribute("transform", 0, DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.)));
|
vector_table.set_attribute(ATTR_TRANSFORM, 0, DAffine2::from_scale_angle_translation(DVec2::splat(10.), 1., DVec2::new(99., 77.)));
|
||||||
|
|
||||||
let beveled = super::bevel((), Table::new_from_element(vector), 2_f64.sqrt() * 10.);
|
let beveled = super::bevel((), Table::new_from_element(vector), 2_f64.sqrt() * 10.);
|
||||||
let beveled = beveled.element(0).unwrap();
|
let beveled = beveled.element(0).unwrap();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue