@woosh/meep-engine 2.163.9 → 2.163.11

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 (41) hide show
  1. package/package.json +1 -1
  2. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_capsule.d.ts +22 -0
  3. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_capsule.d.ts.map +1 -0
  4. package/src/core/bvh2/bvh3/query/bvh_query_user_data_overlaps_capsule.js +101 -0
  5. package/src/core/collection/heap/IndexedFloatMaxHeap.d.ts.map +1 -0
  6. package/src/core/{graph/metis/native/refine → collection/heap}/IndexedFloatMaxHeap.js +1 -1
  7. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_closed.d.ts +40 -0
  8. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_closed.d.ts.map +1 -0
  9. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_closed.js +67 -0
  10. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_iterative.d.ts +45 -0
  11. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_iterative.d.ts.map +1 -0
  12. package/src/core/geom/3d/capsule/capsule_intersects_aabb3_iterative.js +137 -0
  13. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +146 -0
  14. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_aabb3_t.d.ts +44 -0
  15. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_aabb3_t.d.ts.map +1 -0
  16. package/src/core/geom/3d/line/line3_compute_segment_nearest_point_to_aabb3_t.js +153 -0
  17. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.d.ts.map +1 -1
  18. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +18 -7
  19. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.d.ts +6 -0
  20. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.d.ts.map +1 -1
  21. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.js +139 -95
  22. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
  23. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +2 -26
  24. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify_by_error.d.ts +19 -0
  25. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify_by_error.d.ts.map +1 -0
  26. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify_by_error.js +555 -0
  27. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_swap_vertex_slots.d.ts +13 -0
  28. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_swap_vertex_slots.d.ts.map +1 -0
  29. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_swap_vertex_slots.js +28 -0
  30. package/src/core/graph/metis/native/bisection/BisectionScratch.d.ts +1 -1
  31. package/src/core/graph/metis/native/bisection/BisectionScratch.d.ts.map +1 -1
  32. package/src/core/graph/metis/native/bisection/BisectionScratch.js +1 -1
  33. package/src/core/graph/metis/native/refine/RefinementScratch.d.ts +1 -1
  34. package/src/core/graph/metis/native/refine/RefinementScratch.d.ts.map +1 -1
  35. package/src/core/graph/metis/native/refine/RefinementScratch.js +1 -1
  36. package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
  37. package/src/engine/navigation/mesh/build/navmesh_build_topology.js +25 -0
  38. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
  39. package/src/engine/physics/narrowphase/capsule_contacts.js +14 -21
  40. package/src/core/graph/metis/native/refine/IndexedFloatMaxHeap.d.ts.map +0 -1
  41. /package/src/core/{graph/metis/native/refine → collection/heap}/IndexedFloatMaxHeap.d.ts +0 -0
