mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Replace the Boolean Operations node's algorithm with the Linesweeper library (#2670)
* Attempt one-shot n-ary ops * Make it not crash * Try to remove more path_bool * Add quantization * Update to latest * Nits * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -29,7 +29,7 @@ use glam::{DAffine2, DVec2, IVec2};
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
|
||||
use graphene_std::math::quad::Quad;
|
||||
use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
|
||||
use graphene_std::path_bool::boolean_intersect;
|
||||
use graphene_std::raster::BlendMode;
|
||||
use graphene_std::raster_types::Raster;
|
||||
use graphene_std::render_node::wgpu_available;
|
||||
@@ -37,9 +37,9 @@ use graphene_std::subpath::Subpath;
|
||||
use graphene_std::table::Table;
|
||||
use graphene_std::vector::PointId;
|
||||
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
||||
use graphene_std::vector::misc::{dvec2_to_point, point_to_dvec2};
|
||||
use graphene_std::vector::misc::dvec2_to_point;
|
||||
use graphene_std::vector::style::RenderMode;
|
||||
use kurbo::{Affine, CubicBez, Line, ParamCurve, PathSeg, QuadBez};
|
||||
use kurbo::{Affine, BezPath, Line, PathSeg};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -2982,7 +2982,7 @@ fn default_document_network_interface() -> NodeNetworkInterface {
|
||||
enum XRayTarget {
|
||||
Point(DVec2),
|
||||
Quad(Quad),
|
||||
Path(Vec<path_bool_lib::PathSegment>),
|
||||
Path(BezPath),
|
||||
Polygon(Subpath<PointId>),
|
||||
}
|
||||
|
||||
@@ -3000,17 +3000,12 @@ pub struct ClickXRayIter<'a> {
|
||||
parent_targets: Vec<(LayerNodeIdentifier, XRayTarget)>,
|
||||
}
|
||||
|
||||
fn quad_to_path_lib_segments(quad: Quad) -> Vec<path_bool_lib::PathSegment> {
|
||||
quad.all_edges().into_iter().map(|[start, end]| path_bool_lib::PathSegment::Line(start, end)).collect()
|
||||
fn quad_to_kurbo(quad: Quad) -> BezPath {
|
||||
BezPath::from_path_segments(quad.all_edges().into_iter().map(|[start, end]| PathSeg::Line(Line::new(dvec2_to_point(start), dvec2_to_point(end)))))
|
||||
}
|
||||
|
||||
fn click_targets_to_path_lib_segments<'a>(click_targets: impl Iterator<Item = &'a ClickTarget>, transform: DAffine2) -> Vec<path_bool_lib::PathSegment> {
|
||||
let segment = |bezier: PathSeg| match bezier {
|
||||
PathSeg::Line(line) => path_bool_lib::PathSegment::Line(point_to_dvec2(line.p0), point_to_dvec2(line.p1)),
|
||||
PathSeg::Quad(quad_bez) => path_bool_lib::PathSegment::Quadratic(point_to_dvec2(quad_bez.p0), point_to_dvec2(quad_bez.p1), point_to_dvec2(quad_bez.p2)),
|
||||
PathSeg::Cubic(cubic_bez) => path_bool_lib::PathSegment::Cubic(point_to_dvec2(cubic_bez.p0), point_to_dvec2(cubic_bez.p1), point_to_dvec2(cubic_bez.p2), point_to_dvec2(cubic_bez.p3)),
|
||||
};
|
||||
click_targets
|
||||
fn click_targets_to_kurbo<'a>(click_targets: impl Iterator<Item = &'a ClickTarget>, transform: DAffine2) -> BezPath {
|
||||
let segments = click_targets
|
||||
.filter_map(|target| {
|
||||
if let ClickTargetType::Subpath(subpath) = target.target_type() {
|
||||
Some(subpath.iter())
|
||||
@@ -3019,8 +3014,8 @@ fn click_targets_to_path_lib_segments<'a>(click_targets: impl Iterator<Item = &'
|
||||
}
|
||||
})
|
||||
.flatten()
|
||||
.map(|bezier| segment(Affine::new(transform.to_cols_array()) * bezier))
|
||||
.collect()
|
||||
.map(|bezier| Affine::new(transform.to_cols_array()) * bezier);
|
||||
BezPath::from_path_segments(segments)
|
||||
}
|
||||
|
||||
impl<'a> ClickXRayIter<'a> {
|
||||
@@ -3041,22 +3036,8 @@ impl<'a> ClickXRayIter<'a> {
|
||||
}
|
||||
|
||||
/// Handles the checking of the layer where the target is a rect or path
|
||||
fn check_layer_area_target(
|
||||
&mut self,
|
||||
click_targets: Option<&[Arc<ClickTarget>]>,
|
||||
clip: bool,
|
||||
layer: LayerNodeIdentifier,
|
||||
path: Vec<path_bool_lib::PathSegment>,
|
||||
transform: DAffine2,
|
||||
) -> XRayResult {
|
||||
// Convert back to Kurbo types for intersections
|
||||
let segment = |bezier: &path_bool_lib::PathSegment| match *bezier {
|
||||
path_bool_lib::PathSegment::Line(start, end) => PathSeg::Line(Line::new(dvec2_to_point(start), dvec2_to_point(end))),
|
||||
path_bool_lib::PathSegment::Cubic(start, h1, h2, end) => PathSeg::Cubic(CubicBez::new(dvec2_to_point(start), dvec2_to_point(h1), dvec2_to_point(h2), dvec2_to_point(end))),
|
||||
path_bool_lib::PathSegment::Quadratic(start, h1, end) => PathSeg::Quad(QuadBez::new(dvec2_to_point(start), dvec2_to_point(h1), dvec2_to_point(end))),
|
||||
path_bool_lib::PathSegment::Arc(_, _, _, _, _, _, _) => unimplemented!(),
|
||||
};
|
||||
let get_clip = || path.iter().map(segment);
|
||||
fn check_layer_area_target(&mut self, click_targets: Option<&[Arc<ClickTarget>]>, clip: bool, layer: LayerNodeIdentifier, path: BezPath, transform: DAffine2) -> XRayResult {
|
||||
let get_clip = || path.segments();
|
||||
|
||||
let intersects = click_targets.is_some_and(|targets| targets.iter().any(|target| target.intersect_path(get_clip, transform)));
|
||||
let clicked = intersects;
|
||||
@@ -3065,8 +3046,9 @@ impl<'a> ClickXRayIter<'a> {
|
||||
// In the case of a clip path where the area partially intersects, it is necessary to do a boolean operation.
|
||||
// We do this on this using the target area to reduce computation (as the target area is usually very simple).
|
||||
if clip && intersects {
|
||||
let clip_path = click_targets_to_path_lib_segments(click_targets.iter().flat_map(|x| x.iter()).map(|x| x.as_ref()), transform);
|
||||
let subtracted = boolean_intersect(path, clip_path).into_iter().flatten().collect::<Vec<_>>();
|
||||
let clip_path = click_targets_to_kurbo(click_targets.iter().flat_map(|x| x.iter()).map(|x| x.as_ref()), transform);
|
||||
let intersection = boolean_intersect(&path, &clip_path);
|
||||
let subtracted = BezPath::from_path_segments(intersection.iter().flat_map(|p| p.segments()));
|
||||
if subtracted.is_empty() {
|
||||
use_children = false;
|
||||
} else {
|
||||
@@ -3099,13 +3081,10 @@ impl<'a> ClickXRayIter<'a> {
|
||||
use_children: !clip || intersects,
|
||||
}
|
||||
}
|
||||
XRayTarget::Quad(quad) => self.check_layer_area_target(click_targets, clip, layer, quad_to_path_lib_segments(*quad), transform),
|
||||
XRayTarget::Quad(quad) => self.check_layer_area_target(click_targets, clip, layer, quad_to_kurbo(*quad), transform),
|
||||
XRayTarget::Path(path) => self.check_layer_area_target(click_targets, clip, layer, path.clone(), transform),
|
||||
XRayTarget::Polygon(polygon) => {
|
||||
let polygon = polygon
|
||||
.iter_closed()
|
||||
.map(|line| path_bool_lib::PathSegment::Line(point_to_dvec2(line.start()), point_to_dvec2(line.end())))
|
||||
.collect();
|
||||
let polygon = BezPath::from_path_segments(polygon.iter_closed());
|
||||
self.check_layer_area_target(click_targets, clip, layer, polygon, transform)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user