Skip to content

Refactor the 'Centroid' node and 'Subpath' struct and methods to use kurbo #3036

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ graphite-proc-macros = { workspace = true }
graph-craft = { workspace = true }
interpreted-executor = { workspace = true }
graphene-std = { workspace = true }
graphene-core = { workspace = true }
preprocessor = { workspace = true }

# Workspace dependencies
Expand All @@ -33,7 +34,6 @@ bitflags = { workspace = true }
thiserror = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
bezier-rs = { workspace = true }
kurbo = { workspace = true }
futures = { workspace = true }
glam = { workspace = true }
Expand Down
25 changes: 15 additions & 10 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@ use crate::messages::tool::tool_messages::select_tool::SelectToolPointerKeys;
use crate::messages::tool::tool_messages::tool_prelude::Key;
use crate::messages::tool::utility_types::ToolType;
use crate::node_graph_executor::NodeGraphExecutor;
use bezier_rs::Subpath;
use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
use graphene_core::subpath::Subpath;
use graphene_std::math::quad::Quad;
use graphene_std::path_bool::{boolean_intersect, path_bool_lib};
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Raster;
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::style::ViewMode;
use kurbo::{Affine, CubicBez, Line, ParamCurve, PathSeg, QuadBez};
use std::path::PathBuf;
use std::time::Duration;

Expand Down Expand Up @@ -2982,10 +2984,10 @@ fn quad_to_path_lib_segments(quad: Quad) -> Vec<path_bool_lib::PathSegment> {
}

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: bezier_rs::Bezier| match bezier.handles {
bezier_rs::BezierHandles::Linear => path_bool_lib::PathSegment::Line(bezier.start, bezier.end),
bezier_rs::BezierHandles::Quadratic { handle } => path_bool_lib::PathSegment::Quadratic(bezier.start, handle, bezier.end),
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => path_bool_lib::PathSegment::Cubic(bezier.start, handle_start, handle_end, bezier.end),
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
.filter_map(|target| {
Expand All @@ -2996,7 +2998,7 @@ fn click_targets_to_path_lib_segments<'a>(click_targets: impl Iterator<Item = &'
}
})
.flatten()
.map(|bezier| segment(bezier.apply_transformation(|x| transform.transform_point2(x))))
.map(|bezier| segment(Affine::new(transform.to_cols_array()) * bezier))
.collect()
}

