mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-23 14:34:06 +00:00
Refactor the node graph UI wires to render using Kurbo (#2994)
* impl function to check bezpath insideness * refactor network interface wires to use kurbo * refactor * refactor * fix adding MoveTo instead of LineTo to the grid aligned wire bezpath --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
0f638314dc
commit
b1f2cf706e
5 changed files with 107 additions and 126 deletions
|
@ -20,14 +20,13 @@ use crate::messages::tool::common_functionality::graph_modification_utils::{self
|
||||||
use crate::messages::tool::common_functionality::utility_functions::make_path_editable_is_allowed;
|
use crate::messages::tool::common_functionality::utility_functions::make_path_editable_is_allowed;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
use bezier_rs::Subpath;
|
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
use graphene_std::math::math_ext::QuadExt;
|
use graphene_std::math::math_ext::QuadExt;
|
||||||
use graphene_std::vector::misc::subpath_to_kurbo_bezpath;
|
use graphene_std::vector::algorithms::bezpath_algorithms::bezpath_is_inside_bezpath;
|
||||||
use graphene_std::*;
|
use graphene_std::*;
|
||||||
use kurbo::{Line, Point};
|
use kurbo::{DEFAULT_ACCURACY, Shape};
|
||||||
use renderer::Quad;
|
use renderer::Quad;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
@ -958,8 +957,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||||
to_connector_is_layer,
|
to_connector_is_layer,
|
||||||
GraphWireStyle::Direct,
|
GraphWireStyle::Direct,
|
||||||
);
|
);
|
||||||
let mut path_string = String::new();
|
let path_string = vector_wire.to_svg();
|
||||||
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
|
||||||
let wire_path = WirePath {
|
let wire_path = WirePath {
|
||||||
path_string,
|
path_string,
|
||||||
data_type: self.wire_in_progress_type,
|
data_type: self.wire_in_progress_type,
|
||||||
|
@ -1196,7 +1194,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||||
.filter(|input| input.1.as_value().is_some())
|
.filter(|input| input.1.as_value().is_some())
|
||||||
.map(|input| input.0);
|
.map(|input| input.0);
|
||||||
if let Some(selected_node_input_connect_index) = selected_node_input_connect_index {
|
if let Some(selected_node_input_connect_index) = selected_node_input_connect_index {
|
||||||
let Some(bounding_box) = network_interface.node_bounding_box(&selected_node_id, selection_network_path) else {
|
let Some(node_bbox) = network_interface.node_bounding_box(&selected_node_id, selection_network_path) else {
|
||||||
log::error!("Could not get bounding box for node: {selected_node_id}");
|
log::error!("Could not get bounding box for node: {selected_node_id}");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -1220,31 +1218,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||||
log::debug!("preferences.graph_wire_style: {:?}", preferences.graph_wire_style);
|
log::debug!("preferences.graph_wire_style: {:?}", preferences.graph_wire_style);
|
||||||
let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
|
let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
|
||||||
|
|
||||||
let bbox_rect = kurbo::Rect::new(bounding_box[0].x, bounding_box[0].y, bounding_box[1].x, bounding_box[1].y);
|
let node_bbox = kurbo::Rect::new(node_bbox[0].x, node_bbox[0].y, node_bbox[1].x, node_bbox[1].y).to_path(DEFAULT_ACCURACY);
|
||||||
|
let inside = bezpath_is_inside_bezpath(&wire, &node_bbox, None, None);
|
||||||
|
|
||||||
let p1 = DVec2::new(bbox_rect.x0, bbox_rect.y0);
|
let intersect = wire
|
||||||
let p2 = DVec2::new(bbox_rect.x1, bbox_rect.y0);
|
.segments()
|
||||||
let p3 = DVec2::new(bbox_rect.x1, bbox_rect.y1);
|
.any(|segment| node_bbox.segments().filter_map(|segment| segment.as_line()).any(|line| !segment.intersect_line(line).is_empty()));
|
||||||
let p4 = DVec2::new(bbox_rect.x0, bbox_rect.y1);
|
|
||||||
let ps = [p1, p2, p3, p4];
|
|
||||||
|
|
||||||
let inside = wire.is_inside_subpath(&Subpath::from_anchors_linear(ps, true), None, None);
|
|
||||||
|
|
||||||
let wire = subpath_to_kurbo_bezpath(wire);
|
|
||||||
|
|
||||||
let intersect = wire.segments().any(|segment| {
|
|
||||||
let rect = kurbo::Rect::new(bounding_box[0].x, bounding_box[0].y, bounding_box[1].x, bounding_box[1].y);
|
|
||||||
|
|
||||||
let top_line = Line::new(Point::new(rect.x0, rect.y0), Point::new(rect.x1, rect.y0));
|
|
||||||
let bottom_line = Line::new(Point::new(rect.x0, rect.y1), Point::new(rect.x1, rect.y1));
|
|
||||||
let left_line = Line::new(Point::new(rect.x0, rect.y0), Point::new(rect.x0, rect.y1));
|
|
||||||
let right_line = Line::new(Point::new(rect.x1, rect.y0), Point::new(rect.x1, rect.y1));
|
|
||||||
|
|
||||||
!segment.intersect_line(top_line).is_empty()
|
|
||||||
|| !segment.intersect_line(bottom_line).is_empty()
|
|
||||||
|| !segment.intersect_line(left_line).is_empty()
|
|
||||||
|| !segment.intersect_line(right_line).is_empty()
|
|
||||||
});
|
|
||||||
|
|
||||||
(intersect || inside).then_some((input, is_stack))
|
(intersect || inside).then_some((input, is_stack))
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,6 +21,7 @@ use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
||||||
use graphene_std::vector::{PointId, Vector, VectorModificationType};
|
use graphene_std::vector::{PointId, Vector, VectorModificationType};
|
||||||
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
|
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypes;
|
||||||
use interpreted_executor::node_registry::NODE_REGISTRY;
|
use interpreted_executor::node_registry::NODE_REGISTRY;
|
||||||
|
use kurbo::BezPath;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||||
|
@ -2713,8 +2714,7 @@ impl NodeNetworkInterface {
|
||||||
let thick = vertical_end && vertical_start;
|
let thick = vertical_end && vertical_start;
|
||||||
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style);
|
let vector_wire = build_vector_wire(output_position, input_position, vertical_start, vertical_end, graph_wire_style);
|
||||||
|
|
||||||
let mut path_string = String::new();
|
let path_string = vector_wire.to_svg();
|
||||||
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
|
||||||
let data_type = FrontendGraphDataType::from_type(&self.input_type(&input, network_path).0);
|
let data_type = FrontendGraphDataType::from_type(&self.input_type(&input, network_path).0);
|
||||||
let wire_path_update = Some(WirePath {
|
let wire_path_update = Some(WirePath {
|
||||||
path_string,
|
path_string,
|
||||||
|
@ -2731,14 +2731,14 @@ impl NodeNetworkInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the vector subpath and a boolean of whether the wire should be thick.
|
/// Returns the vector subpath and a boolean of whether the wire should be thick.
|
||||||
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(Subpath<PointId>, bool)> {
|
pub fn vector_wire_from_input(&mut self, input: &InputConnector, wire_style: GraphWireStyle, network_path: &[NodeId]) -> Option<(BezPath, bool)> {
|
||||||
let Some(input_position) = self.get_input_center(input, network_path) else {
|
let Some(input_position) = self.get_input_center(input, network_path) else {
|
||||||
log::error!("Could not get dom rect for wire end: {:?}", input);
|
log::error!("Could not get dom rect for wire end: {:?}", input);
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
// An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector
|
// An upstream output could not be found, so the wire does not exist, but it should still be loaded as as empty vector
|
||||||
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
|
let Some(upstream_output) = self.upstream_output_connector(input, network_path) else {
|
||||||
return Some((Subpath::from_anchors(std::iter::empty(), false), false));
|
return Some((BezPath::new(), false));
|
||||||
};
|
};
|
||||||
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
|
let Some(output_position) = self.get_output_center(&upstream_output, network_path) else {
|
||||||
log::error!("Could not get dom rect for wire start: {:?}", upstream_output);
|
log::error!("Could not get dom rect for wire start: {:?}", upstream_output);
|
||||||
|
@ -2752,8 +2752,7 @@ impl NodeNetworkInterface {
|
||||||
|
|
||||||
pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePath> {
|
pub fn wire_path_from_input(&mut self, input: &InputConnector, graph_wire_style: GraphWireStyle, dashed: bool, network_path: &[NodeId]) -> Option<WirePath> {
|
||||||
let (vector_wire, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
|
let (vector_wire, thick) = self.vector_wire_from_input(input, graph_wire_style, network_path)?;
|
||||||
let mut path_string = String::new();
|
let path_string = vector_wire.to_svg();
|
||||||
let _ = vector_wire.subpath_to_svg(&mut path_string, DAffine2::IDENTITY);
|
|
||||||
let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0);
|
let data_type = FrontendGraphDataType::from_type(&self.input_type(input, network_path).0);
|
||||||
Some(WirePath {
|
Some(WirePath {
|
||||||
path_string,
|
path_string,
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
|
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
|
||||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
|
||||||
use glam::{DVec2, IVec2};
|
use glam::{DVec2, IVec2};
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::{uuid::NodeId, vector::misc::dvec2_to_point};
|
||||||
use graphene_std::vector::PointId;
|
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct WirePath {
|
pub struct WirePath {
|
||||||
|
@ -53,7 +52,7 @@ impl GraphWireStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical_out: bool, vertical_in: bool, graph_wire_style: GraphWireStyle) -> Subpath<PointId> {
|
pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical_out: bool, vertical_in: bool, graph_wire_style: GraphWireStyle) -> BezPath {
|
||||||
let grid_spacing = 24.;
|
let grid_spacing = 24.;
|
||||||
match graph_wire_style {
|
match graph_wire_style {
|
||||||
GraphWireStyle::Direct => {
|
GraphWireStyle::Direct => {
|
||||||
|
@ -85,44 +84,21 @@ pub fn build_vector_wire(output_position: DVec2, input_position: DVec2, vertical
|
||||||
let delta01 = DVec2::new((locations[1].x - locations[0].x) * smoothing, (locations[1].y - locations[0].y) * smoothing);
|
let delta01 = DVec2::new((locations[1].x - locations[0].x) * smoothing, (locations[1].y - locations[0].y) * smoothing);
|
||||||
let delta23 = DVec2::new((locations[3].x - locations[2].x) * smoothing, (locations[3].y - locations[2].y) * smoothing);
|
let delta23 = DVec2::new((locations[3].x - locations[2].x) * smoothing, (locations[3].y - locations[2].y) * smoothing);
|
||||||
|
|
||||||
Subpath::new(
|
let mut wire = BezPath::new();
|
||||||
vec![
|
wire.move_to(dvec2_to_point(locations[0]));
|
||||||
ManipulatorGroup {
|
wire.line_to(dvec2_to_point(locations[1]));
|
||||||
anchor: locations[0],
|
wire.curve_to(dvec2_to_point(locations[1] + delta01), dvec2_to_point(locations[2] - delta23), dvec2_to_point(locations[2]));
|
||||||
in_handle: None,
|
wire.line_to(dvec2_to_point(locations[3]));
|
||||||
out_handle: None,
|
wire
|
||||||
id: PointId::generate(),
|
|
||||||
},
|
|
||||||
ManipulatorGroup {
|
|
||||||
anchor: locations[1],
|
|
||||||
in_handle: None,
|
|
||||||
out_handle: Some(locations[1] + delta01),
|
|
||||||
id: PointId::generate(),
|
|
||||||
},
|
|
||||||
ManipulatorGroup {
|
|
||||||
anchor: locations[2],
|
|
||||||
in_handle: Some(locations[2] - delta23),
|
|
||||||
out_handle: None,
|
|
||||||
id: PointId::generate(),
|
|
||||||
},
|
|
||||||
ManipulatorGroup {
|
|
||||||
anchor: locations[3],
|
|
||||||
in_handle: None,
|
|
||||||
out_handle: None,
|
|
||||||
id: PointId::generate(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
GraphWireStyle::GridAligned => {
|
GraphWireStyle::GridAligned => {
|
||||||
let locations = straight_wire_paths(output_position, input_position, vertical_out, vertical_in);
|
let locations = straight_wire_path(output_position, input_position, vertical_out, vertical_in);
|
||||||
straight_wire_subpath(locations)
|
straight_wire_to_bezpath(locations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn straight_wire_paths(output_position: DVec2, input_position: DVec2, vertical_out: bool, vertical_in: bool) -> Vec<IVec2> {
|
fn straight_wire_path(output_position: DVec2, input_position: DVec2, vertical_out: bool, vertical_in: bool) -> Vec<IVec2> {
|
||||||
let grid_spacing = 24;
|
let grid_spacing = 24;
|
||||||
let line_width = 2;
|
let line_width = 2;
|
||||||
|
|
||||||
|
@ -446,40 +422,24 @@ fn straight_wire_paths(output_position: DVec2, input_position: DVec2, vertical_o
|
||||||
vec![IVec2::new(x1, y1), IVec2::new(x20, y1), IVec2::new(x20, y3), IVec2::new(x4, y3)]
|
vec![IVec2::new(x1, y1), IVec2::new(x20, y1), IVec2::new(x20, y3), IVec2::new(x4, y3)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn straight_wire_subpath(locations: Vec<IVec2>) -> Subpath<PointId> {
|
fn straight_wire_to_bezpath(locations: Vec<IVec2>) -> BezPath {
|
||||||
if locations.is_empty() {
|
if locations.is_empty() {
|
||||||
return Subpath::new(Vec::new(), false);
|
return BezPath::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let to_point = |location: IVec2| Point::new(location.x as f64, location.y as f64);
|
||||||
|
|
||||||
if locations.len() == 2 {
|
if locations.len() == 2 {
|
||||||
return Subpath::new(
|
let p1 = to_point(locations[0]);
|
||||||
vec![
|
let p2 = to_point(locations[1]);
|
||||||
ManipulatorGroup {
|
Line::new(p1, p2).to_path(DEFAULT_ACCURACY);
|
||||||
anchor: locations[0].into(),
|
|
||||||
in_handle: None,
|
|
||||||
out_handle: None,
|
|
||||||
id: PointId::generate(),
|
|
||||||
},
|
|
||||||
ManipulatorGroup {
|
|
||||||
anchor: locations[1].into(),
|
|
||||||
in_handle: None,
|
|
||||||
out_handle: None,
|
|
||||||
id: PointId::generate(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let corner_radius = 10;
|
let corner_radius = 10;
|
||||||
|
|
||||||
// Create path with rounded corners
|
// Create path with rounded corners
|
||||||
let mut path = vec![ManipulatorGroup {
|
let mut path = BezPath::new();
|
||||||
anchor: locations[0].into(),
|
path.move_to(to_point(locations[0]));
|
||||||
in_handle: None,
|
|
||||||
out_handle: None,
|
|
||||||
id: PointId::generate(),
|
|
||||||
}];
|
|
||||||
|
|
||||||
for i in 1..(locations.len() - 1) {
|
for i in 1..(locations.len() - 1) {
|
||||||
let prev = locations[i - 1];
|
let prev = locations[i - 1];
|
||||||
|
@ -563,27 +523,9 @@ fn straight_wire_subpath(locations: Vec<IVec2>) -> Subpath<PointId> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
path.extend(vec![
|
path.line_to(to_point(corner_start));
|
||||||
ManipulatorGroup {
|
path.curve_to(to_point(corner_start_mid), to_point(corner_end_mid), to_point(corner_end));
|
||||||
anchor: corner_start.into(),
|
|
||||||
in_handle: None,
|
|
||||||
out_handle: Some(corner_start_mid.into()),
|
|
||||||
id: PointId::generate(),
|
|
||||||
},
|
|
||||||
ManipulatorGroup {
|
|
||||||
anchor: corner_end.into(),
|
|
||||||
in_handle: Some(corner_end_mid.into()),
|
|
||||||
out_handle: None,
|
|
||||||
id: PointId::generate(),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
path.line_to(to_point(*locations.last().unwrap()));
|
||||||
path.push(ManipulatorGroup {
|
path
|
||||||
anchor: (*locations.last().unwrap()).into(),
|
|
||||||
in_handle: None,
|
|
||||||
out_handle: None,
|
|
||||||
id: PointId::generate(),
|
|
||||||
});
|
|
||||||
Subpath::new(path, false)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -466,3 +466,70 @@ pub fn round_line_join(bezpath1: &BezPath, bezpath2: &BezPath, center: DVec2) ->
|
||||||
|
|
||||||
compute_circular_subpath_details(left, arc_point, right, center, Some(angle))
|
compute_circular_subpath_details(left, arc_point, right, center, Some(angle))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the `bezpath1` is completely inside the `bezpath2`.
|
||||||
|
pub fn bezpath_is_inside_bezpath(bezpath1: &BezPath, bezpath2: &BezPath, accuracy: Option<f64>, minimum_separation: Option<f64>) -> bool {
|
||||||
|
// Eliminate any possibility of one being inside the other, if either of them is empty
|
||||||
|
if bezpath1.is_empty() || bezpath2.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inner_bbox = bezpath1.bounding_box();
|
||||||
|
let outer_bbox = bezpath2.bounding_box();
|
||||||
|
|
||||||
|
// Eliminate bezpath1 if its bounding box is not completely inside the bezpath2's bounding box.
|
||||||
|
// Reasoning:
|
||||||
|
// If the inner bezpath bounding box is larger than the outer bezpath bounding box in any direction
|
||||||
|
// then the inner bezpath is intersecting with or outside the outer bezpath.
|
||||||
|
if !outer_bbox.contains_rect(inner_bbox) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eliminate bezpath1 if any of its anchor points are outside the bezpath2.
|
||||||
|
if !bezpath1.elements().iter().filter_map(|el| el.end_point()).all(|point| bezpath2.contains(point)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eliminate this subpath if it intersects with the other subpath.
|
||||||
|
if !bezpath_intersections(bezpath1, bezpath2, accuracy, minimum_separation).is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point:
|
||||||
|
// (1) This subpath's bounding box is inside the other subpath's bounding box,
|
||||||
|
// (2) Its anchors are inside the other subpath, and
|
||||||
|
// (3) It is not intersecting with the other subpath.
|
||||||
|
// Hence, this subpath is completely inside the given other subpath.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
// TODO: add more intersection tests
|
||||||
|
|
||||||
|
use super::bezpath_is_inside_bezpath;
|
||||||
|
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Rect, Shape};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_inside_subpath() {
|
||||||
|
let boundary_polygon = Rect::new(100., 100., 500., 500.).to_path(DEFAULT_ACCURACY);
|
||||||
|
|
||||||
|
let mut curve_intersection = BezPath::new();
|
||||||
|
curve_intersection.move_to(Point::new(189., 289.));
|
||||||
|
curve_intersection.quad_to(Point::new(9., 286.), Point::new(45., 410.));
|
||||||
|
assert!(!bezpath_is_inside_bezpath(&curve_intersection, &boundary_polygon, None, None));
|
||||||
|
|
||||||
|
let mut curve_outside = BezPath::new();
|
||||||
|
curve_outside.move_to(Point::new(115., 37.));
|
||||||
|
curve_outside.quad_to(Point::new(51.4, 91.8), Point::new(76.5, 242.));
|
||||||
|
assert!(!bezpath_is_inside_bezpath(&curve_outside, &boundary_polygon, None, None));
|
||||||
|
|
||||||
|
let mut curve_inside = BezPath::new();
|
||||||
|
curve_inside.move_to(Point::new(210.1, 133.5));
|
||||||
|
curve_inside.curve_to(Point::new(150.2, 436.9), Point::new(436., 285.), Point::new(247.6, 240.7));
|
||||||
|
assert!(bezpath_is_inside_bezpath(&curve_inside, &boundary_polygon, None, None));
|
||||||
|
|
||||||
|
let line_inside = Line::new(Point::new(101., 101.5), Point::new(150.2, 499.)).to_path(DEFAULT_ACCURACY);
|
||||||
|
assert!(bezpath_is_inside_bezpath(&line_inside, &boundary_polygon, None, None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::PointId;
|
use super::PointId;
|
||||||
use super::algorithms::offset_subpath::MAX_ABSOLUTE_DIFFERENCE;
|
use super::algorithms::offset_subpath::MAX_ABSOLUTE_DIFFERENCE;
|
||||||
use crate::vector::{SegmentId, Vector};
|
use crate::vector::{SegmentId, Vector};
|
||||||
use bezier_rs::{BezierHandles, ManipulatorGroup, Subpath};
|
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use kurbo::{BezPath, CubicBez, Line, ParamCurve, PathSeg, Point, QuadBez};
|
use kurbo::{BezPath, CubicBez, Line, ParamCurve, PathSeg, Point, QuadBez};
|
||||||
|
@ -136,12 +136,6 @@ pub fn handles_to_segment(start: DVec2, handles: BezierHandles, end: DVec2) -> P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subpath_to_kurbo_bezpath(subpath: Subpath<PointId>) -> BezPath {
|
|
||||||
let manipulator_groups = subpath.manipulator_groups();
|
|
||||||
let closed = subpath.closed();
|
|
||||||
bezpath_from_manipulator_groups(manipulator_groups, closed)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bezpath_from_manipulator_groups(manipulator_groups: &[ManipulatorGroup<PointId>], closed: bool) -> BezPath {
|
pub fn bezpath_from_manipulator_groups(manipulator_groups: &[ManipulatorGroup<PointId>], closed: bool) -> BezPath {
|
||||||
let mut bezpath = kurbo::BezPath::new();
|
let mut bezpath = kurbo::BezPath::new();
|
||||||
let mut out_handle;
|
let mut out_handle;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue