script/dom/
path2d.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::RefCell;
6
7use canvas_traits::canvas::Path;
8use dom_struct::dom_struct;
9use js::rust::HandleObject;
10use script_bindings::codegen::GenericBindings::DOMMatrixBinding::DOMMatrix2DInit;
11use script_bindings::error::ErrorResult;
12use script_bindings::str::DOMString;
13
14use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::Path2DMethods;
15use crate::dom::bindings::error::{Error, Fallible};
16use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
17use crate::dom::bindings::root::DomRoot;
18use crate::dom::dommatrixreadonly::dommatrix2dinit_to_matrix;
19use crate::dom::globalscope::GlobalScope;
20use crate::script_runtime::CanGc;
21
22#[dom_struct]
23pub(crate) struct Path2D {
24    reflector_: Reflector,
25    #[ignore_malloc_size_of = "Defined in kurbo."]
26    #[no_trace]
27    path: RefCell<Path>,
28}
29
30impl Path2D {
31    pub(crate) fn new() -> Path2D {
32        Self {
33            reflector_: Reflector::new(),
34            path: RefCell::new(Path::new()),
35        }
36    }
37    pub(crate) fn new_with_path(other: &Path2D) -> Path2D {
38        Self {
39            reflector_: Reflector::new(),
40            path: other.path.clone(),
41        }
42    }
43
44    pub(crate) fn new_with_str(path: &str) -> Path2D {
45        Self {
46            reflector_: Reflector::new(),
47            path: RefCell::new(Path::from_svg(path)),
48        }
49    }
50
51    pub(crate) fn segments(&self) -> Path {
52        self.path.borrow().clone()
53    }
54
55    pub(crate) fn is_path_empty(&self) -> bool {
56        self.path.borrow().0.is_empty()
57    }
58
59    fn add_path(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
60        // Step 1. If the Path2D object path has no subpaths, then return.
61        if other.is_path_empty() {
62            return Ok(());
63        }
64
65        // Step 2 Let matrix be the result of creating a DOMMatrix from the 2D dictionary transform.
66        let matrix = dommatrix2dinit_to_matrix(transform)?;
67
68        // Step 3. If one or more of matrix's m11 element, m12 element, m21
69        // element, m22 element, m41 element, or m42 element are infinite or
70        // NaN, then return.
71        if !matrix.m11.is_finite() ||
72            !matrix.m12.is_finite() ||
73            !matrix.m21.is_finite() ||
74            !matrix.m22.is_finite() ||
75            !matrix.m31.is_finite() ||
76            !matrix.m32.is_finite()
77        {
78            return Ok(());
79        }
80
81        // Step 4. Create a copy of all the subpaths in path. Let c be this copy.
82        let mut c = other.segments();
83
84        // Step 5. Transform all the coordinates and lines in c by the transform matrix `matrix`.
85        c.transform(matrix);
86
87        let mut path = self.path.borrow_mut();
88
89        // Step 6. Let (x, y) be the last point in the last subpath of c
90        let last_point = path.last_point();
91
92        // Step 7. Add all the subpaths in c to a.
93        path.0.extend(c.0);
94
95        // Step 8. Create a new subpath in `a` with (x, y) as the only point in the subpath.
96        if let Some(last_point) = last_point {
97            path.move_to(last_point.x, last_point.y);
98        }
99
100        Ok(())
101    }
102}
103
104impl Path2DMethods<crate::DomTypeHolder> for Path2D {
105    /// <https://html.spec.whatwg.org/multipage/#dom-path2d-addpath>
106    fn AddPath(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
107        self.add_path(other, transform)
108    }
109
110    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath>
111    fn ClosePath(&self) {
112        self.path.borrow_mut().close_path();
113    }
114
115    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto>
116    fn MoveTo(&self, x: f64, y: f64) {
117        self.path.borrow_mut().move_to(x, y);
118    }
119
120    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto>
121    fn LineTo(&self, x: f64, y: f64) {
122        self.path.borrow_mut().line_to(x, y);
123    }
124
125    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto>
126    fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
127        self.path.borrow_mut().quadratic_curve_to(cpx, cpy, x, y);
128    }
129
130    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto>
131    fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
132        self.path
133            .borrow_mut()
134            .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
135    }
136
137    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto>
138    fn ArcTo(&self, x1: f64, y1: f64, x2: f64, y2: f64, radius: f64) -> Fallible<()> {
139        self.path
140            .borrow_mut()
141            .arc_to(x1, y1, x2, y2, radius)
142            .map_err(|_| Error::IndexSize)
143    }
144
145    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-rect>
146    fn Rect(&self, x: f64, y: f64, w: f64, h: f64) {
147        self.path.borrow_mut().rect(x, y, w, h);
148    }
149
150    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-arc>
151    fn Arc(
152        &self,
153        x: f64,
154        y: f64,
155        radius: f64,
156        start_angle: f64,
157        end_angle: f64,
158        counterclockwise: bool,
159    ) -> Fallible<()> {
160        self.path
161            .borrow_mut()
162            .arc(x, y, radius, start_angle, end_angle, counterclockwise)
163            .map_err(|_| Error::IndexSize)
164    }
165
166    /// <https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse>
167    fn Ellipse(
168        &self,
169        x: f64,
170        y: f64,
171        radius_x: f64,
172        radius_y: f64,
173        rotation_angle: f64,
174        start_angle: f64,
175        end_angle: f64,
176        counterclockwise: bool,
177    ) -> Fallible<()> {
178        self.path
179            .borrow_mut()
180            .ellipse(
181                x,
182                y,
183                radius_x,
184                radius_y,
185                rotation_angle,
186                start_angle,
187                end_angle,
188                counterclockwise,
189            )
190            .map_err(|_| Error::IndexSize)
191    }
192
193    /// <https://html.spec.whatwg.org/multipage/#dom-path2d-dev>
194    fn Constructor(
195        global: &GlobalScope,
196        proto: Option<HandleObject>,
197        can_gc: CanGc,
198    ) -> DomRoot<Path2D> {
199        reflect_dom_object_with_proto(Box::new(Self::new()), global, proto, can_gc)
200    }
201
202    /// <https://html.spec.whatwg.org/multipage/#dom-path2d-dev>
203    fn Constructor_(
204        global: &GlobalScope,
205        proto: Option<HandleObject>,
206        can_gc: CanGc,
207        other: &Path2D,
208    ) -> DomRoot<Path2D> {
209        reflect_dom_object_with_proto(Box::new(Self::new_with_path(other)), global, proto, can_gc)
210    }
211
212    /// <https://html.spec.whatwg.org/multipage/#dom-path2d-dev>
213    fn Constructor__(
214        global: &GlobalScope,
215        proto: Option<HandleObject>,
216        can_gc: CanGc,
217        path_string: DOMString,
218    ) -> DomRoot<Path2D> {
219        reflect_dom_object_with_proto(
220            Box::new(Self::new_with_str(path_string.str())),
221            global,
222            proto,
223            can_gc,
224        )
225    }
226}