mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 15:28:04 +08: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:
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user