index/utils/
point2d.rs

1use std::{ops::{Add, AddAssign, Index, Mul, MulAssign, Range, RangeFrom, RangeFull, RangeTo, Sub, SubAssign}, rc::Rc};
2
3use usvg::tiny_skia_path;
4use wasm_bindgen::prelude::*;
5
6use crate::utils::{console::log, interpolation::lerp, linear_algebra::matrix_product_path};
7
8use super::{bezier::{AnchorsAndHandles, CubicBezierTuple}, linear_algebra::TransformationMatrix};
9
10/// A 2D point.
11#[wasm_bindgen]
12#[derive(Clone, Copy, Debug, PartialEq)]
13pub struct Point2D {
14    /// The x-coordinate of the point.
15    pub x: f32,
16    /// The y-coordinate of the point.
17    pub y: f32,
18}
19
20impl Default for Point2D {
21    fn default() -> Point2D {
22        Point2D { x: 0.0, y: 0.0 }
23    }
24}
25
26#[wasm_bindgen]
27impl Point2D {
28    /// Creates a new Point2D with the given coordinates.
29    #[wasm_bindgen(constructor, return_description = "A 2D point.")]
30    pub fn new(
31        #[wasm_bindgen(param_description = "The x-coordinate of the point.")]
32        x: f32,
33        #[wasm_bindgen(param_description = "The y-coordinate of the point.")]
34        y: f32
35    ) -> Point2D {
36        Point2D { x, y }
37    }
38
39    /// Linearly interpolates between two Point2D objects.
40    #[wasm_bindgen(return_description = "The interpolated point.")]
41    pub fn lerp(
42        #[wasm_bindgen(param_description = "The start point.")]
43        point1: &Point2D,
44        #[wasm_bindgen(param_description = "The end point.")]
45        point2: &Point2D,
46        #[wasm_bindgen(param_description = "The progress value.")]
47        t: f32
48    ) -> Point2D {
49        Point2D {
50            x: lerp(point1.x, point2.x, t),
51            y: lerp(point1.y, point2.y, t),
52        }
53    }
54    /// Gets a cartesian point from polar coordinates.
55    #[wasm_bindgen(return_description = "A 2D point.")]
56    pub fn from_polar(
57        #[wasm_bindgen(param_description = "The radius of the point.")]
58        radius: f32,
59        #[wasm_bindgen(param_description = "The angle of the point.")]
60        angle: f32
61    ) -> Point2D {
62        Point2D {
63            x: radius * angle.cos(),
64            y: radius * angle.sin(),
65        }
66    }
67    /// Returns the distance between two Point2Ds.
68    #[wasm_bindgen(return_description = "The distance between the two points.")]
69    pub fn distance_squared(
70        &self,
71        #[wasm_bindgen(param_description = "The other point.")]
72        other: &Point2D
73    ) -> f32 {
74        let dx = self.x - other.x;
75        let dy = self.y - other.y;
76        dx * dx + dy * dy
77    }
78    /// Checks if two Point2D objects are equal within a given tolerance.
79    #[wasm_bindgen(return_description = "A boolean indicating whether the two points are equal.")]
80    pub fn equals(
81        &self,
82        #[wasm_bindgen(param_description = "The other point.")]
83        other: &Point2D,
84        #[wasm_bindgen(param_description = "The tolerance.")]
85        tolerance: Option<f32>
86    ) -> bool {
87        let tolerance = tolerance.unwrap_or(0.01);
88        self.distance_squared(other) < tolerance * tolerance
89    }
90    /// Returns the Point2D rotated around a given center point by a given angle.
91    #[wasm_bindgen(return_description = "The rotated point.")]
92    pub fn rotate_around(
93        &self,
94        #[wasm_bindgen(param_description = "The center point to rotate around.")]
95        center: Point2D,
96        #[wasm_bindgen(param_description = "The angle to rotate by.")]
97        angle: f32
98    ) -> Point2D {
99        let (sin, cos) = angle.sin_cos();
100        let x = cos * (self.x - center.x) - sin * (self.y - center.y) + center.x;
101        let y = sin * (self.x - center.x) + cos * (self.y - center.y) + center.y;
102        Point2D { x, y }
103    }
104    /// Returns the distance between two Point2Ds.
105    #[wasm_bindgen(return_description = "The distance between the two points.")]
106    pub fn distance(
107        &self,
108        #[wasm_bindgen(param_description = "The other point.")]
109        other: &Point2D
110    ) -> f32 {
111        self.distance_squared(other).sqrt()
112    }
113    /// Clones the Point2D.
114    #[wasm_bindgen(js_name = clone)]
115    pub fn copy(&self) -> Point2D {
116        *self
117    }
118    /// Returns the squared magnitude of the Point2D.
119    #[wasm_bindgen(getter, return_description = "The squared magnitude of the point.")]
120    pub fn magnitude_squared(&self) -> f32 {
121        self.x * self.x + self.y * self.y
122    }
123    /// Returns the magnitude of the Point2D.
124    #[wasm_bindgen(getter, return_description = "The magnitude of the point.")]
125    pub fn magnitude(&self) -> f32 {
126        self.magnitude_squared().sqrt()
127    }
128    /// Returns the normalized Point2D.
129    #[wasm_bindgen(getter, return_description = "The normalized point.")]
130    pub fn normalized(&self) -> Point2D {
131        let magnitude = self.magnitude();
132        Point2D {
133            x: self.x / magnitude,
134            y: self.y / magnitude,
135        }
136    }
137    /// Returns the dot product of Point2D objects.
138    #[wasm_bindgen(return_description = "The dot product of the two points.")]
139    pub fn dot(
140        &self,
141        #[wasm_bindgen(param_description = "The other point.")]
142        other: &Point2D
143    ) -> f32 {
144        self.x * other.x + self.y * other.y
145    }
146    /// Returns the angle between two Point2Ds.
147    #[wasm_bindgen(return_description = "The angle between the two points.")]
148    pub fn angle(
149        &self,
150        #[wasm_bindgen(param_description = "The other point.")]
151        other: &Point2D
152    ) -> f32 {
153        let dot = self.dot(other);
154        let magnitude_product = self.magnitude() * other.magnitude();
155        (dot / magnitude_product).acos()
156    }
157    /// Gets the direction angle with respect to (0, 0) of the Point2D.
158    #[wasm_bindgen(getter, return_description = "The direction angle of the point.")]
159    pub fn direction(&self) -> f32 {
160        self.y.atan2(self.x)
161    }
162    /// Projects a point onto a line given the start and end points of the line.
163    #[wasm_bindgen(return_description = "The proportion of the line.")]
164    pub fn project_onto_line(
165        &self,
166        #[wasm_bindgen(param_description = "The start point of the line.")]
167        start: &Point2D,
168        #[wasm_bindgen(param_description = "The end point of the line.")]
169        end: &Point2D
170    ) -> f32 {
171        let line = *end - *start;
172        let line_squared = line.magnitude_squared();
173        if line_squared == 0.0 {
174            return 0.0;
175        }
176        let projection = *self - *start;
177        (line.dot(&projection) / line_squared).max(0.0).min(1.0)
178    }
179    /// Checks if a point is finite
180    #[wasm_bindgen(return_description = "A boolean indicating whether the point is finite.")]
181    pub fn is_finite(&self) -> bool {
182        self.x.is_finite() && self.y.is_finite()
183    }
184}
185
186impl Add<Point2D> for Point2D {
187    type Output = Point2D;
188
189    fn add(self, other: Point2D) -> Point2D {
190        Point2D {
191            x: self.x + other.x,
192            y: self.y + other.y,
193        }
194    }
195}
196
197impl Add<Point2D> for f32 {
198    type Output = Point2D;
199
200    fn add(self, point: Point2D) -> Point2D {
201        Point2D {
202            x: self + point.x,
203            y: self + point.y,
204        }
205    }
206}
207
208impl Add<f32> for Point2D {
209    type Output = Point2D;
210
211    fn add(self, scalar: f32) -> Point2D {
212        Point2D {
213            x: self.x + scalar,
214            y: self.y + scalar,
215        }
216    }
217}
218
219impl AddAssign<Point2D> for Point2D {
220    fn add_assign(&mut self, other: Point2D) {
221        self.x += other.x;
222        self.y += other.y;
223    }
224}
225
226impl AddAssign<f32> for Point2D {
227    fn add_assign(&mut self, scalar: f32) {
228        self.x += scalar;
229        self.y += scalar;
230    }
231}
232
233impl Sub<Point2D> for Point2D {
234    type Output = Point2D;
235
236    fn sub(self, other: Point2D) -> Point2D {
237        Point2D {
238            x: self.x - other.x,
239            y: self.y - other.y,
240        }
241    }
242}
243
244impl Sub<Point2D> for f32 {
245    type Output = Point2D;
246
247    fn sub(self, point: Point2D) -> Point2D {
248        Point2D {
249            x: self - point.x,
250            y: self - point.y,
251        }
252    }
253}
254
255impl Sub<f32> for Point2D {
256    type Output = Point2D;
257
258    fn sub(self, scalar: f32) -> Point2D {
259        Point2D {
260            x: self.x - scalar,
261            y: self.y - scalar,
262        }
263    }
264}
265
266impl SubAssign<Point2D> for Point2D {
267    fn sub_assign(&mut self, other: Point2D) {
268        self.x -= other.x;
269        self.y -= other.y;
270    }
271}
272
273impl SubAssign<f32> for Point2D {
274    fn sub_assign(&mut self, scalar: f32) {
275        self.x -= scalar;
276        self.y -= scalar;
277    }
278}
279
280impl Mul<f32> for Point2D {
281    type Output = Point2D;
282
283    fn mul(self, scalar: f32) -> Point2D {
284        Point2D {
285            x: self.x * scalar,
286            y: self.y * scalar,
287        }
288    }
289}
290
291impl Mul<Point2D> for f32 {
292    type Output = Point2D;
293
294    fn mul(self, point: Point2D) -> Point2D {
295        Point2D {
296            x: self * point.x,
297            y: self * point.y,
298        }
299    }
300}
301
302impl Mul<Point2D> for Point2D {
303    type Output = Point2D;
304
305    fn mul(self, other: Point2D) -> Point2D {
306        Point2D {
307            x: self.x * other.x,
308            y: self.y * other.y,
309        }
310    }
311}
312
313impl MulAssign<Point2D> for Point2D {
314    fn mul_assign(&mut self, other: Point2D) {
315        self.x *= other.x;
316        self.y *= other.y;
317    }
318}
319
320impl MulAssign<f32> for Point2D {
321    fn mul_assign(&mut self, scalar: f32) {
322        self.x *= scalar;
323        self.y *= scalar;
324    }
325}
326
327/// A 2D path.
328#[wasm_bindgen]
329#[derive(Clone, Debug, PartialEq)]
330pub struct Path2D {
331    /// The points of the path.
332    points: Rc<Vec<Point2D>>
333}
334
335#[wasm_bindgen]
336impl Path2D {
337    /// Creates a new Path2D with the given points.
338    #[wasm_bindgen(constructor, return_description = "A 2D path.")]
339    pub fn new(
340        #[wasm_bindgen(param_description = "The points of the path.")]
341        points: Vec<Point2D>
342    ) -> Path2D {
343        Path2D { points: Rc::new(points) }
344    }
345
346    /// Creates a new Path2D given a AnchorsAndHandles object.
347    #[wasm_bindgen(return_description = "A 2D path.")]
348    pub fn from_anchors_and_handles(
349        #[wasm_bindgen(param_description = "The AnchorsAndHandles object.")]
350        anchors_and_handles: &AnchorsAndHandles
351    ) -> Path2D {
352        let mut points = Vec::new();
353        for i in 0..anchors_and_handles.len() {
354            points.push(anchors_and_handles.start_anchors()[i]);
355            points.push(anchors_and_handles.first_controls()[i]);
356            points.push(anchors_and_handles.second_controls()[i]);
357            points.push(anchors_and_handles.end_anchors()[i]);
358        }
359        Path2D { points: Rc::new(points) }
360    }
361
362    /// Clones the Path2D.
363    #[wasm_bindgen(js_name = clone)]
364    pub fn copy(&self) -> Path2D {
365        self.clone()
366    }
367
368    /// Repeats the Path2D a given number of times.
369    #[wasm_bindgen(return_description = "The repeated path.")]
370    pub fn repeat(
371        &self,
372        #[wasm_bindgen(param_description = "The number of times to repeat the path.")]
373        count: usize
374    ) -> Path2D {
375        let mut points = Vec::with_capacity(self.points.len() * count);
376        for _ in 0..count {
377            points.extend(Rc::clone(&self.points).iter());
378        }
379        Path2D { points: Rc::new(points) }
380    }
381
382    /// Returns the Point2Ds of the Path2D.
383    #[wasm_bindgen(getter, return_description = "The points of the path.")]
384    pub fn points(&self) -> Vec<Point2D> {
385        Rc::clone(&self.points).to_vec()
386    }
387
388    /// Sets the Point2Ds of the Path2D.
389    #[wasm_bindgen(setter)]
390    pub fn set_points(
391        &mut self,
392        #[wasm_bindgen(param_description = "The points of the path.")]
393        points: Vec<Point2D>
394    ) {
395        self.points = Rc::new(points);
396    }
397
398    /// Returns whether the Path2D is empty.
399    #[wasm_bindgen(getter, return_description = "A boolean indicating whether the path is empty.")]
400    pub fn is_empty(&self) -> bool {
401        self.points.is_empty()
402    }
403
404    /// Returns the closest Point2D in the Path2D to a given Point2D.
405    #[wasm_bindgen(return_description = "The closest point in the path.")]
406    pub fn closest_point(
407        &self,
408        #[wasm_bindgen(param_description = "The point to find the closest point to.")]
409        point: &Point2D
410    ) -> Point2D {
411        let mut closest_point = self.points[0];
412        let mut closest_distance = point.distance(&closest_point);
413        for i in 1..self.points.len() {
414            let distance = point.distance(&self.points[i]);
415            if distance < closest_distance {
416                closest_point = self.points[i];
417                closest_distance = distance;
418            }
419        }
420        closest_point
421    }
422
423    /// Returns the length of the Path2D.
424    #[wasm_bindgen(getter, return_description = "The length of the path.")]
425    pub fn len(&self) -> usize {
426        self.points.len()
427    }
428
429    /// Returns the Point2D at a given index.
430    #[wasm_bindgen(return_description = "The first point of the path.")]
431    pub fn get(
432        &self,
433        #[wasm_bindgen(param_description = "The index of the point.")]
434        index: usize
435    ) -> Point2D {
436        self.points[index]
437    }
438
439    /// Sets the Point2D at the given index.
440    pub fn set(
441        &mut self,
442        #[wasm_bindgen(param_description = "The index of the point.")]
443        index: usize,
444        #[wasm_bindgen(param_description = "The new point.")]
445        point: Point2D
446    ) {
447        Rc::make_mut(&mut self.points)[index] = point;
448    }
449
450    /// Appends a Point2D to the Path2D.
451    pub fn push(
452        &mut self,
453        #[wasm_bindgen(param_description = "The point to append.")]
454        point: Point2D
455    ) {
456        Rc::make_mut(&mut self.points).push(point);
457    }
458
459    /// Removes the last Point2D from the Path2D.
460    #[wasm_bindgen(return_description = "The last point of the path.")]
461    pub fn pop(&mut self) -> Option<Point2D> {
462        Rc::make_mut(&mut self.points).pop()
463    }
464
465    /// Inserts a Point2D at a given index.
466    pub fn insert(
467        &mut self,
468        #[wasm_bindgen(param_description = "The index to insert the point at.")]
469        index: usize,
470        #[wasm_bindgen(param_description = "The point to insert.")]
471        point: Point2D
472    ) {
473        Rc::make_mut(&mut self.points).insert(index, point);
474    }
475
476    /// Removes a Point2D at a given index.
477    #[wasm_bindgen(return_description = "The removed point.")]
478    pub fn remove(
479        &mut self,
480        #[wasm_bindgen(param_description = "The index of the point to remove.")]
481        index: usize
482    ) -> Point2D {
483        Rc::make_mut(&mut self.points).remove(index)
484    }
485
486    /// Removes all Point2Ds from the Path2D.
487    pub fn clear(&mut self) {
488        Rc::make_mut(&mut self.points).clear();
489    }
490
491    /// Returns a new Path2D representing a bezier curve portion of the Path2D.
492    #[wasm_bindgen(return_description = "A Path2D object representing the portion of the input path.")]
493    pub fn partial_bezier_path(
494        &self,
495        #[wasm_bindgen(param_description = "The start proportion of the input path. A number between 0 and 1.")]
496        a: f32,
497        #[wasm_bindgen(param_description = "The end proportion of the input path. A number between 0 and 1.")]
498        b: f32,
499    ) -> Path2D {
500        if a == 1.0 {
501            return Path2D::fill(self.get(self.len() - 1), self.len());
502        }
503        if b == 0.0 {
504            return Path2D::fill(self.get(0), self.len());
505        }
506        let degree = self.len() - 1;
507        if degree == 3 {
508            let (ma, mb) = (
509                1.0 - a,
510                1.0 - b,
511            );
512            let (a2, b2, ma2, mb2) = (
513                a * a,
514                b * b,
515                ma * ma,
516                mb * mb,
517            );
518            let (a3, b3, ma3, mb3) = (
519                a2 * a,
520                b2 * b,
521                ma2 * ma,
522                mb2 * mb,
523            );
524            let portion_matrix = vec![
525                ma3, 3.0 * ma2 * a, 3.0 * ma * a2, a3,
526                ma2 * mb, 2.0 * ma * a * mb + ma2 * b, a2 * mb + 2.0 * ma * a * b, a2 * b,
527                ma * mb2, a * mb2 + 2.0 * ma * mb * b, 2.0 * a * mb * b + ma * b2, a * b2,
528                mb3, 3.0 * mb2 * b, 3.0 * mb * b2, b3,
529            ];
530            let a_rows = 4;
531            let a_columns = 4;
532            let b_columns = 2;
533            let result = matrix_product_path(portion_matrix, &self, a_rows, a_columns, b_columns);
534            return result;
535        }
536        if degree == 2 {
537            let (ma, mb) = (
538                1.0 - a,
539                1.0 - b,
540            );
541            let (a2, b2, ma2, mb2) = (
542                a * a,
543                b * b,
544                ma * ma,
545                mb * mb,
546            );
547            let portion_matrix = vec![
548                ma2, 2.0 * ma * a, a2,
549                ma * mb, ma * b + a * mb, a * b,
550                mb2, 2.0 * mb * b, b2,
551            ];
552            let a_rows = 3;
553            let a_columns = 3;
554            let b_columns = 2;
555            let result = matrix_product_path(portion_matrix, &self, a_rows, a_columns, b_columns);
556            return result;
557        }
558        if degree == 1 {
559            let direction = self[1] - self[0];
560            return Path2D::new(vec![
561                self[0] + direction * a,
562                self[0] + direction * b,
563            ]);
564        }
565        if degree == 0 {
566            return Path2D::fill(self[0], 1);
567        }
568        let n = self.len();
569        let mut points = Rc::clone(&self.points);
570        if a != 0.0 {
571            for i in 1..n {
572                let new_points = points[1..n - i + 1].iter().zip(points[0..n - i].iter()).map(|(a, b)| *a + *a * (*a - *b)).collect::<Vec<Point2D>>();
573                Rc::make_mut(&mut points).splice(0..n - i, new_points);
574            }
575        }
576        if b != 1.0 {
577            let mu = (1.0 - b) / (1.0 - a);
578            for i in 1..n {
579                let new_points = points[i - 1..n - 1].iter().zip(points[i..n].iter()).map(|(a, b)| *a + mu * (*a - *b)).collect::<Vec<Point2D>>();
580                Rc::make_mut(&mut points).splice(i..n, new_points);
581            }
582        }
583        Path2D { points }
584    }
585
586    /// Creates a new Path2D by filling the Path2D with a given Point2D a given number of times.
587    #[wasm_bindgen(return_description = "A path that is a portion of the input path.")]
588    pub fn fill(
589        #[wasm_bindgen(param_description = "The point to fill the path with.")]
590        point: Point2D,
591        #[wasm_bindgen(param_description = "The number of times to fill the path with the point.")]
592        count: usize
593    ) -> Path2D {
594        Path2D {
595            points: Rc::new(vec![point; count])
596        }
597    }
598
599    /// Reverse the Path2D.
600    pub fn reverse(&mut self) {
601        Rc::make_mut(&mut self.points).reverse();
602    }
603
604    /// Sets a slice of the Path2D.
605    pub fn set_slice(
606        &mut self,
607        #[wasm_bindgen(param_description = "The start index of the slice.")]
608        start: usize,
609        #[wasm_bindgen(param_description = "The end index of the slice.")]
610        end: usize,
611        #[wasm_bindgen(param_description = "The new path.")]
612        path: Path2D
613    ) {
614        let points = Rc::clone(&path.points);
615        Rc::make_mut(&mut self.points).splice(start..end, points.to_vec());
616    }
617
618    /// Returns a slice of the Path2D
619    #[wasm_bindgen(return_description = "A slice of the path.")]
620    pub fn slice(
621        &self,
622        #[wasm_bindgen(param_description = "The start index of the slice.")]
623        start: usize,
624        #[wasm_bindgen(param_description = "The end index of the slice.")]
625        end: usize
626    ) -> Path2D {
627        Path2D {
628            points: Rc::new(self.points[start..end].to_vec())
629        }
630    }
631
632    /// Returns a CubicBezierTuple at a given index.
633    #[wasm_bindgen(getter, return_description = "The cubic bezier tuples of the path.")]
634    pub fn cubic_bezier_tuples(&self) -> Vec<CubicBezierTuple> {
635        let remainder = self.points.len() % 4;
636        let points = self.slice(0, self.points.len() - remainder);
637        points.points.chunks(4).map(|chunk| {
638            CubicBezierTuple::new(chunk[0], chunk[1], chunk[2], chunk[3])
639        }).collect()
640    }
641
642    /// Appends a CubicBezierTuple to the Path2D.
643    pub fn push_bezier(
644        &mut self,
645        #[wasm_bindgen(param_description = "The cubic bezier tuple to add.")]
646        cubic_bezier: CubicBezierTuple
647    ) {
648        if self.points.len() % 4 != 0 && !cubic_bezier.start_anchor().equals(&self.points[self.points.len() - 1], None) {
649            log("The path length must be a multiple of 4.");
650            return;
651        }
652        if self.points.len() % 4 == 0 {
653            Rc::make_mut(&mut self.points).push(cubic_bezier.start_anchor());
654        }
655        Rc::make_mut(&mut self.points).push(cubic_bezier.first_control());
656        Rc::make_mut(&mut self.points).push(cubic_bezier.second_control());
657        Rc::make_mut(&mut self.points).push(cubic_bezier.end_anchor());
658    }
659
660    /// Returns an approximation of the length of the path, based on sampling points along each cubic bezier curve in the path and an optional extra length to add to each approximation.
661    #[wasm_bindgen(return_description = "An approximation of the length of the path.")]
662    pub fn length(
663        &self,
664        #[wasm_bindgen(param_description = "The number of samples to take along each cubic bezier curve.")]
665        samples_per_cubic: Option<usize>,
666        #[wasm_bindgen(param_description = "An optional extra length to add to each cubic bezier length approximation.")]
667        extra_length_per_cubic: Option<f32>
668    ) -> f32 {
669        self.cubic_bezier_tuples().iter().map(|tuple| tuple.length(samples_per_cubic, extra_length_per_cubic)).sum()
670    }
671
672    /// Gets the first point of the Path2D.
673    #[wasm_bindgen(getter, return_description = "The first point of the path.")]
674    pub fn first(&self) -> Point2D {
675        self.points[0]
676    }
677
678    /// Gets the last point of the Path2D.
679    #[wasm_bindgen(getter, return_description = "The last point of the path.")]
680    pub fn last(&self) -> Option<Point2D> {
681        self.points.last().copied()
682    }
683}
684
685impl Path2D {
686    /// Creates a Path2D from SVG path data.
687    pub fn from_svg_path_data(
688        data: &tiny_skia_path::Path,
689    ) -> Path2D {
690        let mut path = Path2D::default();
691        let mut move_point = Point2D::default();
692        let mut current_point = Point2D::default();
693        for segment in data.segments() {
694            match segment {
695                tiny_skia_path::PathSegment::MoveTo(point) => {
696                    current_point = Point2D::new(point.x, point.y);
697                    move_point = current_point;
698                }
699                tiny_skia_path::PathSegment::LineTo(point) => {
700                    path.push_bezier(CubicBezierTuple::from_line(current_point, Point2D::new(point.x, point.y)));
701                    current_point = Point2D::new(point.x, point.y);
702                }
703                tiny_skia_path::PathSegment::QuadTo(control, point) => {
704                    path.push_bezier(CubicBezierTuple::from_quadratic(current_point, Point2D::new(control.x, control.y), Point2D::new(point.x, point.y)));
705                    current_point = Point2D::new(point.x, point.y);
706                }
707                tiny_skia_path::PathSegment::CubicTo(control1, control2, point) => {
708                    path.push_bezier(CubicBezierTuple::new(current_point, Point2D::new(control1.x, control1.y), Point2D::new(control2.x, control2.y), Point2D::new(point.x, point.y)));
709                    current_point = Point2D::new(point.x, point.y);
710                }
711                tiny_skia_path::PathSegment::Close => {
712                    path.push_bezier(CubicBezierTuple::from_line(current_point, move_point));
713                    current_point = move_point;
714                }
715            }
716        }
717        path
718    }
719    /// Transforms the path by a given CSS transformation matrix.
720    pub fn transform(
721        &self,
722        matrix: &TransformationMatrix
723    ) -> Path2D {
724        Path2D {
725            points: Rc::new(self.points.iter().map(|point| *matrix * *point).collect())
726        }
727    }
728}
729
730impl Add<Point2D> for Path2D {
731    type Output = Path2D;
732
733    fn add(self, point: Point2D) -> Path2D {
734        Path2D {
735            points: Rc::new(self.points.to_vec().into_iter().map(|p| p + point).collect())
736        }
737    }
738}
739
740impl Add<Path2D> for Point2D {
741    type Output = Path2D;
742
743    fn add(self, path: Path2D) -> Path2D {
744        Path2D {
745            points: Rc::new(path.points.to_vec().into_iter().map(|p| self + p).collect())
746        }
747    }
748}
749
750impl AddAssign<Point2D> for Path2D {
751    fn add_assign(&mut self, point: Point2D) {
752        Rc::make_mut(&mut self.points).iter_mut().for_each(|p| *p += point);
753    }
754}
755
756impl Add<Path2D> for Path2D {
757    type Output = Path2D;
758
759    fn add(self, other: Path2D) -> Path2D {
760        Path2D {
761            points: Rc::new(self.points.iter().zip(other.points.iter()).map(|(a, b)| *a + *b).collect())
762        }
763    }
764}
765
766impl AddAssign<Path2D> for Path2D {
767    fn add_assign(&mut self, other: Path2D) {
768        Rc::make_mut(&mut self.points).iter_mut().zip(other.points.iter()).for_each(|(a, b)| *a += *b);
769    }
770}
771
772impl Sub<Point2D> for Path2D {
773    type Output = Path2D;
774
775    fn sub(self, point: Point2D) -> Path2D {
776        Path2D {
777            points: Rc::new(self.points.to_vec().into_iter().map(|p| p - point).collect())
778        }
779    }
780}
781
782impl Sub<Path2D> for Point2D {
783    type Output = Path2D;
784
785    fn sub(self, path: Path2D) -> Path2D {
786        Path2D {
787            points: Rc::new(path.points.to_vec().into_iter().map(|p| self - p).collect())
788        }
789    }
790}
791
792impl SubAssign<Point2D> for Path2D {
793    fn sub_assign(&mut self, point: Point2D) {
794        Rc::make_mut(&mut self.points).iter_mut().for_each(|p| *p -= point);
795    }
796}
797
798impl Sub<Path2D> for Path2D {
799    type Output = Path2D;
800
801    fn sub(self, other: Path2D) -> Path2D {
802        Path2D {
803            points: Rc::new(self.points.iter().zip(other.points.iter()).map(|(a, b)| *a - *b).collect())
804        }
805    }
806}
807
808impl SubAssign<Path2D> for Path2D {
809    fn sub_assign(&mut self, other: Path2D) {
810        Rc::make_mut(&mut self.points).iter_mut().zip(other.points.iter()).for_each(|(a, b)| *a -= *b);
811    }
812}
813
814impl Mul<f32> for Path2D {
815    type Output = Path2D;
816
817    fn mul(self, scalar: f32) -> Path2D {
818        Path2D {
819            points: Rc::new(self.points.to_vec().into_iter().map(|p| p * scalar).collect())
820        }
821    }
822}
823
824impl Mul<Path2D> for f32 {
825    type Output = Path2D;
826
827    fn mul(self, path: Path2D) -> Path2D {
828        Path2D {
829            points: Rc::new(path.points.to_vec().into_iter().map(|p| self * p).collect())
830        }
831    }
832}
833
834impl Mul<Point2D> for Path2D {
835    type Output = Path2D;
836
837    fn mul(self, point: Point2D) -> Path2D {
838        Path2D {
839            points: Rc::new(self.points.to_vec().into_iter().map(|p| p * point).collect())
840        }
841    }
842}
843
844impl Mul<Path2D> for Point2D {
845    type Output = Path2D;
846
847    fn mul(self, path: Path2D) -> Path2D {
848        Path2D {
849            points: Rc::new(path.points.to_vec().into_iter().map(|p| self * p).collect())
850        }
851    }
852}
853
854impl MulAssign<f32> for Path2D {
855    fn mul_assign(&mut self, scalar: f32) {
856        Rc::make_mut(&mut self.points).iter_mut().for_each(|p| *p *= scalar);
857    }
858}
859
860impl MulAssign<Point2D> for Path2D {
861    fn mul_assign(&mut self, point: Point2D) {
862        Rc::make_mut(&mut self.points).iter_mut().for_each(|p| *p *= point);
863    }
864}
865
866impl Index<usize> for Path2D {
867    type Output = Point2D;
868
869    fn index(&self, index: usize) -> &Point2D {
870        &self.points[index]
871    }
872}
873
874impl Index<Range<usize>> for Path2D {
875    type Output = [Point2D];
876
877    fn index(&self, range: Range<usize>) -> &[Point2D] {
878        &self.points[range]
879    }
880}
881
882impl Index<RangeTo<usize>> for Path2D {
883    type Output = [Point2D];
884
885    fn index(&self, range: RangeTo<usize>) -> &[Point2D] {
886        &self.points[range]
887    }
888}
889
890impl Index<RangeFrom<usize>> for Path2D {
891    type Output = [Point2D];
892
893    fn index(&self, range: RangeFrom<usize>) -> &[Point2D] {
894        &self.points[range]
895    }
896}
897
898impl Index<RangeFull> for Path2D {
899    type Output = [Point2D];
900
901    fn index(&self, range: RangeFull) -> &[Point2D] {
902        &self.points[range]
903    }
904}
905
906impl IntoIterator for Path2D {
907    type Item = Point2D;
908    type IntoIter = std::vec::IntoIter<Point2D>;
909
910    fn into_iter(self) -> Self::IntoIter {
911        self.points.to_vec().into_iter()
912    }
913}
914
915impl Extend<Point2D> for Path2D {
916    fn extend<T: IntoIterator<Item = Point2D>>(&mut self, iter: T) {
917        Rc::make_mut(&mut self.points).extend(iter);
918    }
919}
920
921impl Default for Path2D {
922    fn default() -> Self {
923        Path2D { points: Rc::new(Vec::new()) }
924    }
925}