Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo-artwork/changing-seasons.graphite

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo-artwork/isometric-fountain.graphite

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo-artwork/marbled-mandelbrot.graphite

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo-artwork/painted-dreams.graphite

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo-artwork/parametric-dunescape.graphite

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo-artwork/procedural-string-lights.graphite

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo-artwork/red-dress.graphite

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo-artwork/valley-of-spires.graphite

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions editor/src/messages/broadcast/event/event_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ pub enum EventMessage {
CanvasTransformed,
ToolAbort,
SelectionChanged,
/// The document graph's nodes or wires changed, so state derived from the selection's chains may be stale
GraphChanged,
WorkingColorChanged,
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ pub enum PathStep {
#[derive(PartialEq, Eq, Clone, Copy, Default, Debug, serde::Serialize, serde::Deserialize)]
pub enum VectorTableTab {
#[default]
Properties,
Points,
Segments,
Regions,
Handles,
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup,
use crate::messages::portfolio::document::data_panel::{DataPanelMessage, PathStep};
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::shapes::shape_utility::{format_rounded, round_away_float_noise};
use crate::messages::tool::common_functionality::shapes::shape_utility::format_rounded;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::{Affine2, DAffine2, Vec2};
use graph_craft::document::NodeId;
Expand All @@ -18,17 +18,18 @@ use graphene_std::raster::{
CellularDistanceFunction, CellularReturnType, DomainWarpType, FractalType, LuminanceCalculation, NoiseType, RedGreenBlue, RedGreenBlueAlpha, RelativeAbsolute, SelectiveColorChoice,
};
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::subpath::BezierHandles;
use graphene_std::text::TextAlign;
use graphene_std::text_nodes::StringCapitalization;
use graphene_std::transform::{ReferencePoint, ScaleType};
use graphene_std::vector::misc::{
ArcType, BooleanOperation, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType,
};
use graphene_std::vector::style::{
DashPattern, FillChoice, GradientForm, GradientHueDirection, GradientInterpolation, GradientRamp, GradientSettings, GradientSpace, GradientSpread, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin,
DashPattern, FillChoice, GradientForm, GradientHueDirection, GradientInterpolation, GradientRamp, GradientSettings, GradientSpace, GradientSpread, StrokeAlign, StrokeCap, StrokeJoin,
};
use graphene_std::vector::{QRCodeErrorCorrectionLevel, Vector};
use graphene_std::{Artboard, Color, Context, Graphic};
use graphene_std::{Appearance, Artboard, Color, Context, Cover, Coverage, Graphic};
use std::any::Any;
use std::sync::Arc;

Expand Down Expand Up @@ -228,7 +229,6 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
List<StrokeJoin>,
List<StrokeAlign>,
List<StrokeCap>,
List<PaintOrder>,
List<MergeByDistanceAlgorithm>,
List<ExtrudeJoiningAlgorithm>,
List<PointSpacingType>,
Expand Down Expand Up @@ -284,7 +284,6 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
Item<StrokeJoin>,
Item<StrokeAlign>,
Item<StrokeCap>,
Item<PaintOrder>,
Item<MergeByDistanceAlgorithm>,
Item<ExtrudeJoiningAlgorithm>,
Item<PointSpacingType>,
Expand Down Expand Up @@ -513,6 +512,45 @@ impl TableItemLayout for DashPattern {
}
}

impl TableItemLayout for Appearance {
fn type_name() -> &'static str {
"Appearance"
}
fn identifier(&self) -> String {
"Appearance".to_string()
}
// The wrapping `Item` already contributes the breadcrumb; the inner list supplies the next level
fn layout_with_breadcrumb(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
self.value_page(data)
}
// Label the spreadsheet's element button with the inner list's identifier, like Artboard
fn value_widgets(&self, target: PathStep, data: &LayoutData) -> Vec<WidgetInstance> {
self.0.value_widgets(target, data)
}
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
self.0.layout_with_breadcrumb(data)
}
}

impl TableItemLayout for Coverage {
fn type_name() -> &'static str {
"Coverage"
}
fn identifier(&self) -> String {
"Coverage".to_string()
}
// The wrapping row already contributes the breadcrumb; the inner item supplies the next level
fn layout_with_breadcrumb(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
self.value_page(data)
}
fn value_widgets(&self, target: PathStep, data: &LayoutData) -> Vec<WidgetInstance> {
self.0.value_widgets(target, data)
}
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
self.0.layout_with_breadcrumb(data)
}
}

