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
45 changes: 6 additions & 39 deletions document-legacy/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ use crate::layers::style::RenderData;
use crate::layers::text_layer::{Font, TextLayer};
use crate::{DocumentError, DocumentResponse, Operation};

use graphene_std::vector::subpath::Subpath;

use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
Expand Down Expand Up @@ -177,43 +175,6 @@ impl Document {
Ok(shapes)
}

/// Return a copy of all [Subpath]s currently in the document.
pub fn all_subpaths(&self) -> Vec<Subpath> {
self.root.iter().flat_map(|layer| layer.as_subpath_copy()).collect::<Vec<Subpath>>()
}

/// Returns references to all [Subpath]s currently in the document.
pub fn all_subpaths_ref(&self) -> Vec<&Subpath> {
self.root.iter().flat_map(|layer| layer.as_subpath()).collect::<Vec<&Subpath>>()
}

/// Returns a reference to the requested [Subpath] by providing a path to its owner layer.
pub fn subpath_ref<'a>(&'a self, path: &[LayerId]) -> Option<&'a Subpath> {
self.layer(path).ok()?.as_subpath()
}

/// Returns a mutable reference of the requested [Subpath] by providing a path to its owner layer.
pub fn subpath_mut<'a>(&'a mut self, path: &'a [LayerId]) -> Option<&'a mut Subpath> {
self.layer_mut(path).ok()?.as_subpath_mut()
}

/// Set a [Subpath] at the specified path.
pub fn set_subpath(&mut self, path: &[LayerId], shape: Subpath) {
let layer = self.layer_mut(path);
if let Ok(layer) = layer {
if let LayerDataType::Shape(shape_layer) = &mut layer.data {
shape_layer.shape = shape;
// Is this needed?
layer.cache_dirty = true;
}
}
}

/// Set [Subpath]s for multiple paths at once.
pub fn set_subpaths<'a>(&'a mut self, paths: impl Iterator<Item = &'a [LayerId]>, shapes: Vec<Subpath>) {
paths.zip(shapes).for_each(|(path, shape)| self.set_subpath(path, shape));
}

pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> &'a [LayerId] {
layers.reduce(|a, b| &a[..a.iter().zip(b.iter()).take_while(|&(a, b)| a == b).count()]).unwrap_or_default()
}
Expand Down Expand Up @@ -865,6 +826,12 @@ impl Document {
}
Some(vec![DocumentChanged, LayerChanged { path }])
}
Operation::SetVectorData { path, vector_data } => {
if let LayerDataType::NodeGraphFrame(graph) = &mut self.layer_mut(&path)?.data {
graph.vector_data = Some(vector_data);
}
Some(Vec::new())
}
Operation::InsertManipulatorGroup {
layer_path,
manipulator_group,
Expand Down
37 changes: 37 additions & 0 deletions document-legacy/src/intersection.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::boolean_ops::{split_path_seg, subdivide_path_seg};
use crate::consts::{F64LOOSE, F64PRECISE};

use graphene_core::uuid::ManipulatorGroupId;
use graphene_std::vector::subpath::Subpath;

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

/// Get all the edges in the quad.
pub fn lines_glam(&self) -> impl Iterator<Item = bezier_rs::Bezier> + '_ {
[[self.0[0], self.0[1]], [self.0[1], self.0[2]], [self.0[2], self.0[3]], [self.0[3], self.0[0]]]
.into_iter()
.map(|[start, end]| bezier_rs::Bezier::from_linear_dvec2(start, end))
}

/// Get all the edges in the quad.
pub fn lines(&self) -> [Line; 4] {
[
Expand Down Expand Up @@ -101,6 +109,35 @@ pub fn intersect_quad_bez_path(quad: Quad, shape: &BezPath, filled: bool) -> boo
get_arbitrary_point_on_path(&shape).map(|shape_point| quad.path().contains(shape_point)).unwrap_or_default()
}

pub fn intersect_quad_subpath(quad: Quad, subpath: &bezier_rs::Subpath<ManipulatorGroupId>, close_subpath: bool) -> bool {
let mut subpath = subpath.clone();

// For close_subpath shapes act like shape was closed even if it isn't
if close_subpath && !subpath.closed() {
subpath.set_closed(true);
}

// Check if outlines intersect
if subpath
.iter()
.any(|path_segment| quad.lines_glam().any(|line| !path_segment.intersections(&line, None, None).is_empty()))
{
return true;
}
// Check if selection is entirely within the shape
if close_subpath && subpath.contains_point(quad.center()) {
return true;
}

// Check if shape is entirely within selection
subpath
.manipulator_groups()
.first()
.map(|group| group.anchor)
.map(|shape_point| quad.path().contains(to_point(shape_point)))
.unwrap_or_default()
}

/// Returns a point on `path`.
/// This function will usually return the first point from the path's first segment, but callers should not rely on this behavior.
pub fn get_arbitrary_point_on_path(path: &BezPath) -> Option<Point> {
Expand Down
20 changes: 11 additions & 9 deletions document-legacy/src/layers/layer_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
}
}
Expand Down Expand Up @@ -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),
}
}
Expand Down
34 changes: 30 additions & 4 deletions document-legacy/src/layers/nodegraph_layer.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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)]
Expand All @@ -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();

Expand All @@ -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({})" />"#,
Expand All @@ -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({})" />"#,
Expand All @@ -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 {
Expand All @@ -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());
}
}
Expand Down
4 changes: 4 additions & 0 deletions document-legacy/src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ pub enum Operation {
path: Vec<LayerId>,
subpath: Subpath,
},
SetVectorData {
path: Vec<LayerId>,
vector_data: graphene_core::vector::VectorData,
},
InsertManipulatorGroup {
layer_path: Vec<LayerId>,
manipulator_group: ManipulatorGroup,
Expand Down
14 changes: 3 additions & 11 deletions editor/src/messages/portfolio/document/document_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ pub enum DocumentMessage {
#[remain::unsorted]
#[child]
NodeGraph(NodeGraphMessage),
#[remain::unsorted]
#[child]
GraphOperation(GraphOperationMessage),

// Messages
AbortTransaction,
Expand All @@ -62,9 +65,7 @@ pub enum DocumentMessage {
layer_path: Vec<LayerId>,
},
DeleteSelectedLayers,
DeleteSelectedManipulatorPoints,
DeselectAllLayers,
DeselectAllManipulatorPoints,
DirtyRenderDocument,
DirtyRenderDocumentInOutlineView,
DocumentHistoryBackward,
Expand Down Expand Up @@ -93,11 +94,6 @@ pub enum DocumentMessage {
insert_index: isize,
reverse_index: bool,
},
MoveSelectedManipulatorPoints {
layer_path: Vec<LayerId>,
delta: (f64, f64),
mirror_distance: bool,
},
NodeGraphFrameClear {
layer_path: Vec<LayerId>,
node_id: NodeId,
Expand Down Expand Up @@ -193,10 +189,6 @@ pub enum DocumentMessage {
ToggleLayerVisibility {
layer_path: Vec<LayerId>,
},
ToggleSelectedHandleMirroring {
layer_path: Vec<LayerId>,
toggle_angle: bool,
},
Undo,
UndoFinished,
UngroupLayers {
Expand Down
Loading