mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Remove remnant dead code across the Subpath, Vector, and editor geometry API surfaces (#4454)
This commit is contained in:
@@ -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);
|
||||
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
|
||||
@@ -3791,7 +3792,6 @@ enum XRayTarget {
|
||||
Point(DVec2),
|
||||
Quad(Quad),
|
||||
Path(BezPath),
|
||||
Polygon(Subpath<PointId>),
|
||||
}
|
||||
|
||||
/// 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::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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@ use graphene_std::Color;
|
||||
use graphene_std::brush::brush_stroke::BrushStroke;
|
||||
use graphene_std::raster::BlendMode;
|
||||
use graphene_std::raster_types::Image;
|
||||
use graphene_std::subpath::Subpath;
|
||||
use graphene_std::text::{Font, TypesettingConfig};
|
||||
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)]
|
||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
@@ -162,12 +161,6 @@ pub enum GraphOperationMessage {
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: usize,
|
||||
},
|
||||
NewVectorLayer {
|
||||
id: NodeId,
|
||||
subpaths: Vec<Subpath<PointId>>,
|
||||
parent: LayerNodeIdentifier,
|
||||
insert_index: usize,
|
||||
},
|
||||
NewTextLayer {
|
||||
id: NodeId,
|
||||
text: String,
|
||||
|
||||
@@ -363,13 +363,6 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
|
||||
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
|
||||
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 {
|
||||
id,
|
||||
text,
|
||||
|
||||
@@ -10,7 +10,6 @@ use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||
use graphene_std::Color;
|
||||
use graphene_std::raster::BlendMode;
|
||||
use graphene_std::raster_types::Image;
|
||||
use graphene_std::subpath::Subpath;
|
||||
use graphene_std::text::{Font, TypesettingConfig};
|
||||
use graphene_std::vector::misc::ManipulatorPointId;
|
||||
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 });
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn new_image_layer(image: Image<Color>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
|
||||
let insert_index = 0;
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::messages::tool::utility_types::*;
|
||||
use glam::{DAffine2, DMat2, DVec2};
|
||||
use graph_craft::document::NodeInput;
|
||||
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::misc::{ArcType, GridType, SpiralType, dvec2_to_point};
|
||||
use kurbo::{BezPath, PathEl, Shape};
|
||||
@@ -479,11 +479,7 @@ pub fn arc_outline(layer: Option<LayerNodeIdentifier>, document: &DocumentMessag
|
||||
radius,
|
||||
start_angle / 360. * std::f64::consts::TAU,
|
||||
sweep_angle / 360. * std::f64::consts::TAU,
|
||||
match arc_type {
|
||||
ArcType::Open => subpath::ArcType::Open,
|
||||
ArcType::Closed => subpath::ArcType::Closed,
|
||||
ArcType::PieSlice => subpath::ArcType::PieSlice,
|
||||
},
|
||||
arc_type,
|
||||
))];
|
||||
let viewport = document.metadata().transform_to_viewport(layer);
|
||||
|
||||
|
||||
@@ -13,11 +13,10 @@ use crate::messages::tool::utility_types::ToolType;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
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::misc::{HandleId, ManipulatorPointId, dvec2_to_point};
|
||||
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.
|
||||
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)]
|
||||
pub fn resize_bounds(
|
||||
document: &DocumentMessageHandler,
|
||||
|
||||
@@ -390,7 +390,6 @@ macro_rules! tagged_value {
|
||||
Type::Generic(_) => None,
|
||||
Type::Concrete(concrete_type) => {
|
||||
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.
|
||||
if name == std::any::type_name::<()>() { return Some(TaggedValue::None) }
|
||||
if name == std::any::type_name::<Gradient>() { return Some(TaggedValue::GradientRamp(GradientRamp::default())) }
|
||||
@@ -671,7 +670,6 @@ impl TaggedValue {
|
||||
Type::Concrete(concrete_type) => {
|
||||
let ty = concrete_type.id?;
|
||||
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.
|
||||
let ty = match () {
|
||||
() if ty == TypeId::of::<()>() => TaggedValue::None,
|
||||
|
||||
@@ -36,13 +36,6 @@ impl Rect {
|
||||
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
|
||||
#[must_use]
|
||||
pub fn center(&self) -> DVec2 {
|
||||
|
||||
@@ -9,7 +9,7 @@ pub mod vector;
|
||||
// Re-export commonly used types at the crate root
|
||||
pub use core_types as gcore;
|
||||
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 vector::Vector;
|
||||
pub use vector::reference_point::ReferencePoint;
|
||||
|
||||
@@ -1,32 +1,13 @@
|
||||
use crate::subpath::Bezier;
|
||||
use crate::vector::misc::dvec2_to_point;
|
||||
use core_types::math::quad::Quad;
|
||||
use core_types::math::rect::Rect;
|
||||
use kurbo::{Line, PathSeg};
|
||||
|
||||
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>;
|
||||
}
|
||||
|
||||
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> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use super::consts::*;
|
||||
use super::*;
|
||||
use crate::vector::misc::{SpiralType, point_to_dvec2};
|
||||
use crate::vector::misc::{ArcType, SpiralType, point_to_dvec2};
|
||||
use glam::DVec2;
|
||||
use kurbo::PathSeg;
|
||||
use std::f64::consts::TAU;
|
||||
@@ -36,55 +35,6 @@ impl<PointId: Identifier> Subpath<PointId> {
|
||||
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].
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.manipulator_groups.is_empty()
|
||||
@@ -95,23 +45,6 @@ impl<PointId: Identifier> Subpath<PointId> {
|
||||
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`.
|
||||
pub fn iter(&self) -> SubpathIter<'_, PointId> {
|
||||
SubpathIter {
|
||||
@@ -140,22 +73,6 @@ impl<PointId: Identifier> Subpath<PointId> {
|
||||
&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 {
|
||||
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.
|
||||
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 {
|
||||
SpiralType::Archimedean => archimedean_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.
|
||||
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 {
|
||||
SpiralType::Archimedean => archimedean_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`.
|
||||
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θ)
|
||||
DVec2::new(r * theta.cos(), -r * theta.sin())
|
||||
}
|
||||
|
||||
/// 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();
|
||||
(a / b) * factor * ((b * theta_end).exp() - (b * theta_start).exp())
|
||||
}
|
||||
|
||||
/// 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 dx = r * (b * theta.cos() - theta.sin());
|
||||
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`.
|
||||
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;
|
||||
DVec2::new(r * theta.cos(), -r * theta.sin())
|
||||
}
|
||||
|
||||
/// 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 dx = b * theta.cos() - r * theta.sin();
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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 sqrt_term = (r * r + b * b).sqrt();
|
||||
(r * sqrt_term + b * b * ((r + sqrt_term).ln())) / (2. * b)
|
||||
|
||||
@@ -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
|
||||
///
|
||||
/// **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 err = accuracy.unwrap_or(MAX_ABSOLUTE_DIFFERENCE);
|
||||
let num_curves = self.len();
|
||||
|
||||
@@ -13,27 +13,6 @@ impl<PointId: super::structs::Identifier> Subpath<PointId> {
|
||||
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.
|
||||
pub fn push_manipulator_group(&mut self, group: ManipulatorGroup<PointId>) {
|
||||
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>> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ mod transform;
|
||||
pub use core::*;
|
||||
use kurbo::PathSeg;
|
||||
use std::fmt::{Debug, Formatter, Result};
|
||||
use std::ops::{Index, IndexMut};
|
||||
pub use structs::*;
|
||||
|
||||
/// Structure used to represent a path composed of [Bezier] curves.
|
||||
@@ -27,22 +26,6 @@ pub struct SubpathIter<'a, PointId: Identifier> {
|
||||
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> {
|
||||
type Item = PathSeg;
|
||||
|
||||
@@ -60,7 +43,7 @@ impl<PointId: Identifier> Iterator for SubpathIter<'_, PointId> {
|
||||
let end_index = (self.index + 1) % self.subpath.len();
|
||||
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]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,37 +77,6 @@ impl<PointId: Identifier> ManipulatorGroup<PointId> {
|
||||
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())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -130,10 +99,6 @@ pub enum BezierHandles {
|
||||
}
|
||||
|
||||
impl BezierHandles {
|
||||
pub fn is_cubic(&self) -> bool {
|
||||
matches!(self, Self::Cubic { .. })
|
||||
}
|
||||
|
||||
pub fn is_finite(&self) -> bool {
|
||||
match self {
|
||||
BezierHandles::Linear => true,
|
||||
|
||||
@@ -1,62 +1,12 @@
|
||||
use super::structs::Identifier;
|
||||
use super::*;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use glam::DAffine2;
|
||||
|
||||
/// Functionality that transforms Subpaths, such as split, reduce, offset, etc.
|
||||
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].
|
||||
pub fn apply_transform(&mut self, affine_transform: DAffine2) {
|
||||
for manipulator_group in &mut self.manipulator_groups {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use super::intersection::bezpath_intersections;
|
||||
use super::poisson_disk::poisson_disk_sample;
|
||||
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 core_types::math::polynomial::pathseg_to_parametric_polynomial;
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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: 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.
|
||||
|
||||
@@ -14,12 +14,6 @@ pub fn pathseg_tangent(segment: PathSeg, t: f64) -> DVec2 {
|
||||
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
|
||||
#[cfg(test)]
|
||||
pub fn compare_points(p1: kurbo::Point, p2: kurbo::Point) -> bool {
|
||||
|
||||
@@ -93,11 +93,6 @@ impl PointDomain {
|
||||
Self { id: Vec::new(), position: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.id.clear();
|
||||
self.position.clear();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
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)]
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
self.id.reserve(additional);
|
||||
@@ -401,16 +388,6 @@ impl SegmentDomain {
|
||||
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) {
|
||||
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));
|
||||
@@ -609,12 +586,6 @@ impl RegionDomain {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.id.clear();
|
||||
self.segment_range.clear();
|
||||
self.fill.clear();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
self.id.reserve(additional);
|
||||
@@ -759,10 +730,6 @@ pub struct FoundSubpath {
|
||||
}
|
||||
|
||||
impl FoundSubpath {
|
||||
pub fn new(segments: Vec<HalfEdge>) -> Self {
|
||||
Self { edges: segments }
|
||||
}
|
||||
|
||||
pub fn endpoints(&self) -> Option<(&HalfEdge, &HalfEdge)> {
|
||||
match (self.edges.first(), self.edges.last()) {
|
||||
(Some(first), Some(last)) => Some((first, last)),
|
||||
@@ -774,21 +741,6 @@ impl FoundSubpath {
|
||||
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 {
|
||||
match (self.edges.first(), self.edges.last()) {
|
||||
(Some(first), Some(last)) => first.start == last.end,
|
||||
@@ -1088,49 +1040,6 @@ impl Vector {
|
||||
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<'_> {
|
||||
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() {
|
||||
@@ -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) {
|
||||
self.point_domain.transform(transform);
|
||||
self.segment_domain.transform(transform);
|
||||
|
||||
@@ -806,11 +806,9 @@ impl HandleExt for HandleId {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use kurbo::{PathSeg, QuadBez};
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::subpath::{Bezier, Subpath};
|
||||
use crate::subpath::{Bezier, ManipulatorGroup, Subpath};
|
||||
|
||||
#[test]
|
||||
fn modify_new() {
|
||||
@@ -828,10 +826,11 @@ mod tests {
|
||||
let subpaths = [
|
||||
Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE),
|
||||
Subpath::new_rectangle(DVec2::NEG_ONE, DVec2::ZERO),
|
||||
Subpath::from_beziers(
|
||||
&[
|
||||
PathSeg::Quad(QuadBez::new(Point::new(0., 0.), Point::new(5., 10.), Point::new(10., 0.))),
|
||||
PathSeg::Quad(QuadBez::new(Point::new(10., 0.), Point::new(15., 10.), Point::new(20., 0.))),
|
||||
Subpath::new(
|
||||
vec![
|
||||
ManipulatorGroup::new(DVec2::new(0., 0.), None, None),
|
||||
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,
|
||||
),
|
||||
|
||||
@@ -2,7 +2,6 @@ use super::misc::dvec2_to_point;
|
||||
use super::style::{Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
pub use super::vector_attributes::*;
|
||||
use crate::subpath::{BezierHandles, ManipulatorGroup, Subpath};
|
||||
use crate::vector::click_target::{ClickTargetType, FreePoint};
|
||||
use crate::vector::misc::{HandleId, ManipulatorPointId};
|
||||
use crate::vector::vector_modification::VectorExt;
|
||||
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.
|
||||
pub fn from_subpath(subpath: impl Borrow<Subpath<PointId>>) -> Self {
|
||||
Self::from_subpaths([subpath], false)
|
||||
@@ -170,24 +157,6 @@ impl 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
|
||||
pub fn bounding_box_rect(&self) -> Option<Rect> {
|
||||
self.bounding_box_with_transform_rect(DAffine2::IDENTITY)
|
||||
@@ -321,13 +290,6 @@ impl Vector {
|
||||
[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> + '_ {
|
||||
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])
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn other_point(&self, segment: SegmentId, current: PointId) -> Option<PointId> {
|
||||
let index = self.point_domain.resolve_id(current);
|
||||
@@ -595,7 +552,13 @@ mod tests {
|
||||
#[test]
|
||||
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 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);
|
||||
assert_eq!(vector.point_domain.ids().len(), 2);
|
||||
let bezier_paths = vector.segment_iter().map(|(_, bezier, _, _)| bezier).collect::<Vec<_>>();
|
||||
@@ -607,8 +570,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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::from_bezier(curve);
|
||||
let curve = 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 circle = Subpath::new_ellipse(DVec2::NEG_ONE, DVec2::ONE);
|
||||
|
||||
let vector: Vector = Vector::from_subpaths([&curve, &circle], false);
|
||||
|
||||
@@ -74,7 +74,7 @@ pub mod math {
|
||||
pub use core_types::math::quad;
|
||||
|
||||
pub mod math_ext {
|
||||
pub use vector_types::{QuadExt, RectExt};
|
||||
pub use vector_types::QuadExt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -323,8 +323,8 @@ mod test {
|
||||
.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();
|
||||
assert_eq!(vector.region_manipulator_groups().count(), 3);
|
||||
for (index, (_, manipulator_groups)) in vector.region_manipulator_groups().enumerate() {
|
||||
assert_eq!(vector.stroke_manipulator_groups().count(), 3);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -342,9 +342,9 @@ mod test {
|
||||
.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();
|
||||
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;
|
||||
assert!(anchor.length() < 1e-5, "Expected the single copy to be untransformed, found anchor {anchor}");
|
||||
}
|
||||
@@ -364,8 +364,8 @@ mod test {
|
||||
.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();
|
||||
assert_eq!(vector.region_manipulator_groups().count(), 8);
|
||||
for (index, (_, manipulator_groups)) in vector.region_manipulator_groups().enumerate() {
|
||||
assert_eq!(vector.stroke_manipulator_groups().count(), 8);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -383,9 +383,9 @@ mod test {
|
||||
.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();
|
||||
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 center = (manipulator_groups[0].anchor + manipulator_groups[2].anchor) / 2.;
|
||||
|
||||
@@ -42,11 +42,7 @@ fn arc(
|
||||
radius,
|
||||
start_angle / 360. * std::f64::consts::TAU,
|
||||
sweep_angle / 360. * std::f64::consts::TAU,
|
||||
match arc_type {
|
||||
ArcType::Open => subpath::ArcType::Open,
|
||||
ArcType::Closed => subpath::ArcType::Closed,
|
||||
ArcType::PieSlice => subpath::ArcType::PieSlice,
|
||||
},
|
||||
arc_type,
|
||||
)))
|
||||
}
|
||||
|
||||
|
||||
@@ -3776,12 +3776,12 @@ mod test {
|
||||
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 = 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
|
||||
.region_manipulator_groups()
|
||||
.stroke_manipulator_groups()
|
||||
.next()
|
||||
.unwrap()
|
||||
.1
|
||||
.0
|
||||
.iter()
|
||||
.map(|manipulators| manipulators.anchor)
|
||||
.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));
|
||||
let bounding_box = BoundingBoxNodeMapped { content: FutureWrapperNode(square) }.eval(Footprint::default()).await;
|
||||
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
|
||||
.region_manipulator_groups()
|
||||
.stroke_manipulator_groups()
|
||||
.next()
|
||||
.unwrap()
|
||||
.1
|
||||
.0
|
||||
.iter()
|
||||
.map(|manipulators| manipulators.anchor)
|
||||
.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_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 manipulator_groups_anchors = manipulator_groups.iter().map(|manipulators| manipulators.anchor).collect::<Vec<DVec2>>();
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user