Remove remnant dead code across the Subpath, Vector, and editor geometry API surfaces (#4454)

This commit is contained in:
Keavon Chambers
2026-08-18 12:34:34 -07:00
committed by GitHub
parent 2df0bd25c3
commit 20af96c1d9
25 changed files with 58 additions and 533 deletions

View File

@@ -1836,7 +1836,8 @@ impl DocumentMessageHandler {
let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz); let document_to_viewport = self.navigation_handler.calculate_offset_transform(viewport.center_in_viewport_space().into(), &self.document_ptz);
viewport_polygon.apply_transform(document_to_viewport.inverse()); viewport_polygon.apply_transform(document_to_viewport.inverse());
ClickXRayIter::new(&self.network_interface, XRayTarget::Polygon(viewport_polygon)) let polygon = BezPath::from_path_segments(viewport_polygon.iter_closed());
ClickXRayIter::new(&self.network_interface, XRayTarget::Path(polygon))
} }
/// Runs an intersection test with all layers and a viewport space subpath; ignoring artboards /// Runs an intersection test with all layers and a viewport space subpath; ignoring artboards
@@ -3791,7 +3792,6 @@ enum XRayTarget {
Point(DVec2), Point(DVec2),
Quad(Quad), Quad(Quad),
Path(BezPath), Path(BezPath),
Polygon(Subpath<PointId>),
} }
/// The result for the [`ClickXRayIter`] on the layer /// The result for the [`ClickXRayIter`] on the layer
@@ -3891,10 +3891,6 @@ impl<'a> ClickXRayIter<'a> {
} }
XRayTarget::Quad(quad) => self.check_layer_area_target(click_targets, clip, layer, quad_to_kurbo(*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::Path(path) => self.check_layer_area_target(click_targets, clip, layer, path.clone(), transform),
XRayTarget::Polygon(polygon) => {
let polygon = BezPath::from_path_segments(polygon.iter_closed());
self.check_layer_area_target(click_targets, clip, layer, polygon, transform)
}
} }
} }
} }

View File

@@ -8,10 +8,9 @@ use graphene_std::Color;
use graphene_std::brush::brush_stroke::BrushStroke; use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::raster::BlendMode; use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Image; use graphene_std::raster_types::Image;
use graphene_std::subpath::Subpath;
use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::style::{GradientForm, GradientHueDirection, GradientInterpolation, GradientSettings, GradientSpace, GradientSpread, PaintOrder, Stroke}; use graphene_std::vector::style::{GradientForm, GradientHueDirection, GradientInterpolation, GradientSettings, GradientSpace, GradientSpread, PaintOrder, Stroke};
use graphene_std::vector::{Gradient, PointId, VectorModificationType}; use graphene_std::vector::{Gradient, VectorModificationType};
#[impl_message(Message, DocumentMessage, GraphOperation)] #[impl_message(Message, DocumentMessage, GraphOperation)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -162,12 +161,6 @@ pub enum GraphOperationMessage {
parent: LayerNodeIdentifier, parent: LayerNodeIdentifier,
insert_index: usize, insert_index: usize,
}, },
NewVectorLayer {
id: NodeId,
subpaths: Vec<Subpath<PointId>>,
parent: LayerNodeIdentifier,
insert_index: usize,
},
NewTextLayer { NewTextLayer {
id: NodeId, id: NodeId,
text: String, text: String,

View File

@@ -363,13 +363,6 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]); network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
} }
GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index } => {
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
let layer = modify_inputs.create_layer(id);
modify_inputs.insert_vector(subpaths, layer, true, true, true);
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
responses.add(NodeGraphMessage::RunDocumentGraph);
}
GraphOperationMessage::NewTextLayer { GraphOperationMessage::NewTextLayer {
id, id,
text, text,

View File

@@ -10,7 +10,6 @@ use graph_craft::document::{DocumentNode, NodeId, NodeInput};
use graphene_std::Color; use graphene_std::Color;
use graphene_std::raster::BlendMode; use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Image; use graphene_std::raster_types::Image;
use graphene_std::subpath::Subpath;
use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::misc::ManipulatorPointId; use graphene_std::vector::misc::ManipulatorPointId;
use graphene_std::vector::style::{FillChoice, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, initial_gradient_transform_for_bounding_box}; use graphene_std::vector::style::{FillChoice, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, initial_gradient_transform_for_bounding_box};
@@ -207,15 +206,6 @@ pub fn merge_points(document: &DocumentMessageHandler, layer: LayerNodeIdentifie
responses.add(GraphOperationMessage::Vector { layer, modification_type }); responses.add(GraphOperationMessage::Vector { layer, modification_type });
} }
/// Create a new vector layer.
pub fn new_vector_layer(subpaths: Vec<Subpath<PointId>>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
let insert_index = 0;
responses.add(GraphOperationMessage::NewVectorLayer { id, subpaths, parent, insert_index });
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
LayerNodeIdentifier::new_unchecked(id)
}
/// Create a new bitmap layer. /// Create a new bitmap layer.
pub fn new_image_layer(image: Image<Color>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier { pub fn new_image_layer(image: Image<Color>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
let insert_index = 0; let insert_index = 0;

View File

@@ -16,7 +16,7 @@ use crate::messages::tool::utility_types::*;
use glam::{DAffine2, DMat2, DVec2}; use glam::{DAffine2, DMat2, DVec2};
use graph_craft::document::NodeInput; use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graphene_std::subpath::{self, Subpath}; use graphene_std::subpath::Subpath;
use graphene_std::vector::click_target::ClickTargetType; use graphene_std::vector::click_target::ClickTargetType;
use graphene_std::vector::misc::{ArcType, GridType, SpiralType, dvec2_to_point}; use graphene_std::vector::misc::{ArcType, GridType, SpiralType, dvec2_to_point};
use kurbo::{BezPath, PathEl, Shape}; use kurbo::{BezPath, PathEl, Shape};
@@ -479,11 +479,7 @@ pub fn arc_outline(layer: Option<LayerNodeIdentifier>, document: &DocumentMessag
radius, radius,
start_angle / 360. * std::f64::consts::TAU, start_angle / 360. * std::f64::consts::TAU,
sweep_angle / 360. * std::f64::consts::TAU, sweep_angle / 360. * std::f64::consts::TAU,
match arc_type { arc_type,
ArcType::Open => subpath::ArcType::Open,
ArcType::Closed => subpath::ArcType::Closed,
ArcType::PieSlice => subpath::ArcType::PieSlice,
},
))]; ))];
let viewport = document.metadata().transform_to_viewport(layer); let viewport = document.metadata().transform_to_viewport(layer);

View File

