mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 07:18:04 +08:00
Overhaul Path Tool (#498)
* First pass cleanup omw to handles * Handles dragging with anchors, handles still not draggable and some bugs * Dragging single side of handle works, need to create mirror case * In progress addition of improved anchor / handle representation * partially working * Handle dragging working for non-end points, normal anchor drag bugged * Fixed corner cases, fixed anchors without handles bug * Add snapping * Change path tool selection by clicking on shape * Fixed path close point being draggable * Variable length handle, firstpass of alt to stop mirroring * Alt improved, not done. Only update structures when needed. Added snapping for selected shapes * Can now undo path edits * Do not maintain angle between non-mirrored handles * Replaced segment based overlay setup with anchor based setup * Cleanup, handle angle comparison bug remains. Investigating. * Added OverlayPooler. May closely associate overlays to VectorManipulatorAnchors instead. * Moved anchor / segment creation logic out of document_message_handler * Overlays are now managed by VectorManipulatorShapes * Fixed inconsistent handle mirroring. * Clearly shows which point you have selected * Removed OverlayPooler system * Added more comments * Removed all clones of the vector structures. A little uglier but better. * Resolved Text path initialization bug with a workaround. * Cleaned up comments * More comment cleanup * Fixed issue with quad handle dragging unwanted behavior, renamed VectorShapeManipulator * In progress refactor to allow multi-selection * In progress dragging multiple points, selection works, transform still has issues * Added Multiselect, major refactor * Commented out progress for selection change, bug with hop back on multiple shapes * Removed debug og * Resolved issue with merge * Minor cleanup, added a few comments * Review changes * Resolved unclear comment * Fixed snap back for now * Add todo comment for future snap back fix * Working situations where curve paths do not close. Thanks for points it out @pkupper * Tweaked selection size * Fix curve start point dragability, renames, cleanup * Separated into multiple files, applied @TrueDoctor review feedback * Resolved tests failing due to doc generation * Re-added closed, added concept of distance mirroring * Added shift distance mirroring, removed debounce from anchor Co-authored-by: Keavon Chambers <keavon@keavon.com> Thank you for the reviews @TrueDoctor and @pkupper
This commit is contained in:
committed by
Keavon Chambers
parent
bd844aaf94
commit
108b8be595
@@ -1,6 +1,6 @@
|
||||
use super::clipboards::Clipboard;
|
||||
use super::layer_panel::{layer_panel_entry, LayerDataTypeDiscriminant, LayerMetadata, LayerPanelEntry, RawBuffer};
|
||||
use super::utility_types::{AlignAggregate, AlignAxis, DocumentSave, FlipAxis, VectorManipulatorSegment, VectorManipulatorShape};
|
||||
use super::utility_types::{AlignAggregate, AlignAxis, DocumentSave, FlipAxis};
|
||||
use super::vectorize_layer_metadata;
|
||||
use super::{ArtboardMessageHandler, MovementMessageHandler, OverlaysMessageHandler, TransformLayerMessageHandler};
|
||||
use crate::consts::{
|
||||
@@ -12,6 +12,7 @@ use crate::layout::widgets::{
|
||||
WidgetCallback, WidgetHolder, WidgetLayout,
|
||||
};
|
||||
use crate::message_prelude::*;
|
||||
use crate::viewport_tools::vector_editor::vector_shape::VectorShape;
|
||||
use crate::EditorError;
|
||||
|
||||
use graphene::document::Document as GrapheneDocument;
|
||||
@@ -21,7 +22,6 @@ use graphene::layers::style::ViewMode;
|
||||
use graphene::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use kurbo::PathSeg;
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -138,49 +138,26 @@ impl DocumentMessageHandler {
|
||||
self.graphene_document.combined_viewport_bounding_box(paths)
|
||||
}
|
||||
|
||||
// TODO: Consider moving this to some kind of overlays manager in the future
|
||||
pub fn selected_visible_layers_vector_points(&self) -> Vec<VectorManipulatorShape> {
|
||||
/// Create a new vector shape representation with the underlying kurbo data, VectorManipulatorShape
|
||||
pub fn selected_visible_layers_vector_shapes(&self, responses: &mut VecDeque<Message>) -> Vec<VectorShape> {
|
||||
let shapes = self.selected_layers().filter_map(|path_to_shape| {
|
||||
let viewport_transform = self.graphene_document.generate_transform_relative_to_viewport(path_to_shape).ok()?;
|
||||
let layer = self.graphene_document.layer(path_to_shape);
|
||||
|
||||
// Filter out the non-visible layers from the `filter_map`
|
||||
match &layer {
|
||||
Ok(layer) if layer.visible => {}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let (path, closed) = match &layer.ok()?.data {
|
||||
// TODO: This ClosePath check does not handle all cases, fix this soon
|
||||
LayerDataType::Shape(shape) => Some((shape.path.clone(), shape.path.elements().last() == Some(&kurbo::PathEl::ClosePath))),
|
||||
LayerDataType::Text(text) => Some((text.to_bez_path_nonmut(), true)),
|
||||
// TODO: Create VectorManipulatorShape when creating a kurbo shape as a stopgap, rather than on each new selection
|
||||
match &layer.ok()?.data {
|
||||
LayerDataType::Shape(shape) => Some(VectorShape::new(path_to_shape.to_vec(), viewport_transform, &shape.path, shape.closed, responses)),
|
||||
LayerDataType::Text(text) => Some(VectorShape::new(path_to_shape.to_vec(), viewport_transform, &text.to_bez_path_nonmut(), true, responses)),
|
||||
_ => None,
|
||||
}?;
|
||||
|
||||
let segments = path
|
||||
.segments()
|
||||
.map(|segment| -> VectorManipulatorSegment {
|
||||
let place = |point: kurbo::Point| -> DVec2 { viewport_transform.transform_point2(DVec2::from((point.x, point.y))) };
|
||||
|
||||
match segment {
|
||||
PathSeg::Line(line) => VectorManipulatorSegment::Line(place(line.p0), place(line.p1)),
|
||||
PathSeg::Quad(quad) => VectorManipulatorSegment::Quad(place(quad.p0), place(quad.p1), place(quad.p2)),
|
||||
PathSeg::Cubic(cubic) => VectorManipulatorSegment::Cubic(place(cubic.p0), place(cubic.p1), place(cubic.p2), place(cubic.p3)),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<VectorManipulatorSegment>>();
|
||||
|
||||
Some(VectorManipulatorShape {
|
||||
layer_path: path_to_shape.to_vec(),
|
||||
path,
|
||||
segments,
|
||||
transform: viewport_transform,
|
||||
closed,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Consider refactoring this in a way that avoids needing to collect() so we can skip the heap allocations
|
||||
shapes.collect::<Vec<VectorManipulatorShape>>()
|
||||
shapes.collect::<Vec<VectorShape>>()
|
||||
}
|
||||
|
||||
pub fn selected_layers(&self) -> impl Iterator<Item = &[LayerId]> {
|
||||
@@ -693,7 +670,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
}
|
||||
// TODO: Correctly update layer panel in clear_selection instead of here
|
||||
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
||||
responses.push_back(DocumentMessage::SelectionChanged.into());
|
||||
}
|
||||
AlignSelectedLayers { axis, aggregate } => {
|
||||
self.backup(responses);
|
||||
@@ -756,7 +733,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
responses.push_front(DocumentOperation::DeleteLayer { path: path.to_vec() }.into());
|
||||
}
|
||||
|
||||
responses.push_front(ToolMessage::DocumentIsDirty.into());
|
||||
responses.push_front(DocumentMessage::SelectionChanged.into());
|
||||
}
|
||||
DeselectAllLayers => {
|
||||
responses.push_front(SetSelectedLayers { replacement_selected_layers: vec![] }.into());
|
||||
@@ -1042,6 +1019,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
}
|
||||
SelectionChanged => {
|
||||
// TODO: Hoist this duplicated code into wider system
|
||||
responses.push_back(ToolMessage::SelectionChanged.into());
|
||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
||||
}
|
||||
SelectLayer { layer_path, ctrl, shift } => {
|
||||
@@ -1068,7 +1046,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessorMessageHandler> for Docum
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(ToolMessage::DocumentIsDirty.into());
|
||||
responses.push_back(DocumentMessage::SelectionChanged.into());
|
||||
} else {
|
||||
paths.push(layer_path.clone());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user