use crate::boolean_ops::BooleanOperation as BooleanOperationType; use crate::layers::blend_mode::BlendMode; use crate::layers::layer_info::Layer; use crate::layers::style::{self, Stroke}; use crate::LayerId; use graphene_std::vector::consts::ManipulatorType; use graphene_std::vector::manipulator_group::ManipulatorGroup; use graphene_std::vector::subpath::Subpath; use serde::{Deserialize, Serialize}; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; #[repr(C)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, specta::Type)] // TODO: Rename all instances of `path` to `layer_path` /// Operations that can be performed to mutate the document. pub enum Operation { AddEllipse { path: Vec, insert_index: isize, transform: [f64; 6], style: style::PathStyle, }, AddRect { path: Vec, insert_index: isize, transform: [f64; 6], style: style::PathStyle, }, AddLine { path: Vec, insert_index: isize, transform: [f64; 6], style: style::PathStyle, }, AddText { path: Vec, insert_index: isize, transform: [f64; 6], style: style::PathStyle, text: String, size: f64, font_name: String, font_style: String, }, AddNodeGraphFrame { path: Vec, insert_index: isize, transform: [f64; 6], network: graph_craft::document::NodeNetwork, }, SetNodeGraphFrameImageData { layer_path: Vec, image_data: Vec, }, /// Sets a blob URL as the image source for an Image or Imaginate layer type. /// **Be sure to call `FrontendMessage::TriggerRevokeBlobUrl` together with this.** SetLayerBlobUrl { layer_path: Vec, blob_url: String, resolution: (f64, f64), }, /// Clears the image to leave the layer un-rendered. /// **Be sure to call `FrontendMessage::TriggerRevokeBlobUrl` together with this.** ClearBlobURL { path: Vec, }, SetPivot { layer_path: Vec, pivot: (f64, f64), }, SetTextEditability { path: Vec, editable: bool, }, SetTextContent { path: Vec, new_text: String, }, AddPolyline { path: Vec, insert_index: isize, transform: [f64; 6], style: style::PathStyle, points: Vec<(f64, f64)>, }, AddSpline { path: Vec, insert_index: isize, transform: [f64; 6], style: style::PathStyle, points: Vec<(f64, f64)>, }, AddNgon { path: Vec, insert_index: isize, transform: [f64; 6], style: style::PathStyle, sides: u32, }, AddShape { path: Vec, insert_index: isize, transform: [f64; 6], style: style::PathStyle, // TODO This will become a compound path once we support them. subpath: Subpath, }, BooleanOperation { operation: BooleanOperationType, selected: Vec>, }, DeleteLayer { path: Vec, }, DeleteSelectedManipulatorPoints { layer_paths: Vec>, }, DeselectManipulatorPoints { layer_path: Vec, point_ids: Vec<(u64, ManipulatorType)>, }, DeselectAllManipulatorPoints { layer_path: Vec, }, SelectAllAnchors { layer_path: Vec, }, DuplicateLayer { path: Vec, }, ModifyFont { path: Vec, font_family: String, size: f64, font_style: String, }, MoveSelectedManipulatorPoints { layer_path: Vec, delta: (f64, f64), mirror_distance: bool, }, MoveManipulatorPoint { layer_path: Vec, id: u64, manipulator_type: ManipulatorType, position: (f64, f64), }, SetManipulatorPoints { layer_path: Vec, id: u64, manipulator_type: ManipulatorType, position: Option<(f64, f64)>, }, RenameLayer { layer_path: Vec, new_name: String, }, InsertLayer { layer: Box, destination_path: Vec, insert_index: isize, }, CreateFolder { path: Vec, }, TransformLayer { path: Vec, transform: [f64; 6], }, TransformLayerInViewport { path: Vec, transform: [f64; 6], }, SetLayerTransformInViewport { path: Vec, transform: [f64; 6], }, SelectManipulatorPoints { layer_path: Vec, point_ids: Vec<(u64, ManipulatorType)>, add: bool, }, SetShapePath { path: Vec, subpath: Subpath, }, InsertManipulatorGroup { layer_path: Vec, manipulator_group: ManipulatorGroup, after_id: u64, }, PushManipulatorGroup { layer_path: Vec, manipulator_group: ManipulatorGroup, }, PushFrontManipulatorGroup { layer_path: Vec, manipulator_group: ManipulatorGroup, }, RemoveManipulatorGroup { layer_path: Vec, id: u64, }, RemoveManipulatorPoint { layer_path: Vec, id: u64, manipulator_type: ManipulatorType, }, TransformLayerInScope { path: Vec, transform: [f64; 6], scope: [f64; 6], }, SetLayerTransformInScope { path: Vec, transform: [f64; 6], scope: [f64; 6], }, TransformLayerScaleAroundPivot { path: Vec, scale_factor: (f64, f64), }, SetLayerScaleAroundPivot { path: Vec, new_scale: (f64, f64), }, SetLayerTransform { path: Vec, transform: [f64; 6], }, ToggleLayerVisibility { path: Vec, }, SetLayerVisibility { path: Vec, visible: bool, }, SetLayerName { path: Vec, name: String, }, SetLayerPreserveAspect { layer_path: Vec, preserve_aspect: bool, }, SetLayerBlendMode { path: Vec, blend_mode: BlendMode, }, SetLayerOpacity { path: Vec, opacity: f64, }, SetLayerStyle { path: Vec, style: style::PathStyle, }, SetLayerFill { path: Vec, fill: style::Fill, }, SetLayerStroke { path: Vec, stroke: Stroke, }, SetManipulatorHandleMirroring { layer_path: Vec, id: u64, mirror_angle: bool, }, SetSelectedHandleMirroring { layer_path: Vec, toggle_angle: bool, }, } impl Operation { /// Returns the byte representation of the message. /// /// # Safety /// This function reads from uninitialized memory!!! /// Only use if you know what you are doing unsafe fn as_slice(&self) -> &[u8] { core::slice::from_raw_parts(self as *const Operation as *const u8, std::mem::size_of::()) } /// Returns a pseudo hash that should uniquely identify the operation. /// This is needed because `Hash` is not implemented for f64s /// /// # Safety /// This function reads from uninitialized memory but the generated value should be fine. pub fn pseudo_hash(&self) -> u64 { let mut s = DefaultHasher::new(); unsafe { self.as_slice() }.hash(&mut s); s.finish() } }