@@ -0,0 +1,153 @@
1
+ import { clamp } from "../../../math/clamp.js";
2
+ import { v3_dot } from "../../vec3/v3_dot.js";
3
+
4
+ /**
5
+ * The argmin of `g(t)=distanceSqr(P(t),box)` is an INTERVAL when the minimum is
6
+ * non-unique (segment parallel to a face, or lying inside the box — the closest
7
+ * distance is then constant along a sub-segment). We track that interval as the
8
+ * monotone sign-structure of `g'(t)` is scanned and return its MIDPOINT, so the
9
+ * reported closest point is the central one rather than an arbitrary endpoint.
10
+ * Module-level (single-threaded, non-reentrant — same convention as the
11
+ * narrowphase `g_ct_*` accumulators):
12
+ * a = (max t, g) with g' ≤ 0, b = (min t, g) with g' > 0 → t_hi (rightmost min)
13
+ * c = (max t, g) with g' < 0, d = (min t, g) with g' ≥ 0 → t_lo (leftmost min)
14
+ */
15
+ let _a_t = -1, _a_g = 0;
16
+ let _b_t = 2, _b_g = 0;
17
+ let _c_t = -1, _c_g = 0;
18
+ let _d_t = 2, _d_g = 0;
19
+
20
+ /** Fold one candidate `(t, g'(t))` into the argmin-interval trackers. */
21
+ function _consider(t, g) {
22
+ if (g <= 0 && t > _a_t) { _a_t = t; _a_g = g; }
23
+ if (g > 0 && t < _b_t) { _b_t = t; _b_g = g; }
24
+ if (g < 0 && t > _c_t) { _c_t = t; _c_g = g; }
25
+ if (g >= 0 && t < _d_t) { _d_t = t; _d_g = g; }
26
+ }
27
+
28
+ /**
29
+ * Parameter `t ∈ [0,1]` of the point on the segment `p0 → p1` nearest to an
30
+ * axis-aligned box. The closest segment point is `p0 + t·(p1 − p0)`; the closest
31
+ * box point is that point clamped per-axis into the box. Sibling of
32
+ * {@link line3_compute_segment_nearest_point_to_point_t} (segment-vs-point).
33
+ *
34
+ * Exact and O(1). `g(t) = distanceSqr(P(t), box)` is convex and — for an AABB —
35
+ * piecewise-quadratic, with pieces meeting only where `P(t)` crosses one of the
36
+ * six box face planes (≤6 crossings). `g'(t) ∝ (P(t) − clamp(P(t), box)) · d` is
37
+ * therefore piecewise-linear and monotonically non-decreasing, so the minimiser
38
+ * is found in closed form by evaluating `g'` at the ≤8 candidates
39
+ * `{0, 1, face-plane crossings}`: the minimum is achieved on the interval where
40
+ * `g' = 0` (a single point when strictly convex), and this returns that
41
+ * interval's midpoint. No iteration, no tolerance, no Cramer's-Rule solve.
42
+ *
43
+ * When the minimum is non-unique (segment parallel to / inside the box) the
44
+ * midpoint is the *central* closest point — what the narrowphase
45
+ * `capsule_box_contact` needs for a stable resting manifold; a degenerate
46
+ * (`p0 === p1`) segment returns `0` (its parameter is arbitrary).
47
+ *
48
+ * The AABB specialisation of the Eberly/Ericson closest-point-segment-vs-box
49
+ * reduction. Backs {@link capsule_intersects_aabb3_closed} and the narrowphase
50
+ * `capsule_box_contact`; it replaced a 2-iteration alternating projection that
51
+ * under-detected and mis-placed contacts for segments near-parallel to a box
52
+ * face/edge (see `core/geom/3d/gjk/GJK_REVIEW_NOTES.md`).
53
+ *
54
+ * Box bounds are assumed ordered per axis (`box_*0 ≤ box_*1`).
55
+ *
56
+ * @param {number} p0_x segment start x
57
+ * @param {number} p0_y
58
+ * @param {number} p0_z
59
+ * @param {number} p1_x segment end x
60
+ * @param {number} p1_y
61
+ * @param {number} p1_z
62
+ * @param {number} box_x0 box min x
63
+ * @param {number} box_y0
64
+ * @param {number} box_z0
65
+ * @param {number} box_x1 box max x
66
+ * @param {number} box_y1
67
+ * @param {number} box_z1
68
+ * @returns {number} t in [0, 1]
69
+ */
70
+ export function line3_compute_segment_nearest_point_to_aabb3_t(
71
+ p0_x, p0_y, p0_z,
72
+ p1_x, p1_y, p1_z,
73
+ box_x0, box_y0, box_z0,
74
+ box_x1, box_y1, box_z1
75
+ ) {
76
+ const d_x = p1_x - p0_x;
77
+ const d_y = p1_y - p0_y;
78
+ const d_z = p1_z - p0_z;
79
+
80
+ if (d_x === 0 && d_y === 0 && d_z === 0) {
81
+ // Degenerate segment (a point) — its parameter is arbitrary.
82
+ return 0;
83
+ }
84
+
85
+ _a_t = -1; _a_g = 0;
86
+ _b_t = 2; _b_g = 0;
87
+ _c_t = -1; _c_g = 0;
88
+ _d_t = 2; _d_g = 0;
89
+
90
+ // Endpoint candidates.
91
+ _consider(0, v3_dot(
92
+ p0_x - clamp(p0_x, box_x0, box_x1),
93
+ p0_y - clamp(p0_y, box_y0, box_y1),
94
+ p0_z - clamp(p0_z, box_z0, box_z1),
95
+ d_x, d_y, d_z
96
+ ));
97
+ _consider(1, v3_dot(
98
+ p1_x - clamp(p1_x, box_x0, box_x1),
99
+ p1_y - clamp(p1_y, box_y0, box_y1),
100
+ p1_z - clamp(p1_z, box_z0, box_z1),
101
+ d_x, d_y, d_z
102
+ ));
103
+
104
+ // Face-plane crossing candidates (where an axis enters/leaves its slab).
105
+ for (let axis = 0; axis < 3; axis++) {
106
+ const d_a = axis === 0 ? d_x : (axis === 1 ? d_y : d_z);
107
+ if (d_a === 0) {
108
+ continue;
109
+ }
110
+ const p0_a = axis === 0 ? p0_x : (axis === 1 ? p0_y : p0_z);
111
+ const lo_a = axis === 0 ? box_x0 : (axis === 1 ? box_y0 : box_z0);
112
+ const hi_a = axis === 0 ? box_x1 : (axis === 1 ? box_y1 : box_z1);
113
+ const inv_d = 1 / d_a;
114
+
115
+ for (let side = 0; side < 2; side++) {
116
+ const tc = ((side === 0 ? lo_a : hi_a) - p0_a) * inv_d;
117
+ if (tc <= 0 || tc >= 1) {
118
+ continue;
119
+ }
120
+ const c_x = p0_x + d_x * tc;
121
+ const c_y = p0_y + d_y * tc;
122
+ const c_z = p0_z + d_z * tc;
123
+ _consider(tc, v3_dot(
124
+ c_x - clamp(c_x, box_x0, box_x1),
125
+ c_y - clamp(c_y, box_y0, box_y1),
126
+ c_z - clamp(c_z, box_z0, box_z1),
127
+ d_x, d_y, d_z
128
+ ));
129
+ }
130
+ }
131
+
132
+ // Rightmost minimiser: where g' last reaches 0 (g' linear between a and b).
133
+ let t_hi;
134
+ if (_a_t < 0) {
135
+ t_hi = 0; // g' > 0 everywhere → minimum at the p0 end
136
+ } else if (_b_t > 1) {
137
+ t_hi = 1; // g' ≤ 0 everywhere → minimum at the p1 end
138
+ } else {
139
+ t_hi = _a_t - _a_g * (_b_t - _a_t) / (_b_g - _a_g);
140
+ }
141
+
142
+ // Leftmost minimiser: where g' first reaches 0.
143
+ let t_lo;
144
+ if (_d_t > 1) {
145
+ t_lo = 1; // g' < 0 everywhere → minimum at the p1 end
146
+ } else if (_c_t < 0) {
147
+ t_lo = 0; // g' ≥ 0 everywhere → minimum at the p0 end
148
+ } else {
149
+ t_lo = _c_t - _c_g * (_d_t - _c_t) / (_d_g - _c_g);
150
+ }
151
+
152
+ return (t_lo + t_hi) * 0.5;
153
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"BinaryTopology.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/geom/3d/topology/struct/binary/BinaryTopology.js"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,2BAFU,MAAM,CAEuB;AAQvC;;;GAGG;AACH;IACI;;;;;;;;OAQG;IACH,sBAA4F;IAE5F;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,oBAAsD;IAEtD;;;;;;;;;;;;OAYG;IACH,oBAAsD;IAEtD;;;;;;OAMG;IACH,oBAAsE;IAEtE;;;;;OAKG;IACH,2BAA0B;IAE1B;;;;OAIG;IACH,yBAAwB;IAExB;;;;OAIG;IACH,yBAAwB;IAExB;;;;OAIG;IACH,yBAAwB;IAGxB;;;OAGG;IACH,uBAYC;IAED;;;OAGG;IACH,aAUC;IAED;;;OAGG;IACH,kCAEC;IAED;;;OAGG;IACH,+BAEC;IAED;;;OAGG;IACH,+BAEC;IAED;;;OAGG;IACH,+BAEC;IAED;;;;;;OAMG;IACH,qCAEC;IAED;;;OAGG;IACH,mCAEC;IAED;;;OAGG;IACH,mCAEC;IAED;;;OAGG;IACH,mCAEC;IAED;;;;;;;;;;OAUG;IACH,oCAFW,MAAM,QAKhB;IAED;;;OAGG;IACH,kCAFW,MAAM,QAKhB;IAED;;;OAGG;IACH,kCAFW,MAAM,QAKhB;IAED;;;OAGG;IACH,kCAFW,MAAM,QAKhB;IAED;;OAEG;IACH,sBAFa,OAAO,CAQnB;IAED;;OAEG;IACH,oBAFa,OAAO,CAQnB;IAED;;OAEG;IACH,oBAFa,OAAO,CAQnB;IAED;;OAEG;IACH,oBAFa,OAAO,CAQnB;IAED;;;;;OAKG;IACH,2BAWC;IAGD;;OAEG;IACH,cAUC;IAED;;;;;OAKG;IACH,+BAJW,MAAM,EAAE,GAAC,UAAU,MAAM,CAAC,GAAC,YAAY,iBACvC,MAAM,MACN,MAAM,QAQhB;IAED;;;;;OAKG;IACH,4BAJW,MAAM,SACN,MAAM,EAAE,GAAC,YAAY,gBACrB,MAAM,QAQhB;IAED;;;;;OAKG;IACH,2BAJW,MAAM,EAAE,GAAC,UAAU,MAAM,CAAC,GAAC,YAAY,iBACvC,MAAM,MACN,MAAM,QAQhB;IAED;;;;;OAKG;IACH,wBAJW,MAAM,SACN,MAAM,EAAE,GAAC,YAAY,gBACrB,MAAM,QAQhB;IAED;;;OAGG;IACH,qBAHW,MAAM,GACJ,MAAM,CAQlB;IAED;;;;OAIG;IACH,sBAFW,MAAM,WADN,MAAM,QAQhB;IAED;;;OAGG;IACH,sBAHW,MAAM,GACJ,MAAM,CAQlB;IAED;;;;OAIG;IACH,uBAFW,MAAM,SADN,MAAM,QAQhB;IAED;;;OAGG;IACH,sBAHW,MAAM,GACJ,MAAM,CAQlB;IAED;;;;OAIG;IACH,uBAFW,MAAM,SADN,MAAM,QAQhB;IAGD;;;OAGG;IACH,mBAHW,MAAM,GACJ,MAAM,CAWlB;IAED;;;;OAIG;IACH,oBAFW,MAAM,WADN,MAAM,QAQhB;IAED;;;OAGG;IACH,2BAHW,MAAM,GACJ,MAAM,CAWlB;IAED;;;;OAIG;IACH,4BAFW,MAAM,SADN,MAAM,QAUhB;IAED;;;OAGG;IACH,2BAHW,MAAM,GACJ,MAAM,CAWlB;IAED;;;;OAIG;IACH,4BAFW,MAAM,SADN,MAAM,QAUhB;IAED;;;OAGG;IACH,2BAHW,MAAM,GACJ,MAAM,CAWlB;IAED;;;;OAIG;IACH,4BAFW,MAAM,SADN,MAAM,QAUhB;IAED;;;OAGG;IACH,2BAHW,MAAM,GACJ,MAAM,CAWlB;IAED;;;;OAIG;IACH,4BAFW,MAAM,SADN,MAAM,QAUhB;IAED;;;;;OAKG;IACH,iBAFa,MAAM,CAQlB;IAED;;;OAGG;IACH,eAFa,MAAM,CAQlB;IAED;;;OAGG;IACH,eAFa,MAAM,CAQlB;IAED;;;;OAIG;IACH,eAFa,MAAM,CAUlB;IAED;;;OAGG;IACH,oBAFW,MAAM,QAOhB;IAED;;;OAGG;IACH,qBAHW,MAAM,GACJ,MAAM,CAQlB;IAED;;;;OAIG;IACH,sBAFW,MAAM,SADN,MAAM,QAQhB;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,MAAM,CAOlB;IAED;;;;OAIG;IACH,oBAFW,MAAM,SADN,MAAM,QAQhB;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,MAAM,CAOlB;IAED;;;;OAIG;IACH,oBAFW,MAAM,SADN,MAAM,QAUhB;IAED;;;;OAIG;IACH,0BAHW,MAAM,GACJ,MAAM,CAQlB;IAED;;;;OAIG;IACH,2BAFW,MAAM,SADN,MAAM,QAShB;IAED;;;;OAIG;IACH,0BAHW,MAAM,GACJ,MAAM,CAQlB;IAED;;;;OAIG;IACH,2BAFW,MAAM,SADN,MAAM,QAShB;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,MAAM,CAOlB;IAED;;;;OAIG;IACH,oBAFW,MAAM,SADN,MAAM,QAQhB;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,MAAM,CAOlB;IAED;;;;OAIG;IACH,oBAFW,MAAM,SADN,MAAM,QAQhB;IAED;;;;OAIG;IACH,mBAHW,MAAM,GACJ,MAAM,CAOlB;IAED;;;;OAIG;IACH,oBAFW,MAAM,SADN,MAAM,QAQhB;IAED;;;;OAIG;IACH,yBAJW,MAAM,EAAE,GAAC,UAAU,MAAM,CAAC,GAAC,YAAY,iBACvC,MAAM,MACN,MAAM,QAQhB;IAED;;;;OAIG;IACH,sBAJW,MAAM,SACN,MAAM,EAAE,GAAC,YAAY,gBACrB,MAAM,QAOhB;IAED;;;OAGG;IACH,YAFW,cAAc,QA4CxB;IAED;;;OAGG;IACH,SAFa,cAAc,CAM1B;IAIL;;;;OAIG;IACH,2BAFU,OAAO,CAEwB;CAPxC;kCAp8BiC,wBAAwB"}
1
+ {"version":3,"file":"BinaryTopology.d.ts","sourceRoot":"","sources":["../../../../../../../../src/core/geom/3d/topology/struct/binary/BinaryTopology.js"],"names":[],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { assert } from "../../../../../assert.js";
2
- import { array_copy } from "../../../../../collection/array/array_copy.js";
3
2
  import { BinaryElementPool } from "./BinaryElementPool.js";
4
3
 
5
4
  /**
@@ -358,7 +357,9 @@ export class BinaryTopology {
358
357
 
359
358
  const v_address = pool.element_address(id);
360
359
  const v_offset = v_address >> 2; // get 4-byte boundary
361
- array_copy(pool.data_float32, v_offset, result, result_offset, 3);
360
+ result[result_offset] = pool.data_float32[v_offset];
361
+ result[result_offset + 1] = pool.data_float32[v_offset + 1];
362
+ result[result_offset + 2] = pool.data_float32[v_offset + 2];
362
363
  }
363
364
 
364
365
  /**
@@ -372,7 +373,9 @@ export class BinaryTopology {
372
373
 
373
374
  const v_address = pool.element_address(id);
374
375
  const v_offset = v_address >> 2; // get 4-byte boundary
375
- array_copy(value, value_offset, pool.data_float32, v_offset, 3);
376
+ pool.data_float32[v_offset] = value[value_offset];
377
+ pool.data_float32[v_offset + 1] = value[value_offset + 1];
378
+ pool.data_float32[v_offset + 2] = value[value_offset + 2];
376
379
  }
377
380
 
378
381
  /**
@@ -386,7 +389,9 @@ export class BinaryTopology {
386
389
 
387
390
  const v_address = pool.element_address(id) + 3 * FLOAT_32_SIZE;
388
391
  const v_offset = v_address >> 2; // get 4-byte boundary
389
- array_copy(pool.data_float32, v_offset, result, result_offset, 3);
392
+ result[result_offset] = pool.data_float32[v_offset];
393
+ result[result_offset + 1] = pool.data_float32[v_offset + 1];
394
+ result[result_offset + 2] = pool.data_float32[v_offset + 2];
390
395
  }
391
396
 
392
397
  /**
@@ -400,7 +405,9 @@ export class BinaryTopology {
400
405
 
401
406
  const v_address = pool.element_address(id) + 3 * FLOAT_32_SIZE;
402
407
  const v_offset = v_address >> 2; // get 4-byte boundary
403
- array_copy(value, value_offset, pool.data_float32, v_offset, 3);
408
+ pool.data_float32[v_offset] = value[value_offset];
409
+ pool.data_float32[v_offset + 1] = value[value_offset + 1];
410
+ pool.data_float32[v_offset + 2] = value[value_offset + 2];
404
411
  }
405
412
 
406
413
  /**
@@ -891,7 +898,9 @@ export class BinaryTopology {
891
898
  // Offset by the first loop pointer (UINT_32_SIZE)
892
899
  const f_address = pool.element_address(id) + UINT_32_SIZE;
893
900
  const f_offset = f_address >> 2;
894
- array_copy(pool.data_float32, f_offset, result, result_offset, 3);
901
+ result[result_offset] = pool.data_float32[f_offset];
902
+ result[result_offset + 1] = pool.data_float32[f_offset + 1];
903
+ result[result_offset + 2] = pool.data_float32[f_offset + 2];
895
904
  }
896
905
 
897
906
  /**
@@ -903,7 +912,9 @@ export class BinaryTopology {
903
912
  const pool = this.__face_pool;
904
913
  const f_address = pool.element_address(id) + UINT_32_SIZE;
905
914
  const f_offset = f_address >> 2;
906
- array_copy(value, value_offset, pool.data_float32, f_offset, 3);
915
+ pool.data_float32[f_offset] = value[value_offset];
916
+ pool.data_float32[f_offset + 1] = value[value_offset + 1];
917
+ pool.data_float32[f_offset + 2] = value[value_offset + 2];
907
918
  }
908
919
 
909
920
  /**
@@ -11,6 +11,12 @@
11
11
  * Call AFTER an initial vertex-merge/edge-fuse, then merge + fuse again so the new split vertices weld
12
12
  * onto the T-junction vertices and the resulting duplicate edges fuse.
13
13
  *
14
+ * A BVH over the vertices replaces the naive all-vertices-per-edge scan: for each boundary edge we
15
+ * query only the vertices within the tolerance of the edge segment (a capsule), which IS the on-edge
16
+ * region, so no separate per-candidate distance test is needed. The BVH is built ONCE -- every split
17
+ * adds a vertex snapped exactly onto an existing one's position, so no candidate position is ever
18
+ * missing from the original leaf set, and the tree never needs rebuilding as edges split.
19
+ *
14
20
  * @param {BinaryTopology} mesh
15
21
  * @param {number} [tolerance] max distance from a vertex to an edge for it to count as "on" the edge
16
22
  * @returns {number} number of splits performed
@@ -1 +1 @@
1
- {"version":3,"file":"bt_mesh_resolve_t_junctions.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;GAgBG;AACH,8EAHW,MAAM,GACJ,MAAM,CAuElB"}
1
+ {"version":3,"file":"bt_mesh_resolve_t_junctions.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.js"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,8EAHW,MAAM,GACJ,MAAM,CAwGlB"}
@@ -1,95 +1,139 @@
1
- import { NULL_POINTER } from "../BinaryTopology.js";
2
- import { bt_edge_split } from "./edge/bt_edge_split.js";
3
-
4
- const _a = new Float32Array(3);
5
- const _b = new Float32Array(3);
6
- const _p = new Float32Array(3);
7
- const _hit = new Float32Array(3);
8
-
9
- /**
10
- * Resolve T-junctions: where a vertex lies on the interior of a boundary edge (rather than at one of
11
- * its endpoints), split that edge at the vertex so the two pieces can later be welded/fused into a
12
- * shared edge.
13
- *
14
- * This is required for meshes assembled from independently-authored faces of differing sizes - e.g. an
15
- * L-shaped or ring floor built from rectangles, where one rectangle's corner falls in the middle of a
16
- * neighbour's edge. Without it those faces touch only at a single vertex, so edge-based neighbour
17
- * queries (island detection, erosion, path-finding) treat them as DISCONNECTED.
18
- *
19
- * Call AFTER an initial vertex-merge/edge-fuse, then merge + fuse again so the new split vertices weld
20
- * onto the T-junction vertices and the resulting duplicate edges fuse.
21
- *
22
- * @param {BinaryTopology} mesh
23
- * @param {number} [tolerance] max distance from a vertex to an edge for it to count as "on" the edge
24
- * @returns {number} number of splits performed
25
- */
26
- export function bt_mesh_resolve_t_junctions(mesh, tolerance = 1e-4) {
27
- const tol_sq = tolerance * tolerance;
28
-
29
- let total = 0;
30
- let rounds = 0;
31
- let changed = true;
32
-
33
- while (changed && rounds < 256) {
34
- changed = false;
35
- rounds++;
36
-
37
- const edge_count = mesh.edges.size;
38
-
39
- for (let e = 0; e < edge_count; e++) {
40
- if (!mesh.edges.is_allocated(e)) continue;
41
-
42
- // only boundary edges (a single radial loop) can carry a T-junction
43
- const l = mesh.edge_read_loop(e);
44
- if (l === NULL_POINTER) continue;
45
- if (mesh.loop_read_radial_next(l) !== l) continue;
46
-
47
- const v1 = mesh.edge_read_vertex1(e);
48
- const v2 = mesh.edge_read_vertex2(e);
49
-
50
- mesh.vertex_read_coordinate(_a, 0, v1);
51
- mesh.vertex_read_coordinate(_b, 0, v2);
52
-
53
- const abx = _b[0] - _a[0], aby = _b[1] - _a[1], abz = _b[2] - _a[2];
54
- const ab_sq = abx * abx + aby * aby + abz * abz;
55
- if (ab_sq <= tol_sq) continue;
56
-
57
- const t_margin = tolerance / Math.sqrt(ab_sq);
58
-
59
- let split_t = -1;
60
- let split_v = -1;
61
-
62
- const vertex_count = mesh.vertices.size;
63
- for (let v = 0; v < vertex_count; v++) {
64
- if (v === v1 || v === v2) continue;
65
- if (!mesh.vertices.is_allocated(v)) continue;
66
-
67
- mesh.vertex_read_coordinate(_p, 0, v);
68
-
69
- const t = ((_p[0] - _a[0]) * abx + (_p[1] - _a[1]) * aby + (_p[2] - _a[2]) * abz) / ab_sq;
70
- if (t <= t_margin || t >= 1 - t_margin) continue;
71
-
72
- const cx = _a[0] + t * abx - _p[0];
73
- const cy = _a[1] + t * aby - _p[1];
74
- const cz = _a[2] + t * abz - _p[2];
75
- if (cx * cx + cy * cy + cz * cz > tol_sq) continue;
76
-
77
- split_t = t;
78
- split_v = v;
79
- break;
80
- }
81
-
82
- if (split_t >= 0) {
83
- // snapshot the on-edge vertex coordinate, split, then snap the new vertex exactly onto it
84
- mesh.vertex_read_coordinate(_hit, 0, split_v);
85
- const nv = bt_edge_split(mesh, e, split_t);
86
- mesh.vertex_write_coordinate(nv, _hit, 0);
87
-
88
- total++;
89
- changed = true;
90
- }
91
- }
92
- }
93
-
94
- return total;
95
- }
1
+ import { BVH } from "../../../../../../bvh2/bvh3/BVH.js";
2
+ import {
3
+ bvh_query_user_data_overlaps_capsule
4
+ } from "../../../../../../bvh2/bvh3/query/bvh_query_user_data_overlaps_capsule.js";
5
+ import { NULL_POINTER } from "../BinaryTopology.js";
6
+ import { bt_edge_split } from "./edge/bt_edge_split.js";
7
+
8
+ const _a = new Float32Array(3);
9
+ const _b = new Float32Array(3);
10
+ const _p = new Float32Array(3);
11
+ const _hit = new Float32Array(3);
12
+ const _capsule = new Float32Array(7);
13
+
14
+ /**
15
+ * Resolve T-junctions: where a vertex lies on the interior of a boundary edge (rather than at one of
16
+ * its endpoints), split that edge at the vertex so the two pieces can later be welded/fused into a
17
+ * shared edge.
18
+ *
19
+ * This is required for meshes assembled from independently-authored faces of differing sizes - e.g. an
20
+ * L-shaped or ring floor built from rectangles, where one rectangle's corner falls in the middle of a
21
+ * neighbour's edge. Without it those faces touch only at a single vertex, so edge-based neighbour
22
+ * queries (island detection, erosion, path-finding) treat them as DISCONNECTED.
23
+ *
24
+ * Call AFTER an initial vertex-merge/edge-fuse, then merge + fuse again so the new split vertices weld
25
+ * onto the T-junction vertices and the resulting duplicate edges fuse.
26
+ *
27
+ * A BVH over the vertices replaces the naive all-vertices-per-edge scan: for each boundary edge we
28
+ * query only the vertices within the tolerance of the edge segment (a capsule), which IS the on-edge
29
+ * region, so no separate per-candidate distance test is needed. The BVH is built ONCE -- every split
30
+ * adds a vertex snapped exactly onto an existing one's position, so no candidate position is ever
31
+ * missing from the original leaf set, and the tree never needs rebuilding as edges split.
32
+ *
33
+ * @param {BinaryTopology} mesh
34
+ * @param {number} [tolerance] max distance from a vertex to an edge for it to count as "on" the edge
35
+ * @returns {number} number of splits performed
36
+ */
37
+ export function bt_mesh_resolve_t_junctions(mesh, tolerance = 1e-4) {
38
+ const tol_sq = tolerance * tolerance;
39
+
40
+ // --- Build a vertex BVH over the original vertices (point leaves; user data = vertex id) ---
41
+ const vertices = mesh.vertices;
42
+ const vertex_count = vertices.size;
43
+
44
+ const bvh = new BVH();
45
+
46
+ let leaf_count = 0;
47
+ for (let v = 0; v < vertex_count; v++) {
48
+ if (!vertices.is_allocated(v)) continue;
49
+
50
+ mesh.vertex_read_coordinate(_p, 0, v);
51
+
52
+ const node = bvh.allocate_node();
53
+ bvh.node_set_user_data(node, v);
54
+ bvh.node_set_aabb_primitive(node, _p[0], _p[1], _p[2], _p[0], _p[1], _p[2]);
55
+ bvh.insert_leaf(node);
56
+
57
+ leaf_count++;
58
+ }
59
+
60
+ if (leaf_count === 0) {
61
+ return 0;
62
+ }
63
+
64
+ // The query writes one vertex id per overlapping leaf; it can never exceed the leaf count.
65
+ const candidates = new Uint32Array(leaf_count);
66
+
67
+ let total = 0;
68
+ let rounds = 0;
69
+ let changed = true;
70
+
71
+ while (changed && rounds < 256) {
72
+ changed = false;
73
+ rounds++;
74
+
75
+ const edge_count = mesh.edges.size;
76
+
77
+ for (let e = 0; e < edge_count; e++) {
78
+ if (!mesh.edges.is_allocated(e)) continue;
79
+
80
+ // only boundary edges (a single radial loop) can carry a T-junction
81
+ const l = mesh.edge_read_loop(e);
82
+ if (l === NULL_POINTER) continue;
83
+ if (mesh.loop_read_radial_next(l) !== l) continue;
84
+
85
+ const v1 = mesh.edge_read_vertex1(e);
86
+ const v2 = mesh.edge_read_vertex2(e);
87
+
88
+ mesh.vertex_read_coordinate(_a, 0, v1);
89
+ mesh.vertex_read_coordinate(_b, 0, v2);
90
+
91
+ const abx = _b[0] - _a[0], aby = _b[1] - _a[1], abz = _b[2] - _a[2];
92
+ const ab_sq = abx * abx + aby * aby + abz * abz;
93
+ if (ab_sq <= tol_sq) continue;
94
+
95
+ const t_margin = tolerance / Math.sqrt(ab_sq);
96
+
97
+ // candidate vertices: those within `tolerance` of the edge segment (a capsule)
98
+ _capsule[0] = _a[0]; _capsule[1] = _a[1]; _capsule[2] = _a[2];
99
+ _capsule[3] = _b[0]; _capsule[4] = _b[1]; _capsule[5] = _b[2];
100
+ _capsule[6] = tolerance;
101
+
102
+ const candidate_count = bvh_query_user_data_overlaps_capsule(candidates, 0, bvh, _capsule);
103
+
104
+ let split_t = -1;
105
+ let split_v = -1;
106
+
107
+ for (let i = 0; i < candidate_count; i++) {
108
+ const v = candidates[i];
109
+
110
+ // Candidates are original vertices, which are never removed during resolution (splits
111
+ // only add), so they are always allocated -- no is_allocated check needed.
112
+ if (v === v1 || v === v2) continue;
113
+
114
+ mesh.vertex_read_coordinate(_p, 0, v);
115
+
116
+ // the capsule query already guarantees distance-to-segment <= tolerance; all that is
117
+ // left is to exclude the segment's endpoints (its hemispherical end caps)
118
+ const t = ((_p[0] - _a[0]) * abx + (_p[1] - _a[1]) * aby + (_p[2] - _a[2]) * abz) / ab_sq;
119
+ if (t <= t_margin || t >= 1 - t_margin) continue;
120
+
121
+ split_t = t;
122
+ split_v = v;
123
+ break;
124
+ }
125
+
126
+ if (split_t >= 0) {
127
+ // snapshot the on-edge vertex coordinate, split, then snap the new vertex exactly onto it
128
+ mesh.vertex_read_coordinate(_hit, 0, split_v);
129
+ const nv = bt_edge_split(mesh, e, split_t);
130
+ mesh.vertex_write_coordinate(nv, _hit, 0);
131
+
132
+ total++;
133
+ changed = true;
134
+ }
135
+ }
136
+ }
137
+
138
+ return total;
139
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"bt_mesh_simplify.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js"],"names":[],"mappings":"AAqLA;;;;;;;;;;;;;;GAcG;AACH,0EAHW,MAAM,wBACN,IAAI,MAAM,CAAC,QA6HrB"}
1
+ {"version":3,"file":"bt_mesh_simplify.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js"],"names":[],"mappings":"AA6JA;;;;;;;;;;;;;;GAcG;AACH,0EAHW,MAAM,wBACN,IAAI,MAAM,CAAC,QA6HrB"}
@@ -7,6 +7,7 @@ import { bt_mesh_compute_vertex_quadratics } from "./bt_mesh_compute_vertex_quad
7
7
  import { bt_edge_collapse } from "./edge/bt_edge_collapse.js";
