react-three-rapier-unified 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/types.ts ADDED
@@ -0,0 +1,541 @@
1
+ import { RefObject } from "react";
2
+ import {
3
+ ActiveCollisionTypes,
4
+ CoefficientCombineRule,
5
+ ImpulseJoint,
6
+ InteractionGroups,
7
+ Collider as RapierCollider,
8
+ RigidBody as RapierRigidBody,
9
+ TempContactManifold,
10
+ Rotation,
11
+ Vector
12
+ } from "@dimforge/rapier3d-compat";
13
+ import { Quaternion, ThreeElements, Vector3 } from "@react-three/fiber";
14
+ import { Object3D } from "three";
15
+ import { ColliderProps } from ".";
16
+ import { RigidBodyState } from "./components/Physics";
17
+
18
+ export { CoefficientCombineRule as CoefficientCombineRule } from "@dimforge/rapier3d-compat";
19
+ export { RapierCollider, RapierRigidBody };
20
+
21
+ export type RefGetter<T> = RefObject<() => T | undefined>;
22
+
23
+ export type RigidBodyAutoCollider =
24
+ | "ball"
25
+ | "cuboid"
26
+ | "hull"
27
+ | "trimesh"
28
+ | false;
29
+
30
+ export type CuboidArgs = [
31
+ halfWidth: number,
32
+ halfHeight: number,
33
+ halfDepth: number
34
+ ];
35
+ export type BallArgs = [radius: number];
36
+ export type CapsuleArgs = [halfHeight: number, radius: number];
37
+ export type ConvexHullArgs = [vertices: ArrayLike<number>];
38
+ export type HeightfieldArgs = [
39
+ width: number,
40
+ height: number,
41
+ heights: number[],
42
+ scale: { x: number; y: number; z: number }
43
+ ];
44
+ export type TrimeshArgs = [
45
+ vertices: ArrayLike<number>,
46
+ indices: ArrayLike<number>
47
+ ];
48
+ export type PolylineArgs = [vertices: Float32Array, indices: Uint32Array];
49
+ export type RoundCuboidArgs = [
50
+ halfWidth: number,
51
+ halfHeight: number,
52
+ halfDepth: number,
53
+ borderRadius: number
54
+ ];
55
+ export type CylinderArgs = [halfHeight: number, radius: number];
56
+ export type RoundCylinderArgs = [
57
+ halfHeight: number,
58
+ radius: number,
59
+ borderRadius: number
60
+ ];
61
+ export type ConeArgs = [halfHeight: number, radius: number];
62
+ export type RoundConeArgs = [
63
+ halfHeight: number,
64
+ radius: number,
65
+ borderRadius: number
66
+ ];
67
+ export type ConvexMeshArgs = [
68
+ vertices: ArrayLike<number>,
69
+ indices: ArrayLike<number>
70
+ ];
71
+ export type RoundConvexHullArgs = [
72
+ vertices: ArrayLike<number>,
73
+ indices: ArrayLike<number>,
74
+ borderRadius: number
75
+ ];
76
+ export type RoundConvexMeshArgs = [
77
+ vertices: ArrayLike<number>,
78
+ indices: ArrayLike<number>,
79
+ borderRadius: number
80
+ ];
81
+
82
+ export type UseBodyOptions = Omit<RigidBodyOptions, "shape">;
83
+
84
+ export type RigidBodyTypeString =
85
+ | "fixed"
86
+ | "dynamic"
87
+ | "kinematicPosition"
88
+ | "kinematicVelocity";
89
+
90
+ export type ColliderShape =
91
+ | "cuboid"
92
+ | "trimesh"
93
+ | "ball"
94
+ | "capsule"
95
+ | "convexHull"
96
+ | "heightfield"
97
+ | "polyline"
98
+ | "roundCuboid"
99
+ | "cylinder"
100
+ | "roundCylinder"
101
+ | "cone"
102
+ | "roundCone"
103
+ | "convexMesh"
104
+ | "roundConvexHull"
105
+ | "roundConvexMesh";
106
+
107
+ export type Vector3Tuple = [x: number, y: number, z: number];
108
+ export type Vector4Tuple = [x: number, y: number, z: number, w: number];
109
+ export type Boolean3Tuple = [x: boolean, y: boolean, z: boolean];
110
+ export type Vector3Object = { x: number; y: number; z: number };
111
+
112
+ export interface ColliderOptions<ColliderArgs extends Array<unknown>> {
113
+ /**
114
+ * The optional name passed to THREE's Object3D
115
+ */
116
+ name?: string;
117
+
118
+ /**
119
+ * The shape of your collider
120
+ */
121
+ shape?: ColliderShape;
122
+
123
+ /**
124
+ * Arguments to pass to the collider
125
+ */
126
+ args?: ColliderArgs;
127
+
128
+ /**
129
+ * Principal angular inertia of this rigid body
130
+ */
131
+ principalAngularInertia?: Vector3Tuple;
132
+
133
+ /**
134
+ * Restitution controls how elastic (aka. bouncy) a contact is. Le elasticity of a contact is controlled by the restitution coefficient
135
+ */
136
+ restitution?: number;
137
+
138
+ /**
139
+ * What happens when two bodies meet. See https://rapier.rs/docs/user_guides/javascript/colliders#friction.
140
+ */
141
+ restitutionCombineRule?: CoefficientCombineRule;
142
+
143
+ /**
144
+ * Friction is a force that opposes the relative tangential motion between two rigid-bodies with colliders in contact.
145
+ * A friction coefficient of 0 implies no friction at all (completely sliding contact) and a coefficient
146
+ * greater or equal to 1 implies a very strong friction. Values greater than 1 are allowed.
147
+ */
148
+ friction?: number;
149
+
150
+ /**
151
+ * What happens when two bodies meet. See https://rapier.rs/docs/user_guides/javascript/colliders#friction.
152
+ */
153
+ frictionCombineRule?: CoefficientCombineRule;
154
+
155
+ /**
156
+ * The position of this collider relative to the rigid body
157
+ */
158
+ position?: ThreeElements["object3D"]["position"];
159
+
160
+ /**
161
+ * The rotation of this collider relative to the rigid body
162
+ */
163
+ rotation?: ThreeElements["object3D"]["rotation"];
164
+
165
+ /**
166
+ * The rotation, as a Quaternion, of this collider relative to the rigid body
167
+ */
168
+ quaternion?: ThreeElements["object3D"]["quaternion"];
169
+
170
+ /**
171
+ * The scale of this collider relative to the rigid body
172
+ */
173
+ scale?: ThreeElements["object3D"]["scale"];
174
+
175
+ /**
176
+ * Callback when this collider collideas with another collider.
177
+ */
178
+ onCollisionEnter?: CollisionEnterHandler;
179
+
180
+ /**
181
+ * Callback when this collider stops colliding with another collider.
182
+ */
183
+ onCollisionExit?: CollisionExitHandler;
184
+
185
+ /**
186
+ * Callback when this collider, or another collider starts intersecting, and at least one of them is a `sensor`.
187
+ */
188
+ onIntersectionEnter?: IntersectionEnterHandler;
189
+
190
+ /**
191
+ * Callback when this, or another collider stops intersecting, and at least one of them is a `sensor`.
192
+ */
193
+ onIntersectionExit?: IntersectionExitHandler;
194
+
195
+ /**
196
+ * Callback when this, or another collider triggers a contact force event
197
+ */
198
+ onContactForce?: ContactForceHandler;
199
+
200
+ /**
201
+ * The bit mask configuring the groups and mask for collision handling.
202
+ */
203
+ collisionGroups?: InteractionGroups;
204
+
205
+ /**
206
+ * The bit mask configuring the groups and mask for solver handling.
207
+ */
208
+ solverGroups?: InteractionGroups;
209
+
210
+ /**
211
+ * The collision types active for this collider.
212
+ *
213
+ * Use `ActiveCollisionTypes` to specify which collision types should be active for this collider.
214
+ *
215
+ * @see https://rapier.rs/javascript3d/classes/Collider.html#setActiveCollisionTypes
216
+ * @see https://rapier.rs/javascript3d/enums/ActiveCollisionTypes.html
217
+ */
218
+ activeCollisionTypes?: ActiveCollisionTypes;
219
+
220
+ /**
221
+ * Sets the uniform density of this collider.
222
+ * If this is set, other mass-properties like the angular inertia tensor are computed
223
+ * automatically from the collider's shape.
224
+ * Cannot be used at the same time as the mass or massProperties values.
225
+ * More info https://rapier.rs/docs/user_guides/javascript/colliders#mass-properties
226
+ */
227
+ density?: number;
228
+
229
+ /**
230
+ * The mass of this collider.
231
+ * Generally, it's not recommended to adjust the mass properties as it could lead to
232
+ * unexpected behaviors.
233
+ * Cannot be used at the same time as the density or massProperties values.
234
+ * More info https://rapier.rs/docs/user_guides/javascript/colliders#mass-properties
235
+ */
236
+ mass?: number;
237
+
238
+ /**
239
+ * The mass properties of this rigid body.
240
+ * Cannot be used at the same time as the density or mass values.
241
+ */
242
+ massProperties?: {
243
+ mass: number;
244
+ centerOfMass: Vector;
245
+ principalAngularInertia: Vector;
246
+ angularInertiaLocalFrame: Rotation;
247
+ };
248
+
249
+ /**
250
+ * The contact skin of the collider.
251
+ *
252
+ * The contact skin acts as if the collider was enlarged with a skin of width contactSkin around it, keeping objects further apart when colliding.
253
+ *
254
+ * A non-zero contact skin can increase performance, and in some cases, stability.
255
+ * However it creates a small gap between colliding object (equal to the sum of their skin).
256
+ * If the skin is sufficiently small, this might not be visually significant or can be hidden by the rendering assets.
257
+ *
258
+ * @defaultValue 0
259
+ */
260
+ contactSkin?: number;
261
+
262
+ /**
263
+ * Sets whether or not this collider is a sensor.
264
+ */
265
+ sensor?: boolean;
266
+ }
267
+
268
+ export type CollisionTarget = {
269
+ rigidBody?: RapierRigidBody;
270
+ collider: RapierCollider;
271
+ rigidBodyObject?: Object3D;
272
+ colliderObject?: Object3D;
273
+ };
274
+
275
+ export type CollisionPayload = {
276
+ /** the object firing the event */
277
+ target: CollisionTarget;
278
+ /** the other object involved in the event */
279
+ other: CollisionTarget;
280
+
281
+ /** deprecated use `payload.other.rigidBody` instead */
282
+ rigidBody?: RapierRigidBody;
283
+ /** deprecated use `payload.other.collider` instead */
284
+ collider: RapierCollider;
285
+ /** deprecated use `payload.other.rigidBodyObject` instead */
286
+ rigidBodyObject?: Object3D;
287
+ /** deprecated use `payload.other.colliderObject` instead */
288
+ colliderObject?: Object3D;
289
+ };
290
+
291
+ export type CollisionEnterPayload = CollisionPayload & {
292
+ manifold: TempContactManifold;
293
+ flipped: boolean;
294
+ };
295
+
296
+ export type CollisionExitPayload = CollisionPayload;
297
+
298
+ export type IntersectionEnterPayload = CollisionPayload;
299
+
300
+ export type IntersectionExitPayload = CollisionPayload;
301
+
302
+ export type ContactForcePayload = CollisionPayload & {
303
+ totalForce: Vector;
304
+ totalForceMagnitude: number;
305
+ maxForceDirection: Vector;
306
+ maxForceMagnitude: number;
307
+ };
308
+
309
+ export type CollisionEnterHandler = (payload: CollisionEnterPayload) => void;
310
+
311
+ export type CollisionExitHandler = (payload: CollisionExitPayload) => void;
312
+
313
+ export type IntersectionEnterHandler = (
314
+ payload: IntersectionEnterPayload
315
+ ) => void;
316
+
317
+ export type IntersectionExitHandler = (
318
+ payload: IntersectionExitPayload
319
+ ) => void;
320
+
321
+ export type ContactForceHandler = (payload: ContactForcePayload) => void;
322
+
323
+ export interface RigidBodyOptions extends Omit<ColliderProps, "ref"> {
324
+ /**
325
+ * Specify the type of this rigid body
326
+ */
327
+ type?: RigidBodyTypeString;
328
+
329
+ /**
330
+ * Whether or not this body can sleep.
331
+ * @defaultValue true
332
+ */
333
+ canSleep?: boolean;
334
+
335
+ /** The linear damping coefficient of this rigid-body.*/
336
+ linearDamping?: number;
337
+
338
+ /** The angular damping coefficient of this rigid-body.*/
339
+ angularDamping?: number;
340
+
341
+ /**
342
+ * The initial linear velocity of this body.
343
+ * @defaultValue [0,0,0]
344
+ */
345
+ linearVelocity?: Vector3Tuple;
346
+
347
+ /**
348
+ * The initial angular velocity of this body.
349
+ * @defaultValue [0,0,0]
350
+ */
351
+ angularVelocity?: Vector3Tuple;
352
+
353
+ /**
354
+ * The scaling factor applied to the gravity affecting the rigid-body.
355
+ * @defaultValue 1.0
356
+ */
357
+ gravityScale?: number;
358
+
359
+ /**
360
+ * The dominance group of this RigidBody. If a rigid body has a higher domiance group,
361
+ * on collision it will be immune to forces originating from the other bodies.
362
+ * https://rapier.rs/docs/user_guides/javascript/rigid_bodies#dominance
363
+ * Default: 0
364
+ */
365
+ dominanceGroup?: number;
366
+
367
+ /**
368
+ * Whether or not Continous Collision Detection is enabled for this rigid-body.
369
+ * https://rapier.rs/docs/user_guides/javascript/rigid_bodies#continuous-collision-detection
370
+ * @defaultValue false
371
+ */
372
+ ccd?: boolean;
373
+
374
+ /**
375
+ * The maximum prediction distance Soft Continuous Collision-Detection.
376
+ *
377
+ * When set to 0, soft-CCD is disabled.
378
+ *
379
+ * Soft-CCD helps prevent tunneling especially of slow-but-thin to moderately fast objects.
380
+ * The soft CCD prediction distance indicates how far in the object’s path the CCD algorithm is allowed to inspect.
381
+ * Large values can impact performance badly by increasing the work needed from the broad-phase.
382
+ *
383
+ * It is a generally cheaper variant of regular CCD since it relies on predictive constraints instead of shape-cast and substeps.
384
+ *
385
+ * @defaultValue 0
386
+ */
387
+ softCcdPrediction?: number;
388
+
389
+ /**
390
+ * Initial position of the RigidBody
391
+ */
392
+ position?: ThreeElements["object3D"]["position"];
393
+
394
+ /**
395
+ * Initial rotation of the RigidBody
396
+ */
397
+ rotation?: ThreeElements["object3D"]["rotation"];
398
+
399
+ /**
400
+ * Automatically generate colliders based on meshes inside this
401
+ * rigid body.
402
+ *
403
+ * You can change the default setting globally by setting the colliders
404
+ * prop on the <Physics /> component.
405
+ *
406
+ * Setting this to false will disable automatic colliders.
407
+ */
408
+ colliders?: RigidBodyAutoCollider | false;
409
+
410
+ /**
411
+ * Set the friction of auto-generated colliders.
412
+ * This does not affect any non-automatic child collider-components.
413
+ */
414
+ friction?: number;
415
+
416
+ /**
417
+ * Set the restitution (bounciness) of auto-generated colliders.
418
+ * This does not affect any non-automatic child collider-components.
419
+ */
420
+ restitution?: number;
421
+
422
+ /**
423
+ * Sets the number of additional solver iterations that will be run for this
424
+ * rigid-body and everything that interacts with it directly or indirectly
425
+ * through contacts or joints.
426
+ *
427
+ * Compared to increasing the global `World.numSolverIteration`, setting this
428
+ * value lets you increase accuracy on only a subset of the scene, resulting in reduced
429
+ * performance loss.
430
+ */
431
+ additionalSolverIterations?: number;
432
+
433
+ /**
434
+ * The default collision groups bitmask for all colliders in this rigid body.
435
+ * Can be customized per-collider.
436
+ */
437
+ collisionGroups?: InteractionGroups;
438
+
439
+ /**
440
+ * The default solver groups bitmask for all colliders in this rigid body.
441
+ * Can be customized per-collider.
442
+ */
443
+ solverGroups?: InteractionGroups;
444
+
445
+ /**
446
+ * The default active collision types for all colliders in this rigid body.
447
+ * Can be customized per-collider.
448
+ *
449
+ * Use `ActiveCollisionTypes` to specify which collision types should be active for this collider.
450
+ *
451
+ * @see https://rapier.rs/javascript3d/classes/Collider.html#setActiveCollisionTypes
452
+ * @see https://rapier.rs/javascript3d/enums/ActiveCollisionTypes.html
453
+ */
454
+ activeCollisionTypes?: ActiveCollisionTypes;
455
+
456
+ onSleep?(): void;
457
+
458
+ onWake?(): void;
459
+
460
+ /**
461
+ * Locks all rotations that would have resulted from forces on the created rigid-body.
462
+ */
463
+ lockRotations?: boolean;
464
+
465
+ /**
466
+ * Locks all translations that would have resulted from forces on the created rigid-body.
467
+ */
468
+ lockTranslations?: boolean;
469
+
470
+ /**
471
+ * Allow rotation of this rigid-body only along specific axes.
472
+ */
473
+ enabledRotations?: Boolean3Tuple;
474
+
475
+ /**
476
+ * Allow translation of this rigid-body only along specific axes.
477
+ */
478
+ enabledTranslations?: Boolean3Tuple;
479
+
480
+ /**
481
+ * Passed down to the object3d representing this collider.
482
+ */
483
+ userData?: ThreeElements["object3D"]["userData"];
484
+
485
+ /**
486
+ * Include invisible objects on the collider creation estimation.
487
+ */
488
+ includeInvisible?: boolean;
489
+
490
+ /**
491
+ * Transform the RigidBodyState
492
+ * @internal Do not use. Used internally by the InstancedRigidBodies to alter the RigidBody State
493
+ */
494
+ transformState?: (state: RigidBodyState) => RigidBodyState;
495
+ }
496
+
497
+ // Joints
498
+ export type SphericalJointParams = [body1Anchor: Vector3, body2Anchor: Vector3];
499
+
500
+ export type FixedJointParams = [
501
+ body1Anchor: Vector3,
502
+ body1LocalFrame: Quaternion,
503
+ body2Anchor: Vector3,
504
+ body2LocalFrame: Quaternion
505
+ ];
506
+
507
+ export type PrismaticJointParams = [
508
+ body1Anchor: Vector3,
509
+ body2Anchor: Vector3,
510
+ axis: Vector3,
511
+ limits?: [min: number, max: number]
512
+ ];
513
+
514
+ export type RevoluteJointParams = [
515
+ body1Anchor: Vector3,
516
+ body2Anchor: Vector3,
517
+ axis: Vector3,
518
+ limits?: [min: number, max: number]
519
+ ];
520
+
521
+ export type RopeJointParams = [
522
+ body1Anchor: Vector3,
523
+ body2Anchor: Vector3,
524
+ length: number
525
+ ];
526
+
527
+ export type SpringJointParams = [
528
+ body1Anchor: Vector3,
529
+ body2Anchor: Vector3,
530
+ restLength: number,
531
+ stiffness: number,
532
+ damping: number
533
+ ];
534
+
535
+ export interface UseImpulseJoint<JointParams, JointType extends ImpulseJoint> {
536
+ (
537
+ body1: RefObject<RapierRigidBody>,
538
+ body2: RefObject<RapierRigidBody>,
539
+ params: JointParams
540
+ ): RefObject<JointType | undefined>;
541
+ }
@@ -0,0 +1,43 @@
1
+ import { InteractionGroups } from "@dimforge/rapier3d-compat";
2
+
3
+ /**
4
+ * Calculates an InteractionGroup bitmask for use in the `collisionGroups` or `solverGroups`
5
+ * properties of RigidBody or Collider components. The first argument represents a list of
6
+ * groups the entity is in (expressed as numbers from 0 to 15). The second argument is a list
7
+ * of groups that will be filtered against. When it is omitted, all groups are filtered against.
8
+ *
9
+ * @example
10
+ * A RigidBody that is member of group 0 and will collide with everything from groups 0 and 1:
11
+ *
12
+ * ```tsx
13
+ * <RigidBody collisionGroups={interactionGroups([0], [0, 1])} />
14
+ * ```
15
+ *
16
+ * A RigidBody that is member of groups 0 and 1 and will collide with everything else:
17
+ *
18
+ * ```tsx
19
+ * <RigidBody collisionGroups={interactionGroups([0, 1])} />
20
+ * ```
21
+ *
22
+ * A RigidBody that is member of groups 0 and 1 and will not collide with anything:
23
+ *
24
+ * ```tsx
25
+ * <RigidBody collisionGroups={interactionGroups([0, 1], [])} />
26
+ * ```
27
+ *
28
+ * Please note that Rapier needs interaction filters to evaluate to true between _both_ colliding
29
+ * entities for collision events to trigger.
30
+ *
31
+ * @param memberships Groups the collider is a member of. (Values can range from 0 to 15.)
32
+ * @param filters Groups the interaction group should filter against. (Values can range from 0 to 15.)
33
+ * @returns An InteractionGroup bitmask.
34
+ */
35
+ export const interactionGroups = (
36
+ memberships: number | number[],
37
+ filters?: number | number[]
38
+ ): InteractionGroups =>
39
+ (bitmask(memberships) << 16) +
40
+ (filters !== undefined ? bitmask(filters) : 0b1111_1111_1111_1111);
41
+
42
+ const bitmask = (groups: number | number[]): InteractionGroups =>
43
+ [groups].flat().reduce((acc, layer) => acc | (1 << layer), 0);
@@ -0,0 +1,10 @@
1
+ import { Euler, Matrix4, Object3D, Quaternion, Vector3 } from "three";
2
+
3
+ export const _quaternion = new Quaternion();
4
+ export const _euler = new Euler();
5
+ export const _vector3 = new Vector3();
6
+ export const _object3d = new Object3D();
7
+ export const _matrix4 = new Matrix4();
8
+ export const _position = new Vector3();
9
+ export const _rotation = new Quaternion();
10
+ export const _scale = new Vector3();
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Creates a proxy that will create a singleton instance of the given class
3
+ * when a property is accessed, and not before.
4
+ *
5
+ * @returns A proxy and a reset function, so that the instance can created again
6
+ */
7
+ export const createSingletonProxy = <
8
+ SingletonClass extends object,
9
+ CreationFn extends () => SingletonClass = () => SingletonClass
10
+ >(
11
+ /**
12
+ * A function that returns a new instance of the class
13
+ */
14
+ createInstance: CreationFn
15
+ ): {
16
+ proxy: SingletonClass;
17
+ reset: () => void;
18
+ set: (newInstance: SingletonClass) => void;
19
+ } => {
20
+ let instance: SingletonClass | undefined;
21
+
22
+ const handler: ProxyHandler<SingletonClass> = {
23
+ get(target, prop) {
24
+ if (!instance) {
25
+ instance = createInstance();
26
+ }
27
+ return Reflect.get(instance!, prop);
28
+ },
29
+ set(target, prop, value) {
30
+ if (!instance) {
31
+ instance = createInstance();
32
+ }
33
+ return Reflect.set(instance!, prop, value);
34
+ }
35
+ };
36
+
37
+ const proxy = new Proxy({} as SingletonClass, handler) as SingletonClass;
38
+
39
+ const reset = () => {
40
+ instance = undefined;
41
+ };
42
+
43
+ const set = (newInstance: SingletonClass) => {
44
+ instance = newInstance;
45
+ };
46
+
47
+ /**
48
+ * Return the proxy and a reset function
49
+ */
50
+ return { proxy, reset, set };
51
+ };
@@ -0,0 +1,25 @@
1
+ import { Euler, Quaternion, Vector3 } from "three";
2
+
3
+ /**
4
+ * Takes an object resembling a Vector3 and returs a Three.Vector3
5
+ * @category Math helpers
6
+ */
7
+ export const vec3 = ({ x, y, z } = { x: 0, y: 0, z: 0 }) => {
8
+ return new Vector3(x, y, z);
9
+ };
10
+
11
+ /**
12
+ * Takes an object resembling a Quaternion and returs a Three.Quaternion
13
+ * @category Math helpers
14
+ */
15
+ export const quat = ({ x, y, z, w } = { x: 0, y: 0, z: 0, w: 1 }) => {
16
+ return new Quaternion(x, y, z, w);
17
+ };
18
+
19
+ /**
20
+ * Takes an object resembling an Euler and returs a Three.Euler
21
+ * @category Math helpers
22
+ */
23
+ export const euler = ({ x, y, z } = { x: 0, y: 0, z: 0 }) => {
24
+ return new Euler(x, y, z);
25
+ };