Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ use crate::messages::portfolio::document::data_panel::DataPanelMessage;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::{Affine2, Vec2};
use glam::{Affine2, DAffine2, Vec2};
use graph_craft::document::NodeId;
use graphene_std::Color;
use graphene_std::Context;
use graphene_std::gradient::GradientStops;
use graphene_std::memo::IORecord;
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::table::Table;
use graphene_std::vector::Vector;
use graphene_std::vector::style::{Fill, FillChoice};
use graphene_std::{AlphaBlending, Color};
use graphene_std::{Artboard, Graphic};
use std::any::Any;
use std::sync::Arc;
Expand Down Expand Up @@ -247,9 +247,9 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
}
fn element_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
if let Some(index) = data.desired_path.get(data.current_depth).copied() {
if let Some(row) = self.get(index) {
if let Some(element) = self.element(index) {
data.current_depth += 1;
let result = row.element.layout_with_breadcrumb(data);
let result = element.layout_with_breadcrumb(data);
data.current_depth -= 1;
return result;
} else {
Expand All @@ -258,23 +258,33 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
}
}

let mut rows = self
.iter()
.enumerate()
.map(|(index, row)| {
vec![
TextLabel::new(format!("{index}")).narrow(true).widget_instance(),
row.element.element_widget(index),
TextLabel::new(format_transform_matrix(row.transform)).narrow(true).widget_instance(),
TextLabel::new(format!("{}", row.alpha_blending)).narrow(true).widget_instance(),
TextLabel::new(row.source_node_id.map_or_else(|| "-".to_string(), |id| format!("{}", id.0)))
.narrow(true)
.widget_instance(),
]
let attribute_keys: Vec<String> = self.attribute_keys().map(str::to_string).collect();

let mut rows = (0..self.len())
.map(|index| {
let element = self.element(index).unwrap();
let mut cells = vec![TextLabel::new(format!("{index}")).narrow(true).widget_instance(), element.element_widget(index)];
for key in &attribute_keys {
let value = self
.attribute_display_value(key, index, |ty| {
Some(match () {
() if let Some(&value) = ty.downcast_ref::<DAffine2>() => format_transform_matrix(value),
() if let Some(&value) = ty.downcast_ref::<DVec2>() => format_dvec2(value),
() if let Some(&value) = ty.downcast_ref::<AlphaBlending>() => format_alpha_blending(value),
() if let Some(&value) = ty.downcast_ref::<Option<NodeId>>() => value.map_or_else(|| "-".to_string(), |id| id.to_string()),
_ => return None,
})
})
.unwrap_or_else(|| "-".to_string());
cells.push(TextLabel::new(value).narrow(true).widget_instance());
}
cells
})
.collect::<Vec<_>>();

rows.insert(0, column_headings(&["", "element", "transform", "alpha_blending", "source_node_id"]));
let mut column_names = vec!["", "element"];
column_names.extend(attribute_keys.iter().map(|s| s.as_str()));
rows.insert(0, column_headings(&column_names));

vec![LayoutGroup::table(rows, false)]
}
Expand Down Expand Up @@ -430,7 +440,7 @@ impl TableRowLayout for Vector {
]);
table_rows.push(vec![
TextLabel::new("Stroke Transform").narrow(true).widget_instance(),
TextLabel::new(format_transform_matrix(&stroke.transform)).narrow(true).widget_instance(),
TextLabel::new(format_transform_matrix(stroke.transform)).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Stroke Paint Order").narrow(true).widget_instance(),
Expand Down Expand Up @@ -695,7 +705,7 @@ impl TableRowLayout for DAffine2 {
"Transform".to_string()
}
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
let widgets = vec![TextLabel::new(format_transform_matrix(self)).widget_instance()];
let widgets = vec![TextLabel::new(format_transform_matrix(*self)).widget_instance()];
vec![LayoutGroup::row(widgets)]
}
}
Expand All @@ -709,12 +719,12 @@ impl TableRowLayout for Affine2 {
}
fn element_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
let matrix = DAffine2::from_cols_array(&self.to_cols_array().map(|x| x as f64));
let widgets = vec![TextLabel::new(format_transform_matrix(&matrix)).widget_instance()];
let widgets = vec![TextLabel::new(format_transform_matrix(matrix)).widget_instance()];
vec![LayoutGroup::row(widgets)]
}
}

