From ee7d4982417f01be2d2f7c34f9da8523532d9f30 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Tue, 17 Dec 2024 22:01:42 -0800 Subject: [PATCH] Add Freehand tool drawing new subpaths on an existing layer with Shift held --- .../messages/input_mapper/input_mappings.rs | 2 +- .../tool/tool_messages/freehand_tool.rs | 55 +++++++++++++------ .../messages/tool/tool_messages/pen_tool.rs | 4 +- node-graph/gcore/src/transform.rs | 9 +++ node-graph/gcore/src/vector/vector_nodes.rs | 36 +++++++++--- 5 files changed, 77 insertions(+), 29 deletions(-) diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index d4d80f692c..6b81964697 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -258,7 +258,7 @@ pub fn input_mappings() -> Mapping { // // FreehandToolMessage entry!(PointerMove; action_dispatch=FreehandToolMessage::PointerMove), - entry!(KeyDown(MouseLeft); action_dispatch=FreehandToolMessage::DragStart), + entry!(KeyDown(MouseLeft); action_dispatch=FreehandToolMessage::DragStart { append_to_selected: Shift }), entry!(KeyUp(MouseLeft); action_dispatch=FreehandToolMessage::DragStop), entry!(KeyDown(MouseRight); action_dispatch=FreehandToolMessage::Abort), entry!(KeyDown(Escape); action_dispatch=FreehandToolMessage::Abort), diff --git a/editor/src/messages/tool/tool_messages/freehand_tool.rs b/editor/src/messages/tool/tool_messages/freehand_tool.rs index fb820b7664..76d48ab170 100644 --- a/editor/src/messages/tool/tool_messages/freehand_tool.rs +++ b/editor/src/messages/tool/tool_messages/freehand_tool.rs @@ -47,7 +47,7 @@ pub enum FreehandToolMessage { WorkingColorChanged, // Tool-specific messages - DragStart, + DragStart { append_to_selected: Key }, DragStop, PointerMove, UpdateOptions(FreehandOptionsUpdate), @@ -203,7 +203,7 @@ impl Fsm for FreehandToolFsmState { self } - (FreehandToolFsmState::Ready, FreehandToolMessage::DragStart) => { + (FreehandToolFsmState::Ready, FreehandToolMessage::DragStart { append_to_selected }) => { responses.add(DocumentMessage::StartTransaction); tool_data.dragged = false; @@ -216,11 +216,26 @@ impl Fsm for FreehandToolFsmState { tool_data.layer = Some(layer); tool_data.end_point = Some((position, point)); - extend_path_with_next_segment(tool_data, position, responses); + extend_path_with_next_segment(tool_data, position, true, responses); return FreehandToolFsmState::Drawing; } + if input.keyboard.key(append_to_selected) { + let mut selected_layers_except_artboards = selected_nodes.selected_layers_except_artboards(&document.network_interface); + let existing_layer = selected_layers_except_artboards.next().filter(|_| selected_layers_except_artboards.next().is_none()); + if let Some(layer) = existing_layer { + tool_data.layer = Some(layer); + + let transform = document.metadata().transform_to_viewport(layer); + let position = transform.inverse().transform_point2(input.mouse.position); + + extend_path_with_next_segment(tool_data, position, false, responses); + + return FreehandToolFsmState::Drawing; + } + } + responses.add(DocumentMessage::DeselectAllLayers); let parent = document.new_layer_parent(true); @@ -242,7 +257,7 @@ impl Fsm for FreehandToolFsmState { let transform = document.metadata().transform_to_viewport(layer); let position = transform.inverse().transform_point2(input.mouse.position); - extend_path_with_next_segment(tool_data, position, responses); + extend_path_with_next_segment(tool_data, position, true, responses); } FreehandToolFsmState::Drawing @@ -279,7 +294,11 @@ impl Fsm for FreehandToolFsmState { fn update_hints(&self, responses: &mut VecDeque) { let hint_data = match self { - FreehandToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Draw Polyline")])]), + FreehandToolFsmState::Ready => HintData(vec![HintGroup(vec![ + HintInfo::mouse(MouseMotion::LmbDrag, "Draw Polyline"), + // TODO: Only show this if a single layer is selected and it's of a valid type (e.g. a vector path but not raster or artboard) + HintInfo::keys([Key::Shift], "Append to Selected Layer").prepend_plus(), + ])]), FreehandToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]), }; @@ -291,7 +310,7 @@ impl Fsm for FreehandToolFsmState { } } -fn extend_path_with_next_segment(tool_data: &mut FreehandToolData, position: DVec2, responses: &mut VecDeque) { +fn extend_path_with_next_segment(tool_data: &mut FreehandToolData, position: DVec2, extend: bool, responses: &mut VecDeque) { if !tool_data.end_point.map_or(true, |(last_pos, _)| position != last_pos) || !position.is_finite() { return; } @@ -304,18 +323,20 @@ fn extend_path_with_next_segment(tool_data: &mut FreehandToolData, position: DVe modification_type: VectorModificationType::InsertPoint { id, position }, }); - if let Some((_, previous_position)) = tool_data.end_point { - let next_id = SegmentId::generate(); - let points = [previous_position, id]; + if extend { + if let Some((_, previous_position)) = tool_data.end_point { + let next_id = SegmentId::generate(); + let points = [previous_position, id]; - responses.add(GraphOperationMessage::Vector { - layer, - modification_type: VectorModificationType::InsertSegment { - id: next_id, - points, - handles: [None, None], - }, - }); + responses.add(GraphOperationMessage::Vector { + layer, + modification_type: VectorModificationType::InsertSegment { + id: next_id, + points, + handles: [None, None], + }, + }); + } } tool_data.dragged = true; diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index eb58504dc3..7e4c1fd814 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -51,7 +51,7 @@ pub enum PenToolMessage { // Tool-specific messages - // It is necessary to defer this until the transform of the layer can be accuratly computed (quite hacky) + // It is necessary to defer this until the transform of the layer can be accurately computed (quite hacky) AddPointLayerPosition { layer: LayerNodeIdentifier, viewport: DVec2 }, Confirm, DragStart { append_to_selected: Key }, @@ -500,7 +500,7 @@ impl PenToolData { // This causes the following message to be run only after the next graph evaluation runs and the transforms are updated responses.add(Message::StartBuffer); - // It is necessary to defer this until the transform of the layer can be accuratly computed (quite hacky) + // It is necessary to defer this until the transform of the layer can be accurately computed (quite hacky) responses.add(PenToolMessage::AddPointLayerPosition { layer, viewport }); } diff --git a/node-graph/gcore/src/transform.rs b/node-graph/gcore/src/transform.rs index d72c089703..6c0bda17f7 100644 --- a/node-graph/gcore/src/transform.rs +++ b/node-graph/gcore/src/transform.rs @@ -168,6 +168,15 @@ impl Footprint { pub fn offset(&self) -> DVec2 { self.transform.transform_point2(DVec2::ZERO) } + + pub fn from_point(point: DVec2) -> Self { + Self { + // TODO: Make the scale 0? + transform: DAffine2::from_translation(point), + resolution: glam::UVec2::ONE, + ..Default::default() + } + } } impl From<()> for Footprint { diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index bfe3fb2028..6e68516609 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -1,6 +1,7 @@ use super::misc::CentroidType; use super::style::{Fill, Gradient, GradientStops, Stroke}; use super::{PointId, SegmentDomain, SegmentId, StrokeId, VectorData}; +use crate::raster::Bitmap; use crate::registry::types::{Angle, Fraction, IntegerCount, Length, SeedValue}; use crate::renderer::GraphicElementRendered; use crate::transform::{Footprint, Transform, TransformMut}; @@ -901,24 +902,41 @@ async fn jitter_points( Footprint -> VectorData, )] vector_data: impl Node, + // TODO: Make amount per-axis #[default(5.)] amount: f64, seed: SeedValue, + #[implementations( + Footprint -> crate::raster::ImageFrame, + )] + image: impl Node>, ) -> VectorData { let mut vector_data = vector_data.eval(footprint).await; - let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into()); + // let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into()); - let deltas = (0..vector_data.point_domain.positions().len()) - .map(|_| { - let angle = rng.gen::() * std::f64::consts::TAU; - DVec2::from_angle(angle) * rng.gen::() * amount - }) - .collect::>(); + // let deltas = (0..vector_data.point_domain.positions().len()) + // .map(|_| { + // let angle = rng.gen::() * std::f64::consts::TAU; + // DVec2::from_angle(angle) * rng.gen::() * amount + // }) + // .collect::>(); let mut already_applied = vec![false; vector_data.point_domain.positions().len()]; for (handles, start, end) in vector_data.segment_domain.handles_and_points_mut() { - let start_delta = deltas[*start]; - let end_delta = deltas[*end]; + let sample_start = image + .eval(Footprint::from_point(vector_data.point_domain.positions()[*start])) + .await + .image + .get_pixel(0, 0) + .expect("Broke at A"); + let start_delta = DVec2::new((sample_start.r() as f64 - 0.5) * amount, (sample_start.g() as f64 - 0.5) * amount); + let sample_end = image + .eval(Footprint::from_point(vector_data.point_domain.positions()[*end])) + .await + .image + .get_pixel(0, 0) + .expect("Broke at A"); + let end_delta = DVec2::new((sample_end.r() as f64 - 0.5) * amount, (sample_end.g() as f64 - 0.5) * amount); if !already_applied[*start] { let start_position = vector_data.point_domain.positions()[*start];