mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Use Arc<List<Vector>> for vector_data metadata
This exposes List's attributes to message handlers, enabling them to access the necessary attribute data such as ATTR_STROKE_PAINT_GRAPHIC as `Fill` and `Stroke` will not have paint information in the future.
This commit is contained in:
@@ -12,6 +12,7 @@ use crate::messages::prelude::*;
|
||||
use glam::{DAffine2, IVec2};
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_std::Color;
|
||||
use graphene_std::list::List;
|
||||
use graphene_std::raster::BlendMode;
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::transform::Footprint;
|
||||
@@ -226,8 +227,11 @@ pub enum DocumentMessage {
|
||||
UpdateClipTargets {
|
||||
clip_targets: HashSet<NodeId>,
|
||||
},
|
||||
// `Message` is only serialized at `editor_wrapper.rs`, and only inputs from JS pass through it.
|
||||
// `UpdateVectorData` 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)]
|
||||
UpdateVectorData {
|
||||
vector_data: HashMap<NodeId, Arc<Vector>>,
|
||||
vector_data: HashMap<NodeId, Arc<List<Vector>>>,
|
||||
},
|
||||
Undo,
|
||||
UngroupSelectedLayers,
|
||||
|
||||
@@ -32,6 +32,9 @@ use glam::{DAffine2, DVec2};
|
||||
use graph_craft::descriptor;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
|
||||
use graphene_std::Graphic;
|
||||
use graphene_std::graphic::{color_to_graphic_list, fill_to_graphic_list};
|
||||
use graphene_std::list::{ATTR_FILL_GRAPHIC, ATTR_STROKE_PAINT_GRAPHIC, List};
|
||||
use graphene_std::math::quad::Quad;
|
||||
use graphene_std::path_bool_nodes::boolean_intersect;
|
||||
use graphene_std::raster::BlendMode;
|
||||
@@ -40,8 +43,9 @@ use graphene_std::subpath::Subpath;
|
||||
use graphene_std::vector::PointId;
|
||||
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
||||
use graphene_std::vector::misc::dvec2_to_point;
|
||||
use graphene_std::vector::style::{Fill, RenderMode};
|
||||
use graphene_std::vector::style::{RenderMode, Stroke};
|
||||
use kurbo::{Affine, BezPath, Line, PathSeg};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
@@ -2384,17 +2388,32 @@ impl DocumentMessageHandler {
|
||||
let mut resulting_layers: Vec<NodeId> = Vec::new();
|
||||
|
||||
for layer in selected_layers {
|
||||
let style = self.network_interface.document_metadata().layer_vector_data.get(&layer).map(|arc| arc.style.clone());
|
||||
let Some(style) = style else {
|
||||
let vector_list = self.network_interface.document_metadata().layer_vector_data.get(&layer).cloned();
|
||||
let Some(vector_list) = vector_list else {
|
||||
resulting_layers.push(layer.to_node());
|
||||
continue;
|
||||
};
|
||||
let style = vector_list.element(0).map(|vector| &vector.style);
|
||||
|
||||
let has_fill = !matches!(style.fill, Fill::None);
|
||||
// `style.stroke` is `Some` whenever a `Stroke` node is in the chain, even with weight 0 or a transparent color.
|
||||
// So `is_some()` would treat invisibly-stroked fill-only layers as having a stroke.
|
||||
// FIXME: Consider if we need to check ATTR_STROKE_PAINT_GRAPHIC
|
||||
let has_stroke = style.stroke.as_ref().is_some_and(|s| s.has_renderable_stroke());
|
||||
let fill_graphic_list = vector_list
|
||||
.attribute::<List<Graphic>>(ATTR_FILL_GRAPHIC, 0)
|
||||
.filter(|list| !list.is_empty())
|
||||
.map(Cow::Borrowed)
|
||||
.or_else(|| style.and_then(|style| fill_to_graphic_list(style.fill())).map(Cow::Owned));
|
||||
let fill_graphic = fill_graphic_list.as_ref().and_then(|l| l.element(0));
|
||||
|
||||
let stroke_paint_graphic_list = vector_list
|
||||
.attribute::<List<Graphic>>(ATTR_STROKE_PAINT_GRAPHIC, 0)
|
||||
.filter(|list| !list.is_empty())
|
||||
.map(Cow::Borrowed)
|
||||
.or_else(|| color_to_graphic_list(style.and_then(|style| style.stroke().and_then(|s| s.color()))).map(Cow::Owned));
|
||||
let stroke_paint_graphic = stroke_paint_graphic_list.as_ref().and_then(|l| l.element(0));
|
||||
|
||||
let has_fill = fill_graphic.is_some();
|
||||
|
||||
let stroke_renderable = style.is_some_and(|s| s.stroke.as_ref().is_some_and(Stroke::has_renderable_stroke));
|
||||
let stroke_paint_visible = stroke_paint_graphic.is_some_and(|g| !g.is_fully_transparent());
|
||||
let has_stroke = stroke_renderable && stroke_paint_visible;
|
||||
|
||||
// No stroke means there's nothing to solidify. Fill-only layers are already in the desired form, so skip.
|
||||
if !has_stroke {
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Flow
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_std::list::List;
|
||||
use graphene_std::math::quad::Quad;
|
||||
use graphene_std::subpath;
|
||||
use graphene_std::transform::Footprint;
|
||||
@@ -38,7 +39,7 @@ pub struct DocumentMetadata {
|
||||
pub vector_modify: HashMap<NodeId, Vector>,
|
||||
/// Vector data keyed by layer ID, used as fallback when no Path node exists.
|
||||
/// This provides accurate SegmentIds for layers without explicit Path nodes.
|
||||
pub layer_vector_data: HashMap<LayerNodeIdentifier, Arc<Vector>>,
|
||||
pub layer_vector_data: HashMap<LayerNodeIdentifier, Arc<List<Vector>>>,
|
||||
/// Transform from document space to viewport space.
|
||||
pub document_to_viewport: DAffine2,
|
||||
}
|
||||
@@ -225,7 +226,7 @@ impl DocumentMetadata {
|
||||
/// stroke geometry when the layer is a vector with a stroke style. Falls back to the click-target-based
|
||||
/// bounds for non-vector layers (groups, raster, text, color, gradient).
|
||||
pub fn bounding_box_document_with_stroke(&self, layer: LayerNodeIdentifier) -> Option<[DVec2; 2]> {
|
||||
if let Some(vector) = self.layer_vector_data.get(&layer)
|
||||
if let Some(vector) = self.layer_vector_data.get(&layer).and_then(|vector_list| vector_list.element(0))
|
||||
&& let Some(bounds) = vector.stroke_inclusive_bounding_box_with_transform(self.transform_to_document(layer))
|
||||
{
|
||||
return Some(bounds);
|
||||
|
||||
@@ -23,6 +23,7 @@ use graph_craft::Type;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork};
|
||||
use graphene_std::ContextDependencies;
|
||||
use graphene_std::list::List;
|
||||
use graphene_std::math::quad::Quad;
|
||||
use graphene_std::subpath::Subpath;
|
||||
use graphene_std::transform::Footprint;
|
||||
@@ -3230,8 +3231,9 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
return Some(modified);
|
||||
}
|
||||
|
||||
self.document_metadata.layer_vector_data.get(&layer).map(|arc| arc.as_ref().clone())
|
||||
// Only item 0 is returned since editing tools can only target a single item currently.
|
||||
let vector_list = self.document_metadata.layer_vector_data.get(&layer).cloned();
|
||||
vector_list.and_then(|list| list.element(0).cloned())
|
||||
}
|
||||
|
||||
/// The vector geometry an upstream Path node would surface for editing.
|
||||
@@ -3393,7 +3395,7 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
|
||||
/// Update the layer vector data (for layers without Path nodes)
|
||||
pub fn update_vector_data(&mut self, new_layer_vector_data: HashMap<LayerNodeIdentifier, Arc<Vector>>) {
|
||||
pub fn update_vector_data(&mut self, new_layer_vector_data: HashMap<LayerNodeIdentifier, Arc<List<Vector>>>) {
|
||||
self.document_metadata.layer_vector_data = new_layer_vector_data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +391,9 @@ pub struct RenderMetadata {
|
||||
/// The Text tool composes this with `transform_to_viewport(layer)` to position its drag cage.
|
||||
pub text_frames: HashMap<NodeId, DAffine2>,
|
||||
pub clip_targets: HashSet<NodeId>,
|
||||
pub vector_data: HashMap<NodeId, Arc<Vector>>,
|
||||
// `RenderMetadata` only enters serialization via `TaggedValue::RenderOutput`, which also skips serde.
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
pub vector_data: HashMap<NodeId, Arc<List<Vector>>>,
|
||||
pub backgrounds: Vec<Background>,
|
||||
}
|
||||
|
||||
@@ -1525,6 +1527,11 @@ impl Render for List<Vector> {
|
||||
let mut accumulated_click_targets: HashMap<NodeId, Vec<Arc<ClickTarget>>> = HashMap::new();
|
||||
let mut accumulated_outlines: HashMap<NodeId, Vec<Arc<ClickTarget>>> = HashMap::new();
|
||||
|
||||
// Source geometry (not the click-target override) so editing tools work on letterforms.
|
||||
if let Some(element_id) = caller_element_id {
|
||||
metadata.vector_data.entry(element_id).or_insert_with(|| Arc::new(self.clone()));
|
||||
}
|
||||
|
||||
for index in 0..self.len() {
|
||||
let Some(source) = self.element(index) else { continue };
|
||||
let transform: DAffine2 = self.attribute_cloned_or_default(ATTR_TRANSFORM, index);
|
||||
@@ -1554,10 +1561,6 @@ impl Render for List<Vector> {
|
||||
extend_targets_from_vector(&mut outlines_unwrapped, source, item_relative_transform);
|
||||
accumulated_outlines.entry(element_id).or_default().extend(outlines_unwrapped.into_iter().map(Arc::new));
|
||||
|
||||
// Source geometry (not the click-target override) so editing tools work on letterforms.
|
||||
// Only item 0 is recorded since editing tools can only target a single item currently.
|
||||
metadata.vector_data.entry(element_id).or_insert_with(|| Arc::new(source.clone()));
|
||||
|
||||
// Surface `editor:text_frame` for the Text tool's drag cage
|
||||
if let Some(&frame) = self.attribute::<DAffine2>(ATTR_EDITOR_TEXT_FRAME, index) {
|
||||
metadata.text_frames.entry(element_id).or_insert(frame);
|
||||
|
||||
Reference in New Issue
Block a user