@woosh/meep-engine 2.87.6 → 2.88.1

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 (58) hide show
  1. package/build/meep.cjs +26 -4
  2. package/build/meep.min.js +1 -1
  3. package/build/meep.module.js +26 -4
  4. package/editor/view/ecs/HierarchicalEntityListView.js +1 -1
  5. package/editor/view/v2/prototypeEditor.js +1 -1
  6. package/package.json +1 -1
  7. package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
  8. package/src/core/bvh2/bvh3/BVH.js +4 -2
  9. package/src/core/bvh2/bvh3/BVH.spec.js +15 -3
  10. package/src/core/geom/2d/aabb/aabb2_clip_line_cohen_sutherland.d.ts +16 -0
  11. package/src/core/geom/2d/aabb/aabb2_clip_line_cohen_sutherland.d.ts.map +1 -0
  12. package/src/core/geom/2d/aabb/aabb2_clip_line_cohen_sutherland.js +149 -0
  13. package/src/core/geom/2d/aabb/aabb2_intersects_circle.d.ts +13 -0
  14. package/src/core/geom/2d/aabb/aabb2_intersects_circle.d.ts.map +1 -0
  15. package/src/core/geom/2d/aabb/aabb2_intersects_circle.js +27 -0
  16. package/src/core/geom/2d/hash-grid/SpatialHashGrid.d.ts +41 -6
  17. package/src/core/geom/2d/hash-grid/SpatialHashGrid.d.ts.map +1 -1
  18. package/src/core/geom/2d/hash-grid/SpatialHashGrid.js +92 -22
  19. package/src/core/geom/2d/hash-grid/SpatialHashGrid.spec.js +98 -2
  20. package/src/core/geom/2d/hash-grid/prototypeGridQueries.d.ts +2 -0
  21. package/src/core/geom/2d/hash-grid/prototypeGridQueries.d.ts.map +1 -0
  22. package/src/core/geom/2d/hash-grid/prototypeGridQueries.js +387 -0
  23. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.d.ts +12 -0
  24. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.d.ts.map +1 -0
  25. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.js +160 -0
  26. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.spec.d.ts +2 -0
  27. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.spec.d.ts.map +1 -0
  28. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.spec.js +85 -0
  29. package/src/core/geom/2d/hash-grid/shg_query_elements_line.d.ts +14 -0
  30. package/src/core/geom/2d/hash-grid/shg_query_elements_line.d.ts.map +1 -0
  31. package/src/core/geom/2d/hash-grid/shg_query_elements_line.js +132 -0
  32. package/src/core/geom/2d/hash-grid/shg_query_elements_line.spec.d.ts +2 -0
  33. package/src/core/geom/2d/hash-grid/shg_query_elements_line.spec.d.ts.map +1 -0
  34. package/src/core/geom/2d/hash-grid/shg_query_elements_line.spec.js +102 -0
  35. package/src/core/geom/2d/line/line2_distance_to_point_sqr.d.ts +11 -0
  36. package/src/core/geom/2d/line/line2_distance_to_point_sqr.d.ts.map +1 -0
  37. package/src/core/geom/2d/line/line2_distance_to_point_sqr.js +39 -0
  38. package/src/core/geom/2d/quad-tree/QuadTreeNode.spec.js +53 -0
  39. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.spec.d.ts +2 -0
  40. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.spec.d.ts.map +1 -0
  41. package/src/core/geom/2d/quad-tree/qt_match_data_by_circle.spec.js +86 -0
  42. package/src/core/model/ObservedString.d.ts.map +1 -1
  43. package/src/core/model/ObservedString.js +1 -1
  44. package/src/engine/animation/curve/ecd_bind_animation_curve.js +1 -1
  45. package/src/engine/ecs/name/Name.d.ts +25 -0
  46. package/src/engine/ecs/name/Name.d.ts.map +1 -0
  47. package/src/engine/ecs/name/Name.js +42 -0
  48. package/src/engine/ecs/name/NameSerializationAdapter.d.ts +18 -0
  49. package/src/engine/ecs/name/NameSerializationAdapter.d.ts.map +1 -0
  50. package/src/engine/ecs/name/NameSerializationAdapter.js +29 -0
  51. package/src/engine/ecs/transform/Transform.d.ts +2 -2
  52. package/src/engine/graphics/canvas/canvas2d_draw_grid.js +1 -1
  53. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.d.ts.map +1 -1
  54. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.js +17 -1
  55. package/src/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +1 -1
  56. package/src/core/geom/2d/hash-grid/shg_query_raycast.d.ts +0 -14
  57. package/src/core/geom/2d/hash-grid/shg_query_raycast.d.ts.map +0 -1
  58. package/src/core/geom/2d/hash-grid/shg_query_raycast.js +0 -21
@@ -115,7 +115,7 @@ test("add 3 element and remove in same order, expect grid to be empty", () => {
115
115
  expect(grid.is_cell_empty(1, 1)).toBe(true);
116
116
  });
117
117
 
118
- test.skip("performance, insertion 1m random", () => {
118
+ test.skip("query performance, line insertion 1m random", () => {
119
119
  const grid = new SpatialHashGrid(100, 100, 1);
120
120
 
121
121
  const random = seededRandom(42);
@@ -154,5 +154,101 @@ test.skip("performance, insertion 1m random", () => {
154
154
 
155
155
  const duration = performance.now() - t0;
156
156
 
157
- console.log(`Duration ${duration}ms, ${(N / (duration * 1e-3)).toFixed(2)} records per second`);
157
+ const duration_seconds = duration * 1e-3;
158
+
159
+ console.log(`Duration ${duration_seconds}s, ${(N / duration_seconds).toFixed(2)} records per second`);
160
+ });
161
+ test("remove element from grid", () => {
162
+
163
+ const grid = new SpatialHashGrid(2, 2, 4);
164
+
165
+ const el = grid.element_allocate();
166
+
167
+ grid.element_set_user_data(el, 7);
168
+ grid.element_set_bounds_primitive(el, 0.5, 0.5, 1, 1);
169
+
170
+ grid.element_insert(el);
171
+ grid.element_remove(el);
172
+
173
+ expect(grid.is_cell_empty(0, 0)).toBe(true);
174
+ expect(grid.is_cell_empty(0, 1)).toBe(true);
175
+ expect(grid.is_cell_empty(1, 0)).toBe(true);
176
+ expect(grid.is_cell_empty(1, 1)).toBe(true);
177
+ });
178
+
179
+ test("add 3 element and remove in same order, expect grid to be empty", () => {
180
+
181
+ const grid = new SpatialHashGrid(2, 2, 4);
182
+
183
+ const a = grid.element_allocate();
184
+
185
+ grid.element_set_user_data(a, 3);
186
+ grid.element_set_bounds_primitive(a, 0.5, 0.5, 1, 1);
187
+
188
+ const b = grid.element_allocate();
189
+
190
+ grid.element_set_user_data(b, 7);
191
+ grid.element_set_bounds_primitive(b, 0.5, 0.5, 1, 1);
192
+
193
+ const c = grid.element_allocate();
194
+
195
+ grid.element_set_user_data(c, 11);
196
+ grid.element_set_bounds_primitive(c, 0.5, 0.5, 1, 1);
197
+
198
+ grid.element_insert(a);
199
+ grid.element_insert(b);
200
+ grid.element_insert(c);
201
+
202
+ grid.element_remove(a);
203
+ grid.element_remove(b);
204
+ grid.element_remove(c);
205
+
206
+ expect(grid.is_cell_empty(0, 0)).toBe(true);
207
+ expect(grid.is_cell_empty(0, 1)).toBe(true);
208
+ expect(grid.is_cell_empty(1, 0)).toBe(true);
209
+ expect(grid.is_cell_empty(1, 1)).toBe(true);
210
+ });
211
+
212
+ test("performance, insertion 1m random", () => {
213
+ const grid = new SpatialHashGrid(100, 100, 1);
214
+
215
+ const random = seededRandom(42);
216
+
217
+ const N = 1000000;
218
+
219
+ const elements = new Uint32Array(N);
220
+
221
+ for (let i = 0; i < N; i++) {
222
+
223
+ const position_x = randomFloatBetween(random, 0, 98);
224
+ const position_y = randomFloatBetween(random, 0, 98);
225
+
226
+ const size_x = randomFloatBetween(random, 0.1, 2);
227
+ const size_y = randomFloatBetween(random, 0.1, 2);
228
+
229
+ const element = grid.element_allocate();
230
+
231
+ grid.element_set_user_data(element, i);
232
+ grid.element_set_bounds_primitive(element,
233
+ position_x,
234
+ position_y,
235
+ position_x + size_x,
236
+ position_y + size_y
237
+ );
238
+
239
+ elements[i] = element;
240
+ }
241
+
242
+ // perform insertion
243
+ const t0 = performance.now();
244
+
245
+ for (let i = 0; i < N; i++) {
246
+ grid.element_insert(elements[i]);
247
+ }
248
+
249
+ const duration = performance.now() - t0;
250
+
251
+ const duration_seconds = duration * 1e-3;
252
+
253
+ console.log(`Duration ${duration_seconds}s, ${(N / duration_seconds).toFixed(2)} records per second`);
158
254
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=prototypeGridQueries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prototypeGridQueries.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/2d/hash-grid/prototypeGridQueries.js"],"names":[],"mappings":""}
@@ -0,0 +1,387 @@
1
+ import requestAnimationFrame from "dat.gui/src/dat/utils/requestAnimationFrame.js";
2
+ import { canvas2d_draw_grid } from "../../../../engine/graphics/canvas/canvas2d_draw_grid.js";
3
+ import { MouseEvents } from "../../../../engine/input/devices/events/MouseEvents.js";
4
+ import { PointerDevice } from "../../../../engine/input/devices/PointerDevice.js";
5
+ import { CanvasView } from "../../../../view/elements/CanvasView.js";
6
+ import { FLT_EPSILON_32 } from "../../../math/FLT_EPSILON_32.js";
7
+ import { randomIntegerBetween } from "../../../math/random/randomIntegerBetween.js";
8
+ import { seededRandom } from "../../../math/random/seededRandom.js";
9
+ import { aabb2_clip_line_cohen_sutherland } from "../aabb/aabb2_clip_line_cohen_sutherland.js";
10
+ import { shg_query_elements_circle } from "./shg_query_elements_circle.js";
11
+ import { shg_query_elements_line } from "./shg_query_elements_line.js";
12
+ import { SpatialHashGrid } from "./SpatialHashGrid.js";
13
+
14
+
15
+ const canvas = new CanvasView();
16
+
17
+ canvas.size.set(window.innerWidth, window.innerHeight);
18
+
19
+ canvas.link();
20
+
21
+ document.body.style.margin = 0;
22
+ document.body.style.overflow = "hidden";
23
+ document.body.appendChild(canvas.el);
24
+
25
+ const selected_boxes = [1, 3, 9]
26
+
27
+ const grid = new SpatialHashGrid(16, 16, 32);
28
+
29
+ const render_scale = 2;
30
+
31
+ const BOX_COUNT = 50;
32
+
33
+ const boxes = new Float32Array(BOX_COUNT * 4);
34
+
35
+ const random = seededRandom();
36
+
37
+ function populate_boxes(
38
+ random,
39
+ bounds_x0, bounds_y0, bounds_x1, bounds_y1
40
+ ) {
41
+
42
+ for (let i = 0; i < BOX_COUNT; i++) {
43
+
44
+ const offset = i * 4;
45
+
46
+
47
+ const width = randomIntegerBetween(random, 10, 20);
48
+ const height = randomIntegerBetween(random, 10, 20);
49
+
50
+ const x0 = randomIntegerBetween(random, Math.ceil(bounds_x0), Math.floor(bounds_x1 - width));
51
+ const y0 = randomIntegerBetween(random, Math.ceil(bounds_y0), Math.floor(bounds_y1 - height));
52
+
53
+ const x1 = x0 + width;
54
+ const y1 = y0 + height;
55
+
56
+ boxes[offset] = x0;
57
+ boxes[offset + 1] = y0;
58
+ boxes[offset + 2] = x1;
59
+ boxes[offset + 3] = y1;
60
+
61
+ // add box to grid
62
+ const box = grid.element_allocate();
63
+
64
+ grid.element_set_user_data(box, i);
65
+ grid.element_set_bounds_primitive(box, x0, y0, x1, y1);
66
+
67
+ grid.element_insert(box);
68
+
69
+ }
70
+ }
71
+
72
+ function init() {
73
+ populate_boxes(
74
+ random,
75
+ 0, 0,
76
+ grid.size_x * grid.scale, grid.size_y * grid.scale
77
+ );
78
+
79
+ query.link(canvas);
80
+
81
+ }
82
+
83
+ function render_boxes() {
84
+ const ctx = canvas.context2d;
85
+
86
+ // draw background boxes
87
+
88
+ for (let i = 0; i < BOX_COUNT; i++) {
89
+
90
+ if (selected_boxes.includes(i)) {
91
+ continue;
92
+ }
93
+
94
+ const offset = i * 4;
95
+ const x0 = boxes[offset] * render_scale;
96
+ const y0 = boxes[offset + 1] * render_scale;
97
+ const x1 = boxes[offset + 2] * render_scale;
98
+ const y1 = boxes[offset + 3] * render_scale;
99
+
100
+ const w = x1 - x0;
101
+ const h = y1 - y0;
102
+
103
+ ctx.fillStyle = "rgba(0,184,255,1)";
104
+ ctx.fillRect(x0, y0, w, h);
105
+
106
+ }
107
+
108
+ // draw selected boxes
109
+
110
+ for (let i = 0; i < BOX_COUNT; i++) {
111
+ if (!selected_boxes.includes(i)) {
112
+ continue;
113
+ }
114
+
115
+ const offset = i * 4;
116
+ const x0 = boxes[offset] * render_scale;
117
+ const y0 = boxes[offset + 1] * render_scale;
118
+ const x1 = boxes[offset + 2] * render_scale;
119
+ const y1 = boxes[offset + 3] * render_scale;
120
+
121
+ const w = x1 - x0;
122
+ const h = y1 - y0;
123
+
124
+ ctx.fillStyle = "rgba(238,0,255,1)";
125
+ // ctx.strokeStyle = "rgb(238,0,255)";
126
+ // ctx.lineWidth = 4;
127
+
128
+ // ctx.strokeRect(x0, y0, w, h);
129
+ ctx.fillRect(x0, y0, w, h);
130
+
131
+ }
132
+ }
133
+
134
+ function render_grid() {
135
+ const scale = grid.scale * render_scale;
136
+ canvas2d_draw_grid({
137
+ ctx: canvas.context2d,
138
+ width: grid.size_x * scale + 1,
139
+ height: grid.size_y * scale + 1,
140
+ spacing: scale,
141
+ color: "rgba(0,0,0,0.2)"
142
+ });
143
+ }
144
+
145
+ class Query {
146
+ #view = null
147
+
148
+ /**
149
+ *
150
+ * @param {CanvasRenderingContext2D} ctx
151
+ * @param {SpatialHashGrid} grid
152
+ * @param {number} render_scale
153
+ */
154
+ render(ctx, grid, render_scale) {
155
+
156
+ }
157
+
158
+ /**
159
+ *
160
+ * @param {CanvasRenderingContext2D} ctx
161
+ * @param {SpatialHashGrid} grid
162
+ * @param {number} render_scale
163
+ */
164
+ render_debug(ctx, grid, render_scale) {
165
+
166
+ }
167
+
168
+ /**
169
+ *
170
+ * @param {number[]} result
171
+ * @param {number} result_offset
172
+ * @param {SpatialHashGrid} grid
173
+ * @returns {number}
174
+ */
175
+ run(result, result_offset, grid) {
176
+ return 0;
177
+ }
178
+
179
+ /**
180
+ *
181
+ * @param {View} view
182
+ */
183
+ link(view) {
184
+ if (this.#view !== view) {
185
+ this.unlink();
186
+ }
187
+
188
+ this.#view = view;
189
+ }
190
+
191
+ unlink() {
192
+ this.#view = null;
193
+ }
194
+ }
195
+
196
+ class LineQuery extends Query {
197
+ #line = [0, 0, 0, 0];
198
+
199
+ render_debug(ctx, grid, render_scale) {
200
+
201
+ const visited = shg_query_elements_line.visited;
202
+ if (visited === undefined) {
203
+ return;
204
+ }
205
+ const n = visited.length;
206
+
207
+ const scale = grid.scale * render_scale;
208
+
209
+ for (let i = 0; i < n; i += 2) {
210
+ const x = visited[i];
211
+ const y = visited[i + 1];
212
+
213
+ ctx.fillStyle = "rgba(0,0,255,0.3)";
214
+
215
+ ctx.fillRect(x * scale, y * scale, scale, scale);
216
+ }
217
+
218
+ }
219
+
220
+ render(ctx, grid, render_scale) {
221
+ const line = this.#line;
222
+
223
+
224
+ ctx.lineWidth = 2;
225
+ ctx.strokeStyle = "rgb(255,47,0)";
226
+
227
+ ctx.beginPath();
228
+ ctx.moveTo(line[0] * render_scale, line[1] * render_scale);
229
+ ctx.lineTo(line[2] * render_scale, line[3] * render_scale);
230
+ ctx.closePath();
231
+ ctx.stroke();
232
+ }
233
+
234
+ run(result, result_offset, grid) {
235
+ const line = this.#line;
236
+ const clipped_line = [];
237
+
238
+ if (!aabb2_clip_line_cohen_sutherland(
239
+ clipped_line, 0,
240
+ 0, 0, grid.size_x * grid.scale - FLT_EPSILON_32, grid.size_y * grid.scale - FLT_EPSILON_32,
241
+ line[0], line[1], line[2], line[3]
242
+ )) {
243
+ // line is outsize of the grid
244
+ return 0;
245
+ }
246
+
247
+ const els = [];
248
+ const hits = shg_query_elements_line(
249
+ els, 0,
250
+ grid,
251
+ clipped_line[0], clipped_line[1],
252
+ clipped_line[2], clipped_line[3]
253
+ );
254
+
255
+
256
+ for (let i = 0; i < hits; i++) {
257
+ const id = grid.element_get_user_data(els[i]);
258
+
259
+ result[i + result_offset] = id;
260
+ }
261
+
262
+ return hits;
263
+ }
264
+
265
+ link(view) {
266
+ super.link(view);
267
+
268
+ const line = this.#line;
269
+ let anchor = 0;
270
+
271
+ view.el.addEventListener(MouseEvents.Click, (evt) => {
272
+ const index = anchor * 2;
273
+
274
+ line[index + 0] = evt.clientX / render_scale;
275
+ line[index + 1] = evt.clientY / render_scale;
276
+
277
+ anchor = (anchor + 1) % 2;
278
+ });
279
+
280
+ }
281
+ }
282
+
283
+ class CircleQuery extends Query {
284
+ #circle = [0, 0, 0];
285
+
286
+ link(view) {
287
+ super.link(view);
288
+
289
+ const pointer = new PointerDevice(view.el);
290
+
291
+ pointer.start();
292
+
293
+ pointer.on.drag.add((position, origin) => {
294
+ this.#circle[0] = origin.x / render_scale;
295
+ this.#circle[1] = origin.y / render_scale;
296
+
297
+ this.#circle[2] = position.distanceTo(origin) / render_scale;
298
+ });
299
+ }
300
+
301
+ render_debug(ctx, grid, render_scale) {
302
+
303
+ const visited = shg_query_elements_circle.visited;
304
+ if (visited === undefined) {
305
+ return;
306
+ }
307
+ const n = visited.length;
308
+
309
+ const scale = grid.scale * render_scale;
310
+
311
+ for (let i = 0; i < n; i += 2) {
312
+ const x = visited[i];
313
+ const y = visited[i + 1];
314
+
315
+ ctx.fillStyle = "rgba(0,0,255,0.3)";
316
+
317
+ ctx.fillRect(x * scale, y * scale, scale, scale);
318
+ }
319
+
320
+ }
321
+
322
+ render(ctx, grid, render_scale) {
323
+
324
+ const c = this.#circle;
325
+ ctx.beginPath();
326
+ ctx.arc(c[0] * render_scale, c[1] * render_scale, c[2] * render_scale, 0, Math.PI * 2);
327
+ ctx.closePath();
328
+
329
+ ctx.lineWidth = 3;
330
+ ctx.strokeStyle = "red";
331
+ ctx.stroke();
332
+
333
+ }
334
+
335
+ run(result, result_offset, grid) {
336
+ const els = [];
337
+ const hits = shg_query_elements_circle(
338
+ els, 0,
339
+ grid,
340
+ this.#circle[0], this.#circle[1],
341
+ this.#circle[2]
342
+ );
343
+
344
+
345
+ for (let i = 0; i < hits; i++) {
346
+ const id = grid.element_get_user_data(els[i]);
347
+
348
+ result[i + result_offset] = id;
349
+ }
350
+
351
+ return hits;
352
+ }
353
+ }
354
+
355
+ const query = new CircleQuery();
356
+ // const query = new LineQuery();
357
+
358
+ function render() {
359
+
360
+ canvas.context2d.clearRect(0, 0, canvas.size.x, canvas.size.y);
361
+
362
+ render_grid();
363
+ query.render_debug(canvas.context2d, grid, render_scale);
364
+ render_boxes();
365
+ query.render(canvas.context2d, grid, render_scale);
366
+ }
367
+
368
+
369
+ function update() {
370
+
371
+ requestAnimationFrame(update);
372
+
373
+ selected_boxes.splice(0, selected_boxes.length)
374
+
375
+ query.run(selected_boxes, 0, grid);
376
+
377
+ render();
378
+ }
379
+
380
+ init();
381
+ update();
382
+
383
+ window.addEventListener("resize", () => {
384
+
385
+ canvas.size.set(window.innerWidth, window.innerHeight);
386
+
387
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ *
3
+ * @param {number[]} result
4
+ * @param {number} result_offset
5
+ * @param {SpatialHashGrid} grid
6
+ * @param {number} center_x
7
+ * @param {number} center_y
8
+ * @param {number} radius
9
+ * @returns {number}
10
+ */
11
+ export function shg_query_elements_circle(result: number[], result_offset: number, grid: SpatialHashGrid, center_x: number, center_y: number, radius: number): number;
12
+ //# sourceMappingURL=shg_query_elements_circle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shg_query_elements_circle.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/2d/hash-grid/shg_query_elements_circle.js"],"names":[],"mappings":"AA4CA;;;;;;;;;GASG;AACH,kDARW,MAAM,EAAE,iBACR,MAAM,mCAEN,MAAM,YACN,MAAM,UACN,MAAM,GACJ,MAAM,CAyGlB"}
@@ -0,0 +1,160 @@
1
+ import { fabsf } from "../../../math/fabsf.js";
2
+ import { max2 } from "../../../math/max2.js";
3
+ import { min2 } from "../../../math/min2.js";
4
+ import { aabb2_distance_sqr_to_point } from "../aabb/aabb2_distance_sqr_to_point.js";
5
+ import {
6
+ COLUMN_ELEMENT_X0,
7
+ COLUMN_ELEMENT_X1,
8
+ COLUMN_ELEMENT_Y0,
9
+ COLUMN_ELEMENT_Y1,
10
+ NULL_POINTER
11
+ } from "./SpatialHashGrid.js";
12
+
13
+ /**
14
+ * @see https://stackoverflow.com/questions/47639413/algorithm-for-accurate-detection-of-overlap-between-a-square-and-a-circle
15
+ * @param square_center_x
16
+ * @param square_center_y
17
+ * @param circle_center_x
18
+ * @param circle_center_y
19
+ * @param circle_radius
20
+ * @return {boolean}
21
+ */
22
+ function unit_square_intersects_circle(
23
+ square_center_x,
24
+ square_center_y,
25
+ circle_center_x,
26
+ circle_center_y,
27
+ circle_radius
28
+ ) {
29
+
30
+ const x = fabsf(circle_center_x - square_center_x) - 0.5;
31
+ const y = fabsf(circle_center_y - square_center_y) - 0.5;
32
+
33
+ if (x > 0) {
34
+ if (y > 0) {
35
+ return x * x + y * y < circle_radius * circle_radius;
36
+ } else {
37
+ return x < circle_radius;
38
+ }
39
+ } else {
40
+ return y < circle_radius;
41
+ }
42
+
43
+ }
44
+
45
+ /**
46
+ *
47
+ * @param {number[]} result
48
+ * @param {number} result_offset
49
+ * @param {SpatialHashGrid} grid
50
+ * @param {number} center_x
51
+ * @param {number} center_y
52
+ * @param {number} radius
53
+ * @returns {number}
54
+ */
55
+ export function shg_query_elements_circle(
56
+ result, result_offset,
57
+ grid,
58
+ center_x, center_y,
59
+ radius
60
+ ) {
61
+
62
+ // shg_query_elements_circle.visited.splice(0, shg_query_elements_circle.visited.length); // DEBUG
63
+
64
+ const scale_inverse = grid.scale_inverse;
65
+ const grid_size_x = grid.size_x;
66
+ const grid_size_y = grid.size_y;
67
+
68
+ // rasterize circle aabb
69
+
70
+ const grid_center_x = center_x * scale_inverse;
71
+ const grid_center_y = center_y * scale_inverse;
72
+
73
+ const grid_radius = radius * scale_inverse;
74
+
75
+ const aabb_x0 = max2(
76
+ Math.floor(grid_center_x - grid_radius),
77
+ 0
78
+ );
79
+ const aabb_y0 = max2(
80
+ Math.floor(grid_center_y - grid_radius),
81
+ 0
82
+ );
83
+
84
+ const aabb_x1 = min2(
85
+ Math.floor(grid_center_x + grid_radius),
86
+ grid_size_x - 1
87
+ );
88
+ const aabb_y1 = min2(
89
+ Math.floor(grid_center_y + grid_radius),
90
+ grid_size_y - 1
91
+ );
92
+
93
+ let result_count = 0;
94
+
95
+ const radius2 = radius * radius;
96
+
97
+ const element_pool = grid.element_pool;
98
+ const element_float32 = element_pool.data_float32;
99
+
100
+ for (let y = aabb_y0; y <= aabb_y1; y++) {
101
+ for (let x = aabb_x0; x <= aabb_x1; x++) {
102
+
103
+ if (unit_square_intersects_circle(
104
+ x + 0.5,
105
+ y + 0.5,
106
+ grid_center_x,
107
+ grid_center_y,
108
+ grid_radius
109
+ )) {
110
+
111
+ // cell is inside the circle
112
+
113
+ // shg_query_elements_circle.visited.push(x, y); // DEBUG
114
+
115
+
116
+ const cell_index = grid.cell_position_to_index(x, y);
117
+
118
+
119
+ let node = grid.cell_get_first_node(cell_index);
120
+
121
+ while (node !== NULL_POINTER) {
122
+
123
+ const element = grid.node_get_element(node);
124
+
125
+ // check element bounds
126
+ const element_address = element_pool.element_word(element);
127
+
128
+ const element_x0 = element_float32[element_address + COLUMN_ELEMENT_X0];
129
+ const element_y0 = element_float32[element_address + COLUMN_ELEMENT_Y0];
130
+ const element_x1 = element_float32[element_address + COLUMN_ELEMENT_X1];
131
+ const element_y1 = element_float32[element_address + COLUMN_ELEMENT_Y1];
132
+
133
+ const distance_to_center = aabb2_distance_sqr_to_point(
134
+ element_x0, element_y0,
135
+ element_x1, element_y1,
136
+ center_x, center_y
137
+ );
138
+
139
+ if (distance_to_center <= radius2) {
140
+ // intersection with the element
141
+ result[result_offset + result_count] = element;
142
+
143
+ result_count++;
144
+ }
145
+
146
+
147
+ // read out next element in the linked list
148
+ node = grid.node_get_same_cell_next_node(node);
149
+
150
+ }
151
+
152
+ }
153
+
154
+ }
155
+ }
156
+
157
+ return result_count;
158
+ }
159
+
160
+ // shg_query_elements_circle.visited = []; // DEBUG
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=shg_query_elements_circle.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shg_query_elements_circle.spec.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/2d/hash-grid/shg_query_elements_circle.spec.js"],"names":[],"mappings":""}