impl TableItemLayout for BoxCorners {
fn type_name() -> &'static str {
"BoxCorners"
Expand Down Expand Up @@ -581,7 +619,7 @@ impl TableItemLayout for Vector {
)
}
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
let table_tab_entries = [VectorTableTab::Properties, VectorTableTab::Points, VectorTableTab::Segments, VectorTableTab::Regions]
let table_tab_entries = [VectorTableTab::Points, VectorTableTab::Segments, VectorTableTab::Regions, VectorTableTab::Handles]
.into_iter()
.map(|tab| {
RadioEntryData::new(format!("{tab:?}"))
Expand All @@ -593,89 +631,49 @@ impl TableItemLayout for Vector {

let mut table_rows = Vec::new();
match data.vector_table_tab {
VectorTableTab::Properties => {
table_rows.push(column_headings(&["property", "value"]));

if let Some(stroke) = self.stroke.as_ref() {
table_rows.push(vec![
TextLabel::new("Stroke Weight").narrow(true).widget_instance(),
TextLabel::new(format!("{} px", stroke.weight)).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Stroke Dash Lengths").narrow(true).widget_instance(),
TextLabel::new(if stroke.dash_lengths.is_empty() {
"-".to_string()
} else {
format!("[{}]", stroke.dash_lengths.iter().map(|x| format!("{x} px")).collect::<Vec<_>>().join(", "))
})
.narrow(true)
.widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Stroke Dash Offset").narrow(true).widget_instance(),
TextLabel::new(format!("{}", stroke.dash_offset)).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Stroke Cap").narrow(true).widget_instance(),
TextLabel::new(stroke.cap.to_string()).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Stroke Join").narrow(true).widget_instance(),
TextLabel::new(stroke.join.to_string()).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Stroke Join Miter Limit").narrow(true).widget_instance(),
TextLabel::new(format!("{}", stroke.join_miter_limit)).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("Stroke Align").narrow(true).widget_instance(),
TextLabel::new(stroke.align.to_string()).narrow(true).widget_instance(),
]);
table_rows.push(vec![
TextLabel::new("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(),
TextLabel::new(stroke.paint_order.to_string()).narrow(true).widget_instance(),
]);
}

let colinear = self.colinear_manipulators.iter().map(|[a, b]| format!("[{a} / {b}]")).collect::<Vec<_>>().join(", ");
let colinear = if colinear.is_empty() { "-".to_string() } else { colinear };
table_rows.push(vec![
TextLabel::new("Colinear Handle IDs").narrow(true).widget_instance(),
TextLabel::new(colinear).narrow(true).widget_instance(),
]);
VectorTableTab::Handles => {
table_rows.push(column_headings(&["", "colinear_manipulators[0]", "colinear_manipulators[1]"]));
table_rows.extend(self.colinear_manipulators.iter().enumerate().map(|(index, [a, b])| {
vec![
TextLabel::new(format!("{index}")).narrow(true).widget_instance(),
TextLabel::new(format!("{a}")).narrow(true).widget_instance(),
TextLabel::new(format!("{b}")).narrow(true).widget_instance(),
]
}));
}
VectorTableTab::Points => {
table_rows.push(column_headings(&["", "position"]));
table_rows.extend(self.point_domain.iter().map(|(id, position)| {
let position = DVec2::new(round_away_float_noise(position.x), round_away_float_noise(position.y));
vec![
TextLabel::new(format!("{}", id.inner())).narrow(true).widget_instance(),
TextLabel::new(format!("{position}")).narrow(true).widget_instance(),
TextLabel::new(format_dvec2(position)).narrow(true).widget_instance(),
]
}));
}
VectorTableTab::Segments => {
table_rows.push(column_headings(&["", "start_index", "end_index", "handles"]));
table_rows.push(column_headings(&["", "start_point", "end_point", "handles"]));
Comment thread
Keavon marked this conversation as resolved.
table_rows.extend(self.segment_domain.iter().map(|(id, start, end, handles)| {
let handles = match handles {
BezierHandles::Linear => "Linear".to_string(),
BezierHandles::Quadratic { handle } => format!("Quadratic — {}", format_dvec2(handle)),
BezierHandles::Cubic { handle_start, handle_end } => format!("Cubic — start: {}, end: {}", format_dvec2(handle_start), format_dvec2(handle_end)),
};
vec![
TextLabel::new(format!("{}", id.inner())).narrow(true).widget_instance(),
TextLabel::new(format!("{start}")).narrow(true).widget_instance(),
TextLabel::new(format!("{end}")).narrow(true).widget_instance(),
TextLabel::new(format!("{handles:?}")).narrow(true).widget_instance(),
TextLabel::new(format!("Point {start}")).narrow(true).widget_instance(),
TextLabel::new(format!("Point {end}")).narrow(true).widget_instance(),
TextLabel::new(handles).narrow(true).widget_instance(),
]
}));
}
VectorTableTab::Regions => {
table_rows.push(column_headings(&["", "segment_range", "fill"]));
table_rows.extend(self.region_domain.iter().map(|(id, segment_range, fill)| {
table_rows.push(column_headings(&["", "segment_range"]));
table_rows.extend(self.region_domain.iter().map(|(id, segment_range, _)| {
vec![
TextLabel::new(format!("{}", id.inner())).narrow(true).widget_instance(),
TextLabel::new(format!("{segment_range:?}")).narrow(true).widget_instance(),
TextLabel::new(format!("{}", fill.inner())).narrow(true).widget_instance(),
TextLabel::new(format!("Segment {} – Segment {}", segment_range.start().inner(), segment_range.end().inner()))
.narrow(true)
.widget_instance(),
]
}));
}
Expand Down Expand Up @@ -1019,6 +1017,7 @@ macro_rules! impl_table_item_layout_for_choice_enum {
}
impl_table_item_layout_for_choice_enum!(
BlendMode,
Cover,
GradientForm,
GradientSpread,
GradientSpace,
Expand All @@ -1027,7 +1026,6 @@ impl_table_item_layout_for_choice_enum!(
StrokeJoin,
StrokeAlign,
StrokeCap,
PaintOrder,
MergeByDistanceAlgorithm,
ExtrudeJoiningAlgorithm,
PointSpacingType,
Expand Down Expand Up @@ -1231,6 +1229,9 @@ macro_rules! known_item_types {
Raster<GPU>,
Graphic,
Artboard,
Appearance,
Coverage,
Cover,
DashPattern,
BoxCorners,
BlendMode,
Expand All @@ -1242,7 +1243,6 @@ macro_rules! known_item_types {
StrokeJoin,
StrokeAlign,
StrokeCap,
PaintOrder,
MergeByDistanceAlgorithm,
ExtrudeJoiningAlgorithm,
PointSpacingType,
Expand Down
13 changes: 4 additions & 9 deletions editor/src/messages/portfolio/document/document_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ use crate::messages::portfolio::utility_types::PanelType;
use crate::messages::prelude::*;
use glam::{DAffine2, IVec2};
use graph_craft::document::NodeId;
use graphene_std::Appearance;
use graphene_std::Color;
use graphene_std::Graphic;
use graphene_std::list::List;
use graphene_std::raster::BlendMode;
use graphene_std::raster::Image;
use graphene_std::transform::Footprint;
Expand Down Expand Up @@ -245,14 +244,10 @@ pub enum DocumentMessage {
vector_data: HashMap<NodeId, Arc<Vector>>,
},
// `Message` is only serialized at `editor_wrapper.rs`, and only inputs from JS pass through it.
// `UpdateFillAttributes` and `UpdateStrokeAttributes` are produced inside `editor.handle_message` by `node_graph_executor.rs` and consumed in the same dispatch loop, so it never reaches that serialization point.
// `UpdateAppearanceAttributes` is produced inside `editor.handle_message` by `node_graph_executor.rs` and consumed in the same dispatch loop, so it never reaches that serialization point.
#[serde(skip)]
UpdateFillAttributes {
fill_attributes: HashMap<NodeId, Arc<List<Graphic>>>,
},
#[serde(skip)]
UpdateStrokeAttributes {
stroke_attributes: HashMap<NodeId, Arc<List<Graphic>>>,
UpdateAppearanceAttributes {
appearance_attributes: HashMap<NodeId, Arc<Appearance>>,
},
Undo,
UngroupSelectedLayers,
Expand Down
43 changes: 15 additions & 28 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use graph_craft::application_io::wgpu_available;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
use graph_craft::list;
use graphene_std::graphic::is_paint_present;
use graphene_std::Cover;
use graphene_std::math::quad::Quad;
use graphene_std::path_bool_nodes::boolean_intersect;
use graphene_std::raster::BlendMode;
Expand Down Expand Up @@ -1502,9 +1502,9 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
.collect();
self.network_interface.update_vector_data(layer_vector_data);
}
DocumentMessage::UpdateFillAttributes { fill_attributes } => {
DocumentMessage::UpdateAppearanceAttributes { appearance_attributes } => {
// Convert NodeId keys to LayerNodeIdentifier keys, filtering to only layers
let layer_fill_attributes = fill_attributes
let layer_appearance_attributes = appearance_attributes
.into_iter()
.filter(|(node_id, _)| self.network_interface.document_network().nodes.contains_key(node_id))
.filter_map(|(node_id, attrs)| {
Expand All @@ -1514,21 +1514,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
})
})
.collect();
self.network_interface.update_fill_attributes(layer_fill_attributes);
}
DocumentMessage::UpdateStrokeAttributes { stroke_attributes } => {
// Convert NodeId keys to LayerNodeIdentifier keys, filtering to only layers
let layer_stroke_attributes = stroke_attributes
.into_iter()
.filter(|(node_id, _)| self.network_interface.document_network().nodes.contains_key(node_id))
.filter_map(|(node_id, attrs)| {
self.network_interface.is_layer(&node_id, &[]).then(|| {
let layer = LayerNodeIdentifier::new(node_id, &self.network_interface);
(layer, attrs)
})
})
.collect();
self.network_interface.update_stroke_attributes(layer_stroke_attributes);
self.network_interface.update_appearance_attributes(layer_appearance_attributes);
}
DocumentMessage::Undo => {
if self.network_interface.transaction_status() != TransactionStatus::Finished {
Expand Down Expand Up @@ -2763,20 +2749,21 @@ impl DocumentMessageHandler {
let mut resulting_layers: Vec<NodeId> = Vec::new();

for layer in selected_layers {
let Some(vector_data) = self.network_interface.document_metadata().layer_vector_data.get(&layer) else {
if !self.network_interface.document_metadata().layer_vector_data.contains_key(&layer) {
resulting_layers.push(layer.to_node());
continue;
};
let stroke = vector_data.stroke.as_ref();
}

let fill_graphic_list = self.network_interface.document_metadata().layer_fill_attributes.get(&layer);
let stroke_graphic_list = self.network_interface.document_metadata().layer_stroke_attributes.get(&layer);
let appearance = self.network_interface.document_metadata().layer_appearance_attributes.get(&layer);

let has_fill = fill_graphic_list.is_some_and(|list| is_paint_present(list));
// `Vector.stroke` captures stroke geometry, even with weight 0 or transparent paint.
// So stroke visibility must be checked from `ATTR_STROKE`, the paint source of truth.
let stroke_visible = stroke_graphic_list.is_some_and(|list| list.element(0).is_some_and(|g| !g.is_fully_transparent()));
let has_stroke = stroke.as_ref().is_some_and(|s| s.has_renderable_stroke()) && stroke_visible;
let has_fill = appearance.is_some_and(|appearance| appearance.has_painted_cover(Cover::Fill));
// A visible stroke needs both renderable geometry (non-zero weight) and paint that draws something
let has_stroke = appearance.is_some_and(|appearance| {
appearance.first_coverage_of(Cover::Stroke).is_some_and(|coverage| coverage.stroke_params().has_renderable_stroke())
&& appearance
.first_paint_of(Cover::Stroke)
.is_some_and(|paint| paint.element(0).is_some_and(|graphic| !graphic.is_fully_transparent()))
});

// No stroke means there's nothing to solidify. Fill-only layers are already in the desired form, so skip.
if !has_stroke {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Image;
use graphene_std::subpath::Subpath;
use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::style::{GradientForm, GradientHueDirection, GradientInterpolation, GradientSettings, GradientSpace, GradientSpread, Stroke};
use graphene_std::vector::style::{GradientForm, GradientHueDirection, GradientInterpolation, GradientSettings, GradientSpace, GradientSpread, PaintOrder, Stroke};
use graphene_std::vector::{Gradient, PointId, VectorModificationType};

#[impl_message(Message, DocumentMessage, GraphOperation)]
Expand Down Expand Up @@ -93,6 +93,10 @@ pub enum GraphOperationMessage {
color: Option<Color>,
stroke: Stroke,
},
StrokeOrderSet {
layer: LayerNodeIdentifier,
paint_order: PaintOrder,
},
TransformChange {
layer: LayerNodeIdentifier,
transform: DAffine2,
Expand Down
Loading
Loading