mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-03 21:08:18 +00:00
Use a coarse bounding box to avoid a detailed check for intersection when clicking artwork (#1887)
* Add bounding box check for intersection * Cache the bounding box * private subpath
This commit is contained in:
parent
32f5fba3e3
commit
ea44d1440a
5 changed files with 65 additions and 27 deletions
|
@ -23,11 +23,21 @@ use vello::*;
|
|||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct ClickTarget {
|
||||
pub subpath: bezier_rs::Subpath<PointId>,
|
||||
pub stroke_width: f64,
|
||||
subpath: bezier_rs::Subpath<PointId>,
|
||||
stroke_width: f64,
|
||||
bounding_box: Option<[DVec2; 2]>,
|
||||
}
|
||||
|
||||
impl ClickTarget {
|
||||
pub fn new(subpath: bezier_rs::Subpath<PointId>, stroke_width: f64) -> Self {
|
||||
let bounding_box = subpath.loose_bounding_box();
|
||||
Self { subpath, stroke_width, bounding_box }
|
||||
}
|
||||
|
||||
pub fn subpath(&self) -> &bezier_rs::Subpath<PointId> {
|
||||
&self.subpath
|
||||
}
|
||||
|
||||
/// Does the click target intersect the rectangle
|
||||
pub fn intersect_rectangle(&self, document_quad: Quad, layer_transform: DAffine2) -> bool {
|
||||
// Check if the matrix is not invertible
|
||||
|
@ -60,9 +70,19 @@ impl ClickTarget {
|
|||
|
||||
/// Does the click target intersect the point (accounting for stroke size)
|
||||
pub fn intersect_point(&self, point: DVec2, layer_transform: DAffine2) -> bool {
|
||||
let target_bounds = [point - DVec2::splat(self.stroke_width / 2.), point + DVec2::splat(self.stroke_width / 2.)];
|
||||
let intersects = |a: [DVec2; 2], b: [DVec2; 2]| a[0].x <= b[1].x && a[1].x >= b[0].x && a[0].y <= b[1].y && a[1].y >= b[0].y;
|
||||
// This bounding box is not very accurate as it is the axis aligned version of the transformed bounding box. However it is fast.
|
||||
if !self
|
||||
.bounding_box
|
||||
.is_some_and(|loose| intersects((layer_transform * Quad::from_box(loose)).bounding_box(), target_bounds))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allows for selecting lines
|
||||
// TODO: actual intersection of stroke
|
||||
let inflated_quad = Quad::from_box([point - DVec2::splat(self.stroke_width / 2.), point + DVec2::splat(self.stroke_width / 2.)]);
|
||||
let inflated_quad = Quad::from_box(target_bounds);
|
||||
self.intersect_rectangle(inflated_quad, layer_transform)
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +363,7 @@ impl GraphicElementRendered for VectorData {
|
|||
}
|
||||
subpath
|
||||
};
|
||||
click_targets.extend(self.stroke_bezier_paths().map(fill).map(|subpath| ClickTarget { stroke_width, subpath }));
|
||||
click_targets.extend(self.stroke_bezier_paths().map(fill).map(|subpath| ClickTarget::new(subpath, stroke_width)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "vello")]
|
||||
|
@ -558,7 +578,7 @@ impl GraphicElementRendered for Artboard {
|
|||
fn add_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
let mut subpath = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2());
|
||||
subpath.apply_transform(self.graphic_group.transform.inverse());
|
||||
click_targets.push(ClickTarget { stroke_width: 0., subpath });
|
||||
click_targets.push(ClickTarget::new(subpath, 0.));
|
||||
}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
|
@ -674,7 +694,7 @@ impl GraphicElementRendered for ImageFrame<Color> {
|
|||
|
||||
fn add_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
|
||||
click_targets.push(ClickTarget { subpath, stroke_width: 0. });
|
||||
click_targets.push(ClickTarget::new(subpath, 0.));
|
||||
}
|
||||
|
||||
#[cfg(feature = "vello")]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue