@woosh/meep-engine 2.87.3 → 2.87.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 (47) hide show
  1. package/build/meep.cjs +4 -4
  2. package/build/meep.min.js +1 -1
  3. package/build/meep.module.js +4 -4
  4. package/package.json +1 -1
  5. package/src/core/binary/align_4.d.ts +7 -0
  6. package/src/core/binary/align_4.d.ts.map +1 -0
  7. package/src/core/binary/align_4.js +14 -0
  8. package/src/core/geom/2d/aabb/aabb2_intersects_ray.d.ts +14 -0
  9. package/src/core/geom/2d/aabb/aabb2_intersects_ray.d.ts.map +1 -0
  10. package/src/core/geom/2d/aabb/aabb2_intersects_ray.js +53 -0
  11. package/src/core/geom/2d/aabb/aabb2_intersects_ray.spec.d.ts +2 -0
  12. package/src/core/geom/2d/aabb/aabb2_intersects_ray.spec.d.ts.map +1 -0
  13. package/src/core/geom/2d/aabb/aabb2_intersects_ray.spec.js +28 -0
  14. package/src/core/geom/2d/hash-grid/SpatialHashGrid.d.ts +52 -0
  15. package/src/core/geom/2d/hash-grid/SpatialHashGrid.d.ts.map +1 -0
  16. package/src/core/geom/2d/hash-grid/SpatialHashGrid.js +317 -0
  17. package/src/core/geom/2d/hash-grid/SpatialHashGrid.spec.d.ts +2 -0
  18. package/src/core/geom/2d/hash-grid/SpatialHashGrid.spec.d.ts.map +1 -0
  19. package/src/core/geom/2d/hash-grid/SpatialHashGrid.spec.js +158 -0
  20. package/src/core/geom/2d/hash-grid/shg_query_raycast.d.ts +14 -0
  21. package/src/core/geom/2d/hash-grid/shg_query_raycast.d.ts.map +1 -0
  22. package/src/core/geom/2d/hash-grid/shg_query_raycast.js +21 -0
  23. package/src/core/geom/2d/lt-grid/LooseTightGrid.d.ts +55 -0
  24. package/src/core/geom/2d/lt-grid/LooseTightGrid.d.ts.map +1 -0
  25. package/src/core/geom/2d/lt-grid/LooseTightGrid.js +221 -0
  26. package/src/core/geom/2d/quad-tree/QuadTreeNode.js +3 -3
  27. package/src/core/geom/2d/quad-tree/qt_query_data_raycast.d.ts.map +1 -1
  28. package/src/core/geom/2d/quad-tree/qt_query_data_raycast.js +17 -8
  29. package/src/core/geom/2d/quad-tree-binary/QuadTree.d.ts +94 -0
  30. package/src/core/geom/2d/quad-tree-binary/QuadTree.d.ts.map +1 -0
  31. package/src/core/geom/2d/quad-tree-binary/QuadTree.js +715 -0
  32. package/src/core/geom/2d/quad-tree-binary/QuadTree.spec.d.ts +2 -0
  33. package/src/core/geom/2d/quad-tree-binary/QuadTree.spec.d.ts.map +1 -0
  34. package/src/core/geom/2d/quad-tree-binary/QuadTree.spec.js +53 -0
  35. package/src/core/geom/3d/morton/de_interleave_2_bits.spec.d.ts +2 -0
  36. package/src/core/geom/3d/morton/de_interleave_2_bits.spec.d.ts.map +1 -0
  37. package/src/core/geom/3d/morton/de_interleave_2_bits.spec.js +21 -0
  38. package/src/core/geom/3d/morton/de_interleave_bits_by_2.d.ts +7 -0
  39. package/src/core/geom/3d/morton/de_interleave_bits_by_2.d.ts.map +1 -0
  40. package/src/core/geom/3d/morton/de_interleave_bits_by_2.js +15 -0
  41. package/src/core/geom/3d/morton/split_by_2.d.ts +1 -1
  42. package/src/core/geom/3d/morton/split_by_2.js +1 -1
  43. package/src/core/geom/3d/morton/split_by_3.d.ts +1 -1
  44. package/src/core/geom/3d/morton/split_by_3.js +1 -1
  45. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts +7 -0
  46. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.d.ts.map +1 -1
  47. package/src/core/geom/3d/topology/struct/binary/BinaryElementPool.js +14 -14
@@ -0,0 +1,158 @@
1
+ import { randomFloatBetween } from "../../../math/random/randomFloatBetween.js";
2
+ import { seededRandom } from "../../../math/random/seededRandom.js";
3
+ import { SpatialHashGrid } from "./SpatialHashGrid.js";
4
+
5
+ test("constructor does not throw", () => {
6
+ new SpatialHashGrid()
7
+ });
8
+
9
+ test("new grid should be empty", () => {
10
+ const grid = new SpatialHashGrid(1, 1);
11
+
12
+ expect(grid.is_cell_empty(0, 0)).toBe(true);
13
+ });
14
+
15
+ test("insert a small tile that fits into a single grid cell", () => {
16
+ const grid = new SpatialHashGrid(2, 2, 4);
17
+
18
+ const el = grid.element_allocate();
19
+
20
+ grid.element_set_user_data(el, 7);
21
+ grid.element_set_bounds_primitive(el, 0.5, 0.5, 1, 1);
22
+
23
+ grid.element_insert(el);
24
+
25
+ expect(grid.is_cell_empty(0, 0)).toBe(false);
26
+ });
27
+
28
+ test("insert an element that spans multiple grid cells", () => {
29
+
30
+ const grid = new SpatialHashGrid(2, 3, 4);
31
+
32
+ const el = grid.element_allocate();
33
+
34
+ grid.element_set_user_data(el, 7);
35
+ grid.element_set_bounds_primitive(el, 0.5, 0.5, 6, 6);
36
+
37
+ grid.element_insert(el);
38
+
39
+ expect(grid.is_cell_empty(0, 0)).toBe(false);
40
+ expect(grid.is_cell_empty(0, 1)).toBe(false);
41
+ expect(grid.is_cell_empty(1, 0)).toBe(false);
42
+ expect(grid.is_cell_empty(1, 1)).toBe(false);
43
+ expect(grid.is_cell_empty(0, 2)).toBe(true);
44
+ expect(grid.is_cell_empty(1, 2)).toBe(true);
45
+ });
46
+
47
+ test("remove an element that spans multiple grid cells", () => {
48
+
49
+ const grid = new SpatialHashGrid(2, 3, 4);
50
+
51
+ const el = grid.element_allocate();
52
+
53
+ grid.element_set_user_data(el, 7);
54
+ grid.element_set_bounds_primitive(el, 0.5, 0.5, 6, 6);
55
+
56
+ grid.element_insert(el);
57
+ grid.element_remove(el);
58
+
59
+ expect(grid.is_cell_empty(0, 0)).toBe(true);
60
+ expect(grid.is_cell_empty(0, 1)).toBe(true);
61
+ expect(grid.is_cell_empty(1, 0)).toBe(true);
62
+ expect(grid.is_cell_empty(1, 1)).toBe(true);
63
+ expect(grid.is_cell_empty(0, 2)).toBe(true);
64
+ expect(grid.is_cell_empty(1, 2)).toBe(true);
65
+ });
66
+
67
+ test("remove element from grid", () => {
68
+
69
+ const grid = new SpatialHashGrid(2, 2, 4);
70
+
71
+ const el = grid.element_allocate();
72
+
73
+ grid.element_set_user_data(el, 7);
74
+ grid.element_set_bounds_primitive(el, 0.5, 0.5, 1, 1);
75
+
76
+ grid.element_insert(el);
77
+ grid.element_remove(el);
78
+
79
+ expect(grid.is_cell_empty(0, 0)).toBe(true);
80
+ expect(grid.is_cell_empty(0, 1)).toBe(true);
81
+ expect(grid.is_cell_empty(1, 0)).toBe(true);
82
+ expect(grid.is_cell_empty(1, 1)).toBe(true);
83
+ });
84
+
85
+ test("add 3 element and remove in same order, expect grid to be empty", () => {
86
+
87
+ const grid = new SpatialHashGrid(2, 2, 4);
88
+
89
+ const a = grid.element_allocate();
90
+
91
+ grid.element_set_user_data(a, 3);
92
+ grid.element_set_bounds_primitive(a, 0.5, 0.5, 1, 1);
93
+
94
+ const b = grid.element_allocate();
95
+
96
+ grid.element_set_user_data(b, 7);
97
+ grid.element_set_bounds_primitive(b, 0.5, 0.5, 1, 1);
98
+
99
+ const c = grid.element_allocate();
100
+
101
+ grid.element_set_user_data(c, 11);
102
+ grid.element_set_bounds_primitive(c, 0.5, 0.5, 1, 1);
103
+
104
+ grid.element_insert(a);
105
+ grid.element_insert(b);
106
+ grid.element_insert(c);
107
+
108
+ grid.element_remove(a);
109
+ grid.element_remove(b);
110
+ grid.element_remove(c);
111
+
112
+ expect(grid.is_cell_empty(0, 0)).toBe(true);
113
+ expect(grid.is_cell_empty(0, 1)).toBe(true);
114
+ expect(grid.is_cell_empty(1, 0)).toBe(true);
115
+ expect(grid.is_cell_empty(1, 1)).toBe(true);
116
+ });
117
+
118
+ test.skip("performance, insertion 1m random", () => {
119
+ const grid = new SpatialHashGrid(100, 100, 1);
120
+
121
+ const random = seededRandom(42);
122
+
123
+ const N = 1000000;
124
+
125
+ const elements = new Uint32Array(N);
126
+
127
+ for (let i = 0; i < N; i++) {
128
+
129
+ const position_x = randomFloatBetween(random, 0, 98);
130
+ const position_y = randomFloatBetween(random, 0, 98);
131
+
132
+ const size_x = randomFloatBetween(random, 0.1, 2);
133
+ const size_y = randomFloatBetween(random, 0.1, 2);
134
+
135
+ const element = grid.element_allocate();
136
+
137
+ grid.element_set_user_data(element, i);
138
+ grid.element_set_bounds_primitive(element,
139
+ position_x,
140
+ position_y,
141
+ position_x + size_x,
142
+ position_y + size_y
143
+ );
144
+
145
+ elements[i] = element;
146
+ }
147
+
148
+ // perform insertion
149
+ const t0 = performance.now();
150
+
151
+ for (let i = 0; i < N; i++) {
152
+ grid.element_insert(elements[i]);
153
+ }
154
+
155
+ const duration = performance.now() - t0;
156
+
157
+ console.log(`Duration ${duration}ms, ${(N / (duration * 1e-3)).toFixed(2)} records per second`);
158
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ *
3
+ * @param {SpatialHashGrid} grid
4
+ * @param {number} origin_x
5
+ * @param {number} origin_y
6
+ * @param {number} direction_x
7
+ * @param {number} direction_y
8
+ * @param {number} max_distance
9
+ * @param {function} predicate
10
+ * @param {*} [predicateContext]
11
+ * @returns {number} element ID or -1 if not found
12
+ */
13
+ export function shg_query_raycast(grid: SpatialHashGrid, origin_x: number, origin_y: number, direction_x: number, direction_y: number, max_distance: number, predicate: Function, predicateContext?: any): number;
14
+ //# sourceMappingURL=shg_query_raycast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shg_query_raycast.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/2d/hash-grid/shg_query_raycast.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,mEATW,MAAM,YACN,MAAM,eACN,MAAM,eACN,MAAM,gBACN,MAAM,gDAGJ,MAAM,CAUlB"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ *
3
+ * @param {SpatialHashGrid} grid
4
+ * @param {number} origin_x
5
+ * @param {number} origin_y
6
+ * @param {number} direction_x
7
+ * @param {number} direction_y
8
+ * @param {number} max_distance
9
+ * @param {function} predicate
10
+ * @param {*} [predicateContext]
11
+ * @returns {number} element ID or -1 if not found
12
+ */
13
+ export function shg_query_raycast(
14
+ grid,
15
+ origin_x, origin_y,
16
+ direction_x, direction_y,
17
+ max_distance,
18
+ predicate, predicateContext
19
+ ) {
20
+ throw new Error('NIY');
21
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ *
3
+ * NOTE: THIS CODE IS UNFINISHED, IT IS ONLY A SKETCH
4
+ * TODO finish implementation
5
+ *
6
+ * Inspired by Makyen's post on stackoverflow: https://stackoverflow.com/a/48384354
7
+ * Core idea is to have dynamically-sized bounding volumes that contain our AABBs, and then have a grid-aligned index that stores pointers to these volumes
8
+ * Makyren called it "Loose/Tight Double-Grid", where "loose" refers to dynamic bounding volumes and tight refers to the grid
9
+ *
10
+ * Invariants:
11
+ * - An element inserted into the structure will belong to exactly one "loose" cell
12
+ *
13
+ */
14
+ export class LooseTightGrid {
15
+ /**
16
+ *
17
+ * @param {number} v
18
+ */
19
+ set grid_scale(arg: number);
20
+ /**
21
+ *
22
+ * @returns {number}
23
+ */
24
+ get grid_scale(): number;
25
+ /**
26
+ *
27
+ * @param {number} size_x
28
+ * @param {number} size_y
29
+ */
30
+ resize(size_x: number, size_y: number): void;
31
+ /**
32
+ *
33
+ * @param {number} x
34
+ * @param {number} y
35
+ * @returns {number}
36
+ */
37
+ cell_position_to_index(x: number, y: number): number;
38
+ /**
39
+ *
40
+ * @param {number[]} out
41
+ * @param {number} out_offset
42
+ * @param {number} index
43
+ */
44
+ cell_index_to_position(out: number[], out_offset: number, index: number): void;
45
+ allocate_datum(): void;
46
+ insert(): void;
47
+ /**
48
+ * Rebuild structure from ground-up
49
+ * This is an expensive operation
50
+ * Generally you'll never want to call it explicitly, it is used internally when underlying structure changes
51
+ */
52
+ rebuild(): void;
53
+ #private;
54
+ }
55
+ //# sourceMappingURL=LooseTightGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LooseTightGrid.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/2d/lt-grid/LooseTightGrid.js"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;GAYG;AACH;IAcI;;;OAGG;IACH,4BAYC;IAED;;;OAGG;IACH,yBAEC;IAED;;;;OAIG;IACH,eAHW,MAAM,UACN,MAAM,QAehB;IAED;;;;;OAKG;IACH,0BAJW,MAAM,KACN,MAAM,GACJ,MAAM,CAUlB;IAED;;;;;OAKG;IACH,4BAJW,MAAM,EAAE,cACR,MAAM,SACN,MAAM,QAOhB;IAED,uBAEC;IAED,eAEC;IAwDD;;;;OAIG;IACH,gBAgCC;;CAEJ"}
@@ -0,0 +1,221 @@
1
+ import { assert } from "../../../assert.js";
2
+ import { UINT32_MAX } from "../../../binary/UINT32_MAX.js";
3
+ import { SCRATCH_UINT32_TRAVERSAL_STACK } from "../../../collection/SCRATCH_UINT32_TRAVERSAL_STACK.js";
4
+ import { de_interleave_2_bits } from "../../3d/morton/de_interleave_bits_by_2.js";
5
+ import { split_by_2 } from "../../3d/morton/split_by_2.js";
6
+ import { BinaryElementPool } from "../../3d/topology/struct/binary/BinaryElementPool.js";
7
+
8
+ const NULL_POINTER = UINT32_MAX;
9
+
10
+ const COLUMN_PARENT = 0;
11
+ const COLUMN_CHILD_1 = 1;
12
+ const COLUMN_CHILD_2 = 2;
13
+ const COLUMN_HEIGHT = 3;
14
+ const COLUMN_AABB_OFFSET = 4;
15
+
16
+ const COLUMN_USER_DATA = COLUMN_CHILD_2;
17
+
18
+ /**
19
+ *
20
+ * NOTE: THIS CODE IS UNFINISHED, IT IS ONLY A SKETCH
21
+ * TODO finish implementation
22
+ *
23
+ * Inspired by Makyen's post on stackoverflow: https://stackoverflow.com/a/48384354
24
+ * Core idea is to have dynamically-sized bounding volumes that contain our AABBs, and then have a grid-aligned index that stores pointers to these volumes
25
+ * Makyren called it "Loose/Tight Double-Grid", where "loose" refers to dynamic bounding volumes and tight refers to the grid
26
+ *
27
+ * Invariants:
28
+ * - An element inserted into the structure will belong to exactly one "loose" cell
29
+ *
30
+ */
31
+ export class LooseTightGrid {
32
+
33
+ #resolution_x = 0
34
+ #resolution_y = 0
35
+
36
+ // amount of space covered by a single grid cell
37
+ #grid_scale = 1
38
+
39
+ #node_pool = new BinaryElementPool(10 * 4)
40
+ #grid_data = new Uint32Array(0);
41
+
42
+ constructor() {
43
+ }
44
+
45
+ /**
46
+ *
47
+ * @param {number} v
48
+ */
49
+ set grid_scale(v) {
50
+ assert.isNumber(v, 'v');
51
+ assert.greaterThan(v, 0, 'v');
52
+
53
+ if (this.#grid_scale === v) {
54
+ // no change
55
+ return;
56
+ }
57
+
58
+ this.#grid_scale = v;
59
+
60
+ this.rebuild();
61
+ }
62
+
63
+ /**
64
+ *
65
+ * @returns {number}
66
+ */
67
+ get grid_scale() {
68
+ return this.#grid_scale;
69
+ }
70
+
71
+ /**
72
+ *
73
+ * @param {number} size_x
74
+ * @param {number} size_y
75
+ */
76
+ resize(size_x, size_y) {
77
+ assert.isNonNegativeInteger(size_x, 'size_x');
78
+ assert.isNonNegativeInteger(size_y, 'size_y');
79
+
80
+ if (this.#resolution_x === size_x && this.#resolution_y === size_y) {
81
+ // no change
82
+ return;
83
+ }
84
+
85
+ this.#resolution_x = size_x;
86
+ this.#resolution_y = size_y;
87
+
88
+ this.rebuild();
89
+ }
90
+
91
+ /**
92
+ *
93
+ * @param {number} x
94
+ * @param {number} y
95
+ * @returns {number}
96
+ */
97
+ cell_position_to_index(x, y) {
98
+ assert.isNonNegativeInteger(x, 'x');
99
+ assert.isNonNegativeInteger(y, 'y');
100
+
101
+ const _x = split_by_2(x);
102
+ const _y = split_by_2(y);
103
+
104
+ return _x | (_y << 1);
105
+ }
106
+
107
+ /**
108
+ *
109
+ * @param {number[]} out
110
+ * @param {number} out_offset
111
+ * @param {number} index
112
+ */
113
+ cell_index_to_position(out, out_offset, index) {
114
+
115
+ out[out_offset] = de_interleave_2_bits(index)
116
+ out[out_offset + 1] = de_interleave_2_bits(index >> 1)
117
+
118
+ }
119
+
120
+ allocate_datum() {
121
+
122
+ }
123
+
124
+ insert() {
125
+
126
+ }
127
+
128
+ /**
129
+ *
130
+ * @param {number} node
131
+ */
132
+ #node_is_leaf(node) {
133
+ const address = this.#node_pool.element_word(node);
134
+
135
+ return this.#node_pool.data_uint32[address + COLUMN_CHILD_1] === NULL_POINTER;
136
+ }
137
+
138
+ /**
139
+ * Collect leaves and de-allocate all intermediate nodes
140
+ * @param {number[]} output
141
+ * @param {number} output_offset
142
+ * @param {number} node
143
+ */
144
+ #erase_tree_structure(output, output_offset, node) {
145
+ const pool = this.#node_pool;
146
+
147
+ const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
148
+ const top = SCRATCH_UINT32_TRAVERSAL_STACK.pointer;
149
+
150
+ stack[stack.pointer++] = node;
151
+
152
+ let result_count = 0;
153
+
154
+ while (stack.pointer > top) {
155
+
156
+ stack.pointer--;
157
+
158
+ const n = stack[stack.pointer];
159
+
160
+ const node_address = pool.element_word(n);
161
+
162
+ const child_1 = pool.data_uint32[node_address + COLUMN_CHILD_1];
163
+ const child_2 = pool.data_uint32[node_address + COLUMN_CHILD_2];
164
+
165
+ if (child_1 === NULL_POINTER) {
166
+ // leaf node
167
+ output[output_offset + result_count] = n;
168
+ result_count++;
169
+ } else {
170
+ // binary node
171
+ stack[stack.pointer++] = child_2;
172
+ stack[stack.pointer++] = child_1;
173
+
174
+ // de-allocate node
175
+ pool.release(n);
176
+ }
177
+ }
178
+
179
+ return result_count;
180
+ }
181
+
182
+ /**
183
+ * Rebuild structure from ground-up
184
+ * This is an expensive operation
185
+ * Generally you'll never want to call it explicitly, it is used internally when underlying structure changes
186
+ */
187
+ rebuild() {
188
+ //TODO implement
189
+
190
+ let leaf_count = 0;
191
+ const leaf_nodes = [];
192
+ // traverse previous grid
193
+ for (let i = 0; i < this.#grid_data.length; i++) {
194
+ const node = this.#grid_data[i];
195
+
196
+ if (node === NULL_POINTER) {
197
+ // empty grid cell
198
+ continue;
199
+ }
200
+
201
+ leaf_count += this.#erase_tree_structure(leaf_nodes, leaf_count, node);
202
+ }
203
+
204
+
205
+ const resolution_y = this.#resolution_y;
206
+ const resolution_x = this.#resolution_x;
207
+
208
+ const grid_cell_count = resolution_x * resolution_y;
209
+
210
+ if (grid_cell_count !== this.#grid_data.length) {
211
+ this.#grid_data = new Uint32Array(grid_cell_count);
212
+ }
213
+
214
+ // drop all roots
215
+ this.#grid_data.fill(NULL_POINTER);
216
+
217
+ // re-insert all leaves
218
+
219
+ }
220
+
221
+ }
@@ -174,8 +174,8 @@ export class QuadTreeNode extends AABB2 {
174
174
  }
175
175
  }
