@woosh/meep-engine 2.49.2 → 2.49.4

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 (60) hide show
  1. package/package.json +1 -1
  2. package/src/core/bvh2/binary/IndexedBinaryBVH.spec.js +20 -0
  3. package/src/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.d.ts +4 -0
  4. package/src/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.spec.js +30 -0
  5. package/src/core/bvh2/bvh3/ebvh_build_for_geometry_incremental.js +6 -1
  6. package/src/core/bvh2/bvh3/query/bvh_collect_user_data.js +5 -1
  7. package/src/core/cache/Cache.d.ts +2 -0
  8. package/src/core/cache/Cache.js +1 -1
  9. package/src/core/cache/Cache.spec.js +19 -0
  10. package/src/core/cache/LoadingCache.d.ts +18 -0
  11. package/src/core/cache/LoadingCache.js +115 -0
  12. package/src/core/codegen/LineBuilder.js +8 -1
  13. package/src/core/codegen/LineBuilder.spec.js +34 -0
  14. package/src/core/collection/array/arrayQuickSort.js +11 -2
  15. package/src/core/collection/array/array_contains_duplicates.js +6 -1
  16. package/src/core/collection/array/typed/is_typed_array_equals.js +13 -4
  17. package/src/core/collection/array/typed/is_typed_array_equals.spec.js +71 -0
  18. package/src/core/collection/table/RowFirstTable.js +1 -1
  19. package/src/core/collection/table/RowFirstTable.spec.js +36 -0
  20. package/src/core/collection/table/RowFirstTableSpec.js +3 -0
  21. package/src/core/color/hsv2rgb.js +8 -3
  22. package/src/core/color/hsv2rgb.spec.js +43 -0
  23. package/src/core/geom/3d/aabb/aabb3_array_intersects_ray_array.js +2 -1
  24. package/src/core/geom/3d/compute_triangle_normal.js +1 -51
  25. package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +2 -2
  26. package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.spec.js +27 -0
  27. package/src/core/geom/3d/triangle/computeTriangleRayIntersection.spec.js +25 -0
  28. package/src/core/geom/3d/v3_compute_triangle_normal.js +51 -0
  29. package/src/core/geom/3d/v3_compute_triangle_normal.spec.js +27 -0
  30. package/src/core/geom/packing/miniball/Miniball.spec.js +11 -0
  31. package/src/core/graph/SquareMatrix.js +1 -9
  32. package/src/core/graph/graph_k_means_cluster.spec.js +23 -0
  33. package/src/core/{land → lang}/reactive/compileReactiveExpression.spec.js +2 -2
  34. package/src/core/model/reactive/trigger/ReactiveTrigger.js +1 -1
  35. package/src/engine/ecs/dynamic_actions/actions/definition/SendRequestActionDescription.js +1 -1
  36. package/src/engine/ecs/dynamic_actions/actions/definition/WhiteToBlackboardActionDescription.js +1 -1
  37. package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.js +1 -1
  38. package/src/engine/ecs/fow/FogOfWarRevealer.js +0 -27
  39. package/src/engine/ecs/fow/serialization/FogOfWarRevealerSerializationAdapter.js +29 -0
  40. package/src/engine/ecs/transform/TransformSerializationAdapter.spec.js +28 -0
  41. package/src/engine/knowledge/database/StaticKnowledgeDataTable.spec.js +5 -0
  42. package/src/engine/options/Option.js +36 -14
  43. package/src/engine/options/Option.spec.js +69 -0
  44. package/src/engine/reference/v2/Reference.d.ts +2 -0
  45. package/src/engine/reference/v2/Reference.js +3 -0
  46. package/src/engine/reference/v2/Reference.spec.js +44 -0
  47. package/src/core/bvh2/bvh3/BVHNodeProxy.js +0 -53
  48. package/src/core/graph/GraphElement.js +0 -6
  49. package/src/core/graph/Node.js +0 -12
  50. package/src/generation/GridGeneratorConfigurator.js +0 -0
  51. /package/src/core/{land → lang}/reactive/AbstractCachingParser.js +0 -0
  52. /package/src/core/{land → lang}/reactive/ReactiveOperatorType.js +0 -0
  53. /package/src/core/{land → lang}/reactive/compileReactiveExpression.js +0 -0
  54. /package/src/core/{land → lang}/reactive/nearley/Reactive.ne +0 -0
  55. /package/src/core/{land → lang}/reactive/pegjs/Reactive.peg +0 -0
  56. /package/src/core/{land → lang}/reactive/pegjs/ReactivePegCompiler.js +0 -0
  57. /package/src/core/{land → lang}/reactive/pegjs/ReactivePegParser.js +0 -0
  58. /package/src/core/{land → lang}/reactive/pegjs/parser.js +0 -0
  59. /package/src/core/{land → lang}/reactive/validateReactiveExpression.js +0 -0
  60. /package/src/generation/rules/{CellMatcherContainsTag.spec.js → CellMatcherLayerBitMaskTest.spec.js} +0 -0
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "description": "Fully featured ECS game engine written in JavaScript",
6
6
  "type": "module",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.49.2",