Expand All @@ -3021,9 +3023,9 @@ impl<'a> ClickXRayIter<'a> {
fn check_layer_area_target(&mut self, click_targets: Option<&Vec<ClickTarget>>, clip: bool, layer: LayerNodeIdentifier, path: Vec<path_bool_lib::PathSegment>, transform: DAffine2) -> XRayResult {
// Convert back to Bezier-rs types for intersections
let segment = |bezier: &path_bool_lib::PathSegment| match *bezier {
path_bool_lib::PathSegment::Line(start, end) => bezier_rs::Bezier::from_linear_dvec2(start, end),
path_bool_lib::PathSegment::Cubic(start, h1, h2, end) => bezier_rs::Bezier::from_cubic_dvec2(start, h1, h2, end),
path_bool_lib::PathSegment::Quadratic(start, h1, end) => bezier_rs::Bezier::from_quadratic_dvec2(start, h1, end),
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);
Expand Down Expand Up @@ -3072,7 +3074,10 @@ impl<'a> ClickXRayIter<'a> {
XRayTarget::Quad(quad) => self.check_layer_area_target(click_targets, clip, layer, quad_to_path_lib_segments(*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(line.start, line.end)).collect();
let polygon = polygon
.iter_closed()
.map(|line| path_bool_lib::PathSegment::Line(point_to_dvec2(line.start()), point_to_dvec2(line.end())))
.collect();
self.check_layer_area_target(click_targets, clip, layer, polygon, transform)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use super::utility_types::TransformIn;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::NodeTemplate;
use crate::messages::prelude::*;
use bezier_rs::Subpath;
use glam::{DAffine2, IVec2};
use graph_craft::document::NodeId;
use graphene_core::subpath::Subpath;
use graphene_std::Artboard;
use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::raster::BlendMode;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface};
use bezier_rs::Subpath;
use glam::{DAffine2, DVec2};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graphene_core::subpath::Subpath;
use graphene_std::vector::PointId;

/// Convert an affine transform into the tuple `(scale, angle, translation, shear)` assuming `shear.y = 0`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{self, InputConnector, NodeNetworkInterface, OutputConnector};
use crate::messages::prelude::*;
use bezier_rs::Subpath;
use glam::{DAffine2, IVec2};
use graph_craft::concrete;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graphene_core::subpath::Subpath;
use graphene_std::Artboard;
use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::raster::BlendMode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1836,7 +1836,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
continue;
};
let quad = Quad::from_box([box_selection_start, box_selection_end_graph]);
if click_targets.node_click_target.intersect_path(|| quad.bezier_lines(), DAffine2::IDENTITY) {
if click_targets.node_click_target.intersect_path(|| quad.to_lines(), DAffine2::IDENTITY) {
nodes.insert(node_id);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::consts::HIDE_HANDLE_DISTANCE;
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::tool::common_functionality::shape_editor::{SelectedLayerState, ShapeState};
use crate::messages::tool::tool_messages::tool_prelude::{DocumentMessageHandler, PreferencesMessageHandler};
use bezier_rs::{Bezier, BezierHandles};
use glam::{DAffine2, DVec2};
use graphene_core::subpath::{Bezier, BezierHandles};
use graphene_std::vector::misc::ManipulatorPointId;
use graphene_std::vector::{PointId, SegmentId};
use wasm_bindgen::JsCast;
Expand Down Expand Up @@ -125,7 +125,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
}

// Get the selected segments and then add a bold line overlay on them
for (segment_id, bezier, _, _) in vector.segment_bezier_iter() {
for (segment_id, bezier, _, _) in vector.segment_iter() {
let Some(selected_shape_state) = shape_editor.selected_shape_state.get_mut(&layer) else {
continue;
};
Expand Down
71 changes: 34 additions & 37 deletions editor/src/messages/portfolio/document/overlays/utility_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ use crate::consts::{
PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER, SEGMENT_SELECTED_THICKNESS,
};
use crate::messages::prelude::Message;
use bezier_rs::{Bezier, Subpath};
use core::borrow::Borrow;
use core::f64::consts::{FRAC_PI_2, PI, TAU};
use glam::{DAffine2, DVec2};
use graphene_core::subpath::Subpath;
use graphene_std::Color;
use graphene_std::math::quad::Quad;
use graphene_std::vector::click_target::ClickTargetType;
use graphene_std::vector::misc::{dvec2_to_point, point_to_dvec2};
use graphene_std::vector::{PointId, SegmentId, Vector};
use kurbo::{self, Affine, CubicBez, ParamCurve, PathSeg};
use std::collections::HashMap;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{OffscreenCanvas, OffscreenCanvasRenderingContext2d};
Expand Down Expand Up @@ -571,11 +573,7 @@ impl OverlayContext {
let handle_start = start + start_vec.perp() * radius * factor;
let handle_end = end - end_vec.perp() * radius * factor;

let bezier = Bezier {
start,
end,
handles: bezier_rs::BezierHandles::Cubic { handle_start, handle_end },
};
let bezier = PathSeg::Cubic(CubicBez::new(dvec2_to_point(start), dvec2_to_point(handle_start), dvec2_to_point(handle_end), dvec2_to_point(end)));

self.bezier_command(bezier, DAffine2::IDENTITY, i == 0);
}
Expand Down Expand Up @@ -762,7 +760,7 @@ impl OverlayContext {

self.render_context.begin_path();
let mut last_point = None;
for (_, bezier, start_id, end_id) in vector.segment_bezier_iter() {
for (_, bezier, start_id, end_id) in vector.segment_iter() {
let move_to = last_point != Some(start_id);
last_point = Some(end_id);

Expand All @@ -776,7 +774,7 @@ impl OverlayContext {
}

/// Used by the Pen tool in order to show how the bezier curve would look like.
pub fn outline_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
pub fn outline_bezier(&mut self, bezier: PathSeg, transform: DAffine2) {
self.start_dpi_aware_transform();

self.render_context.begin_path();
Expand All @@ -788,7 +786,7 @@ impl OverlayContext {
}

/// Used by the path tool segment mode in order to show the selected segments.
pub fn outline_select_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
pub fn outline_select_bezier(&mut self, bezier: PathSeg, transform: DAffine2) {
self.start_dpi_aware_transform();

self.render_context.begin_path();
Expand All @@ -802,7 +800,7 @@ impl OverlayContext {
self.end_dpi_aware_transform();
}

pub fn outline_overlay_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
pub fn outline_overlay_bezier(&mut self, bezier: PathSeg, transform: DAffine2) {
self.start_dpi_aware_transform();

self.render_context.begin_path();
Expand All @@ -816,18 +814,18 @@ impl OverlayContext {
self.end_dpi_aware_transform();
}

fn bezier_command(&self, bezier: Bezier, transform: DAffine2, move_to: bool) {
fn bezier_command(&self, bezier: PathSeg, transform: DAffine2, move_to: bool) {
self.start_dpi_aware_transform();

let Bezier { start, end, handles } = bezier.apply_transformation(|point| transform.transform_point2(point));
let bezier = Affine::new(transform.to_cols_array()) * bezier;
if move_to {
self.render_context.move_to(start.x, start.y);
self.render_context.move_to(bezier.start().x, bezier.start().y);
}

match handles {
bezier_rs::BezierHandles::Linear => self.render_context.line_to(end.x, end.y),
bezier_rs::BezierHandles::Quadratic { handle } => self.render_context.quadratic_curve_to(handle.x, handle.y, end.x, end.y),
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => self.render_context.bezier_curve_to(handle_start.x, handle_start.y, handle_end.x, handle_end.y, end.x, end.y),
match bezier.as_path_el() {
kurbo::PathEl::LineTo(point) => self.render_context.line_to(point.x, point.y),
kurbo::PathEl::QuadTo(point, point1) => self.render_context.quadratic_curve_to(point.x, point.y, point1.x, point1.y),
kurbo::PathEl::CurveTo(point, point1, point2) => self.render_context.bezier_curve_to(point.x, point.y, point1.x, point1.y, point2.x, point2.y),
_ => unreachable!(),
}

self.end_dpi_aware_transform();
Expand All @@ -841,36 +839,35 @@ impl OverlayContext {
let subpath = subpath.borrow();
let mut curves = subpath.iter().peekable();

let Some(first) = curves.peek() else {
let Some(&first) = curves.peek() else {
continue;
};

self.render_context.move_to(transform.transform_point2(first.start()).x, transform.transform_point2(first.start()).y);
let start_point = transform.transform_point2(point_to_dvec2(first.start()));
self.render_context.move_to(start_point.x, start_point.y);

for curve in curves {
match curve.handles {
bezier_rs::BezierHandles::Linear => {
let a = transform.transform_point2(curve.end());
match curve {
PathSeg::Line(line) => {
let a = transform.transform_point2(point_to_dvec2(line.p1));
let a = a.round() - DVec2::splat(0.5);

self.render_context.line_to(a.x, a.y)
self.render_context.line_to(a.x, a.y);
}
bezier_rs::BezierHandles::Quadratic { handle } => {
let a = transform.transform_point2(handle);
let b = transform.transform_point2(curve.end());
PathSeg::Quad(quad_bez) => {
let a = transform.transform_point2(point_to_dvec2(quad_bez.p1));
let b = transform.transform_point2(point_to_dvec2(quad_bez.p2));
let a = a.round() - DVec2::splat(0.5);
let b = b.round() - DVec2::splat(0.5);

self.render_context.quadratic_curve_to(a.x, a.y, b.x, b.y)
self.render_context.quadratic_curve_to(a.x, a.y, b.x, b.y);
}
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => {
let a = transform.transform_point2(handle_start);
let b = transform.transform_point2(handle_end);
let c = transform.transform_point2(curve.end());
PathSeg::Cubic(cubic_bez) => {
let a = transform.transform_point2(point_to_dvec2(cubic_bez.p1));
let b = transform.transform_point2(point_to_dvec2(cubic_bez.p2));
let c = transform.transform_point2(point_to_dvec2(cubic_bez.p3));
let a = a.round() - DVec2::splat(0.5);
let b = b.round() - DVec2::splat(0.5);
let c = c.round() - DVec2::splat(0.5);

self.render_context.bezier_curve_to(a.x, a.y, b.x, b.y, c.x, c.y)
self.render_context.bezier_curve_to(a.x, a.y, b.x, b.y, c.x, c.y);
}
}
}
Expand All @@ -885,7 +882,7 @@ impl OverlayContext {

/// Used by the Select tool to outline a path or a free point when selected or hovered.
pub fn outline(&mut self, target_types: impl Iterator<Item = impl Borrow<ClickTargetType>>, transform: DAffine2, color: Option<&str>) {
let mut subpaths: Vec<bezier_rs::Subpath<PointId>> = vec![];
let mut subpaths: Vec<Subpath<PointId>> = vec![];

target_types.for_each(|target_type| match target_type.borrow() {
ClickTargetType::FreePoint(point) => {
Expand Down
Loading