add: add all of the stuff for path tool

This commit is contained in:
mtvare6 2025-07-06 10:29:21 +05:30 committed by Keavon Chambers
parent 06e8e4e6e0
commit 7d46c7dcfd
4 changed files with 182 additions and 125 deletions

View file

@ -9,7 +9,7 @@ use crate::messages::tool::tool_messages::path_tool::PathOptionsUpdate;
use crate::messages::tool::tool_messages::select_tool::SelectOptionsUpdate;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::{DAffine2, DVec2};
use graphene_std::transform::ReferencePoint;
use graphene_std::{transform::ReferencePoint, vector::ManipulatorPointId};
use std::fmt;
pub fn pin_pivot_widget(disabled: bool, source: Source) -> WidgetHolder {
@ -79,6 +79,7 @@ pub struct Dot {
pub pivot: Pivot,
pub state: DotState,
pub layer: Option<LayerNodeIdentifier>,
pub point: Option<ManipulatorPointId>,
}
impl Dot {
@ -223,6 +224,23 @@ impl Pivot {
self.pivot = Some(self.transform_from_normalized.transform_point2(self.normalized_pivot));
}
pub fn recalculate_pivot_for_layer(&mut self, document: &DocumentMessageHandler, layer: LayerNodeIdentifier, bounds: Option<[DVec2; 2]>) {
if !self.active {
return;
}
let selected = document.network_interface.selected_nodes();
if !selected.has_selected_nodes() {
self.normalized_pivot = DVec2::splat(0.5);
self.pivot = None;
return;
};
let [min, max] = bounds.unwrap_or([DVec2::ZERO, DVec2::ONE]);
self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min);
self.pivot = Some(self.transform_from_normalized.transform_point2(self.normalized_pivot));
}
pub fn update(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, draw_data: Option<(f64,)>, draw: bool) {
if !overlay_context.visibility_settings.pivot() {
self.active = false;

View file

@ -500,6 +500,7 @@ struct PathToolData {
dragging_state: DraggingState,
angle: f64,
dot: Dot,
ordered_points: Vec<ManipulatorPointId>,
opposite_handle_position: Option<DVec2>,
last_clicked_point_was_selected: bool,
last_clicked_segment_was_selected: bool,
@ -1377,6 +1378,12 @@ impl PathToolData {
fn get_as_dot(&self) -> Dot {
self.dot.clone()
}
fn sync_history(&mut self, points: &Vec<ManipulatorPointId>) {
self.ordered_points.retain(|layer| points.contains(layer));
self.ordered_points.extend(points.iter().find(|&layer| !self.ordered_points.contains(layer)));
self.dot.point = self.ordered_points.last().map(|x| *x)
}
}
impl Fsm for PathToolFsmState {
@ -1389,9 +1396,6 @@ impl Fsm for PathToolFsmState {
update_dynamic_hints(self, responses, shape_editor, document, tool_data, tool_options);
let ToolMessage::Path(event) = event else { return self };
if !matches!(event, PathToolMessage::Overlays(_) | PathToolMessage::UpdateSelectedPointsStatus { .. }) {
// debug!("{event:?}");
}
match (self, event) {
(_, PathToolMessage::SelectionChanged) => {
// Set the newly targeted layers to visible
@ -1408,6 +1412,9 @@ impl Fsm for PathToolFsmState {
shape_editor.update_selected_anchors_status(display_anchors);
shape_editor.update_selected_handles_status(display_handles);
let new_points = shape_editor.selected_points().copied().collect::<Vec<_>>();
tool_data.sync_history(&new_points);
self
}
(_, PathToolMessage::Overlays(mut overlay_context)) => {

View file

@ -361,7 +361,7 @@ struct SelectToolData {
lasso_polygon: Vec<ViewportPosition>,
selection_mode: Option<SelectionMode>,
layers_dragging: Vec<LayerNodeIdentifier>, // Unordered, often used as temporary buffer
orderer_layers: Vec<LayerNodeIdentifier>, // Ordered list of layers
ordered_layers: Vec<LayerNodeIdentifier>, // Ordered list of layers
layer_selected_on_start: Option<LayerNodeIdentifier>,
select_single_layer: Option<LayerNodeIdentifier>,
axis_align: bool,
@ -548,9 +548,9 @@ impl SelectToolData {
}
fn sync_history(&mut self) {
self.orderer_layers.retain(|layer| self.layers_dragging.contains(layer));
self.orderer_layers.extend(self.layers_dragging.iter().find(|&layer| !self.orderer_layers.contains(layer)));
self.dot.layer = self.orderer_layers.last().map(|x| *x)
self.ordered_layers.retain(|layer| self.layers_dragging.contains(layer));
self.ordered_layers.extend(self.layers_dragging.iter().find(|&layer| !self.ordered_layers.contains(layer)));
self.dot.layer = self.ordered_layers.last().map(|x| *x)
}
}

View file

@ -38,6 +38,8 @@ pub struct TransformLayerMessageHandler {
dot: Dot,
pivot: ViewportPosition,
path_bounds: Option<[DVec2; 2]>,
local_pivot: DocumentPosition,
local_mouse_start: DocumentPosition,
grab_target: DocumentPosition,
@ -64,43 +66,66 @@ impl TransformLayerMessageHandler {
}
fn calculate_pivot(
document: &DocumentMessageHandler,
selected_points: &Vec<&ManipulatorPointId>,
vector_data: &VectorData,
viewspace: DAffine2,
get_location: impl Fn(&ManipulatorPointId) -> Option<DVec2>,
dot: &Dot,
) -> Option<(DVec2, DVec2)> {
dot: &mut Dot,
layers: LayerNodeIdentifier,
) -> (Option<(DVec2, DVec2)>, Option<[DVec2; 2]>) {
let average_position = || {
let mut point_count = 0;
selected_points.iter().filter_map(|p| get_location(p)).inspect(|_| point_count += 1).sum::<DVec2>() / point_count as f64
};
let bounds = selected_points.iter().filter_map(|p| get_location(p)).fold(None, |acc: Option<[DVec2; 2]>, point| {
if let Some([mut min, mut max]) = acc {
min.x = min.x.min(point.x);
min.y = min.y.min(point.y);
max.x = max.x.max(point.x);
max.y = max.y.max(point.y);
Some([min, max])
} else {
Some([point, point])
}
});
dot.pivot.recalculate_pivot_for_layer(document, layers, bounds);
let position = || {
if !dot.state.enabled {
return average_position();
}
{
if dot.state.enabled {
match dot.state.dot {
DotType::Average => average_position(),
DotType::Active => selected_points.first().map(|p| get_location(p)).flatten().unwrap_or_else(average_position),
DotType::Pivot => average_position(),
DotType::Average => None,
DotType::Active => dot.point.and_then(|p| get_location(&p)),
DotType::Pivot => dot.pivot.position(),
}
} else {
None
}
}
.unwrap_or_else(average_position)
};
let [point] = selected_points.as_slice() else {
// Handle the case where there are multiple points
let position = position();
return Some((position, position));
return (Some((position, position)), bounds);
};
match point {
ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) => {
// Get the anchor position and transform it to the pivot
let pivot_pos = point.get_anchor_position(vector_data).map(|anchor_position| viewspace.transform_point2(anchor_position))?;
let target = viewspace.transform_point2(point.get_position(vector_data)?);
Some((pivot_pos, target))
let (Some(pivot_pos), Some(position)) = (
point.get_anchor_position(vector_data).map(|anchor_position| viewspace.transform_point2(anchor_position)),
point.get_position(vector_data),
) else {
return (None, None);
};
let target = viewspace.transform_point2(position);
(Some((pivot_pos, target)), None)
}
_ => {
// Calculate the average position of all selected points
let position = position();
Some((position, position))
(Some((position, position)), bounds)
}
}
}
@ -222,8 +247,17 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
let affected_point_refs = affected_points.iter().collect();
let get_location = |point: &&ManipulatorPointId| point.get_position(&vector_data).map(|position| viewspace.transform_point2(position));
if let Some((new_pivot, grab_target)) = calculate_pivot(&affected_point_refs, &vector_data, viewspace, |point: &ManipulatorPointId| get_location(&point), &self.dot) {
if let (Some((new_pivot, grab_target)), bounds) = calculate_pivot(
document,
&affected_point_refs,
&vector_data,
viewspace,
|point: &ManipulatorPointId| get_location(&point),
&mut self.dot,
selected_layers[0],
) {
*selected.pivot = new_pivot;
self.path_bounds = bounds;
self.local_pivot = document_to_viewport.inverse().transform_point2(*selected.pivot);
self.grab_target = document_to_viewport.inverse().transform_point2(grab_target);
@ -249,11 +283,6 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
return;
}
for layer in document.metadata().all_layers() {
if !document.network_interface.is_artboard(&layer.to_node(), &[]) {
continue;
};
let viewport_box = input.viewport_bounds.size();
let axis_constraint = self.transform_operation.axis_constraint();
@ -360,6 +389,9 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
overlay_context.text(&text, COLOR_OVERLAY_BLUE, None, transform, 16., [Pivot::Middle, Pivot::Middle]);
}
}
if let Some(bounds) = self.path_bounds {
overlay_context.quad(Quad::from_box(bounds), None, None);
}
}