Refactor font_cache into render_data; delete image layer type

This commit is contained in:
Keavon Chambers 2023-01-29 20:31:14 -08:00
parent 7ab601c0c6
commit beab0f01c6
41 changed files with 395 additions and 554 deletions

View file

@ -1,12 +1,11 @@
use crate::boolean_ops::composite_boolean_operation;
use crate::intersection::Quad;
use crate::layers::folder_layer::FolderLayer;
use crate::layers::image_layer::ImageLayer;
use crate::layers::layer_info::{Layer, LayerData, LayerDataType, LayerDataTypeDiscriminant};
use crate::layers::nodegraph_layer::NodeGraphFrameLayer;
use crate::layers::shape_layer::ShapeLayer;
use crate::layers::style::RenderData;
use crate::layers::text_layer::{Font, FontCache, TextLayer};
use crate::layers::text_layer::{Font, TextLayer};
use crate::{DocumentError, DocumentResponse, Operation};
use graphene_std::vector::subpath::Subpath;
@ -49,7 +48,7 @@ impl Default for Document {
impl Document {
/// Wrapper around render, that returns the whole document as a Response.
pub fn render_root(&mut self, render_data: RenderData) -> String {
pub fn render_root(&mut self, render_data: &RenderData) -> String {
// Render and append to the defs section
let mut svg_defs = String::from("<defs>");
self.root.render(&mut vec![], &mut svg_defs, render_data);
@ -62,7 +61,7 @@ impl Document {
}
/// Renders everything below the given layer contained within its parent folder.
pub fn render_layers_below(&mut self, below_layer_path: &[LayerId], render_data: RenderData) -> Option<String> {
pub fn render_layers_below(&mut self, below_layer_path: &[LayerId], render_data: &RenderData) -> Option<String> {
// Split the path into the layer ID and its parent folder
let (layer_id_to_render_below, parent_folder_path) = below_layer_path.split_last()?;
@ -89,7 +88,7 @@ impl Document {
}
/// Renders a layer and its children
pub fn render_layer(&mut self, layer_path: &[LayerId], render_data: RenderData) -> Option<String> {
pub fn render_layer(&mut self, layer_path: &[LayerId], render_data: &RenderData) -> Option<String> {
// Note: it is bad practice to directly clone and modify the document structure, this is a temporary hack until this whole system is replaced by the node graph
let mut temp_clone = self.layer_mut(layer_path).ok()?.clone();
@ -109,14 +108,14 @@ impl Document {
}
/// Checks whether each layer under `path` intersects with the provided `quad` and adds all intersection layers as paths to `intersections`.
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
self.layer(path).unwrap().intersects_quad(quad, path, intersections, font_cache);
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
self.layer(path).unwrap().intersects_quad(quad, path, intersections, render_data);
}
/// Checks whether each layer under the root path intersects with the provided `quad` and returns the paths to all intersecting layers.
pub fn intersects_quad_root(&self, quad: Quad, font_cache: &FontCache) -> Vec<Vec<LayerId>> {
pub fn intersects_quad_root(&self, quad: Quad, render_data: &RenderData) -> Vec<Vec<LayerId>> {
let mut intersections = Vec::new();
self.intersects_quad(quad, &mut vec![], &mut intersections, font_cache);
self.intersects_quad(quad, &mut vec![], &mut intersections, render_data);
intersections
}
@ -407,32 +406,32 @@ impl Document {
Ok(())
}
pub fn viewport_bounding_box(&self, path: &[LayerId], font_cache: &FontCache) -> Result<Option<[DVec2; 2]>, DocumentError> {
pub fn viewport_bounding_box(&self, path: &[LayerId], render_data: &RenderData) -> Result<Option<[DVec2; 2]>, DocumentError> {
let layer = self.layer(path)?;
let transform = self.multiply_transforms(path)?;
Ok(layer.data.bounding_box(transform, font_cache))
Ok(layer.data.bounding_box(transform, render_data))
}
pub fn bounding_box_and_transform(&self, path: &[LayerId], font_cache: &FontCache) -> Result<Option<([DVec2; 2], DAffine2)>, DocumentError> {
pub fn bounding_box_and_transform(&self, path: &[LayerId], render_data: &RenderData) -> Result<Option<([DVec2; 2], DAffine2)>, DocumentError> {
let layer = self.layer(path)?;
let transform = self.multiply_transforms(&path[..path.len() - 1])?;
Ok(layer.data.bounding_box(layer.transform, font_cache).map(|bounds| (bounds, transform)))
Ok(layer.data.bounding_box(layer.transform, render_data).map(|bounds| (bounds, transform)))
}
/// Compute the center of transformation multiplied with `Document::multiply_transforms`.
pub fn pivot(&self, path: &[LayerId], font_cache: &FontCache) -> Option<DVec2> {
pub fn pivot(&self, path: &[LayerId], render_data: &RenderData) -> Option<DVec2> {
let layer = self.layer(path).ok()?;
Some(self.multiply_transforms(path).unwrap_or_default().transform_point2(layer.layerspace_pivot(font_cache)))
Some(self.multiply_transforms(path).unwrap_or_default().transform_point2(layer.layerspace_pivot(render_data)))
}
pub fn visible_layers_bounding_box(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
pub fn visible_layers_bounding_box(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
let mut paths = vec![];
self.visible_layers(&mut vec![], &mut paths).ok()?;
self.combined_viewport_bounding_box(paths.iter().map(|x| x.as_slice()), font_cache)
self.combined_viewport_bounding_box(paths.iter().map(|x| x.as_slice()), render_data)
}
pub fn combined_viewport_bounding_box<'a>(&self, paths: impl Iterator<Item = &'a [LayerId]>, font_cache: &FontCache) -> Option<[DVec2; 2]> {
let boxes = paths.filter_map(|path| self.viewport_bounding_box(path, font_cache).ok()?);
pub fn combined_viewport_bounding_box<'a>(&self, paths: impl Iterator<Item = &'a [LayerId]>, render_data: &RenderData) -> Option<[DVec2; 2]> {
let boxes = paths.filter_map(|path| self.viewport_bounding_box(path, render_data).ok()?);
boxes.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])])
}
@ -552,7 +551,7 @@ impl Document {
/// Mutate the document by applying the `operation` to it. If the operation necessitates a
/// reaction from the frontend, responses may be returned.
pub fn handle_operation(&mut self, operation: Operation, font_cache: &FontCache) -> Result<Option<Vec<DocumentResponse>>, DocumentError> {
pub fn handle_operation(&mut self, operation: Operation, render_data: &RenderData) -> Result<Option<Vec<DocumentResponse>>, DocumentError> {
use DocumentResponse::*;
operation.pseudo_hash().hash(&mut self.state_identifier);
@ -590,7 +589,7 @@ impl Document {
font_style,
} => {
let font = Font::new(font_name, font_style);
let layer_text = TextLayer::new(text, style, size, font, font_cache);
let layer_text = TextLayer::new(text, style, size, font, render_data);
let layer_data = LayerDataType::Text(layer_text);
let layer = Layer::new(layer_data, transform);
@ -598,20 +597,6 @@ impl Document {
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat())
}
Operation::AddImage {
path,
transform,
insert_index,
image_data,
mime,
} => {
let image_data = std::sync::Arc::new(image_data);
let layer = Layer::new(LayerDataType::Image(ImageLayer::new(mime, image_data)), transform);
self.set_layer(&path, layer, insert_index)?;
Some([vec![DocumentChanged, CreatedLayer { path: path.clone() }], update_thumbnails_upstream(&path)].concat())
}
Operation::AddNodeGraphFrame {
path,
insert_index,
@ -658,7 +643,7 @@ impl Document {
.layer_mut(id)
.ok_or_else(|| DocumentError::LayerNotFound(path.clone()))?
.as_text_mut()?
.update_text(new_text, font_cache);
.update_text(new_text, render_data);
self.mark_as_dirty(&path)?;
@ -808,7 +793,7 @@ impl Document {
text.font = Font::new(font_family, font_style);
text.size = size;
text.cached_path = Some(text.generate_path(text.load_face(font_cache)));
text.cached_path = Some(text.generate_path(text.load_face(render_data)));
self.mark_as_dirty(&path)?;
Some([vec![DocumentChanged, LayerChanged { path: path.clone() }], update_thumbnails_upstream(&path)].concat())
}
@ -836,18 +821,14 @@ impl Document {
Some([vec![DocumentChanged], update_thumbnails_upstream(&path)].concat())
}
Operation::SetLayerBlobUrl { layer_path, blob_url, resolution } => {
let layer = self.layer_mut(&layer_path).unwrap_or_else(|_| panic!("Blob url for invalid layer with path '{:?}'", layer_path));
match &mut layer.data {
LayerDataType::Image(image) => {
image.blob_url = Some(blob_url);
image.dimensions = resolution.into();
}
LayerDataType::NodeGraphFrame(node_graph_frame) => {
node_graph_frame.blob_url = Some(blob_url);
node_graph_frame.dimensions = resolution.into();
}
_ => panic!("Incorrectly trying to set the image blob URL for a layer that is not an Image, NodeGraphFrame or Imaginate layer type"),
}
let layer = self.layer_mut(&layer_path).unwrap_or_else(|_| panic!("Blob URL for invalid layer with path '{:?}'", layer_path));
let LayerDataType::NodeGraphFrame(node_graph_frame) = &mut layer.data else {
panic!("Incorrectly trying to set the image blob URL for a layer that is not a NodeGraphFrame layer type");
};
node_graph_frame.blob_url = Some(blob_url);
node_graph_frame.dimensions = resolution.into();
self.mark_as_dirty(&layer_path)?;
Some([vec![DocumentChanged, LayerChanged { path: layer_path.clone() }], update_thumbnails_upstream(&layer_path)].concat())
@ -1099,7 +1080,7 @@ impl Document {
// Delete the layer if there are no longer any manipulator groups
if (shape.manipulator_groups().len() - 1) == 0 {
// Delegate deletion to DeleteLayer to update Layer Tree in frontend
match self.handle_operation(Operation::DeleteLayer { path: layer_path.clone() }, font_cache) {
match self.handle_operation(Operation::DeleteLayer { path: layer_path.clone() }, render_data) {
Ok(Some(delete_responses)) => {
responses.extend(delete_responses);
responses.push(DocumentResponse::DeletedSelectedManipulatorPoints);
@ -1169,8 +1150,8 @@ fn update_thumbnails_upstream(path: &[LayerId]) -> Vec<DocumentResponse> {
responses
}
pub fn pick_layer_safe_imaginate_resolution(layer: &Layer, font_cache: &FontCache) -> (u64, u64) {
let layer_bounds = layer.bounding_transform(font_cache);
pub fn pick_layer_safe_imaginate_resolution(layer: &Layer, render_data: &RenderData) -> (u64, u64) {
let layer_bounds = layer.bounding_transform(render_data);
let layer_bounds_size = (layer_bounds.transform_vector2((1., 0.).into()).length(), layer_bounds.transform_vector2((0., 1.).into()).length());
pick_safe_imaginate_resolution(layer_bounds_size)

View file

@ -1,7 +1,6 @@
use super::layer_info::{Layer, LayerData, LayerDataType};
use super::style::RenderData;
use crate::intersection::Quad;
use crate::layers::text_layer::FontCache;
use crate::{DocumentError, LayerId};
use glam::DVec2;
@ -21,7 +20,7 @@ pub struct FolderLayer {
}
impl LayerData for FolderLayer {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: &RenderData) -> bool {
let mut any_child_requires_redraw = false;
for layer in &mut self.layers {
let (svg_value, requires_redraw) = layer.render(transforms, svg_defs, render_data);
@ -31,18 +30,18 @@ impl LayerData for FolderLayer {
any_child_requires_redraw
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
for (layer, layer_id) in self.layers().iter().zip(&self.layer_ids) {
path.push(*layer_id);
layer.intersects_quad(quad, path, intersections, font_cache);
layer.intersects_quad(quad, path, intersections, render_data);
path.pop();
}
}
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
fn bounding_box(&self, transform: glam::DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.layers
.iter()
.filter_map(|layer| layer.data.bounding_box(transform * layer.transform, font_cache))
.filter_map(|layer| layer.data.bounding_box(transform * layer.transform, render_data))
.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])])
}
}

View file

@ -1,116 +0,0 @@
use super::base64_serde;
use super::layer_info::LayerData;
use super::style::{RenderData, ViewMode};
use crate::intersection::{intersect_quad_bez_path, Quad};
use crate::layers::text_layer::FontCache;
use crate::LayerId;
use glam::{DAffine2, DMat2, DVec2};
use kurbo::{Affine, BezPath, Shape as KurboShape};
use serde::{Deserialize, Serialize};
use std::fmt::Write;
#[derive(Clone, PartialEq, Deserialize, Serialize, specta::Type)]
pub struct ImageLayer {
pub mime: String,
#[serde(serialize_with = "base64_serde::as_base64", deserialize_with = "base64_serde::from_base64")]
#[specta(type = String)]
pub image_data: std::sync::Arc<Vec<u8>>,
// TODO: Have the browser dispose of this blob URL when this is dropped (like when the layer is deleted)
#[serde(skip)]
pub blob_url: Option<String>,
#[serde(skip)]
pub dimensions: DVec2,
}
impl LayerData for ImageLayer {
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();
if !inverse.is_finite() {
let _ = write!(svg, "<!-- SVG shape has an invalid transform -->");
return false;
}
let _ = writeln!(svg, r#"<g transform="matrix("#);
inverse.to_cols_array().iter().enumerate().for_each(|(i, entry)| {
let _ = svg.write_str(&(entry.to_string() + if i == 5 { "" } else { "," }));
});
let _ = svg.write_str(r#")">"#);
let svg_transform = transform
.to_cols_array()
.iter()
.enumerate()
.map(|(i, entry)| entry.to_string() + if i == 5 { "" } else { "," })
.collect::<String>();
let _ = write!(
svg,
r#"<image width="{}" height="{}" transform="matrix({})" href="{}"/>"#,
self.dimensions.x,
self.dimensions.y,
svg_transform,
self.blob_url.as_ref().unwrap_or(&String::new())
);
let _ = svg.write_str("</g>");
false
}
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
let mut path = self.bounds();
if transform.matrix2 == DMat2::ZERO {
return None;
}
path.apply_affine(glam_to_kurbo(transform));
let kurbo::Rect { x0, y0, x1, y1 } = path.bounding_box();
Some([(x0, y0).into(), (x1, y1).into()])
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _font_cache: &FontCache) {
if intersect_quad_bez_path(quad, &self.bounds(), true) {
intersections.push(path.clone());
}
}
}
impl ImageLayer {
pub fn new(mime: String, image_data: std::sync::Arc<Vec<u8>>) -> Self {
Self {
mime,
image_data,
blob_url: None,
dimensions: DVec2::ONE,
}
}
pub fn transform(&self, transforms: &[DAffine2], mode: ViewMode) -> DAffine2 {
let start = match mode {
ViewMode::Outline => 0,
_ => (transforms.len() as i32 - 1).max(0) as usize,
};
transforms.iter().skip(start).cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY)
}
fn bounds(&self) -> BezPath {
kurbo::Rect::from_origin_size(kurbo::Point::ZERO, kurbo::Size::new(self.dimensions.x, self.dimensions.y)).to_path(0.)
}
}
impl std::fmt::Debug for ImageLayer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ImageLayer")
.field("mime", &self.mime)
.field("image_data", &"...")
.field("blob_url", &self.blob_url)
.field("dimensions", &self.dimensions)
.finish()
}
}
fn glam_to_kurbo(transform: DAffine2) -> Affine {
Affine::new(transform.to_cols_array())
}

View file

@ -1,12 +1,10 @@
use super::blend_mode::BlendMode;
use super::folder_layer::FolderLayer;
use super::image_layer::ImageLayer;
use super::nodegraph_layer::NodeGraphFrameLayer;
use super::shape_layer::ShapeLayer;
use super::style::{PathStyle, RenderData};
use super::text_layer::TextLayer;
use crate::intersection::Quad;
use crate::layers::text_layer::FontCache;
use crate::DocumentError;
use crate::LayerId;
@ -26,8 +24,6 @@ pub enum LayerDataType {
Shape(ShapeLayer),
/// A layer that wraps a [TextLayer] struct.
Text(TextLayer),
/// A layer that wraps an [ImageLayer] struct.
Image(ImageLayer),
/// A layer that wraps an [NodeGraphFrameLayer] struct.
NodeGraphFrame(NodeGraphFrameLayer),
}
@ -38,7 +34,6 @@ impl LayerDataType {
LayerDataType::Shape(s) => s,
LayerDataType::Folder(f) => f,
LayerDataType::Text(t) => t,
LayerDataType::Image(i) => i,
LayerDataType::NodeGraphFrame(n) => n,
}
}
@ -48,7 +43,6 @@ impl LayerDataType {
LayerDataType::Shape(s) => s,
LayerDataType::Folder(f) => f,
LayerDataType::Text(t) => t,
LayerDataType::Image(i) => i,
LayerDataType::NodeGraphFrame(n) => n,
}
}
@ -83,7 +77,6 @@ impl From<&LayerDataType> for LayerDataTypeDiscriminant {
Folder(_) => LayerDataTypeDiscriminant::Folder,
Shape(_) => LayerDataTypeDiscriminant::Shape,
Text(_) => LayerDataTypeDiscriminant::Text,
Image(_) => LayerDataTypeDiscriminant::Image,
NodeGraphFrame(_) => LayerDataTypeDiscriminant::NodeGraphFrame,
}
}
@ -133,8 +126,8 @@ pub trait LayerData {
///
/// // Render the shape without any transforms, in normal view mode
/// # let font_cache = Default::default();
/// let render_data = RenderData::new(ViewMode::Normal, &font_cache, None);
/// shape.render(&mut svg, &mut String::new(), &mut vec![], render_data);
/// let render_data = RenderData::new(&font_cache, ViewMode::Normal, None);
/// shape.render(&mut svg, &mut String::new(), &mut vec![], &render_data);
///
/// assert_eq!(
/// svg,
@ -143,13 +136,13 @@ pub trait LayerData {
/// </g>"
/// );
/// ```
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool;
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: &RenderData) -> bool;
/// Determine the layers within this layer that intersect a given quad.
/// # Example
/// ```
/// # use graphite_document_legacy::layers::shape_layer::ShapeLayer;
/// # use graphite_document_legacy::layers::style::{Fill, PathStyle, ViewMode};
/// # use graphite_document_legacy::layers::style::{Fill, PathStyle, ViewMode, RenderData};
/// # use graphite_document_legacy::layers::layer_info::LayerData;
/// # use graphite_document_legacy::intersection::Quad;
/// # use glam::f64::{DAffine2, DVec2};
@ -162,18 +155,20 @@ pub trait LayerData {
/// let quad = Quad::from_box([DVec2::ZERO, DVec2::ONE]);
/// let mut intersections = vec![];
///
/// shape.intersects_quad(quad, &mut vec![shape_id], &mut intersections, &Default::default());
/// let font_cache = Default::default();
/// let render_data = RenderData::new(&font_cache, Default::default(), None);
/// shape.intersects_quad(quad, &mut vec![shape_id], &mut intersections, &render_data);
///
/// assert_eq!(intersections, vec![vec![shape_id]]);
/// ```
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache);
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData);
// TODO: this doctest fails because 0 != 1e-32, maybe assert difference < epsilon?
/// Calculate the bounding box for the layer's contents after applying a given transform.
/// # Example
/// ```no_run
/// # use graphite_document_legacy::layers::shape_layer::ShapeLayer;
/// # use graphite_document_legacy::layers::style::{Fill, PathStyle};
/// # use graphite_document_legacy::layers::style::{Fill, PathStyle, RenderData};
/// # use graphite_document_legacy::layers::layer_info::LayerData;
/// # use glam::f64::{DAffine2, DVec2};
/// # use std::collections::HashMap;
@ -182,24 +177,26 @@ pub trait LayerData {
/// // Calculate the bounding box without applying any transformations.
/// // (The identity transform maps every vector to itself.)
/// let transform = DAffine2::IDENTITY;
/// let bounding_box = shape.bounding_box(transform, &Default::default());
/// let font_cache = Default::default();
/// let render_data = RenderData::new(&font_cache, Default::default(), None);
/// let bounding_box = shape.bounding_box(transform, &render_data);
///
/// assert_eq!(bounding_box, Some([DVec2::ZERO, DVec2::ONE]));
/// ```
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]>;
fn bounding_box(&self, transform: glam::DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]>;
}
impl LayerData for LayerDataType {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: RenderData) -> bool {
fn render(&mut self, svg: &mut String, svg_defs: &mut String, transforms: &mut Vec<glam::DAffine2>, render_data: &RenderData) -> bool {
self.inner_mut().render(svg, svg_defs, transforms, render_data)
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
self.inner().intersects_quad(quad, path, intersections, font_cache)
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
self.inner().intersects_quad(quad, path, intersections, render_data)
}
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
self.inner().bounding_box(transform, font_cache)
fn bounding_box(&self, transform: glam::DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.inner().bounding_box(transform, render_data)
}
}
@ -305,7 +302,7 @@ impl Layer {
}
/// Renders the layer, returning the result and if a redraw is required
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, svg_defs: &mut String, render_data: RenderData) -> (&str, bool) {
pub fn render(&mut self, transforms: &mut Vec<DAffine2>, svg_defs: &mut String, render_data: &RenderData) -> (&str, bool) {
if !self.visible {
return ("", false);
}
@ -314,10 +311,7 @@ impl Layer {
// Skip rendering if outside the viewport bounds
if let Some(viewport_bounds) = render_data.culling_bounds {
if let Some(bounding_box) = self
.data
.bounding_box(transforms.iter().cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY), render_data.font_cache)
{
if let Some(bounding_box) = self.data.bounding_box(transforms.iter().cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY), render_data) {
let is_overlapping =
viewport_bounds[0].x < bounding_box[1].x && bounding_box[0].x < viewport_bounds[1].x && viewport_bounds[0].y < bounding_box[1].y && bounding_box[0].y < viewport_bounds[1].y;
if !is_overlapping {
@ -363,13 +357,13 @@ impl Layer {
(self.cache.as_str(), requires_redraw)
}
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
pub fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
if !self.visible {
return;
}
let transformed_quad = self.transform.inverse() * quad;
self.data.intersects_quad(transformed_quad, path, intersections, font_cache)
self.data.intersects_quad(transformed_quad, path, intersections, render_data)
}
/// Compute the bounding box of the layer after applying a transform to it.
@ -378,7 +372,7 @@ impl Layer {
/// ```
/// # use graphite_document_legacy::layers::shape_layer::ShapeLayer;
/// # use graphite_document_legacy::layers::layer_info::Layer;
/// # use graphite_document_legacy::layers::style::PathStyle;
/// # use graphite_document_legacy::layers::style::{PathStyle, RenderData};
/// # use glam::DVec2;
/// # use glam::f64::DAffine2;
/// # use std::collections::HashMap;
@ -386,27 +380,30 @@ impl Layer {
/// let layer: Layer = ShapeLayer::rectangle(PathStyle::default()).into();
///
/// // Apply the Identity transform, which leaves the points unchanged
/// let transform = DAffine2::IDENTITY;
/// let font_cache = Default::default();
/// let render_data = RenderData::new(&font_cache, Default::default(), None);
/// assert_eq!(
/// layer.aabb_for_transform(DAffine2::IDENTITY, &Default::default()),
/// layer.aabb_for_transform(transform, &render_data),
/// Some([DVec2::ZERO, DVec2::ONE]),
/// );
///
/// // Apply a transform that scales every point by a factor of two
/// let transform = DAffine2::from_scale(DVec2::ONE * 2.);
/// assert_eq!(
/// layer.aabb_for_transform(transform, &Default::default()),
/// layer.aabb_for_transform(transform, &render_data),
/// Some([DVec2::ZERO, DVec2::ONE * 2.]),
/// );
pub fn aabb_for_transform(&self, transform: DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
self.data.bounding_box(transform, font_cache)
pub fn aabb_for_transform(&self, transform: DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.data.bounding_box(transform, render_data)
}
pub fn aabb(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
self.aabb_for_transform(self.transform, font_cache)
pub fn aabb(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.aabb_for_transform(self.transform, render_data)
}
pub fn bounding_transform(&self, font_cache: &FontCache) -> DAffine2 {
let scale = match self.aabb_for_transform(DAffine2::IDENTITY, font_cache) {
pub fn bounding_transform(&self, render_data: &RenderData) -> DAffine2 {
let scale = match self.aabb_for_transform(DAffine2::IDENTITY, render_data) {
Some([a, b]) => {
let dimensions = b - a;
DAffine2::from_scale(dimensions)
@ -417,8 +414,8 @@ impl Layer {
self.transform * scale
}
pub fn layerspace_pivot(&self, font_cache: &FontCache) -> DVec2 {
let [mut min, max] = self.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]);
pub fn layerspace_pivot(&self, render_data: &RenderData) -> DVec2 {
let [mut min, max] = self.aabb_for_transform(DAffine2::IDENTITY, render_data).unwrap_or([DVec2::ZERO, DVec2::ONE]);
// If the layer bounds are 0 in either axis then set them to one (to avoid div 0)
if (max.x - min.x) < f64::EPSILON * 1000. {
@ -560,12 +557,6 @@ impl From<TextLayer> for Layer {
}
}
impl From<ImageLayer> for Layer {
fn from(from: ImageLayer) -> Layer {
Layer::new(LayerDataType::Image(from), DAffine2::IDENTITY.to_cols_array())
}
}
impl<'a> IntoIterator for &'a Layer {
type Item = &'a Layer;
type IntoIter = LayerIter<'a>;

View file

@ -5,8 +5,7 @@
//! * [Folder layers](folder_layer::FolderLayer), which encapsulate sub-layers
//! * [Shape layers](shape_layer::ShapeLayer), which contain generic SVG [`<path>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path)s
//! * [Text layers](text_layer::TextLayer), which contain a description of laid out text
//! * [Image layers](image_layer::ImageLayer), which contain a bitmap image
//! * [Nodegraph layers](nodegraph_layer::NodegraphLayer), which contain a node graph frame
//! * [Node Graph layers](nodegraph_layer::NodegraphLayer), which contain a node graph frame
//!
//! Refer to the module-level documentation for detailed information on each layer.
//!
@ -20,8 +19,6 @@ pub mod base64_serde;
pub mod blend_mode;
/// Contains the [FolderLayer](folder_layer::FolderLayer) type that encapsulates other layers, including more folders.
pub mod folder_layer;
/// Contains the [ImageLayer](image_layer::ImageLayer) type that contains a bitmap image.
pub mod image_layer;
/// Contains the base [Layer](layer_info::Layer) type, an abstraction over the different types of layers.
pub mod layer_info;
/// Contains the [NodegraphLayer](nodegraph_layer::NodegraphLayer) type that contains a node graph.

View file

@ -2,7 +2,6 @@ use super::base64_serde;
use super::layer_info::LayerData;
use super::style::{RenderData, ViewMode};
use crate::intersection::{intersect_quad_bez_path, Quad};
use crate::layers::text_layer::FontCache;
use crate::LayerId;
use glam::{DAffine2, DMat2, DVec2};
@ -34,7 +33,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();
@ -81,7 +80,7 @@ impl LayerData for NodeGraphFrameLayer {
false
}
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
fn bounding_box(&self, transform: glam::DAffine2, _render_data: &RenderData) -> Option<[DVec2; 2]> {
let mut path = self.bounds();
if transform.matrix2 == DMat2::ZERO {
@ -93,7 +92,7 @@ impl LayerData for NodeGraphFrameLayer {
Some([(x0, y0).into(), (x1, y1).into()])
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _font_cache: &FontCache) {
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) {
intersections.push(path.clone());
}

View file

@ -1,7 +1,6 @@
use super::layer_info::LayerData;
use super::style::{self, PathStyle, RenderData, ViewMode};
use crate::intersection::{intersect_quad_bez_path, Quad};
use crate::layers::text_layer::FontCache;
use crate::LayerId;
use graphene_std::vector::subpath::Subpath;
@ -27,7 +26,7 @@ pub struct ShapeLayer {
}
impl LayerData for ShapeLayer {
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 mut subpath = self.shape.clone();
let layer_bounds = subpath.bounding_box().unwrap_or_default();
@ -58,7 +57,7 @@ impl LayerData for ShapeLayer {
false
}
fn bounding_box(&self, transform: glam::DAffine2, _font_cache: &FontCache) -> Option<[DVec2; 2]> {
fn bounding_box(&self, transform: glam::DAffine2, _render_data: &RenderData) -> Option<[DVec2; 2]> {
let mut subpath = self.shape.clone();
if transform.matrix2 == DMat2::ZERO {
return None;
@ -68,7 +67,7 @@ impl LayerData for ShapeLayer {
subpath.bounding_box()
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _font_cache: &FontCache) {
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _render_data: &RenderData) {
let filled = self.style.fill().is_some() || self.shape.manipulator_groups().last().filter(|manipulator_group| manipulator_group.is_close()).is_some();
if intersect_quad_bez_path(quad, &(&self.shape).into(), filled) {
intersections.push(path.clone());

View file

@ -35,16 +35,16 @@ pub enum ViewMode {
/// Contains metadata for rendering the document as an svg
#[derive(Debug, Clone, Copy)]
pub struct RenderData<'a> {
pub view_mode: ViewMode,
pub font_cache: &'a FontCache,
pub view_mode: ViewMode,
pub culling_bounds: Option<[DVec2; 2]>,
}
impl<'a> RenderData<'a> {
pub fn new(view_mode: ViewMode, font_cache: &'a FontCache, culling_bounds: Option<[DVec2; 2]>) -> Self {
pub fn new(font_cache: &'a FontCache, view_mode: ViewMode, culling_bounds: Option<[DVec2; 2]>) -> Self {
Self {
view_mode,
font_cache,
view_mode,
culling_bounds,
}
}

View file

@ -34,7 +34,7 @@ pub struct TextLayer {
}
impl LayerData for TextLayer {
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();
@ -67,7 +67,7 @@ impl LayerData for TextLayer {
font.map(|_| r#" style="font-family: local-font;""#).unwrap_or_default()
);
} else {
let buzz_face = self.load_face(render_data.font_cache);
let buzz_face = self.load_face(render_data);
let mut path = self.to_subpath(buzz_face);
@ -89,8 +89,8 @@ impl LayerData for TextLayer {
false
}
fn bounding_box(&self, transform: glam::DAffine2, font_cache: &FontCache) -> Option<[DVec2; 2]> {
let buzz_face = Some(self.load_face(font_cache)?);
fn bounding_box(&self, transform: glam::DAffine2, render_data: &RenderData) -> Option<[DVec2; 2]> {
let buzz_face = Some(self.load_face(render_data)?);
if transform.matrix2 == DMat2::ZERO {
return None;
@ -99,8 +99,8 @@ impl LayerData for TextLayer {
Some((transform * self.bounding_box(&self.text, buzz_face)).bounding_box())
}
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, font_cache: &FontCache) {
let buzz_face = self.load_face(font_cache);
fn intersects_quad(&self, quad: Quad, path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, render_data: &RenderData) {
let buzz_face = self.load_face(render_data);
if intersect_quad_bez_path(quad, &self.bounding_box(&self.text, buzz_face).path(), true) {
intersections.push(path.clone());
@ -109,8 +109,8 @@ impl LayerData for TextLayer {
}
impl TextLayer {
pub fn load_face<'a>(&self, font_cache: &'a FontCache) -> Option<Face<'a>> {
font_cache.get(&self.font).map(|data| rustybuzz::Face::from_slice(data, 0).expect("Loading font failed"))
pub fn load_face<'a>(&self, render_data: &'a RenderData) -> Option<Face<'a>> {
render_data.font_cache.get(&self.font).map(|data| rustybuzz::Face::from_slice(data, 0).expect("Loading font failed"))
}
pub fn transform(&self, transforms: &[DAffine2], mode: ViewMode) -> DAffine2 {
@ -121,7 +121,7 @@ impl TextLayer {
transforms.iter().skip(start).cloned().reduce(|a, b| a * b).unwrap_or(DAffine2::IDENTITY)
}
pub fn new(text: String, style: PathStyle, size: f64, font: Font, font_cache: &FontCache) -> Self {
pub fn new(text: String, style: PathStyle, size: f64, font: Font, render_data: &RenderData) -> Self {
let mut new = Self {
text,
path_style: style,
@ -132,7 +132,7 @@ impl TextLayer {
cached_path: None,
};
new.cached_path = Some(new.generate_path(new.load_face(font_cache)));
new.cached_path = Some(new.generate_path(new.load_face(render_data)));
new
}
@ -150,8 +150,8 @@ impl TextLayer {
/// Converts to a [Subpath], without populating the cache.
#[inline]
pub fn to_subpath_nonmut(&self, font_cache: &FontCache) -> Subpath {
let buzz_face = self.load_face(font_cache);
pub fn to_subpath_nonmut(&self, render_data: &RenderData) -> Subpath {
let buzz_face = self.load_face(render_data);
self.cached_path
.clone()
@ -170,8 +170,8 @@ impl TextLayer {
Quad::from_box([DVec2::ZERO, far])
}
pub fn update_text(&mut self, text: String, font_cache: &FontCache) {
let buzz_face = self.load_face(font_cache);
pub fn update_text(&mut self, text: String, render_data: &RenderData) {
let buzz_face = self.load_face(render_data);
self.text = text;
self.cached_path = Some(self.generate_path(buzz_face));

View file

@ -45,13 +45,6 @@ pub enum Operation {
font_name: String,
font_style: String,
},
AddImage {
path: Vec<LayerId>,
insert_index: isize,
transform: [f64; 6],
mime: String,
image_data: Vec<u8>,
},
AddNodeGraphFrame {
path: Vec<LayerId>,
insert_index: isize,

View file

@ -1,10 +1,10 @@
use crate::application::generate_uuid;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use document_legacy::color::Color;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::layers::style::{self, Fill, RenderData, ViewMode};
use document_legacy::layers::text_layer::FontCache;
use document_legacy::DocumentResponse;
use document_legacy::LayerId;
use document_legacy::Operation as DocumentOperation;
@ -18,30 +18,34 @@ pub struct ArtboardMessageHandler {
pub artboard_ids: Vec<LayerId>,
}
impl MessageHandler<ArtboardMessage, &FontCache> for ArtboardMessageHandler {
impl MessageHandler<ArtboardMessage, &PersistentData> for ArtboardMessageHandler {
#[remain::check]
fn process_message(&mut self, message: ArtboardMessage, responses: &mut VecDeque<Message>, font_cache: &FontCache) {
fn process_message(&mut self, message: ArtboardMessage, responses: &mut VecDeque<Message>, persistent_data: &PersistentData) {
use ArtboardMessage::*;
#[remain::sorted]
match message {
// Sub-messages
#[remain::unsorted]
DispatchOperation(operation) => match self.artboards_document.handle_operation(*operation, font_cache) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::LayerChanged { path } => responses.push_back(PropertiesPanelMessage::CheckSelectedWasUpdated { path: path.clone() }.into()),
DocumentResponse::DeletedLayer { path } => responses.push_back(PropertiesPanelMessage::CheckSelectedWasDeleted { path: path.clone() }.into()),
DocumentResponse::DocumentChanged => responses.push_back(ArtboardMessage::RenderArtboards.into()),
_ => {}
};
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
DispatchOperation(operation) => {
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
match self.artboards_document.handle_operation(*operation, &render_data) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::LayerChanged { path } => responses.push_back(PropertiesPanelMessage::CheckSelectedWasUpdated { path: path.clone() }.into()),
DocumentResponse::DeletedLayer { path } => responses.push_back(PropertiesPanelMessage::CheckSelectedWasDeleted { path: path.clone() }.into()),
DocumentResponse::DocumentChanged => responses.push_back(ArtboardMessage::RenderArtboards.into()),
_ => {}
};
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
}
Ok(None) => {}
Err(e) => error!("Artboard Error: {:?}", e),
}
Ok(None) => {}
Err(e) => error!("Artboard Error: {:?}", e),
},
}
// Messages
AddArtboard { id, position, size } => {
@ -85,10 +89,10 @@ impl MessageHandler<ArtboardMessage, &FontCache> for ArtboardMessageHandler {
.into(),
)
} else {
let render_data = RenderData::new(ViewMode::Normal, font_cache, None);
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
responses.push_back(
FrontendMessage::UpdateDocumentArtboards {
svg: self.artboards_document.render_root(render_data),
svg: self.artboards_document.render_root(&render_data),
}
.into(),
);

View file

@ -29,7 +29,7 @@ use document_legacy::layers::blend_mode::BlendMode;
use document_legacy::layers::folder_layer::FolderLayer;
use document_legacy::layers::layer_info::{LayerDataType, LayerDataTypeDiscriminant};
use document_legacy::layers::style::{Fill, RenderData, ViewMode};
use document_legacy::layers::text_layer::{Font, FontCache};
use document_legacy::layers::text_layer::Font;
use document_legacy::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
use graph_craft::document::NodeId;
use graphene_std::vector::subpath::Subpath;
@ -114,70 +114,74 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
) {
use DocumentMessage::*;
let render_data = RenderData::new(&persistent_data.font_cache, self.view_mode, Some(ipp.document_bounds()));
#[remain::sorted]
match message {
// Sub-messages
#[remain::unsorted]
DispatchOperation(op) => match self.document_legacy.handle_operation(*op, &persistent_data.font_cache) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::FolderChanged { path } => responses.push_back(FolderChanged { affected_folder_path: path.clone() }.into()),
DocumentResponse::DeletedLayer { path } => {
self.layer_metadata.remove(path);
}
DocumentResponse::LayerChanged { path } => responses.push_back(LayerChanged { affected_layer_path: path.clone() }.into()),
DocumentResponse::CreatedLayer { path } => {
if self.layer_metadata.contains_key(path) {
warn!("CreatedLayer overrides existing layer metadata.");
DispatchOperation(op) => {
match self.document_legacy.handle_operation(*op, &render_data) {
Ok(Some(document_responses)) => {
for response in document_responses {
match &response {
DocumentResponse::FolderChanged { path } => responses.push_back(FolderChanged { affected_folder_path: path.clone() }.into()),
DocumentResponse::DeletedLayer { path } => {
self.layer_metadata.remove(path);
}
self.layer_metadata.insert(path.clone(), LayerMetadata::new(false));
DocumentResponse::LayerChanged { path } => responses.push_back(LayerChanged { affected_layer_path: path.clone() }.into()),
DocumentResponse::CreatedLayer { path } => {
if self.layer_metadata.contains_key(path) {
warn!("CreatedLayer overrides existing layer metadata.");
}
self.layer_metadata.insert(path.clone(), LayerMetadata::new(false));
responses.push_back(LayerChanged { affected_layer_path: path.clone() }.into());
self.layer_range_selection_reference = path.clone();
responses.push_back(
AddSelectedLayers {
additional_layers: vec![path.clone()],
}
.into(),
);
}
DocumentResponse::DocumentChanged => responses.push_back(RenderDocument.into()),
DocumentResponse::DeletedSelectedManipulatorPoints => {
// Clear Properties panel after deleting all points by updating backend widget state.
responses.push_back(
LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesOptions,
}
.into(),
);
responses.push_back(
LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesSections,
}
.into(),
);
}
};
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
responses.push_back(LayerChanged { affected_layer_path: path.clone() }.into());
self.layer_range_selection_reference = path.clone();
responses.push_back(
AddSelectedLayers {
additional_layers: vec![path.clone()],
}
.into(),
);
}
DocumentResponse::DocumentChanged => responses.push_back(RenderDocument.into()),
DocumentResponse::DeletedSelectedManipulatorPoints => {
// Clear Properties panel after deleting all points by updating backend widget state.
responses.push_back(
LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesOptions,
}
.into(),
);
responses.push_back(
LayoutMessage::SendLayout {
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
layout_target: LayoutTarget::PropertiesSections,
}
.into(),
);
}
};
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
}
}
// Display boolean operation error to the user (except if it is a nothing done error).
Err(DocumentError::BooleanOperationError(boolean_operation_error)) if boolean_operation_error != BooleanOperationError::NothingDone => responses.push_back(
DialogMessage::DisplayDialogError {
title: "Failed to calculate boolean operation".into(),
description: format!("Unfortunately, this feature not that robust yet.\n\nError: {boolean_operation_error:?}"),
}
.into(),
),
Err(e) => error!("DocumentError: {:?}", e),
Ok(_) => (),
}
// Display boolean operation error to the user (except if it is a nothing done error).
Err(DocumentError::BooleanOperationError(boolean_operation_error)) if boolean_operation_error != BooleanOperationError::NothingDone => responses.push_back(
DialogMessage::DisplayDialogError {
title: "Failed to calculate boolean operation".into(),
description: format!("Unfortunately, this feature not that robust yet.\n\nError: {boolean_operation_error:?}"),
}
.into(),
),
Err(e) => error!("DocumentError: {:?}", e),
Ok(_) => (),
},
}
#[remain::unsorted]
Artboard(message) => {
self.artboard_message_handler.process_message(message, responses, &persistent_data.font_cache);
self.artboard_message_handler.process_message(message, responses, persistent_data);
}
#[remain::unsorted]
Navigation(message) => {
@ -185,13 +189,12 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
#[remain::unsorted]
Overlays(message) => {
self.overlays_message_handler
.process_message(message, responses, (self.overlays_visible, &persistent_data.font_cache, ipp));
self.overlays_message_handler.process_message(message, responses, (self.overlays_visible, persistent_data, ipp));
}
#[remain::unsorted]
TransformLayer(message) => {
self.transform_layer_handler
.process_message(message, responses, (&mut self.layer_metadata, &mut self.document_legacy, ipp, &persistent_data.font_cache));
.process_message(message, responses, (&mut self.layer_metadata, &mut self.document_legacy, ipp, &render_data));
}
#[remain::unsorted]
PropertiesPanel(message) => {
@ -219,20 +222,20 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
AddSelectedLayers { additional_layers } => {
for layer_path in &additional_layers {
responses.extend(self.select_layer(layer_path, &persistent_data.font_cache));
responses.extend(self.select_layer(layer_path, &render_data));
}
// TODO: Correctly update layer panel in clear_selection instead of here
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
responses.push_back(BroadcastEvent::SelectionChanged.into());
self.update_layer_tree_options_bar_widgets(responses, &persistent_data.font_cache);
self.update_layer_tree_options_bar_widgets(responses, &render_data);
}
AlignSelectedLayers { axis, aggregate } => {
self.backup(responses);
let (paths, boxes): (Vec<_>, Vec<_>) = self
.selected_layers()
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, &persistent_data.font_cache).ok()?.map(|b| (path, b)))
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, &render_data).ok()?.map(|b| (path, b)))
.unzip();
let axis = match axis {
@ -240,7 +243,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
AlignAxis::Y => DVec2::Y,
};
let lerp = |bbox: &[DVec2; 2]| bbox[0].lerp(bbox[1], 0.5);
if let Some(combined_box) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &persistent_data.font_cache) {
if let Some(combined_box) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &render_data) {
let aggregated = match aggregate {
AlignAggregate::Min => combined_box[0],
AlignAggregate::Max => combined_box[1],
@ -376,14 +379,9 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
// Calculate the bounding box of the region to be exported
let bounds = match bounds {
ExportBounds::AllArtwork => self.all_layer_bounds(&persistent_data.font_cache),
ExportBounds::Selection => self.selected_visible_layers_bounding_box(&persistent_data.font_cache),
ExportBounds::Artboard(id) => self
.artboard_message_handler
.artboards_document
.layer(&[id])
.ok()
.and_then(|layer| layer.aabb(&persistent_data.font_cache)),
ExportBounds::AllArtwork => self.all_layer_bounds(&render_data),
ExportBounds::Selection => self.selected_visible_layers_bounding_box(&render_data),
ExportBounds::Artboard(id) => self.artboard_message_handler.artboards_document.layer(&[id]).ok().and_then(|layer| layer.aabb(&render_data)),
}
.unwrap_or_default();
let size = bounds[1] - bounds[0];
@ -412,7 +410,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
FlipAxis::X => DVec2::new(-1., 1.),
FlipAxis::Y => DVec2::new(1., -1.),
};
if let Some([min, max]) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &persistent_data.font_cache) {
if let Some([min, max]) = self.document_legacy.combined_viewport_bounding_box(self.selected_layers(), &render_data) {
let center = (max + min) / 2.;
let bbox_trans = DAffine2::from_translation(-center);
for path in self.selected_layers() {
@ -483,11 +481,11 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
);
}
LayerChanged { affected_layer_path } => {
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone(), &persistent_data.font_cache) {
if let Ok(layer_entry) = self.layer_panel_entry(affected_layer_path.clone(), &render_data) {
responses.push_back(FrontendMessage::UpdateDocumentLayerDetails { data: layer_entry }.into());
}
responses.push_back(PropertiesPanelMessage::CheckSelectedWasUpdated { path: affected_layer_path }.into());
self.update_layer_tree_options_bar_widgets(responses, &persistent_data.font_cache);
self.update_layer_tree_options_bar_widgets(responses, &render_data);
}
MoveSelectedLayersTo {
folder_path,
@ -632,10 +630,9 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
}
RenameLayer { layer_path, new_name } => responses.push_back(DocumentOperation::RenameLayer { layer_path, new_name }.into()),
RenderDocument => {
let render_data = RenderData::new(self.view_mode, &persistent_data.font_cache, Some(ipp.document_bounds()));
responses.push_back(
FrontendMessage::UpdateDocumentArtwork {
svg: self.document_legacy.render_root(render_data),
svg: self.document_legacy.render_root(&render_data),
}
.into(),
);
@ -645,7 +642,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
let scale = 0.5 + ASYMPTOTIC_EFFECT + document_transform_scale * SCALE_EFFECT;
let viewport_size = ipp.viewport_bounds.size();
let viewport_mid = ipp.viewport_bounds.center();
let [bounds1, bounds2] = self.document_bounds(&persistent_data.font_cache).unwrap_or([viewport_mid; 2]);
let [bounds1, bounds2] = self.document_bounds(&render_data).unwrap_or([viewport_mid; 2]);
let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale;
let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale;
let bounds_length = (bounds2 - bounds1) * (1. + SCROLLBAR_SPACING);
@ -782,7 +779,6 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
responses.push_back(FrontendMessage::TriggerRevokeBlobUrl { url: url.clone() }.into());
}
}
LayerDataType::Image(_) => {}
other => panic!(
"Setting blob URL for invalid layer type, which must be an `Imaginate`, `NodeGraphFrame` or `Image`. Found: `{:?}`",
other
@ -803,7 +799,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
responses.push_back(LayerChanged { affected_layer_path: layer_path }.into())
}
SetLayerName { layer_path, name } => {
if let Some(layer) = self.layer_panel_entry_from_path(&layer_path, &persistent_data.font_cache) {
if let Some(layer) = self.layer_panel_entry_from_path(&layer_path, &render_data) {
// Only save the history state if the name actually changed to something different
if layer.name != name {
self.backup(responses);
@ -926,7 +922,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
responses.push_front(NavigationMessage::SetCanvasZoom { zoom_factor: 2. }.into());
}
ZoomCanvasToFitAll => {
if let Some(bounds) = self.document_bounds(&persistent_data.font_cache) {
if let Some(bounds) = self.document_bounds(&render_data) {
responses.push_back(
NavigationMessage::FitViewportToBounds {
bounds,
@ -1057,14 +1053,14 @@ impl DocumentMessageHandler {
pub fn render_document(&mut self, size: DVec2, transform: DAffine2, persistent_data: &PersistentData, render_mode: DocumentRenderMode) -> String {
// Render the document SVG code
let render_data = RenderData::new(ViewMode::Normal, &persistent_data.font_cache, None);
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
let (artwork, outside) = match render_mode {
DocumentRenderMode::Root => (self.document_legacy.render_root(render_data), None),
DocumentRenderMode::OnlyBelowLayerInFolder(below_layer_path) => (self.document_legacy.render_layers_below(below_layer_path, render_data).unwrap(), None),
DocumentRenderMode::LayerCutout(layer_path, background) => (self.document_legacy.render_layer(layer_path, render_data).unwrap(), Some(background)),
DocumentRenderMode::Root => (self.document_legacy.render_root(&render_data), None),
DocumentRenderMode::OnlyBelowLayerInFolder(below_layer_path) => (self.document_legacy.render_layers_below(below_layer_path, &render_data).unwrap(), None),
DocumentRenderMode::LayerCutout(layer_path, background) => (self.document_legacy.render_layer(layer_path, &render_data).unwrap(), Some(background)),
};
let artboards = self.artboard_message_handler.artboards_document.render_root(render_data);
let artboards = self.artboard_message_handler.artboards_document.render_root(&render_data);
let outside_artboards_color = outside.map_or_else(
|| if self.artboard_message_handler.artboard_ids.is_empty() { "ffffff" } else { "222222" }.to_string(),
|col| col.rgba_hex(),
@ -1129,12 +1125,14 @@ impl DocumentMessageHandler {
&& self.name.starts_with(DEFAULT_DOCUMENT_NAME)
}
fn select_layer(&mut self, path: &[LayerId], font_cache: &FontCache) -> Option<Message> {
fn select_layer(&mut self, path: &[LayerId], render_data: &RenderData) -> Option<Message> {
println!("Select_layer fail: {:?}", self.all_layers_sorted());
if let Some(layer) = self.layer_metadata.get_mut(path) {
let render_data = RenderData::new(render_data.font_cache, self.view_mode, None);
layer.selected = true;
let data = self.layer_panel_entry(path.to_vec(), font_cache).ok()?;
let data = self.layer_panel_entry(path.to_vec(), &render_data).ok()?;
(!path.is_empty()).then(|| FrontendMessage::UpdateDocumentLayerDetails { data }.into())
} else {
warn!("Tried to select non existing layer {:?}", path);
@ -1142,13 +1140,13 @@ impl DocumentMessageHandler {
}
}
pub fn selected_visible_layers_bounding_box(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
pub fn selected_visible_layers_bounding_box(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
let paths = self.selected_visible_layers();
self.document_legacy.combined_viewport_bounding_box(paths, font_cache)
self.document_legacy.combined_viewport_bounding_box(paths, render_data)
}
pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId], font_cache: &FontCache) -> Option<([DVec2; 2], DAffine2)> {
self.artboard_message_handler.artboards_document.bounding_box_and_transform(path, font_cache).unwrap_or(None)
pub fn artboard_bounding_box_and_transform(&self, path: &[LayerId], render_data: &RenderData) -> Option<([DVec2; 2], DAffine2)> {
self.artboard_message_handler.artboards_document.bounding_box_and_transform(path, render_data).unwrap_or(None)
}
pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
@ -1220,16 +1218,16 @@ impl DocumentMessageHandler {
}
/// Returns the bounding boxes for all visible layers and artboards, optionally excluding any paths.
pub fn bounding_boxes<'a>(&'a self, ignore_document: Option<&'a Vec<Vec<LayerId>>>, ignore_artboard: Option<LayerId>, font_cache: &'a FontCache) -> impl Iterator<Item = [DVec2; 2]> + 'a {
pub fn bounding_boxes<'a>(&'a self, ignore_document: Option<&'a Vec<Vec<LayerId>>>, ignore_artboard: Option<LayerId>, render_data: &'a RenderData) -> impl Iterator<Item = [DVec2; 2]> + 'a {
self.visible_layers()
.filter(move |path| ignore_document.map_or(true, |ignore_document| !ignore_document.iter().any(|ig| ig.as_slice() == *path)))
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, font_cache).ok()?)
.filter_map(|path| self.document_legacy.viewport_bounding_box(path, render_data).ok()?)
.chain(
self.artboard_message_handler
.artboard_ids
.iter()
.filter(move |&&id| Some(id) != ignore_artboard)
.filter_map(|&path| self.artboard_message_handler.artboards_document.viewport_bounding_box(&[path], font_cache).ok()?),
.filter_map(|&path| self.artboard_message_handler.artboards_document.viewport_bounding_box(&[path], render_data).ok()?),
)
}
@ -1505,31 +1503,31 @@ impl DocumentMessageHandler {
}
// TODO: This should probably take a slice not a vec, also why does this even exist when `layer_panel_entry_from_path` also exists?
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>, font_cache: &FontCache) -> Result<LayerPanelEntry, EditorError> {
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>, render_data: &RenderData) -> Result<LayerPanelEntry, EditorError> {
let data: LayerMetadata = *self
.layer_metadata
.get_mut(&path)
.ok_or_else(|| EditorError::Document(format!("Could not get layer metadata for {:?}", path)))?;
let layer = self.document_legacy.layer(&path)?;
let entry = LayerPanelEntry::new(&data, self.document_legacy.multiply_transforms(&path)?, layer, path, font_cache);
let entry = LayerPanelEntry::new(&data, self.document_legacy.multiply_transforms(&path)?, layer, path, render_data);
Ok(entry)
}
/// Returns a list of `LayerPanelEntry`s intended for display purposes. These don't contain
/// any actual data, but rather attributes such as visibility and names of the layers.
pub fn layer_panel(&mut self, path: &[LayerId], font_cache: &FontCache) -> Result<Vec<LayerPanelEntry>, EditorError> {
pub fn layer_panel(&mut self, path: &[LayerId], render_data: &RenderData) -> Result<Vec<LayerPanelEntry>, EditorError> {
let folder = self.document_legacy.folder(path)?;
let paths: Vec<Vec<LayerId>> = folder.layer_ids.iter().map(|id| [path, &[*id]].concat()).collect();
let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path, font_cache)).collect();
let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path, render_data)).collect();
Ok(entries)
}
pub fn layer_panel_entry_from_path(&self, path: &[LayerId], font_cache: &FontCache) -> Option<LayerPanelEntry> {
pub fn layer_panel_entry_from_path(&self, path: &[LayerId], render_data: &RenderData) -> Option<LayerPanelEntry> {
let layer_metadata = self.layer_metadata(path);
let transform = self.document_legacy.generate_transform_across_scope(path, Some(self.document_legacy.root.transform.inverse())).ok()?;
let layer = self.document_legacy.layer(path).ok()?;
Some(LayerPanelEntry::new(layer_metadata, transform, layer, path.to_vec(), font_cache))
Some(LayerPanelEntry::new(layer_metadata, transform, layer, path.to_vec(), render_data))
}
/// When working with an insert index, deleting the layers may cause the insert index to point to a different location (if the layer being deleted was located before the insert index).
@ -1544,16 +1542,16 @@ impl DocumentMessageHandler {
}
/// Calculates the bounding box of all layers in the document
pub fn all_layer_bounds(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
self.document_legacy.viewport_bounding_box(&[], font_cache).ok().flatten()
pub fn all_layer_bounds(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
self.document_legacy.viewport_bounding_box(&[], render_data).ok().flatten()
}
/// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable))
pub fn document_bounds(&self, font_cache: &FontCache) -> Option<[DVec2; 2]> {
pub fn document_bounds(&self, render_data: &RenderData) -> Option<[DVec2; 2]> {
if self.artboard_message_handler.is_infinite_canvas() {
self.all_layer_bounds(font_cache)
self.all_layer_bounds(render_data)
} else {
self.artboard_message_handler.artboards_document.viewport_bounding_box(&[], font_cache).ok().flatten()
self.artboard_message_handler.artboards_document.viewport_bounding_box(&[], render_data).ok().flatten()
}
}
@ -1581,11 +1579,6 @@ impl DocumentMessageHandler {
LayerDataType::Text(text) => {
fonts.insert(text.font.clone());
}
LayerDataType::Image(image) => image_data.push(FrontendImageData {
path: path.clone(),
image_data: image.image_data.clone(),
mime: image.mime.clone(),
}),
LayerDataType::NodeGraphFrame(node_graph_frame) => {
if let Some(data) = &node_graph_frame.image_data {
image_data.push(FrontendImageData {
@ -1829,7 +1822,7 @@ impl DocumentMessageHandler {
);
}
pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque<Message>, font_cache: &FontCache) {
pub fn update_layer_tree_options_bar_widgets(&self, responses: &mut VecDeque<Message>, render_data: &RenderData) {
let mut opacity = None;
let mut opacity_is_mixed = false;
@ -1838,7 +1831,7 @@ impl DocumentMessageHandler {
self.layer_metadata
.keys()
.filter_map(|path| self.layer_panel_entry_from_path(path, font_cache))
.filter_map(|path| self.layer_panel_entry_from_path(path, render_data))
.filter(|layer_panel_entry| layer_panel_entry.layer_metadata.selected)
.flat_map(|layer_panel_entry| self.document_legacy.layer(layer_panel_entry.path.as_slice()))
.for_each(|layer| {

View file

@ -1,27 +1,31 @@
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use document_legacy::document::Document as DocumentLegacy;
use document_legacy::layers::style::{RenderData, ViewMode};
use document_legacy::layers::text_layer::FontCache;
#[derive(Debug, Clone, Default)]
pub struct OverlaysMessageHandler {
pub overlays_document: DocumentLegacy,
}
impl MessageHandler<OverlaysMessage, (bool, &FontCache, &InputPreprocessorMessageHandler)> for OverlaysMessageHandler {
impl MessageHandler<OverlaysMessage, (bool, &PersistentData, &InputPreprocessorMessageHandler)> for OverlaysMessageHandler {
#[remain::check]
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, (overlays_visible, font_cache, ipp): (bool, &FontCache, &InputPreprocessorMessageHandler)) {
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, (overlays_visible, persistent_data, ipp): (bool, &PersistentData, &InputPreprocessorMessageHandler)) {
use OverlaysMessage::*;
#[remain::sorted]
match message {
// Sub-messages
#[remain::unsorted]
DispatchOperation(operation) => match self.overlays_document.handle_operation(*operation, font_cache) {
Ok(_) => responses.push_back(OverlaysMessage::Rerender.into()),
Err(e) => error!("OverlaysError: {:?}", e),
},
DispatchOperation(operation) => {
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, Some(ipp.document_bounds()));
match self.overlays_document.handle_operation(*operation, &render_data) {
Ok(_) => responses.push_back(OverlaysMessage::Rerender.into()),
Err(e) => error!("OverlaysError: {:?}", e),
}
}
// Messages
ClearAllOverlays => {
@ -33,8 +37,8 @@ impl MessageHandler<OverlaysMessage, (bool, &FontCache, &InputPreprocessorMessag
responses.push_back(
FrontendMessage::UpdateDocumentOverlays {
svg: if overlays_visible {
let render_data = RenderData::new(ViewMode::Normal, font_cache, Some(ipp.document_bounds()));
self.overlays_document.render_root(render_data)
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, Some(ipp.document_bounds()));
self.overlays_document.render_root(&render_data)
} else {
String::from("")
},

View file

@ -8,6 +8,7 @@ use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use document_legacy::layers::layer_info::LayerDataTypeDiscriminant;
use document_legacy::layers::style::{RenderData, ViewMode};
use document_legacy::{LayerId, Operation};
use serde::{Deserialize, Serialize};
@ -20,6 +21,8 @@ pub struct PropertiesPanelMessageHandler {
impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'a>)> for PropertiesPanelMessageHandler {
#[remain::check]
fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, (persistent_data, data): (&PersistentData, PropertiesPanelMessageHandlerData)) {
use PropertiesPanelMessage::*;
let PropertiesPanelMessageHandlerData {
artwork_document,
artboard_document,
@ -30,7 +33,8 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
TargetDocument::Artboard => artboard_document,
TargetDocument::Artwork => artwork_document,
};
use PropertiesPanelMessage::*;
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
match message {
SetActiveLayers { paths, document } => {
if paths.len() != 1 {
@ -99,7 +103,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
let (path, target_document) = self.active_selection.as_ref().expect("Received update for properties panel with no active layer");
let layer = get_document(*target_document).layer(path).unwrap();
let transform = apply_transform_operation(layer, transform_op, value, &persistent_data.font_cache);
let transform = apply_transform_operation(layer, transform_op, value, &render_data);
self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }, true, responses);
}

View file

@ -12,14 +12,14 @@ use crate::messages::prelude::*;
use document_legacy::color::Color;
use document_legacy::document::Document;
use document_legacy::layers::layer_info::{Layer, LayerDataType, LayerDataTypeDiscriminant};
use document_legacy::layers::style::{Fill, Gradient, GradientType, LineCap, LineJoin, Stroke};
use document_legacy::layers::text_layer::{FontCache, TextLayer};
use document_legacy::layers::style::{Fill, Gradient, GradientType, LineCap, LineJoin, RenderData, Stroke, ViewMode};
use document_legacy::layers::text_layer::TextLayer;
use glam::{DAffine2, DVec2};
use std::f64::consts::PI;
use std::sync::Arc;
pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value: f64, font_cache: &FontCache) -> [f64; 6] {
pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value: f64, render_data: &RenderData) -> [f64; 6] {
let transformation = match transform_op {
TransformOp::X => DAffine2::update_x,
TransformOp::Y => DAffine2::update_y,
@ -29,8 +29,8 @@ pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value
};
let scale = match transform_op {
TransformOp::Width => layer.bounding_transform(font_cache).scale_x() / layer.transform.scale_x(),
TransformOp::Height => layer.bounding_transform(font_cache).scale_y() / layer.transform.scale_y(),
TransformOp::Width => layer.bounding_transform(render_data).scale_x() / layer.transform.scale_x(),
TransformOp::Height => layer.bounding_transform(render_data).scale_y() / layer.transform.scale_y(),
_ => 1.,
};
@ -43,7 +43,7 @@ pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value
}
// Find the layerspace pivot
let pivot = DAffine2::from_translation(layer.transform.transform_point2(layer.layerspace_pivot(font_cache)));
let pivot = DAffine2::from_translation(layer.transform.transform_point2(layer.layerspace_pivot(render_data)));
// Find the delta transform
let mut delta = layer.transform.inverse() * transform;
@ -94,17 +94,15 @@ pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDequ
}];
let properties_body = {
let shape = if let LayerDataType::Shape(shape) = &layer.data {
shape
} else {
let LayerDataType::Shape(shape) = &layer.data else {
panic!("Artboards can only be shapes")
};
let color = if let Fill::Solid(color) = shape.style.fill() {
color
} else {
let Fill::Solid(color) = shape.style.fill() else {
panic!("Artboard must have a solid fill")
};
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&persistent_data.font_cache));
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None);
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&render_data));
vec![LayoutGroup::Section {
name: "Artboard".into(),
@ -167,7 +165,7 @@ pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDequ
WidgetHolder::related_separator(),
WidgetHolder::unrelated_separator(),
WidgetHolder::new(Widget::NumberInput(NumberInput {
value: Some(layer.bounding_transform(&persistent_data.font_cache).scale_x()),
value: Some(layer.bounding_transform(&render_data).scale_x()),
label: "W".into(),
unit: " px".into(),
is_integer: true,
@ -183,7 +181,7 @@ pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDequ
})),
WidgetHolder::related_separator(),
WidgetHolder::new(Widget::NumberInput(NumberInput {
value: Some(layer.bounding_transform(&persistent_data.font_cache).scale_y()),
value: Some(layer.bounding_transform(&render_data).scale_y()),
label: "H".into(),
unit: " px".into(),
is_integer: true,
@ -267,11 +265,6 @@ pub fn register_artwork_layer_properties(
tooltip: "Text".into(),
..Default::default()
})),
LayerDataType::Image(_) => WidgetHolder::new(Widget::IconLabel(IconLabel {
icon: "NodeImage".into(),
tooltip: "Image".into(),
..Default::default()
})),
LayerDataType::NodeGraphFrame(_) => WidgetHolder::new(Widget::IconLabel(IconLabel {
icon: "NodeNodes".into(),
tooltip: "Node Graph Frame".into(),
@ -321,9 +314,6 @@ pub fn register_artwork_layer_properties(
node_section_stroke(&text.path_style.stroke().unwrap_or_default()),
]
}
LayerDataType::Image(_) => {
vec![node_section_transform(layer, persistent_data)]
}
LayerDataType::NodeGraphFrame(node_graph_frame) => {
let mut properties_sections = vec![node_section_transform(layer, persistent_data)];
@ -360,7 +350,8 @@ pub fn register_artwork_layer_properties(
}
fn node_section_transform(layer: &Layer, persistent_data: &PersistentData) -> LayoutGroup {
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&persistent_data.font_cache));
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::default(), None);
let pivot = layer.transform.transform_vector2(layer.layerspace_pivot(&render_data));
LayoutGroup::Section {
name: "Transform".into(),
layout: vec![
@ -492,7 +483,7 @@ fn node_section_transform(layer: &Layer, persistent_data: &PersistentData) -> La
WidgetHolder::unrelated_separator(), // TODO: Remove these when we have proper entry row formatting that includes room for Assists.
WidgetHolder::unrelated_separator(),
WidgetHolder::new(Widget::NumberInput(NumberInput {
value: Some(layer.bounding_transform(&persistent_data.font_cache).scale_x()),
value: Some(layer.bounding_transform(&render_data).scale_x()),
label: "W".into(),
unit: " px".into(),
on_update: WidgetCallback::new(|number_input: &NumberInput| {
@ -506,7 +497,7 @@ fn node_section_transform(layer: &Layer, persistent_data: &PersistentData) -> La
})),
WidgetHolder::related_separator(),
WidgetHolder::new(Widget::NumberInput(NumberInput {
value: Some(layer.bounding_transform(&persistent_data.font_cache).scale_y()),
value: Some(layer.bounding_transform(&render_data).scale_y()),
label: "H".into(),
unit: " px".into(),
on_update: WidgetCallback::new(|number_input: &NumberInput| {

View file

@ -5,9 +5,9 @@ use crate::messages::portfolio::document::utility_types::transformation::{Axis,
use crate::messages::prelude::*;
use document_legacy::document::Document;
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use document_legacy::layers::text_layer::FontCache;
use glam::DVec2;
#[derive(Debug, Clone, Default, PartialEq)]
@ -25,10 +25,10 @@ pub struct TransformLayerMessageHandler {
pivot: DVec2,
}
type TransformData<'a> = (&'a mut HashMap<Vec<LayerId>, LayerMetadata>, &'a mut Document, &'a InputPreprocessorMessageHandler, &'a FontCache);
type TransformData<'a> = (&'a mut HashMap<Vec<LayerId>, LayerMetadata>, &'a mut Document, &'a InputPreprocessorMessageHandler, &'a RenderData<'a>);
impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformLayerMessageHandler {
#[remain::check]
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (layer_metadata, document, ipp, font_cache): TransformData) {
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (layer_metadata, document, ipp, render_data): TransformData) {
use TransformLayerMessage::*;
let selected_layers = layer_metadata.iter().filter_map(|(layer_path, data)| data.selected.then_some(layer_path)).collect::<Vec<_>>();
@ -39,7 +39,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
selected.revert_operation();
typing.clear();
} else {
*selected.pivot = selected.mean_average_of_pivots(font_cache);
*selected.pivot = selected.mean_average_of_pivots(render_data);
}
*mouse_position = ipp.mouse.position;
@ -137,7 +137,7 @@ impl<'a> MessageHandler<TransformLayerMessage, TransformData<'a>> for TransformL
self.transform_operation.apply_transform_operation(&mut selected, self.snap);
}
TransformOperation::Rotating(rotation) => {
let selected_pivot = selected.mean_average_of_pivots(font_cache);
let selected_pivot = selected.mean_average_of_pivots(render_data);
let angle = {
let start_offset = self.mouse_position - selected_pivot;
let end_offset = ipp.mouse.position - selected_pivot;

View file

@ -1,6 +1,5 @@
use document_legacy::layers::layer_info::{Layer, LayerData, LayerDataTypeDiscriminant};
use document_legacy::layers::style::{RenderData, ViewMode};
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use glam::{DAffine2, DVec2};
@ -65,7 +64,7 @@ pub struct LayerPanelEntry {
}
impl LayerPanelEntry {
pub fn new(layer_metadata: &LayerMetadata, transform: DAffine2, layer: &Layer, path: Vec<LayerId>, font_cache: &FontCache) -> Self {
pub fn new(layer_metadata: &LayerMetadata, transform: DAffine2, layer: &Layer, path: Vec<LayerId>, render_data: &RenderData) -> Self {
let name = layer.name.clone().unwrap_or_else(|| String::from(""));
let mut tooltip = name.clone();
@ -75,11 +74,10 @@ impl LayerPanelEntry {
tooltip = tooltip.trim().to_string();
}
let arr = layer.data.bounding_box(transform, font_cache).unwrap_or([DVec2::ZERO, DVec2::ZERO]);
let arr = layer.data.bounding_box(transform, render_data).unwrap_or([DVec2::ZERO, DVec2::ZERO]);
let arr = arr.iter().map(|x| (*x).into()).collect::<Vec<(f64, f64)>>();
let mut thumbnail = String::new();
let mut svg_defs = String::new();
let render_data = RenderData::new(ViewMode::Normal, font_cache, None);
layer.data.clone().render(&mut thumbnail, &mut svg_defs, &mut vec![transform], render_data);
let transform = transform.to_cols_array().iter().map(ToString::to_string).collect::<Vec<_>>().join(",");
let thumbnail = if let [(x_min, y_min), (x_max, y_max)] = arr.as_slice() {

View file

@ -2,7 +2,7 @@ use crate::consts::{ROTATE_SNAP_ANGLE, SCALE_SNAP_INTERVAL};
use crate::messages::prelude::*;
use document_legacy::document::Document;
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use document_legacy::Operation as DocumentOperation;
@ -206,20 +206,20 @@ impl<'a> Selected<'a> {
}
}
pub fn mean_average_of_pivots(&mut self, font_cache: &FontCache) -> DVec2 {
let xy_summation = self.selected.iter().filter_map(|path| self.document.pivot(path, font_cache)).reduce(|a, b| a + b).unwrap_or_default();
pub fn mean_average_of_pivots(&mut self, render_data: &RenderData) -> DVec2 {
let xy_summation = self.selected.iter().filter_map(|path| self.document.pivot(path, render_data)).reduce(|a, b| a + b).unwrap_or_default();
xy_summation / self.selected.len() as f64
}
pub fn center_of_aabb(&mut self, font_cache: &FontCache) -> DVec2 {
pub fn center_of_aabb(&mut self, render_data: &RenderData) -> DVec2 {
let [min, max] = self
.selected
.iter()
.filter_map(|path| {
let multiplied_transform = self.document.multiply_transforms(path).unwrap();
self.document.layer(path).unwrap().aabb_for_transform(multiplied_transform, font_cache)
self.document.layer(path).unwrap().aabb_for_transform(multiplied_transform, render_data)
})
.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])])
.unwrap_or_default();

View file

@ -14,6 +14,7 @@ use crate::messages::prelude::*;
use crate::messages::tool::utility_types::{HintData, HintGroup};
use document_legacy::document::pick_safe_imaginate_resolution;
use document_legacy::layers::layer_info::{LayerDataType, LayerDataTypeDiscriminant};
use document_legacy::layers::style::RenderData;
use document_legacy::layers::text_layer::Font;
use document_legacy::{LayerId, Operation as DocumentOperation};
use graph_craft::document::value::TaggedValue;
@ -635,19 +636,21 @@ impl PortfolioMessageHandler {
}
}
// TODO Fix how this doesn't preserve tab order upon loading new document from *File > Load*
// TODO: Fix how this doesn't preserve tab order upon loading new document from *File > Load*
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, responses: &mut VecDeque<Message>) {
let render_data = RenderData::new(&self.persistent_data.font_cache, new_document.view_mode, None);
self.document_ids.push(document_id);
responses.extend(
new_document
.layer_metadata
.keys()
.filter_map(|path| new_document.layer_panel_entry_from_path(path, &self.persistent_data.font_cache))
.filter_map(|path| new_document.layer_panel_entry_from_path(path, &render_data))
.map(|entry| FrontendMessage::UpdateDocumentLayerDetails { data: entry }.into())
.collect::<Vec<_>>(),
);
new_document.update_layer_tree_options_bar_widgets(responses, &self.persistent_data.font_cache);
new_document.update_layer_tree_options_bar_widgets(responses, &render_data);
self.documents.insert(document_id, new_document);

View file

@ -4,8 +4,7 @@ use crate::messages::prelude::*;
use document_legacy::intersection::Quad;
use document_legacy::layers::layer_info::LayerDataType;
use document_legacy::layers::style::{self, Fill, Stroke};
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::{self, Fill, RenderData, Stroke};
use document_legacy::{LayerId, Operation};
use graphene_std::vector::subpath::Subpath;
@ -26,7 +25,7 @@ impl PathOutline {
overlay_path: Option<Vec<LayerId>>,
document: &DocumentMessageHandler,
responses: &mut VecDeque<Message>,
font_cache: &FontCache,
render_data: &RenderData,
) -> Option<Vec<LayerId>> {
// Get layer data
let document_layer = document.document_legacy.layer(&document_layer_path).ok()?;
@ -35,8 +34,8 @@ impl PathOutline {
// Get the bezpath from the shape or text
let subpath = match &document_layer.data {
LayerDataType::Shape(layer_shape) => Some(layer_shape.shape.clone()),
LayerDataType::Text(text) => Some(text.to_subpath_nonmut(font_cache)),
_ => document_layer.aabb_for_transform(DAffine2::IDENTITY, font_cache).map(|[p1, p2]| Subpath::new_rect(p1, p2)),
LayerDataType::Text(text) => Some(text.to_subpath_nonmut(render_data)),
_ => document_layer.aabb_for_transform(DAffine2::IDENTITY, render_data).map(|[p1, p2]| Subpath::new_rect(p1, p2)),
}?;
// Generate a new overlay layer if necessary
@ -74,16 +73,16 @@ impl PathOutline {
/// Creates an outline of a layer either with a pre-existing overlay or by generating a new one
///
/// Creates an outline, discarding the overlay on failiure
/// Creates an outline, discarding the overlay on failure
fn create_outline(
document_layer_path: Vec<LayerId>,
overlay_path: Option<Vec<LayerId>>,
document: &DocumentMessageHandler,
responses: &mut VecDeque<Message>,
font_cache: &FontCache,
render_data: &RenderData,
) -> Option<Vec<LayerId>> {
let copied_overlay_path = overlay_path.clone();
let result = Self::try_create_outline(document_layer_path, overlay_path, document, responses, font_cache);
let result = Self::try_create_outline(document_layer_path, overlay_path, document, responses, render_data);
if result.is_none() {
// Discard the overlay layer if it exists
if let Some(overlay_path) = copied_overlay_path {
@ -104,17 +103,17 @@ impl PathOutline {
}
/// Performs an intersect test and generates a hovered overlay if necessary
pub fn intersect_test_hovered(&mut self, input: &InputPreprocessorMessageHandler, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, font_cache: &FontCache) {
pub fn intersect_test_hovered(&mut self, input: &InputPreprocessorMessageHandler, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, render_data: &RenderData) {
// Get the layer the user is hovering over
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
let mut intersection = document.document_legacy.intersects_quad_root(quad, font_cache);
let mut intersection = document.document_legacy.intersects_quad_root(quad, render_data);
// If the user is hovering over a layer they have not already selected, then update outline
if let Some(path) = intersection.pop() {
if !document.selected_visible_layers().any(|visible| visible == path.as_slice()) {
// Updates the overlay, generating a new one if necessary
self.hovered_overlay_path = Self::create_outline(path.clone(), self.hovered_overlay_path.take(), document, responses, font_cache);
self.hovered_overlay_path = Self::create_outline(path.clone(), self.hovered_overlay_path.take(), document, responses, render_data);
if self.hovered_overlay_path.is_none() {
self.clear_hovered(responses);
}
@ -137,11 +136,11 @@ impl PathOutline {
}
/// Updates the selected overlays, generating or removing overlays if necessary
pub fn update_selected<'a>(&mut self, selected: impl Iterator<Item = &'a [LayerId]>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, font_cache: &FontCache) {
pub fn update_selected<'a>(&mut self, selected: impl Iterator<Item = &'a [LayerId]>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, render_data: &RenderData) {
let mut old_overlay_paths = std::mem::take(&mut self.selected_overlay_paths);
for document_layer_path in selected {
if let Some(overlay_path) = Self::create_outline(document_layer_path.to_vec(), old_overlay_paths.pop(), document, responses, font_cache) {
if let Some(overlay_path) = Self::create_outline(document_layer_path.to_vec(), old_overlay_paths.pop(), document, responses, render_data) {
self.selected_overlay_paths.push(overlay_path);
}
}

View file

@ -5,8 +5,7 @@ use crate::consts::{COLOR_ACCENT, PIVOT_INNER, PIVOT_OUTER, PIVOT_OUTER_OUTLINE_
use crate::messages::layout::utility_types::widgets::assist_widgets::PivotPosition;
use crate::messages::prelude::*;
use document_legacy::layers::style;
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::{self, RenderData};
use document_legacy::{LayerId, Operation};
use glam::{DAffine2, DVec2};
@ -40,8 +39,8 @@ impl Default for Pivot {
impl Pivot {
/// Calculates the transform that gets from normalized pivot to viewspace.
fn get_layer_pivot_transform(layer_path: &[LayerId], layer: &document_legacy::layers::layer_info::Layer, document: &DocumentMessageHandler, font_cache: &FontCache) -> DAffine2 {
let [mut min, max] = layer.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]);
fn get_layer_pivot_transform(layer_path: &[LayerId], layer: &document_legacy::layers::layer_info::Layer, document: &DocumentMessageHandler, render_data: &RenderData) -> DAffine2 {
let [mut min, max] = layer.aabb_for_transform(DAffine2::IDENTITY, render_data).unwrap_or([DVec2::ZERO, DVec2::ONE]);
// If the layer bounds are 0 in either axis then set them to one (to avoid div 0)
if (max.x - min.x) < f64::EPSILON * 1000. {
@ -56,7 +55,7 @@ impl Pivot {
}
/// Recomputes the pivot position and transform.
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler, font_cache: &FontCache) {
fn recalculate_pivot(&mut self, document: &DocumentMessageHandler, render_data: &RenderData) {
let mut layers = document.selected_visible_layers();
if let Some(first) = layers.next() {
// Add one because the first item is consumed above.
@ -66,20 +65,20 @@ impl Pivot {
if selected_layers_count == 1 {
if let Ok(layer) = document.document_legacy.layer(first) {
self.normalized_pivot = layer.pivot;
self.transform_from_normalized = Self::get_layer_pivot_transform(first, layer, document, font_cache);
self.transform_from_normalized = Self::get_layer_pivot_transform(first, layer, document, render_data);
self.pivot = Some(self.transform_from_normalized.transform_point2(layer.pivot));
}
} else {
// If more than one layer is selected we use the AABB with the mean of the pivots
let xy_summation = document
.selected_visible_layers()
.filter_map(|path| document.document_legacy.pivot(path, font_cache))
.filter_map(|path| document.document_legacy.pivot(path, render_data))
.reduce(|a, b| a + b)
.unwrap_or_default();
let pivot = xy_summation / selected_layers_count as f64;
self.pivot = Some(pivot);
let [min, max] = document.selected_visible_layers_bounding_box(font_cache).unwrap_or([DVec2::ZERO, DVec2::ONE]);
let [min, max] = document.selected_visible_layers_bounding_box(render_data).unwrap_or([DVec2::ZERO, DVec2::ONE]);
self.normalized_pivot = (pivot - min) / (max - min);
self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min);
@ -147,8 +146,8 @@ impl Pivot {
responses.push_back(DocumentMessage::Overlays(Operation::TransformLayerInViewport { path: inner, transform }.into()).into());
}
pub fn update_pivot(&mut self, document: &DocumentMessageHandler, font_cache: &FontCache, responses: &mut VecDeque<Message>) {
self.recalculate_pivot(document, font_cache);
pub fn update_pivot(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
self.recalculate_pivot(document, render_data);
self.redraw_pivot(responses);
}
@ -165,10 +164,10 @@ impl Pivot {
}
/// Sets the viewport position of the pivot for all selected layers.
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, font_cache: &FontCache, responses: &mut VecDeque<Message>) {
pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
for layer_path in document.selected_visible_layers() {
if let Ok(layer) = document.document_legacy.layer(layer_path) {
let transform = Self::get_layer_pivot_transform(layer_path, layer, document, font_cache);
let transform = Self::get_layer_pivot_transform(layer_path, layer, document, render_data);
let pivot = transform.inverse().transform_point2(position);
// Only update the pivot when computed position is finite. Infinite can happen when scale is 0.
if pivot.is_finite() {
@ -180,8 +179,8 @@ impl Pivot {
}
/// Set the pivot using the normalized transform that is set above.
pub fn set_normalized_position(&self, position: DVec2, document: &DocumentMessageHandler, font_cache: &FontCache, responses: &mut VecDeque<Message>) {
self.set_viewport_position(self.transform_from_normalized.transform_point2(position), document, font_cache, responses);
pub fn set_normalized_position(&self, position: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
self.set_viewport_position(self.transform_from_normalized.transform_point2(position), document, render_data, responses);
}
/// Answers if the pointer is currently positioned over the pivot.

View file

@ -3,7 +3,7 @@ use crate::messages::input_mapper::utility_types::input_mouse::ViewportPosition;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::snapping::SnapManager;
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::RenderData;
use document_legacy::LayerId;
use document_legacy::Operation;
@ -18,8 +18,8 @@ pub struct Resize {
impl Resize {
/// Starts a resize, assigning the snap targets and snapping the starting position.
pub fn start(&mut self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, font_cache: &FontCache) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true);
pub fn start(&mut self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, render_data: &RenderData) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let root_transform = document.document_legacy.root.transform;
self.drag_start = root_transform.inverse().transform_point2(self.snap_manager.snap_position(responses, document, input.mouse.position));

View file

@ -7,6 +7,7 @@ use crate::messages::prelude::*;
use crate::messages::tool::utility_types::ToolType;
use document_legacy::color::Color;
use document_legacy::layers::style::RenderData;
#[derive(Debug, Default)]
pub struct ToolMessageHandler {
@ -21,6 +22,8 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
responses: &mut VecDeque<Message>,
(document, document_id, input, persistent_data): (&DocumentMessageHandler, u64, &InputPreprocessorMessageHandler, &PersistentData),
) {
let render_data = RenderData::new(&persistent_data.font_cache, document.view_mode, None);
#[remain::sorted]
match message {
// Messages
@ -73,12 +76,12 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
let mut send_abort_to_tool = |tool_type, update_hints_and_cursor: bool| {
if let Some(tool) = tool_data.tools.get_mut(&tool_type) {
if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort {
tool.process_message(tool_abort_message, responses, (document, document_id, document_data, input, &persistent_data.font_cache));
tool.process_message(tool_abort_message, responses, (document, document_id, document_data, input, &render_data));
}
if update_hints_and_cursor {
tool.process_message(ToolMessage::UpdateHints, responses, (document, document_id, document_data, input, &persistent_data.font_cache));
tool.process_message(ToolMessage::UpdateCursor, responses, (document, document_id, document_data, input, &persistent_data.font_cache));
tool.process_message(ToolMessage::UpdateHints, responses, (document, document_id, document_data, input, &render_data));
tool.process_message(ToolMessage::UpdateCursor, responses, (document, document_id, document_data, input, &render_data));
}
}
};
@ -134,10 +137,10 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
// Set initial hints and cursor
tool_data
.active_tool_mut()
.process_message(ToolMessage::UpdateHints, responses, (document, document_id, document_data, input, &persistent_data.font_cache));
.process_message(ToolMessage::UpdateHints, responses, (document, document_id, document_data, input, &render_data));
tool_data
.active_tool_mut()
.process_message(ToolMessage::UpdateCursor, responses, (document, document_id, document_data, input, &persistent_data.font_cache));
.process_message(ToolMessage::UpdateCursor, responses, (document, document_id, document_data, input, &render_data));
}
ToolMessage::RefreshToolOptions => {
let tool_data = &mut self.tool_state.tool_data;
@ -196,7 +199,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
if let Some(tool) = tool_data.tools.get_mut(&tool_type) {
if tool_type == tool_data.active_tool_type {
tool.process_message(tool_message, responses, (document, document_id, document_data, input, &persistent_data.font_cache));
tool.process_message(tool_message, responses, (document, document_id, document_data, input, &render_data));
}
}
}

View file

@ -112,14 +112,14 @@ impl Fsm for ArtboardToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, _global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, _global_tool_data, input, render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
if let ToolMessage::Artboard(event) = event {
match (self, event) {
(state, ArtboardToolMessage::DocumentIsDirty) if state != ArtboardToolFsmState::Drawing => {
let current_artboard = tool_data.selected_artboard.and_then(|path| document.artboard_bounding_box_and_transform(&[path], font_cache));
let current_artboard = tool_data.selected_artboard.and_then(|path| document.artboard_bounding_box_and_transform(&[path], render_data));
match (current_artboard, tool_data.bounding_box_overlays.take()) {
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
(Some((bounds, transform)), paths) => {
@ -173,11 +173,11 @@ impl Fsm for ArtboardToolFsmState {
let artboard = tool_data.selected_artboard.unwrap();
tool_data
.snap_manager
.start_snap(document, input, document.bounding_boxes(None, Some(artboard), font_cache), snap_x, snap_y);
.start_snap(document, input, document.bounding_boxes(None, Some(artboard), render_data), snap_x, snap_y);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
let pivot = document.artboard_message_handler.artboards_document.pivot(&[artboard], font_cache).unwrap_or_default();
let pivot = document.artboard_message_handler.artboards_document.pivot(&[artboard], render_data).unwrap_or_default();
let root = document.document_legacy.root.transform;
let pivot = root.inverse().transform_point2(pivot);
bounds.center_of_transformation = pivot;
@ -188,7 +188,7 @@ impl Fsm for ArtboardToolFsmState {
responses.push_back(DocumentMessage::StartTransaction.into());
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
let intersection = document.artboard_message_handler.artboards_document.intersects_quad_root(quad, font_cache);
let intersection = document.artboard_message_handler.artboards_document.intersects_quad_root(quad, render_data);
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
if let Some(intersection) = intersection.last() {
@ -196,7 +196,7 @@ impl Fsm for ArtboardToolFsmState {
tool_data
.snap_manager
.start_snap(document, input, document.bounding_boxes(None, Some(intersection[0]), font_cache), true, true);
.start_snap(document, input, document.bounding_boxes(None, Some(intersection[0]), render_data), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
responses.push_back(
@ -304,7 +304,7 @@ impl Fsm for ArtboardToolFsmState {
let id = generate_uuid();
tool_data.selected_artboard = Some(id);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, Some(id), font_cache), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, Some(id), render_data), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
responses.push_back(

View file

@ -100,7 +100,7 @@ impl Fsm for EllipseToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -112,7 +112,7 @@ impl Fsm for EllipseToolFsmState {
if let ToolMessage::Ellipse(event) = event {
match (self, event) {
(Ready, DragStart) => {
shape_data.start(responses, document, input, font_cache);
shape_data.start(responses, document, input, render_data);
responses.push_back(DocumentMessage::StartTransaction.into());
shape_data.path = Some(document.get_path_for_new_layer());
responses.push_back(DocumentMessage::DeselectAllLayers.into());

View file

@ -87,7 +87,7 @@ impl Fsm for EyedropperToolFsmState {
self,
event: ToolMessage,
_tool_data: &mut Self::ToolData,
(_document, _document_id, global_tool_data, input, _font_cache): ToolActionHandlerData,
(_document, _document_id, global_tool_data, input, _render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {

View file

@ -84,7 +84,7 @@ impl Fsm for FillToolFsmState {
self,
event: ToolMessage,
_tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -98,7 +98,7 @@ impl Fsm for FillToolFsmState {
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
if let Some(path) = document.document_legacy.intersects_quad_root(quad, font_cache).last() {
if let Some(path) = document.document_legacy.intersects_quad_root(quad, render_data).last() {
let color = match lmb_or_rmb {
LeftPointerDown => global_tool_data.primary_color,
RightPointerDown => global_tool_data.secondary_color,

View file

@ -137,7 +137,7 @@ impl Fsm for FreehandToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, _font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, _render_data): ToolActionHandlerData,
tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {

View file

@ -12,11 +12,10 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use document_legacy::color::Color;
use document_legacy::intersection::Quad;
use document_legacy::layers::layer_info::Layer;
use document_legacy::layers::style::{Fill, Gradient, GradientType, PathStyle, Stroke};
use document_legacy::layers::style::{Fill, Gradient, GradientType, PathStyle, RenderData, Stroke};
use document_legacy::LayerId;
use document_legacy::Operation;
use document_legacy::layers::text_layer::FontCache;
use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize};
@ -130,8 +129,8 @@ enum GradientToolFsmState {
}
/// Computes the transform from gradient space to layer space (where gradient space is 0..1 in layer space)
fn gradient_space_transform(path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, font_cache: &FontCache) -> DAffine2 {
let bounds = layer.aabb_for_transform(DAffine2::IDENTITY, font_cache).unwrap();
fn gradient_space_transform(path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, render_data: &RenderData) -> DAffine2 {
let bounds = layer.aabb_for_transform(DAffine2::IDENTITY, render_data).unwrap();
let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
let multiplied = document.document_legacy.multiply_transforms(path).unwrap();
@ -195,9 +194,9 @@ impl GradientOverlay {
layer: &Layer,
document: &DocumentMessageHandler,
responses: &mut VecDeque<Message>,
font_cache: &FontCache,
render_data: &RenderData,
) -> Self {
let transform = gradient_space_transform(path, layer, document, font_cache);
let transform = gradient_space_transform(path, layer, document, render_data);
let Gradient { start, end, positions, .. } = fill;
let [start, end] = [transform.transform_point2(*start), transform.transform_point2(*end)];
@ -261,8 +260,8 @@ struct SelectedGradient {
}
impl SelectedGradient {
pub fn new(gradient: Gradient, path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, font_cache: &FontCache) -> Self {
let transform = gradient_space_transform(path, layer, document, font_cache);
pub fn new(gradient: Gradient, path: &[LayerId], layer: &Layer, document: &DocumentMessageHandler, render_data: &RenderData) -> Self {
let transform = gradient_space_transform(path, layer, document, render_data);
Self {
path: path.to_vec(),
transform,
@ -272,7 +271,7 @@ impl SelectedGradient {
}
/// Update the selected gradient, checking for removal or change of gradient.
pub fn update(gradient: &mut Option<Self>, document: &DocumentMessageHandler, font_cache: &FontCache, responses: &mut VecDeque<Message>) {
pub fn update(gradient: &mut Option<Self>, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
let Some(inner_gradient) = gradient else {
return;
};
@ -285,7 +284,7 @@ impl SelectedGradient {
};
// Update transform
inner_gradient.transform = gradient_space_transform(&inner_gradient.path, layer, document, font_cache);
inner_gradient.transform = gradient_space_transform(&inner_gradient.path, layer, document, render_data);
// Clear if no longer a gradient
let Some(gradient) = layer.style().ok().and_then(|style|style.fill().as_gradient()) else {
@ -384,8 +383,8 @@ struct GradientToolData {
drag_start: DVec2,
}
pub fn start_snap(snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, font_cache: &FontCache) {
snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true);
pub fn start_snap(snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, render_data: &RenderData) {
snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
}
@ -397,7 +396,7 @@ impl Fsm for GradientToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -409,7 +408,7 @@ impl Fsm for GradientToolFsmState {
}
if self != GradientToolFsmState::Drawing {
SelectedGradient::update(&mut tool_data.selected_gradient, document, font_cache, responses);
SelectedGradient::update(&mut tool_data.selected_gradient, document, render_data, responses);
}
for path in document.selected_visible_layers() {
@ -423,7 +422,9 @@ impl Fsm for GradientToolFsmState {
.selected_gradient
.as_ref()
.and_then(|selected| if selected.path == path { Some(selected.dragging) } else { None });
tool_data.gradient_overlays.push(GradientOverlay::new(gradient, dragging, path, layer, document, responses, font_cache))
tool_data
.gradient_overlays
.push(GradientOverlay::new(gradient, dragging, path, layer, document, responses, render_data))
}
}
@ -493,7 +494,7 @@ impl Fsm for GradientToolFsmState {
let layer = document.document_legacy.layer(&overlay.path);
if let Ok(layer) = layer {
let mut selected_gradient = SelectedGradient::new(gradient, &overlay.path, layer, document, font_cache);
let mut selected_gradient = SelectedGradient::new(gradient, &overlay.path, layer, document, render_data);
// Select the new point
selected_gradient.dragging = GradientDragTarget::Step(index);
@ -541,7 +542,7 @@ impl Fsm for GradientToolFsmState {
] {
if pos.distance_squared(mouse) < tolerance {
dragging = true;
start_snap(&mut tool_data.snap_manager, document, input, font_cache);
start_snap(&mut tool_data.snap_manager, document, input, render_data);
tool_data.selected_gradient = Some(SelectedGradient {
path: overlay.path.clone(),
transform: overlay.transform,
@ -557,7 +558,7 @@ impl Fsm for GradientToolFsmState {
} else {
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
let quad = Quad::from_box([input.mouse.position - tolerance, input.mouse.position + tolerance]);
let intersection = document.document_legacy.intersects_quad_root(quad, font_cache).pop();
let intersection = document.document_legacy.intersects_quad_root(quad, render_data).pop();
if let Some(intersection) = intersection {
if !document.selected_layers_contains(&intersection) {
@ -585,11 +586,11 @@ impl Fsm for GradientToolFsmState {
tool_options.gradient_type,
)
};
let selected_gradient = SelectedGradient::new(gradient, &intersection, layer, document, font_cache).with_gradient_start(input.mouse.position);
let selected_gradient = SelectedGradient::new(gradient, &intersection, layer, document, render_data).with_gradient_start(input.mouse.position);
tool_data.selected_gradient = Some(selected_gradient);
start_snap(&mut tool_data.snap_manager, document, input, font_cache);
start_snap(&mut tool_data.snap_manager, document, input, render_data);
GradientToolFsmState::Drawing
} else {

View file

@ -100,7 +100,7 @@ impl Fsm for ImaginateToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, _global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, _global_tool_data, input, render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -112,7 +112,7 @@ impl Fsm for ImaginateToolFsmState {
if let ToolMessage::Imaginate(event) = event {
match (self, event) {
(Ready, DragStart) => {
shape_data.start(responses, document, input, font_cache);
shape_data.start(responses, document, input, render_data);
responses.push_back(DocumentMessage::StartTransaction.into());
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: true }.into());
shape_data.path = Some(document.get_path_for_new_layer());

View file

@ -137,7 +137,7 @@ impl Fsm for LineToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -147,7 +147,7 @@ impl Fsm for LineToolFsmState {
if let ToolMessage::Line(event) = event {
match (self, event) {
(Ready, DragStart) => {
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
tool_data.drag_start = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);

View file

@ -105,7 +105,7 @@ impl Fsm for NavigateToolFsmState {
self,
message: ToolMessage,
tool_data: &mut Self::ToolData,
(_document, _document_id, _global_tool_data, input, _font_cache): ToolActionHandlerData,
(_document, _document_id, _global_tool_data, input, _render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
messages: &mut VecDeque<Message>,
) -> Self {

View file

@ -99,7 +99,7 @@ impl Fsm for NodeGraphToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, _global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, _global_tool_data, input, render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -111,7 +111,7 @@ impl Fsm for NodeGraphToolFsmState {
if let ToolMessage::NodeGraphFrame(event) = event {
match (self, event) {
(Ready, DragStart) => {
shape_data.start(responses, document, input, font_cache);
shape_data.start(responses, document, input, render_data);
responses.push_back(DocumentMessage::StartTransaction.into());
responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: true }.into());
shape_data.path = Some(document.get_path_for_new_layer());

View file

@ -120,7 +120,7 @@ impl Fsm for PathToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, _global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, _global_tool_data, input, render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -166,7 +166,7 @@ impl Fsm for PathToolFsmState {
let ignore_document = tool_data.shape_editor.selected_layers().clone();
tool_data
.snap_manager
.start_snap(document, input, document.bounding_boxes(Some(&ignore_document), None, font_cache), true, true);
.start_snap(document, input, document.bounding_boxes(Some(&ignore_document), None, render_data), true, true);
// Do not snap against handles when anchor is selected
let mut extension = Vec::new();
@ -196,7 +196,7 @@ impl Fsm for PathToolFsmState {
// Select shapes directly under our mouse
let intersection = document
.document_legacy
.intersects_quad_root(Quad::from_box([input.mouse.position - selection_size, input.mouse.position + selection_size]), font_cache);
.intersects_quad_root(Quad::from_box([input.mouse.position - selection_size, input.mouse.position + selection_size]), render_data);
if !intersection.is_empty() {
if toggle_add_to_selection {
responses.push_back(DocumentMessage::AddSelectedLayers { additional_layers: intersection }.into());

View file

@ -158,7 +158,7 @@ impl Fsm for PenToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -190,7 +190,7 @@ impl Fsm for PenToolFsmState {
responses.push_back(DocumentMessage::StartTransaction.into());
// Initialize snapping
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
// Disable this tool's mirroring

View file

@ -100,7 +100,7 @@ impl Fsm for RectangleToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -112,7 +112,7 @@ impl Fsm for RectangleToolFsmState {
if let ToolMessage::Rectangle(event) = event {
match (self, event) {
(Ready, DragStart) => {
shape_data.start(responses, document, input, font_cache);
shape_data.start(responses, document, input, render_data);
responses.push_back(DocumentMessage::StartTransaction.into());
shape_data.path = Some(document.get_path_for_new_layer());
responses.push_back(DocumentMessage::DeselectAllLayers.into());

View file

@ -285,7 +285,7 @@ impl Fsm for SelectToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, _global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, _global_tool_data, input, render_data): ToolActionHandlerData,
_tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -295,7 +295,7 @@ impl Fsm for SelectToolFsmState {
if let ToolMessage::Select(event) = event {
match (self, event) {
(_, DocumentIsDirty | SelectionChanged) => {
match (document.selected_visible_layers_bounding_box(font_cache), tool_data.bounding_box_overlays.take()) {
match (document.selected_visible_layers_bounding_box(render_data), tool_data.bounding_box_overlays.take()) {
(None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses),
(Some(bounds), paths) => {
let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses));
@ -310,9 +310,9 @@ impl Fsm for SelectToolFsmState {
(_, _) => {}
};
tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, font_cache);
tool_data.path_outlines.intersect_test_hovered(input, document, responses, font_cache);
tool_data.pivot.update_pivot(document, font_cache, responses);
tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data);
tool_data.path_outlines.intersect_test_hovered(input, document, responses, render_data);
tool_data.pivot.update_pivot(document, render_data, responses);
self
}
@ -325,7 +325,7 @@ impl Fsm for SelectToolFsmState {
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
// Check the last (top most) intersection layer.
if let Some(intersect_layer_path) = document.document_legacy.intersects_quad_root(quad, font_cache).last() {
if let Some(intersect_layer_path) = document.document_legacy.intersects_quad_root(quad, render_data).last() {
if let Ok(intersect) = document.document_legacy.layer(intersect_layer_path) {
match intersect.data {
LayerDataType::Text(_) => {
@ -376,7 +376,7 @@ impl Fsm for SelectToolFsmState {
let mut selected: Vec<_> = document.selected_visible_layers().map(|path| path.to_vec()).collect();
let quad = tool_data.selection_quad();
let mut intersection = document.document_legacy.intersects_quad_root(quad, font_cache);
let mut intersection = document.document_legacy.intersects_quad_root(quad, render_data);
// If the user is dragging the bounding box bounds, go into ResizingBounds mode.
// If the user is dragging the rotate trigger, go into RotatingBounds mode.
// If the user clicks on a layer that is in their current selection, go into the dragging mode.
@ -385,7 +385,7 @@ impl Fsm for SelectToolFsmState {
let state = if tool_data.pivot.is_over(input.mouse.position) {
responses.push_back(DocumentMessage::StartTransaction.into());
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
DraggingPivot
@ -397,7 +397,7 @@ impl Fsm for SelectToolFsmState {
tool_data
.snap_manager
.start_snap(document, input, document.bounding_boxes(Some(&selected), None, font_cache), snap_x, snap_y);
.start_snap(document, input, document.bounding_boxes(Some(&selected), None, render_data), snap_x, snap_y);
tool_data
.snap_manager
.add_all_document_handles(document, input, &[], &selected.iter().map(|x| x.as_slice()).collect::<Vec<_>>(), &[]);
@ -409,7 +409,7 @@ impl Fsm for SelectToolFsmState {
let selected = &tool_data.layers_dragging.iter().collect::<Vec<_>>();
let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.center_of_transformation, selected, responses, document);
bounds.center_of_transformation = selected.mean_average_of_pivots(font_cache);
bounds.center_of_transformation = selected.mean_average_of_pivots(render_data);
}
ResizingBounds
@ -420,7 +420,7 @@ impl Fsm for SelectToolFsmState {
let selected = selected.iter().collect::<Vec<_>>();
let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.center_of_transformation, &selected, responses, &document.document_legacy);
bounds.center_of_transformation = selected.mean_average_of_pivots(font_cache);
bounds.center_of_transformation = selected.mean_average_of_pivots(render_data);
}
tool_data.layers_dragging = selected;
@ -433,7 +433,7 @@ impl Fsm for SelectToolFsmState {
tool_data
.snap_manager
.start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true);
.start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, render_data), true, true);
Dragging
} else {
@ -449,7 +449,7 @@ impl Fsm for SelectToolFsmState {
tool_data.layers_dragging.append(&mut selected);
tool_data
.snap_manager
.start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true);
.start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, render_data), true, true);
Dragging
} else {
@ -472,7 +472,7 @@ impl Fsm for SelectToolFsmState {
let snap = tool_data
.layers_dragging
.iter()
.filter_map(|path| document.document_legacy.viewport_bounding_box(path, font_cache).ok()?)
.filter_map(|path| document.document_legacy.viewport_bounding_box(path, render_data).ok()?)
.flat_map(snapping::expand_bounds)
.collect();
@ -546,7 +546,7 @@ impl Fsm for SelectToolFsmState {
(DraggingPivot, PointerMove { .. }) => {
let mouse_position = input.mouse.position;
let snapped_mouse_position = tool_data.snap_manager.snap_position(responses, document, mouse_position);
tool_data.pivot.set_viewport_position(snapped_mouse_position, document, font_cache, responses);
tool_data.pivot.set_viewport_position(snapped_mouse_position, document, render_data, responses);
DraggingPivot
}
@ -575,7 +575,7 @@ impl Fsm for SelectToolFsmState {
// Generate the select outline (but not if the user is going to use the bound overlays)
if cursor == MouseCursorIcon::Default {
tool_data.path_outlines.intersect_test_hovered(input, document, responses, font_cache);
tool_data.path_outlines.intersect_test_hovered(input, document, responses, render_data);
} else {
tool_data.path_outlines.clear_hovered(responses);
}
@ -639,7 +639,7 @@ impl Fsm for SelectToolFsmState {
let quad = tool_data.selection_quad();
responses.push_front(
DocumentMessage::AddSelectedLayers {
additional_layers: document.document_legacy.intersects_quad_root(quad, font_cache),
additional_layers: document.document_legacy.intersects_quad_root(quad, render_data),
}
.into(),
);
@ -708,7 +708,7 @@ impl Fsm for SelectToolFsmState {
responses.push_back(DocumentMessage::StartTransaction.into());
let pos: Option<DVec2> = position.into();
tool_data.pivot.set_normalized_position(pos.unwrap(), document, font_cache, responses);
tool_data.pivot.set_normalized_position(pos.unwrap(), document, render_data, responses);
self
}

View file

@ -139,7 +139,7 @@ impl Fsm for ShapeToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -151,7 +151,7 @@ impl Fsm for ShapeToolFsmState {
if let ToolMessage::Shape(event) = event {
match (self, event) {
(Ready, DragStart) => {
shape_data.start(responses, document, input, font_cache);
shape_data.start(responses, document, input, render_data);
responses.push_back(DocumentMessage::StartTransaction.into());
shape_data.path = Some(document.get_path_for_new_layer());
responses.push_back(DocumentMessage::DeselectAllLayers.into());

View file

@ -146,7 +146,7 @@ impl Fsm for SplineToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -162,7 +162,7 @@ impl Fsm for SplineToolFsmState {
responses.push_back(DocumentMessage::DeselectAllLayers.into());
tool_data.path = Some(document.get_path_for_new_layer());
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, render_data), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);

View file

@ -10,8 +10,7 @@ use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHan
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
use document_legacy::intersection::Quad;
use document_legacy::layers::style::{self, Fill, Stroke};
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::{self, Fill, RenderData, Stroke};
use document_legacy::LayerId;
use document_legacy::Operation;
@ -213,7 +212,7 @@ fn resize_overlays(overlays: &mut Vec<Vec<LayerId>>, responses: &mut VecDeque<Me
}
}
fn update_overlays(document: &DocumentMessageHandler, tool_data: &mut TextToolData, responses: &mut VecDeque<Message>, font_cache: &FontCache) {
fn update_overlays(document: &DocumentMessageHandler, tool_data: &mut TextToolData, responses: &mut VecDeque<Message>, render_data: &RenderData) {
let visible_text_layers = document.selected_visible_text_layers().collect::<Vec<_>>();
resize_overlays(&mut tool_data.overlays, responses, visible_text_layers.len());
@ -225,7 +224,7 @@ fn update_overlays(document: &DocumentMessageHandler, tool_data: &mut TextToolDa
.document_legacy
.layer(layer_path)
.unwrap()
.aabb_for_transform(document.document_legacy.multiply_transforms(layer_path).unwrap(), font_cache)
.aabb_for_transform(document.document_legacy.multiply_transforms(layer_path).unwrap(), render_data)
.map(|bounds| (bounds, overlay_path))
})
.collect::<Vec<_>>();
@ -250,7 +249,7 @@ impl Fsm for TextToolFsmState {
self,
event: ToolMessage,
tool_data: &mut Self::ToolData,
(document, _document_id, global_tool_data, input, font_cache): ToolActionHandlerData,
(document, _document_id, global_tool_data, input, render_data): ToolActionHandlerData,
tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>,
) -> Self {
@ -260,7 +259,7 @@ impl Fsm for TextToolFsmState {
if let ToolMessage::Text(event) = event {
match (self, event) {
(state, DocumentIsDirty) => {
update_overlays(document, tool_data, responses, font_cache);
update_overlays(document, tool_data, responses, render_data);
state
}
@ -272,7 +271,7 @@ impl Fsm for TextToolFsmState {
// Check if the user has selected an existing text layer
let new_state = if let Some(clicked_text_layer_path) = document
.document_legacy
.intersects_quad_root(quad, font_cache)
.intersects_quad_root(quad, render_data)
.last()
.filter(|l| document.document_legacy.layer(l).map(|l| l.as_text().is_ok()).unwrap_or(false))
{
@ -367,7 +366,7 @@ impl Fsm for TextToolFsmState {
(Editing, UpdateBounds { new_text }) => {
resize_overlays(&mut tool_data.overlays, responses, 1);
let text = document.document_legacy.layer(&tool_data.layer_path).unwrap().as_text().unwrap();
let quad = text.bounding_box(&new_text, text.load_face(font_cache));
let quad = text.bounding_box(&new_text, text.load_face(render_data));
let transformed_quad = document.document_legacy.multiply_transforms(&tool_data.layer_path).unwrap() * quad;
let bounds = transformed_quad.bounding_box();

View file

@ -10,12 +10,12 @@ use crate::messages::layout::utility_types::widgets::label_widgets::{Separator,
use crate::messages::prelude::*;
use document_legacy::color::Color;
use document_legacy::layers::text_layer::FontCache;
use document_legacy::layers::style::RenderData;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Debug};
pub type ToolActionHandlerData<'a> = (&'a DocumentMessageHandler, u64, &'a DocumentToolData, &'a InputPreprocessorMessageHandler, &'a FontCache);
pub type ToolActionHandlerData<'a> = (&'a DocumentMessageHandler, u64, &'a DocumentToolData, &'a InputPreprocessorMessageHandler, &'a RenderData<'a>);
pub trait ToolCommon: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder + ToolTransition + ToolMetadata {}
impl<T> ToolCommon for T where T: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder + ToolTransition + ToolMetadata {}