@woosh/meep-engine 2.37.18 → 2.37.19

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.
Files changed (111) hide show
  1. package/core/assert.js +1 -1
  2. package/core/collection/array/typed/isTypedArray.js +20 -0
  3. package/core/collection/array/typedArrayToDataType.js +1 -0
  4. package/core/collection/table/RowFirstTable.js +34 -0
  5. package/core/collection/table/RowFirstTable.spec.js +59 -1
  6. package/core/color/Color.js +14 -0
  7. package/core/color/rgb2hex.js +1 -1
  8. package/core/events/signal/Signal.spec.js +16 -0
  9. package/core/geom/Quaternion.d.ts +5 -0
  10. package/core/geom/Quaternion.js +152 -137
  11. package/core/geom/Quaternion.spec.js +47 -2
  12. package/core/geom/Vector3.schema.json +16 -0
  13. package/core/json/JsonUtils.js +2 -20
  14. package/core/model/ObservedEnum.js +8 -0
  15. package/editor/Editor.js +97 -1
  16. package/editor/actions/concrete/PatchTerrainHeightAction.js +1 -1
  17. package/editor/ecs/component/FieldDescriptor.js +34 -0
  18. package/editor/ecs/component/FieldValueAdapter.js +20 -0
  19. package/editor/ecs/component/TypeEditor.js +33 -0
  20. package/editor/ecs/component/TypeSchema.d.ts +38 -0
  21. package/editor/ecs/component/createFieldEditor.js +90 -0
  22. package/editor/ecs/component/createObjectEditor.js +242 -60
  23. package/editor/ecs/component/editors/ColorEditor.js +39 -0
  24. package/editor/ecs/component/editors/HTMLElementEditor.js +17 -0
  25. package/editor/ecs/component/editors/ImagePathEditor.js +50 -0
  26. package/editor/ecs/component/editors/NumericIntervalEditor.js +86 -0
  27. package/editor/ecs/component/editors/ObservedBooleanEditor.js +13 -0
  28. package/editor/ecs/component/editors/ObservedEnumEditor.js +32 -0
  29. package/editor/ecs/component/editors/ObservedIntegerEditor.js +43 -0
  30. package/editor/ecs/component/editors/ObservedStringEditor.js +51 -0
  31. package/editor/ecs/component/editors/Sampler2DEditor.js +60 -0
  32. package/editor/ecs/component/editors/collection/ListEditor.js +83 -0
  33. package/editor/ecs/component/editors/common/BitFlagsEditor.js +80 -0
  34. package/editor/ecs/component/editors/common/EnumEditor.js +41 -0
  35. package/editor/ecs/component/editors/common/makeV3_editor.js +85 -0
  36. package/editor/ecs/component/editors/common/noEditor.js +9 -0
  37. package/editor/ecs/component/editors/ecs/GridObstacleEditor.js +17 -0
  38. package/editor/ecs/component/editors/ecs/MinimapMarkerEditor.js +16 -0
  39. package/editor/ecs/component/editors/ecs/ParameterLookupTableEditor.js +44 -0
  40. package/editor/ecs/component/editors/ecs/ParameterTrackEditor.js +17 -0
  41. package/editor/ecs/component/editors/ecs/ParticleEmitterEditor.js +58 -0
  42. package/editor/ecs/component/editors/ecs/ParticleEmitterLayerEditor.js +54 -0
  43. package/editor/ecs/component/editors/ecs/SimulationStepDefinitionEditor.js +21 -0
  44. package/editor/ecs/component/editors/ecs/Trail2DEditor.js +33 -0
  45. package/editor/ecs/component/editors/ecs/TransformEditor.js +23 -0
  46. package/editor/ecs/component/editors/ecs/terrain/SplatMappingEditor.js +21 -0
  47. package/editor/ecs/component/editors/ecs/terrain/TerrainEditor.js +42 -0
  48. package/editor/ecs/component/editors/ecs/terrain/TerrainLayerEditor.js +18 -0
  49. package/editor/ecs/component/editors/ecs/terrain/TerrainLayersEditor.js +22 -0
  50. package/editor/ecs/component/editors/ecs/terrain/TerrainOverlayEditor.js +20 -0
  51. package/editor/ecs/component/editors/geom/QuaternionEditor.js +56 -0
  52. package/editor/ecs/component/editors/geom/Vector1Editor.js +57 -0
  53. package/editor/ecs/component/editors/geom/Vector2Editor.js +11 -0
  54. package/editor/ecs/component/editors/geom/Vector3Editor.js +13 -0
  55. package/editor/ecs/component/editors/geom/Vector4Editor.js +12 -0
  56. package/editor/ecs/component/editors/primitive/ArrayEditor.js +46 -0
  57. package/editor/ecs/component/editors/primitive/BooleanEditor.js +27 -0
  58. package/editor/ecs/component/editors/primitive/FunctionEditor.js +25 -0
  59. package/editor/ecs/component/editors/primitive/NumberEditor.js +60 -0
  60. package/editor/ecs/component/editors/primitive/ObjectEditor.js +12 -0
  61. package/editor/ecs/component/editors/primitive/StringEditor.js +31 -0
  62. package/editor/ecs/component/editors/three/BufferGeometryEditor.js +28 -0
  63. package/editor/ecs/component/editors/three/MaterialEditor.js +27 -0
  64. package/editor/ecs/component/editors/three/MeshEditor.js +35 -0
  65. package/editor/ecs/component/editors/three/TextureEditor.js +32 -0
  66. package/editor/ecs/component/findNearestRegisteredType.js +59 -0
  67. package/editor/ecs/component/prototypeObjectEditor.js +379 -0
  68. package/editor/view/EditorView.js +1 -1
  69. package/editor/view/ecs/ComponentControlView.js +2 -30
  70. package/editor/view/ecs/EntityEditor.js +61 -139
  71. package/editor/view/ecs/components/GridObstacleController.js +4 -4
  72. package/editor/view/ecs/components/TerrainController.js +1 -1
  73. package/editor/view/ecs/components/common/NumberController.js +19 -7
  74. package/engine/animation/keyed2/AnimationTrack.js +1 -1
  75. package/engine/ecs/components/TagEditor.js +15 -0
  76. package/engine/ecs/terrain/ecs/Terrain.js +23 -42
  77. package/engine/ecs/terrain/ecs/TerrainSystem.js +2 -2
  78. package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +1 -1
  79. package/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +1 -1
  80. package/engine/ecs/transform/Transform.editor.schema.json +16 -0
  81. package/engine/graphics/ecs/highlight/HighlightEditor.js +17 -0
  82. package/engine/graphics/ecs/mesh/MeshEditor.js +28 -0
  83. package/engine/graphics/ecs/mesh-v2/aggregate/prototypeSGMesh.js +3 -3
  84. package/engine/graphics/micron/render/instanced/shader/shader_rewrite_standard.js +15 -15
  85. package/engine/graphics/particles/particular/engine/ParticularEngine.js +5 -0
  86. package/engine/graphics/texture/sampler/Sampler2D.js +16 -0
  87. package/engine/graphics/util/ScaleObject3ToBox.js +14 -1
  88. package/engine/graphics/util/makeMeshPreviewScene.js +2 -1
  89. package/engine/grid/components/GridObstacle.js +0 -44
  90. package/engine/grid/components/GridObstacleSerializationAdapter.js +46 -0
  91. package/engine/navigation/ecs/components/Path.d.ts +2 -0
  92. package/engine/navigation/ecs/components/Path.js +6 -1
  93. package/generation/example/SampleGenerator0.js +33 -29
  94. package/generation/example/generators/interactive/mir_generator_place_buff_objects.js +7 -6
  95. package/generation/example/generators/mir_generator_place_bases.js +7 -3
  96. package/generation/example/generators/mir_generator_place_road_decorators.js +3 -3
  97. package/generation/example/generators/mir_generator_place_starting_point.js +3 -2
  98. package/generation/markers/GridCellActionPlaceMarker.js +9 -3
  99. package/generation/markers/transform/MarkerNodeTransformerOffsetPosition.js +1 -1
  100. package/generation/placement/GridCellPlacementRule.js +22 -23
  101. package/generation/theme/ThemeEngine.js +1 -1
  102. package/package.json +1 -1
  103. package/samples/terrain/main.js +1 -1
  104. package/view/View.js +23 -1
  105. package/view/common/LabelView.js +1 -1
  106. package/view/compose3x3transform.js +32 -8
  107. package/view/controller/dat/DatGuiUtils.js +1 -1
  108. package/view/elements/DropDownSelectionView.js +11 -3
  109. package/view/elements/image/ImageView.js +3 -1
  110. package/core/model/ObservedReal.js +0 -55
  111. package/editor/ecs/component/ObjectEditor.js +0 -0
package/core/assert.js CHANGED
@@ -1,5 +1,5 @@
1
- import { isTypedArray } from "./json/JsonUtils.js";
2
1
  import { isArrayEqualStrict } from "./collection/array/isArrayEqualStrict.js";
2
+ import { isTypedArray } from "./collection/array/typed/isTypedArray.js";
3
3
 
4
4
  function equal(a, b, m) {
5
5
  assert(a === b, m) // eslint-disable-line eqeqeq
@@ -0,0 +1,20 @@
1
+ /**
2
+ *
3
+ * @param o
4
+ * @returns {boolean}
5
+ */
6
+ function isTypedArray(o) {
7
+ return (
8
+ o instanceof Int8Array ||
9
+ o instanceof Uint8Array ||
10
+ o instanceof Uint8ClampedArray ||
11
+ o instanceof Int16Array ||
12
+ o instanceof Uint16Array ||
13
+ o instanceof Int32Array ||
14
+ o instanceof Uint32Array ||
15
+ o instanceof Float32Array ||
16
+ o instanceof Float64Array
17
+ );
18
+ }
19
+
20
+ export { isTypedArray };
@@ -10,6 +10,7 @@ export function typedArrayToDataType(v) {
10
10
 
11
11
  switch (ctor) {
12
12
  case Uint8Array:
13
+ case Uint8ClampedArray:
13
14
  return DataType.Uint8;
14
15
  case Uint16Array:
15
16
  return DataType.Uint16;
@@ -6,6 +6,7 @@
6
6
  import { assert } from "../../assert.js";
7
7
  import Signal from "../../events/signal/Signal.js";
8
8
  import { max2 } from "../../math/max2.js";
9
+ import { array_copy } from "../array/copyArray.js";
9
10
 
10
11
  /**
11
12
  *
@@ -414,6 +415,39 @@ RowFirstTable.prototype.setRow = function (index, record) {
414
415
  this.writeRowMethod(this.dataView, this.bytesPerRecord * index, record);
415
416
  }
416
417
 
418
+ /**
419
+ * Reverse order of rows, row-0 will end up at and previously last row will become the first row etc.
420
+ */
421
+ RowFirstTable.prototype.reverse_rows = function () {
422
+ const bpr = this.bytesPerRecord;
423
+ const copy_buffer = new Uint8Array(bpr);
424
+
425
+ const buffer = this.data;
426
+ const wrapper = new Uint8Array(buffer);
427
+
428
+ const length = this.length;
429
+
430
+ if (length <= 1) {
431
+ // need at least 2 rows for reversal to make any change
432
+ return;
433
+ }
434
+
435
+ const last_row_index = length - 1;
436
+
437
+
438
+ const traversal_limit = last_row_index >>> 1;
439
+ for (let i = 0; i <= traversal_limit; i++) {
440
+ const address = i * bpr;
441
+ array_copy(wrapper, address, copy_buffer, 0, bpr);
442
+
443
+ const swap_index = last_row_index - i;
444
+ const swap_address = swap_index * bpr;
445
+
446
+ wrapper.copyWithin(address, swap_address, swap_address + bpr)
447
+ wrapper.set(copy_buffer, swap_address);
448
+ }
449
+ };
450
+
417
451
  /**
418
452
  * clear out all the data and free memory
419
453
  */
@@ -223,7 +223,65 @@ test('copy', () => {
223
223
  a.addRow([-11.3, 13]);
224
224
 
225
225
  const b = new RowFirstTable(spec);
226
- b.copy(a);
226
+ b.copy(a);
227
227
 
228
228
 
229
229
  });
230
+
231
+
232
+ test('reverse_rows 3 rows (odd number)', () => {
233
+
234
+ const spec = new RowFirstTableSpec([DataType.Uint8]);
235
+
236
+ const a = new RowFirstTable(spec);
237
+ a.addRow([1]);
238
+ a.addRow([2]);
239
+ a.addRow([3]);
240
+
241
+ a.reverse_rows();
242
+
243
+ expect(a.readCellValue(0, 0)).toBeCloseTo(3);
244
+ expect(a.readCellValue(1, 0)).toBeCloseTo(2);
245
+ expect(a.readCellValue(2, 0)).toBeCloseTo(1);
246
+ });
247
+
248
+ test('reverse_rows 2 rows', () => {
249
+
250
+ const spec = new RowFirstTableSpec([DataType.Float64, DataType.Uint8]);
251
+
252
+ const a = new RowFirstTable(spec);
253
+ a.addRow([3.1, 7]);
254
+ a.addRow([-11.3, 13]);
255
+
256
+ a.reverse_rows();
257
+
258
+ expect(a.readCellValue(1, 0)).toBeCloseTo(3.1);
259
+ expect(a.readCellValue(1, 1)).toBeCloseTo(7);
260
+
261
+ expect(a.readCellValue(0, 0)).toBeCloseTo(-11.3);
262
+ expect(a.readCellValue(0, 1)).toBeCloseTo(13);
263
+ });
264
+
265
+ test('reverse_rows 1 row', () => {
266
+
267
+ const spec = new RowFirstTableSpec([DataType.Float64, DataType.Uint8]);
268
+
269
+ const a = new RowFirstTable(spec);
270
+
271
+ a.addRow([3.1, 7]);
272
+ a.reverse_rows();
273
+
274
+ expect(a.readCellValue(0, 0)).toBeCloseTo(3.1);
275
+ expect(a.readCellValue(0, 1)).toBeCloseTo(7);
276
+ });
277
+
278
+ test('reverse_rows empty', () => {
279
+
280
+ const spec = new RowFirstTableSpec([DataType.Float64, DataType.Uint8]);
281
+
282
+ const a = new RowFirstTable(spec);
283
+
284
+ a.reverse_rows();
285
+
286
+ expect(a.length).toBe(0);
287
+ });
@@ -9,6 +9,7 @@ import { computeHashFloat } from "../math/hash/computeHashFloat.js";
9
9
  import { rgb2hsv } from "./rgb2hsv.js";
10
10
  import { rgb2uint24 } from "./rgb2uint24.js";
11
11
  import { clamp01 } from "../math/clamp01.js";
12
+ import { rgb2hex } from "./rgb2hex.js";
12
13
 
13
14
  /**
14
15
  * @class
@@ -346,6 +347,19 @@ export class Color {
346
347
  this.setRGB(r / 255, g / 255, b / 255);
347
348
  }
348
349
 
350
+ /**
351
+ * Returns color in CSS hex color format
352
+ * @example #FF0000
353
+ * @returns {string}
354
+ */
355
+ toHex() {
356
+ return '#' + rgb2hex(
357
+ Math.round(this.r),
358
+ Math.round(this.g),
359
+ Math.round(this.b)
360
+ );
361
+ }
362
+
349
363
  /**
350
364
  *
351
365
  * @param {Color} other
@@ -1,5 +1,5 @@
1
1
  function dec2hex(c) {
2
- const hex = c.toString(16);
2
+ const hex = Math.round(c).toString(16);
3
3
  return hex.length === 1 ? "0" + hex : hex;
4
4
  }
5
5
 
@@ -147,3 +147,19 @@ test("handler context is properly setup during dispatch", () => {
147
147
 
148
148
  expect(handler).toHaveBeenCalled();
149
149
  });
150
+
151
+ test("handlers should be executed in the order in which they are added", () => {
152
+ const signal = new Signal();
153
+
154
+ const marks = [];
155
+
156
+ const write_1 = jest.fn(() => marks.push(1));
157
+ const write_2 = jest.fn(() => marks.push(2));
158
+
159
+ signal.add(write_1);
160
+ signal.add(write_2);
161
+
162
+ signal.send0();
163
+
164
+ expect(marks).toEqual([1, 2]);
165
+ });
@@ -24,4 +24,9 @@ export default class Quaternion {
24
24
  setFromRotationMatrix(m: Matrix4): void
25
25
 
26
26
  decodeFromUint32(uint: number): void
27
+
28
+
29
+ toEulerAnglesXYZ(v3: { set(x: number, y: number, z: number): any }): void
30
+
31
+ fromEulerAnglesXYZ(x: number, y: number, z: number): void
27
32
  }
@@ -7,9 +7,8 @@ import { Matrix4 as ThreeMatrix4 } from "three";
7
7
  import { assert } from "../assert.js";
8
8
  import Signal from "../events/signal/Signal.js";
9
9
  import { clamp } from "../math/clamp.js";
10
- import { copysign } from "../math/copysign.js";
11
10
  import { lerp } from "../math/lerp.js";
12
- import { EPSILON, epsilonEquals, PI2, PI_HALF } from "../math/MathUtils.js";
11
+ import { EPSILON, epsilonEquals, PI2 } from "../math/MathUtils.js";
13
12
  import Vector3 from "./Vector3.js";
14
13
  import { v3_dot } from "./v3_dot.js";
15
14
 
@@ -503,84 +502,33 @@ class Quaternion {
503
502
 
504
503
  // http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
505
504
 
506
- const cos = Math.cos;
507
- const sin = Math.sin;
508
-
509
- const half_x = x * 0.5;
510
- const half_y = y * 0.5;
511
- const half_z = z * 0.5;
512
-
513
- const c1 = cos(half_x);
514
- const c2 = cos(half_y);
515
- const c3 = cos(half_z);
516
-
517
- const s1 = sin(half_x);
518
- const s2 = sin(half_y);
519
- const s3 = sin(half_z);
520
-
521
505
  if (order === 'XYZ') {
522
506
 
523
- return this.set(
524
- s1 * c2 * c3 + c1 * s2 * s3,
525
- c1 * s2 * c3 - s1 * c2 * s3,
526
- c1 * c2 * s3 + s1 * s2 * c3,
527
- c1 * c2 * c3 - s1 * s2 * s3
528
- );
507
+ this.fromEulerAnglesXYZ(x, y, z);
529
508
 
530
509
  } else if (order === 'YXZ') {
531
510
 
532
-
533
- return this.set(
534
- s1 * c2 * c3 + c1 * s2 * s3,
535
- c1 * s2 * c3 - s1 * c2 * s3,
536
- c1 * c2 * s3 - s1 * s2 * c3,
537
- c1 * c2 * c3 + s1 * s2 * s3
538
- );
539
-
540
- // return this.set(
541
- // c1 * s2 * c3 + s1 * c2 * s3,
542
- // s1 * c2 * c3 - c1 * s2 * s3,
543
- // c1 * c2 * s3 - s1 * s2 * c3,
544
- // c1 * c2 * c3 + s1 * s2 * s3
545
- // );
511
+ this.fromEulerAnglesYXZ(x, y, z);
546
512
 
547
513
  } else if (order === 'ZXY') {
548
514
 
549
- return this.set(
550
- s1 * c2 * c3 - c1 * s2 * s3,
551
- c1 * s2 * c3 + s1 * c2 * s3,
552
- c1 * c2 * s3 + s1 * s2 * c3,
553
- c1 * c2 * c3 - s1 * s2 * s3
554
- );
515
+ this.fromEulerAnglesZXY(x, y, z);
555
516
 
556
517
  } else if (order === 'ZYX') {
557
518
 
558
- return this.set(
559
- s1 * c2 * c3 - c1 * s2 * s3,
560
- c1 * s2 * c3 + s1 * c2 * s3,
561
- c1 * c2 * s3 - s1 * s2 * c3,
562
- c1 * c2 * c3 + s1 * s2 * s3
563
- );
519
+ this.fromEulerAnglesZYX(x, y, z);
564
520
 
565
521
  } else if (order === 'YZX') {
566
522
 
567
- return this.set(
568
- s1 * c2 * c3 + c1 * s2 * s3,
569
- c1 * s2 * c3 + s1 * c2 * s3,
570
- c1 * c2 * s3 - s1 * s2 * c3,
571
- c1 * c2 * c3 - s1 * s2 * s3
572
- );
523
+ this.fromEulerAnglesYZX(x, y, z);
573
524
 
574
525
  } else if (order === 'XZY') {
575
526
 
576
- return this.set(
577
- s1 * c2 * c3 - c1 * s2 * s3,
578
- c1 * s2 * c3 - s1 * c2 * s3,
579
- c1 * c2 * s3 + s1 * s2 * c3,
580
- c1 * c2 * c3 + s1 * s2 * s3
581
- );
527
+ this.fromEulerAnglesXZY(x, y, z);
582
528
 
583
529
  }
530
+
531
+ return this;
584
532
  }
585
533
 
586
534
  /**
@@ -599,9 +547,9 @@ class Quaternion {
599
547
  const y2 = y * y;
600
548
  const z2 = z * z;
601
549
 
602
- const psi = Math.atan2(-2 * (x * w - y * z), (w2 - x2 - y2 + z2));
550
+ const psi = Math.atan2(2 * (x * w - y * z), (w2 - x2 - y2 + z2));
603
551
  const theta = Math.asin(2 * (x * z + y * w));
604
- const phi = Math.atan2(-2 * (z * w - x * y), (w2 + x2 - y2 - z2));
552
+ const phi = Math.atan2(2 * (z * w - x * y), (w2 + x2 - y2 - z2));
605
553
 
606
554
  result.set(psi, theta, phi);
607
555
  }
@@ -727,64 +675,75 @@ class Quaternion {
727
675
  }
728
676
 
729
677
  /**
730
- * Writes Pitch(Y), Yaw(Z), Roll(X) to a given vector
731
- * @param {Vector3} result
678
+ * XYZ order
679
+ * @source: https://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
680
+ * @param {number} x angle in X axis in radians
681
+ * @param {number} y angle in Y axis in radians
682
+ * @param {number} z angle in Z axis in radians
732
683
  */
733
- toEulerAngles2(result) {
734
- const x = this.x;
735
- const y = this.y;
736
- const z = this.z;
737
- const w = this.w;
684
+ fromEulerAngles(x, y, z) {
685
+ return this.fromEulerAnglesXYZ(x, y, z);
686
+ }
738
687
 
739
- const yy = y * y;
740
- const xx = x * x;
741
- const zz = z * z;
688
+ /**
689
+ * XYZ order
690
+ * @source: https://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
691
+ * @see http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
692
+ * @see https://github.com/mrdoob/three.js/blob/510705cde208b165fd87946b0f8504a1cd6dbe83/src/math/Quaternion.js#L206
693
+ * @param {number} x angle in X axis in radians
694
+ * @param {number} y angle in Y axis in radians
695
+ * @param {number} z angle in Z axis in radians
696
+ */
697
+ fromEulerAnglesXYZ(x, y, z) {
698
+ const half_x = x * 0.5;
699
+ const half_y = y * 0.5;
700
+ const half_z = z * 0.5;
742
701
 
743
- // roll (x-axis rotation)
744
- const sinr_cosp = +2.0 * (w * x + y * z);
745
- const cosr_cosp = +1.0 - 2.0 * (xx + yy);
746
- const roll = Math.atan2(sinr_cosp, cosr_cosp);
702
+ const sin = Math.sin;
703
+ const cos = Math.cos;
747
704
 
748
- let pitch;
749
- // pitch (y-axis rotation)
750
- const sinp = +2.0 * (w * y - z * x);
751
- if (Math.abs(sinp) >= 1) {
752
- pitch = copysign(Math.PI / 2, sinp); // use 90 degrees if out of range
753
- } else {
754
- pitch = Math.asin(sinp);
755
- }
705
+ const s1 = sin(half_x);
706
+ const s2 = sin(half_y);
707
+ const s3 = sin(half_z);
756
708
 
757
- // yaw (z-axis rotation)
758
- const siny_cosp = +2.0 * (w * z + x * y);
759
- const cosy_cosp = +1.0 - 2.0 * (yy + zz);
760
- const yaw = Math.atan2(siny_cosp, cosy_cosp);
709
+ const c1 = cos(half_x);
710
+ const c2 = cos(half_y);
711
+ const c3 = cos(half_z);
712
+
713
+ const _x = s1 * c2 * c3 + c1 * s2 * s3;
714
+ const _y = c1 * s2 * c3 - s1 * c2 * s3;
715
+ const _z = c1 * c2 * s3 + s1 * s2 * c3;
716
+ const _w = c1 * c2 * c3 - s1 * s2 * s3;
761
717
 
762
- result.set(roll, pitch, yaw);
718
+ this.set(_x, _y, _z, _w);
763
719
  }
764
720
 
765
721
  /**
766
722
  * XYZ order
767
723
  * @source: https://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
724
+ * @see http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
725
+ * @see https://github.com/mrdoob/three.js/blob/510705cde208b165fd87946b0f8504a1cd6dbe83/src/math/Quaternion.js#L206
768
726
  * @param {number} x angle in X axis in radians
769
727
  * @param {number} y angle in Y axis in radians
770
728
  * @param {number} z angle in Z axis in radians
771
729
  */
772
- fromEulerAngles(x, y, z) {
730
+ fromEulerAnglesYXZ(x, y, z) {
773
731
  const scaled_x = x * 0.5;
774
732
  const scaled_y = y * 0.5;
775
733
  const scaled_z = z * 0.5;
776
734
 
777
- const sx = Math.sin(scaled_x);
778
- const cx = Math.cos(scaled_x);
779
- const sy = Math.sin(scaled_y);
780
- const cy = Math.cos(scaled_y);
781
- const sz = Math.sin(scaled_z);
782
- const cz = Math.cos(scaled_z);
735
+ const s1 = Math.sin(scaled_x);
736
+ const s2 = Math.sin(scaled_y);
737
+ const s3 = Math.sin(scaled_z);
738
+
739
+ const c1 = Math.cos(scaled_x);
740
+ const c2 = Math.cos(scaled_y);
741
+ const c3 = Math.cos(scaled_z);
783
742
 
784
- const _x = sx * cy * cz - cx * sy * sz;
785
- const _y = cx * sy * cz + sx * cy * sz;
786
- const _z = cx * cy * sz - sx * sy * cz;
787
- const _w = cx * cy * cz + sx * sy * sz;
743
+ const _x = s1 * c2 * c3 + c1 * s2 * s3;
744
+ const _y = c1 * s2 * c3 - s1 * c2 * s3;
745
+ const _z = c1 * c2 * s3 - s1 * s2 * c3;
746
+ const _w = c1 * c2 * c3 + s1 * s2 * s3;
788
747
 
789
748
  this.set(_x, _y, _z, _w);
790
749
  }
@@ -798,7 +757,7 @@ class Quaternion {
798
757
  * @param {number} y angle in Y axis in radians
799
758
  * @param {number} z angle in Z axis in radians
800
759
  */
801
- fromEulerAnglesXYZ(x, y, z) {
760
+ fromEulerAnglesZXY(x, y, z) {
802
761
  const scaled_x = x * 0.5;
803
762
  const scaled_y = y * 0.5;
804
763
  const scaled_z = z * 0.5;
@@ -811,8 +770,8 @@ class Quaternion {
811
770
  const c2 = Math.cos(scaled_y);
812
771
  const c3 = Math.cos(scaled_z);
813
772
 
814
- const _x = s1 * c2 * c3 + c1 * s2 * s3;
815
- const _y = c1 * s2 * c3 - s1 * c2 * s3;
773
+ const _x = s1 * c2 * c3 - c1 * s2 * s3;
774
+ const _y = c1 * s2 * c3 + s1 * c2 * s3;
816
775
  const _z = c1 * c2 * s3 + s1 * s2 * c3;
817
776
  const _w = c1 * c2 * c3 - s1 * s2 * s3;
818
777
 
@@ -820,48 +779,93 @@ class Quaternion {
820
779
  }
821
780
 
822
781
  /**
823
- * Writes Pitch(Y), Yaw(Z), Roll(X) to a given vector
824
- * @source http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
825
- * @param {Vector3} result
782
+ * XYZ order
783
+ * @source: https://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
784
+ * @see http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
785
+ * @see https://github.com/mrdoob/three.js/blob/510705cde208b165fd87946b0f8504a1cd6dbe83/src/math/Quaternion.js#L206
786
+ * @param {number} x angle in X axis in radians
787
+ * @param {number} y angle in Y axis in radians
788
+ * @param {number} z angle in Z axis in radians
826
789
  */
827
- toEulerAngles(result) {
828
- const x = this.x;
829
- const y = this.y;
830
- const z = this.z;
831
- const w = this.w;
790
+ fromEulerAnglesZYX(x, y, z) {
791
+ const scaled_x = x * 0.5;
792
+ const scaled_y = y * 0.5;
793
+ const scaled_z = z * 0.5;
832
794
 
833
- const sqw = w * w;
834
- const sqx = x * x;
835
- const sqy = y * y;
836
- const sqz = z * z;
795
+ const s1 = Math.sin(scaled_x);
796
+ const s2 = Math.sin(scaled_y);
797
+ const s3 = Math.sin(scaled_z);
837
798
 
838
- const unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
799
+ const c1 = Math.cos(scaled_x);
800
+ const c2 = Math.cos(scaled_y);
801
+ const c3 = Math.cos(scaled_z);
839
802
 
840
- const test = x * w - y * z;
803
+ const _x = s1 * c2 * c3 - c1 * s2 * s3;
804
+ const _y = c1 * s2 * c3 + s1 * c2 * s3;
805
+ const _z = c1 * c2 * s3 - s1 * s2 * c3;
806
+ const _w = c1 * c2 * c3 + s1 * s2 * s3;
841
807
 
808
+ this.set(_x, _y, _z, _w);
809
+ }
842
810
 
843
- let yaw, pitch, roll;
811
+ /**
812
+ * XYZ order
813
+ * @source: https://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
814
+ * @see http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
815
+ * @see https://github.com/mrdoob/three.js/blob/510705cde208b165fd87946b0f8504a1cd6dbe83/src/math/Quaternion.js#L206
816
+ * @param {number} x angle in X axis in radians
817
+ * @param {number} y angle in Y axis in radians
818
+ * @param {number} z angle in Z axis in radians
819
+ */
820
+ fromEulerAnglesYZX(x, y, z) {
821
+ const scaled_x = x * 0.5;
822
+ const scaled_y = y * 0.5;
823
+ const scaled_z = z * 0.5;
844
824
 
845
- if (test > 0.4995 * unit) { // singularity at north pole
846
- yaw = 2 * Math.atan2(y, x);
847
- pitch = PI_HALF;
848
- roll = 0;
849
- } else if (test < -0.4995 * unit) { // singularity at south pole
850
- yaw = -2 * Math.atan2(y, x);
851
- pitch = -PI_HALF;
852
- roll = 0;
853
- } else {
854
- yaw = Math.atan2(2 * x * w + 2 * y * z, 1 - 2 * (sqz + sqw));
855
- pitch = Math.asin(2 * (x * z - w * y));
856
- roll = Math.atan2(2 * x * y - 2 * z * w, 1 - 2 * (sqy + sqz))
857
- }
825
+ const s1 = Math.sin(scaled_x);
826
+ const s2 = Math.sin(scaled_y);
827
+ const s3 = Math.sin(scaled_z);
828
+
829
+ const c1 = Math.cos(scaled_x);
830
+ const c2 = Math.cos(scaled_y);
831
+ const c3 = Math.cos(scaled_z);
832
+
833
+ const _x = s1 * c2 * c3 + c1 * s2 * s3;
834
+ const _y = c1 * s2 * c3 + s1 * c2 * s3;
835
+ const _z = c1 * c2 * s3 - s1 * s2 * c3;
836
+ const _w = c1 * c2 * c3 - s1 * s2 * s3;
837
+
838
+ this.set(_x, _y, _z, _w);
839
+ }
840
+
841
+ /**
842
+ * XYZ order
843
+ * @source: https://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
844
+ * @see http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
845
+ * @see https://github.com/mrdoob/three.js/blob/510705cde208b165fd87946b0f8504a1cd6dbe83/src/math/Quaternion.js#L206
846
+ * @param {number} x angle in X axis in radians
847
+ * @param {number} y angle in Y axis in radians
848
+ * @param {number} z angle in Z axis in radians
849
+ */
850
+ fromEulerAnglesXZY(x, y, z) {
851
+ const scaled_x = x * 0.5;
852
+ const scaled_y = y * 0.5;
853
+ const scaled_z = z * 0.5;
854
+
855
+ const s1 = Math.sin(scaled_x);
856
+ const s2 = Math.sin(scaled_y);
857
+ const s3 = Math.sin(scaled_z);
858
+
859
+ const c1 = Math.cos(scaled_x);
860
+ const c2 = Math.cos(scaled_y);
861
+ const c3 = Math.cos(scaled_z);
858
862
 
859
- //normalize angles
860
- yaw = normalizeAngle(yaw);
861
- pitch = normalizeAngle(pitch);
862
- roll = normalizeAngle(roll);
863
+ const _x = s1 * c2 * c3 - c1 * s2 * s3;
864
+ const _y = c1 * s2 * c3 - s1 * c2 * s3;
865
+ const _z = c1 * c2 * s3 + s1 * s2 * c3;
866
+ const _w = c1 * c2 * c3 + s1 * s2 * s3;
863
867
 
864
- result.set(pitch, yaw, roll);
868
+ this.set(_x, _y, _z, _w);
865
869
  }
866
870
 
867
871
  /**
@@ -1204,6 +1208,17 @@ class Quaternion {
1204
1208
  this.set(_x, _y, _z, _w);
1205
1209
  }
1206
1210
 
1211
+ /**
1212
+ * @see https://github.com/gareth-cross/quat/blob/master/include/quaternion.hpp
1213
+ * TODO implement
1214
+ * @param {Quaternion} a
1215
+ * @param {Quaternion} b
1216
+ * @param {number} t
1217
+ */
1218
+ quaternionsInterpolateRungeKutta4(a, b, t) {
1219
+ throw new Error('Not Implemented');
1220
+ }
1221
+
1207
1222
  /**
1208
1223
  *
1209
1224
  * @param {function} handler
@@ -162,8 +162,8 @@ test('to/from euler YXZ consistency', () => {
162
162
 
163
163
  const u = new Vector3();
164
164
 
165
- r.__setFromEuler(x, y, z, 'YXZ');
166
- r.toEulerAnglesYXZ(u);
165
+ r.fromEulerAnglesXYZ(x, y, z);
166
+ r.toEulerAnglesXYZ(u);
167
167
 
168
168
  expect(u.x).toBeCloseTo(x);
169
169
  expect(u.y).toBeCloseTo(y);
@@ -177,4 +177,49 @@ test('to/from euler YXZ consistency', () => {
177
177
  check(1, 0, 1);
178
178
  check(1, 1, 0);
179
179
  check(1, 1, 1);
180
+
181
+ check(0, 0, -1);
182
+ check(0, -1, 0);
183
+ check(0, -1, -1);
184
+ check(-1, 0, 0);
185
+ check(-1, 0, -1);
186
+ check(-1, -1, 0);
187
+ check(-1, -1, -1);
188
+ });
189
+
190
+
191
+ /**
192
+ * See https://www.andre-gaschler.com/rotationconverter/
193
+ */
194
+ test('toEulerAnglesXYZ', () => {
195
+
196
+ function check(quat, euler) {
197
+
198
+ const q = new Quaternion(...quat);
199
+
200
+ const r = new Vector3();
201
+ q.toEulerAnglesXYZ(r)
202
+
203
+ expect(r.x).toBeCloseTo(euler[0]);
204
+ expect(r.y).toBeCloseTo(euler[1]);
205
+ expect(r.z).toBeCloseTo(euler[2]);
206
+ }
207
+
208
+
209
+ check([0.4794255, 0, 0, 0.8775826], [1, 0, 0]);
210
+ check([0, 0.4794255, 0, 0.8775826], [0, 1, 0]);
211
+ check([0, 0, 0.4794255, 0.8775826], [0, 0, 1]);
212
+
213
+ check([0.4207355, 0.4207355, 0.2298488, 0.7701512], [1, 1, 0]);
214
+ check([0.2298488, 0.4207355, 0.4207355, 0.7701512], [0, 1, 1]);
215
+ check([0.4207355, -0.2298488, 0.4207355, 0.7701512], [1, 0, 1]);
216
+
217
+ check([0.5709415, 0.1675188, 0.5709415, 0.5656758], [1, 1, 1]);
218
+
219
+
220
+ check([-0.4794255, 0, 0, 0.8775826], [-1, 0, 0]);
221
+ check([0, -0.4794255, 0, 0.8775826], [0, -1, 0]);
222
+ check([0, 0, -0.4794255, 0.8775826], [0, 0, -1]);
223
+
224
+
180
225
  });