Skip to content

Commit 959e790

Browse files
0HyperCubeKeavon
andcommitted
Migrate vector data and tools to use nodes (GraphiteEditor#1065)
* Add rendering to vector nodes * Add line, shape, rectange and freehand tool * Fix transforms, strokes and fills * Migrate spline tool * Remove blank lines * Fix test * Fix fill in properties * Select layers when filling * Properties panel transform around pivot * Fix select tool outlines * Select tool modifies node graph pivot * Add the pivot assist to the properties * Improve setting non existant fill UX * Cleanup hash function * Path and pen tools * Bug fixes * Disable boolean ops * Fix default handle smoothing on ellipses * Fix test and warnings --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent 639a24d commit 959e790

64 files changed

Lines changed: 2648 additions & 1561 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

document-legacy/src/document.rs

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ use crate::layers::style::RenderData;
88
use crate::layers::text_layer::{Font, TextLayer};
99
use crate::{DocumentError, DocumentResponse, Operation};
1010

11-
use graphene_std::vector::subpath::Subpath;
12-
1311
use glam::{DAffine2, DVec2};
1412
use serde::{Deserialize, Serialize};
1513
use std::cell::RefCell;
@@ -177,43 +175,6 @@ impl Document {
177175
Ok(shapes)
178176
}
179177

180-
/// Return a copy of all [Subpath]s currently in the document.
181-
pub fn all_subpaths(&self) -> Vec<Subpath> {
182-
self.root.iter().flat_map(|layer| layer.as_subpath_copy()).collect::<Vec<Subpath>>()
183-
}
184-
185-
/// Returns references to all [Subpath]s currently in the document.
186-
pub fn all_subpaths_ref(&self) -> Vec<&Subpath> {
187-
self.root.iter().flat_map(|layer| layer.as_subpath()).collect::<Vec<&Subpath>>()
188-
}
189-
190-
/// Returns a reference to the requested [Subpath] by providing a path to its owner layer.
191-
pub fn subpath_ref<'a>(&'a self, path: &[LayerId]) -> Option<&'a Subpath> {
192-
self.layer(path).ok()?.as_subpath()
193-
}
194-
195-
/// Returns a mutable reference of the requested [Subpath] by providing a path to its owner layer.
196-
pub fn subpath_mut<'a>(&'a mut self, path: &'a [LayerId]) -> Option<&'a mut Subpath> {
197-
self.layer_mut(path).ok()?.as_subpath_mut()
198-
}
199-
200-
/// Set a [Subpath] at the specified path.
201-
pub fn set_subpath(&mut self, path: &[LayerId], shape: Subpath) {
202-
let layer = self.layer_mut(path);
203-
if let Ok(layer) = layer {
204-
if let LayerDataType::Shape(shape_layer) = &mut layer.data {
205-
shape_layer.shape = shape;
206-
// Is this needed?
207-
layer.cache_dirty = true;
208-
}
209-
}
210-
}
211-
212-
/// Set [Subpath]s for multiple paths at once.
213-
pub fn set_subpaths<'a>(&'a mut self, paths: impl Iterator<Item = &'a [LayerId]>, shapes: Vec<Subpath>) {
214-
paths.zip(shapes).for_each(|(path, shape)| self.set_subpath(path, shape));
215-
}
216-
217178
pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> &'a [LayerId] {
218179
layers.reduce(|a, b| &a[..a.iter().zip(b.iter()).take_while(|&(a, b)| a == b).count()]).unwrap_or_default()
219180
}
@@ -865,6 +826,12 @@ impl Document {
865826
}
866827
Some(vec![DocumentChanged, LayerChanged { path }])
867828
}
829+
Operation::SetVectorData { path, vector_data } => {
830+
if let LayerDataType::NodeGraphFrame(graph) = &mut self.layer_mut(&path)?.data {
831+
graph.vector_data = Some(vector_data);
832+
}
833+
Some(Vec::new())
834+
}
868835
Operation::InsertManipulatorGroup {
869836
layer_path,
870837
manipulator_group,

document-legacy/src/intersection.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::boolean_ops::{split_path_seg, subdivide_path_seg};
22
use crate::consts::{F64LOOSE, F64PRECISE};
33

4+
use graphene_core::uuid::ManipulatorGroupId;
45
use graphene_std::vector::subpath::Subpath;
56

67
use glam::{DAffine2, DMat2, DVec2};
@@ -19,6 +20,13 @@ impl Quad {
1920
Self([bbox[0], bbox[0] + size * DVec2::X, bbox[1], bbox[0] + size * DVec2::Y])
2021
}
2122

23+
/// Get all the edges in the quad.
24+
pub fn lines_glam(&self) -> impl Iterator<Item = bezier_rs::Bezier> + '_ {
25+
[[self.0[0], self.0[1]], [self.0[1], self.0[2]], [self.0[2], self.0[3]], [self.0[3], self.0[0]]]
26+
.into_iter()
27+
.map(|[start, end]| bezier_rs::Bezier::from_linear_dvec2(start, end))
28+
}
29+
2230
/// Get all the edges in the quad.
2331
pub fn lines(&self) -> [Line; 4] {
2432
[
@@ -101,6 +109,35 @@ pub fn intersect_quad_bez_path(quad: Quad, shape: &BezPath, filled: bool) -> boo
101109
get_arbitrary_point_on_path(&shape).map(|shape_point| quad.path().contains(shape_point)).unwrap_or_default()
102110
}
103111

112+
pub fn intersect_quad_subpath(quad: Quad, subpath: &bezier_rs::Subpath<ManipulatorGroupId>, close_subpath: bool) -> bool {
113+
let mut subpath = subpath.clone();
114+
115+
// For close_subpath shapes act like shape was closed even if it isn't
116+
if close_subpath && !subpath.closed() {
117+
subpath.set_closed(true);
118+
}
119+
120+
// Check if outlines intersect
121+
if subpath
122+
.iter()
123+
.any(|path_segment| quad.lines_glam().any(|line| !path_segment.intersections(&line, None, None).is_empty()))
124+
{
125+
return true;
126+
}
127+
// Check if selection is entirely within the shape
128+
if close_subpath && subpath.contains_point(quad.center()) {
129+
return true;
130+
}
131+
132+
// Check if shape is entirely within selection
133+
subpath
134+
.manipulator_groups()
135+
.first()
136+
.map(|group| group.anchor)
137+
.map(|shape_point| quad.path().contains(to_point(shape_point)))
138+
.unwrap_or_default()
139+
}
140+
104141
/// Returns a point on `path`.
105142
/// This function will usually return the first point from the path's first segment, but callers should not rely on this behavior.
106143
pub fn get_arbitrary_point_on_path(path: &BezPath) -> Option<Point> {

document-legacy/src/layers/layer_info.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::intersection::Quad;
88
use crate::DocumentError;
99
use crate::LayerId;
1010

11+
use graphene_core::vector::VectorData;
1112
use graphene_std::vector::subpath::Subpath;
1213

1314
use core::fmt;
@@ -437,16 +438,9 @@ impl Layer {
437438
}
438439
}
439440

440-
pub fn as_subpath(&self) -> Option<&Subpath> {
441+
pub fn as_vector_data(&self) -> Option<&VectorData> {
441442
match &self.data {
442-
LayerDataType::Shape(s) => Some(&s.shape),
443-
_ => None,
444-
}
445-
}
446-
447-
pub fn as_subpath_copy(&self) -> Option<Subpath> {
448-
match &self.data {
449-
LayerDataType::Shape(s) => Some(s.shape.clone()),
443+
LayerDataType::NodeGraphFrame(NodeGraphFrameLayer { vector_data: Some(vector_data), .. }) => Some(vector_data),
450444
_ => None,
451445
}
452446
}
@@ -503,10 +497,18 @@ impl Layer {
503497
}
504498
}
505499

500+
pub fn as_graph_frame(&self) -> Result<&NodeGraphFrameLayer, DocumentError> {
501+
match &self.data {
502+
LayerDataType::NodeGraphFrame(frame) => Ok(frame),
503+
_ => Err(DocumentError::NotNodeGraph),
504+
}
505+
}
506+
506507
pub fn style(&self) -> Result<&PathStyle, DocumentError> {
507508
match &self.data {
508509
LayerDataType::Shape(s) => Ok(&s.style),
509510
LayerDataType::Text(t) => Ok(&t.path_style),
511+
LayerDataType::NodeGraphFrame(t) => t.vector_data.as_ref().map(|vector| &vector.style).ok_or(DocumentError::NotShape),
510512
_ => Err(DocumentError::NotShape),
511513
}
512514
}

document-legacy/src/layers/nodegraph_layer.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use super::base64_serde;
22
use super::layer_info::LayerData;
33
use super::style::{RenderData, ViewMode};
4-
use crate::intersection::{intersect_quad_bez_path, Quad};
4+
use crate::intersection::{intersect_quad_bez_path, intersect_quad_subpath, Quad};
55
use crate::LayerId;
66

77
use glam::{DAffine2, DMat2, DVec2};
8+
use graphene_core::vector::VectorData;
89
use kurbo::{Affine, BezPath, Shape as KurboShape};
910
use serde::{Deserialize, Serialize};
1011
use std::fmt::Write;
@@ -23,6 +24,7 @@ pub struct NodeGraphFrameLayer {
2324
#[serde(skip)]
2425
pub dimensions: DVec2,
2526
pub image_data: Option<ImageData>,
27+
pub vector_data: Option<VectorData>,
2628
}
2729

2830
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, specta::Type)]
@@ -33,7 +35,7 @@ pub struct ImageData {
3335
}
3436

