Migrate vector data and tools to use nodes (#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>
This commit is contained in:
0HyperCube
2023-03-26 08:03:51 +01:00
committed by Keavon Chambers
co-authored by Keavon Chambers
parent 639a24d8ad
commit 959e790cdf
64 changed files with 2639 additions and 1552 deletions
+11 -9
View File
@@ -8,6 +8,7 @@ use crate::intersection::Quad;
use crate::DocumentError;
use crate::LayerId;
use graphene_core::vector::VectorData;
use graphene_std::vector::subpath::Subpath;
use core::fmt;
@@ -437,16 +438,9 @@ impl Layer {
}
}
pub fn as_subpath(&self) -> Option<&Subpath> {
pub fn as_vector_data(&self) -> Option<&VectorData> {
match &self.data {
LayerDataType::Shape(s) => Some(&s.shape),
_ => None,
}
}
pub fn as_subpath_copy(&self) -> Option<Subpath> {
match &self.data {
LayerDataType::Shape(s) => Some(s.shape.clone()),
LayerDataType::NodeGraphFrame(NodeGraphFrameLayer { vector_data: Some(vector_data), .. }) => Some(vector_data),
_ => None,
}
}
@@ -503,10 +497,18 @@ impl Layer {
}
}
pub fn as_graph_frame(&self) -> Result<&NodeGraphFrameLayer, DocumentError> {
match &self.data {
LayerDataType::NodeGraphFrame(frame) => Ok(frame),
_ => Err(DocumentError::NotNodeGraph),
}
}
pub fn style(&self) -> Result<&PathStyle, DocumentError> {
match &self.data {
LayerDataType::Shape(s) => Ok(&s.style),
LayerDataType::Text(t) => Ok(&t.path_style),
LayerDataType::NodeGraphFrame(t) => t.vector_data.as_ref().map(|vector| &vector.style).ok_or(DocumentError::NotShape),
_ => Err(DocumentError::NotShape),
}
}
+30 -4
View File
@@ -1,10 +1,11 @@
use super::base64_serde;
use super::layer_info::LayerData;
use super::style::{RenderData, ViewMode};
use crate::intersection::{intersect_quad_bez_path, Quad};
use crate::intersection::{intersect_quad_bez_path, intersect_quad_subpath, Quad};
use crate::LayerId;
use glam::{DAffine2, DMat2, DVec2};
use graphene_core::vector::VectorData;
use kurbo::{Affine, BezPath, Shape as KurboShape};
use serde::{Deserialize, Serialize};
use std::fmt::Write;
@@ -23,6 +24,7 @@ pub struct NodeGraphFrameLayer {
#[serde(skip)]
pub dimensions: DVec2,
pub image_data: Option<ImageData>,
pub vector_data: Option<VectorData>,
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, specta::Type)]
@@ -33,7 +35,7 @@ pub struct ImageData {
}
impl LayerData for NodeGraphFrameLayer {
fn render(&mut self, svg: &mut String, _svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: &RenderData) -> bool {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<DAffine2>, render_data: &RenderData) -> bool {
let transform = self.transform(transforms, render_data.view_mode);
let inverse = transform.inverse();
@@ -56,7 +58,21 @@ impl LayerData for NodeGraphFrameLayer {
.enumerate()
.fold(String::new(), |val, (i, entry)| val + &(entry.to_string() + if i == 5 { "" } else { "," }));
if let Some(blob_url) = &self.blob_url {
// Render any paths if they exist
if let Some(vector_data) = &self.vector_data {
let layer_bounds = vector_data.bounding_box().unwrap_or_default();
let transfomed_bounds = vector_data.bounding_box_with_transform(transform).unwrap_or_default();
let _ = write!(svg, "<path d=\"");
for subpath in &vector_data.subpaths {
let _ = subpath.subpath_to_svg(svg, transform);
}
svg.push('"');
svg.push_str(&vector_data.style.render(render_data.view_mode, svg_defs, transform, layer_bounds, transfomed_bounds));
let _ = write!(svg, "/>");
} else if let Some(blob_url) = &self.blob_url {
// Render the image if it exists
let _ = write!(
svg,
r#"<image width="{}" height="{}" preserveAspectRatio="none" href="{}" transform="matrix({})" />"#,
@@ -66,6 +82,7 @@ impl LayerData for NodeGraphFrameLayer {
matrix
);
} else {
// Render a dotted blue outline if there is no image or vector data
let _ = write!(
svg,
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 {
}
fn bounding_box(&self, transform: glam::DAffine2, _render_data: &RenderData) -> Option<[DVec2; 2]> {
if let Some(vector_data) = &self.vector_data {
return vector_data.bounding_box_with_transform(transform);
}
let mut path = self.bounds();
if transform.matrix2 == DMat2::ZERO {
@@ -93,7 +114,12 @@ impl LayerData for NodeGraphFrameLayer {
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _render_data: &RenderData) {
if intersect_quad_bez_path(quad, &self.bounds(), true) {
if let Some(vector_data) = &self.vector_data {
let filled_style = vector_data.style.fill().is_some();
if vector_data.subpaths.iter().any(|subpath| intersect_quad_subpath(quad, subpath, filled_style || subpath.closed())) {
intersections.push(path.clone());
}
} else if intersect_quad_bez_path(quad, &self.bounds(), true) {
intersections.push(path.clone());
}
}