forgecad 0.10.2 → 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.
- package/README.md +7 -6
- package/dist/assets/{AdminPage-CHY6ZN-p.js → AdminPage-B3L3W1Uo.js} +1 -1
- package/dist/assets/{BenchmarkPage-BcRT5iGN.js → BenchmarkPage-DXKVXMrJ.js} +2 -2
- package/dist/assets/{BlogPage-BssBbnb-.js → BlogPage-B7BWxOCg.js} +1 -1
- package/dist/assets/{DocsPage-DsvdiRNK.js → DocsPage-BPGGwht1.js} +28 -48
- package/dist/assets/{EditorApp-Bfd3jbtC.js → EditorApp-BWUGCdD5.js} +183 -21
- package/dist/assets/{EditorApp-BpjZgzk0.css → EditorApp-C5f24ZN9.css} +8 -0
- package/dist/assets/{EmbedViewer-D5t8WamV.js → EmbedViewer-DygByZS2.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-DbN7o-Be.js → LandingPageProofDriven-BoVE7JGY.js} +54 -36
- package/dist/assets/{LegalPage-DNGrrY0p.js → LegalPage-Din8wv8d.js} +2 -2
- package/dist/assets/{PricingPage-Nczr3pRz.js → PricingPage-C2PMzmDc.js} +2 -2
- package/dist/assets/{SettingsPage-DZlyu4d4.js → SettingsPage-BlJDCRe8.js} +1 -1
- package/dist/assets/{app-C9ct2hRD.js → app-BsRYSfxY.js} +2264 -6259
- package/dist/assets/{backendInit-ymjonyQp.js → backendInit-6C0DLgH0.js} +8290 -2136
- package/dist/assets/cli/{render-B_0lQwKU.js → render-XXol_ET7.js} +822 -105
- package/dist/assets/{constructionHistoryWorker-CZ42Dksy.js → constructionHistoryWorker-cTHWRJEi.js} +699 -284
- package/dist/assets/{evalWorker-C2pm8LHP.js → evalWorker-BssDYW9u.js} +2559 -1330
- package/dist/assets/{forgecad_geometry-BlMtqluF.js → forgecad_geometry-CZ_IfuvA.js} +1 -9
- package/dist/assets/{forgecad_geometry_bg-BllP_WiL.wasm → forgecad_geometry_bg-C3rQHfwg.wasm} +0 -0
- package/dist/assets/{inspectWorker-D5T5VbfK.js → inspectWorker-ymhBV4Ll.js} +6254 -671
- package/dist/assets/{jointPose-4r8ed8_5.js → jointPose-B0blBj9A.js} +1 -1
- package/dist/assets/{landing-proof-driven-ORyigZ6p.css → landing-proof-driven-Cpf-MIbI.css} +73 -13
- package/dist/assets/{manifold-5PP1eGLN.js → manifold-B_7QXpGB.js} +1 -1
- package/dist/assets/{manifold-DjBkyIc8.js → manifold-CNShmpEJ.js} +1 -1
- package/dist/assets/{manifold-C4r6B-XY.js → manifold-CYlIm-M6.js} +2 -2
- package/dist/assets/{reportWorker-CwenM7wB.js → reportWorker-Cb5eyM7D.js} +2485 -1275
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +17 -17
- package/dist/docs-raw/CLI.md +9 -7
- package/dist/docs-raw/README.md +1 -1
- package/dist/docs-raw/component-model.md +2 -2
- package/dist/docs-raw/generated/assembly.md +1 -1
- package/dist/docs-raw/generated/concepts.md +10 -4
- package/dist/docs-raw/generated/core.md +96 -1
- package/dist/docs-raw/generated/curves.md +8 -1
- package/dist/docs-raw/generated/output.md +0 -64
- package/dist/docs-raw/generated/runtime-names.md +6 -6
- package/dist/docs-raw/generated/viewport.md +3 -12
- package/dist/docs-raw/guides/inspection-bundles.md +1 -1
- package/dist/docs-raw/simulation-workflow.md +58 -0
- package/{dist-skill/website/skills/forgecad-make-a-model.md → dist/docs-raw/skills/forgecad-build-model.md} +18 -8
- package/dist/docs-raw/skills/forgecad-design-spec.md +145 -0
- package/dist/docs-raw/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
- package/{dist-skill/website/skills/forgecad-visual-spec.md → dist/docs-raw/skills/forgecad-image-prompt.md} +7 -7
- package/dist/docs-raw/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
- package/{dist-skill/website/skills/forgecad-project.md → dist/docs-raw/skills/forgecad-project-sync.md} +5 -5
- package/{dist-skill/website/skills/forgecad-3d-reconstruction.md → dist/docs-raw/skills/forgecad-reconstruct-cad-file.md} +7 -7
- package/dist/docs-raw/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
- package/dist/docs-raw/skills/forgecad-verify-mujoco.md +78 -0
- package/dist/docs-raw/skills/forgecad.md +24 -24
- package/dist/docs-raw/skills/index.md +9 -13
- package/dist/index.html +9 -9
- package/dist/llms.txt +7 -7
- package/dist/sitemap.xml +16 -16
- package/dist-cli/{check-compiler-SP7FAL7R.js → check-compiler-4RPB6SB5.js} +1 -1
- package/dist-cli/{check-query-propagation-BRLSHP22.js → check-query-propagation-KN3DFQTX.js} +1 -1
- package/dist-cli/{chunk-RQQ42YCP.js → chunk-UHBRMYA6.js} +30770 -29253
- package/dist-cli/forgecad.js +3277 -237
- package/dist-cli/{forgecad_geometry-7TVSNVUB.js → forgecad_geometry-2IMYCUWW.js} +0 -8
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +111 -73
- package/dist-skill/SKILL.md +1 -1
- package/dist-skill/docs/CLI.md +9 -7
- package/dist-skill/docs/generated/assembly.md +1 -1
- package/dist-skill/docs/generated/core.md +96 -1
- package/dist-skill/docs/generated/curves.md +8 -1
- package/dist-skill/docs/generated/output.md +0 -64
- package/dist-skill/docs/generated/runtime-names.md +6 -6
- package/dist-skill/docs/generated/viewport.md +3 -12
- package/dist-skill/docs/guides/inspection-bundles.md +1 -1
- package/dist-skill/library/README.md +9 -13
- package/dist-skill/library/{forgecad-make-a-model → forgecad-build-model}/SKILL.md +16 -6
- package/dist-skill/library/forgecad-design-spec/SKILL.md +132 -0
- package/dist-skill/library/{forgecad-prepare-prompt → forgecad-design-spec}/references/master-prompt.md +1 -1
- package/dist-skill/library/{forgecad-model-grader → forgecad-grade-model}/SKILL.md +6 -4
- package/dist-skill/library/forgecad-grade-model/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-visual-spec → forgecad-image-prompt}/SKILL.md +5 -5
- package/dist-skill/library/forgecad-image-prompt/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/SKILL.md +4 -4
- package/dist-skill/library/{forgecad-project → forgecad-project-sync}/SKILL.md +3 -3
- package/dist-skill/library/{forgecad-3d-reconstruction → forgecad-reconstruct-cad-file}/SKILL.md +5 -5
- package/dist-skill/library/forgecad-reconstruct-cad-file/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/SKILL.md +10 -10
- package/dist-skill/library/forgecad-reconstruct-from-images/agents/openai.yaml +4 -0
- package/dist-skill/library/forgecad-verify-mujoco/SKILL.md +66 -0
- package/dist-skill/library/forgecad-verify-mujoco/scripts/mujoco_verify.py +385 -0
- package/{dist/docs-raw/skills/forgecad-make-a-model.md → dist-skill/website/skills/forgecad-build-model.md} +18 -8
- package/dist-skill/website/skills/forgecad-design-spec.md +145 -0
- package/dist-skill/website/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
- package/{dist/docs-raw/skills/forgecad-visual-spec.md → dist-skill/website/skills/forgecad-image-prompt.md} +7 -7
- package/dist-skill/website/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
- package/{dist/docs-raw/skills/forgecad-project.md → dist-skill/website/skills/forgecad-project-sync.md} +5 -5
- package/{dist/docs-raw/skills/forgecad-3d-reconstruction.md → dist-skill/website/skills/forgecad-reconstruct-cad-file.md} +7 -7
- package/dist-skill/website/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
- package/dist-skill/website/skills/forgecad-verify-mujoco.md +78 -0
- package/dist-skill/website/skills/forgecad.md +24 -24
- package/dist-skill/website/skills/index.md +9 -13
- package/examples/analysis/clearance-fit.forge.js +31 -0
- package/examples/analysis/lever-arm-actuator.forge.js +43 -0
- package/examples/analysis/tipping-tripod.forge.js +35 -0
- package/examples/api/texture-projection.forge.js +75 -0
- package/examples/assets/uv-grid.png +0 -0
- package/examples/products/sportscar.forge.js +77 -0
- package/package.json +1 -3
- package/dist/docs-raw/skills/forgecad-blockout-model.md +0 -49
- package/dist/docs-raw/skills/forgecad-component-model.md +0 -53
- package/dist/docs-raw/skills/forgecad-high-level-spec.md +0 -101
- package/dist/docs-raw/skills/forgecad-lld.md +0 -41
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +0 -63
- package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +0 -60
- package/dist-skill/library/forgecad-3d-reconstruction/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +0 -42
- package/dist-skill/library/forgecad-component-model/SKILL.md +0 -46
- package/dist-skill/library/forgecad-high-level-spec/SKILL.md +0 -94
- package/dist-skill/library/forgecad-image-replicator/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-lld/SKILL.md +0 -34
- package/dist-skill/library/forgecad-model-grader/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +0 -50
- package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +0 -48
- package/dist-skill/library/forgecad-reconstruction-benchmark/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-visual-spec/agents/openai.yaml +0 -4
- package/dist-skill/website/skills/forgecad-blockout-model.md +0 -49
- package/dist-skill/website/skills/forgecad-component-model.md +0 -53
- package/dist-skill/website/skills/forgecad-high-level-spec.md +0 -101
- package/dist-skill/website/skills/forgecad-lld.md +0 -41
- package/dist-skill/website/skills/forgecad-prepare-prompt.md +0 -63
- package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +0 -60
- /package/dist/assets/{landing-proof-driven-DiGqdtWa.js → landing-proof-driven-BxZZh5r5.js} +0 -0
- /package/dist-skill/library/{forgecad-prepare-prompt → forgecad-design-spec}/references/default-profiles.md +0 -0
- /package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/summarize_manifest.py +0 -0
- /package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/scripts/compare_images.py +0 -0
package/dist/assets/{constructionHistoryWorker-CZ42Dksy.js → constructionHistoryWorker-cTHWRJEi.js}
RENAMED
|
@@ -722,12 +722,12 @@ class Vector2 {
|
|
|
722
722
|
* @param {number} angle - The angle to rotate, in radians.
|
|
723
723
|
* @return {Vector2} A reference to this vector.
|
|
724
724
|
*/
|
|
725
|
-
rotateAround(
|
|
725
|
+
rotateAround(center2, angle) {
|
|
726
726
|
const c2 = Math.cos(angle), s = Math.sin(angle);
|
|
727
|
-
const x2 = this.x -
|
|
728
|
-
const y2 = this.y -
|
|
729
|
-
this.x = x2 * c2 - y2 * s +
|
|
730
|
-
this.y = x2 * s + y2 * c2 +
|
|
727
|
+
const x2 = this.x - center2.x;
|
|
728
|
+
const y2 = this.y - center2.y;
|
|
729
|
+
this.x = x2 * c2 - y2 * s + center2.x;
|
|
730
|
+
this.y = x2 * s + y2 * c2 + center2.y;
|
|
731
731
|
return this;
|
|
732
732
|
}
|
|
733
733
|
/**
|
|
@@ -3256,10 +3256,10 @@ class Box3 {
|
|
|
3256
3256
|
* @param {Vector3} size - The x, y and z dimensions of the box.
|
|
3257
3257
|
* @return {Box3} A reference to this bounding box.
|
|
3258
3258
|
*/
|
|
3259
|
-
setFromCenterAndSize(
|
|
3259
|
+
setFromCenterAndSize(center2, size) {
|
|
3260
3260
|
const halfSize = _vector$b.copy(size).multiplyScalar(0.5);
|
|
3261
|
-
this.min.copy(
|
|
3262
|
-
this.max.copy(
|
|
3261
|
+
this.min.copy(center2).sub(halfSize);
|
|
3262
|
+
this.max.copy(center2).add(halfSize);
|
|
3263
3263
|
return this;
|
|
3264
3264
|
}
|
|
3265
3265
|
/**
|
|
@@ -3709,9 +3709,9 @@ class Sphere {
|
|
|
3709
3709
|
* @param {Vector3} [center=(0,0,0)] - The center of the sphere
|
|
3710
3710
|
* @param {number} [radius=-1] - The radius of the sphere.
|
|
3711
3711
|
*/
|
|
3712
|
-
constructor(
|
|
3712
|
+
constructor(center2 = new Vector3(), radius = -1) {
|
|
3713
3713
|
this.isSphere = true;
|
|
3714
|
-
this.center =
|
|
3714
|
+
this.center = center2;
|
|
3715
3715
|
this.radius = radius;
|
|
3716
3716
|
}
|
|
3717
3717
|
/**
|
|
@@ -3721,8 +3721,8 @@ class Sphere {
|
|
|
3721
3721
|
* @param {number} radius - The radius.
|
|
3722
3722
|
* @return {Sphere} A reference to this sphere.
|
|
3723
3723
|
*/
|
|
3724
|
-
set(
|
|
3725
|
-
this.center.copy(
|
|
3724
|
+
set(center2, radius) {
|
|
3725
|
+
this.center.copy(center2);
|
|
3726
3726
|
this.radius = radius;
|
|
3727
3727
|
return this;
|
|
3728
3728
|
}
|
|
@@ -3737,15 +3737,15 @@ class Sphere {
|
|
|
3737
3737
|
* @return {Sphere} A reference to this sphere.
|
|
3738
3738
|
*/
|
|
3739
3739
|
setFromPoints(points, optionalCenter) {
|
|
3740
|
-
const
|
|
3740
|
+
const center2 = this.center;
|
|
3741
3741
|
if (optionalCenter !== void 0) {
|
|
3742
|
-
|
|
3742
|
+
center2.copy(optionalCenter);
|
|
3743
3743
|
} else {
|
|
3744
|
-
_box$3.setFromPoints(points).getCenter(
|
|
3744
|
+
_box$3.setFromPoints(points).getCenter(center2);
|
|
3745
3745
|
}
|
|
3746
3746
|
let maxRadiusSq = 0;
|
|
3747
3747
|
for (let i = 0, il = points.length; i < il; i++) {
|
|
3748
|
-
maxRadiusSq = Math.max(maxRadiusSq,
|
|
3748
|
+
maxRadiusSq = Math.max(maxRadiusSq, center2.distanceToSquared(points[i]));
|
|
3749
3749
|
}
|
|
3750
3750
|
this.radius = Math.sqrt(maxRadiusSq);
|
|
3751
3751
|
return this;
|
|
@@ -7133,7 +7133,7 @@ class BufferGeometry extends EventDispatcher {
|
|
|
7133
7133
|
return;
|
|
7134
7134
|
}
|
|
7135
7135
|
if (position) {
|
|
7136
|
-
const
|
|
7136
|
+
const center2 = this.boundingSphere.center;
|
|
7137
7137
|
_box$2.setFromBufferAttribute(position);
|
|
7138
7138
|
if (morphAttributesPosition) {
|
|
7139
7139
|
for (let i = 0, il = morphAttributesPosition.length; i < il; i++) {
|
|
@@ -7150,11 +7150,11 @@ class BufferGeometry extends EventDispatcher {
|
|
|
7150
7150
|
}
|
|
7151
7151
|
}
|
|
7152
7152
|
}
|
|
7153
|
-
_box$2.getCenter(
|
|
7153
|
+
_box$2.getCenter(center2);
|
|
7154
7154
|
let maxRadiusSq = 0;
|
|
7155
7155
|
for (let i = 0, il = position.count; i < il; i++) {
|
|
7156
7156
|
_vector$8.fromBufferAttribute(position, i);
|
|
7157
|
-
maxRadiusSq = Math.max(maxRadiusSq,
|
|
7157
|
+
maxRadiusSq = Math.max(maxRadiusSq, center2.distanceToSquared(_vector$8));
|
|
7158
7158
|
}
|
|
7159
7159
|
if (morphAttributesPosition) {
|
|
7160
7160
|
for (let i = 0, il = morphAttributesPosition.length; i < il; i++) {
|
|
@@ -7166,7 +7166,7 @@ class BufferGeometry extends EventDispatcher {
|
|
|
7166
7166
|
_offset.fromBufferAttribute(position, j);
|
|
7167
7167
|
_vector$8.add(_offset);
|
|
7168
7168
|
}
|
|
7169
|
-
maxRadiusSq = Math.max(maxRadiusSq,
|
|
7169
|
+
maxRadiusSq = Math.max(maxRadiusSq, center2.distanceToSquared(_vector$8));
|
|
7170
7170
|
}
|
|
7171
7171
|
}
|
|
7172
7172
|
}
|
|
@@ -8043,6 +8043,12 @@ if (typeof window !== "undefined") {
|
|
|
8043
8043
|
}
|
|
8044
8044
|
}
|
|
8045
8045
|
const SHAPE_BACKEND_MARKER = Symbol.for("forgecad.shapeBackend");
|
|
8046
|
+
const POSITION_OFFSET = 0;
|
|
8047
|
+
const NORMAL_OFFSET = 3;
|
|
8048
|
+
const UV_OFFSET = 6;
|
|
8049
|
+
const NUM_PROP_POSITION_ONLY = 3;
|
|
8050
|
+
const NUM_PROP_WITH_NORMAL = 6;
|
|
8051
|
+
const NUM_PROP_WITH_UV = 8;
|
|
8046
8052
|
function isShapeBackend(value) {
|
|
8047
8053
|
return Boolean(value && typeof value === "object" && value[SHAPE_BACKEND_MARKER] === true);
|
|
8048
8054
|
}
|
|
@@ -9187,11 +9193,11 @@ function applyFilletSelectionToManifold(base, selection, radius, segments, wasm)
|
|
|
9187
9193
|
const height = edgeLength(selection);
|
|
9188
9194
|
if (!(height > 1e-6)) return base;
|
|
9189
9195
|
const pad = Math.max(MIN_EDGE_PAD, radius);
|
|
9190
|
-
const
|
|
9196
|
+
const span2 = height + pad * 2;
|
|
9191
9197
|
const frame = edgeFrameMatrix(selection, -pad);
|
|
9192
9198
|
const cs = buildFilletCrossSection(selection, radius, segments, wasm) ?? legacyFilletCrossSection(selection, radius, segments, wasm);
|
|
9193
|
-
const corner = cs.wedge.extrude(
|
|
9194
|
-
const cyl = cs.cylinder.extrude(
|
|
9199
|
+
const corner = cs.wedge.extrude(span2, 0, 0, void 0, false).transform(frame);
|
|
9200
|
+
const cyl = cs.cylinder.extrude(span2, 0, 0, void 0, false).transform(frame);
|
|
9195
9201
|
const crescent = wasm.Manifold.difference([corner, cyl]);
|
|
9196
9202
|
return wasm.Manifold.difference([base, crescent]);
|
|
9197
9203
|
}
|
|
@@ -9199,11 +9205,11 @@ function applyConcaveFilletSelectionToManifold(base, selection, radius, segments
|
|
|
9199
9205
|
const height = edgeLength(selection);
|
|
9200
9206
|
if (!(height > 1e-6)) return base;
|
|
9201
9207
|
const pad = Math.max(MIN_EDGE_PAD, radius);
|
|
9202
|
-
const
|
|
9208
|
+
const span2 = height + pad * 2;
|
|
9203
9209
|
const frame = edgeFrameMatrix(selection, -pad);
|
|
9204
9210
|
const cs = buildFilletCrossSection(selection, radius, segments, wasm) ?? legacyFilletCrossSection(selection, radius, segments, wasm);
|
|
9205
|
-
const corner = cs.wedge.extrude(
|
|
9206
|
-
const cyl = cs.cylinder.extrude(
|
|
9211
|
+
const corner = cs.wedge.extrude(span2, 0, 0, void 0, false).transform(frame);
|
|
9212
|
+
const cyl = cs.cylinder.extrude(span2, 0, 0, void 0, false).transform(frame);
|
|
9207
9213
|
const crescent = wasm.Manifold.difference([corner, cyl]);
|
|
9208
9214
|
return wasm.Manifold.union([base, crescent]);
|
|
9209
9215
|
}
|
|
@@ -9211,20 +9217,20 @@ function applyChamferSelectionToManifold(base, selection, size, wasm) {
|
|
|
9211
9217
|
const height = edgeLength(selection);
|
|
9212
9218
|
if (!(height > 1e-6)) return base;
|
|
9213
9219
|
const pad = Math.max(MIN_EDGE_PAD, size);
|
|
9214
|
-
const
|
|
9220
|
+
const span2 = height + pad * 2;
|
|
9215
9221
|
const frame = edgeFrameMatrix(selection, -pad);
|
|
9216
9222
|
const triangle = buildChamferCrossSection(selection, size, wasm) ?? legacyChamferCrossSection(selection, size, wasm);
|
|
9217
|
-
const chamfer = triangle.extrude(
|
|
9223
|
+
const chamfer = triangle.extrude(span2, 0, 0, void 0, false).transform(frame);
|
|
9218
9224
|
return wasm.Manifold.difference([base, chamfer]);
|
|
9219
9225
|
}
|
|
9220
9226
|
function applyConcaveChamferSelectionToManifold(base, selection, size, wasm) {
|
|
9221
9227
|
const height = edgeLength(selection);
|
|
9222
9228
|
if (!(height > 1e-6)) return base;
|
|
9223
9229
|
const pad = Math.max(MIN_EDGE_PAD, size);
|
|
9224
|
-
const
|
|
9230
|
+
const span2 = height + pad * 2;
|
|
9225
9231
|
const frame = edgeFrameMatrix(selection, -pad);
|
|
9226
9232
|
const triangle = buildChamferCrossSection(selection, size, wasm) ?? legacyChamferCrossSection(selection, size, wasm);
|
|
9227
|
-
const chamfer = triangle.extrude(
|
|
9233
|
+
const chamfer = triangle.extrude(span2, 0, 0, void 0, false).transform(frame);
|
|
9228
9234
|
return wasm.Manifold.union([base, chamfer]);
|
|
9229
9235
|
}
|
|
9230
9236
|
function cloneEdgeFinishQuadrant(quadrant) {
|
|
@@ -9378,6 +9384,7 @@ function cloneTopologyRewritePropagation(propagation) {
|
|
|
9378
9384
|
}
|
|
9379
9385
|
function faceQueryRefsEqual(a2, b) {
|
|
9380
9386
|
if (a2 == null || b == null) return a2 == null && b == null;
|
|
9387
|
+
if (a2 === b) return true;
|
|
9381
9388
|
if (a2.kind !== b.kind) return false;
|
|
9382
9389
|
switch (a2.kind) {
|
|
9383
9390
|
case "canonical-face":
|
|
@@ -9394,6 +9401,7 @@ function faceQueryRefsEqual(a2, b) {
|
|
|
9394
9401
|
}
|
|
9395
9402
|
function edgeQueryRefsEqual(a2, b) {
|
|
9396
9403
|
if (a2 == null || b == null) return a2 == null && b == null;
|
|
9404
|
+
if (a2 === b) return true;
|
|
9397
9405
|
if (a2.kind !== b.kind) return false;
|
|
9398
9406
|
switch (a2.kind) {
|
|
9399
9407
|
case "tracked-edge":
|
|
@@ -9760,8 +9768,8 @@ function deriveSheetMetalModel(model) {
|
|
|
9760
9768
|
const trimStart = flanges.has(adjacent.start) ? model.cornerRelief.size : 0;
|
|
9761
9769
|
const trimEnd = flanges.has(adjacent.end) ? model.cornerRelief.size : 0;
|
|
9762
9770
|
const fullLength = edge === "top" || edge === "bottom" ? model.panel.width : model.panel.height;
|
|
9763
|
-
const
|
|
9764
|
-
if (!(
|
|
9771
|
+
const span2 = fullLength - trimStart - trimEnd;
|
|
9772
|
+
if (!(span2 > EPS$5)) {
|
|
9765
9773
|
throw new Error(
|
|
9766
9774
|
`${edgeDisplayName(edge)} loses all usable span after applying the defended rectangular corner relief size ${model.cornerRelief.size}.`
|
|
9767
9775
|
);
|
|
@@ -9773,7 +9781,7 @@ function deriveSheetMetalModel(model) {
|
|
|
9773
9781
|
bendAllowance,
|
|
9774
9782
|
trimStart,
|
|
9775
9783
|
trimEnd,
|
|
9776
|
-
span,
|
|
9784
|
+
span: span2,
|
|
9777
9785
|
centerAlongEdge: (trimStart - trimEnd) / 2
|
|
9778
9786
|
});
|
|
9779
9787
|
}
|
|
@@ -9952,10 +9960,10 @@ function lowerSheetMetalBasePlan(model, output) {
|
|
|
9952
9960
|
const pieces = lowerSheetMetalBasePiecePlans(model, output).map((piece) => piece.plan);
|
|
9953
9961
|
return pieces.length === 1 ? pieces[0] : buildBooleanShapeCompilePlan("union", pieces);
|
|
9954
9962
|
}
|
|
9955
|
-
function descriptor(name,
|
|
9963
|
+
function descriptor(name, center2, normal2, planar, uAxis, vAxis, semantic = "face", memberNames = [name], coplanar = planar) {
|
|
9956
9964
|
return {
|
|
9957
9965
|
name,
|
|
9958
|
-
center: cloneVec3$3(
|
|
9966
|
+
center: cloneVec3$3(center2),
|
|
9959
9967
|
normal: cloneVec3$3(normal2),
|
|
9960
9968
|
planar,
|
|
9961
9969
|
uAxis: cloneFaceAxis(uAxis),
|
|
@@ -11408,14 +11416,14 @@ function findSpan(n, degree, u2, knots) {
|
|
|
11408
11416
|
}
|
|
11409
11417
|
return mid;
|
|
11410
11418
|
}
|
|
11411
|
-
function basisFuns(
|
|
11419
|
+
function basisFuns(span2, u2, degree, knots) {
|
|
11412
11420
|
const N = new Array(degree + 1);
|
|
11413
11421
|
const left = new Array(degree + 1);
|
|
11414
11422
|
const right = new Array(degree + 1);
|
|
11415
11423
|
N[0] = 1;
|
|
11416
11424
|
for (let j = 1; j <= degree; j++) {
|
|
11417
|
-
left[j] = u2 - knots[
|
|
11418
|
-
right[j] = knots[
|
|
11425
|
+
left[j] = u2 - knots[span2 + 1 - j];
|
|
11426
|
+
right[j] = knots[span2 + j] - u2;
|
|
11419
11427
|
let saved = 0;
|
|
11420
11428
|
for (let r = 0; r < j; r++) {
|
|
11421
11429
|
const denom = right[r + 1] + left[j - r];
|
|
@@ -11427,13 +11435,72 @@ function basisFuns(span, u2, degree, knots) {
|
|
|
11427
11435
|
}
|
|
11428
11436
|
return N;
|
|
11429
11437
|
}
|
|
11438
|
+
function basisFunsDeriv(span2, u2, degree, knots, nDeriv) {
|
|
11439
|
+
const ndu = Array.from({ length: degree + 1 }, () => new Array(degree + 1).fill(0));
|
|
11440
|
+
const a2 = Array.from({ length: 2 }, () => new Array(degree + 1).fill(0));
|
|
11441
|
+
const left = new Array(degree + 1);
|
|
11442
|
+
const right = new Array(degree + 1);
|
|
11443
|
+
ndu[0][0] = 1;
|
|
11444
|
+
for (let j = 1; j <= degree; j++) {
|
|
11445
|
+
left[j] = u2 - knots[span2 + 1 - j];
|
|
11446
|
+
right[j] = knots[span2 + j] - u2;
|
|
11447
|
+
let saved = 0;
|
|
11448
|
+
for (let r2 = 0; r2 < j; r2++) {
|
|
11449
|
+
ndu[j][r2] = right[r2 + 1] + left[j - r2];
|
|
11450
|
+
const temp = ndu[j][r2] === 0 ? 0 : ndu[r2][j - 1] / ndu[j][r2];
|
|
11451
|
+
ndu[r2][j] = saved + right[r2 + 1] * temp;
|
|
11452
|
+
saved = left[j - r2] * temp;
|
|
11453
|
+
}
|
|
11454
|
+
ndu[j][j] = saved;
|
|
11455
|
+
}
|
|
11456
|
+
const ders = Array.from({ length: nDeriv + 1 }, () => new Array(degree + 1).fill(0));
|
|
11457
|
+
for (let j = 0; j <= degree; j++) {
|
|
11458
|
+
ders[0][j] = ndu[j][degree];
|
|
11459
|
+
}
|
|
11460
|
+
for (let r2 = 0; r2 <= degree; r2++) {
|
|
11461
|
+
let s1 = 0;
|
|
11462
|
+
let s2 = 1;
|
|
11463
|
+
a2[0][0] = 1;
|
|
11464
|
+
for (let k2 = 1; k2 <= nDeriv; k2++) {
|
|
11465
|
+
let d2 = 0;
|
|
11466
|
+
const rk = r2 - k2;
|
|
11467
|
+
const pk = degree - k2;
|
|
11468
|
+
if (r2 >= k2) {
|
|
11469
|
+
a2[s2][0] = ndu[pk + 1][rk] === 0 ? 0 : a2[s1][0] / ndu[pk + 1][rk];
|
|
11470
|
+
d2 = a2[s2][0] * ndu[rk][pk];
|
|
11471
|
+
}
|
|
11472
|
+
const j1 = rk >= -1 ? 1 : -rk;
|
|
11473
|
+
const j2 = r2 - 1 <= pk ? k2 - 1 : degree - r2;
|
|
11474
|
+
for (let j = j1; j <= j2; j++) {
|
|
11475
|
+
a2[s2][j] = ndu[pk + 1][rk + j] === 0 ? 0 : (a2[s1][j] - a2[s1][j - 1]) / ndu[pk + 1][rk + j];
|
|
11476
|
+
d2 += a2[s2][j] * ndu[rk + j][pk];
|
|
11477
|
+
}
|
|
11478
|
+
if (r2 <= pk) {
|
|
11479
|
+
a2[s2][k2] = ndu[pk + 1][r2] === 0 ? 0 : -a2[s1][k2 - 1] / ndu[pk + 1][r2];
|
|
11480
|
+
d2 += a2[s2][k2] * ndu[r2][pk];
|
|
11481
|
+
}
|
|
11482
|
+
ders[k2][r2] = d2;
|
|
11483
|
+
const tmp = s1;
|
|
11484
|
+
s1 = s2;
|
|
11485
|
+
s2 = tmp;
|
|
11486
|
+
}
|
|
11487
|
+
}
|
|
11488
|
+
let r = degree;
|
|
11489
|
+
for (let k2 = 1; k2 <= nDeriv; k2++) {
|
|
11490
|
+
for (let j = 0; j <= degree; j++) {
|
|
11491
|
+
ders[k2][j] *= r;
|
|
11492
|
+
}
|
|
11493
|
+
r *= degree - k2;
|
|
11494
|
+
}
|
|
11495
|
+
return ders;
|
|
11496
|
+
}
|
|
11430
11497
|
function deBoor3D(controlPoints, weights, knots, degree, u2) {
|
|
11431
11498
|
const n = controlPoints.length;
|
|
11432
|
-
const
|
|
11433
|
-
const N = basisFuns(
|
|
11499
|
+
const span2 = findSpan(n, degree, u2, knots);
|
|
11500
|
+
const N = basisFuns(span2, u2, degree, knots);
|
|
11434
11501
|
let wx = 0, wy = 0, wz = 0, wSum = 0;
|
|
11435
11502
|
for (let j = 0; j <= degree; j++) {
|
|
11436
|
-
const idx =
|
|
11503
|
+
const idx = span2 - degree + j;
|
|
11437
11504
|
const w2 = weights[idx] * N[j];
|
|
11438
11505
|
wx += w2 * controlPoints[idx][0];
|
|
11439
11506
|
wy += w2 * controlPoints[idx][1];
|
|
@@ -11445,11 +11512,11 @@ function deBoor3D(controlPoints, weights, knots, degree, u2) {
|
|
|
11445
11512
|
}
|
|
11446
11513
|
function deBoor2D(controlPoints, weights, knots, degree, u2) {
|
|
11447
11514
|
const n = controlPoints.length;
|
|
11448
|
-
const
|
|
11449
|
-
const N = basisFuns(
|
|
11515
|
+
const span2 = findSpan(n, degree, u2, knots);
|
|
11516
|
+
const N = basisFuns(span2, u2, degree, knots);
|
|
11450
11517
|
let wx = 0, wy = 0, wSum = 0;
|
|
11451
11518
|
for (let j = 0; j <= degree; j++) {
|
|
11452
|
-
const idx =
|
|
11519
|
+
const idx = span2 - degree + j;
|
|
11453
11520
|
const w2 = weights[idx] * N[j];
|
|
11454
11521
|
wx += w2 * controlPoints[idx][0];
|
|
11455
11522
|
wy += w2 * controlPoints[idx][1];
|
|
@@ -15413,6 +15480,23 @@ function analyzeNodeUV(node, toLocal) {
|
|
|
15413
15480
|
function clampUnit(v) {
|
|
15414
15481
|
return v < -1 ? -1 : v > 1 ? 1 : v;
|
|
15415
15482
|
}
|
|
15483
|
+
function sphereUVLocal(lx, ly, lz, radius) {
|
|
15484
|
+
const u2 = atan2(ly, lx) * radius;
|
|
15485
|
+
const len = sqrt$3(lx * lx + ly * ly + lz * lz);
|
|
15486
|
+
const v = acos(clampUnit(lz / (len || 1))) * radius;
|
|
15487
|
+
return [u2, v];
|
|
15488
|
+
}
|
|
15489
|
+
function cylinderUVLocal(lx, ly, lz, radius) {
|
|
15490
|
+
const u2 = atan2(ly, lx) * radius;
|
|
15491
|
+
const v = lz;
|
|
15492
|
+
return [u2, v];
|
|
15493
|
+
}
|
|
15494
|
+
function torusUVLocal(lx, ly, lz, majorRadius, minorRadius) {
|
|
15495
|
+
const u2 = atan2(ly, lx) * majorRadius;
|
|
15496
|
+
const xyDist = sqrt$3(lx * lx + ly * ly) - majorRadius;
|
|
15497
|
+
const v = atan2(lz, xyDist) * minorRadius;
|
|
15498
|
+
return [u2, v];
|
|
15499
|
+
}
|
|
15416
15500
|
function compileUVFunction(analysis) {
|
|
15417
15501
|
if (analysis.mode === "triplanar") return null;
|
|
15418
15502
|
const toLocal = analysis.toLocal;
|
|
@@ -15421,19 +15505,14 @@ function compileUVFunction(analysis) {
|
|
|
15421
15505
|
const R = analysis.radius;
|
|
15422
15506
|
return (p2) => {
|
|
15423
15507
|
const lp = toLocal(p2);
|
|
15424
|
-
|
|
15425
|
-
const len = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1] + lp[2] * lp[2]);
|
|
15426
|
-
const v = acos(clampUnit(lp[2] / (len || 1))) * R;
|
|
15427
|
-
return [u2, v];
|
|
15508
|
+
return sphereUVLocal(lp[0], lp[1], lp[2], R);
|
|
15428
15509
|
};
|
|
15429
15510
|
}
|
|
15430
15511
|
case "cylinder": {
|
|
15431
15512
|
const r = analysis.radius;
|
|
15432
15513
|
return (p2) => {
|
|
15433
15514
|
const lp = toLocal(p2);
|
|
15434
|
-
|
|
15435
|
-
const v = lp[2];
|
|
15436
|
-
return [u2, v];
|
|
15515
|
+
return cylinderUVLocal(lp[0], lp[1], lp[2], r);
|
|
15437
15516
|
};
|
|
15438
15517
|
}
|
|
15439
15518
|
case "torus": {
|
|
@@ -15441,10 +15520,7 @@ function compileUVFunction(analysis) {
|
|
|
15441
15520
|
const r = analysis.radius;
|
|
15442
15521
|
return (p2) => {
|
|
15443
15522
|
const lp = toLocal(p2);
|
|
15444
|
-
|
|
15445
|
-
const xyDist = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1]) - R;
|
|
15446
|
-
const v = atan2(lp[2], xyDist) * r;
|
|
15447
|
-
return [u2, v];
|
|
15523
|
+
return torusUVLocal(lp[0], lp[1], lp[2], R, r);
|
|
15448
15524
|
};
|
|
15449
15525
|
}
|
|
15450
15526
|
}
|
|
@@ -15755,8 +15831,8 @@ function sdProfileRevolve$1(profileFn, degrees, x2, y2, z2) {
|
|
|
15755
15831
|
const profileDistance = profileFn(radial, z2);
|
|
15756
15832
|
if (abs(degrees) >= 360) return profileDistance;
|
|
15757
15833
|
const sweep = abs(degrees) * DEG$1;
|
|
15758
|
-
const
|
|
15759
|
-
let angle = Math.atan2(y2, x2) -
|
|
15834
|
+
const center2 = degrees * DEG$1 * 0.5;
|
|
15835
|
+
let angle = Math.atan2(y2, x2) - center2;
|
|
15760
15836
|
while (angle <= -PI$1) angle += PI$1 * 2;
|
|
15761
15837
|
while (angle > PI$1) angle -= PI$1 * 2;
|
|
15762
15838
|
const half = sweep * 0.5;
|
|
@@ -15774,9 +15850,9 @@ function smax$1(a2, b, k2) {
|
|
|
15774
15850
|
function repeatCoord$1(v, spacing, count) {
|
|
15775
15851
|
if (spacing <= 0) return v;
|
|
15776
15852
|
if (count > 0) {
|
|
15777
|
-
const
|
|
15778
|
-
const index2 = clamp$3(Math.round(v / spacing +
|
|
15779
|
-
return v - (index2 -
|
|
15853
|
+
const center2 = (count - 1) * 0.5;
|
|
15854
|
+
const index2 = clamp$3(Math.round(v / spacing + center2), 0, count - 1);
|
|
15855
|
+
return v - (index2 - center2) * spacing;
|
|
15780
15856
|
}
|
|
15781
15857
|
return v - spacing * Math.round(v / spacing);
|
|
15782
15858
|
}
|
|
@@ -16702,8 +16778,8 @@ function sdProfileRevolve(b, node, x2, y2, z2) {
|
|
|
16702
16778
|
const profileDistance = profilePolygonsSdf(b, node.polygons, radial, z2);
|
|
16703
16779
|
if (Math.abs(node.degrees) >= 360) return profileDistance;
|
|
16704
16780
|
const sweep = Math.abs(node.degrees) * DEG;
|
|
16705
|
-
const
|
|
16706
|
-
const angle = b.sub(b.atan2(y2, x2), b.constant(
|
|
16781
|
+
const center2 = node.degrees * DEG * 0.5;
|
|
16782
|
+
const angle = b.sub(b.atan2(y2, x2), b.constant(center2));
|
|
16707
16783
|
const wrapped = b.sub(angle, b.mul(b.constant(TAU), b.round(b.div(angle, b.constant(TAU)))));
|
|
16708
16784
|
const absAngle = b.abs(wrapped);
|
|
16709
16785
|
const half = sweep * 0.5;
|
|
@@ -16770,9 +16846,9 @@ function select01(b, selector, ifOne, ifZero) {
|
|
|
16770
16846
|
function repeatCoord(b, v, spacing, count) {
|
|
16771
16847
|
if (spacing <= 0) return v;
|
|
16772
16848
|
if (count > 0) {
|
|
16773
|
-
const
|
|
16774
|
-
const index2 = clampSlot(b, b.round(b.add(b.div(v, b.constant(spacing)), b.constant(
|
|
16775
|
-
return b.sub(v, b.mul(b.sub(index2, b.constant(
|
|
16849
|
+
const center2 = (count - 1) * 0.5;
|
|
16850
|
+
const index2 = clampSlot(b, b.round(b.add(b.div(v, b.constant(spacing)), b.constant(center2))), 0, count - 1);
|
|
16851
|
+
return b.sub(v, b.mul(b.sub(index2, b.constant(center2)), b.constant(spacing)));
|
|
16776
16852
|
}
|
|
16777
16853
|
return b.sub(v, b.mul(b.constant(spacing), b.round(b.div(v, b.constant(spacing)))));
|
|
16778
16854
|
}
|
|
@@ -16863,7 +16939,7 @@ async function initTruckGeometryWasm() {
|
|
|
16863
16939
|
if (_initPromise$1) return _initPromise$1;
|
|
16864
16940
|
_initPromise$1 = (async () => {
|
|
16865
16941
|
try {
|
|
16866
|
-
const geometryModule = await import("./forgecad_geometry-
|
|
16942
|
+
const geometryModule = await import("./forgecad_geometry-CZ_IfuvA.js");
|
|
16867
16943
|
const isNode = isNodeRuntime();
|
|
16868
16944
|
if (isNode) {
|
|
16869
16945
|
const { readFileSync, existsSync } = await import("./__vite-browser-external-Dhvy_jtL.js");
|
|
@@ -17328,23 +17404,62 @@ class NurbsSurface {
|
|
|
17328
17404
|
return [wx / wSum, wy / wSum, wz / wSum];
|
|
17329
17405
|
}
|
|
17330
17406
|
/**
|
|
17331
|
-
* Evaluate the surface normal at (u, v)
|
|
17407
|
+
* Evaluate the surface unit normal at (u, v) from analytic first derivatives.
|
|
17408
|
+
*
|
|
17409
|
+
* Uses Algorithm A2.3 basis-function derivatives with the rational quotient
|
|
17410
|
+
* rule, so the normal is exact (no finite-difference epsilon, no error near
|
|
17411
|
+
* the boundary). Constant chain-rule factors from the parameter remap scale Su
|
|
17412
|
+
* and Sv positively and cancel under normalization, so they are omitted.
|
|
17332
17413
|
*/
|
|
17333
17414
|
normalAt(u2, v) {
|
|
17334
|
-
const
|
|
17335
|
-
const
|
|
17336
|
-
const
|
|
17337
|
-
const
|
|
17338
|
-
const pv = this.pointAt(u2, v1), pmv = this.pointAt(u2, v0);
|
|
17339
|
-
const du = [pu[0] - pmu[0], pu[1] - pmu[1], pu[2] - pmu[2]];
|
|
17340
|
-
const dv = [pv[0] - pmv[0], pv[1] - pmv[1], pv[2] - pmv[2]];
|
|
17341
|
-
const nx = du[1] * dv[2] - du[2] * dv[1];
|
|
17342
|
-
const ny = du[2] * dv[0] - du[0] * dv[2];
|
|
17343
|
-
const nz = du[0] * dv[1] - du[1] * dv[0];
|
|
17415
|
+
const { Su, Sv } = this.derivativesAt(u2, v);
|
|
17416
|
+
const nx = Su[1] * Sv[2] - Su[2] * Sv[1];
|
|
17417
|
+
const ny = Su[2] * Sv[0] - Su[0] * Sv[2];
|
|
17418
|
+
const nz = Su[0] * Sv[1] - Su[1] * Sv[0];
|
|
17344
17419
|
const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
17345
17420
|
if (len < 1e-12) return [0, 0, 1];
|
|
17346
17421
|
return [nx / len, ny / len, nz / len];
|
|
17347
17422
|
}
|
|
17423
|
+
/** Analytic first partial derivatives S_u, S_v (rational quotient rule). */
|
|
17424
|
+
derivativesAt(u2, v) {
|
|
17425
|
+
const uu = this.remapU(Math.max(0, Math.min(1, u2)));
|
|
17426
|
+
const vv = this.remapV(Math.max(0, Math.min(1, v)));
|
|
17427
|
+
const spanU = findSpan(this.nU, this.degreeU, uu, this.knotsU);
|
|
17428
|
+
const spanV = findSpan(this.nV, this.degreeV, vv, this.knotsV);
|
|
17429
|
+
const dU = basisFunsDeriv(spanU, uu, this.degreeU, this.knotsU, 1);
|
|
17430
|
+
const dV = basisFunsDeriv(spanV, vv, this.degreeV, this.knotsV, 1);
|
|
17431
|
+
const A = [0, 0, 0];
|
|
17432
|
+
const Au = [0, 0, 0];
|
|
17433
|
+
const Av = [0, 0, 0];
|
|
17434
|
+
let w2 = 0;
|
|
17435
|
+
let wu = 0;
|
|
17436
|
+
let wv = 0;
|
|
17437
|
+
for (let i = 0; i <= this.degreeU; i++) {
|
|
17438
|
+
const rowIdx = spanU - this.degreeU + i;
|
|
17439
|
+
for (let j = 0; j <= this.degreeV; j++) {
|
|
17440
|
+
const colIdx = spanV - this.degreeV + j;
|
|
17441
|
+
const weight = this.weightsGrid[rowIdx][colIdx];
|
|
17442
|
+
const pt = this.controlGrid[rowIdx][colIdx];
|
|
17443
|
+
const n00 = dU[0][i] * dV[0][j] * weight;
|
|
17444
|
+
const n10 = dU[1][i] * dV[0][j] * weight;
|
|
17445
|
+
const n01 = dU[0][i] * dV[1][j] * weight;
|
|
17446
|
+
w2 += n00;
|
|
17447
|
+
wu += n10;
|
|
17448
|
+
wv += n01;
|
|
17449
|
+
for (let c2 = 0; c2 < 3; c2++) {
|
|
17450
|
+
A[c2] += n00 * pt[c2];
|
|
17451
|
+
Au[c2] += n10 * pt[c2];
|
|
17452
|
+
Av[c2] += n01 * pt[c2];
|
|
17453
|
+
}
|
|
17454
|
+
}
|
|
17455
|
+
}
|
|
17456
|
+
if (w2 === 0) return { S: [0, 0, 0], Su: [0, 0, 0], Sv: [0, 0, 0] };
|
|
17457
|
+
const invW = 1 / w2;
|
|
17458
|
+
const S = [A[0] * invW, A[1] * invW, A[2] * invW];
|
|
17459
|
+
const Su = [(Au[0] - wu * S[0]) * invW, (Au[1] - wu * S[1]) * invW, (Au[2] - wu * S[2]) * invW];
|
|
17460
|
+
const Sv = [(Av[0] - wv * S[0]) * invW, (Av[1] - wv * S[1]) * invW, (Av[2] - wv * S[2]) * invW];
|
|
17461
|
+
return { S, Su, Sv };
|
|
17462
|
+
}
|
|
17348
17463
|
/**
|
|
17349
17464
|
* Tessellate the surface into a triangle mesh.
|
|
17350
17465
|
* Returns positions, normals, and triangle indices.
|
|
@@ -17789,49 +17904,55 @@ function signedArea2D(loop) {
|
|
|
17789
17904
|
}
|
|
17790
17905
|
return area2 * 0.5;
|
|
17791
17906
|
}
|
|
17792
|
-
function
|
|
17907
|
+
function buildSdfLoopEdges(pts) {
|
|
17908
|
+
const n = pts.length;
|
|
17909
|
+
const ax = new Float64Array(n);
|
|
17910
|
+
const ay = new Float64Array(n);
|
|
17911
|
+
const ex = new Float64Array(n);
|
|
17912
|
+
const ey = new Float64Array(n);
|
|
17913
|
+
const invLen2 = new Float64Array(n);
|
|
17914
|
+
for (let i = 0; i < n; i += 1) {
|
|
17915
|
+
const a2 = pts[i];
|
|
17916
|
+
const b = pts[(i + 1) % n];
|
|
17917
|
+
const vx = b[0] - a2[0];
|
|
17918
|
+
const vy = b[1] - a2[1];
|
|
17919
|
+
ax[i] = a2[0];
|
|
17920
|
+
ay[i] = a2[1];
|
|
17921
|
+
ex[i] = vx;
|
|
17922
|
+
ey[i] = vy;
|
|
17923
|
+
const len2 = vx * vx + vy * vy;
|
|
17924
|
+
invLen2[i] = len2 < 1e-12 ? 0 : 1 / len2;
|
|
17925
|
+
}
|
|
17926
|
+
return { n, area: signedArea2D(pts), ax, ay, ex, ey, invLen2 };
|
|
17927
|
+
}
|
|
17928
|
+
function loopSignedDistanceScalar(px, py, e) {
|
|
17929
|
+
let minDist2 = Infinity;
|
|
17793
17930
|
let inside = false;
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
const
|
|
17797
|
-
const
|
|
17798
|
-
const
|
|
17799
|
-
|
|
17800
|
-
|
|
17801
|
-
|
|
17802
|
-
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
|
|
17806
|
-
|
|
17807
|
-
const
|
|
17808
|
-
|
|
17809
|
-
const t = den < 1e-12 ? 0 : clamp$1((apx * abx + apy * aby) / den, 0, 1);
|
|
17810
|
-
const qx = a2[0] + abx * t;
|
|
17811
|
-
const qy = a2[1] + aby * t;
|
|
17812
|
-
const dx = point2[0] - qx;
|
|
17813
|
-
const dy = point2[1] - qy;
|
|
17814
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
17815
|
-
}
|
|
17816
|
-
function loopSignedDistance(point2, loop) {
|
|
17817
|
-
let minDist = Infinity;
|
|
17818
|
-
for (let index2 = 0; index2 < loop.length; index2 += 1) {
|
|
17819
|
-
const a2 = loop[index2];
|
|
17820
|
-
const b = loop[(index2 + 1) % loop.length];
|
|
17821
|
-
minDist = Math.min(minDist, pointSegDist2D(point2, a2, b));
|
|
17822
|
-
}
|
|
17823
|
-
return pointInLoop$2(point2, loop) ? minDist : -minDist;
|
|
17931
|
+
for (let i = 0; i < e.n; i += 1) {
|
|
17932
|
+
const ax = e.ax[i];
|
|
17933
|
+
const ay = e.ay[i];
|
|
17934
|
+
const vx = e.ex[i];
|
|
17935
|
+
const vy = e.ey[i];
|
|
17936
|
+
const t = clamp$1(((px - ax) * vx + (py - ay) * vy) * e.invLen2[i], 0, 1);
|
|
17937
|
+
const ddx = px - (ax + vx * t);
|
|
17938
|
+
const ddy = py - (ay + vy * t);
|
|
17939
|
+
const d22 = ddx * ddx + ddy * ddy;
|
|
17940
|
+
if (d22 < minDist2) minDist2 = d22;
|
|
17941
|
+
const by = ay + vy;
|
|
17942
|
+
if (ay > py !== by > py && px < vx * (py - ay) / (vy + 1e-20) + ax) inside = !inside;
|
|
17943
|
+
}
|
|
17944
|
+
const d2 = Math.sqrt(minDist2);
|
|
17945
|
+
return inside ? d2 : -d2;
|
|
17824
17946
|
}
|
|
17825
17947
|
function compilePolygonsSdf(polygons) {
|
|
17826
|
-
const loops = polygons.filter((loop) => Array.isArray(loop) && loop.length >= 3).map((loop) => (
|
|
17948
|
+
const loops = polygons.filter((loop) => Array.isArray(loop) && loop.length >= 3).map((loop) => buildSdfLoopEdges(loop.map(([x2, y2]) => [x2, y2])));
|
|
17827
17949
|
if (loops.length === 0) {
|
|
17828
17950
|
return () => -1;
|
|
17829
17951
|
}
|
|
17830
17952
|
return (x2, y2) => {
|
|
17831
|
-
const point2 = [x2, y2];
|
|
17832
17953
|
let field2 = -Infinity;
|
|
17833
17954
|
for (const loop of loops) {
|
|
17834
|
-
const loopField =
|
|
17955
|
+
const loopField = loopSignedDistanceScalar(x2, y2, loop);
|
|
17835
17956
|
field2 = loop.area >= 0 ? Math.max(field2, loopField) : Math.min(field2, -loopField);
|
|
17836
17957
|
}
|
|
17837
17958
|
return field2;
|
|
@@ -17989,24 +18110,40 @@ function interpolateSweepFrame(segment, alpha, origin) {
|
|
|
17989
18110
|
function findNearestSweepPoint(point2, segments) {
|
|
17990
18111
|
let bestIndex = 0;
|
|
17991
18112
|
let bestAlpha = 0;
|
|
17992
|
-
let
|
|
18113
|
+
let bestAlong = 0;
|
|
17993
18114
|
let bestDist2 = Infinity;
|
|
18115
|
+
const px = point2[0];
|
|
18116
|
+
const py = point2[1];
|
|
18117
|
+
const pz = point2[2];
|
|
17994
18118
|
for (let index2 = 0; index2 < segments.length; index2 += 1) {
|
|
17995
18119
|
const segment = segments[index2];
|
|
17996
|
-
const
|
|
17997
|
-
const
|
|
17998
|
-
const
|
|
17999
|
-
const
|
|
18000
|
-
const
|
|
18001
|
-
const
|
|
18120
|
+
const ax = segment.a[0];
|
|
18121
|
+
const ay = segment.a[1];
|
|
18122
|
+
const az = segment.a[2];
|
|
18123
|
+
const tx = segment.t[0];
|
|
18124
|
+
const ty = segment.t[1];
|
|
18125
|
+
const tz = segment.t[2];
|
|
18126
|
+
const along = clamp$1((px - ax) * tx + (py - ay) * ty + (pz - az) * tz, 0, segment.len);
|
|
18127
|
+
const qx = ax + tx * along;
|
|
18128
|
+
const qy = ay + ty * along;
|
|
18129
|
+
const qz = az + tz * along;
|
|
18130
|
+
const dx = px - qx;
|
|
18131
|
+
const dy = py - qy;
|
|
18132
|
+
const dz = pz - qz;
|
|
18133
|
+
const dist2 = dx * dx + dy * dy + dz * dz;
|
|
18002
18134
|
if (dist2 < bestDist2) {
|
|
18003
18135
|
bestIndex = index2;
|
|
18004
|
-
|
|
18005
|
-
|
|
18136
|
+
bestAlong = along;
|
|
18137
|
+
bestAlpha = segment.len > 1e-9 ? along / segment.len : 0;
|
|
18006
18138
|
bestDist2 = dist2;
|
|
18007
18139
|
}
|
|
18008
18140
|
}
|
|
18009
18141
|
const bestSegment = segments[bestIndex];
|
|
18142
|
+
const bestPoint = [
|
|
18143
|
+
bestSegment.a[0] + bestSegment.t[0] * bestAlong,
|
|
18144
|
+
bestSegment.a[1] + bestSegment.t[1] * bestAlong,
|
|
18145
|
+
bestSegment.a[2] + bestSegment.t[2] * bestAlong
|
|
18146
|
+
];
|
|
18010
18147
|
return {
|
|
18011
18148
|
segmentIndex: bestIndex,
|
|
18012
18149
|
segment: bestSegment,
|
|
@@ -18995,8 +19132,8 @@ function stitchSingleLoopLoft(loops, heights, wasm, options) {
|
|
|
18995
19132
|
return null;
|
|
18996
19133
|
}
|
|
18997
19134
|
}
|
|
18998
|
-
function surfaceNormal(chord,
|
|
18999
|
-
const n = cross3$6(chord,
|
|
19135
|
+
function surfaceNormal(chord, span2) {
|
|
19136
|
+
const n = cross3$6(chord, span2);
|
|
19000
19137
|
const len = Math.hypot(n[0], n[1], n[2]);
|
|
19001
19138
|
if (len < 1e-12) {
|
|
19002
19139
|
const radial = Math.hypot(chord[0], chord[1]);
|
|
@@ -19656,6 +19793,30 @@ function fromSlicesSingleSliceHalfExtentForManifold(plan) {
|
|
|
19656
19793
|
}
|
|
19657
19794
|
return Math.max(1, radius + maxOffsetMagnitude + plan.boundsPadding + plan.edgeLength * 3);
|
|
19658
19795
|
}
|
|
19796
|
+
const BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG = 60;
|
|
19797
|
+
const BOOLEAN_OPERAND_UV_EXTRA_PROP_MIN = 4;
|
|
19798
|
+
function rejectParametricUvBooleanOperands(shapes) {
|
|
19799
|
+
for (const shape of shapes) {
|
|
19800
|
+
if (shape.numProp() >= BOOLEAN_OPERAND_UV_EXTRA_PROP_MIN) {
|
|
19801
|
+
throw new Error(
|
|
19802
|
+
"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."
|
|
19803
|
+
);
|
|
19804
|
+
}
|
|
19805
|
+
}
|
|
19806
|
+
}
|
|
19807
|
+
function promoteBooleanOperandNormals(shapes) {
|
|
19808
|
+
let maxExtra = 0;
|
|
19809
|
+
for (const shape of shapes) maxExtra = Math.max(maxExtra, shape.numProp());
|
|
19810
|
+
if (maxExtra < 3) return { operands: shapes, created: [] };
|
|
19811
|
+
const created = [];
|
|
19812
|
+
const operands = shapes.map((shape) => {
|
|
19813
|
+
if (shape.numProp() >= 3) return shape;
|
|
19814
|
+
const promoted = shape.calculateNormals(0, BOOLEAN_OPERAND_NORMAL_SHARP_ANGLE_DEG);
|
|
19815
|
+
created.push(promoted);
|
|
19816
|
+
return promoted;
|
|
19817
|
+
});
|
|
19818
|
+
return { operands, created };
|
|
19819
|
+
}
|
|
19659
19820
|
function lowerShapeBooleanCompilePlan(plan, wasm) {
|
|
19660
19821
|
const shapes = plan.shapes.map((shape) => lowerShapeCompilePlanToManifold(shape, wasm));
|
|
19661
19822
|
if (shapes.length === 0) {
|
|
@@ -19664,16 +19825,24 @@ function lowerShapeBooleanCompilePlan(plan, wasm) {
|
|
|
19664
19825
|
if (shapes.length === 1) {
|
|
19665
19826
|
return shapes[0];
|
|
19666
19827
|
}
|
|
19828
|
+
try {
|
|
19829
|
+
rejectParametricUvBooleanOperands(shapes);
|
|
19830
|
+
} catch (error) {
|
|
19831
|
+
disposeWasmObjects(shapes);
|
|
19832
|
+
throw error;
|
|
19833
|
+
}
|
|
19834
|
+
const { operands, created } = promoteBooleanOperandNormals(shapes);
|
|
19667
19835
|
try {
|
|
19668
19836
|
switch (plan.op) {
|
|
19669
19837
|
case "union":
|
|
19670
|
-
return wasm.Manifold.union(
|
|
19838
|
+
return wasm.Manifold.union(operands);
|
|
19671
19839
|
case "difference":
|
|
19672
|
-
return wasm.Manifold.difference(
|
|
19840
|
+
return wasm.Manifold.difference(operands);
|
|
19673
19841
|
case "intersection":
|
|
19674
|
-
return wasm.Manifold.intersection(
|
|
19842
|
+
return wasm.Manifold.intersection(operands);
|
|
19675
19843
|
}
|
|
19676
19844
|
} finally {
|
|
19845
|
+
disposeWasmObjects(created);
|
|
19677
19846
|
disposeWasmObjects(shapes);
|
|
19678
19847
|
}
|
|
19679
19848
|
}
|
|
@@ -20419,9 +20588,9 @@ function lowerVerticalVariableSweepStitchedForManifold(plan, sectionPolygons, wa
|
|
|
20419
20588
|
return null;
|
|
20420
20589
|
}
|
|
20421
20590
|
const frameY = cross3$5(tangent, frameX);
|
|
20422
|
-
const
|
|
20591
|
+
const span2 = end[2] - start[2];
|
|
20423
20592
|
const sections = sectionPolygons.map((section) => ({
|
|
20424
|
-
height: start[2] +
|
|
20593
|
+
height: start[2] + span2 * section.t,
|
|
20425
20594
|
polygons: section.polygons.map(
|
|
20426
20595
|
(loop) => loop.map(([u2, v]) => [start[0] + u2 * frameX[0] + v * frameY[0], start[1] + u2 * frameX[1] + v * frameY[1]])
|
|
20427
20596
|
)
|
|
@@ -21023,12 +21192,16 @@ function lowerNurbsSurfaceToManifold(plan, wasm) {
|
|
|
21023
21192
|
const { positions, normals, indices } = surface.tessellate(res, res);
|
|
21024
21193
|
const thickness = plan.thickness;
|
|
21025
21194
|
const numVerts = positions.length;
|
|
21026
|
-
const
|
|
21027
|
-
for (const [x2, y2, z2] of positions) allPositions.push(x2, y2, z2);
|
|
21195
|
+
const vertProps = [];
|
|
21028
21196
|
for (let i = 0; i < numVerts; i++) {
|
|
21029
21197
|
const [x2, y2, z2] = positions[i];
|
|
21030
21198
|
const [nx, ny, nz] = normals[i];
|
|
21031
|
-
|
|
21199
|
+
vertProps.push(x2, y2, z2, nx, ny, nz);
|
|
21200
|
+
}
|
|
21201
|
+
for (let i = 0; i < numVerts; i++) {
|
|
21202
|
+
const [x2, y2, z2] = positions[i];
|
|
21203
|
+
const [nx, ny, nz] = normals[i];
|
|
21204
|
+
vertProps.push(x2 - nx * thickness, y2 - ny * thickness, z2 - nz * thickness, -nx, -ny, -nz);
|
|
21032
21205
|
}
|
|
21033
21206
|
const allIndices = [];
|
|
21034
21207
|
for (const idx of indices) allIndices.push(idx);
|
|
@@ -21053,11 +21226,12 @@ function lowerNurbsSurfaceToManifold(plan, wasm) {
|
|
|
21053
21226
|
allIndices.push(c2, c2 + numVerts, d2, d2, c2 + numVerts, d2 + numVerts);
|
|
21054
21227
|
}
|
|
21055
21228
|
const mesh = new wasm.Mesh({
|
|
21056
|
-
numProp:
|
|
21057
|
-
vertProperties: new Float32Array(
|
|
21229
|
+
numProp: 6,
|
|
21230
|
+
vertProperties: new Float32Array(vertProps),
|
|
21058
21231
|
triVerts: new Uint32Array(allIndices)
|
|
21059
21232
|
});
|
|
21060
21233
|
try {
|
|
21234
|
+
mesh.merge();
|
|
21061
21235
|
return new wasm.Manifold(mesh);
|
|
21062
21236
|
} finally {
|
|
21063
21237
|
disposeWasmObject(mesh);
|
|
@@ -22963,7 +23137,7 @@ function lowerAxisymmetricEllipsoidForOCCT(oc, radius, heightRadius) {
|
|
|
22963
23137
|
}
|
|
22964
23138
|
function lowerOrthogonalEllipseFromSlicesPlan(oc, plan) {
|
|
22965
23139
|
if (plan.groups.length !== 2) return null;
|
|
22966
|
-
const
|
|
23140
|
+
const center2 = [0, 0, 0];
|
|
22967
23141
|
const radii = [null, null, null];
|
|
22968
23142
|
for (const group of plan.groups) {
|
|
22969
23143
|
if (group.slices.length !== 1) return null;
|
|
@@ -22975,7 +23149,7 @@ function lowerOrthogonalEllipseFromSlicesPlan(oc, plan) {
|
|
|
22975
23149
|
const uAxis = axisAlignedDirectionForOCCT(frame.u);
|
|
22976
23150
|
const vAxis = axisAlignedDirectionForOCCT(frame.v);
|
|
22977
23151
|
if (!normalAxis || !uAxis || !vAxis) return null;
|
|
22978
|
-
|
|
23152
|
+
center2[normalAxis.axis] = normalAxis.sign * slice.offset;
|
|
22979
23153
|
if (!setEllipsoidRadiusForOCCT(radii, uAxis.axis, profileRadii[0])) return null;
|
|
22980
23154
|
if (!setEllipsoidRadiusForOCCT(radii, vAxis.axis, profileRadii[1])) return null;
|
|
22981
23155
|
}
|
|
@@ -22986,9 +23160,9 @@ function lowerOrthogonalEllipseFromSlicesPlan(oc, plan) {
|
|
|
22986
23160
|
const axisymmetricTolerance = Math.max(1e-7, Math.max(Math.abs(rx), Math.abs(ry)) * 1e-7);
|
|
22987
23161
|
if (Math.abs(rx - ry) > axisymmetricTolerance) return null;
|
|
22988
23162
|
let shape = lowerAxisymmetricEllipsoidForOCCT(oc, (rx + ry) / 2, rz);
|
|
22989
|
-
if (Math.hypot(
|
|
23163
|
+
if (Math.hypot(center2[0], center2[1], center2[2]) > 1e-10) {
|
|
22990
23164
|
const trsf = new oc.gp_Trsf_1();
|
|
22991
|
-
trsf.SetTranslation_1(new oc.gp_Vec_4(
|
|
23165
|
+
trsf.SetTranslation_1(new oc.gp_Vec_4(center2[0], center2[1], center2[2]));
|
|
22992
23166
|
const moved = new oc.BRepBuilderAPI_Transform_2(shape, trsf, true);
|
|
22993
23167
|
shape = moved.Shape();
|
|
22994
23168
|
}
|
|
@@ -23910,8 +24084,8 @@ function resamplePolyline$1(points, count) {
|
|
|
23910
24084
|
for (let index2 = 0; index2 < count; index2 += 1) {
|
|
23911
24085
|
const target = index2 / (count - 1) * total;
|
|
23912
24086
|
while (segment < cumulative.length - 2 && cumulative[segment + 1] < target) segment += 1;
|
|
23913
|
-
const
|
|
23914
|
-
const t =
|
|
24087
|
+
const span2 = cumulative[segment + 1] - cumulative[segment];
|
|
24088
|
+
const t = span2 > 1e-12 ? (target - cumulative[segment]) / span2 : 0;
|
|
23915
24089
|
const a2 = points[segment];
|
|
23916
24090
|
const b = points[segment + 1];
|
|
23917
24091
|
out.push([a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t]);
|
|
@@ -24042,19 +24216,23 @@ function surfaceGridForFillPlan$1(plan) {
|
|
|
24042
24216
|
function surfaceGridForAnalyticPlan$1(plan) {
|
|
24043
24217
|
const count = Math.max(2, plan.resolution + 1);
|
|
24044
24218
|
const grid = [];
|
|
24219
|
+
const uvGrid = [];
|
|
24045
24220
|
const { uMin, uMax, vMin, vMax } = plan.surface;
|
|
24046
24221
|
for (let uIndex = 0; uIndex < count; uIndex += 1) {
|
|
24047
24222
|
const uNorm = uIndex / (count - 1);
|
|
24048
24223
|
const u2 = uMin + (uMax - uMin) * uNorm;
|
|
24049
24224
|
const row = [];
|
|
24225
|
+
const uvRow = [];
|
|
24050
24226
|
for (let vIndex = 0; vIndex < count; vIndex += 1) {
|
|
24051
24227
|
const vNorm = vIndex / (count - 1);
|
|
24052
24228
|
const v = vMin + (vMax - vMin) * vNorm;
|
|
24053
24229
|
row.push(analyticSurfacePoint$1(plan.surface, u2, v));
|
|
24230
|
+
uvRow.push([uNorm, vNorm]);
|
|
24054
24231
|
}
|
|
24055
24232
|
grid.push(row);
|
|
24233
|
+
uvGrid.push(uvRow);
|
|
24056
24234
|
}
|
|
24057
|
-
return { grid, trim: sampleTrim(plan.trim) };
|
|
24235
|
+
return { grid, trim: sampleTrim(plan.trim), uvGrid };
|
|
24058
24236
|
}
|
|
24059
24237
|
function computeGridNormals(positions, indices) {
|
|
24060
24238
|
const normals = Array.from({ length: positions.length }, () => [0, 0, 0]);
|
|
@@ -24080,6 +24258,7 @@ function meshFromSurfaceGrid(grid, trim = null, context = "SDF surface sheet", u
|
|
|
24080
24258
|
const cols = ((_a3 = grid[0]) == null ? void 0 : _a3.length) ?? 0;
|
|
24081
24259
|
if (rows < 2 || cols < 2) throw new Error(`${context} requires at least a 2x2 sampled grid.`);
|
|
24082
24260
|
const positions = grid.flat();
|
|
24261
|
+
const hasParametricUv = uvGrid !== void 0;
|
|
24083
24262
|
const uvs = [];
|
|
24084
24263
|
if (uvGrid) {
|
|
24085
24264
|
if (uvGrid.length !== rows || uvGrid.some((row) => row.length !== cols)) {
|
|
@@ -24091,6 +24270,16 @@ function meshFromSurfaceGrid(grid, trim = null, context = "SDF surface sheet", u
|
|
|
24091
24270
|
for (let col = 0; col < cols; col += 1) uvs.push([row / (rows - 1), col / (cols - 1)]);
|
|
24092
24271
|
}
|
|
24093
24272
|
}
|
|
24273
|
+
const normalizeUv = (() => {
|
|
24274
|
+
if (!uvGrid) return (uv) => uv;
|
|
24275
|
+
const { uDomain, vDomain } = uvDomainFromGrid(uvGrid);
|
|
24276
|
+
const uSpan = uDomain[1] - uDomain[0];
|
|
24277
|
+
const vSpan = vDomain[1] - vDomain[0];
|
|
24278
|
+
return (uv) => [
|
|
24279
|
+
uSpan > 0 ? (uv[0] - uDomain[0]) / uSpan : 0,
|
|
24280
|
+
vSpan > 0 ? (uv[1] - vDomain[0]) / vSpan : 0
|
|
24281
|
+
];
|
|
24282
|
+
})();
|
|
24094
24283
|
const sourceIndices = [];
|
|
24095
24284
|
const includeTriangle = (a2, b, c2) => {
|
|
24096
24285
|
const centroid2 = [(uvs[a2][0] + uvs[b][0] + uvs[c2][0]) / 3, (uvs[a2][1] + uvs[b][1] + uvs[c2][1]) / 3];
|
|
@@ -24109,29 +24298,37 @@ function meshFromSurfaceGrid(grid, trim = null, context = "SDF surface sheet", u
|
|
|
24109
24298
|
if (sourceIndices.length === 0) throw new Error(`${context} trim loops removed the whole sampled surface.`);
|
|
24110
24299
|
const used = /* @__PURE__ */ new Map();
|
|
24111
24300
|
const compactPositions = [];
|
|
24301
|
+
const compactUvs = [];
|
|
24112
24302
|
const compactIndices = sourceIndices.map((sourceIndex) => {
|
|
24113
24303
|
const existing = used.get(sourceIndex);
|
|
24114
24304
|
if (existing !== void 0) return existing;
|
|
24115
24305
|
const next = compactPositions.length;
|
|
24116
24306
|
used.set(sourceIndex, next);
|
|
24117
24307
|
compactPositions.push(positions[sourceIndex]);
|
|
24308
|
+
if (hasParametricUv) compactUvs.push(normalizeUv(uvs[sourceIndex]));
|
|
24118
24309
|
return next;
|
|
24119
24310
|
});
|
|
24120
24311
|
const compactNormals = computeGridNormals(compactPositions, compactIndices);
|
|
24121
|
-
const
|
|
24312
|
+
const numProp = hasParametricUv ? NUM_PROP_WITH_UV : NUM_PROP_WITH_NORMAL;
|
|
24313
|
+
const vertProperties = new Float32Array(compactPositions.length * numProp);
|
|
24122
24314
|
for (let index2 = 0; index2 < compactPositions.length; index2 += 1) {
|
|
24123
24315
|
const [x2, y2, z2] = compactPositions[index2];
|
|
24124
24316
|
const [nx, ny, nz] = compactNormals[index2];
|
|
24125
|
-
const offset = index2 *
|
|
24126
|
-
vertProperties[offset] = x2;
|
|
24127
|
-
vertProperties[offset + 1] = y2;
|
|
24128
|
-
vertProperties[offset + 2] = z2;
|
|
24129
|
-
vertProperties[offset +
|
|
24130
|
-
vertProperties[offset +
|
|
24131
|
-
vertProperties[offset +
|
|
24317
|
+
const offset = index2 * numProp;
|
|
24318
|
+
vertProperties[offset + POSITION_OFFSET] = x2;
|
|
24319
|
+
vertProperties[offset + POSITION_OFFSET + 1] = y2;
|
|
24320
|
+
vertProperties[offset + POSITION_OFFSET + 2] = z2;
|
|
24321
|
+
vertProperties[offset + NORMAL_OFFSET] = nx;
|
|
24322
|
+
vertProperties[offset + NORMAL_OFFSET + 1] = ny;
|
|
24323
|
+
vertProperties[offset + NORMAL_OFFSET + 2] = nz;
|
|
24324
|
+
if (hasParametricUv) {
|
|
24325
|
+
const [u2, v] = compactUvs[index2];
|
|
24326
|
+
vertProperties[offset + UV_OFFSET] = u2;
|
|
24327
|
+
vertProperties[offset + UV_OFFSET + 1] = v;
|
|
24328
|
+
}
|
|
24132
24329
|
}
|
|
24133
24330
|
return {
|
|
24134
|
-
numProp
|
|
24331
|
+
numProp,
|
|
24135
24332
|
numVert: compactPositions.length,
|
|
24136
24333
|
numTri: compactIndices.length / 3,
|
|
24137
24334
|
vertProperties,
|
|
@@ -24216,16 +24413,20 @@ function surfaceGridForNurbsPlan(plan) {
|
|
|
24216
24413
|
});
|
|
24217
24414
|
const resolution = Math.max(2, Math.round(plan.resolution));
|
|
24218
24415
|
const grid = [];
|
|
24416
|
+
const uvGrid = [];
|
|
24219
24417
|
for (let i = 0; i <= resolution; i += 1) {
|
|
24220
24418
|
const u2 = i / resolution;
|
|
24221
24419
|
const row = [];
|
|
24420
|
+
const uvRow = [];
|
|
24222
24421
|
for (let j = 0; j <= resolution; j += 1) {
|
|
24223
24422
|
const v = j / resolution;
|
|
24224
24423
|
row.push(surface.pointAt(u2, v));
|
|
24424
|
+
uvRow.push([u2, v]);
|
|
24225
24425
|
}
|
|
24226
24426
|
grid.push(row);
|
|
24427
|
+
uvGrid.push(uvRow);
|
|
24227
24428
|
}
|
|
24228
|
-
return { grid, rowAxis: "u", trim: sampleTrim(plan.trim), uDomain: [0, 1], vDomain: [0, 1] };
|
|
24429
|
+
return { grid, rowAxis: "u", trim: sampleTrim(plan.trim), uDomain: [0, 1], vDomain: [0, 1], uvGrid };
|
|
24229
24430
|
}
|
|
24230
24431
|
function surfaceGridForSheetPlan(plan) {
|
|
24231
24432
|
if (plan.kind === "queryOwner") return surfaceGridForSheetPlan(plan.base);
|
|
@@ -24240,8 +24441,8 @@ function surfaceGridForSheetPlan(plan) {
|
|
|
24240
24441
|
}
|
|
24241
24442
|
if (plan.kind === "nurbsSurface") return surfaceGridForNurbsPlan(plan);
|
|
24242
24443
|
if (plan.kind === "analyticSurface") {
|
|
24243
|
-
const { grid, trim } = surfaceGridForAnalyticPlan$1(plan);
|
|
24244
|
-
return { grid, rowAxis: "u", trim, uDomain: [0, 1], vDomain: [0, 1] };
|
|
24444
|
+
const { grid, trim, uvGrid } = surfaceGridForAnalyticPlan$1(plan);
|
|
24445
|
+
return { grid, rowAxis: "u", trim, uDomain: [0, 1], vDomain: [0, 1], uvGrid };
|
|
24245
24446
|
}
|
|
24246
24447
|
if (plan.kind === "surfaceRuled") return { grid: surfaceGridForRuledPlan$1(plan), rowAxis: "v" };
|
|
24247
24448
|
if (plan.kind === "surfaceFill") return { grid: surfaceGridForFillPlan$1(plan), rowAxis: "v" };
|
|
@@ -24515,8 +24716,8 @@ function meshFromOpenSurfaceSheetPlan(plan) {
|
|
|
24515
24716
|
}
|
|
24516
24717
|
if (plan.kind === "nurbsSurface") return meshFromOpenNurbsSurface(plan);
|
|
24517
24718
|
if (plan.kind === "analyticSurface") {
|
|
24518
|
-
const { grid, trim } = surfaceGridForAnalyticPlan$1(plan);
|
|
24519
|
-
return meshFromSurfaceGrid(grid, trim, "SDF analytic surface");
|
|
24719
|
+
const { grid, trim, uvGrid } = surfaceGridForAnalyticPlan$1(plan);
|
|
24720
|
+
return meshFromSurfaceGrid(grid, trim, "SDF analytic surface", uvGrid);
|
|
24520
24721
|
}
|
|
24521
24722
|
if (plan.kind === "surfaceRuled") return meshFromSurfaceGrid(surfaceGridForRuledPlan$1(plan), null, "SDF ruled surface");
|
|
24522
24723
|
if (plan.kind === "surfaceFill") return meshFromSurfaceFillPlan(plan);
|
|
@@ -31998,8 +32199,8 @@ function revolveProfilePolygonsToSdfField(polygons, degrees = 360) {
|
|
|
31998
32199
|
const profileDistance = profile.eval(radial, z2);
|
|
31999
32200
|
if (Math.abs(degrees) >= 360) return profileDistance;
|
|
32000
32201
|
const sweep = Math.abs(degrees) * (Math.PI / 180);
|
|
32001
|
-
const
|
|
32002
|
-
let angle = Math.atan2(y2, x2) -
|
|
32202
|
+
const center2 = degrees * Math.PI / 360;
|
|
32203
|
+
let angle = Math.atan2(y2, x2) - center2;
|
|
32003
32204
|
while (angle <= -Math.PI) angle += Math.PI * 2;
|
|
32004
32205
|
while (angle > Math.PI) angle -= Math.PI * 2;
|
|
32005
32206
|
const half = sweep / 2;
|
|
@@ -33305,15 +33506,15 @@ function deBoorPoint(degree, knots, controlPoints, parameter) {
|
|
|
33305
33506
|
const domainEnd = knots[controlPoints.length];
|
|
33306
33507
|
const t = Math.max(domainStart, Math.min(domainEnd, parameter));
|
|
33307
33508
|
if (t >= domainEnd - 1e-12) return controlPoints[controlPoints.length - 1];
|
|
33308
|
-
let
|
|
33509
|
+
let span2 = degree;
|
|
33309
33510
|
const lastSpan = controlPoints.length - 1;
|
|
33310
|
-
while (
|
|
33511
|
+
while (span2 < lastSpan && t >= knots[span2 + 1]) span2 += 1;
|
|
33311
33512
|
const points = [];
|
|
33312
|
-
for (let index2 = 0; index2 <= degree; index2 += 1) points.push([...controlPoints[
|
|
33513
|
+
for (let index2 = 0; index2 <= degree; index2 += 1) points.push([...controlPoints[span2 - degree + index2]]);
|
|
33313
33514
|
for (let order = 1; order <= degree; order += 1) {
|
|
33314
33515
|
for (let index2 = degree; index2 >= order; index2 -= 1) {
|
|
33315
|
-
const knotLeft = knots[
|
|
33316
|
-
const knotRight = knots[
|
|
33516
|
+
const knotLeft = knots[span2 - degree + index2];
|
|
33517
|
+
const knotRight = knots[span2 + index2 + 1 - order];
|
|
33317
33518
|
const alpha = Math.abs(knotRight - knotLeft) <= 1e-12 ? 0 : (t - knotLeft) / (knotRight - knotLeft);
|
|
33318
33519
|
points[index2] = [
|
|
33319
33520
|
points[index2 - 1][0] * (1 - alpha) + points[index2][0] * alpha,
|
|
@@ -33329,16 +33530,16 @@ function deBoorHomogeneousPoint(degree, knots, controlPoints, parameter) {
|
|
|
33329
33530
|
const domainEnd = knots[controlPoints.length];
|
|
33330
33531
|
const t = Math.max(domainStart, Math.min(domainEnd, parameter));
|
|
33331
33532
|
if (t >= domainEnd - 1e-12) return controlPoints[controlPoints.length - 1];
|
|
33332
|
-
let
|
|
33533
|
+
let span2 = degree;
|
|
33333
33534
|
const lastSpan = controlPoints.length - 1;
|
|
33334
|
-
while (
|
|
33535
|
+
while (span2 < lastSpan && t >= knots[span2 + 1]) span2 += 1;
|
|
33335
33536
|
const points = [];
|
|
33336
33537
|
for (let index2 = 0; index2 <= degree; index2 += 1)
|
|
33337
|
-
points.push([...controlPoints[
|
|
33538
|
+
points.push([...controlPoints[span2 - degree + index2]]);
|
|
33338
33539
|
for (let order = 1; order <= degree; order += 1) {
|
|
33339
33540
|
for (let index2 = degree; index2 >= order; index2 -= 1) {
|
|
33340
|
-
const knotLeft = knots[
|
|
33341
|
-
const knotRight = knots[
|
|
33541
|
+
const knotLeft = knots[span2 - degree + index2];
|
|
33542
|
+
const knotRight = knots[span2 + index2 + 1 - order];
|
|
33342
33543
|
const alpha = Math.abs(knotRight - knotLeft) <= 1e-12 ? 0 : (t - knotLeft) / (knotRight - knotLeft);
|
|
33343
33544
|
points[index2] = [
|
|
33344
33545
|
points[index2 - 1][0] * (1 - alpha) + points[index2][0] * alpha,
|
|
@@ -33400,9 +33601,9 @@ function closestBSplineParameter(curve, point2) {
|
|
|
33400
33601
|
function wrappedBSplinePoint(curve, parameter) {
|
|
33401
33602
|
const [domainStart, domainEnd] = bsplineDomain(curve);
|
|
33402
33603
|
if (!curve.closed) return bsplinePoint(curve, parameter);
|
|
33403
|
-
const
|
|
33404
|
-
let wrapped = (parameter - domainStart) %
|
|
33405
|
-
if (wrapped < domainStart) wrapped +=
|
|
33604
|
+
const span2 = domainEnd - domainStart;
|
|
33605
|
+
let wrapped = (parameter - domainStart) % span2 + domainStart;
|
|
33606
|
+
if (wrapped < domainStart) wrapped += span2;
|
|
33406
33607
|
return bsplinePoint(curve, wrapped);
|
|
33407
33608
|
}
|
|
33408
33609
|
function bsplineSurfacePoint(surface, u2, v) {
|
|
@@ -33718,8 +33919,8 @@ function resampleClosedLoop(loop, count) {
|
|
|
33718
33919
|
for (let index2 = 0; index2 < count; index2 += 1) {
|
|
33719
33920
|
const target = index2 / count * total;
|
|
33720
33921
|
while (segment < points.length - 1 && cumulative[segment + 1] < target) segment += 1;
|
|
33721
|
-
const
|
|
33722
|
-
const t =
|
|
33922
|
+
const span2 = cumulative[segment + 1] - cumulative[segment];
|
|
33923
|
+
const t = span2 <= 1e-12 ? 0 : (target - cumulative[segment]) / span2;
|
|
33723
33924
|
const a2 = points[segment];
|
|
33724
33925
|
const b = points[(segment + 1) % points.length];
|
|
33725
33926
|
out.push([a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t]);
|
|
@@ -33747,14 +33948,14 @@ function orientRadialTriangles(triangles, frame, sameSense) {
|
|
|
33747
33948
|
const flip = sameSense ? !radialOutward : radialOutward;
|
|
33748
33949
|
return flip ? triangles.map(reverseTriangle) : triangles;
|
|
33749
33950
|
}
|
|
33750
|
-
function orientCenteredTriangles(triangles,
|
|
33951
|
+
function orientCenteredTriangles(triangles, center2, sameSense) {
|
|
33751
33952
|
return triangles.map((triangle) => {
|
|
33752
33953
|
const sample = [
|
|
33753
33954
|
(triangle[0][0] + triangle[1][0] + triangle[2][0]) / 3,
|
|
33754
33955
|
(triangle[0][1] + triangle[1][1] + triangle[2][1]) / 3,
|
|
33755
33956
|
(triangle[0][2] + triangle[1][2] + triangle[2][2]) / 3
|
|
33756
33957
|
];
|
|
33757
|
-
const outward = [sample[0] -
|
|
33958
|
+
const outward = [sample[0] - center2[0], sample[1] - center2[1], sample[2] - center2[2]];
|
|
33758
33959
|
const normal2 = cross3$4(
|
|
33759
33960
|
[triangle[1][0] - triangle[0][0], triangle[1][1] - triangle[0][1], triangle[1][2] - triangle[0][2]],
|
|
33760
33961
|
[triangle[2][0] - triangle[0][0], triangle[2][1] - triangle[0][1], triangle[2][2] - triangle[0][2]]
|
|
@@ -33993,8 +34194,8 @@ function orientRevolvedProfileTriangles(triangles, frame, axis, profileCenter, p
|
|
|
33993
34194
|
(triangle[0][2] + triangle[1][2] + triangle[2][2]) / 3
|
|
33994
34195
|
];
|
|
33995
34196
|
const angle = revolutionAngle(frame, sample, filePath, "SURFACE_OF_REVOLUTION");
|
|
33996
|
-
const
|
|
33997
|
-
const outward = sub3$1(sample,
|
|
34197
|
+
const center2 = rotateAroundAxis(profileCenter, axis, normalizeAngleDelta(angle - profileAngle));
|
|
34198
|
+
const outward = sub3$1(sample, center2);
|
|
33998
34199
|
if (Math.hypot(outward[0], outward[1], outward[2]) <= 1e-12)
|
|
33999
34200
|
failStepImport(filePath, "SURFACE_OF_REVOLUTION sample hit degenerate profile centerline.");
|
|
34000
34201
|
const normal2 = cross3$4(sub3$1(triangle[1], triangle[0]), sub3$1(triangle[2], triangle[0]));
|
|
@@ -34399,18 +34600,18 @@ function triangulateTrimmedBSplineSurface(surface, loops, sameSense, filePath, c
|
|
|
34399
34600
|
const { outer: uvLoop, holes } = trimmedBSplineSurfaceUvLoops(surface, loops, filePath, context);
|
|
34400
34601
|
if (holes.length > 0 || !isConvexUvLoop(uvLoop))
|
|
34401
34602
|
return triangulateBSplineSurfaceUvPolygon(surface, uvLoop, holes, sameSense, filePath, context);
|
|
34402
|
-
const
|
|
34603
|
+
const center2 = uvCentroid(uvLoop);
|
|
34403
34604
|
const ringCount = Math.max(3, Math.ceil(Math.max(surface.controlPoints.length, surface.controlPoints[0].length) * 1.5));
|
|
34404
34605
|
const rings = [];
|
|
34405
34606
|
for (let ringIndex = 1; ringIndex <= ringCount; ringIndex += 1) {
|
|
34406
34607
|
const t = ringIndex / ringCount;
|
|
34407
|
-
rings.push(uvLoop.map((point2) => [
|
|
34608
|
+
rings.push(uvLoop.map((point2) => [center2[0] + (point2[0] - center2[0]) * t, center2[1] + (point2[1] - center2[1]) * t]));
|
|
34408
34609
|
}
|
|
34409
34610
|
const mapPoint = (point2) => bsplineSurfacePoint(surface, point2[0], point2[1]);
|
|
34410
34611
|
const triangles = [];
|
|
34411
34612
|
const firstRing = rings[0];
|
|
34412
34613
|
for (let index2 = 0; index2 < firstRing.length; index2 += 1) {
|
|
34413
|
-
triangles.push([mapPoint(
|
|
34614
|
+
triangles.push([mapPoint(center2), mapPoint(firstRing[index2]), mapPoint(firstRing[(index2 + 1) % firstRing.length])]);
|
|
34414
34615
|
}
|
|
34415
34616
|
for (let ringIndex = 1; ringIndex < rings.length; ringIndex += 1) {
|
|
34416
34617
|
const inner = rings[ringIndex - 1];
|
|
@@ -34427,9 +34628,9 @@ function rotateArray(items, start) {
|
|
|
34427
34628
|
return [...items.slice(start), ...items.slice(0, start)];
|
|
34428
34629
|
}
|
|
34429
34630
|
function periodicParameterDistance(a2, b, start, end) {
|
|
34430
|
-
const
|
|
34631
|
+
const span2 = end - start;
|
|
34431
34632
|
const raw = Math.abs(a2 - b);
|
|
34432
|
-
return Math.min(raw, Math.max(0,
|
|
34633
|
+
return Math.min(raw, Math.max(0, span2 - raw));
|
|
34433
34634
|
}
|
|
34434
34635
|
function closedUBSplineBoundaryRing(surface, loop, count, filePath, context) {
|
|
34435
34636
|
const points = resampleClosedLoop(loop, count);
|
|
@@ -34694,11 +34895,11 @@ function sphericalLoopInfo(frame, radius, loop, filePath, context) {
|
|
|
34694
34895
|
if (Math.abs(distance - radius) > tolerance)
|
|
34695
34896
|
failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE boundary point is not on the sphere.`);
|
|
34696
34897
|
}
|
|
34697
|
-
const
|
|
34898
|
+
const center2 = centroid(points);
|
|
34698
34899
|
const normal2 = normalize3$2(newellNormal(points), `${context} bounded SPHERICAL_SURFACE boundary normal`);
|
|
34699
|
-
const maxPlaneError = points.reduce((max2, point2) => Math.max(max2, Math.abs(dot3$3(sub3$1(point2,
|
|
34900
|
+
const maxPlaneError = points.reduce((max2, point2) => Math.max(max2, Math.abs(dot3$3(sub3$1(point2, center2), normal2))), 0);
|
|
34700
34901
|
if (maxPlaneError > tolerance) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE boundary loop is not planar.`);
|
|
34701
|
-
return { points, center, normal: normal2 };
|
|
34902
|
+
return { points, center: center2, normal: normal2 };
|
|
34702
34903
|
}
|
|
34703
34904
|
function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePath, context) {
|
|
34704
34905
|
if (loops.length !== 2) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE band supports exactly two loops right now.`);
|
|
@@ -34719,15 +34920,15 @@ function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePa
|
|
|
34719
34920
|
const xProjection = dot3$3(rawX, axis);
|
|
34720
34921
|
const xAxis = normalize3$2([rawX[0] - axis[0] * xProjection, rawX[1] - axis[1] * xProjection, rawX[2] - axis[2] * xProjection], context);
|
|
34721
34922
|
const yAxis = cross3$4(axis, xAxis);
|
|
34722
|
-
const loopAngle = (point2,
|
|
34723
|
-
const local = sub3$1(point2,
|
|
34923
|
+
const loopAngle = (point2, center2) => {
|
|
34924
|
+
const local = sub3$1(point2, center2);
|
|
34724
34925
|
return Math.atan2(dot3$3(local, yAxis), dot3$3(local, xAxis));
|
|
34725
34926
|
};
|
|
34726
|
-
const rotateLoop = (loop,
|
|
34927
|
+
const rotateLoop = (loop, center2, targetAngle) => {
|
|
34727
34928
|
let bestIndex = 0;
|
|
34728
34929
|
let bestDistance = Number.POSITIVE_INFINITY;
|
|
34729
34930
|
for (let index2 = 0; index2 < loop.length; index2 += 1) {
|
|
34730
|
-
const distance = angleDistance(loopAngle(loop[index2],
|
|
34931
|
+
const distance = angleDistance(loopAngle(loop[index2], center2), targetAngle);
|
|
34731
34932
|
if (distance < bestDistance) {
|
|
34732
34933
|
bestDistance = distance;
|
|
34733
34934
|
bestIndex = index2;
|
|
@@ -34771,14 +34972,14 @@ function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePa
|
|
|
34771
34972
|
return orientCenteredTriangles(triangles, frame.origin, sameSense);
|
|
34772
34973
|
}
|
|
34773
34974
|
function orientBridgeTriangles(triangles, aCenter, bCenter, sameSense) {
|
|
34774
|
-
const
|
|
34975
|
+
const center2 = [(aCenter[0] + bCenter[0]) / 2, (aCenter[1] + bCenter[1]) / 2, (aCenter[2] + bCenter[2]) / 2];
|
|
34775
34976
|
const sample = triangles[0];
|
|
34776
34977
|
const mid = [
|
|
34777
34978
|
(sample[0][0] + sample[1][0] + sample[2][0]) / 3,
|
|
34778
34979
|
(sample[0][1] + sample[1][1] + sample[2][1]) / 3,
|
|
34779
34980
|
(sample[0][2] + sample[1][2] + sample[2][2]) / 3
|
|
34780
34981
|
];
|
|
34781
|
-
const outward = [mid[0] -
|
|
34982
|
+
const outward = [mid[0] - center2[0], mid[1] - center2[1], mid[2] - center2[2]];
|
|
34782
34983
|
const normal2 = cross3$4(
|
|
34783
34984
|
[sample[1][0] - sample[0][0], sample[1][1] - sample[0][1], sample[1][2] - sample[0][2]],
|
|
34784
34985
|
[sample[2][0] - sample[0][0], sample[2][1] - sample[0][1], sample[2][2] - sample[0][2]]
|
|
@@ -36286,18 +36487,18 @@ function circleFootprintFromProfile(plan) {
|
|
|
36286
36487
|
if (plan.kind !== "circle") return null;
|
|
36287
36488
|
const radius = Math.abs(plan.radius);
|
|
36288
36489
|
if (!finitePositive(radius)) return null;
|
|
36289
|
-
const
|
|
36490
|
+
const center2 = transformProfilePointThrough([0, 0], plan.transforms);
|
|
36290
36491
|
const xPoint = transformProfilePointThrough([1, 0], plan.transforms);
|
|
36291
36492
|
const yPoint = transformProfilePointThrough([0, 1], plan.transforms);
|
|
36292
|
-
const xAxis = [xPoint[0] -
|
|
36293
|
-
const yAxis = [yPoint[0] -
|
|
36493
|
+
const xAxis = [xPoint[0] - center2[0], xPoint[1] - center2[1]];
|
|
36494
|
+
const yAxis = [yPoint[0] - center2[0], yPoint[1] - center2[1]];
|
|
36294
36495
|
const xScale = Math.hypot(xAxis[0], xAxis[1]);
|
|
36295
36496
|
const yScale = Math.hypot(yAxis[0], yAxis[1]);
|
|
36296
36497
|
const dot2 = xAxis[0] * yAxis[0] + xAxis[1] * yAxis[1];
|
|
36297
36498
|
if (!finitePositive(xScale) || !finitePositive(yScale)) return null;
|
|
36298
36499
|
if (Math.abs(xScale - yScale) > EPS || Math.abs(dot2) > EPS * xScale * yScale) return null;
|
|
36299
36500
|
return {
|
|
36300
|
-
center,
|
|
36501
|
+
center: center2,
|
|
36301
36502
|
radius: radius * xScale,
|
|
36302
36503
|
segments: plan.segments
|
|
36303
36504
|
};
|
|
@@ -36856,19 +37057,19 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
36856
37057
|
}
|
|
36857
37058
|
}
|
|
36858
37059
|
if ("AnalyticSphere" in geometry) {
|
|
36859
|
-
const
|
|
37060
|
+
const center2 = geometry.AnalyticSphere.center;
|
|
36860
37061
|
const radius = geometry.AnalyticSphere.radius;
|
|
36861
|
-
if (isVec3(
|
|
36862
|
-
return { kind: "sphere", center, radius };
|
|
37062
|
+
if (isVec3(center2) && isFiniteNumber(radius) && radius > 0) {
|
|
37063
|
+
return { kind: "sphere", center: center2, radius };
|
|
36863
37064
|
}
|
|
36864
37065
|
}
|
|
36865
37066
|
if ("AnalyticTorus" in geometry) {
|
|
36866
|
-
const
|
|
37067
|
+
const center2 = geometry.AnalyticTorus.center;
|
|
36867
37068
|
const axis = geometry.AnalyticTorus.axis;
|
|
36868
37069
|
const majorRadius = geometry.AnalyticTorus.major_radius;
|
|
36869
37070
|
const minorRadius = geometry.AnalyticTorus.minor_radius;
|
|
36870
|
-
if (isVec3(
|
|
36871
|
-
return { kind: "torus", center, axis: normalizeVec3(axis), majorRadius, minorRadius };
|
|
37071
|
+
if (isVec3(center2) && isVec3(axis) && isFiniteNumber(majorRadius) && isFiniteNumber(minorRadius) && majorRadius > 0 && minorRadius > 0) {
|
|
37072
|
+
return { kind: "torus", center: center2, axis: normalizeVec3(axis), majorRadius, minorRadius };
|
|
36872
37073
|
}
|
|
36873
37074
|
}
|
|
36874
37075
|
if ("NurbsPatch" in geometry) {
|
|
@@ -36962,13 +37163,13 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
36962
37163
|
function explicitEdgeCurve(geometry, faceName) {
|
|
36963
37164
|
var _a3, _b3, _c2, _d2, _e2;
|
|
36964
37165
|
if (!geometry) return void 0;
|
|
36965
|
-
const
|
|
37166
|
+
const center2 = (_a3 = geometry.CircularArc) == null ? void 0 : _a3.center;
|
|
36966
37167
|
const axis = (_b3 = geometry.CircularArc) == null ? void 0 : _b3.axis;
|
|
36967
37168
|
const radius = (_c2 = geometry.CircularArc) == null ? void 0 : _c2.radius;
|
|
36968
|
-
if (
|
|
37169
|
+
if (center2 !== void 0 && axis !== void 0 && radius !== void 0 && isVec3(center2) && isVec3(axis) && isFiniteNumber(radius) && radius > 0) {
|
|
36969
37170
|
return {
|
|
36970
37171
|
kind: "circle",
|
|
36971
|
-
center,
|
|
37172
|
+
center: center2,
|
|
36972
37173
|
axis: normalizeVec3(axis),
|
|
36973
37174
|
radius,
|
|
36974
37175
|
faceName
|
|
@@ -37485,6 +37686,7 @@ const _TruckShapeBackend = class _TruckShapeBackend {
|
|
|
37485
37686
|
const payload = JSON.parse(getTruckGeometryWasm().geometry_mesh(this.getLiveHandle("getMesh()")));
|
|
37486
37687
|
const numTri = payload.triangles.length / 3;
|
|
37487
37688
|
const numVert = payload.positions.length / 3;
|
|
37689
|
+
const cornerNormals = payload.normals && payload.normals.length === numTri * 9 ? new Float32Array(payload.normals) : void 0;
|
|
37488
37690
|
this.resource.mesh = {
|
|
37489
37691
|
numProp: 3,
|
|
37490
37692
|
numTri,
|
|
@@ -37498,7 +37700,8 @@ const _TruckShapeBackend = class _TruckShapeBackend {
|
|
|
37498
37700
|
runTransform: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0]),
|
|
37499
37701
|
faceID: new Int32Array(payload.face_ids),
|
|
37500
37702
|
faceIdNames: payload.face_id_names ?? [],
|
|
37501
|
-
halfedgeTangent: new Float32Array(0)
|
|
37703
|
+
halfedgeTangent: new Float32Array(0),
|
|
37704
|
+
cornerNormals
|
|
37502
37705
|
};
|
|
37503
37706
|
return this.resource.mesh;
|
|
37504
37707
|
}
|
|
@@ -38719,6 +38922,16 @@ function boundsInteriorOverlap(a2, b) {
|
|
|
38719
38922
|
const tolerance = 1e-8;
|
|
38720
38923
|
return Math.min(a2.max[0], b.max[0]) - Math.max(a2.min[0], b.min[0]) > tolerance && Math.min(a2.max[1], b.max[1]) - Math.max(a2.min[1], b.min[1]) > tolerance && Math.min(a2.max[2], b.max[2]) - Math.max(a2.min[2], b.min[2]) > tolerance;
|
|
38721
38924
|
}
|
|
38925
|
+
function boundsFaceOrInteriorOverlap(a2, b) {
|
|
38926
|
+
const tolerance = 1e-8;
|
|
38927
|
+
const overlaps = [
|
|
38928
|
+
Math.min(a2.max[0], b.max[0]) - Math.max(a2.min[0], b.min[0]),
|
|
38929
|
+
Math.min(a2.max[1], b.max[1]) - Math.max(a2.min[1], b.min[1]),
|
|
38930
|
+
Math.min(a2.max[2], b.max[2]) - Math.max(a2.min[2], b.min[2])
|
|
38931
|
+
];
|
|
38932
|
+
if (overlaps.some((overlap) => overlap < -tolerance)) return false;
|
|
38933
|
+
return overlaps.filter((overlap) => overlap <= tolerance).length <= 1;
|
|
38934
|
+
}
|
|
38722
38935
|
function boundsFromPoints(points) {
|
|
38723
38936
|
const first = points[0];
|
|
38724
38937
|
if (!first) return null;
|
|
@@ -38801,16 +39014,16 @@ function shapePlanBounds(plan) {
|
|
|
38801
39014
|
return null;
|
|
38802
39015
|
}
|
|
38803
39016
|
}
|
|
38804
|
-
function
|
|
39017
|
+
function hasPairwiseFaceOrInteriorBoundsOverlap(shapes) {
|
|
38805
39018
|
const bounds = shapes.map((shape) => shape.boundingBox());
|
|
38806
39019
|
for (let i = 0; i < bounds.length; i++) {
|
|
38807
39020
|
for (let j = i + 1; j < bounds.length; j++) {
|
|
38808
|
-
if (
|
|
39021
|
+
if (boundsFaceOrInteriorOverlap(bounds[i], bounds[j])) return true;
|
|
38809
39022
|
}
|
|
38810
39023
|
}
|
|
38811
39024
|
return false;
|
|
38812
39025
|
}
|
|
38813
|
-
function
|
|
39026
|
+
function faceOrInteriorOverlapComponents(shapes) {
|
|
38814
39027
|
const bounds = shapes.map((shape) => shape.boundingBox());
|
|
38815
39028
|
const visited = /* @__PURE__ */ new Set();
|
|
38816
39029
|
const components = [];
|
|
@@ -38824,7 +39037,7 @@ function interiorOverlapComponents(shapes) {
|
|
|
38824
39037
|
component.push(current);
|
|
38825
39038
|
for (let next = 0; next < shapes.length; next++) {
|
|
38826
39039
|
if (visited.has(next)) continue;
|
|
38827
|
-
if (
|
|
39040
|
+
if (boundsFaceOrInteriorOverlap(bounds[current], bounds[next])) {
|
|
38828
39041
|
visited.add(next);
|
|
38829
39042
|
queue.push(next);
|
|
38830
39043
|
}
|
|
@@ -38869,10 +39082,10 @@ function lowerGenericBooleanPlan(plan) {
|
|
|
38869
39082
|
let returned = null;
|
|
38870
39083
|
try {
|
|
38871
39084
|
if (plan.op === "union") {
|
|
38872
|
-
if (!
|
|
39085
|
+
if (!hasPairwiseFaceOrInteriorBoundsOverlap(shapes)) {
|
|
38873
39086
|
return null;
|
|
38874
39087
|
}
|
|
38875
|
-
const components =
|
|
39088
|
+
const components = faceOrInteriorOverlapComponents(shapes);
|
|
38876
39089
|
if (components.length > 1) {
|
|
38877
39090
|
returned = lowerClusteredUnion(shapes, components);
|
|
38878
39091
|
return returned;
|
|
@@ -39402,9 +39615,13 @@ function shapeHasClosedNativeTopology(shape) {
|
|
|
39402
39615
|
function normalizeTruckShapeForBooleanInput(shape) {
|
|
39403
39616
|
if (shapeHasClosedNativeTopology(shape)) return shape;
|
|
39404
39617
|
if (!meshHasRawBoundaryEdges(shape.getMesh())) return shape;
|
|
39405
|
-
|
|
39406
|
-
|
|
39407
|
-
|
|
39618
|
+
try {
|
|
39619
|
+
const normalized = normalizeFacetedTruckShape(shape);
|
|
39620
|
+
disposeShapeBackend(shape);
|
|
39621
|
+
return normalized;
|
|
39622
|
+
} catch {
|
|
39623
|
+
return shape;
|
|
39624
|
+
}
|
|
39408
39625
|
}
|
|
39409
39626
|
function lowerSdfPlan(plan) {
|
|
39410
39627
|
if (getUnsupportedSdfProgramReason(plan.tree) === void 0) {
|
|
@@ -39899,8 +40116,8 @@ function resamplePolyline(points, count) {
|
|
|
39899
40116
|
for (let idx = 0; idx < count; idx++) {
|
|
39900
40117
|
const target = idx / (count - 1) * total;
|
|
39901
40118
|
while (segment < cumulative.length - 2 && cumulative[segment + 1] < target) segment++;
|
|
39902
|
-
const
|
|
39903
|
-
const t =
|
|
40119
|
+
const span2 = cumulative[segment + 1] - cumulative[segment];
|
|
40120
|
+
const t = span2 > 1e-12 ? (target - cumulative[segment]) / span2 : 0;
|
|
39904
40121
|
const a2 = points[segment];
|
|
39905
40122
|
const b = points[segment + 1];
|
|
39906
40123
|
out.push([a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t]);
|
|
@@ -40658,12 +40875,12 @@ function transformedSphereProfile(basePlan, steps, sliceOffset) {
|
|
|
40658
40875
|
transform = transform.mul(transformForShapeTransform(step));
|
|
40659
40876
|
scale2 *= stepScale;
|
|
40660
40877
|
}
|
|
40661
|
-
const
|
|
40878
|
+
const center2 = transform.point([0, 0, 0]);
|
|
40662
40879
|
const radius = Math.abs(source.radius * scale2);
|
|
40663
|
-
const sectionRadius = sliceOffset == null ? radius : Math.sqrt(Math.max(0, radius * radius - (sliceOffset -
|
|
40880
|
+
const sectionRadius = sliceOffset == null ? radius : Math.sqrt(Math.max(0, radius * radius - (sliceOffset - center2[2]) * (sliceOffset - center2[2])));
|
|
40664
40881
|
if (sectionRadius <= EXACT_PROFILE_EPS) return emptyProfilePlan();
|
|
40665
40882
|
const profile = circleProfilePlan(sectionRadius, source.segments);
|
|
40666
|
-
if (!isNearlyZero(
|
|
40883
|
+
if (!isNearlyZero(center2[0]) || !isNearlyZero(center2[1])) profile.transforms.push({ kind: "translate", x: center2[0], y: center2[1] });
|
|
40667
40884
|
return profile;
|
|
40668
40885
|
}
|
|
40669
40886
|
function profileTransformsForShapeTransform(step) {
|
|
@@ -40766,20 +40983,20 @@ function sliceOffsetBeforeShapeTransforms(steps, offset) {
|
|
|
40766
40983
|
}
|
|
40767
40984
|
return sourceOffset;
|
|
40768
40985
|
}
|
|
40769
|
-
function transformZSpan(
|
|
40986
|
+
function transformZSpan(span2, step) {
|
|
40770
40987
|
switch (step.kind) {
|
|
40771
40988
|
case "translate":
|
|
40772
|
-
return [
|
|
40989
|
+
return [span2[0] + step.z, span2[1] + step.z];
|
|
40773
40990
|
case "scale":
|
|
40774
40991
|
if (!Number.isFinite(step.z) || isNearlyZero(step.z)) return null;
|
|
40775
|
-
return [Math.min(
|
|
40992
|
+
return [Math.min(span2[0] * step.z, span2[1] * step.z), Math.max(span2[0] * step.z, span2[1] * step.z)];
|
|
40776
40993
|
case "rotateAround": {
|
|
40777
40994
|
const axisLength = Math.hypot(step.axisX, step.axisY, step.axisZ);
|
|
40778
40995
|
if (axisLength <= EXACT_PROFILE_EPS) return null;
|
|
40779
40996
|
const axisX = step.axisX / axisLength;
|
|
40780
40997
|
const axisY = step.axisY / axisLength;
|
|
40781
40998
|
const axisZ = step.axisZ / axisLength;
|
|
40782
|
-
return !isNearlyZero(axisX) || !isNearlyZero(axisY) || Math.abs(Math.abs(axisZ) - 1) > EXACT_PROFILE_EPS ? null :
|
|
40999
|
+
return !isNearlyZero(axisX) || !isNearlyZero(axisY) || Math.abs(Math.abs(axisZ) - 1) > EXACT_PROFILE_EPS ? null : span2;
|
|
40783
41000
|
}
|
|
40784
41001
|
case "mirror": {
|
|
40785
41002
|
const normalLength = Math.hypot(step.normalX, step.normalY, step.normalZ);
|
|
@@ -40787,14 +41004,14 @@ function transformZSpan(span, step) {
|
|
|
40787
41004
|
const normalX = step.normalX / normalLength;
|
|
40788
41005
|
const normalY = step.normalY / normalLength;
|
|
40789
41006
|
const normalZ = step.normalZ / normalLength;
|
|
40790
|
-
if (isNearlyZero(normalX) && isNearlyZero(normalY)) return [-
|
|
40791
|
-
return isNearlyZero(normalZ) ?
|
|
41007
|
+
if (isNearlyZero(normalX) && isNearlyZero(normalY)) return [-span2[1], -span2[0]];
|
|
41008
|
+
return isNearlyZero(normalZ) ? span2 : null;
|
|
40792
41009
|
}
|
|
40793
41010
|
case "workplanePlacement": {
|
|
40794
41011
|
const replay = workplanePlacementReplay(step.matrix);
|
|
40795
41012
|
if (!replay) return null;
|
|
40796
|
-
const z0 = replay.zScale *
|
|
40797
|
-
const z1 = replay.zScale *
|
|
41013
|
+
const z0 = replay.zScale * span2[0] + replay.zTranslate;
|
|
41014
|
+
const z1 = replay.zScale * span2[1] + replay.zTranslate;
|
|
40798
41015
|
return [Math.min(z0, z1), Math.max(z0, z1)];
|
|
40799
41016
|
}
|
|
40800
41017
|
default:
|
|
@@ -40848,14 +41065,14 @@ function transformProfilePointThroughRadial(point2, transforms) {
|
|
|
40848
41065
|
return out;
|
|
40849
41066
|
}
|
|
40850
41067
|
function profileRadialTransformBasis(transforms) {
|
|
40851
|
-
const
|
|
41068
|
+
const center2 = transformProfilePointThroughRadial([0, 0], transforms);
|
|
40852
41069
|
const xBasis = transformProfilePointThroughRadial([1, 0], transforms);
|
|
40853
41070
|
const yBasis = transformProfilePointThroughRadial([0, 1], transforms);
|
|
40854
|
-
if (!
|
|
41071
|
+
if (!center2 || !xBasis || !yBasis) return null;
|
|
40855
41072
|
return {
|
|
40856
|
-
center,
|
|
40857
|
-
xAxis: [xBasis[0] -
|
|
40858
|
-
yAxis: [yBasis[0] -
|
|
41073
|
+
center: center2,
|
|
41074
|
+
xAxis: [xBasis[0] - center2[0], xBasis[1] - center2[1]],
|
|
41075
|
+
yAxis: [yBasis[0] - center2[0], yBasis[1] - center2[1]]
|
|
40859
41076
|
};
|
|
40860
41077
|
}
|
|
40861
41078
|
function circleRadialProjectionInterval(plan) {
|
|
@@ -41003,13 +41220,13 @@ function radialRoundedRectBoundaryMaxDistanceFromPoint(footprint, point2) {
|
|
|
41003
41220
|
[-1, -1]
|
|
41004
41221
|
];
|
|
41005
41222
|
for (const [sx, sy] of cornerSigns) {
|
|
41006
|
-
const
|
|
41007
|
-
includeLocal([
|
|
41008
|
-
includeLocal([
|
|
41009
|
-
const away = [
|
|
41223
|
+
const center2 = [sx * coreHalfWidth, sy * coreHalfHeight];
|
|
41224
|
+
includeLocal([center2[0] + sx * footprint.radius, center2[1]]);
|
|
41225
|
+
includeLocal([center2[0], center2[1] + sy * footprint.radius]);
|
|
41226
|
+
const away = [center2[0] - localPoint[0], center2[1] - localPoint[1]];
|
|
41010
41227
|
const awayLength = Math.hypot(away[0], away[1]);
|
|
41011
41228
|
if (awayLength > EXACT_PROFILE_EPS && away[0] * sx >= -EXACT_PROFILE_EPS && away[1] * sy >= -EXACT_PROFILE_EPS) {
|
|
41012
|
-
includeLocal([
|
|
41229
|
+
includeLocal([center2[0] + away[0] / awayLength * footprint.radius, center2[1] + away[1] / awayLength * footprint.radius]);
|
|
41013
41230
|
}
|
|
41014
41231
|
}
|
|
41015
41232
|
return maxDistance;
|
|
@@ -41107,10 +41324,10 @@ function lineIntervalsForRect(base, tangent, halfWidth, halfHeight) {
|
|
|
41107
41324
|
if (!applyAxis(base[0], tangent[0], halfWidth) || !applyAxis(base[1], tangent[1], halfHeight)) return [];
|
|
41108
41325
|
return [[min2, max2]];
|
|
41109
41326
|
}
|
|
41110
|
-
function lineIntervalsForCircle(base, tangent,
|
|
41327
|
+
function lineIntervalsForCircle(base, tangent, center2, radius) {
|
|
41111
41328
|
if (radius < -EXACT_PROFILE_EPS) return null;
|
|
41112
|
-
const dx = base[0] -
|
|
41113
|
-
const dy = base[1] -
|
|
41329
|
+
const dx = base[0] - center2[0];
|
|
41330
|
+
const dy = base[1] - center2[1];
|
|
41114
41331
|
const projection = dx * tangent[0] + dy * tangent[1];
|
|
41115
41332
|
const distanceSq = dx * dx + dy * dy - projection * projection;
|
|
41116
41333
|
const radiusSq = radius * radius;
|
|
@@ -41153,7 +41370,7 @@ function roundedRectRadialSliceIntervals(plan, y2) {
|
|
|
41153
41370
|
[-coreHalfWidth, coreHalfHeight],
|
|
41154
41371
|
[-coreHalfWidth, -coreHalfHeight]
|
|
41155
41372
|
];
|
|
41156
|
-
for (const
|
|
41373
|
+
for (const center2 of centers) include(lineIntervalsForCircle(base, tangent, center2, radius));
|
|
41157
41374
|
}
|
|
41158
41375
|
const xFunctional = [basis.xAxis[0], basis.yAxis[0]];
|
|
41159
41376
|
const xAtBase = basis.center[0] + xFunctional[0] * base[0] + xFunctional[1] * base[1];
|
|
@@ -41288,7 +41505,7 @@ function circleProfilePlan(radius, segments) {
|
|
|
41288
41505
|
}
|
|
41289
41506
|
function annulusProfilePlan(outerRadius, innerRadius, segments) {
|
|
41290
41507
|
const outer = Math.abs(outerRadius);
|
|
41291
|
-
const inner = Math.
|
|
41508
|
+
const inner = Math.max(0, innerRadius);
|
|
41292
41509
|
if (outer <= EXACT_PROFILE_EPS) return emptyProfilePlan();
|
|
41293
41510
|
if (inner <= EXACT_PROFILE_EPS) return circleProfilePlan(outer, segments);
|
|
41294
41511
|
return {
|
|
@@ -42461,9 +42678,9 @@ function expandSimpleFullRevolutionProfileFootprint(plan, distance) {
|
|
|
42461
42678
|
}
|
|
42462
42679
|
}
|
|
42463
42680
|
function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
|
|
42464
|
-
const translate = (profile,
|
|
42465
|
-
if (!isNearlyZero(
|
|
42466
|
-
profile.transforms.push({ kind: "translate", x:
|
|
42681
|
+
const translate = (profile, center2) => {
|
|
42682
|
+
if (!isNearlyZero(center2[0]) || !isNearlyZero(center2[1])) {
|
|
42683
|
+
profile.transforms.push({ kind: "translate", x: center2[0], y: center2[1] });
|
|
42467
42684
|
}
|
|
42468
42685
|
return profile;
|
|
42469
42686
|
};
|
|
@@ -42481,7 +42698,7 @@ function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
|
|
|
42481
42698
|
footprint.footprint.center
|
|
42482
42699
|
) : null;
|
|
42483
42700
|
case "roundedRect": {
|
|
42484
|
-
const { halfWidth, halfHeight, radius, xAxis, yAxis, center } = footprint.footprint;
|
|
42701
|
+
const { halfWidth, halfHeight, radius, xAxis, yAxis, center: center2 } = footprint.footprint;
|
|
42485
42702
|
if (halfWidth <= EXACT_PROFILE_EPS || halfHeight <= EXACT_PROFILE_EPS || radius < -EXACT_PROFILE_EPS) return null;
|
|
42486
42703
|
const profile = roundedRectProfilePlan({
|
|
42487
42704
|
width: halfWidth * 2,
|
|
@@ -42496,7 +42713,7 @@ function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
|
|
|
42496
42713
|
m11: yAxis[1]
|
|
42497
42714
|
})
|
|
42498
42715
|
);
|
|
42499
|
-
return translate(profile,
|
|
42716
|
+
return translate(profile, center2);
|
|
42500
42717
|
}
|
|
42501
42718
|
default:
|
|
42502
42719
|
return assertExhaustive(footprint);
|
|
@@ -43083,16 +43300,16 @@ function exactVerticalProjectionProfilePlan(plan) {
|
|
|
43083
43300
|
const base = exactVerticalProjectionProfilePlan(plan.base);
|
|
43084
43301
|
if (!base) return null;
|
|
43085
43302
|
let profile = base.profile;
|
|
43086
|
-
let
|
|
43303
|
+
let span2 = [base.zMin, base.zMax];
|
|
43087
43304
|
for (const step of plan.steps) {
|
|
43088
43305
|
const projectedTransforms = profileTransformsForShapeTransform(step);
|
|
43089
43306
|
if (!projectedTransforms) return null;
|
|
43090
|
-
const nextSpan = transformZSpan(
|
|
43307
|
+
const nextSpan = transformZSpan(span2, step);
|
|
43091
43308
|
if (!nextSpan) return null;
|
|
43092
43309
|
profile = appendProfileTransforms(profile, projectedTransforms);
|
|
43093
|
-
|
|
43310
|
+
span2 = nextSpan;
|
|
43094
43311
|
}
|
|
43095
|
-
return { profile, zMin:
|
|
43312
|
+
return { profile, zMin: span2[0], zMax: span2[1] };
|
|
43096
43313
|
}
|
|
43097
43314
|
default:
|
|
43098
43315
|
return null;
|
|
@@ -43112,7 +43329,7 @@ function projectVerticalDifferenceProfilePlan(subject, clips) {
|
|
|
43112
43329
|
const clipped = clips.map((clip) => ({ clip, span: clippedZSpan(subject, clip) })).filter((entry) => entry.span != null);
|
|
43113
43330
|
if (clipped.length === 0) return cloneProfileCompilePlan(subject.profile);
|
|
43114
43331
|
const zBreaks = [subject.zMin, subject.zMax];
|
|
43115
|
-
for (const { span } of clipped) zBreaks.push(
|
|
43332
|
+
for (const { span: span2 } of clipped) zBreaks.push(span2[0], span2[1]);
|
|
43116
43333
|
zBreaks.sort((a2, b) => a2 - b);
|
|
43117
43334
|
const uniqueBreaks = [];
|
|
43118
43335
|
for (const z2 of zBreaks) {
|
|
@@ -43125,7 +43342,7 @@ function projectVerticalDifferenceProfilePlan(subject, clips) {
|
|
|
43125
43342
|
const zMax = uniqueBreaks[idx + 1];
|
|
43126
43343
|
if (zMax - zMin <= EXACT_PROFILE_EPS) continue;
|
|
43127
43344
|
const zMid = (zMin + zMax) / 2;
|
|
43128
|
-
const activeClips = clipped.filter(({ span }) => offsetInRange(zMid,
|
|
43345
|
+
const activeClips = clipped.filter(({ span: span2 }) => offsetInRange(zMid, span2[0], span2[1]));
|
|
43129
43346
|
if (activeClips.length === 0) return cloneProfileCompilePlan(subject.profile);
|
|
43130
43347
|
slabProfiles.push({
|
|
43131
43348
|
kind: "boolean",
|
|
@@ -44098,7 +44315,7 @@ function clusterMeshFaces(shape) {
|
|
|
44098
44315
|
return clusters;
|
|
44099
44316
|
}
|
|
44100
44317
|
function clusterToFaceRef(cluster, name = "") {
|
|
44101
|
-
const
|
|
44318
|
+
const center2 = [
|
|
44102
44319
|
cluster.centroidSum[0] / cluster.count,
|
|
44103
44320
|
cluster.centroidSum[1] / cluster.count,
|
|
44104
44321
|
cluster.centroidSum[2] / cluster.count
|
|
@@ -44107,7 +44324,7 @@ function clusterToFaceRef(cluster, name = "") {
|
|
|
44107
44324
|
return {
|
|
44108
44325
|
name,
|
|
44109
44326
|
normal: cluster.normal,
|
|
44110
|
-
center,
|
|
44327
|
+
center: center2,
|
|
44111
44328
|
planar: true,
|
|
44112
44329
|
uAxis: u2,
|
|
44113
44330
|
vAxis: v
|
|
@@ -44279,7 +44496,7 @@ function representativeFaceForMembers(name, faces, query, semantic) {
|
|
|
44279
44496
|
const cloned = faces.map((face) => cloneFaceRefValue(face));
|
|
44280
44497
|
const first = cloned[0];
|
|
44281
44498
|
const coplanar = facesAreCoplanar(cloned);
|
|
44282
|
-
const
|
|
44499
|
+
const center2 = cloned.reduce(
|
|
44283
44500
|
(acc, face) => [acc[0] + face.center[0], acc[1] + face.center[1], acc[2] + face.center[2]],
|
|
44284
44501
|
[0, 0, 0]
|
|
44285
44502
|
);
|
|
@@ -44287,7 +44504,7 @@ function representativeFaceForMembers(name, faces, query, semantic) {
|
|
|
44287
44504
|
const representative = {
|
|
44288
44505
|
...first,
|
|
44289
44506
|
name,
|
|
44290
|
-
center: [
|
|
44507
|
+
center: [center2[0] / count, center2[1] / count, center2[2] / count],
|
|
44291
44508
|
query: cloneFaceQueryRef(query),
|
|
44292
44509
|
planar: coplanar ? first.planar : false,
|
|
44293
44510
|
uAxis: coplanar ? first.uAxis : void 0,
|
|
@@ -45407,10 +45624,10 @@ function resolveShapeFaceTableInternal(plan, owner) {
|
|
|
45407
45624
|
if (!planeCapQuery) return table;
|
|
45408
45625
|
const baseTable = resolveShapeFaceTable(plan.base);
|
|
45409
45626
|
const centers = Array.from(baseTable.faces.values()).map((face) => face.center);
|
|
45410
|
-
const averageCenter = centers.length > 0 ? centers.reduce((acc,
|
|
45627
|
+
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];
|
|
45411
45628
|
const normal2 = normalizeAxis([plan.normalX, plan.normalY, plan.normalZ]);
|
|
45412
45629
|
const planeDistance = averageCenter[0] * normal2[0] + averageCenter[1] * normal2[1] + averageCenter[2] * normal2[2] - plan.originOffset;
|
|
45413
|
-
const
|
|
45630
|
+
const center2 = [
|
|
45414
45631
|
averageCenter[0] - normal2[0] * planeDistance,
|
|
45415
45632
|
averageCenter[1] - normal2[1] * planeDistance,
|
|
45416
45633
|
averageCenter[2] - normal2[2] * planeDistance
|
|
@@ -45419,7 +45636,7 @@ function resolveShapeFaceTableInternal(plan, owner) {
|
|
|
45419
45636
|
registerFace(table, {
|
|
45420
45637
|
name: "plane-cap",
|
|
45421
45638
|
normal: normal2,
|
|
45422
|
-
center,
|
|
45639
|
+
center: center2,
|
|
45423
45640
|
planar: true,
|
|
45424
45641
|
uAxis: basis.u,
|
|
45425
45642
|
vAxis: basis.v,
|
|
@@ -48319,6 +48536,99 @@ function computeSeatOverTranslation(targetFaceVertices, targetFaceNormal, selfMe
|
|
|
48319
48536
|
sampleCount: targetFaceVertices.length
|
|
48320
48537
|
};
|
|
48321
48538
|
}
|
|
48539
|
+
const UV_ANCHORS = ["top", "bottom", "front", "back", "left", "right"];
|
|
48540
|
+
new Set(UV_ANCHORS);
|
|
48541
|
+
const FACE_FRAME = {
|
|
48542
|
+
front: { u: 0, v: 2 },
|
|
48543
|
+
// -y face: u→x, v→z
|
|
48544
|
+
back: { u: 0, v: 2 },
|
|
48545
|
+
// +y face
|
|
48546
|
+
left: { u: 1, v: 2 },
|
|
48547
|
+
// -x face: u→y, v→z
|
|
48548
|
+
right: { u: 1, v: 2 },
|
|
48549
|
+
// +x face
|
|
48550
|
+
top: { u: 0, v: 1 },
|
|
48551
|
+
// +z face: u→x, v→y
|
|
48552
|
+
bottom: { u: 0, v: 1 }
|
|
48553
|
+
// -z face
|
|
48554
|
+
};
|
|
48555
|
+
const AXIS_INDEX = { x: 0, y: 1, z: 2 };
|
|
48556
|
+
function span(bbox, axis) {
|
|
48557
|
+
return bbox.max[axis] - bbox.min[axis];
|
|
48558
|
+
}
|
|
48559
|
+
function center(bbox) {
|
|
48560
|
+
return [
|
|
48561
|
+
(bbox.min[0] + bbox.max[0]) / 2,
|
|
48562
|
+
(bbox.min[1] + bbox.max[1]) / 2,
|
|
48563
|
+
(bbox.min[2] + bbox.max[2]) / 2
|
|
48564
|
+
];
|
|
48565
|
+
}
|
|
48566
|
+
function autoFitProjection(spec, bbox) {
|
|
48567
|
+
switch (spec.kind) {
|
|
48568
|
+
case "flat": {
|
|
48569
|
+
const frame = FACE_FRAME[spec.onto];
|
|
48570
|
+
const out = { ...spec };
|
|
48571
|
+
if (out.width === void 0) {
|
|
48572
|
+
const w2 = span(bbox, frame.u);
|
|
48573
|
+
if (w2 > 0) {
|
|
48574
|
+
out.width = w2;
|
|
48575
|
+
if (out.offsetU === void 0) out.offsetU = bbox.min[frame.u];
|
|
48576
|
+
}
|
|
48577
|
+
}
|
|
48578
|
+
if (out.height === void 0) {
|
|
48579
|
+
const h = span(bbox, frame.v);
|
|
48580
|
+
if (h > 0) {
|
|
48581
|
+
out.height = h;
|
|
48582
|
+
if (out.offsetV === void 0) out.offsetV = bbox.min[frame.v];
|
|
48583
|
+
}
|
|
48584
|
+
}
|
|
48585
|
+
return out;
|
|
48586
|
+
}
|
|
48587
|
+
case "box": {
|
|
48588
|
+
const out = { ...spec };
|
|
48589
|
+
if (out.size === void 0) {
|
|
48590
|
+
const sx = span(bbox, 0);
|
|
48591
|
+
const sy = span(bbox, 1);
|
|
48592
|
+
const sz = span(bbox, 2);
|
|
48593
|
+
if (sx > 0 && sy > 0 && sz > 0) out.size = [sx, sy, sz];
|
|
48594
|
+
}
|
|
48595
|
+
if (out.origin === void 0) out.origin = center(bbox);
|
|
48596
|
+
return out;
|
|
48597
|
+
}
|
|
48598
|
+
case "cylinder": {
|
|
48599
|
+
const axis = AXIS_INDEX[spec.axis];
|
|
48600
|
+
const out = { ...spec };
|
|
48601
|
+
if (out.height === void 0) {
|
|
48602
|
+
const h = span(bbox, axis);
|
|
48603
|
+
if (h > 0) {
|
|
48604
|
+
out.height = h;
|
|
48605
|
+
if (out.offsetV === void 0) out.offsetV = bbox.min[axis];
|
|
48606
|
+
}
|
|
48607
|
+
}
|
|
48608
|
+
if (out.origin === void 0) out.origin = center(bbox);
|
|
48609
|
+
return out;
|
|
48610
|
+
}
|
|
48611
|
+
case "sphere": {
|
|
48612
|
+
const out = { ...spec };
|
|
48613
|
+
if (out.origin === void 0) out.origin = center(bbox);
|
|
48614
|
+
if (out.radius === void 0) {
|
|
48615
|
+
const c2 = center(bbox);
|
|
48616
|
+
const radius = Math.max(
|
|
48617
|
+
bbox.max[0] - c2[0],
|
|
48618
|
+
bbox.max[1] - c2[1],
|
|
48619
|
+
bbox.max[2] - c2[2]
|
|
48620
|
+
);
|
|
48621
|
+
if (radius > 0) out.radius = radius;
|
|
48622
|
+
}
|
|
48623
|
+
return out;
|
|
48624
|
+
}
|
|
48625
|
+
default:
|
|
48626
|
+
throw new Error(`autoFitProjection: unknown projection kind "${spec.kind}"`);
|
|
48627
|
+
}
|
|
48628
|
+
}
|
|
48629
|
+
function isImageHandle(value) {
|
|
48630
|
+
return typeof value === "object" && value !== null && value.__forgeImage === true;
|
|
48631
|
+
}
|
|
48322
48632
|
const _shapeDimensions = /* @__PURE__ */ new WeakMap();
|
|
48323
48633
|
let _shapeDimensionCounter = 0;
|
|
48324
48634
|
function nextShapeDimensionId() {
|
|
@@ -48691,8 +49001,8 @@ function mergeShapeSourceSpans(sources, target) {
|
|
|
48691
49001
|
records = /* @__PURE__ */ new Map();
|
|
48692
49002
|
_shapeSourceSpans.set(target, records);
|
|
48693
49003
|
}
|
|
48694
|
-
for (const [key2,
|
|
48695
|
-
if (!records.has(key2)) records.set(key2,
|
|
49004
|
+
for (const [key2, span2] of sourceRecords) {
|
|
49005
|
+
if (!records.has(key2)) records.set(key2, span2);
|
|
48696
49006
|
}
|
|
48697
49007
|
}
|
|
48698
49008
|
}
|
|
@@ -49098,7 +49408,7 @@ function annotateTruckPrimitiveAnalyticFaces(plan, topology) {
|
|
|
49098
49408
|
const radiusTop = (source.radiusTop ?? source.radius) * distanceScale;
|
|
49099
49409
|
const origin = transform.point(signedHeight >= 0 ? [0, 0, 0] : [0, 0, signedHeight]);
|
|
49100
49410
|
const sidePointRadius = (Math.abs(radiusBottom) + Math.abs(radiusTop)) / 2;
|
|
49101
|
-
const
|
|
49411
|
+
const center2 = transform.point([sidePointRadius / distanceScale, 0, signedHeight / 2]);
|
|
49102
49412
|
const normal2 = transformedUnitAxis(transform, [1, 0, 0]) ?? [1, 0, 0];
|
|
49103
49413
|
const surface = sameScalar(radiusBottom, radiusTop) ? { kind: "cylinder", origin, axis, radius: radiusBottom, height } : { kind: "cone", origin, axis, radiusBottom, radiusTop, height };
|
|
49104
49414
|
for (const [name, face] of faces) {
|
|
@@ -49109,7 +49419,7 @@ function annotateTruckPrimitiveAnalyticFaces(plan, topology) {
|
|
|
49109
49419
|
upsertAnalyticFace(faces, {
|
|
49110
49420
|
name: "side",
|
|
49111
49421
|
normal: normal2,
|
|
49112
|
-
center,
|
|
49422
|
+
center: center2,
|
|
49113
49423
|
planar: false,
|
|
49114
49424
|
surface
|
|
49115
49425
|
});
|
|
@@ -49119,8 +49429,8 @@ function annotateTruckPrimitiveAnalyticFaces(plan, topology) {
|
|
|
49119
49429
|
if (distanceScale === null) return;
|
|
49120
49430
|
const normal2 = transformedUnitAxis(transform, [1, 0, 0]) ?? [1, 0, 0];
|
|
49121
49431
|
const radius = source.radius * distanceScale;
|
|
49122
|
-
const
|
|
49123
|
-
const surface = { kind: "sphere", center, radius };
|
|
49432
|
+
const center2 = transform.point([0, 0, 0]);
|
|
49433
|
+
const surface = { kind: "sphere", center: center2, radius };
|
|
49124
49434
|
for (const [name, face] of faces) {
|
|
49125
49435
|
if (name === "surface" || /^north-\d+$/.test(name) || /^band-\d+-\d+$/.test(name) || /^south-\d+$/.test(name)) {
|
|
49126
49436
|
faces.set(name, { ...face, surface: cloneFaceSurface(surface) ?? surface });
|
|
@@ -49140,10 +49450,10 @@ function annotateTruckPrimitiveAnalyticFaces(plan, topology) {
|
|
|
49140
49450
|
const axis = transformedUnitAxis(transform, [0, 0, 1]);
|
|
49141
49451
|
const normal2 = transformedUnitAxis(transform, [1, 0, 0]) ?? [1, 0, 0];
|
|
49142
49452
|
if (!axis) return;
|
|
49143
|
-
const
|
|
49453
|
+
const center2 = transform.point([0, 0, 0]);
|
|
49144
49454
|
const surface = {
|
|
49145
49455
|
kind: "torus",
|
|
49146
|
-
center,
|
|
49456
|
+
center: center2,
|
|
49147
49457
|
axis,
|
|
49148
49458
|
majorRadius: source.majorRadius * distanceScale,
|
|
49149
49459
|
minorRadius: source.minorRadius * distanceScale
|
|
@@ -49318,7 +49628,7 @@ function addCompileAliasesToTopology(plan, topology) {
|
|
|
49318
49628
|
["top-rim", [radiusTop, 0, source.height], [0, radiusTop, source.height], [0, 0, source.height], radiusTop, [0, 0, 1]],
|
|
49319
49629
|
["bottom-rim", [source.radius, 0, 0], [0, source.radius, 0], [0, 0, 0], source.radius, [0, 0, -1]]
|
|
49320
49630
|
];
|
|
49321
|
-
for (const [name, start, end,
|
|
49631
|
+
for (const [name, start, end, center2, radius, axis] of semanticEdges2) {
|
|
49322
49632
|
if (topology.edges.has(name)) continue;
|
|
49323
49633
|
const faceName = name === "top-rim" ? "top" : "bottom";
|
|
49324
49634
|
topology.edges.set(name, {
|
|
@@ -49333,7 +49643,7 @@ function addCompileAliasesToTopology(plan, topology) {
|
|
|
49333
49643
|
},
|
|
49334
49644
|
curve: {
|
|
49335
49645
|
kind: "circle",
|
|
49336
|
-
center,
|
|
49646
|
+
center: center2,
|
|
49337
49647
|
axis,
|
|
49338
49648
|
radius,
|
|
49339
49649
|
faceName
|
|
@@ -49527,6 +49837,7 @@ function withBaseDimensions(base, out) {
|
|
|
49527
49837
|
if (baseTopo) _shapeTopology.set(out, cloneTopology(baseTopo));
|
|
49528
49838
|
const baseLabels = cloneFaceLabelMap(_shapeFaceLabels.get(base));
|
|
49529
49839
|
if (baseLabels) _shapeFaceLabels.set(out, baseLabels);
|
|
49840
|
+
if (base.materialProps) out.materialProps = { ...base.materialProps };
|
|
49530
49841
|
copyShapeReferenceMetadata(base, out);
|
|
49531
49842
|
copyShapeSourceSpans(base, out);
|
|
49532
49843
|
return setShapeCompilePlanInternal(out, getShapeCompilePlanInternal(base));
|
|
@@ -49615,6 +49926,30 @@ function createOwnedTopologyRewritePlan(plan, operation2, buildPropagation) {
|
|
|
49615
49926
|
const owner = createShapeQueryOwner(operation2);
|
|
49616
49927
|
return wrapShapeCompilePlanWithQueryOwner(attachTopologyRewritePropagation(plan, buildPropagation(owner)), owner);
|
|
49617
49928
|
}
|
|
49929
|
+
const KNOWN_PROJECTION_KINDS = /* @__PURE__ */ new Set(["flat", "cylinder", "sphere", "box"]);
|
|
49930
|
+
function validateProjectionFinite(spec) {
|
|
49931
|
+
switch (spec.kind) {
|
|
49932
|
+
case "flat":
|
|
49933
|
+
if (spec.width !== void 0) requireFiniteNumber(spec.width, "wrapTexture() flat width");
|
|
49934
|
+
if (spec.height !== void 0) requireFiniteNumber(spec.height, "wrapTexture() flat height");
|
|
49935
|
+
if (spec.offsetU !== void 0) requireFiniteNumber(spec.offsetU, "wrapTexture() flat offsetU");
|
|
49936
|
+
if (spec.offsetV !== void 0) requireFiniteNumber(spec.offsetV, "wrapTexture() flat offsetV");
|
|
49937
|
+
break;
|
|
49938
|
+
case "box":
|
|
49939
|
+
if (spec.size !== void 0) requireFiniteVec3$1(spec.size, "wrapTexture() box size");
|
|
49940
|
+
if (spec.origin !== void 0) requireFiniteVec3$1(spec.origin, "wrapTexture() box origin");
|
|
49941
|
+
break;
|
|
49942
|
+
case "cylinder":
|
|
49943
|
+
if (spec.height !== void 0) requireFiniteNumber(spec.height, "wrapTexture() cylinder height");
|
|
49944
|
+
if (spec.offsetV !== void 0) requireFiniteNumber(spec.offsetV, "wrapTexture() cylinder offsetV");
|
|
49945
|
+
if (spec.origin !== void 0) requireFiniteVec3$1(spec.origin, "wrapTexture() cylinder origin");
|
|
49946
|
+
break;
|
|
49947
|
+
case "sphere":
|
|
49948
|
+
if (spec.origin !== void 0) requireFiniteVec3$1(spec.origin, "wrapTexture() sphere origin");
|
|
49949
|
+
if (spec.radius !== void 0) requireFiniteNumber(spec.radius, "wrapTexture() sphere radius");
|
|
49950
|
+
break;
|
|
49951
|
+
}
|
|
49952
|
+
}
|
|
49618
49953
|
function parseReferencePath(path) {
|
|
49619
49954
|
const trimmed = path.trim();
|
|
49620
49955
|
if (!trimmed) throw new Error("Shape.ref() requires a non-empty path.");
|
|
@@ -49960,7 +50295,7 @@ class Shape {
|
|
|
49960
50295
|
*
|
|
49961
50296
|
* Use `.color()` to set the base diffuse color; `.material()` controls how that color behaves
|
|
49962
50297
|
* under light (metalness, roughness, clearcoat) and can add emissive glow independent of
|
|
49963
|
-
* lighting.
|
|
50298
|
+
* lighting.
|
|
49964
50299
|
*
|
|
49965
50300
|
* **Example**
|
|
49966
50301
|
*
|
|
@@ -49992,6 +50327,61 @@ class Shape {
|
|
|
49992
50327
|
out.materialProps = { ...this.materialProps ?? {}, ...props };
|
|
49993
50328
|
return out;
|
|
49994
50329
|
}
|
|
50330
|
+
/**
|
|
50331
|
+
* Wrap an imported bitmap image around this shape using a projection.
|
|
50332
|
+
*
|
|
50333
|
+
* **Details**
|
|
50334
|
+
*
|
|
50335
|
+
* The `image` comes from `Import.image('path.png')`; the `projection` is one of the `Wrap.*`
|
|
50336
|
+
* helpers — `Wrap.flat({ onto: 'top' })` lays it flat on a face, `Wrap.aroundCylinder({ axis: 'z' })`
|
|
50337
|
+
* wraps it like a can label, `Wrap.onSphere()` maps it like a globe, and `Wrap.box()` cube-maps
|
|
50338
|
+
* it onto the six sides.
|
|
50339
|
+
*
|
|
50340
|
+
* By default the image **auto-fits** the shape — one copy across the relevant extent, so no
|
|
50341
|
+
* `width`/`height`/`size` is needed (pass them only to override). The (u,v) is derived from each
|
|
50342
|
+
* vertex's final world position, so the image stays glued to the surface through transforms and
|
|
50343
|
+
* boolean cuts with no UV layout to maintain — apply `wrapTexture` *after* positioning the shape.
|
|
50344
|
+
* Returns a new Shape; the original is unchanged.
|
|
50345
|
+
*
|
|
50346
|
+
* **Example**
|
|
50347
|
+
*
|
|
50348
|
+
* ```js
|
|
50349
|
+
* const logo = Import.image('./logo.png');
|
|
50350
|
+
* box(80, 80, 10).wrapTexture(logo, Wrap.flat({ onto: 'top' })); // auto-fits the face
|
|
50351
|
+
*
|
|
50352
|
+
* const label = Import.image('./label.jpg');
|
|
50353
|
+
* cylinder(60, 20).wrapTexture(label, Wrap.aroundCylinder({ axis: 'z' })); // wraps the side
|
|
50354
|
+
* ```
|
|
50355
|
+
*
|
|
50356
|
+
* @param image - An imported bitmap from `Import.image(...)`
|
|
50357
|
+
* @param projection - A projection spec from `Wrap.flat` / `Wrap.aroundCylinder` / `Wrap.onSphere` / `Wrap.box`
|
|
50358
|
+
* @returns A new Shape with the projected texture recorded in its material properties
|
|
50359
|
+
* @category Materials
|
|
50360
|
+
*/
|
|
50361
|
+
wrapTexture(image, projection) {
|
|
50362
|
+
if (!isImageHandle(image)) {
|
|
50363
|
+
throw new Error("wrapTexture() expects an image from Import.image(...) as its first argument");
|
|
50364
|
+
}
|
|
50365
|
+
if (!projection || typeof projection !== "object" || !KNOWN_PROJECTION_KINDS.has(projection.kind)) {
|
|
50366
|
+
throw new Error(
|
|
50367
|
+
`wrapTexture() expects a projection from Wrap.flat / Wrap.aroundCylinder / Wrap.onSphere / Wrap.box, got ${JSON.stringify(projection == null ? void 0 : projection.kind)}`
|
|
50368
|
+
);
|
|
50369
|
+
}
|
|
50370
|
+
const bbox = this.boundingBox();
|
|
50371
|
+
const filled = autoFitProjection(projection, bbox);
|
|
50372
|
+
validateProjectionFinite(filled);
|
|
50373
|
+
const out = this.clone();
|
|
50374
|
+
out.materialProps = {
|
|
50375
|
+
...this.materialProps ?? {},
|
|
50376
|
+
texture: {
|
|
50377
|
+
image: image.dataUri,
|
|
50378
|
+
projection: filled,
|
|
50379
|
+
imageWidth: image.width,
|
|
50380
|
+
imageHeight: image.height
|
|
50381
|
+
}
|
|
50382
|
+
};
|
|
50383
|
+
return out;
|
|
50384
|
+
}
|
|
49995
50385
|
/** Return a new Shape wrapper for explicit duplication in scripts. */
|
|
49996
50386
|
clone() {
|
|
49997
50387
|
const out = withCopiedDimensions(this, new Shape(getShapeRuntimeBackendInternal(this).clone(), this.colorHex));
|
|
@@ -50519,12 +50909,12 @@ class Shape {
|
|
|
50519
50909
|
scale(v) {
|
|
50520
50910
|
const scale2 = requireNonZeroFiniteScale3(v, "Shape.scale() scale");
|
|
50521
50911
|
const bb = this.boundingBox();
|
|
50522
|
-
const
|
|
50912
|
+
const center2 = [
|
|
50523
50913
|
(bb.min[0] + bb.max[0]) / 2,
|
|
50524
50914
|
(bb.min[1] + bb.max[1]) / 2,
|
|
50525
50915
|
(bb.min[2] + bb.max[2]) / 2
|
|
50526
50916
|
];
|
|
50527
|
-
return this.scaleAround(
|
|
50917
|
+
return this.scaleAround(center2, scale2);
|
|
50528
50918
|
}
|
|
50529
50919
|
/** Scale the shape uniformly or per-axis from an explicit pivot point. */
|
|
50530
50920
|
scaleAround(pivot, v) {
|
|
@@ -50558,12 +50948,12 @@ class Shape {
|
|
|
50558
50948
|
/** Mirror across a plane through the shape's bounding box center, defined by its normal vector. */
|
|
50559
50949
|
mirror(normal2) {
|
|
50560
50950
|
const bb = this.boundingBox();
|
|
50561
|
-
const
|
|
50951
|
+
const center2 = [
|
|
50562
50952
|
(bb.min[0] + bb.max[0]) / 2,
|
|
50563
50953
|
(bb.min[1] + bb.max[1]) / 2,
|
|
50564
50954
|
(bb.min[2] + bb.max[2]) / 2
|
|
50565
50955
|
];
|
|
50566
|
-
return this.mirrorThrough(
|
|
50956
|
+
return this.mirrorThrough(center2, normal2);
|
|
50567
50957
|
}
|
|
50568
50958
|
/** Mirror across a plane through an explicit point, defined by its normal vector. */
|
|
50569
50959
|
mirrorThrough(point2, normal2) {
|
|
@@ -51323,7 +51713,8 @@ class FrozenShapeBackend {
|
|
|
51323
51713
|
edgePositions: this._data.geometryEdgePositions,
|
|
51324
51714
|
triangleFaceIds: this._data.geometryTriangleFaceIds,
|
|
51325
51715
|
faceIdNames: this._data.geometryFaceIdNames,
|
|
51326
|
-
hasSmoothNormals: this._data.hasSmoothNormals ?? false
|
|
51716
|
+
hasSmoothNormals: this._data.hasSmoothNormals ?? false,
|
|
51717
|
+
uvs: this._data.geometryUvs
|
|
51327
51718
|
};
|
|
51328
51719
|
}
|
|
51329
51720
|
getMeshReconstructionData() {
|
|
@@ -51493,9 +51884,18 @@ class FrozenShape extends Shape {
|
|
|
51493
51884
|
const EDGE_THRESHOLD_DOT = Math.cos(Math.PI / 180);
|
|
51494
51885
|
const SMOOTH_THRESHOLD_DOT = Math.cos(30 * Math.PI / 180);
|
|
51495
51886
|
function computeGeometryArrays(mesh, options = {}) {
|
|
51496
|
-
const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals } = mesh;
|
|
51887
|
+
const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals, cornerNormals } = mesh;
|
|
51888
|
+
if (numProp !== NUM_PROP_POSITION_ONLY && numProp !== NUM_PROP_WITH_NORMAL && numProp !== NUM_PROP_WITH_UV) {
|
|
51889
|
+
throw new Error(
|
|
51890
|
+
`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.`
|
|
51891
|
+
);
|
|
51892
|
+
}
|
|
51893
|
+
const hasStoredNormals = numProp === NUM_PROP_WITH_NORMAL || numProp === NUM_PROP_WITH_UV;
|
|
51894
|
+
const hasStoredUvs = numProp === NUM_PROP_WITH_UV;
|
|
51895
|
+
const useCornerNormals = !!cornerNormals && cornerNormals.length === triCount * 9;
|
|
51497
51896
|
const positions = new Float32Array(triCount * 9);
|
|
51498
51897
|
const normals = new Float32Array(triCount * 9);
|
|
51898
|
+
const uvs = hasStoredUvs ? new Float32Array(triCount * 6) : void 0;
|
|
51499
51899
|
const faceNx = new Float32Array(triCount);
|
|
51500
51900
|
const faceNy = new Float32Array(triCount);
|
|
51501
51901
|
const faceNz = new Float32Array(triCount);
|
|
@@ -51525,7 +51925,18 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
51525
51925
|
positions[o + 6] = cx;
|
|
51526
51926
|
positions[o + 7] = cy;
|
|
51527
51927
|
positions[o + 8] = cz;
|
|
51528
|
-
if (
|
|
51928
|
+
if (uvs) {
|
|
51929
|
+
const u2 = t * 6;
|
|
51930
|
+
uvs[u2] = vertProperties[i0 * numProp + UV_OFFSET];
|
|
51931
|
+
uvs[u2 + 1] = vertProperties[i0 * numProp + UV_OFFSET + 1];
|
|
51932
|
+
uvs[u2 + 2] = vertProperties[i1 * numProp + UV_OFFSET];
|
|
51933
|
+
uvs[u2 + 3] = vertProperties[i1 * numProp + UV_OFFSET + 1];
|
|
51934
|
+
uvs[u2 + 4] = vertProperties[i2 * numProp + UV_OFFSET];
|
|
51935
|
+
uvs[u2 + 5] = vertProperties[i2 * numProp + UV_OFFSET + 1];
|
|
51936
|
+
}
|
|
51937
|
+
if (useCornerNormals) {
|
|
51938
|
+
for (let k2 = 0; k2 < 9; k2++) normals[o + k2] = cornerNormals[o + k2];
|
|
51939
|
+
} else if (vertNormals) {
|
|
51529
51940
|
normals[o] = vertNormals[i0 * 3];
|
|
51530
51941
|
normals[o + 1] = vertNormals[i0 * 3 + 1];
|
|
51531
51942
|
normals[o + 2] = vertNormals[i0 * 3 + 2];
|
|
@@ -51535,13 +51946,13 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
51535
51946
|
normals[o + 6] = vertNormals[i2 * 3];
|
|
51536
51947
|
normals[o + 7] = vertNormals[i2 * 3 + 1];
|
|
51537
51948
|
normals[o + 8] = vertNormals[i2 * 3 + 2];
|
|
51538
|
-
} else if (
|
|
51949
|
+
} else if (hasStoredNormals) {
|
|
51539
51950
|
const corners = [i0, i1, i2];
|
|
51540
51951
|
for (let v = 0; v < 3; v++) {
|
|
51541
51952
|
const base = corners[v] * numProp;
|
|
51542
|
-
const nx = vertProperties[base +
|
|
51543
|
-
const ny = vertProperties[base +
|
|
51544
|
-
const nz = vertProperties[base +
|
|
51953
|
+
const nx = vertProperties[base + NORMAL_OFFSET];
|
|
51954
|
+
const ny = vertProperties[base + NORMAL_OFFSET + 1];
|
|
51955
|
+
const nz = vertProperties[base + NORMAL_OFFSET + 2];
|
|
51545
51956
|
const oc = o + v * 3;
|
|
51546
51957
|
if (nx * nx + ny * ny + nz * nz > 1e-12) {
|
|
51547
51958
|
normals[oc] = nx;
|
|
@@ -51568,7 +51979,7 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
51568
51979
|
faceNy[t] = fny;
|
|
51569
51980
|
faceNz[t] = fnz;
|
|
51570
51981
|
}
|
|
51571
|
-
if (!
|
|
51982
|
+
if (!useCornerNormals && !vertNormals && !hasStoredNormals && triCount > 0) {
|
|
51572
51983
|
computeAutoSmoothNormals(
|
|
51573
51984
|
triVerts,
|
|
51574
51985
|
vertProperties,
|
|
@@ -51587,7 +51998,8 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
51587
51998
|
positions,
|
|
51588
51999
|
normals,
|
|
51589
52000
|
edgePositions,
|
|
51590
|
-
hasSmoothNormals: triCount > 0
|
|
52001
|
+
hasSmoothNormals: triCount > 0,
|
|
52002
|
+
uvs
|
|
51591
52003
|
};
|
|
51592
52004
|
}
|
|
51593
52005
|
function computeAutoSmoothNormals(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, normals, mergeFromVert, mergeToVert) {
|
|
@@ -51722,10 +52134,11 @@ function attachKernelFaceMetadata(geometry, triangleFaceIds, faceIdNames) {
|
|
|
51722
52134
|
}
|
|
51723
52135
|
function shapeToGeometry(shape) {
|
|
51724
52136
|
if (shape instanceof FrozenShape) {
|
|
51725
|
-
const { positions, normals, edgePositions, triangleFaceIds, faceIdNames, hasSmoothNormals } = shape.getPrecomputedGeometry();
|
|
52137
|
+
const { positions, normals, edgePositions, triangleFaceIds, faceIdNames, hasSmoothNormals, uvs } = shape.getPrecomputedGeometry();
|
|
51726
52138
|
const solid = new BufferGeometry();
|
|
51727
52139
|
solid.setAttribute("position", new BufferAttribute(positions, 3));
|
|
51728
52140
|
solid.setAttribute("normal", new BufferAttribute(normals, 3));
|
|
52141
|
+
if (uvs) solid.setAttribute("uv", new BufferAttribute(uvs, 2));
|
|
51729
52142
|
attachKernelFaceMetadata(solid, triangleFaceIds, faceIdNames);
|
|
51730
52143
|
const edges = new BufferGeometry();
|
|
51731
52144
|
edges.setAttribute("position", new BufferAttribute(edgePositions, 3));
|
|
@@ -51748,18 +52161,20 @@ function shapeToGeometryFallback(shape) {
|
|
|
51748
52161
|
} catch {
|
|
51749
52162
|
mesh = shape.getMesh();
|
|
51750
52163
|
}
|
|
51751
|
-
const { positions, normals, edgePositions, hasSmoothNormals } = computeGeometryArrays({
|
|
52164
|
+
const { positions, normals, edgePositions, hasSmoothNormals, uvs } = computeGeometryArrays({
|
|
51752
52165
|
numProp: mesh.numProp,
|
|
51753
52166
|
numTri: mesh.numTri,
|
|
51754
52167
|
triVerts: mesh.triVerts,
|
|
51755
52168
|
vertProperties: mesh.vertProperties,
|
|
51756
52169
|
mergeFromVert: mesh.mergeFromVert,
|
|
51757
52170
|
mergeToVert: mesh.mergeToVert,
|
|
51758
|
-
vertNormals
|
|
52171
|
+
vertNormals,
|
|
52172
|
+
cornerNormals: mesh.cornerNormals && mesh.cornerNormals.length === mesh.numTri * 9 ? mesh.cornerNormals : void 0
|
|
51759
52173
|
});
|
|
51760
52174
|
const solid = new BufferGeometry();
|
|
51761
52175
|
solid.setAttribute("position", new BufferAttribute(positions, 3));
|
|
51762
52176
|
solid.setAttribute("normal", new BufferAttribute(normals, 3));
|
|
52177
|
+
if (uvs) solid.setAttribute("uv", new BufferAttribute(uvs, 2));
|
|
51763
52178
|
attachKernelFaceMetadata(solid, mesh.faceID, mesh.faceIdNames);
|
|
51764
52179
|
const edges = new BufferGeometry();
|
|
51765
52180
|
edges.setAttribute("position", new BufferAttribute(edgePositions, 3));
|