8
+ "version": "2.49.4",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -5,3 +5,23 @@ test("constructor does not throw", () => {
5
5
  });
6
6
 
7
7
 
8
+ test("build a tree with a single leaf", () => {
9
+ const bvh = new IndexedBinaryBVH();
10
+
11
+ bvh.initialize(1);
12
+ bvh.writeLeaf(0, -1, -3, -7, 11, 13, 17);
13
+
14
+ bvh.unsortedBuiltIntermediate();
15
+
16
+ expect(bvh.leafNodeCount).toBe(1);
17
+ expect(bvh.binaryNodeCount).toBe(0);
18
+
19
+ expect(bvh.x0).toBe(-1);
20
+ expect(bvh.y0).toBe(-3);
21
+ expect(bvh.z0).toBe(-7);
22
+
23
+ expect(bvh.x1).toBe(11);
24
+ expect(bvh.y1).toBe(13);
25
+ expect(bvh.z1).toBe(17);
26
+
27
+ });
@@ -13,6 +13,8 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
13
13
 
14
14
  collect_nodes_all(destination: ArrayLike<number>, destination_offset: number): void
15
15
 
16
+ node_set_aabb_primitive(node_id: number, x0: number, y0: number, z0: number, x1: number, y1: number, z1: number): void
17
+
16
18
  node_set_aabb(node: number, aabb: ArrayLike<number>): void
17
19
 
18
20
  node_get_aabb(node: number, aabb: ArrayLike<number>): void
@@ -37,6 +39,8 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
37
39
 
38
40
  node_get_height(node: number): number
39
41
 
42
+ swap_nodes(node_a_id: number, node_b_id: number): boolean
43
+
40
44
  release_all(): void
41
45
 
42
46
  trim(): void
@@ -218,3 +218,33 @@ test("node property setters/getters", () => {
218
218
 
219
219
  expect(actual_aabb).toEqual(Array.from(expected_aabb));
220
220
  });
221
+
222
+
223
+ test("node_set_aabb_primitive", () => {
224
+ const bvh = new ExplicitBinaryBoundingVolumeHierarchy();
225
+
226
+ const node = bvh.allocate_node();
227
+
228
+ bvh.node_set_aabb_primitive(node, -1, -3, -5, 7, 11, 13);
229
+
230
+ const aabb = [];
231
+
232
+ bvh.node_get_aabb(node, aabb);
233
+
234
+ expect(aabb).toEqual([-1, -3, -5, 7, 11, 13]);
235
+ });
236
+
237
+ test("swap two detached nodes", () => {
238
+ const bvh = new ExplicitBinaryBoundingVolumeHierarchy();
239
+
240
+ const a = bvh.allocate_node();
241
+ const b = bvh.allocate_node();
242
+
243
+ bvh.node_set_user_data(a, 7);
244
+ bvh.node_set_user_data(b, 11);
245
+
246
+ bvh.swap_nodes(a, b);
247
+
248
+ expect(bvh.node_get_user_data(a)).toBe(11);
249
+ expect(bvh.node_get_user_data(b)).toBe(7);
250
+ });
@@ -8,7 +8,12 @@ import { compute_triangle_group_aabb3 } from "../../../engine/graphics/sh3/path_
8
8
  * @param {number[]|Float32Array} positions_array
9
9
  * @param {number} [batch_size] can batch triangles in groups of up-to this many triangles per BVH leaf
10
10
  */
