mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Implement viewport selection (#178)
* Begin implementing viewport selection * Implement viewport click and drag selection for ellipse and rectangle * Begin implementing line selection * Remove debug prints * Run cargo format * Use DVec2 instead of kurbo::Point * Line and polyline intersection * Run cargo format * Add fix for missing layer panel update * Replace point selection with box selection * Formatting Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
20420c1286
commit
ad064602a5
13 changed files with 293 additions and 25 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use glam::DAffine2;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
use crate::{
|
||||
layers::{self, style::PathStyle, Folder, Layer, LayerDataTypes, Line, PolyLine, Rect, Shape},
|
||||
|
|
@ -64,6 +64,19 @@ impl Document {
|
|||
svg
|
||||
}
|
||||
|
||||
/// Checks whether each layer under `path` intersects with the provided `quad` and adds all intersection layers as paths to `intersections`.
|
||||
pub fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>) {
|
||||
self.document_folder(path).unwrap().intersects_quad(quad, path, intersections);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Checks whether each layer under the root path intersects with the provided `quad` and returns the paths to all intersecting layers.
|
||||
pub fn intersects_quad_root(&self, quad: [DVec2; 4]) -> Vec<Vec<LayerId>> {
|
||||
let mut intersections = Vec::new();
|
||||
self.intersects_quad(quad, &mut vec![], &mut intersections);
|
||||
intersections
|
||||
}
|
||||
|
||||
fn is_mounted(&self, mount_path: &[LayerId], path: &[LayerId]) -> bool {
|
||||
path.starts_with(mount_path) && self.work_mounted
|
||||
}
|
||||
|
|
|
|||
57
core/document/src/intersection.rs
Normal file
57
core/document/src/intersection.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
use glam::DVec2;
|
||||
use kurbo::{BezPath, Line, PathSeg, Point, Shape, Vec2};
|
||||
|
||||
fn to_point(vec: DVec2) -> Point {
|
||||
Point::new(vec.x, vec.y)
|
||||
}
|
||||
|
||||
pub fn intersect_quad_bez_path(quad: [DVec2; 4], shape: &BezPath, closed: bool) -> bool {
|
||||
let lines = vec![
|
||||
Line::new(to_point(quad[0]), to_point(quad[1])),
|
||||
Line::new(to_point(quad[1]), to_point(quad[2])),
|
||||
Line::new(to_point(quad[2]), to_point(quad[3])),
|
||||
Line::new(to_point(quad[3]), to_point(quad[0])),
|
||||
];
|
||||
// check if outlines intersect
|
||||
for path_segment in shape.segments() {
|
||||
for line in &lines {
|
||||
if !path_segment.intersect_line(*line).is_empty() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if selection is entirely within the shape
|
||||
if closed && shape.contains(to_point(quad[0])) {
|
||||
return true;
|
||||
}
|
||||
// check if shape is entirely within the selection
|
||||
if let Some(shape_point) = get_arbitrary_point_on_path(shape) {
|
||||
let mut pos = 0;
|
||||
let mut neg = 0;
|
||||
for line in lines {
|
||||
if line.p0 == shape_point {
|
||||
return true;
|
||||
};
|
||||
let line_vec = Vec2::new(line.p1.x - line.p0.x, line.p1.y - line.p0.y);
|
||||
let point_vec = Vec2::new(line.p1.x - shape_point.x, line.p1.y - shape_point.y);
|
||||
let cross = line_vec.cross(point_vec);
|
||||
if cross > 0.0 {
|
||||
pos += 1;
|
||||
} else if cross < 0.0 {
|
||||
neg += 1;
|
||||
}
|
||||
if pos > 0 && neg > 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn get_arbitrary_point_on_path(path: &BezPath) -> Option<Point> {
|
||||
path.segments().next().map(|seg| match seg {
|
||||
PathSeg::Line(line) => line.p0,
|
||||
PathSeg::Quad(quad) => quad.p0,
|
||||
PathSeg::Cubic(cubic) => cubic.p0,
|
||||
})
|
||||
}
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
use glam::DAffine2;
|
||||
use glam::DVec2;
|
||||
use kurbo::Shape;
|
||||
|
||||
use crate::intersection::intersect_quad_bez_path;
|
||||
use crate::LayerId;
|
||||
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
|
|
@ -16,10 +21,17 @@ impl Ellipse {
|
|||
}
|
||||
|
||||
impl LayerData for Ellipse {
|
||||
fn to_kurbo_path(&mut self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
|
||||
kurbo::Ellipse::from_affine(kurbo::Affine::new(transform.to_cols_array())).to_path(0.1)
|
||||
fn to_kurbo_path(&self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
|
||||
kurbo::Ellipse::from_affine(kurbo::Affine::new(transform.to_cols_array())).to_path(0.01)
|
||||
}
|
||||
|
||||
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) {
|
||||
let _ = write!(svg, r#"<path d="{}" {} />"#, self.to_kurbo_path(transform, style).to_svg(), style.render());
|
||||
}
|
||||
|
||||
fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, style: style::PathStyle) {
|
||||
if intersect_quad_bez_path(quad, &self.to_kurbo_path(DAffine2::IDENTITY, style), true) {
|
||||
intersections.push(path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use glam::DVec2;
|
||||
|
||||
use crate::{DocumentError, LayerId};
|
||||
|
||||
use super::{style, Layer, LayerData, LayerDataTypes};
|
||||
|
|
@ -13,6 +15,10 @@ pub struct Folder {
|
|||
}
|
||||
|
||||
impl LayerData for Folder {
|
||||
fn to_kurbo_path(&self, _: glam::DAffine2, _: style::PathStyle) -> kurbo::BezPath {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, _style: style::PathStyle) {
|
||||
let _ = writeln!(svg, r#"<g transform="matrix("#);
|
||||
transform.to_cols_array().iter().enumerate().for_each(|(i, f)| {
|
||||
|
|
@ -26,8 +32,12 @@ impl LayerData for Folder {
|
|||
let _ = writeln!(svg, "</g>");
|
||||
}
|
||||
|
||||
fn to_kurbo_path(&mut self, _: glam::DAffine2, _: style::PathStyle) -> kurbo::BezPath {
|
||||
unimplemented!()
|
||||
fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, _style: style::PathStyle) {
|
||||
for (layer, layer_id) in self.layers().iter().zip(&self.layer_ids) {
|
||||
path.push(*layer_id);
|
||||
layer.intersects_quad(quad, path, intersections);
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
use glam::DAffine2;
|
||||
use glam::DVec2;
|
||||
use kurbo::Point;
|
||||
|
||||
use crate::intersection::intersect_quad_bez_path;
|
||||
use crate::LayerId;
|
||||
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
|
|
@ -17,7 +21,7 @@ impl Line {
|
|||
}
|
||||
|
||||
impl LayerData for Line {
|
||||
fn to_kurbo_path(&mut self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
|
||||
fn to_kurbo_path(&self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
|
||||
fn new_point(a: DVec2) -> Point {
|
||||
Point::new(a.x, a.y)
|
||||
}
|
||||
|
|
@ -26,10 +30,17 @@ impl LayerData for Line {
|
|||
path.line_to(new_point(transform.transform_point2(DVec2::ONE)));
|
||||
path
|
||||
}
|
||||
|
||||
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) {
|
||||
let [x1, y1] = transform.translation.to_array();
|
||||
let [x2, y2] = transform.transform_point2(DVec2::ONE).to_array();
|
||||
|
||||
let _ = write!(svg, r#"<line x1="{}" y1="{}" x2="{}" y2="{}"{} />"#, x1, y1, x2, y2, style.render(),);
|
||||
}
|
||||
|
||||
fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, style: style::PathStyle) {
|
||||
if intersect_quad_bez_path(quad, &self.to_kurbo_path(DAffine2::IDENTITY, style), false) {
|
||||
intersections.push(path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,16 @@ pub use shape::Shape;
|
|||
|
||||
pub mod folder;
|
||||
use crate::DocumentError;
|
||||
use crate::LayerId;
|
||||
pub use folder::Folder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const SELECTION_TOLERANCE: f64 = 5.0;
|
||||
|
||||
pub trait LayerData {
|
||||
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle);
|
||||
fn to_kurbo_path(&mut self, transform: glam::DAffine2, style: style::PathStyle) -> BezPath;
|
||||
fn to_kurbo_path(&self, transform: glam::DAffine2, style: style::PathStyle) -> BezPath;
|
||||
fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, style: style::PathStyle);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
|
|
@ -51,6 +55,15 @@ macro_rules! call_kurbo_path {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! call_intersects_quad {
|
||||
($self:ident.intersects_quad($quad:ident, $path:ident, $intersections:ident, $style:ident) { $($variant:ident),* }) => {
|
||||
match $self {
|
||||
$(Self::$variant(x) => x.intersects_quad($quad, $path, $intersections, $style)),*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl LayerDataTypes {
|
||||
pub fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) {
|
||||
call_render! {
|
||||
|
|
@ -64,7 +77,7 @@ impl LayerDataTypes {
|
|||
}
|
||||
}
|
||||
}
|
||||
pub fn to_kurbo_path(&mut self, transform: glam::DAffine2, style: style::PathStyle) -> BezPath {
|
||||
pub fn to_kurbo_path(&self, transform: glam::DAffine2, style: style::PathStyle) -> BezPath {
|
||||
call_kurbo_path! {
|
||||
self.to_kurbo_path(transform, style) {
|
||||
Folder,
|
||||
|
|
@ -76,6 +89,19 @@ impl LayerDataTypes {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, style: style::PathStyle) {
|
||||
call_intersects_quad! {
|
||||
self.intersects_quad(quad, path, intersections, style) {
|
||||
Folder,
|
||||
Ellipse,
|
||||
Rect,
|
||||
Line,
|
||||
PolyLine,
|
||||
Shape
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
|
@ -122,6 +148,20 @@ impl Layer {
|
|||
self.cache.as_str()
|
||||
}
|
||||
|
||||
pub fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>) {
|
||||
let inv_transform = self.transform.inverse();
|
||||
let transformed_quad = [
|
||||
inv_transform.transform_point2(quad[0]),
|
||||
inv_transform.transform_point2(quad[1]),
|
||||
inv_transform.transform_point2(quad[2]),
|
||||
inv_transform.transform_point2(quad[3]),
|
||||
];
|
||||
if !self.visible {
|
||||
return;
|
||||
}
|
||||
self.data.intersects_quad(transformed_quad, path, intersections, self.style)
|
||||
}
|
||||
|
||||
pub fn render_on(&mut self, svg: &mut String) {
|
||||
*svg += self.render();
|
||||
}
|
||||
|
|
@ -129,12 +169,14 @@ impl Layer {
|
|||
pub fn to_kurbo_path(&mut self) -> BezPath {
|
||||
self.data.to_kurbo_path(self.transform, self.style)
|
||||
}
|
||||
|
||||
pub fn as_folder_mut(&mut self) -> Result<&mut Folder, DocumentError> {
|
||||
match &mut self.data {
|
||||
LayerDataTypes::Folder(f) => Ok(f),
|
||||
_ => Err(DocumentError::NotAFolder),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_folder(&self) -> Result<&Folder, DocumentError> {
|
||||
match &self.data {
|
||||
LayerDataTypes::Folder(f) => Ok(&f),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use crate::{intersection::intersect_quad_bez_path, LayerId};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
|
|
@ -17,7 +19,7 @@ impl PolyLine {
|
|||
}
|
||||
|
||||
impl LayerData for PolyLine {
|
||||
fn to_kurbo_path(&mut self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
|
||||
fn to_kurbo_path(&self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
|
||||
let mut path = kurbo::BezPath::new();
|
||||
self.points
|
||||
.iter()
|
||||
|
|
@ -27,6 +29,7 @@ impl LayerData for PolyLine {
|
|||
.for_each(|(i, p)| if i == 0 { path.move_to(p) } else { path.line_to(p) });
|
||||
path
|
||||
}
|
||||
|
||||
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) {
|
||||
if self.points.is_empty() {
|
||||
return;
|
||||
|
|
@ -40,6 +43,12 @@ impl LayerData for PolyLine {
|
|||
}
|
||||
let _ = write!(svg, r#""{} />"#, style.render());
|
||||
}
|
||||
|
||||
fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, style: style::PathStyle) {
|
||||
if intersect_quad_bez_path(quad, &self.to_kurbo_path(DAffine2::IDENTITY, style), false) {
|
||||
intersections.push(path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
use glam::DAffine2;
|
||||
use glam::DVec2;
|
||||
use kurbo::Point;
|
||||
|
||||
use crate::intersection::intersect_quad_bez_path;
|
||||
use crate::LayerId;
|
||||
|
||||
use super::style;
|
||||
use super::LayerData;
|
||||
|
||||
|
|
@ -17,7 +21,7 @@ impl Rect {
|
|||
}
|
||||
|
||||
impl LayerData for Rect {
|
||||
fn to_kurbo_path(&mut self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
|
||||
fn to_kurbo_path(&self, transform: glam::DAffine2, _style: style::PathStyle) -> kurbo::BezPath {
|
||||
fn new_point(a: DVec2) -> Point {
|
||||
Point::new(a.x, a.y)
|
||||
}
|
||||
|
|
@ -32,4 +36,10 @@ impl LayerData for Rect {
|
|||
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) {
|
||||
let _ = write!(svg, r#"<path d="{}" {} />"#, self.to_kurbo_path(transform, style).to_svg(), style.render());
|
||||
}
|
||||
|
||||
fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, style: style::PathStyle) {
|
||||
if intersect_quad_bez_path(quad, &self.to_kurbo_path(DAffine2::IDENTITY, style), true) {
|
||||
intersections.push(path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
use glam::DAffine2;
|
||||
use glam::DVec2;
|
||||
|
||||
use crate::intersection::intersect_quad_bez_path;
|
||||
use crate::LayerId;
|
||||
use kurbo::BezPath;
|
||||
use kurbo::Vec2;
|
||||
|
||||
|
|
@ -20,7 +25,7 @@ impl Shape {
|
|||
}
|
||||
|
||||
impl LayerData for Shape {
|
||||
fn to_kurbo_path(&mut self, transform: glam::DAffine2, _style: style::PathStyle) -> BezPath {
|
||||
fn to_kurbo_path(&self, transform: glam::DAffine2, _style: style::PathStyle) -> BezPath {
|
||||
fn unit_rotation(theta: f64) -> Vec2 {
|
||||
Vec2::new(-theta.sin(), theta.cos())
|
||||
}
|
||||
|
|
@ -66,4 +71,10 @@ impl LayerData for Shape {
|
|||
fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) {
|
||||
let _ = write!(svg, r#"<path d="{}" {} />"#, self.to_kurbo_path(transform, style).to_svg(), style.render());
|
||||
}
|
||||
|
||||
fn intersects_quad(&self, quad: [DVec2; 4], path: &mut Vec<LayerId>, intersections: &mut Vec<Vec<LayerId>>, style: style::PathStyle) {
|
||||
if intersect_quad_bez_path(quad, &self.to_kurbo_path(DAffine2::IDENTITY, style), true) {
|
||||
intersections.push(path.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
pub mod color;
|
||||
pub mod document;
|
||||
pub mod intersection;
|
||||
pub mod layers;
|
||||
pub mod operation;
|
||||
pub mod response;
|
||||
|
|
|
|||
|
|
@ -281,6 +281,8 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
|||
for path in paths {
|
||||
responses.extend(self.select_layer(&path));
|
||||
}
|
||||
// TODO: Correctly update layer panel in clear_selection instead of here
|
||||
responses.extend(self.handle_folder_changed(Vec::new()));
|
||||
}
|
||||
Undo => {
|
||||
// this is a temporary fix and will be addressed by #123
|
||||
|
|
|
|||
|
|
@ -104,6 +104,12 @@ impl Default for Mapping {
|
|||
fn default() -> Self {
|
||||
let (up, down, pointer_move) = mapping![
|
||||
entry! {action=DocumentMessage::PasteLayers, key_down=KeyV, modifiers=[KeyControl]},
|
||||
// Select
|
||||
entry! {action=SelectMessage::MouseMove, message=InputMapperMessage::PointerMove},
|
||||
entry! {action=SelectMessage::DragStart, key_down=Lmb},
|
||||
entry! {action=SelectMessage::DragStop, key_up=Lmb},
|
||||
entry! {action=SelectMessage::Abort, key_down=Rmb},
|
||||
entry! {action=SelectMessage::Abort, key_down=KeyEscape},
|
||||
// Rectangle
|
||||
entry! {action=RectangleMessage::Center, key_down=KeyAlt},
|
||||
entry! {action=RectangleMessage::UnCenter, key_up=KeyAlt},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
use crate::input::InputPreprocessor;
|
||||
use document_core::color::Color;
|
||||
use document_core::layers::style::Fill;
|
||||
use document_core::layers::style::Stroke;
|
||||
use document_core::layers::{style, SELECTION_TOLERANCE};
|
||||
use document_core::Operation;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
use crate::input::{mouse::ViewportPosition, InputPreprocessor};
|
||||
use crate::tool::{DocumentToolData, Fsm, ToolActionHandlerData};
|
||||
use crate::{message_prelude::*, SvgDocument};
|
||||
|
||||
|
|
@ -11,19 +18,29 @@ pub struct Select {
|
|||
#[impl_message(Message, ToolMessage, Select)]
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum SelectMessage {
|
||||
DragStart,
|
||||
DragStop,
|
||||
MouseMove,
|
||||
Abort,
|
||||
}
|
||||
|
||||
impl<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> for Select {
|
||||
fn process_action(&mut self, action: ToolMessage, data: ToolActionHandlerData<'a>, responses: &mut VecDeque<Message>) {
|
||||
self.fsm_state = self.fsm_state.transition(action, data.0, data.1, &mut self.data, data.2, responses);
|
||||
}
|
||||
advertise_actions!();
|
||||
fn actions(&self) -> ActionList {
|
||||
use SelectToolFsmState::*;
|
||||
match self.fsm_state {
|
||||
Ready => actions!(SelectMessageDiscriminant; DragStart),
|
||||
Dragging => actions!(SelectMessageDiscriminant; DragStop, MouseMove, Abort),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum SelectToolFsmState {
|
||||
Ready,
|
||||
Dragging,
|
||||
}
|
||||
|
||||
impl Default for SelectToolFsmState {
|
||||
|
|
@ -32,29 +49,96 @@ impl Default for SelectToolFsmState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SelectToolData;
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct SelectToolData {
|
||||
drag_start: ViewportPosition,
|
||||
drag_current: ViewportPosition,
|
||||
}
|
||||
|
||||
impl Fsm for SelectToolFsmState {
|
||||
type ToolData = SelectToolData;
|
||||
|
||||
fn transition(
|
||||
self,
|
||||
event: ToolMessage,
|
||||
_document: &SvgDocument,
|
||||
_tool_data: &DocumentToolData,
|
||||
_data: &mut Self::ToolData,
|
||||
_input: &InputPreprocessor,
|
||||
_responses: &mut VecDeque<Message>,
|
||||
) -> Self {
|
||||
fn transition(self, event: ToolMessage, document: &SvgDocument, tool_data: &DocumentToolData, data: &mut Self::ToolData, input: &InputPreprocessor, responses: &mut VecDeque<Message>) -> Self {
|
||||
let transform = document.root.transform;
|
||||
use SelectMessage::*;
|
||||
use SelectToolFsmState::*;
|
||||
if let ToolMessage::Select(event) = event {
|
||||
match (self, event) {
|
||||
(Ready, MouseMove) => self,
|
||||
(Ready, DragStart) => {
|
||||
data.drag_start = input.mouse.position;
|
||||
data.drag_current = input.mouse.position;
|
||||
responses.push_back(Operation::MountWorkingFolder { path: vec![] }.into());
|
||||
Dragging
|
||||
}
|
||||
(Dragging, MouseMove) => {
|
||||
data.drag_current = input.mouse.position;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
responses.push_back(make_operation(data, tool_data, transform));
|
||||
|
||||
Dragging
|
||||
}
|
||||
(Dragging, DragStop) => {
|
||||
data.drag_current = input.mouse.position;
|
||||
|
||||
responses.push_back(Operation::ClearWorkingFolder.into());
|
||||
|
||||
let (point_1, point_2) = if data.drag_start == data.drag_current {
|
||||
let (x, y) = (data.drag_current.x as f64, data.drag_current.y as f64);
|
||||
(
|
||||
DVec2::new(x - SELECTION_TOLERANCE, y - SELECTION_TOLERANCE),
|
||||
DVec2::new(x + SELECTION_TOLERANCE, y + SELECTION_TOLERANCE),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
DVec2::new(data.drag_start.x as f64, data.drag_start.y as f64),
|
||||
DVec2::new(data.drag_current.x as f64, data.drag_current.y as f64),
|
||||
)
|
||||
};
|
||||
|
||||
let quad = [
|
||||
DVec2::new(point_1.x, point_1.y),
|
||||
DVec2::new(point_2.x, point_1.y),
|
||||
DVec2::new(point_2.x, point_2.y),
|
||||
DVec2::new(point_1.x, point_2.y),
|
||||
];
|
||||
|
||||
if data.drag_start == data.drag_current {
|
||||
if let Some(intersection) = document.intersects_quad_root(quad).last() {
|
||||
responses.push_back(DocumentMessage::SelectLayers(vec![intersection.clone()]).into());
|
||||
} else {
|
||||
responses.push_back(DocumentMessage::SelectLayers(vec![]).into());
|
||||
}
|
||||
} else {
|
||||
responses.push_back(DocumentMessage::SelectLayers(document.intersects_quad_root(quad)).into());
|
||||
}
|
||||
|
||||
Ready
|
||||
}
|
||||
(Dragging, Abort) => {
|
||||
responses.push_back(Operation::DiscardWorkingFolder.into());
|
||||
|
||||
Ready
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_operation(data: &SelectToolData, _tool_data: &DocumentToolData, transform: DAffine2) -> Message {
|
||||
let x0 = data.drag_start.x as f64;
|
||||
let y0 = data.drag_start.y as f64;
|
||||
let x1 = data.drag_current.x as f64;
|
||||
let y1 = data.drag_current.y as f64;
|
||||
|
||||
Operation::AddRect {
|
||||
path: vec![],
|
||||
insert_index: -1,
|
||||
transform: (transform.inverse() * glam::DAffine2::from_scale_angle_translation(DVec2::new(x1 - x0, y1 - y0), 0., DVec2::new(x0, y0))).to_cols_array(),
|
||||
style: style::PathStyle::new(Some(Stroke::new(Color::from_rgb8(0x31, 0x94, 0xD6), 2.0)), Some(Fill::none())),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue