forgecad 0.10.3 → 0.10.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 (99) hide show
  1. package/dist/assets/{AdminPage-CK7ObBz3.js → AdminPage-B3L3W1Uo.js} +1 -1
  2. package/dist/assets/{BenchmarkPage-Ds7Z2doN.js → BenchmarkPage-DXKVXMrJ.js} +2 -2
  3. package/dist/assets/{BlogPage-DlPbpt6A.js → BlogPage-B7BWxOCg.js} +1 -1
  4. package/dist/assets/{DocsPage-vZb3b3Y0.js → DocsPage-BPGGwht1.js} +28 -43
  5. package/dist/assets/{EditorApp-HLoKfe15.js → EditorApp-BWUGCdD5.js} +49 -16
  6. package/dist/assets/{EmbedViewer--KnqBKrJ.js → EmbedViewer-DygByZS2.js} +2 -2
  7. package/dist/assets/{LandingPageProofDriven-C_LssmnA.js → LandingPageProofDriven-BoVE7JGY.js} +54 -36
  8. package/dist/assets/{LegalPage-DGsyo4n1.js → LegalPage-Din8wv8d.js} +2 -2
  9. package/dist/assets/{PricingPage-BOE27B-R.js → PricingPage-C2PMzmDc.js} +2 -2
  10. package/dist/assets/{SettingsPage-f47cnk39.js → SettingsPage-BlJDCRe8.js} +1 -1
  11. package/dist/assets/{app-D6ccu2Xx.js → app-BsRYSfxY.js} +238 -3714
  12. package/dist/assets/{backendInit-DbTkQN9J.js → backendInit-6C0DLgH0.js} +5972 -1566
  13. package/dist/assets/cli/{render-BsngirjC.js → render-XXol_ET7.js} +724 -112
  14. package/dist/assets/{constructionHistoryWorker-PCwXrTDB.js → constructionHistoryWorker-cTHWRJEi.js} +528 -252
  15. package/dist/assets/{evalWorker-CS63PfZu.js → evalWorker-BssDYW9u.js} +1453 -902
  16. package/dist/assets/{inspectWorker-Y4cOzNyA.js → inspectWorker-ymhBV4Ll.js} +2635 -1024
  17. package/dist/assets/{jointPose-AMvCywzS.js → jointPose-B0blBj9A.js} +1 -1
  18. package/dist/assets/{landing-proof-driven-ORyigZ6p.css → landing-proof-driven-Cpf-MIbI.css} +73 -13
  19. package/dist/assets/{manifold-Crd_F2qx.js → manifold-B_7QXpGB.js} +1 -1
  20. package/dist/assets/{manifold-k2kRcc85.js → manifold-CNShmpEJ.js} +1 -1
  21. package/dist/assets/{manifold-CBry38ly.js → manifold-CYlIm-M6.js} +2 -2
  22. package/dist/assets/{reportWorker-CWvn0CEv.js → reportWorker-Cb5eyM7D.js} +1407 -892
  23. package/dist/cli/render.html +1 -1
  24. package/dist/docs/index.html +2 -2
  25. package/dist/docs-raw/AI/usage.md +17 -15
  26. package/dist/docs-raw/component-model.md +2 -2
  27. package/dist/docs-raw/generated/concepts.md +5 -1
  28. package/dist/docs-raw/generated/core.md +26 -0
  29. package/dist/docs-raw/generated/runtime-names.md +1 -1
  30. package/dist/docs-raw/guides/inspection-bundles.md +1 -1
  31. package/dist/docs-raw/simulation-workflow.md +1 -1
  32. package/dist/docs-raw/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
  33. package/dist/docs-raw/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
  34. package/dist/docs-raw/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
  35. package/{dist-skill/website/skills/forgecad-visual-spec.md → dist/docs-raw/skills/forgecad-image-prompt.md} +7 -7
  36. package/dist/docs-raw/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
  37. package/{dist-skill/website/skills/forgecad-project.md → dist/docs-raw/skills/forgecad-project-sync.md} +5 -5
  38. package/dist/docs-raw/skills/{forgecad-3d-reconstruction.md → forgecad-reconstruct-cad-file.md} +7 -7
  39. package/dist/docs-raw/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
  40. package/dist/docs-raw/skills/{forgecad-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
  41. package/dist/docs-raw/skills/index.md +9 -12
  42. package/dist/index.html +9 -9
  43. package/dist/llms.txt +7 -7
  44. package/dist/sitemap.xml +16 -16
  45. package/dist-cli/{check-compiler-HPF2T2FS.js → check-compiler-4RPB6SB5.js} +1 -1
  46. package/dist-cli/{check-query-propagation-HYSLTXAB.js → check-query-propagation-KN3DFQTX.js} +1 -1
  47. package/dist-cli/{chunk-WLUKAW3H.js → chunk-UHBRMYA6.js} +28802 -28152
  48. package/dist-cli/forgecad.js +660 -9
  49. package/dist-skill/CONTEXT.md +27 -1
  50. package/dist-skill/docs/generated/core.md +26 -0
  51. package/dist-skill/docs/generated/runtime-names.md +1 -1
  52. package/dist-skill/docs/guides/inspection-bundles.md +1 -1
  53. package/dist-skill/library/README.md +9 -12
  54. package/dist-skill/library/{forgecad-make-a-model → forgecad-build-model}/SKILL.md +16 -6
  55. package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/SKILL.md +4 -4
  56. package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/references/master-prompt.md +1 -1
  57. package/dist-skill/library/{forgecad-model-grader → forgecad-grade-model}/SKILL.md +6 -4
  58. package/dist-skill/library/forgecad-grade-model/agents/openai.yaml +4 -0
  59. package/dist-skill/library/{forgecad-visual-spec → forgecad-image-prompt}/SKILL.md +5 -5
  60. package/dist-skill/library/forgecad-image-prompt/agents/openai.yaml +4 -0
  61. package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/SKILL.md +4 -4
  62. package/dist-skill/library/{forgecad-project → forgecad-project-sync}/SKILL.md +3 -3
  63. package/dist-skill/library/{forgecad-3d-reconstruction → forgecad-reconstruct-cad-file}/SKILL.md +5 -5
  64. package/dist-skill/library/forgecad-reconstruct-cad-file/agents/openai.yaml +4 -0
  65. package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/SKILL.md +10 -10
  66. package/dist-skill/library/forgecad-reconstruct-from-images/agents/openai.yaml +4 -0
  67. package/dist-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/SKILL.md +4 -4
  68. package/dist-skill/website/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
  69. package/dist-skill/website/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
  70. package/dist-skill/website/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
  71. package/{dist/docs-raw/skills/forgecad-visual-spec.md → dist-skill/website/skills/forgecad-image-prompt.md} +7 -7
  72. package/dist-skill/website/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
  73. package/{dist/docs-raw/skills/forgecad-project.md → dist-skill/website/skills/forgecad-project-sync.md} +5 -5
  74. package/dist-skill/website/skills/{forgecad-3d-reconstruction.md → forgecad-reconstruct-cad-file.md} +7 -7
  75. package/dist-skill/website/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
  76. package/dist-skill/website/skills/{forgecad-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
  77. package/dist-skill/website/skills/index.md +9 -12
  78. package/examples/api/texture-projection.forge.js +75 -0
  79. package/examples/assets/uv-grid.png +0 -0
  80. package/package.json +1 -1
  81. package/dist/docs-raw/skills/forgecad-blockout-model.md +0 -49
  82. package/dist/docs-raw/skills/forgecad-component-model.md +0 -53
  83. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +0 -60
  84. package/dist-skill/library/forgecad-3d-reconstruction/agents/openai.yaml +0 -4
  85. package/dist-skill/library/forgecad-blockout-model/SKILL.md +0 -42
  86. package/dist-skill/library/forgecad-component-model/SKILL.md +0 -46
  87. package/dist-skill/library/forgecad-image-replicator/agents/openai.yaml +0 -4
  88. package/dist-skill/library/forgecad-model-grader/agents/openai.yaml +0 -4
  89. package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +0 -48
  90. package/dist-skill/library/forgecad-reconstruction-benchmark/agents/openai.yaml +0 -4
  91. package/dist-skill/library/forgecad-visual-spec/agents/openai.yaml +0 -4
  92. package/dist-skill/website/skills/forgecad-blockout-model.md +0 -49
  93. package/dist-skill/website/skills/forgecad-component-model.md +0 -53
  94. package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +0 -60
  95. /package/dist/assets/{landing-proof-driven-DiGqdtWa.js → landing-proof-driven-BxZZh5r5.js} +0 -0
  96. /package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/references/default-profiles.md +0 -0
  97. /package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/summarize_manifest.py +0 -0
  98. /package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/scripts/compare_images.py +0 -0
  99. /package/dist-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/scripts/mujoco_verify.py +0 -0
@@ -21,11 +21,14 @@ const UVMapping = 300;
21
21
  const RepeatWrapping = 1e3;
22
22
  const ClampToEdgeWrapping = 1001;
23
23
  const MirroredRepeatWrapping = 1002;
24
+ const NearestFilter = 1003;
24
25
  const LinearFilter = 1006;
25
26
  const LinearMipmapLinearFilter = 1008;
26
27
  const UnsignedByteType = 1009;
28
+ const UnsignedIntType = 1014;
27
29
  const FloatType = 1015;
28
30
  const RGBAFormat = 1023;
31
+ const RedIntegerFormat = 1029;
29
32
  const NoColorSpace = "";
30
33
  const SRGBColorSpace = "srgb";
31
34
  const LinearSRGBColorSpace = "srgb-linear";
@@ -1083,12 +1086,12 @@ class Vector2 {
1083
1086
  * @param {number} angle - The angle to rotate, in radians.
1084
1087
  * @return {Vector2} A reference to this vector.
1085
1088
  */
1086
- rotateAround(center, angle) {
1089
+ rotateAround(center2, angle) {
1087
1090
  const c2 = Math.cos(angle), s = Math.sin(angle);
1088
- const x2 = this.x - center.x;
1089
- const y2 = this.y - center.y;
1090
- this.x = x2 * c2 - y2 * s + center.x;
1091
- this.y = x2 * s + y2 * c2 + center.y;
1091
+ const x2 = this.x - center2.x;
1092
+ const y2 = this.y - center2.y;
1093
+ this.x = x2 * c2 - y2 * s + center2.x;
1094
+ this.y = x2 * s + y2 * c2 + center2.y;
1092
1095
  return this;
1093
1096
  }
1094
1097
  /**
@@ -4472,10 +4475,10 @@ class Box3 {
4472
4475
  * @param {Vector3} size - The x, y and z dimensions of the box.
4473
4476
  * @return {Box3} A reference to this bounding box.
4474
4477
  */
4475
- setFromCenterAndSize(center, size) {
4478
+ setFromCenterAndSize(center2, size) {
4476
4479
  const halfSize = _vector$b.copy(size).multiplyScalar(0.5);
4477
- this.min.copy(center).sub(halfSize);
4478
- this.max.copy(center).add(halfSize);
4480
+ this.min.copy(center2).sub(halfSize);
4481
+ this.max.copy(center2).add(halfSize);
4479
4482
  return this;
4480
4483
  }
4481
4484
  /**
@@ -4925,9 +4928,9 @@ class Sphere {
4925
4928
  * @param {Vector3} [center=(0,0,0)] - The center of the sphere
4926
4929
  * @param {number} [radius=-1] - The radius of the sphere.
4927
4930
  */
4928
- constructor(center = new Vector3(), radius = -1) {
4931
+ constructor(center2 = new Vector3(), radius = -1) {
4929
4932
  this.isSphere = true;
4930
- this.center = center;
4933
+ this.center = center2;
4931
4934
  this.radius = radius;
4932
4935
  }
4933
4936
  /**
@@ -4937,8 +4940,8 @@ class Sphere {
4937
4940
  * @param {number} radius - The radius.
4938
4941
  * @return {Sphere} A reference to this sphere.
4939
4942
  */
4940
- set(center, radius) {
4941
- this.center.copy(center);
4943
+ set(center2, radius) {
4944
+ this.center.copy(center2);
4942
4945
  this.radius = radius;
4943
4946
  return this;
4944
4947
  }
@@ -4953,15 +4956,15 @@ class Sphere {
4953
4956
  * @return {Sphere} A reference to this sphere.
4954
4957
  */
4955
4958
  setFromPoints(points, optionalCenter) {
4956
- const center = this.center;
4959
+ const center2 = this.center;
4957
4960
  if (optionalCenter !== void 0) {
4958
- center.copy(optionalCenter);
4961
+ center2.copy(optionalCenter);
4959
4962
  } else {
4960
- _box$3.setFromPoints(points).getCenter(center);
4963
+ _box$3.setFromPoints(points).getCenter(center2);
4961
4964
  }
4962
4965
  let maxRadiusSq = 0;
4963
4966
  for (let i = 0, il = points.length; i < il; i++) {
4964
- maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i]));
4967
+ maxRadiusSq = Math.max(maxRadiusSq, center2.distanceToSquared(points[i]));
4965
4968
  }
4966
4969
  this.radius = Math.sqrt(maxRadiusSq);
4967
4970
  return this;
@@ -5178,9 +5181,9 @@ class Ray {
5178
5181
  * @param {Vector3} [origin=(0,0,0)] - The origin of the ray.
5179
5182
  * @param {Vector3} [direction=(0,0,-1)] - The (normalized) direction of the ray.
5180
5183
  */
5181
- constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) {
5184
+ constructor(origin = new Vector3(), direction2 = new Vector3(0, 0, -1)) {
5182
5185
  this.origin = origin;
5183
- this.direction = direction;
5186
+ this.direction = direction2;
5184
5187
  }
5185
5188
  /**
5186
5189
  * Sets the ray's components by copying the given values.
@@ -5189,9 +5192,9 @@ class Ray {
5189
5192
  * @param {Vector3} direction - The direction.
5190
5193
  * @return {Ray} A reference to this ray.
5191
5194
  */
5192
- set(origin, direction) {
5195
+ set(origin, direction2) {
5193
5196
  this.origin.copy(origin);
5194
- this.direction.copy(direction);
5197
+ this.direction.copy(direction2);
5195
5198
  return this;
5196
5199
  }
5197
5200
  /**
@@ -5200,9 +5203,9 @@ class Ray {
5200
5203
  * @param {Ray} ray - The ray to copy.
5201
5204
  * @return {Ray} A reference to this ray.
5202
5205
  */
5203
- copy(ray) {
5204
- this.origin.copy(ray.origin);
5205
- this.direction.copy(ray.direction);
5206
+ copy(ray2) {
5207
+ this.origin.copy(ray2.origin);
5208
+ this.direction.copy(ray2.direction);
5206
5209
  return this;
5207
5210
  }
5208
5211
  /**
@@ -5534,8 +5537,8 @@ class Ray {
5534
5537
  * @param {Ray} ray - The ray to test for equality.
5535
5538
  * @return {boolean} Whether this ray is equal with the given one.
5536
5539
  */
5537
- equals(ray) {
5538
- return ray.origin.equals(this.origin) && ray.direction.equals(this.direction);
5540
+ equals(ray2) {
5541
+ return ray2.origin.equals(this.origin) && ray2.direction.equals(this.direction);
5539
5542
  }
5540
5543
  /**
5541
5544
  * Returns a new ray with copied values from this instance.
@@ -8069,10 +8072,10 @@ class Triangle {
8069
8072
  * @param {Vector3} direction - The (normalized) direction vector.
8070
8073
  * @return {boolean} Whether the triangle is oriented towards the given direction or not.
8071
8074
  */
8072
- static isFrontFacing(a2, b, c2, direction) {
8075
+ static isFrontFacing(a2, b, c2, direction2) {
8073
8076
  _v0$1.subVectors(c2, b);
8074
8077
  _v1$3.subVectors(a2, b);
8075
- return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false;
8078
+ return _v0$1.cross(_v1$3).dot(direction2) < 0 ? true : false;
8076
8079
  }
8077
8080
  /**
8078
8081
  * Sets the triangle's vertices by copying the given values.
@@ -8217,8 +8220,8 @@ class Triangle {
8217
8220
  * @param {Vector3} direction - The (normalized) direction vector.
8218
8221
  * @return {boolean} Whether the triangle is oriented towards the given direction or not.
8219
8222
  */
8220
- isFrontFacing(direction) {
8221
- return Triangle.isFrontFacing(this.a, this.b, this.c, direction);
8223
+ isFrontFacing(direction2) {
8224
+ return Triangle.isFrontFacing(this.a, this.b, this.c, direction2);
8222
8225
  }
8223
8226
  /**
8224
8227
  * Returns `true` if this triangle intersects with the given box.
@@ -10330,7 +10333,7 @@ class BufferGeometry extends EventDispatcher {
10330
10333
  return;
10331
10334
  }
10332
10335
  if (position) {
10333
- const center = this.boundingSphere.center;
10336
+ const center2 = this.boundingSphere.center;
10334
10337
  _box$2.setFromBufferAttribute(position);
10335
10338
  if (morphAttributesPosition) {
10336
10339
  for (let i = 0, il = morphAttributesPosition.length; i < il; i++) {
@@ -10347,11 +10350,11 @@ class BufferGeometry extends EventDispatcher {
10347
10350
  }
10348
10351
  }
10349
10352
  }
10350
- _box$2.getCenter(center);
10353
+ _box$2.getCenter(center2);
10351
10354
  let maxRadiusSq = 0;
10352
10355
  for (let i = 0, il = position.count; i < il; i++) {
10353
10356
  _vector$8.fromBufferAttribute(position, i);
10354
- maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8));
10357
+ maxRadiusSq = Math.max(maxRadiusSq, center2.distanceToSquared(_vector$8));
10355
10358
  }
10356
10359
  if (morphAttributesPosition) {
10357
10360
  for (let i = 0, il = morphAttributesPosition.length; i < il; i++) {
@@ -10363,7 +10366,7 @@ class BufferGeometry extends EventDispatcher {
10363
10366
  _offset.fromBufferAttribute(position, j);
10364
10367
  _vector$8.add(_offset);
10365
10368
  }
10366
- maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$8));
10369
+ maxRadiusSq = Math.max(maxRadiusSq, center2.distanceToSquared(_vector$8));
10367
10370
  }
10368
10371
  }
10369
10372
  }
@@ -10930,12 +10933,12 @@ class Mesh extends Object3D {
10930
10933
  }
10931
10934
  }
10932
10935
  }
10933
- function checkIntersection$1(object, material, raycaster, ray, pA, pB, pC, point2) {
10936
+ function checkIntersection$1(object, material, raycaster, ray2, pA, pB, pC, point2) {
10934
10937
  let intersect2;
10935
10938
  if (material.side === BackSide) {
10936
- intersect2 = ray.intersectTriangle(pC, pB, pA, true, point2);
10939
+ intersect2 = ray2.intersectTriangle(pC, pB, pA, true, point2);
10937
10940
  } else {
10938
- intersect2 = ray.intersectTriangle(pA, pB, pC, material.side === FrontSide, point2);
10941
+ intersect2 = ray2.intersectTriangle(pA, pB, pC, material.side === FrontSide, point2);
10939
10942
  }
10940
10943
  if (intersect2 === null) return null;
10941
10944
  _intersectionPointWorld.copy(point2);
@@ -10948,11 +10951,11 @@ function checkIntersection$1(object, material, raycaster, ray, pA, pB, pC, point
10948
10951
  object
10949
10952
  };
10950
10953
  }
10951
- function checkGeometryIntersection(object, material, raycaster, ray, uv, uv1, normal, a2, b, c2) {
10954
+ function checkGeometryIntersection(object, material, raycaster, ray2, uv, uv1, normal, a2, b, c2) {
10952
10955
  object.getVertexPosition(a2, _vA$1);
10953
10956
  object.getVertexPosition(b, _vB$1);
10954
10957
  object.getVertexPosition(c2, _vC$1);
10955
- const intersection2 = checkIntersection$1(object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint$1);
10958
+ const intersection2 = checkIntersection$1(object, material, raycaster, ray2, _vA$1, _vB$1, _vC$1, _intersectionPoint$1);
10956
10959
  if (intersection2) {
10957
10960
  const barycoord = new Vector3();
10958
10961
  Triangle.getBarycoord(_intersectionPoint$1, _vA$1, _vB$1, _vC$1, barycoord);
@@ -10964,7 +10967,7 @@ function checkGeometryIntersection(object, material, raycaster, ray, uv, uv1, no
10964
10967
  }
10965
10968
  if (normal) {
10966
10969
  intersection2.normal = Triangle.getInterpolatedAttribute(normal, a2, b, c2, barycoord, new Vector3());
10967
- if (intersection2.normal.dot(ray.direction) > 0) {
10970
+ if (intersection2.normal.dot(ray2.direction) > 0) {
10968
10971
  intersection2.normal.multiplyScalar(-1);
10969
10972
  }
10970
10973
  }
@@ -10981,6 +10984,32 @@ function checkGeometryIntersection(object, material, raycaster, ray, uv, uv1, no
10981
10984
  }
10982
10985
  return intersection2;
10983
10986
  }
10987
+ class DataTexture extends Texture {
10988
+ /**
10989
+ * Constructs a new data texture.
10990
+ *
10991
+ * @param {?TypedArray} [data=null] - The buffer data.
10992
+ * @param {number} [width=1] - The width of the texture.
10993
+ * @param {number} [height=1] - The height of the texture.
10994
+ * @param {number} [format=RGBAFormat] - The texture format.
10995
+ * @param {number} [type=UnsignedByteType] - The texture type.
10996
+ * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping.
10997
+ * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value.
10998
+ * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value.
10999
+ * @param {number} [magFilter=NearestFilter] - The mag filter value.
11000
+ * @param {number} [minFilter=NearestFilter] - The min filter value.
11001
+ * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value.
11002
+ * @param {string} [colorSpace=NoColorSpace] - The color space.
11003
+ */
11004
+ constructor(data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace) {
11005
+ super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace);
11006
+ this.isDataTexture = true;
11007
+ this.image = { data, width, height };
11008
+ this.generateMipmaps = false;
11009
+ this.flipY = false;
11010
+ this.unpackAlignment = 1;
11011
+ }
11012
+ }
10984
11013
  const _vector1 = /* @__PURE__ */ new Vector3();
10985
11014
  const _vector2 = /* @__PURE__ */ new Vector3();
10986
11015
  const _normalMatrix = /* @__PURE__ */ new Matrix3();
@@ -11121,8 +11150,8 @@ class Plane {
11121
11150
  * @return {?Vector3} The intersection point.
11122
11151
  */
11123
11152
  intersectLine(line, target) {
11124
- const direction = line.delta(_vector1);
11125
- const denominator = this.normal.dot(direction);
11153
+ const direction2 = line.delta(_vector1);
11154
+ const denominator = this.normal.dot(direction2);
11126
11155
  if (denominator === 0) {
11127
11156
  if (this.distanceToPoint(line.start) === 0) {
11128
11157
  return target.copy(line.start);
@@ -11133,7 +11162,7 @@ class Plane {
11133
11162
  if (t < 0 || t > 1) {
11134
11163
  return null;
11135
11164
  }
11136
- return target.copy(line.start).addScaledVector(direction, t);
11165
+ return target.copy(line.start).addScaledVector(direction2, t);
11137
11166
  }
11138
11167
  /**
11139
11168
  * Returns `true` if the given line segment intersects with (passes through) the plane.
@@ -11222,6 +11251,1144 @@ class Plane {
11222
11251
  return new this.constructor().copy(this);
11223
11252
  }
11224
11253
  }
11254
+ const _sphere$3 = /* @__PURE__ */ new Sphere();
11255
+ const _vector$6 = /* @__PURE__ */ new Vector3();
11256
+ class Frustum {
11257
+ /**
11258
+ * Constructs a new frustum.
11259
+ *
11260
+ * @param {Plane} [p0] - The first plane that encloses the frustum.
11261
+ * @param {Plane} [p1] - The second plane that encloses the frustum.
11262
+ * @param {Plane} [p2] - The third plane that encloses the frustum.
11263
+ * @param {Plane} [p3] - The fourth plane that encloses the frustum.
11264
+ * @param {Plane} [p4] - The fifth plane that encloses the frustum.
11265
+ * @param {Plane} [p5] - The sixth plane that encloses the frustum.
11266
+ */
11267
+ constructor(p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane()) {
11268
+ this.planes = [p0, p1, p2, p3, p4, p5];
11269
+ }
11270
+ /**
11271
+ * Sets the frustum planes by copying the given planes.
11272
+ *
11273
+ * @param {Plane} [p0] - The first plane that encloses the frustum.
11274
+ * @param {Plane} [p1] - The second plane that encloses the frustum.
11275
+ * @param {Plane} [p2] - The third plane that encloses the frustum.
11276
+ * @param {Plane} [p3] - The fourth plane that encloses the frustum.
11277
+ * @param {Plane} [p4] - The fifth plane that encloses the frustum.
11278
+ * @param {Plane} [p5] - The sixth plane that encloses the frustum.
11279
+ * @return {Frustum} A reference to this frustum.
11280
+ */
11281
+ set(p0, p1, p2, p3, p4, p5) {
11282
+ const planes = this.planes;
11283
+ planes[0].copy(p0);
11284
+ planes[1].copy(p1);
11285
+ planes[2].copy(p2);
11286
+ planes[3].copy(p3);
11287
+ planes[4].copy(p4);
11288
+ planes[5].copy(p5);
11289
+ return this;
11290
+ }
11291
+ /**
11292
+ * Copies the values of the given frustum to this instance.
11293
+ *
11294
+ * @param {Frustum} frustum - The frustum to copy.
11295
+ * @return {Frustum} A reference to this frustum.
11296
+ */
11297
+ copy(frustum) {
11298
+ const planes = this.planes;
11299
+ for (let i = 0; i < 6; i++) {
11300
+ planes[i].copy(frustum.planes[i]);
11301
+ }
11302
+ return this;
11303
+ }
11304
+ /**
11305
+ * Sets the frustum planes from the given projection matrix.
11306
+ *
11307
+ * @param {Matrix4} m - The projection matrix.
11308
+ * @param {(WebGLCoordinateSystem|WebGPUCoordinateSystem)} coordinateSystem - The coordinate system.
11309
+ * @return {Frustum} A reference to this frustum.
11310
+ */
11311
+ setFromProjectionMatrix(m2, coordinateSystem = WebGLCoordinateSystem) {
11312
+ const planes = this.planes;
11313
+ const me = m2.elements;
11314
+ const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3];
11315
+ const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7];
11316
+ const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11];
11317
+ const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15];
11318
+ planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize();
11319
+ planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize();
11320
+ planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize();
11321
+ planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize();
11322
+ planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize();
11323
+ if (coordinateSystem === WebGLCoordinateSystem) {
11324
+ planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize();
11325
+ } else if (coordinateSystem === WebGPUCoordinateSystem) {
11326
+ planes[5].setComponents(me2, me6, me10, me14).normalize();
11327
+ } else {
11328
+ throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: " + coordinateSystem);
11329
+ }
11330
+ return this;
11331
+ }
11332
+ /**
11333
+ * Returns `true` if the 3D object's bounding sphere is intersecting this frustum.
11334
+ *
11335
+ * Note that the 3D object must have a geometry so that the bounding sphere can be calculated.
11336
+ *
11337
+ * @param {Object3D} object - The 3D object to test.
11338
+ * @return {boolean} Whether the 3D object's bounding sphere is intersecting this frustum or not.
11339
+ */
11340
+ intersectsObject(object) {
11341
+ if (object.boundingSphere !== void 0) {
11342
+ if (object.boundingSphere === null) object.computeBoundingSphere();
11343
+ _sphere$3.copy(object.boundingSphere).applyMatrix4(object.matrixWorld);
11344
+ } else {
11345
+ const geometry = object.geometry;
11346
+ if (geometry.boundingSphere === null) geometry.computeBoundingSphere();
11347
+ _sphere$3.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld);
11348
+ }
11349
+ return this.intersectsSphere(_sphere$3);
11350
+ }
11351
+ /**
11352
+ * Returns `true` if the given sprite is intersecting this frustum.
11353
+ *
11354
+ * @param {Sprite} sprite - The sprite to test.
11355
+ * @return {boolean} Whether the sprite is intersecting this frustum or not.
11356
+ */
11357
+ intersectsSprite(sprite) {
11358
+ _sphere$3.center.set(0, 0, 0);
11359
+ _sphere$3.radius = 0.7071067811865476;
11360
+ _sphere$3.applyMatrix4(sprite.matrixWorld);
11361
+ return this.intersectsSphere(_sphere$3);
11362
+ }
11363
+ /**
11364
+ * Returns `true` if the given bounding sphere is intersecting this frustum.
11365
+ *
11366
+ * @param {Sphere} sphere - The bounding sphere to test.
11367
+ * @return {boolean} Whether the bounding sphere is intersecting this frustum or not.
11368
+ */
11369
+ intersectsSphere(sphere) {
11370
+ const planes = this.planes;
11371
+ const center2 = sphere.center;
11372
+ const negRadius = -sphere.radius;
11373
+ for (let i = 0; i < 6; i++) {
11374
+ const distance = planes[i].distanceToPoint(center2);
11375
+ if (distance < negRadius) {
11376
+ return false;
11377
+ }
11378
+ }
11379
+ return true;
11380
+ }
11381
+ /**
11382
+ * Returns `true` if the given bounding box is intersecting this frustum.
11383
+ *
11384
+ * @param {Box3} box - The bounding box to test.
11385
+ * @return {boolean} Whether the bounding box is intersecting this frustum or not.
11386
+ */
11387
+ intersectsBox(box) {
11388
+ const planes = this.planes;
11389
+ for (let i = 0; i < 6; i++) {
11390
+ const plane = planes[i];
11391
+ _vector$6.x = plane.normal.x > 0 ? box.max.x : box.min.x;
11392
+ _vector$6.y = plane.normal.y > 0 ? box.max.y : box.min.y;
11393
+ _vector$6.z = plane.normal.z > 0 ? box.max.z : box.min.z;
11394
+ if (plane.distanceToPoint(_vector$6) < 0) {
11395
+ return false;
11396
+ }
11397
+ }
11398
+ return true;
11399
+ }
11400
+ /**
11401
+ * Returns `true` if the given point lies within the frustum.
11402
+ *
11403
+ * @param {Vector3} point - The point to test.
11404
+ * @return {boolean} Whether the point lies within this frustum or not.
11405
+ */
11406
+ containsPoint(point2) {
11407
+ const planes = this.planes;
11408
+ for (let i = 0; i < 6; i++) {
11409
+ if (planes[i].distanceToPoint(point2) < 0) {
11410
+ return false;
11411
+ }
11412
+ }
11413
+ return true;
11414
+ }
11415
+ /**
11416
+ * Returns a new frustum with copied values from this instance.
11417
+ *
11418
+ * @return {Frustum} A clone of this instance.
11419
+ */
11420
+ clone() {
11421
+ return new this.constructor().copy(this);
11422
+ }
11423
+ }
11424
+ function ascIdSort(a2, b) {
11425
+ return a2 - b;
11426
+ }
11427
+ function sortOpaque(a2, b) {
11428
+ return a2.z - b.z;
11429
+ }
11430
+ function sortTransparent(a2, b) {
11431
+ return b.z - a2.z;
11432
+ }
11433
+ class MultiDrawRenderList {
11434
+ constructor() {
11435
+ this.index = 0;
11436
+ this.pool = [];
11437
+ this.list = [];
11438
+ }
11439
+ push(start, count, z2, index2) {
11440
+ const pool = this.pool;
11441
+ const list = this.list;
11442
+ if (this.index >= pool.length) {
11443
+ pool.push({
11444
+ start: -1,
11445
+ count: -1,
11446
+ z: -1,
11447
+ index: -1
11448
+ });
11449
+ }
11450
+ const item = pool[this.index];
11451
+ list.push(item);
11452
+ this.index++;
11453
+ item.start = start;
11454
+ item.count = count;
11455
+ item.z = z2;
11456
+ item.index = index2;
11457
+ }
11458
+ reset() {
11459
+ this.list.length = 0;
11460
+ this.index = 0;
11461
+ }
11462
+ }
11463
+ const _matrix$1 = /* @__PURE__ */ new Matrix4();
11464
+ const _whiteColor = /* @__PURE__ */ new Color(1, 1, 1);
11465
+ const _frustum = /* @__PURE__ */ new Frustum();
11466
+ const _box$1 = /* @__PURE__ */ new Box3();
11467
+ const _sphere$2 = /* @__PURE__ */ new Sphere();
11468
+ const _vector$5 = /* @__PURE__ */ new Vector3();
11469
+ const _forward = /* @__PURE__ */ new Vector3();
11470
+ const _temp = /* @__PURE__ */ new Vector3();
11471
+ const _renderList = /* @__PURE__ */ new MultiDrawRenderList();
11472
+ const _mesh$1 = /* @__PURE__ */ new Mesh();
11473
+ const _batchIntersects$1 = [];
11474
+ function copyAttributeData(src, target, targetOffset = 0) {
11475
+ const itemSize = target.itemSize;
11476
+ if (src.isInterleavedBufferAttribute || src.array.constructor !== target.array.constructor) {
11477
+ const vertexCount = src.count;
11478
+ for (let i = 0; i < vertexCount; i++) {
11479
+ for (let c2 = 0; c2 < itemSize; c2++) {
11480
+ target.setComponent(i + targetOffset, c2, src.getComponent(i, c2));
11481
+ }
11482
+ }
11483
+ } else {
11484
+ target.array.set(src.array, targetOffset * itemSize);
11485
+ }
11486
+ target.needsUpdate = true;
11487
+ }
11488
+ function copyArrayContents(src, target) {
11489
+ if (src.constructor !== target.constructor) {
11490
+ const len = Math.min(src.length, target.length);
11491
+ for (let i = 0; i < len; i++) {
11492
+ target[i] = src[i];
11493
+ }
11494
+ } else {
11495
+ const len = Math.min(src.length, target.length);
11496
+ target.set(new src.constructor(src.buffer, 0, len));
11497
+ }
11498
+ }
11499
+ class BatchedMesh extends Mesh {
11500
+ /**
11501
+ * Constructs a new batched mesh.
11502
+ *
11503
+ * @param {number} maxInstanceCount - The maximum number of individual instances planned to be added and rendered.
11504
+ * @param {number} maxVertexCount - The maximum number of vertices to be used by all unique geometries.
11505
+ * @param {number} [maxIndexCount=maxVertexCount*2] - The maximum number of indices to be used by all unique geometries
11506
+ * @param {Material|Array<Material>} [material] - The mesh material.
11507
+ */
11508
+ constructor(maxInstanceCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material) {
11509
+ super(new BufferGeometry(), material);
11510
+ this.isBatchedMesh = true;
11511
+ this.perObjectFrustumCulled = true;
11512
+ this.sortObjects = true;
11513
+ this.boundingBox = null;
11514
+ this.boundingSphere = null;
11515
+ this.customSort = null;
11516
+ this._instanceInfo = [];
11517
+ this._geometryInfo = [];
11518
+ this._availableInstanceIds = [];
11519
+ this._availableGeometryIds = [];
11520
+ this._nextIndexStart = 0;
11521
+ this._nextVertexStart = 0;
11522
+ this._geometryCount = 0;
11523
+ this._visibilityChanged = true;
11524
+ this._geometryInitialized = false;
11525
+ this._maxInstanceCount = maxInstanceCount;
11526
+ this._maxVertexCount = maxVertexCount;
11527
+ this._maxIndexCount = maxIndexCount;
11528
+ this._multiDrawCounts = new Int32Array(maxInstanceCount);
11529
+ this._multiDrawStarts = new Int32Array(maxInstanceCount);
11530
+ this._multiDrawCount = 0;
11531
+ this._multiDrawInstances = null;
11532
+ this._matricesTexture = null;
11533
+ this._indirectTexture = null;
11534
+ this._colorsTexture = null;
11535
+ this._initMatricesTexture();
11536
+ this._initIndirectTexture();
11537
+ }
11538
+ /**
11539
+ * The maximum number of individual instances that can be stored in the batch.
11540
+ *
11541
+ * @type {number}
11542
+ * @readonly
11543
+ */
11544
+ get maxInstanceCount() {
11545
+ return this._maxInstanceCount;
11546
+ }
11547
+ /**
11548
+ * The instance count.
11549
+ *
11550
+ * @type {number}
11551
+ * @readonly
11552
+ */
11553
+ get instanceCount() {
11554
+ return this._instanceInfo.length - this._availableInstanceIds.length;
11555
+ }
11556
+ /**
11557
+ * The number of unused vertices.
11558
+ *
11559
+ * @type {number}
11560
+ * @readonly
11561
+ */
11562
+ get unusedVertexCount() {
11563
+ return this._maxVertexCount - this._nextVertexStart;
11564
+ }
11565
+ /**
11566
+ * The number of unused indices.
11567
+ *
11568
+ * @type {number}
11569
+ * @readonly
11570
+ */
11571
+ get unusedIndexCount() {
11572
+ return this._maxIndexCount - this._nextIndexStart;
11573
+ }
11574
+ _initMatricesTexture() {
11575
+ let size = Math.sqrt(this._maxInstanceCount * 4);
11576
+ size = Math.ceil(size / 4) * 4;
11577
+ size = Math.max(size, 4);
11578
+ const matricesArray = new Float32Array(size * size * 4);
11579
+ const matricesTexture = new DataTexture(matricesArray, size, size, RGBAFormat, FloatType);
11580
+ this._matricesTexture = matricesTexture;
11581
+ }
11582
+ _initIndirectTexture() {
11583
+ let size = Math.sqrt(this._maxInstanceCount);
11584
+ size = Math.ceil(size);
11585
+ const indirectArray = new Uint32Array(size * size);
11586
+ const indirectTexture = new DataTexture(indirectArray, size, size, RedIntegerFormat, UnsignedIntType);
11587
+ this._indirectTexture = indirectTexture;
11588
+ }
11589
+ _initColorsTexture() {
11590
+ let size = Math.sqrt(this._maxInstanceCount);
11591
+ size = Math.ceil(size);
11592
+ const colorsArray = new Float32Array(size * size * 4).fill(1);
11593
+ const colorsTexture = new DataTexture(colorsArray, size, size, RGBAFormat, FloatType);
11594
+ colorsTexture.colorSpace = ColorManagement.workingColorSpace;
11595
+ this._colorsTexture = colorsTexture;
11596
+ }
11597
+ _initializeGeometry(reference) {
11598
+ const geometry = this.geometry;
11599
+ const maxVertexCount = this._maxVertexCount;
11600
+ const maxIndexCount = this._maxIndexCount;
11601
+ if (this._geometryInitialized === false) {
11602
+ for (const attributeName in reference.attributes) {
11603
+ const srcAttribute = reference.getAttribute(attributeName);
11604
+ const { array, itemSize, normalized } = srcAttribute;
11605
+ const dstArray = new array.constructor(maxVertexCount * itemSize);
11606
+ const dstAttribute = new BufferAttribute(dstArray, itemSize, normalized);
11607
+ geometry.setAttribute(attributeName, dstAttribute);
11608
+ }
11609
+ if (reference.getIndex() !== null) {
11610
+ const indexArray = maxVertexCount > 65535 ? new Uint32Array(maxIndexCount) : new Uint16Array(maxIndexCount);
11611
+ geometry.setIndex(new BufferAttribute(indexArray, 1));
11612
+ }
11613
+ this._geometryInitialized = true;
11614
+ }
11615
+ }
11616
+ // Make sure the geometry is compatible with the existing combined geometry attributes
11617
+ _validateGeometry(geometry) {
11618
+ const batchGeometry = this.geometry;
11619
+ if (Boolean(geometry.getIndex()) !== Boolean(batchGeometry.getIndex())) {
11620
+ throw new Error('THREE.BatchedMesh: All geometries must consistently have "index".');
11621
+ }
11622
+ for (const attributeName in batchGeometry.attributes) {
11623
+ if (!geometry.hasAttribute(attributeName)) {
11624
+ throw new Error(`THREE.BatchedMesh: Added geometry missing "${attributeName}". All geometries must have consistent attributes.`);
11625
+ }
11626
+ const srcAttribute = geometry.getAttribute(attributeName);
11627
+ const dstAttribute = batchGeometry.getAttribute(attributeName);
11628
+ if (srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized) {
11629
+ throw new Error("THREE.BatchedMesh: All attributes must have a consistent itemSize and normalized value.");
11630
+ }
11631
+ }
11632
+ }
11633
+ /**
11634
+ * Validates the instance defined by the given ID.
11635
+ *
11636
+ * @param {number} instanceId - The instance to validate.
11637
+ */
11638
+ validateInstanceId(instanceId) {
11639
+ const instanceInfo = this._instanceInfo;
11640
+ if (instanceId < 0 || instanceId >= instanceInfo.length || instanceInfo[instanceId].active === false) {
11641
+ throw new Error(`THREE.BatchedMesh: Invalid instanceId ${instanceId}. Instance is either out of range or has been deleted.`);
11642
+ }
11643
+ }
11644
+ /**
11645
+ * Validates the geometry defined by the given ID.
11646
+ *
11647
+ * @param {number} geometryId - The geometry to validate.
11648
+ */
11649
+ validateGeometryId(geometryId) {
11650
+ const geometryInfoList = this._geometryInfo;
11651
+ if (geometryId < 0 || geometryId >= geometryInfoList.length || geometryInfoList[geometryId].active === false) {
11652
+ throw new Error(`THREE.BatchedMesh: Invalid geometryId ${geometryId}. Geometry is either out of range or has been deleted.`);
11653
+ }
11654
+ }
11655
+ /**
11656
+ * Takes a sort a function that is run before render. The function takes a list of instances to
11657
+ * sort and a camera. The objects in the list include a "z" field to perform a depth-ordered sort with.
11658
+ *
11659
+ * @param {Function} func - The custom sort function.
11660
+ * @return {BatchedMesh} A reference to this batched mesh.
11661
+ */
11662
+ setCustomSort(func) {
11663
+ this.customSort = func;
11664
+ return this;
11665
+ }
11666
+ /**
11667
+ * Computes the bounding box, updating {@link BatchedMesh#boundingBox}.
11668
+ * Bounding boxes aren't computed by default. They need to be explicitly computed,
11669
+ * otherwise they are `null`.
11670
+ */
11671
+ computeBoundingBox() {
11672
+ if (this.boundingBox === null) {
11673
+ this.boundingBox = new Box3();
11674
+ }
11675
+ const boundingBox2 = this.boundingBox;
11676
+ const instanceInfo = this._instanceInfo;
11677
+ boundingBox2.makeEmpty();
11678
+ for (let i = 0, l = instanceInfo.length; i < l; i++) {
11679
+ if (instanceInfo[i].active === false) continue;
11680
+ const geometryId = instanceInfo[i].geometryIndex;
11681
+ this.getMatrixAt(i, _matrix$1);
11682
+ this.getBoundingBoxAt(geometryId, _box$1).applyMatrix4(_matrix$1);
11683
+ boundingBox2.union(_box$1);
11684
+ }
11685
+ }
11686
+ /**
11687
+ * Computes the bounding sphere, updating {@link BatchedMesh#boundingSphere}.
11688
+ * Bounding spheres aren't computed by default. They need to be explicitly computed,
11689
+ * otherwise they are `null`.
11690
+ */
11691
+ computeBoundingSphere() {
11692
+ if (this.boundingSphere === null) {
11693
+ this.boundingSphere = new Sphere();
11694
+ }
11695
+ const boundingSphere = this.boundingSphere;
11696
+ const instanceInfo = this._instanceInfo;
11697
+ boundingSphere.makeEmpty();
11698
+ for (let i = 0, l = instanceInfo.length; i < l; i++) {
11699
+ if (instanceInfo[i].active === false) continue;
11700
+ const geometryId = instanceInfo[i].geometryIndex;
11701
+ this.getMatrixAt(i, _matrix$1);
11702
+ this.getBoundingSphereAt(geometryId, _sphere$2).applyMatrix4(_matrix$1);
11703
+ boundingSphere.union(_sphere$2);
11704
+ }
11705
+ }
11706
+ /**
11707
+ * Adds a new instance to the batch using the geometry of the given ID and returns
11708
+ * a new id referring to the new instance to be used by other functions.
11709
+ *
11710
+ * @param {number} geometryId - The ID of a previously added geometry via {@link BatchedMesh#addGeometry}.
11711
+ * @return {number} The instance ID.
11712
+ */
11713
+ addInstance(geometryId) {
11714
+ const atCapacity = this._instanceInfo.length >= this.maxInstanceCount;
11715
+ if (atCapacity && this._availableInstanceIds.length === 0) {
11716
+ throw new Error("THREE.BatchedMesh: Maximum item count reached.");
11717
+ }
11718
+ const instanceInfo = {
11719
+ visible: true,
11720
+ active: true,
11721
+ geometryIndex: geometryId
11722
+ };
11723
+ let drawId = null;
11724
+ if (this._availableInstanceIds.length > 0) {
11725
+ this._availableInstanceIds.sort(ascIdSort);
11726
+ drawId = this._availableInstanceIds.shift();
11727
+ this._instanceInfo[drawId] = instanceInfo;
11728
+ } else {
11729
+ drawId = this._instanceInfo.length;
11730
+ this._instanceInfo.push(instanceInfo);
11731
+ }
11732
+ const matricesTexture = this._matricesTexture;
11733
+ _matrix$1.identity().toArray(matricesTexture.image.data, drawId * 16);
11734
+ matricesTexture.needsUpdate = true;
11735
+ const colorsTexture = this._colorsTexture;
11736
+ if (colorsTexture) {
11737
+ _whiteColor.toArray(colorsTexture.image.data, drawId * 4);
11738
+ colorsTexture.needsUpdate = true;
11739
+ }
11740
+ this._visibilityChanged = true;
11741
+ return drawId;
11742
+ }
11743
+ /**
11744
+ * Adds the given geometry to the batch and returns the associated
11745
+ * geometry id referring to it to be used in other functions.
11746
+ *
11747
+ * @param {BufferGeometry} geometry - The geometry to add.
11748
+ * @param {number} [reservedVertexCount=-1] - Optional parameter specifying the amount of
11749
+ * vertex buffer space to reserve for the added geometry. This is necessary if it is planned
11750
+ * to set a new geometry at this index at a later time that is larger than the original geometry.
11751
+ * Defaults to the length of the given geometry vertex buffer.
11752
+ * @param {number} [reservedIndexCount=-1] - Optional parameter specifying the amount of index
11753
+ * buffer space to reserve for the added geometry. This is necessary if it is planned to set a
11754
+ * new geometry at this index at a later time that is larger than the original geometry. Defaults to
11755
+ * the length of the given geometry index buffer.
11756
+ * @return {number} The geometry ID.
11757
+ */
11758
+ addGeometry(geometry, reservedVertexCount = -1, reservedIndexCount = -1) {
11759
+ this._initializeGeometry(geometry);
11760
+ this._validateGeometry(geometry);
11761
+ const geometryInfo = {
11762
+ // geometry information
11763
+ vertexStart: -1,
11764
+ vertexCount: -1,
11765
+ reservedVertexCount: -1,
11766
+ indexStart: -1,
11767
+ indexCount: -1,
11768
+ reservedIndexCount: -1,
11769
+ // draw range information
11770
+ start: -1,
11771
+ count: -1,
11772
+ // state
11773
+ boundingBox: null,
11774
+ boundingSphere: null,
11775
+ active: true
11776
+ };
11777
+ const geometryInfoList = this._geometryInfo;
11778
+ geometryInfo.vertexStart = this._nextVertexStart;
11779
+ geometryInfo.reservedVertexCount = reservedVertexCount === -1 ? geometry.getAttribute("position").count : reservedVertexCount;
11780
+ const index2 = geometry.getIndex();
11781
+ const hasIndex = index2 !== null;
11782
+ if (hasIndex) {
11783
+ geometryInfo.indexStart = this._nextIndexStart;
11784
+ geometryInfo.reservedIndexCount = reservedIndexCount === -1 ? index2.count : reservedIndexCount;
11785
+ }
11786
+ if (geometryInfo.indexStart !== -1 && geometryInfo.indexStart + geometryInfo.reservedIndexCount > this._maxIndexCount || geometryInfo.vertexStart + geometryInfo.reservedVertexCount > this._maxVertexCount) {
11787
+ throw new Error("THREE.BatchedMesh: Reserved space request exceeds the maximum buffer size.");
11788
+ }
11789
+ let geometryId;
11790
+ if (this._availableGeometryIds.length > 0) {
11791
+ this._availableGeometryIds.sort(ascIdSort);
11792
+ geometryId = this._availableGeometryIds.shift();
11793
+ geometryInfoList[geometryId] = geometryInfo;
11794
+ } else {
11795
+ geometryId = this._geometryCount;
11796
+ this._geometryCount++;
11797
+ geometryInfoList.push(geometryInfo);
11798
+ }
11799
+ this.setGeometryAt(geometryId, geometry);
11800
+ this._nextIndexStart = geometryInfo.indexStart + geometryInfo.reservedIndexCount;
11801
+ this._nextVertexStart = geometryInfo.vertexStart + geometryInfo.reservedVertexCount;
11802
+ return geometryId;
11803
+ }
11804
+ /**
11805
+ * Replaces the geometry at the given ID with the provided geometry. Throws an error if there
11806
+ * is not enough space reserved for geometry. Calling this will change all instances that are
11807
+ * rendering that geometry.
11808
+ *
11809
+ * @param {number} geometryId - The ID of the geometry that should be replaced with the given geometry.
11810
+ * @param {BufferGeometry} geometry - The new geometry.
11811
+ * @return {number} The geometry ID.
11812
+ */
11813
+ setGeometryAt(geometryId, geometry) {
11814
+ if (geometryId >= this._geometryCount) {
11815
+ throw new Error("THREE.BatchedMesh: Maximum geometry count reached.");
11816
+ }
11817
+ this._validateGeometry(geometry);
11818
+ const batchGeometry = this.geometry;
11819
+ const hasIndex = batchGeometry.getIndex() !== null;
11820
+ const dstIndex = batchGeometry.getIndex();
11821
+ const srcIndex = geometry.getIndex();
11822
+ const geometryInfo = this._geometryInfo[geometryId];
11823
+ if (hasIndex && srcIndex.count > geometryInfo.reservedIndexCount || geometry.attributes.position.count > geometryInfo.reservedVertexCount) {
11824
+ throw new Error("THREE.BatchedMesh: Reserved space not large enough for provided geometry.");
11825
+ }
11826
+ const vertexStart = geometryInfo.vertexStart;
11827
+ const reservedVertexCount = geometryInfo.reservedVertexCount;
11828
+ geometryInfo.vertexCount = geometry.getAttribute("position").count;
11829
+ for (const attributeName in batchGeometry.attributes) {
11830
+ const srcAttribute = geometry.getAttribute(attributeName);
11831
+ const dstAttribute = batchGeometry.getAttribute(attributeName);
11832
+ copyAttributeData(srcAttribute, dstAttribute, vertexStart);
11833
+ const itemSize = srcAttribute.itemSize;
11834
+ for (let i = srcAttribute.count, l = reservedVertexCount; i < l; i++) {
11835
+ const index2 = vertexStart + i;
11836
+ for (let c2 = 0; c2 < itemSize; c2++) {
11837
+ dstAttribute.setComponent(index2, c2, 0);
11838
+ }
11839
+ }
11840
+ dstAttribute.needsUpdate = true;
11841
+ dstAttribute.addUpdateRange(vertexStart * itemSize, reservedVertexCount * itemSize);
11842
+ }
11843
+ if (hasIndex) {
11844
+ const indexStart = geometryInfo.indexStart;
11845
+ const reservedIndexCount = geometryInfo.reservedIndexCount;
11846
+ geometryInfo.indexCount = geometry.getIndex().count;
11847
+ for (let i = 0; i < srcIndex.count; i++) {
11848
+ dstIndex.setX(indexStart + i, vertexStart + srcIndex.getX(i));
11849
+ }
11850
+ for (let i = srcIndex.count, l = reservedIndexCount; i < l; i++) {
11851
+ dstIndex.setX(indexStart + i, vertexStart);
11852
+ }
11853
+ dstIndex.needsUpdate = true;
11854
+ dstIndex.addUpdateRange(indexStart, geometryInfo.reservedIndexCount);
11855
+ }
11856
+ geometryInfo.start = hasIndex ? geometryInfo.indexStart : geometryInfo.vertexStart;
11857
+ geometryInfo.count = hasIndex ? geometryInfo.indexCount : geometryInfo.vertexCount;
11858
+ geometryInfo.boundingBox = null;
11859
+ if (geometry.boundingBox !== null) {
11860
+ geometryInfo.boundingBox = geometry.boundingBox.clone();
11861
+ }
11862
+ geometryInfo.boundingSphere = null;
11863
+ if (geometry.boundingSphere !== null) {
11864
+ geometryInfo.boundingSphere = geometry.boundingSphere.clone();
11865
+ }
11866
+ this._visibilityChanged = true;
11867
+ return geometryId;
11868
+ }
11869
+ /**
11870
+ * Deletes the geometry defined by the given ID from this batch. Any instances referencing
11871
+ * this geometry will also be removed as a side effect.
11872
+ *
11873
+ * @param {number} geometryId - The ID of the geometry to remove from the batch.
11874
+ * @return {BatchedMesh} A reference to this batched mesh.
11875
+ */
11876
+ deleteGeometry(geometryId) {
11877
+ const geometryInfoList = this._geometryInfo;
11878
+ if (geometryId >= geometryInfoList.length || geometryInfoList[geometryId].active === false) {
11879
+ return this;
11880
+ }
11881
+ const instanceInfo = this._instanceInfo;
11882
+ for (let i = 0, l = instanceInfo.length; i < l; i++) {
11883
+ if (instanceInfo[i].active && instanceInfo[i].geometryIndex === geometryId) {
11884
+ this.deleteInstance(i);
11885
+ }
11886
+ }
11887
+ geometryInfoList[geometryId].active = false;
11888
+ this._availableGeometryIds.push(geometryId);
11889
+ this._visibilityChanged = true;
11890
+ return this;
11891
+ }
11892
+ /**
11893
+ * Deletes an existing instance from the batch using the given ID.
11894
+ *
11895
+ * @param {number} instanceId - The ID of the instance to remove from the batch.
11896
+ * @return {BatchedMesh} A reference to this batched mesh.
11897
+ */
11898
+ deleteInstance(instanceId) {
11899
+ this.validateInstanceId(instanceId);
11900
+ this._instanceInfo[instanceId].active = false;
11901
+ this._availableInstanceIds.push(instanceId);
11902
+ this._visibilityChanged = true;
11903
+ return this;
11904
+ }
11905
+ /**
11906
+ * Repacks the sub geometries in [name] to remove any unused space remaining from
11907
+ * previously deleted geometry, freeing up space to add new geometry.
11908
+ *
11909
+ * @param {number} instanceId - The ID of the instance to remove from the batch.
11910
+ * @return {BatchedMesh} A reference to this batched mesh.
11911
+ */
11912
+ optimize() {
11913
+ let nextVertexStart = 0;
11914
+ let nextIndexStart = 0;
11915
+ const geometryInfoList = this._geometryInfo;
11916
+ const indices = geometryInfoList.map((e, i) => i).sort((a2, b) => {
11917
+ return geometryInfoList[a2].vertexStart - geometryInfoList[b].vertexStart;
11918
+ });
11919
+ const geometry = this.geometry;
11920
+ for (let i = 0, l = geometryInfoList.length; i < l; i++) {
11921
+ const index2 = indices[i];
11922
+ const geometryInfo = geometryInfoList[index2];
11923
+ if (geometryInfo.active === false) {
11924
+ continue;
11925
+ }
11926
+ if (geometry.index !== null) {
11927
+ if (geometryInfo.indexStart !== nextIndexStart) {
11928
+ const { indexStart, vertexStart, reservedIndexCount } = geometryInfo;
11929
+ const index3 = geometry.index;
11930
+ const array = index3.array;
11931
+ const elementDelta = nextVertexStart - vertexStart;
11932
+ for (let j = indexStart; j < indexStart + reservedIndexCount; j++) {
11933
+ array[j] = array[j] + elementDelta;
11934
+ }
11935
+ index3.array.copyWithin(nextIndexStart, indexStart, indexStart + reservedIndexCount);
11936
+ index3.addUpdateRange(nextIndexStart, reservedIndexCount);
11937
+ geometryInfo.indexStart = nextIndexStart;
11938
+ }
11939
+ nextIndexStart += geometryInfo.reservedIndexCount;
11940
+ }
11941
+ if (geometryInfo.vertexStart !== nextVertexStart) {
11942
+ const { vertexStart, reservedVertexCount } = geometryInfo;
11943
+ const attributes = geometry.attributes;
11944
+ for (const key2 in attributes) {
11945
+ const attribute = attributes[key2];
11946
+ const { array, itemSize } = attribute;
11947
+ array.copyWithin(nextVertexStart * itemSize, vertexStart * itemSize, (vertexStart + reservedVertexCount) * itemSize);
11948
+ attribute.addUpdateRange(nextVertexStart * itemSize, reservedVertexCount * itemSize);
11949
+ }
11950
+ geometryInfo.vertexStart = nextVertexStart;
11951
+ }
11952
+ nextVertexStart += geometryInfo.reservedVertexCount;
11953
+ geometryInfo.start = geometry.index ? geometryInfo.indexStart : geometryInfo.vertexStart;
11954
+ this._nextIndexStart = geometry.index ? geometryInfo.indexStart + geometryInfo.reservedIndexCount : 0;
11955
+ this._nextVertexStart = geometryInfo.vertexStart + geometryInfo.reservedVertexCount;
11956
+ }
11957
+ return this;
11958
+ }
11959
+ /**
11960
+ * Returns the bounding box for the given geometry.
11961
+ *
11962
+ * @param {number} geometryId - The ID of the geometry to return the bounding box for.
11963
+ * @param {Box3} target - The target object that is used to store the method's result.
11964
+ * @return {Box3|null} The geometry's bounding box. Returns `null` if no geometry has been found for the given ID.
11965
+ */
11966
+ getBoundingBoxAt(geometryId, target) {
11967
+ if (geometryId >= this._geometryCount) {
11968
+ return null;
11969
+ }
11970
+ const geometry = this.geometry;
11971
+ const geometryInfo = this._geometryInfo[geometryId];
11972
+ if (geometryInfo.boundingBox === null) {
11973
+ const box = new Box3();
11974
+ const index2 = geometry.index;
11975
+ const position = geometry.attributes.position;
11976
+ for (let i = geometryInfo.start, l = geometryInfo.start + geometryInfo.count; i < l; i++) {
11977
+ let iv = i;
11978
+ if (index2) {
11979
+ iv = index2.getX(iv);
11980
+ }
11981
+ box.expandByPoint(_vector$5.fromBufferAttribute(position, iv));
11982
+ }
11983
+ geometryInfo.boundingBox = box;
11984
+ }
11985
+ target.copy(geometryInfo.boundingBox);
11986
+ return target;
11987
+ }
11988
+ /**
11989
+ * Returns the bounding sphere for the given geometry.
11990
+ *
11991
+ * @param {number} geometryId - The ID of the geometry to return the bounding sphere for.
11992
+ * @param {Sphere} target - The target object that is used to store the method's result.
11993
+ * @return {Sphere|null} The geometry's bounding sphere. Returns `null` if no geometry has been found for the given ID.
11994
+ */
11995
+ getBoundingSphereAt(geometryId, target) {
11996
+ if (geometryId >= this._geometryCount) {
11997
+ return null;
11998
+ }
11999
+ const geometry = this.geometry;
12000
+ const geometryInfo = this._geometryInfo[geometryId];
12001
+ if (geometryInfo.boundingSphere === null) {
12002
+ const sphere = new Sphere();
12003
+ this.getBoundingBoxAt(geometryId, _box$1);
12004
+ _box$1.getCenter(sphere.center);
12005
+ const index2 = geometry.index;
12006
+ const position = geometry.attributes.position;
12007
+ let maxRadiusSq = 0;
12008
+ for (let i = geometryInfo.start, l = geometryInfo.start + geometryInfo.count; i < l; i++) {
12009
+ let iv = i;
12010
+ if (index2) {
12011
+ iv = index2.getX(iv);
12012
+ }
12013
+ _vector$5.fromBufferAttribute(position, iv);
12014
+ maxRadiusSq = Math.max(maxRadiusSq, sphere.center.distanceToSquared(_vector$5));
12015
+ }
12016
+ sphere.radius = Math.sqrt(maxRadiusSq);
12017
+ geometryInfo.boundingSphere = sphere;
12018
+ }
12019
+ target.copy(geometryInfo.boundingSphere);
12020
+ return target;
12021
+ }
12022
+ /**
12023
+ * Sets the given local transformation matrix to the defined instance.
12024
+ * Negatively scaled matrices are not supported.
12025
+ *
12026
+ * @param {number} instanceId - The ID of an instance to set the matrix of.
12027
+ * @param {Matrix4} matrix - A 4x4 matrix representing the local transformation of a single instance.
12028
+ * @return {BatchedMesh} A reference to this batched mesh.
12029
+ */
12030
+ setMatrixAt(instanceId, matrix) {
12031
+ this.validateInstanceId(instanceId);
12032
+ const matricesTexture = this._matricesTexture;
12033
+ const matricesArray = this._matricesTexture.image.data;
12034
+ matrix.toArray(matricesArray, instanceId * 16);
12035
+ matricesTexture.needsUpdate = true;
12036
+ return this;
12037
+ }
12038
+ /**
12039
+ * Returns the local transformation matrix of the defined instance.
12040
+ *
12041
+ * @param {number} instanceId - The ID of an instance to get the matrix of.
12042
+ * @param {Matrix4} matrix - The target object that is used to store the method's result.
12043
+ * @return {Matrix4} The instance's local transformation matrix.
12044
+ */
12045
+ getMatrixAt(instanceId, matrix) {
12046
+ this.validateInstanceId(instanceId);
12047
+ return matrix.fromArray(this._matricesTexture.image.data, instanceId * 16);
12048
+ }
12049
+ /**
12050
+ * Sets the given color to the defined instance.
12051
+ *
12052
+ * @param {number} instanceId - The ID of an instance to set the color of.
12053
+ * @param {Color} color - The color to set the instance to.
12054
+ * @return {BatchedMesh} A reference to this batched mesh.
12055
+ */
12056
+ setColorAt(instanceId, color) {
12057
+ this.validateInstanceId(instanceId);
12058
+ if (this._colorsTexture === null) {
12059
+ this._initColorsTexture();
12060
+ }
12061
+ color.toArray(this._colorsTexture.image.data, instanceId * 4);
12062
+ this._colorsTexture.needsUpdate = true;
12063
+ return this;
12064
+ }
12065
+ /**
12066
+ * Returns the color of the defined instance.
12067
+ *
12068
+ * @param {number} instanceId - The ID of an instance to get the color of.
12069
+ * @param {Color} color - The target object that is used to store the method's result.
12070
+ * @return {Color} The instance's color.
12071
+ */
12072
+ getColorAt(instanceId, color) {
12073
+ this.validateInstanceId(instanceId);
12074
+ return color.fromArray(this._colorsTexture.image.data, instanceId * 4);
12075
+ }
12076
+ /**
12077
+ * Sets the visibility of the instance.
12078
+ *
12079
+ * @param {number} instanceId - The id of the instance to set the visibility of.
12080
+ * @param {boolean} visible - Whether the instance is visible or not.
12081
+ * @return {BatchedMesh} A reference to this batched mesh.
12082
+ */
12083
+ setVisibleAt(instanceId, visible) {
12084
+ this.validateInstanceId(instanceId);
12085
+ if (this._instanceInfo[instanceId].visible === visible) {
12086
+ return this;
12087
+ }
12088
+ this._instanceInfo[instanceId].visible = visible;
12089
+ this._visibilityChanged = true;
12090
+ return this;
12091
+ }
12092
+ /**
12093
+ * Returns the visibility state of the defined instance.
12094
+ *
12095
+ * @param {number} instanceId - The ID of an instance to get the visibility state of.
12096
+ * @return {boolean} Whether the instance is visible or not.
12097
+ */
12098
+ getVisibleAt(instanceId) {
12099
+ this.validateInstanceId(instanceId);
12100
+ return this._instanceInfo[instanceId].visible;
12101
+ }
12102
+ /**
12103
+ * Sets the geometry ID of the instance at the given index.
12104
+ *
12105
+ * @param {number} instanceId - The ID of the instance to set the geometry ID of.
12106
+ * @param {number} geometryId - The geometry ID to be use by the instance.
12107
+ * @return {BatchedMesh} A reference to this batched mesh.
12108
+ */
12109
+ setGeometryIdAt(instanceId, geometryId) {
12110
+ this.validateInstanceId(instanceId);
12111
+ this.validateGeometryId(geometryId);
12112
+ this._instanceInfo[instanceId].geometryIndex = geometryId;
12113
+ return this;
12114
+ }
12115
+ /**
12116
+ * Returns the geometry ID of the defined instance.
12117
+ *
12118
+ * @param {number} instanceId - The ID of an instance to get the geometry ID of.
12119
+ * @return {number} The instance's geometry ID.
12120
+ */
12121
+ getGeometryIdAt(instanceId) {
12122
+ this.validateInstanceId(instanceId);
12123
+ return this._instanceInfo[instanceId].geometryIndex;
12124
+ }
12125
+ /**
12126
+ * Get the range representing the subset of triangles related to the attached geometry,
12127
+ * indicating the starting offset and count, or `null` if invalid.
12128
+ *
12129
+ * @param {number} geometryId - The id of the geometry to get the range of.
12130
+ * @param {Object} [target] - The target object that is used to store the method's result.
12131
+ * @return {{
12132
+ * vertexStart:number,vertexCount:number,reservedVertexCount:number,
12133
+ * indexStart:number,indexCount:number,reservedIndexCount:number,
12134
+ * start:number,count:number
12135
+ * }} The result object with range data.
12136
+ */
12137
+ getGeometryRangeAt(geometryId, target = {}) {
12138
+ this.validateGeometryId(geometryId);
12139
+ const geometryInfo = this._geometryInfo[geometryId];
12140
+ target.vertexStart = geometryInfo.vertexStart;
12141
+ target.vertexCount = geometryInfo.vertexCount;
12142
+ target.reservedVertexCount = geometryInfo.reservedVertexCount;
12143
+ target.indexStart = geometryInfo.indexStart;
12144
+ target.indexCount = geometryInfo.indexCount;
12145
+ target.reservedIndexCount = geometryInfo.reservedIndexCount;
12146
+ target.start = geometryInfo.start;
12147
+ target.count = geometryInfo.count;
12148
+ return target;
12149
+ }
12150
+ /**
12151
+ * Resizes the necessary buffers to support the provided number of instances.
12152
+ * If the provided arguments shrink the number of instances but there are not enough
12153
+ * unused Ids at the end of the list then an error is thrown.
12154
+ *
12155
+ * @param {number} maxInstanceCount - The max number of individual instances that can be added and rendered by the batch.
12156
+ */
12157
+ setInstanceCount(maxInstanceCount) {
12158
+ const availableInstanceIds = this._availableInstanceIds;
12159
+ const instanceInfo = this._instanceInfo;
12160
+ availableInstanceIds.sort(ascIdSort);
12161
+ while (availableInstanceIds[availableInstanceIds.length - 1] === instanceInfo.length) {
12162
+ instanceInfo.pop();
12163
+ availableInstanceIds.pop();
12164
+ }
12165
+ if (maxInstanceCount < instanceInfo.length) {
12166
+ throw new Error(`BatchedMesh: Instance ids outside the range ${maxInstanceCount} are being used. Cannot shrink instance count.`);
12167
+ }
12168
+ const multiDrawCounts = new Int32Array(maxInstanceCount);
12169
+ const multiDrawStarts = new Int32Array(maxInstanceCount);
12170
+ copyArrayContents(this._multiDrawCounts, multiDrawCounts);
12171
+ copyArrayContents(this._multiDrawStarts, multiDrawStarts);
12172
+ this._multiDrawCounts = multiDrawCounts;
12173
+ this._multiDrawStarts = multiDrawStarts;
12174
+ this._maxInstanceCount = maxInstanceCount;
12175
+ const indirectTexture = this._indirectTexture;
12176
+ const matricesTexture = this._matricesTexture;
12177
+ const colorsTexture = this._colorsTexture;
12178
+ indirectTexture.dispose();
12179
+ this._initIndirectTexture();
12180
+ copyArrayContents(indirectTexture.image.data, this._indirectTexture.image.data);
12181
+ matricesTexture.dispose();
12182
+ this._initMatricesTexture();
12183
+ copyArrayContents(matricesTexture.image.data, this._matricesTexture.image.data);
12184
+ if (colorsTexture) {
12185
+ colorsTexture.dispose();
12186
+ this._initColorsTexture();
12187
+ copyArrayContents(colorsTexture.image.data, this._colorsTexture.image.data);
12188
+ }
12189
+ }
12190
+ /**
12191
+ * Resizes the available space in the batch's vertex and index buffer attributes to the provided sizes.
12192
+ * If the provided arguments shrink the geometry buffers but there is not enough unused space at the
12193
+ * end of the geometry attributes then an error is thrown.
12194
+ *
12195
+ * @param {number} maxVertexCount - The maximum number of vertices to be used by all unique geometries to resize to.
12196
+ * @param {number} maxIndexCount - The maximum number of indices to be used by all unique geometries to resize to.
12197
+ */
12198
+ setGeometrySize(maxVertexCount, maxIndexCount) {
12199
+ const validRanges = [...this._geometryInfo].filter((info) => info.active);
12200
+ const requiredVertexLength = Math.max(...validRanges.map((range) => range.vertexStart + range.reservedVertexCount));
12201
+ if (requiredVertexLength > maxVertexCount) {
12202
+ throw new Error(`BatchedMesh: Geometry vertex values are being used outside the range ${maxIndexCount}. Cannot shrink further.`);
12203
+ }
12204
+ if (this.geometry.index) {
12205
+ const requiredIndexLength = Math.max(...validRanges.map((range) => range.indexStart + range.reservedIndexCount));
12206
+ if (requiredIndexLength > maxIndexCount) {
12207
+ throw new Error(`BatchedMesh: Geometry index values are being used outside the range ${maxIndexCount}. Cannot shrink further.`);
12208
+ }
12209
+ }
12210
+ const oldGeometry = this.geometry;
12211
+ oldGeometry.dispose();
12212
+ this._maxVertexCount = maxVertexCount;
12213
+ this._maxIndexCount = maxIndexCount;
12214
+ if (this._geometryInitialized) {
12215
+ this._geometryInitialized = false;
12216
+ this.geometry = new BufferGeometry();
12217
+ this._initializeGeometry(oldGeometry);
12218
+ }
12219
+ const geometry = this.geometry;
12220
+ if (oldGeometry.index) {
12221
+ copyArrayContents(oldGeometry.index.array, geometry.index.array);
12222
+ }
12223
+ for (const key2 in oldGeometry.attributes) {
12224
+ copyArrayContents(oldGeometry.attributes[key2].array, geometry.attributes[key2].array);
12225
+ }
12226
+ }
12227
+ raycast(raycaster, intersects) {
12228
+ const instanceInfo = this._instanceInfo;
12229
+ const geometryInfoList = this._geometryInfo;
12230
+ const matrixWorld = this.matrixWorld;
12231
+ const batchGeometry = this.geometry;
12232
+ _mesh$1.material = this.material;
12233
+ _mesh$1.geometry.index = batchGeometry.index;
12234
+ _mesh$1.geometry.attributes = batchGeometry.attributes;
12235
+ if (_mesh$1.geometry.boundingBox === null) {
12236
+ _mesh$1.geometry.boundingBox = new Box3();
12237
+ }
12238
+ if (_mesh$1.geometry.boundingSphere === null) {
12239
+ _mesh$1.geometry.boundingSphere = new Sphere();
12240
+ }
12241
+ for (let i = 0, l = instanceInfo.length; i < l; i++) {
12242
+ if (!instanceInfo[i].visible || !instanceInfo[i].active) {
12243
+ continue;
12244
+ }
12245
+ const geometryId = instanceInfo[i].geometryIndex;
12246
+ const geometryInfo = geometryInfoList[geometryId];
12247
+ _mesh$1.geometry.setDrawRange(geometryInfo.start, geometryInfo.count);
12248
+ this.getMatrixAt(i, _mesh$1.matrixWorld).premultiply(matrixWorld);
12249
+ this.getBoundingBoxAt(geometryId, _mesh$1.geometry.boundingBox);
12250
+ this.getBoundingSphereAt(geometryId, _mesh$1.geometry.boundingSphere);
12251
+ _mesh$1.raycast(raycaster, _batchIntersects$1);
12252
+ for (let j = 0, l2 = _batchIntersects$1.length; j < l2; j++) {
12253
+ const intersect2 = _batchIntersects$1[j];
12254
+ intersect2.object = this;
12255
+ intersect2.batchId = i;
12256
+ intersects.push(intersect2);
12257
+ }
12258
+ _batchIntersects$1.length = 0;
12259
+ }
12260
+ _mesh$1.material = null;
12261
+ _mesh$1.geometry.index = null;
12262
+ _mesh$1.geometry.attributes = {};
12263
+ _mesh$1.geometry.setDrawRange(0, Infinity);
12264
+ }
12265
+ copy(source) {
12266
+ super.copy(source);
12267
+ this.geometry = source.geometry.clone();
12268
+ this.perObjectFrustumCulled = source.perObjectFrustumCulled;
12269
+ this.sortObjects = source.sortObjects;
12270
+ this.boundingBox = source.boundingBox !== null ? source.boundingBox.clone() : null;
12271
+ this.boundingSphere = source.boundingSphere !== null ? source.boundingSphere.clone() : null;
12272
+ this._geometryInfo = source._geometryInfo.map((info) => ({
12273
+ ...info,
12274
+ boundingBox: info.boundingBox !== null ? info.boundingBox.clone() : null,
12275
+ boundingSphere: info.boundingSphere !== null ? info.boundingSphere.clone() : null
12276
+ }));
12277
+ this._instanceInfo = source._instanceInfo.map((info) => ({ ...info }));
12278
+ this._maxInstanceCount = source._maxInstanceCount;
12279
+ this._maxVertexCount = source._maxVertexCount;
12280
+ this._maxIndexCount = source._maxIndexCount;
12281
+ this._geometryInitialized = source._geometryInitialized;
12282
+ this._geometryCount = source._geometryCount;
12283
+ this._multiDrawCounts = source._multiDrawCounts.slice();
12284
+ this._multiDrawStarts = source._multiDrawStarts.slice();
12285
+ this._matricesTexture = source._matricesTexture.clone();
12286
+ this._matricesTexture.image.data = this._matricesTexture.image.data.slice();
12287
+ if (this._colorsTexture !== null) {
12288
+ this._colorsTexture = source._colorsTexture.clone();
12289
+ this._colorsTexture.image.data = this._colorsTexture.image.data.slice();
12290
+ }
12291
+ return this;
12292
+ }
12293
+ /**
12294
+ * Frees the GPU-related resources allocated by this instance. Call this
12295
+ * method whenever this instance is no longer used in your app.
12296
+ */
12297
+ dispose() {
12298
+ this.geometry.dispose();
12299
+ this._matricesTexture.dispose();
12300
+ this._matricesTexture = null;
12301
+ this._indirectTexture.dispose();
12302
+ this._indirectTexture = null;
12303
+ if (this._colorsTexture !== null) {
12304
+ this._colorsTexture.dispose();
12305
+ this._colorsTexture = null;
12306
+ }
12307
+ }
12308
+ onBeforeRender(renderer, scene, camera, geometry, material) {
12309
+ if (!this._visibilityChanged && !this.perObjectFrustumCulled && !this.sortObjects) {
12310
+ return;
12311
+ }
12312
+ const index2 = geometry.getIndex();
12313
+ const bytesPerElement = index2 === null ? 1 : index2.array.BYTES_PER_ELEMENT;
12314
+ const instanceInfo = this._instanceInfo;
12315
+ const multiDrawStarts = this._multiDrawStarts;
12316
+ const multiDrawCounts = this._multiDrawCounts;
12317
+ const geometryInfoList = this._geometryInfo;
12318
+ const perObjectFrustumCulled = this.perObjectFrustumCulled;
12319
+ const indirectTexture = this._indirectTexture;
12320
+ const indirectArray = indirectTexture.image.data;
12321
+ if (perObjectFrustumCulled) {
12322
+ _matrix$1.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(this.matrixWorld);
12323
+ _frustum.setFromProjectionMatrix(
12324
+ _matrix$1,
12325
+ renderer.coordinateSystem
12326
+ );
12327
+ }
12328
+ let multiDrawCount = 0;
12329
+ if (this.sortObjects) {
12330
+ _matrix$1.copy(this.matrixWorld).invert();
12331
+ _vector$5.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(_matrix$1);
12332
+ _forward.set(0, 0, -1).transformDirection(camera.matrixWorld).transformDirection(_matrix$1);
12333
+ for (let i = 0, l = instanceInfo.length; i < l; i++) {
12334
+ if (instanceInfo[i].visible && instanceInfo[i].active) {
12335
+ const geometryId = instanceInfo[i].geometryIndex;
12336
+ this.getMatrixAt(i, _matrix$1);
12337
+ this.getBoundingSphereAt(geometryId, _sphere$2).applyMatrix4(_matrix$1);
12338
+ let culled = false;
12339
+ if (perObjectFrustumCulled) {
12340
+ culled = !_frustum.intersectsSphere(_sphere$2);
12341
+ }
12342
+ if (!culled) {
12343
+ const geometryInfo = geometryInfoList[geometryId];
12344
+ const z2 = _temp.subVectors(_sphere$2.center, _vector$5).dot(_forward);
12345
+ _renderList.push(geometryInfo.start, geometryInfo.count, z2, i);
12346
+ }
12347
+ }
12348
+ }
12349
+ const list = _renderList.list;
12350
+ const customSort = this.customSort;
12351
+ if (customSort === null) {
12352
+ list.sort(material.transparent ? sortTransparent : sortOpaque);
12353
+ } else {
12354
+ customSort.call(this, list, camera);
12355
+ }
12356
+ for (let i = 0, l = list.length; i < l; i++) {
12357
+ const item = list[i];
12358
+ multiDrawStarts[multiDrawCount] = item.start * bytesPerElement;
12359
+ multiDrawCounts[multiDrawCount] = item.count;
12360
+ indirectArray[multiDrawCount] = item.index;
12361
+ multiDrawCount++;
12362
+ }
12363
+ _renderList.reset();
12364
+ } else {
12365
+ for (let i = 0, l = instanceInfo.length; i < l; i++) {
12366
+ if (instanceInfo[i].visible && instanceInfo[i].active) {
12367
+ const geometryId = instanceInfo[i].geometryIndex;
12368
+ let culled = false;
12369
+ if (perObjectFrustumCulled) {
12370
+ this.getMatrixAt(i, _matrix$1);
12371
+ this.getBoundingSphereAt(geometryId, _sphere$2).applyMatrix4(_matrix$1);
12372
+ culled = !_frustum.intersectsSphere(_sphere$2);
12373
+ }
12374
+ if (!culled) {
12375
+ const geometryInfo = geometryInfoList[geometryId];
12376
+ multiDrawStarts[multiDrawCount] = geometryInfo.start * bytesPerElement;
12377
+ multiDrawCounts[multiDrawCount] = geometryInfo.count;
12378
+ indirectArray[multiDrawCount] = i;
12379
+ multiDrawCount++;
12380
+ }
12381
+ }
12382
+ }
12383
+ }
12384
+ indirectTexture.needsUpdate = true;
12385
+ this._multiDrawCount = multiDrawCount;
12386
+ this._visibilityChanged = false;
12387
+ }
12388
+ onBeforeShadow(renderer, object, camera, shadowCamera, geometry, depthMaterial) {
12389
+ this.onBeforeRender(renderer, null, shadowCamera, geometry, depthMaterial);
12390
+ }
12391
+ }
11225
12392
  const _matrix = /* @__PURE__ */ new Matrix4();
11226
12393
  class Raycaster {
11227
12394
  /**
@@ -11232,8 +12399,8 @@ class Raycaster {
11232
12399
  * @param {number} [near=0] - All results returned are further away than near. Near can't be negative.
11233
12400
  * @param {number} [far=Infinity] - All results returned are closer than far. Far can't be lower than near.
11234
12401
  */
11235
- constructor(origin, direction, near = 0, far = Infinity) {
11236
- this.ray = new Ray(origin, direction);
12402
+ constructor(origin, direction2, near = 0, far = Infinity) {
12403
+ this.ray = new Ray(origin, direction2);
11237
12404
  this.near = near;
11238
12405
  this.far = far;
11239
12406
  this.camera = null;
@@ -11252,8 +12419,8 @@ class Raycaster {
11252
12419
  * @param {Vector3} origin - The origin vector where the ray casts from.
11253
12420
  * @param {Vector3} direction - The (normalized) direction vector that gives direction to the ray.
11254
12421
  */
11255
- set(origin, direction) {
11256
- this.ray.set(origin, direction);
12422
+ set(origin, direction2) {
12423
+ this.ray.set(origin, direction2);
11257
12424
  }
11258
12425
  /**
11259
12426
  * Uses the given coordinates and camera to compute a new origin and direction for the internal ray.
@@ -11663,6 +12830,7 @@ function cloneTopologyRewritePropagation(propagation) {
11663
12830
  }
11664
12831
  function faceQueryRefsEqual(a2, b) {
11665
12832
  if (a2 == null || b == null) return a2 == null && b == null;
12833
+ if (a2 === b) return true;
11666
12834
  if (a2.kind !== b.kind) return false;
11667
12835
  switch (a2.kind) {
11668
12836
  case "canonical-face":
@@ -11679,6 +12847,7 @@ function faceQueryRefsEqual(a2, b) {
11679
12847
  }
11680
12848
  function edgeQueryRefsEqual(a2, b) {
11681
12849
  if (a2 == null || b == null) return a2 == null && b == null;
12850
+ if (a2 === b) return true;
11682
12851
  if (a2.kind !== b.kind) return false;
11683
12852
  switch (a2.kind) {
11684
12853
  case "tracked-edge":
@@ -12045,8 +13214,8 @@ function deriveSheetMetalModel(model) {
12045
13214
  const trimStart = flanges.has(adjacent.start) ? model.cornerRelief.size : 0;
12046
13215
  const trimEnd = flanges.has(adjacent.end) ? model.cornerRelief.size : 0;
12047
13216
  const fullLength = edge === "top" || edge === "bottom" ? model.panel.width : model.panel.height;
12048
- const span = fullLength - trimStart - trimEnd;
12049
- if (!(span > EPS$3)) {
13217
+ const span2 = fullLength - trimStart - trimEnd;
13218
+ if (!(span2 > EPS$3)) {
12050
13219
  throw new Error(
12051
13220
  `${edgeDisplayName(edge)} loses all usable span after applying the defended rectangular corner relief size ${model.cornerRelief.size}.`
12052
13221
  );
@@ -12058,7 +13227,7 @@ function deriveSheetMetalModel(model) {
12058
13227
  bendAllowance,
12059
13228
  trimStart,
12060
13229
  trimEnd,
12061
- span,
13230
+ span: span2,
12062
13231
  centerAlongEdge: (trimStart - trimEnd) / 2
12063
13232
  });
12064
13233
  }
@@ -12237,10 +13406,10 @@ function lowerSheetMetalBasePlan(model, output) {
12237
13406
  const pieces = lowerSheetMetalBasePiecePlans(model, output).map((piece) => piece.plan);
12238
13407
  return pieces.length === 1 ? pieces[0] : buildBooleanShapeCompilePlan("union", pieces);
12239
13408
  }
12240
- function descriptor(name, center, normal, planar, uAxis, vAxis, semantic = "face", memberNames = [name], coplanar = planar) {
13409
+ function descriptor(name, center2, normal, planar, uAxis, vAxis, semantic = "face", memberNames = [name], coplanar = planar) {
12241
13410
  return {
12242
13411
  name,
12243
- center: cloneVec3$7(center),
13412
+ center: cloneVec3$7(center2),
12244
13413
  normal: cloneVec3$7(normal),
12245
13414
  planar,
12246
13415
  uAxis: cloneFaceAxis(uAxis),
@@ -16089,6 +17258,23 @@ function analyzeNodeUV(node, toLocal) {
16089
17258
  function clampUnit$1(v) {
16090
17259
  return v < -1 ? -1 : v > 1 ? 1 : v;
16091
17260
  }
17261
+ function sphereUVLocal(lx, ly, lz, radius) {
17262
+ const u2 = atan2(ly, lx) * radius;
17263
+ const len = sqrt$3(lx * lx + ly * ly + lz * lz);
17264
+ const v = acos(clampUnit$1(lz / (len || 1))) * radius;
17265
+ return [u2, v];
17266
+ }
17267
+ function cylinderUVLocal(lx, ly, lz, radius) {
17268
+ const u2 = atan2(ly, lx) * radius;
17269
+ const v = lz;
17270
+ return [u2, v];
17271
+ }
17272
+ function torusUVLocal(lx, ly, lz, majorRadius, minorRadius) {
17273
+ const u2 = atan2(ly, lx) * majorRadius;
17274
+ const xyDist = sqrt$3(lx * lx + ly * ly) - majorRadius;
17275
+ const v = atan2(lz, xyDist) * minorRadius;
17276
+ return [u2, v];
17277
+ }
16092
17278
  function compileUVFunction(analysis) {
16093
17279
  if (analysis.mode === "triplanar") return null;
16094
17280
  const toLocal = analysis.toLocal;
@@ -16097,19 +17283,14 @@ function compileUVFunction(analysis) {
16097
17283
  const R = analysis.radius;
16098
17284
  return (p2) => {
16099
17285
  const lp = toLocal(p2);
16100
- const u2 = atan2(lp[1], lp[0]) * R;
16101
- const len = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1] + lp[2] * lp[2]);
16102
- const v = acos(clampUnit$1(lp[2] / (len || 1))) * R;
16103
- return [u2, v];
17286
+ return sphereUVLocal(lp[0], lp[1], lp[2], R);
16104
17287
  };
16105
17288
  }
16106
17289
  case "cylinder": {
16107
17290
  const r = analysis.radius;
16108
17291
  return (p2) => {
16109
17292
  const lp = toLocal(p2);
16110
- const u2 = atan2(lp[1], lp[0]) * r;
16111
- const v = lp[2];
16112
- return [u2, v];
17293
+ return cylinderUVLocal(lp[0], lp[1], lp[2], r);
16113
17294
  };
16114
17295
  }
16115
17296
  case "torus": {
@@ -16117,10 +17298,7 @@ function compileUVFunction(analysis) {
16117
17298
  const r = analysis.radius;
16118
17299
  return (p2) => {
16119
17300
  const lp = toLocal(p2);
16120
- const u2 = atan2(lp[1], lp[0]) * R;
16121
- const xyDist = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1]) - R;
16122
- const v = atan2(lp[2], xyDist) * r;
16123
- return [u2, v];
17301
+ return torusUVLocal(lp[0], lp[1], lp[2], R, r);
16124
17302
  };
16125
17303
  }
16126
17304
  }
@@ -16431,8 +17609,8 @@ function sdProfileRevolve$1(profileFn, degrees, x2, y2, z2) {
16431
17609
  const profileDistance = profileFn(radial, z2);
16432
17610
  if (abs(degrees) >= 360) return profileDistance;
16433
17611
  const sweep = abs(degrees) * DEG$1;
16434
- const center = degrees * DEG$1 * 0.5;
16435
- let angle = Math.atan2(y2, x2) - center;
17612
+ const center2 = degrees * DEG$1 * 0.5;
17613
+ let angle = Math.atan2(y2, x2) - center2;
16436
17614
  while (angle <= -PI$1) angle += PI$1 * 2;
16437
17615
  while (angle > PI$1) angle -= PI$1 * 2;
16438
17616
  const half = sweep * 0.5;
@@ -16450,9 +17628,9 @@ function smax$1(a2, b, k2) {
16450
17628
  function repeatCoord$1(v, spacing, count) {
16451
17629
  if (spacing <= 0) return v;
16452
17630
  if (count > 0) {
16453
- const center = (count - 1) * 0.5;
16454
- const index2 = clamp$3(Math.round(v / spacing + center), 0, count - 1);
16455
- return v - (index2 - center) * spacing;
17631
+ const center2 = (count - 1) * 0.5;
17632
+ const index2 = clamp$3(Math.round(v / spacing + center2), 0, count - 1);
17633
+ return v - (index2 - center2) * spacing;
16456
17634
  }
16457
17635
  return v - spacing * Math.round(v / spacing);
16458
17636
  }
@@ -16482,13 +17660,13 @@ function compileTypedSurfacePattern(pattern) {
16482
17660
  case "surfacePattern:constant":
16483
17661
  return () => pattern.value;
16484
17662
  case "surfacePattern:sineWave": {
16485
- const { direction, wavelength, amplitude, phase, bias } = pattern;
17663
+ const { direction: direction2, wavelength, amplitude, phase, bias } = pattern;
16486
17664
  const frequency = 2 * PI$1 / wavelength;
16487
- return (u2, v) => bias + sin$1((u2 * direction[0] + v * direction[1]) * frequency + phase) * amplitude;
17665
+ return (u2, v) => bias + sin$1((u2 * direction2[0] + v * direction2[1]) * frequency + phase) * amplitude;
16488
17666
  }
16489
17667
  case "surfacePattern:stripes": {
16490
- const { direction, spacing, width, depth } = pattern;
16491
- return (u2, v) => evalStripesPattern(u2, v, direction[0], direction[1], spacing, width, depth);
17668
+ const { direction: direction2, spacing, width, depth } = pattern;
17669
+ return (u2, v) => evalStripesPattern(u2, v, direction2[0], direction2[1], spacing, width, depth);
16492
17670
  }
16493
17671
  case "surfacePattern:overUnderWeave": {
16494
17672
  const { spacing, threadWidth, depth, underScale } = pattern;
@@ -17378,8 +18556,8 @@ function sdProfileRevolve(b, node, x2, y2, z2) {
17378
18556
  const profileDistance = profilePolygonsSdf(b, node.polygons, radial, z2);
17379
18557
  if (Math.abs(node.degrees) >= 360) return profileDistance;
17380
18558
  const sweep = Math.abs(node.degrees) * DEG;
17381
- const center = node.degrees * DEG * 0.5;
17382
- const angle = b.sub(b.atan2(y2, x2), b.constant(center));
18559
+ const center2 = node.degrees * DEG * 0.5;
18560
+ const angle = b.sub(b.atan2(y2, x2), b.constant(center2));
17383
18561
  const wrapped = b.sub(angle, b.mul(b.constant(TAU), b.round(b.div(angle, b.constant(TAU)))));
17384
18562
  const absAngle = b.abs(wrapped);
17385
18563
  const half = sweep * 0.5;
@@ -17446,9 +18624,9 @@ function select01(b, selector, ifOne, ifZero) {
17446
18624
  function repeatCoord(b, v, spacing, count) {
17447
18625
  if (spacing <= 0) return v;
17448
18626
  if (count > 0) {
17449
- const center = (count - 1) * 0.5;
17450
- const index2 = clampSlot(b, b.round(b.add(b.div(v, b.constant(spacing)), b.constant(center))), 0, count - 1);
17451
- return b.sub(v, b.mul(b.sub(index2, b.constant(center)), b.constant(spacing)));
18627
+ const center2 = (count - 1) * 0.5;
18628
+ const index2 = clampSlot(b, b.round(b.add(b.div(v, b.constant(spacing)), b.constant(center2))), 0, count - 1);
18629
+ return b.sub(v, b.mul(b.sub(index2, b.constant(center2)), b.constant(spacing)));
17452
18630
  }
17453
18631
  return b.sub(v, b.mul(b.constant(spacing), b.round(b.div(v, b.constant(spacing)))));
17454
18632
  }
@@ -17719,6 +18897,11 @@ function deepFreezePlanData(value) {
17719
18897
  return value;
17720
18898
  }
17721
18899
  const SHAPE_BACKEND_MARKER = Symbol.for("forgecad.shapeBackend");
18900
+ const NORMAL_OFFSET = 3;
18901
+ const UV_OFFSET = 6;
18902
+ const NUM_PROP_POSITION_ONLY = 3;
18903
+ const NUM_PROP_WITH_NORMAL = 6;
18904
+ const NUM_PROP_WITH_UV = 8;
17722
18905
  function isShapeBackend(value) {
17723
18906
  return Boolean(value && typeof value === "object" && value[SHAPE_BACKEND_MARKER] === true);
17724
18907
  }
@@ -18252,14 +19435,14 @@ function findSpan(n, degree, u2, knots) {
18252
19435
  }
18253
19436
  return mid;
18254
19437
  }
18255
- function basisFuns(span, u2, degree, knots) {
19438
+ function basisFuns(span2, u2, degree, knots) {
18256
19439
  const N = new Array(degree + 1);
18257
19440
  const left = new Array(degree + 1);
18258
19441
  const right = new Array(degree + 1);
18259
19442
  N[0] = 1;
18260
19443
  for (let j = 1; j <= degree; j++) {
18261
- left[j] = u2 - knots[span + 1 - j];
18262
- right[j] = knots[span + j] - u2;
19444
+ left[j] = u2 - knots[span2 + 1 - j];
19445
+ right[j] = knots[span2 + j] - u2;
18263
19446
  let saved = 0;
18264
19447
  for (let r = 0; r < j; r++) {
18265
19448
  const denom = right[r + 1] + left[j - r];
@@ -18271,15 +19454,15 @@ function basisFuns(span, u2, degree, knots) {
18271
19454
  }
18272
19455
  return N;
18273
19456
  }
18274
- function basisFunsDeriv(span, u2, degree, knots, nDeriv) {
19457
+ function basisFunsDeriv(span2, u2, degree, knots, nDeriv) {
18275
19458
  const ndu = Array.from({ length: degree + 1 }, () => new Array(degree + 1).fill(0));
18276
19459
  const a2 = Array.from({ length: 2 }, () => new Array(degree + 1).fill(0));
18277
19460
  const left = new Array(degree + 1);
18278
19461
  const right = new Array(degree + 1);
18279
19462
  ndu[0][0] = 1;
18280
19463
  for (let j = 1; j <= degree; j++) {
18281
- left[j] = u2 - knots[span + 1 - j];
18282
- right[j] = knots[span + j] - u2;
19464
+ left[j] = u2 - knots[span2 + 1 - j];
19465
+ right[j] = knots[span2 + j] - u2;
18283
19466
  let saved = 0;
18284
19467
  for (let r2 = 0; r2 < j; r2++) {
18285
19468
  ndu[j][r2] = right[r2 + 1] + left[j - r2];
@@ -18332,11 +19515,11 @@ function basisFunsDeriv(span, u2, degree, knots, nDeriv) {
18332
19515
  }
18333
19516
  function deBoor3D(controlPoints, weights, knots, degree, u2) {
18334
19517
  const n = controlPoints.length;
18335
- const span = findSpan(n, degree, u2, knots);
18336
- const N = basisFuns(span, u2, degree, knots);
19518
+ const span2 = findSpan(n, degree, u2, knots);
19519
+ const N = basisFuns(span2, u2, degree, knots);
18337
19520
  let wx = 0, wy = 0, wz = 0, wSum = 0;
18338
19521
  for (let j = 0; j <= degree; j++) {
18339
- const idx = span - degree + j;
19522
+ const idx = span2 - degree + j;
18340
19523
  const w2 = weights[idx] * N[j];
18341
19524
  wx += w2 * controlPoints[idx][0];
18342
19525
  wy += w2 * controlPoints[idx][1];
@@ -18732,49 +19915,55 @@ function signedArea2D(loop) {
18732
19915
  }
18733
19916
  return area * 0.5;
18734
19917
  }
18735
- function pointInLoop$1(point2, loop) {
19918
+ function buildSdfLoopEdges(pts) {
19919
+ const n = pts.length;
19920
+ const ax = new Float64Array(n);
19921
+ const ay = new Float64Array(n);
19922
+ const ex = new Float64Array(n);
19923
+ const ey = new Float64Array(n);
19924
+ const invLen2 = new Float64Array(n);
19925
+ for (let i = 0; i < n; i += 1) {
19926
+ const a2 = pts[i];
19927
+ const b = pts[(i + 1) % n];
19928
+ const vx = b[0] - a2[0];
19929
+ const vy = b[1] - a2[1];
19930
+ ax[i] = a2[0];
19931
+ ay[i] = a2[1];
19932
+ ex[i] = vx;
19933
+ ey[i] = vy;
19934
+ const len2 = vx * vx + vy * vy;
19935
+ invLen2[i] = len2 < 1e-12 ? 0 : 1 / len2;
19936
+ }
19937
+ return { n, area: signedArea2D(pts), ax, ay, ex, ey, invLen2 };
19938
+ }
19939
+ function loopSignedDistanceScalar(px, py, e) {
19940
+ let minDist2 = Infinity;
18736
19941
  let inside = false;
18737
- const [px, py] = point2;
18738
- for (let index2 = 0, prev = loop.length - 1; index2 < loop.length; prev = index2, index2 += 1) {
18739
- const [xi, yi] = loop[index2];
18740
- const [xj, yj] = loop[prev];
18741
- const intersects = yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi + 1e-20) + xi;
18742
- if (intersects) inside = !inside;
18743
- }
18744
- return inside;
18745
- }
18746
- function pointSegDist2D(point2, a2, b) {
18747
- const abx = b[0] - a2[0];
18748
- const aby = b[1] - a2[1];
18749
- const apx = point2[0] - a2[0];
18750
- const apy = point2[1] - a2[1];
18751
- const den = abx * abx + aby * aby;
18752
- const t = den < 1e-12 ? 0 : clamp$1((apx * abx + apy * aby) / den, 0, 1);
18753
- const qx = a2[0] + abx * t;
18754
- const qy = a2[1] + aby * t;
18755
- const dx = point2[0] - qx;
18756
- const dy = point2[1] - qy;
18757
- return Math.sqrt(dx * dx + dy * dy);
18758
- }
18759
- function loopSignedDistance(point2, loop) {
18760
- let minDist = Infinity;
18761
- for (let index2 = 0; index2 < loop.length; index2 += 1) {
18762
- const a2 = loop[index2];
18763
- const b = loop[(index2 + 1) % loop.length];
18764
- minDist = Math.min(minDist, pointSegDist2D(point2, a2, b));
18765
- }
18766
- return pointInLoop$1(point2, loop) ? minDist : -minDist;
19942
+ for (let i = 0; i < e.n; i += 1) {
19943
+ const ax = e.ax[i];
19944
+ const ay = e.ay[i];
19945
+ const vx = e.ex[i];
19946
+ const vy = e.ey[i];
19947
+ const t = clamp$1(((px - ax) * vx + (py - ay) * vy) * e.invLen2[i], 0, 1);
19948
+ const ddx = px - (ax + vx * t);
19949
+ const ddy = py - (ay + vy * t);
19950
+ const d22 = ddx * ddx + ddy * ddy;
19951
+ if (d22 < minDist2) minDist2 = d22;
19952
+ const by = ay + vy;
19953
+ if (ay > py !== by > py && px < vx * (py - ay) / (vy + 1e-20) + ax) inside = !inside;
19954
+ }
19955
+ const d2 = Math.sqrt(minDist2);
19956
+ return inside ? d2 : -d2;
18767
19957
  }
18768
19958
  function compilePolygonsSdf(polygons) {
18769
- const loops = polygons.filter((loop) => Array.isArray(loop) && loop.length >= 3).map((loop) => ({ pts: loop.map(([x2, y2]) => [x2, y2]), area: signedArea2D(loop) }));
19959
+ const loops = polygons.filter((loop) => Array.isArray(loop) && loop.length >= 3).map((loop) => buildSdfLoopEdges(loop.map(([x2, y2]) => [x2, y2])));
18770
19960
  if (loops.length === 0) {
18771
19961
  return () => -1;
18772
19962
  }
18773
19963
  return (x2, y2) => {
18774
- const point2 = [x2, y2];
18775
19964
  let field2 = -Infinity;
18776
19965
  for (const loop of loops) {
18777
- const loopField = loopSignedDistance(point2, loop.pts);
19966
+ const loopField = loopSignedDistanceScalar(x2, y2, loop);
18778
19967
  field2 = loop.area >= 0 ? Math.max(field2, loopField) : Math.min(field2, -loopField);
18779
19968
  }
18780
19969
  return field2;
@@ -18932,24 +20121,40 @@ function interpolateSweepFrame(segment, alpha, origin) {
18932
20121
  function findNearestSweepPoint(point2, segments) {
18933
20122
  let bestIndex = 0;
18934
20123
  let bestAlpha = 0;
18935
- let bestPoint = segments[0].a;
20124
+ let bestAlong = 0;
18936
20125
  let bestDist2 = Infinity;
20126
+ const px = point2[0];
20127
+ const py = point2[1];
20128
+ const pz = point2[2];
18937
20129
  for (let index2 = 0; index2 < segments.length; index2 += 1) {
18938
20130
  const segment = segments[index2];
18939
- const offset = vec3Sub(point2, segment.a);
18940
- const along = clamp$1(vec3Dot(offset, segment.t), 0, segment.len);
18941
- const alpha = segment.len > 1e-9 ? along / segment.len : 0;
18942
- const pointOnPath = vec3Add(segment.a, vec3Scale(segment.t, along));
18943
- const delta = vec3Sub(point2, pointOnPath);
18944
- const dist2 = vec3Dot(delta, delta);
20131
+ const ax = segment.a[0];
20132
+ const ay = segment.a[1];
20133
+ const az = segment.a[2];
20134
+ const tx = segment.t[0];
20135
+ const ty = segment.t[1];
20136
+ const tz = segment.t[2];
20137
+ const along = clamp$1((px - ax) * tx + (py - ay) * ty + (pz - az) * tz, 0, segment.len);
20138
+ const qx = ax + tx * along;
20139
+ const qy = ay + ty * along;
20140
+ const qz = az + tz * along;
20141
+ const dx = px - qx;
20142
+ const dy = py - qy;
20143
+ const dz = pz - qz;
20144
+ const dist2 = dx * dx + dy * dy + dz * dz;
18945
20145
  if (dist2 < bestDist2) {
18946
20146
  bestIndex = index2;
18947
- bestAlpha = alpha;
18948
- bestPoint = pointOnPath;
20147
+ bestAlong = along;
20148
+ bestAlpha = segment.len > 1e-9 ? along / segment.len : 0;
18949
20149
  bestDist2 = dist2;
18950
20150
  }
18951
20151
  }
18952
20152
  const bestSegment = segments[bestIndex];
20153
+ const bestPoint = [
20154
+ bestSegment.a[0] + bestSegment.t[0] * bestAlong,
20155
+ bestSegment.a[1] + bestSegment.t[1] * bestAlong,
20156
+ bestSegment.a[2] + bestSegment.t[2] * bestAlong
20157
+ ];
18953
20158
  return {
18954
20159
  segmentIndex: bestIndex,
18955
20160
  segment: bestSegment,
@@ -19946,11 +21151,11 @@ function applyFilletSelectionToManifold(base, selection, radius, segments, wasm)
19946
21151
  const height = edgeLength(selection);
19947
21152
  if (!(height > 1e-6)) return base;
19948
21153
  const pad = Math.max(MIN_EDGE_PAD, radius);
19949
- const span = height + pad * 2;
21154
+ const span2 = height + pad * 2;
19950
21155
  const frame = edgeFrameMatrix(selection, -pad);
19951
21156
  const cs = buildFilletCrossSection(selection, radius, segments, wasm) ?? legacyFilletCrossSection(selection, radius, segments, wasm);
19952
- const corner = cs.wedge.extrude(span, 0, 0, void 0, false).transform(frame);
19953
- const cyl = cs.cylinder.extrude(span, 0, 0, void 0, false).transform(frame);
21157
+ const corner = cs.wedge.extrude(span2, 0, 0, void 0, false).transform(frame);
21158
+ const cyl = cs.cylinder.extrude(span2, 0, 0, void 0, false).transform(frame);
19954
21159
  const crescent = wasm.Manifold.difference([corner, cyl]);
19955
21160
  return wasm.Manifold.difference([base, crescent]);
19956
21161
  }
@@ -19958,11 +21163,11 @@ function applyConcaveFilletSelectionToManifold(base, selection, radius, segments
19958
21163
  const height = edgeLength(selection);
19959
21164
  if (!(height > 1e-6)) return base;
19960
21165
  const pad = Math.max(MIN_EDGE_PAD, radius);
19961
- const span = height + pad * 2;
21166
+ const span2 = height + pad * 2;
19962
21167
  const frame = edgeFrameMatrix(selection, -pad);
19963
21168
  const cs = buildFilletCrossSection(selection, radius, segments, wasm) ?? legacyFilletCrossSection(selection, radius, segments, wasm);
19964
- const corner = cs.wedge.extrude(span, 0, 0, void 0, false).transform(frame);
19965
- const cyl = cs.cylinder.extrude(span, 0, 0, void 0, false).transform(frame);
21169
+ const corner = cs.wedge.extrude(span2, 0, 0, void 0, false).transform(frame);
21170
+ const cyl = cs.cylinder.extrude(span2, 0, 0, void 0, false).transform(frame);
19966
21171
  const crescent = wasm.Manifold.difference([corner, cyl]);
19967
21172
  return wasm.Manifold.union([base, crescent]);
19968
21173
  }
@@ -19970,20 +21175,20 @@ function applyChamferSelectionToManifold(base, selection, size, wasm) {
19970
21175
  const height = edgeLength(selection);
19971
21176
  if (!(height > 1e-6)) return base;
19972
21177
  const pad = Math.max(MIN_EDGE_PAD, size);
19973
- const span = height + pad * 2;
21178
+ const span2 = height + pad * 2;
19974
21179
  const frame = edgeFrameMatrix(selection, -pad);
19975
21180
  const triangle3 = buildChamferCrossSection(selection, size, wasm) ?? legacyChamferCrossSection(selection, size, wasm);
19976
- const chamfer = triangle3.extrude(span, 0, 0, void 0, false).transform(frame);
21181
+ const chamfer = triangle3.extrude(span2, 0, 0, void 0, false).transform(frame);
19977
21182
  return wasm.Manifold.difference([base, chamfer]);
19978
21183
  }
19979
21184
  function applyConcaveChamferSelectionToManifold(base, selection, size, wasm) {
19980
21185
  const height = edgeLength(selection);
19981
21186
  if (!(height > 1e-6)) return base;
19982
21187
  const pad = Math.max(MIN_EDGE_PAD, size);
19983
- const span = height + pad * 2;
21188
+ const span2 = height + pad * 2;
19984
21189
  const frame = edgeFrameMatrix(selection, -pad);
19985
21190
  const triangle3 = buildChamferCrossSection(selection, size, wasm) ?? legacyChamferCrossSection(selection, size, wasm);
19986
- const chamfer = triangle3.extrude(span, 0, 0, void 0, false).transform(frame);
21191
+ const chamfer = triangle3.extrude(span2, 0, 0, void 0, false).transform(frame);
19987
21192
  return wasm.Manifold.union([base, chamfer]);
19988
21193
  }
19989
21194
  const EPS$1 = 1e-8;
@@ -21832,8 +23037,8 @@ function stitchSingleLoopLoft(loops, heights, wasm, options) {
21832
23037
  return null;
21833
23038
  }
21834
23039
  }
21835
- function surfaceNormal(chord, span) {
21836
- const n = cross3$5(chord, span);
23040
+ function surfaceNormal(chord, span2) {
23041
+ const n = cross3$5(chord, span2);
21837
23042
  const len = Math.hypot(n[0], n[1], n[2]);
21838
23043
  if (len < 1e-12) {
21839
23044
  const radial = Math.hypot(chord[0], chord[1]);
@@ -22476,6 +23681,16 @@ function fromSlicesSingleSliceHalfExtentForManifold(plan) {
22476
23681
  return Math.max(1, radius + maxOffsetMagnitude + plan.boundsPadding + plan.edgeLength * 3);
22477
23682
  }
22478
23683
  const BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG = 60;
23684
+ const BOOLEAN_OPERAND_UV_EXTRA_PROP_MIN = 4;
23685
+ function rejectParametricUvBooleanOperands(shapes) {
23686
+ for (const shape of shapes) {
23687
+ if (shape.numProp() >= BOOLEAN_OPERAND_UV_EXTRA_PROP_MIN) {
23688
+ throw new Error(
23689
+ "Cannot boolean a shape that carries parametric UV (mesh numProp 8, vertProperties[6..7]). Parametric UV is baked into the mesh and is destroyed by cuts — Manifold interpolates the uv channels from the other operand onto every new cut/seam vertex. Apply booleans BEFORE texturing the result, or use a projected texture (Shape.wrapTexture with Wrap.*), which is computed from final position and survives booleans."
23690
+ );
23691
+ }
23692
+ }
23693
+ }
22479
23694
  function promoteBooleanOperandNormals(shapes) {
22480
23695
  let maxExtra = 0;
22481
23696
  for (const shape of shapes) maxExtra = Math.max(maxExtra, shape.numProp());
@@ -22497,6 +23712,12 @@ function lowerShapeBooleanCompilePlan(plan, wasm) {
22497
23712
  if (shapes.length === 1) {
22498
23713
  return shapes[0];
22499
23714
  }
23715
+ try {
23716
+ rejectParametricUvBooleanOperands(shapes);
23717
+ } catch (error) {
23718
+ disposeWasmObjects(shapes);
23719
+ throw error;
23720
+ }
22500
23721
  const { operands, created } = promoteBooleanOperandNormals(shapes);
22501
23722
  try {
22502
23723
  switch (plan.op) {
@@ -22538,8 +23759,8 @@ function vectorLength3$1(v) {
22538
23759
  function subtract3$1(a2, b) {
22539
23760
  return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
22540
23761
  }
22541
- function addScaled3$1(point2, direction, scale2) {
22542
- return [point2[0] + direction[0] * scale2, point2[1] + direction[1] * scale2, point2[2] + direction[2] * scale2];
23762
+ function addScaled3$1(point2, direction2, scale2) {
23763
+ return [point2[0] + direction2[0] * scale2, point2[1] + direction2[1] * scale2, point2[2] + direction2[2] * scale2];
22543
23764
  }
22544
23765
  function cross3$4(a2, b) {
22545
23766
  return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
@@ -23254,9 +24475,9 @@ function lowerVerticalVariableSweepStitchedForManifold(plan, sectionPolygons, wa
23254
24475
  return null;
23255
24476
  }
23256
24477
  const frameY = cross3$4(tangent, frameX);
23257
- const span = end[2] - start[2];
24478
+ const span2 = end[2] - start[2];
23258
24479
  const sections = sectionPolygons.map((section) => ({
23259
- height: start[2] + span * section.t,
24480
+ height: start[2] + span2 * section.t,
23260
24481
  polygons: section.polygons.map(
23261
24482
  (loop) => loop.map(([u2, v]) => [start[0] + u2 * frameX[0] + v * frameY[0], start[1] + u2 * frameX[1] + v * frameY[1]])
23262
24483
  )
@@ -31765,8 +32986,8 @@ function revolveProfilePolygonsToSdfField(polygons, degrees = 360) {
31765
32986
  const profileDistance = profile.eval(radial, z2);
31766
32987
  if (Math.abs(degrees) >= 360) return profileDistance;
31767
32988
  const sweep = Math.abs(degrees) * (Math.PI / 180);
31768
- const center = degrees * Math.PI / 360;
31769
- let angle = Math.atan2(y2, x2) - center;
32989
+ const center2 = degrees * Math.PI / 360;
32990
+ let angle = Math.atan2(y2, x2) - center2;
31770
32991
  while (angle <= -Math.PI) angle += Math.PI * 2;
31771
32992
  while (angle > Math.PI) angle -= Math.PI * 2;
31772
32993
  const half = sweep / 2;
@@ -32683,18 +33904,18 @@ function circleFootprintFromProfile(plan) {
32683
33904
  if (plan.kind !== "circle") return null;
32684
33905
  const radius = Math.abs(plan.radius);
32685
33906
  if (!finitePositive$1(radius)) return null;
32686
- const center = transformProfilePointThrough([0, 0], plan.transforms);
33907
+ const center2 = transformProfilePointThrough([0, 0], plan.transforms);
32687
33908
  const xPoint = transformProfilePointThrough([1, 0], plan.transforms);
32688
33909
  const yPoint = transformProfilePointThrough([0, 1], plan.transforms);
32689
- const xAxis = [xPoint[0] - center[0], xPoint[1] - center[1]];
32690
- const yAxis = [yPoint[0] - center[0], yPoint[1] - center[1]];
33910
+ const xAxis = [xPoint[0] - center2[0], xPoint[1] - center2[1]];
33911
+ const yAxis = [yPoint[0] - center2[0], yPoint[1] - center2[1]];
32691
33912
  const xScale = Math.hypot(xAxis[0], xAxis[1]);
32692
33913
  const yScale = Math.hypot(yAxis[0], yAxis[1]);
32693
33914
  const dot2 = xAxis[0] * yAxis[0] + xAxis[1] * yAxis[1];
32694
33915
  if (!finitePositive$1(xScale) || !finitePositive$1(yScale)) return null;
32695
33916
  if (Math.abs(xScale - yScale) > EPS || Math.abs(dot2) > EPS * xScale * yScale) return null;
32696
33917
  return {
32697
- center,
33918
+ center: center2,
32698
33919
  radius: radius * xScale,
32699
33920
  segments: plan.segments
32700
33921
  };
@@ -33493,19 +34714,19 @@ function explicitGeometrySurface(geometry, face) {
33493
34714
  }
33494
34715
  }
33495
34716
  if ("AnalyticSphere" in geometry) {
33496
- const center = geometry.AnalyticSphere.center;
34717
+ const center2 = geometry.AnalyticSphere.center;
33497
34718
  const radius = geometry.AnalyticSphere.radius;
33498
- if (isVec3(center) && isFiniteNumber(radius) && radius > 0) {
33499
- return { kind: "sphere", center, radius };
34719
+ if (isVec3(center2) && isFiniteNumber(radius) && radius > 0) {
34720
+ return { kind: "sphere", center: center2, radius };
33500
34721
  }
33501
34722
  }
33502
34723
  if ("AnalyticTorus" in geometry) {
33503
- const center = geometry.AnalyticTorus.center;
34724
+ const center2 = geometry.AnalyticTorus.center;
33504
34725
  const axis = geometry.AnalyticTorus.axis;
33505
34726
  const majorRadius = geometry.AnalyticTorus.major_radius;
33506
34727
  const minorRadius = geometry.AnalyticTorus.minor_radius;
33507
- if (isVec3(center) && isVec3(axis) && isFiniteNumber(majorRadius) && isFiniteNumber(minorRadius) && majorRadius > 0 && minorRadius > 0) {
33508
- return { kind: "torus", center, axis: normalizeVec3(axis), majorRadius, minorRadius };
34728
+ if (isVec3(center2) && isVec3(axis) && isFiniteNumber(majorRadius) && isFiniteNumber(minorRadius) && majorRadius > 0 && minorRadius > 0) {
34729
+ return { kind: "torus", center: center2, axis: normalizeVec3(axis), majorRadius, minorRadius };
33509
34730
  }
33510
34731
  }
33511
34732
  if ("NurbsPatch" in geometry) {
@@ -33599,13 +34820,13 @@ function explicitGeometrySurface(geometry, face) {
33599
34820
  function explicitEdgeCurve(geometry, faceName) {
33600
34821
  var _a3, _b3, _c2, _d2, _e2;
33601
34822
  if (!geometry) return void 0;
33602
- const center = (_a3 = geometry.CircularArc) == null ? void 0 : _a3.center;
34823
+ const center2 = (_a3 = geometry.CircularArc) == null ? void 0 : _a3.center;
33603
34824
  const axis = (_b3 = geometry.CircularArc) == null ? void 0 : _b3.axis;
33604
34825
  const radius = (_c2 = geometry.CircularArc) == null ? void 0 : _c2.radius;
33605
- if (center !== void 0 && axis !== void 0 && radius !== void 0 && isVec3(center) && isVec3(axis) && isFiniteNumber(radius) && radius > 0) {
34826
+ if (center2 !== void 0 && axis !== void 0 && radius !== void 0 && isVec3(center2) && isVec3(axis) && isFiniteNumber(radius) && radius > 0) {
33606
34827
  return {
33607
34828
  kind: "circle",
33608
- center,
34829
+ center: center2,
33609
34830
  axis: normalizeVec3(axis),
33610
34831
  radius,
33611
34832
  faceName
@@ -36221,15 +37442,15 @@ function regionsHaveMatchingTopology$1(stations) {
36221
37442
  );
36222
37443
  }
36223
37444
  function lowerDraftPlan(plan) {
36224
- const direction = normalizedVector3(plan.pullDirection, "draft() pull direction");
36225
- if (Math.abs(direction[0]) > 1e-9 || Math.abs(direction[1]) > 1e-9 || Math.abs(Math.abs(direction[2]) - 1) > 1e-9) {
37445
+ const direction2 = normalizedVector3(plan.pullDirection, "draft() pull direction");
37446
+ if (Math.abs(direction2[0]) > 1e-9 || Math.abs(direction2[1]) > 1e-9 || Math.abs(Math.abs(direction2[2]) - 1) > 1e-9) {
36226
37447
  return truckUnsupported("draft() for non-Z pull directions");
36227
37448
  }
36228
37449
  const prism = lowerPlanToVerticalPrism(plan.base, true);
36229
37450
  if (!prism) return truckUnsupported("draft() for non-vertical-prism solids");
36230
37451
  if (!(prism.zMax > prism.zMin + 1e-9)) return truckUnsupported("draft() with collapsed vertical span");
36231
37452
  const tanAngle = Math.tan(plan.angleDeg * Math.PI / 180);
36232
- const axialSign = direction[2] >= 0 ? 1 : -1;
37453
+ const axialSign = direction2[2] >= 0 ? 1 : -1;
36233
37454
  const offsetAtZ = (z2) => (axialSign * z2 - plan.neutralPlaneOffset) * tanAngle;
36234
37455
  const bottomReferenceRegions = prism.regions.map(simplifyCollinearRegion);
36235
37456
  const sourceTopRegions = prism.topRegions ?? prism.regions;
@@ -36279,8 +37500,8 @@ function offsetLoftPlan(plan, thickness) {
36279
37500
  )
36280
37501
  );
36281
37502
  }
36282
- function addScaled3(point2, direction, scale2) {
36283
- return [point2[0] + direction[0] * scale2, point2[1] + direction[1] * scale2, point2[2] + direction[2] * scale2];
37503
+ function addScaled3(point2, direction2, scale2) {
37504
+ return [point2[0] + direction2[0] * scale2, point2[1] + direction2[1] * scale2, point2[2] + direction2[2] * scale2];
36284
37505
  }
36285
37506
  function isStraightMonotonePolyline(points) {
36286
37507
  if (points.length < 2) return false;
@@ -36549,8 +37770,8 @@ function resamplePolyline(points, count) {
36549
37770
  for (let idx = 0; idx < count; idx++) {
36550
37771
  const target = idx / (count - 1) * total;
36551
37772
  while (segment < cumulative.length - 2 && cumulative[segment + 1] < target) segment++;
36552
- const span = cumulative[segment + 1] - cumulative[segment];
36553
- const t = span > 1e-12 ? (target - cumulative[segment]) / span : 0;
37773
+ const span2 = cumulative[segment + 1] - cumulative[segment];
37774
+ const t = span2 > 1e-12 ? (target - cumulative[segment]) / span2 : 0;
36554
37775
  const a2 = points[segment];
36555
37776
  const b = points[segment + 1];
36556
37777
  out.push([a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t]);
@@ -37308,12 +38529,12 @@ function transformedSphereProfile(basePlan, steps, sliceOffset) {
37308
38529
  transform = transform.mul(transformForShapeTransform(step));
37309
38530
  scale2 *= stepScale;
37310
38531
  }
37311
- const center = transform.point([0, 0, 0]);
38532
+ const center2 = transform.point([0, 0, 0]);
37312
38533
  const radius = Math.abs(source.radius * scale2);
37313
- const sectionRadius = sliceOffset == null ? radius : Math.sqrt(Math.max(0, radius * radius - (sliceOffset - center[2]) * (sliceOffset - center[2])));
38534
+ const sectionRadius = sliceOffset == null ? radius : Math.sqrt(Math.max(0, radius * radius - (sliceOffset - center2[2]) * (sliceOffset - center2[2])));
37314
38535
  if (sectionRadius <= EXACT_PROFILE_EPS) return emptyProfilePlan();
37315
38536
  const profile = circleProfilePlan(sectionRadius, source.segments);
37316
- if (!isNearlyZero(center[0]) || !isNearlyZero(center[1])) profile.transforms.push({ kind: "translate", x: center[0], y: center[1] });
38537
+ if (!isNearlyZero(center2[0]) || !isNearlyZero(center2[1])) profile.transforms.push({ kind: "translate", x: center2[0], y: center2[1] });
37317
38538
  return profile;
37318
38539
  }
37319
38540
  function profileTransformsForShapeTransform(step) {
@@ -37416,20 +38637,20 @@ function sliceOffsetBeforeShapeTransforms(steps, offset) {
37416
38637
  }
37417
38638
  return sourceOffset;
37418
38639
  }
37419
- function transformZSpan(span, step) {
38640
+ function transformZSpan(span2, step) {
37420
38641
  switch (step.kind) {
37421
38642
  case "translate":
37422
- return [span[0] + step.z, span[1] + step.z];
38643
+ return [span2[0] + step.z, span2[1] + step.z];
37423
38644
  case "scale":
37424
38645
  if (!Number.isFinite(step.z) || isNearlyZero(step.z)) return null;
37425
- return [Math.min(span[0] * step.z, span[1] * step.z), Math.max(span[0] * step.z, span[1] * step.z)];
38646
+ return [Math.min(span2[0] * step.z, span2[1] * step.z), Math.max(span2[0] * step.z, span2[1] * step.z)];
37426
38647
  case "rotateAround": {
37427
38648
  const axisLength = Math.hypot(step.axisX, step.axisY, step.axisZ);
37428
38649
  if (axisLength <= EXACT_PROFILE_EPS) return null;
37429
38650
  const axisX = step.axisX / axisLength;
37430
38651
  const axisY = step.axisY / axisLength;
37431
38652
  const axisZ = step.axisZ / axisLength;
37432
- return !isNearlyZero(axisX) || !isNearlyZero(axisY) || Math.abs(Math.abs(axisZ) - 1) > EXACT_PROFILE_EPS ? null : span;
38653
+ return !isNearlyZero(axisX) || !isNearlyZero(axisY) || Math.abs(Math.abs(axisZ) - 1) > EXACT_PROFILE_EPS ? null : span2;
37433
38654
  }
37434
38655
  case "mirror": {
37435
38656
  const normalLength = Math.hypot(step.normalX, step.normalY, step.normalZ);
@@ -37437,14 +38658,14 @@ function transformZSpan(span, step) {
37437
38658
  const normalX = step.normalX / normalLength;
37438
38659
  const normalY = step.normalY / normalLength;
37439
38660
  const normalZ = step.normalZ / normalLength;
37440
- if (isNearlyZero(normalX) && isNearlyZero(normalY)) return [-span[1], -span[0]];
37441
- return isNearlyZero(normalZ) ? span : null;
38661
+ if (isNearlyZero(normalX) && isNearlyZero(normalY)) return [-span2[1], -span2[0]];
38662
+ return isNearlyZero(normalZ) ? span2 : null;
37442
38663
  }
37443
38664
  case "workplanePlacement": {
37444
38665
  const replay = workplanePlacementReplay(step.matrix);
37445
38666
  if (!replay) return null;
37446
- const z0 = replay.zScale * span[0] + replay.zTranslate;
37447
- const z1 = replay.zScale * span[1] + replay.zTranslate;
38667
+ const z0 = replay.zScale * span2[0] + replay.zTranslate;
38668
+ const z1 = replay.zScale * span2[1] + replay.zTranslate;
37448
38669
  return [Math.min(z0, z1), Math.max(z0, z1)];
37449
38670
  }
37450
38671
  default:
@@ -37498,14 +38719,14 @@ function transformProfilePointThroughRadial(point2, transforms) {
37498
38719
  return out;
37499
38720
  }
37500
38721
  function profileRadialTransformBasis(transforms) {
37501
- const center = transformProfilePointThroughRadial([0, 0], transforms);
38722
+ const center2 = transformProfilePointThroughRadial([0, 0], transforms);
37502
38723
  const xBasis = transformProfilePointThroughRadial([1, 0], transforms);
37503
38724
  const yBasis = transformProfilePointThroughRadial([0, 1], transforms);
37504
- if (!center || !xBasis || !yBasis) return null;
38725
+ if (!center2 || !xBasis || !yBasis) return null;
37505
38726
  return {
37506
- center,
37507
- xAxis: [xBasis[0] - center[0], xBasis[1] - center[1]],
37508
- yAxis: [yBasis[0] - center[0], yBasis[1] - center[1]]
38727
+ center: center2,
38728
+ xAxis: [xBasis[0] - center2[0], xBasis[1] - center2[1]],
38729
+ yAxis: [yBasis[0] - center2[0], yBasis[1] - center2[1]]
37509
38730
  };
37510
38731
  }
37511
38732
  function circleRadialProjectionInterval(plan) {
@@ -37625,11 +38846,11 @@ function radialRoundedRectSignedDistance(footprint, point2) {
37625
38846
  const qy = Math.abs(localPoint[1]) - Math.max(0, footprint.halfHeight - footprint.radius);
37626
38847
  return Math.hypot(Math.max(qx, 0), Math.max(qy, 0)) + Math.min(Math.max(qx, qy), 0) - footprint.radius;
37627
38848
  }
37628
- function radialRoundedRectHalfExtentAlong(footprint, direction) {
38849
+ function radialRoundedRectHalfExtentAlong(footprint, direction2) {
37629
38850
  const coreHalfWidth = Math.max(0, footprint.halfWidth - footprint.radius);
37630
38851
  const coreHalfHeight = Math.max(0, footprint.halfHeight - footprint.radius);
37631
- const directionLength = Math.hypot(direction[0], direction[1]);
37632
- return Math.abs(direction[0] * footprint.xAxis[0] + direction[1] * footprint.xAxis[1]) * coreHalfWidth + Math.abs(direction[0] * footprint.yAxis[0] + direction[1] * footprint.yAxis[1]) * coreHalfHeight + footprint.radius * directionLength;
38852
+ const directionLength = Math.hypot(direction2[0], direction2[1]);
38853
+ return Math.abs(direction2[0] * footprint.xAxis[0] + direction2[1] * footprint.xAxis[1]) * coreHalfWidth + Math.abs(direction2[0] * footprint.yAxis[0] + direction2[1] * footprint.yAxis[1]) * coreHalfHeight + footprint.radius * directionLength;
37633
38854
  }
37634
38855
  function radialRoundedRectBoundaryMaxDistanceFromPoint(footprint, point2) {
37635
38856
  const coreHalfWidth = Math.max(0, footprint.halfWidth - footprint.radius);
@@ -37653,13 +38874,13 @@ function radialRoundedRectBoundaryMaxDistanceFromPoint(footprint, point2) {
37653
38874
  [-1, -1]
37654
38875
  ];
37655
38876
  for (const [sx, sy] of cornerSigns) {
37656
- const center = [sx * coreHalfWidth, sy * coreHalfHeight];
37657
- includeLocal([center[0] + sx * footprint.radius, center[1]]);
37658
- includeLocal([center[0], center[1] + sy * footprint.radius]);
37659
- const away = [center[0] - localPoint[0], center[1] - localPoint[1]];
38877
+ const center2 = [sx * coreHalfWidth, sy * coreHalfHeight];
38878
+ includeLocal([center2[0] + sx * footprint.radius, center2[1]]);
38879
+ includeLocal([center2[0], center2[1] + sy * footprint.radius]);
38880
+ const away = [center2[0] - localPoint[0], center2[1] - localPoint[1]];
37660
38881
  const awayLength = Math.hypot(away[0], away[1]);
37661
38882
  if (awayLength > EXACT_PROFILE_EPS && away[0] * sx >= -EXACT_PROFILE_EPS && away[1] * sy >= -EXACT_PROFILE_EPS) {
37662
- includeLocal([center[0] + away[0] / awayLength * footprint.radius, center[1] + away[1] / awayLength * footprint.radius]);
38883
+ includeLocal([center2[0] + away[0] / awayLength * footprint.radius, center2[1] + away[1] / awayLength * footprint.radius]);
37663
38884
  }
37664
38885
  }
37665
38886
  return maxDistance;
@@ -37746,10 +38967,10 @@ function lineIntervalsForRect(base, tangent, halfWidth, halfHeight) {
37746
38967
  if (halfWidth < -EXACT_PROFILE_EPS || halfHeight < -EXACT_PROFILE_EPS) return null;
37747
38968
  let min2 = -Infinity;
37748
38969
  let max2 = Infinity;
37749
- const applyAxis = (origin, direction, extent) => {
37750
- if (Math.abs(direction) <= EXACT_PROFILE_EPS) return Math.abs(origin) <= extent + EXACT_PROFILE_EPS;
37751
- const a2 = (-extent - origin) / direction;
37752
- const b = (extent - origin) / direction;
38970
+ const applyAxis = (origin, direction2, extent) => {
38971
+ if (Math.abs(direction2) <= EXACT_PROFILE_EPS) return Math.abs(origin) <= extent + EXACT_PROFILE_EPS;
38972
+ const a2 = (-extent - origin) / direction2;
38973
+ const b = (extent - origin) / direction2;
37753
38974
  min2 = Math.max(min2, Math.min(a2, b));
37754
38975
  max2 = Math.min(max2, Math.max(a2, b));
37755
38976
  return min2 <= max2 + EXACT_PROFILE_EPS;
@@ -37757,10 +38978,10 @@ function lineIntervalsForRect(base, tangent, halfWidth, halfHeight) {
37757
38978
  if (!applyAxis(base[0], tangent[0], halfWidth) || !applyAxis(base[1], tangent[1], halfHeight)) return [];
37758
38979
  return [[min2, max2]];
37759
38980
  }
37760
- function lineIntervalsForCircle(base, tangent, center, radius) {
38981
+ function lineIntervalsForCircle(base, tangent, center2, radius) {
37761
38982
  if (radius < -EXACT_PROFILE_EPS) return null;
37762
- const dx = base[0] - center[0];
37763
- const dy = base[1] - center[1];
38983
+ const dx = base[0] - center2[0];
38984
+ const dy = base[1] - center2[1];
37764
38985
  const projection = dx * tangent[0] + dy * tangent[1];
37765
38986
  const distanceSq = dx * dx + dy * dy - projection * projection;
37766
38987
  const radiusSq = radius * radius;
@@ -37803,7 +39024,7 @@ function roundedRectRadialSliceIntervals(plan, y2) {
37803
39024
  [-coreHalfWidth, coreHalfHeight],
37804
39025
  [-coreHalfWidth, -coreHalfHeight]
37805
39026
  ];
37806
- for (const center of centers) include(lineIntervalsForCircle(base, tangent, center, radius));
39027
+ for (const center2 of centers) include(lineIntervalsForCircle(base, tangent, center2, radius));
37807
39028
  }
37808
39029
  const xFunctional = [basis.xAxis[0], basis.yAxis[0]];
37809
39030
  const xAtBase = basis.center[0] + xFunctional[0] * base[0] + xFunctional[1] * base[1];
@@ -39111,9 +40332,9 @@ function expandSimpleFullRevolutionProfileFootprint(plan, distance) {
39111
40332
  }
39112
40333
  }
39113
40334
  function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
39114
- const translate = (profile, center) => {
39115
- if (!isNearlyZero(center[0]) || !isNearlyZero(center[1])) {
39116
- profile.transforms.push({ kind: "translate", x: center[0], y: center[1] });
40335
+ const translate = (profile, center2) => {
40336
+ if (!isNearlyZero(center2[0]) || !isNearlyZero(center2[1])) {
40337
+ profile.transforms.push({ kind: "translate", x: center2[0], y: center2[1] });
39117
40338
  }
39118
40339
  return profile;
39119
40340
  };
@@ -39131,7 +40352,7 @@ function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
39131
40352
  footprint.footprint.center
39132
40353
  ) : null;
39133
40354
  case "roundedRect": {
39134
- const { halfWidth, halfHeight, radius, xAxis, yAxis, center } = footprint.footprint;
40355
+ const { halfWidth, halfHeight, radius, xAxis, yAxis, center: center2 } = footprint.footprint;
39135
40356
  if (halfWidth <= EXACT_PROFILE_EPS || halfHeight <= EXACT_PROFILE_EPS || radius < -EXACT_PROFILE_EPS) return null;
39136
40357
  const profile = roundedRectProfilePlan({
39137
40358
  width: halfWidth * 2,
@@ -39146,7 +40367,7 @@ function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
39146
40367
  m11: yAxis[1]
39147
40368
  })
39148
40369
  );
39149
- return translate(profile, center);
40370
+ return translate(profile, center2);
39150
40371
  }
39151
40372
  default:
39152
40373
  return assertExhaustive(footprint);
@@ -39603,28 +40824,28 @@ function cleanSweepPolyline(points) {
39603
40824
  function verticalSweepFrameTransforms(path, up) {
39604
40825
  if (!up.every(Number.isFinite)) return null;
39605
40826
  const [originX, originY] = path[0];
39606
- let direction = 0;
40827
+ let direction2 = 0;
39607
40828
  for (let idx = 1; idx < path.length; idx += 1) {
39608
40829
  const [x2, y2, z2] = path[idx];
39609
40830
  if (!isNearlyZero(x2 - originX) || !isNearlyZero(y2 - originY)) return null;
39610
40831
  const dz = z2 - path[idx - 1][2];
39611
40832
  if (isNearlyZero(dz)) return null;
39612
40833
  const segmentDirection = dz > 0 ? 1 : -1;
39613
- if (direction === 0) direction = segmentDirection;
39614
- if (direction !== segmentDirection) return null;
40834
+ if (direction2 === 0) direction2 = segmentDirection;
40835
+ if (direction2 !== segmentDirection) return null;
39615
40836
  }
39616
- let xAxisX = up[1] * direction;
39617
- let xAxisY = -up[0] * direction;
40837
+ let xAxisX = up[1] * direction2;
40838
+ let xAxisY = -up[0] * direction2;
39618
40839
  let xAxisLength = Math.hypot(xAxisX, xAxisY);
39619
40840
  if (xAxisLength <= 1e-8) {
39620
40841
  xAxisX = 0;
39621
- xAxisY = -direction;
40842
+ xAxisY = -direction2;
39622
40843
  xAxisLength = 1;
39623
40844
  }
39624
40845
  xAxisX /= xAxisLength;
39625
40846
  xAxisY /= xAxisLength;
39626
- const yAxisX = -direction * xAxisY;
39627
- const yAxisY = direction * xAxisX;
40847
+ const yAxisX = -direction2 * xAxisY;
40848
+ const yAxisY = direction2 * xAxisX;
39628
40849
  const determinant = xAxisX * yAxisY - yAxisX * xAxisY;
39629
40850
  if (Math.abs(Math.abs(determinant) - 1) > 1e-7) return null;
39630
40851
  const expectedYAxisX = determinant > 0 ? -xAxisY : xAxisY;
@@ -39733,16 +40954,16 @@ function exactVerticalProjectionProfilePlan(plan) {
39733
40954
  const base = exactVerticalProjectionProfilePlan(plan.base);
39734
40955
  if (!base) return null;
39735
40956
  let profile = base.profile;
39736
- let span = [base.zMin, base.zMax];
40957
+ let span2 = [base.zMin, base.zMax];
39737
40958
  for (const step of plan.steps) {
39738
40959
  const projectedTransforms = profileTransformsForShapeTransform(step);
39739
40960
  if (!projectedTransforms) return null;
39740
- const nextSpan = transformZSpan(span, step);
40961
+ const nextSpan = transformZSpan(span2, step);
39741
40962
  if (!nextSpan) return null;
39742
40963
  profile = appendProfileTransforms(profile, projectedTransforms);
39743
- span = nextSpan;
40964
+ span2 = nextSpan;
39744
40965
  }
39745
- return { profile, zMin: span[0], zMax: span[1] };
40966
+ return { profile, zMin: span2[0], zMax: span2[1] };
39746
40967
  }
39747
40968
  default:
39748
40969
  return null;
@@ -39762,7 +40983,7 @@ function projectVerticalDifferenceProfilePlan(subject, clips) {
39762
40983
  const clipped = clips.map((clip) => ({ clip, span: clippedZSpan(subject, clip) })).filter((entry) => entry.span != null);
39763
40984
  if (clipped.length === 0) return cloneProfileCompilePlan(subject.profile);
39764
40985
  const zBreaks = [subject.zMin, subject.zMax];
39765
- for (const { span } of clipped) zBreaks.push(span[0], span[1]);
40986
+ for (const { span: span2 } of clipped) zBreaks.push(span2[0], span2[1]);
39766
40987
  zBreaks.sort((a2, b) => a2 - b);
39767
40988
  const uniqueBreaks = [];
39768
40989
  for (const z2 of zBreaks) {
@@ -39775,7 +40996,7 @@ function projectVerticalDifferenceProfilePlan(subject, clips) {
39775
40996
  const zMax = uniqueBreaks[idx + 1];
39776
40997
  if (zMax - zMin <= EXACT_PROFILE_EPS) continue;
39777
40998
  const zMid = (zMin + zMax) / 2;
39778
- const activeClips = clipped.filter(({ span }) => offsetInRange(zMid, span[0], span[1]));
40999
+ const activeClips = clipped.filter(({ span: span2 }) => offsetInRange(zMid, span2[0], span2[1]));
39779
41000
  if (activeClips.length === 0) return cloneProfileCompilePlan(subject.profile);
39780
41001
  slabProfiles.push({
39781
41002
  kind: "boolean",
@@ -39933,9 +41154,9 @@ function offsetSolidVerticalVariableSweepSlicedProfilePlan(plan, offset) {
39933
41154
  if (base.kind !== "variableSweep" || !Number.isFinite(plan.thickness) || isNearlyZero(plan.thickness)) return null;
39934
41155
  const pathInfo = verticalSweepPathInfo(base.path, base.up);
39935
41156
  if (!pathInfo || isNearlyZero(pathInfo.endZ - pathInfo.startZ)) return null;
39936
- const direction = pathInfo.endZ > pathInfo.startZ ? 1 : -1;
39937
- const startZ = pathInfo.startZ - direction * plan.thickness;
39938
- const endZ = pathInfo.endZ + direction * plan.thickness;
41157
+ const direction2 = pathInfo.endZ > pathInfo.startZ ? 1 : -1;
41158
+ const startZ = pathInfo.startZ - direction2 * plan.thickness;
41159
+ const endZ = pathInfo.endZ + direction2 * plan.thickness;
39939
41160
  if (Math.abs(endZ - startZ) <= EXACT_PROFILE_EPS) return null;
39940
41161
  if (!offsetInRange(offset, startZ, endZ)) return emptyProfilePlan();
39941
41162
  const offsetProfileSections = base.sections.map(
@@ -40748,7 +41969,7 @@ function clusterMeshFaces(shape) {
40748
41969
  return clusters;
40749
41970
  }
40750
41971
  function clusterToFaceRef(cluster, name = "") {
40751
- const center = [
41972
+ const center2 = [
40752
41973
  cluster.centroidSum[0] / cluster.count,
40753
41974
  cluster.centroidSum[1] / cluster.count,
40754
41975
  cluster.centroidSum[2] / cluster.count
@@ -40757,7 +41978,7 @@ function clusterToFaceRef(cluster, name = "") {
40757
41978
  return {
40758
41979
  name,
40759
41980
  normal: cluster.normal,
40760
- center,
41981
+ center: center2,
40761
41982
  planar: true,
40762
41983
  uAxis: u2,
40763
41984
  vAxis: v
@@ -40929,7 +42150,7 @@ function representativeFaceForMembers(name, faces, query, semantic) {
40929
42150
  const cloned = faces.map((face) => cloneFaceRefValue(face));
40930
42151
  const first = cloned[0];
40931
42152
  const coplanar = facesAreCoplanar(cloned);
40932
- const center = cloned.reduce(
42153
+ const center2 = cloned.reduce(
40933
42154
  (acc, face) => [acc[0] + face.center[0], acc[1] + face.center[1], acc[2] + face.center[2]],
40934
42155
  [0, 0, 0]
40935
42156
  );
@@ -40937,7 +42158,7 @@ function representativeFaceForMembers(name, faces, query, semantic) {
40937
42158
  const representative = {
40938
42159
  ...first,
40939
42160
  name,
40940
- center: [center[0] / count, center[1] / count, center[2] / count],
42161
+ center: [center2[0] / count, center2[1] / count, center2[2] / count],
40941
42162
  query: cloneFaceQueryRef(query),
40942
42163
  planar: coplanar ? first.planar : false,
40943
42164
  uAxis: coplanar ? first.uAxis : void 0,
@@ -42057,10 +43278,10 @@ function resolveShapeFaceTableInternal(plan, owner) {
42057
43278
  if (!planeCapQuery) return table;
42058
43279
  const baseTable = resolveShapeFaceTable(plan.base);
42059
43280
  const centers = Array.from(baseTable.faces.values()).map((face) => face.center);
42060
- const averageCenter = centers.length > 0 ? centers.reduce((acc, center2) => [acc[0] + center2[0], acc[1] + center2[1], acc[2] + center2[2]], [0, 0, 0]).map((value) => value / centers.length) : [0, 0, plan.originOffset];
43281
+ const averageCenter = centers.length > 0 ? centers.reduce((acc, center22) => [acc[0] + center22[0], acc[1] + center22[1], acc[2] + center22[2]], [0, 0, 0]).map((value) => value / centers.length) : [0, 0, plan.originOffset];
42061
43282
  const normal = normalizeAxis([plan.normalX, plan.normalY, plan.normalZ]);
42062
43283
  const planeDistance = averageCenter[0] * normal[0] + averageCenter[1] * normal[1] + averageCenter[2] * normal[2] - plan.originOffset;
42063
- const center = [
43284
+ const center2 = [
42064
43285
  averageCenter[0] - normal[0] * planeDistance,
42065
43286
  averageCenter[1] - normal[1] * planeDistance,
42066
43287
  averageCenter[2] - normal[2] * planeDistance
@@ -42069,7 +43290,7 @@ function resolveShapeFaceTableInternal(plan, owner) {
42069
43290
  registerFace(table, {
42070
43291
  name: "plane-cap",
42071
43292
  normal,
42072
- center,
43293
+ center: center2,
42073
43294
  planar: true,
42074
43295
  uAxis: basis.u,
42075
43296
  vAxis: basis.v,
@@ -43256,8 +44477,8 @@ class ShapeGroup {
43256
44477
  );
43257
44478
  }
43258
44479
  /** Reorient the group so its local Z axis points along `direction`. */
43259
- pointAlong(direction) {
43260
- const [dx, dy, dz] = requireNonZeroFiniteVec3(direction, "ShapeGroup.pointAlong() direction");
44480
+ pointAlong(direction2) {
44481
+ const [dx, dy, dz] = requireNonZeroFiniteVec3(direction2, "ShapeGroup.pointAlong() direction");
43261
44482
  const len = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1;
43262
44483
  const nx = dx / len, ny = dy / len, nz = dz / len;
43263
44484
  const cx = -ny, cy = nx, cz = 0;
@@ -44801,10 +46022,10 @@ function rayTriangle(ox, oy, oz, dx, dy, dz, ax, ay, az, bx, by, bz, cx, cy, cz)
44801
46022
  const t = (e2x * qx + e2y * qy + e2z * qz) * invDet;
44802
46023
  return t;
44803
46024
  }
44804
- function rayMeshIntersect(mesh, origin, direction) {
46025
+ function rayMeshIntersect(mesh, origin, direction2) {
44805
46026
  const { numProp, numTri, triVerts, vertProperties } = mesh;
44806
46027
  const [ox, oy, oz] = origin;
44807
- const [dx, dy, dz] = direction;
46028
+ const [dx, dy, dz] = direction2;
44808
46029
  const hits = [];
44809
46030
  for (let t = 0; t < numTri; t++) {
44810
46031
  const i0 = triVerts[t * 3];
@@ -44969,6 +46190,99 @@ function computeSeatOverTranslation(targetFaceVertices, targetFaceNormal, selfMe
44969
46190
  sampleCount: targetFaceVertices.length
44970
46191
  };
44971
46192
  }
46193
+ const UV_ANCHORS = ["top", "bottom", "front", "back", "left", "right"];
46194
+ new Set(UV_ANCHORS);
46195
+ const FACE_FRAME = {
46196
+ front: { u: 0, v: 2 },
46197
+ // -y face: u→x, v→z
46198
+ back: { u: 0, v: 2 },
46199
+ // +y face
46200
+ left: { u: 1, v: 2 },
46201
+ // -x face: u→y, v→z
46202
+ right: { u: 1, v: 2 },
46203
+ // +x face
46204
+ top: { u: 0, v: 1 },
46205
+ // +z face: u→x, v→y
46206
+ bottom: { u: 0, v: 1 }
46207
+ // -z face
46208
+ };
46209
+ const AXIS_INDEX = { x: 0, y: 1, z: 2 };
46210
+ function span(bbox, axis) {
46211
+ return bbox.max[axis] - bbox.min[axis];
46212
+ }
46213
+ function center(bbox) {
46214
+ return [
46215
+ (bbox.min[0] + bbox.max[0]) / 2,
46216
+ (bbox.min[1] + bbox.max[1]) / 2,
46217
+ (bbox.min[2] + bbox.max[2]) / 2
46218
+ ];
46219
+ }
46220
+ function autoFitProjection(spec, bbox) {
46221
+ switch (spec.kind) {
46222
+ case "flat": {
46223
+ const frame = FACE_FRAME[spec.onto];
46224
+ const out = { ...spec };
46225
+ if (out.width === void 0) {
46226
+ const w2 = span(bbox, frame.u);
46227
+ if (w2 > 0) {
46228
+ out.width = w2;
46229
+ if (out.offsetU === void 0) out.offsetU = bbox.min[frame.u];
46230
+ }
46231
+ }
46232
+ if (out.height === void 0) {
46233
+ const h = span(bbox, frame.v);
46234
+ if (h > 0) {
46235
+ out.height = h;
46236
+ if (out.offsetV === void 0) out.offsetV = bbox.min[frame.v];
46237
+ }
46238
+ }
46239
+ return out;
46240
+ }
46241
+ case "box": {
46242
+ const out = { ...spec };
46243
+ if (out.size === void 0) {
46244
+ const sx = span(bbox, 0);
46245
+ const sy = span(bbox, 1);
46246
+ const sz = span(bbox, 2);
46247
+ if (sx > 0 && sy > 0 && sz > 0) out.size = [sx, sy, sz];
46248
+ }
46249
+ if (out.origin === void 0) out.origin = center(bbox);
46250
+ return out;
46251
+ }
46252
+ case "cylinder": {
46253
+ const axis = AXIS_INDEX[spec.axis];
46254
+ const out = { ...spec };
46255
+ if (out.height === void 0) {
46256
+ const h = span(bbox, axis);
46257
+ if (h > 0) {
46258
+ out.height = h;
46259
+ if (out.offsetV === void 0) out.offsetV = bbox.min[axis];
46260
+ }
46261
+ }
46262
+ if (out.origin === void 0) out.origin = center(bbox);
46263
+ return out;
46264
+ }
46265
+ case "sphere": {
46266
+ const out = { ...spec };
46267
+ if (out.origin === void 0) out.origin = center(bbox);
46268
+ if (out.radius === void 0) {
46269
+ const c2 = center(bbox);
46270
+ const radius = Math.max(
46271
+ bbox.max[0] - c2[0],
46272
+ bbox.max[1] - c2[1],
46273
+ bbox.max[2] - c2[2]
46274
+ );
46275
+ if (radius > 0) out.radius = radius;
46276
+ }
46277
+ return out;
46278
+ }
46279
+ default:
46280
+ throw new Error(`autoFitProjection: unknown projection kind "${spec.kind}"`);
46281
+ }
46282
+ }
46283
+ function isImageHandle(value) {
46284
+ return typeof value === "object" && value !== null && value.__forgeImage === true;
46285
+ }
44972
46286
  const _shapeDimensions = /* @__PURE__ */ new WeakMap();
44973
46287
  let _shapeDimensionCounter = 0;
44974
46288
  function nextShapeDimensionId() {
@@ -45321,8 +46635,8 @@ function mergeShapeSourceSpans(sources, target) {
45321
46635
  records = /* @__PURE__ */ new Map();
45322
46636
  _shapeSourceSpans.set(target, records);
45323
46637
  }
45324
- for (const [key2, span] of sourceRecords) {
45325
- if (!records.has(key2)) records.set(key2, span);
46638
+ for (const [key2, span2] of sourceRecords) {
46639
+ if (!records.has(key2)) records.set(key2, span2);
45326
46640
  }
45327
46641
  }
45328
46642
  }
@@ -45559,6 +46873,7 @@ function withBaseDimensions(base, out) {
45559
46873
  if (baseTopo) _shapeTopology.set(out, cloneTopology(baseTopo));
45560
46874
  const baseLabels = cloneFaceLabelMap(_shapeFaceLabels.get(base));
45561
46875
  if (baseLabels) _shapeFaceLabels.set(out, baseLabels);
46876
+ if (base.materialProps) out.materialProps = { ...base.materialProps };
45562
46877
  copyShapeReferenceMetadata(base, out);
45563
46878
  copyShapeSourceSpans(base, out);
45564
46879
  return setShapeCompilePlanInternal(out, getShapeCompilePlanInternal(base));
@@ -45623,6 +46938,30 @@ function createOwnedTopologyRewritePlan(plan, operation2, buildPropagation) {
45623
46938
  const owner = createShapeQueryOwner(operation2);
45624
46939
  return wrapShapeCompilePlanWithQueryOwner(attachTopologyRewritePropagation(plan, buildPropagation(owner)), owner);
45625
46940
  }
46941
+ const KNOWN_PROJECTION_KINDS = /* @__PURE__ */ new Set(["flat", "cylinder", "sphere", "box"]);
46942
+ function validateProjectionFinite(spec) {
46943
+ switch (spec.kind) {
46944
+ case "flat":
46945
+ if (spec.width !== void 0) requireFiniteNumber(spec.width, "wrapTexture() flat width");
46946
+ if (spec.height !== void 0) requireFiniteNumber(spec.height, "wrapTexture() flat height");
46947
+ if (spec.offsetU !== void 0) requireFiniteNumber(spec.offsetU, "wrapTexture() flat offsetU");
46948
+ if (spec.offsetV !== void 0) requireFiniteNumber(spec.offsetV, "wrapTexture() flat offsetV");
46949
+ break;
46950
+ case "box":
46951
+ if (spec.size !== void 0) requireFiniteVec3$1(spec.size, "wrapTexture() box size");
46952
+ if (spec.origin !== void 0) requireFiniteVec3$1(spec.origin, "wrapTexture() box origin");
46953
+ break;
46954
+ case "cylinder":
46955
+ if (spec.height !== void 0) requireFiniteNumber(spec.height, "wrapTexture() cylinder height");
46956
+ if (spec.offsetV !== void 0) requireFiniteNumber(spec.offsetV, "wrapTexture() cylinder offsetV");
46957
+ if (spec.origin !== void 0) requireFiniteVec3$1(spec.origin, "wrapTexture() cylinder origin");
46958
+ break;
46959
+ case "sphere":
46960
+ if (spec.origin !== void 0) requireFiniteVec3$1(spec.origin, "wrapTexture() sphere origin");
46961
+ if (spec.radius !== void 0) requireFiniteNumber(spec.radius, "wrapTexture() sphere radius");
46962
+ break;
46963
+ }
46964
+ }
45626
46965
  function parseReferencePath(path) {
45627
46966
  const trimmed = path.trim();
45628
46967
  if (!trimmed) throw new Error("Shape.ref() requires a non-empty path.");
@@ -46000,6 +47339,61 @@ class Shape {
46000
47339
  out.materialProps = { ...this.materialProps ?? {}, ...props };
46001
47340
  return out;
46002
47341
  }
47342
+ /**
47343
+ * Wrap an imported bitmap image around this shape using a projection.
47344
+ *
47345
+ * **Details**
47346
+ *
47347
+ * The `image` comes from `Import.image('path.png')`; the `projection` is one of the `Wrap.*`
47348
+ * helpers — `Wrap.flat({ onto: 'top' })` lays it flat on a face, `Wrap.aroundCylinder({ axis: 'z' })`
47349
+ * wraps it like a can label, `Wrap.onSphere()` maps it like a globe, and `Wrap.box()` cube-maps
47350
+ * it onto the six sides.
47351
+ *
47352
+ * By default the image **auto-fits** the shape — one copy across the relevant extent, so no
47353
+ * `width`/`height`/`size` is needed (pass them only to override). The (u,v) is derived from each
47354
+ * vertex's final world position, so the image stays glued to the surface through transforms and
47355
+ * boolean cuts with no UV layout to maintain — apply `wrapTexture` *after* positioning the shape.
47356
+ * Returns a new Shape; the original is unchanged.
47357
+ *
47358
+ * **Example**
47359
+ *
47360
+ * ```js
47361
+ * const logo = Import.image('./logo.png');
47362
+ * box(80, 80, 10).wrapTexture(logo, Wrap.flat({ onto: 'top' })); // auto-fits the face
47363
+ *
47364
+ * const label = Import.image('./label.jpg');
47365
+ * cylinder(60, 20).wrapTexture(label, Wrap.aroundCylinder({ axis: 'z' })); // wraps the side
47366
+ * ```
47367
+ *
47368
+ * @param image - An imported bitmap from `Import.image(...)`
47369
+ * @param projection - A projection spec from `Wrap.flat` / `Wrap.aroundCylinder` / `Wrap.onSphere` / `Wrap.box`
47370
+ * @returns A new Shape with the projected texture recorded in its material properties
47371
+ * @category Materials
47372
+ */
47373
+ wrapTexture(image, projection) {
47374
+ if (!isImageHandle(image)) {
47375
+ throw new Error("wrapTexture() expects an image from Import.image(...) as its first argument");
47376
+ }
47377
+ if (!projection || typeof projection !== "object" || !KNOWN_PROJECTION_KINDS.has(projection.kind)) {
47378
+ throw new Error(
47379
+ `wrapTexture() expects a projection from Wrap.flat / Wrap.aroundCylinder / Wrap.onSphere / Wrap.box, got ${JSON.stringify(projection == null ? void 0 : projection.kind)}`
47380
+ );
47381
+ }
47382
+ const bbox = this.boundingBox();
47383
+ const filled = autoFitProjection(projection, bbox);
47384
+ validateProjectionFinite(filled);
47385
+ const out = this.clone();
47386
+ out.materialProps = {
47387
+ ...this.materialProps ?? {},
47388
+ texture: {
47389
+ image: image.dataUri,
47390
+ projection: filled,
47391
+ imageWidth: image.width,
47392
+ imageHeight: image.height
47393
+ }
47394
+ };
47395
+ return out;
47396
+ }
46003
47397
  /** Return a new Shape wrapper for explicit duplication in scripts. */
46004
47398
  clone() {
46005
47399
  const out = withCopiedDimensions(this, new Shape(getShapeRuntimeBackendInternal(this).clone(), this.colorHex));
@@ -46527,12 +47921,12 @@ class Shape {
46527
47921
  scale(v) {
46528
47922
  const scale2 = requireNonZeroFiniteScale3(v, "Shape.scale() scale");
46529
47923
  const bb = this.boundingBox();
46530
- const center = [
47924
+ const center2 = [
46531
47925
  (bb.min[0] + bb.max[0]) / 2,
46532
47926
  (bb.min[1] + bb.max[1]) / 2,
46533
47927
  (bb.min[2] + bb.max[2]) / 2
46534
47928
  ];
46535
- return this.scaleAround(center, scale2);
47929
+ return this.scaleAround(center2, scale2);
46536
47930
  }
46537
47931
  /** Scale the shape uniformly or per-axis from an explicit pivot point. */
46538
47932
  scaleAround(pivot, v) {
@@ -46566,12 +47960,12 @@ class Shape {
46566
47960
  /** Mirror across a plane through the shape's bounding box center, defined by its normal vector. */
46567
47961
  mirror(normal) {
46568
47962
  const bb = this.boundingBox();
46569
- const center = [
47963
+ const center2 = [
46570
47964
  (bb.min[0] + bb.max[0]) / 2,
46571
47965
  (bb.min[1] + bb.max[1]) / 2,
46572
47966
  (bb.min[2] + bb.max[2]) / 2
46573
47967
  ];
46574
- return this.mirrorThrough(center, normal);
47968
+ return this.mirrorThrough(center2, normal);
46575
47969
  }
46576
47970
  /** Mirror across a plane through an explicit point, defined by its normal vector. */
46577
47971
  mirrorThrough(point2, normal) {
@@ -46611,8 +48005,8 @@ class Shape {
46611
48005
  *
46612
48006
  * Example: cylinder(40, 5).pointAlong([1, 0, 0]) — lays cylinder along X, starting at origin
46613
48007
  */
46614
- pointAlong(direction) {
46615
- const [dx, dy, dz] = requireNonZeroFiniteVec3(direction, "Shape.pointAlong() direction");
48008
+ pointAlong(direction2) {
48009
+ const [dx, dy, dz] = requireNonZeroFiniteVec3(direction2, "Shape.pointAlong() direction");
46616
48010
  const len = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1;
46617
48011
  const nx = dx / len, ny = dy / len, nz = dz / len;
46618
48012
  const cx = -ny, cy = nx, cz = 0;
@@ -49304,731 +50698,204 @@ function thicknessClass(thickness, options) {
49304
50698
  }
49305
50699
  function thicknessColor(thickness, options) {
49306
50700
  if (thickness == null || !Number.isFinite(thickness) || thickness <= 0) return THICKNESS_COLORS.unknown;
49307
- const span = Math.max(1e-9, options.colorMaxThickness - options.colorMinThickness);
49308
- return gradientColor(THICKNESS_GRADIENT_COLORS, (thickness - options.colorMinThickness) / span);
50701
+ const span2 = Math.max(1e-9, options.colorMaxThickness - options.colorMinThickness);
50702
+ return gradientColor(THICKNESS_GRADIENT_COLORS, (thickness - options.colorMinThickness) / span2);
49309
50703
  }
49310
- function cloneGeometryForFaceColors(geometry) {
49311
- return geometry.index ? geometry.toNonIndexed() : geometry.clone();
50704
+ const CENTER = 0;
50705
+ const AVERAGE = 1;
50706
+ const SAH = 2;
50707
+ const CONTAINED = 2;
50708
+ const TRIANGLE_INTERSECT_COST = 1.25;
50709
+ const TRAVERSAL_COST = 1;
50710
+ const BYTES_PER_NODE = 6 * 4 + 4 + 4;
50711
+ const IS_LEAFNODE_FLAG = 65535;
50712
+ const FLOAT32_EPSILON = Math.pow(2, -24);
50713
+ const SKIP_GENERATION = Symbol("SKIP_GENERATION");
50714
+ function getVertexCount(geo) {
50715
+ return geo.index ? geo.index.count : geo.attributes.position.count;
49312
50716
  }
49313
- function geometryMaxDimension(geometry) {
49314
- geometry.computeBoundingBox();
49315
- const box = geometry.boundingBox;
49316
- if (!box) return 1;
49317
- const size = new Vector3();
49318
- box.getSize(size);
49319
- return Math.max(1, size.x, size.y, size.z);
50717
+ function getTriCount(geo) {
50718
+ return getVertexCount(geo) / 3;
49320
50719
  }
49321
- function firstOppositeSurfaceDistance(raycaster, rayTargetMeshes, jumpableMeshes, point2, direction, epsilon2, far, contactTolerance) {
49322
- const origin = point2.clone().addScaledVector(direction, epsilon2);
49323
- raycaster.set(origin, direction);
49324
- raycaster.near = epsilon2;
49325
- raycaster.far = far;
49326
- const hits = raycaster.intersectObjects(rayTargetMeshes, false);
49327
- for (const hit of hits) {
49328
- if (hit.distance <= epsilon2) continue;
49329
- if (hit.distance <= contactTolerance && jumpableMeshes.has(hit.object)) continue;
49330
- return hit.distance + epsilon2;
50720
+ function getIndexArray(vertexCount, BufferConstructor = ArrayBuffer) {
50721
+ if (vertexCount > 65535) {
50722
+ return new Uint32Array(new BufferConstructor(4 * vertexCount));
50723
+ } else {
50724
+ return new Uint16Array(new BufferConstructor(2 * vertexCount));
49331
50725
  }
49332
- return null;
49333
50726
  }
49334
- function triangleThickness(raycaster, rayTargetMeshes, jumpableMeshes, centroid, normal, epsilon2, far, contactTolerance) {
49335
- const forward = firstOppositeSurfaceDistance(
49336
- raycaster,
49337
- rayTargetMeshes,
49338
- jumpableMeshes,
49339
- centroid,
49340
- normal,
49341
- epsilon2,
49342
- far,
49343
- contactTolerance
49344
- );
49345
- const backward = firstOppositeSurfaceDistance(
49346
- raycaster,
49347
- rayTargetMeshes,
49348
- jumpableMeshes,
49349
- centroid,
49350
- normal.clone().negate(),
49351
- epsilon2,
49352
- far,
49353
- contactTolerance
49354
- );
49355
- if (forward == null) return backward;
49356
- if (backward == null) return forward;
49357
- return Math.min(forward, backward);
50727
+ function ensureIndex(geo, options) {
50728
+ if (!geo.index) {
50729
+ const vertexCount = geo.attributes.position.count;
50730
+ const BufferConstructor = options.useSharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer;
50731
+ const index2 = getIndexArray(vertexCount, BufferConstructor);
50732
+ geo.setIndex(new BufferAttribute(index2, 1));
50733
+ for (let i = 0; i < vertexCount; i++) {
50734
+ index2[i] = i;
50735
+ }
50736
+ }
49358
50737
  }
49359
- function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}, context = {}) {
49360
- const options = resolveThicknessInspectionOptions(rawOptions);
49361
- const geometry = cloneGeometryForFaceColors(sourceGeometry);
49362
- const position = geometry.getAttribute("position");
49363
- if (!position || position.count < 3) {
49364
- return {
49365
- geometry,
49366
- samples: [],
49367
- pointSamples: [],
49368
- triangleCount: 0,
49369
- sampledTriangleCount: 0,
49370
- sampleStride: 1,
49371
- warnings: ["No triangle geometry."]
49372
- };
50738
+ function getFullGeometryRange(geo, range) {
50739
+ const triCount = getTriCount(geo);
50740
+ const drawRange = range ? range : geo.drawRange;
50741
+ const start = drawRange.start / 3;
50742
+ const end = (drawRange.start + drawRange.count) / 3;
50743
+ const offset = Math.max(0, start);
50744
+ const count = Math.min(triCount, end) - offset;
50745
+ return [{
50746
+ offset: Math.floor(offset),
50747
+ count: Math.floor(count)
50748
+ }];
50749
+ }
50750
+ function getRootIndexRanges(geo, range) {
50751
+ if (!geo.groups || !geo.groups.length) {
50752
+ return getFullGeometryRange(geo, range);
49373
50753
  }
49374
- const triangleCount = Math.floor(position.count / 3);
49375
- const surfaceTriangles = readSurfaceTriangles(position);
49376
- const surfaceSamples = sampleSurfaceTriangles(surfaceTriangles, options.maxSamplesPerObject);
49377
- const connectedGeometries = context.connectedGeometries ?? [];
49378
- const maxDim2 = Math.max(geometryMaxDimension(geometry), ...connectedGeometries.map(geometryMaxDimension));
49379
- const epsilon2 = Math.max(1e-4, maxDim2 * 1e-6);
49380
- const far = Math.max(maxDim2 * 4, options.maxThickness * 4, 1);
49381
- const colors = new Float32Array(position.count * 3);
49382
- const triangleThicknessValues = new Array(triangleCount).fill(void 0);
49383
- const samples = [];
49384
- const pointSamples = [];
49385
- const warnings = [];
49386
- const rayMaterial = new MeshBasicMaterial({ side: DoubleSide });
49387
- const rayTargets = [
49388
- { mesh: new Mesh(geometry, rayMaterial), jumpable: false },
49389
- ...connectedGeometries.map((connectedGeometry) => ({
49390
- mesh: new Mesh(connectedGeometry, rayMaterial),
49391
- jumpable: true
49392
- }))
49393
- ];
49394
- const rayTargetMeshes = rayTargets.map((target) => target.mesh);
49395
- const jumpableMeshes = new Set(rayTargets.filter((target) => target.jumpable).map((target) => target.mesh));
49396
- const raycaster = new Raycaster();
49397
- if (surfaceTriangles.length === 0) {
49398
- warnings.push("No non-degenerate triangle surface was available for thickness sampling.");
49399
- } else if (surfaceSamples.length < surfaceTriangles.length) {
49400
- warnings.push(
49401
- `Area sampling budget ${surfaceSamples.length} covers ${surfaceTriangles.length} surface triangles; increase --thickness-samples for denser analysis.`
49402
- );
50754
+ const ranges = [];
50755
+ const rangeBoundaries = /* @__PURE__ */ new Set();
50756
+ const drawRange = range ? range : geo.drawRange;
50757
+ const drawRangeStart = drawRange.start / 3;
50758
+ const drawRangeEnd = (drawRange.start + drawRange.count) / 3;
50759
+ for (const group of geo.groups) {
50760
+ const groupStart = group.start / 3;
50761
+ const groupEnd = (group.start + group.count) / 3;
50762
+ rangeBoundaries.add(Math.max(drawRangeStart, groupStart));
50763
+ rangeBoundaries.add(Math.min(drawRangeEnd, groupEnd));
49403
50764
  }
49404
- const sampledTriangleIndexes = /* @__PURE__ */ new Set();
49405
- for (const sample of surfaceSamples) {
49406
- sampledTriangleIndexes.add(sample.triangle.index);
49407
- const thickness = triangleThickness(
49408
- raycaster,
49409
- rayTargetMeshes,
49410
- jumpableMeshes,
49411
- sample.position,
49412
- sample.normal,
49413
- epsilon2,
49414
- far,
49415
- options.contactTolerance
49416
- );
49417
- samples.push({ thickness, area: sample.area });
49418
- const previous = triangleThicknessValues[sample.triangle.index];
49419
- if (previous === void 0 || previous == null || thickness != null && thickness < previous) {
49420
- triangleThicknessValues[sample.triangle.index] = thickness;
49421
- }
49422
- pointSamples.push({
49423
- position: [sample.position.x, sample.position.y, sample.position.z],
49424
- normal: [sample.normal.x, sample.normal.y, sample.normal.z],
49425
- value: thickness,
49426
- className: thicknessClass(thickness, options),
49427
- color: thicknessColor(thickness, options),
49428
- area: sample.area
50765
+ const sortedBoundaries = Array.from(rangeBoundaries.values()).sort((a2, b) => a2 - b);
50766
+ for (let i = 0; i < sortedBoundaries.length - 1; i++) {
50767
+ const start = sortedBoundaries[i];
50768
+ const end = sortedBoundaries[i + 1];
50769
+ ranges.push({
50770
+ offset: Math.floor(start),
50771
+ count: Math.floor(end - start)
49429
50772
  });
49430
50773
  }
49431
- for (let tri = 0; tri < triangleCount; tri += 1) {
49432
- const color = thicknessColor(triangleThicknessValues[tri], options);
49433
- const offset = tri * 3;
49434
- for (let vertex2 = 0; vertex2 < 3; vertex2 += 1) {
49435
- const colorOffset = (offset + vertex2) * 3;
49436
- colors[colorOffset] = color[0] / 255;
49437
- colors[colorOffset + 1] = color[1] / 255;
49438
- colors[colorOffset + 2] = color[2] / 255;
49439
- }
49440
- }
49441
- geometry.setAttribute("color", new BufferAttribute(colors, 3));
49442
- rayMaterial.dispose();
49443
- return {
49444
- geometry,
49445
- samples,
49446
- pointSamples,
49447
- triangleCount,
49448
- sampledTriangleCount: sampledTriangleIndexes.size,
49449
- sampleStride: Math.max(1, Math.ceil(Math.max(1, surfaceTriangles.length) / Math.max(1, sampledTriangleIndexes.size))),
49450
- warnings
49451
- };
49452
- }
49453
- const DEFAULT_VERTEX_CAP = 2e6;
49454
- const DEFAULT_TARGET_EDGE_SPACING_FACTOR = 2;
49455
- const DEFAULT_K = 8;
49456
- const DEFAULT_GATE_DOT = 0.3;
49457
- const SCATTER_CELL_SPACING_FACTOR = 1.5;
49458
- const SCATTER_RADIUS_SPACING_FACTOR = 3;
49459
- const IDW_DISTANCE_FLOOR = 1e-9;
49460
- const MAX_SUBDIVISION_PASSES = 24;
49461
- function subVec(a2, b) {
49462
- return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
49463
- }
49464
- function crossVec(a2, b) {
49465
- return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
49466
- }
49467
- function lenVec(a2) {
49468
- return Math.hypot(a2[0], a2[1], a2[2]);
49469
- }
49470
- function getVert(positions, i) {
49471
- const o = i * 3;
49472
- return [positions[o], positions[o + 1], positions[o + 2]];
49473
- }
49474
- function triArea(positions, a2, b, c2) {
49475
- const va = getVert(positions, a2);
49476
- return 0.5 * lenVec(crossVec(subVec(getVert(positions, b), va), subVec(getVert(positions, c2), va)));
49477
- }
49478
- function triNormal(positions, a2, b, c2) {
49479
- const va = getVert(positions, a2);
49480
- const n = crossVec(subVec(getVert(positions, b), va), subVec(getVert(positions, c2), va));
49481
- const l = lenVec(n) || 1;
49482
- return [n[0] / l, n[1] / l, n[2] / l];
50774
+ return ranges;
49483
50775
  }
49484
- function edgeLen(positions, a2, b) {
49485
- return lenVec(subVec(getVert(positions, a2), getVert(positions, b)));
50776
+ function hasGroupGaps(geometry, range) {
50777
+ const vertexCount = getTriCount(geometry);
50778
+ const groups = getRootIndexRanges(geometry, range).sort((a2, b) => a2.offset - b.offset);
50779
+ const finalGroup = groups[groups.length - 1];
50780
+ finalGroup.count = Math.min(vertexCount - finalGroup.offset, finalGroup.count);
50781
+ let total = 0;
50782
+ groups.forEach(({ count }) => total += count);
50783
+ return vertexCount !== total;
49486
50784
  }
49487
- function maxEdge(positions, a2, b, c2) {
49488
- return Math.max(edgeLen(positions, a2, b), edgeLen(positions, b, c2), edgeLen(positions, c2, a2));
50785
+ function getBounds(triangleBounds2, offset, count, target, centroidTarget) {
50786
+ let minx = Infinity;
50787
+ let miny = Infinity;
50788
+ let minz = Infinity;
50789
+ let maxx = -Infinity;
50790
+ let maxy = -Infinity;
50791
+ let maxz = -Infinity;
50792
+ let cminx = Infinity;
50793
+ let cminy = Infinity;
50794
+ let cminz = Infinity;
50795
+ let cmaxx = -Infinity;
50796
+ let cmaxy = -Infinity;
50797
+ let cmaxz = -Infinity;
50798
+ for (let i = offset * 6, end = (offset + count) * 6; i < end; i += 6) {
50799
+ const cx = triangleBounds2[i + 0];
50800
+ const hx = triangleBounds2[i + 1];
50801
+ const lx = cx - hx;
50802
+ const rx = cx + hx;
50803
+ if (lx < minx) minx = lx;
50804
+ if (rx > maxx) maxx = rx;
50805
+ if (cx < cminx) cminx = cx;
50806
+ if (cx > cmaxx) cmaxx = cx;
50807
+ const cy = triangleBounds2[i + 2];
50808
+ const hy = triangleBounds2[i + 3];
50809
+ const ly = cy - hy;
50810
+ const ry = cy + hy;
50811
+ if (ly < miny) miny = ly;
50812
+ if (ry > maxy) maxy = ry;
50813
+ if (cy < cminy) cminy = cy;
50814
+ if (cy > cmaxy) cmaxy = cy;
50815
+ const cz = triangleBounds2[i + 4];
50816
+ const hz = triangleBounds2[i + 5];
50817
+ const lz = cz - hz;
50818
+ const rz = cz + hz;
50819
+ if (lz < minz) minz = lz;
50820
+ if (rz > maxz) maxz = rz;
50821
+ if (cz < cminz) cminz = cz;
50822
+ if (cz > cmaxz) cmaxz = cz;
50823
+ }
50824
+ target[0] = minx;
50825
+ target[1] = miny;
50826
+ target[2] = minz;
50827
+ target[3] = maxx;
50828
+ target[4] = maxy;
50829
+ target[5] = maxz;
50830
+ centroidTarget[0] = cminx;
50831
+ centroidTarget[1] = cminy;
50832
+ centroidTarget[2] = cminz;
50833
+ centroidTarget[3] = cmaxx;
50834
+ centroidTarget[4] = cmaxy;
50835
+ centroidTarget[5] = cmaxz;
49489
50836
  }
49490
- function weld(positions) {
49491
- if (positions.length % 9 !== 0) {
49492
- throw new Error(
49493
- `weld: positions length must be a multiple of 9 (9 floats per triangle), got ${positions.length}`
49494
- );
50837
+ function computeTriangleBounds(geo, target = null, offset = null, count = null) {
50838
+ const posAttr = geo.attributes.position;
50839
+ const index2 = geo.index ? geo.index.array : null;
50840
+ const triCount = getTriCount(geo);
50841
+ const normalized = posAttr.normalized;
50842
+ let triangleBounds2;
50843
+ if (target === null) {
50844
+ triangleBounds2 = new Float32Array(triCount * 6);
50845
+ offset = 0;
50846
+ count = triCount;
50847
+ } else {
50848
+ triangleBounds2 = target;
50849
+ offset = offset || 0;
50850
+ count = count || triCount;
49495
50851
  }
49496
- positions.length / 3;
49497
- let minX = Infinity;
49498
- let minY = Infinity;
49499
- let minZ = Infinity;
49500
- let maxX = -Infinity;
49501
- let maxY = -Infinity;
49502
- let maxZ = -Infinity;
49503
- for (let i = 0; i < positions.length; i += 3) {
49504
- const x2 = positions[i];
49505
- const y2 = positions[i + 1];
49506
- const z2 = positions[i + 2];
49507
- if (!Number.isFinite(x2) || !Number.isFinite(y2) || !Number.isFinite(z2)) {
49508
- throw new Error(`weld: non-finite vertex position at float index ${i}`);
49509
- }
49510
- if (x2 < minX) minX = x2;
49511
- if (y2 < minY) minY = y2;
49512
- if (z2 < minZ) minZ = z2;
49513
- if (x2 > maxX) maxX = x2;
49514
- if (y2 > maxY) maxY = y2;
49515
- if (z2 > maxZ) maxZ = z2;
50852
+ const posArr = posAttr.array;
50853
+ const bufferOffset = posAttr.offset || 0;
50854
+ let stride = 3;
50855
+ if (posAttr.isInterleavedBufferAttribute) {
50856
+ stride = posAttr.data.stride;
49516
50857
  }
49517
- const diagonal = Math.hypot(maxX - minX, maxY - minY, maxZ - minZ);
49518
- const tolerance = Math.max(diagonal * 1e-5, Number.EPSILON);
49519
- const remap = /* @__PURE__ */ new Map();
49520
- const out = [];
49521
- const tris = [];
49522
- const inv = 1 / tolerance;
49523
- const quantKey = (i) => {
49524
- const qx = Math.round(positions[i] * inv);
49525
- const qy = Math.round(positions[i + 1] * inv);
49526
- const qz = Math.round(positions[i + 2] * inv);
49527
- return `${qx},${qy},${qz}`;
49528
- };
49529
- for (let tri = 0; tri < positions.length; tri += 9) {
49530
- const idx = [];
49531
- for (let corner = 0; corner < 3; corner += 1) {
49532
- const o = tri + corner * 3;
49533
- const key2 = quantKey(o);
49534
- let vi = remap.get(key2);
49535
- if (vi === void 0) {
49536
- vi = out.length / 3;
49537
- out.push(positions[o], positions[o + 1], positions[o + 2]);
49538
- remap.set(key2, vi);
49539
- }
49540
- idx.push(vi);
50858
+ const getters = ["getX", "getY", "getZ"];
50859
+ for (let tri = offset; tri < offset + count; tri++) {
50860
+ const tri3 = tri * 3;
50861
+ const tri6 = tri * 6;
50862
+ let ai = tri3 + 0;
50863
+ let bi = tri3 + 1;
50864
+ let ci = tri3 + 2;
50865
+ if (index2) {
50866
+ ai = index2[ai];
50867
+ bi = index2[bi];
50868
+ ci = index2[ci];
49541
50869
  }
49542
- if (idx[0] !== idx[1] && idx[1] !== idx[2] && idx[2] !== idx[0]) {
49543
- tris.push(idx[0], idx[1], idx[2]);
50870
+ if (!normalized) {
50871
+ ai = ai * stride + bufferOffset;
50872
+ bi = bi * stride + bufferOffset;
50873
+ ci = ci * stride + bufferOffset;
49544
50874
  }
49545
- }
49546
- const distinctVertexCount = out.length / 3;
49547
- const degenerate = distinctVertexCount < 3 || tris.length === 0;
49548
- return { positions: out, tris, degenerate };
49549
- }
49550
- function adaptiveSubdivide(mesh, targetEdge, vertexCap) {
49551
- requirePositiveFiniteNumber(targetEdge, "adaptiveSubdivide targetEdge");
49552
- requireIntegerAtLeast(vertexCap, "adaptiveSubdivide vertexCap", 3);
49553
- const positions = mesh.positions.slice();
49554
- let tris = mesh.tris.slice();
49555
- let capped = false;
49556
- const keyOf = (a2, b) => a2 < b ? `${a2}_${b}` : `${b}_${a2}`;
49557
- for (let pass = 0; pass < MAX_SUBDIVISION_PASSES; pass += 1) {
49558
- const next = [];
49559
- const midCache = /* @__PURE__ */ new Map();
49560
- let changed = false;
49561
- const getMid = (a2, b) => {
49562
- const key2 = keyOf(a2, b);
49563
- let mi = midCache.get(key2);
49564
- if (mi === void 0) {
49565
- mi = positions.length / 3;
49566
- const ao = a2 * 3;
49567
- const bo = b * 3;
49568
- positions.push(
49569
- (positions[ao] + positions[bo]) / 2,
49570
- (positions[ao + 1] + positions[bo + 1]) / 2,
49571
- (positions[ao + 2] + positions[bo + 2]) / 2
49572
- );
49573
- midCache.set(key2, mi);
49574
- }
49575
- return mi;
49576
- };
49577
- for (let t = 0; t < tris.length; t += 3) {
49578
- const a2 = tris[t];
49579
- const b = tris[t + 1];
49580
- const c2 = tris[t + 2];
49581
- const vertexCount = positions.length / 3;
49582
- if (maxEdge(positions, a2, b, c2) > targetEdge && vertexCount < vertexCap) {
49583
- const ab = getMid(a2, b);
49584
- const bc = getMid(b, c2);
49585
- const ca = getMid(c2, a2);
49586
- next.push(a2, ab, ca, ab, b, bc, ca, bc, c2, ab, bc, ca);
49587
- changed = true;
50875
+ for (let el = 0; el < 3; el++) {
50876
+ let a2, b, c2;
50877
+ if (normalized) {
50878
+ a2 = posAttr[getters[el]](ai);
50879
+ b = posAttr[getters[el]](bi);
50880
+ c2 = posAttr[getters[el]](ci);
49588
50881
  } else {
49589
- if (positions.length / 3 >= vertexCap) capped = true;
49590
- next.push(a2, b, c2);
50882
+ a2 = posArr[ai + el];
50883
+ b = posArr[bi + el];
50884
+ c2 = posArr[ci + el];
49591
50885
  }
50886
+ let min2 = a2;
50887
+ if (b < min2) min2 = b;
50888
+ if (c2 < min2) min2 = c2;
50889
+ let max2 = a2;
50890
+ if (b > max2) max2 = b;
50891
+ if (c2 > max2) max2 = c2;
50892
+ const halfExtents = (max2 - min2) / 2;
50893
+ const el2 = el * 2;
50894
+ triangleBounds2[tri6 + el2 + 0] = min2 + halfExtents;
50895
+ triangleBounds2[tri6 + el2 + 1] = halfExtents + (Math.abs(min2) + halfExtents) * FLOAT32_EPSILON;
49592
50896
  }
49593
- tris = next;
49594
- if (!changed) break;
49595
50897
  }
49596
- return { positions, tris, capped };
49597
- }
49598
- function vertexNormals(mesh) {
49599
- const vertexCount = mesh.positions.length / 3;
49600
- const acc = new Float32Array(vertexCount * 3);
49601
- for (let t = 0; t < mesh.tris.length; t += 3) {
49602
- const a2 = mesh.tris[t];
49603
- const b = mesh.tris[t + 1];
49604
- const c2 = mesh.tris[t + 2];
49605
- const n = triNormal(mesh.positions, a2, b, c2);
49606
- const area = triArea(mesh.positions, a2, b, c2);
49607
- const nx = n[0] * area;
49608
- const ny = n[1] * area;
49609
- const nz = n[2] * area;
49610
- for (const vi of [a2, b, c2]) {
49611
- acc[vi * 3] += nx;
49612
- acc[vi * 3 + 1] += ny;
49613
- acc[vi * 3 + 2] += nz;
49614
- }
49615
- }
49616
- for (let i = 0; i < vertexCount; i += 1) {
49617
- const o = i * 3;
49618
- const l = Math.hypot(acc[o], acc[o + 1], acc[o + 2]) || 1;
49619
- acc[o] /= l;
49620
- acc[o + 1] /= l;
49621
- acc[o + 2] /= l;
49622
- }
49623
- return acc;
49624
- }
49625
- function buildSampleGrid(positions, cell) {
49626
- const grid = /* @__PURE__ */ new Map();
49627
- const sampleCount = positions.length / 3;
49628
- for (let i = 0; i < sampleCount; i += 1) {
49629
- const o = i * 3;
49630
- const key2 = `${Math.floor(positions[o] / cell)},${Math.floor(positions[o + 1] / cell)},${Math.floor(positions[o + 2] / cell)}`;
49631
- let arr = grid.get(key2);
49632
- if (!arr) {
49633
- arr = [];
49634
- grid.set(key2, arr);
49635
- }
49636
- arr.push(i);
49637
- }
49638
- return { grid, cell };
49639
- }
49640
- function gatherSamples(sampleGrid, p2, rings, out) {
49641
- out.length = 0;
49642
- const { cell, grid } = sampleGrid;
49643
- const bx = Math.floor(p2[0] / cell);
49644
- const by = Math.floor(p2[1] / cell);
49645
- const bz = Math.floor(p2[2] / cell);
49646
- for (let dx = -rings; dx <= rings; dx += 1) {
49647
- for (let dy = -rings; dy <= rings; dy += 1) {
49648
- for (let dz = -rings; dz <= rings; dz += 1) {
49649
- const arr = grid.get(`${bx + dx},${by + dy},${bz + dz}`);
49650
- if (arr) for (const i of arr) out.push(i);
49651
- }
49652
- }
49653
- }
49654
- }
49655
- function scatterToVertices(positions, vnormals, samples, spacing, k2, gateDot) {
49656
- const cell = spacing * SCATTER_CELL_SPACING_FACTOR;
49657
- const sampleGrid = buildSampleGrid(samples.positions, cell);
49658
- const radius = spacing * SCATTER_RADIUS_SPACING_FACTOR;
49659
- const radius2 = radius * radius;
49660
- const vertexCount = positions.length / 3;
49661
- const values = new Float32Array(vertexCount);
49662
- let holeCount = 0;
49663
- const candidates = [];
49664
- const scratchP = [0, 0, 0];
49665
- const bestD2 = new Float64Array(k2);
49666
- const bestVal = new Float64Array(k2);
49667
- for (let vi = 0; vi < vertexCount; vi += 1) {
49668
- const pi = vi * 3;
49669
- const px = positions[pi];
49670
- const py = positions[pi + 1];
49671
- const pz = positions[pi + 2];
49672
- const nx = vnormals[pi];
49673
- const ny = vnormals[pi + 1];
49674
- const nz = vnormals[pi + 2];
49675
- scratchP[0] = px;
49676
- scratchP[1] = py;
49677
- scratchP[2] = pz;
49678
- gatherSamples(sampleGrid, scratchP, 3, candidates);
49679
- let count = 0;
49680
- let worst = 0;
49681
- let worstD2 = -Infinity;
49682
- for (let c2 = 0; c2 < candidates.length; c2 += 1) {
49683
- const si = candidates[c2];
49684
- const so = si * 3;
49685
- const dx = samples.positions[so] - px;
49686
- const dy = samples.positions[so + 1] - py;
49687
- const dz = samples.positions[so + 2] - pz;
49688
- const d2 = dx * dx + dy * dy + dz * dz;
49689
- if (d2 > radius2) continue;
49690
- if (samples.normals[so] * nx + samples.normals[so + 1] * ny + samples.normals[so + 2] * nz < gateDot) continue;
49691
- const val = samples.values[si];
49692
- if (count < k2) {
49693
- bestD2[count] = d2;
49694
- bestVal[count] = val;
49695
- if (d2 > worstD2) {
49696
- worstD2 = d2;
49697
- worst = count;
49698
- }
49699
- count += 1;
49700
- } else if (d2 < worstD2) {
49701
- bestD2[worst] = d2;
49702
- bestVal[worst] = val;
49703
- worstD2 = -Infinity;
49704
- for (let t = 0; t < k2; t += 1) {
49705
- if (bestD2[t] > worstD2) {
49706
- worstD2 = bestD2[t];
49707
- worst = t;
49708
- }
49709
- }
49710
- }
49711
- }
49712
- if (count === 0) {
49713
- holeCount += 1;
49714
- values[vi] = NaN;
49715
- continue;
49716
- }
49717
- let w2 = 0;
49718
- let acc = 0;
49719
- for (let t = 0; t < count; t += 1) {
49720
- const ww = 1 / Math.max(bestD2[t], IDW_DISTANCE_FLOOR);
49721
- w2 += ww;
49722
- acc += ww * bestVal[t];
49723
- }
49724
- values[vi] = acc / w2;
49725
- }
49726
- return { values, holeCount };
49727
- }
49728
- function backfillHoles(values, mesh) {
49729
- const vertexCount = values.length;
49730
- const adjacency = Array.from({ length: vertexCount }, () => []);
49731
- for (let t = 0; t < mesh.tris.length; t += 3) {
49732
- const a2 = mesh.tris[t];
49733
- const b = mesh.tris[t + 1];
49734
- const c2 = mesh.tris[t + 2];
49735
- adjacency[a2].push(b, c2);
49736
- adjacency[b].push(a2, c2);
49737
- adjacency[c2].push(a2, b);
49738
- }
49739
- let frontier = [];
49740
- for (let i = 0; i < vertexCount; i += 1) {
49741
- if (Number.isFinite(values[i])) frontier.push(i);
49742
- }
49743
- if (frontier.length === 0) {
49744
- values.fill(0);
49745
- return;
49746
- }
49747
- while (frontier.length > 0) {
49748
- const nextFrontier = [];
49749
- for (const v of frontier) {
49750
- const val = values[v];
49751
- for (const n of adjacency[v]) {
49752
- if (!Number.isFinite(values[n])) {
49753
- values[n] = val;
49754
- nextFrontier.push(n);
49755
- }
49756
- }
49757
- }
49758
- frontier = nextFrontier;
49759
- }
49760
- }
49761
- function reconstructSurfaceScalarField(trianglePositions, samples, options = {}) {
49762
- if (!(trianglePositions instanceof Float32Array)) {
49763
- throw new Error("reconstructSurfaceScalarField: trianglePositions must be a Float32Array");
49764
- }
49765
- if (trianglePositions.length === 0 || trianglePositions.length % 9 !== 0) {
49766
- throw new Error(
49767
- `reconstructSurfaceScalarField: trianglePositions length must be a positive multiple of 9, got ${trianglePositions.length}`
49768
- );
49769
- }
49770
- if (!(samples.positions instanceof Float32Array) || !(samples.values instanceof Float32Array) || !(samples.normals instanceof Float32Array)) {
49771
- throw new Error("reconstructSurfaceScalarField: samples positions/values/normals must be Float32Arrays");
49772
- }
49773
- const sampleCount = samples.values.length;
49774
- if (sampleCount === 0) {
49775
- throw new Error("reconstructSurfaceScalarField: samples must be non-empty");
49776
- }
49777
- if (samples.positions.length !== sampleCount * 3) {
49778
- throw new Error(
49779
- `reconstructSurfaceScalarField: samples.positions length ${samples.positions.length} must equal values*3 (${sampleCount * 3})`
49780
- );
49781
- }
49782
- if (samples.normals.length !== sampleCount * 3) {
49783
- throw new Error(
49784
- `reconstructSurfaceScalarField: samples.normals length ${samples.normals.length} must equal values*3 (${sampleCount * 3})`
49785
- );
49786
- }
49787
- for (let i = 0; i < sampleCount; i += 1) {
49788
- requireFiniteNumber(samples.values[i], `samples.values[${i}]`);
49789
- }
49790
- const vertexCap = options.vertexCap === void 0 ? DEFAULT_VERTEX_CAP : requireIntegerAtLeast(options.vertexCap, "vertexCap", 3);
49791
- const targetEdgeSpacingFactor = options.targetEdgeSpacingFactor === void 0 ? DEFAULT_TARGET_EDGE_SPACING_FACTOR : requirePositiveFiniteNumber(options.targetEdgeSpacingFactor, "targetEdgeSpacingFactor");
49792
- const k2 = options.k === void 0 ? DEFAULT_K : requireIntegerAtLeast(options.k, "k", 1);
49793
- const gateDot = options.gateDot === void 0 ? DEFAULT_GATE_DOT : requireFiniteNumber(options.gateDot, "gateDot");
49794
- const welded = weld(trianglePositions);
49795
- let surfaceArea = 0;
49796
- for (let t = 0; t < welded.tris.length; t += 3) {
49797
- surfaceArea += triArea(welded.positions, welded.tris[t], welded.tris[t + 1], welded.tris[t + 2]);
49798
- }
49799
- const sampleSpacing = surfaceArea > 0 ? Math.sqrt(surfaceArea / sampleCount) : 0;
49800
- let subdivided;
49801
- if (welded.degenerate || !(sampleSpacing > 0)) {
49802
- subdivided = { positions: welded.positions, tris: welded.tris, capped: false };
49803
- } else {
49804
- const targetEdge = targetEdgeSpacingFactor * sampleSpacing;
49805
- subdivided = adaptiveSubdivide(welded, targetEdge, vertexCap);
49806
- }
49807
- const normals = vertexNormals(subdivided);
49808
- const effectiveSpacing = sampleSpacing > 0 ? sampleSpacing : 1;
49809
- const scatter = scatterToVertices(subdivided.positions, normals, samples, effectiveSpacing, k2, gateDot);
49810
- let valueMin = Infinity;
49811
- let valueMax = -Infinity;
49812
- for (let i = 0; i < scatter.values.length; i += 1) {
49813
- const v = scatter.values[i];
49814
- if (!Number.isFinite(v)) continue;
49815
- if (v < valueMin) valueMin = v;
49816
- if (v > valueMax) valueMax = v;
49817
- }
49818
- if (!Number.isFinite(valueMin) || !Number.isFinite(valueMax)) {
49819
- valueMin = 0;
49820
- valueMax = 0;
49821
- }
49822
- backfillHoles(scatter.values, subdivided);
49823
- const vertexCount = subdivided.positions.length / 3;
49824
- return {
49825
- positions: Float32Array.from(subdivided.positions),
49826
- normals,
49827
- index: Uint32Array.from(subdivided.tris),
49828
- values: scatter.values,
49829
- valueMin,
49830
- valueMax,
49831
- capped: subdivided.capped,
49832
- degenerate: welded.degenerate,
49833
- holeCount: scatter.holeCount,
49834
- vertexCount
49835
- };
49836
- }
49837
- const CENTER = 0;
49838
- const AVERAGE = 1;
49839
- const SAH = 2;
49840
- const CONTAINED = 2;
49841
- const TRIANGLE_INTERSECT_COST = 1.25;
49842
- const TRAVERSAL_COST = 1;
49843
- const BYTES_PER_NODE = 6 * 4 + 4 + 4;
49844
- const IS_LEAFNODE_FLAG = 65535;
49845
- const FLOAT32_EPSILON = Math.pow(2, -24);
49846
- const SKIP_GENERATION = Symbol("SKIP_GENERATION");
49847
- function getVertexCount(geo) {
49848
- return geo.index ? geo.index.count : geo.attributes.position.count;
49849
- }
49850
- function getTriCount(geo) {
49851
- return getVertexCount(geo) / 3;
49852
- }
49853
- function getIndexArray(vertexCount, BufferConstructor = ArrayBuffer) {
49854
- if (vertexCount > 65535) {
49855
- return new Uint32Array(new BufferConstructor(4 * vertexCount));
49856
- } else {
49857
- return new Uint16Array(new BufferConstructor(2 * vertexCount));
49858
- }
49859
- }
49860
- function ensureIndex(geo, options) {
49861
- if (!geo.index) {
49862
- const vertexCount = geo.attributes.position.count;
49863
- const BufferConstructor = options.useSharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer;
49864
- const index2 = getIndexArray(vertexCount, BufferConstructor);
49865
- geo.setIndex(new BufferAttribute(index2, 1));
49866
- for (let i = 0; i < vertexCount; i++) {
49867
- index2[i] = i;
49868
- }
49869
- }
49870
- }
49871
- function getFullGeometryRange(geo, range) {
49872
- const triCount = getTriCount(geo);
49873
- const drawRange = range ? range : geo.drawRange;
49874
- const start = drawRange.start / 3;
49875
- const end = (drawRange.start + drawRange.count) / 3;
49876
- const offset = Math.max(0, start);
49877
- const count = Math.min(triCount, end) - offset;
49878
- return [{
49879
- offset: Math.floor(offset),
49880
- count: Math.floor(count)
49881
- }];
49882
- }
49883
- function getRootIndexRanges(geo, range) {
49884
- if (!geo.groups || !geo.groups.length) {
49885
- return getFullGeometryRange(geo, range);
49886
- }
49887
- const ranges = [];
49888
- const rangeBoundaries = /* @__PURE__ */ new Set();
49889
- const drawRange = range ? range : geo.drawRange;
49890
- const drawRangeStart = drawRange.start / 3;
49891
- const drawRangeEnd = (drawRange.start + drawRange.count) / 3;
49892
- for (const group of geo.groups) {
49893
- const groupStart = group.start / 3;
49894
- const groupEnd = (group.start + group.count) / 3;
49895
- rangeBoundaries.add(Math.max(drawRangeStart, groupStart));
49896
- rangeBoundaries.add(Math.min(drawRangeEnd, groupEnd));
49897
- }
49898
- const sortedBoundaries = Array.from(rangeBoundaries.values()).sort((a2, b) => a2 - b);
49899
- for (let i = 0; i < sortedBoundaries.length - 1; i++) {
49900
- const start = sortedBoundaries[i];
49901
- const end = sortedBoundaries[i + 1];
49902
- ranges.push({
49903
- offset: Math.floor(start),
49904
- count: Math.floor(end - start)
49905
- });
49906
- }
49907
- return ranges;
49908
- }
49909
- function hasGroupGaps(geometry, range) {
49910
- const vertexCount = getTriCount(geometry);
49911
- const groups = getRootIndexRanges(geometry, range).sort((a2, b) => a2.offset - b.offset);
49912
- const finalGroup = groups[groups.length - 1];
49913
- finalGroup.count = Math.min(vertexCount - finalGroup.offset, finalGroup.count);
49914
- let total = 0;
49915
- groups.forEach(({ count }) => total += count);
49916
- return vertexCount !== total;
49917
- }
49918
- function getBounds(triangleBounds2, offset, count, target, centroidTarget) {
49919
- let minx = Infinity;
49920
- let miny = Infinity;
49921
- let minz = Infinity;
49922
- let maxx = -Infinity;
49923
- let maxy = -Infinity;
49924
- let maxz = -Infinity;
49925
- let cminx = Infinity;
49926
- let cminy = Infinity;
49927
- let cminz = Infinity;
49928
- let cmaxx = -Infinity;
49929
- let cmaxy = -Infinity;
49930
- let cmaxz = -Infinity;
49931
- for (let i = offset * 6, end = (offset + count) * 6; i < end; i += 6) {
49932
- const cx = triangleBounds2[i + 0];
49933
- const hx = triangleBounds2[i + 1];
49934
- const lx = cx - hx;
49935
- const rx = cx + hx;
49936
- if (lx < minx) minx = lx;
49937
- if (rx > maxx) maxx = rx;
49938
- if (cx < cminx) cminx = cx;
49939
- if (cx > cmaxx) cmaxx = cx;
49940
- const cy = triangleBounds2[i + 2];
49941
- const hy = triangleBounds2[i + 3];
49942
- const ly = cy - hy;
49943
- const ry = cy + hy;
49944
- if (ly < miny) miny = ly;
49945
- if (ry > maxy) maxy = ry;
49946
- if (cy < cminy) cminy = cy;
49947
- if (cy > cmaxy) cmaxy = cy;
49948
- const cz = triangleBounds2[i + 4];
49949
- const hz = triangleBounds2[i + 5];
49950
- const lz = cz - hz;
49951
- const rz = cz + hz;
49952
- if (lz < minz) minz = lz;
49953
- if (rz > maxz) maxz = rz;
49954
- if (cz < cminz) cminz = cz;
49955
- if (cz > cmaxz) cmaxz = cz;
49956
- }
49957
- target[0] = minx;
49958
- target[1] = miny;
49959
- target[2] = minz;
49960
- target[3] = maxx;
49961
- target[4] = maxy;
49962
- target[5] = maxz;
49963
- centroidTarget[0] = cminx;
49964
- centroidTarget[1] = cminy;
49965
- centroidTarget[2] = cminz;
49966
- centroidTarget[3] = cmaxx;
49967
- centroidTarget[4] = cmaxy;
49968
- centroidTarget[5] = cmaxz;
49969
- }
49970
- function computeTriangleBounds(geo, target = null, offset = null, count = null) {
49971
- const posAttr = geo.attributes.position;
49972
- const index2 = geo.index ? geo.index.array : null;
49973
- const triCount = getTriCount(geo);
49974
- const normalized = posAttr.normalized;
49975
- let triangleBounds2;
49976
- if (target === null) {
49977
- triangleBounds2 = new Float32Array(triCount * 6);
49978
- offset = 0;
49979
- count = triCount;
49980
- } else {
49981
- triangleBounds2 = target;
49982
- offset = offset || 0;
49983
- count = count || triCount;
49984
- }
49985
- const posArr = posAttr.array;
49986
- const bufferOffset = posAttr.offset || 0;
49987
- let stride = 3;
49988
- if (posAttr.isInterleavedBufferAttribute) {
49989
- stride = posAttr.data.stride;
49990
- }
49991
- const getters = ["getX", "getY", "getZ"];
49992
- for (let tri = offset; tri < offset + count; tri++) {
49993
- const tri3 = tri * 3;
49994
- const tri6 = tri * 6;
49995
- let ai = tri3 + 0;
49996
- let bi = tri3 + 1;
49997
- let ci = tri3 + 2;
49998
- if (index2) {
49999
- ai = index2[ai];
50000
- bi = index2[bi];
50001
- ci = index2[ci];
50002
- }
50003
- if (!normalized) {
50004
- ai = ai * stride + bufferOffset;
50005
- bi = bi * stride + bufferOffset;
50006
- ci = ci * stride + bufferOffset;
50007
- }
50008
- for (let el = 0; el < 3; el++) {
50009
- let a2, b, c2;
50010
- if (normalized) {
50011
- a2 = posAttr[getters[el]](ai);
50012
- b = posAttr[getters[el]](bi);
50013
- c2 = posAttr[getters[el]](ci);
50014
- } else {
50015
- a2 = posArr[ai + el];
50016
- b = posArr[bi + el];
50017
- c2 = posArr[ci + el];
50018
- }
50019
- let min2 = a2;
50020
- if (b < min2) min2 = b;
50021
- if (c2 < min2) min2 = c2;
50022
- let max2 = a2;
50023
- if (b > max2) max2 = b;
50024
- if (c2 > max2) max2 = c2;
50025
- const halfExtents = (max2 - min2) / 2;
50026
- const el2 = el * 2;
50027
- triangleBounds2[tri6 + el2 + 0] = min2 + halfExtents;
50028
- triangleBounds2[tri6 + el2 + 1] = halfExtents + (Math.abs(min2) + halfExtents) * FLOAT32_EPSILON;
50029
- }
50030
- }
50031
- return triangleBounds2;
50898
+ return triangleBounds2;
50032
50899
  }
50033
50900
  function arrayToBox(nodeIndex32, array, target) {
50034
50901
  target.min.x = array[nodeIndex32];
@@ -50154,10 +51021,10 @@ function getOptimalSplit(nodeBoundingData, centroidBoundingData, triangleBounds2
50154
51021
  }
50155
51022
  }
50156
51023
  for (let c2 = cStart; c2 < cEnd; c2 += 6) {
50157
- const center = triangleBounds2[c2 + 2 * a2];
51024
+ const center2 = triangleBounds2[c2 + 2 * a2];
50158
51025
  for (let bi = 0; bi < splitCount; bi++) {
50159
51026
  const bin = truncatedBins[bi];
50160
- if (center >= bin.candidate) {
51027
+ if (center2 >= bin.candidate) {
50161
51028
  expandByTriangleBounds(c2, triangleBounds2, bin.rightCacheBounds);
50162
51029
  } else {
50163
51030
  expandByTriangleBounds(c2, triangleBounds2, bin.leftCacheBounds);
@@ -50652,24 +51519,24 @@ const sphereIntersectTriangle = (function() {
50652
51519
  const planeTemp = new Plane();
50653
51520
  const lineTemp = new Line3();
50654
51521
  return function sphereIntersectTriangle2(sphere, triangle3) {
50655
- const { radius, center } = sphere;
51522
+ const { radius, center: center2 } = sphere;
50656
51523
  const { a: a2, b, c: c2 } = triangle3;
50657
51524
  lineTemp.start = a2;
50658
51525
  lineTemp.end = b;
50659
- const closestPoint1 = lineTemp.closestPointToPoint(center, true, closestPointTemp);
50660
- if (closestPoint1.distanceTo(center) <= radius) return true;
51526
+ const closestPoint1 = lineTemp.closestPointToPoint(center2, true, closestPointTemp);
51527
+ if (closestPoint1.distanceTo(center2) <= radius) return true;
50661
51528
  lineTemp.start = a2;
50662
51529
  lineTemp.end = c2;
50663
- const closestPoint2 = lineTemp.closestPointToPoint(center, true, closestPointTemp);
50664
- if (closestPoint2.distanceTo(center) <= radius) return true;
51530
+ const closestPoint2 = lineTemp.closestPointToPoint(center2, true, closestPointTemp);
51531
+ if (closestPoint2.distanceTo(center2) <= radius) return true;
50665
51532
  lineTemp.start = b;
50666
51533
  lineTemp.end = c2;
50667
- const closestPoint3 = lineTemp.closestPointToPoint(center, true, closestPointTemp);
50668
- if (closestPoint3.distanceTo(center) <= radius) return true;
51534
+ const closestPoint3 = lineTemp.closestPointToPoint(center2, true, closestPointTemp);
51535
+ if (closestPoint3.distanceTo(center2) <= radius) return true;
50669
51536
  const plane = triangle3.getPlane(planeTemp);
50670
- const dp = Math.abs(plane.distanceToPoint(center));
51537
+ const dp = Math.abs(plane.distanceToPoint(center2));
50671
51538
  if (dp <= radius) {
50672
- const pp = plane.projectPoint(center, projectedPointTemp);
51539
+ const pp = plane.projectPoint(center2, projectedPointTemp);
50673
51540
  const cp = triangle3.containsPoint(pp);
50674
51541
  if (cp) return true;
50675
51542
  }
@@ -51467,26 +52334,26 @@ const _normalA = /* @__PURE__ */ new Vector3();
51467
52334
  const _normalB = /* @__PURE__ */ new Vector3();
51468
52335
  const _normalC = /* @__PURE__ */ new Vector3();
51469
52336
  const _intersectionPoint = /* @__PURE__ */ new Vector3();
51470
- function checkIntersection(ray, pA, pB, pC, point2, side, near, far) {
52337
+ function checkIntersection(ray2, pA, pB, pC, point2, side, near, far) {
51471
52338
  let intersect2;
51472
52339
  if (side === BackSide) {
51473
- intersect2 = ray.intersectTriangle(pC, pB, pA, true, point2);
52340
+ intersect2 = ray2.intersectTriangle(pC, pB, pA, true, point2);
51474
52341
  } else {
51475
- intersect2 = ray.intersectTriangle(pA, pB, pC, side !== DoubleSide, point2);
52342
+ intersect2 = ray2.intersectTriangle(pA, pB, pC, side !== DoubleSide, point2);
51476
52343
  }
51477
52344
  if (intersect2 === null) return null;
51478
- const distance = ray.origin.distanceTo(point2);
52345
+ const distance = ray2.origin.distanceTo(point2);
51479
52346
  if (distance < near || distance > far) return null;
51480
52347
  return {
51481
52348
  distance,
51482
52349
  point: point2.clone()
51483
52350
  };
51484
52351
  }
51485
- function checkBufferGeometryIntersection(ray, position, normal, uv, uv1, a2, b, c2, side, near, far) {
52352
+ function checkBufferGeometryIntersection(ray2, position, normal, uv, uv1, a2, b, c2, side, near, far) {
51486
52353
  _vA.fromBufferAttribute(position, a2);
51487
52354
  _vB.fromBufferAttribute(position, b);
51488
52355
  _vC.fromBufferAttribute(position, c2);
51489
- const intersection2 = checkIntersection(ray, _vA, _vB, _vC, _intersectionPoint, side, near, far);
52356
+ const intersection2 = checkIntersection(ray2, _vA, _vB, _vC, _intersectionPoint, side, near, far);
51490
52357
  if (intersection2) {
51491
52358
  const barycoord = new Vector3();
51492
52359
  Triangle.getBarycoord(_intersectionPoint, _vA, _vB, _vC, barycoord);
@@ -51507,7 +52374,7 @@ function checkBufferGeometryIntersection(ray, position, normal, uv, uv1, a2, b,
51507
52374
  _normalB.fromBufferAttribute(normal, b);
51508
52375
  _normalC.fromBufferAttribute(normal, c2);
51509
52376
  intersection2.normal = Triangle.getInterpolation(_intersectionPoint, _vA, _vB, _vC, _normalA, _normalB, _normalC, new Vector3());
51510
- if (intersection2.normal.dot(ray.direction) > 0) {
52377
+ if (intersection2.normal.dot(ray2.direction) > 0) {
51511
52378
  intersection2.normal.multiplyScalar(-1);
51512
52379
  }
51513
52380
  }
@@ -51527,7 +52394,7 @@ function checkBufferGeometryIntersection(ray, position, normal, uv, uv1, a2, b,
51527
52394
  }
51528
52395
  return intersection2;
51529
52396
  }
51530
- function intersectTri(geo, side, ray, tri, intersections, near, far) {
52397
+ function intersectTri(geo, side, ray2, tri, intersections, near, far) {
51531
52398
  const triOffset = tri * 3;
51532
52399
  let a2 = triOffset + 0;
51533
52400
  let b = triOffset + 1;
@@ -51539,7 +52406,7 @@ function intersectTri(geo, side, ray, tri, intersections, near, far) {
51539
52406
  c2 = index2.getX(c2);
51540
52407
  }
51541
52408
  const { position, normal, uv, uv1 } = geo.attributes;
51542
- const intersection2 = checkBufferGeometryIntersection(ray, position, normal, uv, uv1, a2, b, c2, side, near, far);
52409
+ const intersection2 = checkBufferGeometryIntersection(ray2, position, normal, uv, uv1, a2, b, c2, side, near, far);
51543
52410
  if (intersection2) {
51544
52411
  intersection2.faceIndex = tri;
51545
52412
  if (intersections) intersections.push(intersection2);
@@ -51569,19 +52436,19 @@ function setTriangle(tri, i, index2, pos) {
51569
52436
  tc.y = pos.getY(i2);
51570
52437
  tc.z = pos.getZ(i2);
51571
52438
  }
51572
- function intersectTris(bvh, side, ray, offset, count, intersections, near, far) {
52439
+ function intersectTris(bvh, side, ray2, offset, count, intersections, near, far) {
51573
52440
  const { geometry, _indirectBuffer } = bvh;
51574
52441
  for (let i = offset, end = offset + count; i < end; i++) {
51575
- intersectTri(geometry, side, ray, i, intersections, near, far);
52442
+ intersectTri(geometry, side, ray2, i, intersections, near, far);
51576
52443
  }
51577
52444
  }
51578
- function intersectClosestTri(bvh, side, ray, offset, count, near, far) {
52445
+ function intersectClosestTri(bvh, side, ray2, offset, count, near, far) {
51579
52446
  const { geometry, _indirectBuffer } = bvh;
51580
52447
  let dist = Infinity;
51581
52448
  let res = null;
51582
52449
  for (let i = offset, end = offset + count; i < end; i++) {
51583
52450
  let intersection2;
51584
- intersection2 = intersectTri(geometry, side, ray, i, null, near, far);
52451
+ intersection2 = intersectTri(geometry, side, ray2, i, null, near, far);
51585
52452
  if (intersection2 && intersection2.distance < dist) {
51586
52453
  res = intersection2;
51587
52454
  dist = intersection2.distance;
@@ -51702,12 +52569,12 @@ function refit(bvh, nodeIndices = null) {
51702
52569
  }
51703
52570
  }
51704
52571
  }
51705
- function intersectRay(nodeIndex32, array, ray, near, far) {
52572
+ function intersectRay(nodeIndex32, array, ray2, near, far) {
51706
52573
  let tmin, tmax, tymin, tymax, tzmin, tzmax;
51707
- const invdirx = 1 / ray.direction.x, invdiry = 1 / ray.direction.y, invdirz = 1 / ray.direction.z;
51708
- const ox = ray.origin.x;
51709
- const oy = ray.origin.y;
51710
- const oz = ray.origin.z;
52574
+ const invdirx = 1 / ray2.direction.x, invdiry = 1 / ray2.direction.y, invdirz = 1 / ray2.direction.z;
52575
+ const ox = ray2.origin.x;
52576
+ const oy = ray2.origin.y;
52577
+ const oz = ray2.origin.z;
51711
52578
  let minx = array[nodeIndex32];
51712
52579
  let maxx = array[nodeIndex32 + 3];
51713
52580
  let miny = array[nodeIndex32 + 1];
@@ -51743,20 +52610,20 @@ function intersectRay(nodeIndex32, array, ray, near, far) {
51743
52610
  if (tzmax < tmax || tmax !== tmax) tmax = tzmax;
51744
52611
  return tmin <= far && tmax >= near;
51745
52612
  }
51746
- function intersectTris_indirect(bvh, side, ray, offset, count, intersections, near, far) {
52613
+ function intersectTris_indirect(bvh, side, ray2, offset, count, intersections, near, far) {
51747
52614
  const { geometry, _indirectBuffer } = bvh;
51748
52615
  for (let i = offset, end = offset + count; i < end; i++) {
51749
52616
  let vi = _indirectBuffer ? _indirectBuffer[i] : i;
51750
- intersectTri(geometry, side, ray, vi, intersections, near, far);
52617
+ intersectTri(geometry, side, ray2, vi, intersections, near, far);
51751
52618
  }
51752
52619
  }
51753
- function intersectClosestTri_indirect(bvh, side, ray, offset, count, near, far) {
52620
+ function intersectClosestTri_indirect(bvh, side, ray2, offset, count, near, far) {
51754
52621
  const { geometry, _indirectBuffer } = bvh;
51755
52622
  let dist = Infinity;
51756
52623
  let res = null;
51757
52624
  for (let i = offset, end = offset + count; i < end; i++) {
51758
52625
  let intersection2;
51759
- intersection2 = intersectTri(geometry, side, ray, _indirectBuffer ? _indirectBuffer[i] : i, null, near, far);
52626
+ intersection2 = intersectTri(geometry, side, ray2, _indirectBuffer ? _indirectBuffer[i] : i, null, near, far);
51760
52627
  if (intersection2 && intersection2.distance < dist) {
51761
52628
  res = intersection2;
51762
52629
  dist = intersection2.distance;
@@ -51779,49 +52646,49 @@ function iterateOverTriangles_indirect(offset, count, bvh, intersectsTriangleFun
51779
52646
  }
51780
52647
  return false;
51781
52648
  }
51782
- function raycast(bvh, root, side, ray, intersects, near, far) {
52649
+ function raycast(bvh, root, side, ray2, intersects, near, far) {
51783
52650
  BufferStack.setBuffer(bvh._roots[root]);
51784
- _raycast$1(0, bvh, side, ray, intersects, near, far);
52651
+ _raycast$1(0, bvh, side, ray2, intersects, near, far);
51785
52652
  BufferStack.clearBuffer();
51786
52653
  }
51787
- function _raycast$1(nodeIndex32, bvh, side, ray, intersects, near, far) {
52654
+ function _raycast$1(nodeIndex32, bvh, side, ray2, intersects, near, far) {
51788
52655
  const { float32Array: float32Array2, uint16Array: uint16Array2, uint32Array: uint32Array2 } = BufferStack;
51789
52656
  const nodeIndex16 = nodeIndex32 * 2;
51790
52657
  const isLeaf = IS_LEAF(nodeIndex16, uint16Array2);
51791
52658
  if (isLeaf) {
51792
52659
  const offset = OFFSET(nodeIndex32, uint32Array2);
51793
52660
  const count = COUNT(nodeIndex16, uint16Array2);
51794
- intersectTris(bvh, side, ray, offset, count, intersects, near, far);
52661
+ intersectTris(bvh, side, ray2, offset, count, intersects, near, far);
51795
52662
  } else {
51796
52663
  const leftIndex = LEFT_NODE(nodeIndex32);
51797
- if (intersectRay(leftIndex, float32Array2, ray, near, far)) {
51798
- _raycast$1(leftIndex, bvh, side, ray, intersects, near, far);
52664
+ if (intersectRay(leftIndex, float32Array2, ray2, near, far)) {
52665
+ _raycast$1(leftIndex, bvh, side, ray2, intersects, near, far);
51799
52666
  }
51800
52667
  const rightIndex = RIGHT_NODE(nodeIndex32, uint32Array2);
51801
- if (intersectRay(rightIndex, float32Array2, ray, near, far)) {
51802
- _raycast$1(rightIndex, bvh, side, ray, intersects, near, far);
52668
+ if (intersectRay(rightIndex, float32Array2, ray2, near, far)) {
52669
+ _raycast$1(rightIndex, bvh, side, ray2, intersects, near, far);
51803
52670
  }
51804
52671
  }
51805
52672
  }
51806
52673
  const _xyzFields$1 = ["x", "y", "z"];
51807
- function raycastFirst(bvh, root, side, ray, near, far) {
52674
+ function raycastFirst(bvh, root, side, ray2, near, far) {
51808
52675
  BufferStack.setBuffer(bvh._roots[root]);
51809
- const result = _raycastFirst$1(0, bvh, side, ray, near, far);
52676
+ const result = _raycastFirst$1(0, bvh, side, ray2, near, far);
51810
52677
  BufferStack.clearBuffer();
51811
52678
  return result;
51812
52679
  }
51813
- function _raycastFirst$1(nodeIndex32, bvh, side, ray, near, far) {
52680
+ function _raycastFirst$1(nodeIndex32, bvh, side, ray2, near, far) {
51814
52681
  const { float32Array: float32Array2, uint16Array: uint16Array2, uint32Array: uint32Array2 } = BufferStack;
51815
52682
  let nodeIndex16 = nodeIndex32 * 2;
51816
52683
  const isLeaf = IS_LEAF(nodeIndex16, uint16Array2);
51817
52684
  if (isLeaf) {
51818
52685
  const offset = OFFSET(nodeIndex32, uint32Array2);
51819
52686
  const count = COUNT(nodeIndex16, uint16Array2);
51820
- return intersectClosestTri(bvh, side, ray, offset, count, near, far);
52687
+ return intersectClosestTri(bvh, side, ray2, offset, count, near, far);
51821
52688
  } else {
51822
52689
  const splitAxis = SPLIT_AXIS(nodeIndex32, uint32Array2);
51823
52690
  const xyzAxis = _xyzFields$1[splitAxis];
51824
- const rayDir = ray.direction[xyzAxis];
52691
+ const rayDir = ray2.direction[xyzAxis];
51825
52692
  const leftToRight = rayDir >= 0;
51826
52693
  let c1, c2;
51827
52694
  if (leftToRight) {
@@ -51831,8 +52698,8 @@ function _raycastFirst$1(nodeIndex32, bvh, side, ray, near, far) {
51831
52698
  c1 = RIGHT_NODE(nodeIndex32, uint32Array2);
51832
52699
  c2 = LEFT_NODE(nodeIndex32);
51833
52700
  }
51834
- const c1Intersection = intersectRay(c1, float32Array2, ray, near, far);
51835
- const c1Result = c1Intersection ? _raycastFirst$1(c1, bvh, side, ray, near, far) : null;
52701
+ const c1Intersection = intersectRay(c1, float32Array2, ray2, near, far);
52702
+ const c1Result = c1Intersection ? _raycastFirst$1(c1, bvh, side, ray2, near, far) : null;
51836
52703
  if (c1Result) {
51837
52704
  const point2 = c1Result.point[xyzAxis];
51838
52705
  const isOutside = leftToRight ? point2 <= float32Array2[c2 + splitAxis] : (
@@ -51843,8 +52710,8 @@ function _raycastFirst$1(nodeIndex32, bvh, side, ray, near, far) {
51843
52710
  return c1Result;
51844
52711
  }
51845
52712
  }
51846
- const c2Intersection = intersectRay(c2, float32Array2, ray, near, far);
51847
- const c2Result = c2Intersection ? _raycastFirst$1(c2, bvh, side, ray, near, far) : null;
52713
+ const c2Intersection = intersectRay(c2, float32Array2, ray2, near, far);
52714
+ const c2Result = c2Intersection ? _raycastFirst$1(c2, bvh, side, ray2, near, far) : null;
51848
52715
  if (c1Result && c2Result) {
51849
52716
  return c1Result.distance <= c2Result.distance ? c1Result : c2Result;
51850
52717
  } else {
@@ -52173,49 +53040,49 @@ function refit_indirect(bvh, nodeIndices = null) {
52173
53040
  }
52174
53041
  }
52175
53042
  }
52176
- function raycast_indirect(bvh, root, side, ray, intersects, near, far) {
53043
+ function raycast_indirect(bvh, root, side, ray2, intersects, near, far) {
52177
53044
  BufferStack.setBuffer(bvh._roots[root]);
52178
- _raycast(0, bvh, side, ray, intersects, near, far);
53045
+ _raycast(0, bvh, side, ray2, intersects, near, far);
52179
53046
  BufferStack.clearBuffer();
52180
53047
  }
52181
- function _raycast(nodeIndex32, bvh, side, ray, intersects, near, far) {
53048
+ function _raycast(nodeIndex32, bvh, side, ray2, intersects, near, far) {
52182
53049
  const { float32Array: float32Array2, uint16Array: uint16Array2, uint32Array: uint32Array2 } = BufferStack;
52183
53050
  const nodeIndex16 = nodeIndex32 * 2;
52184
53051
  const isLeaf = IS_LEAF(nodeIndex16, uint16Array2);
52185
53052
  if (isLeaf) {
52186
53053
  const offset = OFFSET(nodeIndex32, uint32Array2);
52187
53054
  const count = COUNT(nodeIndex16, uint16Array2);
52188
- intersectTris_indirect(bvh, side, ray, offset, count, intersects, near, far);
53055
+ intersectTris_indirect(bvh, side, ray2, offset, count, intersects, near, far);
52189
53056
  } else {
52190
53057
  const leftIndex = LEFT_NODE(nodeIndex32);
52191
- if (intersectRay(leftIndex, float32Array2, ray, near, far)) {
52192
- _raycast(leftIndex, bvh, side, ray, intersects, near, far);
53058
+ if (intersectRay(leftIndex, float32Array2, ray2, near, far)) {
53059
+ _raycast(leftIndex, bvh, side, ray2, intersects, near, far);
52193
53060
  }
52194
53061
  const rightIndex = RIGHT_NODE(nodeIndex32, uint32Array2);
52195
- if (intersectRay(rightIndex, float32Array2, ray, near, far)) {
52196
- _raycast(rightIndex, bvh, side, ray, intersects, near, far);
53062
+ if (intersectRay(rightIndex, float32Array2, ray2, near, far)) {
53063
+ _raycast(rightIndex, bvh, side, ray2, intersects, near, far);
52197
53064
  }
52198
53065
  }
52199
53066
  }
52200
53067
  const _xyzFields = ["x", "y", "z"];
52201
- function raycastFirst_indirect(bvh, root, side, ray, near, far) {
53068
+ function raycastFirst_indirect(bvh, root, side, ray2, near, far) {
52202
53069
  BufferStack.setBuffer(bvh._roots[root]);
52203
- const result = _raycastFirst(0, bvh, side, ray, near, far);
53070
+ const result = _raycastFirst(0, bvh, side, ray2, near, far);
52204
53071
  BufferStack.clearBuffer();
52205
53072
  return result;
52206
53073
  }
52207
- function _raycastFirst(nodeIndex32, bvh, side, ray, near, far) {
53074
+ function _raycastFirst(nodeIndex32, bvh, side, ray2, near, far) {
52208
53075
  const { float32Array: float32Array2, uint16Array: uint16Array2, uint32Array: uint32Array2 } = BufferStack;
52209
53076
  let nodeIndex16 = nodeIndex32 * 2;
52210
53077
  const isLeaf = IS_LEAF(nodeIndex16, uint16Array2);
52211
53078
  if (isLeaf) {
52212
53079
  const offset = OFFSET(nodeIndex32, uint32Array2);
52213
53080
  const count = COUNT(nodeIndex16, uint16Array2);
52214
- return intersectClosestTri_indirect(bvh, side, ray, offset, count, near, far);
53081
+ return intersectClosestTri_indirect(bvh, side, ray2, offset, count, near, far);
52215
53082
  } else {
52216
53083
  const splitAxis = SPLIT_AXIS(nodeIndex32, uint32Array2);
52217
53084
  const xyzAxis = _xyzFields[splitAxis];
52218
- const rayDir = ray.direction[xyzAxis];
53085
+ const rayDir = ray2.direction[xyzAxis];
52219
53086
  const leftToRight = rayDir >= 0;
52220
53087
  let c1, c2;
52221
53088
  if (leftToRight) {
@@ -52225,8 +53092,8 @@ function _raycastFirst(nodeIndex32, bvh, side, ray, near, far) {
52225
53092
  c1 = RIGHT_NODE(nodeIndex32, uint32Array2);
52226
53093
  c2 = LEFT_NODE(nodeIndex32);
52227
53094
  }
52228
- const c1Intersection = intersectRay(c1, float32Array2, ray, near, far);
52229
- const c1Result = c1Intersection ? _raycastFirst(c1, bvh, side, ray, near, far) : null;
53095
+ const c1Intersection = intersectRay(c1, float32Array2, ray2, near, far);
53096
+ const c1Result = c1Intersection ? _raycastFirst(c1, bvh, side, ray2, near, far) : null;
52230
53097
  if (c1Result) {
52231
53098
  const point2 = c1Result.point[xyzAxis];
52232
53099
  const isOutside = leftToRight ? point2 <= float32Array2[c2 + splitAxis] : (
@@ -52237,8 +53104,8 @@ function _raycastFirst(nodeIndex32, bvh, side, ray, near, far) {
52237
53104
  return c1Result;
52238
53105
  }
52239
53106
  }
52240
- const c2Intersection = intersectRay(c2, float32Array2, ray, near, far);
52241
- const c2Result = c2Intersection ? _raycastFirst(c2, bvh, side, ray, near, far) : null;
53107
+ const c2Intersection = intersectRay(c2, float32Array2, ray2, near, far);
53108
+ const c2Result = c2Intersection ? _raycastFirst(c2, bvh, side, ray2, near, far) : null;
52242
53109
  if (c1Result && c2Result) {
52243
53110
  return c1Result.distance <= c2Result.distance ? c1Result : c2Result;
52244
53111
  } else {
@@ -52860,7 +53727,7 @@ class MeshBVH {
52860
53727
  }
52861
53728
  }
52862
53729
  /* Core Cast Functions */
52863
- raycast(ray, materialOrSide = FrontSide, near = 0, far = Infinity) {
53730
+ raycast(ray2, materialOrSide = FrontSide, near = 0, far = Infinity) {
52864
53731
  const roots = this._roots;
52865
53732
  const geometry = this.geometry;
52866
53733
  const intersects = [];
@@ -52872,7 +53739,7 @@ class MeshBVH {
52872
53739
  for (let i = 0, l = roots.length; i < l; i++) {
52873
53740
  const materialSide = isArrayMaterial ? materialOrSide[groups[i].materialIndex].side : side;
52874
53741
  const startCount = intersects.length;
52875
- raycastFunc(this, i, materialSide, ray, intersects, near, far);
53742
+ raycastFunc(this, i, materialSide, ray2, intersects, near, far);
52876
53743
  if (isArrayMaterial) {
52877
53744
  const materialIndex = groups[i].materialIndex;
52878
53745
  for (let j = startCount, jl = intersects.length; j < jl; j++) {
@@ -52882,7 +53749,7 @@ class MeshBVH {
52882
53749
  }
52883
53750
  return intersects;
52884
53751
  }
52885
- raycastFirst(ray, materialOrSide = FrontSide, near = 0, far = Infinity) {
53752
+ raycastFirst(ray2, materialOrSide = FrontSide, near = 0, far = Infinity) {
52886
53753
  const roots = this._roots;
52887
53754
  const geometry = this.geometry;
52888
53755
  const isMaterial = materialOrSide.isMaterial;
@@ -52893,7 +53760,7 @@ class MeshBVH {
52893
53760
  const raycastFirstFunc = this.indirect ? raycastFirst_indirect : raycastFirst;
52894
53761
  for (let i = 0, l = roots.length; i < l; i++) {
52895
53762
  const materialSide = isArrayMaterial ? materialOrSide[groups[i].materialIndex].side : side;
52896
- const result = raycastFirstFunc(this, i, materialSide, ray, near, far);
53763
+ const result = raycastFirstFunc(this, i, materialSide, ray2, near, far);
52897
53764
  if (result != null && (closestResult == null || result.distance < closestResult.distance)) {
52898
53765
  closestResult = result;
52899
53766
  if (isArrayMaterial) {
@@ -53062,6 +53929,637 @@ class MeshBVH {
53062
53929
  return target;
53063
53930
  }
53064
53931
  }
53932
+ function convertRaycastIntersect(hit, object, raycaster) {
53933
+ if (hit === null) {
53934
+ return null;
53935
+ }
53936
+ hit.point.applyMatrix4(object.matrixWorld);
53937
+ hit.distance = hit.point.distanceTo(raycaster.ray.origin);
53938
+ hit.object = object;
53939
+ return hit;
53940
+ }
53941
+ const ray = /* @__PURE__ */ new Ray();
53942
+ const direction = /* @__PURE__ */ new Vector3();
53943
+ const tmpInverseMatrix = /* @__PURE__ */ new Matrix4();
53944
+ const origMeshRaycastFunc = Mesh.prototype.raycast;
53945
+ const origBatchedRaycastFunc = BatchedMesh.prototype.raycast;
53946
+ const _worldScale = /* @__PURE__ */ new Vector3();
53947
+ const _mesh = /* @__PURE__ */ new Mesh();
53948
+ const _batchIntersects = [];
53949
+ function acceleratedRaycast(raycaster, intersects) {
53950
+ if (this.isBatchedMesh) {
53951
+ acceleratedBatchedMeshRaycast.call(this, raycaster, intersects);
53952
+ } else {
53953
+ acceleratedMeshRaycast.call(this, raycaster, intersects);
53954
+ }
53955
+ }
53956
+ function acceleratedBatchedMeshRaycast(raycaster, intersects) {
53957
+ if (this.boundsTrees) {
53958
+ const boundsTrees = this.boundsTrees;
53959
+ const drawInfo = this._drawInfo || this._instanceInfo;
53960
+ const drawRanges = this._drawRanges || this._geometryInfo;
53961
+ const matrixWorld = this.matrixWorld;
53962
+ _mesh.material = this.material;
53963
+ _mesh.geometry = this.geometry;
53964
+ const oldBoundsTree = _mesh.geometry.boundsTree;
53965
+ const oldDrawRange = _mesh.geometry.drawRange;
53966
+ if (_mesh.geometry.boundingSphere === null) {
53967
+ _mesh.geometry.boundingSphere = new Sphere();
53968
+ }
53969
+ for (let i = 0, l = drawInfo.length; i < l; i++) {
53970
+ if (!this.getVisibleAt(i)) {
53971
+ continue;
53972
+ }
53973
+ const geometryId = drawInfo[i].geometryIndex;
53974
+ _mesh.geometry.boundsTree = boundsTrees[geometryId];
53975
+ this.getMatrixAt(i, _mesh.matrixWorld).premultiply(matrixWorld);
53976
+ if (!_mesh.geometry.boundsTree) {
53977
+ this.getBoundingBoxAt(geometryId, _mesh.geometry.boundingBox);
53978
+ this.getBoundingSphereAt(geometryId, _mesh.geometry.boundingSphere);
53979
+ const drawRange = drawRanges[geometryId];
53980
+ _mesh.geometry.setDrawRange(drawRange.start, drawRange.count);
53981
+ }
53982
+ _mesh.raycast(raycaster, _batchIntersects);
53983
+ for (let j = 0, l2 = _batchIntersects.length; j < l2; j++) {
53984
+ const intersect2 = _batchIntersects[j];
53985
+ intersect2.object = this;
53986
+ intersect2.batchId = i;
53987
+ intersects.push(intersect2);
53988
+ }
53989
+ _batchIntersects.length = 0;
53990
+ }
53991
+ _mesh.geometry.boundsTree = oldBoundsTree;
53992
+ _mesh.geometry.drawRange = oldDrawRange;
53993
+ _mesh.material = null;
53994
+ _mesh.geometry = null;
53995
+ } else {
53996
+ origBatchedRaycastFunc.call(this, raycaster, intersects);
53997
+ }
53998
+ }
53999
+ function acceleratedMeshRaycast(raycaster, intersects) {
54000
+ if (this.geometry.boundsTree) {
54001
+ if (this.material === void 0) return;
54002
+ tmpInverseMatrix.copy(this.matrixWorld).invert();
54003
+ ray.copy(raycaster.ray).applyMatrix4(tmpInverseMatrix);
54004
+ _worldScale.setFromMatrixScale(this.matrixWorld);
54005
+ direction.copy(ray.direction).multiply(_worldScale);
54006
+ const scaleFactor = direction.length();
54007
+ const near = raycaster.near / scaleFactor;
54008
+ const far = raycaster.far / scaleFactor;
54009
+ const bvh = this.geometry.boundsTree;
54010
+ if (raycaster.firstHitOnly === true) {
54011
+ const hit = convertRaycastIntersect(bvh.raycastFirst(ray, this.material, near, far), this, raycaster);
54012
+ if (hit) {
54013
+ intersects.push(hit);
54014
+ }
54015
+ } else {
54016
+ const hits = bvh.raycast(ray, this.material, near, far);
54017
+ for (let i = 0, l = hits.length; i < l; i++) {
54018
+ const hit = convertRaycastIntersect(hits[i], this, raycaster);
54019
+ if (hit) {
54020
+ intersects.push(hit);
54021
+ }
54022
+ }
54023
+ }
54024
+ } else {
54025
+ origMeshRaycastFunc.call(this, raycaster, intersects);
54026
+ }
54027
+ }
54028
+ function cloneGeometryForFaceColors(geometry) {
54029
+ return geometry.index ? geometry.toNonIndexed() : geometry.clone();
54030
+ }
54031
+ function makeThicknessRaycastTarget(sourceGeometry, rayMaterial, jumpable) {
54032
+ const geometry = sourceGeometry.clone();
54033
+ geometry.boundsTree = new MeshBVH(geometry);
54034
+ const mesh = new Mesh(geometry, rayMaterial);
54035
+ mesh.raycast = acceleratedRaycast;
54036
+ return { mesh, jumpable, geometry };
54037
+ }
54038
+ function geometryMaxDimension(geometry) {
54039
+ geometry.computeBoundingBox();
54040
+ const box = geometry.boundingBox;
54041
+ if (!box) return 1;
54042
+ const size = new Vector3();
54043
+ box.getSize(size);
54044
+ return Math.max(1, size.x, size.y, size.z);
54045
+ }
54046
+ function firstOppositeSurfaceDistance(raycaster, rayTargetMeshes, jumpableMeshes, point2, direction2, epsilon2, far, contactTolerance) {
54047
+ const origin = point2.clone().addScaledVector(direction2, epsilon2);
54048
+ raycaster.set(origin, direction2);
54049
+ raycaster.near = epsilon2;
54050
+ raycaster.far = far;
54051
+ const hits = raycaster.intersectObjects(rayTargetMeshes, false);
54052
+ for (const hit of hits) {
54053
+ if (hit.distance <= epsilon2) continue;
54054
+ if (hit.distance <= contactTolerance && jumpableMeshes.has(hit.object)) continue;
54055
+ return hit.distance + epsilon2;
54056
+ }
54057
+ return null;
54058
+ }
54059
+ function triangleThickness(raycaster, rayTargetMeshes, jumpableMeshes, centroid, normal, epsilon2, far, contactTolerance) {
54060
+ const forward = firstOppositeSurfaceDistance(
54061
+ raycaster,
54062
+ rayTargetMeshes,
54063
+ jumpableMeshes,
54064
+ centroid,
54065
+ normal,
54066
+ epsilon2,
54067
+ far,
54068
+ contactTolerance
54069
+ );
54070
+ const backward = firstOppositeSurfaceDistance(
54071
+ raycaster,
54072
+ rayTargetMeshes,
54073
+ jumpableMeshes,
54074
+ centroid,
54075
+ normal.clone().negate(),
54076
+ epsilon2,
54077
+ far,
54078
+ contactTolerance
54079
+ );
54080
+ if (forward == null) return backward;
54081
+ if (backward == null) return forward;
54082
+ return Math.min(forward, backward);
54083
+ }
54084
+ function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}, context = {}) {
54085
+ const options = resolveThicknessInspectionOptions(rawOptions);
54086
+ const geometry = cloneGeometryForFaceColors(sourceGeometry);
54087
+ const position = geometry.getAttribute("position");
54088
+ if (!position || position.count < 3) {
54089
+ return {
54090
+ geometry,
54091
+ samples: [],
54092
+ pointSamples: [],
54093
+ triangleCount: 0,
54094
+ sampledTriangleCount: 0,
54095
+ sampleStride: 1,
54096
+ warnings: ["No triangle geometry."]
54097
+ };
54098
+ }
54099
+ const triangleCount = Math.floor(position.count / 3);
54100
+ const surfaceTriangles = readSurfaceTriangles(position);
54101
+ const surfaceSamples = sampleSurfaceTriangles(surfaceTriangles, options.maxSamplesPerObject);
54102
+ const connectedGeometries = context.connectedGeometries ?? [];
54103
+ const maxDim2 = Math.max(geometryMaxDimension(geometry), ...connectedGeometries.map(geometryMaxDimension));
54104
+ const epsilon2 = Math.max(1e-4, maxDim2 * 1e-6);
54105
+ const far = Math.max(maxDim2 * 4, options.maxThickness * 4, 1);
54106
+ const colors = new Float32Array(position.count * 3);
54107
+ const triangleThicknessValues = new Array(triangleCount).fill(void 0);
54108
+ const samples = [];
54109
+ const pointSamples = [];
54110
+ const warnings = [];
54111
+ const rayMaterial = new MeshBasicMaterial({ side: DoubleSide });
54112
+ const rayTargets = [
54113
+ makeThicknessRaycastTarget(geometry, rayMaterial, false),
54114
+ ...connectedGeometries.map((connectedGeometry) => makeThicknessRaycastTarget(connectedGeometry, rayMaterial, true))
54115
+ ];
54116
+ try {
54117
+ const rayTargetMeshes = rayTargets.map((target) => target.mesh);
54118
+ const jumpableMeshes = new Set(rayTargets.filter((target) => target.jumpable).map((target) => target.mesh));
54119
+ const raycaster = new Raycaster();
54120
+ if (surfaceTriangles.length === 0) {
54121
+ warnings.push("No non-degenerate triangle surface was available for thickness sampling.");
54122
+ } else if (surfaceSamples.length < surfaceTriangles.length) {
54123
+ warnings.push(
54124
+ `Area sampling budget ${surfaceSamples.length} covers ${surfaceTriangles.length} surface triangles; increase --thickness-samples for denser analysis.`
54125
+ );
54126
+ }
54127
+ const sampledTriangleIndexes = /* @__PURE__ */ new Set();
54128
+ for (const sample of surfaceSamples) {
54129
+ sampledTriangleIndexes.add(sample.triangle.index);
54130
+ const thickness = triangleThickness(
54131
+ raycaster,
54132
+ rayTargetMeshes,
54133
+ jumpableMeshes,
54134
+ sample.position,
54135
+ sample.normal,
54136
+ epsilon2,
54137
+ far,
54138
+ options.contactTolerance
54139
+ );
54140
+ samples.push({ thickness, area: sample.area });
54141
+ const previous = triangleThicknessValues[sample.triangle.index];
54142
+ if (previous === void 0 || previous == null || thickness != null && thickness < previous) {
54143
+ triangleThicknessValues[sample.triangle.index] = thickness;
54144
+ }
54145
+ pointSamples.push({
54146
+ position: [sample.position.x, sample.position.y, sample.position.z],
54147
+ normal: [sample.normal.x, sample.normal.y, sample.normal.z],
54148
+ value: thickness,
54149
+ className: thicknessClass(thickness, options),
54150
+ color: thicknessColor(thickness, options),
54151
+ area: sample.area
54152
+ });
54153
+ }
54154
+ for (let tri = 0; tri < triangleCount; tri += 1) {
54155
+ const color = thicknessColor(triangleThicknessValues[tri], options);
54156
+ const offset = tri * 3;
54157
+ for (let vertex2 = 0; vertex2 < 3; vertex2 += 1) {
54158
+ const colorOffset = (offset + vertex2) * 3;
54159
+ colors[colorOffset] = color[0] / 255;
54160
+ colors[colorOffset + 1] = color[1] / 255;
54161
+ colors[colorOffset + 2] = color[2] / 255;
54162
+ }
54163
+ }
54164
+ geometry.setAttribute("color", new BufferAttribute(colors, 3));
54165
+ return {
54166
+ geometry,
54167
+ samples,
54168
+ pointSamples,
54169
+ triangleCount,
54170
+ sampledTriangleCount: sampledTriangleIndexes.size,
54171
+ sampleStride: Math.max(1, Math.ceil(Math.max(1, surfaceTriangles.length) / Math.max(1, sampledTriangleIndexes.size))),
54172
+ warnings
54173
+ };
54174
+ } finally {
54175
+ rayTargets.forEach((target) => target.geometry.dispose());
54176
+ rayMaterial.dispose();
54177
+ }
54178
+ }
54179
+ const DEFAULT_VERTEX_CAP = 2e6;
54180
+ const DEFAULT_TARGET_EDGE_SPACING_FACTOR = 2;
54181
+ const DEFAULT_K = 8;
54182
+ const DEFAULT_GATE_DOT = 0.3;
54183
+ const SCATTER_CELL_SPACING_FACTOR = 1.5;
54184
+ const SCATTER_RADIUS_SPACING_FACTOR = 3;
54185
+ const IDW_DISTANCE_FLOOR = 1e-9;
54186
+ const MAX_SUBDIVISION_PASSES = 24;
54187
+ function subVec(a2, b) {
54188
+ return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
54189
+ }
54190
+ function crossVec(a2, b) {
54191
+ return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
54192
+ }
54193
+ function lenVec(a2) {
54194
+ return Math.hypot(a2[0], a2[1], a2[2]);
54195
+ }
54196
+ function getVert(positions, i) {
54197
+ const o = i * 3;
54198
+ return [positions[o], positions[o + 1], positions[o + 2]];
54199
+ }
54200
+ function triArea(positions, a2, b, c2) {
54201
+ const va = getVert(positions, a2);
54202
+ return 0.5 * lenVec(crossVec(subVec(getVert(positions, b), va), subVec(getVert(positions, c2), va)));
54203
+ }
54204
+ function triNormal(positions, a2, b, c2) {
54205
+ const va = getVert(positions, a2);
54206
+ const n = crossVec(subVec(getVert(positions, b), va), subVec(getVert(positions, c2), va));
54207
+ const l = lenVec(n) || 1;
54208
+ return [n[0] / l, n[1] / l, n[2] / l];
54209
+ }
54210
+ function edgeLen(positions, a2, b) {
54211
+ return lenVec(subVec(getVert(positions, a2), getVert(positions, b)));
54212
+ }
54213
+ function maxEdge(positions, a2, b, c2) {
54214
+ return Math.max(edgeLen(positions, a2, b), edgeLen(positions, b, c2), edgeLen(positions, c2, a2));
54215
+ }
54216
+ function weld(positions) {
54217
+ if (positions.length % 9 !== 0) {
54218
+ throw new Error(
54219
+ `weld: positions length must be a multiple of 9 (9 floats per triangle), got ${positions.length}`
54220
+ );
54221
+ }
54222
+ positions.length / 3;
54223
+ let minX = Infinity;
54224
+ let minY = Infinity;
54225
+ let minZ = Infinity;
54226
+ let maxX = -Infinity;
54227
+ let maxY = -Infinity;
54228
+ let maxZ = -Infinity;
54229
+ for (let i = 0; i < positions.length; i += 3) {
54230
+ const x2 = positions[i];
54231
+ const y2 = positions[i + 1];
54232
+ const z2 = positions[i + 2];
54233
+ if (!Number.isFinite(x2) || !Number.isFinite(y2) || !Number.isFinite(z2)) {
54234
+ throw new Error(`weld: non-finite vertex position at float index ${i}`);
54235
+ }
54236
+ if (x2 < minX) minX = x2;
54237
+ if (y2 < minY) minY = y2;
54238
+ if (z2 < minZ) minZ = z2;
54239
+ if (x2 > maxX) maxX = x2;
54240
+ if (y2 > maxY) maxY = y2;
54241
+ if (z2 > maxZ) maxZ = z2;
54242
+ }
54243
+ const diagonal = Math.hypot(maxX - minX, maxY - minY, maxZ - minZ);
54244
+ const tolerance = Math.max(diagonal * 1e-5, Number.EPSILON);
54245
+ const remap = /* @__PURE__ */ new Map();
54246
+ const out = [];
54247
+ const tris = [];
54248
+ const inv = 1 / tolerance;
54249
+ const quantKey = (i) => {
54250
+ const qx = Math.round(positions[i] * inv);
54251
+ const qy = Math.round(positions[i + 1] * inv);
54252
+ const qz = Math.round(positions[i + 2] * inv);
54253
+ return `${qx},${qy},${qz}`;
54254
+ };
54255
+ for (let tri = 0; tri < positions.length; tri += 9) {
54256
+ const idx = [];
54257
+ for (let corner = 0; corner < 3; corner += 1) {
54258
+ const o = tri + corner * 3;
54259
+ const key2 = quantKey(o);
54260
+ let vi = remap.get(key2);
54261
+ if (vi === void 0) {
54262
+ vi = out.length / 3;
54263
+ out.push(positions[o], positions[o + 1], positions[o + 2]);
54264
+ remap.set(key2, vi);
54265
+ }
54266
+ idx.push(vi);
54267
+ }
54268
+ if (idx[0] !== idx[1] && idx[1] !== idx[2] && idx[2] !== idx[0]) {
54269
+ tris.push(idx[0], idx[1], idx[2]);
54270
+ }
54271
+ }
54272
+ const distinctVertexCount = out.length / 3;
54273
+ const degenerate = distinctVertexCount < 3 || tris.length === 0;
54274
+ return { positions: out, tris, degenerate };
54275
+ }
54276
+ function adaptiveSubdivide(mesh, targetEdge, vertexCap) {
54277
+ requirePositiveFiniteNumber(targetEdge, "adaptiveSubdivide targetEdge");
54278
+ requireIntegerAtLeast(vertexCap, "adaptiveSubdivide vertexCap", 3);
54279
+ const positions = mesh.positions.slice();
54280
+ let tris = mesh.tris.slice();
54281
+ let capped = false;
54282
+ const keyOf = (a2, b) => a2 < b ? `${a2}_${b}` : `${b}_${a2}`;
54283
+ for (let pass = 0; pass < MAX_SUBDIVISION_PASSES; pass += 1) {
54284
+ const next = [];
54285
+ const midCache = /* @__PURE__ */ new Map();
54286
+ let changed = false;
54287
+ const getMid = (a2, b) => {
54288
+ const key2 = keyOf(a2, b);
54289
+ let mi = midCache.get(key2);
54290
+ if (mi === void 0) {
54291
+ mi = positions.length / 3;
54292
+ const ao = a2 * 3;
54293
+ const bo = b * 3;
54294
+ positions.push(
54295
+ (positions[ao] + positions[bo]) / 2,
54296
+ (positions[ao + 1] + positions[bo + 1]) / 2,
54297
+ (positions[ao + 2] + positions[bo + 2]) / 2
54298
+ );
54299
+ midCache.set(key2, mi);
54300
+ }
54301
+ return mi;
54302
+ };
54303
+ for (let t = 0; t < tris.length; t += 3) {
54304
+ const a2 = tris[t];
54305
+ const b = tris[t + 1];
54306
+ const c2 = tris[t + 2];
54307
+ const vertexCount = positions.length / 3;
54308
+ if (maxEdge(positions, a2, b, c2) > targetEdge && vertexCount < vertexCap) {
54309
+ const ab = getMid(a2, b);
54310
+ const bc = getMid(b, c2);
54311
+ const ca = getMid(c2, a2);
54312
+ next.push(a2, ab, ca, ab, b, bc, ca, bc, c2, ab, bc, ca);
54313
+ changed = true;
54314
+ } else {
54315
+ if (positions.length / 3 >= vertexCap) capped = true;
54316
+ next.push(a2, b, c2);
54317
+ }
54318
+ }
54319
+ tris = next;
54320
+ if (!changed) break;
54321
+ }
54322
+ return { positions, tris, capped };
54323
+ }
54324
+ function vertexNormals(mesh) {
54325
+ const vertexCount = mesh.positions.length / 3;
54326
+ const acc = new Float32Array(vertexCount * 3);
54327
+ for (let t = 0; t < mesh.tris.length; t += 3) {
54328
+ const a2 = mesh.tris[t];
54329
+ const b = mesh.tris[t + 1];
54330
+ const c2 = mesh.tris[t + 2];
54331
+ const n = triNormal(mesh.positions, a2, b, c2);
54332
+ const area = triArea(mesh.positions, a2, b, c2);
54333
+ const nx = n[0] * area;
54334
+ const ny = n[1] * area;
54335
+ const nz = n[2] * area;
54336
+ for (const vi of [a2, b, c2]) {
54337
+ acc[vi * 3] += nx;
54338
+ acc[vi * 3 + 1] += ny;
54339
+ acc[vi * 3 + 2] += nz;
54340
+ }
54341
+ }
54342
+ for (let i = 0; i < vertexCount; i += 1) {
54343
+ const o = i * 3;
54344
+ const l = Math.hypot(acc[o], acc[o + 1], acc[o + 2]) || 1;
54345
+ acc[o] /= l;
54346
+ acc[o + 1] /= l;
54347
+ acc[o + 2] /= l;
54348
+ }
54349
+ return acc;
54350
+ }
54351
+ function buildSampleGrid(positions, cell) {
54352
+ const grid = /* @__PURE__ */ new Map();
54353
+ const sampleCount = positions.length / 3;
54354
+ for (let i = 0; i < sampleCount; i += 1) {
54355
+ const o = i * 3;
54356
+ const key2 = `${Math.floor(positions[o] / cell)},${Math.floor(positions[o + 1] / cell)},${Math.floor(positions[o + 2] / cell)}`;
54357
+ let arr = grid.get(key2);
54358
+ if (!arr) {
54359
+ arr = [];
54360
+ grid.set(key2, arr);
54361
+ }
54362
+ arr.push(i);
54363
+ }
54364
+ return { grid, cell };
54365
+ }
54366
+ function gatherSamples(sampleGrid, p2, rings, out) {
54367
+ out.length = 0;
54368
+ const { cell, grid } = sampleGrid;
54369
+ const bx = Math.floor(p2[0] / cell);
54370
+ const by = Math.floor(p2[1] / cell);
54371
+ const bz = Math.floor(p2[2] / cell);
54372
+ for (let dx = -rings; dx <= rings; dx += 1) {
54373
+ for (let dy = -rings; dy <= rings; dy += 1) {
54374
+ for (let dz = -rings; dz <= rings; dz += 1) {
54375
+ const arr = grid.get(`${bx + dx},${by + dy},${bz + dz}`);
54376
+ if (arr) for (const i of arr) out.push(i);
54377
+ }
54378
+ }
54379
+ }
54380
+ }
54381
+ function scatterToVertices(positions, vnormals, samples, spacing, k2, gateDot) {
54382
+ const cell = spacing * SCATTER_CELL_SPACING_FACTOR;
54383
+ const sampleGrid = buildSampleGrid(samples.positions, cell);
54384
+ const radius = spacing * SCATTER_RADIUS_SPACING_FACTOR;
54385
+ const radius2 = radius * radius;
54386
+ const vertexCount = positions.length / 3;
54387
+ const values = new Float32Array(vertexCount);
54388
+ let holeCount = 0;
54389
+ const candidates = [];
54390
+ const scratchP = [0, 0, 0];
54391
+ const bestD2 = new Float64Array(k2);
54392
+ const bestVal = new Float64Array(k2);
54393
+ for (let vi = 0; vi < vertexCount; vi += 1) {
54394
+ const pi = vi * 3;
54395
+ const px = positions[pi];
54396
+ const py = positions[pi + 1];
54397
+ const pz = positions[pi + 2];
54398
+ const nx = vnormals[pi];
54399
+ const ny = vnormals[pi + 1];
54400
+ const nz = vnormals[pi + 2];
54401
+ scratchP[0] = px;
54402
+ scratchP[1] = py;
54403
+ scratchP[2] = pz;
54404
+ gatherSamples(sampleGrid, scratchP, 3, candidates);
54405
+ let count = 0;
54406
+ let worst = 0;
54407
+ let worstD2 = -Infinity;
54408
+ for (let c2 = 0; c2 < candidates.length; c2 += 1) {
54409
+ const si = candidates[c2];
54410
+ const so = si * 3;
54411
+ const dx = samples.positions[so] - px;
54412
+ const dy = samples.positions[so + 1] - py;
54413
+ const dz = samples.positions[so + 2] - pz;
54414
+ const d2 = dx * dx + dy * dy + dz * dz;
54415
+ if (d2 > radius2) continue;
54416
+ if (samples.normals[so] * nx + samples.normals[so + 1] * ny + samples.normals[so + 2] * nz < gateDot) continue;
54417
+ const val = samples.values[si];
54418
+ if (count < k2) {
54419
+ bestD2[count] = d2;
54420
+ bestVal[count] = val;
54421
+ if (d2 > worstD2) {
54422
+ worstD2 = d2;
54423
+ worst = count;
54424
+ }
54425
+ count += 1;
54426
+ } else if (d2 < worstD2) {
54427
+ bestD2[worst] = d2;
54428
+ bestVal[worst] = val;
54429
+ worstD2 = -Infinity;
54430
+ for (let t = 0; t < k2; t += 1) {
54431
+ if (bestD2[t] > worstD2) {
54432
+ worstD2 = bestD2[t];
54433
+ worst = t;
54434
+ }
54435
+ }
54436
+ }
54437
+ }
54438
+ if (count === 0) {
54439
+ holeCount += 1;
54440
+ values[vi] = NaN;
54441
+ continue;
54442
+ }
54443
+ let w2 = 0;
54444
+ let acc = 0;
54445
+ for (let t = 0; t < count; t += 1) {
54446
+ const ww = 1 / Math.max(bestD2[t], IDW_DISTANCE_FLOOR);
54447
+ w2 += ww;
54448
+ acc += ww * bestVal[t];
54449
+ }
54450
+ values[vi] = acc / w2;
54451
+ }
54452
+ return { values, holeCount };
54453
+ }
54454
+ function backfillHoles(values, mesh) {
54455
+ const vertexCount = values.length;
54456
+ const adjacency = Array.from({ length: vertexCount }, () => []);
54457
+ for (let t = 0; t < mesh.tris.length; t += 3) {
54458
+ const a2 = mesh.tris[t];
54459
+ const b = mesh.tris[t + 1];
54460
+ const c2 = mesh.tris[t + 2];
54461
+ adjacency[a2].push(b, c2);
54462
+ adjacency[b].push(a2, c2);
54463
+ adjacency[c2].push(a2, b);
54464
+ }
54465
+ let frontier = [];
54466
+ for (let i = 0; i < vertexCount; i += 1) {
54467
+ if (Number.isFinite(values[i])) frontier.push(i);
54468
+ }
54469
+ if (frontier.length === 0) {
54470
+ values.fill(0);
54471
+ return;
54472
+ }
54473
+ while (frontier.length > 0) {
54474
+ const nextFrontier = [];
54475
+ for (const v of frontier) {
54476
+ const val = values[v];
54477
+ for (const n of adjacency[v]) {
54478
+ if (!Number.isFinite(values[n])) {
54479
+ values[n] = val;
54480
+ nextFrontier.push(n);
54481
+ }
54482
+ }
54483
+ }
54484
+ frontier = nextFrontier;
54485
+ }
54486
+ }
54487
+ function reconstructSurfaceScalarField(trianglePositions, samples, options = {}) {
54488
+ if (!(trianglePositions instanceof Float32Array)) {
54489
+ throw new Error("reconstructSurfaceScalarField: trianglePositions must be a Float32Array");
54490
+ }
54491
+ if (trianglePositions.length === 0 || trianglePositions.length % 9 !== 0) {
54492
+ throw new Error(
54493
+ `reconstructSurfaceScalarField: trianglePositions length must be a positive multiple of 9, got ${trianglePositions.length}`
54494
+ );
54495
+ }
54496
+ if (!(samples.positions instanceof Float32Array) || !(samples.values instanceof Float32Array) || !(samples.normals instanceof Float32Array)) {
54497
+ throw new Error("reconstructSurfaceScalarField: samples positions/values/normals must be Float32Arrays");
54498
+ }
54499
+ const sampleCount = samples.values.length;
54500
+ if (sampleCount === 0) {
54501
+ throw new Error("reconstructSurfaceScalarField: samples must be non-empty");
54502
+ }
54503
+ if (samples.positions.length !== sampleCount * 3) {
54504
+ throw new Error(
54505
+ `reconstructSurfaceScalarField: samples.positions length ${samples.positions.length} must equal values*3 (${sampleCount * 3})`
54506
+ );
54507
+ }
54508
+ if (samples.normals.length !== sampleCount * 3) {
54509
+ throw new Error(
54510
+ `reconstructSurfaceScalarField: samples.normals length ${samples.normals.length} must equal values*3 (${sampleCount * 3})`
54511
+ );
54512
+ }
54513
+ for (let i = 0; i < sampleCount; i += 1) {
54514
+ requireFiniteNumber(samples.values[i], `samples.values[${i}]`);
54515
+ }
54516
+ const vertexCap = options.vertexCap === void 0 ? DEFAULT_VERTEX_CAP : requireIntegerAtLeast(options.vertexCap, "vertexCap", 3);
54517
+ const targetEdgeSpacingFactor = options.targetEdgeSpacingFactor === void 0 ? DEFAULT_TARGET_EDGE_SPACING_FACTOR : requirePositiveFiniteNumber(options.targetEdgeSpacingFactor, "targetEdgeSpacingFactor");
54518
+ const k2 = options.k === void 0 ? DEFAULT_K : requireIntegerAtLeast(options.k, "k", 1);
54519
+ const gateDot = options.gateDot === void 0 ? DEFAULT_GATE_DOT : requireFiniteNumber(options.gateDot, "gateDot");
54520
+ const welded = weld(trianglePositions);
54521
+ let surfaceArea = 0;
54522
+ for (let t = 0; t < welded.tris.length; t += 3) {
54523
+ surfaceArea += triArea(welded.positions, welded.tris[t], welded.tris[t + 1], welded.tris[t + 2]);
54524
+ }
54525
+ const sampleSpacing = surfaceArea > 0 ? Math.sqrt(surfaceArea / sampleCount) : 0;
54526
+ let subdivided;
54527
+ if (welded.degenerate || !(sampleSpacing > 0)) {
54528
+ subdivided = { positions: welded.positions, tris: welded.tris, capped: false };
54529
+ } else {
54530
+ const targetEdge = targetEdgeSpacingFactor * sampleSpacing;
54531
+ subdivided = adaptiveSubdivide(welded, targetEdge, vertexCap);
54532
+ }
54533
+ const normals = vertexNormals(subdivided);
54534
+ const effectiveSpacing = sampleSpacing > 0 ? sampleSpacing : 1;
54535
+ const scatter = scatterToVertices(subdivided.positions, normals, samples, effectiveSpacing, k2, gateDot);
54536
+ let valueMin = Infinity;
54537
+ let valueMax = -Infinity;
54538
+ for (let i = 0; i < scatter.values.length; i += 1) {
54539
+ const v = scatter.values[i];
54540
+ if (!Number.isFinite(v)) continue;
54541
+ if (v < valueMin) valueMin = v;
54542
+ if (v > valueMax) valueMax = v;
54543
+ }
54544
+ if (!Number.isFinite(valueMin) || !Number.isFinite(valueMax)) {
54545
+ valueMin = 0;
54546
+ valueMax = 0;
54547
+ }
54548
+ backfillHoles(scatter.values, subdivided);
54549
+ const vertexCount = subdivided.positions.length / 3;
54550
+ return {
54551
+ positions: Float32Array.from(subdivided.positions),
54552
+ normals,
54553
+ index: Uint32Array.from(subdivided.tris),
54554
+ values: scatter.values,
54555
+ valueMin,
54556
+ valueMax,
54557
+ capped: subdivided.capped,
54558
+ degenerate: welded.degenerate,
54559
+ holeCount: scatter.holeCount,
54560
+ vertexCount
54561
+ };
54562
+ }
53065
54563
  const DEFAULT_COLORMAP = "viridis";
53066
54564
  function cloneSerializedFaceRef(face) {
53067
54565
  return {
@@ -53096,7 +54594,8 @@ class FrozenShapeBackend {
53096
54594
  edgePositions: this._data.geometryEdgePositions,
53097
54595
  triangleFaceIds: this._data.geometryTriangleFaceIds,
53098
54596
  faceIdNames: this._data.geometryFaceIdNames,
53099
- hasSmoothNormals: this._data.hasSmoothNormals ?? false
54597
+ hasSmoothNormals: this._data.hasSmoothNormals ?? false,
54598
+ uvs: this._data.geometryUvs
53100
54599
  };
53101
54600
  }
53102
54601
  getMeshReconstructionData() {
@@ -53264,9 +54763,17 @@ const EDGE_THRESHOLD_DOT = Math.cos(Math.PI / 180);
53264
54763
  const SMOOTH_THRESHOLD_DOT = Math.cos(30 * Math.PI / 180);
53265
54764
  function computeGeometryArrays(mesh, options = {}) {
53266
54765
  const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals, cornerNormals } = mesh;
54766
+ if (numProp !== NUM_PROP_POSITION_ONLY && numProp !== NUM_PROP_WITH_NORMAL && numProp !== NUM_PROP_WITH_UV) {
54767
+ throw new Error(
54768
+ `computeGeometryArrays: illegal vertProperties numProp ${numProp}. Only ${NUM_PROP_POSITION_ONLY} (position), ${NUM_PROP_WITH_NORMAL} (position+normal), and ${NUM_PROP_WITH_UV} (position+normal+uv) are valid; ${numProp} (e.g. a truncated uv channel) is not.`
54769
+ );
54770
+ }
54771
+ const hasStoredNormals = numProp === NUM_PROP_WITH_NORMAL || numProp === NUM_PROP_WITH_UV;
54772
+ const hasStoredUvs = numProp === NUM_PROP_WITH_UV;
53267
54773
  const useCornerNormals = !!cornerNormals && cornerNormals.length === triCount * 9;
53268
54774
  const positions = new Float32Array(triCount * 9);
53269
54775
  const normals = new Float32Array(triCount * 9);
54776
+ const uvs = hasStoredUvs ? new Float32Array(triCount * 6) : void 0;
53270
54777
  const faceNx = new Float32Array(triCount);
53271
54778
  const faceNy = new Float32Array(triCount);
53272
54779
  const faceNz = new Float32Array(triCount);
@@ -53296,6 +54803,15 @@ function computeGeometryArrays(mesh, options = {}) {
53296
54803
  positions[o + 6] = cx;
53297
54804
  positions[o + 7] = cy;
53298
54805
  positions[o + 8] = cz;
54806
+ if (uvs) {
54807
+ const u2 = t * 6;
54808
+ uvs[u2] = vertProperties[i0 * numProp + UV_OFFSET];
54809
+ uvs[u2 + 1] = vertProperties[i0 * numProp + UV_OFFSET + 1];
54810
+ uvs[u2 + 2] = vertProperties[i1 * numProp + UV_OFFSET];
54811
+ uvs[u2 + 3] = vertProperties[i1 * numProp + UV_OFFSET + 1];
54812
+ uvs[u2 + 4] = vertProperties[i2 * numProp + UV_OFFSET];
54813
+ uvs[u2 + 5] = vertProperties[i2 * numProp + UV_OFFSET + 1];
54814
+ }
53299
54815
  if (useCornerNormals) {
53300
54816
  for (let k2 = 0; k2 < 9; k2++) normals[o + k2] = cornerNormals[o + k2];
53301
54817
  } else if (vertNormals) {
@@ -53308,13 +54824,13 @@ function computeGeometryArrays(mesh, options = {}) {
53308
54824
  normals[o + 6] = vertNormals[i2 * 3];
53309
54825
  normals[o + 7] = vertNormals[i2 * 3 + 1];
53310
54826
  normals[o + 8] = vertNormals[i2 * 3 + 2];
53311
- } else if (numProp >= 6) {
54827
+ } else if (hasStoredNormals) {
53312
54828
  const corners = [i0, i1, i2];
53313
54829
  for (let v = 0; v < 3; v++) {
53314
54830
  const base = corners[v] * numProp;
53315
- const nx = vertProperties[base + 3];
53316
- const ny = vertProperties[base + 4];
53317
- const nz = vertProperties[base + 5];
54831
+ const nx = vertProperties[base + NORMAL_OFFSET];
54832
+ const ny = vertProperties[base + NORMAL_OFFSET + 1];
54833
+ const nz = vertProperties[base + NORMAL_OFFSET + 2];
53318
54834
  const oc = o + v * 3;
53319
54835
  if (nx * nx + ny * ny + nz * nz > 1e-12) {
53320
54836
  normals[oc] = nx;
@@ -53341,7 +54857,7 @@ function computeGeometryArrays(mesh, options = {}) {
53341
54857
  faceNy[t] = fny;
53342
54858
  faceNz[t] = fnz;
53343
54859
  }
53344
- if (!useCornerNormals && !vertNormals && numProp < 6 && triCount > 0) {
54860
+ if (!useCornerNormals && !vertNormals && !hasStoredNormals && triCount > 0) {
53345
54861
  computeAutoSmoothNormals(
53346
54862
  triVerts,
53347
54863
  vertProperties,
@@ -53360,7 +54876,8 @@ function computeGeometryArrays(mesh, options = {}) {
53360
54876
  positions,
53361
54877
  normals,
53362
54878
  edgePositions,
53363
- hasSmoothNormals: triCount > 0
54879
+ hasSmoothNormals: triCount > 0,
54880
+ uvs
53364
54881
  };
53365
54882
  }
53366
54883
  function computeAutoSmoothNormals(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, normals, mergeFromVert, mergeToVert) {
@@ -53495,10 +55012,11 @@ function attachKernelFaceMetadata(geometry, triangleFaceIds, faceIdNames) {
53495
55012
  }
53496
55013
  function shapeToGeometry(shape) {
53497
55014
  if (shape instanceof FrozenShape) {
53498
- const { positions, normals, edgePositions, triangleFaceIds, faceIdNames, hasSmoothNormals } = shape.getPrecomputedGeometry();
55015
+ const { positions, normals, edgePositions, triangleFaceIds, faceIdNames, hasSmoothNormals, uvs } = shape.getPrecomputedGeometry();
53499
55016
  const solid = new BufferGeometry();
53500
55017
  solid.setAttribute("position", new BufferAttribute(positions, 3));
53501
55018
  solid.setAttribute("normal", new BufferAttribute(normals, 3));
55019
+ if (uvs) solid.setAttribute("uv", new BufferAttribute(uvs, 2));
53502
55020
  attachKernelFaceMetadata(solid, triangleFaceIds, faceIdNames);
53503
55021
  const edges = new BufferGeometry();
53504
55022
  edges.setAttribute("position", new BufferAttribute(edgePositions, 3));
@@ -53521,7 +55039,7 @@ function shapeToGeometryFallback(shape) {
53521
55039
  } catch {
53522
55040
  mesh = shape.getMesh();
53523
55041
  }
53524
- const { positions, normals, edgePositions, hasSmoothNormals } = computeGeometryArrays({
55042
+ const { positions, normals, edgePositions, hasSmoothNormals, uvs } = computeGeometryArrays({
53525
55043
  numProp: mesh.numProp,
53526
55044
  numTri: mesh.numTri,
53527
55045
  triVerts: mesh.triVerts,
@@ -53534,6 +55052,7 @@ function shapeToGeometryFallback(shape) {
53534
55052
  const solid = new BufferGeometry();
53535
55053
  solid.setAttribute("position", new BufferAttribute(positions, 3));
53536
55054
  solid.setAttribute("normal", new BufferAttribute(normals, 3));
55055
+ if (uvs) solid.setAttribute("uv", new BufferAttribute(uvs, 2));
53537
55056
  attachKernelFaceMetadata(solid, mesh.faceID, mesh.faceIdNames);
53538
55057
  const edges = new BufferGeometry();
53539
55058
  edges.setAttribute("position", new BufferAttribute(edgePositions, 3));
@@ -53727,8 +55246,25 @@ function meshVertexColorBuffersFor(input, colorForEntryId) {
53727
55246
  }
53728
55247
  return out;
53729
55248
  }
55249
+ const COLLISION_SOURCE_COLOR = [180, 200, 220];
55250
+ const COLLISION_HIGHLIGHT_COLOR = [255, 68, 16];
53730
55251
  const FLOATING_HIGHLIGHT_COLOR = [255, 68, 16];
53731
55252
  const FLOATING_HIDDEN_COLOR = [0, 0, 0];
55253
+ const FLOATING_CONTEXT_COLOR = [38, 49, 58];
55254
+ const COLLISION_PALETTE = [
55255
+ COLLISION_HIGHLIGHT_COLOR,
55256
+ [0, 204, 255],
55257
+ [255, 214, 0],
55258
+ [66, 220, 120],
55259
+ [255, 76, 196],
55260
+ [142, 106, 255],
55261
+ [255, 145, 48],
55262
+ [86, 232, 202],
55263
+ [210, 245, 60],
55264
+ [255, 120, 120],
55265
+ [80, 150, 255],
55266
+ [190, 110, 60]
55267
+ ];
53732
55268
  const MASK_PALETTE = [
53733
55269
  [230, 25, 75],
53734
55270
  [60, 180, 75],
@@ -53764,6 +55300,9 @@ function maskRgbForIndex(index2) {
53764
55300
  function maskColorForIndex(index2) {
53765
55301
  return rgbToHex(maskRgbForIndex(index2));
53766
55302
  }
55303
+ function collisionColorForIndex(index2) {
55304
+ return rgbToHex(COLLISION_PALETTE[(index2 - 1) % COLLISION_PALETTE.length]);
55305
+ }
53767
55306
  function lerp(a2, b, t) {
53768
55307
  return a2 + (b - a2) * Math.max(0, Math.min(1, t));
53769
55308
  }
@@ -53779,6 +55318,7 @@ function distanceColorForRootDistance(distance, maxDistance) {
53779
55318
  }
53780
55319
  const workerScope = self;
53781
55320
  const SCALAR_SURFACE_VERTEX_CAP = 2e6;
55321
+ const SCALAR_SURFACE_TARGET_EDGE_SPACING_FACTOR = 8;
53782
55322
  class EmptyIntersectionShape {
53783
55323
  intersect() {
53784
55324
  return {
@@ -53852,6 +55392,69 @@ function objectColorsFor(objects, input, colorByEntryId) {
53852
55392
  })
53853
55393
  );
53854
55394
  }
55395
+ function countLabel(count, singular, plural) {
55396
+ return `${count} ${count === 1 ? singular : plural}`;
55397
+ }
55398
+ function componentBodyDetail(component) {
55399
+ if (component.objectCount === component.bodyCount) return countLabel(component.bodyCount, "body", "bodies");
55400
+ return `${countLabel(component.bodyCount, "body", "bodies")}, ${countLabel(component.objectCount, "object", "objects")}`;
55401
+ }
55402
+ function formatDistance(value) {
55403
+ if (!Number.isFinite(value)) return "unreachable";
55404
+ if (Math.abs(value) <= 1e-9) return "root";
55405
+ const fixed = Math.abs(value) < 10 ? value.toFixed(2) : value.toFixed(1);
55406
+ return `${fixed.replace(/\.?0+$/, "")} from root`;
55407
+ }
55408
+ function connectivityLegendSwatches(report) {
55409
+ return report.components.map(
55410
+ (component) => ({
55411
+ label: `Component ${component.index}`,
55412
+ detail: componentBodyDetail(component),
55413
+ color: maskColorForIndex(component.index - 1)
55414
+ })
55415
+ );
55416
+ }
55417
+ function distanceLegendSwatches(report) {
55418
+ return report.components.map(
55419
+ (component) => ({
55420
+ label: component.isRoot ? `Root ${component.index}` : `Component ${component.index}`,
55421
+ detail: `${componentBodyDetail(component)}, ${formatDistance(component.rootDistance)}`,
55422
+ color: distanceColorForRootDistance(component.rootDistance, report.maxRootDistance)
55423
+ })
55424
+ );
55425
+ }
55426
+ function floatingLegendSwatches(report) {
55427
+ const bodyCount = report.components.reduce((total, component) => total + component.bodyCount, 0);
55428
+ const groundedBodyCount = Math.max(0, bodyCount - report.floatingBodyCount);
55429
+ return [
55430
+ {
55431
+ label: "Floating",
55432
+ detail: countLabel(report.floatingBodyCount, "body", "bodies"),
55433
+ color: rgbToHex(FLOATING_HIGHLIGHT_COLOR)
55434
+ },
55435
+ {
55436
+ label: "Context",
55437
+ detail: countLabel(groundedBodyCount, "grounded body", "grounded bodies"),
55438
+ color: rgbToHex(FLOATING_CONTEXT_COLOR)
55439
+ }
55440
+ ];
55441
+ }
55442
+ function collisionLegendSwatches(collisions) {
55443
+ return [
55444
+ ...collisions.map(
55445
+ (collision) => ({
55446
+ label: `Overlap ${collision.index}`,
55447
+ detail: `${collision.sourceName} vs ${collision.targetName}`,
55448
+ color: collisionColorForIndex(collision.index)
55449
+ })
55450
+ ),
55451
+ {
55452
+ label: "Ghost body",
55453
+ detail: "source geometry",
55454
+ color: rgbToHex(COLLISION_SOURCE_COLOR)
55455
+ }
55456
+ ];
55457
+ }
53855
55458
  function scalarSamplesFromPointSamples(pointSamples) {
53856
55459
  const finite = pointSamples.filter((sample) => sample.value != null && Number.isFinite(sample.value));
53857
55460
  const positions = new Float32Array(finite.length * 3);
@@ -53887,7 +55490,10 @@ function analyzeScalarChannel(request) {
53887
55490
  });
53888
55491
  const samples = scalarSamplesFromPointSamples(analysis.pointSamples);
53889
55492
  if (samples.values.length > 0) {
53890
- const field2 = reconstructSurfaceScalarField(object.positions, samples, { vertexCap: SCALAR_SURFACE_VERTEX_CAP });
55493
+ const field2 = reconstructSurfaceScalarField(object.positions, samples, {
55494
+ vertexCap: SCALAR_SURFACE_VERTEX_CAP,
55495
+ targetEdgeSpacingFactor: SCALAR_SURFACE_TARGET_EDGE_SPACING_FACTOR
55496
+ });
53891
55497
  const bvhGeometry = new BufferGeometry();
53892
55498
  bvhGeometry.setAttribute("position", new BufferAttribute(field2.positions, 3));
53893
55499
  bvhGeometry.setIndex(new BufferAttribute(field2.index, 1));
@@ -53937,6 +55543,7 @@ function analyzeScalarChannel(request) {
53937
55543
  meshColorObjects: [],
53938
55544
  scalarSurfaceObjects,
53939
55545
  collisionGeometryObjects: [],
55546
+ legendSwatches: [],
53940
55547
  warnings
53941
55548
  };
53942
55549
  }
@@ -53968,6 +55575,7 @@ function analyzeConnectivityChannel(request) {
53968
55575
  meshColorObjects: meshColorObjectsFor(bodyInput, colorByEntryId2),
53969
55576
  scalarSurfaceObjects: [],
53970
55577
  collisionGeometryObjects: [],
55578
+ legendSwatches: distanceLegendSwatches(report2),
53971
55579
  warnings: report2.warnings
53972
55580
  };
53973
55581
  }
@@ -53983,6 +55591,7 @@ function analyzeConnectivityChannel(request) {
53983
55591
  meshColorObjects: meshColorObjectsFor(bodyInput, colorByEntryId2),
53984
55592
  scalarSurfaceObjects: [],
53985
55593
  collisionGeometryObjects: [],
55594
+ legendSwatches: floatingLegendSwatches(report2),
53986
55595
  warnings: report2.warnings
53987
55596
  };
53988
55597
  }
@@ -53995,6 +55604,7 @@ function analyzeConnectivityChannel(request) {
53995
55604
  meshColorObjects: meshColorObjectsFor(bodyInput, colorByEntryId),
53996
55605
  scalarSurfaceObjects: [],
53997
55606
  collisionGeometryObjects: [],
55607
+ legendSwatches: connectivityLegendSwatches(report),
53998
55608
  warnings: report.warnings
53999
55609
  };
54000
55610
  }
@@ -54101,6 +55711,7 @@ function analyzeCollisionChannel(request) {
54101
55711
  meshColorObjects: [],
54102
55712
  scalarSurfaceObjects: [],
54103
55713
  collisionGeometryObjects,
55714
+ legendSwatches: collisionLegendSwatches(collisionGeometryObjects),
54104
55715
  warnings: [...request.warnings ?? [], ...report.warnings]
54105
55716
  };
54106
55717
  }