1use 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 if other.is_path_empty() {
62 return Ok(());
63 }
64
65 let matrix = dommatrix2dinit_to_matrix(transform)?;
67
68 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 let mut c = other.segments();
83
84 c.transform(matrix);
86
87 let mut path = self.path.borrow_mut();
88
89 let last_point = path.last_point();
91
92 path.0.extend(c.0);
94
95 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 fn AddPath(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
107 self.add_path(other, transform)
108 }
109
110 fn ClosePath(&self) {
112 self.path.borrow_mut().close_path();
113 }
114
115 fn MoveTo(&self, x: f64, y: f64) {
117 self.path.borrow_mut().move_to(x, y);
118 }
119
120 fn LineTo(&self, x: f64, y: f64) {
122 self.path.borrow_mut().line_to(x, y);
123 }
124
125 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 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 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 fn Rect(&self, x: f64, y: f64, w: f64, h: f64) {
147 self.path.borrow_mut().rect(x, y, w, h);
148 }
149
150 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 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 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 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 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}