multi layer click and repeat

This commit is contained in:
0SlowPoke0
2025-08-26 02:39:33 +05:30
parent dabdef7bed
commit 40db3238f3
@@ -1,19 +1,10 @@
use super::tool_prelude::*; use super::tool_prelude::*;
use crate::consts::{DEFAULT_STROKE_WIDTH, DRAG_THRESHOLD, PATH_JOIN_THRESHOLD, SNAP_POINT_TOLERANCE}; use crate::consts::DEFAULT_STROKE_WIDTH;
use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys;
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, find_spline, merge_layers, merge_points}; use crate::messages::tool::common_functionality::shapes::shape_utility::extract_circular_repeat_parameters;
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_circular_repeat_parameters, extract_star_parameters};
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnapTypeConfiguration, SnappedPoint};
use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend};
use graph_craft::document::{NodeId, NodeInput};
use graphene_std::Color; use graphene_std::Color;
use graphene_std::vector::{PointId, SegmentId, VectorModificationType};
#[derive(Default, ExtractField)] #[derive(Default, ExtractField)]
pub struct OperationTool { pub struct OperationTool {
@@ -224,7 +215,7 @@ impl ToolTransition for OperationTool {
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
struct OperationToolData { struct OperationToolData {
drag_start: DVec2, drag_start: DVec2,
clicked_layer: LayerNodeIdentifier, clicked_layer_radius: (LayerNodeIdentifier, f64),
layers_dragging: Vec<(LayerNodeIdentifier, f64)>, layers_dragging: Vec<(LayerNodeIdentifier, f64)>,
initial_center: DVec2, initial_center: DVec2,
} }
@@ -233,8 +224,6 @@ impl OperationToolData {
fn cleanup(&mut self) { fn cleanup(&mut self) {
self.layers_dragging.clear(); self.layers_dragging.clear();
} }
fn modify_circular_repeat(&mut self) {}
} }
impl Fsm for OperationToolFsmState { impl Fsm for OperationToolFsmState {
@@ -246,54 +235,95 @@ impl Fsm for OperationToolFsmState {
event: ToolMessage, event: ToolMessage,
tool_data: &mut Self::ToolData, tool_data: &mut Self::ToolData,
tool_action_data: &mut ToolActionMessageContext, tool_action_data: &mut ToolActionMessageContext,
tool_options: &Self::ToolOptions, _tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>, responses: &mut VecDeque<Message>,
) -> Self { ) -> Self {
let ToolActionMessageContext { let ToolActionMessageContext { document, input, .. } = tool_action_data;
document,
global_tool_data,
input,
shape_editor,
preferences,
..
} = tool_action_data;
let ToolMessage::Operation(event) = event else { return self }; let ToolMessage::Operation(event) = event else { return self };
match (self, event) { match (self, event) {
(_, OperationToolMessage::Overlays { context: mut overlay_context }) => {} (_, OperationToolMessage::Overlays { context: mut overlay_context }) => {
match self {
OperationToolFsmState::Ready => {
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
let viewport = document.metadata().transform_to_viewport(layer);
let center = viewport.transform_point2(DVec2::ZERO);
if center.distance(input.mouse.position) < 5. {
overlay_context.circle(center, 3., None, None);
}
overlay_context.outline_vector(&vector, viewport);
}
if let Some(layer) = document.click(&input) {
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return self };
let viewport = document.metadata().transform_to_viewport(layer);
let center = viewport.transform_point2(DVec2::ZERO);
if center.distance(input.mouse.position) < 5. {
overlay_context.circle(center, 3., None, None);
}
overlay_context.outline_vector(&vector, viewport);
}
}
_ => {
for layer in tool_data.layers_dragging.iter().map(|(l, _)| l) {
let Some(vector) = document.network_interface.compute_modified_vector(*layer) else { continue };
let viewport = document.metadata().transform_to_viewport(*layer);
overlay_context.outline_vector(&vector, viewport);
}
}
}
self
}
(OperationToolFsmState::Ready, OperationToolMessage::DragStart) => { (OperationToolFsmState::Ready, OperationToolMessage::DragStart) => {
let selected_layers = document let selected_layers = document
.network_interface .network_interface
.selected_nodes() .selected_nodes()
.selected_layers(document.metadata()) .selected_layers(document.metadata())
.collect::<HashSet<LayerNodeIdentifier>>(); .collect::<HashSet<LayerNodeIdentifier>>();
let Some(layer) = document.click(&input) else { return self }; let Some(clicked_layer) = document.click(&input) else { return self };
let viewport = document.metadata().transform_to_viewport(layer); responses.add(DocumentMessage::StartTransaction);
let viewport = document.metadata().transform_to_viewport(clicked_layer);
let center = viewport.transform_point2(DVec2::ZERO);
if selected_layers.contains(&layer) { if center.distance(input.mouse.position) > 5. {
return self;
};
if selected_layers.contains(&clicked_layer) {
// store all // store all
tool_data.layers_dragging = selected_layers tool_data.layers_dragging = selected_layers
.iter() .iter()
.map(|layer| { .map(|layer| {
let (_angle_offset, radius, _count) = extract_circular_repeat_parameters(Some(*layer), document).unwrap_or((0.0, 0.0, 6)); let (_angle_offset, radius, _count) = extract_circular_repeat_parameters(Some(*layer), document).unwrap_or((0.0, 0.0, 6));
if *layer == clicked_layer {
tool_data.clicked_layer_radius = (*layer, radius)
}
(*layer, radius) (*layer, radius)
}) })
.collect::<Vec<(LayerNodeIdentifier, f64)>>(); .collect::<Vec<(LayerNodeIdentifier, f64)>>();
} else { } else {
// deselect all the layer and store the clicked layer for repeat and dragging // deselect all the layer and store the clicked layer for repeat and dragging
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![clicked_layer.to_node()] });
let (_angle_offset, radius, _count) = extract_circular_repeat_parameters(Some(layer), document).unwrap_or((0.0, 0.0, 6)); let (_angle_offset, radius, _count) = extract_circular_repeat_parameters(Some(clicked_layer), document).unwrap_or((0.0, 0.0, 6));
tool_data.layers_dragging = vec![(layer, radius)]; tool_data.clicked_layer_radius = (clicked_layer, radius);
tool_data.layers_dragging = vec![(clicked_layer, radius)];
} }
tool_data.drag_start = input.mouse.position; tool_data.drag_start = input.mouse.position;
tool_data.clicked_layer = layer;
tool_data.initial_center = viewport.transform_point2(DVec2::ZERO); tool_data.initial_center = viewport.transform_point2(DVec2::ZERO);
OperationToolFsmState::Drawing OperationToolFsmState::Drawing
} }
(OperationToolFsmState::Drawing, OperationToolMessage::DragStop) => { (OperationToolFsmState::Drawing, OperationToolMessage::DragStop) => {
if tool_data.drag_start.distance(input.mouse.position) < 5. {
responses.add(DocumentMessage::AbortTransaction);
};
tool_data.cleanup(); tool_data.cleanup();
responses.add(DocumentMessage::EndTransaction);
OperationToolFsmState::Ready OperationToolFsmState::Ready
} }
(OperationToolFsmState::Drawing, OperationToolMessage::PointerMove) => { (OperationToolFsmState::Drawing, OperationToolMessage::PointerMove) => {
@@ -302,16 +332,27 @@ impl Fsm for OperationToolFsmState {
return self; return self;
}; };
let viewport = document.metadata().transform_to_viewport(tool_data.clicked_layer); let (_clicked_layer, clicked_radius) = tool_data.clicked_layer_radius;
let center = viewport.transform_point2(DVec2::ZERO); let viewport = document.metadata().transform_to_viewport(tool_data.clicked_layer_radius.0);
let sign = (input.mouse.position - tool_data.initial_center).dot(tool_data.drag_start - tool_data.initial_center).signum(); let sign = (input.mouse.position - tool_data.initial_center).dot(viewport.transform_vector2(DVec2::Y)).signum();
let delta = viewport.inverse().transform_vector2(input.mouse.position - tool_data.drag_start).length() * sign; let delta = document
log::info!("{:?}", delta); .metadata()
.downstream_transform_to_viewport(tool_data.clicked_layer_radius.0)
.inverse()
.transform_vector2(input.mouse.position - tool_data.initial_center)
.length() * sign;
for (layer, initial_radius) in &tool_data.layers_dragging { for (layer, initial_radius) in &tool_data.layers_dragging {
let new_radius = if initial_radius.signum() == clicked_radius.signum() {
*initial_radius + delta
} else {
*initial_radius + delta.signum() * -1. * delta.abs()
};
responses.add(GraphOperationMessage::CircularRepeatSet { responses.add(GraphOperationMessage::CircularRepeatSet {
layer: *layer, layer: *layer,
angle: 0., angle: 0.,
radius: initial_radius + delta, radius: new_radius,
count: 6, count: 6,
}); });
} }
@@ -319,11 +360,17 @@ impl Fsm for OperationToolFsmState {
OperationToolFsmState::Drawing OperationToolFsmState::Drawing
} }
(_, OperationToolMessage::PointerMove) => self, (_, OperationToolMessage::PointerMove) => {
responses.add(OverlaysMessage::Draw);
self
}
(OperationToolFsmState::Drawing, OperationToolMessage::PointerOutsideViewport) => OperationToolFsmState::Drawing, (OperationToolFsmState::Drawing, OperationToolMessage::PointerOutsideViewport) => OperationToolFsmState::Drawing,
(state, OperationToolMessage::PointerOutsideViewport) => state, (state, OperationToolMessage::PointerOutsideViewport) => state,
(OperationToolFsmState::Drawing, OperationToolMessage::Abort) => OperationToolFsmState::Ready, (OperationToolFsmState::Drawing, OperationToolMessage::Abort) => {
responses.add(DocumentMessage::AbortTransaction);
OperationToolFsmState::Ready
}
(_, OperationToolMessage::WorkingColorChanged) => self, (_, OperationToolMessage::WorkingColorChanged) => self,
_ => self, _ => self,
} }