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