176
176
 
177
- const xm = (x0 + x1) / 2;
178
- const ym = (y0 + y1) / 2;
177
+ const xm = (x0 + x1) *0.5;
178
+ const ym = (y0 + y1) *0.5;
179
179
 
180
180
  if (datum.y1 < ym) {
181
181
  //top
@@ -189,7 +189,7 @@ export class QuadTreeNode extends AABB2 {
189
189
  this.addDatum(datum);
190
190
  }
191
191
  } else if (datum.y0 >= ym) {
192
- //top
192
+ //bottom
193
193
  if (datum.x1 < xm) {
194
194
  //left
195
195
  this.bottomLeft.insertDatum(datum);
@@ -1 +1 @@
1
- {"version":3,"file":"qt_query_data_raycast.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/2d/quad-tree/qt_query_data_raycast.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;GAWG;AACH,0EATW,MAAM,YACN,MAAM,eACN,MAAM,eACN,MAAM,gBACN,MAAM,2CAGJ,mBAAiB,SAAS,CAiEtC"}
1
+ {"version":3,"file":"qt_query_data_raycast.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/2d/quad-tree/qt_query_data_raycast.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;GAWG;AACH,0EATW,MAAM,YACN,MAAM,eACN,MAAM,eACN,MAAM,gBACN,MAAM,2CAGJ,mBAAiB,SAAS,CA0EtC"}
@@ -1,4 +1,4 @@
1
- import { aabb2_distance_sqr_to_point } from "../aabb/aabb2_distance_sqr_to_point.js";
1
+ import { aabb2_intersects_ray } from "../aabb/aabb2_intersects_ray.js";
2
2
 
3
3
  /**
4
4
  *
@@ -38,16 +38,21 @@ export function qt_query_data_raycast(
38
38
  * @type {QuadTreeDatum<T>|undefined}
39
39
  */
40
40
  let best_match = undefined;
41
- let best_match_distance2 = max_distance2;
41
+ let best_match_distance = max_distance;
42
42
 
43
43
  while (stack_pointer > 0) {
44
44
 
45
45
  --stack_pointer;
46
46
  const node = node_stack[stack_pointer];
47
47
 
48
- const d2 = aabb2_distance_sqr_to_point(node.x0, node.y0, node.x1, node.y1, origin_x, origin_y);
48
+ const d2 = aabb2_intersects_ray(node.x0, node.y0, node.x1, node.y1, origin_x, origin_y, direction_x, direction_y);
49
49
 
50
- if (d2 > best_match_distance2) {
50
+ if (d2 < 0) {
51
+ // no hit
52
+ continue;
53
+ }
54
+
55
+ if (d2 > best_match_distance) {
51
56
  // too far, not a match
52
57
  continue;
53
58
  }
@@ -58,17 +63,21 @@ export function qt_query_data_raycast(
58
63
  for (let i = 0; i < data_count; i++) {
59
64
  const datum = data[i];
60
65
 
61
- const d2_to_datum = aabb2_distance_sqr_to_point(datum.x0, datum.y0, datum.x1, datum.y1, origin_x, origin_y);
66
+ const d_to_datum = aabb2_intersects_ray(datum.x0, datum.y0, datum.x1, datum.y1, origin_x, origin_y, direction_x, direction_y);
67
+
68
+ if (d_to_datum < 0) {
69
+ continue;
70
+ }
62
71
 
63
- if (d2_to_datum < best_match_distance2 && predicate.call(
72
+ if (d_to_datum < best_match_distance && predicate.call(
64
73
  predicateContext,
65
74
  datum.data,
66
75
  origin_x, origin_y,
67
76
  direction_x, direction_y,
68
- best_match_distance2
77
+ best_match_distance
69
78
  )) {
70
79
  best_match = datum;
71
- best_match_distance2 = d2_to_datum;
80
+ best_match_distance = d_to_datum;
72
81
  }
73
82
  }
74
83
 
@@ -0,0 +1,94 @@
1
+ export const QT_NULL_POINTER: number;
2
+ /**
3
+ *
4
+ * NOTE: THIS CODE IS UNFINISHED, IT IS ONLY A SKETCH
5
+ * TODO finish implementation
6
+ */
7
+ export class QuadTree {
8
+ /**
9
+ * Parameters allow us to set initial bounds to prevent early resizing
10
+ * @param {number} x0
11
+ * @param {number} y0
12
+ * @param {number} x1
13
+ * @param {number} y1
14
+ */
15
+ constructor(x0?: number, y0?: number, x1?: number, y1?: number);
16
+ get root(): number;
17
+ /**
18
+ * Resize dimensions of the tree to tightly fit all inserted elements
19
+ */
20
+ shrink(): void;
21
+ /**
22
+ *
23
+ * @param {number[]|Float32Array} output
24
+ */
25
+ compute_tight_bounds(output: number[] | Float32Array): void;
26
+ /**
27
+ * Rebuild tree but keep all the data
28
+ */
29
+ rebuild(): void;
30
+ /**
31
+ *
32
+ * @returns {number} ID of data in the tree
33
+ */
34
+ element_allocate(): number;
35
+ /**
36
+ *
37
+ * @param {number} element
38
+ * @param {number} user_data
39
+ */
40
+ element_set_user_data(element: number, user_data: number): void;
41
+ /**
42
+ *
43
+ * @param {number} element
44
+ * @return {number}
45
+ */
46
+ element_get_user_data(element: number): number;
47
+ /**
48
+ *
49
+ * @param {number} element
50
+ * @param {number} x0
51
+ * @param {number} y0
52
+ * @param {number} x1
53
+ * @param {number} y1
54
+ */
55
+ element_set_bounds_primitive(element: number, x0: number, y0: number, x1: number, y1: number): void;
56
+ /**
57
+ *
58
+ * @param {number} element
59
+ * @return {number} next element in the node
60
+ */
61
+ element_get_next(element: number): number;
62
+ /**
63
+ *
64
+ * @param {number} x0
65
+ * @param {number} y0
66
+ * @param {number} x1
67
+ * @param {number} y1
68
+ */
69
+ ensure_bounds(x0: number, y0: number, x1: number, y1: number): void;
70
+ /**
71
+ * Assumes element is allocated and is not present in the tree yet
72
+ * @param {number} element
73
+ */
74
+ element_insert(element: number): void;
75
+ /**
76
+ *
77
+ * @param {number} node
78
+ * @param {number[]|Uint32Array} destination
79
+ * @param {number} destination_offset
80
+ * @return {number} number of elements read
81
+ */
82
+ node_read_elements(node: number, destination: number[] | Uint32Array, destination_offset: number): number;
83
+ /**
84
+ *
85
+ * @param {number} datum_id ID of data in tree
86
+ */
87
+ element_remove(datum_id: number): void;
88
+ /**
89
+ * Remove all data
90
+ */
91
+ release_all(): void;
92
+ #private;
93
+ }
94
+ //# sourceMappingURL=QuadTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuadTree.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/2d/quad-tree-binary/QuadTree.js"],"names":[],"mappings":"AAMA,qCAA0C;AA4C1C;;;;GAIG;AACH;IAaI;;;;;;OAMG;IACH,iBALW,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,EAIhB;IAED,mBAEC;IAiCD;;OAEG;IACH,eAaC;IAED;;;OAGG;IACH,6BAFW,MAAM,EAAE,GAAC,YAAY,QAmC/B;IAED;;OAEG;IACH,gBAcC;IAED;;;OAGG;IACH,oBAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,+BAHW,MAAM,aACN,MAAM,QAUhB;IAED;;;;OAIG;IACH,+BAHW,MAAM,GACL,MAAM,CAOjB;IAED;;;;;;;OAOG;IACH,sCANW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QA+BhB;IAED;;;;OAIG;IACH,0BAHW,MAAM,GACL,MAAM,CAOjB;IAsFD;;;;;;OAMG;IACH,kBALW,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QA6BhB;IAED;;;OAGG;IACH,wBAFW,MAAM,QA4BhB;IAwCD;;;;;;OAMG;IACH,yBALW,MAAM,eACN,MAAM,EAAE,GAAC,WAAW,sBACpB,MAAM,GACL,MAAM,CAmBjB;IA0DD;;;OAGG;IACH,yBAFW,MAAM,QAUhB;IAkJD;;OAEG;IACH,oBAIC;;CACJ"}