From d6ab417bcb6f95a7cc1c4815f14be9a12a3e5a50 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sun, 26 Mar 2023 11:49:25 -0700 Subject: [PATCH] Code review previous commit, and improve G/R/S hints --- .deploy/deploy.sh | 3 +- .../node_graph/graph_operation_message.rs | 3 +- .../graph_operation_message_handler.rs | 23 +++++--- .../transform_utils.rs | 59 ++++++++++++------- .../node_properties.rs | 2 +- .../document/utility_types/transformation.rs | 34 ++++++++--- .../graph_modification_utils.rs | 3 +- .../common_functionality/overlay_renderer.rs | 14 +++-- .../tool/common_functionality/shape_editor.rs | 8 +-- .../src/messages/tool/tool_message_handler.rs | 2 +- .../transform_layer_message_handler.rs | 25 +++++--- editor/src/node_graph_executor.rs | 2 +- libraries/bezier-rs/src/bezier/solvers.rs | 16 ++--- .../bezier-rs/src/subpath/manipulators.rs | 6 +- node-graph/gcore/Cargo.toml | 1 - .../gcore/src/vector/generator_nodes.rs | 3 +- node-graph/gcore/src/vector/subpath.rs | 3 +- node-graph/gcore/src/vector/vector_data.rs | 11 ++-- 18 files changed, 135 insertions(+), 83 deletions(-) diff --git a/.deploy/deploy.sh b/.deploy/deploy.sh index 54de60fb..4b81d693 100644 --- a/.deploy/deploy.sh +++ b/.deploy/deploy.sh @@ -30,5 +30,4 @@ cargo install cargo-about # Build for production echo 👷 Build Graphite web client export NODE_ENV=production -npm run build -mv public dist +npm run build && mv public dist # `&&` is used here to preserve the exit code diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs index 836c8867..bfec6d31 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message.rs @@ -1,10 +1,11 @@ use crate::messages::prelude::*; -use glam::{DAffine2, DVec2}; use graphene_core::uuid::ManipulatorGroupId; use graphene_core::vector::style::{Fill, Stroke}; use graphene_core::vector::ManipulatorPointId; +use glam::{DAffine2, DVec2}; + pub type LayerIdentifier = Vec; #[impl_message(Message, DocumentMessage, GraphOperation)] diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs index 80e0d2f1..3be3e0ab 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs @@ -1,14 +1,14 @@ +use super::{resolve_document_node_type, VectorDataModification}; use crate::messages::prelude::*; use document_legacy::document::Document; use document_legacy::{LayerId, Operation}; -use glam::{DAffine2, DVec2}; use graph_craft::document::value::TaggedValue; use graph_craft::document::{generate_uuid, NodeId, NodeInput, NodeNetwork}; use graphene_core::vector::style::{Fill, FillType, Stroke}; use transform_utils::LayerBounds; -use super::{resolve_document_node_type, VectorDataModification}; +use glam::{DAffine2, DVec2}; mod transform_utils; @@ -37,7 +37,8 @@ impl<'a> ModifyInputsContext<'a> { let document_node = self.network.nodes.get_mut(&node_id).unwrap(); update_input(&mut document_node.inputs); } - /// Insert a new node and modify the inputs + + /// Inserts a new node and modifies the inputs fn modify_new_node(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec)) { let output_node_id = self.network.outputs[0].node_id; let Some(output_node) = self.network.nodes.get_mut(&output_node_id) else { @@ -73,6 +74,7 @@ impl<'a> ModifyInputsContext<'a> { let layer_path = self.layer.to_vec(); self.responses.add(DocumentMessage::NodeGraphFrameGenerate { layer_path }); } + fn fill_set(&mut self, fill: Fill) { self.modify_inputs("Fill", |inputs| { let fill_type = match fill { @@ -94,6 +96,7 @@ impl<'a> ModifyInputsContext<'a> { } }); } + fn stroke_set(&mut self, stroke: Stroke) { self.modify_inputs("Stroke", |inputs| { inputs[1] = NodeInput::value(TaggedValue::Color(stroke.color.unwrap_or_default()), false); @@ -114,11 +117,12 @@ impl<'a> ModifyInputsContext<'a> { TransformIn::Scope { scope } => scope * parent_transform, TransformIn::Viewport => parent_transform, }; - let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalised_pivot(inputs))); + let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs))); let transform = to.inverse() * pivot.inverse() * transform * pivot * to * layer_transform; transform_utils::update_transform(inputs, transform); }); } + fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, bounds: LayerBounds) { self.modify_inputs("Transform", |inputs| { let to = match transform_in { @@ -126,15 +130,16 @@ impl<'a> ModifyInputsContext<'a> { TransformIn::Scope { scope } => scope * parent_transform, TransformIn::Viewport => parent_transform, }; - let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalised_pivot(inputs))); + let pivot = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs))); let transform = to.inverse() * pivot.inverse() * transform * pivot; transform_utils::update_transform(inputs, transform); }); } + fn pivot_set(&mut self, new_pivot: DVec2, bounds: LayerBounds) { self.modify_inputs("Transform", |inputs| { let layer_transform = transform_utils::get_current_transform(inputs); - let old_pivot_transform = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalised_pivot(inputs))); + let old_pivot_transform = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs))); let new_pivot_transform = DAffine2::from_translation(bounds.local_pivot(new_pivot)); let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform; transform_utils::update_transform(inputs, transform); @@ -172,10 +177,10 @@ impl<'a> ModifyInputsContext<'a> { }); self.modify_inputs("Transform", |inputs| { let layer_transform = transform_utils::get_current_transform(inputs); - let normalised_pivot = transform_utils::get_current_normalised_pivot(inputs); + let normalized_pivot = transform_utils::get_current_normalized_pivot(inputs); - let old_layerspace_pivot = (old_bounds_max - old_bounds_min) * normalised_pivot + old_bounds_min; - let new_layerspace_pivot = (new_bounds_max - new_bounds_min) * normalised_pivot + new_bounds_min; + let old_layerspace_pivot = (old_bounds_max - old_bounds_min) * normalized_pivot + old_bounds_min; + let new_layerspace_pivot = (new_bounds_max - new_bounds_min) * normalized_pivot + new_bounds_min; let new_pivot_transform = DAffine2::from_translation(new_layerspace_pivot); let old_pivot_transform = DAffine2::from_translation(old_layerspace_pivot); diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs index f7fef427..5aa1e03c 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler/transform_utils.rs @@ -6,7 +6,7 @@ use graph_craft::document::{value::TaggedValue, NodeInput}; use graphene_core::uuid::ManipulatorGroupId; use graphene_core::vector::{ManipulatorPointId, SelectedType}; -/// Convert an affine transform into scale angle translation and shear, assuming shear.y = 0. +/// Convert an affine transform into the tuple `(scale, angle, translation, shear)` assuming `shear.y = 0`. pub fn compute_scale_angle_translation_shear(transform: DAffine2) -> (DVec2, f64, DVec2, DVec2) { let x_axis = transform.matrix2.x_axis; let y_axis = transform.matrix2.y_axis; @@ -33,15 +33,15 @@ pub fn compute_scale_angle_translation_shear(transform: DAffine2) -> (DVec2, f64 /// Update the inputs of the transform node to match a new transform pub fn update_transform(inputs: &mut [NodeInput], transform: DAffine2) { - let (scale, angle, translation, skew) = compute_scale_angle_translation_shear(transform); + let (scale, angle, translation, shear) = compute_scale_angle_translation_shear(transform); inputs[1] = NodeInput::value(TaggedValue::DVec2(translation), false); inputs[2] = NodeInput::value(TaggedValue::F64(angle), false); inputs[3] = NodeInput::value(TaggedValue::DVec2(scale), false); - inputs[4] = NodeInput::value(TaggedValue::DVec2(skew), false); + inputs[4] = NodeInput::value(TaggedValue::DVec2(shear), false); } -/// TODO: This should be extracted from the graph at the location of the transform node. +// TODO: This should be extracted from the graph at the location of the transform node. pub struct LayerBounds { pub bounds: [DVec2; 2], pub bounds_transform: DAffine2, @@ -64,11 +64,13 @@ impl LayerBounds { layer_transform, } } - pub fn layerspace_pivot(&self, normalised_pivot: DVec2) -> DVec2 { - self.bounds[0] + (self.bounds[1] - self.bounds[0]) * normalised_pivot + + pub fn layerspace_pivot(&self, normalized_pivot: DVec2) -> DVec2 { + self.bounds[0] + (self.bounds[1] - self.bounds[0]) * normalized_pivot } - pub fn local_pivot(&self, normalised_pivot: DVec2) -> DVec2 { - self.bounds_transform.transform_point2(self.layerspace_pivot(normalised_pivot)) + + pub fn local_pivot(&self, normalized_pivot: DVec2) -> DVec2 { + self.bounds_transform.transform_point2(self.layerspace_pivot(normalized_pivot)) } } @@ -83,6 +85,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 { } else { DVec2::ZERO }; + let angle = if let NodeInput::Value { tagged_value: TaggedValue::F64(angle), .. @@ -92,6 +95,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 { } else { 0. }; + let scale = if let NodeInput::Value { tagged_value: TaggedValue::DVec2(scale), .. @@ -101,6 +105,7 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 { } else { DVec2::ONE }; + let shear = if let NodeInput::Value { tagged_value: TaggedValue::DVec2(shear), .. @@ -110,11 +115,12 @@ pub fn get_current_transform(inputs: &[NodeInput]) -> DAffine2 { } else { DVec2::ZERO }; + DAffine2::from_scale_angle_translation(scale, angle, translation) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.]) } -/// Extract the current normalised pivot from the layer -pub fn get_current_normalised_pivot(inputs: &[NodeInput]) -> DVec2 { +/// Extract the current normalized pivot from the layer +pub fn get_current_normalized_pivot(inputs: &[NodeInput]) -> DVec2 { if let NodeInput::Value { tagged_value: TaggedValue::DVec2(pivot), .. @@ -126,12 +132,16 @@ pub fn get_current_normalised_pivot(inputs: &[NodeInput]) -> DVec2 { } } +/// ![](https://files.keavon.com/-/OptimisticSpotlessTinamou/capture.png) /// -// \begin{bmatrix} -// S_{x}\cos(\theta)-S_{y}\sin(\theta)H_{y} & S_{x}\cos(\theta)H_{x}-S_{y}\sin(\theta) & T_{x}\\ -// S_{x}\sin(\theta)+S_{y}\cos(\theta)H_{y} & S_{x}\sin(\theta)H_{x}+S_{y}\cos(\theta) & T_{y}\\ -// 0 & 0 & 1 -// \end{bmatrix} +/// Source: +/// ```tex +/// \begin{bmatrix} +/// S_{x}\cos(\theta)-S_{y}\sin(\theta)H_{y} & S_{x}\cos(\theta)H_{x}-S_{y}\sin(\theta) & T_{x}\\ +/// S_{x}\sin(\theta)+S_{y}\cos(\theta)H_{y} & S_{x}\sin(\theta)H_{x}+S_{y}\cos(\theta) & T_{y}\\ +/// 0 & 0 & 1 +/// \end{bmatrix} +/// ``` #[test] fn derive_transform() { for shear_x in -10..=10 { @@ -147,19 +157,19 @@ fn derive_transform() { let scale = DVec2::new(scale_x, scale_y); let translate = DVec2::new(5666., 644.); - let origional_transform = DAffine2::from_cols( + let original_transform = DAffine2::from_cols( DVec2::new(scale.x * angle.cos() - scale.y * angle.sin() * shear.y, scale.x * angle.sin() + scale.y * angle.cos() * shear.y), DVec2::new(scale.x * angle.cos() * shear.x - scale.y * angle.sin(), scale.x * angle.sin() * shear.x + scale.y * angle.cos()), translate, ); - let (new_scale, new_angle, new_translation, new_shear) = compute_scale_angle_translation_shear(origional_transform); + let (new_scale, new_angle, new_translation, new_shear) = compute_scale_angle_translation_shear(original_transform); let new_transform = DAffine2::from_scale_angle_translation(new_scale, new_angle, new_translation) * DAffine2::from_cols_array(&[1., new_shear.y, new_shear.x, 1., 0., 0.]); assert!( - new_transform.abs_diff_eq(origional_transform, 1e-10), - "origional_transform {} new_transform {} / scale {} new_scale {} / angle {} new_angle {} / shear {} / new_shear {}", - origional_transform, + new_transform.abs_diff_eq(original_transform, 1e-10), + "original_transform {} new_transform {} / scale {} new_scale {} / angle {} new_angle {} / shear {} / new_shear {}", + original_transform, new_transform, scale, new_scale, @@ -194,7 +204,7 @@ fn subpath_bounds(subpaths: &[Subpath]) -> [DVec2; 2] { .unwrap_or_default() } -/// Returns corners of all subpaths (but expanded to avoid div zero errors) +/// Returns corners of all subpaths (but expanded to avoid division-by-zero errors) pub fn nonzero_subpath_bounds(subpaths: &[Subpath]) -> [DVec2; 2] { let [bounds_min, bounds_max] = subpath_bounds(subpaths); clamp_bounds(bounds_min, bounds_max) @@ -213,6 +223,7 @@ impl<'a> VectorModificationState<'a> { let subpath = &mut self.subpaths[subpath_index]; subpath.insert_manipulator_group(subpath.len(), manipulator_group) } + fn insert(&mut self, manipulator_group: ManipulatorGroup, after_id: ManipulatorGroupId) { for subpath in self.subpaths.iter_mut() { if let Some(index) = subpath.manipulator_index_from_id(after_id) { @@ -221,6 +232,7 @@ impl<'a> VectorModificationState<'a> { } } } + fn remove_group(&mut self, id: ManipulatorGroupId) { for subpath in self.subpaths.iter_mut() { if let Some(index) = subpath.manipulator_index_from_id(id) { @@ -229,6 +241,7 @@ impl<'a> VectorModificationState<'a> { } } } + fn remove_point(&mut self, point: ManipulatorPointId) { for subpath in self.subpaths.iter_mut() { if point.manipulator_type == SelectedType::Anchor { @@ -245,6 +258,7 @@ impl<'a> VectorModificationState<'a> { } } } + fn set_mirror(&mut self, id: ManipulatorGroupId, mirror_angle: bool) { if !mirror_angle { self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id); @@ -252,6 +266,7 @@ impl<'a> VectorModificationState<'a> { self.mirror_angle_groups.push(id); } } + fn toggle_mirror(&mut self, id: ManipulatorGroupId) { if self.mirror_angle_groups.contains(&id) { self.mirror_angle_groups.retain(|&mirrored_id| mirrored_id != id); @@ -259,6 +274,7 @@ impl<'a> VectorModificationState<'a> { self.mirror_angle_groups.push(id); } } + fn set_position(&mut self, point: ManipulatorPointId, position: DVec2) { for subpath in self.subpaths.iter_mut() { if let Some(manipulator) = subpath.manipulator_mut_from_id(point.group) { @@ -285,6 +301,7 @@ impl<'a> VectorModificationState<'a> { } } } + pub fn modify(&mut self, modification: VectorDataModification) { match modification { VectorDataModification::AddEndManipulatorGroup { subpath_index, manipulator_group } => self.insert_end(subpath_index, manipulator_group), diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs index 88cbce86..9c776788 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs @@ -1066,7 +1066,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte widgets.extend_from_slice(&[ WidgetHolder::unrelated_separator(), - DropdownInput::new(entries).selected_index(Some(sampling_method as u32)).tooltip("When selecing a layer in a folder, shallow select will select the parent folder whereas deep select will select the layer. Double clicking in shallow select mode will select the layer.").widget_holder(), + DropdownInput::new(entries).selected_index(Some(sampling_method as u32)).widget_holder(), ]); } LayoutGroup::Row { widgets }.with_tooltip("Algorithm used to generate the image during each sampling step") diff --git a/editor/src/messages/portfolio/document/utility_types/transformation.rs b/editor/src/messages/portfolio/document/utility_types/transformation.rs index 1ae46f44..9b657fd8 100644 --- a/editor/src/messages/portfolio/document/utility_types/transformation.rs +++ b/editor/src/messages/portfolio/document/utility_types/transformation.rs @@ -141,7 +141,7 @@ pub enum TransformOperation { } impl TransformOperation { - pub fn apply_transform_operation(&self, selected: &mut Selected, snapping: bool) { + pub fn apply_transform_operation(&self, selected: &mut Selected, snapping: bool, axis_constraint: Axis) { if self != &TransformOperation::None { let transformation = match self { TransformOperation::Grabbing(translation) => DAffine2::from_translation(translation.to_dvec()), @@ -151,7 +151,7 @@ impl TransformOperation { }; selected.update_transforms(transformation); - self.hints(snapping, selected.responses); + self.hints(snapping, axis_constraint, selected.responses); } } @@ -163,7 +163,7 @@ impl TransformOperation { TransformOperation::Scaling(scale) => scale.constraint.set_or_toggle(axis), }; - self.apply_transform_operation(selected, snapping); + self.apply_transform_operation(selected, snapping, axis); } pub fn handle_typed(&mut self, typed: Option, selected: &mut Selected, snapping: bool) { @@ -174,20 +174,38 @@ impl TransformOperation { TransformOperation::Scaling(scale) => scale.typed_factor = typed, }; - self.apply_transform_operation(selected, snapping); + let axis_constraint = match self { + TransformOperation::Grabbing(grabbing) => grabbing.constraint, + TransformOperation::Scaling(scaling) => scaling.constraint, + _ => Axis::Both, + }; + + self.apply_transform_operation(selected, snapping, axis_constraint); } - pub fn hints(&self, snapping: bool, responses: &mut VecDeque) { + pub fn hints(&self, snapping: bool, axis_constraint: Axis, responses: &mut VecDeque) { use crate::messages::input_mapper::utility_types::input_keyboard::Key; use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo}; let mut hints = Vec::new(); + let axis_str = |vector: DVec2, separate: bool| match axis_constraint { + Axis::Both => { + if separate { + format!("X: {}, Y: {}", vector.x, vector.y) + } else { + vector.x.to_string() + } + } + Axis::X => format!("X: {}", vector.x), + Axis::Y => format!("Y: {}", vector.y), + }; + let value_str = match self { TransformOperation::None => String::new(), - TransformOperation::Grabbing(translation) => format!("Translate X: {} Y: {}", translation.to_dvec().x, translation.to_dvec().y), - TransformOperation::Rotating(rotation) => format!("Rotate {}°", rotation.to_f64(snapping)), - TransformOperation::Scaling(scale) => format!("Scale X: {} Y: {}", scale.to_dvec(snapping).x, scale.to_dvec(snapping).y), + TransformOperation::Grabbing(translation) => format!("Translate {}", axis_str(translation.to_dvec(), true)), + TransformOperation::Rotating(rotation) => format!("Rotate {}°", rotation.to_f64(snapping) * 360. / std::f64::consts::TAU), + TransformOperation::Scaling(scale) => format!("Scale {}", axis_str(scale.to_dvec(snapping), false)), }; hints.push(HintInfo::label(value_str)); hints.push(HintInfo::keys([Key::Shift], "Precision Mode")); diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index f1071adf..8302f2c4 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -3,8 +3,9 @@ use crate::messages::prelude::*; use bezier_rs::Subpath; use document_legacy::{LayerId, Operation}; -use glam::DAffine2; use graphene_core::uuid::ManipulatorGroupId; + +use glam::DAffine2; use std::collections::VecDeque; /// Create a new vector layer from a vector of [`bezier_rs::Subpath`]. diff --git a/editor/src/messages/tool/common_functionality/overlay_renderer.rs b/editor/src/messages/tool/common_functionality/overlay_renderer.rs index ecaeb043..a7a250d5 100644 --- a/editor/src/messages/tool/common_functionality/overlay_renderer.rs +++ b/editor/src/messages/tool/common_functionality/overlay_renderer.rs @@ -1,3 +1,4 @@ +use super::shape_editor::SelectedShapeState; use crate::application::generate_uuid; use crate::consts::VIEWPORT_GRID_ROUNDING_BIAS; use crate::consts::{COLOR_ACCENT, HIDE_HANDLE_DISTANCE, MANIPULATOR_GROUP_MARKER_SIZE, PATH_OUTLINE_WEIGHT}; @@ -9,11 +10,9 @@ use document_legacy::layers::style::{self, Fill, Stroke}; use document_legacy::{LayerId, Operation}; use graphene_core::raster::color::Color; use graphene_core::uuid::ManipulatorGroupId; - -use glam::{DAffine2, DVec2}; use graphene_core::vector::{ManipulatorPointId, SelectedType}; -use super::shape_editor::SelectedShapeState; +use glam::{DAffine2, DVec2}; /// [ManipulatorGroupOverlay]s is the collection of overlays that make up an [ManipulatorGroup] visible in the editor. #[derive(Clone, Debug, Default)] @@ -238,20 +237,25 @@ impl OverlayRenderer { /// Updates the position of the overlays based on the [Subpath] points. fn place_manipulator_group_overlays(manipulator_group: &GraphiteManipulatorGroup, overlays: &mut ManipulatorGroupOverlays, parent_transform: &DAffine2, responses: &mut VecDeque) { let anchor = manipulator_group.anchor; + let mut place_handle_and_line = |handle_position: DVec2, line_overlay: &[LayerId], marker_source: &mut Option>| { let line_vector = parent_transform.transform_point2(anchor) - parent_transform.transform_point2(handle_position); let scale = DVec2::splat(line_vector.length()); let angle = -line_vector.angle_between(DVec2::X); + let translation = (parent_transform.transform_point2(handle_position) + VIEWPORT_GRID_ROUNDING_BIAS).round() + DVec2::splat(0.5); let transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array(); responses.push_back(Self::overlay_transform_message(line_overlay.to_vec(), transform)); let marker_overlay = marker_source.take().unwrap_or_else(|| Self::create_handle_overlay(responses)); + let scale = DVec2::splat(MANIPULATOR_GROUP_MARKER_SIZE); let angle = 0.; let translation = (parent_transform.transform_point2(handle_position) - (scale / 2.) + VIEWPORT_GRID_ROUNDING_BIAS).round(); let transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array(); + responses.push_back(Self::overlay_transform_message(marker_overlay.clone(), transform)); + *marker_source = Some(marker_overlay); }; @@ -259,8 +263,8 @@ impl OverlayRenderer { if let (Some(handle_position), Some(line_overlay)) = (manipulator_group.in_handle, overlays.in_line.as_mut()) { place_handle_and_line(handle_position, line_overlay, &mut overlays.in_handle); } - if let (Some(handle_psoition), Some(line_overlay)) = (manipulator_group.out_handle, overlays.out_line.as_ref()) { - place_handle_and_line(handle_psoition, line_overlay, &mut overlays.out_handle); + if let (Some(handle_position), Some(line_overlay)) = (manipulator_group.out_handle, overlays.out_line.as_ref()) { + place_handle_and_line(handle_position, line_overlay, &mut overlays.out_handle); } // Place the anchor point overlay diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 70511ec7..6407c8d6 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -179,14 +179,14 @@ impl ShapeState { } if mirror_distance && point.manipulator_type != SelectedType::Anchor && vector_data.mirror_angle.contains(&point.group) { - let Some(mut origional_handle_position) = point.manipulator_type.get_position(group) else { continue }; - origional_handle_position += delta; + let Some(mut original_handle_position) = point.manipulator_type.get_position(group) else { continue }; + original_handle_position += delta; let point = ManipulatorPointId::new(point.group, point.manipulator_type.opposite()); if state.is_selected(point) { continue; } - let position = group.anchor - (origional_handle_position - group.anchor); + let position = group.anchor - (original_handle_position - group.anchor); responses.add(GraphOperationMessage::Vector { layer: layer_path.clone(), modification: VectorDataModification::SetManipulatorPosition { point, position }, @@ -363,8 +363,6 @@ impl ShapeState { } /// Find the `t` value along the path segment we have clicked upon, together with that segment ID. - /// - /// Returns a tuple of subpath_index, manipulator_start and `t` as an f64. fn closest_segment(&self, document: &Document, layer_path: &[LayerId], position: glam::DVec2, tolerance: f64) -> Option<(ManipulatorGroupId, ManipulatorGroupId, Bezier, f64)> { let transform = document.generate_transform_relative_to_viewport(layer_path).ok()?; let layer_pos = transform.inverse().transform_point2(position); diff --git a/editor/src/messages/tool/tool_message_handler.rs b/editor/src/messages/tool/tool_message_handler.rs index 6c32129e..741ba3c3 100644 --- a/editor/src/messages/tool/tool_message_handler.rs +++ b/editor/src/messages/tool/tool_message_handler.rs @@ -101,7 +101,7 @@ impl MessageHandler) { - self.transform_operation.hints(self.snap, responses); + let axis_constraint = match self.transform_operation { + TransformOperation::Grabbing(grabbing) => grabbing.constraint, + TransformOperation::Scaling(scaling) => scaling.constraint, + _ => Axis::Both, + }; + self.transform_operation.hints(self.snap, axis_constraint, responses); } } @@ -115,7 +119,7 @@ impl<'a> MessageHandler> for TransformL begin_operation(self.transform_operation, &mut self.typing, &mut self.mouse_position, &mut self.start_mouse); self.transform_operation = TransformOperation::Scaling(Default::default()); - self.transform_operation.apply_transform_operation(&mut selected, self.snap); + self.transform_operation.apply_transform_operation(&mut selected, self.snap, Axis::Both); responses.push_back(BroadcastEvent::DocumentIsDirty.into()); self.original_transforms.clear(); @@ -139,7 +143,12 @@ impl<'a> MessageHandler> for TransformL let new_snap = ipp.keyboard.get(snap_key as usize); if new_snap != self.snap { self.snap = new_snap; - self.transform_operation.apply_transform_operation(&mut selected, self.snap); + let axis_constraint = match self.transform_operation { + TransformOperation::Grabbing(grabbing) => grabbing.constraint, + TransformOperation::Scaling(scaling) => scaling.constraint, + _ => Axis::Both, + }; + self.transform_operation.apply_transform_operation(&mut selected, self.snap, axis_constraint); } if self.typing.digits.is_empty() { @@ -149,8 +158,9 @@ impl<'a> MessageHandler> for TransformL TransformOperation::None => unreachable!(), TransformOperation::Grabbing(translation) => { let change = if self.slow { delta_pos / SLOWING_DIVISOR } else { delta_pos }; + let axis_constraint = translation.constraint; self.transform_operation = TransformOperation::Grabbing(translation.increment_amount(change)); - self.transform_operation.apply_transform_operation(&mut selected, self.snap); + self.transform_operation.apply_transform_operation(&mut selected, self.snap, axis_constraint); } TransformOperation::Rotating(rotation) => { let selected_pivot = selected.mean_average_of_pivots(render_data); @@ -163,7 +173,7 @@ impl<'a> MessageHandler> for TransformL let change = if self.slow { angle / SLOWING_DIVISOR } else { angle }; self.transform_operation = TransformOperation::Rotating(rotation.increment_amount(change)); - self.transform_operation.apply_transform_operation(&mut selected, self.snap); + self.transform_operation.apply_transform_operation(&mut selected, self.snap, Axis::Both); } TransformOperation::Scaling(scale) => { let change = { @@ -175,8 +185,9 @@ impl<'a> MessageHandler> for TransformL }; let change = if self.slow { change / SLOWING_DIVISOR } else { change }; + let axis_constraint = scale.constraint; self.transform_operation = TransformOperation::Scaling(scale.increment_amount(change)); - self.transform_operation.apply_transform_operation(&mut selected, self.snap); + self.transform_operation.apply_transform_operation(&mut selected, self.snap, axis_constraint); } }; } diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 3d4414d8..7c03ef6c 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -245,7 +245,7 @@ impl NodeGraphExecutor { }?; let network = node_graph_frame.network.clone(); - // Special execution path for generating imaginate (as generation requires io from outside node graph) + // Special execution path for generating Imaginate (as generation requires IO from outside node graph) if let Some(imaginate_node) = imaginate_node { responses.push_back(self.generate_imaginate(network, imaginate_node, (document, document_id), layer_path, image_frame, persistent_data)?); return Ok(()); diff --git a/libraries/bezier-rs/src/bezier/solvers.rs b/libraries/bezier-rs/src/bezier/solvers.rs index fa48c455..b0b771f9 100644 --- a/libraries/bezier-rs/src/bezier/solvers.rs +++ b/libraries/bezier-rs/src/bezier/solvers.rs @@ -435,7 +435,7 @@ impl Bezier { let a = self.end.y - self.start.y; let b = self.start.x - self.end.x; let c = a * self.start.x + b * self.start.y; - if (a * target_point.x + b * target_point.y - c) * (resulting_sign as f64) <= 0.0 { + if (a * target_point.x + b * target_point.y - c) * (resulting_sign as f64) <= 0. { resulting_sign } else { 0 @@ -448,14 +448,14 @@ impl Bezier { if target_point.x >= self.start.x.max(self.end.x).max(p1.x) { return resulting_sign; } - let a = self.end.y - 2.0 * p1.y + self.start.y; - let b = 2.0 * (p1.y - self.start.y); + let a = self.end.y - 2. * p1.y + self.start.y; + let b = 2. * (p1.y - self.start.y); let c = self.start.y - target_point.y; let discriminant = b * b - 4. * a * c; let two_times_a = 2. * a; for t in solve_quadratic(discriminant, two_times_a, b, c) { - if (0.0..=1.0).contains(&t) { + if (0.0..=1.).contains(&t) { let x = self.evaluate(TValue::Parametric(t)).x; if target_point.x >= x { return resulting_sign; @@ -473,12 +473,12 @@ impl Bezier { if target_point.x >= self.start.x.max(self.end.x).max(p1.x).max(p2.x) { return resulting_sign; } - let a = self.end.y - 3.0 * p2.y + 3.0 * p1.y - self.start.y; - let b = 3.0 * (p2.y - 2.0 * p1.y + self.start.y); - let c = 3.0 * (p1.y - self.start.y); + let a = self.end.y - 3. * p2.y + 3. * p1.y - self.start.y; + let b = 3. * (p2.y - 2. * p1.y + self.start.y); + let c = 3. * (p1.y - self.start.y); let d = self.start.y - target_point.y; for t in solve_cubic(a, b, c, d) { - if (0.0..=1.0).contains(&t) { + if (0.0..=1.).contains(&t) { let x = self.evaluate(TValue::Parametric(t)).x; if target_point.x >= x { return resulting_sign; diff --git a/libraries/bezier-rs/src/subpath/manipulators.rs b/libraries/bezier-rs/src/subpath/manipulators.rs index 6bb2fd9d..286f6850 100644 --- a/libraries/bezier-rs/src/subpath/manipulators.rs +++ b/libraries/bezier-rs/src/subpath/manipulators.rs @@ -9,7 +9,7 @@ impl Subpath { self.closed } - /// Set if the subpath is closed. + /// Set whether the subpath is closed. pub fn set_closed(&mut self, new_closed: bool) { self.closed = new_closed; } @@ -29,12 +29,12 @@ impl Subpath { self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id) } - /// Insert a manipulator group at an index + /// Insert a manipulator group at an index. pub fn insert_manipulator_group(&mut self, index: usize, group: ManipulatorGroup) { self.manipulator_groups.insert(index, group) } - /// Remove a manipulator group at an index + /// Remove a manipulator group at an index. pub fn remove_manipulator_group(&mut self, index: usize) -> ManipulatorGroup { self.manipulator_groups.remove(index) } diff --git a/node-graph/gcore/Cargo.toml b/node-graph/gcore/Cargo.toml index 14628b68..3efb245d 100644 --- a/node-graph/gcore/Cargo.toml +++ b/node-graph/gcore/Cargo.toml @@ -39,4 +39,3 @@ node-macro = {path = "../node-macro"} specta.workspace = true specta.optional = true once_cell = { version = "1.17.0", default-features = false, optional = true } -# forma = { version = "0.1.0", package = "forma-render" } diff --git a/node-graph/gcore/src/vector/generator_nodes.rs b/node-graph/gcore/src/vector/generator_nodes.rs index 9f99c217..99cab79a 100644 --- a/node-graph/gcore/src/vector/generator_nodes.rs +++ b/node-graph/gcore/src/vector/generator_nodes.rs @@ -20,8 +20,7 @@ fn unit_square(_input: ()) -> VectorData { super::VectorData::from_subpaths(vec![Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE)]) } -// TODO: I removed the Arc requirement we shouuld think about when it makes sense to use its -// vs making a generic value node +// TODO(TrueDoctor): I removed the Arc requirement we should think about when it makes sense to use it vs making a generic value node #[derive(Debug, Clone)] pub struct PathGenerator { mirror: Mirror, diff --git a/node-graph/gcore/src/vector/subpath.rs b/node-graph/gcore/src/vector/subpath.rs index 16618b67..d55f6fd5 100644 --- a/node-graph/gcore/src/vector/subpath.rs +++ b/node-graph/gcore/src/vector/subpath.rs @@ -1,9 +1,8 @@ -use crate::uuid::ManipulatorGroupId; - use super::consts::ManipulatorType; use super::id_vec::IdBackedVec; use super::manipulator_group::ManipulatorGroup; use super::manipulator_point::ManipulatorPoint; +use crate::uuid::ManipulatorGroupId; use alloc::string::String; use alloc::vec; diff --git a/node-graph/gcore/src/vector/vector_data.rs b/node-graph/gcore/src/vector/vector_data.rs index af1785c1..e30af091 100644 --- a/node-graph/gcore/src/vector/vector_data.rs +++ b/node-graph/gcore/src/vector/vector_data.rs @@ -3,6 +3,7 @@ use crate::{uuid::ManipulatorGroupId, Color}; use bezier_rs::ManipulatorGroup; use dyn_any::{DynAny, StaticType}; + use glam::{DAffine2, DVec2}; /// [VectorData] is passed between nodes. @@ -17,7 +18,7 @@ pub struct VectorData { } impl VectorData { - /// An empty subpath with no data, an identity transform and a black fill. + /// An empty subpath with no data, an identity transform, and a black fill. pub const fn empty() -> Self { Self { subpaths: Vec::new(), @@ -77,15 +78,15 @@ impl VectorData { } /// Compute the pivot of the layer in layerspace (the coordinates of the subpaths) - pub fn layerspace_pivot(&self, normalised_pivot: DVec2) -> DVec2 { + pub fn layerspace_pivot(&self, normalized_pivot: DVec2) -> DVec2 { let [bounds_min, bounds_max] = self.nonzero_bounding_box(); let bounds_size = bounds_max - bounds_min; - bounds_min + bounds_size * normalised_pivot + bounds_min + bounds_size * normalized_pivot } /// Compute the pivot in local space with the current transform applied - pub fn local_pivot(&self, normalised_pivot: DVec2) -> DVec2 { - self.transform.transform_point2(self.layerspace_pivot(normalised_pivot)) + pub fn local_pivot(&self, normalized_pivot: DVec2) -> DVec2 { + self.transform.transform_point2(self.layerspace_pivot(normalized_pivot)) } }