8
8
  import { bt_edge_kill } from "./edge/bt_edge_kill.js";
9
9
  import { bt_edge_kill_parallels } from "./edge/bt_edge_kill_parallels.js";
10
+ import { bt_edge_swap_vertex_slots } from "./edge/bt_edge_swap_vertex_slots.js";
10
11
  import { bt_vert_fuse_duplicate_edges } from "./vertex/bt_vert_fuse_duplicate_edges.js";
11
12
  import { bt_vert_kill } from "./vertex/bt_vert_kill.js";
12
13
 
@@ -50,31 +51,6 @@ function count_edge_faces(mesh, edge_id) {
50
51
  return count;
51
52
  }
52
53
 
53
- /**
54
- * Swap the v1/v2 slots of an edge. Disk cycle pointers for the two slots are swapped
55
- * alongside so that the disk cycles on both attached vertices remain valid.
56
- *
57
- * @param {BinaryTopology} mesh
58
- * @param {number} edge_id
59
- */
60
- function swap_edge_vertex_slots(mesh, edge_id) {
61
- const v1 = mesh.edge_read_vertex1(edge_id);
62
- const v2 = mesh.edge_read_vertex2(edge_id);
63
-
64
- const v1_next = mesh.edge_read_v1_disk_next(edge_id);
65
- const v1_prev = mesh.edge_read_v1_disk_prev(edge_id);
66
- const v2_next = mesh.edge_read_v2_disk_next(edge_id);
67
- const v2_prev = mesh.edge_read_v2_disk_prev(edge_id);
68
-
69
- mesh.edge_write_vertex1(edge_id, v2);
70
- mesh.edge_write_vertex2(edge_id, v1);
71
-
72
- mesh.edge_write_v1_disk_next(edge_id, v2_next);
73
- mesh.edge_write_v1_disk_prev(edge_id, v2_prev);
74
- mesh.edge_write_v2_disk_next(edge_id, v1_next);
75
- mesh.edge_write_v2_disk_prev(edge_id, v1_prev);
76
- }
77
-
78
54
  /**
79
55
  * Evaluate a potential edge collapse: choose the optimal surviving vertex position
80
56
  * and return the quadric-based cost. Returns Infinity if the edge cannot be collapsed.
@@ -285,7 +261,7 @@ export function bt_mesh_simplify(
285
261
  // bt_edge_collapse keeps v1 as the survivor. If v2 is the pinned endpoint,
286
262
  // swap the edge's slots so the pinned vertex ends up in the v1 position.
287
263
  if (v2_restricted) {
288
- swap_edge_vertex_slots(mesh, edge_id);
264
+ bt_edge_swap_vertex_slots(mesh, edge_id);
289
265
  }
290
266
 
291
267
  const pre_survivor = mesh.edge_read_vertex1(edge_id);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Clean up a triangle mesh in place by removing only geometrically redundant vertices, so the
3
+ * surface and its boundary are preserved to within `max_error`. Unlike {@link bt_mesh_simplify}
4
+ * (quadric LoD decimation driven to a face-count target, free to relocate vertices), this is a
5
+ * conservative half-edge cleanup: it never moves a surviving vertex and never changes the
6
+ * boundary outline or hole topology by more than `max_error`. It removes the dense interior of
7
+ * flat/near-coplanar regions and collinear runs of boundary vertices, and stops once the
8
+ * cheapest remaining collapse would exceed the tolerance.
9
+ *
10
+ * Intended for navmesh post-processing, where the input is a walkable surface whose outline and
11
+ * holes (obstacle footprints) carry meaning and must not drift, but whose interior inherits far
12
+ * more triangles than navigation needs.
13
+ *
14
+ * @param {BinaryTopology} mesh Mesh to clean up in-place.
15
+ * @param {number} max_error Maximum world-space deviation allowed for any removed vertex and for
16
+ * the boundary polyline. A no-op when <= 0.
17
+ */
18
+ export function bt_mesh_simplify_by_error(mesh: BinaryTopology, max_error: number): void;
19
+ //# sourceMappingURL=bt_mesh_simplify_by_error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bt_mesh_simplify_by_error.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify_by_error.js"],"names":[],"mappings":"AA4aA;;;;;;;;;;;;;;;;GAgBG;AACH,2EAHW,MAAM,QAgHhB"}