Skip to main content

script/dom/geometry/
domquad.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 dom_struct::dom_struct;
6use js::context::JSContext;
7use js::rust::HandleObject;
8use rustc_hash::FxHashMap;
9use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto_and_cx};
10use servo_base::id::{DomQuadId, DomQuadIndex};
11use servo_constellation_traits::{DomPoint, DomQuad};
12
13use crate::dom::bindings::codegen::Bindings::DOMPointBinding::{DOMPointInit, DOMPointMethods};
14use crate::dom::bindings::codegen::Bindings::DOMQuadBinding::{DOMQuadInit, DOMQuadMethods};
15use crate::dom::bindings::codegen::Bindings::DOMRectReadOnlyBinding::DOMRectInit;
16use crate::dom::bindings::error::Fallible;
17use crate::dom::bindings::reflector::DomGlobal;
18use crate::dom::bindings::root::{Dom, DomRoot};
19use crate::dom::bindings::serializable::Serializable;
20use crate::dom::bindings::structuredclone::StructuredData;
21use crate::dom::dompoint::DOMPoint;
22use crate::dom::domrect::DOMRect;
23use crate::dom::globalscope::GlobalScope;
24
25/// <https://drafts.fxtf.org/geometry/#DOMQuad>
26#[dom_struct]
27pub(crate) struct DOMQuad {
28    reflector_: Reflector,
29    p1: Dom<DOMPoint>,
30    p2: Dom<DOMPoint>,
31    p3: Dom<DOMPoint>,
32    p4: Dom<DOMPoint>,
33}
34
35impl DOMQuad {
36    fn new_inherited(p1: &DOMPoint, p2: &DOMPoint, p3: &DOMPoint, p4: &DOMPoint) -> DOMQuad {
37        DOMQuad {
38            reflector_: Reflector::new(),
39            p1: Dom::from_ref(p1),
40            p2: Dom::from_ref(p2),
41            p3: Dom::from_ref(p3),
42            p4: Dom::from_ref(p4),
43        }
44    }
45
46    pub(crate) fn new(
47        cx: &mut JSContext,
48        global: &GlobalScope,
49        p1: &DOMPoint,
50        p2: &DOMPoint,
51        p3: &DOMPoint,
52        p4: &DOMPoint,
53    ) -> DomRoot<DOMQuad> {
54        Self::new_with_proto(cx, global, None, p1, p2, p3, p4)
55    }
56
57    fn new_with_proto(
58        cx: &mut JSContext,
59        global: &GlobalScope,
60        proto: Option<HandleObject>,
61        p1: &DOMPoint,
62        p2: &DOMPoint,
63        p3: &DOMPoint,
64        p4: &DOMPoint,
65    ) -> DomRoot<DOMQuad> {
66        reflect_dom_object_with_proto_and_cx(
67            Box::new(DOMQuad::new_inherited(p1, p2, p3, p4)),
68            global,
69            proto,
70            cx,
71        )
72    }
73}
74
75impl DOMQuadMethods<crate::DomTypeHolder> for DOMQuad {
76    /// <https://drafts.fxtf.org/geometry/#dom-domquad-domquad>
77    fn Constructor(
78        cx: &mut JSContext,
79        global: &GlobalScope,
80        proto: Option<HandleObject>,
81        p1: &DOMPointInit,
82        p2: &DOMPointInit,
83        p3: &DOMPointInit,
84        p4: &DOMPointInit,
85    ) -> Fallible<DomRoot<DOMQuad>> {
86        let p1 = DOMPoint::new_from_init(cx, global, p1);
87        let p2 = DOMPoint::new_from_init(cx, global, p2);
88        let p3 = DOMPoint::new_from_init(cx, global, p3);
89        let p4 = DOMPoint::new_from_init(cx, global, p4);
90        Ok(DOMQuad::new_with_proto(
91            cx, global, proto, &p1, &p2, &p3, &p4,
92        ))
93    }
94
95    /// <https://drafts.fxtf.org/geometry/#dom-domquad-fromrect>
96    fn FromRect(cx: &mut JSContext, global: &GlobalScope, other: &DOMRectInit) -> DomRoot<DOMQuad> {
97        let p1 = DOMPoint::new(cx, global, other.x, other.y, 0f64, 1f64);
98        let p2 = DOMPoint::new(cx, global, other.x + other.width, other.y, 0f64, 1f64);
99        let p3 = DOMPoint::new(
100            cx,
101            global,
102            other.x + other.width,
103            other.y + other.height,
104            0f64,
105            1f64,
106        );
107        let p4 = DOMPoint::new(cx, global, other.x, other.y + other.height, 0f64, 1f64);
108        DOMQuad::new(cx, global, &p1, &p2, &p3, &p4)
109    }
110
111    /// <https://drafts.fxtf.org/geometry/#dom-domquad-fromquad>
112    fn FromQuad(cx: &mut JSContext, global: &GlobalScope, other: &DOMQuadInit) -> DomRoot<DOMQuad> {
113        let p1 = DOMPoint::new_from_init(cx, global, &other.p1);
114        let p2 = DOMPoint::new_from_init(cx, global, &other.p2);
115        let p3 = DOMPoint::new_from_init(cx, global, &other.p3);
116        let p4 = DOMPoint::new_from_init(cx, global, &other.p4);
117        DOMQuad::new(cx, global, &p1, &p2, &p3, &p4)
118    }
119
120    /// <https://drafts.fxtf.org/geometry/#dom-domquad-p1>
121    fn P1(&self) -> DomRoot<DOMPoint> {
122        DomRoot::from_ref(&self.p1)
123    }
124
125    /// <https://drafts.fxtf.org/geometry/#dom-domquad-p2>
126    fn P2(&self) -> DomRoot<DOMPoint> {
127        DomRoot::from_ref(&self.p2)
128    }
129
130    /// <https://drafts.fxtf.org/geometry/#dom-domquad-p3>
131    fn P3(&self) -> DomRoot<DOMPoint> {
132        DomRoot::from_ref(&self.p3)
133    }
134
135    /// <https://drafts.fxtf.org/geometry/#dom-domquad-p4>
136    fn P4(&self) -> DomRoot<DOMPoint> {
137        DomRoot::from_ref(&self.p4)
138    }
139
140    /// <https://drafts.fxtf.org/geometry/#dom-domquad-getbounds>
141    fn GetBounds(&self, cx: &mut JSContext) -> DomRoot<DOMRect> {
142        // https://drafts.fxtf.org/geometry/#nan-safe-minimum
143        let nan_safe_minimum = |a: f64, b: f64| {
144            if a.is_nan() || b.is_nan() {
145                f64::NAN
146            } else {
147                a.min(b)
148            }
149        };
150
151        // https://drafts.fxtf.org/geometry/#nan-safe-maximum
152        let nan_safe_maximum = |a: f64, b: f64| {
153            if a.is_nan() || b.is_nan() {
154                f64::NAN
155            } else {
156                a.max(b)
157            }
158        };
159
160        // Step 1. Let bounds be a DOMRect object.
161        // NOTE: We construct the object at the end
162
163        // Step 2. Let left be the NaN-safe minimum of point 1’s x coordinate,
164        // point 2’s x coordinate, point 3’s x coordinate and point 4’s x coordinate.
165        let left = nan_safe_minimum(
166            nan_safe_minimum(self.p1.X(), self.p2.X()),
167            nan_safe_minimum(self.p3.X(), self.p4.X()),
168        );
169
170        // Step 3. Let top be the NaN-safe minimum of point 1’s y coordinate,
171        // point 2’s y coordinate, point 3’s y coordinate and point 4’s y coordinate.
172        let top = nan_safe_minimum(
173            nan_safe_minimum(self.p1.Y(), self.p2.Y()),
174            nan_safe_minimum(self.p3.Y(), self.p4.Y()),
175        );
176
177        // Step 4. Let right be the NaN-safe maximum of point 1’s x coordinate,
178        // point 2’s x coordinate, point 3’s x coordinate and point 4’s x coordinate.
179        let right = nan_safe_maximum(
180            nan_safe_maximum(self.p1.X(), self.p2.X()),
181            nan_safe_maximum(self.p3.X(), self.p4.X()),
182        );
183
184        // Step 5. Let bottom be the NaN-safe maximum of point 1’s y coordinate,
185        // point 2’s y coordinate, point 3’s y coordinate and point 4’s y coordinate.
186        let bottom = nan_safe_maximum(
187            nan_safe_maximum(self.p1.Y(), self.p2.Y()),
188            nan_safe_maximum(self.p3.Y(), self.p4.Y()),
189        );
190
191        // Step 6. Set x coordinate of bounds to left, y coordinate of bounds to top,
192        // width dimension of bounds to right - left and height dimension of bounds to bottom - top.
193        // NOTE: Combined with Step 1.
194        DOMRect::new(cx, &self.global(), left, top, right - left, bottom - top)
195    }
196}
197
198impl Serializable for DOMQuad {
199    type Index = DomQuadIndex;
200    type Data = DomQuad;
201
202    fn serialize(&self) -> Result<(DomQuadId, Self::Data), ()> {
203        let make_point = |src: DomRoot<DOMPoint>| -> DomPoint {
204            DomPoint {
205                x: src.X(),
206                y: src.Y(),
207                z: src.Z(),
208                w: src.W(),
209            }
210        };
211        let serialized = DomQuad {
212            p1: make_point(self.P1()),
213            p2: make_point(self.P2()),
214            p3: make_point(self.P3()),
215            p4: make_point(self.P4()),
216        };
217        Ok((DomQuadId::new(), serialized))
218    }
219
220    fn deserialize(
221        cx: &mut JSContext,
222        owner: &GlobalScope,
223        serialized: Self::Data,
224    ) -> Result<DomRoot<Self>, ()>
225    where
226        Self: Sized,
227    {
228        let p1 = DOMPoint::new(
229            cx,
230            owner,
231            serialized.p1.x,
232            serialized.p1.y,
233            serialized.p1.z,
234            serialized.p1.w,
235        );
236        let p2 = DOMPoint::new(
237            cx,
238            owner,
239            serialized.p2.x,
240            serialized.p2.y,
241            serialized.p2.z,
242            serialized.p2.w,
243        );
244        let p3 = DOMPoint::new(
245            cx,
246            owner,
247            serialized.p3.x,
248            serialized.p3.y,
249            serialized.p3.z,
250            serialized.p3.w,
251        );
252        let p4 = DOMPoint::new(
253            cx,
254            owner,
255            serialized.p4.x,
256            serialized.p4.y,
257            serialized.p4.z,
258            serialized.p4.w,
259        );
260        Ok(Self::new(cx, owner, &p1, &p2, &p3, &p4))
261    }
262
263    fn serialized_storage<'a>(
264        data: StructuredData<'a, '_>,
265    ) -> &'a mut Option<FxHashMap<DomQuadId, Self::Data>> {
266        match data {
267            StructuredData::Reader(reader) => &mut reader.quads,
268            StructuredData::Writer(writer) => &mut writer.quads,
269        }
270    }
271}