mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 03:58:12 +08:00
Add lasso selection mode (#2235)
* add document method to check intersection and insideness with polygon * impl lasso selection * impl lasso select to always add to selection * add hint for lasso selection * fix calculating selection mode based on direction on each pointer move * fix lasso polygon lagging behing mouse position * add overlay to draw lasso polygon with fill color same as quad * fix comment * change removing from selection key binding from Sift + Ctrl to just Alt * impl Alt to shrink selection for quad in path tool * refactor rename SelectionType to SelectionShape * impl lasso overlay for path tool * impl selecting anchors and handles intersection lasso in path tool * add keys hint info * fix converting lasso polygon to closed subpath which is has less than two points * Code review * impl preferences-based selection mode to the Path tool for only for overlays, --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
f462963a36
commit
6802365e14
@@ -7,13 +7,32 @@ use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
|
||||
use crate::messages::tool::tool_messages::path_tool::PointSelectState;
|
||||
|
||||
use bezier_rs::{Bezier, BezierHandles, TValue};
|
||||
use bezier_rs::{Bezier, BezierHandles, Subpath, TValue};
|
||||
use graphene_core::transform::Transform;
|
||||
use graphene_core::vector::{ManipulatorPointId, PointId, VectorData, VectorModificationType};
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_std::vector::{HandleId, SegmentId};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum SelectionChange {
|
||||
Clear,
|
||||
Extend,
|
||||
Shrink,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum SelectionShape<'a> {
|
||||
Box([DVec2; 2]),
|
||||
Lasso(&'a Vec<DVec2>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum SelectionShapeType {
|
||||
Box,
|
||||
Lasso,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
|
||||
pub enum ManipulatorAngle {
|
||||
#[default]
|
||||
@@ -1394,9 +1413,9 @@ impl ShapeState {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn select_all_in_quad(&mut self, network_interface: &NodeNetworkInterface, quad: [DVec2; 2], clear_selection: bool) {
|
||||
pub fn select_all_in_shape(&mut self, network_interface: &NodeNetworkInterface, selection_shape: SelectionShape, selection_change: SelectionChange) {
|
||||
for (&layer, state) in &mut self.selected_shape_state {
|
||||
if clear_selection {
|
||||
if selection_change == SelectionChange::Clear {
|
||||
state.clear_points()
|
||||
}
|
||||
|
||||
@@ -1413,13 +1432,34 @@ impl ShapeState {
|
||||
assert!(vector_data.point_domain.ids().contains(&end));
|
||||
}
|
||||
|
||||
let polygon_subpath = if let SelectionShape::Lasso(polygon) = selection_shape {
|
||||
if polygon.len() < 2 {
|
||||
return;
|
||||
}
|
||||
let polygon: Subpath<PointId> = Subpath::from_anchors_linear(polygon.to_vec(), true);
|
||||
Some(polygon)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
for (id, bezier, _, _) in vector_data.segment_bezier_iter() {
|
||||
for (position, id) in [(bezier.handle_start(), ManipulatorPointId::PrimaryHandle(id)), (bezier.handle_end(), ManipulatorPointId::EndHandle(id))] {
|
||||
let Some(position) = position else { continue };
|
||||
let transformed_position = transform.transform_point2(position);
|
||||
|
||||
if quad[0].min(quad[1]).cmple(transformed_position).all() && quad[0].max(quad[1]).cmpge(transformed_position).all() {
|
||||
state.select_point(id);
|
||||
let select = match selection_shape {
|
||||
SelectionShape::Box(quad) => quad[0].min(quad[1]).cmple(transformed_position).all() && quad[0].max(quad[1]).cmpge(transformed_position).all(),
|
||||
SelectionShape::Lasso(_) => polygon_subpath
|
||||
.as_ref()
|
||||
.expect("If `selection_shape` is a polygon then subpath is constructed beforehand.")
|
||||
.contains_point(transformed_position),
|
||||
};
|
||||
|
||||
if select {
|
||||
match selection_change {
|
||||
SelectionChange::Shrink => state.deselect_point(id),
|
||||
_ => state.select_point(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1427,8 +1467,19 @@ impl ShapeState {
|
||||
for (&id, &position) in vector_data.point_domain.ids().iter().zip(vector_data.point_domain.positions()) {
|
||||
let transformed_position = transform.transform_point2(position);
|
||||
|
||||
if quad[0].min(quad[1]).cmple(transformed_position).all() && quad[0].max(quad[1]).cmpge(transformed_position).all() {
|
||||
state.select_point(ManipulatorPointId::Anchor(id));
|
||||
let select = match selection_shape {
|
||||
SelectionShape::Box(quad) => quad[0].min(quad[1]).cmple(transformed_position).all() && quad[0].max(quad[1]).cmpge(transformed_position).all(),
|
||||
SelectionShape::Lasso(_) => polygon_subpath
|
||||
.as_ref()
|
||||
.expect("If `selection_shape` is a polygon then subpath is constructed beforehand.")
|
||||
.contains_point(transformed_position),
|
||||
};
|
||||
|
||||
if select {
|
||||
match selection_change {
|
||||
SelectionChange::Shrink => state.deselect_point(ManipulatorPointId::Anchor(id)),
|
||||
_ => state.select_point(ManipulatorPointId::Anchor(id)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user