11
- export function ebvh_build_for_geometry_incremental(bvh, index_array, positions_array, batch_size = 1) {
11
+ export function ebvh_build_for_geometry_incremental(
12
+ bvh,
13
+ index_array, positions_array,
14
+ batch_size = 1
15
+ ) {
16
+
12
17
  bvh.release_all();
13
18
  const triangle_count = index_array.length / 3;
14
19
 
@@ -11,8 +11,12 @@ const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
11
11
  * @param {number} destination_offset
12
12
  * @param {ExplicitBinaryBoundingVolumeHierarchy} bvh
13
13
  * @param {number} node
14
+ * @returns {number} number of collected elements
14
15
  */
15
- export function bvh_collect_user_data(destination, destination_offset, bvh, node) {
16
+ export function bvh_collect_user_data(
17
+ destination, destination_offset,
18
+ bvh, node
19
+ ) {
16
20
 
17
21
  if (node === NULL_NODE) {
18
22
  return 0;
@@ -33,6 +33,8 @@ export class Cache<K, V> {
33
33
 
34
34
  remove(k: K): boolean
35
35
 
36
+ evictOne(): boolean
37
+
36
38
  clear(): void
37
39
 
38
40
  drop(): void
@@ -18,7 +18,7 @@ export class Cache {
18
18
  * @param {function(value:Value):number} [valueWeigher= value=>1]
19
19
  * @param {function(Key):number} [keyHashFunction]
20
20
  * @param {function(Key, Key):boolean} [keyEqualityFunction]
21
- * @param {number} capacity initial capacity for the cache, helps prevent early resizing
21
+ * @param {number} [capacity] initial capacity for the cache, helps prevent early resizing
22
22
  * @constructor
23
23
  */
24
24
  constructor({
@@ -18,6 +18,19 @@ test("elements beyond capacity are evicted", () => {
18
18
 
19
19
  });
20
20
 
21
+ test("trying to insert an element into the cache that's larger than the cache capacity should result in the item not being inserted", () => {
22
+ const cache = new Cache({
23
+ maxWeight: 1,
24
+ keyWeigher(key) {
25
+ return key;
26
+ }
27
+ });
28
+
29
+ cache.put(2, "hello");
30
+
31
+ expect(cache.contains(2)).toBe(false);
32
+ });
33
+
21
34
  test("removeListener is called when element is evicted", () => {
22
35
 
23
36
  const removeListener = jest.fn();
@@ -140,3 +153,9 @@ test("clear with a single entry", () => {
140
153
  expect(cache.contains("x")).toBe(false);
141
154
 
142
155
  });
156
+
157
+ test("calling 'evictOne' on empty cache should return false", () => {
158
+ const cache = new Cache();
159
+
160
+ expect(cache.evictOne()).toBe(false);
161
+ });
@@ -0,0 +1,18 @@
1
+ export declare class LoadingCache<K,V>{
2
+ constructor(options:{
3
+ maxWeight?: number,
4
+ keyWeigher?: (key: K) => number,
5
+ valueWeigher?: (value: V) => number,
6
+ keyHashFunction?: (key: K) => number,
7
+ keyEqualityFunction?: (a: K, b: K) => boolean,
8
+ capacity?: number,
9
+ timeToLive?:number,
10
+ load:(key:K)=> Promise<V>
11
+ })
12
+
13
+ invalidate(key:K):void
14
+
15
+ clear():void
16
+
17
+ get(key:K):Promise<V>
18
+ }
@@ -0,0 +1,115 @@
1
+ //
2
+
3
+ import { Cache } from "./Cache.js";
4
+
5
+ /**
6
+ * @template {V}
7
+ */
8
+ class Record {
9
+ /**
10
+ *
11
+ * @param {V} value
12
+ * @param {number} time
13
+ */
14
+ constructor(value, time) {
15
+ this.value = value;
16
+ this.time = time;
17
+ }
18
+ }
19
+
20
+ const DEFAULT_TIME_TO_LIVE = 10;
21
+
22
+ /**
23
+ * @template K,V
24
+ */
25
+ export class LoadingCache {
26
+ /**
27
+ * @type {Cache<K,Record<Promise<V>>>}
28
+ */
29
+ #internal
30
+ /**
31
+ * In seconds
32
+ * @type {number}
33
+ */
34
+ #timeToLive = DEFAULT_TIME_TO_LIVE;
35
+
36
+ /**
37
+ * @type {function(key:K):Promise<V>}
38
+ */
39
+ #load
40
+
41
+ /**
42
+ * @see {@link Cache} for more details on what each parameter means
43
+ * @param maxWeight
44
+ * @param keyWeigher
45
+ * @param valueWeigher
46
+ * @param keyHashFunction
47
+ * @param keyEqualityFunction
48
+ * @param capacity
49
+ * @param {number} [timeToLive] in seconds
50
+ * @param load
51
+ */
52
+ constructor({
53
+ maxWeight,
54
+ keyWeigher,
55
+ valueWeigher,
56
+ keyHashFunction,
57
+ keyEqualityFunction,
58
+ capacity,
59
+ timeToLive = DEFAULT_TIME_TO_LIVE,
60
+ load
61
+ }) {
62
+
63
+ this.#internal = new Cache({
64
+ maxWeight,
65
+ keyWeigher,
66
+ valueWeigher,
67
+ keyHashFunction,
68
+ keyEqualityFunction,
69
+ capacity,
70
+ });
71
+
72
+ this.#timeToLive = timeToLive;
73
+ this.#load = load;
74
+ }
75
+
76
+ /**
77
+ *
78
+ * @param {K} key
79
+ */
80
+ invalidate(key) {
81
+ this.#internal.remove(key);
82
+ }
83
+
84
+ /**
85
+ *
86
+ */
87
+ clear() {
88
+ this.#internal.clear();
89
+ }
90
+
91
+ /**
92
+ *
93
+ * @param {K} key
94
+ * @return {Promise<V>}
95
+ */
96
+ async get(key) {
97
+ const currentTime = performance.now() * 1e-3;
98
+
99
+ let record = this.#internal.get(key);
100
+
101
+ if (record === null || (record.time + this.#timeToLive) < currentTime) {
102
+
103
+ // missing or stale record
104
+ const promise = this.#load(key);
105
+
106
+ record = new Record(promise, currentTime);
107
+
108
+ this.#internal.put(key, record);
109
+
110
+ }
111
+
112
+
113
+ return record.value;
114
+ }
115
+ }
@@ -39,6 +39,14 @@ class LineBuilder {
39
39
  */
40
40
  indentSpaces = DEFAULT_INDENT_SPACES;
41
41
 
42
+ /**
43
+ * Number of lines
44
+ * @return {number}
45
+ */
46
+ get count() {
47
+ return this.lines.length;
48
+ }
49
+
42
50
  /**
43
51
  *
44
52
  * @return {boolean}
@@ -115,7 +123,6 @@ class LineBuilder {
115
123
  clear() {
116
124
  this.lines = [];
117
125
  this.indentation = 0;
118
- this.indentSpaces = DEFAULT_INDENT_SPACES;
119
126
  }
120
127
 
121
128
  /**
@@ -5,3 +5,37 @@ test("Empty builder should produce empty string", () => {
5
5
 
6
6
  expect(b.build()).toEqual("");
7
7
  });
8
+
9
+ test("build simple indented fragment", () => {
10
+ const b = new LineBuilder();
11
+
12
+ b.indentSpaces = 1;
13
+
14
+ b.add('a');
15
+ b.indent();
16
+ b.add('b');
17
+ b.dedent();
18
+ b.add('c');
19
+
20
+ expect(b.build()).toEqual('a\n b\nc');
21
+ });
22
+
23
+ test("adding another line builder copies lines correctly", () => {
24
+ const a = new LineBuilder();
25
+
26
+ const b = new LineBuilder();
27
+
28
+ b.add('a');
29
+
30
+ a.addLines(b);
31
+
32
+ expect(a.build()).toEqual('a');
33
+ });
34
+
35
+ test("fromText parses correct number of lines", () => {
36
+
37
+ const builder = LineBuilder.fromText('a\nb');
38
+
39
+ expect(builder.count).toBe(2);
40
+
41
+ });
@@ -12,7 +12,12 @@ const stack = [];
12
12
  * @param {function(T[],number, number):*} [swap_operator]
13
13
  * @param {*} [swap_context]
14
14
  */
15
- export function arrayQuickSort(data, score_function, score_function_context, start, end, swap_operator = arraySwapElements, swap_context = undefined) {
15
+ export function arrayQuickSort(
16
+ data,
17
+ score_function, score_function_context,
18
+ start, end,
19
+ swap_operator = arraySwapElements, swap_context = undefined
20
+ ) {
16
21
  if (start >= end) {
17
22
  // section of 0 size, nothing to sort
18
23
  return;
@@ -79,7 +84,11 @@ export function arrayQuickSort(data, score_function, score_function_context, sta
79
84
  * @param {number} start
80
85
  * @param {number} end
81
86
  */
82
- export function array_quick_sort_by_comparator(data, compare_function, compare_function_context, start, end) {
87
+ export function array_quick_sort_by_comparator(
88
+ data,
89
+ compare_function, compare_function_context,
90
+ start, end
91
+ ) {
83
92
  if (start >= end) {
84
93
  // section of 0 size, nothing to sort
85
94
  return;
@@ -5,7 +5,12 @@
5
5
  * @param {number} [end_index] last index to be included in the search
6
6
  * @return {boolean}
7
7
  */
8
- export function array_contains_duplicates(source, start_index = 0, end_index = source.length - 1) {
8
+ export function array_contains_duplicates(
9
+ source,
10
+ start_index = 0,
11
+ end_index = source.length - 1
12
+ ) {
13
+
9
14
  for (let k = start_index; k <= end_index; k++) {
10
15
 
11
16
  const element = source[k];
@@ -3,11 +3,16 @@ import { isArrayEqualStrict } from "../isArrayEqualStrict.js";
3
3
 
4
4
  /**
5
5
  *
6
- * @param {Uint8Array} a
7
- * @param {Uint8Array} b
6
+ * @param {Uint8Array|Uint32Array} a
7
+ * @param {Uint8Array|Uint32Array} b
8
8
  * @returns {boolean}
9
9
  */
10
10
  export function is_typed_array_equals(a, b) {
11
+ if (a === b) {
12
+ // quick shortcut
13
+ return true;
14
+ }
15
+
11
16
  const a_length = a.length;
12
17
  const b_length = b.length;
13
18
 
@@ -26,13 +31,15 @@ export function is_typed_array_equals(a, b) {
26
31
  return false;
27
32
  }
28
33
 
29
- const bytes_per_element = a.constructor.BYTES_PER_ELEMENT;
34
+ const a_constructor = a.constructor;
35
+ const b_constructor = b.constructor;
30
36
 
31
- if (bytes_per_element !== b.constructor.BYTES_PER_ELEMENT) {
37
+ if (a_constructor !== b_constructor) {
32
38
  // incompatible constructors
33
39
  return false;
34
40
  }
35
41
 
42
+
36
43
  const a_buffer = a.buffer;
37
44
  const b_buffer = b.buffer;
38
45
 
@@ -45,6 +52,8 @@ export function is_typed_array_equals(a, b) {
45
52
  let a_proxy = a;
46
53
  let b_proxy = b;
47
54
 
55
+ const bytes_per_element = a_constructor.BYTES_PER_ELEMENT;
56
+
48
57
  if (bytes_per_element < 4 && byte_length % 4 === 0) {
49
58
  // 4 byte alignment, can use uint32
50
59
  a_proxy = new Uint32Array(a_buffer, a.byteOffset, byte_length / 4);
@@ -0,0 +1,71 @@
1
+ import { is_typed_array_equals } from "./is_typed_array_equals.js";
2
+
3
+ test("small uint32 arrays", () => {
4
+ expect(is_typed_array_equals(
5
+ new Uint32Array(0),
6
+ new Uint32Array(0)
7
+ )).toBe(true);
8
+
9
+ expect(is_typed_array_equals(
10
+ new Uint32Array([1, 3, 5]),
11
+ new Uint32Array([1, 3, 5])
12
+ )).toBe(true);
13
+
14
+ expect(is_typed_array_equals(
15
+ new Uint32Array([7, 3]),
16
+ new Uint32Array([1, 3])
17
+ )).toBe(false);
18
+
19
+ expect(is_typed_array_equals(
20
+ new Uint32Array([1, 3]),
21
+ new Uint32Array([7, 3])
22
+ )).toBe(false);
23
+
24
+ expect(is_typed_array_equals(
25
+ new Uint32Array([1, 7]),
26
+ new Uint32Array([1, 3])
27
+ )).toBe(false);
28
+
29
+ expect(is_typed_array_equals(
30
+ new Uint32Array([1, 3]),
31
+ new Uint32Array([1, 7])
32
+ )).toBe(false);
33
+
34
+ expect(is_typed_array_equals(
35
+ new Uint32Array([1, 3]),
36
+ new Uint32Array([1, 3, 7])
37
+ )).toBe(false);
38
+
39
+ expect(is_typed_array_equals(
40
+ new Uint32Array([1, 3, 7]),
41
+ new Uint32Array([1, 3])
42
+ )).toBe(false);
43
+
44
+ });
45
+
46
+ test("large uin32 array", () => {
47
+ expect(is_typed_array_equals(
48
+ new Uint32Array(1024),
49
+ new Uint32Array(1024),
50
+ )).toBe(true);
51
+
52
+ expect(is_typed_array_equals(
53
+ new Uint32Array(1024),
54
+ new Uint32Array(1023),
55
+ )).toBe(false);
56
+
57
+ expect(is_typed_array_equals(
58
+ new Uint32Array(1023),
59
+ new Uint32Array(1024),
60
+ )).toBe(false);
61
+
62
+ const a = new Uint32Array(1024);
63
+ const b = new Uint32Array(1024);
64
+
65
+ a[127] = 1;
66
+
67
+ expect(is_typed_array_equals(
68
+ a,
69
+ b,
70
+ )).toBe(false);
71
+ });
@@ -98,7 +98,7 @@ RowFirstTable.prototype.hash = function () {
98
98
 
99
99
  const byteLength = this.data.byteLength;
100
100
 
101
- const tap_count = 32;
101
+ const tap_count = 31;
102
102
 
103
103
  const step_size = max2(
104
104
  Math.floor(byteLength / (tap_count * 4)),
@@ -285,3 +285,39 @@ test('reverse_rows empty', () => {
285
285
 
286
286
  expect(a.length).toBe(0);
287
287
  });
288
+
289
+ test("hash on an empty ", () => {
290
+ const table = new RowFirstTable(new RowFirstTableSpec([BinaryDataType.Uint8]));
291
+
292
+ expect(() => table.hash()).not.toThrow();
293
+ expect(table.hash()).toEqual(table.hash());
294
+ expect(typeof table.hash() === "number").toBe(true);
295
+ expect(Number.isInteger(table.hash())).toBe(true);
296
+ });
297
+
298
+ test("equals method", () => {
299
+ const a = new RowFirstTable(new RowFirstTableSpec([BinaryDataType.Uint8]));
300
+ const b = new RowFirstTable(new RowFirstTableSpec([BinaryDataType.Uint8]));
301
+
302
+ expect(a.equals(b)).toBe(true);
303
+
304
+ a.addRow([1]);
305
+
306
+ expect(a.equals(b)).toBe(false);
307
+ expect(b.equals(a)).toBe(false);
308
+
309
+ b.addRow([3]);
310
+
311
+ expect(a.equals(b)).toBe(false);
312
+ });
313
+
314
+ test("toRowArray", () => {
315
+ const a = new RowFirstTable(new RowFirstTableSpec([BinaryDataType.Uint8, BinaryDataType.Float64]));
316
+ a.addRow([3, 7.11]);
317
+ a.addRow([5, 13.17]);
318
+
319
+ expect(a.toRowArray()).toEqual([
320
+ [3, 7.11],
321
+ [5, 13.17],
322
+ ]);
323
+ });
@@ -9,6 +9,7 @@ import { computeStringHash } from "../../primitives/strings/computeStringHash.js
9
9
  import { isArrayEqualStrict } from "../array/isArrayEqualStrict.js";
10
10
  import { BinaryDataType } from "../../binary/type/BinaryDataType.js";
11
11
  import { DataTypeByteSizes } from "../../binary/type/DataTypeByteSizes.js";
12
+ import { assert } from "../../assert.js";
12
13
 
13
14
  /**
14
15
  * @readonly
@@ -158,6 +159,8 @@ function genCellReader(type, offset, endianType = EndianType.BigEndian) {
158
159
  * @constructor
159
160
  */
160
161
  export function RowFirstTableSpec(types, endianType = EndianType.BigEndian) {
162
+ assert.isArray(types, 'types');
163
+
161
164
  const numTypes = types.length;
162
165
 
163
166
  /**
@@ -10,22 +10,27 @@ import { float2uint8 } from "../binary/float2uint8.js";
10
10
  */
11
11
  export function hsv2rgb(h, s, v) {
12
12
 
13
- let _h = h % 1;
13
+ let _h = h;
14
14
 
15
15
  if (_h < 0) {
16
- _h = h + 1;
16
+ _h = _h + Math.ceil(Math.abs(_h));
17
17
  }
18
18
 
19
+ _h = _h % 1;
20
+
19
21
  const _s = clamp01(s);
20
22
  const _v = clamp01(v);
21
23
 
22
24
  let r, g, b, i, f, p, q, t;
23
25
 
24
- i = Math.floor(h * 6);
26
+ i = Math.floor(_h * 6);
27
+
25
28
  f = _h * 6 - i;
29
+
26
30
  p = _v * (1 - _s);
27
31
  q = _v * (1 - f * _s);
28
32
  t = _v * (1 - (1 - f) * _s);
33
+
29
34
  switch (i % 6) {
30
35
  case 0:
31
36
  r = _v, g = t, b = p;
@@ -0,0 +1,43 @@
1
+ import { hsv2rgb } from "./hsv2rgb.js";
2
+
3
+ function validate_element(number) {
4
+ expect(typeof number).toBe('number');
5
+ expect(Number.isNaN(number)).toBe(false);
6
+
7
+ expect(number).toBeLessThanOrEqual(255);
8
+ expect(number).toBeGreaterThanOrEqual(0);
9
+ }
10
+
11
+ function validate_rgb(rgb) {
12
+ expect(rgb).not.toBeUndefined();
13
+ expect(rgb).not.toBeNull();
14
+ expect(typeof rgb).toBe('object');
15
+
16
+ validate_element(rgb.r);
17
+ validate_element(rgb.g);
18
+ validate_element(rgb.b);
19
+ }
20
+
21
+ test("sanity test", () => {
22
+
23
+ validate_rgb(hsv2rgb(0, 0, 0));
24
+ validate_rgb(hsv2rgb(0, 0, 1));
25
+ validate_rgb(hsv2rgb(0, 1, 0));
26
+ validate_rgb(hsv2rgb(0, 1, 1));
27
+ validate_rgb(hsv2rgb(1, 0, 0));
28
+ validate_rgb(hsv2rgb(1, 0, 1));
29
+ validate_rgb(hsv2rgb(1, 1, 0));
30
+ validate_rgb(hsv2rgb(1, 1, 1));
31
+
32
+ validate_rgb(hsv2rgb(0.5, 0.5, 0.5));
33
+
34
+ validate_rgb(hsv2rgb(0.1, 0.5, 0.5));
35
+ validate_rgb(hsv2rgb(0.2, 0.5, 0.5));
36
+ validate_rgb(hsv2rgb(0.3, 0.5, 0.5));
37
+ validate_rgb(hsv2rgb(0.4, 0.5, 0.5));
38
+ validate_rgb(hsv2rgb(0.6, 0.5, 0.5));
39
+ validate_rgb(hsv2rgb(0.7, 0.5, 0.5));
40
+ validate_rgb(hsv2rgb(0.8, 0.5, 0.5));
41
+ validate_rgb(hsv2rgb(0.9, 0.5, 0.5));
42
+
43
+ });
@@ -7,7 +7,8 @@ import { aabb3_intersects_ray } from "./aabb3_intersects_ray.js";
7
7
  * @returns {boolean}
8
8
  */
9
9
  export function aabb3_array_intersects_ray_array(aabb, ray) {
10
- return aabb3_intersects_ray(aabb[0], aabb[1], aabb[2], aabb[3], aabb[4], aabb[5],
10
+ return aabb3_intersects_ray(
11
+ aabb[0], aabb[1], aabb[2], aabb[3], aabb[4], aabb[5],
11
12
  ray[0], ray[1], ray[2],
12
13
  ray[3], ray[4], ray[5]
13
14
  );