fn format_transform_matrix(transform: &DAffine2) -> String {
fn format_transform_matrix(transform: DAffine2) -> String {
let (scale, angle, translation) = if transform.matrix2.determinant().abs() <= f64::EPSILON {
let [col_0, col_1] = transform.matrix2.to_cols_array_2d().map(|[x, y]| DVec2::new(x, y));

Expand Down Expand Up @@ -748,3 +758,14 @@ fn format_dvec2(value: DVec2) -> String {
let round = |x: f64| (x * 1e3).round() / 1e3;
format!("({} px, {} px)", round(value.x), round(value.y))
}

fn format_alpha_blending(value: AlphaBlending) -> String {
let round = |x: f32| (x * 1e3).round() / 1e3;
format!(
"Blend Mode: {} — Opacity: {}% — Fill: {}% — Clip: {}",
value.blend_mode,
round(value.opacity * 100.),
round(value.fill * 100.),
if value.clip { "Yes" } else { "No" }
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -713,10 +713,10 @@ fn set_import_child_positions(
let child_pos = IVec2::new(child_x, current_y);

if i == 0 {
// Top of stack set to `Absolute` position
// Top of stack: set to `Absolute` position
network_interface.set_layer_position_for_import(&child_layer.to_node(), LayerPosition::Absolute(child_pos), &[]);
} else {
// Below top set `Stack` with `y_offset` based on previous sibling's subtree extent
// Below top: set `Stack` with `y_offset` based on previous sibling's subtree extent
let prev_sibling_svg_index = n - i;
let y_offset = child_extents_svg_order[prev_sibling_svg_index] + STACK_VERTICAL_GAP as u32;
network_interface.set_layer_position_for_import(&child_layer.to_node(), LayerPosition::Stack(y_offset), &[]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1178,8 +1178,8 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
match &**tagged_value {
TaggedValue::Color(color_table) => widgets.push(
color_button
.value(match color_table.iter().next() {
Some(color) => FillChoice::Solid(*color.element),
.value(match color_table.element(0) {
Some(color) => FillChoice::Solid(*color),
None => FillChoice::None,
})
.on_update(update_value(
Expand All @@ -1192,8 +1192,8 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
),
TaggedValue::GradientTable(gradient_table) => widgets.push(
color_button
.value(match gradient_table.iter().next() {
Some(row) => FillChoice::Gradient(row.element.clone()),
.value(match gradient_table.element(0) {
Some(gradient) => FillChoice::Gradient(gradient.clone()),
None => FillChoice::Gradient(GradientStops::default()),
})
.on_update(update_value(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1166,16 +1166,18 @@ impl OverlayContextInternal {
fn render_text_paths(&mut self, text_table: &Table<Vector>, font_color: &str, base_transform: kurbo::Affine) {
let color = Self::parse_color(font_color);

for row in text_table.iter() {
for index in 0..text_table.len() {
// Use the existing bezier_to_path infrastructure to convert Vector to BezPath
let mut path = BezPath::new();
let mut last_point = None;
let transform: DAffine2 = text_table.attribute_cloned_or_default("transform", index);

for (_, bezier, start_id, end_id) in row.element.segment_iter() {
let Some(element) = text_table.element(index) else { continue };
Comment thread
Keavon marked this conversation as resolved.
for (_, bezier, start_id, end_id) in element.segment_iter() {
let move_to = last_point != Some(start_id);
last_point = Some(end_id);

self.bezier_to_path(bezier, *row.transform, move_to, &mut path);
self.bezier_to_path(bezier, transform, move_to, &mut path);
}

// Render the path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl DocumentMetadata {
.any(|upstream| Some(upstream) == source)
{
use_local = false;
info!("Local transform is invalid — using the identity for the local transform instead")
info!("Local transform is invalid. Using the identity for the local transform instead.");
}
let local_transform = use_local.then(|| self.local_transforms.get(&layer.to_node()).copied()).flatten().unwrap_or_default();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5521,11 +5521,11 @@ impl NodeNetworkInterface {

match post_node_input {
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Inline(_) | NodeInput::Reflection(_) => {
// First child in the stack wire layer output to the post_node input
// First child in the stack: wire layer output to the post_node input
self.set_input_for_import(&post_node, layer_output, network_path);
}
NodeInput::Node { .. } => {
// Subsequent childinsert layer between post_node and its current upstream:
// Subsequent child: insert layer between post_node and its current upstream...
// 1. Disconnect old upstream from post_node, wire layer output to post_node
self.set_input_for_import(&post_node, layer_output, network_path);
// 2. Wire old upstream into layer's primary (stack) input
Expand Down
6 changes: 3 additions & 3 deletions editor/src/messages/portfolio/document_migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1962,7 +1962,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
&& let TaggedValue::Vector(vector_table) = &**tagged_value
&& !vector_table.is_empty()
{
let vector = vector_table.iter().next()?.element;
let vector = vector_table.element(0)?;
let modification = Box::new(graphene_std::vector::VectorModification::create_from_vector(vector));

// Reset input 0 to the default exposed state
Expand All @@ -1981,9 +1981,9 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::image::IDENTIFIER)
&& let Some(NodeInput::Value { tagged_value, .. }) = node.inputs.get(1)
&& let TaggedValue::Raster(raster_table) = &**tagged_value
&& let Some(row) = raster_table.iter().next()
&& let Some(element) = raster_table.element(0)
{
let image = row.element.data().clone();
let image = element.data().clone();

document
.network_interface
Expand Down
10 changes: 6 additions & 4 deletions editor/src/messages/tool/tool_messages/artboard_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,10 +646,12 @@ mod test_artboard {

/// Check if all of the artboards exist in any ordering
async fn has_artboards(editor: &mut EditorTestUtils, mut expected: Vec<ArtboardLayoutDocument>) {
let artboards = get_artboards(editor)
.await
.iter()
.map(|row| ArtboardLayoutDocument::new(row.element.location, row.element.dimensions))
let artboards = get_artboards(editor).await;
let artboards = (0..artboards.len())
.map(|index| {
let element = artboards.element(index).unwrap();
ArtboardLayoutDocument::new(element.location, element.dimensions)
})
.collect::<Vec<_>>();
assert_eq!(artboards.len(), expected.len(), "incorrect len: actual {:?}, expected {:?}", artboards, expected);

Expand Down
6 changes: 2 additions & 4 deletions editor/src/node_graph_executor/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use graphene_std::ops::Convert;
use graphene_std::platform_application_io::canvas_utils::{Canvas, CanvasSurface, CanvasSurfaceHandle};
use graphene_std::raster_types::Raster;
use graphene_std::renderer::{Render, RenderParams, RenderSvgSegmentList, SvgRender, SvgSegment};
use graphene_std::table::{Table, TableRow};
use graphene_std::table::Table;
use graphene_std::text::FontCache;
use graphene_std::transform::RenderQuality;
use graphene_std::vector::Vector;
Expand Down Expand Up @@ -441,9 +441,7 @@ impl NodeRuntime {
// Vector table: vector modifications
else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Vector>>>() {
// Insert the vector modify
let default = TableRow::default();
self.vector_modify
.insert(parent_network_node_id, io.output.iter().next().unwrap_or_else(|| default.as_ref()).element.clone());
self.vector_modify.insert(parent_network_node_id, io.output.element(0).cloned().unwrap_or_default());
}
// Other
else {
Expand Down
18 changes: 9 additions & 9 deletions node-graph/graph-craft/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ wasm = [
# Local dependencies
dyn-any = { workspace = true }
graphene-hash = { workspace = true }
core-types = { workspace = true }
brush-nodes = { workspace = true }
graphene-core = { workspace = true }
graphene-application-io = { workspace = true }
rendering = { workspace = true }
raster-nodes = { workspace = true }
vector-nodes = { workspace = true }
graphic-types = { workspace = true }
text-nodes = { workspace = true }
core-types = { workspace = true, features = ["serde"] }
brush-nodes = { workspace = true, features = ["serde"] }
graphene-core = { workspace = true, features = ["serde"] }
graphene-application-io = { workspace = true, features = ["serde"] }
rendering = { workspace = true, features = ["serde"] }
raster-nodes = { workspace = true, features = ["serde"] }
vector-nodes = { workspace = true, features = ["serde"] }
graphic-types = { workspace = true, features = ["serde"] }
text-nodes = { workspace = true, features = ["serde"] }

# Workspace dependencies
log = { workspace = true }
Expand Down
1 change: 0 additions & 1 deletion node-graph/graph-craft/src/document/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ tagged_value! {
// ===========
// TABLE TYPES
// ===========
GraphicUnused(Graphic), // TODO: This is unused but removing it causes `cargo test` to infinitely recurse its type solving; figure out why and then remove this
#[serde(deserialize_with = "graphic_types::migrations::migrate_vector")] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "VectorData")]
Vector(Table<Vector>),
Expand Down
6 changes: 5 additions & 1 deletion node-graph/libraries/application-io/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ authors = ["Graphite Authors <contact@graphite.art>"]
license = "MIT OR Apache-2.0"

[features]
default = ["serde"]
serde = ["dep:serde", "core-types/serde", "vector-types/serde", "text-nodes/serde"]
wasm = ["dep:web-sys"]
wgpu = ["dep:wgpu"]

Expand All @@ -19,9 +21,11 @@ text-nodes = { workspace = true }

# Workspace dependencies
glam = { workspace = true }
serde = { workspace = true }
log = { workspace = true }

# Optional workspace dependencies
serde = { workspace = true, optional = true }

# Optional workspace dependencies
web-sys = { workspace = true, optional = true }
wgpu = { workspace = true, optional = true }
14 changes: 9 additions & 5 deletions node-graph/libraries/application-io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ pub enum ApplicationError {
InvalidUrl,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NodeGraphUpdateMessage {}

pub trait NodeGraphUpdateSender {
Expand All @@ -90,26 +91,29 @@ pub trait GetEditorPreferences {
fn max_render_region_area(&self) -> u32;
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ExportFormat {
#[default]
Svg,
Raster,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TimingInformation {
pub time: f64,
pub animation_time: Duration,
}

#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RenderConfig {
pub viewport: Footprint,
pub scale: f64,
pub time: TimingInformation,
pub pointer: DVec2,
#[serde(alias = "view_mode")]
#[cfg_attr(feature = "serde", serde(alias = "view_mode"))]
pub render_mode: RenderMode,
pub export_format: ExportFormat,
pub for_export: bool,
Expand Down
1 change: 1 addition & 0 deletions node-graph/libraries/core-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0"

[features]
default = ["serde"]
serde = ["dep:serde"]
nightly = []
type_id_logging = []
dealloc_nodes = []
Expand Down
Loading
Loading