Skip to content

Commit 7b5505f

Browse files
indierustyKeavon
authored andcommitted
fix selection insideness checking
1 parent 59d1439 commit 7b5505f

File tree

2 files changed

+16
-7
lines changed

2 files changed

+16
-7
lines changed

node-graph/gcore/src/vector/algorithms/bezpath_algorithms.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ pub fn round_line_join(bezpath1: &BezPath, bezpath2: &BezPath, center: DVec2) ->
608608
}
609609

610610
/// Returns `true` if the `bezpath1` is completely inside the `bezpath2`.
611+
/// NOTE: The `bezpath2` has to be a closed path to get correct result.
611612
pub fn bezpath_is_inside_bezpath(bezpath1: &BezPath, bezpath2: &BezPath, accuracy: Option<f64>, minimum_separation: Option<f64>) -> bool {
612613
// Eliminate any possibility of one being inside the other, if either of them is empty
613614
if bezpath1.is_empty() || bezpath2.is_empty() {
@@ -617,19 +618,25 @@ pub fn bezpath_is_inside_bezpath(bezpath1: &BezPath, bezpath2: &BezPath, accurac
617618
let inner_bbox = bezpath1.bounding_box();
618619
let outer_bbox = bezpath2.bounding_box();
619620

620-
// Eliminate 'bezpath1' if its bounding box is completely outside the bezpath2's bounding box
621+
// Eliminate if the 'bezpath1' bounding box is completely outside the bezpath2's bounding box
621622
if !outer_bbox.contains_rect(inner_bbox) && outer_bbox.intersect(inner_bbox).is_zero_area() {
622623
return false;
623624
}
624625

625-
// Eliminate this subpath if it intersects with the other subpath.
626+
// Eliminate if any anchors point of the 'bezpath1' is outside the 'bezpath2'.
627+
if !bezpath1.elements().iter().filter_map(|elm| elm.end_point()).all(|point| bezpath2.contains(point)) {
628+
return false;
629+
}
630+
631+
// Eliminate if 'bezpath1' intersects with 'bezpath2'.
626632
if !bezpath_intersections(bezpath1, &bezpath2, accuracy, minimum_separation).is_empty() {
627633
return false;
628634
}
629635

630636
// At this point:
631637
// (1) The 'bezpath1' bounding box either intersect or is inside the 'bezpath2's bounding box,
632-
// (2) The 'bezpath1' is not intersecting with the 'bezpath2'.
638+
// (2) All the anchor point of the 'bezpath1' is inside 'bezpath2'
639+
// (3) The 'bezpath1' is not intersecting with the 'bezpath2'.
633640
// Hence, this subpath is completely inside the given other subpath.
634641
true
635642
}

node-graph/gcore/src/vector/click_target.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use super::algorithms::intersection::filtered_segment_intersections;
1+
use super::algorithms::{bezpath_algorithms::bezpath_is_inside_bezpath, intersection::filtered_segment_intersections};
22
use super::misc::dvec2_to_point;
33
use crate::math::math_ext::QuadExt;
44
use crate::math::quad::Quad;
55
use crate::subpath::Subpath;
66
use crate::vector::PointId;
77
use crate::vector::misc::point_to_dvec2;
88
use glam::{DAffine2, DMat2, DVec2};
9-
use kurbo::{Affine, ParamCurve, PathSeg, Point, Shape};
9+
use kurbo::{Affine, BezPath, ParamCurve, PathSeg, Shape};
1010

1111
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
1212
pub struct FreePoint {
@@ -125,9 +125,11 @@ impl ClickTarget {
125125
return true;
126126
}
127127

128+
let mut selection = BezPath::from_path_segments(bezier_iter());
129+
selection.close_path();
130+
128131
// Check if shape is entirely within selection
129-
let any_point_from_subpath = subpath.manipulator_groups().first().map(|manipulators| manipulators.anchor);
130-
any_point_from_subpath.is_some_and(|shape_point| bezier_iter().map(|bezier| bezier.winding(Point::new(shape_point.x, shape_point.y))).sum::<i32>() != 0)
132+
bezpath_is_inside_bezpath(&subpath.to_bezpath(), &selection, None, None)
131133
}
132134
ClickTargetType::FreePoint(point) => bezier_iter().map(|bezier: PathSeg| bezier.winding(dvec2_to_point(point.position))).sum::<i32>() != 0,
133135
}

0 commit comments

Comments
 (0)