@@ -13,11 +13,10 @@ use crate::messages::tool::utility_types::ToolType;
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graphene_std::renderer::Quad; use graphene_std::renderer::Quad;
use graphene_std::subpath::{Bezier, BezierHandles};
use graphene_std::vector::algorithms::bezpath_algorithms::pathseg_compute_lookup_table; use graphene_std::vector::algorithms::bezpath_algorithms::pathseg_compute_lookup_table;
use graphene_std::vector::misc::{HandleId, ManipulatorPointId, dvec2_to_point}; use graphene_std::vector::misc::{HandleId, ManipulatorPointId, dvec2_to_point};
use graphene_std::vector::{HandleExt, PointId, SegmentId, Vector, VectorModification, VectorModificationType}; use graphene_std::vector::{HandleExt, PointId, SegmentId, Vector, VectorModification, VectorModificationType};
use kurbo::{CubicBez, DEFAULT_ACCURACY, Line, ParamCurve, PathSeg, Point, QuadBez, Shape}; use kurbo::{CubicBez, DEFAULT_ACCURACY, ParamCurve, PathSeg, Point, Shape};
/// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable. /// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable.
pub fn should_extend(document: &DocumentMessageHandler, goal: DVec2, tolerance: f64, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Option<(LayerNodeIdentifier, PointId, DVec2)> { pub fn should_extend(document: &DocumentMessageHandler, goal: DVec2, tolerance: f64, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Option<(LayerNodeIdentifier, PointId, DVec2)> {
@@ -196,51 +195,6 @@ pub fn is_visible_point(
} }
} }
pub fn is_intersecting(bezier: Bezier, quad: [DVec2; 2], transform: DAffine2) -> bool {
let to_layerspace = transform.inverse();
let quad = [to_layerspace.transform_point2(quad[0]), to_layerspace.transform_point2(quad[1])];
let start = Point::new(bezier.start.x, bezier.start.y);
let end = Point::new(bezier.end.x, bezier.end.y);
let segment = match bezier.handles {
BezierHandles::Cubic { handle_start, handle_end } => {
let p1 = Point::new(handle_start.x, handle_start.y);
let p2 = Point::new(handle_end.x, handle_end.y);
PathSeg::Cubic(CubicBez::new(start, p1, p2, end))
}
BezierHandles::Quadratic { handle } => {
let p1 = Point::new(handle.x, handle.y);
PathSeg::Quad(QuadBez::new(start, p1, end))
}
BezierHandles::Linear => PathSeg::Line(Line::new(start, end)),
};
// Create a list of all the sides
let sides = [
Line::new((quad[0].x, quad[0].y), (quad[1].x, quad[0].y)),
Line::new((quad[0].x, quad[0].y), (quad[0].x, quad[1].y)),
Line::new((quad[1].x, quad[1].y), (quad[1].x, quad[0].y)),
Line::new((quad[1].x, quad[1].y), (quad[0].x, quad[1].y)),
];
let mut is_intersecting = false;
for line in sides {
let intersections = segment.intersect_line(line);
let mut intersects = false;
for intersection in intersections {
if intersection.line_t <= 1. && intersection.line_t >= 0. && intersection.segment_t <= 1. && intersection.segment_t >= 0. {
// There is a valid intersection point
intersects = true;
break;
}
}
if intersects {
is_intersecting = true;
break;
}
}
is_intersecting
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn resize_bounds( pub fn resize_bounds(
document: &DocumentMessageHandler, document: &DocumentMessageHandler,

View File

@@ -390,7 +390,6 @@ macro_rules! tagged_value {
Type::Generic(_) => None, Type::Generic(_) => None,
Type::Concrete(concrete_type) => { Type::Concrete(concrete_type) => {
let name = concrete_type.name.as_ref(); let name = concrete_type.name.as_ref();
// TODO: Add default implementations for types such as TaggedValue::Subpaths, and use the defaults here and in document_node_types
// Tries using the default for the tagged value type. If it not implemented, then uses the default used in document_node_types. If it is not used there, then TaggedValue::None is returned. // Tries using the default for the tagged value type. If it not implemented, then uses the default used in document_node_types. If it is not used there, then TaggedValue::None is returned.
if name == std::any::type_name::<()>() { return Some(TaggedValue::None) } if name == std::any::type_name::<()>() { return Some(TaggedValue::None) }
if name == std::any::type_name::<Gradient>() { return Some(TaggedValue::GradientRamp(GradientRamp::default())) } if name == std::any::type_name::<Gradient>() { return Some(TaggedValue::GradientRamp(GradientRamp::default())) }
@@ -671,7 +670,6 @@ impl TaggedValue {
Type::Concrete(concrete_type) => { Type::Concrete(concrete_type) => {
let ty = concrete_type.id?; let ty = concrete_type.id?;
use std::any::TypeId; use std::any::TypeId;
// TODO: Add default implementations for types such as TaggedValue::Subpaths, and use the defaults here and in document_node_types
// Tries using the default for the tagged value type. If it not implemented, then uses the default used in document_node_types. If it is not used there, then TaggedValue::None is returned. // Tries using the default for the tagged value type. If it not implemented, then uses the default used in document_node_types. If it is not used there, then TaggedValue::None is returned.
let ty = match () { let ty = match () {
() if ty == TypeId::of::<()>() => TaggedValue::None, () if ty == TypeId::of::<()>() => TaggedValue::None,

View File

@@ -36,13 +36,6 @@ impl Rect {
bounds bounds
} }
/// Get all the edges in the rect.
#[must_use]
pub fn edges(&self) -> [[DVec2; 2]; 4] {
let corners = [self[0], DVec2::new(self[0].x, self[1].y), self[1], DVec2::new(self[1].y, self[0].x)];
[[corners[0], corners[1]], [corners[1], corners[2]], [corners[2], corners[3]], [corners[3], corners[0]]]
}
/// Gets the center of a rect /// Gets the center of a rect
#[must_use] #[must_use]
pub fn center(&self) -> DVec2 { pub fn center(&self) -> DVec2 {

View File

@@ -9,7 +9,7 @@ pub mod vector;
// Re-export commonly used types at the crate root // Re-export commonly used types at the crate root
pub use core_types as gcore; pub use core_types as gcore;
pub use gradient::{Gradient, GradientForm, GradientHueDirection, GradientInterpolation, GradientRamp, GradientSettings, GradientSpace, GradientSpread, GradientStop}; pub use gradient::{Gradient, GradientForm, GradientHueDirection, GradientInterpolation, GradientRamp, GradientSettings, GradientSpace, GradientSpread, GradientStop};
pub use math::{QuadExt, RectExt}; pub use math::QuadExt;
pub use subpath::Subpath; pub use subpath::Subpath;
pub use vector::Vector; pub use vector::Vector;
pub use vector::reference_point::ReferencePoint; pub use vector::reference_point::ReferencePoint;

View File

@@ -1,32 +1,13 @@
use crate::subpath::Bezier;
use crate::vector::misc::dvec2_to_point; use crate::vector::misc::dvec2_to_point;
use core_types::math::quad::Quad; use core_types::math::quad::Quad;
use core_types::math::rect::Rect;
use kurbo::{Line, PathSeg}; use kurbo::{Line, PathSeg};
pub trait QuadExt { pub trait QuadExt {
/// Get all the edges in the rect as linear bezier curves
fn bezier_lines(&self) -> impl Iterator<Item = Bezier> + '_;
fn to_lines(&self) -> impl Iterator<Item = PathSeg>; fn to_lines(&self) -> impl Iterator<Item = PathSeg>;
} }
impl QuadExt for Quad { impl QuadExt for Quad {
fn bezier_lines(&self) -> impl Iterator<Item = Bezier> + '_ {
self.all_edges().into_iter().map(|[start, end]| Bezier::from_linear_dvec2(start, end))
}
fn to_lines(&self) -> impl Iterator<Item = PathSeg> { fn to_lines(&self) -> impl Iterator<Item = PathSeg> {
self.all_edges().into_iter().map(|[start, end]| PathSeg::Line(Line::new(dvec2_to_point(start), dvec2_to_point(end)))) self.all_edges().into_iter().map(|[start, end]| PathSeg::Line(Line::new(dvec2_to_point(start), dvec2_to_point(end))))
} }
} }
pub trait RectExt {
/// Get all the edges in the quad as linear bezier curves
fn bezier_lines(&self) -> impl Iterator<Item = Bezier> + '_;
}
impl RectExt for Rect {
fn bezier_lines(&self) -> impl Iterator<Item = Bezier> + '_ {
self.edges().into_iter().map(|[start, end]| Bezier::from_linear_dvec2(start, end))
}
}

View File

@@ -1,6 +1,5 @@
use super::consts::*;
use super::*; use super::*;
use crate::vector::misc::{SpiralType, point_to_dvec2}; use crate::vector::misc::{ArcType, SpiralType, point_to_dvec2};
use glam::DVec2; use glam::DVec2;
use kurbo::PathSeg; use kurbo::PathSeg;
use std::f64::consts::TAU; use std::f64::consts::TAU;
@@ -36,55 +35,6 @@ impl<PointId: Identifier> Subpath<PointId> {
Self { manipulator_groups, closed } Self { manipulator_groups, closed }
} }
/// Create a `Subpath` consisting of 2 manipulator groups from a `Bezier`.
pub fn from_bezier(segment: PathSeg) -> Self {
let PathSegPoints { p0, p1, p2, p3 } = pathseg_points(segment);
Subpath::new(vec![ManipulatorGroup::new(p0, None, p1), ManipulatorGroup::new(p3, p2, None)], false)
}
/// Creates a subpath from a slice of [Bezier]. When two consecutive Beziers do not share an end and start point, this function
/// resolves the discrepancy by simply taking the start-point of the second Bezier as the anchor of the Manipulator Group.
pub fn from_beziers(beziers: &[PathSeg], closed: bool) -> Self {
assert!(!closed || beziers.len() > 1, "A closed Subpath must contain at least 1 Bezier.");
if beziers.is_empty() {
return Subpath::new(vec![], closed);
}
let beziers: Vec<_> = beziers.iter().map(|b| pathseg_points(*b)).collect();
let first = beziers.first().unwrap();
let mut manipulator_groups = vec![ManipulatorGroup {
anchor: first.p0,
in_handle: None,
out_handle: first.p1,
id: PointId::new(),
}];
let mut inner_groups: Vec<ManipulatorGroup<PointId>> = beziers
.windows(2)
.map(|bezier_pair| ManipulatorGroup {
anchor: bezier_pair[1].p0,
in_handle: bezier_pair[0].p2,
out_handle: bezier_pair[1].p1,
id: PointId::new(),
})
.collect::<Vec<ManipulatorGroup<PointId>>>();
manipulator_groups.append(&mut inner_groups);
let last = beziers.last().unwrap();
if !closed {
manipulator_groups.push(ManipulatorGroup {
anchor: last.p3,
in_handle: last.p2,
out_handle: None,
id: PointId::new(),
});
return Subpath::new(manipulator_groups, false);
}
manipulator_groups[0].in_handle = last.p2;
Subpath::new(manipulator_groups, true)
}
/// Returns true if the `Subpath` contains no [ManipulatorGroup]. /// Returns true if the `Subpath` contains no [ManipulatorGroup].
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.manipulator_groups.is_empty() self.manipulator_groups.is_empty()
@@ -95,23 +45,6 @@ impl<PointId: Identifier> Subpath<PointId> {
self.manipulator_groups.len() self.manipulator_groups.len()
} }
/// Returns the number of segments contained within the `Subpath`.
pub fn len_segments(&self) -> usize {
let mut number_of_curves = self.len();
if !self.closed && number_of_curves > 0 {
number_of_curves -= 1
}
number_of_curves
}
/// Returns a copy of the bezier segment at the given segment index, if this segment exists.
pub fn get_segment(&self, segment_index: usize) -> Option<PathSeg> {
if segment_index >= self.len_segments() {
return None;
}
Some(self[segment_index].to_bezier(&self[(segment_index + 1) % self.len()]))
}
/// Returns an iterator of the [Bezier]s along the `Subpath`. /// Returns an iterator of the [Bezier]s along the `Subpath`.
pub fn iter(&self) -> SubpathIter<'_, PointId> { pub fn iter(&self) -> SubpathIter<'_, PointId> {
SubpathIter { SubpathIter {
@@ -140,22 +73,6 @@ impl<PointId: Identifier> Subpath<PointId> {
&mut self.manipulator_groups &mut self.manipulator_groups
} }
/// Returns a vector of all the anchors (DVec2) for this `Subpath`.
pub fn anchors(&self) -> Vec<DVec2> {
self.manipulator_groups().iter().map(|group| group.anchor).collect()
}
/// Returns if the Subpath is equivalent to a single point.
pub fn is_point(&self) -> bool {
if self.is_empty() {
return false;
}
let point = self.manipulator_groups[0].anchor;
self.manipulator_groups
.iter()
.all(|manipulator_group| manipulator_group.anchor.abs_diff_eq(point, MAX_ABSOLUTE_DIFFERENCE))
}
pub fn from_anchors(anchor_positions: impl IntoIterator<Item = DVec2>, closed: bool) -> Self { pub fn from_anchors(anchor_positions: impl IntoIterator<Item = DVec2>, closed: bool) -> Self {
Self::new(anchor_positions.into_iter().map(|anchor| ManipulatorGroup::new_anchor(anchor)).collect(), closed) Self::new(anchor_positions.into_iter().map(|anchor| ManipulatorGroup::new_anchor(anchor)).collect(), closed)
} }
@@ -406,7 +323,7 @@ pub fn spiral_point(theta: f64, a: f64, b: f64, spiral_type: SpiralType) -> DVec
} }
/// Returns the tangent direction at angle `theta` for the given spiral type. /// Returns the tangent direction at angle `theta` for the given spiral type.
pub fn spiral_tangent(theta: f64, a: f64, b: f64, spiral_type: SpiralType) -> DVec2 { fn spiral_tangent(theta: f64, a: f64, b: f64, spiral_type: SpiralType) -> DVec2 {
match spiral_type { match spiral_type {
SpiralType::Archimedean => archimedean_spiral_tangent(theta, a, b), SpiralType::Archimedean => archimedean_spiral_tangent(theta, a, b),
SpiralType::Logarithmic => log_spiral_tangent(theta, a, b), SpiralType::Logarithmic => log_spiral_tangent(theta, a, b),
@@ -414,7 +331,7 @@ pub fn spiral_tangent(theta: f64, a: f64, b: f64, spiral_type: SpiralType) -> DV
} }
/// Computes arc length between two angles for the given spiral type. /// Computes arc length between two angles for the given spiral type.
pub fn spiral_arc_length(theta_start: f64, theta_end: f64, a: f64, b: f64, spiral_type: SpiralType) -> f64 { fn spiral_arc_length(theta_start: f64, theta_end: f64, a: f64, b: f64, spiral_type: SpiralType) -> f64 {
match spiral_type { match spiral_type {
SpiralType::Archimedean => archimedean_spiral_arc_length(theta_start, theta_end, a, b), SpiralType::Archimedean => archimedean_spiral_arc_length(theta_start, theta_end, a, b),
SpiralType::Logarithmic => log_spiral_arc_length(theta_start, theta_end, a, b), SpiralType::Logarithmic => log_spiral_arc_length(theta_start, theta_end, a, b),
@@ -422,19 +339,19 @@ pub fn spiral_arc_length(theta_start: f64, theta_end: f64, a: f64, b: f64, spira
} }
/// Returns a point on a logarithmic spiral at angle `theta`. /// Returns a point on a logarithmic spiral at angle `theta`.
pub fn log_spiral_point(theta: f64, a: f64, b: f64) -> DVec2 { fn log_spiral_point(theta: f64, a: f64, b: f64) -> DVec2 {
let r = a * (b * theta).exp(); // a * e^(bθ) let r = a * (b * theta).exp(); // a * e^(bθ)
DVec2::new(r * theta.cos(), -r * theta.sin()) DVec2::new(r * theta.cos(), -r * theta.sin())
} }
/// Computes arc length along a logarithmic spiral between two angles. /// Computes arc length along a logarithmic spiral between two angles.
pub fn log_spiral_arc_length(theta_start: f64, theta_end: f64, a: f64, b: f64) -> f64 { fn log_spiral_arc_length(theta_start: f64, theta_end: f64, a: f64, b: f64) -> f64 {
let factor = (1. + b * b).sqrt(); let factor = (1. + b * b).sqrt();
(a / b) * factor * ((b * theta_end).exp() - (b * theta_start).exp()) (a / b) * factor * ((b * theta_end).exp() - (b * theta_start).exp())
} }
/// Returns the tangent direction of a logarithmic spiral at angle `theta`. /// Returns the tangent direction of a logarithmic spiral at angle `theta`.
pub fn log_spiral_tangent(theta: f64, a: f64, b: f64) -> DVec2 { fn log_spiral_tangent(theta: f64, a: f64, b: f64) -> DVec2 {
let r = a * (b * theta).exp(); let r = a * (b * theta).exp();
let dx = r * (b * theta.cos() - theta.sin()); let dx = r * (b * theta.cos() - theta.sin());
let dy = r * (b * theta.sin() + theta.cos()); let dy = r * (b * theta.sin() + theta.cos());
@@ -443,13 +360,13 @@ pub fn log_spiral_tangent(theta: f64, a: f64, b: f64) -> DVec2 {
} }
/// Returns a point on an Archimedean spiral at angle `theta`. /// Returns a point on an Archimedean spiral at angle `theta`.
pub fn archimedean_spiral_point(theta: f64, a: f64, b: f64) -> DVec2 { fn archimedean_spiral_point(theta: f64, a: f64, b: f64) -> DVec2 {
let r = a + b * theta; let r = a + b * theta;
DVec2::new(r * theta.cos(), -r * theta.sin()) DVec2::new(r * theta.cos(), -r * theta.sin())
} }
/// Returns the tangent direction of an Archimedean spiral at angle `theta`. /// Returns the tangent direction of an Archimedean spiral at angle `theta`.
pub fn archimedean_spiral_tangent(theta: f64, a: f64, b: f64) -> DVec2 { fn archimedean_spiral_tangent(theta: f64, a: f64, b: f64) -> DVec2 {
let r = a + b * theta; let r = a + b * theta;
let dx = b * theta.cos() - r * theta.sin(); let dx = b * theta.cos() - r * theta.sin();
let dy = b * theta.sin() + r * theta.cos(); let dy = b * theta.sin() + r * theta.cos();
@@ -457,12 +374,12 @@ pub fn archimedean_spiral_tangent(theta: f64, a: f64, b: f64) -> DVec2 {
} }
/// Computes arc length along an Archimedean spiral between two angles. /// Computes arc length along an Archimedean spiral between two angles.
pub fn archimedean_spiral_arc_length(theta_start: f64, theta_end: f64, a: f64, b: f64) -> f64 { fn archimedean_spiral_arc_length(theta_start: f64, theta_end: f64, a: f64, b: f64) -> f64 {
archimedean_spiral_arc_length_origin(theta_end, a, b) - archimedean_spiral_arc_length_origin(theta_start, a, b) archimedean_spiral_arc_length_origin(theta_end, a, b) - archimedean_spiral_arc_length_origin(theta_start, a, b)
} }
/// Computes arc length from origin to a point on Archimedean spiral at angle `theta`. /// Computes arc length from origin to a point on Archimedean spiral at angle `theta`.
pub fn archimedean_spiral_arc_length_origin(theta: f64, a: f64, b: f64) -> f64 { fn archimedean_spiral_arc_length_origin(theta: f64, a: f64, b: f64) -> f64 {
let r = a + b * theta; let r = a + b * theta;
let sqrt_term = (r * r + b * b).sqrt(); let sqrt_term = (r * r + b * b).sqrt();
(r * sqrt_term + b * b * ((r + sqrt_term).ln())) / (2. * b) (r * sqrt_term + b * b * ((r + sqrt_term).ln())) / (2. * b)

View File

@@ -14,7 +14,7 @@ impl<PointId: Identifier> Subpath<PointId> {
/// If the comparison condition is not satisfied, the function takes the larger `t`-value of the two /// If the comparison condition is not satisfied, the function takes the larger `t`-value of the two
/// ///
/// **NOTE**: if an intersection were to occur within an `error` distance away from an anchor point, the algorithm will filter that intersection out. /// **NOTE**: if an intersection were to occur within an `error` distance away from an anchor point, the algorithm will filter that intersection out.
pub fn all_self_intersections(&self, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> { fn all_self_intersections(&self, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
let mut intersections_vec = Vec::new(); let mut intersections_vec = Vec::new();
let err = accuracy.unwrap_or(MAX_ABSOLUTE_DIFFERENCE); let err = accuracy.unwrap_or(MAX_ABSOLUTE_DIFFERENCE);
let num_curves = self.len(); let num_curves = self.len();

View File

@@ -13,27 +13,6 @@ impl<PointId: super::structs::Identifier> Subpath<PointId> {
self.closed = new_closed; self.closed = new_closed;
} }
/// Access a [ManipulatorGroup] from a PointId.
pub fn manipulator_from_id(&self, id: PointId) -> Option<&ManipulatorGroup<PointId>> {
self.manipulator_groups.iter().find(|manipulator_group| manipulator_group.id == id)
}
/// Access a mutable [ManipulatorGroup] from a PointId.
pub fn manipulator_mut_from_id(&mut self, id: PointId) -> Option<&mut ManipulatorGroup<PointId>> {
self.manipulator_groups.iter_mut().find(|manipulator_group| manipulator_group.id == id)
}
/// Access the index of a [ManipulatorGroup] from a PointId.
pub fn manipulator_index_from_id(&self, id: PointId) -> Option<usize> {
self.manipulator_groups.iter().position(|manipulator_group| manipulator_group.id == id)
}
/// Insert a manipulator group at an index.
pub fn insert_manipulator_group(&mut self, index: usize, group: ManipulatorGroup<PointId>) {
assert!(group.is_finite(), "Inserting non finite manipulator group");
self.manipulator_groups.insert(index, group)
}
/// Push a manipulator group to the end. /// Push a manipulator group to the end.
pub fn push_manipulator_group(&mut self, group: ManipulatorGroup<PointId>) { pub fn push_manipulator_group(&mut self, group: ManipulatorGroup<PointId>) {
assert!(group.is_finite(), "Pushing non finite manipulator group"); assert!(group.is_finite(), "Pushing non finite manipulator group");
@@ -44,9 +23,4 @@ impl<PointId: super::structs::Identifier> Subpath<PointId> {
pub fn last_manipulator_group_mut(&mut self) -> Option<&mut ManipulatorGroup<PointId>> { pub fn last_manipulator_group_mut(&mut self) -> Option<&mut ManipulatorGroup<PointId>> {
self.manipulator_groups.last_mut() self.manipulator_groups.last_mut()
} }
/// Remove a manipulator group at an index.
pub fn remove_manipulator_group(&mut self, index: usize) -> ManipulatorGroup<PointId> {
self.manipulator_groups.remove(index)
}
} }

View File

@@ -9,7 +9,6 @@ mod transform;
pub use core::*; pub use core::*;
use kurbo::PathSeg; use kurbo::PathSeg;
use std::fmt::{Debug, Formatter, Result}; use std::fmt::{Debug, Formatter, Result};
use std::ops::{Index, IndexMut};
pub use structs::*; pub use structs::*;
/// Structure used to represent a path composed of [Bezier] curves. /// Structure used to represent a path composed of [Bezier] curves.
@@ -27,22 +26,6 @@ pub struct SubpathIter<'a, PointId: Identifier> {
is_always_closed: bool, is_always_closed: bool,
} }
impl<PointId: Identifier> Index<usize> for Subpath<PointId> {
type Output = ManipulatorGroup<PointId>;
fn index(&self, index: usize) -> &Self::Output {
assert!(index < self.len(), "Index out of bounds in trait Index of SubPath.");
&self.manipulator_groups[index]
}
}
impl<PointId: Identifier> IndexMut<usize> for Subpath<PointId> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
assert!(index < self.len(), "Index out of bounds in trait IndexMut of SubPath.");
&mut self.manipulator_groups[index]
}
}
impl<PointId: Identifier> Iterator for SubpathIter<'_, PointId> { impl<PointId: Identifier> Iterator for SubpathIter<'_, PointId> {
type Item = PathSeg; type Item = PathSeg;
@@ -60,7 +43,7 @@ impl<PointId: Identifier> Iterator for SubpathIter<'_, PointId> {
let end_index = (self.index + 1) % self.subpath.len(); let end_index = (self.index + 1) % self.subpath.len();
self.index += 1; self.index += 1;
Some(self.subpath[start_index].to_bezier(&self.subpath[end_index])) Some(self.subpath.manipulator_groups[start_index].to_bezier(&self.subpath.manipulator_groups[end_index]))
} }
} }

View File

@@ -77,37 +77,6 @@ impl<PointId: Identifier> ManipulatorGroup<PointId> {
pub fn is_finite(&self) -> bool { pub fn is_finite(&self) -> bool {
self.anchor.is_finite() && self.in_handle.is_none_or(|handle| handle.is_finite()) && self.out_handle.is_none_or(|handle| handle.is_finite()) self.anchor.is_finite() && self.in_handle.is_none_or(|handle| handle.is_finite()) && self.out_handle.is_none_or(|handle| handle.is_finite())
} }
/// Reverse directions of handles
pub fn flip(mut self) -> Self {
std::mem::swap(&mut self.in_handle, &mut self.out_handle);
self
}
pub fn has_in_handle(&self) -> bool {
self.in_handle.map(|handle| Self::has_handle(self.anchor, handle)).unwrap_or(false)
}
pub fn has_out_handle(&self) -> bool {
self.out_handle.map(|handle| Self::has_handle(self.anchor, handle)).unwrap_or(false)
}
fn has_handle(anchor: DVec2, handle: DVec2) -> bool {
!((handle.x - anchor.x).abs() < f64::EPSILON && (handle.y - anchor.y).abs() < f64::EPSILON)
}
}
#[derive(Copy, Clone)]
pub enum AppendType {
IgnoreStart,
SmoothJoin(f64),
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, graphene_hash::CacheHash)]
pub enum ArcType {
Open,
Closed,
PieSlice,
} }
/// Representation of the handle point(s) in a bezier segment. /// Representation of the handle point(s) in a bezier segment.
@@ -130,10 +99,6 @@ pub enum BezierHandles {
} }
impl BezierHandles { impl BezierHandles {
pub fn is_cubic(&self) -> bool {
matches!(self, Self::Cubic { .. })
}
pub fn is_finite(&self) -> bool { pub fn is_finite(&self) -> bool {
match self { match self {
BezierHandles::Linear => true, BezierHandles::Linear => true,

View File

@@ -1,62 +1,12 @@
use super::structs::Identifier; use super::structs::Identifier;
use super::*; use super::*;
use glam::{DAffine2, DVec2}; use glam::DAffine2;
/// Functionality that transforms Subpaths, such as split, reduce, offset, etc.
impl<PointId: Identifier> Subpath<PointId> { impl<PointId: Identifier> Subpath<PointId> {
/// Returns [ManipulatorGroup]s with a reversed winding order.
fn reverse_manipulator_groups(manipulator_groups: &[ManipulatorGroup<PointId>]) -> Vec<ManipulatorGroup<PointId>> {
manipulator_groups
.iter()
.rev()
.map(|group| ManipulatorGroup {
anchor: group.anchor,
in_handle: group.out_handle,
out_handle: group.in_handle,
id: PointId::new(),
})
.collect::<Vec<ManipulatorGroup<PointId>>>()
}
/// Returns a [Subpath] with a reversed winding order.
/// Note that a reversed closed subpath will start on the same manipulator group and simply wind the other direction
pub fn reverse(&self) -> Subpath<PointId> {
let mut reversed = Subpath::reverse_manipulator_groups(self.manipulator_groups());
if self.closed {
reversed.rotate_right(1);
};
Subpath {
manipulator_groups: reversed,
closed: self.closed,
}
}
/// Apply a transformation to all of the [ManipulatorGroup]s in the [Subpath]. /// Apply a transformation to all of the [ManipulatorGroup]s in the [Subpath].
pub fn apply_transform(&mut self, affine_transform: DAffine2) { pub fn apply_transform(&mut self, affine_transform: DAffine2) {
for manipulator_group in &mut self.manipulator_groups { for manipulator_group in &mut self.manipulator_groups {
manipulator_group.apply_transform(affine_transform); manipulator_group.apply_transform(affine_transform);
} }
} }
/// Returns a subpath that results from rotating this subpath around the origin by the given angle (in radians).
pub fn rotate(&self, angle: f64) -> Subpath<PointId> {
let mut rotated_subpath = self.clone();
let affine_transform: DAffine2 = DAffine2::from_angle(angle);
rotated_subpath.apply_transform(affine_transform);
rotated_subpath
}
/// Returns a subpath that results from rotating this subpath around the provided point by the given angle (in radians).
pub fn rotate_about_point(&self, angle: f64, pivot: DVec2) -> Subpath<PointId> {
// Translate before and after the rotation to account for the pivot
let translate: DAffine2 = DAffine2::from_translation(pivot);
let rotate: DAffine2 = DAffine2::from_angle(angle);
let translate_inverse = translate.inverse();
let mut rotated_subpath = self.clone();
rotated_subpath.apply_transform(translate * rotate * translate_inverse);
rotated_subpath
}
} }

View File

@@ -1,7 +1,6 @@
use super::intersection::bezpath_intersections; use super::intersection::bezpath_intersections;
use super::poisson_disk::poisson_disk_sample; use super::poisson_disk::poisson_disk_sample;
use super::util::pathseg_tangent; use super::util::pathseg_tangent;
use crate::vector::algorithms::offset_subpath::MAX_ABSOLUTE_DIFFERENCE;
use crate::vector::misc::{PointSpacingType, dvec2_to_point, point_to_dvec2}; use crate::vector::misc::{PointSpacingType, dvec2_to_point, point_to_dvec2};
use core_types::math::polynomial::pathseg_to_parametric_polynomial; use core_types::math::polynomial::pathseg_to_parametric_polynomial;
use glam::{DMat2, DVec2}; use glam::{DMat2, DVec2};
@@ -415,19 +414,6 @@ pub fn poisson_disk_points(bezpath_index: usize, bezpaths: &[(BezPath, Rect)], s
poisson_disk_sample(offset, width, height, separation_disk_diameter, point_in_shape_checker, line_intersect_shape_checker, rng) poisson_disk_sample(offset, width, height, separation_disk_diameter, point_in_shape_checker, line_intersect_shape_checker, rng)
} }
/// Returns true if the Bezier curve is equivalent to a line.
///
/// **NOTE**: This is different from simply checking if the segment is [`PathSeg::Line`] or [`PathSeg::Quad`] or [`PathSeg::Cubic`]. Bezier curve can also be a line if the control points are colinear to the start and end points. Therefore if the handles exceed the start and end point, it will still be considered as a line.
pub fn is_linear(segment: &PathSeg) -> bool {
let is_colinear = |a: Point, b: Point, c: Point| -> bool { ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)).abs() < MAX_ABSOLUTE_DIFFERENCE };
match *segment {
PathSeg::Line(_) => true,
PathSeg::Quad(QuadBez { p0, p1, p2 }) => is_colinear(p0, p1, p2),
PathSeg::Cubic(CubicBez { p0, p1, p2, p3 }) => is_colinear(p0, p1, p3) && is_colinear(p0, p2, p3),
}
}
// TODO: If a segment curls back on itself tightly enough it could intersect again at the portion that should be trimmed. This could cause the Subpaths to be clipped // TODO: If a segment curls back on itself tightly enough it could intersect again at the portion that should be trimmed. This could cause the Subpaths to be clipped
// TODO: at the incorrect location. This can be avoided by first trimming the two Subpaths at any extrema, effectively ignoring loopbacks. // TODO: at the incorrect location. This can be avoided by first trimming the two Subpaths at any extrema, effectively ignoring loopbacks.
/// Helper function to clip overlap of two intersecting open BezPaths. Returns an Option because intersections may not exist for certain arrangements and distances. /// Helper function to clip overlap of two intersecting open BezPaths. Returns an Option because intersections may not exist for certain arrangements and distances.

View File

@@ -14,12 +14,6 @@ pub fn pathseg_tangent(segment: PathSeg, t: f64) -> DVec2 {
DVec2::new(tangent.x, tangent.y) DVec2::new(tangent.x, tangent.y)
} }
// Compare two f64s with some maximum absolute difference to account for floating point errors
#[cfg(test)]
pub fn compare_f64s(f1: f64, f2: f64) -> bool {
(f1 - f2).abs() < super::contants::MAX_ABSOLUTE_DIFFERENCE
}
/// Compare points by allowing some maximum absolute difference to account for floating point errors /// Compare points by allowing some maximum absolute difference to account for floating point errors
#[cfg(test)] #[cfg(test)]
pub fn compare_points(p1: kurbo::Point, p2: kurbo::Point) -> bool { pub fn compare_points(p1: kurbo::Point, p2: kurbo::Point) -> bool {

View File

@@ -93,11 +93,6 @@ impl PointDomain {
Self { id: Vec::new(), position: Vec::new() } Self { id: Vec::new(), position: Vec::new() }
} }
pub fn clear(&mut self) {
self.id.clear();
self.position.clear();
}
#[inline(always)] #[inline(always)]
pub fn reserve(&mut self, additional: usize) { pub fn reserve(&mut self, additional: usize) {
self.id.reserve(additional); self.id.reserve(additional);
@@ -229,14 +224,6 @@ impl SegmentDomain {
} }
} }
pub fn clear(&mut self) {
self.id.clear();
self.start_point.clear();
self.end_point.clear();
self.handles.clear();
self.stroke.clear();
}
#[inline(always)] #[inline(always)]
pub fn reserve(&mut self, additional: usize) { pub fn reserve(&mut self, additional: usize) {
self.id.reserve(additional); self.id.reserve(additional);
@@ -401,16 +388,6 @@ impl SegmentDomain {
self.id.iter().position(|&check_id| check_id == id) self.id.iter().position(|&check_id| check_id == id)
} }
fn resolve_range(&self, range: &std::ops::RangeInclusive<SegmentId>) -> Option<std::ops::RangeInclusive<usize>> {
match (self.id_to_index(*range.start()), self.id_to_index(*range.end())) {
(Some(start), Some(end)) if start.max(end) < self.handles.len().min(self.id.len()).min(self.start_point.len()).min(self.end_point.len()) => Some(start..=end),
_ => {
warn!("Resolving range with invalid id");
None
}
}
}
pub fn concat(&mut self, other: &Self, transform: DAffine2, id_map: &IdMap) { pub fn concat(&mut self, other: &Self, transform: DAffine2, id_map: &IdMap) {
self.id.extend(other.id.iter().map(|id| *id_map.segment_map.get(id).unwrap_or(id))); self.id.extend(other.id.iter().map(|id| *id_map.segment_map.get(id).unwrap_or(id)));
self.start_point.extend(other.start_point.iter().map(|&index| id_map.point_offset + index)); self.start_point.extend(other.start_point.iter().map(|&index| id_map.point_offset + index));
@@ -609,12 +586,6 @@ impl RegionDomain {
} }
} }
pub fn clear(&mut self) {
self.id.clear();
self.segment_range.clear();
self.fill.clear();
}
#[inline(always)] #[inline(always)]
pub fn reserve(&mut self, additional: usize) { pub fn reserve(&mut self, additional: usize) {
self.id.reserve(additional); self.id.reserve(additional);
@@ -759,10 +730,6 @@ pub struct FoundSubpath {
} }
impl FoundSubpath { impl FoundSubpath {
pub fn new(segments: Vec<HalfEdge>) -> Self {
Self { edges: segments }
}
pub fn endpoints(&self) -> Option<(&HalfEdge, &HalfEdge)> { pub fn endpoints(&self) -> Option<(&HalfEdge, &HalfEdge)> {
match (self.edges.first(), self.edges.last()) { match (self.edges.first(), self.edges.last()) {
(Some(first), Some(last)) => Some((first, last)), (Some(first), Some(last)) => Some((first, last)),
@@ -774,21 +741,6 @@ impl FoundSubpath {
self.edges.push(segment); self.edges.push(segment);
} }
pub fn insert(&mut self, index: usize, segment: HalfEdge) {
self.edges.insert(index, segment);
}
pub fn extend(&mut self, segments: impl IntoIterator<Item = HalfEdge>) {
self.edges.extend(segments);
}
pub fn splice<I>(&mut self, range: std::ops::Range<usize>, replace_with: I)
where
I: IntoIterator<Item = HalfEdge>,
{
self.edges.splice(range, replace_with);
}
pub fn is_closed(&self) -> bool { pub fn is_closed(&self) -> bool {
match (self.edges.first(), self.edges.last()) { match (self.edges.first(), self.edges.last()) {
(Some(first), Some(last)) => first.start == last.end, (Some(first), Some(last)) => first.start == last.end,
@@ -1088,49 +1040,6 @@ impl Vector {
Some(Subpath::new(manipulators_list, closed)) Some(Subpath::new(manipulators_list, closed))
} }
/// Construct a [`Bezier`] curve for each region, skipping invalid regions.
pub fn region_manipulator_groups(&self) -> impl Iterator<Item = (RegionId, Vec<ManipulatorGroup<PointId>>)> + '_ {
self.region_domain
.id
.iter()
.zip(&self.region_domain.segment_range)
.filter_map(|(&id, segment_range)| self.segment_domain.resolve_range(segment_range).map(|range| (id, range)))
.filter_map(|(id, range)| {
let segments_iter = self
.segment_domain
.handles
.get(range.clone())?
.iter()
.zip(self.segment_domain.start_point.get(range.clone())?)
.zip(self.segment_domain.end_point.get(range)?)
.map(|((&handles, &start), &end)| (handles, start, end));
let mut manipulator_groups = Vec::new();
let mut in_handle = None;
for segment in segments_iter {
let (handles, start_point_index, _end_point_index) = segment;
let start_point_id = self.point_domain.id[start_point_index];
let start_point = self.point_domain.position[start_point_index];
let (manipulator_group, next_in_handle) = match handles {
BezierHandles::Linear => (ManipulatorGroup::new_with_id(start_point, in_handle, None, start_point_id), None),
BezierHandles::Quadratic { handle } => (ManipulatorGroup::new_with_id(start_point, in_handle, Some(handle), start_point_id), None),
BezierHandles::Cubic { handle_start, handle_end } => (ManipulatorGroup::new_with_id(start_point, in_handle, Some(handle_start), start_point_id), Some(handle_end)),
};
in_handle = next_in_handle;
manipulator_groups.push(manipulator_group);
}
if let Some(first) = manipulator_groups.first_mut() {
first.in_handle = in_handle;
}
Some((id, manipulator_groups))
})
}
pub fn build_stroke_path_iter(&self) -> StrokePathIter<'_> { pub fn build_stroke_path_iter(&self) -> StrokePathIter<'_> {
let mut points = vec![StrokePathIterPointMetadata::default(); self.point_domain.ids().len()]; let mut points = vec![StrokePathIterPointMetadata::default(); self.point_domain.ids().len()];
for (segment_index, (&start, &end)) in self.segment_domain.start_point.iter().zip(&self.segment_domain.end_point).enumerate() { for (segment_index, (&start, &end)) in self.segment_domain.start_point.iter().zip(&self.segment_domain.end_point).enumerate() {
@@ -1190,16 +1099,6 @@ impl Vector {
}) })
} }
/// Construct an iterator [`ManipulatorGroup`] for stroke.
pub fn manipulator_groups(&self) -> impl Iterator<Item = ManipulatorGroup<PointId>> + '_ {
self.stroke_bezier_paths().flat_map(|mut path| std::mem::take(path.manipulator_groups_mut()))
}
pub fn manipulator_group_id(&self, id: impl Into<PointId>) -> Option<ManipulatorGroup<PointId>> {
let id = id.into();
self.manipulator_groups().find(|manipulators| manipulators.id == id)
}
pub fn transform(&mut self, transform: DAffine2) { pub fn transform(&mut self, transform: DAffine2) {
self.point_domain.transform(transform); self.point_domain.transform(transform);
self.segment_domain.transform(transform); self.segment_domain.transform(transform);

View File

@@ -806,11 +806,9 @@ impl HandleExt for HandleId {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use kurbo::{PathSeg, QuadBez};
use super::*; use super::*;
use crate::subpath::{Bezier, Subpath}; use crate::subpath::{Bezier, ManipulatorGroup, Subpath};
#[test] #[test]
fn modify_new() { fn modify_new() {
@@ -828,10 +826,11 @@ mod tests {
let subpaths = [ let subpaths = [
Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE), Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE),
Subpath::new_rectangle(DVec2::NEG_ONE, DVec2::ZERO), Subpath::new_rectangle(DVec2::NEG_ONE, DVec2::ZERO),
Subpath::from_beziers( Subpath::new(
&[ vec![
PathSeg::Quad(QuadBez::new(Point::new(0., 0.), Point::new(5., 10.), Point::new(10., 0.))), ManipulatorGroup::new(DVec2::new(0., 0.), None, None),
PathSeg::Quad(QuadBez::new(Point::new(10., 0.), Point::new(15., 10.), Point::new(20., 0.))), ManipulatorGroup::new(DVec2::new(10., 0.), Some(DVec2::new(5., 10.)), None),
ManipulatorGroup::new(DVec2::new(20., 0.), Some(DVec2::new(15., 10.)), None),
], ],
false, false,
), ),

View File

@@ -2,7 +2,6 @@ use super::misc::dvec2_to_point;
use super::style::{Stroke, StrokeAlign, StrokeCap, StrokeJoin}; use super::style::{Stroke, StrokeAlign, StrokeCap, StrokeJoin};
pub use super::vector_attributes::*; pub use super::vector_attributes::*;
use crate::subpath::{BezierHandles, ManipulatorGroup, Subpath}; use crate::subpath::{BezierHandles, ManipulatorGroup, Subpath};
use crate::vector::click_target::{ClickTargetType, FreePoint};
use crate::vector::misc::{HandleId, ManipulatorPointId}; use crate::vector::misc::{HandleId, ManipulatorPointId};
use crate::vector::vector_modification::VectorExt; use crate::vector::vector_modification::VectorExt;
use core::borrow::Borrow; use core::borrow::Borrow;
@@ -135,18 +134,6 @@ impl Vector {
} }
} }
pub fn append_free_point(&mut self, point: &FreePoint, preserve_id: bool) {
let mut point_id = self.point_domain.next_id();
// Use the current point ID if it's not already in the domain, otherwise generate a new one
let id = if preserve_id && !self.point_domain.ids().contains(&point.id) {
point.id
} else {
point_id.next_id()
};
self.point_domain.push(id, point.position);
}
/// Construct some new vector path from a single subpath with an identity transform and black fill. /// Construct some new vector path from a single subpath with an identity transform and black fill.
pub fn from_subpath(subpath: impl Borrow<Subpath<PointId>>) -> Self { pub fn from_subpath(subpath: impl Borrow<Subpath<PointId>>) -> Self {
Self::from_subpaths([subpath], false) Self::from_subpaths([subpath], false)
@@ -170,24 +157,6 @@ impl Vector {
vector vector
} }
pub fn from_target_types(target_types: impl IntoIterator<Item = impl Borrow<ClickTargetType>>, preserve_id: bool) -> Self {
let mut vector = Self::default();
for target_type in target_types.into_iter() {
match target_type.borrow() {
ClickTargetType::Subpath(subpath) => vector.append_subpath(subpath, preserve_id),
ClickTargetType::FreePoint(point) => vector.append_free_point(point, preserve_id),
ClickTargetType::CompoundPath(subpaths) => {
for subpath in subpaths {
vector.append_subpath(subpath, preserve_id);
}
}
}
}
vector
}
/// Compute the bounding boxes of the bezpaths without any transform /// Compute the bounding boxes of the bezpaths without any transform
pub fn bounding_box_rect(&self) -> Option<Rect> { pub fn bounding_box_rect(&self) -> Option<Rect> {
self.bounding_box_with_transform_rect(DAffine2::IDENTITY) self.bounding_box_with_transform_rect(DAffine2::IDENTITY)
@@ -321,13 +290,6 @@ impl Vector {
[bounds_min, bounds_max] [bounds_min, bounds_max]
} }
/// Compute the pivot of the layer in layerspace (the coordinates of the subpaths)
pub fn layerspace_pivot(&self, normalized_pivot: DVec2) -> DVec2 {
let [bounds_min, bounds_max] = self.nonzero_bounding_box();
let bounds_size = bounds_max - bounds_min;
bounds_min + bounds_size * normalized_pivot
}
pub fn start_point(&self) -> impl Iterator<Item = PointId> + '_ { pub fn start_point(&self) -> impl Iterator<Item = PointId> + '_ {
self.segment_domain.start_point().iter().map(|&index| self.point_domain.ids()[index]) self.segment_domain.start_point().iter().map(|&index| self.point_domain.ids()[index])
} }
@@ -362,11 +324,6 @@ impl Vector {
self.segment_domain.segment_end_from_id(segment).map(|index| self.point_domain.ids()[index]) self.segment_domain.segment_end_from_id(segment).map(|index| self.point_domain.ids()[index])
} }
/// Returns an array for the start and end points of a segment.
pub fn points_from_id(&self, segment: SegmentId) -> Option<[PointId; 2]> {
self.segment_domain.points_from_id(segment).map(|val| val.map(|index| self.point_domain.ids()[index]))
}
/// Attempts to find another point in the segment that is not the one passed in. /// Attempts to find another point in the segment that is not the one passed in.
pub fn other_point(&self, segment: SegmentId, current: PointId) -> Option<PointId> { pub fn other_point(&self, segment: SegmentId, current: PointId) -> Option<PointId> {
let index = self.point_domain.resolve_id(current); let index = self.point_domain.resolve_id(current);
@@ -595,7 +552,13 @@ mod tests {
#[test] #[test]
fn construct_open_subpath() { fn construct_open_subpath() {
let bezier = PathSeg::Cubic(CubicBez::new(Point::ZERO, Point::new(-1., -1.), Point::new(1., 1.), Point::new(1., 0.))); let bezier = PathSeg::Cubic(CubicBez::new(Point::ZERO, Point::new(-1., -1.), Point::new(1., 1.), Point::new(1., 0.)));
let subpath = Subpath::from_bezier(bezier); let subpath = Subpath::new(
vec![
ManipulatorGroup::new(DVec2::ZERO, None, Some(DVec2::new(-1., -1.))),
ManipulatorGroup::new(DVec2::new(1., 0.), Some(DVec2::new(1., 1.)), None),
],
false,
);
let vector: Vector = Vector::from_subpath(&subpath); let vector: Vector = Vector::from_subpath(&subpath);
assert_eq!(vector.point_domain.ids().len(), 2); assert_eq!(vector.point_domain.ids().len(), 2);
let bezier_paths = vector.segment_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>(); let bezier_paths = vector.segment_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>();
@@ -607,8 +570,13 @@ mod tests {
#[test] #[test]
fn construct_many_subpath() { fn construct_many_subpath() {
let curve = PathSeg::Cubic(CubicBez::new(Point::ZERO, Point::new(-1., -1.), Point::new(1., 1.), Point::new(1., 0.))); let curve = Subpath::new(
let curve = Subpath::from_bezier(curve); vec![
ManipulatorGroup::new(DVec2::ZERO, None, Some(DVec2::new(-1., -1.))),
ManipulatorGroup::new(DVec2::new(1., 0.), Some(DVec2::new(1., 1.)), None),
],
false,
);
let circle = Subpath::new_ellipse(DVec2::NEG_ONE, DVec2::ONE); let circle = Subpath::new_ellipse(DVec2::NEG_ONE, DVec2::ONE);
let vector: Vector = Vector::from_subpaths([&curve, &circle], false); let vector: Vector = Vector::from_subpaths([&curve, &circle], false);

View File

@@ -74,7 +74,7 @@ pub mod math {
pub use core_types::math::quad; pub use core_types::math::quad;
pub mod math_ext { pub mod math_ext {
pub use vector_types::{QuadExt, RectExt}; pub use vector_types::QuadExt;
} }
} }

View File

@@ -323,8 +323,8 @@ mod test {
.await; .await;
let vector_list = List::new_from_item(vector_nodes::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(repeated))).await); let vector_list = List::new_from_item(vector_nodes::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(repeated))).await);
let vector = vector_list.element(0).unwrap(); let vector = vector_list.element(0).unwrap();
assert_eq!(vector.region_manipulator_groups().count(), 3); assert_eq!(vector.stroke_manipulator_groups().count(), 3);
for (index, (_, manipulator_groups)) in vector.region_manipulator_groups().enumerate() { for (index, (manipulator_groups, _)) in vector.stroke_manipulator_groups().enumerate() {
assert!((manipulator_groups[0].anchor - direction * index as f64 / (count - 1) as f64).length() < 1e-5); assert!((manipulator_groups[0].anchor - direction * index as f64 / (count - 1) as f64).length() < 1e-5);
} }
} }
@@ -342,9 +342,9 @@ mod test {
.await; .await;
let vector_list = List::new_from_item(vector_nodes::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(repeated))).await); let vector_list = List::new_from_item(vector_nodes::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(repeated))).await);
let vector = vector_list.element(0).unwrap(); let vector = vector_list.element(0).unwrap();
assert_eq!(vector.region_manipulator_groups().count(), 1); assert_eq!(vector.stroke_manipulator_groups().count(), 1);
let (_, manipulator_groups) = vector.region_manipulator_groups().next().unwrap(); let (manipulator_groups, _) = vector.stroke_manipulator_groups().next().unwrap();
let anchor = manipulator_groups[0].anchor; let anchor = manipulator_groups[0].anchor;
assert!(anchor.length() < 1e-5, "Expected the single copy to be untransformed, found anchor {anchor}"); assert!(anchor.length() < 1e-5, "Expected the single copy to be untransformed, found anchor {anchor}");
} }
@@ -364,8 +364,8 @@ mod test {
.await; .await;
let vector_list = List::new_from_item(vector_nodes::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(repeated))).await); let vector_list = List::new_from_item(vector_nodes::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(repeated))).await);
let vector = vector_list.element(0).unwrap(); let vector = vector_list.element(0).unwrap();
assert_eq!(vector.region_manipulator_groups().count(), 8); assert_eq!(vector.stroke_manipulator_groups().count(), 8);
for (index, (_, manipulator_groups)) in vector.region_manipulator_groups().enumerate() { for (index, (manipulator_groups, _)) in vector.stroke_manipulator_groups().enumerate() {
assert!((manipulator_groups[0].anchor - direction * index as f64 / (count - 1) as f64).length() < 1e-5); assert!((manipulator_groups[0].anchor - direction * index as f64 / (count - 1) as f64).length() < 1e-5);
} }
} }
@@ -383,9 +383,9 @@ mod test {
.await; .await;
let vector_list = List::new_from_item(vector_nodes::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(repeated))).await); let vector_list = List::new_from_item(vector_nodes::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(repeated))).await);
let vector = vector_list.element(0).unwrap(); let vector = vector_list.element(0).unwrap();
assert_eq!(vector.region_manipulator_groups().count(), 8); assert_eq!(vector.stroke_manipulator_groups().count(), 8);
for (index, (_, manipulator_groups)) in vector.region_manipulator_groups().enumerate() { for (index, (manipulator_groups, _)) in vector.stroke_manipulator_groups().enumerate() {
let expected_angle = (index as f64 + 1.) * 45.; let expected_angle = (index as f64 + 1.) * 45.;
let center = (manipulator_groups[0].anchor + manipulator_groups[2].anchor) / 2.; let center = (manipulator_groups[0].anchor + manipulator_groups[2].anchor) / 2.;

View File

@@ -42,11 +42,7 @@ fn arc(
radius, radius,
start_angle / 360. * std::f64::consts::TAU, start_angle / 360. * std::f64::consts::TAU,
sweep_angle / 360. * std::f64::consts::TAU, sweep_angle / 360. * std::f64::consts::TAU,
match arc_type { arc_type,
ArcType::Open => subpath::ArcType::Open,
ArcType::Closed => subpath::ArcType::Closed,
ArcType::PieSlice => subpath::ArcType::PieSlice,
},
))) )))
} }

View File

@@ -3776,12 +3776,12 @@ mod test {
async fn bounding_box() { async fn bounding_box() {
let bounding_box = super::bounding_box((), Item::new_from_element(Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY)))).await; let bounding_box = super::bounding_box((), Item::new_from_element(Vector::from_bezpath(Rect::new(-1., -1., 1., 1.).to_path(DEFAULT_ACCURACY)))).await;
let bounding_box = bounding_box.element(); let bounding_box = bounding_box.element();
assert_eq!(bounding_box.region_manipulator_groups().count(), 1); assert_eq!(bounding_box.stroke_manipulator_groups().count(), 1);
let manipulator_groups_anchors = bounding_box let manipulator_groups_anchors = bounding_box
.region_manipulator_groups() .stroke_manipulator_groups()
.next() .next()
.unwrap() .unwrap()
.1 .0
.iter() .iter()
.map(|manipulators| manipulators.anchor) .map(|manipulators| manipulators.anchor)
.collect::<Vec<DVec2>>(); .collect::<Vec<DVec2>>();
@@ -3794,12 +3794,12 @@ mod test {
square.with_attribute_mut_or_default(ATTR_TRANSFORM, 0, |t: &mut DAffine2| *t *= DAffine2::from_angle(std::f64::consts::FRAC_PI_4)); square.with_attribute_mut_or_default(ATTR_TRANSFORM, 0, |t: &mut DAffine2| *t *= DAffine2::from_angle(std::f64::consts::FRAC_PI_4));
let bounding_box = BoundingBoxNodeMapped { content: FutureWrapperNode(square) }.eval(Footprint::default()).await; let bounding_box = BoundingBoxNodeMapped { content: FutureWrapperNode(square) }.eval(Footprint::default()).await;
let bounding_box = bounding_box.element(0).unwrap(); let bounding_box = bounding_box.element(0).unwrap();
assert_eq!(bounding_box.region_manipulator_groups().count(), 1); assert_eq!(bounding_box.stroke_manipulator_groups().count(), 1);
let manipulator_groups_anchors = bounding_box let manipulator_groups_anchors = bounding_box
.region_manipulator_groups() .stroke_manipulator_groups()
.next() .next()
.unwrap() .unwrap()
.1 .0
.iter() .iter()
.map(|manipulators| manipulators.anchor) .map(|manipulators| manipulators.anchor)
.collect::<Vec<DVec2>>(); .collect::<Vec<DVec2>>();
@@ -3831,9 +3831,9 @@ mod test {
let combined = List::new_from_item(super::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(copy_to_points))).await); let combined = List::new_from_item(super::combine_paths(Footprint::default(), List::new_from_element(Graphic::VectorList(copy_to_points))).await);
let combined_copy_to_points = combined.element(0).unwrap(); let combined_copy_to_points = combined.element(0).unwrap();
assert_eq!(combined_copy_to_points.region_manipulator_groups().count(), expected_points.len()); assert_eq!(combined_copy_to_points.stroke_manipulator_groups().count(), expected_points.len());
for (index, (_, manipulator_groups)) in combined_copy_to_points.region_manipulator_groups().enumerate() { for (index, (manipulator_groups, _)) in combined_copy_to_points.stroke_manipulator_groups().enumerate() {
let offset = expected_points[index]; let offset = expected_points[index];
let manipulator_groups_anchors = manipulator_groups.iter().map(|manipulators| manipulators.anchor).collect::<Vec<DVec2>>(); let manipulator_groups_anchors = manipulator_groups.iter().map(|manipulators| manipulators.anchor).collect::<Vec<DVec2>>();
assert_eq!( assert_eq!(