script/dom/webxr/
xrrigidtransform.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 euclid::{RigidTransform3D, Rotation3D, Vector3D};
7use js::rust::HandleObject;
8use js::typedarray::{Float32, Float32Array};
9
10use crate::dom::bindings::buffer_source::HeapBufferSource;
11use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
12use crate::dom::bindings::codegen::Bindings::XRRigidTransformBinding::XRRigidTransformMethods;
13use crate::dom::bindings::error::{Error, Fallible};
14use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
15use crate::dom::bindings::root::{DomRoot, MutNullableDom};
16use crate::dom::dompointreadonly::DOMPointReadOnly;
17use crate::dom::window::Window;
18use crate::dom::xrsession::ApiRigidTransform;
19use crate::script_runtime::{CanGc, JSContext};
20
21#[dom_struct]
22pub(crate) struct XRRigidTransform {
23    reflector_: Reflector,
24    position: MutNullableDom<DOMPointReadOnly>,
25    orientation: MutNullableDom<DOMPointReadOnly>,
26    #[ignore_malloc_size_of = "defined in euclid"]
27    #[no_trace]
28    transform: ApiRigidTransform,
29    inverse: MutNullableDom<XRRigidTransform>,
30    #[ignore_malloc_size_of = "defined in mozjs"]
31    matrix: HeapBufferSource<Float32>,
32}
33
34impl XRRigidTransform {
35    fn new_inherited(transform: ApiRigidTransform) -> XRRigidTransform {
36        XRRigidTransform {
37            reflector_: Reflector::new(),
38            position: MutNullableDom::default(),
39            orientation: MutNullableDom::default(),
40            transform,
41            inverse: MutNullableDom::default(),
42            matrix: HeapBufferSource::default(),
43        }
44    }
45
46    pub(crate) fn new(
47        window: &Window,
48        transform: ApiRigidTransform,
49        can_gc: CanGc,
50    ) -> DomRoot<XRRigidTransform> {
51        Self::new_with_proto(window, None, transform, can_gc)
52    }
53
54    fn new_with_proto(
55        window: &Window,
56        proto: Option<HandleObject>,
57        transform: ApiRigidTransform,
58        can_gc: CanGc,
59    ) -> DomRoot<XRRigidTransform> {
60        reflect_dom_object_with_proto(
61            Box::new(XRRigidTransform::new_inherited(transform)),
62            window,
63            proto,
64            can_gc,
65        )
66    }
67
68    pub(crate) fn identity(window: &Window, can_gc: CanGc) -> DomRoot<XRRigidTransform> {
69        let transform = RigidTransform3D::identity();
70        XRRigidTransform::new(window, transform, can_gc)
71    }
72}
73
74impl XRRigidTransformMethods<crate::DomTypeHolder> for XRRigidTransform {
75    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-xrrigidtransform
76    fn Constructor(
77        window: &Window,
78        proto: Option<HandleObject>,
79        can_gc: CanGc,
80        position: &DOMPointInit,
81        orientation: &DOMPointInit,
82    ) -> Fallible<DomRoot<Self>> {
83        if position.w != 1.0 {
84            return Err(Error::Type(format!(
85                "XRRigidTransform must be constructed with a position that has a w value of of 1.0, not {}",
86                position.w
87            )));
88        }
89
90        if !position.x.is_finite() ||
91            !position.y.is_finite() ||
92            !position.z.is_finite() ||
93            !position.w.is_finite()
94        {
95            return Err(Error::Type(
96                "Position must not contain non-finite values".into(),
97            ));
98        }
99
100        if !orientation.x.is_finite() ||
101            !orientation.y.is_finite() ||
102            !orientation.z.is_finite() ||
103            !orientation.w.is_finite()
104        {
105            return Err(Error::Type(
106                "Orientation must not contain non-finite values".into(),
107            ));
108        }
109
110        let translate = Vector3D::new(position.x as f32, position.y as f32, position.z as f32);
111        let rotate = Rotation3D::unit_quaternion(
112            orientation.x as f32,
113            orientation.y as f32,
114            orientation.z as f32,
115            orientation.w as f32,
116        );
117
118        if !rotate.i.is_finite() {
119            // if quaternion has zero norm, we'll get an infinite or NaN
120            // value for each element. This is preferable to checking for zero.
121            return Err(Error::InvalidState);
122        }
123        let transform = RigidTransform3D::new(rotate, translate);
124        Ok(XRRigidTransform::new_with_proto(
125            window, proto, transform, can_gc,
126        ))
127    }
128
129    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-position
130    fn Position(&self, can_gc: CanGc) -> DomRoot<DOMPointReadOnly> {
131        self.position.or_init(|| {
132            let t = &self.transform.translation;
133            DOMPointReadOnly::new(
134                &self.global(),
135                t.x.into(),
136                t.y.into(),
137                t.z.into(),
138                1.0,
139                can_gc,
140            )
141        })
142    }
143    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-orientation
144    fn Orientation(&self, can_gc: CanGc) -> DomRoot<DOMPointReadOnly> {
145        self.orientation.or_init(|| {
146            let r = &self.transform.rotation;
147            DOMPointReadOnly::new(
148                &self.global(),
149                r.i.into(),
150                r.j.into(),
151                r.k.into(),
152                r.r.into(),
153                can_gc,
154            )
155        })
156    }
157    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-inverse
158    fn Inverse(&self, can_gc: CanGc) -> DomRoot<XRRigidTransform> {
159        self.inverse.or_init(|| {
160            let transform =
161                XRRigidTransform::new(self.global().as_window(), self.transform.inverse(), can_gc);
162            transform.inverse.set(Some(self));
163            transform
164        })
165    }
166    // https://immersive-web.github.io/webxr/#dom-xrrigidtransform-matrix
167    fn Matrix(&self, _cx: JSContext, can_gc: CanGc) -> Float32Array {
168        if !self.matrix.is_initialized() {
169            self.matrix
170                .set_data(_cx, &self.transform.to_transform().to_array(), can_gc)
171                .expect("Failed to set on data on transform's internal matrix.")
172        }
173
174        self.matrix
175            .get_typed_array()
176            .expect("Failed to get transform's internal matrix.")
177    }
178}
179
180impl XRRigidTransform {
181    /// <https://immersive-web.github.io/webxr/#dom-xrpose-transform>
182    pub(crate) fn transform(&self) -> ApiRigidTransform {
183        self.transform
184    }
185}