forgecad 0.10.3 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{AdminPage-CK7ObBz3.js → AdminPage-B3L3W1Uo.js} +1 -1
- package/dist/assets/{BenchmarkPage-Ds7Z2doN.js → BenchmarkPage-DXKVXMrJ.js} +2 -2
- package/dist/assets/{BlogPage-DlPbpt6A.js → BlogPage-B7BWxOCg.js} +1 -1
- package/dist/assets/{DocsPage-vZb3b3Y0.js → DocsPage-BPGGwht1.js} +28 -43
- package/dist/assets/{EditorApp-HLoKfe15.js → EditorApp-BWUGCdD5.js} +49 -16
- package/dist/assets/{EmbedViewer--KnqBKrJ.js → EmbedViewer-DygByZS2.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-C_LssmnA.js → LandingPageProofDriven-BoVE7JGY.js} +54 -36
- package/dist/assets/{LegalPage-DGsyo4n1.js → LegalPage-Din8wv8d.js} +2 -2
- package/dist/assets/{PricingPage-BOE27B-R.js → PricingPage-C2PMzmDc.js} +2 -2
- package/dist/assets/{SettingsPage-f47cnk39.js → SettingsPage-BlJDCRe8.js} +1 -1
- package/dist/assets/{app-D6ccu2Xx.js → app-BsRYSfxY.js} +238 -3714
- package/dist/assets/{backendInit-DbTkQN9J.js → backendInit-6C0DLgH0.js} +5972 -1566
- package/dist/assets/cli/{render-BsngirjC.js → render-XXol_ET7.js} +724 -112
- package/dist/assets/{constructionHistoryWorker-PCwXrTDB.js → constructionHistoryWorker-cTHWRJEi.js} +528 -252
- package/dist/assets/{evalWorker-CS63PfZu.js → evalWorker-BssDYW9u.js} +1453 -902
- package/dist/assets/{inspectWorker-Y4cOzNyA.js → inspectWorker-ymhBV4Ll.js} +2635 -1024
- package/dist/assets/{jointPose-AMvCywzS.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-Crd_F2qx.js → manifold-B_7QXpGB.js} +1 -1
- package/dist/assets/{manifold-k2kRcc85.js → manifold-CNShmpEJ.js} +1 -1
- package/dist/assets/{manifold-CBry38ly.js → manifold-CYlIm-M6.js} +2 -2
- package/dist/assets/{reportWorker-CWvn0CEv.js → reportWorker-Cb5eyM7D.js} +1407 -892
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +17 -15
- package/dist/docs-raw/component-model.md +2 -2
- package/dist/docs-raw/generated/concepts.md +5 -1
- package/dist/docs-raw/generated/core.md +26 -0
- package/dist/docs-raw/generated/runtime-names.md +1 -1
- package/dist/docs-raw/guides/inspection-bundles.md +1 -1
- package/dist/docs-raw/simulation-workflow.md +1 -1
- package/dist/docs-raw/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
- package/dist/docs-raw/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
- 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/docs-raw/skills/{forgecad-3d-reconstruction.md → 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-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
- package/dist/docs-raw/skills/index.md +9 -12
- package/dist/index.html +9 -9
- package/dist/llms.txt +7 -7
- package/dist/sitemap.xml +16 -16
- package/dist-cli/{check-compiler-HPF2T2FS.js → check-compiler-4RPB6SB5.js} +1 -1
- package/dist-cli/{check-query-propagation-HYSLTXAB.js → check-query-propagation-KN3DFQTX.js} +1 -1
- package/dist-cli/{chunk-WLUKAW3H.js → chunk-UHBRMYA6.js} +28802 -28152
- package/dist-cli/forgecad.js +660 -9
- package/dist-skill/CONTEXT.md +27 -1
- package/dist-skill/docs/generated/core.md +26 -0
- package/dist-skill/docs/generated/runtime-names.md +1 -1
- package/dist-skill/docs/guides/inspection-bundles.md +1 -1
- package/dist-skill/library/README.md +9 -12
- package/dist-skill/library/{forgecad-make-a-model → forgecad-build-model}/SKILL.md +16 -6
- package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/SKILL.md +4 -4
- package/dist-skill/library/{forgecad-spec-by-walking-through-it → 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-mujoco-verify → forgecad-verify-mujoco}/SKILL.md +4 -4
- package/dist-skill/website/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
- package/dist-skill/website/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
- 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-skill/website/skills/{forgecad-3d-reconstruction.md → 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-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
- package/dist-skill/website/skills/index.md +9 -12
- package/examples/api/texture-projection.forge.js +75 -0
- package/examples/assets/uv-grid.png +0 -0
- package/package.json +1 -1
- 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-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-image-replicator/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-model-grader/agents/openai.yaml +0 -4
- 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-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-spec-by-walking-through-it → 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-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/scripts/mujoco_verify.py +0 -0
package/dist/assets/{constructionHistoryWorker-PCwXrTDB.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,15 +11435,15 @@ function basisFuns(span, u2, degree, knots) {
|
|
|
11427
11435
|
}
|
|
11428
11436
|
return N;
|
|
11429
11437
|
}
|
|
11430
|
-
function basisFunsDeriv(
|
|
11438
|
+
function basisFunsDeriv(span2, u2, degree, knots, nDeriv) {
|
|
11431
11439
|
const ndu = Array.from({ length: degree + 1 }, () => new Array(degree + 1).fill(0));
|
|
11432
11440
|
const a2 = Array.from({ length: 2 }, () => new Array(degree + 1).fill(0));
|
|
11433
11441
|
const left = new Array(degree + 1);
|
|
11434
11442
|
const right = new Array(degree + 1);
|
|
11435
11443
|
ndu[0][0] = 1;
|
|
11436
11444
|
for (let j = 1; j <= degree; j++) {
|
|
11437
|
-
left[j] = u2 - knots[
|
|
11438
|
-
right[j] = knots[
|
|
11445
|
+
left[j] = u2 - knots[span2 + 1 - j];
|
|
11446
|
+
right[j] = knots[span2 + j] - u2;
|
|
11439
11447
|
let saved = 0;
|
|
11440
11448
|
for (let r2 = 0; r2 < j; r2++) {
|
|
11441
11449
|
ndu[j][r2] = right[r2 + 1] + left[j - r2];
|
|
@@ -11488,11 +11496,11 @@ function basisFunsDeriv(span, u2, degree, knots, nDeriv) {
|
|
|
11488
11496
|
}
|
|
11489
11497
|
function deBoor3D(controlPoints, weights, knots, degree, u2) {
|
|
11490
11498
|
const n = controlPoints.length;
|
|
11491
|
-
const
|
|
11492
|
-
const N = basisFuns(
|
|
11499
|
+
const span2 = findSpan(n, degree, u2, knots);
|
|
11500
|
+
const N = basisFuns(span2, u2, degree, knots);
|
|
11493
11501
|
let wx = 0, wy = 0, wz = 0, wSum = 0;
|
|
11494
11502
|
for (let j = 0; j <= degree; j++) {
|
|
11495
|
-
const idx =
|
|
11503
|
+
const idx = span2 - degree + j;
|
|
11496
11504
|
const w2 = weights[idx] * N[j];
|
|
11497
11505
|
wx += w2 * controlPoints[idx][0];
|
|
11498
11506
|
wy += w2 * controlPoints[idx][1];
|
|
@@ -11504,11 +11512,11 @@ function deBoor3D(controlPoints, weights, knots, degree, u2) {
|
|
|
11504
11512
|
}
|
|
11505
11513
|
function deBoor2D(controlPoints, weights, knots, degree, u2) {
|
|
11506
11514
|
const n = controlPoints.length;
|
|
11507
|
-
const
|
|
11508
|
-
const N = basisFuns(
|
|
11515
|
+
const span2 = findSpan(n, degree, u2, knots);
|
|
11516
|
+
const N = basisFuns(span2, u2, degree, knots);
|
|
11509
11517
|
let wx = 0, wy = 0, wSum = 0;
|
|
11510
11518
|
for (let j = 0; j <= degree; j++) {
|
|
11511
|
-
const idx =
|
|
11519
|
+
const idx = span2 - degree + j;
|
|
11512
11520
|
const w2 = weights[idx] * N[j];
|
|
11513
11521
|
wx += w2 * controlPoints[idx][0];
|
|
11514
11522
|
wy += w2 * controlPoints[idx][1];
|
|
@@ -15472,6 +15480,23 @@ function analyzeNodeUV(node, toLocal) {
|
|
|
15472
15480
|
function clampUnit(v) {
|
|
15473
15481
|
return v < -1 ? -1 : v > 1 ? 1 : v;
|
|
15474
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
|
+
}
|
|
15475
15500
|
function compileUVFunction(analysis) {
|
|
15476
15501
|
if (analysis.mode === "triplanar") return null;
|
|
15477
15502
|
const toLocal = analysis.toLocal;
|
|
@@ -15480,19 +15505,14 @@ function compileUVFunction(analysis) {
|
|
|
15480
15505
|
const R = analysis.radius;
|
|
15481
15506
|
return (p2) => {
|
|
15482
15507
|
const lp = toLocal(p2);
|
|
15483
|
-
|
|
15484
|
-
const len = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1] + lp[2] * lp[2]);
|
|
15485
|
-
const v = acos(clampUnit(lp[2] / (len || 1))) * R;
|
|
15486
|
-
return [u2, v];
|
|
15508
|
+
return sphereUVLocal(lp[0], lp[1], lp[2], R);
|
|
15487
15509
|
};
|
|
15488
15510
|
}
|
|
15489
15511
|
case "cylinder": {
|
|
15490
15512
|
const r = analysis.radius;
|
|
15491
15513
|
return (p2) => {
|
|
15492
15514
|
const lp = toLocal(p2);
|
|
15493
|
-
|
|
15494
|
-
const v = lp[2];
|
|
15495
|
-
return [u2, v];
|
|
15515
|
+
return cylinderUVLocal(lp[0], lp[1], lp[2], r);
|
|
15496
15516
|
};
|
|
15497
15517
|
}
|
|
15498
15518
|
case "torus": {
|
|
@@ -15500,10 +15520,7 @@ function compileUVFunction(analysis) {
|
|
|
15500
15520
|
const r = analysis.radius;
|
|
15501
15521
|
return (p2) => {
|
|
15502
15522
|
const lp = toLocal(p2);
|
|
15503
|
-
|
|
15504
|
-
const xyDist = sqrt$3(lp[0] * lp[0] + lp[1] * lp[1]) - R;
|
|
15505
|
-
const v = atan2(lp[2], xyDist) * r;
|
|
15506
|
-
return [u2, v];
|
|
15523
|
+
return torusUVLocal(lp[0], lp[1], lp[2], R, r);
|
|
15507
15524
|
};
|
|
15508
15525
|
}
|
|
15509
15526
|
}
|
|
@@ -15814,8 +15831,8 @@ function sdProfileRevolve$1(profileFn, degrees, x2, y2, z2) {
|
|
|
15814
15831
|
const profileDistance = profileFn(radial, z2);
|
|
15815
15832
|
if (abs(degrees) >= 360) return profileDistance;
|
|
15816
15833
|
const sweep = abs(degrees) * DEG$1;
|
|
15817
|
-
const
|
|
15818
|
-
let angle = Math.atan2(y2, x2) -
|
|
15834
|
+
const center2 = degrees * DEG$1 * 0.5;
|
|
15835
|
+
let angle = Math.atan2(y2, x2) - center2;
|
|
15819
15836
|
while (angle <= -PI$1) angle += PI$1 * 2;
|
|
15820
15837
|
while (angle > PI$1) angle -= PI$1 * 2;
|
|
15821
15838
|
const half = sweep * 0.5;
|
|
@@ -15833,9 +15850,9 @@ function smax$1(a2, b, k2) {
|
|
|
15833
15850
|
function repeatCoord$1(v, spacing, count) {
|
|
15834
15851
|
if (spacing <= 0) return v;
|
|
15835
15852
|
if (count > 0) {
|
|
15836
|
-
const
|
|
15837
|
-
const index2 = clamp$3(Math.round(v / spacing +
|
|
15838
|
-
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;
|
|
15839
15856
|
}
|
|
15840
15857
|
return v - spacing * Math.round(v / spacing);
|
|
15841
15858
|
}
|
|
@@ -16761,8 +16778,8 @@ function sdProfileRevolve(b, node, x2, y2, z2) {
|
|
|
16761
16778
|
const profileDistance = profilePolygonsSdf(b, node.polygons, radial, z2);
|
|
16762
16779
|
if (Math.abs(node.degrees) >= 360) return profileDistance;
|
|
16763
16780
|
const sweep = Math.abs(node.degrees) * DEG;
|
|
16764
|
-
const
|
|
16765
|
-
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));
|
|
16766
16783
|
const wrapped = b.sub(angle, b.mul(b.constant(TAU), b.round(b.div(angle, b.constant(TAU)))));
|
|
16767
16784
|
const absAngle = b.abs(wrapped);
|
|
16768
16785
|
const half = sweep * 0.5;
|
|
@@ -16829,9 +16846,9 @@ function select01(b, selector, ifOne, ifZero) {
|
|
|
16829
16846
|
function repeatCoord(b, v, spacing, count) {
|
|
16830
16847
|
if (spacing <= 0) return v;
|
|
16831
16848
|
if (count > 0) {
|
|
16832
|
-
const
|
|
16833
|
-
const index2 = clampSlot(b, b.round(b.add(b.div(v, b.constant(spacing)), b.constant(
|
|
16834
|
-
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)));
|
|
16835
16852
|
}
|
|
16836
16853
|
return b.sub(v, b.mul(b.constant(spacing), b.round(b.div(v, b.constant(spacing)))));
|
|
16837
16854
|
}
|
|
@@ -17887,49 +17904,55 @@ function signedArea2D(loop) {
|
|
|
17887
17904
|
}
|
|
17888
17905
|
return area2 * 0.5;
|
|
17889
17906
|
}
|
|
17890
|
-
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;
|
|
17891
17930
|
let inside = false;
|
|
17892
|
-
|
|
17893
|
-
|
|
17894
|
-
const
|
|
17895
|
-
const
|
|
17896
|
-
const
|
|
17897
|
-
|
|
17898
|
-
|
|
17899
|
-
|
|
17900
|
-
|
|
17901
|
-
|
|
17902
|
-
|
|
17903
|
-
|
|
17904
|
-
|
|
17905
|
-
const
|
|
17906
|
-
|
|
17907
|
-
const t = den < 1e-12 ? 0 : clamp$1((apx * abx + apy * aby) / den, 0, 1);
|
|
17908
|
-
const qx = a2[0] + abx * t;
|
|
17909
|
-
const qy = a2[1] + aby * t;
|
|
17910
|
-
const dx = point2[0] - qx;
|
|
17911
|
-
const dy = point2[1] - qy;
|
|
17912
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
17913
|
-
}
|
|
17914
|
-
function loopSignedDistance(point2, loop) {
|
|
17915
|
-
let minDist = Infinity;
|
|
17916
|
-
for (let index2 = 0; index2 < loop.length; index2 += 1) {
|
|
17917
|
-
const a2 = loop[index2];
|
|
17918
|
-
const b = loop[(index2 + 1) % loop.length];
|
|
17919
|
-
minDist = Math.min(minDist, pointSegDist2D(point2, a2, b));
|
|
17920
|
-
}
|
|
17921
|
-
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;
|
|
17922
17946
|
}
|
|
17923
17947
|
function compilePolygonsSdf(polygons) {
|
|
17924
|
-
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])));
|
|
17925
17949
|
if (loops.length === 0) {
|
|
17926
17950
|
return () => -1;
|
|
17927
17951
|
}
|
|
17928
17952
|
return (x2, y2) => {
|
|
17929
|
-
const point2 = [x2, y2];
|
|
17930
17953
|
let field2 = -Infinity;
|
|
17931
17954
|
for (const loop of loops) {
|
|
17932
|
-
const loopField =
|
|
17955
|
+
const loopField = loopSignedDistanceScalar(x2, y2, loop);
|
|
17933
17956
|
field2 = loop.area >= 0 ? Math.max(field2, loopField) : Math.min(field2, -loopField);
|
|
17934
17957
|
}
|
|
17935
17958
|
return field2;
|
|
@@ -18087,24 +18110,40 @@ function interpolateSweepFrame(segment, alpha, origin) {
|
|
|
18087
18110
|
function findNearestSweepPoint(point2, segments) {
|
|
18088
18111
|
let bestIndex = 0;
|
|
18089
18112
|
let bestAlpha = 0;
|
|
18090
|
-
let
|
|
18113
|
+
let bestAlong = 0;
|
|
18091
18114
|
let bestDist2 = Infinity;
|
|
18115
|
+
const px = point2[0];
|
|
18116
|
+
const py = point2[1];
|
|
18117
|
+
const pz = point2[2];
|
|
18092
18118
|
for (let index2 = 0; index2 < segments.length; index2 += 1) {
|
|
18093
18119
|
const segment = segments[index2];
|
|
18094
|
-
const
|
|
18095
|
-
const
|
|
18096
|
-
const
|
|
18097
|
-
const
|
|
18098
|
-
const
|
|
18099
|
-
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;
|
|
18100
18134
|
if (dist2 < bestDist2) {
|
|
18101
18135
|
bestIndex = index2;
|
|
18102
|
-
|
|
18103
|
-
|
|
18136
|
+
bestAlong = along;
|
|
18137
|
+
bestAlpha = segment.len > 1e-9 ? along / segment.len : 0;
|
|
18104
18138
|
bestDist2 = dist2;
|
|
18105
18139
|
}
|
|
18106
18140
|
}
|
|
18107
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
|
+
];
|
|
18108
18147
|
return {
|
|
18109
18148
|
segmentIndex: bestIndex,
|
|
18110
18149
|
segment: bestSegment,
|
|
@@ -19093,8 +19132,8 @@ function stitchSingleLoopLoft(loops, heights, wasm, options) {
|
|
|
19093
19132
|
return null;
|
|
19094
19133
|
}
|
|
19095
19134
|
}
|
|
19096
|
-
function surfaceNormal(chord,
|
|
19097
|
-
const n = cross3$6(chord,
|
|
19135
|
+
function surfaceNormal(chord, span2) {
|
|
19136
|
+
const n = cross3$6(chord, span2);
|
|
19098
19137
|
const len = Math.hypot(n[0], n[1], n[2]);
|
|
19099
19138
|
if (len < 1e-12) {
|
|
19100
19139
|
const radial = Math.hypot(chord[0], chord[1]);
|
|
@@ -19755,6 +19794,16 @@ function fromSlicesSingleSliceHalfExtentForManifold(plan) {
|
|
|
19755
19794
|
return Math.max(1, radius + maxOffsetMagnitude + plan.boundsPadding + plan.edgeLength * 3);
|
|
19756
19795
|
}
|
|
19757
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
|
+
}
|
|
19758
19807
|
function promoteBooleanOperandNormals(shapes) {
|
|
19759
19808
|
let maxExtra = 0;
|
|
19760
19809
|
for (const shape of shapes) maxExtra = Math.max(maxExtra, shape.numProp());
|
|
@@ -19776,6 +19825,12 @@ function lowerShapeBooleanCompilePlan(plan, wasm) {
|
|
|
19776
19825
|
if (shapes.length === 1) {
|
|
19777
19826
|
return shapes[0];
|
|
19778
19827
|
}
|
|
19828
|
+
try {
|
|
19829
|
+
rejectParametricUvBooleanOperands(shapes);
|
|
19830
|
+
} catch (error) {
|
|
19831
|
+
disposeWasmObjects(shapes);
|
|
19832
|
+
throw error;
|
|
19833
|
+
}
|
|
19779
19834
|
const { operands, created } = promoteBooleanOperandNormals(shapes);
|
|
19780
19835
|
try {
|
|
19781
19836
|
switch (plan.op) {
|
|
@@ -20533,9 +20588,9 @@ function lowerVerticalVariableSweepStitchedForManifold(plan, sectionPolygons, wa
|
|
|
20533
20588
|
return null;
|
|
20534
20589
|
}
|
|
20535
20590
|
const frameY = cross3$5(tangent, frameX);
|
|
20536
|
-
const
|
|
20591
|
+
const span2 = end[2] - start[2];
|
|
20537
20592
|
const sections = sectionPolygons.map((section) => ({
|
|
20538
|
-
height: start[2] +
|
|
20593
|
+
height: start[2] + span2 * section.t,
|
|
20539
20594
|
polygons: section.polygons.map(
|
|
20540
20595
|
(loop) => loop.map(([u2, v]) => [start[0] + u2 * frameX[0] + v * frameY[0], start[1] + u2 * frameX[1] + v * frameY[1]])
|
|
20541
20596
|
)
|
|
@@ -23082,7 +23137,7 @@ function lowerAxisymmetricEllipsoidForOCCT(oc, radius, heightRadius) {
|
|
|
23082
23137
|
}
|
|
23083
23138
|
function lowerOrthogonalEllipseFromSlicesPlan(oc, plan) {
|
|
23084
23139
|
if (plan.groups.length !== 2) return null;
|
|
23085
|
-
const
|
|
23140
|
+
const center2 = [0, 0, 0];
|
|
23086
23141
|
const radii = [null, null, null];
|
|
23087
23142
|
for (const group of plan.groups) {
|
|
23088
23143
|
if (group.slices.length !== 1) return null;
|
|
@@ -23094,7 +23149,7 @@ function lowerOrthogonalEllipseFromSlicesPlan(oc, plan) {
|
|
|
23094
23149
|
const uAxis = axisAlignedDirectionForOCCT(frame.u);
|
|
23095
23150
|
const vAxis = axisAlignedDirectionForOCCT(frame.v);
|
|
23096
23151
|
if (!normalAxis || !uAxis || !vAxis) return null;
|
|
23097
|
-
|
|
23152
|
+
center2[normalAxis.axis] = normalAxis.sign * slice.offset;
|
|
23098
23153
|
if (!setEllipsoidRadiusForOCCT(radii, uAxis.axis, profileRadii[0])) return null;
|
|
23099
23154
|
if (!setEllipsoidRadiusForOCCT(radii, vAxis.axis, profileRadii[1])) return null;
|
|
23100
23155
|
}
|
|
@@ -23105,9 +23160,9 @@ function lowerOrthogonalEllipseFromSlicesPlan(oc, plan) {
|
|
|
23105
23160
|
const axisymmetricTolerance = Math.max(1e-7, Math.max(Math.abs(rx), Math.abs(ry)) * 1e-7);
|
|
23106
23161
|
if (Math.abs(rx - ry) > axisymmetricTolerance) return null;
|
|
23107
23162
|
let shape = lowerAxisymmetricEllipsoidForOCCT(oc, (rx + ry) / 2, rz);
|
|
23108
|
-
if (Math.hypot(
|
|
23163
|
+
if (Math.hypot(center2[0], center2[1], center2[2]) > 1e-10) {
|
|
23109
23164
|
const trsf = new oc.gp_Trsf_1();
|
|
23110
|
-
trsf.SetTranslation_1(new oc.gp_Vec_4(
|
|
23165
|
+
trsf.SetTranslation_1(new oc.gp_Vec_4(center2[0], center2[1], center2[2]));
|
|
23111
23166
|
const moved = new oc.BRepBuilderAPI_Transform_2(shape, trsf, true);
|
|
23112
23167
|
shape = moved.Shape();
|
|
23113
23168
|
}
|
|
@@ -24029,8 +24084,8 @@ function resamplePolyline$1(points, count) {
|
|
|
24029
24084
|
for (let index2 = 0; index2 < count; index2 += 1) {
|
|
24030
24085
|
const target = index2 / (count - 1) * total;
|
|
24031
24086
|
while (segment < cumulative.length - 2 && cumulative[segment + 1] < target) segment += 1;
|
|
24032
|
-
const
|
|
24033
|
-
const t =
|
|
24087
|
+
const span2 = cumulative[segment + 1] - cumulative[segment];
|
|
24088
|
+
const t = span2 > 1e-12 ? (target - cumulative[segment]) / span2 : 0;
|
|
24034
24089
|
const a2 = points[segment];
|
|
24035
24090
|
const b = points[segment + 1];
|
|
24036
24091
|
out.push([a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t]);
|
|
@@ -24161,19 +24216,23 @@ function surfaceGridForFillPlan$1(plan) {
|
|
|
24161
24216
|
function surfaceGridForAnalyticPlan$1(plan) {
|
|
24162
24217
|
const count = Math.max(2, plan.resolution + 1);
|
|
24163
24218
|
const grid = [];
|
|
24219
|
+
const uvGrid = [];
|
|
24164
24220
|
const { uMin, uMax, vMin, vMax } = plan.surface;
|
|
24165
24221
|
for (let uIndex = 0; uIndex < count; uIndex += 1) {
|
|
24166
24222
|
const uNorm = uIndex / (count - 1);
|
|
24167
24223
|
const u2 = uMin + (uMax - uMin) * uNorm;
|
|
24168
24224
|
const row = [];
|
|
24225
|
+
const uvRow = [];
|
|
24169
24226
|
for (let vIndex = 0; vIndex < count; vIndex += 1) {
|
|
24170
24227
|
const vNorm = vIndex / (count - 1);
|
|
24171
24228
|
const v = vMin + (vMax - vMin) * vNorm;
|
|
24172
24229
|
row.push(analyticSurfacePoint$1(plan.surface, u2, v));
|
|
24230
|
+
uvRow.push([uNorm, vNorm]);
|
|
24173
24231
|
}
|
|
24174
24232
|
grid.push(row);
|
|
24233
|
+
uvGrid.push(uvRow);
|
|
24175
24234
|
}
|
|
24176
|
-
return { grid, trim: sampleTrim(plan.trim) };
|
|
24235
|
+
return { grid, trim: sampleTrim(plan.trim), uvGrid };
|
|
24177
24236
|
}
|
|
24178
24237
|
function computeGridNormals(positions, indices) {
|
|
24179
24238
|
const normals = Array.from({ length: positions.length }, () => [0, 0, 0]);
|
|
@@ -24199,6 +24258,7 @@ function meshFromSurfaceGrid(grid, trim = null, context = "SDF surface sheet", u
|
|
|
24199
24258
|
const cols = ((_a3 = grid[0]) == null ? void 0 : _a3.length) ?? 0;
|
|
24200
24259
|
if (rows < 2 || cols < 2) throw new Error(`${context} requires at least a 2x2 sampled grid.`);
|
|
24201
24260
|
const positions = grid.flat();
|
|
24261
|
+
const hasParametricUv = uvGrid !== void 0;
|
|
24202
24262
|
const uvs = [];
|
|
24203
24263
|
if (uvGrid) {
|
|
24204
24264
|
if (uvGrid.length !== rows || uvGrid.some((row) => row.length !== cols)) {
|
|
@@ -24210,6 +24270,16 @@ function meshFromSurfaceGrid(grid, trim = null, context = "SDF surface sheet", u
|
|
|
24210
24270
|
for (let col = 0; col < cols; col += 1) uvs.push([row / (rows - 1), col / (cols - 1)]);
|
|
24211
24271
|
}
|
|
24212
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
|
+
})();
|
|
24213
24283
|
const sourceIndices = [];
|
|
24214
24284
|
const includeTriangle = (a2, b, c2) => {
|
|
24215
24285
|
const centroid2 = [(uvs[a2][0] + uvs[b][0] + uvs[c2][0]) / 3, (uvs[a2][1] + uvs[b][1] + uvs[c2][1]) / 3];
|
|
@@ -24228,29 +24298,37 @@ function meshFromSurfaceGrid(grid, trim = null, context = "SDF surface sheet", u
|
|
|
24228
24298
|
if (sourceIndices.length === 0) throw new Error(`${context} trim loops removed the whole sampled surface.`);
|
|
24229
24299
|
const used = /* @__PURE__ */ new Map();
|
|
24230
24300
|
const compactPositions = [];
|
|
24301
|
+
const compactUvs = [];
|
|
24231
24302
|
const compactIndices = sourceIndices.map((sourceIndex) => {
|
|
24232
24303
|
const existing = used.get(sourceIndex);
|
|
24233
24304
|
if (existing !== void 0) return existing;
|
|
24234
24305
|
const next = compactPositions.length;
|
|
24235
24306
|
used.set(sourceIndex, next);
|
|
24236
24307
|
compactPositions.push(positions[sourceIndex]);
|
|
24308
|
+
if (hasParametricUv) compactUvs.push(normalizeUv(uvs[sourceIndex]));
|
|
24237
24309
|
return next;
|
|
24238
24310
|
});
|
|
24239
24311
|
const compactNormals = computeGridNormals(compactPositions, compactIndices);
|
|
24240
|
-
const
|
|
24312
|
+
const numProp = hasParametricUv ? NUM_PROP_WITH_UV : NUM_PROP_WITH_NORMAL;
|
|
24313
|
+
const vertProperties = new Float32Array(compactPositions.length * numProp);
|
|
24241
24314
|
for (let index2 = 0; index2 < compactPositions.length; index2 += 1) {
|
|
24242
24315
|
const [x2, y2, z2] = compactPositions[index2];
|
|
24243
24316
|
const [nx, ny, nz] = compactNormals[index2];
|
|
24244
|
-
const offset = index2 *
|
|
24245
|
-
vertProperties[offset] = x2;
|
|
24246
|
-
vertProperties[offset + 1] = y2;
|
|
24247
|
-
vertProperties[offset + 2] = z2;
|
|
24248
|
-
vertProperties[offset +
|
|
24249
|
-
vertProperties[offset +
|
|
24250
|
-
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
|
+
}
|
|
24251
24329
|
}
|
|
24252
24330
|
return {
|
|
24253
|
-
numProp
|
|
24331
|
+
numProp,
|
|
24254
24332
|
numVert: compactPositions.length,
|
|
24255
24333
|
numTri: compactIndices.length / 3,
|
|
24256
24334
|
vertProperties,
|
|
@@ -24335,16 +24413,20 @@ function surfaceGridForNurbsPlan(plan) {
|
|
|
24335
24413
|
});
|
|
24336
24414
|
const resolution = Math.max(2, Math.round(plan.resolution));
|
|
24337
24415
|
const grid = [];
|
|
24416
|
+
const uvGrid = [];
|
|
24338
24417
|
for (let i = 0; i <= resolution; i += 1) {
|
|
24339
24418
|
const u2 = i / resolution;
|
|
24340
24419
|
const row = [];
|
|
24420
|
+
const uvRow = [];
|
|
24341
24421
|
for (let j = 0; j <= resolution; j += 1) {
|
|
24342
24422
|
const v = j / resolution;
|
|
24343
24423
|
row.push(surface.pointAt(u2, v));
|
|
24424
|
+
uvRow.push([u2, v]);
|
|
24344
24425
|
}
|
|
24345
24426
|
grid.push(row);
|
|
24427
|
+
uvGrid.push(uvRow);
|
|
24346
24428
|
}
|
|
24347
|
-
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 };
|
|
24348
24430
|
}
|
|
24349
24431
|
function surfaceGridForSheetPlan(plan) {
|
|
24350
24432
|
if (plan.kind === "queryOwner") return surfaceGridForSheetPlan(plan.base);
|
|
@@ -24359,8 +24441,8 @@ function surfaceGridForSheetPlan(plan) {
|
|
|
24359
24441
|
}
|
|
24360
24442
|
if (plan.kind === "nurbsSurface") return surfaceGridForNurbsPlan(plan);
|
|
24361
24443
|
if (plan.kind === "analyticSurface") {
|
|
24362
|
-
const { grid, trim } = surfaceGridForAnalyticPlan$1(plan);
|
|
24363
|
-
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 };
|
|
24364
24446
|
}
|
|
24365
24447
|
if (plan.kind === "surfaceRuled") return { grid: surfaceGridForRuledPlan$1(plan), rowAxis: "v" };
|
|
24366
24448
|
if (plan.kind === "surfaceFill") return { grid: surfaceGridForFillPlan$1(plan), rowAxis: "v" };
|
|
@@ -24634,8 +24716,8 @@ function meshFromOpenSurfaceSheetPlan(plan) {
|
|
|
24634
24716
|
}
|
|
24635
24717
|
if (plan.kind === "nurbsSurface") return meshFromOpenNurbsSurface(plan);
|
|
24636
24718
|
if (plan.kind === "analyticSurface") {
|
|
24637
|
-
const { grid, trim } = surfaceGridForAnalyticPlan$1(plan);
|
|
24638
|
-
return meshFromSurfaceGrid(grid, trim, "SDF analytic surface");
|
|
24719
|
+
const { grid, trim, uvGrid } = surfaceGridForAnalyticPlan$1(plan);
|
|
24720
|
+
return meshFromSurfaceGrid(grid, trim, "SDF analytic surface", uvGrid);
|
|
24639
24721
|
}
|
|
24640
24722
|
if (plan.kind === "surfaceRuled") return meshFromSurfaceGrid(surfaceGridForRuledPlan$1(plan), null, "SDF ruled surface");
|
|
24641
24723
|
if (plan.kind === "surfaceFill") return meshFromSurfaceFillPlan(plan);
|
|
@@ -32117,8 +32199,8 @@ function revolveProfilePolygonsToSdfField(polygons, degrees = 360) {
|
|
|
32117
32199
|
const profileDistance = profile.eval(radial, z2);
|
|
32118
32200
|
if (Math.abs(degrees) >= 360) return profileDistance;
|
|
32119
32201
|
const sweep = Math.abs(degrees) * (Math.PI / 180);
|
|
32120
|
-
const
|
|
32121
|
-
let angle = Math.atan2(y2, x2) -
|
|
32202
|
+
const center2 = degrees * Math.PI / 360;
|
|
32203
|
+
let angle = Math.atan2(y2, x2) - center2;
|
|
32122
32204
|
while (angle <= -Math.PI) angle += Math.PI * 2;
|
|
32123
32205
|
while (angle > Math.PI) angle -= Math.PI * 2;
|
|
32124
32206
|
const half = sweep / 2;
|
|
@@ -33424,15 +33506,15 @@ function deBoorPoint(degree, knots, controlPoints, parameter) {
|
|
|
33424
33506
|
const domainEnd = knots[controlPoints.length];
|
|
33425
33507
|
const t = Math.max(domainStart, Math.min(domainEnd, parameter));
|
|
33426
33508
|
if (t >= domainEnd - 1e-12) return controlPoints[controlPoints.length - 1];
|
|
33427
|
-
let
|
|
33509
|
+
let span2 = degree;
|
|
33428
33510
|
const lastSpan = controlPoints.length - 1;
|
|
33429
|
-
while (
|
|
33511
|
+
while (span2 < lastSpan && t >= knots[span2 + 1]) span2 += 1;
|
|
33430
33512
|
const points = [];
|
|
33431
|
-
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]]);
|
|
33432
33514
|
for (let order = 1; order <= degree; order += 1) {
|
|
33433
33515
|
for (let index2 = degree; index2 >= order; index2 -= 1) {
|
|
33434
|
-
const knotLeft = knots[
|
|
33435
|
-
const knotRight = knots[
|
|
33516
|
+
const knotLeft = knots[span2 - degree + index2];
|
|
33517
|
+
const knotRight = knots[span2 + index2 + 1 - order];
|
|
33436
33518
|
const alpha = Math.abs(knotRight - knotLeft) <= 1e-12 ? 0 : (t - knotLeft) / (knotRight - knotLeft);
|
|
33437
33519
|
points[index2] = [
|
|
33438
33520
|
points[index2 - 1][0] * (1 - alpha) + points[index2][0] * alpha,
|
|
@@ -33448,16 +33530,16 @@ function deBoorHomogeneousPoint(degree, knots, controlPoints, parameter) {
|
|
|
33448
33530
|
const domainEnd = knots[controlPoints.length];
|
|
33449
33531
|
const t = Math.max(domainStart, Math.min(domainEnd, parameter));
|
|
33450
33532
|
if (t >= domainEnd - 1e-12) return controlPoints[controlPoints.length - 1];
|
|
33451
|
-
let
|
|
33533
|
+
let span2 = degree;
|
|
33452
33534
|
const lastSpan = controlPoints.length - 1;
|
|
33453
|
-
while (
|
|
33535
|
+
while (span2 < lastSpan && t >= knots[span2 + 1]) span2 += 1;
|
|
33454
33536
|
const points = [];
|
|
33455
33537
|
for (let index2 = 0; index2 <= degree; index2 += 1)
|
|
33456
|
-
points.push([...controlPoints[
|
|
33538
|
+
points.push([...controlPoints[span2 - degree + index2]]);
|
|
33457
33539
|
for (let order = 1; order <= degree; order += 1) {
|
|
33458
33540
|
for (let index2 = degree; index2 >= order; index2 -= 1) {
|
|
33459
|
-
const knotLeft = knots[
|
|
33460
|
-
const knotRight = knots[
|
|
33541
|
+
const knotLeft = knots[span2 - degree + index2];
|
|
33542
|
+
const knotRight = knots[span2 + index2 + 1 - order];
|
|
33461
33543
|
const alpha = Math.abs(knotRight - knotLeft) <= 1e-12 ? 0 : (t - knotLeft) / (knotRight - knotLeft);
|
|
33462
33544
|
points[index2] = [
|
|
33463
33545
|
points[index2 - 1][0] * (1 - alpha) + points[index2][0] * alpha,
|
|
@@ -33519,9 +33601,9 @@ function closestBSplineParameter(curve, point2) {
|
|
|
33519
33601
|
function wrappedBSplinePoint(curve, parameter) {
|
|
33520
33602
|
const [domainStart, domainEnd] = bsplineDomain(curve);
|
|
33521
33603
|
if (!curve.closed) return bsplinePoint(curve, parameter);
|
|
33522
|
-
const
|
|
33523
|
-
let wrapped = (parameter - domainStart) %
|
|
33524
|
-
if (wrapped < domainStart) wrapped +=
|
|
33604
|
+
const span2 = domainEnd - domainStart;
|
|
33605
|
+
let wrapped = (parameter - domainStart) % span2 + domainStart;
|
|
33606
|
+
if (wrapped < domainStart) wrapped += span2;
|
|
33525
33607
|
return bsplinePoint(curve, wrapped);
|
|
33526
33608
|
}
|
|
33527
33609
|
function bsplineSurfacePoint(surface, u2, v) {
|
|
@@ -33837,8 +33919,8 @@ function resampleClosedLoop(loop, count) {
|
|
|
33837
33919
|
for (let index2 = 0; index2 < count; index2 += 1) {
|
|
33838
33920
|
const target = index2 / count * total;
|
|
33839
33921
|
while (segment < points.length - 1 && cumulative[segment + 1] < target) segment += 1;
|
|
33840
|
-
const
|
|
33841
|
-
const t =
|
|
33922
|
+
const span2 = cumulative[segment + 1] - cumulative[segment];
|
|
33923
|
+
const t = span2 <= 1e-12 ? 0 : (target - cumulative[segment]) / span2;
|
|
33842
33924
|
const a2 = points[segment];
|
|
33843
33925
|
const b = points[(segment + 1) % points.length];
|
|
33844
33926
|
out.push([a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t]);
|
|
@@ -33866,14 +33948,14 @@ function orientRadialTriangles(triangles, frame, sameSense) {
|
|
|
33866
33948
|
const flip = sameSense ? !radialOutward : radialOutward;
|
|
33867
33949
|
return flip ? triangles.map(reverseTriangle) : triangles;
|
|
33868
33950
|
}
|
|
33869
|
-
function orientCenteredTriangles(triangles,
|
|
33951
|
+
function orientCenteredTriangles(triangles, center2, sameSense) {
|
|
33870
33952
|
return triangles.map((triangle) => {
|
|
33871
33953
|
const sample = [
|
|
33872
33954
|
(triangle[0][0] + triangle[1][0] + triangle[2][0]) / 3,
|
|
33873
33955
|
(triangle[0][1] + triangle[1][1] + triangle[2][1]) / 3,
|
|
33874
33956
|
(triangle[0][2] + triangle[1][2] + triangle[2][2]) / 3
|
|
33875
33957
|
];
|
|
33876
|
-
const outward = [sample[0] -
|
|
33958
|
+
const outward = [sample[0] - center2[0], sample[1] - center2[1], sample[2] - center2[2]];
|
|
33877
33959
|
const normal2 = cross3$4(
|
|
33878
33960
|
[triangle[1][0] - triangle[0][0], triangle[1][1] - triangle[0][1], triangle[1][2] - triangle[0][2]],
|
|
33879
33961
|
[triangle[2][0] - triangle[0][0], triangle[2][1] - triangle[0][1], triangle[2][2] - triangle[0][2]]
|
|
@@ -34112,8 +34194,8 @@ function orientRevolvedProfileTriangles(triangles, frame, axis, profileCenter, p
|
|
|
34112
34194
|
(triangle[0][2] + triangle[1][2] + triangle[2][2]) / 3
|
|
34113
34195
|
];
|
|
34114
34196
|
const angle = revolutionAngle(frame, sample, filePath, "SURFACE_OF_REVOLUTION");
|
|
34115
|
-
const
|
|
34116
|
-
const outward = sub3$1(sample,
|
|
34197
|
+
const center2 = rotateAroundAxis(profileCenter, axis, normalizeAngleDelta(angle - profileAngle));
|
|
34198
|
+
const outward = sub3$1(sample, center2);
|
|
34117
34199
|
if (Math.hypot(outward[0], outward[1], outward[2]) <= 1e-12)
|
|
34118
34200
|
failStepImport(filePath, "SURFACE_OF_REVOLUTION sample hit degenerate profile centerline.");
|
|
34119
34201
|
const normal2 = cross3$4(sub3$1(triangle[1], triangle[0]), sub3$1(triangle[2], triangle[0]));
|
|
@@ -34518,18 +34600,18 @@ function triangulateTrimmedBSplineSurface(surface, loops, sameSense, filePath, c
|
|
|
34518
34600
|
const { outer: uvLoop, holes } = trimmedBSplineSurfaceUvLoops(surface, loops, filePath, context);
|
|
34519
34601
|
if (holes.length > 0 || !isConvexUvLoop(uvLoop))
|
|
34520
34602
|
return triangulateBSplineSurfaceUvPolygon(surface, uvLoop, holes, sameSense, filePath, context);
|
|
34521
|
-
const
|
|
34603
|
+
const center2 = uvCentroid(uvLoop);
|
|
34522
34604
|
const ringCount = Math.max(3, Math.ceil(Math.max(surface.controlPoints.length, surface.controlPoints[0].length) * 1.5));
|
|
34523
34605
|
const rings = [];
|
|
34524
34606
|
for (let ringIndex = 1; ringIndex <= ringCount; ringIndex += 1) {
|
|
34525
34607
|
const t = ringIndex / ringCount;
|
|
34526
|
-
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]));
|
|
34527
34609
|
}
|
|
34528
34610
|
const mapPoint = (point2) => bsplineSurfacePoint(surface, point2[0], point2[1]);
|
|
34529
34611
|
const triangles = [];
|
|
34530
34612
|
const firstRing = rings[0];
|
|
34531
34613
|
for (let index2 = 0; index2 < firstRing.length; index2 += 1) {
|
|
34532
|
-
triangles.push([mapPoint(
|
|
34614
|
+
triangles.push([mapPoint(center2), mapPoint(firstRing[index2]), mapPoint(firstRing[(index2 + 1) % firstRing.length])]);
|
|
34533
34615
|
}
|
|
34534
34616
|
for (let ringIndex = 1; ringIndex < rings.length; ringIndex += 1) {
|
|
34535
34617
|
const inner = rings[ringIndex - 1];
|
|
@@ -34546,9 +34628,9 @@ function rotateArray(items, start) {
|
|
|
34546
34628
|
return [...items.slice(start), ...items.slice(0, start)];
|
|
34547
34629
|
}
|
|
34548
34630
|
function periodicParameterDistance(a2, b, start, end) {
|
|
34549
|
-
const
|
|
34631
|
+
const span2 = end - start;
|
|
34550
34632
|
const raw = Math.abs(a2 - b);
|
|
34551
|
-
return Math.min(raw, Math.max(0,
|
|
34633
|
+
return Math.min(raw, Math.max(0, span2 - raw));
|
|
34552
34634
|
}
|
|
34553
34635
|
function closedUBSplineBoundaryRing(surface, loop, count, filePath, context) {
|
|
34554
34636
|
const points = resampleClosedLoop(loop, count);
|
|
@@ -34813,11 +34895,11 @@ function sphericalLoopInfo(frame, radius, loop, filePath, context) {
|
|
|
34813
34895
|
if (Math.abs(distance - radius) > tolerance)
|
|
34814
34896
|
failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE boundary point is not on the sphere.`);
|
|
34815
34897
|
}
|
|
34816
|
-
const
|
|
34898
|
+
const center2 = centroid(points);
|
|
34817
34899
|
const normal2 = normalize3$2(newellNormal(points), `${context} bounded SPHERICAL_SURFACE boundary normal`);
|
|
34818
|
-
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);
|
|
34819
34901
|
if (maxPlaneError > tolerance) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE boundary loop is not planar.`);
|
|
34820
|
-
return { points, center, normal: normal2 };
|
|
34902
|
+
return { points, center: center2, normal: normal2 };
|
|
34821
34903
|
}
|
|
34822
34904
|
function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePath, context) {
|
|
34823
34905
|
if (loops.length !== 2) failStepImport(filePath, `${context} bounded SPHERICAL_SURFACE band supports exactly two loops right now.`);
|
|
@@ -34838,15 +34920,15 @@ function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePa
|
|
|
34838
34920
|
const xProjection = dot3$3(rawX, axis);
|
|
34839
34921
|
const xAxis = normalize3$2([rawX[0] - axis[0] * xProjection, rawX[1] - axis[1] * xProjection, rawX[2] - axis[2] * xProjection], context);
|
|
34840
34922
|
const yAxis = cross3$4(axis, xAxis);
|
|
34841
|
-
const loopAngle = (point2,
|
|
34842
|
-
const local = sub3$1(point2,
|
|
34923
|
+
const loopAngle = (point2, center2) => {
|
|
34924
|
+
const local = sub3$1(point2, center2);
|
|
34843
34925
|
return Math.atan2(dot3$3(local, yAxis), dot3$3(local, xAxis));
|
|
34844
34926
|
};
|
|
34845
|
-
const rotateLoop = (loop,
|
|
34927
|
+
const rotateLoop = (loop, center2, targetAngle) => {
|
|
34846
34928
|
let bestIndex = 0;
|
|
34847
34929
|
let bestDistance = Number.POSITIVE_INFINITY;
|
|
34848
34930
|
for (let index2 = 0; index2 < loop.length; index2 += 1) {
|
|
34849
|
-
const distance = angleDistance(loopAngle(loop[index2],
|
|
34931
|
+
const distance = angleDistance(loopAngle(loop[index2], center2), targetAngle);
|
|
34850
34932
|
if (distance < bestDistance) {
|
|
34851
34933
|
bestDistance = distance;
|
|
34852
34934
|
bestIndex = index2;
|
|
@@ -34890,14 +34972,14 @@ function triangulateBoundedSphericalBand(frame, radius, loops, sameSense, filePa
|
|
|
34890
34972
|
return orientCenteredTriangles(triangles, frame.origin, sameSense);
|
|
34891
34973
|
}
|
|
34892
34974
|
function orientBridgeTriangles(triangles, aCenter, bCenter, sameSense) {
|
|
34893
|
-
const
|
|
34975
|
+
const center2 = [(aCenter[0] + bCenter[0]) / 2, (aCenter[1] + bCenter[1]) / 2, (aCenter[2] + bCenter[2]) / 2];
|
|
34894
34976
|
const sample = triangles[0];
|
|
34895
34977
|
const mid = [
|
|
34896
34978
|
(sample[0][0] + sample[1][0] + sample[2][0]) / 3,
|
|
34897
34979
|
(sample[0][1] + sample[1][1] + sample[2][1]) / 3,
|
|
34898
34980
|
(sample[0][2] + sample[1][2] + sample[2][2]) / 3
|
|
34899
34981
|
];
|
|
34900
|
-
const outward = [mid[0] -
|
|
34982
|
+
const outward = [mid[0] - center2[0], mid[1] - center2[1], mid[2] - center2[2]];
|
|
34901
34983
|
const normal2 = cross3$4(
|
|
34902
34984
|
[sample[1][0] - sample[0][0], sample[1][1] - sample[0][1], sample[1][2] - sample[0][2]],
|
|
34903
34985
|
[sample[2][0] - sample[0][0], sample[2][1] - sample[0][1], sample[2][2] - sample[0][2]]
|
|
@@ -36405,18 +36487,18 @@ function circleFootprintFromProfile(plan) {
|
|
|
36405
36487
|
if (plan.kind !== "circle") return null;
|
|
36406
36488
|
const radius = Math.abs(plan.radius);
|
|
36407
36489
|
if (!finitePositive(radius)) return null;
|
|
36408
|
-
const
|
|
36490
|
+
const center2 = transformProfilePointThrough([0, 0], plan.transforms);
|
|
36409
36491
|
const xPoint = transformProfilePointThrough([1, 0], plan.transforms);
|
|
36410
36492
|
const yPoint = transformProfilePointThrough([0, 1], plan.transforms);
|
|
36411
|
-
const xAxis = [xPoint[0] -
|
|
36412
|
-
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]];
|
|
36413
36495
|
const xScale = Math.hypot(xAxis[0], xAxis[1]);
|
|
36414
36496
|
const yScale = Math.hypot(yAxis[0], yAxis[1]);
|
|
36415
36497
|
const dot2 = xAxis[0] * yAxis[0] + xAxis[1] * yAxis[1];
|
|
36416
36498
|
if (!finitePositive(xScale) || !finitePositive(yScale)) return null;
|
|
36417
36499
|
if (Math.abs(xScale - yScale) > EPS || Math.abs(dot2) > EPS * xScale * yScale) return null;
|
|
36418
36500
|
return {
|
|
36419
|
-
center,
|
|
36501
|
+
center: center2,
|
|
36420
36502
|
radius: radius * xScale,
|
|
36421
36503
|
segments: plan.segments
|
|
36422
36504
|
};
|
|
@@ -36975,19 +37057,19 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
36975
37057
|
}
|
|
36976
37058
|
}
|
|
36977
37059
|
if ("AnalyticSphere" in geometry) {
|
|
36978
|
-
const
|
|
37060
|
+
const center2 = geometry.AnalyticSphere.center;
|
|
36979
37061
|
const radius = geometry.AnalyticSphere.radius;
|
|
36980
|
-
if (isVec3(
|
|
36981
|
-
return { kind: "sphere", center, radius };
|
|
37062
|
+
if (isVec3(center2) && isFiniteNumber(radius) && radius > 0) {
|
|
37063
|
+
return { kind: "sphere", center: center2, radius };
|
|
36982
37064
|
}
|
|
36983
37065
|
}
|
|
36984
37066
|
if ("AnalyticTorus" in geometry) {
|
|
36985
|
-
const
|
|
37067
|
+
const center2 = geometry.AnalyticTorus.center;
|
|
36986
37068
|
const axis = geometry.AnalyticTorus.axis;
|
|
36987
37069
|
const majorRadius = geometry.AnalyticTorus.major_radius;
|
|
36988
37070
|
const minorRadius = geometry.AnalyticTorus.minor_radius;
|
|
36989
|
-
if (isVec3(
|
|
36990
|
-
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 };
|
|
36991
37073
|
}
|
|
36992
37074
|
}
|
|
36993
37075
|
if ("NurbsPatch" in geometry) {
|
|
@@ -37081,13 +37163,13 @@ function explicitGeometrySurface(geometry, face) {
|
|
|
37081
37163
|
function explicitEdgeCurve(geometry, faceName) {
|
|
37082
37164
|
var _a3, _b3, _c2, _d2, _e2;
|
|
37083
37165
|
if (!geometry) return void 0;
|
|
37084
|
-
const
|
|
37166
|
+
const center2 = (_a3 = geometry.CircularArc) == null ? void 0 : _a3.center;
|
|
37085
37167
|
const axis = (_b3 = geometry.CircularArc) == null ? void 0 : _b3.axis;
|
|
37086
37168
|
const radius = (_c2 = geometry.CircularArc) == null ? void 0 : _c2.radius;
|
|
37087
|
-
if (
|
|
37169
|
+
if (center2 !== void 0 && axis !== void 0 && radius !== void 0 && isVec3(center2) && isVec3(axis) && isFiniteNumber(radius) && radius > 0) {
|
|
37088
37170
|
return {
|
|
37089
37171
|
kind: "circle",
|
|
37090
|
-
center,
|
|
37172
|
+
center: center2,
|
|
37091
37173
|
axis: normalizeVec3(axis),
|
|
37092
37174
|
radius,
|
|
37093
37175
|
faceName
|
|
@@ -40034,8 +40116,8 @@ function resamplePolyline(points, count) {
|
|
|
40034
40116
|
for (let idx = 0; idx < count; idx++) {
|
|
40035
40117
|
const target = idx / (count - 1) * total;
|
|
40036
40118
|
while (segment < cumulative.length - 2 && cumulative[segment + 1] < target) segment++;
|
|
40037
|
-
const
|
|
40038
|
-
const t =
|
|
40119
|
+
const span2 = cumulative[segment + 1] - cumulative[segment];
|
|
40120
|
+
const t = span2 > 1e-12 ? (target - cumulative[segment]) / span2 : 0;
|
|
40039
40121
|
const a2 = points[segment];
|
|
40040
40122
|
const b = points[segment + 1];
|
|
40041
40123
|
out.push([a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t]);
|
|
@@ -40793,12 +40875,12 @@ function transformedSphereProfile(basePlan, steps, sliceOffset) {
|
|
|
40793
40875
|
transform = transform.mul(transformForShapeTransform(step));
|
|
40794
40876
|
scale2 *= stepScale;
|
|
40795
40877
|
}
|
|
40796
|
-
const
|
|
40878
|
+
const center2 = transform.point([0, 0, 0]);
|
|
40797
40879
|
const radius = Math.abs(source.radius * scale2);
|
|
40798
|
-
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])));
|
|
40799
40881
|
if (sectionRadius <= EXACT_PROFILE_EPS) return emptyProfilePlan();
|
|
40800
40882
|
const profile = circleProfilePlan(sectionRadius, source.segments);
|
|
40801
|
-
if (!isNearlyZero(
|
|
40883
|
+
if (!isNearlyZero(center2[0]) || !isNearlyZero(center2[1])) profile.transforms.push({ kind: "translate", x: center2[0], y: center2[1] });
|
|
40802
40884
|
return profile;
|
|
40803
40885
|
}
|
|
40804
40886
|
function profileTransformsForShapeTransform(step) {
|
|
@@ -40901,20 +40983,20 @@ function sliceOffsetBeforeShapeTransforms(steps, offset) {
|
|
|
40901
40983
|
}
|
|
40902
40984
|
return sourceOffset;
|
|
40903
40985
|
}
|
|
40904
|
-
function transformZSpan(
|
|
40986
|
+
function transformZSpan(span2, step) {
|
|
40905
40987
|
switch (step.kind) {
|
|
40906
40988
|
case "translate":
|
|
40907
|
-
return [
|
|
40989
|
+
return [span2[0] + step.z, span2[1] + step.z];
|
|
40908
40990
|
case "scale":
|
|
40909
40991
|
if (!Number.isFinite(step.z) || isNearlyZero(step.z)) return null;
|
|
40910
|
-
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)];
|
|
40911
40993
|
case "rotateAround": {
|
|
40912
40994
|
const axisLength = Math.hypot(step.axisX, step.axisY, step.axisZ);
|
|
40913
40995
|
if (axisLength <= EXACT_PROFILE_EPS) return null;
|
|
40914
40996
|
const axisX = step.axisX / axisLength;
|
|
40915
40997
|
const axisY = step.axisY / axisLength;
|
|
40916
40998
|
const axisZ = step.axisZ / axisLength;
|
|
40917
|
-
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;
|
|
40918
41000
|
}
|
|
40919
41001
|
case "mirror": {
|
|
40920
41002
|
const normalLength = Math.hypot(step.normalX, step.normalY, step.normalZ);
|
|
@@ -40922,14 +41004,14 @@ function transformZSpan(span, step) {
|
|
|
40922
41004
|
const normalX = step.normalX / normalLength;
|
|
40923
41005
|
const normalY = step.normalY / normalLength;
|
|
40924
41006
|
const normalZ = step.normalZ / normalLength;
|
|
40925
|
-
if (isNearlyZero(normalX) && isNearlyZero(normalY)) return [-
|
|
40926
|
-
return isNearlyZero(normalZ) ?
|
|
41007
|
+
if (isNearlyZero(normalX) && isNearlyZero(normalY)) return [-span2[1], -span2[0]];
|
|
41008
|
+
return isNearlyZero(normalZ) ? span2 : null;
|
|
40927
41009
|
}
|
|
40928
41010
|
case "workplanePlacement": {
|
|
40929
41011
|
const replay = workplanePlacementReplay(step.matrix);
|
|
40930
41012
|
if (!replay) return null;
|
|
40931
|
-
const z0 = replay.zScale *
|
|
40932
|
-
const z1 = replay.zScale *
|
|
41013
|
+
const z0 = replay.zScale * span2[0] + replay.zTranslate;
|
|
41014
|
+
const z1 = replay.zScale * span2[1] + replay.zTranslate;
|
|
40933
41015
|
return [Math.min(z0, z1), Math.max(z0, z1)];
|
|
40934
41016
|
}
|
|
40935
41017
|
default:
|
|
@@ -40983,14 +41065,14 @@ function transformProfilePointThroughRadial(point2, transforms) {
|
|
|
40983
41065
|
return out;
|
|
40984
41066
|
}
|
|
40985
41067
|
function profileRadialTransformBasis(transforms) {
|
|
40986
|
-
const
|
|
41068
|
+
const center2 = transformProfilePointThroughRadial([0, 0], transforms);
|
|
40987
41069
|
const xBasis = transformProfilePointThroughRadial([1, 0], transforms);
|
|
40988
41070
|
const yBasis = transformProfilePointThroughRadial([0, 1], transforms);
|
|
40989
|
-
if (!
|
|
41071
|
+
if (!center2 || !xBasis || !yBasis) return null;
|
|
40990
41072
|
return {
|
|
40991
|
-
center,
|
|
40992
|
-
xAxis: [xBasis[0] -
|
|
40993
|
-
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]]
|
|
40994
41076
|
};
|
|
40995
41077
|
}
|
|
40996
41078
|
function circleRadialProjectionInterval(plan) {
|
|
@@ -41138,13 +41220,13 @@ function radialRoundedRectBoundaryMaxDistanceFromPoint(footprint, point2) {
|
|
|
41138
41220
|
[-1, -1]
|
|
41139
41221
|
];
|
|
41140
41222
|
for (const [sx, sy] of cornerSigns) {
|
|
41141
|
-
const
|
|
41142
|
-
includeLocal([
|
|
41143
|
-
includeLocal([
|
|
41144
|
-
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]];
|
|
41145
41227
|
const awayLength = Math.hypot(away[0], away[1]);
|
|
41146
41228
|
if (awayLength > EXACT_PROFILE_EPS && away[0] * sx >= -EXACT_PROFILE_EPS && away[1] * sy >= -EXACT_PROFILE_EPS) {
|
|
41147
|
-
includeLocal([
|
|
41229
|
+
includeLocal([center2[0] + away[0] / awayLength * footprint.radius, center2[1] + away[1] / awayLength * footprint.radius]);
|
|
41148
41230
|
}
|
|
41149
41231
|
}
|
|
41150
41232
|
return maxDistance;
|
|
@@ -41242,10 +41324,10 @@ function lineIntervalsForRect(base, tangent, halfWidth, halfHeight) {
|
|
|
41242
41324
|
if (!applyAxis(base[0], tangent[0], halfWidth) || !applyAxis(base[1], tangent[1], halfHeight)) return [];
|
|
41243
41325
|
return [[min2, max2]];
|
|
41244
41326
|
}
|
|
41245
|
-
function lineIntervalsForCircle(base, tangent,
|
|
41327
|
+
function lineIntervalsForCircle(base, tangent, center2, radius) {
|
|
41246
41328
|
if (radius < -EXACT_PROFILE_EPS) return null;
|
|
41247
|
-
const dx = base[0] -
|
|
41248
|
-
const dy = base[1] -
|
|
41329
|
+
const dx = base[0] - center2[0];
|
|
41330
|
+
const dy = base[1] - center2[1];
|
|
41249
41331
|
const projection = dx * tangent[0] + dy * tangent[1];
|
|
41250
41332
|
const distanceSq = dx * dx + dy * dy - projection * projection;
|
|
41251
41333
|
const radiusSq = radius * radius;
|
|
@@ -41288,7 +41370,7 @@ function roundedRectRadialSliceIntervals(plan, y2) {
|
|
|
41288
41370
|
[-coreHalfWidth, coreHalfHeight],
|
|
41289
41371
|
[-coreHalfWidth, -coreHalfHeight]
|
|
41290
41372
|
];
|
|
41291
|
-
for (const
|
|
41373
|
+
for (const center2 of centers) include(lineIntervalsForCircle(base, tangent, center2, radius));
|
|
41292
41374
|
}
|
|
41293
41375
|
const xFunctional = [basis.xAxis[0], basis.yAxis[0]];
|
|
41294
41376
|
const xAtBase = basis.center[0] + xFunctional[0] * base[0] + xFunctional[1] * base[1];
|
|
@@ -42596,9 +42678,9 @@ function expandSimpleFullRevolutionProfileFootprint(plan, distance) {
|
|
|
42596
42678
|
}
|
|
42597
42679
|
}
|
|
42598
42680
|
function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
|
|
42599
|
-
const translate = (profile,
|
|
42600
|
-
if (!isNearlyZero(
|
|
42601
|
-
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] });
|
|
42602
42684
|
}
|
|
42603
42685
|
return profile;
|
|
42604
42686
|
};
|
|
@@ -42616,7 +42698,7 @@ function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
|
|
|
42616
42698
|
footprint.footprint.center
|
|
42617
42699
|
) : null;
|
|
42618
42700
|
case "roundedRect": {
|
|
42619
|
-
const { halfWidth, halfHeight, radius, xAxis, yAxis, center } = footprint.footprint;
|
|
42701
|
+
const { halfWidth, halfHeight, radius, xAxis, yAxis, center: center2 } = footprint.footprint;
|
|
42620
42702
|
if (halfWidth <= EXACT_PROFILE_EPS || halfHeight <= EXACT_PROFILE_EPS || radius < -EXACT_PROFILE_EPS) return null;
|
|
42621
42703
|
const profile = roundedRectProfilePlan({
|
|
42622
42704
|
width: halfWidth * 2,
|
|
@@ -42631,7 +42713,7 @@ function profilePlanForSimpleFullRevolutionFootprint(footprint, segments) {
|
|
|
42631
42713
|
m11: yAxis[1]
|
|
42632
42714
|
})
|
|
42633
42715
|
);
|
|
42634
|
-
return translate(profile,
|
|
42716
|
+
return translate(profile, center2);
|
|
42635
42717
|
}
|
|
42636
42718
|
default:
|
|
42637
42719
|
return assertExhaustive(footprint);
|
|
@@ -43218,16 +43300,16 @@ function exactVerticalProjectionProfilePlan(plan) {
|
|
|
43218
43300
|
const base = exactVerticalProjectionProfilePlan(plan.base);
|
|
43219
43301
|
if (!base) return null;
|
|
43220
43302
|
let profile = base.profile;
|
|
43221
|
-
let
|
|
43303
|
+
let span2 = [base.zMin, base.zMax];
|
|
43222
43304
|
for (const step of plan.steps) {
|
|
43223
43305
|
const projectedTransforms = profileTransformsForShapeTransform(step);
|
|
43224
43306
|
if (!projectedTransforms) return null;
|
|
43225
|
-
const nextSpan = transformZSpan(
|
|
43307
|
+
const nextSpan = transformZSpan(span2, step);
|
|
43226
43308
|
if (!nextSpan) return null;
|
|
43227
43309
|
profile = appendProfileTransforms(profile, projectedTransforms);
|
|
43228
|
-
|
|
43310
|
+
span2 = nextSpan;
|
|
43229
43311
|
}
|
|
43230
|
-
return { profile, zMin:
|
|
43312
|
+
return { profile, zMin: span2[0], zMax: span2[1] };
|
|
43231
43313
|
}
|
|
43232
43314
|
default:
|
|
43233
43315
|
return null;
|
|
@@ -43247,7 +43329,7 @@ function projectVerticalDifferenceProfilePlan(subject, clips) {
|
|
|
43247
43329
|
const clipped = clips.map((clip) => ({ clip, span: clippedZSpan(subject, clip) })).filter((entry) => entry.span != null);
|
|
43248
43330
|
if (clipped.length === 0) return cloneProfileCompilePlan(subject.profile);
|
|
43249
43331
|
const zBreaks = [subject.zMin, subject.zMax];
|
|
43250
|
-
for (const { span } of clipped) zBreaks.push(
|
|
43332
|
+
for (const { span: span2 } of clipped) zBreaks.push(span2[0], span2[1]);
|
|
43251
43333
|
zBreaks.sort((a2, b) => a2 - b);
|
|
43252
43334
|
const uniqueBreaks = [];
|
|
43253
43335
|
for (const z2 of zBreaks) {
|
|
@@ -43260,7 +43342,7 @@ function projectVerticalDifferenceProfilePlan(subject, clips) {
|
|
|
43260
43342
|
const zMax = uniqueBreaks[idx + 1];
|
|
43261
43343
|
if (zMax - zMin <= EXACT_PROFILE_EPS) continue;
|
|
43262
43344
|
const zMid = (zMin + zMax) / 2;
|
|
43263
|
-
const activeClips = clipped.filter(({ span }) => offsetInRange(zMid,
|
|
43345
|
+
const activeClips = clipped.filter(({ span: span2 }) => offsetInRange(zMid, span2[0], span2[1]));
|
|
43264
43346
|
if (activeClips.length === 0) return cloneProfileCompilePlan(subject.profile);
|
|
43265
43347
|
slabProfiles.push({
|
|
43266
43348
|
kind: "boolean",
|
|
@@ -44233,7 +44315,7 @@ function clusterMeshFaces(shape) {
|
|
|
44233
44315
|
return clusters;
|
|
44234
44316
|
}
|
|
44235
44317
|
function clusterToFaceRef(cluster, name = "") {
|
|
44236
|
-
const
|
|
44318
|
+
const center2 = [
|
|
44237
44319
|
cluster.centroidSum[0] / cluster.count,
|
|
44238
44320
|
cluster.centroidSum[1] / cluster.count,
|
|
44239
44321
|
cluster.centroidSum[2] / cluster.count
|
|
@@ -44242,7 +44324,7 @@ function clusterToFaceRef(cluster, name = "") {
|
|
|
44242
44324
|
return {
|
|
44243
44325
|
name,
|
|
44244
44326
|
normal: cluster.normal,
|
|
44245
|
-
center,
|
|
44327
|
+
center: center2,
|
|
44246
44328
|
planar: true,
|
|
44247
44329
|
uAxis: u2,
|
|
44248
44330
|
vAxis: v
|
|
@@ -44414,7 +44496,7 @@ function representativeFaceForMembers(name, faces, query, semantic) {
|
|
|
44414
44496
|
const cloned = faces.map((face) => cloneFaceRefValue(face));
|
|
44415
44497
|
const first = cloned[0];
|
|
44416
44498
|
const coplanar = facesAreCoplanar(cloned);
|
|
44417
|
-
const
|
|
44499
|
+
const center2 = cloned.reduce(
|
|
44418
44500
|
(acc, face) => [acc[0] + face.center[0], acc[1] + face.center[1], acc[2] + face.center[2]],
|
|
44419
44501
|
[0, 0, 0]
|
|
44420
44502
|
);
|
|
@@ -44422,7 +44504,7 @@ function representativeFaceForMembers(name, faces, query, semantic) {
|
|
|
44422
44504
|
const representative = {
|
|
44423
44505
|
...first,
|
|
44424
44506
|
name,
|
|
44425
|
-
center: [
|
|
44507
|
+
center: [center2[0] / count, center2[1] / count, center2[2] / count],
|
|
44426
44508
|
query: cloneFaceQueryRef(query),
|
|
44427
44509
|
planar: coplanar ? first.planar : false,
|
|
44428
44510
|
uAxis: coplanar ? first.uAxis : void 0,
|
|
@@ -45542,10 +45624,10 @@ function resolveShapeFaceTableInternal(plan, owner) {
|
|
|
45542
45624
|
if (!planeCapQuery) return table;
|
|
45543
45625
|
const baseTable = resolveShapeFaceTable(plan.base);
|
|
45544
45626
|
const centers = Array.from(baseTable.faces.values()).map((face) => face.center);
|
|
45545
|
-
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];
|
|
45546
45628
|
const normal2 = normalizeAxis([plan.normalX, plan.normalY, plan.normalZ]);
|
|
45547
45629
|
const planeDistance = averageCenter[0] * normal2[0] + averageCenter[1] * normal2[1] + averageCenter[2] * normal2[2] - plan.originOffset;
|
|
45548
|
-
const
|
|
45630
|
+
const center2 = [
|
|
45549
45631
|
averageCenter[0] - normal2[0] * planeDistance,
|
|
45550
45632
|
averageCenter[1] - normal2[1] * planeDistance,
|
|
45551
45633
|
averageCenter[2] - normal2[2] * planeDistance
|
|
@@ -45554,7 +45636,7 @@ function resolveShapeFaceTableInternal(plan, owner) {
|
|
|
45554
45636
|
registerFace(table, {
|
|
45555
45637
|
name: "plane-cap",
|
|
45556
45638
|
normal: normal2,
|
|
45557
|
-
center,
|
|
45639
|
+
center: center2,
|
|
45558
45640
|
planar: true,
|
|
45559
45641
|
uAxis: basis.u,
|
|
45560
45642
|
vAxis: basis.v,
|
|
@@ -48454,6 +48536,99 @@ function computeSeatOverTranslation(targetFaceVertices, targetFaceNormal, selfMe
|
|
|
48454
48536
|
sampleCount: targetFaceVertices.length
|
|
48455
48537
|
};
|
|
48456
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
|
+
}
|
|
48457
48632
|
const _shapeDimensions = /* @__PURE__ */ new WeakMap();
|
|
48458
48633
|
let _shapeDimensionCounter = 0;
|
|
48459
48634
|
function nextShapeDimensionId() {
|
|
@@ -48826,8 +49001,8 @@ function mergeShapeSourceSpans(sources, target) {
|
|
|
48826
49001
|
records = /* @__PURE__ */ new Map();
|
|
48827
49002
|
_shapeSourceSpans.set(target, records);
|
|
48828
49003
|
}
|
|
48829
|
-
for (const [key2,
|
|
48830
|
-
if (!records.has(key2)) records.set(key2,
|
|
49004
|
+
for (const [key2, span2] of sourceRecords) {
|
|
49005
|
+
if (!records.has(key2)) records.set(key2, span2);
|
|
48831
49006
|
}
|
|
48832
49007
|
}
|
|
48833
49008
|
}
|
|
@@ -49233,7 +49408,7 @@ function annotateTruckPrimitiveAnalyticFaces(plan, topology) {
|
|
|
49233
49408
|
const radiusTop = (source.radiusTop ?? source.radius) * distanceScale;
|
|
49234
49409
|
const origin = transform.point(signedHeight >= 0 ? [0, 0, 0] : [0, 0, signedHeight]);
|
|
49235
49410
|
const sidePointRadius = (Math.abs(radiusBottom) + Math.abs(radiusTop)) / 2;
|
|
49236
|
-
const
|
|
49411
|
+
const center2 = transform.point([sidePointRadius / distanceScale, 0, signedHeight / 2]);
|
|
49237
49412
|
const normal2 = transformedUnitAxis(transform, [1, 0, 0]) ?? [1, 0, 0];
|
|
49238
49413
|
const surface = sameScalar(radiusBottom, radiusTop) ? { kind: "cylinder", origin, axis, radius: radiusBottom, height } : { kind: "cone", origin, axis, radiusBottom, radiusTop, height };
|
|
49239
49414
|
for (const [name, face] of faces) {
|
|
@@ -49244,7 +49419,7 @@ function annotateTruckPrimitiveAnalyticFaces(plan, topology) {
|
|
|
49244
49419
|
upsertAnalyticFace(faces, {
|
|
49245
49420
|
name: "side",
|
|
49246
49421
|
normal: normal2,
|
|
49247
|
-
center,
|
|
49422
|
+
center: center2,
|
|
49248
49423
|
planar: false,
|
|
49249
49424
|
surface
|
|
49250
49425
|
});
|
|
@@ -49254,8 +49429,8 @@ function annotateTruckPrimitiveAnalyticFaces(plan, topology) {
|
|
|
49254
49429
|
if (distanceScale === null) return;
|
|
49255
49430
|
const normal2 = transformedUnitAxis(transform, [1, 0, 0]) ?? [1, 0, 0];
|
|
49256
49431
|
const radius = source.radius * distanceScale;
|
|
49257
|
-
const
|
|
49258
|
-
const surface = { kind: "sphere", center, radius };
|
|
49432
|
+
const center2 = transform.point([0, 0, 0]);
|
|
49433
|
+
const surface = { kind: "sphere", center: center2, radius };
|
|
49259
49434
|
for (const [name, face] of faces) {
|
|
49260
49435
|
if (name === "surface" || /^north-\d+$/.test(name) || /^band-\d+-\d+$/.test(name) || /^south-\d+$/.test(name)) {
|
|
49261
49436
|
faces.set(name, { ...face, surface: cloneFaceSurface(surface) ?? surface });
|
|
@@ -49275,10 +49450,10 @@ function annotateTruckPrimitiveAnalyticFaces(plan, topology) {
|
|
|
49275
49450
|
const axis = transformedUnitAxis(transform, [0, 0, 1]);
|
|
49276
49451
|
const normal2 = transformedUnitAxis(transform, [1, 0, 0]) ?? [1, 0, 0];
|
|
49277
49452
|
if (!axis) return;
|
|
49278
|
-
const
|
|
49453
|
+
const center2 = transform.point([0, 0, 0]);
|
|
49279
49454
|
const surface = {
|
|
49280
49455
|
kind: "torus",
|
|
49281
|
-
center,
|
|
49456
|
+
center: center2,
|
|
49282
49457
|
axis,
|
|
49283
49458
|
majorRadius: source.majorRadius * distanceScale,
|
|
49284
49459
|
minorRadius: source.minorRadius * distanceScale
|
|
@@ -49453,7 +49628,7 @@ function addCompileAliasesToTopology(plan, topology) {
|
|
|
49453
49628
|
["top-rim", [radiusTop, 0, source.height], [0, radiusTop, source.height], [0, 0, source.height], radiusTop, [0, 0, 1]],
|
|
49454
49629
|
["bottom-rim", [source.radius, 0, 0], [0, source.radius, 0], [0, 0, 0], source.radius, [0, 0, -1]]
|
|
49455
49630
|
];
|
|
49456
|
-
for (const [name, start, end,
|
|
49631
|
+
for (const [name, start, end, center2, radius, axis] of semanticEdges2) {
|
|
49457
49632
|
if (topology.edges.has(name)) continue;
|
|
49458
49633
|
const faceName = name === "top-rim" ? "top" : "bottom";
|
|
49459
49634
|
topology.edges.set(name, {
|
|
@@ -49468,7 +49643,7 @@ function addCompileAliasesToTopology(plan, topology) {
|
|
|
49468
49643
|
},
|
|
49469
49644
|
curve: {
|
|
49470
49645
|
kind: "circle",
|
|
49471
|
-
center,
|
|
49646
|
+
center: center2,
|
|
49472
49647
|
axis,
|
|
49473
49648
|
radius,
|
|
49474
49649
|
faceName
|
|
@@ -49662,6 +49837,7 @@ function withBaseDimensions(base, out) {
|
|
|
49662
49837
|
if (baseTopo) _shapeTopology.set(out, cloneTopology(baseTopo));
|
|
49663
49838
|
const baseLabels = cloneFaceLabelMap(_shapeFaceLabels.get(base));
|
|
49664
49839
|
if (baseLabels) _shapeFaceLabels.set(out, baseLabels);
|
|
49840
|
+
if (base.materialProps) out.materialProps = { ...base.materialProps };
|
|
49665
49841
|
copyShapeReferenceMetadata(base, out);
|
|
49666
49842
|
copyShapeSourceSpans(base, out);
|
|
49667
49843
|
return setShapeCompilePlanInternal(out, getShapeCompilePlanInternal(base));
|
|
@@ -49750,6 +49926,30 @@ function createOwnedTopologyRewritePlan(plan, operation2, buildPropagation) {
|
|
|
49750
49926
|
const owner = createShapeQueryOwner(operation2);
|
|
49751
49927
|
return wrapShapeCompilePlanWithQueryOwner(attachTopologyRewritePropagation(plan, buildPropagation(owner)), owner);
|
|
49752
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
|
+
}
|
|
49753
49953
|
function parseReferencePath(path) {
|
|
49754
49954
|
const trimmed = path.trim();
|
|
49755
49955
|
if (!trimmed) throw new Error("Shape.ref() requires a non-empty path.");
|
|
@@ -50127,6 +50327,61 @@ class Shape {
|
|
|
50127
50327
|
out.materialProps = { ...this.materialProps ?? {}, ...props };
|
|
50128
50328
|
return out;
|
|
50129
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
|
+
}
|
|
50130
50385
|
/** Return a new Shape wrapper for explicit duplication in scripts. */
|
|
50131
50386
|
clone() {
|
|
50132
50387
|
const out = withCopiedDimensions(this, new Shape(getShapeRuntimeBackendInternal(this).clone(), this.colorHex));
|
|
@@ -50654,12 +50909,12 @@ class Shape {
|
|
|
50654
50909
|
scale(v) {
|
|
50655
50910
|
const scale2 = requireNonZeroFiniteScale3(v, "Shape.scale() scale");
|
|
50656
50911
|
const bb = this.boundingBox();
|
|
50657
|
-
const
|
|
50912
|
+
const center2 = [
|
|
50658
50913
|
(bb.min[0] + bb.max[0]) / 2,
|
|
50659
50914
|
(bb.min[1] + bb.max[1]) / 2,
|
|
50660
50915
|
(bb.min[2] + bb.max[2]) / 2
|
|
50661
50916
|
];
|
|
50662
|
-
return this.scaleAround(
|
|
50917
|
+
return this.scaleAround(center2, scale2);
|
|
50663
50918
|
}
|
|
50664
50919
|
/** Scale the shape uniformly or per-axis from an explicit pivot point. */
|
|
50665
50920
|
scaleAround(pivot, v) {
|
|
@@ -50693,12 +50948,12 @@ class Shape {
|
|
|
50693
50948
|
/** Mirror across a plane through the shape's bounding box center, defined by its normal vector. */
|
|
50694
50949
|
mirror(normal2) {
|
|
50695
50950
|
const bb = this.boundingBox();
|
|
50696
|
-
const
|
|
50951
|
+
const center2 = [
|
|
50697
50952
|
(bb.min[0] + bb.max[0]) / 2,
|
|
50698
50953
|
(bb.min[1] + bb.max[1]) / 2,
|
|
50699
50954
|
(bb.min[2] + bb.max[2]) / 2
|
|
50700
50955
|
];
|
|
50701
|
-
return this.mirrorThrough(
|
|
50956
|
+
return this.mirrorThrough(center2, normal2);
|
|
50702
50957
|
}
|
|
50703
50958
|
/** Mirror across a plane through an explicit point, defined by its normal vector. */
|
|
50704
50959
|
mirrorThrough(point2, normal2) {
|
|
@@ -51458,7 +51713,8 @@ class FrozenShapeBackend {
|
|
|
51458
51713
|
edgePositions: this._data.geometryEdgePositions,
|
|
51459
51714
|
triangleFaceIds: this._data.geometryTriangleFaceIds,
|
|
51460
51715
|
faceIdNames: this._data.geometryFaceIdNames,
|
|
51461
|
-
hasSmoothNormals: this._data.hasSmoothNormals ?? false
|
|
51716
|
+
hasSmoothNormals: this._data.hasSmoothNormals ?? false,
|
|
51717
|
+
uvs: this._data.geometryUvs
|
|
51462
51718
|
};
|
|
51463
51719
|
}
|
|
51464
51720
|
getMeshReconstructionData() {
|
|
@@ -51629,9 +51885,17 @@ const EDGE_THRESHOLD_DOT = Math.cos(Math.PI / 180);
|
|
|
51629
51885
|
const SMOOTH_THRESHOLD_DOT = Math.cos(30 * Math.PI / 180);
|
|
51630
51886
|
function computeGeometryArrays(mesh, options = {}) {
|
|
51631
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;
|
|
51632
51895
|
const useCornerNormals = !!cornerNormals && cornerNormals.length === triCount * 9;
|
|
51633
51896
|
const positions = new Float32Array(triCount * 9);
|
|
51634
51897
|
const normals = new Float32Array(triCount * 9);
|
|
51898
|
+
const uvs = hasStoredUvs ? new Float32Array(triCount * 6) : void 0;
|
|
51635
51899
|
const faceNx = new Float32Array(triCount);
|
|
51636
51900
|
const faceNy = new Float32Array(triCount);
|
|
51637
51901
|
const faceNz = new Float32Array(triCount);
|
|
@@ -51661,6 +51925,15 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
51661
51925
|
positions[o + 6] = cx;
|
|
51662
51926
|
positions[o + 7] = cy;
|
|
51663
51927
|
positions[o + 8] = cz;
|
|
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
|
+
}
|
|
51664
51937
|
if (useCornerNormals) {
|
|
51665
51938
|
for (let k2 = 0; k2 < 9; k2++) normals[o + k2] = cornerNormals[o + k2];
|
|
51666
51939
|
} else if (vertNormals) {
|
|
@@ -51673,13 +51946,13 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
51673
51946
|
normals[o + 6] = vertNormals[i2 * 3];
|
|
51674
51947
|
normals[o + 7] = vertNormals[i2 * 3 + 1];
|
|
51675
51948
|
normals[o + 8] = vertNormals[i2 * 3 + 2];
|
|
51676
|
-
} else if (
|
|
51949
|
+
} else if (hasStoredNormals) {
|
|
51677
51950
|
const corners = [i0, i1, i2];
|
|
51678
51951
|
for (let v = 0; v < 3; v++) {
|
|
51679
51952
|
const base = corners[v] * numProp;
|
|
51680
|
-
const nx = vertProperties[base +
|
|
51681
|
-
const ny = vertProperties[base +
|
|
51682
|
-
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];
|
|
51683
51956
|
const oc = o + v * 3;
|
|
51684
51957
|
if (nx * nx + ny * ny + nz * nz > 1e-12) {
|
|
51685
51958
|
normals[oc] = nx;
|
|
@@ -51706,7 +51979,7 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
51706
51979
|
faceNy[t] = fny;
|
|
51707
51980
|
faceNz[t] = fnz;
|
|
51708
51981
|
}
|
|
51709
|
-
if (!useCornerNormals && !vertNormals &&
|
|
51982
|
+
if (!useCornerNormals && !vertNormals && !hasStoredNormals && triCount > 0) {
|
|
51710
51983
|
computeAutoSmoothNormals(
|
|
51711
51984
|
triVerts,
|
|
51712
51985
|
vertProperties,
|
|
@@ -51725,7 +51998,8 @@ function computeGeometryArrays(mesh, options = {}) {
|
|
|
51725
51998
|
positions,
|
|
51726
51999
|
normals,
|
|
51727
52000
|
edgePositions,
|
|
51728
|
-
hasSmoothNormals: triCount > 0
|
|
52001
|
+
hasSmoothNormals: triCount > 0,
|
|
52002
|
+
uvs
|
|
51729
52003
|
};
|
|
51730
52004
|
}
|
|
51731
52005
|
function computeAutoSmoothNormals(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, normals, mergeFromVert, mergeToVert) {
|
|
@@ -51860,10 +52134,11 @@ function attachKernelFaceMetadata(geometry, triangleFaceIds, faceIdNames) {
|
|
|
51860
52134
|
}
|
|
51861
52135
|
function shapeToGeometry(shape) {
|
|
51862
52136
|
if (shape instanceof FrozenShape) {
|
|
51863
|
-
const { positions, normals, edgePositions, triangleFaceIds, faceIdNames, hasSmoothNormals } = shape.getPrecomputedGeometry();
|
|
52137
|
+
const { positions, normals, edgePositions, triangleFaceIds, faceIdNames, hasSmoothNormals, uvs } = shape.getPrecomputedGeometry();
|
|
51864
52138
|
const solid = new BufferGeometry();
|
|
51865
52139
|
solid.setAttribute("position", new BufferAttribute(positions, 3));
|
|
51866
52140
|
solid.setAttribute("normal", new BufferAttribute(normals, 3));
|
|
52141
|
+
if (uvs) solid.setAttribute("uv", new BufferAttribute(uvs, 2));
|
|
51867
52142
|
attachKernelFaceMetadata(solid, triangleFaceIds, faceIdNames);
|
|
51868
52143
|
const edges = new BufferGeometry();
|
|
51869
52144
|
edges.setAttribute("position", new BufferAttribute(edgePositions, 3));
|
|
@@ -51886,7 +52161,7 @@ function shapeToGeometryFallback(shape) {
|
|
|
51886
52161
|
} catch {
|
|
51887
52162
|
mesh = shape.getMesh();
|
|
51888
52163
|
}
|
|
51889
|
-
const { positions, normals, edgePositions, hasSmoothNormals } = computeGeometryArrays({
|
|
52164
|
+
const { positions, normals, edgePositions, hasSmoothNormals, uvs } = computeGeometryArrays({
|
|
51890
52165
|
numProp: mesh.numProp,
|
|
51891
52166
|
numTri: mesh.numTri,
|
|
51892
52167
|
triVerts: mesh.triVerts,
|
|
@@ -51899,6 +52174,7 @@ function shapeToGeometryFallback(shape) {
|
|
|
51899
52174
|
const solid = new BufferGeometry();
|
|
51900
52175
|
solid.setAttribute("position", new BufferAttribute(positions, 3));
|
|
51901
52176
|
solid.setAttribute("normal", new BufferAttribute(normals, 3));
|
|
52177
|
+
if (uvs) solid.setAttribute("uv", new BufferAttribute(uvs, 2));
|
|
51902
52178
|
attachKernelFaceMetadata(solid, mesh.faceID, mesh.faceIdNames);
|
|
51903
52179
|
const edges = new BufferGeometry();
|
|
51904
52180
|
edges.setAttribute("position", new BufferAttribute(edgePositions, 3));
|