Clean up the Vector struct implementations by privatizing and deleting unused APIs (#4458)

Trim the Vector API surface by privatizing and deleting caller-less items
This commit is contained in:
Keavon Chambers
2026-08-18 15:54:55 -07:00
committed by GitHub
parent e3b968f7e2
commit 5c580f9dac
12 changed files with 76 additions and 147 deletions

View File

@@ -12,7 +12,7 @@ const MAX_ABSOLUTE_DIFFERENCE: f64 = 1e-3;
/// Splits the [`BezPath`] at segment index at `t` value which lie in the range of [0, 1].
/// Returns [`None`] if the given [`BezPath`] has no segments or `t` is within f64::EPSILON of 0 or 1.
pub fn split_bezpath_at_segment(bezpath: &BezPath, segment_index: usize, t: f64) -> Option<(BezPath, BezPath)> {
fn split_bezpath_at_segment(bezpath: &BezPath, segment_index: usize, t: f64) -> Option<(BezPath, BezPath)> {
if t <= f64::EPSILON || (1. - t) <= f64::EPSILON || bezpath.segments().count() == 0 {
return None;
}
@@ -184,7 +184,7 @@ pub enum TValue {
}
/// Default LUT step size in `compute_lookup_table` function.
pub const DEFAULT_LUT_STEP_SIZE: usize = 10;
const DEFAULT_LUT_STEP_SIZE: usize = 10;
/// Return a selection of equidistant points on the bezier curve.
/// If no value is provided for `steps`, then the function will default `steps` to be 10.
@@ -488,7 +488,7 @@ pub fn miter_line_join(bezpath1: &BezPath, bezpath2: &BezPath, miter_limit: Opti
/// Computes the [`PathEl`] to form a circular join from `left` to `right`, along a circle around `center`.
/// By default, the angle is assumed to be 180 degrees.
pub fn compute_circular_subpath_details(left: DVec2, arc_point: DVec2, right: DVec2, center: DVec2, angle: Option<f64>) -> [PathEl; 2] {
fn compute_circular_subpath_details(left: DVec2, arc_point: DVec2, right: DVec2, center: DVec2, angle: Option<f64>) -> [PathEl; 2] {
let center_to_arc_point = arc_point - center;
// Based on https://pomax.github.io/bezierinfo/#circles_cubic

View File

@@ -1,6 +1,6 @@
/// Minimum allowable separation between adjacent `t` values when calculating curve intersections
pub const MIN_SEPARATION_VALUE: f64 = 5. * 1e-3;
pub(crate) const MIN_SEPARATION_VALUE: f64 = 5. * 1e-3;
/// Constant used to determine if `f64`s are equivalent.
#[cfg(test)]
pub const MAX_ABSOLUTE_DIFFERENCE: f64 = 1e-3;
pub(crate) const MAX_ABSOLUTE_DIFFERENCE: f64 = 1e-3;

View File

@@ -25,7 +25,7 @@ fn cubic_cubic_intersections_lyon(cubic1: kurbo::CubicBez, cubic2: kurbo::CubicB
/// that segment where the intersection occurred.
///
/// `minimum_separation` is the minimum difference that two adjacent `t`-values must have when comparing adjacent `t`-values in sorted order.
pub fn bezpath_and_segment_intersections(bezpath: &BezPath, segment: PathSeg, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
fn bezpath_and_segment_intersections(bezpath: &BezPath, segment: PathSeg, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
bezpath
.segments()
.enumerate()
@@ -39,7 +39,7 @@ pub fn bezpath_and_segment_intersections(bezpath: &BezPath, segment: PathSeg, ac
}
/// Calculates the intersection points the bezpath has with another given bezpath and returns a list of parametric `t`-values.
pub fn bezpath_intersections(bezpath1: &BezPath, bezpath2: &BezPath, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
pub(crate) fn bezpath_intersections(bezpath1: &BezPath, bezpath2: &BezPath, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(usize, f64)> {
let mut intersection_t_values: Vec<(usize, f64)> = bezpath2
.segments()
.flat_map(|bezier| bezpath_and_segment_intersections(bezpath1, bezier, accuracy, minimum_separation))
@@ -50,7 +50,7 @@ pub fn bezpath_intersections(bezpath1: &BezPath, bezpath2: &BezPath, accuracy: O
}
/// Calculates the intersection points the segment has with another given segment and returns a list of parametric `t`-values with given accuracy.
pub fn segment_intersections(segment1: PathSeg, segment2: PathSeg, accuracy: Option<f64>) -> Vec<(f64, f64)> {
fn segment_intersections(segment1: PathSeg, segment2: PathSeg, accuracy: Option<f64>) -> Vec<(f64, f64)> {
let accuracy = accuracy.unwrap_or(DEFAULT_ACCURACY);
match (segment1, segment2) {
@@ -66,7 +66,7 @@ pub fn segment_intersections(segment1: PathSeg, segment2: PathSeg, accuracy: Opt
}
}
pub fn subsegment_intersections(segment1: PathSeg, min_t1: f64, max_t1: f64, segment2: PathSeg, min_t2: f64, max_t2: f64, accuracy: Option<f64>) -> Vec<(f64, f64)> {
fn subsegment_intersections(segment1: PathSeg, min_t1: f64, max_t1: f64, segment2: PathSeg, min_t2: f64, max_t2: f64, accuracy: Option<f64>) -> Vec<(f64, f64)> {
let accuracy = accuracy.unwrap_or(DEFAULT_ACCURACY);
match (segment1, segment2) {
@@ -180,7 +180,7 @@ pub fn filtered_segment_intersections(segment1: PathSeg, segment2: PathSeg, accu
/// `error`, for intersections where the provided bezier is non-linear, defines the threshold for bounding boxes to be considered an intersection point.
///
/// `minimum_separation` is the minimum difference between adjacent `t` values in sorted order
pub fn filtered_all_segment_intersections(segment1: PathSeg, segment2: PathSeg, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(f64, f64)> {
pub(crate) fn filtered_all_segment_intersections(segment1: PathSeg, segment2: PathSeg, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(f64, f64)> {
let mut intersection_t_values = segment_intersections(segment1, segment2, accuracy);
intersection_t_values.sort_by(|a, b| (a.0 + a.1).partial_cmp(&(b.0 + b.1)).unwrap());
@@ -240,7 +240,7 @@ fn pathseg_self_intersection(segment: PathSeg, accuracy: Option<f64>) -> Vec<(f6
/// If the difference between 2 adjacent `t` values is less than the minimum difference, the filtering takes the larger `t` value and discards the smaller `t` value.
/// - `error` - For intersections with non-linear beziers, `error` defines the threshold for bounding boxes to be considered an intersection point.
/// - `minimum_separation` - The minimum difference between adjacent `t` values in sorted order
pub fn pathseg_self_intersections(segment: PathSeg, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(f64, f64)> {
pub(crate) fn pathseg_self_intersections(segment: PathSeg, accuracy: Option<f64>, minimum_separation: Option<f64>) -> Vec<(f64, f64)> {
let mut intersection_t_values = pathseg_self_intersection(segment, accuracy);
intersection_t_values.sort_by(|a, b| (a.0 + a.1).partial_cmp(&(b.0 + b.1)).unwrap());

View File

@@ -224,7 +224,7 @@ pub(crate) struct Point {
/// Useful indexes to speed up various operations on [`Vector`].
///
/// Important: It is the user's responsibility to ensure the indexes remain valid after mutations to the data.
pub struct VectorIndex {
pub(crate) struct VectorIndex {
/// Points and segments form a graph. Store it here in a form amenable to graph algorithms.
///
/// Currently, segment data is not stored as it is not used, but it could easily be added.
@@ -237,7 +237,7 @@ pub struct VectorIndex {
impl VectorIndex {
/// Construct a [`VectorIndex`] by building indexes from the given [`Vector`]. Takes `O(n)` time.
pub fn build_from(data: &Vector) -> Self {
fn build_from(data: &Vector) -> Self {
let point_to_offset = data.point_domain.ids().iter().copied().enumerate().map(|(a, b)| (b, a)).collect::<FxHashMap<_, _>>();
let mut point_to_node = FxHashMap::default();
@@ -270,7 +270,7 @@ impl VectorIndex {
/// # Panics
///
/// Will panic if no segment with the given ID is found.
pub fn segment_chord_length(&self, id: SegmentId) -> f64 {
fn segment_chord_length(&self, id: SegmentId) -> f64 {
let edge_idx = self.segment_to_edge[&id];
let (start, end) = self.point_graph.edge_endpoints(edge_idx).unwrap();
let start_position = self.point_graph.node_weight(start).unwrap().position;
@@ -285,7 +285,7 @@ impl VectorIndex {
/// # Panics
///
/// This function will panic if the ID is not present.
pub fn segment_ends(&self, id: SegmentId) -> [NodeIndex; 2] {
fn segment_ends(&self, id: SegmentId) -> [NodeIndex; 2] {
let (start, end) = self.point_graph.edge_endpoints(self.segment_to_edge[&id]).unwrap();
if start < end { [start, end] } else { [end, start] }
}
@@ -295,7 +295,7 @@ impl VectorIndex {
/// # Panics
///
/// Will panic if `id` isn't in the data.
pub fn point_position(&self, id: PointId, data: &Vector) -> DVec2 {
fn point_position(&self, id: PointId, data: &Vector) -> DVec2 {
let offset = self.point_to_offset[&id];
data.point_domain.positions()[offset]
}

View File

@@ -3,7 +3,7 @@ mod contants;
pub mod intersection;
pub mod merge_by_distance;
pub mod offset_subpath;
pub mod poisson_disk;
mod poisson_disk;
pub mod shapes;
pub mod spline;
pub mod util;

View File

@@ -8,7 +8,7 @@ const DEEPEST_SUBDIVISION_LEVEL_BEFORE_DISCARDING: usize = 8;
/// Based on the paper:
/// "Poisson Disk Point Sets by Hierarchical Dart Throwing"
/// <https://scholarsarchive.byu.edu/facpub/237/>
pub fn poisson_disk_sample(
pub(crate) fn poisson_disk_sample(
offset: DVec2,
width: f64,
height: f64,
@@ -187,23 +187,23 @@ where
/// A square (represented by its top left corner position and width/height of `square_size`) that is currently a candidate for targetting by the dart throwing process.
/// The positive sign bit encodes if the square is contained entirely within the masking shape, or negative if it's outside or intersects the shape path.
pub struct ActiveSquare(DVec2);
struct ActiveSquare(DVec2);
impl ActiveSquare {
pub fn new(top_left_corner: DVec2, fully_in_shape: bool) -> Self {
fn new(top_left_corner: DVec2, fully_in_shape: bool) -> Self {
Self(if fully_in_shape { top_left_corner } else { -top_left_corner })
}
pub fn top_left_corner(&self) -> DVec2 {
fn top_left_corner(&self) -> DVec2 {
self.0.abs()
}
pub fn fully_in_shape(&self) -> bool {
fn fully_in_shape(&self) -> bool {
self.0.x.is_sign_positive()
}
}
pub struct ActiveListLevel {
struct ActiveListLevel {
/// List of all subdivided squares of the same size that are currently candidates for targetting by the dart throwing process
active_squares: Vec<ActiveSquare>,
/// Width and height of the squares in this level of subdivision
@@ -214,7 +214,7 @@ pub struct ActiveListLevel {
impl ActiveListLevel {
#[inline(always)]
pub fn new(square_size: f64) -> Self {
fn new(square_size: f64) -> Self {
Self {
active_squares: Vec::new(),
square_size,
@@ -222,7 +222,7 @@ impl ActiveListLevel {
}
}
pub fn new_filled(
fn new_filled(
square_size: f64,
offset: DVec2,
width: f64,
@@ -295,14 +295,14 @@ impl ActiveListLevel {
#[must_use]
#[inline(always)]
pub fn take_square(&mut self, active_square_index: usize) -> ActiveSquare {
fn take_square(&mut self, active_square_index: usize) -> ActiveSquare {
let targetted_square = self.active_squares.swap_remove(active_square_index);
self.total_area = self.square_size.powi(2) * self.active_squares.len() as f64;
targetted_square
}
#[inline(always)]
pub fn add_squares(&mut self, new_squares: impl Iterator<Item = ActiveSquare>) {
fn add_squares(&mut self, new_squares: impl Iterator<Item = ActiveSquare>) {
for new_square in new_squares {
self.active_squares.push(new_square);
}
@@ -310,28 +310,28 @@ impl ActiveListLevel {
}
#[inline(always)]
pub fn square_size(&self) -> f64 {
fn square_size(&self) -> f64 {
self.square_size
}
#[inline(always)]
pub fn square_area(&self) -> f64 {
fn square_area(&self) -> f64 {
self.square_size.powi(2)
}
#[inline(always)]
pub fn total_area(&self) -> f64 {
fn total_area(&self) -> f64 {
self.total_area
}
#[inline(always)]
pub fn not_empty(&self) -> bool {
fn not_empty(&self) -> bool {
!self.active_squares.is_empty()
}
}
#[derive(Clone, Default)]
pub struct PointsList {
struct PointsList {
// The worst-case number of points in a 3x3 grid is 16 (one at each intersection of the four gridlines per axis)
storage_slots: [DVec2; 16],
length: usize,
@@ -339,19 +339,19 @@ pub struct PointsList {
impl PointsList {
#[inline(always)]
pub fn push(&mut self, point: DVec2) {
fn push(&mut self, point: DVec2) {
self.storage_slots[self.length] = point;
self.length += 1;
}
#[inline(always)]
pub fn list_cell_and_neighbors(&self) -> impl Iterator<Item = DVec2> {
fn list_cell_and_neighbors(&self) -> impl Iterator<Item = DVec2> {
// The negative bit is used to store whether a point belongs to a neighboring cell
self.storage_slots.into_iter().take(self.length).map(|point| (point.x.abs(), point.y.abs()).into())
}
#[inline(always)]
pub fn list_cell(&self) -> impl Iterator<Item = DVec2> {
fn list_cell(&self) -> impl Iterator<Item = DVec2> {
// The negative bit is used to store whether a point belongs to a neighboring cell
self.storage_slots
.into_iter()
@@ -360,7 +360,7 @@ impl PointsList {
}
}
pub struct AccelerationGrid {
struct AccelerationGrid {
size: f64,
dimension_x: usize,
dimension_y: usize,
@@ -369,7 +369,7 @@ pub struct AccelerationGrid {
impl AccelerationGrid {
#[inline(always)]
pub fn new(width: f64, height: f64, size: f64) -> Self {
fn new(width: f64, height: f64, size: f64) -> Self {
let dimension_x = (width / size).ceil() as usize + 1;
let dimension_y = (height / size).ceil() as usize + 1;
@@ -382,7 +382,7 @@ impl AccelerationGrid {
}
#[inline(always)]
pub fn insert(&mut self, point: DVec2) {
fn insert(&mut self, point: DVec2) {
let x = (point.x / self.size).floor() as usize;
let y = (point.y / self.size).floor() as usize;
@@ -408,7 +408,7 @@ impl AccelerationGrid {
}
#[inline(always)]
pub fn nearby_points(&self, point: DVec2) -> impl Iterator<Item = DVec2> {
fn nearby_points(&self, point: DVec2) -> impl Iterator<Item = DVec2> {
let x = (point.x / self.size).floor() as usize;
let y = (point.y / self.size).floor() as usize;
@@ -416,7 +416,7 @@ impl AccelerationGrid {
}
#[inline(always)]
pub fn final_points(&self, offset: DVec2) -> Vec<DVec2> {
fn final_points(&self, offset: DVec2) -> Vec<DVec2> {
self.cells.iter().flat_map(|cell| cell.list_cell()).map(|point| point + offset).collect()
}
}

View File

@@ -16,14 +16,14 @@ pub fn pathseg_tangent(segment: PathSeg, t: f64) -> DVec2 {
/// 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 {
pub(crate) fn compare_points(p1: kurbo::Point, p2: kurbo::Point) -> bool {
let (p1, p2) = (crate::vector::misc::point_to_dvec2(p1), crate::vector::misc::point_to_dvec2(p2));
p1.abs_diff_eq(p2, super::contants::MAX_ABSOLUTE_DIFFERENCE)
}
/// Compare vectors of points by allowing some maximum absolute difference to account for floating point errors
#[cfg(test)]
pub fn compare_vec_of_points(a: Vec<kurbo::Point>, b: Vec<kurbo::Point>, max_absolute_difference: f64) -> bool {
pub(crate) fn compare_vec_of_points(a: Vec<kurbo::Point>, b: Vec<kurbo::Point>, max_absolute_difference: f64) -> bool {
a.len() == b.len()
&& a.into_iter()
.zip(b)
@@ -33,6 +33,6 @@ pub fn compare_vec_of_points(a: Vec<kurbo::Point>, b: Vec<kurbo::Point>, max_abs
/// Compare the two values in a `DVec2` independently with a provided max absolute value difference.
#[cfg(test)]
pub fn dvec2_compare(a: kurbo::Point, b: kurbo::Point, max_abs_diff: f64) -> glam::BVec2 {
pub(crate) fn dvec2_compare(a: kurbo::Point, b: kurbo::Point, max_abs_diff: f64) -> glam::BVec2 {
glam::BVec2::new((a.x - b.x).abs() < max_abs_diff, (a.y - b.y).abs() < max_abs_diff)
}

View File

@@ -120,25 +120,6 @@ impl AsU64 for f64 {
}
}
pub trait AsI64 {
fn as_i64(&self) -> i64;
}
impl AsI64 for u32 {
fn as_i64(&self) -> i64 {
*self as i64
}
}
impl AsI64 for u64 {
fn as_i64(&self) -> i64 {
*self as i64
}
}
impl AsI64 for f64 {
fn as_i64(&self) -> i64 {
*self as i64
}
}
#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -322,7 +303,7 @@ pub fn is_linear(segment: PathSeg) -> bool {
}
/// Get an vec of all the points in a path segment.
pub fn pathseg_points_vec(segment: PathSeg) -> Vec<Point> {
fn pathseg_points_vec(segment: PathSeg) -> Vec<Point> {
match segment {
PathSeg::Line(line) => [line.p0, line.p1].to_vec(),
PathSeg::Quad(quad_bez) => [quad_bez.p0, quad_bez.p1, quad_bez.p2].to_vec(),
@@ -681,25 +662,6 @@ impl ManipulatorGroup {
Self { anchor, in_handle, out_handle, id }
}
/// Construct a new manipulator group from an anchor, in handle, out handle and an id
pub fn new_with_id(anchor: DVec2, in_handle: Option<DVec2>, out_handle: Option<DVec2>, id: PointId) -> Self {
Self { anchor, in_handle, out_handle, id }
}
/// Create a bezier curve that starts at the current manipulator group and finishes in the `end_group` manipulator group.
pub fn to_bezier(&self, end_group: &ManipulatorGroup) -> PathSeg {
let start = self.anchor;
let end = end_group.anchor;
let out_handle = self.out_handle;
let in_handle = end_group.in_handle;
match (out_handle, in_handle) {
(Some(handle1), Some(handle2)) => PathSeg::Cubic(CubicBez::new(dvec2_to_point(start), dvec2_to_point(handle1), dvec2_to_point(handle2), dvec2_to_point(end))),
(Some(handle), None) | (None, Some(handle)) => PathSeg::Quad(QuadBez::new(dvec2_to_point(start), dvec2_to_point(handle), dvec2_to_point(end))),
(None, None) => PathSeg::Line(Line::new(dvec2_to_point(start), dvec2_to_point(end))),
}
}
/// Apply a transformation to all of the [ManipulatorGroup] points
pub fn apply_transform(&mut self, affine_transform: DAffine2) {
self.anchor = affine_transform.transform_point2(self.anchor);

View File

@@ -295,7 +295,7 @@ impl Stroke {
}
/// Get the effective stroke weight.
pub fn effective_width(&self) -> f64 {
pub(crate) fn effective_width(&self) -> f64 {
self.weight
* match self.align {
StrokeAlign::Center => 1.,
@@ -342,14 +342,6 @@ impl Stroke {
self.dash_offset
}
pub fn cap_index(&self) -> u32 {
self.cap as u32
}
pub fn join_index(&self) -> u32 {
self.join as u32
}
pub fn join_miter_limit(&self) -> f32 {
self.join_miter_limit as f32
}
@@ -359,31 +351,6 @@ impl Stroke {
self
}
pub fn with_dash_offset(mut self, dash_offset: f64) -> Self {
self.dash_offset = dash_offset;
self
}
pub fn with_stroke_cap(mut self, stroke_cap: StrokeCap) -> Self {
self.cap = stroke_cap;
self
}
pub fn with_stroke_join(mut self, stroke_join: StrokeJoin) -> Self {
self.join = stroke_join;
self
}
pub fn with_stroke_join_miter_limit(mut self, limit: f64) -> Self {
self.join_miter_limit = limit;
self
}
pub fn with_stroke_align(mut self, stroke_align: StrokeAlign) -> Self {
self.align = stroke_align;
self
}
pub fn has_renderable_stroke(&self) -> bool {
self.weight > 0.
}

View File

@@ -98,7 +98,7 @@ impl PointDomain {
self.position.reserve(additional);
}
pub fn retain(&mut self, segment_domain: &mut SegmentDomain, f: impl Fn(&PointId) -> bool) {
pub(crate) fn retain(&mut self, segment_domain: &mut SegmentDomain, f: impl Fn(&PointId) -> bool) {
let mut keep = self.id.iter().map(&f);
self.position.retain(|_| keep.next().unwrap_or_default());
@@ -171,16 +171,16 @@ impl PointDomain {
self.id.iter().position(|&check_id| check_id == id)
}
pub fn concat(&mut self, other: &Self, transform: DAffine2, id_map: &IdMap) {
pub(crate) fn concat(&mut self, other: &Self, transform: DAffine2, id_map: &IdMap) {
self.id.extend(other.id.iter().map(|id| *id_map.point_map.get(id).unwrap_or(id)));
self.position.extend(other.position.iter().map(|&pos| transform.transform_point2(pos)));
}
pub fn map_ids(&mut self, id_map: &IdMap) {
pub(crate) fn map_ids(&mut self, id_map: &IdMap) {
self.id.iter_mut().for_each(|id| *id = *id_map.point_map.get(id).unwrap_or(id));
}
pub fn transform(&mut self, transform: DAffine2) {
pub(crate) fn transform(&mut self, transform: DAffine2) {
for pos in &mut self.position {
*pos = transform.transform_point2(*pos);
}
@@ -232,7 +232,7 @@ impl SegmentDomain {
self.stroke.reserve(additional);
}
pub fn retain(&mut self, f: impl Fn(&SegmentId) -> bool, points_length: usize) {
pub(crate) fn retain(&mut self, f: impl Fn(&SegmentId) -> bool, points_length: usize) {
let additional_delete_ids = self
.id
.iter()
@@ -348,7 +348,7 @@ impl SegmentDomain {
nested.map(|((a, b), c)| (a, b, c))
}
pub fn stroke_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut StrokeId)> {
pub(crate) fn stroke_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut StrokeId)> {
self.id.iter().copied().zip(self.stroke.iter_mut())
}
@@ -387,7 +387,7 @@ impl SegmentDomain {
self.id.iter().position(|&check_id| check_id == id)
}
pub fn concat(&mut self, other: &Self, transform: DAffine2, id_map: &IdMap) {
pub(crate) 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));
self.end_point.extend(other.end_point.iter().map(|&index| id_map.point_offset + index));
@@ -395,11 +395,11 @@ impl SegmentDomain {
self.stroke.extend(&other.stroke);
}
pub fn map_ids(&mut self, id_map: &IdMap) {
pub(crate) fn map_ids(&mut self, id_map: &IdMap) {
self.id.iter_mut().for_each(|id| *id = *id_map.segment_map.get(id).unwrap_or(id));
}
pub fn transform(&mut self, transform: DAffine2) {
pub(crate) fn transform(&mut self, transform: DAffine2) {
for handles in &mut self.handles {
*handles = handles.apply_transformation(|p| transform.transform_point2(p));
}
@@ -592,7 +592,7 @@ impl RegionDomain {
self.fill.reserve(additional);
}
pub fn retain(&mut self, f: impl Fn(&RegionId) -> bool) {
pub(crate) fn retain(&mut self, f: impl Fn(&RegionId) -> bool) {
let mut keep = self.id.iter().map(&f);
self.segment_range.retain(|_| keep.next().unwrap_or_default());
let mut keep = self.id.iter().map(&f);
@@ -603,7 +603,7 @@ impl RegionDomain {
/// Like [`Self::retain`] but also gives the function access to the segment range.
///
/// Note that this function requires an allocation that `retain` avoids.
pub fn retain_with_region(&mut self, f: impl Fn(&RegionId, &std::ops::RangeInclusive<SegmentId>) -> bool) {
pub(crate) fn retain_with_region(&mut self, f: impl Fn(&RegionId, &std::ops::RangeInclusive<SegmentId>) -> bool) {
let keep = self.id.iter().zip(self.segment_range.iter()).map(|(id, range)| f(id, range)).collect::<Vec<_>>();
let mut iter = keep.iter().copied();
self.segment_range.retain(|_| iter.next().unwrap());
@@ -638,11 +638,11 @@ impl RegionDomain {
self.id.iter().copied().max_by(|a, b| a.0.cmp(&b.0)).map(|mut id| id.next_id()).unwrap_or(RegionId::ZERO)
}
pub fn segment_range_mut(&mut self) -> impl Iterator<Item = (RegionId, &mut std::ops::RangeInclusive<SegmentId>)> {
pub(crate) fn segment_range_mut(&mut self) -> impl Iterator<Item = (RegionId, &mut std::ops::RangeInclusive<SegmentId>)> {
self.id.iter().copied().zip(self.segment_range.iter_mut())
}
pub fn fill_mut(&mut self) -> impl Iterator<Item = (RegionId, &mut FillId)> {
pub(crate) fn fill_mut(&mut self) -> impl Iterator<Item = (RegionId, &mut FillId)> {
self.id.iter().copied().zip(self.fill.iter_mut())
}
@@ -650,7 +650,7 @@ impl RegionDomain {
&self.id
}
pub fn segment_range(&self) -> &[std::ops::RangeInclusive<SegmentId>] {
pub(crate) fn segment_range(&self) -> &[std::ops::RangeInclusive<SegmentId>] {
&self.segment_range
}
@@ -658,7 +658,7 @@ impl RegionDomain {
&self.fill
}
pub fn concat(&mut self, other: &Self, _transform: DAffine2, id_map: &IdMap) {
pub(crate) fn concat(&mut self, other: &Self, _transform: DAffine2, id_map: &IdMap) {
self.id.extend(other.id.iter().map(|id| *id_map.region_map.get(id).unwrap_or(id)));
self.segment_range.extend(
other
@@ -669,7 +669,7 @@ impl RegionDomain {
self.fill.extend(&other.fill);
}
pub fn map_ids(&mut self, id_map: &IdMap) {
pub(crate) fn map_ids(&mut self, id_map: &IdMap) {
self.id.iter_mut().for_each(|id| *id = *id_map.region_map.get(id).unwrap_or(id));
self.segment_range
.iter_mut()
@@ -696,11 +696,11 @@ pub struct HalfEdge {
}
impl HalfEdge {
pub fn new(id: SegmentId, start: usize, end: usize, reverse: bool) -> Self {
fn new(id: SegmentId, start: usize, end: usize, reverse: bool) -> Self {
Self { id, start, end, reverse }
}
pub fn reversed(&self) -> Self {
fn reversed(&self) -> Self {
Self {
id: self.id,
start: self.start,
@@ -709,7 +709,7 @@ impl HalfEdge {
}
}
pub fn normalize_direction(&self) -> Self {
fn normalize_direction(&self) -> Self {
if self.reverse {
Self {
id: self.id,
@@ -729,14 +729,14 @@ pub struct FoundSubpath {
}
impl FoundSubpath {
pub fn endpoints(&self) -> Option<(&HalfEdge, &HalfEdge)> {
fn endpoints(&self) -> Option<(&HalfEdge, &HalfEdge)> {
match (self.edges.first(), self.edges.last()) {
(Some(first), Some(last)) => Some((first, last)),
_ => None,
}
}
pub fn push(&mut self, segment: HalfEdge) {
fn push(&mut self, segment: HalfEdge) {
self.edges.push(segment);
}
@@ -747,7 +747,7 @@ impl FoundSubpath {
}
}
pub fn from_segment(segment: HalfEdge) -> Self {
fn from_segment(segment: HalfEdge) -> Self {
Self { edges: vec![segment] }
}
@@ -1108,7 +1108,7 @@ impl Vector {
false
}
pub fn has_regions(&self) -> bool {
fn has_regions(&self) -> bool {
!self.region_domain.id.is_empty()
}
@@ -1321,7 +1321,7 @@ impl Iterator for StrokePathIter<'_> {
}
/// Represents the conversion of IDs used when concatenating vector paths with conflicting IDs.
pub struct IdMap {
pub(crate) struct IdMap {
pub point_offset: usize,
pub point_map: HashMap<PointId, PointId>,
pub segment_map: HashMap<SegmentId, SegmentId>,

View File

@@ -16,7 +16,7 @@ use std::hash::Hash;
/// Represents a procedural change to the [`PointDomain`] in [`Vector`].
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PointModification {
pub(crate) struct PointModification {
add: Vec<PointId>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_hashset"))]
remove: HashSet<PointId>,
@@ -83,7 +83,7 @@ impl PointModification {
/// Represents a procedural change to the [`SegmentDomain`] in [`Vector`].
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SegmentModification {
pub(crate) struct SegmentModification {
add: Vec<SegmentId>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_hashset"))]
remove: HashSet<SegmentId>,
@@ -261,7 +261,7 @@ impl SegmentModification {
/// Represents a procedural change to the [`RegionDomain`] in [`Vector`].
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RegionModification {
pub(crate) struct RegionModification {
add: Vec<RegionId>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_hashset"))]
remove: HashSet<RegionId>,
@@ -579,7 +579,7 @@ where
}
/// Serializes as sorted `[value, ...]` (JSON array)
pub fn serialize_hashset<T, S, H>(set: &HashSet<T, H>, serializer: S) -> Result<S::Ok, S::Error>
pub(crate) fn serialize_hashset<T, S, H>(set: &HashSet<T, H>, serializer: S) -> Result<S::Ok, S::Error>
where
T: Serialize + Eq + Hash + Ord,
S: Serializer,
@@ -639,7 +639,7 @@ where
/// Matches Kurbo's default path accuracy, so points within an offset operation's own precision are not split into separate anchors.
const CLOSE_POINT_TOLERANCE: f64 = 1e-6;
pub struct AppendBezpath<'a> {
pub(crate) struct AppendBezpath<'a> {
first_point: Option<Point>,
last_point: Option<Point>,
first_point_index: Option<usize>,

View File

@@ -1,6 +1,6 @@
use super::misc::dvec2_to_point;
use super::style::{Stroke, StrokeAlign, StrokeCap, StrokeJoin};
pub use super::vector_attributes::*;
use super::vector_attributes::*;
use crate::vector::misc::{BezierHandles, ManipulatorGroup};
use crate::vector::misc::{HandleId, ManipulatorPointId};
use crate::vector::vector_modification::VectorExt;
@@ -174,7 +174,7 @@ impl Vector {
}
/// Compute the bounding boxes of the bezpaths with the specified transform
pub fn bounding_box_with_transform_rect(&self, transform: DAffine2) -> Option<Rect> {
fn bounding_box_with_transform_rect(&self, transform: DAffine2) -> Option<Rect> {
let combine = |r1: Rect, r2: Rect| r1.union(r2);
self.stroke_bezpath_iter()
.map(|mut bezpath| {