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