mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 07:58:11 +08:00
Eliminate bare Graphic and Artboard graph data by making Merge and Artboard nodes internally use tables (#2996)
* Eliminate bare Graphic and Artboard graph data by making Merge and Artboard nodes internally use tables * Make the Extend node user-facing
This commit is contained in:
@@ -95,7 +95,7 @@ impl BoundingBox for Table<Artboard> {
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
|
||||
async fn create_artboard<T: Into<Table<Graphic>> + 'n>(
|
||||
ctx: impl ExtractAll + CloneVarArgs + Ctx,
|
||||
#[implementations(
|
||||
Context -> Table<Graphic>,
|
||||
@@ -104,13 +104,13 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> DAffine2,
|
||||
)]
|
||||
contents: impl Node<Context<'static>, Output = Data>,
|
||||
content: impl Node<Context<'static>, Output = T>,
|
||||
label: String,
|
||||
location: DVec2,
|
||||
dimensions: DVec2,
|
||||
background: Color,
|
||||
clip: bool,
|
||||
) -> Artboard {
|
||||
) -> Table<Artboard> {
|
||||
let location = location.as_ivec2();
|
||||
|
||||
let footprint = ctx.try_footprint().copied();
|
||||
@@ -119,7 +119,7 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
|
||||
footprint.translate(location.as_dvec2());
|
||||
new_ctx = new_ctx.with_footprint(footprint);
|
||||
}
|
||||
let group = contents.eval(new_ctx.into_context()).await.into();
|
||||
let group = content.eval(new_ctx.into_context()).await.into();
|
||||
|
||||
let dimensions = dimensions.as_ivec2().max(IVec2::ONE);
|
||||
|
||||
@@ -127,28 +127,12 @@ async fn to_artboard<Data: Into<Table<Graphic>> + 'n>(
|
||||
|
||||
let dimensions = dimensions.abs();
|
||||
|
||||
Artboard {
|
||||
Table::new_from_element(Artboard {
|
||||
group,
|
||||
label,
|
||||
location,
|
||||
dimensions,
|
||||
background,
|
||||
clip,
|
||||
}
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
pub async fn append_artboard(_ctx: impl Ctx, mut artboards: Table<Artboard>, artboard: Artboard, node_path: Vec<NodeId>) -> Table<Artboard> {
|
||||
// Get the penultimate element of the node path, or None if the path is too short.
|
||||
// This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node).
|
||||
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
|
||||
artboards.push(TableRow {
|
||||
element: artboard,
|
||||
transform: DAffine2::IDENTITY,
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
source_node_id: encapsulating_node_id,
|
||||
});
|
||||
|
||||
artboards
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use glam::{DAffine2, DVec2};
|
||||
#[node_macro::node(category("Debug"), name("Log to Console"))]
|
||||
fn log_to_console<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, Table<Vector>, DAffine2, Color, Option<Color>)] value: T) -> T {
|
||||
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
|
||||
log::debug!("{:#?}", value);
|
||||
log::debug!("{value:#?}");
|
||||
value
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ fn some<T>(_: impl Ctx, #[implementations(f64, f32, u32, u64, String, Color)] in
|
||||
|
||||
/// Meant for debugging purposes, not general use. Unwraps the input value from an Option, returning the default value if the input is None.
|
||||
#[node_macro::node(category("Debug"))]
|
||||
fn unwrap<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<f32>, Option<u32>, Option<u64>, Option<String>, Option<Color>)] input: Option<T>) -> T {
|
||||
fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<u32>, Option<u64>, Option<String>, Option<Color>)] input: Option<T>) -> T {
|
||||
input.unwrap_or_default()
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::table::{Table, TableRow};
|
||||
use crate::uuid::NodeId;
|
||||
use crate::vector::Vector;
|
||||
use crate::{Color, Ctx};
|
||||
use crate::{Artboard, Color, Ctx};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::hash::Hash;
|
||||
@@ -194,27 +194,66 @@ impl BoundingBox for Table<Graphic> {
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
async fn layer<I: 'n + Send + Clone>(
|
||||
async fn source_node_id<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
#[implementations(Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)] mut stack: Table<I>,
|
||||
#[implementations(Graphic, Vector, Raster<CPU>, Raster<GPU>)] element: I,
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)] content: Table<I>,
|
||||
node_path: Vec<NodeId>,
|
||||
) -> Table<I> {
|
||||
// Get the penultimate element of the node path, or None if the path is too short
|
||||
// This is used to get the ID of the user-facing parent layer-style node (which encapsulates this internal node).
|
||||
let source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
|
||||
stack.push(TableRow {
|
||||
element,
|
||||
transform: DAffine2::IDENTITY,
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
source_node_id,
|
||||
});
|
||||
let mut content = content;
|
||||
for row in content.iter_mut() {
|
||||
*row.source_node_id = source_node_id;
|
||||
}
|
||||
|
||||
stack
|
||||
content
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug"))]
|
||||
async fn to_element<Data: Into<Graphic> + 'n>(
|
||||
/// Joins two tables of the same type, extending the base table with the rows of the new table.
|
||||
#[node_macro::node(category("General"))]
|
||||
async fn extend<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
/// The table whose rows will appear at the start of the extended table.
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)]
|
||||
base: Table<I>,
|
||||
/// The table whose rows will appear at the end of the extended table.
|
||||
#[expose]
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)]
|
||||
new: Table<I>,
|
||||
) -> Table<I> {
|
||||
let mut base = base;
|
||||
base.extend(new);
|
||||
|
||||
base
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
#[node_macro::node(category(""))]
|
||||
async fn legacy_layer_extend<I: 'n + Send + Clone>(
|
||||
_: impl Ctx,
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)] base: Table<I>,
|
||||
#[expose]
|
||||
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>)]
|
||||
new: Table<I>,
|
||||
nested_node_path: Vec<NodeId>,
|
||||
) -> Table<I> {
|
||||
// Get the penultimate element of the node path, or None if the path is too short
|
||||
// This is used to get the ID of the user-facing parent layer-style node (which encapsulates this internal node).
|
||||
let source_node_id = nested_node_path.get(nested_node_path.len().wrapping_sub(2)).copied();
|
||||
|
||||
let mut base = base;
|
||||
for row in new.into_iter() {
|
||||
base.push(TableRow { source_node_id, ..row });
|
||||
}
|
||||
|
||||
base
|
||||
}
|
||||
|
||||
/// Places a table of graphical content into an element of a new wrapper graphic table.
|
||||
#[node_macro::node(category("General"))]
|
||||
async fn wrap_graphic<T: Into<Graphic> + 'n>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
@@ -223,13 +262,15 @@ async fn to_element<Data: Into<Graphic> + 'n>(
|
||||
Table<Raster<GPU>>,
|
||||
DAffine2,
|
||||
)]
|
||||
data: Data,
|
||||
) -> Graphic {
|
||||
data.into()
|
||||
content: T,
|
||||
) -> Table<Graphic> {
|
||||
Table::new_from_element(content.into())
|
||||
}
|
||||
|
||||
#[node_macro::node(category("General"))]
|
||||
async fn to_group<Data: Into<Table<Graphic>> + 'n>(
|
||||
/// Converts a table of graphical content into a graphic table by placing it into an element of a new wrapper graphic table.
|
||||
/// If it is already a graphic table, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
|
||||
#[node_macro::node(category("Type Conversion"))]
|
||||
async fn to_graphic<T: Into<Table<Graphic>> + 'n>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Graphic>,
|
||||
@@ -237,34 +278,34 @@ async fn to_group<Data: Into<Table<Graphic>> + 'n>(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Raster<GPU>>,
|
||||
)]
|
||||
element: Data,
|
||||
content: T,
|
||||
) -> Table<Graphic> {
|
||||
element.into()
|
||||
content.into()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("General"))]
|
||||
async fn flatten_group(_: impl Ctx, group: Table<Graphic>, fully_flatten: bool) -> Table<Graphic> {
|
||||
async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten: bool) -> Table<Graphic> {
|
||||
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
|
||||
fn flatten_group(output_group_table: &mut Table<Graphic>, current_group_table: Table<Graphic>, fully_flatten: bool, recursion_depth: usize) {
|
||||
for current_row in current_group_table.iter() {
|
||||
fn flatten_table(output_graphic_table: &mut Table<Graphic>, current_graphic_table: Table<Graphic>, fully_flatten: bool, recursion_depth: usize) {
|
||||
for current_row in current_graphic_table.iter() {
|
||||
let current_element = current_row.element.clone();
|
||||
let reference = *current_row.source_node_id;
|
||||
|
||||
let recurse = fully_flatten || recursion_depth == 0;
|
||||
|
||||
match current_element {
|
||||
// If we're allowed to recurse, flatten any groups we encounter
|
||||
// If we're allowed to recurse, flatten any graphics we encounter
|
||||
Graphic::Group(mut current_element) if recurse => {
|
||||
// Apply the parent group's transform to all child elements
|
||||
// Apply the parent graphic's transform to all child elements
|
||||
for graphic in current_element.iter_mut() {
|
||||
*graphic.transform = *current_row.transform * *graphic.transform;
|
||||
}
|
||||
|
||||
flatten_group(output_group_table, current_element, fully_flatten, recursion_depth + 1);
|
||||
flatten_table(output_graphic_table, current_element, fully_flatten, recursion_depth + 1);
|
||||
}
|
||||
// Handle any leaf elements we encounter, which can be either non-group elements or groups that we don't want to flatten
|
||||
// Push any leaf Graphic elements we encounter, which can be either Graphic table elements beyond the recursion depth, or table elements other than Graphic tables
|
||||
_ => {
|
||||
output_group_table.push(TableRow {
|
||||
output_graphic_table.push(TableRow {
|
||||
element: current_element,
|
||||
transform: *current_row.transform,
|
||||
alpha_blending: *current_row.alpha_blending,
|
||||
@@ -276,33 +317,33 @@ async fn flatten_group(_: impl Ctx, group: Table<Graphic>, fully_flatten: bool)
|
||||
}
|
||||
|
||||
let mut output = Table::new();
|
||||
flatten_group(&mut output, group, fully_flatten, 0);
|
||||
flatten_table(&mut output, content, fully_flatten, 0);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector"))]
|
||||
async fn flatten_vector(_: impl Ctx, group: Table<Graphic>) -> Table<Vector> {
|
||||
async fn flatten_vector(_: impl Ctx, content: Table<Graphic>) -> Table<Vector> {
|
||||
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
|
||||
fn flatten_group(output_group_table: &mut Table<Vector>, current_group_table: Table<Graphic>) {
|
||||
for current_graphic_row in current_group_table.iter() {
|
||||
fn flatten_table(output_vector_table: &mut Table<Vector>, current_graphic_table: Table<Graphic>) {
|
||||
for current_graphic_row in current_graphic_table.iter() {
|
||||
let current_graphic = current_graphic_row.element.clone();
|
||||
let source_node_id = *current_graphic_row.source_node_id;
|
||||
|
||||
match current_graphic {
|
||||
// If we're allowed to recurse, flatten any groups we encounter
|
||||
// If we're allowed to recurse, flatten any tables we encounter
|
||||
Graphic::Group(mut current_graphic_table) => {
|
||||
// Apply the parent group's transform to all child elements
|
||||
// Apply the parent graphic's transform to all child elements
|
||||
for graphic in current_graphic_table.iter_mut() {
|
||||
*graphic.transform = *current_graphic_row.transform * *graphic.transform;
|
||||
}
|
||||
|
||||
flatten_group(output_group_table, current_graphic_table);
|
||||
flatten_table(output_vector_table, current_graphic_table);
|
||||
}
|
||||
// Handle any leaf elements we encounter, which can be either non-group elements or groups that we don't want to flatten
|
||||
// Push any leaf Vector elements we encounter
|
||||
Graphic::Vector(vector_table) => {
|
||||
for current_vector_row in vector_table.iter() {
|
||||
output_group_table.push(TableRow {
|
||||
output_vector_table.push(TableRow {
|
||||
element: current_vector_row.element.clone(),
|
||||
transform: *current_graphic_row.transform * *current_vector_row.transform,
|
||||
alpha_blending: AlphaBlending {
|
||||
@@ -321,7 +362,7 @@ async fn flatten_vector(_: impl Ctx, group: Table<Graphic>) -> Table<Vector> {
|
||||
}
|
||||
|
||||
let mut output = Table::new();
|
||||
flatten_group(&mut output, group);
|
||||
flatten_table(&mut output, content);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::vector::Vector;
|
||||
use crate::{Context, Ctx};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[node_macro::node(category("Text"))]
|
||||
#[node_macro::node(category("Type Conversion"))]
|
||||
fn to_string<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Table<Vector>)] value: T) -> String {
|
||||
format!("{:?}", value)
|
||||
}
|
||||
@@ -60,11 +60,10 @@ async fn switch<T, C: Send + 'n + Clone>(
|
||||
Context -> DVec2,
|
||||
Context -> DAffine2,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Graphic,
|
||||
Context -> Color,
|
||||
Context -> Option<Color>,
|
||||
Context -> GradientStops,
|
||||
@@ -81,11 +80,10 @@ async fn switch<T, C: Send + 'n + Clone>(
|
||||
Context -> DVec2,
|
||||
Context -> DAffine2,
|
||||
Context -> Table<Artboard>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Graphic>,
|
||||
Context -> Table<Vector>,
|
||||
Context -> Table<Raster<CPU>>,
|
||||
Context -> Table<Raster<GPU>>,
|
||||
Context -> Graphic,
|
||||
Context -> Color,
|
||||
Context -> Option<Color>,
|
||||
Context -> GradientStops,
|
||||
|
||||
@@ -965,8 +965,8 @@ where
|
||||
// connected to a Flatten Path connected to an if else node, another connection from the cache directly to
|
||||
// the if else node, and another connection from the cache to a matches type node connected to the if else node.
|
||||
|
||||
fn flatten_group(group: &Table<Graphic>, output: &mut TableRowMut<Vector>) {
|
||||
for (group_index, current_element) in group.iter().enumerate() {
|
||||
fn flatten_table(output: &mut TableRowMut<Vector>, graphic_table: &Table<Graphic>) {
|
||||
for (current_index, current_element) in graphic_table.iter().enumerate() {
|
||||
match current_element.element {
|
||||
Graphic::Vector(vector) => {
|
||||
// Loop through every row of the `Table<Vector>` and concatenate each element's subpath into the output `Vector` element.
|
||||
@@ -976,7 +976,7 @@ where
|
||||
let node_id = current_element.source_node_id.map(|node_id| node_id.0).unwrap_or_default();
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
(group_index, vector_index, node_id).hash(&mut hasher);
|
||||
(current_index, vector_index, node_id).hash(&mut hasher);
|
||||
let collision_hash_seed = hasher.finish();
|
||||
|
||||
output.element.concat(other, transform, collision_hash_seed);
|
||||
@@ -985,13 +985,13 @@ where
|
||||
output.element.style = row.element.style.clone();
|
||||
}
|
||||
}
|
||||
Graphic::Group(group) => {
|
||||
let mut group = group.clone();
|
||||
for row in group.iter_mut() {
|
||||
Graphic::Group(graphic) => {
|
||||
let mut graphic = graphic.clone();
|
||||
for row in graphic.iter_mut() {
|
||||
*row.transform = *current_element.transform * *row.transform;
|
||||
}
|
||||
|
||||
flatten_group(&group, output);
|
||||
flatten_table(output, &graphic);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1000,13 +1000,11 @@ where
|
||||
|
||||
// Create a table with one empty `Vector` element, then get a mutable reference to it which we append flattened subpaths to
|
||||
let mut output_table = Table::new_from_element(Vector::default());
|
||||
let Some(mut output) = output_table.iter_mut().next() else {
|
||||
return output_table;
|
||||
};
|
||||
let Some(mut output) = output_table.iter_mut().next() else { return output_table };
|
||||
|
||||
// Flatten the group input into the output `Vector` element
|
||||
let base_group = Table::new_from_element(Graphic::from(content));
|
||||
flatten_group(&base_group, &mut output);
|
||||
// Flatten the graphic input into the output `Vector` element
|
||||
let base_graphic_table = Table::new_from_element(Graphic::from(content));
|
||||
flatten_table(&mut output, &base_graphic_table);
|
||||
|
||||
// Return the single-row Table<Vector> containing the flattened Vector subpaths
|
||||
output_table
|
||||
|
||||
Reference in New Issue
Block a user