3537
impl LayerData for NodeGraphFrameLayer {
36-
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: &RenderData) -> bool {
38+
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: &RenderData) -> bool {
3739
let transform = self.transform(transforms, render_data.view_mode);
3840
let inverse = transform.inverse();
3941

@@ -56,7 +58,21 @@ impl LayerData for NodeGraphFrameLayer {
5658
.enumerate()
5759
.fold(String::new(), |val, (i, entry)| val + &(entry.to_string() + if i == 5 { "" } else { "," }));
5860

59-
if let Some(blob_url) = &self.blob_url {
61+
// Render any paths if they exist
62+
if let Some(vector_data) = &self.vector_data {
63+
let layer_bounds = vector_data.bounding_box().unwrap_or_default();
64+
let transfomed_bounds = vector_data.bounding_box_with_transform(transform).unwrap_or_default();
65+
66+
let _ = write!(svg, "<path d=\"");
67+
for subpath in &vector_data.subpaths {
68+
let _ = subpath.subpath_to_svg(svg, transform);
69+
}
70+
svg.push('"');
71+
72+
svg.push_str(&vector_data.style.render(render_data.view_mode, svg_defs, transform, layer_bounds, transfomed_bounds));
73+
let _ = write!(svg, "/>");
74+
} else if let Some(blob_url) = &self.blob_url {
75+
// Render the image if it exists
6076
let _ = write!(
6177
svg,
6278
r#"<image width="{}" height="{}" preserveAspectRatio="none" href="{}" transform="matrix({})" />"#,
@@ -66,6 +82,7 @@ impl LayerData for NodeGraphFrameLayer {
6682
matrix
6783
);
6884
} else {
85+
// Render a dotted blue outline if there is no image or vector data
6986
let _ = write!(
7087
svg,
7188
r#"<rect width="{}" height="{}" fill="none" stroke="var(--color-data-vector)" stroke-width="3" stroke-dasharray="8" transform="matrix({})" />"#,
@@ -81,6 +98,10 @@ impl LayerData for NodeGraphFrameLayer {
8198
}
8299

83100
fn bounding_box(&self, transform: glam::DAffine2, _render_data: &RenderData) -> Option<[DVec2; 2]> {
101+
if let Some(vector_data) = &self.vector_data {
102+
return vector_data.bounding_box_with_transform(transform);
103+
}
104+
84105
let mut path = self.bounds();
85106

86107
if transform.matrix2 == DMat2::ZERO {
@@ -93,7 +114,12 @@ impl LayerData for NodeGraphFrameLayer {
93114
}
94115

95116
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _render_data: &RenderData) {
96-
if intersect_quad_bez_path(quad, &self.bounds(), true) {
117+
if let Some(vector_data) = &self.vector_data {
118+
let filled_style = vector_data.style.fill().is_some();
119+
if vector_data.subpaths.iter().any(|subpath| intersect_quad_subpath(quad, subpath, filled_style || subpath.closed())) {
120+
intersections.push(path.clone());
121+
}
122+
} else if intersect_quad_bez_path(quad, &self.bounds(), true) {
97123
intersections.push(path.clone());
98124
}
99125
}

document-legacy/src/operation.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ pub enum Operation {
187187
path: Vec<LayerId>,
188188
subpath: Subpath,
189189
},
190+
SetVectorData {
191+
path: Vec<LayerId>,
192+
vector_data: graphene_core::vector::VectorData,
193+
},
190194
InsertManipulatorGroup {
191195
layer_path: Vec<LayerId>,
192196
manipulator_group: ManipulatorGroup,

editor/src/messages/portfolio/document/document_message.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ pub enum DocumentMessage {
3636
#[remain::unsorted]
3737
#[child]
3838
NodeGraph(NodeGraphMessage),
39+
#[remain::unsorted]
40+
#[child]
41+
GraphOperation(GraphOperationMessage),
3942

4043
// Messages
4144
AbortTransaction,
@@ -62,9 +65,7 @@ pub enum DocumentMessage {
6265
layer_path: Vec<LayerId>,
6366
},
6467
DeleteSelectedLayers,
65-
DeleteSelectedManipulatorPoints,
6668
DeselectAllLayers,
67-
DeselectAllManipulatorPoints,
6869
DirtyRenderDocument,
6970
DirtyRenderDocumentInOutlineView,
7071
DocumentHistoryBackward,
@@ -93,11 +94,6 @@ pub enum DocumentMessage {
9394
insert_index: isize,
9495
reverse_index: bool,
9596
},
96-
MoveSelectedManipulatorPoints {
97-
layer_path: Vec<LayerId>,
98-
delta: (f64, f64),
99-
mirror_distance: bool,
100-
},
10197
NodeGraphFrameClear {
10298
layer_path: Vec<LayerId>,
10399
node_id: NodeId,
@@ -193,10 +189,6 @@ pub enum DocumentMessage {
193189
ToggleLayerVisibility {
194190
layer_path: Vec<LayerId>,
195191
},
196-
ToggleSelectedHandleMirroring {
197-
layer_path: Vec<LayerId>,
198-
toggle_angle: bool,
199-
},
200192
Undo,
201193
UndoFinished,
202194
UngroupLayers {

0 commit comments

Comments
 (0)