forgecad 0.10.4 → 0.10.5
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-B3L3W1Uo.js → AdminPage-raksfnNA.js} +1 -1
- package/dist/assets/{BenchmarkPage-DXKVXMrJ.js → BenchmarkPage-DP3RxhPs.js} +2 -2
- package/dist/assets/{BlogPage-B7BWxOCg.js → BlogPage-D7Dos-vl.js} +1 -1
- package/dist/assets/{DocsPage-BPGGwht1.js → DocsPage-DO1kvBns.js} +7 -1
- package/dist/assets/{EditorApp-BWUGCdD5.js → EditorApp-DQJmcmRT.js} +9 -8
- package/dist/assets/{EmbedViewer-DygByZS2.js → EmbedViewer-DFDUhOma.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-BoVE7JGY.js → LandingPageProofDriven-DbE_tp8-.js} +2 -2
- package/dist/assets/{LegalPage-Din8wv8d.js → LegalPage-CominSso.js} +2 -2
- package/dist/assets/{PricingPage-C2PMzmDc.js → PricingPage-CcVIN9yj.js} +2 -2
- package/dist/assets/{SettingsPage-BlJDCRe8.js → SettingsPage-DLWcP289.js} +1 -1
- package/dist/assets/{app-BsRYSfxY.js → app-xW3hOdq9.js} +1135 -320
- package/dist/assets/{backendInit-6C0DLgH0.js → backendInit-mDHk97u7.js} +6630 -2493
- package/dist/assets/cli/{render-XXol_ET7.js → render--SIU27W_.js} +1263 -112
- package/dist/assets/{constructionHistoryWorker-cTHWRJEi.js → constructionHistoryWorker-uEe_Q7Kg.js} +1861 -610
- package/dist/assets/{evalWorker-BssDYW9u.js → evalWorker-BqyDHDcI.js} +6254 -2177
- package/dist/assets/{forgecad_geometry-CZ_IfuvA.js → forgecad_geometry-D8rWX7nQ.js} +1 -1
- package/dist/assets/{forgecad_geometry_bg-C3rQHfwg.wasm → forgecad_geometry_bg-ObqfqjJT.wasm} +0 -0
- package/dist/assets/{inspectWorker-ymhBV4Ll.js → inspectWorker-UXMxlcR8.js} +2738 -742
- package/dist/assets/{jointPose-B0blBj9A.js → jointPose-bYMlwU3v.js} +1 -1
- package/dist/assets/{landing-proof-driven-Cpf-MIbI.css → landing-proof-driven-_u4v_xQb.css} +2 -2
- package/dist/assets/{manifold-B_7QXpGB.js → manifold-BR7UYI4P.js} +1 -1
- package/dist/assets/{manifold-CYlIm-M6.js → manifold-CyOV5B9S.js} +2 -2
- package/dist/assets/{manifold-CNShmpEJ.js → manifold-D4d5NQst.js} +1 -1
- package/dist/assets/{reportWorker-Cb5eyM7D.js → reportWorker-DsaICZsn.js} +6010 -2032
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/CLI.md +4 -2
- package/dist/docs-raw/generated/assembly.md +76 -3
- package/dist/docs-raw/generated/concepts.md +31 -4
- package/dist/docs-raw/generated/core.md +159 -21
- package/dist/docs-raw/generated/curves.md +344 -6
- package/dist/docs-raw/generated/runtime-names.md +12 -12
- package/dist/docs-raw/generated/sketch.md +16 -3
- package/dist/docs-raw/guides/inspection-bundles.md +4 -2
- package/dist/docs-raw/guides/structural-fea.md +224 -0
- package/dist/docs-raw/skills/forgecad.md +1 -0
- package/dist/index.html +1 -1
- package/dist/sitemap.xml +15 -15
- package/dist-cli/{check-compiler-4RPB6SB5.js → check-compiler-7YAHVXYM.js} +1 -1
- package/dist-cli/{check-query-propagation-KN3DFQTX.js → check-query-propagation-ZRR6IOJW.js} +1 -1
- package/dist-cli/{chunk-UHBRMYA6.js → chunk-VNM67DIV.js} +6489 -2333
- package/dist-cli/forgecad.js +5258 -717
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +827 -45
- package/dist-skill/SKILL.md +1 -0
- package/dist-skill/docs/CLI.md +4 -2
- package/dist-skill/docs/generated/assembly.md +73 -3
- package/dist-skill/docs/generated/core.md +159 -21
- package/dist-skill/docs/generated/curves.md +343 -6
- package/dist-skill/docs/generated/runtime-names.md +12 -12
- package/dist-skill/docs/generated/sketch.md +16 -3
- package/dist-skill/docs/guides/inspection-bundles.md +4 -2
- package/dist-skill/docs/guides/structural-fea.md +224 -0
- package/dist-skill/website/skills/forgecad.md +1 -0
- package/examples/analysis/structural-stress-fea.forge.js +19 -0
- package/examples/api/blend-full-round.forge.js +37 -0
- package/examples/api/blend-variable-radius.forge.js +51 -0
- package/examples/api/curve-project-and-intersect.forge.js +59 -0
- package/examples/api/extrude-up-to-face.forge.js +47 -0
- package/examples/api/spoon-full-tang-handle.forge.js +148 -0
- package/examples/api/surface-boundarynet-dished-bowl.forge.js +63 -0
- package/examples/api/surface-fill-interior-constraints.forge.js +59 -0
- package/package.json +4 -1
- /package/dist/assets/{landing-proof-driven-BxZZh5r5.js → landing-proof-driven-DNPRKL_p.js} +0 -0
package/dist/assets/{constructionHistoryWorker-cTHWRJEi.js → constructionHistoryWorker-uEe_Q7Kg.js}
RENAMED
|
@@ -118,7 +118,7 @@ function denormalize(value, array) {
|
|
|
118
118
|
throw new Error("Invalid component type.");
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
function normalize$
|
|
121
|
+
function normalize$5(value, array) {
|
|
122
122
|
switch (array.constructor) {
|
|
123
123
|
case Float32Array:
|
|
124
124
|
return value;
|
|
@@ -1692,9 +1692,9 @@ class Quaternion {
|
|
|
1692
1692
|
let s = 1 - t;
|
|
1693
1693
|
const cos2 = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos2 >= 0 ? 1 : -1, sqrSin = 1 - cos2 * cos2;
|
|
1694
1694
|
if (sqrSin > Number.EPSILON) {
|
|
1695
|
-
const sin2 = Math.sqrt(sqrSin),
|
|
1696
|
-
s = Math.sin(s *
|
|
1697
|
-
t = Math.sin(t *
|
|
1695
|
+
const sin2 = Math.sqrt(sqrSin), len2 = Math.atan2(sin2, cos2 * dir);
|
|
1696
|
+
s = Math.sin(s * len2) / sin2;
|
|
1697
|
+
t = Math.sin(t * len2) / sin2;
|
|
1698
1698
|
}
|
|
1699
1699
|
const tDir = t * dir;
|
|
1700
1700
|
x0 = x0 * s + x1 * tDir;
|
|
@@ -6531,7 +6531,7 @@ class BufferAttribute {
|
|
|
6531
6531
|
* @return {BufferAttribute} A reference to this instance.
|
|
6532
6532
|
*/
|
|
6533
6533
|
setComponent(index2, component, value) {
|
|
6534
|
-
if (this.normalized) value = normalize$
|
|
6534
|
+
if (this.normalized) value = normalize$5(value, this.array);
|
|
6535
6535
|
this.array[index2 * this.itemSize + component] = value;
|
|
6536
6536
|
return this;
|
|
6537
6537
|
}
|
|
@@ -6554,7 +6554,7 @@ class BufferAttribute {
|
|
|
6554
6554
|
* @return {BufferAttribute} A reference to this instance.
|
|
6555
6555
|
*/
|
|
6556
6556
|
setX(index2, x2) {
|
|
6557
|
-
if (this.normalized) x2 = normalize$
|
|
6557
|
+
if (this.normalized) x2 = normalize$5(x2, this.array);
|
|
6558
6558
|
this.array[index2 * this.itemSize] = x2;
|
|
6559
6559
|
return this;
|
|
6560
6560
|
}
|
|
@@ -6577,7 +6577,7 @@ class BufferAttribute {
|
|
|
6577
6577
|
* @return {BufferAttribute} A reference to this instance.
|
|
6578
6578
|
*/
|
|
6579
6579
|
setY(index2, y2) {
|
|
6580
|
-
if (this.normalized) y2 = normalize$
|
|
6580
|
+
if (this.normalized) y2 = normalize$5(y2, this.array);
|
|
6581
6581
|
this.array[index2 * this.itemSize + 1] = y2;
|
|
6582
6582
|
return this;
|
|
6583
6583
|
}
|
|
@@ -6600,7 +6600,7 @@ class BufferAttribute {
|
|
|
6600
6600
|
* @return {BufferAttribute} A reference to this instance.
|
|
6601
6601
|
*/
|
|
6602
6602
|
setZ(index2, z2) {
|
|
6603
|
-
if (this.normalized) z2 = normalize$
|
|
6603
|
+
if (this.normalized) z2 = normalize$5(z2, this.array);
|
|
6604
6604
|
this.array[index2 * this.itemSize + 2] = z2;
|
|
6605
6605
|
return this;
|
|
6606
6606
|
}
|
|
@@ -6623,7 +6623,7 @@ class BufferAttribute {
|
|
|
6623
6623
|
* @return {BufferAttribute} A reference to this instance.
|
|
6624
6624
|
*/
|
|
6625
6625
|
setW(index2, w2) {
|
|
6626
|
-
if (this.normalized) w2 = normalize$
|
|
6626
|
+
if (this.normalized) w2 = normalize$5(w2, this.array);
|
|
6627
6627
|
this.array[index2 * this.itemSize + 3] = w2;
|
|
6628
6628
|
return this;
|
|
6629
6629
|
}
|
|
@@ -6638,8 +6638,8 @@ class BufferAttribute {
|
|
|
6638
6638
|
setXY(index2, x2, y2) {
|
|
6639
6639
|
index2 *= this.itemSize;
|
|
6640
6640
|
if (this.normalized) {
|
|
6641
|
-
x2 = normalize$
|
|
6642
|
-
y2 = normalize$
|
|
6641
|
+
x2 = normalize$5(x2, this.array);
|
|
6642
|
+
y2 = normalize$5(y2, this.array);
|
|
6643
6643
|
}
|
|
6644
6644
|
this.array[index2 + 0] = x2;
|
|
6645
6645
|
this.array[index2 + 1] = y2;
|
|
@@ -6657,9 +6657,9 @@ class BufferAttribute {
|
|
|
6657
6657
|
setXYZ(index2, x2, y2, z2) {
|
|
6658
6658
|
index2 *= this.itemSize;
|
|
6659
6659
|
if (this.normalized) {
|
|
6660
|
-
x2 = normalize$
|
|
6661
|
-
y2 = normalize$
|
|
6662
|
-
z2 = normalize$
|
|
6660
|
+
x2 = normalize$5(x2, this.array);
|
|
6661
|
+
y2 = normalize$5(y2, this.array);
|
|
6662
|
+
z2 = normalize$5(z2, this.array);
|
|
6663
6663
|
}
|
|
6664
6664
|
this.array[index2 + 0] = x2;
|
|
6665
6665
|
this.array[index2 + 1] = y2;
|
|
@@ -6679,10 +6679,10 @@ class BufferAttribute {
|
|
|
6679
6679
|
setXYZW(index2, x2, y2, z2, w2) {
|
|
6680
6680
|
index2 *= this.itemSize;
|
|
6681
6681
|
if (this.normalized) {
|
|
6682
|
-
x2 = normalize$
|
|
6683
|
-
y2 = normalize$
|
|
6684
|
-
z2 = normalize$
|
|
6685
|
-
w2 = normalize$
|
|
6682
|
+
x2 = normalize$5(x2, this.array);
|
|
6683
|
+
y2 = normalize$5(y2, this.array);
|
|
6684
|
+
z2 = normalize$5(z2, this.array);
|
|
6685
|
+
w2 = normalize$5(w2, this.array);
|
|
6686
6686
|
}
|
|
6687
6687
|
this.array[index2 + 0] = x2;
|
|
6688
6688
|
this.array[index2 + 1] = y2;
|
|
@@ -7688,9 +7688,9 @@ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
|
|
|
7688
7688
|
}
|
|
7689
7689
|
function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
7690
7690
|
const queue = [];
|
|
7691
|
-
for (let i = 0,
|
|
7691
|
+
for (let i = 0, len2 = holeIndices.length; i < len2; i++) {
|
|
7692
7692
|
const start = holeIndices[i] * dim;
|
|
7693
|
-
const end = i <
|
|
7693
|
+
const end = i < len2 - 1 ? holeIndices[i + 1] * dim : data.length;
|
|
7694
7694
|
const list = linkedList(data, start, end, dim, false);
|
|
7695
7695
|
if (list === list.next) list.steiner = true;
|
|
7696
7696
|
queue.push(getLeftmost(list));
|
|
@@ -8200,9 +8200,9 @@ function multiplyMat4(a2, b) {
|
|
|
8200
8200
|
return out;
|
|
8201
8201
|
}
|
|
8202
8202
|
function normalizeVec3$3(v) {
|
|
8203
|
-
const
|
|
8204
|
-
if (
|
|
8205
|
-
return [v[0] /
|
|
8203
|
+
const len2 = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
8204
|
+
if (len2 < EPS$6) throw new Error("Axis must be non-zero");
|
|
8205
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
8206
8206
|
}
|
|
8207
8207
|
function transformPoint$2(m2, p2, w2) {
|
|
8208
8208
|
const x2 = p2[0], y2 = p2[1], z2 = p2[2];
|
|
@@ -10285,6 +10285,8 @@ function findShapePrimaryQueryOwner(plan) {
|
|
|
10285
10285
|
case "chamfer":
|
|
10286
10286
|
case "filletEdges":
|
|
10287
10287
|
case "cornerYBlend":
|
|
10288
|
+
case "faceFillet":
|
|
10289
|
+
case "fullRound":
|
|
10288
10290
|
case "chamferEdges":
|
|
10289
10291
|
case "draft":
|
|
10290
10292
|
case "offsetSolid":
|
|
@@ -10309,6 +10311,7 @@ function findShapePrimaryQueryOwner(plan) {
|
|
|
10309
10311
|
case "importedMesh":
|
|
10310
10312
|
case "sdf":
|
|
10311
10313
|
case "fromSlices":
|
|
10314
|
+
case "fromSectionFrames":
|
|
10312
10315
|
case "analyticSurface":
|
|
10313
10316
|
case "nurbsSurface":
|
|
10314
10317
|
case "surfaceRuled":
|
|
@@ -10331,6 +10334,8 @@ function visitBaseShape(plan, visit) {
|
|
|
10331
10334
|
case "chamfer":
|
|
10332
10335
|
case "filletEdges":
|
|
10333
10336
|
case "cornerYBlend":
|
|
10337
|
+
case "faceFillet":
|
|
10338
|
+
case "fullRound":
|
|
10334
10339
|
case "chamferEdges":
|
|
10335
10340
|
case "draft":
|
|
10336
10341
|
case "offsetSolid":
|
|
@@ -10356,6 +10361,7 @@ function visitBaseShape(plan, visit) {
|
|
|
10356
10361
|
case "importedMesh":
|
|
10357
10362
|
case "sdf":
|
|
10358
10363
|
case "fromSlices":
|
|
10364
|
+
case "fromSectionFrames":
|
|
10359
10365
|
case "analyticSurface":
|
|
10360
10366
|
case "nurbsSurface":
|
|
10361
10367
|
case "surfaceRuled":
|
|
@@ -10509,6 +10515,13 @@ function cloneAnalyticSurfaceCompilePlan(surface) {
|
|
|
10509
10515
|
return assertExhaustive(surface);
|
|
10510
10516
|
}
|
|
10511
10517
|
}
|
|
10518
|
+
function cloneVariableRadiusSpec(spec) {
|
|
10519
|
+
if (!spec) return void 0;
|
|
10520
|
+
if ("stations" in spec) {
|
|
10521
|
+
return { kind: "stations", stations: spec.stations.map((s) => ({ at: canonicalNumber(s.at), radius: canonicalNumber(s.radius) })) };
|
|
10522
|
+
}
|
|
10523
|
+
return { kind: "linear", start: canonicalNumber(spec.start), end: canonicalNumber(spec.end) };
|
|
10524
|
+
}
|
|
10512
10525
|
function cloneProfileTransform(step) {
|
|
10513
10526
|
switch (step.kind) {
|
|
10514
10527
|
case "translate":
|
|
@@ -10576,6 +10589,12 @@ function cloneSurfaceBoundaryCompilePlan(plan) {
|
|
|
10576
10589
|
support: plan.support ? cloneSurfaceSupportCompilePlan(plan.support) : void 0
|
|
10577
10590
|
};
|
|
10578
10591
|
}
|
|
10592
|
+
function cloneSurfaceInteriorConstraintCompilePlan(plan) {
|
|
10593
|
+
return {
|
|
10594
|
+
curve: cloneSweepPathCompilePlan(plan.curve),
|
|
10595
|
+
continuity: plan.continuity
|
|
10596
|
+
};
|
|
10597
|
+
}
|
|
10579
10598
|
function cloneHoleCounterboreCompilePlan(plan) {
|
|
10580
10599
|
return {
|
|
10581
10600
|
radius: canonicalNumber(plan.radius),
|
|
@@ -10899,7 +10918,7 @@ function cloneProfileEdge(edge) {
|
|
|
10899
10918
|
}
|
|
10900
10919
|
}
|
|
10901
10920
|
function cloneShapeCompilePlan(plan) {
|
|
10902
|
-
var _a3, _b3, _c2;
|
|
10921
|
+
var _a3, _b3, _c2, _d2, _e2;
|
|
10903
10922
|
if (!plan) return null;
|
|
10904
10923
|
let result;
|
|
10905
10924
|
switch (plan.kind) {
|
|
@@ -11078,6 +11097,7 @@ function cloneShapeCompilePlan(plan) {
|
|
|
11078
11097
|
kind: "filletEdges",
|
|
11079
11098
|
base: cloneShapeCompilePlan(plan.base),
|
|
11080
11099
|
radius: plan.radius,
|
|
11100
|
+
variableRadius: cloneVariableRadiusSpec(plan.variableRadius),
|
|
11081
11101
|
segments: plan.segments,
|
|
11082
11102
|
continuity: plan.continuity,
|
|
11083
11103
|
edgeTargets: (_a3 = plan.edgeTargets) == null ? void 0 : _a3.map(cloneEdgeFeatureTarget),
|
|
@@ -11085,6 +11105,32 @@ function cloneShapeCompilePlan(plan) {
|
|
|
11085
11105
|
edgeSelection: plan.edgeSelection
|
|
11086
11106
|
};
|
|
11087
11107
|
break;
|
|
11108
|
+
case "faceFillet":
|
|
11109
|
+
result = {
|
|
11110
|
+
kind: "faceFillet",
|
|
11111
|
+
base: cloneShapeCompilePlan(plan.base),
|
|
11112
|
+
radius: plan.radius,
|
|
11113
|
+
variableRadius: cloneVariableRadiusSpec(plan.variableRadius),
|
|
11114
|
+
segments: plan.segments,
|
|
11115
|
+
continuity: plan.continuity,
|
|
11116
|
+
faceA: cloneFaceQueryRef(plan.faceA),
|
|
11117
|
+
faceB: cloneFaceQueryRef(plan.faceB),
|
|
11118
|
+
edgeTargets: plan.edgeTargets.map(cloneEdgeFeatureTarget)
|
|
11119
|
+
};
|
|
11120
|
+
break;
|
|
11121
|
+
case "fullRound":
|
|
11122
|
+
result = {
|
|
11123
|
+
kind: "fullRound",
|
|
11124
|
+
base: cloneShapeCompilePlan(plan.base),
|
|
11125
|
+
radius: plan.radius,
|
|
11126
|
+
segments: plan.segments,
|
|
11127
|
+
continuity: plan.continuity,
|
|
11128
|
+
centerFace: cloneFaceQueryRef(plan.centerFace),
|
|
11129
|
+
sideFaceA: cloneFaceQueryRef(plan.sideFaceA),
|
|
11130
|
+
sideFaceB: cloneFaceQueryRef(plan.sideFaceB),
|
|
11131
|
+
edgeTargets: plan.edgeTargets.map(cloneEdgeFeatureTarget)
|
|
11132
|
+
};
|
|
11133
|
+
break;
|
|
11088
11134
|
case "cornerYBlend":
|
|
11089
11135
|
result = {
|
|
11090
11136
|
kind: "cornerYBlend",
|
|
@@ -11156,6 +11202,20 @@ function cloneShapeCompilePlan(plan) {
|
|
|
11156
11202
|
boundsPadding: canonicalNumber(plan.boundsPadding)
|
|
11157
11203
|
};
|
|
11158
11204
|
break;
|
|
11205
|
+
case "fromSectionFrames":
|
|
11206
|
+
result = {
|
|
11207
|
+
kind: "fromSectionFrames",
|
|
11208
|
+
slices: plan.slices.map((s) => ({
|
|
11209
|
+
origin: s.origin.map(canonicalNumber),
|
|
11210
|
+
normal: s.normal.map(canonicalNumber),
|
|
11211
|
+
tangentU: s.tangentU.map(canonicalNumber),
|
|
11212
|
+
tangentV: s.tangentV.map(canonicalNumber),
|
|
11213
|
+
profile: cloneProfileCompilePlan(s.profile)
|
|
11214
|
+
})),
|
|
11215
|
+
edgeLength: canonicalNumber(plan.edgeLength),
|
|
11216
|
+
boundsPadding: canonicalNumber(plan.boundsPadding)
|
|
11217
|
+
};
|
|
11218
|
+
break;
|
|
11159
11219
|
case "importedStep":
|
|
11160
11220
|
result = { kind: "importedStep", filePath: plan.filePath, fileData: plan.fileData };
|
|
11161
11221
|
break;
|
|
@@ -11217,7 +11277,9 @@ function cloneShapeCompilePlan(plan) {
|
|
|
11217
11277
|
boundaries: plan.boundaries.map(cloneSurfaceBoundaryCompilePlan),
|
|
11218
11278
|
style: plan.style,
|
|
11219
11279
|
resolution: plan.resolution,
|
|
11220
|
-
allowApproximation: plan.allowApproximation
|
|
11280
|
+
allowApproximation: plan.allowApproximation,
|
|
11281
|
+
interiorCurves: (_d2 = plan.interiorCurves) == null ? void 0 : _d2.map(cloneSurfaceInteriorConstraintCompilePlan),
|
|
11282
|
+
interiorPoints: (_e2 = plan.interiorPoints) == null ? void 0 : _e2.map(canonicalVec3)
|
|
11221
11283
|
};
|
|
11222
11284
|
break;
|
|
11223
11285
|
case "surfaceSew":
|
|
@@ -11344,12 +11406,12 @@ function profilePlanFromCrossSection(cross4) {
|
|
|
11344
11406
|
return { kind: "boolean", op: "union", profiles: plans, transforms: [] };
|
|
11345
11407
|
}
|
|
11346
11408
|
function buildTrimByPlaneShapeCompilePlan(base, normal2, originOffset) {
|
|
11347
|
-
const
|
|
11348
|
-
if (
|
|
11349
|
-
const nx = normal2[0] /
|
|
11350
|
-
const ny = normal2[1] /
|
|
11351
|
-
const nz = normal2[2] /
|
|
11352
|
-
const adjustedOffset = originOffset /
|
|
11409
|
+
const len2 = Math.sqrt(normal2[0] * normal2[0] + normal2[1] * normal2[1] + normal2[2] * normal2[2]);
|
|
11410
|
+
if (len2 === 0) throw new Error("trimByPlane: normal vector must not be zero");
|
|
11411
|
+
const nx = normal2[0] / len2;
|
|
11412
|
+
const ny = normal2[1] / len2;
|
|
11413
|
+
const nz = normal2[2] / len2;
|
|
11414
|
+
const adjustedOffset = originOffset / len2;
|
|
11353
11415
|
return {
|
|
11354
11416
|
kind: "trimByPlane",
|
|
11355
11417
|
base: cloneShapeCompilePlan(base),
|
|
@@ -11378,6 +11440,10 @@ function buildSurfaceThickenShapeCompilePlan(base, thickness) {
|
|
|
11378
11440
|
thickness: canonicalNumber(thickness)
|
|
11379
11441
|
};
|
|
11380
11442
|
}
|
|
11443
|
+
function maxVariableRadius(spec) {
|
|
11444
|
+
if ("stations" in spec) return Math.max(...spec.stations.map((s) => s.radius));
|
|
11445
|
+
return Math.max(spec.start, spec.end);
|
|
11446
|
+
}
|
|
11381
11447
|
function generateClampedKnots(n, degree) {
|
|
11382
11448
|
const m2 = n + degree + 1;
|
|
11383
11449
|
const knots = new Array(m2);
|
|
@@ -11531,32 +11597,32 @@ function remapToKnotDomain(t, n, degree, knots) {
|
|
|
11531
11597
|
return uMin + Math.max(0, Math.min(1, t)) * (uMax - uMin);
|
|
11532
11598
|
}
|
|
11533
11599
|
const EPSILON$1 = 1e-9;
|
|
11534
|
-
function add$
|
|
11600
|
+
function add$3(a2, b) {
|
|
11535
11601
|
return [a2[0] + b[0], a2[1] + b[1], a2[2] + b[2]];
|
|
11536
11602
|
}
|
|
11537
11603
|
function scale$2(v, factor) {
|
|
11538
11604
|
return [v[0] * factor, v[1] * factor, v[2] * factor];
|
|
11539
11605
|
}
|
|
11540
|
-
function sub$
|
|
11606
|
+
function sub$4(a2, b) {
|
|
11541
11607
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
11542
11608
|
}
|
|
11543
|
-
function dot$
|
|
11609
|
+
function dot$4(a2, b) {
|
|
11544
11610
|
return a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2];
|
|
11545
11611
|
}
|
|
11546
|
-
function cross$
|
|
11612
|
+
function cross$4(a2, b) {
|
|
11547
11613
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
11548
11614
|
}
|
|
11549
11615
|
function rotateAroundAxis$1(v, axis, angleRad) {
|
|
11550
11616
|
const c2 = Math.cos(angleRad);
|
|
11551
11617
|
const s = Math.sin(angleRad);
|
|
11552
11618
|
const term1 = scale$2(v, c2);
|
|
11553
|
-
const term2 = scale$2(cross$
|
|
11554
|
-
const term3 = scale$2(axis, dot$
|
|
11555
|
-
return add$
|
|
11619
|
+
const term2 = scale$2(cross$4(axis, v), s);
|
|
11620
|
+
const term3 = scale$2(axis, dot$4(axis, v) * (1 - c2));
|
|
11621
|
+
return add$3(add$3(term1, term2), term3);
|
|
11556
11622
|
}
|
|
11557
11623
|
function arcPointAt(segment, t) {
|
|
11558
11624
|
const sweepRad = segment.sweepDeg * Math.PI / 180 * t;
|
|
11559
|
-
return add$
|
|
11625
|
+
return add$3(segment.center, rotateAroundAxis$1(sub$4(segment.start, segment.center), segment.axis, sweepRad));
|
|
11560
11626
|
}
|
|
11561
11627
|
function pushUnique(points, point2) {
|
|
11562
11628
|
const prev = points[points.length - 1];
|
|
@@ -11579,7 +11645,7 @@ function routePointAt(plan, t) {
|
|
|
11579
11645
|
const next = station + segment.length;
|
|
11580
11646
|
if (target <= next || segment === plan.segments[plan.segments.length - 1]) {
|
|
11581
11647
|
const localT = segment.length <= EPSILON$1 ? 1 : Math.max(0, Math.min(1, (target - station) / segment.length));
|
|
11582
|
-
if (segment.kind === "line") return add$
|
|
11648
|
+
if (segment.kind === "line") return add$3(segment.from, scale$2(sub$4(segment.to, segment.from), localT));
|
|
11583
11649
|
return arcPointAt(segment, localT);
|
|
11584
11650
|
}
|
|
11585
11651
|
station = next;
|
|
@@ -11599,7 +11665,7 @@ function sampleRoute3DCompilePlan(plan, options) {
|
|
|
11599
11665
|
pushUnique(points, start);
|
|
11600
11666
|
for (let i = 1; i <= intervals; i += 1) {
|
|
11601
11667
|
if (segment.kind === "line") {
|
|
11602
|
-
pushUnique(points, add$
|
|
11668
|
+
pushUnique(points, add$3(segment.from, scale$2(sub$4(segment.to, segment.from), i / intervals)));
|
|
11603
11669
|
} else {
|
|
11604
11670
|
pushUnique(points, arcPointAt(segment, i / intervals));
|
|
11605
11671
|
}
|
|
@@ -11830,10 +11896,10 @@ const EPS$4 = 1e-8;
|
|
|
11830
11896
|
function midpoint$1(start, end) {
|
|
11831
11897
|
return [(start[0] + end[0]) * 0.5, (start[1] + end[1]) * 0.5, (start[2] + end[2]) * 0.5];
|
|
11832
11898
|
}
|
|
11833
|
-
function normalize$
|
|
11834
|
-
const
|
|
11835
|
-
if (
|
|
11836
|
-
return [v[0] /
|
|
11899
|
+
function normalize$4(v) {
|
|
11900
|
+
const len2 = Math.hypot(v[0], v[1], v[2]);
|
|
11901
|
+
if (len2 <= EPS$4) throw new Error("Edge feature selection requires a non-zero direction vector");
|
|
11902
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
11837
11903
|
}
|
|
11838
11904
|
function subtract(a2, b) {
|
|
11839
11905
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
@@ -11910,11 +11976,11 @@ function rigidTransformForEdgeStep(step) {
|
|
|
11910
11976
|
return Transform.rotationAxis([step.axisX, step.axisY, step.axisZ], step.degrees, [step.pivotX, step.pivotY, step.pivotZ]);
|
|
11911
11977
|
case "mirror": {
|
|
11912
11978
|
const [nx0, ny0, nz0] = [step.normalX, step.normalY, step.normalZ];
|
|
11913
|
-
const
|
|
11914
|
-
if (
|
|
11915
|
-
const nx = nx0 /
|
|
11916
|
-
const ny = ny0 /
|
|
11917
|
-
const nz = nz0 /
|
|
11979
|
+
const len2 = Math.hypot(nx0, ny0, nz0);
|
|
11980
|
+
if (len2 <= EPS$4) return Transform.identity();
|
|
11981
|
+
const nx = nx0 / len2;
|
|
11982
|
+
const ny = ny0 / len2;
|
|
11983
|
+
const nz = nz0 / len2;
|
|
11918
11984
|
return Transform.from([
|
|
11919
11985
|
1 - 2 * nx * nx,
|
|
11920
11986
|
-2 * ny * nx,
|
|
@@ -11994,6 +12060,8 @@ function searchOwnerMatch(plan, owner) {
|
|
|
11994
12060
|
}
|
|
11995
12061
|
case "filletEdges":
|
|
11996
12062
|
case "cornerYBlend":
|
|
12063
|
+
case "faceFillet":
|
|
12064
|
+
case "fullRound":
|
|
11997
12065
|
case "chamferEdges":
|
|
11998
12066
|
case "draft":
|
|
11999
12067
|
case "offsetSolid":
|
|
@@ -12027,6 +12095,7 @@ function searchOwnerMatch(plan, owner) {
|
|
|
12027
12095
|
case "importedMesh":
|
|
12028
12096
|
case "sdf":
|
|
12029
12097
|
case "fromSlices":
|
|
12098
|
+
case "fromSectionFrames":
|
|
12030
12099
|
case "analyticSurface":
|
|
12031
12100
|
case "nurbsSurface":
|
|
12032
12101
|
case "surfaceRuled":
|
|
@@ -12116,7 +12185,7 @@ function propagateCandidateAcrossRewrite(plan, candidate) {
|
|
|
12116
12185
|
return edgeSuccess(candidate.selection, preservedEntry.query);
|
|
12117
12186
|
}
|
|
12118
12187
|
function resolvePropagatedEdgeQueryAtOwnerBase(ownerBase, ref) {
|
|
12119
|
-
if (ownerBase.kind === "box" || ownerBase.kind === "cylinder" || ownerBase.kind === "sphere" || ownerBase.kind === "extrude" || ownerBase.kind === "sheetMetal" || ownerBase.kind === "revolve" || ownerBase.kind === "loft" || ownerBase.kind === "sweep" || ownerBase.kind === "variableSweep" || ownerBase.kind === "transform" || ownerBase.kind === "queryOwner" || ownerBase.kind === "filletEdges" || ownerBase.kind === "cornerYBlend" || ownerBase.kind === "chamferEdges" || ownerBase.kind === "importedMesh" || ownerBase.kind === "sdf" || ownerBase.kind === "fromSlices" || ownerBase.kind === "analyticSurface" || ownerBase.kind === "nurbsSurface" || ownerBase.kind === "surfaceRuled" || ownerBase.kind === "surfaceFill" || ownerBase.kind === "surfaceSew" || ownerBase.kind === "surfaceExtend" || ownerBase.kind === "surfaceThicken" || ownerBase.kind === "surfaceSolid" || ownerBase.kind === "importedStep" || ownerBase.kind === "torus" || ownerBase.kind === "draft" || ownerBase.kind === "offsetSolid") {
|
|
12188
|
+
if (ownerBase.kind === "box" || ownerBase.kind === "cylinder" || ownerBase.kind === "sphere" || ownerBase.kind === "extrude" || ownerBase.kind === "sheetMetal" || ownerBase.kind === "revolve" || ownerBase.kind === "loft" || ownerBase.kind === "sweep" || ownerBase.kind === "variableSweep" || ownerBase.kind === "transform" || ownerBase.kind === "queryOwner" || ownerBase.kind === "filletEdges" || ownerBase.kind === "cornerYBlend" || ownerBase.kind === "faceFillet" || ownerBase.kind === "fullRound" || ownerBase.kind === "chamferEdges" || ownerBase.kind === "importedMesh" || ownerBase.kind === "sdf" || ownerBase.kind === "fromSlices" || ownerBase.kind === "fromSectionFrames" || ownerBase.kind === "analyticSurface" || ownerBase.kind === "nurbsSurface" || ownerBase.kind === "surfaceRuled" || ownerBase.kind === "surfaceFill" || ownerBase.kind === "surfaceSew" || ownerBase.kind === "surfaceExtend" || ownerBase.kind === "surfaceThicken" || ownerBase.kind === "surfaceSolid" || ownerBase.kind === "importedStep" || ownerBase.kind === "torus" || ownerBase.kind === "draft" || ownerBase.kind === "offsetSolid") {
|
|
12120
12189
|
return edgeIssue(
|
|
12121
12190
|
"edge-query-propagation-mismatch",
|
|
12122
12191
|
"The selected propagated edge query does not point at a topology-rewrite result on this target shape."
|
|
@@ -12249,8 +12318,8 @@ function extrudeEdgeSelection(plan, edgeName) {
|
|
|
12249
12318
|
}
|
|
12250
12319
|
const points = plan.profile.points;
|
|
12251
12320
|
const [bl, br, _tr, tl] = points;
|
|
12252
|
-
const u2 = normalize$
|
|
12253
|
-
const v = normalize$
|
|
12321
|
+
const u2 = normalize$4([br[0] - bl[0], br[1] - bl[1], 0]);
|
|
12322
|
+
const v = normalize$4([tl[0] - bl[0], tl[1] - bl[1], 0]);
|
|
12254
12323
|
const vertex2 = points[index2];
|
|
12255
12324
|
const quadrant = index2 === 0 ? [1, -1] : index2 === 1 ? [-1, -1] : index2 === 2 ? [-1, 1] : [1, 1];
|
|
12256
12325
|
return {
|
|
@@ -12271,9 +12340,9 @@ function extrudeEdgeSelection(plan, edgeName) {
|
|
|
12271
12340
|
function applySelectionTransform(selection, transform) {
|
|
12272
12341
|
const start = transform.point(selection.start);
|
|
12273
12342
|
const end = transform.point(selection.end);
|
|
12274
|
-
const basisX = normalize$
|
|
12275
|
-
const basisY = normalize$
|
|
12276
|
-
const axis = normalize$
|
|
12343
|
+
const basisX = normalize$4(transform.vector(selection.basisX));
|
|
12344
|
+
const basisY = normalize$4(transform.vector(selection.basisY));
|
|
12345
|
+
const axis = normalize$4(subtract(end, start));
|
|
12277
12346
|
return {
|
|
12278
12347
|
kind: "line-segment",
|
|
12279
12348
|
edgeName: selection.edgeName,
|
|
@@ -12308,6 +12377,8 @@ function resolveSelectionFromOwnerBase(plan, edgeName) {
|
|
|
12308
12377
|
case "chamfer":
|
|
12309
12378
|
case "filletEdges":
|
|
12310
12379
|
case "cornerYBlend":
|
|
12380
|
+
case "faceFillet":
|
|
12381
|
+
case "fullRound":
|
|
12311
12382
|
case "chamferEdges":
|
|
12312
12383
|
case "draft":
|
|
12313
12384
|
case "offsetSolid":
|
|
@@ -12327,6 +12398,7 @@ function resolveSelectionFromOwnerBase(plan, edgeName) {
|
|
|
12327
12398
|
case "importedMesh":
|
|
12328
12399
|
case "sdf":
|
|
12329
12400
|
case "fromSlices":
|
|
12401
|
+
case "fromSectionFrames":
|
|
12330
12402
|
case "analyticSurface":
|
|
12331
12403
|
case "nurbsSurface":
|
|
12332
12404
|
case "surfaceRuled":
|
|
@@ -12370,7 +12442,7 @@ function resolveSupportedEdgeFeatureSelection(plan, ref) {
|
|
|
12370
12442
|
return edgeIssue("edge-query-unsupported-after-rewrite", descendant.reason);
|
|
12371
12443
|
}
|
|
12372
12444
|
function resolveEdgeChainAtOwnerBase(ownerBase, ref) {
|
|
12373
|
-
if (ownerBase.kind === "box" || ownerBase.kind === "cylinder" || ownerBase.kind === "sphere" || ownerBase.kind === "extrude" || ownerBase.kind === "sheetMetal" || ownerBase.kind === "revolve" || ownerBase.kind === "loft" || ownerBase.kind === "sweep" || ownerBase.kind === "variableSweep" || ownerBase.kind === "transform" || ownerBase.kind === "queryOwner" || ownerBase.kind === "filletEdges" || ownerBase.kind === "cornerYBlend" || ownerBase.kind === "chamferEdges" || ownerBase.kind === "importedMesh" || ownerBase.kind === "sdf" || ownerBase.kind === "fromSlices" || ownerBase.kind === "analyticSurface" || ownerBase.kind === "nurbsSurface" || ownerBase.kind === "surfaceRuled" || ownerBase.kind === "surfaceFill" || ownerBase.kind === "surfaceSew" || ownerBase.kind === "surfaceExtend" || ownerBase.kind === "surfaceThicken" || ownerBase.kind === "surfaceSolid" || ownerBase.kind === "importedStep" || ownerBase.kind === "torus" || ownerBase.kind === "draft" || ownerBase.kind === "offsetSolid") {
|
|
12445
|
+
if (ownerBase.kind === "box" || ownerBase.kind === "cylinder" || ownerBase.kind === "sphere" || ownerBase.kind === "extrude" || ownerBase.kind === "sheetMetal" || ownerBase.kind === "revolve" || ownerBase.kind === "loft" || ownerBase.kind === "sweep" || ownerBase.kind === "variableSweep" || ownerBase.kind === "transform" || ownerBase.kind === "queryOwner" || ownerBase.kind === "filletEdges" || ownerBase.kind === "cornerYBlend" || ownerBase.kind === "faceFillet" || ownerBase.kind === "fullRound" || ownerBase.kind === "chamferEdges" || ownerBase.kind === "importedMesh" || ownerBase.kind === "sdf" || ownerBase.kind === "fromSlices" || ownerBase.kind === "fromSectionFrames" || ownerBase.kind === "analyticSurface" || ownerBase.kind === "nurbsSurface" || ownerBase.kind === "surfaceRuled" || ownerBase.kind === "surfaceFill" || ownerBase.kind === "surfaceSew" || ownerBase.kind === "surfaceExtend" || ownerBase.kind === "surfaceThicken" || ownerBase.kind === "surfaceSolid" || ownerBase.kind === "importedStep" || ownerBase.kind === "torus" || ownerBase.kind === "draft" || ownerBase.kind === "offsetSolid") {
|
|
12374
12446
|
return {
|
|
12375
12447
|
kind: "unsupported",
|
|
12376
12448
|
query: cloneEdgeQueryRef(ref),
|
|
@@ -12403,7 +12475,7 @@ function resolveEdgeChainAtOwnerBase(ownerBase, ref) {
|
|
|
12403
12475
|
};
|
|
12404
12476
|
}
|
|
12405
12477
|
function resolveCreatedEdgeChainAtOwnerBase(ownerBase, ref) {
|
|
12406
|
-
if (ownerBase.kind === "box" || ownerBase.kind === "cylinder" || ownerBase.kind === "sphere" || ownerBase.kind === "extrude" || ownerBase.kind === "sheetMetal" || ownerBase.kind === "revolve" || ownerBase.kind === "loft" || ownerBase.kind === "sweep" || ownerBase.kind === "variableSweep" || ownerBase.kind === "transform" || ownerBase.kind === "queryOwner" || ownerBase.kind === "filletEdges" || ownerBase.kind === "cornerYBlend" || ownerBase.kind === "chamferEdges" || ownerBase.kind === "importedMesh" || ownerBase.kind === "sdf" || ownerBase.kind === "fromSlices" || ownerBase.kind === "analyticSurface" || ownerBase.kind === "nurbsSurface" || ownerBase.kind === "surfaceRuled" || ownerBase.kind === "surfaceFill" || ownerBase.kind === "surfaceSew" || ownerBase.kind === "surfaceExtend" || ownerBase.kind === "surfaceThicken" || ownerBase.kind === "surfaceSolid" || ownerBase.kind === "importedStep" || ownerBase.kind === "torus" || ownerBase.kind === "draft" || ownerBase.kind === "offsetSolid") {
|
|
12478
|
+
if (ownerBase.kind === "box" || ownerBase.kind === "cylinder" || ownerBase.kind === "sphere" || ownerBase.kind === "extrude" || ownerBase.kind === "sheetMetal" || ownerBase.kind === "revolve" || ownerBase.kind === "loft" || ownerBase.kind === "sweep" || ownerBase.kind === "variableSweep" || ownerBase.kind === "transform" || ownerBase.kind === "queryOwner" || ownerBase.kind === "filletEdges" || ownerBase.kind === "cornerYBlend" || ownerBase.kind === "faceFillet" || ownerBase.kind === "fullRound" || ownerBase.kind === "chamferEdges" || ownerBase.kind === "importedMesh" || ownerBase.kind === "sdf" || ownerBase.kind === "fromSlices" || ownerBase.kind === "fromSectionFrames" || ownerBase.kind === "analyticSurface" || ownerBase.kind === "nurbsSurface" || ownerBase.kind === "surfaceRuled" || ownerBase.kind === "surfaceFill" || ownerBase.kind === "surfaceSew" || ownerBase.kind === "surfaceExtend" || ownerBase.kind === "surfaceThicken" || ownerBase.kind === "surfaceSolid" || ownerBase.kind === "importedStep" || ownerBase.kind === "torus" || ownerBase.kind === "draft" || ownerBase.kind === "offsetSolid") {
|
|
12407
12479
|
return {
|
|
12408
12480
|
kind: "unsupported",
|
|
12409
12481
|
query: cloneEdgeQueryRef(ref),
|
|
@@ -12936,16 +13008,16 @@ function extractEdgeSegments(mesh) {
|
|
|
12936
13008
|
let nx = e1y * e2z - e1z * e2y;
|
|
12937
13009
|
let ny = e1z * e2x - e1x * e2z;
|
|
12938
13010
|
let nz = e1x * e2y - e1y * e2x;
|
|
12939
|
-
const
|
|
12940
|
-
nx /=
|
|
12941
|
-
ny /=
|
|
12942
|
-
nz /=
|
|
13011
|
+
const len2 = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
|
|
13012
|
+
nx /= len2;
|
|
13013
|
+
ny /= len2;
|
|
13014
|
+
nz /= len2;
|
|
12943
13015
|
faceNx[t] = nx;
|
|
12944
13016
|
faceNy[t] = ny;
|
|
12945
13017
|
faceNz[t] = nz;
|
|
12946
13018
|
}
|
|
12947
13019
|
const numVerts = vertProperties.length / numProp;
|
|
12948
|
-
const canon = buildCanonicalMap$
|
|
13020
|
+
const canon = buildCanonicalMap$3(numVerts, mesh.mergeFromVert, mesh.mergeToVert);
|
|
12949
13021
|
const MAX_NUMERIC = 1 << 21;
|
|
12950
13022
|
let maxCanon = 0;
|
|
12951
13023
|
for (let i = 0; i < numVerts; i++) {
|
|
@@ -13034,7 +13106,7 @@ function buildEdgeSegment(index2, origVa, origVb, vertProperties, numProp, triA,
|
|
|
13034
13106
|
boundary
|
|
13035
13107
|
};
|
|
13036
13108
|
}
|
|
13037
|
-
function buildCanonicalMap$
|
|
13109
|
+
function buildCanonicalMap$3(numVerts, mergeFromVert, mergeToVert) {
|
|
13038
13110
|
const canon = new Uint32Array(numVerts);
|
|
13039
13111
|
for (let i = 0; i < numVerts; i++) canon[i] = i;
|
|
13040
13112
|
if (mergeFromVert && mergeToVert) {
|
|
@@ -14040,10 +14112,10 @@ function distSq$1(a2, b) {
|
|
|
14040
14112
|
function vecLength$2(v) {
|
|
14041
14113
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
14042
14114
|
}
|
|
14043
|
-
function normalize$
|
|
14044
|
-
const
|
|
14045
|
-
if (
|
|
14046
|
-
return [v[0] /
|
|
14115
|
+
function normalize$3(v) {
|
|
14116
|
+
const len2 = vecLength$2(v);
|
|
14117
|
+
if (len2 < 1e-12) return [0, 0, 0];
|
|
14118
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
14047
14119
|
}
|
|
14048
14120
|
function absDot$1(a2, b) {
|
|
14049
14121
|
return Math.abs(a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2]);
|
|
@@ -14076,11 +14148,11 @@ function applyEdgeQueryFilters(edges, query) {
|
|
|
14076
14148
|
result = result.filter((e) => e.length <= max2);
|
|
14077
14149
|
}
|
|
14078
14150
|
if (query.parallel) {
|
|
14079
|
-
const dir = normalize$
|
|
14151
|
+
const dir = normalize$3(query.parallel);
|
|
14080
14152
|
result = result.filter((e) => absDot$1(e.direction, dir) >= cosAngleTol);
|
|
14081
14153
|
}
|
|
14082
14154
|
if (query.perpendicular) {
|
|
14083
|
-
const dir = normalize$
|
|
14155
|
+
const dir = normalize$3(query.perpendicular);
|
|
14084
14156
|
const sinAngleTol = Math.sin(angleTol * Math.PI / 180);
|
|
14085
14157
|
result = result.filter((e) => absDot$1(e.direction, dir) <= sinAngleTol);
|
|
14086
14158
|
}
|
|
@@ -14328,6 +14400,8 @@ function lowerBaseShellPlanToConcretePlan(plan, thickness, openFaces) {
|
|
|
14328
14400
|
case "chamfer":
|
|
14329
14401
|
case "filletEdges":
|
|
14330
14402
|
case "cornerYBlend":
|
|
14403
|
+
case "faceFillet":
|
|
14404
|
+
case "fullRound":
|
|
14331
14405
|
case "chamferEdges":
|
|
14332
14406
|
case "draft":
|
|
14333
14407
|
case "offsetSolid":
|
|
@@ -14352,6 +14426,7 @@ function lowerBaseShellPlanToConcretePlan(plan, thickness, openFaces) {
|
|
|
14352
14426
|
case "importedMesh":
|
|
14353
14427
|
case "sdf":
|
|
14354
14428
|
case "fromSlices":
|
|
14429
|
+
case "fromSectionFrames":
|
|
14355
14430
|
case "analyticSurface":
|
|
14356
14431
|
case "nurbsSurface":
|
|
14357
14432
|
case "surfaceRuled":
|
|
@@ -14683,6 +14758,8 @@ function planComplexityScore(value) {
|
|
|
14683
14758
|
case "fillet":
|
|
14684
14759
|
case "filletEdges":
|
|
14685
14760
|
case "cornerYBlend":
|
|
14761
|
+
case "faceFillet":
|
|
14762
|
+
case "fullRound":
|
|
14686
14763
|
case "chamfer":
|
|
14687
14764
|
case "chamferEdges":
|
|
14688
14765
|
return 5 + childScore(record.base);
|
|
@@ -15482,8 +15559,8 @@ function clampUnit(v) {
|
|
|
15482
15559
|
}
|
|
15483
15560
|
function sphereUVLocal(lx, ly, lz, radius) {
|
|
15484
15561
|
const u2 = atan2(ly, lx) * radius;
|
|
15485
|
-
const
|
|
15486
|
-
const v = acos(clampUnit(lz / (
|
|
15562
|
+
const len2 = sqrt$3(lx * lx + ly * ly + lz * lz);
|
|
15563
|
+
const v = acos(clampUnit(lz / (len2 || 1))) * radius;
|
|
15487
15564
|
return [u2, v];
|
|
15488
15565
|
}
|
|
15489
15566
|
function cylinderUVLocal(lx, ly, lz, radius) {
|
|
@@ -16939,7 +17016,7 @@ async function initTruckGeometryWasm() {
|
|
|
16939
17016
|
if (_initPromise$1) return _initPromise$1;
|
|
16940
17017
|
_initPromise$1 = (async () => {
|
|
16941
17018
|
try {
|
|
16942
|
-
const geometryModule = await import("./forgecad_geometry-
|
|
17019
|
+
const geometryModule = await import("./forgecad_geometry-D8rWX7nQ.js");
|
|
16943
17020
|
const isNode = isNodeRuntime();
|
|
16944
17021
|
if (isNode) {
|
|
16945
17022
|
const { readFileSync, existsSync } = await import("./__vite-browser-external-Dhvy_jtL.js");
|
|
@@ -17302,6 +17379,9 @@ function topologyToPlacementReferences(topo) {
|
|
|
17302
17379
|
}
|
|
17303
17380
|
return { surfaces, edges };
|
|
17304
17381
|
}
|
|
17382
|
+
function towardInterior(t, delta) {
|
|
17383
|
+
return t <= 0.5 ? Math.min(t + delta, 0.5) : Math.max(t - delta, 0.5);
|
|
17384
|
+
}
|
|
17305
17385
|
function requireFinite(v, label) {
|
|
17306
17386
|
if (!Number.isFinite(v)) throw new Error(`nurbsSurface: ${label} must be finite, got ${v}`);
|
|
17307
17387
|
}
|
|
@@ -17412,13 +17492,32 @@ class NurbsSurface {
|
|
|
17412
17492
|
* and Sv positively and cancel under normalization, so they are omitted.
|
|
17413
17493
|
*/
|
|
17414
17494
|
normalAt(u2, v) {
|
|
17495
|
+
const direct = this.crossNormalAt(u2, v);
|
|
17496
|
+
if (direct) return direct;
|
|
17497
|
+
let delta = 2e-4;
|
|
17498
|
+
while (delta < 0.5) {
|
|
17499
|
+
const limit = this.crossNormalAt(towardInterior(u2, delta), towardInterior(v, delta));
|
|
17500
|
+
if (limit) return limit;
|
|
17501
|
+
delta *= 2;
|
|
17502
|
+
}
|
|
17503
|
+
return [0, 0, 1];
|
|
17504
|
+
}
|
|
17505
|
+
/**
|
|
17506
|
+
* Unit normal from the analytic partials, or null when the parameterization is
|
|
17507
|
+
* degenerate at (u, v) (a partial collapses, so the cross product is tiny
|
|
17508
|
+
* relative to the tangent spans — a pole, not a real normal).
|
|
17509
|
+
*/
|
|
17510
|
+
crossNormalAt(u2, v) {
|
|
17415
17511
|
const { Su, Sv } = this.derivativesAt(u2, v);
|
|
17512
|
+
const spanU = Math.hypot(Su[0], Su[1], Su[2]);
|
|
17513
|
+
const spanV = Math.hypot(Sv[0], Sv[1], Sv[2]);
|
|
17514
|
+
if (spanU <= 1e-6 * spanV || spanV <= 1e-6 * spanU) return null;
|
|
17416
17515
|
const nx = Su[1] * Sv[2] - Su[2] * Sv[1];
|
|
17417
17516
|
const ny = Su[2] * Sv[0] - Su[0] * Sv[2];
|
|
17418
17517
|
const nz = Su[0] * Sv[1] - Su[1] * Sv[0];
|
|
17419
|
-
const
|
|
17420
|
-
if (
|
|
17421
|
-
return [nx /
|
|
17518
|
+
const len2 = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
17519
|
+
if (len2 <= 1e-6 * spanU * spanV) return null;
|
|
17520
|
+
return [nx / len2, ny / len2, nz / len2];
|
|
17422
17521
|
}
|
|
17423
17522
|
/** Analytic first partial derivatives S_u, S_v (rational quotient rule). */
|
|
17424
17523
|
derivativesAt(u2, v) {
|
|
@@ -17891,9 +17990,9 @@ function vec3Lerp(a2, b, t) {
|
|
|
17891
17990
|
return [a2[0] + (b[0] - a2[0]) * t, a2[1] + (b[1] - a2[1]) * t, a2[2] + (b[2] - a2[2]) * t];
|
|
17892
17991
|
}
|
|
17893
17992
|
function vec3Norm(v) {
|
|
17894
|
-
const
|
|
17895
|
-
if (
|
|
17896
|
-
return [v[0] /
|
|
17993
|
+
const len2 = vec3Len(v);
|
|
17994
|
+
if (len2 < 1e-9) return [0, 0, 1];
|
|
17995
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
17897
17996
|
}
|
|
17898
17997
|
function signedArea2D(loop) {
|
|
17899
17998
|
let area2 = 0;
|
|
@@ -18077,22 +18176,22 @@ function buildSweepSegments(pathPoints, preferredUp) {
|
|
|
18077
18176
|
const a2 = clean[index2];
|
|
18078
18177
|
const b = clean[index2 + 1];
|
|
18079
18178
|
const delta = vec3Sub(b, a2);
|
|
18080
|
-
const
|
|
18081
|
-
if (
|
|
18082
|
-
const tangent = vec3Scale(delta, 1 /
|
|
18179
|
+
const len2 = vec3Len(delta);
|
|
18180
|
+
if (len2 < 1e-6) continue;
|
|
18181
|
+
const tangent = vec3Scale(delta, 1 / len2);
|
|
18083
18182
|
segments.push({
|
|
18084
18183
|
a: a2,
|
|
18085
18184
|
b,
|
|
18086
18185
|
t: tangent,
|
|
18087
18186
|
x: frames[index2].x,
|
|
18088
18187
|
y: frames[index2].y,
|
|
18089
|
-
len,
|
|
18188
|
+
len: len2,
|
|
18090
18189
|
arcStart: totalLen,
|
|
18091
|
-
arcEnd: totalLen +
|
|
18190
|
+
arcEnd: totalLen + len2,
|
|
18092
18191
|
frameA: frames[index2],
|
|
18093
18192
|
frameB: frames[index2 + 1]
|
|
18094
18193
|
});
|
|
18095
|
-
totalLen +=
|
|
18194
|
+
totalLen += len2;
|
|
18096
18195
|
}
|
|
18097
18196
|
if (segments.length === 0) {
|
|
18098
18197
|
throw new Error("sweep path has no non-zero segments");
|
|
@@ -18579,6 +18678,58 @@ function loftStitched(profiles, heights, wasm, options = {}) {
|
|
|
18579
18678
|
}
|
|
18580
18679
|
return result;
|
|
18581
18680
|
}
|
|
18681
|
+
function loftStitchedOnFrames(profiles, frames, wasm, options = {}) {
|
|
18682
|
+
if (profiles.length < 2 || profiles.length !== frames.length) return null;
|
|
18683
|
+
const classified = profiles.map((loops) => classifyLoops(loops));
|
|
18684
|
+
const outerCount = classified[0].outers.length;
|
|
18685
|
+
const holeCount = classified[0].holes.length;
|
|
18686
|
+
if (outerCount === 0) return null;
|
|
18687
|
+
for (let i = 1; i < classified.length; i++) {
|
|
18688
|
+
if (classified[i].outers.length !== outerCount || classified[i].holes.length !== holeCount) return null;
|
|
18689
|
+
}
|
|
18690
|
+
const outerGroups = matchLoopsAcrossProfiles(classified.map((c2) => c2.outers));
|
|
18691
|
+
const holeGroups = holeCount > 0 ? matchLoopsAcrossProfiles(classified.map((c2) => c2.holes)) : [];
|
|
18692
|
+
const outerSolids = [];
|
|
18693
|
+
for (const group of outerGroups) {
|
|
18694
|
+
const solid = stitchSingleLoopLoftOnFrames(group, frames, wasm, options);
|
|
18695
|
+
if (!solid) {
|
|
18696
|
+
for (const s of outerSolids) s.delete();
|
|
18697
|
+
return null;
|
|
18698
|
+
}
|
|
18699
|
+
outerSolids.push(solid);
|
|
18700
|
+
}
|
|
18701
|
+
let result;
|
|
18702
|
+
if (outerSolids.length === 1) {
|
|
18703
|
+
result = outerSolids[0];
|
|
18704
|
+
} else {
|
|
18705
|
+
result = wasm.Manifold.union(outerSolids);
|
|
18706
|
+
for (const s of outerSolids) s.delete();
|
|
18707
|
+
}
|
|
18708
|
+
if (holeGroups.length > 0) {
|
|
18709
|
+
const holeSolids = [];
|
|
18710
|
+
for (const group of holeGroups) {
|
|
18711
|
+
const solid = stitchSingleLoopLoftOnFrames(group, frames, wasm, options);
|
|
18712
|
+
if (!solid) {
|
|
18713
|
+
result.delete();
|
|
18714
|
+
for (const s of holeSolids) s.delete();
|
|
18715
|
+
return null;
|
|
18716
|
+
}
|
|
18717
|
+
holeSolids.push(solid);
|
|
18718
|
+
}
|
|
18719
|
+
let holeUnion;
|
|
18720
|
+
if (holeSolids.length === 1) {
|
|
18721
|
+
holeUnion = holeSolids[0];
|
|
18722
|
+
} else {
|
|
18723
|
+
holeUnion = wasm.Manifold.union(holeSolids);
|
|
18724
|
+
for (const s of holeSolids) s.delete();
|
|
18725
|
+
}
|
|
18726
|
+
const subtracted = wasm.Manifold.difference([result, holeUnion]);
|
|
18727
|
+
result.delete();
|
|
18728
|
+
holeUnion.delete();
|
|
18729
|
+
result = subtracted;
|
|
18730
|
+
}
|
|
18731
|
+
return result;
|
|
18732
|
+
}
|
|
18582
18733
|
function classifyLoops(loops) {
|
|
18583
18734
|
const outers = [];
|
|
18584
18735
|
const holes = [];
|
|
@@ -18737,9 +18888,9 @@ function maxQuadDeviation(rings, heights, colA, colB) {
|
|
|
18737
18888
|
const c2 = [rings[i + 1][colB][0], rings[i + 1][colB][1], heights[i + 1]];
|
|
18738
18889
|
const d2 = [rings[i + 1][colA][0], rings[i + 1][colA][1], heights[i + 1]];
|
|
18739
18890
|
const n = cross3$6(sub3$4(b, a2), sub3$4(d2, a2));
|
|
18740
|
-
const
|
|
18741
|
-
if (
|
|
18742
|
-
const deviation = Math.abs((n[0] * (c2[0] - a2[0]) + n[1] * (c2[1] - a2[1]) + n[2] * (c2[2] - a2[2])) /
|
|
18891
|
+
const len2 = Math.hypot(n[0], n[1], n[2]);
|
|
18892
|
+
if (len2 < 1e-12) continue;
|
|
18893
|
+
const deviation = Math.abs((n[0] * (c2[0] - a2[0]) + n[1] * (c2[1] - a2[1]) + n[2] * (c2[2] - a2[2])) / len2);
|
|
18743
18894
|
if (deviation > worst) worst = deviation;
|
|
18744
18895
|
}
|
|
18745
18896
|
return worst;
|
|
@@ -18795,18 +18946,18 @@ function buildCornerAnchoredRings(loops, heights, cornerSets, edgeLength2) {
|
|
|
18795
18946
|
const start = dists[from];
|
|
18796
18947
|
let end = dists[to];
|
|
18797
18948
|
if (to <= from) end += total;
|
|
18798
|
-
const
|
|
18799
|
-
if (
|
|
18949
|
+
const len2 = end - start;
|
|
18950
|
+
if (len2 < 1e-9) return null;
|
|
18800
18951
|
const interior = [];
|
|
18801
18952
|
const n = loop.length;
|
|
18802
18953
|
for (let v = (from + 1) % n; v !== to; v = (v + 1) % n) {
|
|
18803
18954
|
let d2 = dists[v];
|
|
18804
18955
|
if (d2 < start) d2 += total;
|
|
18805
|
-
const p2 = (d2 - start) /
|
|
18956
|
+
const p2 = (d2 - start) / len2;
|
|
18806
18957
|
if (p2 > 1e-9 && p2 < 1 - 1e-9) interior.push(p2);
|
|
18807
18958
|
}
|
|
18808
18959
|
stationSegs.push(interior);
|
|
18809
|
-
stationLens.push(
|
|
18960
|
+
stationLens.push(len2);
|
|
18810
18961
|
}
|
|
18811
18962
|
segParams.push(stationSegs);
|
|
18812
18963
|
segLengths.push(stationLens);
|
|
@@ -18832,12 +18983,12 @@ function buildCornerAnchoredRings(loops, heights, cornerSets, edgeLength2) {
|
|
|
18832
18983
|
const start = dists[from];
|
|
18833
18984
|
let end = dists[to];
|
|
18834
18985
|
if (to <= from) end += total;
|
|
18835
|
-
const
|
|
18986
|
+
const len2 = end - start;
|
|
18836
18987
|
for (const p2 of paramsBySegment[s]) {
|
|
18837
18988
|
if (p2 === 0) {
|
|
18838
18989
|
ring.push([loop[from][0], loop[from][1]]);
|
|
18839
18990
|
} else {
|
|
18840
|
-
ring.push(pointAtArcLength(loop, dists, total, start + p2 *
|
|
18991
|
+
ring.push(pointAtArcLength(loop, dists, total, start + p2 * len2));
|
|
18841
18992
|
}
|
|
18842
18993
|
}
|
|
18843
18994
|
}
|
|
@@ -18965,9 +19116,11 @@ function buildSeamAlignedRings(loops, _heights, edgeLength2) {
|
|
|
18965
19116
|
return { rings, cornerColumns: [] };
|
|
18966
19117
|
}
|
|
18967
19118
|
function buildSpanRows(rings, heights) {
|
|
18968
|
-
|
|
18969
|
-
|
|
18970
|
-
|
|
19119
|
+
return buildSpanRowsFromStations(rings.map((ring, i) => ring.map(([x2, y2]) => [x2, y2, heights[i]])));
|
|
19120
|
+
}
|
|
19121
|
+
function buildSpanRowsFromStations(stations) {
|
|
19122
|
+
const R = stations.length;
|
|
19123
|
+
const N = stations[0].length;
|
|
18971
19124
|
const t = [0];
|
|
18972
19125
|
for (let i = 0; i < R - 1; i++) {
|
|
18973
19126
|
let sum2 = 0;
|
|
@@ -19132,15 +19285,117 @@ function stitchSingleLoopLoft(loops, heights, wasm, options) {
|
|
|
19132
19285
|
return null;
|
|
19133
19286
|
}
|
|
19134
19287
|
}
|
|
19288
|
+
function stitchSingleLoopLoftOnFrames(loops, frames, wasm, options) {
|
|
19289
|
+
const normalizedLoops = loops.map((loop) => {
|
|
19290
|
+
const area2 = signedArea$3(loop);
|
|
19291
|
+
return area2 < 0 ? [...loop].reverse() : loop;
|
|
19292
|
+
});
|
|
19293
|
+
const stationDistances = cumulativeFrameDistances(frames);
|
|
19294
|
+
const compatible = buildCompatibleRings(normalizedLoops, stationDistances, options.edgeLength);
|
|
19295
|
+
if (!compatible) return null;
|
|
19296
|
+
const { rings, cornerColumns } = compatible;
|
|
19297
|
+
const N = rings[0].length;
|
|
19298
|
+
if (N < 3) return null;
|
|
19299
|
+
const stations = rings.map((ring, i) => ring.map(([x2, y2]) => pointOnFrame(frames[i], x2, y2)));
|
|
19300
|
+
const rows = buildSpanRowsFromStations(stations);
|
|
19301
|
+
const R = rows.length;
|
|
19302
|
+
const cornerSet = new Set(cornerColumns);
|
|
19303
|
+
const vertProps = [];
|
|
19304
|
+
let vertCount = 0;
|
|
19305
|
+
const fwdIdx = [];
|
|
19306
|
+
const bwdIdx = [];
|
|
19307
|
+
const pushVert = (p2, n) => {
|
|
19308
|
+
vertProps.push(p2[0], p2[1], p2[2], n[0], n[1], n[2]);
|
|
19309
|
+
return vertCount++;
|
|
19310
|
+
};
|
|
19311
|
+
for (let r = 0; r < R; r++) {
|
|
19312
|
+
const { points, tangents } = rows[r];
|
|
19313
|
+
const fwd = new Array(N);
|
|
19314
|
+
const bwd = new Array(N);
|
|
19315
|
+
for (let j = 0; j < N; j++) {
|
|
19316
|
+
const prev = points[(j - 1 + N) % N];
|
|
19317
|
+
const curr = points[j];
|
|
19318
|
+
const next = points[(j + 1) % N];
|
|
19319
|
+
if (cornerSet.has(j)) {
|
|
19320
|
+
const nFwd = surfaceNormal(sub3$4(next, curr), tangents[j]);
|
|
19321
|
+
const nBwd = surfaceNormal(sub3$4(curr, prev), tangents[j]);
|
|
19322
|
+
fwd[j] = pushVert(curr, nFwd);
|
|
19323
|
+
bwd[j] = pushVert(curr, nBwd);
|
|
19324
|
+
} else {
|
|
19325
|
+
const idx = pushVert(curr, surfaceNormal(sub3$4(next, prev), tangents[j]));
|
|
19326
|
+
fwd[j] = idx;
|
|
19327
|
+
bwd[j] = idx;
|
|
19328
|
+
}
|
|
19329
|
+
}
|
|
19330
|
+
fwdIdx.push(fwd);
|
|
19331
|
+
bwdIdx.push(bwd);
|
|
19332
|
+
}
|
|
19333
|
+
const triangles = [];
|
|
19334
|
+
for (let r = 0; r < R - 1; r++) {
|
|
19335
|
+
for (let j = 0; j < N; j++) {
|
|
19336
|
+
const j1 = (j + 1) % N;
|
|
19337
|
+
const v0 = fwdIdx[r][j];
|
|
19338
|
+
const v3 = bwdIdx[r][j1];
|
|
19339
|
+
const v2 = bwdIdx[r + 1][j1];
|
|
19340
|
+
const v1 = fwdIdx[r + 1][j];
|
|
19341
|
+
triangles.push(v0, v3, v2);
|
|
19342
|
+
triangles.push(v0, v2, v1);
|
|
19343
|
+
}
|
|
19344
|
+
}
|
|
19345
|
+
const bottomTris = wasm.triangulate([rings[0]]);
|
|
19346
|
+
const bottomBase = vertCount;
|
|
19347
|
+
const bottomNormal = scale3$2(frames[0].normal, -1);
|
|
19348
|
+
for (const p2 of rows[0].points) pushVert(p2, bottomNormal);
|
|
19349
|
+
for (const tri of bottomTris) {
|
|
19350
|
+
const [v0, v1, v2] = Array.isArray(tri) ? tri : [tri[0], tri[1], tri[2]];
|
|
19351
|
+
triangles.push(bottomBase + v0, bottomBase + v2, bottomBase + v1);
|
|
19352
|
+
}
|
|
19353
|
+
const topTris = wasm.triangulate([rings[rings.length - 1]]);
|
|
19354
|
+
const topBase = vertCount;
|
|
19355
|
+
const topNormal = frames[frames.length - 1].normal;
|
|
19356
|
+
for (const p2 of rows[R - 1].points) pushVert(p2, topNormal);
|
|
19357
|
+
for (const tri of topTris) {
|
|
19358
|
+
const [v0, v1, v2] = Array.isArray(tri) ? tri : [tri[0], tri[1], tri[2]];
|
|
19359
|
+
triangles.push(topBase + v0, topBase + v1, topBase + v2);
|
|
19360
|
+
}
|
|
19361
|
+
const mesh = new wasm.Mesh({
|
|
19362
|
+
numProp: 6,
|
|
19363
|
+
vertProperties: new Float32Array(vertProps),
|
|
19364
|
+
triVerts: new Uint32Array(triangles)
|
|
19365
|
+
});
|
|
19366
|
+
try {
|
|
19367
|
+
mesh.merge();
|
|
19368
|
+
return new wasm.Manifold(mesh);
|
|
19369
|
+
} catch (_e2) {
|
|
19370
|
+
return null;
|
|
19371
|
+
}
|
|
19372
|
+
}
|
|
19373
|
+
function pointOnFrame(frame, x2, y2) {
|
|
19374
|
+
return [
|
|
19375
|
+
frame.origin[0] + frame.xAxis[0] * x2 + frame.yAxis[0] * y2,
|
|
19376
|
+
frame.origin[1] + frame.xAxis[1] * x2 + frame.yAxis[1] * y2,
|
|
19377
|
+
frame.origin[2] + frame.xAxis[2] * x2 + frame.yAxis[2] * y2
|
|
19378
|
+
];
|
|
19379
|
+
}
|
|
19380
|
+
function cumulativeFrameDistances(frames) {
|
|
19381
|
+
const distances = [0];
|
|
19382
|
+
for (let i = 1; i < frames.length; i++) {
|
|
19383
|
+
distances.push(distances[i - 1] + dist3(frames[i].origin, frames[i - 1].origin));
|
|
19384
|
+
}
|
|
19385
|
+
if (distances[distances.length - 1] < 1e-9) {
|
|
19386
|
+
for (let i = 0; i < frames.length; i++) distances[i] = i;
|
|
19387
|
+
}
|
|
19388
|
+
return distances;
|
|
19389
|
+
}
|
|
19135
19390
|
function surfaceNormal(chord, span2) {
|
|
19136
19391
|
const n = cross3$6(chord, span2);
|
|
19137
|
-
const
|
|
19138
|
-
if (
|
|
19392
|
+
const len2 = Math.hypot(n[0], n[1], n[2]);
|
|
19393
|
+
if (len2 < 1e-12) {
|
|
19139
19394
|
const radial = Math.hypot(chord[0], chord[1]);
|
|
19140
19395
|
if (radial > 1e-12) return [chord[1] / radial, -chord[0] / radial, 0];
|
|
19141
19396
|
return [0, 0, 1];
|
|
19142
19397
|
}
|
|
19143
|
-
return [n[0] /
|
|
19398
|
+
return [n[0] / len2, n[1] / len2, n[2] / len2];
|
|
19144
19399
|
}
|
|
19145
19400
|
function sub3$4(a2, b) {
|
|
19146
19401
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
@@ -19428,32 +19683,32 @@ function sweepStitched(profilePolygons, pathPoints, up, wasm) {
|
|
|
19428
19683
|
function computeParallelTransportFrames(path, preferredUp) {
|
|
19429
19684
|
const n = path.length;
|
|
19430
19685
|
const frames = [];
|
|
19431
|
-
const firstTangent = normalize$
|
|
19686
|
+
const firstTangent = normalize$2(sub$3(path[1], path[0]));
|
|
19432
19687
|
if (!firstTangent) return null;
|
|
19433
|
-
let x2 = normalize$
|
|
19688
|
+
let x2 = normalize$2(cross$3(preferredUp, firstTangent));
|
|
19434
19689
|
if (!x2 || length$1(x2) < 1e-8) {
|
|
19435
19690
|
const fallback = Math.abs(firstTangent[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];
|
|
19436
|
-
x2 = normalize$
|
|
19691
|
+
x2 = normalize$2(cross$3(fallback, firstTangent));
|
|
19437
19692
|
if (!x2) return null;
|
|
19438
19693
|
}
|
|
19439
|
-
let y2 = normalize$
|
|
19694
|
+
let y2 = normalize$2(cross$3(firstTangent, x2));
|
|
19440
19695
|
frames.push({ origin: path[0], x: x2, y: y2, t: firstTangent });
|
|
19441
19696
|
for (let i = 1; i < n; i++) {
|
|
19442
19697
|
const prevT = frames[i - 1].t;
|
|
19443
19698
|
let nextT;
|
|
19444
19699
|
if (i < n - 1) {
|
|
19445
|
-
const t1 = normalize$
|
|
19446
|
-
const t2 = normalize$
|
|
19700
|
+
const t1 = normalize$2(sub$3(path[i], path[i - 1]));
|
|
19701
|
+
const t2 = normalize$2(sub$3(path[i + 1], path[i]));
|
|
19447
19702
|
if (!t1 || !t2) return null;
|
|
19448
|
-
nextT = normalize$
|
|
19703
|
+
nextT = normalize$2(add$2(t1, t2)) || t1;
|
|
19449
19704
|
} else {
|
|
19450
|
-
const nt = normalize$
|
|
19705
|
+
const nt = normalize$2(sub$3(path[i], path[i - 1]));
|
|
19451
19706
|
if (!nt) return null;
|
|
19452
19707
|
nextT = nt;
|
|
19453
19708
|
}
|
|
19454
|
-
const v = cross$
|
|
19709
|
+
const v = cross$3(prevT, nextT);
|
|
19455
19710
|
const vLen = length$1(v);
|
|
19456
|
-
const c2 = dot$
|
|
19711
|
+
const c2 = dot$3(prevT, nextT);
|
|
19457
19712
|
if (vLen > 1e-10) {
|
|
19458
19713
|
const axis = scale$1(v, 1 / vLen);
|
|
19459
19714
|
x2 = rotateVector(frames[i - 1].x, axis, c2, vLen);
|
|
@@ -19527,32 +19782,32 @@ function signedArea$2(loop) {
|
|
|
19527
19782
|
}
|
|
19528
19783
|
return area2 * 0.5;
|
|
19529
19784
|
}
|
|
19530
|
-
function sub$
|
|
19785
|
+
function sub$3(a2, b) {
|
|
19531
19786
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
19532
19787
|
}
|
|
19533
|
-
function add$
|
|
19788
|
+
function add$2(a2, b) {
|
|
19534
19789
|
return [a2[0] + b[0], a2[1] + b[1], a2[2] + b[2]];
|
|
19535
19790
|
}
|
|
19536
19791
|
function scale$1(v, s) {
|
|
19537
19792
|
return [v[0] * s, v[1] * s, v[2] * s];
|
|
19538
19793
|
}
|
|
19539
|
-
function dot$
|
|
19794
|
+
function dot$3(a2, b) {
|
|
19540
19795
|
return a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2];
|
|
19541
19796
|
}
|
|
19542
|
-
function cross$
|
|
19797
|
+
function cross$3(a2, b) {
|
|
19543
19798
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
19544
19799
|
}
|
|
19545
19800
|
function length$1(v) {
|
|
19546
19801
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
19547
19802
|
}
|
|
19548
|
-
function normalize$
|
|
19549
|
-
const
|
|
19550
|
-
if (
|
|
19551
|
-
return [v[0] /
|
|
19803
|
+
function normalize$2(v) {
|
|
19804
|
+
const len2 = length$1(v);
|
|
19805
|
+
if (len2 < 1e-12) return null;
|
|
19806
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
19552
19807
|
}
|
|
19553
19808
|
function rotateVector(v, axis, c2, s) {
|
|
19554
|
-
const kDotV = dot$
|
|
19555
|
-
const kCrossV = cross$
|
|
19809
|
+
const kDotV = dot$3(axis, v);
|
|
19810
|
+
const kCrossV = cross$3(axis, v);
|
|
19556
19811
|
return [
|
|
19557
19812
|
v[0] * c2 + kCrossV[0] * s + axis[0] * kDotV * (1 - c2),
|
|
19558
19813
|
v[1] * c2 + kCrossV[1] * s + axis[1] * kDotV * (1 - c2),
|
|
@@ -19896,24 +20151,7 @@ function fromSlicesPlaneFrameForManifold(normalInput) {
|
|
|
19896
20151
|
}
|
|
19897
20152
|
function fromSlicesLocalToWorldMatrixForManifold(normal2) {
|
|
19898
20153
|
const frame = fromSlicesPlaneFrameForManifold(normal2);
|
|
19899
|
-
return [
|
|
19900
|
-
frame.u[0],
|
|
19901
|
-
frame.u[1],
|
|
19902
|
-
frame.u[2],
|
|
19903
|
-
0,
|
|
19904
|
-
frame.v[0],
|
|
19905
|
-
frame.v[1],
|
|
19906
|
-
frame.v[2],
|
|
19907
|
-
0,
|
|
19908
|
-
frame.normal[0],
|
|
19909
|
-
frame.normal[1],
|
|
19910
|
-
frame.normal[2],
|
|
19911
|
-
0,
|
|
19912
|
-
0,
|
|
19913
|
-
0,
|
|
19914
|
-
0,
|
|
19915
|
-
1
|
|
19916
|
-
];
|
|
20154
|
+
return Transform.from(planeFrameToWorldToPlaneMatrix({ origin: [0, 0, 0], u: frame.u, v: frame.v, normal: frame.normal })).inverse().toArray();
|
|
19917
20155
|
}
|
|
19918
20156
|
function transformZAlignedFromSlicesPlanForManifold(base, normal2) {
|
|
19919
20157
|
const matrix = fromSlicesLocalToWorldMatrixForManifold(normal2);
|
|
@@ -20502,35 +20740,41 @@ function lowerFromSlicesToManifold(plan, wasm) {
|
|
|
20502
20740
|
solid = lowerSdfToManifold(levelSetFieldToStandardSdf3(input.sdf), input.bounds, input.edgeLength, wasm);
|
|
20503
20741
|
}
|
|
20504
20742
|
}
|
|
20505
|
-
if (Math.abs(nx) > 1e-10 || Math.abs(ny) > 1e-10 || nz
|
|
20506
|
-
|
|
20507
|
-
|
|
20508
|
-
|
|
20509
|
-
if (solid !== prev) disposeWasmObject(prev);
|
|
20510
|
-
} else {
|
|
20511
|
-
const ax = -ny;
|
|
20512
|
-
const ay = nx;
|
|
20513
|
-
const len = Math.sqrt(ax * ax + ay * ay);
|
|
20514
|
-
const c2 = nz;
|
|
20515
|
-
const s = len;
|
|
20516
|
-
const ux = ax / len;
|
|
20517
|
-
const uy = ay / len;
|
|
20518
|
-
const m00 = c2 + ux * ux * (1 - c2);
|
|
20519
|
-
const m01 = ux * uy * (1 - c2);
|
|
20520
|
-
const m02 = uy * s;
|
|
20521
|
-
const m10 = uy * ux * (1 - c2);
|
|
20522
|
-
const m11 = c2 + uy * uy * (1 - c2);
|
|
20523
|
-
const m12 = -ux * s;
|
|
20524
|
-
const m20 = -uy * s;
|
|
20525
|
-
const m21 = ux * s;
|
|
20526
|
-
const m22 = c2;
|
|
20527
|
-
const prev = solid;
|
|
20528
|
-
solid = solid.transform([m00, m01, m02, 0, m10, m11, m12, 0, m20, m21, m22, 0, 0, 0, 0, 1]);
|
|
20529
|
-
if (solid !== prev) disposeWasmObject(prev);
|
|
20530
|
-
}
|
|
20743
|
+
if (Math.abs(nx) > 1e-10 || Math.abs(ny) > 1e-10 || Math.abs(nz - 1) > 1e-10) {
|
|
20744
|
+
const prev = solid;
|
|
20745
|
+
solid = solid.transform(fromSlicesLocalToWorldMatrixForManifold(group.normal));
|
|
20746
|
+
if (solid !== prev) disposeWasmObject(prev);
|
|
20531
20747
|
}
|
|
20532
20748
|
return solid;
|
|
20533
20749
|
}
|
|
20750
|
+
function finiteVec3(value, context) {
|
|
20751
|
+
if (value.some((component) => !Number.isFinite(component))) throw new Error(`${context} must contain finite numbers.`);
|
|
20752
|
+
return [value[0], value[1], value[2]];
|
|
20753
|
+
}
|
|
20754
|
+
function lowerFromSectionFramesToManifold(plan, wasm) {
|
|
20755
|
+
if (plan.slices.length < 2) throw new Error("Shape.fromSlices: framed section loft requires at least two slices.");
|
|
20756
|
+
const profiles = plan.slices.map((s) => {
|
|
20757
|
+
const crossSection = lowerProfileCompilePlanToCrossSection(s.profile, wasm);
|
|
20758
|
+
try {
|
|
20759
|
+
return crossSection.toPolygons();
|
|
20760
|
+
} finally {
|
|
20761
|
+
disposeWasmObject(crossSection);
|
|
20762
|
+
}
|
|
20763
|
+
});
|
|
20764
|
+
const frames = plan.slices.map((s, index2) => ({
|
|
20765
|
+
origin: finiteVec3(s.origin, `Shape.fromSlices framed slice ${index2} origin`),
|
|
20766
|
+
normal: normalizeVec3$2(s.normal, `Shape.fromSlices framed slice ${index2} normal`),
|
|
20767
|
+
xAxis: normalizeVec3$2(s.tangentU, `Shape.fromSlices framed slice ${index2} tangentU`),
|
|
20768
|
+
yAxis: normalizeVec3$2(s.tangentV, `Shape.fromSlices framed slice ${index2} tangentV`)
|
|
20769
|
+
}));
|
|
20770
|
+
const stitched = loftStitchedOnFrames(profiles, frames, wasm, { edgeLength: plan.edgeLength });
|
|
20771
|
+
if (!stitched) {
|
|
20772
|
+
throw new Error(
|
|
20773
|
+
"Shape.fromSlices: framed section loft could not be stitched into a manifold mesh. Check that every section has compatible closed profile topology and that adjacent frames do not self-intersect."
|
|
20774
|
+
);
|
|
20775
|
+
}
|
|
20776
|
+
return stitched;
|
|
20777
|
+
}
|
|
20534
20778
|
function lowerShapeVariableSweepCompilePlan(plan, wasm) {
|
|
20535
20779
|
const sectionPolygons = plan.sections.map((s) => {
|
|
20536
20780
|
const crossSection = lowerProfileCompilePlanToCrossSection(s.profile, wasm);
|
|
@@ -20725,6 +20969,9 @@ function selectEdgeSegmentsForFeature(segments, plan) {
|
|
|
20725
20969
|
return matched;
|
|
20726
20970
|
}
|
|
20727
20971
|
function lowerFilletEdgesCompilePlan(plan, wasm) {
|
|
20972
|
+
if (plan.variableRadius) {
|
|
20973
|
+
throw new Error(`Variable-radius fillet requires the OCCT backend. ${OCCT_BACKEND_REQUIRED_HINT}`);
|
|
20974
|
+
}
|
|
20728
20975
|
let manifold = lowerShapeCompilePlanToManifold(plan.base, wasm);
|
|
20729
20976
|
const mesh = manifold.getMesh();
|
|
20730
20977
|
const segments = extractEdgeSegments({
|
|
@@ -20861,6 +21108,10 @@ function lowerShapeCompilePlanToManifold(plan, wasm) {
|
|
|
20861
21108
|
return lowerFilletEdgesCompilePlan(plan, wasm);
|
|
20862
21109
|
case "cornerYBlend":
|
|
20863
21110
|
throw new Error(`Blend.CornerY() requires the OCCT backend. ${OCCT_BACKEND_REQUIRED_HINT}`);
|
|
21111
|
+
case "faceFillet":
|
|
21112
|
+
throw new Error(`Blend.Face() requires the OCCT backend. ${OCCT_BACKEND_REQUIRED_HINT}`);
|
|
21113
|
+
case "fullRound":
|
|
21114
|
+
throw new Error(`Blend.FullRound() requires the OCCT backend. ${OCCT_BACKEND_REQUIRED_HINT}`);
|
|
20864
21115
|
case "chamferEdges":
|
|
20865
21116
|
return lowerChamferEdgesCompilePlan(plan, wasm);
|
|
20866
21117
|
case "draft": {
|
|
@@ -20890,6 +21141,8 @@ function lowerShapeCompilePlanToManifold(plan, wasm) {
|
|
|
20890
21141
|
}
|
|
20891
21142
|
case "fromSlices":
|
|
20892
21143
|
return lowerFromSlicesToManifold(plan, wasm);
|
|
21144
|
+
case "fromSectionFrames":
|
|
21145
|
+
return lowerFromSectionFramesToManifold(plan, wasm);
|
|
20893
21146
|
case "nurbsSurface":
|
|
20894
21147
|
return lowerNurbsSurfaceToManifold(plan, wasm);
|
|
20895
21148
|
case "analyticSurface":
|
|
@@ -22117,6 +22370,27 @@ function occtEdgeTouchesPoint(oc, edge, target, tolerance) {
|
|
|
22117
22370
|
const end = curve.get().Value(last.current);
|
|
22118
22371
|
return Math.min(occtPointDistance(start, target), occtPointDistance(end, target)) <= tolerance;
|
|
22119
22372
|
}
|
|
22373
|
+
function occtEdgeLength(oc, edge) {
|
|
22374
|
+
const adaptor = new oc.BRepAdaptor_Curve_2(edge);
|
|
22375
|
+
return oc.GCPnts_AbscissaPoint.Length_1(adaptor);
|
|
22376
|
+
}
|
|
22377
|
+
function addFilletEdge(oc, mkFillet, edge, radius, variableRadius) {
|
|
22378
|
+
if (!variableRadius) {
|
|
22379
|
+
mkFillet.Add_2(radius, edge);
|
|
22380
|
+
return;
|
|
22381
|
+
}
|
|
22382
|
+
if ("stations" in variableRadius) {
|
|
22383
|
+
const length4 = occtEdgeLength(oc, edge);
|
|
22384
|
+
const stations = variableRadius.stations;
|
|
22385
|
+
const arr = new oc.TColgp_Array1OfPnt2d_2(1, stations.length);
|
|
22386
|
+
for (let i = 0; i < stations.length; i++) {
|
|
22387
|
+
arr.SetValue(i + 1, new oc.gp_Pnt2d_3(stations[i].at * length4, stations[i].radius));
|
|
22388
|
+
}
|
|
22389
|
+
mkFillet.Add_5(arr, edge);
|
|
22390
|
+
return;
|
|
22391
|
+
}
|
|
22392
|
+
mkFillet.Add_3(variableRadius.start, variableRadius.end, edge);
|
|
22393
|
+
}
|
|
22120
22394
|
function lowerFilletEdgesPlan$1(oc, plan) {
|
|
22121
22395
|
const base = lowerShapeCompilePlanToOCCT(plan.base, oc);
|
|
22122
22396
|
const mkFillet = new oc.BRepFilletAPI_MakeFillet(base, oc.ChFi3d_FilletShape.ChFi3d_Rational);
|
|
@@ -22127,7 +22401,7 @@ function lowerFilletEdgesPlan$1(oc, plan) {
|
|
|
22127
22401
|
for (const target of selectOCCTEdgeFeatureTargets(base, plan)) {
|
|
22128
22402
|
const matchedEdge = findOCCTEdgeByMidpoint(oc, base, target.midpoint);
|
|
22129
22403
|
if (matchedEdge) {
|
|
22130
|
-
mkFillet
|
|
22404
|
+
addFilletEdge(oc, mkFillet, matchedEdge, plan.radius, plan.variableRadius);
|
|
22131
22405
|
addedCount++;
|
|
22132
22406
|
}
|
|
22133
22407
|
}
|
|
@@ -22136,7 +22410,55 @@ function lowerFilletEdgesPlan$1(oc, plan) {
|
|
|
22136
22410
|
}
|
|
22137
22411
|
mkFillet.Build(new oc.Message_ProgressRange_1());
|
|
22138
22412
|
if (!mkFillet.IsDone()) {
|
|
22139
|
-
|
|
22413
|
+
const radiusDesc = plan.variableRadius ? `variableRadius(max=${maxVariableRadius(plan.variableRadius)})` : `radius=${plan.radius}`;
|
|
22414
|
+
throw new Error(`filletEdges(): OCCT fillet operation failed (${radiusDesc}, ${addedCount} edges).`);
|
|
22415
|
+
}
|
|
22416
|
+
return mkFillet.Shape();
|
|
22417
|
+
}
|
|
22418
|
+
function lowerFaceFilletPlan(oc, plan) {
|
|
22419
|
+
const base = lowerShapeCompilePlanToOCCT(plan.base, oc);
|
|
22420
|
+
const mkFillet = new oc.BRepFilletAPI_MakeFillet(base, oc.ChFi3d_FilletShape.ChFi3d_Rational);
|
|
22421
|
+
if (plan.continuity) {
|
|
22422
|
+
mkFillet.SetContinuity(mapSurfaceContinuityToOcct(oc, plan.continuity), 2 * Math.PI / 180);
|
|
22423
|
+
}
|
|
22424
|
+
let addedCount = 0;
|
|
22425
|
+
const touchedEdges = /* @__PURE__ */ new Set();
|
|
22426
|
+
for (const target of plan.edgeTargets) {
|
|
22427
|
+
const matchedEdge = findOCCTEdgeByMidpoint(oc, base, target.midpoint);
|
|
22428
|
+
if (!matchedEdge || touchedEdges.has(matchedEdge)) continue;
|
|
22429
|
+
addFilletEdge(oc, mkFillet, matchedEdge, plan.radius, plan.variableRadius);
|
|
22430
|
+
touchedEdges.add(matchedEdge);
|
|
22431
|
+
addedCount++;
|
|
22432
|
+
}
|
|
22433
|
+
if (addedCount === 0) {
|
|
22434
|
+
throw new Error("Blend.Face(): no matching OCCT edges found shared by the face pair.");
|
|
22435
|
+
}
|
|
22436
|
+
mkFillet.Build(new oc.Message_ProgressRange_1());
|
|
22437
|
+
if (!mkFillet.IsDone()) {
|
|
22438
|
+
const radiusDesc = plan.variableRadius ? `variableRadius(max=${maxVariableRadius(plan.variableRadius)})` : `radius=${plan.radius}`;
|
|
22439
|
+
throw new Error(`Blend.Face(): OCCT face fillet failed (${radiusDesc}, ${addedCount} shared edges).`);
|
|
22440
|
+
}
|
|
22441
|
+
return mkFillet.Shape();
|
|
22442
|
+
}
|
|
22443
|
+
function lowerFullRoundPlan(oc, plan) {
|
|
22444
|
+
const base = lowerShapeCompilePlanToOCCT(plan.base, oc);
|
|
22445
|
+
const mkFillet = new oc.BRepFilletAPI_MakeFillet(base, oc.ChFi3d_FilletShape.ChFi3d_Rational);
|
|
22446
|
+
mkFillet.SetContinuity(mapSurfaceContinuityToOcct(oc, plan.continuity ?? "G1"), 2 * Math.PI / 180);
|
|
22447
|
+
let addedCount = 0;
|
|
22448
|
+
const touchedEdges = /* @__PURE__ */ new Set();
|
|
22449
|
+
for (const target of plan.edgeTargets) {
|
|
22450
|
+
const matchedEdge = findOCCTEdgeByMidpoint(oc, base, target.midpoint);
|
|
22451
|
+
if (!matchedEdge || touchedEdges.has(matchedEdge)) continue;
|
|
22452
|
+
mkFillet.Add_2(plan.radius, matchedEdge);
|
|
22453
|
+
touchedEdges.add(matchedEdge);
|
|
22454
|
+
addedCount++;
|
|
22455
|
+
}
|
|
22456
|
+
if (addedCount === 0) {
|
|
22457
|
+
throw new Error("Blend.FullRound(): no matching OCCT edges found around the center face.");
|
|
22458
|
+
}
|
|
22459
|
+
mkFillet.Build(new oc.Message_ProgressRange_1());
|
|
22460
|
+
if (!mkFillet.IsDone()) {
|
|
22461
|
+
throw new Error(`Blend.FullRound(): OCCT full round failed (radius=${plan.radius}, ${addedCount} edges).`);
|
|
22140
22462
|
}
|
|
22141
22463
|
return mkFillet.Shape();
|
|
22142
22464
|
}
|
|
@@ -22409,6 +22731,10 @@ function _lowerShapeCompilePlanToOCCTInner(plan, oc) {
|
|
|
22409
22731
|
return lowerChamferPlan(oc, plan);
|
|
22410
22732
|
case "filletEdges":
|
|
22411
22733
|
return lowerFilletEdgesPlan$1(oc, plan);
|
|
22734
|
+
case "faceFillet":
|
|
22735
|
+
return lowerFaceFilletPlan(oc, plan);
|
|
22736
|
+
case "fullRound":
|
|
22737
|
+
return lowerFullRoundPlan(oc, plan);
|
|
22412
22738
|
case "cornerYBlend":
|
|
22413
22739
|
return lowerCornerYBlendPlan$1(oc, plan);
|
|
22414
22740
|
case "chamferEdges":
|
|
@@ -22708,8 +23034,10 @@ function lowerSurfaceRuledPlan$1(oc, plan) {
|
|
|
22708
23034
|
return sections.Shape();
|
|
22709
23035
|
}
|
|
22710
23036
|
function lowerSurfaceFillPlan$1(oc, plan) {
|
|
23037
|
+
var _a3, _b3;
|
|
22711
23038
|
const hasSupport = plan.boundaries.some((boundary) => boundary.support != null);
|
|
22712
|
-
|
|
23039
|
+
const hasInteriorConstraints = (((_a3 = plan.interiorCurves) == null ? void 0 : _a3.length) ?? 0) > 0 || (((_b3 = plan.interiorPoints) == null ? void 0 : _b3.length) ?? 0) > 0;
|
|
23040
|
+
if (!hasSupport && !hasInteriorConstraints && (plan.boundaries.length === 3 || plan.boundaries.length === 4)) {
|
|
22713
23041
|
try {
|
|
22714
23042
|
const handles = plan.boundaries.map(
|
|
22715
23043
|
(boundary, index2) => buildBSplineCurveHandleFromSweepPathPlan(
|
|
@@ -22739,6 +23067,13 @@ function lowerSurfaceFillPlan$1(oc, plan) {
|
|
|
22739
23067
|
filling.Add_1(edge, oc.GeomAbs_Shape.GeomAbs_C0, true);
|
|
22740
23068
|
}
|
|
22741
23069
|
}
|
|
23070
|
+
for (const interior of plan.interiorCurves ?? []) {
|
|
23071
|
+
const edge = buildCurveEdgeFromSweepPathPlan(oc, interior.curve, "Surface.Fill(through)", plan.allowApproximation);
|
|
23072
|
+
filling.Add_1(edge, mapSurfaceContinuityToOcct(oc, interior.continuity), false);
|
|
23073
|
+
}
|
|
23074
|
+
for (const point2 of plan.interiorPoints ?? []) {
|
|
23075
|
+
filling.Add_4(new oc.gp_Pnt_3(point2[0], point2[1], point2[2]));
|
|
23076
|
+
}
|
|
22742
23077
|
filling.Build(new oc.Message_ProgressRange_1());
|
|
22743
23078
|
if (!filling.IsDone()) {
|
|
22744
23079
|
throw new Error("Surface.Fill(): OCCT failed to build the constrained filling surface.");
|
|
@@ -23288,9 +23623,9 @@ function crossVec3$1(a2, b) {
|
|
|
23288
23623
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
23289
23624
|
}
|
|
23290
23625
|
function normalizeVec3$1(v) {
|
|
23291
|
-
const
|
|
23292
|
-
if (
|
|
23293
|
-
return [v[0] /
|
|
23626
|
+
const len2 = Math.hypot(v[0], v[1], v[2]);
|
|
23627
|
+
if (len2 < 1e-10) return null;
|
|
23628
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
23294
23629
|
}
|
|
23295
23630
|
function buildSweepStartFrame(tangent, preferredUp) {
|
|
23296
23631
|
let up = normalizeVec3$1(preferredUp) ?? [0, 0, 1];
|
|
@@ -23722,15 +24057,15 @@ function meshTriangles(mesh) {
|
|
|
23722
24057
|
}
|
|
23723
24058
|
return out;
|
|
23724
24059
|
}
|
|
23725
|
-
function sub$
|
|
24060
|
+
function sub$2(a2, b) {
|
|
23726
24061
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
23727
24062
|
}
|
|
23728
24063
|
function scaleAdd(a2, b, scale2) {
|
|
23729
24064
|
return [a2[0] + b[0] * scale2, a2[1] + b[1] * scale2, a2[2] + b[2] * scale2];
|
|
23730
24065
|
}
|
|
23731
24066
|
function distanceSqToSegment(p2, a2, b) {
|
|
23732
|
-
const ab = sub$
|
|
23733
|
-
const t = Math.max(0, Math.min(1, dot3$3(sub$
|
|
24067
|
+
const ab = sub$2(b, a2);
|
|
24068
|
+
const t = Math.max(0, Math.min(1, dot3$3(sub$2(p2, a2), ab) / Math.max(dot3$3(ab, ab), 1e-20)));
|
|
23734
24069
|
const q = scaleAdd(a2, ab, t);
|
|
23735
24070
|
const dx = p2[0] - q[0];
|
|
23736
24071
|
const dy = p2[1] - q[1];
|
|
@@ -23738,19 +24073,19 @@ function distanceSqToSegment(p2, a2, b) {
|
|
|
23738
24073
|
return dx * dx + dy * dy + dz * dz;
|
|
23739
24074
|
}
|
|
23740
24075
|
function distanceSqToTriangle(p2, tri) {
|
|
23741
|
-
const ab = sub$
|
|
23742
|
-
const ac = sub$
|
|
23743
|
-
const ap = sub$
|
|
24076
|
+
const ab = sub$2(tri.b, tri.a);
|
|
24077
|
+
const ac = sub$2(tri.c, tri.a);
|
|
24078
|
+
const ap = sub$2(p2, tri.a);
|
|
23744
24079
|
const d1 = dot3$3(ab, ap);
|
|
23745
24080
|
const d2 = dot3$3(ac, ap);
|
|
23746
24081
|
if (d1 <= 0 && d2 <= 0) return dot3$3(ap, ap);
|
|
23747
|
-
const bp = sub$
|
|
24082
|
+
const bp = sub$2(p2, tri.b);
|
|
23748
24083
|
const d3 = dot3$3(ab, bp);
|
|
23749
24084
|
const d4 = dot3$3(ac, bp);
|
|
23750
24085
|
if (d3 >= 0 && d4 <= d3) return dot3$3(bp, bp);
|
|
23751
24086
|
const vc = d1 * d4 - d3 * d2;
|
|
23752
24087
|
if (vc <= 0 && d1 >= 0 && d3 <= 0) return distanceSqToSegment(p2, tri.a, tri.b);
|
|
23753
|
-
const cp = sub$
|
|
24088
|
+
const cp = sub$2(p2, tri.c);
|
|
23754
24089
|
const d5 = dot3$3(ab, cp);
|
|
23755
24090
|
const d6 = dot3$3(ac, cp);
|
|
23756
24091
|
if (d6 >= 0 && d5 <= d6) return dot3$3(cp, cp);
|
|
@@ -23765,13 +24100,13 @@ function distanceSqToTriangle(p2, tri) {
|
|
|
23765
24100
|
}
|
|
23766
24101
|
function rayHitsTriangleX(p2, tri) {
|
|
23767
24102
|
const dir = [1, 0, 0];
|
|
23768
|
-
const e1 = sub$
|
|
23769
|
-
const e2 = sub$
|
|
24103
|
+
const e1 = sub$2(tri.b, tri.a);
|
|
24104
|
+
const e2 = sub$2(tri.c, tri.a);
|
|
23770
24105
|
const h = [0, -e2[2], e2[1]];
|
|
23771
24106
|
const det = dot3$3(e1, h);
|
|
23772
24107
|
if (Math.abs(det) < 1e-10) return false;
|
|
23773
24108
|
const inv = 1 / det;
|
|
23774
|
-
const s = sub$
|
|
24109
|
+
const s = sub$2(p2, tri.a);
|
|
23775
24110
|
const u2 = inv * dot3$3(s, h);
|
|
23776
24111
|
if (u2 < 0 || u2 > 1) return false;
|
|
23777
24112
|
const q = [s[1] * e1[2] - s[2] * e1[1], s[2] * e1[0] - s[0] * e1[2], s[0] * e1[1] - s[1] * e1[0]];
|
|
@@ -24146,7 +24481,16 @@ function stitchBoundaryLoop(boundaries, tolerance) {
|
|
|
24146
24481
|
};
|
|
24147
24482
|
return tryStitch(boundaries[0], boundaries.slice(1)) ?? tryStitch(reversePoints$1(boundaries[0]), boundaries.slice(1));
|
|
24148
24483
|
}
|
|
24484
|
+
function rejectSdfInteriorConstraints(plan) {
|
|
24485
|
+
var _a3, _b3;
|
|
24486
|
+
if ((((_a3 = plan.interiorCurves) == null ? void 0 : _a3.length) ?? 0) > 0 || (((_b3 = plan.interiorPoints) == null ? void 0 : _b3.length) ?? 0) > 0) {
|
|
24487
|
+
throw new Error(
|
|
24488
|
+
"SDF backend cannot build a Surface.Fill() with interior constraint curves/points. Use the OCCT backend for constrained fills."
|
|
24489
|
+
);
|
|
24490
|
+
}
|
|
24491
|
+
}
|
|
24149
24492
|
function sampledFillBoundaryLoop(plan) {
|
|
24493
|
+
rejectSdfInteriorConstraints(plan);
|
|
24150
24494
|
if (plan.boundaries.length < 3) throw new Error("SDF backend sampled Surface.Fill() requires at least three boundary curves.");
|
|
24151
24495
|
const rawBoundaries = plan.boundaries.map((boundary) => sampleSurfaceCurve$1(boundary.curve, plan.resolution));
|
|
24152
24496
|
const tolerance = pointTolerance(rawBoundaries.flat());
|
|
@@ -24172,6 +24516,7 @@ function hasUnconstrainedCoonsFillBoundaries(plan) {
|
|
|
24172
24516
|
return !!namedBoundaryCurve$1(plan, "v0") && !!namedBoundaryCurve$1(plan, "u1") && !!namedBoundaryCurve$1(plan, "v1") && !!namedBoundaryCurve$1(plan, "u0") && !plan.boundaries.some((boundary) => boundary.support);
|
|
24173
24517
|
}
|
|
24174
24518
|
function surfaceGridForFillPlan$1(plan) {
|
|
24519
|
+
rejectSdfInteriorConstraints(plan);
|
|
24175
24520
|
const bottomCurve = namedBoundaryCurve$1(plan, "v0");
|
|
24176
24521
|
const rightCurve = namedBoundaryCurve$1(plan, "u1");
|
|
24177
24522
|
const topCurve = namedBoundaryCurve$1(plan, "v1");
|
|
@@ -28604,7 +28949,7 @@ function requireClipper() {
|
|
|
28604
28949
|
ClipperLib2.Error("DoMaxima error");
|
|
28605
28950
|
};
|
|
28606
28951
|
ClipperLib2.Clipper.ReversePaths = function(polys) {
|
|
28607
|
-
for (var i = 0,
|
|
28952
|
+
for (var i = 0, len2 = polys.length; i < len2; i++)
|
|
28608
28953
|
polys[i].reverse();
|
|
28609
28954
|
};
|
|
28610
28955
|
ClipperLib2.Clipper.Orientation = function(poly) {
|
|
@@ -29599,11 +29944,11 @@ function requireClipper() {
|
|
|
29599
29944
|
for (var i = 0; i < this.m_polyNodes.ChildCount(); i++) {
|
|
29600
29945
|
var node = this.m_polyNodes.Childs()[i];
|
|
29601
29946
|
this.m_srcPoly = node.m_polygon;
|
|
29602
|
-
var
|
|
29603
|
-
if (
|
|
29947
|
+
var len2 = this.m_srcPoly.length;
|
|
29948
|
+
if (len2 === 0 || delta <= 0 && (len2 < 3 || node.m_endtype !== ClipperLib2.EndType.etClosedPolygon))
|
|
29604
29949
|
continue;
|
|
29605
29950
|
this.m_destPoly = new Array();
|
|
29606
|
-
if (
|
|
29951
|
+
if (len2 === 1) {
|
|
29607
29952
|
if (node.m_jointype === ClipperLib2.JoinType.jtRound) {
|
|
29608
29953
|
var X = 1, Y = 0;
|
|
29609
29954
|
for (var j = 1; j <= steps; j++) {
|
|
@@ -29628,45 +29973,45 @@ function requireClipper() {
|
|
|
29628
29973
|
continue;
|
|
29629
29974
|
}
|
|
29630
29975
|
this.m_normals.length = 0;
|
|
29631
|
-
for (var j = 0; j <
|
|
29976
|
+
for (var j = 0; j < len2 - 1; j++)
|
|
29632
29977
|
this.m_normals.push(ClipperLib2.ClipperOffset.GetUnitNormal(this.m_srcPoly[j], this.m_srcPoly[j + 1]));
|
|
29633
29978
|
if (node.m_endtype === ClipperLib2.EndType.etClosedLine || node.m_endtype === ClipperLib2.EndType.etClosedPolygon)
|
|
29634
|
-
this.m_normals.push(ClipperLib2.ClipperOffset.GetUnitNormal(this.m_srcPoly[
|
|
29979
|
+
this.m_normals.push(ClipperLib2.ClipperOffset.GetUnitNormal(this.m_srcPoly[len2 - 1], this.m_srcPoly[0]));
|
|
29635
29980
|
else
|
|
29636
|
-
this.m_normals.push(new ClipperLib2.DoublePoint1(this.m_normals[
|
|
29981
|
+
this.m_normals.push(new ClipperLib2.DoublePoint1(this.m_normals[len2 - 2]));
|
|
29637
29982
|
if (node.m_endtype === ClipperLib2.EndType.etClosedPolygon) {
|
|
29638
|
-
var k2 =
|
|
29639
|
-
for (var j = 0; j <
|
|
29983
|
+
var k2 = len2 - 1;
|
|
29984
|
+
for (var j = 0; j < len2; j++)
|
|
29640
29985
|
k2 = this.OffsetPoint(j, k2, node.m_jointype);
|
|
29641
29986
|
this.m_destPolys.push(this.m_destPoly);
|
|
29642
29987
|
} else if (node.m_endtype === ClipperLib2.EndType.etClosedLine) {
|
|
29643
|
-
var k2 =
|
|
29644
|
-
for (var j = 0; j <
|
|
29988
|
+
var k2 = len2 - 1;
|
|
29989
|
+
for (var j = 0; j < len2; j++)
|
|
29645
29990
|
k2 = this.OffsetPoint(j, k2, node.m_jointype);
|
|
29646
29991
|
this.m_destPolys.push(this.m_destPoly);
|
|
29647
29992
|
this.m_destPoly = new Array();
|
|
29648
|
-
var n = this.m_normals[
|
|
29649
|
-
for (var j =
|
|
29993
|
+
var n = this.m_normals[len2 - 1];
|
|
29994
|
+
for (var j = len2 - 1; j > 0; j--)
|
|
29650
29995
|
this.m_normals[j] = new ClipperLib2.DoublePoint2(-this.m_normals[j - 1].X, -this.m_normals[j - 1].Y);
|
|
29651
29996
|
this.m_normals[0] = new ClipperLib2.DoublePoint2(-n.X, -n.Y);
|
|
29652
29997
|
k2 = 0;
|
|
29653
|
-
for (var j =
|
|
29998
|
+
for (var j = len2 - 1; j >= 0; j--)
|
|
29654
29999
|
k2 = this.OffsetPoint(j, k2, node.m_jointype);
|
|
29655
30000
|
this.m_destPolys.push(this.m_destPoly);
|
|
29656
30001
|
} else {
|
|
29657
30002
|
var k2 = 0;
|
|
29658
|
-
for (var j = 1; j <
|
|
30003
|
+
for (var j = 1; j < len2 - 1; ++j)
|
|
29659
30004
|
k2 = this.OffsetPoint(j, k2, node.m_jointype);
|
|
29660
30005
|
var pt1;
|
|
29661
30006
|
if (node.m_endtype === ClipperLib2.EndType.etOpenButt) {
|
|
29662
|
-
var j =
|
|
30007
|
+
var j = len2 - 1;
|
|
29663
30008
|
pt1 = new ClipperLib2.IntPoint2(ClipperLib2.ClipperOffset.Round(this.m_srcPoly[j].X + this.m_normals[j].X * delta), ClipperLib2.ClipperOffset.Round(this.m_srcPoly[j].Y + this.m_normals[j].Y * delta));
|
|
29664
30009
|
this.m_destPoly.push(pt1);
|
|
29665
30010
|
pt1 = new ClipperLib2.IntPoint2(ClipperLib2.ClipperOffset.Round(this.m_srcPoly[j].X - this.m_normals[j].X * delta), ClipperLib2.ClipperOffset.Round(this.m_srcPoly[j].Y - this.m_normals[j].Y * delta));
|
|
29666
30011
|
this.m_destPoly.push(pt1);
|
|
29667
30012
|
} else {
|
|
29668
|
-
var j =
|
|
29669
|
-
k2 =
|
|
30013
|
+
var j = len2 - 1;
|
|
30014
|
+
k2 = len2 - 2;
|
|
29670
30015
|
this.m_sinA = 0;
|
|
29671
30016
|
this.m_normals[j] = new ClipperLib2.DoublePoint2(-this.m_normals[j].X, -this.m_normals[j].Y);
|
|
29672
30017
|
if (node.m_endtype === ClipperLib2.EndType.etOpenSquare)
|
|
@@ -29674,10 +30019,10 @@ function requireClipper() {
|
|
|
29674
30019
|
else
|
|
29675
30020
|
this.DoRound(j, k2);
|
|
29676
30021
|
}
|
|
29677
|
-
for (var j =
|
|
30022
|
+
for (var j = len2 - 1; j > 0; j--)
|
|
29678
30023
|
this.m_normals[j] = new ClipperLib2.DoublePoint2(-this.m_normals[j - 1].X, -this.m_normals[j - 1].Y);
|
|
29679
30024
|
this.m_normals[0] = new ClipperLib2.DoublePoint2(-this.m_normals[1].X, -this.m_normals[1].Y);
|
|
29680
|
-
k2 =
|
|
30025
|
+
k2 = len2 - 1;
|
|
29681
30026
|
for (var j = k2 - 1; j > 0; --j)
|
|
29682
30027
|
k2 = this.OffsetPoint(j, k2, node.m_jointype);
|
|
29683
30028
|
if (node.m_endtype === ClipperLib2.EndType.etOpenButt) {
|
|
@@ -29881,13 +30226,13 @@ function requireClipper() {
|
|
|
29881
30226
|
if (polygon.length === 0 || polygon.length === 1 && polygon[0].length === 0 || delta < 0) return polygon;
|
|
29882
30227
|
if (!isPolygons) polygon = [polygon];
|
|
29883
30228
|
var k_length = polygon.length;
|
|
29884
|
-
var
|
|
30229
|
+
var len2, poly, result, d2, p2, j, i;
|
|
29885
30230
|
var results = [];
|
|
29886
30231
|
for (var k2 = 0; k2 < k_length; k2++) {
|
|
29887
30232
|
poly = polygon[k2];
|
|
29888
|
-
|
|
29889
|
-
if (
|
|
29890
|
-
else if (
|
|
30233
|
+
len2 = poly.length;
|
|
30234
|
+
if (len2 === 0) continue;
|
|
30235
|
+
else if (len2 < 3) {
|
|
29891
30236
|
result = poly;
|
|
29892
30237
|
results.push(result);
|
|
29893
30238
|
continue;
|
|
@@ -29896,7 +30241,7 @@ function requireClipper() {
|
|
|
29896
30241
|
d2 = delta * delta;
|
|
29897
30242
|
p2 = poly[0];
|
|
29898
30243
|
j = 1;
|
|
29899
|
-
for (i = 1; i <
|
|
30244
|
+
for (i = 1; i < len2; i++) {
|
|
29900
30245
|
if ((poly[i].X - p2.X) * (poly[i].X - p2.X) + (poly[i].Y - p2.Y) * (poly[i].Y - p2.Y) <= d2)
|
|
29901
30246
|
continue;
|
|
29902
30247
|
result[j] = poly[i];
|
|
@@ -29906,8 +30251,8 @@ function requireClipper() {
|
|
|
29906
30251
|
p2 = poly[j - 1];
|
|
29907
30252
|
if ((poly[0].X - p2.X) * (poly[0].X - p2.X) + (poly[0].Y - p2.Y) * (poly[0].Y - p2.Y) <= d2)
|
|
29908
30253
|
j--;
|
|
29909
|
-
if (j <
|
|
29910
|
-
result.splice(j,
|
|
30254
|
+
if (j < len2)
|
|
30255
|
+
result.splice(j, len2 - j);
|
|
29911
30256
|
if (result.length) results.push(result);
|
|
29912
30257
|
}
|
|
29913
30258
|
if (!isPolygons && results.length) results = results[0];
|
|
@@ -29925,9 +30270,9 @@ function requireClipper() {
|
|
|
29925
30270
|
];
|
|
29926
30271
|
var isPolygons = polygon[0] instanceof Array;
|
|
29927
30272
|
if (!isPolygons) polygon = [polygon];
|
|
29928
|
-
var
|
|
29929
|
-
var results = new Array(
|
|
29930
|
-
for (i = 0; i <
|
|
30273
|
+
var len2 = polygon.length, plen, i, j, result;
|
|
30274
|
+
var results = new Array(len2);
|
|
30275
|
+
for (i = 0; i < len2; i++) {
|
|
29931
30276
|
plen = polygon[i].length;
|
|
29932
30277
|
result = new Array(plen);
|
|
29933
30278
|
for (j = 0; j < plen; j++) {
|
|
@@ -29954,10 +30299,10 @@ function requireClipper() {
|
|
|
29954
30299
|
if (!isPolygons) polygon = [polygon];
|
|
29955
30300
|
var i, j, poly, k2, poly2, plen, A, B2, P, d2, rem, addlast;
|
|
29956
30301
|
var bxax, byay, l, ax, ay;
|
|
29957
|
-
var
|
|
30302
|
+
var len2 = polygon.length;
|
|
29958
30303
|
var toleranceSq = tolerance * tolerance;
|
|
29959
30304
|
var results = [];
|
|
29960
|
-
for (i = 0; i <
|
|
30305
|
+
for (i = 0; i < len2; i++) {
|
|
29961
30306
|
poly = polygon[i];
|
|
29962
30307
|
plen = poly.length;
|
|
29963
30308
|
if (plen === 0) continue;
|
|
@@ -32229,7 +32574,7 @@ function key(point2) {
|
|
|
32229
32574
|
const clean = (value) => (Math.abs(value) < 5e-8 ? 0 : value).toFixed(7);
|
|
32230
32575
|
return `${clean(point2[0])},${clean(point2[1])}`;
|
|
32231
32576
|
}
|
|
32232
|
-
function edgeKey$
|
|
32577
|
+
function edgeKey$2(a2, b) {
|
|
32233
32578
|
return a2 < b ? `${a2}|${b}` : `${b}|${a2}`;
|
|
32234
32579
|
}
|
|
32235
32580
|
function loopsFromSegments$1(segments) {
|
|
@@ -32250,18 +32595,18 @@ function loopsFromSegments$1(segments) {
|
|
|
32250
32595
|
const loops = [];
|
|
32251
32596
|
for (const start of adjacency.keys()) {
|
|
32252
32597
|
for (const first of adjacency.get(start) ?? []) {
|
|
32253
|
-
if (visited.has(edgeKey$
|
|
32598
|
+
if (visited.has(edgeKey$2(start, first))) continue;
|
|
32254
32599
|
const loop = [start];
|
|
32255
32600
|
let previous = start;
|
|
32256
32601
|
let current = first;
|
|
32257
|
-
visited.add(edgeKey$
|
|
32602
|
+
visited.add(edgeKey$2(start, first));
|
|
32258
32603
|
while (current !== start) {
|
|
32259
32604
|
loop.push(current);
|
|
32260
32605
|
const next = Array.from(adjacency.get(current) ?? []).find(
|
|
32261
|
-
(candidate) => candidate !== previous && !visited.has(edgeKey$
|
|
32606
|
+
(candidate) => candidate !== previous && !visited.has(edgeKey$2(current, candidate))
|
|
32262
32607
|
);
|
|
32263
32608
|
if (!next) break;
|
|
32264
|
-
visited.add(edgeKey$
|
|
32609
|
+
visited.add(edgeKey$2(current, next));
|
|
32265
32610
|
previous = current;
|
|
32266
32611
|
current = next;
|
|
32267
32612
|
}
|
|
@@ -32948,11 +33293,11 @@ function triangulateStepFace(face, filePath) {
|
|
|
32948
33293
|
const a2 = allPoints[ia];
|
|
32949
33294
|
const b = allPoints[ib];
|
|
32950
33295
|
const c2 = allPoints[ic];
|
|
32951
|
-
const
|
|
32952
|
-
if (Math.hypot(
|
|
33296
|
+
const triNormal2 = cross3$4(sub3$2(b, a2), sub3$2(c2, a2));
|
|
33297
|
+
if (Math.hypot(triNormal2[0], triNormal2[1], triNormal2[2]) <= EPS$2) {
|
|
32953
33298
|
failStepImport(filePath, `${face.context} triangulates to a degenerate triangle.`);
|
|
32954
33299
|
}
|
|
32955
|
-
return dot3$3(
|
|
33300
|
+
return dot3$3(triNormal2, normal2) >= 0 ? [a2, b, c2] : [a2, c2, b];
|
|
32956
33301
|
});
|
|
32957
33302
|
}
|
|
32958
33303
|
const EPS$1 = 1e-6;
|
|
@@ -36094,6 +36439,8 @@ function lowerShapeCompilePlanToSdfField(plan) {
|
|
|
36094
36439
|
}
|
|
36095
36440
|
case "fromSlices":
|
|
36096
36441
|
return lowerFromSlices(plan);
|
|
36442
|
+
case "fromSectionFrames":
|
|
36443
|
+
throw new Error("Shape.fromSlices framed section lofts require the Manifold backend.");
|
|
36097
36444
|
case "fillet":
|
|
36098
36445
|
throwSdfEdgeFeatureUnsupported("fillet()");
|
|
36099
36446
|
case "chamfer":
|
|
@@ -36102,6 +36449,10 @@ function lowerShapeCompilePlanToSdfField(plan) {
|
|
|
36102
36449
|
throwSdfEdgeFeatureUnsupported("filletEdges()");
|
|
36103
36450
|
case "cornerYBlend":
|
|
36104
36451
|
throwSdfEdgeFeatureUnsupported("Blend.CornerY()");
|
|
36452
|
+
case "faceFillet":
|
|
36453
|
+
throwSdfEdgeFeatureUnsupported("Blend.Face()");
|
|
36454
|
+
case "fullRound":
|
|
36455
|
+
throwSdfEdgeFeatureUnsupported("Blend.FullRound()");
|
|
36105
36456
|
case "chamferEdges":
|
|
36106
36457
|
throwSdfEdgeFeatureUnsupported("chamferEdges()");
|
|
36107
36458
|
case "nurbsSurface":
|
|
@@ -36404,10 +36755,10 @@ function transformProfilePoint(point2, transform) {
|
|
|
36404
36755
|
case "mirror": {
|
|
36405
36756
|
const nx = transform.normalX;
|
|
36406
36757
|
const ny = transform.normalY;
|
|
36407
|
-
const
|
|
36408
|
-
if (
|
|
36409
|
-
const ux = nx /
|
|
36410
|
-
const uy = ny /
|
|
36758
|
+
const len2 = Math.hypot(nx, ny);
|
|
36759
|
+
if (len2 <= EPS) return point2;
|
|
36760
|
+
const ux = nx / len2;
|
|
36761
|
+
const uy = ny / len2;
|
|
36411
36762
|
const d2 = point2[0] * ux + point2[1] * uy;
|
|
36412
36763
|
return [point2[0] - 2 * d2 * ux, point2[1] - 2 * d2 * uy];
|
|
36413
36764
|
}
|
|
@@ -36899,10 +37250,10 @@ class TruckPolygonProfileBackend {
|
|
|
36899
37250
|
}
|
|
36900
37251
|
mirror(ax) {
|
|
36901
37252
|
const [nx0, ny0] = ax;
|
|
36902
|
-
const
|
|
36903
|
-
if (
|
|
36904
|
-
const nx = nx0 /
|
|
36905
|
-
const ny = ny0 /
|
|
37253
|
+
const len2 = Math.hypot(nx0, ny0);
|
|
37254
|
+
if (len2 <= 1e-12) return wrapTruckProfileRegions(this.regions);
|
|
37255
|
+
const nx = nx0 / len2;
|
|
37256
|
+
const ny = ny0 / len2;
|
|
36906
37257
|
return wrapTruckProfileRegions(
|
|
36907
37258
|
transformRegions(this.regions, ([x2, y2]) => {
|
|
36908
37259
|
const d2 = 2 * (x2 * nx + y2 * ny);
|
|
@@ -36981,23 +37332,23 @@ function boundsInteriorOverlap$1(a2, b) {
|
|
|
36981
37332
|
return Math.min(a2.max[0], b.max[0]) - Math.max(a2.min[0], b.min[0]) > tolerance && Math.min(a2.max[1], b.max[1]) - Math.max(a2.min[1], b.min[1]) > tolerance && Math.min(a2.max[2], b.max[2]) - Math.max(a2.min[2], b.min[2]) > tolerance;
|
|
36982
37333
|
}
|
|
36983
37334
|
function normalizeVec3(v) {
|
|
36984
|
-
const
|
|
36985
|
-
if (
|
|
36986
|
-
return [v[0] /
|
|
37335
|
+
const len2 = Math.hypot(v[0], v[1], v[2]);
|
|
37336
|
+
if (len2 <= 1e-12) return [0, 0, 0];
|
|
37337
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
36987
37338
|
}
|
|
36988
|
-
function dot$
|
|
37339
|
+
function dot$2(a2, b) {
|
|
36989
37340
|
return a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2];
|
|
36990
37341
|
}
|
|
36991
|
-
function add(a2, b) {
|
|
37342
|
+
function add$1(a2, b) {
|
|
36992
37343
|
return [a2[0] + b[0], a2[1] + b[1], a2[2] + b[2]];
|
|
36993
37344
|
}
|
|
36994
|
-
function sub(a2, b) {
|
|
37345
|
+
function sub$1(a2, b) {
|
|
36995
37346
|
return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
36996
37347
|
}
|
|
36997
37348
|
function scale(v, scalar) {
|
|
36998
37349
|
return [v[0] * scalar, v[1] * scalar, v[2] * scalar];
|
|
36999
37350
|
}
|
|
37000
|
-
function cross$
|
|
37351
|
+
function cross$2(a2, b) {
|
|
37001
37352
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
37002
37353
|
}
|
|
37003
37354
|
function isFiniteNumber(value) {
|
|
@@ -37017,7 +37368,7 @@ function axisSpan(face, axis) {
|
|
|
37017
37368
|
let min2 = Infinity;
|
|
37018
37369
|
let max2 = -Infinity;
|
|
37019
37370
|
for (const vertex2 of face.vertices) {
|
|
37020
|
-
const station = dot$
|
|
37371
|
+
const station = dot$2(vertex2, axis);
|
|
37021
37372
|
min2 = Math.min(min2, station);
|
|
37022
37373
|
max2 = Math.max(max2, station);
|
|
37023
37374
|
}
|
|
@@ -37233,8 +37584,8 @@ function boundsCorners(bounds) {
|
|
|
37233
37584
|
}
|
|
37234
37585
|
function perpendicularAxes(normal2) {
|
|
37235
37586
|
const seed = Math.abs(normal2[2]) < 0.9 ? [0, 0, 1] : [0, 1, 0];
|
|
37236
|
-
const uAxis = normalizeVec3(cross$
|
|
37237
|
-
const vAxis = normalizeVec3(cross$
|
|
37587
|
+
const uAxis = normalizeVec3(cross$2(seed, normal2));
|
|
37588
|
+
const vAxis = normalizeVec3(cross$2(normal2, uAxis));
|
|
37238
37589
|
return { uAxis, vAxis };
|
|
37239
37590
|
}
|
|
37240
37591
|
function createBoundedHalfSpace(bounds, normal2, originOffset) {
|
|
@@ -37253,10 +37604,10 @@ function createBoundedHalfSpace(bounds, normal2, originOffset) {
|
|
|
37253
37604
|
let maxV = -Infinity;
|
|
37254
37605
|
let maxDistance = -Infinity;
|
|
37255
37606
|
for (const corner of corners) {
|
|
37256
|
-
const relative = sub(corner, planeOrigin);
|
|
37257
|
-
const u2 = dot$
|
|
37258
|
-
const v = dot$
|
|
37259
|
-
const distance = dot$
|
|
37607
|
+
const relative = sub$1(corner, planeOrigin);
|
|
37608
|
+
const u2 = dot$2(relative, uAxis);
|
|
37609
|
+
const v = dot$2(relative, vAxis);
|
|
37610
|
+
const distance = dot$2(corner, n) - signedOffset;
|
|
37260
37611
|
minU = Math.min(minU, u2);
|
|
37261
37612
|
maxU = Math.max(maxU, u2);
|
|
37262
37613
|
minV = Math.min(minV, v);
|
|
@@ -37268,7 +37619,7 @@ function createBoundedHalfSpace(bounds, normal2, originOffset) {
|
|
|
37268
37619
|
const height = Math.max(maxDistance + margin, margin);
|
|
37269
37620
|
const centerU = (minU + maxU) / 2;
|
|
37270
37621
|
const centerV = (minV + maxV) / 2;
|
|
37271
|
-
const translation = add(add(planeOrigin, scale(uAxis, centerU)), scale(vAxis, centerV));
|
|
37622
|
+
const translation = add$1(add$1(planeOrigin, scale(uAxis, centerU)), scale(vAxis, centerV));
|
|
37272
37623
|
const matrix = [
|
|
37273
37624
|
uAxis[0],
|
|
37274
37625
|
uAxis[1],
|
|
@@ -37302,7 +37653,7 @@ function faceAxes(face) {
|
|
|
37302
37653
|
for (const vertex2 of face.vertices.slice(1)) {
|
|
37303
37654
|
const uAxis = normalizeVec3([vertex2[0] - origin[0], vertex2[1] - origin[1], vertex2[2] - origin[2]]);
|
|
37304
37655
|
if (Math.hypot(...uAxis) <= 1e-12) continue;
|
|
37305
|
-
const vAxis = normalizeVec3(cross$
|
|
37656
|
+
const vAxis = normalizeVec3(cross$2(face.normal, uAxis));
|
|
37306
37657
|
if (Math.hypot(...vAxis) <= 1e-12) continue;
|
|
37307
37658
|
return {
|
|
37308
37659
|
uAxis,
|
|
@@ -37311,18 +37662,18 @@ function faceAxes(face) {
|
|
|
37311
37662
|
}
|
|
37312
37663
|
return {};
|
|
37313
37664
|
}
|
|
37314
|
-
function edgeKey(start, end) {
|
|
37665
|
+
function edgeKey$1(start, end) {
|
|
37315
37666
|
const encode = (p2) => p2.map((value) => value.toFixed(9)).join(",");
|
|
37316
37667
|
const a2 = encode(start);
|
|
37317
37668
|
const b = encode(end);
|
|
37318
37669
|
return a2 < b ? `${a2}|${b}` : `${b}|${a2}`;
|
|
37319
37670
|
}
|
|
37320
37671
|
function faceEdgeIndex(face, start, end) {
|
|
37321
|
-
const target = edgeKey(start, end);
|
|
37672
|
+
const target = edgeKey$1(start, end);
|
|
37322
37673
|
for (let i = 0; i < face.vertices.length; i++) {
|
|
37323
37674
|
const faceStart = face.vertices[i];
|
|
37324
37675
|
const faceEnd = face.vertices[(i + 1) % face.vertices.length];
|
|
37325
|
-
if (faceStart && faceEnd && edgeKey(faceStart, faceEnd) === target) return i;
|
|
37676
|
+
if (faceStart && faceEnd && edgeKey$1(faceStart, faceEnd) === target) return i;
|
|
37326
37677
|
}
|
|
37327
37678
|
return null;
|
|
37328
37679
|
}
|
|
@@ -37523,7 +37874,7 @@ function topologyPayloadToTopology(payload) {
|
|
|
37523
37874
|
const start = explicitVertices[explicitEdge.vertices[0]];
|
|
37524
37875
|
const end = explicitVertices[explicitEdge.vertices[1]];
|
|
37525
37876
|
if (!isVec3(start) || !isVec3(end)) continue;
|
|
37526
|
-
const key2 = edgeKey(start, end);
|
|
37877
|
+
const key2 = edgeKey$1(start, end);
|
|
37527
37878
|
if (seenEdges.has(key2)) continue;
|
|
37528
37879
|
seenEdges.set(key2, edges.size);
|
|
37529
37880
|
const display = explicitEdgeDisplayName(payload, explicitEdge, explicitEdgeIndex, start, end);
|
|
@@ -37543,7 +37894,7 @@ function topologyPayloadToTopology(payload) {
|
|
|
37543
37894
|
const start = face.vertices[i];
|
|
37544
37895
|
const end = face.vertices[(i + 1) % face.vertices.length];
|
|
37545
37896
|
if (!start || !end) continue;
|
|
37546
|
-
const key2 = edgeKey(start, end);
|
|
37897
|
+
const key2 = edgeKey$1(start, end);
|
|
37547
37898
|
if (seenEdges.has(key2)) continue;
|
|
37548
37899
|
seenEdges.set(key2, edges.size);
|
|
37549
37900
|
edges.set(`${face.id}:edge-${i}`, {
|
|
@@ -37592,11 +37943,11 @@ const _TruckShapeBackend = class _TruckShapeBackend {
|
|
|
37592
37943
|
}
|
|
37593
37944
|
mirror(normal2) {
|
|
37594
37945
|
const [nx0, ny0, nz0] = normal2;
|
|
37595
|
-
const
|
|
37596
|
-
if (
|
|
37597
|
-
const nx = nx0 /
|
|
37598
|
-
const ny = ny0 /
|
|
37599
|
-
const nz = nz0 /
|
|
37946
|
+
const len2 = Math.hypot(nx0, ny0, nz0);
|
|
37947
|
+
if (len2 < 1e-12) return this.clone();
|
|
37948
|
+
const nx = nx0 / len2;
|
|
37949
|
+
const ny = ny0 / len2;
|
|
37950
|
+
const nz = nz0 / len2;
|
|
37600
37951
|
const matrix = [
|
|
37601
37952
|
1 - 2 * nx * nx,
|
|
37602
37953
|
-2 * ny * nx,
|
|
@@ -37638,7 +37989,7 @@ const _TruckShapeBackend = class _TruckShapeBackend {
|
|
|
37638
37989
|
if (normalLength <= 1e-12) throw new Error("Truck trimByPlane() normal vector must not be zero.");
|
|
37639
37990
|
const n = [normal2[0] / normalLength, normal2[1] / normalLength, normal2[2] / normalLength];
|
|
37640
37991
|
const signedOffset = originOffset / normalLength;
|
|
37641
|
-
const distances = boundsCorners(bounds).map((corner) => dot$
|
|
37992
|
+
const distances = boundsCorners(bounds).map((corner) => dot$2(corner, n) - signedOffset);
|
|
37642
37993
|
if (distances.every((distance) => distance >= -1e-8)) return this.clone();
|
|
37643
37994
|
if (distances.every((distance) => distance <= 1e-8)) return new _TruckShapeBackend(wasm.geometry_create_empty());
|
|
37644
37995
|
const halfSpace = createBoundedHalfSpace(bounds, normal2, originOffset);
|
|
@@ -37785,10 +38136,10 @@ function applyProfileTransformPoint(point2, step) {
|
|
|
37785
38136
|
case "scale":
|
|
37786
38137
|
return [x2 * step.x, y2 * step.y];
|
|
37787
38138
|
case "mirror": {
|
|
37788
|
-
const
|
|
37789
|
-
if (
|
|
37790
|
-
const nx = step.normalX /
|
|
37791
|
-
const ny = step.normalY /
|
|
38139
|
+
const len2 = Math.hypot(step.normalX, step.normalY);
|
|
38140
|
+
if (len2 < 1e-12) return [x2, y2];
|
|
38141
|
+
const nx = step.normalX / len2;
|
|
38142
|
+
const ny = step.normalY / len2;
|
|
37792
38143
|
const d2 = x2 * nx + y2 * ny;
|
|
37793
38144
|
return [x2 - 2 * d2 * nx, y2 - 2 * d2 * ny];
|
|
37794
38145
|
}
|
|
@@ -39715,6 +40066,9 @@ function applyNativeTruckFilletTargets(initialShape, targets, radius, segments,
|
|
|
39715
40066
|
}
|
|
39716
40067
|
}
|
|
39717
40068
|
function lowerFilletEdgesPlan(plan) {
|
|
40069
|
+
if (plan.variableRadius) {
|
|
40070
|
+
throw new Error("Variable-radius fillet requires the OCCT backend. Run the CLI with --backend occt.");
|
|
40071
|
+
}
|
|
39718
40072
|
if (plan.edgeQuery) {
|
|
39719
40073
|
throw new Error("filletEdges(): deferred edge queries are not supported by the Truck lowerer yet.");
|
|
39720
40074
|
}
|
|
@@ -40250,11 +40604,11 @@ function applyShapeTransformToPoint(point2, step) {
|
|
|
40250
40604
|
point2
|
|
40251
40605
|
);
|
|
40252
40606
|
case "mirror": {
|
|
40253
|
-
const
|
|
40254
|
-
if (
|
|
40255
|
-
const nx = step.normalX /
|
|
40256
|
-
const ny = step.normalY /
|
|
40257
|
-
const nz = step.normalZ /
|
|
40607
|
+
const len2 = Math.hypot(step.normalX, step.normalY, step.normalZ);
|
|
40608
|
+
if (len2 < 1e-12) return [point2[0], point2[1], point2[2]];
|
|
40609
|
+
const nx = step.normalX / len2;
|
|
40610
|
+
const ny = step.normalY / len2;
|
|
40611
|
+
const nz = step.normalZ / len2;
|
|
40258
40612
|
const d2 = point2[0] * nx + point2[1] * ny + point2[2] * nz;
|
|
40259
40613
|
return [point2[0] - 2 * d2 * nx, point2[1] - 2 * d2 * ny, point2[2] - 2 * d2 * nz];
|
|
40260
40614
|
}
|
|
@@ -40335,9 +40689,9 @@ function triangleNormal(vertices, triangle) {
|
|
|
40335
40689
|
const ab = [b[0] - a2[0], b[1] - a2[1], b[2] - a2[2]];
|
|
40336
40690
|
const ac = [c2[0] - a2[0], c2[1] - a2[1], c2[2] - a2[2]];
|
|
40337
40691
|
const normal2 = cross3$3(ab, ac);
|
|
40338
|
-
const
|
|
40339
|
-
if (
|
|
40340
|
-
return [normal2[0] /
|
|
40692
|
+
const len2 = Math.hypot(normal2[0], normal2[1], normal2[2]);
|
|
40693
|
+
if (len2 <= 1e-12) return null;
|
|
40694
|
+
return [normal2[0] / len2, normal2[1] / len2, normal2[2] / len2];
|
|
40341
40695
|
}
|
|
40342
40696
|
function offsetClosedSurfaceMesh(mesh, thickness) {
|
|
40343
40697
|
let triangles;
|
|
@@ -40361,12 +40715,12 @@ function offsetClosedSurfaceMesh(mesh, thickness) {
|
|
|
40361
40715
|
}
|
|
40362
40716
|
const offsetVertices = mesh.vertices.map((vertex2, index2) => {
|
|
40363
40717
|
const normal2 = normals[index2];
|
|
40364
|
-
const
|
|
40365
|
-
if (
|
|
40718
|
+
const len2 = Math.hypot(normal2[0], normal2[1], normal2[2]);
|
|
40719
|
+
if (len2 <= 1e-12) return [vertex2[0], vertex2[1], vertex2[2]];
|
|
40366
40720
|
return [
|
|
40367
|
-
vertex2[0] - normal2[0] /
|
|
40368
|
-
vertex2[1] - normal2[1] /
|
|
40369
|
-
vertex2[2] - normal2[2] /
|
|
40721
|
+
vertex2[0] - normal2[0] / len2 * thickness,
|
|
40722
|
+
vertex2[1] - normal2[1] / len2 * thickness,
|
|
40723
|
+
vertex2[2] - normal2[2] / len2 * thickness
|
|
40370
40724
|
];
|
|
40371
40725
|
});
|
|
40372
40726
|
const innerOffset = mesh.vertices.length;
|
|
@@ -40425,6 +40779,10 @@ function lowerSurfaceRuledPlan(plan) {
|
|
|
40425
40779
|
return lowerSurfaceGrid(surfaceGridForRuledPlan(plan));
|
|
40426
40780
|
}
|
|
40427
40781
|
function lowerSurfaceFillPlan(plan) {
|
|
40782
|
+
var _a3, _b3;
|
|
40783
|
+
if ((((_a3 = plan.interiorCurves) == null ? void 0 : _a3.length) ?? 0) > 0 || (((_b3 = plan.interiorPoints) == null ? void 0 : _b3.length) ?? 0) > 0) {
|
|
40784
|
+
truckUnsupported("surface fill with interior constraint curves/points");
|
|
40785
|
+
}
|
|
40428
40786
|
const grid = surfaceGridForFillPlan(plan);
|
|
40429
40787
|
if (!grid) truckUnsupported("surface fill constraints beyond four-boundary Coons patches");
|
|
40430
40788
|
return lowerSurfaceGrid(grid);
|
|
@@ -40701,6 +41059,10 @@ function lowerShapeCompilePlanToTruckBackendUncached(plan) {
|
|
|
40701
41059
|
return lowerChamferEdgesPlan(plan);
|
|
40702
41060
|
case "cornerYBlend":
|
|
40703
41061
|
return lowerCornerYBlendPlan();
|
|
41062
|
+
case "faceFillet":
|
|
41063
|
+
throw new Error("Blend.Face() requires the OCCT backend. Run the CLI with --backend occt.");
|
|
41064
|
+
case "fullRound":
|
|
41065
|
+
throw new Error("Blend.FullRound() requires the OCCT backend. Run the CLI with --backend occt.");
|
|
40704
41066
|
case "draft":
|
|
40705
41067
|
return lowerDraftPlan(plan);
|
|
40706
41068
|
case "shell":
|
|
@@ -41044,10 +41406,10 @@ function transformProfilePointForRadial(point2, transform) {
|
|
|
41044
41406
|
return [point2[0] * c2 - point2[1] * s, point2[0] * s + point2[1] * c2];
|
|
41045
41407
|
}
|
|
41046
41408
|
case "mirror": {
|
|
41047
|
-
const
|
|
41048
|
-
if (
|
|
41049
|
-
const nx = transform.normalX /
|
|
41050
|
-
const ny = transform.normalY /
|
|
41409
|
+
const len2 = Math.hypot(transform.normalX, transform.normalY);
|
|
41410
|
+
if (len2 <= EXACT_PROFILE_EPS) return null;
|
|
41411
|
+
const nx = transform.normalX / len2;
|
|
41412
|
+
const ny = transform.normalY / len2;
|
|
41051
41413
|
const d2 = point2[0] * nx + point2[1] * ny;
|
|
41052
41414
|
return [point2[0] - 2 * d2 * nx, point2[1] - 2 * d2 * ny];
|
|
41053
41415
|
}
|
|
@@ -43296,6 +43658,8 @@ function exactVerticalProjectionProfilePlan(plan) {
|
|
|
43296
43658
|
const equivalent = zAlignedFromSlicesShapePlan(plan);
|
|
43297
43659
|
return equivalent ? exactVerticalProjectionProfilePlan(equivalent) : null;
|
|
43298
43660
|
}
|
|
43661
|
+
case "fromSectionFrames":
|
|
43662
|
+
return null;
|
|
43299
43663
|
case "transform": {
|
|
43300
43664
|
const base = exactVerticalProjectionProfilePlan(plan.base);
|
|
43301
43665
|
if (!base) return null;
|
|
@@ -43736,6 +44100,8 @@ function exactTruckProjectedProfilePlan(plan) {
|
|
|
43736
44100
|
const equivalent = zAlignedFromSlicesShapePlan(plan);
|
|
43737
44101
|
return equivalent ? exactTruckProjectedProfilePlan(equivalent) : null;
|
|
43738
44102
|
}
|
|
44103
|
+
case "fromSectionFrames":
|
|
44104
|
+
return null;
|
|
43739
44105
|
case "sheetMetal":
|
|
43740
44106
|
case "shell":
|
|
43741
44107
|
case "hole":
|
|
@@ -43745,6 +44111,8 @@ function exactTruckProjectedProfilePlan(plan) {
|
|
|
43745
44111
|
case "chamfer":
|
|
43746
44112
|
case "filletEdges":
|
|
43747
44113
|
case "cornerYBlend":
|
|
44114
|
+
case "faceFillet":
|
|
44115
|
+
case "fullRound":
|
|
43748
44116
|
case "chamferEdges":
|
|
43749
44117
|
case "draft":
|
|
43750
44118
|
case "importedMesh":
|
|
@@ -43884,6 +44252,8 @@ function exactTruckSlicedProfilePlan(plan, offset) {
|
|
|
43884
44252
|
const equivalent = zAlignedFromSlicesShapePlan(plan);
|
|
43885
44253
|
return equivalent ? exactTruckSlicedProfilePlan(equivalent, offset) : null;
|
|
43886
44254
|
}
|
|
44255
|
+
case "fromSectionFrames":
|
|
44256
|
+
return null;
|
|
43887
44257
|
case "sheetMetal":
|
|
43888
44258
|
case "shell":
|
|
43889
44259
|
case "hole":
|
|
@@ -43893,6 +44263,8 @@ function exactTruckSlicedProfilePlan(plan, offset) {
|
|
|
43893
44263
|
case "chamfer":
|
|
43894
44264
|
case "filletEdges":
|
|
43895
44265
|
case "cornerYBlend":
|
|
44266
|
+
case "faceFillet":
|
|
44267
|
+
case "fullRound":
|
|
43896
44268
|
case "chamferEdges":
|
|
43897
44269
|
case "draft":
|
|
43898
44270
|
case "importedMesh":
|
|
@@ -44046,6 +44418,9 @@ function tracePlanTransformations(plan) {
|
|
|
44046
44418
|
case "fillet":
|
|
44047
44419
|
case "chamfer":
|
|
44048
44420
|
case "filletEdges":
|
|
44421
|
+
case "faceFillet":
|
|
44422
|
+
case "fullRound":
|
|
44423
|
+
case "cornerYBlend":
|
|
44049
44424
|
case "chamferEdges":
|
|
44050
44425
|
case "draft":
|
|
44051
44426
|
case "offsetSolid":
|
|
@@ -44059,24 +44434,27 @@ function findOriginOperation(plan) {
|
|
|
44059
44434
|
if (!plan) return { operation: "unknown" };
|
|
44060
44435
|
switch (plan.kind) {
|
|
44061
44436
|
case "queryOwner":
|
|
44062
|
-
return { operation: plan.owner.operation, owner: plan.owner };
|
|
44437
|
+
return { operation: plan.owner.operation, owner: plan.owner, originPlan: plan };
|
|
44063
44438
|
case "transform":
|
|
44064
44439
|
return findOriginOperation(plan.base);
|
|
44065
44440
|
case "boolean":
|
|
44066
|
-
return { operation: `${plan.op} (${plan.shapes.length} operands)
|
|
44441
|
+
return { operation: `${plan.op} (${plan.shapes.length} operands)`, originPlan: plan };
|
|
44067
44442
|
case "shell":
|
|
44068
44443
|
case "hole":
|
|
44069
44444
|
case "cut":
|
|
44070
44445
|
case "fillet":
|
|
44071
44446
|
case "chamfer":
|
|
44072
44447
|
case "filletEdges":
|
|
44448
|
+
case "faceFillet":
|
|
44449
|
+
case "fullRound":
|
|
44450
|
+
case "cornerYBlend":
|
|
44073
44451
|
case "chamferEdges":
|
|
44074
44452
|
case "draft":
|
|
44075
44453
|
case "offsetSolid":
|
|
44076
44454
|
case "trimByPlane":
|
|
44077
44455
|
return findOriginOperation(plan.base);
|
|
44078
44456
|
default:
|
|
44079
|
-
return { operation: plan.kind };
|
|
44457
|
+
return { operation: plan.kind, originPlan: plan };
|
|
44080
44458
|
}
|
|
44081
44459
|
}
|
|
44082
44460
|
function summarizeProfile(profile) {
|
|
@@ -44138,6 +44516,18 @@ function collectTimelineEntries(plan, entries) {
|
|
|
44138
44516
|
collectTimelineEntries(plan.base, entries);
|
|
44139
44517
|
entries.push({ kind: "filletEdges", label: "Fillet Edges", summary: `r = ${plan.radius}`, category: "modifier" });
|
|
44140
44518
|
return;
|
|
44519
|
+
case "faceFillet":
|
|
44520
|
+
collectTimelineEntries(plan.base, entries);
|
|
44521
|
+
entries.push({ kind: "faceFillet", label: "Face Fillet", summary: `r = ${plan.radius}`, category: "modifier" });
|
|
44522
|
+
return;
|
|
44523
|
+
case "fullRound":
|
|
44524
|
+
collectTimelineEntries(plan.base, entries);
|
|
44525
|
+
entries.push({ kind: "fullRound", label: "Full Round", summary: `r = ${plan.radius}`, category: "modifier" });
|
|
44526
|
+
return;
|
|
44527
|
+
case "cornerYBlend":
|
|
44528
|
+
collectTimelineEntries(plan.base, entries);
|
|
44529
|
+
entries.push({ kind: "cornerYBlend", label: "Corner Y-Blend", summary: `r = ${plan.radius}`, category: "modifier" });
|
|
44530
|
+
return;
|
|
44141
44531
|
case "chamferEdges":
|
|
44142
44532
|
collectTimelineEntries(plan.base, entries);
|
|
44143
44533
|
entries.push({ kind: "chamferEdges", label: "Chamfer Edges", summary: `size = ${plan.size}`, category: "modifier" });
|
|
@@ -44221,13 +44611,14 @@ function buildOperationTimeline(plan) {
|
|
|
44221
44611
|
collectTimelineEntries(plan, entries);
|
|
44222
44612
|
return entries;
|
|
44223
44613
|
}
|
|
44224
|
-
function traceFaceTransformationHistory(plan, face) {
|
|
44614
|
+
function traceFaceTransformationHistory(plan, face, sourceSpans) {
|
|
44225
44615
|
const transformations = tracePlanTransformations(plan);
|
|
44226
|
-
const origin = findOriginOperation(plan);
|
|
44616
|
+
const { originPlan, ...origin } = findOriginOperation(plan);
|
|
44227
44617
|
const timeline = buildOperationTimeline(plan);
|
|
44618
|
+
const sourceSpan = originPlan && sourceSpans ? sourceSpans.get(shapeCompilePlanCacheKey(originPlan)) : void 0;
|
|
44228
44619
|
return {
|
|
44229
44620
|
faceName: face.name,
|
|
44230
|
-
origin,
|
|
44621
|
+
origin: sourceSpan ? { ...origin, sourceSpan } : origin,
|
|
44231
44622
|
transformations,
|
|
44232
44623
|
query: face.query,
|
|
44233
44624
|
timeline
|
|
@@ -44250,21 +44641,21 @@ function normalizeFaceSelector(selector) {
|
|
|
44250
44641
|
}
|
|
44251
44642
|
return { compilePlanName: null, query: selector };
|
|
44252
44643
|
}
|
|
44253
|
-
function cross(a2, b) {
|
|
44644
|
+
function cross$1(a2, b) {
|
|
44254
44645
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
44255
44646
|
}
|
|
44256
|
-
function dot(a2, b) {
|
|
44647
|
+
function dot$1(a2, b) {
|
|
44257
44648
|
return a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2];
|
|
44258
44649
|
}
|
|
44259
44650
|
function normVec3(v) {
|
|
44260
|
-
const
|
|
44261
|
-
if (
|
|
44262
|
-
return [v[0] /
|
|
44651
|
+
const len2 = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
44652
|
+
if (len2 < 1e-10) return null;
|
|
44653
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
44263
44654
|
}
|
|
44264
44655
|
function tangentFrame(normal2) {
|
|
44265
44656
|
const ref = Math.abs(normal2[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];
|
|
44266
|
-
const v = normVec3(cross(normal2, ref));
|
|
44267
|
-
const u2 = normVec3(cross(v, normal2));
|
|
44657
|
+
const v = normVec3(cross$1(normal2, ref));
|
|
44658
|
+
const u2 = normVec3(cross$1(v, normal2));
|
|
44268
44659
|
return { u: u2, v };
|
|
44269
44660
|
}
|
|
44270
44661
|
const NORMAL_COS_EPS$1 = 0.9998;
|
|
@@ -44283,19 +44674,19 @@ function clusterMeshFaces(shape) {
|
|
|
44283
44674
|
const v2 = [vertProperties[i2 * numProp], vertProperties[i2 * numProp + 1], vertProperties[i2 * numProp + 2]];
|
|
44284
44675
|
const e1 = [v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]];
|
|
44285
44676
|
const e2 = [v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]];
|
|
44286
|
-
const rawCross = cross(e1, e2);
|
|
44677
|
+
const rawCross = cross$1(e1, e2);
|
|
44287
44678
|
const normal2 = normVec3(rawCross);
|
|
44288
44679
|
if (!normal2) continue;
|
|
44289
44680
|
const crossLen = Math.sqrt(rawCross[0] * rawCross[0] + rawCross[1] * rawCross[1] + rawCross[2] * rawCross[2]);
|
|
44290
44681
|
const triArea = crossLen / 2;
|
|
44291
|
-
const planeOffset = dot(normal2, v0);
|
|
44292
|
-
const
|
|
44682
|
+
const planeOffset = dot$1(normal2, v0);
|
|
44683
|
+
const triCentroid2 = [(v0[0] + v1[0] + v2[0]) / 3, (v0[1] + v1[1] + v2[1]) / 3, (v0[2] + v1[2] + v2[2]) / 3];
|
|
44293
44684
|
let merged = false;
|
|
44294
44685
|
for (const c2 of clusters) {
|
|
44295
|
-
if (dot(c2.normal, normal2) > NORMAL_COS_EPS$1 && Math.abs(c2.planeOffset - planeOffset) < PLANE_OFFSET_EPS$1) {
|
|
44296
|
-
c2.centroidSum[0] +=
|
|
44297
|
-
c2.centroidSum[1] +=
|
|
44298
|
-
c2.centroidSum[2] +=
|
|
44686
|
+
if (dot$1(c2.normal, normal2) > NORMAL_COS_EPS$1 && Math.abs(c2.planeOffset - planeOffset) < PLANE_OFFSET_EPS$1) {
|
|
44687
|
+
c2.centroidSum[0] += triCentroid2[0];
|
|
44688
|
+
c2.centroidSum[1] += triCentroid2[1];
|
|
44689
|
+
c2.centroidSum[2] += triCentroid2[2];
|
|
44299
44690
|
c2.count++;
|
|
44300
44691
|
c2.area += triArea;
|
|
44301
44692
|
merged = true;
|
|
@@ -44306,7 +44697,7 @@ function clusterMeshFaces(shape) {
|
|
|
44306
44697
|
clusters.push({
|
|
44307
44698
|
normal: normal2,
|
|
44308
44699
|
planeOffset,
|
|
44309
|
-
centroidSum: [
|
|
44700
|
+
centroidSum: [triCentroid2[0], triCentroid2[1], triCentroid2[2]],
|
|
44310
44701
|
count: 1,
|
|
44311
44702
|
area: triArea
|
|
44312
44703
|
});
|
|
@@ -44334,7 +44725,7 @@ function queryMeshFaces(shape, query) {
|
|
|
44334
44725
|
let clusters = clusterMeshFaces(shape);
|
|
44335
44726
|
if (query.normal) {
|
|
44336
44727
|
const qn = query.normal;
|
|
44337
|
-
clusters = clusters.filter((c2) => dot(c2.normal, qn) > NORMAL_COS_EPS$1);
|
|
44728
|
+
clusters = clusters.filter((c2) => dot$1(c2.normal, qn) > NORMAL_COS_EPS$1);
|
|
44338
44729
|
}
|
|
44339
44730
|
if (query.planar !== false) {
|
|
44340
44731
|
clusters = clusters.filter((c2) => c2.normal !== null);
|
|
@@ -44350,7 +44741,7 @@ function queryMeshFace(shape, query) {
|
|
|
44350
44741
|
let clusters = clusterMeshFaces(shape);
|
|
44351
44742
|
if (query.normal) {
|
|
44352
44743
|
const qn = query.normal;
|
|
44353
|
-
clusters = clusters.filter((c2) => dot(c2.normal, qn) > NORMAL_COS_EPS$1);
|
|
44744
|
+
clusters = clusters.filter((c2) => dot$1(c2.normal, qn) > NORMAL_COS_EPS$1);
|
|
44354
44745
|
}
|
|
44355
44746
|
if (query.planar !== false) {
|
|
44356
44747
|
clusters = clusters.filter((c2) => c2.normal !== null);
|
|
@@ -44599,7 +44990,8 @@ function applyMatrixToFace(face, matrix) {
|
|
|
44599
44990
|
normal: normalizeAxis(tx.vector(face.normal)),
|
|
44600
44991
|
query: cloneFaceQueryRef(face.query),
|
|
44601
44992
|
uAxis: face.uAxis ? normalizeAxis(tx.vector(face.uAxis)) : void 0,
|
|
44602
|
-
vAxis: face.vAxis ? normalizeAxis(tx.vector(face.vAxis)) : void 0
|
|
44993
|
+
vAxis: face.vAxis ? normalizeAxis(tx.vector(face.vAxis)) : void 0,
|
|
44994
|
+
surface: transformFaceSurface(face.surface, tx)
|
|
44603
44995
|
};
|
|
44604
44996
|
}
|
|
44605
44997
|
function applyMatrixToFaceTable(table, matrix) {
|
|
@@ -44622,11 +45014,11 @@ function canonicalShapeStepMatrix(step) {
|
|
|
44622
45014
|
}
|
|
44623
45015
|
}
|
|
44624
45016
|
function mirrorMatrix$1(normal2) {
|
|
44625
|
-
const
|
|
44626
|
-
if (
|
|
44627
|
-
const nx = normal2[0] /
|
|
44628
|
-
const ny = normal2[1] /
|
|
44629
|
-
const nz = normal2[2] /
|
|
45017
|
+
const len2 = Math.hypot(normal2[0], normal2[1], normal2[2]);
|
|
45018
|
+
if (len2 < 1e-12) return Transform.identity().toArray();
|
|
45019
|
+
const nx = normal2[0] / len2;
|
|
45020
|
+
const ny = normal2[1] / len2;
|
|
45021
|
+
const nz = normal2[2] / len2;
|
|
44630
45022
|
return [
|
|
44631
45023
|
1 - 2 * nx * nx,
|
|
44632
45024
|
-2 * nx * ny,
|
|
@@ -44689,9 +45081,9 @@ function pointOnWorkplane(origin, workplaneU, workplaneV, u2, v, depthDir, depth
|
|
|
44689
45081
|
];
|
|
44690
45082
|
}
|
|
44691
45083
|
function normalize2d(vec2) {
|
|
44692
|
-
const
|
|
44693
|
-
if (
|
|
44694
|
-
return [vec2[0] /
|
|
45084
|
+
const len2 = Math.hypot(vec2[0], vec2[1]);
|
|
45085
|
+
if (len2 < 1e-12) return [1, 0];
|
|
45086
|
+
return [vec2[0] / len2, vec2[1] / len2];
|
|
44695
45087
|
}
|
|
44696
45088
|
function cross3$2(a2, b) {
|
|
44697
45089
|
return [a2[1] * b[2] - a2[2] * b[1], a2[2] * b[0] - a2[0] * b[2], a2[0] * b[1] - a2[1] * b[0]];
|
|
@@ -44717,6 +45109,9 @@ function faceFrom2DEdge(name, start, end, zMid, ownerQuery) {
|
|
|
44717
45109
|
query: cloneFaceQueryRef(ownerQuery)
|
|
44718
45110
|
};
|
|
44719
45111
|
}
|
|
45112
|
+
function withPlaneCarrier(face) {
|
|
45113
|
+
return { ...face, surface: { kind: "plane", normal: cloneVec3$1(face.normal) } };
|
|
45114
|
+
}
|
|
44720
45115
|
function createTrackedFaceQuery(name, owner) {
|
|
44721
45116
|
if (!owner) return void 0;
|
|
44722
45117
|
return {
|
|
@@ -44780,6 +45175,7 @@ function buildBoxFaceTable(plan, owner) {
|
|
|
44780
45175
|
planar: true,
|
|
44781
45176
|
uAxis: [1, 0, 0],
|
|
44782
45177
|
vAxis: [0, 1, 0],
|
|
45178
|
+
surface: { kind: "plane", normal: [0, 0, 1] },
|
|
44783
45179
|
query: topQuery
|
|
44784
45180
|
});
|
|
44785
45181
|
registerFace(table, {
|
|
@@ -44789,22 +45185,23 @@ function buildBoxFaceTable(plan, owner) {
|
|
|
44789
45185
|
planar: true,
|
|
44790
45186
|
uAxis: [1, 0, 0],
|
|
44791
45187
|
vAxis: [0, -1, 0],
|
|
45188
|
+
surface: { kind: "plane", normal: [0, 0, -1] },
|
|
44792
45189
|
query: bottomQuery
|
|
44793
45190
|
});
|
|
44794
|
-
registerFace(table,
|
|
44795
|
-
|
|
44796
|
-
|
|
44797
|
-
registerFace(table,
|
|
44798
|
-
...faceFrom2DEdge("side-right", br, tr, (zTop + zBot) / 2, createTrackedFaceQuery("side-right", owner))
|
|
44799
|
-
});
|
|
44800
|
-
registerFace(table, {
|
|
44801
|
-
...faceFrom2DEdge("side-top", tr, tl, (zTop + zBot) / 2, createTrackedFaceQuery("side-top", owner))
|
|
44802
|
-
});
|
|
44803
|
-
registerFace(table, {
|
|
44804
|
-
...faceFrom2DEdge("side-left", tl, bl, (zTop + zBot) / 2, createTrackedFaceQuery("side-left", owner))
|
|
44805
|
-
});
|
|
45191
|
+
registerFace(table, withPlaneCarrier(faceFrom2DEdge("side-bottom", bl, br, (zTop + zBot) / 2, createTrackedFaceQuery("side-bottom", owner))));
|
|
45192
|
+
registerFace(table, withPlaneCarrier(faceFrom2DEdge("side-right", br, tr, (zTop + zBot) / 2, createTrackedFaceQuery("side-right", owner))));
|
|
45193
|
+
registerFace(table, withPlaneCarrier(faceFrom2DEdge("side-top", tr, tl, (zTop + zBot) / 2, createTrackedFaceQuery("side-top", owner))));
|
|
45194
|
+
registerFace(table, withPlaneCarrier(faceFrom2DEdge("side-left", tl, bl, (zTop + zBot) / 2, createTrackedFaceQuery("side-left", owner))));
|
|
44806
45195
|
return table;
|
|
44807
45196
|
}
|
|
45197
|
+
function cylinderSideCarrier(plan) {
|
|
45198
|
+
const radiusBottom = Math.abs(plan.radius);
|
|
45199
|
+
const radiusTop = Math.abs(plan.radiusTop ?? plan.radius);
|
|
45200
|
+
const height = Math.abs(plan.height);
|
|
45201
|
+
const origin = [0, 0, Math.min(0, plan.height)];
|
|
45202
|
+
const axis = [0, 0, 1];
|
|
45203
|
+
return radiusBottom === radiusTop ? { kind: "cylinder", origin, axis, radius: radiusBottom, height } : { kind: "cone", origin, axis, radiusBottom, radiusTop, height };
|
|
45204
|
+
}
|
|
44808
45205
|
function buildCylinderFaceTable(plan, owner) {
|
|
44809
45206
|
const table = emptyFaceTable();
|
|
44810
45207
|
const zBot = 0;
|
|
@@ -44818,6 +45215,7 @@ function buildCylinderFaceTable(plan, owner) {
|
|
|
44818
45215
|
planar: true,
|
|
44819
45216
|
uAxis: [1, 0, 0],
|
|
44820
45217
|
vAxis: [0, 1, 0],
|
|
45218
|
+
surface: { kind: "plane", normal: [0, 0, 1] },
|
|
44821
45219
|
query: createTrackedFaceQuery("top", owner)
|
|
44822
45220
|
});
|
|
44823
45221
|
registerFace(table, {
|
|
@@ -44827,6 +45225,7 @@ function buildCylinderFaceTable(plan, owner) {
|
|
|
44827
45225
|
planar: true,
|
|
44828
45226
|
uAxis: [1, 0, 0],
|
|
44829
45227
|
vAxis: [0, -1, 0],
|
|
45228
|
+
surface: { kind: "plane", normal: [0, 0, -1] },
|
|
44830
45229
|
query: createTrackedFaceQuery("bottom", owner)
|
|
44831
45230
|
});
|
|
44832
45231
|
registerFace(table, {
|
|
@@ -44834,10 +45233,38 @@ function buildCylinderFaceTable(plan, owner) {
|
|
|
44834
45233
|
normal: [1, 0, 0],
|
|
44835
45234
|
center: [sideRadius, 0, (zTop + zBot) / 2],
|
|
44836
45235
|
planar: false,
|
|
45236
|
+
surface: cylinderSideCarrier(plan),
|
|
44837
45237
|
query: createTrackedFaceQuery("side", owner)
|
|
44838
45238
|
});
|
|
44839
45239
|
return table;
|
|
44840
45240
|
}
|
|
45241
|
+
function buildSphereFaceTable(plan, owner) {
|
|
45242
|
+
const table = emptyFaceTable();
|
|
45243
|
+
const radius = Math.abs(plan.radius);
|
|
45244
|
+
registerFace(table, {
|
|
45245
|
+
name: "surface",
|
|
45246
|
+
normal: [1, 0, 0],
|
|
45247
|
+
center: [radius, 0, 0],
|
|
45248
|
+
planar: false,
|
|
45249
|
+
surface: { kind: "sphere", center: [0, 0, 0], radius },
|
|
45250
|
+
query: createTrackedFaceQuery("surface", owner)
|
|
45251
|
+
});
|
|
45252
|
+
return table;
|
|
45253
|
+
}
|
|
45254
|
+
function buildTorusFaceTable(plan, owner) {
|
|
45255
|
+
const table = emptyFaceTable();
|
|
45256
|
+
const majorRadius = Math.abs(plan.majorRadius);
|
|
45257
|
+
const minorRadius = Math.abs(plan.minorRadius);
|
|
45258
|
+
registerFace(table, {
|
|
45259
|
+
name: "surface",
|
|
45260
|
+
normal: [1, 0, 0],
|
|
45261
|
+
center: [majorRadius + minorRadius, 0, 0],
|
|
45262
|
+
planar: false,
|
|
45263
|
+
surface: { kind: "torus", center: [0, 0, 0], axis: [0, 0, 1], majorRadius, minorRadius },
|
|
45264
|
+
query: createTrackedFaceQuery("surface", owner)
|
|
45265
|
+
});
|
|
45266
|
+
return table;
|
|
45267
|
+
}
|
|
44841
45268
|
function buildRectExtrudeFaceTable(profile, height, owner) {
|
|
44842
45269
|
const corners = rectLikeProfileCorners(profile);
|
|
44843
45270
|
if (!corners) return emptyFaceTable();
|
|
@@ -44855,6 +45282,7 @@ function buildRectExtrudeFaceTable(profile, height, owner) {
|
|
|
44855
45282
|
planar: true,
|
|
44856
45283
|
uAxis: topU,
|
|
44857
45284
|
vAxis: topV,
|
|
45285
|
+
surface: { kind: "plane", normal: [0, 0, 1] },
|
|
44858
45286
|
query: createTrackedFaceQuery("top", owner)
|
|
44859
45287
|
});
|
|
44860
45288
|
registerFace(table, {
|
|
@@ -44864,12 +45292,13 @@ function buildRectExtrudeFaceTable(profile, height, owner) {
|
|
|
44864
45292
|
planar: true,
|
|
44865
45293
|
uAxis: topU,
|
|
44866
45294
|
vAxis: [-topV[0], -topV[1], -topV[2]],
|
|
45295
|
+
surface: { kind: "plane", normal: [0, 0, -1] },
|
|
44867
45296
|
query: createTrackedFaceQuery("bottom", owner)
|
|
44868
45297
|
});
|
|
44869
|
-
registerFace(table, faceFrom2DEdge("side-bottom", bl, br, (zTop + zBot) / 2, createTrackedFaceQuery("side-bottom", owner)));
|
|
44870
|
-
registerFace(table, faceFrom2DEdge("side-right", br, tr, (zTop + zBot) / 2, createTrackedFaceQuery("side-right", owner)));
|
|
44871
|
-
registerFace(table, faceFrom2DEdge("side-top", tr, tl, (zTop + zBot) / 2, createTrackedFaceQuery("side-top", owner)));
|
|
44872
|
-
registerFace(table, faceFrom2DEdge("side-left", tl, bl, (zTop + zBot) / 2, createTrackedFaceQuery("side-left", owner)));
|
|
45298
|
+
registerFace(table, withPlaneCarrier(faceFrom2DEdge("side-bottom", bl, br, (zTop + zBot) / 2, createTrackedFaceQuery("side-bottom", owner))));
|
|
45299
|
+
registerFace(table, withPlaneCarrier(faceFrom2DEdge("side-right", br, tr, (zTop + zBot) / 2, createTrackedFaceQuery("side-right", owner))));
|
|
45300
|
+
registerFace(table, withPlaneCarrier(faceFrom2DEdge("side-top", tr, tl, (zTop + zBot) / 2, createTrackedFaceQuery("side-top", owner))));
|
|
45301
|
+
registerFace(table, withPlaneCarrier(faceFrom2DEdge("side-left", tl, bl, (zTop + zBot) / 2, createTrackedFaceQuery("side-left", owner))));
|
|
44873
45302
|
return table;
|
|
44874
45303
|
}
|
|
44875
45304
|
function buildCircleExtrudeFaceTable(profile, height, owner) {
|
|
@@ -44889,6 +45318,7 @@ function buildCircleExtrudeFaceTable(profile, height, owner) {
|
|
|
44889
45318
|
planar: true,
|
|
44890
45319
|
uAxis: xAxis,
|
|
44891
45320
|
vAxis: yAxis,
|
|
45321
|
+
surface: { kind: "plane", normal: [0, 0, 1] },
|
|
44892
45322
|
query: createTrackedFaceQuery("top", owner)
|
|
44893
45323
|
});
|
|
44894
45324
|
registerFace(table, {
|
|
@@ -44898,6 +45328,7 @@ function buildCircleExtrudeFaceTable(profile, height, owner) {
|
|
|
44898
45328
|
planar: true,
|
|
44899
45329
|
uAxis: xAxis,
|
|
44900
45330
|
vAxis: [-yAxis[0], -yAxis[1], -yAxis[2]],
|
|
45331
|
+
surface: { kind: "plane", normal: [0, 0, -1] },
|
|
44901
45332
|
query: createTrackedFaceQuery("bottom", owner)
|
|
44902
45333
|
});
|
|
44903
45334
|
registerFace(table, {
|
|
@@ -44905,10 +45336,65 @@ function buildCircleExtrudeFaceTable(profile, height, owner) {
|
|
|
44905
45336
|
normal: sideNormal,
|
|
44906
45337
|
center: [sidePoint[0], sidePoint[1], (zTop + zBot) / 2],
|
|
44907
45338
|
planar: false,
|
|
45339
|
+
surface: { kind: "cylinder", origin: [origin[0], origin[1], zBot], axis: [0, 0, 1], radius: Math.abs(profile.radius), height },
|
|
44908
45340
|
query: createTrackedFaceQuery("side", owner)
|
|
44909
45341
|
});
|
|
44910
45342
|
return table;
|
|
44911
45343
|
}
|
|
45344
|
+
function collectExtrudeSideCarriers(profile, height, parent, prefix) {
|
|
45345
|
+
switch (profile.kind) {
|
|
45346
|
+
case "circle": {
|
|
45347
|
+
const side = buildCircleExtrudeFaceTable(profile, height, null).faces.get("side");
|
|
45348
|
+
return side ? [applyMatrixToFace({ ...side, name: `${prefix}side` }, parent.toArray())] : [];
|
|
45349
|
+
}
|
|
45350
|
+
case "rect":
|
|
45351
|
+
case "roundedRect":
|
|
45352
|
+
case "polygon": {
|
|
45353
|
+
const leaf = buildRectExtrudeFaceTable(profile, height, null);
|
|
45354
|
+
const out = [];
|
|
45355
|
+
for (const name of ["side-bottom", "side-right", "side-top", "side-left"]) {
|
|
45356
|
+
const face = leaf.faces.get(name);
|
|
45357
|
+
if (face) out.push(applyMatrixToFace({ ...face, name: `${prefix}${name}` }, parent.toArray()));
|
|
45358
|
+
}
|
|
45359
|
+
return out;
|
|
45360
|
+
}
|
|
45361
|
+
case "boolean": {
|
|
45362
|
+
const composed = parent.mul(profileTransformMatrix(profile.transforms));
|
|
45363
|
+
return profile.profiles.flatMap((child, index2) => collectExtrudeSideCarriers(child, height, composed, `${prefix}op${index2}/`));
|
|
45364
|
+
}
|
|
45365
|
+
default:
|
|
45366
|
+
return [];
|
|
45367
|
+
}
|
|
45368
|
+
}
|
|
45369
|
+
function buildBooleanExtrudeFaceTable(profile, height, owner) {
|
|
45370
|
+
const table = emptyFaceTable();
|
|
45371
|
+
const zBot = 0;
|
|
45372
|
+
const zTop = zBot + height;
|
|
45373
|
+
registerFace(table, {
|
|
45374
|
+
name: "top",
|
|
45375
|
+
normal: [0, 0, 1],
|
|
45376
|
+
center: [0, 0, zTop],
|
|
45377
|
+
planar: true,
|
|
45378
|
+
uAxis: [1, 0, 0],
|
|
45379
|
+
vAxis: [0, 1, 0],
|
|
45380
|
+
surface: { kind: "plane", normal: [0, 0, 1] },
|
|
45381
|
+
query: createTrackedFaceQuery("top", owner)
|
|
45382
|
+
});
|
|
45383
|
+
registerFace(table, {
|
|
45384
|
+
name: "bottom",
|
|
45385
|
+
normal: [0, 0, -1],
|
|
45386
|
+
center: [0, 0, zBot],
|
|
45387
|
+
planar: true,
|
|
45388
|
+
uAxis: [1, 0, 0],
|
|
45389
|
+
vAxis: [0, -1, 0],
|
|
45390
|
+
surface: { kind: "plane", normal: [0, 0, -1] },
|
|
45391
|
+
query: createTrackedFaceQuery("bottom", owner)
|
|
45392
|
+
});
|
|
45393
|
+
for (const side of collectExtrudeSideCarriers(profile, height, Transform.identity(), "")) {
|
|
45394
|
+
registerFace(table, side);
|
|
45395
|
+
}
|
|
45396
|
+
return table;
|
|
45397
|
+
}
|
|
44912
45398
|
function buildExtrudeFaceTable(plan, owner) {
|
|
44913
45399
|
var _a3, _b3;
|
|
44914
45400
|
let table;
|
|
@@ -44921,6 +45407,9 @@ function buildExtrudeFaceTable(plan, owner) {
|
|
|
44921
45407
|
case "circle":
|
|
44922
45408
|
table = buildCircleExtrudeFaceTable(plan.profile, plan.height, owner);
|
|
44923
45409
|
break;
|
|
45410
|
+
case "boolean":
|
|
45411
|
+
table = buildBooleanExtrudeFaceTable(plan.profile, plan.height, owner);
|
|
45412
|
+
break;
|
|
44924
45413
|
default:
|
|
44925
45414
|
table = emptyFaceTable();
|
|
44926
45415
|
break;
|
|
@@ -45602,7 +46091,9 @@ function resolveShapeFaceTableInternal(plan, owner) {
|
|
|
45602
46091
|
return table;
|
|
45603
46092
|
}
|
|
45604
46093
|
case "sphere":
|
|
46094
|
+
return buildSphereFaceTable(plan, owner);
|
|
45605
46095
|
case "torus":
|
|
46096
|
+
return buildTorusFaceTable(plan, owner);
|
|
45606
46097
|
case "variableSweep":
|
|
45607
46098
|
case "fillet":
|
|
45608
46099
|
case "chamfer":
|
|
@@ -45610,8 +46101,13 @@ function resolveShapeFaceTableInternal(plan, owner) {
|
|
|
45610
46101
|
case "offsetSolid":
|
|
45611
46102
|
return emptyFaceTable();
|
|
45612
46103
|
// Fillet/chamfer edges preserve all base faces — delegate to base plan.
|
|
46104
|
+
// Caveat: cornerYBlend and fullRound each CONSUME a base face (the Y-corner
|
|
46105
|
+
// region / the narrow center face), so the delegated base table is a known
|
|
46106
|
+
// provenance approximation for those two kinds.
|
|
45613
46107
|
case "filletEdges":
|
|
45614
46108
|
case "cornerYBlend":
|
|
46109
|
+
case "faceFillet":
|
|
46110
|
+
case "fullRound":
|
|
45615
46111
|
case "chamferEdges":
|
|
45616
46112
|
return resolveShapeFaceTableInternal(plan.base, owner);
|
|
45617
46113
|
case "revolve":
|
|
@@ -45692,6 +46188,7 @@ function resolveShapeFaceTableInternal(plan, owner) {
|
|
|
45692
46188
|
case "importedMesh":
|
|
45693
46189
|
case "sdf":
|
|
45694
46190
|
case "fromSlices":
|
|
46191
|
+
case "fromSectionFrames":
|
|
45695
46192
|
case "analyticSurface":
|
|
45696
46193
|
case "nurbsSurface":
|
|
45697
46194
|
case "surfaceRuled":
|
|
@@ -45722,6 +46219,55 @@ function resolveShapeFace(plan, name) {
|
|
|
45722
46219
|
if (face) return cloneFaceRefValue(face);
|
|
45723
46220
|
return null;
|
|
45724
46221
|
}
|
|
46222
|
+
function gatherShapeCarriers(plan) {
|
|
46223
|
+
const carriers = [];
|
|
46224
|
+
const seen = /* @__PURE__ */ new Set();
|
|
46225
|
+
const emit = (prefix, table, tx) => {
|
|
46226
|
+
for (const [name, raw] of table.faces.entries()) {
|
|
46227
|
+
const face = applyMatrixToFace(raw, tx.toArray());
|
|
46228
|
+
const surface = face.surface;
|
|
46229
|
+
if (!surface) continue;
|
|
46230
|
+
if (surface.kind === "ruled" || surface.kind === "nurbs") continue;
|
|
46231
|
+
const planePoint = surface.kind === "plane" ? cloneVec3$1(face.center) : void 0;
|
|
46232
|
+
const key2 = JSON.stringify([surface, planePoint ?? null]);
|
|
46233
|
+
if (seen.has(key2)) continue;
|
|
46234
|
+
seen.add(key2);
|
|
46235
|
+
carriers.push({ name: prefix ? `${prefix}${name}` : name, surface: cloneFaceSurface(surface), planePoint });
|
|
46236
|
+
}
|
|
46237
|
+
};
|
|
46238
|
+
const walk = (node, tx, prefix) => {
|
|
46239
|
+
if (!node) return;
|
|
46240
|
+
switch (node.kind) {
|
|
46241
|
+
case "queryOwner":
|
|
46242
|
+
case "surfaceExtend":
|
|
46243
|
+
walk(node.base, tx, prefix);
|
|
46244
|
+
return;
|
|
46245
|
+
case "transform": {
|
|
46246
|
+
let composed = tx;
|
|
46247
|
+
for (const step of node.steps) composed = composed.mul(canonicalShapeStepMatrix(step));
|
|
46248
|
+
walk(node.base, composed, prefix);
|
|
46249
|
+
return;
|
|
46250
|
+
}
|
|
46251
|
+
case "boolean": {
|
|
46252
|
+
emit(prefix, resolveShapeFaceTableInternal(node, null), tx);
|
|
46253
|
+
node.shapes.forEach((shape, index2) => walk(shape, tx, `${prefix}op${index2}/`));
|
|
46254
|
+
return;
|
|
46255
|
+
}
|
|
46256
|
+
case "filletEdges":
|
|
46257
|
+
case "cornerYBlend":
|
|
46258
|
+
case "faceFillet":
|
|
46259
|
+
case "fullRound":
|
|
46260
|
+
case "chamferEdges":
|
|
46261
|
+
walk(node.base, tx, prefix);
|
|
46262
|
+
return;
|
|
46263
|
+
default:
|
|
46264
|
+
emit(prefix, resolveShapeFaceTableInternal(node, null), tx);
|
|
46265
|
+
return;
|
|
46266
|
+
}
|
|
46267
|
+
};
|
|
46268
|
+
walk(plan, Transform.identity(), "");
|
|
46269
|
+
return carriers;
|
|
46270
|
+
}
|
|
45725
46271
|
const DEPRECATED_SIDE_NAMES = {
|
|
45726
46272
|
"side-left": "left",
|
|
45727
46273
|
"side-right": "right",
|
|
@@ -45746,6 +46292,631 @@ function supportedShellCreatedFaceNames(basePlan, openFaces) {
|
|
|
45746
46292
|
function preservedShapeFaceQueries(basePlan) {
|
|
45747
46293
|
return listShapeFaceQueries(basePlan);
|
|
45748
46294
|
}
|
|
46295
|
+
const EDGE_THRESHOLD_DOT = Math.cos(Math.PI / 180);
|
|
46296
|
+
const SMOOTH_THRESHOLD_DOT = Math.cos(30 * Math.PI / 180);
|
|
46297
|
+
function computeGeometryArrays(mesh, options = {}) {
|
|
46298
|
+
const { numProp, numTri: triCount, triVerts, vertProperties, vertNormals, cornerNormals } = mesh;
|
|
46299
|
+
if (numProp !== NUM_PROP_POSITION_ONLY && numProp !== NUM_PROP_WITH_NORMAL && numProp !== NUM_PROP_WITH_UV) {
|
|
46300
|
+
throw new Error(
|
|
46301
|
+
`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.`
|
|
46302
|
+
);
|
|
46303
|
+
}
|
|
46304
|
+
const hasStoredNormals = numProp === NUM_PROP_WITH_NORMAL || numProp === NUM_PROP_WITH_UV;
|
|
46305
|
+
const hasStoredUvs = numProp === NUM_PROP_WITH_UV;
|
|
46306
|
+
const useCornerNormals = !!cornerNormals && cornerNormals.length === triCount * 9;
|
|
46307
|
+
const positions = new Float32Array(triCount * 9);
|
|
46308
|
+
const normals = new Float32Array(triCount * 9);
|
|
46309
|
+
const uvs = hasStoredUvs ? new Float32Array(triCount * 6) : void 0;
|
|
46310
|
+
const faceNx = new Float32Array(triCount);
|
|
46311
|
+
const faceNy = new Float32Array(triCount);
|
|
46312
|
+
const faceNz = new Float32Array(triCount);
|
|
46313
|
+
for (let t = 0; t < triCount; t++) {
|
|
46314
|
+
const i0 = triVerts[t * 3];
|
|
46315
|
+
const i1 = triVerts[t * 3 + 1];
|
|
46316
|
+
const i2 = triVerts[t * 3 + 2];
|
|
46317
|
+
const ax = vertProperties[i0 * numProp], ay = vertProperties[i0 * numProp + 1], az = vertProperties[i0 * numProp + 2];
|
|
46318
|
+
const bx = vertProperties[i1 * numProp], by = vertProperties[i1 * numProp + 1], bz = vertProperties[i1 * numProp + 2];
|
|
46319
|
+
const cx = vertProperties[i2 * numProp], cy = vertProperties[i2 * numProp + 1], cz = vertProperties[i2 * numProp + 2];
|
|
46320
|
+
const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
|
|
46321
|
+
const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
|
|
46322
|
+
let fnx = e1y * e2z - e1z * e2y;
|
|
46323
|
+
let fny = e1z * e2x - e1x * e2z;
|
|
46324
|
+
let fnz = e1x * e2y - e1y * e2x;
|
|
46325
|
+
const len2 = Math.sqrt(fnx * fnx + fny * fny + fnz * fnz) || 1;
|
|
46326
|
+
fnx /= len2;
|
|
46327
|
+
fny /= len2;
|
|
46328
|
+
fnz /= len2;
|
|
46329
|
+
const o = t * 9;
|
|
46330
|
+
positions[o] = ax;
|
|
46331
|
+
positions[o + 1] = ay;
|
|
46332
|
+
positions[o + 2] = az;
|
|
46333
|
+
positions[o + 3] = bx;
|
|
46334
|
+
positions[o + 4] = by;
|
|
46335
|
+
positions[o + 5] = bz;
|
|
46336
|
+
positions[o + 6] = cx;
|
|
46337
|
+
positions[o + 7] = cy;
|
|
46338
|
+
positions[o + 8] = cz;
|
|
46339
|
+
if (uvs) {
|
|
46340
|
+
const u2 = t * 6;
|
|
46341
|
+
uvs[u2] = vertProperties[i0 * numProp + UV_OFFSET];
|
|
46342
|
+
uvs[u2 + 1] = vertProperties[i0 * numProp + UV_OFFSET + 1];
|
|
46343
|
+
uvs[u2 + 2] = vertProperties[i1 * numProp + UV_OFFSET];
|
|
46344
|
+
uvs[u2 + 3] = vertProperties[i1 * numProp + UV_OFFSET + 1];
|
|
46345
|
+
uvs[u2 + 4] = vertProperties[i2 * numProp + UV_OFFSET];
|
|
46346
|
+
uvs[u2 + 5] = vertProperties[i2 * numProp + UV_OFFSET + 1];
|
|
46347
|
+
}
|
|
46348
|
+
if (useCornerNormals) {
|
|
46349
|
+
for (let k2 = 0; k2 < 9; k2++) normals[o + k2] = cornerNormals[o + k2];
|
|
46350
|
+
} else if (vertNormals) {
|
|
46351
|
+
normals[o] = vertNormals[i0 * 3];
|
|
46352
|
+
normals[o + 1] = vertNormals[i0 * 3 + 1];
|
|
46353
|
+
normals[o + 2] = vertNormals[i0 * 3 + 2];
|
|
46354
|
+
normals[o + 3] = vertNormals[i1 * 3];
|
|
46355
|
+
normals[o + 4] = vertNormals[i1 * 3 + 1];
|
|
46356
|
+
normals[o + 5] = vertNormals[i1 * 3 + 2];
|
|
46357
|
+
normals[o + 6] = vertNormals[i2 * 3];
|
|
46358
|
+
normals[o + 7] = vertNormals[i2 * 3 + 1];
|
|
46359
|
+
normals[o + 8] = vertNormals[i2 * 3 + 2];
|
|
46360
|
+
} else if (hasStoredNormals) {
|
|
46361
|
+
const corners = [i0, i1, i2];
|
|
46362
|
+
for (let v = 0; v < 3; v++) {
|
|
46363
|
+
const base = corners[v] * numProp;
|
|
46364
|
+
const nx = vertProperties[base + NORMAL_OFFSET];
|
|
46365
|
+
const ny = vertProperties[base + NORMAL_OFFSET + 1];
|
|
46366
|
+
const nz = vertProperties[base + NORMAL_OFFSET + 2];
|
|
46367
|
+
const oc = o + v * 3;
|
|
46368
|
+
if (nx * nx + ny * ny + nz * nz > 1e-12) {
|
|
46369
|
+
normals[oc] = nx;
|
|
46370
|
+
normals[oc + 1] = ny;
|
|
46371
|
+
normals[oc + 2] = nz;
|
|
46372
|
+
} else {
|
|
46373
|
+
normals[oc] = fnx;
|
|
46374
|
+
normals[oc + 1] = fny;
|
|
46375
|
+
normals[oc + 2] = fnz;
|
|
46376
|
+
}
|
|
46377
|
+
}
|
|
46378
|
+
} else {
|
|
46379
|
+
normals[o] = fnx;
|
|
46380
|
+
normals[o + 1] = fny;
|
|
46381
|
+
normals[o + 2] = fnz;
|
|
46382
|
+
normals[o + 3] = fnx;
|
|
46383
|
+
normals[o + 4] = fny;
|
|
46384
|
+
normals[o + 5] = fnz;
|
|
46385
|
+
normals[o + 6] = fnx;
|
|
46386
|
+
normals[o + 7] = fny;
|
|
46387
|
+
normals[o + 8] = fnz;
|
|
46388
|
+
}
|
|
46389
|
+
faceNx[t] = fnx;
|
|
46390
|
+
faceNy[t] = fny;
|
|
46391
|
+
faceNz[t] = fnz;
|
|
46392
|
+
}
|
|
46393
|
+
if (!useCornerNormals && !vertNormals && !hasStoredNormals && triCount > 0) {
|
|
46394
|
+
computeAutoSmoothNormals(
|
|
46395
|
+
triVerts,
|
|
46396
|
+
vertProperties,
|
|
46397
|
+
numProp,
|
|
46398
|
+
triCount,
|
|
46399
|
+
faceNx,
|
|
46400
|
+
faceNy,
|
|
46401
|
+
faceNz,
|
|
46402
|
+
normals,
|
|
46403
|
+
mesh.mergeFromVert,
|
|
46404
|
+
mesh.mergeToVert
|
|
46405
|
+
);
|
|
46406
|
+
}
|
|
46407
|
+
const edgePositions = options.skipEdges ? new Float32Array(0) : computeSharpEdges(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, mesh.mergeFromVert, mesh.mergeToVert);
|
|
46408
|
+
return {
|
|
46409
|
+
positions,
|
|
46410
|
+
normals,
|
|
46411
|
+
edgePositions,
|
|
46412
|
+
hasSmoothNormals: triCount > 0,
|
|
46413
|
+
uvs
|
|
46414
|
+
};
|
|
46415
|
+
}
|
|
46416
|
+
function computeAutoSmoothNormals(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, normals, mergeFromVert, mergeToVert) {
|
|
46417
|
+
const numVerts = vertProperties.length / numProp;
|
|
46418
|
+
const canon = buildCanonicalMap$2(numVerts, mergeFromVert, mergeToVert);
|
|
46419
|
+
const vertToTris = /* @__PURE__ */ new Map();
|
|
46420
|
+
for (let t = 0; t < triCount; t++) {
|
|
46421
|
+
for (let v = 0; v < 3; v++) {
|
|
46422
|
+
const cv = canon[triVerts[t * 3 + v]];
|
|
46423
|
+
let list = vertToTris.get(cv);
|
|
46424
|
+
if (!list) {
|
|
46425
|
+
list = [];
|
|
46426
|
+
vertToTris.set(cv, list);
|
|
46427
|
+
}
|
|
46428
|
+
list.push(t);
|
|
46429
|
+
}
|
|
46430
|
+
}
|
|
46431
|
+
for (let t = 0; t < triCount; t++) {
|
|
46432
|
+
for (let v = 0; v < 3; v++) {
|
|
46433
|
+
const cv = canon[triVerts[t * 3 + v]];
|
|
46434
|
+
const adjacentTris = vertToTris.get(cv);
|
|
46435
|
+
if (!adjacentTris || adjacentTris.length <= 1) continue;
|
|
46436
|
+
let sx = 0, sy = 0, sz = 0;
|
|
46437
|
+
for (const adj of adjacentTris) {
|
|
46438
|
+
const dot2 = faceNx[t] * faceNx[adj] + faceNy[t] * faceNy[adj] + faceNz[t] * faceNz[adj];
|
|
46439
|
+
if (dot2 >= SMOOTH_THRESHOLD_DOT) {
|
|
46440
|
+
sx += faceNx[adj];
|
|
46441
|
+
sy += faceNy[adj];
|
|
46442
|
+
sz += faceNz[adj];
|
|
46443
|
+
}
|
|
46444
|
+
}
|
|
46445
|
+
const slen = Math.sqrt(sx * sx + sy * sy + sz * sz);
|
|
46446
|
+
if (slen > 1e-9) {
|
|
46447
|
+
sx /= slen;
|
|
46448
|
+
sy /= slen;
|
|
46449
|
+
sz /= slen;
|
|
46450
|
+
const o = t * 9 + v * 3;
|
|
46451
|
+
normals[o] = sx;
|
|
46452
|
+
normals[o + 1] = sy;
|
|
46453
|
+
normals[o + 2] = sz;
|
|
46454
|
+
}
|
|
46455
|
+
}
|
|
46456
|
+
}
|
|
46457
|
+
}
|
|
46458
|
+
function computeSharpEdges(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, mergeFromVert, mergeToVert) {
|
|
46459
|
+
const numVerts = vertProperties.length / numProp;
|
|
46460
|
+
const canon = buildCanonicalMap$2(numVerts, mergeFromVert, mergeToVert);
|
|
46461
|
+
const MAX_NUMERIC = 1 << 21;
|
|
46462
|
+
let maxCanon = 0;
|
|
46463
|
+
for (let i = 0; i < numVerts; i++) {
|
|
46464
|
+
if (canon[i] > maxCanon) maxCanon = canon[i];
|
|
46465
|
+
}
|
|
46466
|
+
const useNumeric = maxCanon < MAX_NUMERIC;
|
|
46467
|
+
const halfEdges = /* @__PURE__ */ new Map();
|
|
46468
|
+
const edgeList = [];
|
|
46469
|
+
for (let t = 0; t < triCount; t++) {
|
|
46470
|
+
const ca = canon[triVerts[t * 3]];
|
|
46471
|
+
const cb = canon[triVerts[t * 3 + 1]];
|
|
46472
|
+
const cc = canon[triVerts[t * 3 + 2]];
|
|
46473
|
+
const tv = [ca, cb, cc];
|
|
46474
|
+
for (let e = 0; e < 3; e++) {
|
|
46475
|
+
const va = tv[e], vb = tv[(e + 1) % 3];
|
|
46476
|
+
const fwdKey = useNumeric ? va * MAX_NUMERIC + vb : `${va},${vb}`;
|
|
46477
|
+
const revKey = useNumeric ? vb * MAX_NUMERIC + va : `${vb},${va}`;
|
|
46478
|
+
const adjTri = halfEdges.get(revKey);
|
|
46479
|
+
if (adjTri !== void 0) {
|
|
46480
|
+
const dot2 = faceNx[t] * faceNx[adjTri] + faceNy[t] * faceNy[adjTri] + faceNz[t] * faceNz[adjTri];
|
|
46481
|
+
if (dot2 <= EDGE_THRESHOLD_DOT) {
|
|
46482
|
+
const origVa = triVerts[t * 3 + e];
|
|
46483
|
+
const origVb = triVerts[t * 3 + (e + 1) % 3];
|
|
46484
|
+
edgeList.push(
|
|
46485
|
+
vertProperties[origVa * numProp],
|
|
46486
|
+
vertProperties[origVa * numProp + 1],
|
|
46487
|
+
vertProperties[origVa * numProp + 2],
|
|
46488
|
+
vertProperties[origVb * numProp],
|
|
46489
|
+
vertProperties[origVb * numProp + 1],
|
|
46490
|
+
vertProperties[origVb * numProp + 2]
|
|
46491
|
+
);
|
|
46492
|
+
}
|
|
46493
|
+
halfEdges.delete(revKey);
|
|
46494
|
+
} else {
|
|
46495
|
+
halfEdges.set(fwdKey, t);
|
|
46496
|
+
}
|
|
46497
|
+
}
|
|
46498
|
+
}
|
|
46499
|
+
for (const [key2, t] of halfEdges) {
|
|
46500
|
+
const ca = canon[triVerts[t * 3]];
|
|
46501
|
+
const cb = canon[triVerts[t * 3 + 1]];
|
|
46502
|
+
const cc = canon[triVerts[t * 3 + 2]];
|
|
46503
|
+
const tv = [ca, cb, cc];
|
|
46504
|
+
for (let e = 0; e < 3; e++) {
|
|
46505
|
+
const va = tv[e], vb = tv[(e + 1) % 3];
|
|
46506
|
+
const fwdKey = useNumeric ? va * MAX_NUMERIC + vb : `${va},${vb}`;
|
|
46507
|
+
if (fwdKey === key2) {
|
|
46508
|
+
const origVa = triVerts[t * 3 + e];
|
|
46509
|
+
const origVb = triVerts[t * 3 + (e + 1) % 3];
|
|
46510
|
+
edgeList.push(
|
|
46511
|
+
vertProperties[origVa * numProp],
|
|
46512
|
+
vertProperties[origVa * numProp + 1],
|
|
46513
|
+
vertProperties[origVa * numProp + 2],
|
|
46514
|
+
vertProperties[origVb * numProp],
|
|
46515
|
+
vertProperties[origVb * numProp + 1],
|
|
46516
|
+
vertProperties[origVb * numProp + 2]
|
|
46517
|
+
);
|
|
46518
|
+
break;
|
|
46519
|
+
}
|
|
46520
|
+
}
|
|
46521
|
+
}
|
|
46522
|
+
return new Float32Array(edgeList);
|
|
46523
|
+
}
|
|
46524
|
+
function buildCanonicalMap$2(numVerts, mergeFromVert, mergeToVert) {
|
|
46525
|
+
const canon = new Uint32Array(numVerts);
|
|
46526
|
+
for (let i = 0; i < numVerts; i++) canon[i] = i;
|
|
46527
|
+
if (mergeFromVert && mergeToVert) {
|
|
46528
|
+
for (let i = 0; i < mergeFromVert.length; i++) {
|
|
46529
|
+
canon[mergeFromVert[i]] = mergeToVert[i];
|
|
46530
|
+
}
|
|
46531
|
+
for (let i = 0; i < numVerts; i++) {
|
|
46532
|
+
let v = canon[i];
|
|
46533
|
+
while (canon[v] !== v) v = canon[v];
|
|
46534
|
+
canon[i] = v;
|
|
46535
|
+
}
|
|
46536
|
+
}
|
|
46537
|
+
return canon;
|
|
46538
|
+
}
|
|
46539
|
+
const sub = (a2, b) => [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
|
|
46540
|
+
const add = (a2, b) => [a2[0] + b[0], a2[1] + b[1], a2[2] + b[2]];
|
|
46541
|
+
const dot = (a2, b) => a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2];
|
|
46542
|
+
const len = (a2) => Math.hypot(a2[0], a2[1], a2[2]);
|
|
46543
|
+
const scl = (a2, s) => [a2[0] * s, a2[1] * s, a2[2] * s];
|
|
46544
|
+
const cross = (a2, b) => [
|
|
46545
|
+
a2[1] * b[2] - a2[2] * b[1],
|
|
46546
|
+
a2[2] * b[0] - a2[0] * b[2],
|
|
46547
|
+
a2[0] * b[1] - a2[1] * b[0]
|
|
46548
|
+
];
|
|
46549
|
+
const DEGENERATE = 1e-12;
|
|
46550
|
+
function normalize$1(a2) {
|
|
46551
|
+
const L = len(a2);
|
|
46552
|
+
if (L < DEGENERATE) throw new Error("vec3exact.normalize: degenerate (zero-length) vector");
|
|
46553
|
+
return [a2[0] / L, a2[1] / L, a2[2] / L];
|
|
46554
|
+
}
|
|
46555
|
+
function carrierSignedDistance(surface, p2, planePoint) {
|
|
46556
|
+
switch (surface.kind) {
|
|
46557
|
+
case "plane": {
|
|
46558
|
+
if (!planePoint) throw new Error("plane carrier requires planePoint (FaceRef.center)");
|
|
46559
|
+
const n = normalize$1(surface.normal);
|
|
46560
|
+
return dot(n, sub(p2, planePoint));
|
|
46561
|
+
}
|
|
46562
|
+
case "cylinder": {
|
|
46563
|
+
const ax = normalize$1(surface.axis);
|
|
46564
|
+
const v = sub(p2, surface.origin);
|
|
46565
|
+
const radial = sub(v, scl(ax, dot(v, ax)));
|
|
46566
|
+
return len(radial) - surface.radius;
|
|
46567
|
+
}
|
|
46568
|
+
case "sphere": {
|
|
46569
|
+
return len(sub(p2, surface.center)) - surface.radius;
|
|
46570
|
+
}
|
|
46571
|
+
case "cone": {
|
|
46572
|
+
const ax = normalize$1(surface.axis);
|
|
46573
|
+
const v = sub(p2, surface.origin);
|
|
46574
|
+
const t = dot(v, ax);
|
|
46575
|
+
const rad = len(sub(v, scl(ax, t)));
|
|
46576
|
+
const surfaceR = surface.radiusBottom + (surface.radiusTop - surface.radiusBottom) * (t / surface.height);
|
|
46577
|
+
const slope = (surface.radiusTop - surface.radiusBottom) / surface.height;
|
|
46578
|
+
const cosHalf = 1 / Math.hypot(1, slope);
|
|
46579
|
+
return (rad - surfaceR) * cosHalf;
|
|
46580
|
+
}
|
|
46581
|
+
case "torus": {
|
|
46582
|
+
const ax = normalize$1(surface.axis);
|
|
46583
|
+
const v = sub(p2, surface.center);
|
|
46584
|
+
const z2 = dot(v, ax);
|
|
46585
|
+
const rho = len(sub(v, scl(ax, z2)));
|
|
46586
|
+
const dr = rho - surface.majorRadius;
|
|
46587
|
+
return Math.hypot(dr, z2) - surface.minorRadius;
|
|
46588
|
+
}
|
|
46589
|
+
case "ruled":
|
|
46590
|
+
case "nurbs":
|
|
46591
|
+
return null;
|
|
46592
|
+
}
|
|
46593
|
+
}
|
|
46594
|
+
function carrierNormalAt(surface, p2, planePoint) {
|
|
46595
|
+
switch (surface.kind) {
|
|
46596
|
+
case "plane":
|
|
46597
|
+
return normalize$1(surface.normal);
|
|
46598
|
+
case "cylinder": {
|
|
46599
|
+
const ax = normalize$1(surface.axis);
|
|
46600
|
+
const v = sub(p2, surface.origin);
|
|
46601
|
+
const radial = sub(v, scl(ax, dot(v, ax)));
|
|
46602
|
+
const r = len(radial);
|
|
46603
|
+
return r < DEGENERATE ? ax : scl(radial, 1 / r);
|
|
46604
|
+
}
|
|
46605
|
+
case "sphere": {
|
|
46606
|
+
const v = sub(p2, surface.center);
|
|
46607
|
+
const r = len(v);
|
|
46608
|
+
return r < DEGENERATE ? [1, 0, 0] : scl(v, 1 / r);
|
|
46609
|
+
}
|
|
46610
|
+
case "cone": {
|
|
46611
|
+
const ax = normalize$1(surface.axis);
|
|
46612
|
+
const v = sub(p2, surface.origin);
|
|
46613
|
+
const t = dot(v, ax);
|
|
46614
|
+
const radialVec = sub(v, scl(ax, t));
|
|
46615
|
+
const rad = len(radialVec);
|
|
46616
|
+
const slope = (surface.radiusTop - surface.radiusBottom) / surface.height;
|
|
46617
|
+
const h = Math.hypot(1, slope);
|
|
46618
|
+
const cosHalf = 1 / h;
|
|
46619
|
+
const sinHalf = slope / h;
|
|
46620
|
+
const rHat = rad < DEGENERATE ? ax : scl(radialVec, 1 / rad);
|
|
46621
|
+
return normalize$1([
|
|
46622
|
+
rHat[0] * cosHalf - ax[0] * sinHalf,
|
|
46623
|
+
rHat[1] * cosHalf - ax[1] * sinHalf,
|
|
46624
|
+
rHat[2] * cosHalf - ax[2] * sinHalf
|
|
46625
|
+
]);
|
|
46626
|
+
}
|
|
46627
|
+
case "torus": {
|
|
46628
|
+
const ax = normalize$1(surface.axis);
|
|
46629
|
+
const v = sub(p2, surface.center);
|
|
46630
|
+
const z2 = dot(v, ax);
|
|
46631
|
+
const radialVec = sub(v, scl(ax, z2));
|
|
46632
|
+
const rho = len(radialVec);
|
|
46633
|
+
const rHat = rho < DEGENERATE ? [1, 0, 0] : scl(radialVec, 1 / rho);
|
|
46634
|
+
const dr = rho - surface.majorRadius;
|
|
46635
|
+
const tubeDist = Math.hypot(dr, z2);
|
|
46636
|
+
if (tubeDist < DEGENERATE) return rHat;
|
|
46637
|
+
return normalize$1([
|
|
46638
|
+
rHat[0] * dr + ax[0] * z2,
|
|
46639
|
+
rHat[1] * dr + ax[1] * z2,
|
|
46640
|
+
rHat[2] * dr + ax[2] * z2
|
|
46641
|
+
]);
|
|
46642
|
+
}
|
|
46643
|
+
case "ruled":
|
|
46644
|
+
case "nurbs":
|
|
46645
|
+
return null;
|
|
46646
|
+
}
|
|
46647
|
+
}
|
|
46648
|
+
function minCurvatureRadius(surface, p2) {
|
|
46649
|
+
switch (surface.kind) {
|
|
46650
|
+
case "plane":
|
|
46651
|
+
return Number.POSITIVE_INFINITY;
|
|
46652
|
+
case "sphere":
|
|
46653
|
+
return surface.radius;
|
|
46654
|
+
case "cylinder":
|
|
46655
|
+
return surface.radius;
|
|
46656
|
+
case "cone": {
|
|
46657
|
+
const ax = normalize$1(surface.axis);
|
|
46658
|
+
const v = sub(p2, surface.origin);
|
|
46659
|
+
const t = dot(v, ax);
|
|
46660
|
+
const surfaceR = surface.radiusBottom + (surface.radiusTop - surface.radiusBottom) * (t / surface.height);
|
|
46661
|
+
return Math.max(Math.abs(surfaceR), DEGENERATE);
|
|
46662
|
+
}
|
|
46663
|
+
case "torus":
|
|
46664
|
+
return surface.minorRadius;
|
|
46665
|
+
default:
|
|
46666
|
+
return Number.POSITIVE_INFINITY;
|
|
46667
|
+
}
|
|
46668
|
+
}
|
|
46669
|
+
function triCentroid(mesh, t) {
|
|
46670
|
+
const { triVerts, vertProperties, numProp } = mesh;
|
|
46671
|
+
const i0 = triVerts[t * 3] * numProp;
|
|
46672
|
+
const i1 = triVerts[t * 3 + 1] * numProp;
|
|
46673
|
+
const i2 = triVerts[t * 3 + 2] * numProp;
|
|
46674
|
+
return [
|
|
46675
|
+
(vertProperties[i0] + vertProperties[i1] + vertProperties[i2]) / 3,
|
|
46676
|
+
(vertProperties[i0 + 1] + vertProperties[i1 + 1] + vertProperties[i2 + 1]) / 3,
|
|
46677
|
+
(vertProperties[i0 + 2] + vertProperties[i1 + 2] + vertProperties[i2 + 2]) / 3
|
|
46678
|
+
];
|
|
46679
|
+
}
|
|
46680
|
+
function triNormal(mesh, t) {
|
|
46681
|
+
const { triVerts, vertProperties, numProp } = mesh;
|
|
46682
|
+
const i0 = triVerts[t * 3] * numProp;
|
|
46683
|
+
const i1 = triVerts[t * 3 + 1] * numProp;
|
|
46684
|
+
const i2 = triVerts[t * 3 + 2] * numProp;
|
|
46685
|
+
const v0 = [vertProperties[i0], vertProperties[i0 + 1], vertProperties[i0 + 2]];
|
|
46686
|
+
const v1 = [vertProperties[i1], vertProperties[i1 + 1], vertProperties[i1 + 2]];
|
|
46687
|
+
const v2 = [vertProperties[i2], vertProperties[i2 + 1], vertProperties[i2 + 2]];
|
|
46688
|
+
return cross(sub(v1, v0), sub(v2, v0));
|
|
46689
|
+
}
|
|
46690
|
+
function triMaxEdge(mesh, t) {
|
|
46691
|
+
const { triVerts, vertProperties, numProp } = mesh;
|
|
46692
|
+
const i0 = triVerts[t * 3] * numProp;
|
|
46693
|
+
const i1 = triVerts[t * 3 + 1] * numProp;
|
|
46694
|
+
const i2 = triVerts[t * 3 + 2] * numProp;
|
|
46695
|
+
const v0 = [vertProperties[i0], vertProperties[i0 + 1], vertProperties[i0 + 2]];
|
|
46696
|
+
const v1 = [vertProperties[i1], vertProperties[i1 + 1], vertProperties[i1 + 2]];
|
|
46697
|
+
const v2 = [vertProperties[i2], vertProperties[i2 + 1], vertProperties[i2 + 2]];
|
|
46698
|
+
return Math.max(len(sub(v1, v0)), len(sub(v2, v1)), len(sub(v0, v2)));
|
|
46699
|
+
}
|
|
46700
|
+
function chordSagitta(R, h) {
|
|
46701
|
+
if (!Number.isFinite(R)) return 0;
|
|
46702
|
+
const c2 = h / 2;
|
|
46703
|
+
if (c2 >= R) return R;
|
|
46704
|
+
return R - Math.sqrt(R * R - c2 * c2);
|
|
46705
|
+
}
|
|
46706
|
+
function triVertices(mesh, t) {
|
|
46707
|
+
const { triVerts, vertProperties, numProp } = mesh;
|
|
46708
|
+
const i0 = triVerts[t * 3] * numProp;
|
|
46709
|
+
const i1 = triVerts[t * 3 + 1] * numProp;
|
|
46710
|
+
const i2 = triVerts[t * 3 + 2] * numProp;
|
|
46711
|
+
return [
|
|
46712
|
+
[vertProperties[i0], vertProperties[i0 + 1], vertProperties[i0 + 2]],
|
|
46713
|
+
[vertProperties[i1], vertProperties[i1 + 1], vertProperties[i1 + 2]],
|
|
46714
|
+
[vertProperties[i2], vertProperties[i2 + 1], vertProperties[i2 + 2]]
|
|
46715
|
+
];
|
|
46716
|
+
}
|
|
46717
|
+
function triTolerance(surface, p2, h, safety) {
|
|
46718
|
+
const R = minCurvatureRadius(surface, p2);
|
|
46719
|
+
const sagitta = chordSagitta(R, h);
|
|
46720
|
+
const floor = h * 1e-3;
|
|
46721
|
+
return safety * Math.max(sagitta, floor);
|
|
46722
|
+
}
|
|
46723
|
+
function triAngularCosMin(surface, p2, h, safety) {
|
|
46724
|
+
const R = minCurvatureRadius(surface, p2);
|
|
46725
|
+
const theta = Number.isFinite(R) ? Math.asin(Math.min(1, h / (2 * R))) : safety * 0.01;
|
|
46726
|
+
return Math.cos(Math.min(safety * theta, Math.PI / 2));
|
|
46727
|
+
}
|
|
46728
|
+
function classifyMeshFaces(mesh, carriers, opts = {}) {
|
|
46729
|
+
const safety = opts.safety ?? 2;
|
|
46730
|
+
const numTri = mesh.numTri;
|
|
46731
|
+
for (const c2 of carriers) {
|
|
46732
|
+
if (c2.surface.kind === "plane" && !c2.planePoint) {
|
|
46733
|
+
throw new Error(`carrier '${c2.name}' is a plane but has no planePoint (FaceRef.center)`);
|
|
46734
|
+
}
|
|
46735
|
+
}
|
|
46736
|
+
const assigned = new Array(numTri).fill(null);
|
|
46737
|
+
const deviation = new Array(numTri).fill(Number.POSITIVE_INFINITY);
|
|
46738
|
+
const unitNormal = new Array(numTri).fill(null);
|
|
46739
|
+
for (let t = 0; t < numTri; t++) {
|
|
46740
|
+
const p2 = triCentroid(mesh, t);
|
|
46741
|
+
const nRaw = triNormal(mesh, t);
|
|
46742
|
+
const nLen = len(nRaw);
|
|
46743
|
+
if (nLen < DEGENERATE) continue;
|
|
46744
|
+
const nTri = scl(nRaw, 1 / nLen);
|
|
46745
|
+
unitNormal[t] = nTri;
|
|
46746
|
+
const h = triMaxEdge(mesh, t);
|
|
46747
|
+
let bestName = null;
|
|
46748
|
+
let bestAbsD = Number.POSITIVE_INFINITY;
|
|
46749
|
+
const verts = triVertices(mesh, t);
|
|
46750
|
+
for (const c2 of carriers) {
|
|
46751
|
+
const d2 = carrierSignedDistance(c2.surface, p2, c2.planePoint);
|
|
46752
|
+
if (d2 === null) continue;
|
|
46753
|
+
const absD = Math.abs(d2);
|
|
46754
|
+
const tol = triTolerance(c2.surface, p2, h, safety);
|
|
46755
|
+
if (absD > tol) continue;
|
|
46756
|
+
let vertsOnCarrier = true;
|
|
46757
|
+
for (const vtx of verts) {
|
|
46758
|
+
const dv = carrierSignedDistance(c2.surface, vtx, c2.planePoint);
|
|
46759
|
+
if (dv === null || Math.abs(dv) > tol) {
|
|
46760
|
+
vertsOnCarrier = false;
|
|
46761
|
+
break;
|
|
46762
|
+
}
|
|
46763
|
+
}
|
|
46764
|
+
if (!vertsOnCarrier) continue;
|
|
46765
|
+
const nCarrier = carrierNormalAt(c2.surface, p2, c2.planePoint);
|
|
46766
|
+
if (!nCarrier) continue;
|
|
46767
|
+
const align = Math.abs(dot(nTri, nCarrier));
|
|
46768
|
+
if (align < triAngularCosMin(c2.surface, p2, h, safety)) continue;
|
|
46769
|
+
if (absD < bestAbsD) {
|
|
46770
|
+
bestAbsD = absD;
|
|
46771
|
+
bestName = c2.name;
|
|
46772
|
+
}
|
|
46773
|
+
}
|
|
46774
|
+
assigned[t] = bestName;
|
|
46775
|
+
if (bestName !== null) deviation[t] = bestAbsD;
|
|
46776
|
+
}
|
|
46777
|
+
const parent = new Int32Array(numTri);
|
|
46778
|
+
for (let i = 0; i < numTri; i++) parent[i] = i;
|
|
46779
|
+
const find = (x2) => {
|
|
46780
|
+
let r = x2;
|
|
46781
|
+
while (parent[r] !== r) r = parent[r];
|
|
46782
|
+
while (parent[x2] !== r) {
|
|
46783
|
+
const nx = parent[x2];
|
|
46784
|
+
parent[x2] = r;
|
|
46785
|
+
x2 = nx;
|
|
46786
|
+
}
|
|
46787
|
+
return r;
|
|
46788
|
+
};
|
|
46789
|
+
const unite = (a2, b) => {
|
|
46790
|
+
const ra = find(a2);
|
|
46791
|
+
const rb = find(b);
|
|
46792
|
+
if (ra !== rb) parent[ra] = rb;
|
|
46793
|
+
};
|
|
46794
|
+
const edgeMap = /* @__PURE__ */ new Map();
|
|
46795
|
+
const edgeKey2 = (a2, b) => a2 < b ? `${a2}_${b}` : `${b}_${a2}`;
|
|
46796
|
+
for (let t = 0; t < numTri; t++) {
|
|
46797
|
+
const a2 = mesh.triVerts[t * 3];
|
|
46798
|
+
const b = mesh.triVerts[t * 3 + 1];
|
|
46799
|
+
const c2 = mesh.triVerts[t * 3 + 2];
|
|
46800
|
+
for (const [u2, v] of [
|
|
46801
|
+
[a2, b],
|
|
46802
|
+
[b, c2],
|
|
46803
|
+
[c2, a2]
|
|
46804
|
+
]) {
|
|
46805
|
+
const k2 = edgeKey2(u2, v);
|
|
46806
|
+
const other = edgeMap.get(k2);
|
|
46807
|
+
if (other === void 0) {
|
|
46808
|
+
edgeMap.set(k2, t);
|
|
46809
|
+
} else if (assigned[t] === assigned[other]) {
|
|
46810
|
+
if (assigned[t] !== null) {
|
|
46811
|
+
unite(t, other);
|
|
46812
|
+
} else {
|
|
46813
|
+
const na = unitNormal[t];
|
|
46814
|
+
const nb = unitNormal[other];
|
|
46815
|
+
if (na && nb && dot(na, nb) >= SMOOTH_THRESHOLD_DOT) unite(t, other);
|
|
46816
|
+
}
|
|
46817
|
+
}
|
|
46818
|
+
}
|
|
46819
|
+
}
|
|
46820
|
+
const groups = /* @__PURE__ */ new Map();
|
|
46821
|
+
for (let t = 0; t < numTri; t++) {
|
|
46822
|
+
const r = find(t);
|
|
46823
|
+
let g2 = groups.get(r);
|
|
46824
|
+
if (!g2) {
|
|
46825
|
+
g2 = [];
|
|
46826
|
+
groups.set(r, g2);
|
|
46827
|
+
}
|
|
46828
|
+
g2.push(t);
|
|
46829
|
+
}
|
|
46830
|
+
const faces = [];
|
|
46831
|
+
let unclassified = 0;
|
|
46832
|
+
for (const tris of groups.values()) {
|
|
46833
|
+
const name = assigned[tris[0]];
|
|
46834
|
+
if (name === null) {
|
|
46835
|
+
unclassified += tris.length;
|
|
46836
|
+
}
|
|
46837
|
+
const carrier = carriers.find((c2) => c2.name === name);
|
|
46838
|
+
let maxDev = 0;
|
|
46839
|
+
let maxTol = 0;
|
|
46840
|
+
for (const t of tris) {
|
|
46841
|
+
if (deviation[t] !== Number.POSITIVE_INFINITY) maxDev = Math.max(maxDev, deviation[t]);
|
|
46842
|
+
if (carrier) {
|
|
46843
|
+
const p2 = triCentroid(mesh, t);
|
|
46844
|
+
const h = triMaxEdge(mesh, t);
|
|
46845
|
+
maxTol = Math.max(maxTol, triTolerance(carrier.surface, p2, h, safety));
|
|
46846
|
+
}
|
|
46847
|
+
}
|
|
46848
|
+
faces.push({
|
|
46849
|
+
carrier: name,
|
|
46850
|
+
kind: carrier ? carrier.surface.kind : "unidentified",
|
|
46851
|
+
triangleIndices: tris,
|
|
46852
|
+
connected: true,
|
|
46853
|
+
maxMemberDeviation: maxDev,
|
|
46854
|
+
tol: maxTol
|
|
46855
|
+
});
|
|
46856
|
+
}
|
|
46857
|
+
return { faces, unclassifiedTriangleCount: unclassified };
|
|
46858
|
+
}
|
|
46859
|
+
const UNIDENTIFIED_FACE_NAME = "unidentified";
|
|
46860
|
+
function synthesizePerTriangleFaceIds(result, numTri) {
|
|
46861
|
+
const faceID = new Int32Array(numTri).fill(-1);
|
|
46862
|
+
const faceIdNames = [];
|
|
46863
|
+
for (const face of result.faces) {
|
|
46864
|
+
const id = faceIdNames.length;
|
|
46865
|
+
faceIdNames.push(face.carrier ?? UNIDENTIFIED_FACE_NAME);
|
|
46866
|
+
for (const t of face.triangleIndices) faceID[t] = id;
|
|
46867
|
+
}
|
|
46868
|
+
return { faceID, faceIdNames };
|
|
46869
|
+
}
|
|
46870
|
+
function buildCarrierFaceRefs(mesh, carriers) {
|
|
46871
|
+
const out = /* @__PURE__ */ new Map();
|
|
46872
|
+
const ids = mesh.faceID;
|
|
46873
|
+
const names = mesh.faceIdNames;
|
|
46874
|
+
if (!ids || !names || ids.length === 0 || names.length === 0) return out;
|
|
46875
|
+
const surfaceByName = /* @__PURE__ */ new Map();
|
|
46876
|
+
for (const c2 of carriers) surfaceByName.set(c2.name, c2.surface);
|
|
46877
|
+
const { triVerts, vertProperties, numProp, numTri } = mesh;
|
|
46878
|
+
const acc = /* @__PURE__ */ new Map();
|
|
46879
|
+
for (let t = 0; t < numTri; t++) {
|
|
46880
|
+
const id = ids[t];
|
|
46881
|
+
if (id < 0) continue;
|
|
46882
|
+
const name = names[id];
|
|
46883
|
+
if (!name || name === UNIDENTIFIED_FACE_NAME) continue;
|
|
46884
|
+
if (!surfaceByName.has(name)) continue;
|
|
46885
|
+
const i0 = triVerts[t * 3] * numProp;
|
|
46886
|
+
const i1 = triVerts[t * 3 + 1] * numProp;
|
|
46887
|
+
const i2 = triVerts[t * 3 + 2] * numProp;
|
|
46888
|
+
const ax = vertProperties[i0], ay = vertProperties[i0 + 1], az = vertProperties[i0 + 2];
|
|
46889
|
+
const bx = vertProperties[i1], by = vertProperties[i1 + 1], bz = vertProperties[i1 + 2];
|
|
46890
|
+
const cx = vertProperties[i2], cy = vertProperties[i2 + 1], cz = vertProperties[i2 + 2];
|
|
46891
|
+
const nx = (by - ay) * (cz - az) - (bz - az) * (cy - ay);
|
|
46892
|
+
const ny = (bz - az) * (cx - ax) - (bx - ax) * (cz - az);
|
|
46893
|
+
const nz = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
|
|
46894
|
+
const twoArea = Math.hypot(nx, ny, nz);
|
|
46895
|
+
const area2 = twoArea * 0.5;
|
|
46896
|
+
let a2 = acc.get(name);
|
|
46897
|
+
if (!a2) {
|
|
46898
|
+
a2 = { area: 0, cx: 0, cy: 0, cz: 0, nx: 0, ny: 0, nz: 0, facetNormal: null };
|
|
46899
|
+
acc.set(name, a2);
|
|
46900
|
+
}
|
|
46901
|
+
a2.area += area2;
|
|
46902
|
+
a2.cx += (ax + bx + cx) / 3 * area2;
|
|
46903
|
+
a2.cy += (ay + by + cy) / 3 * area2;
|
|
46904
|
+
a2.cz += (az + bz + cz) / 3 * area2;
|
|
46905
|
+
a2.nx += nx * 0.5;
|
|
46906
|
+
a2.ny += ny * 0.5;
|
|
46907
|
+
a2.nz += nz * 0.5;
|
|
46908
|
+
if (!a2.facetNormal && twoArea > 1e-12) a2.facetNormal = [nx / twoArea, ny / twoArea, nz / twoArea];
|
|
46909
|
+
}
|
|
46910
|
+
for (const [name, a2] of acc) {
|
|
46911
|
+
if (a2.area <= 0) continue;
|
|
46912
|
+
const center2 = [a2.cx / a2.area, a2.cy / a2.area, a2.cz / a2.area];
|
|
46913
|
+
const nlen = Math.hypot(a2.nx, a2.ny, a2.nz);
|
|
46914
|
+
const normal2 = nlen > 1e-9 ? [a2.nx / nlen, a2.ny / nlen, a2.nz / nlen] : a2.facetNormal ?? [0, 0, 1];
|
|
46915
|
+
const surface = surfaceByName.get(name);
|
|
46916
|
+
out.set(name, { name, center: center2, normal: normal2, surface, planar: surface ? surface.kind === "plane" : void 0 });
|
|
46917
|
+
}
|
|
46918
|
+
return out;
|
|
46919
|
+
}
|
|
45749
46920
|
const PLACEMENT_REFERENCE_KINDS = ["points", "edges", "surfaces", "objects"];
|
|
45750
46921
|
function cloneVec3(value, label) {
|
|
45751
46922
|
if (!Array.isArray(value) || value.length < 3) {
|
|
@@ -45763,9 +46934,9 @@ function midpoint(start, end) {
|
|
|
45763
46934
|
return [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2, (start[2] + end[2]) / 2];
|
|
45764
46935
|
}
|
|
45765
46936
|
function normalizeVector(value) {
|
|
45766
|
-
const
|
|
45767
|
-
if (
|
|
45768
|
-
return [value[0] /
|
|
46937
|
+
const len2 = Math.hypot(value[0], value[1], value[2]);
|
|
46938
|
+
if (len2 < 1e-10) return [0, 0, 1];
|
|
46939
|
+
return [value[0] / len2, value[1] / len2, value[2] / len2];
|
|
45769
46940
|
}
|
|
45770
46941
|
function isBoundsObject(value) {
|
|
45771
46942
|
return !!value && typeof value === "object" && "min" in value && "max" in value;
|
|
@@ -46578,9 +47749,9 @@ function requireVec3Pivot(v, method) {
|
|
|
46578
47749
|
}
|
|
46579
47750
|
function mirrorPlaneMatrix(normal2) {
|
|
46580
47751
|
const [nx0, ny0, nz0] = normal2;
|
|
46581
|
-
const
|
|
46582
|
-
if (
|
|
46583
|
-
const nx = nx0 /
|
|
47752
|
+
const len2 = Math.hypot(nx0, ny0, nz0);
|
|
47753
|
+
if (len2 < 1e-12) return Transform.identity().toArray();
|
|
47754
|
+
const nx = nx0 / len2, ny = ny0 / len2, nz = nz0 / len2;
|
|
46584
47755
|
const m00 = 1 - 2 * nx * nx, m01 = -2 * nx * ny, m02 = -2 * nx * nz;
|
|
46585
47756
|
const m10 = -2 * ny * nx, m11 = 1 - 2 * ny * ny, m12 = -2 * ny * nz;
|
|
46586
47757
|
const m20 = -2 * nz * nx, m21 = -2 * nz * ny, m22 = 1 - 2 * nz * nz;
|
|
@@ -46825,8 +47996,8 @@ class ShapeGroup {
|
|
|
46825
47996
|
/** Reorient the group so its local Z axis points along `direction`. */
|
|
46826
47997
|
pointAlong(direction) {
|
|
46827
47998
|
const [dx, dy, dz] = requireNonZeroFiniteVec3(direction, "ShapeGroup.pointAlong() direction");
|
|
46828
|
-
const
|
|
46829
|
-
const nx = dx /
|
|
47999
|
+
const len2 = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1;
|
|
48000
|
+
const nx = dx / len2, ny = dy / len2, nz = dz / len2;
|
|
46830
48001
|
const cx = -ny, cy = nx, cz = 0;
|
|
46831
48002
|
const sinA = Math.sqrt(cx * cx + cy * cy + cz * cz);
|
|
46832
48003
|
const cosA = nz;
|
|
@@ -47037,9 +48208,9 @@ class ShapeGroup {
|
|
|
47037
48208
|
if (firstPair) {
|
|
47038
48209
|
const selfAxis = firstPair.selfPort.axis;
|
|
47039
48210
|
const transformed = tx.vector(selfAxis);
|
|
47040
|
-
const
|
|
47041
|
-
if (
|
|
47042
|
-
_groupExplodeHint.set(result, [transformed[0] /
|
|
48211
|
+
const len2 = Math.hypot(transformed[0], transformed[1], transformed[2]);
|
|
48212
|
+
if (len2 > 1e-10) {
|
|
48213
|
+
_groupExplodeHint.set(result, [transformed[0] / len2, transformed[1] / len2, transformed[2] / len2]);
|
|
47043
48214
|
}
|
|
47044
48215
|
}
|
|
47045
48216
|
return result;
|
|
@@ -47296,12 +48467,15 @@ function rootTopologyRewritePropagation(plan) {
|
|
|
47296
48467
|
case "variableSweep":
|
|
47297
48468
|
case "filletEdges":
|
|
47298
48469
|
case "cornerYBlend":
|
|
48470
|
+
case "faceFillet":
|
|
48471
|
+
case "fullRound":
|
|
47299
48472
|
case "chamferEdges":
|
|
47300
48473
|
case "draft":
|
|
47301
48474
|
case "offsetSolid":
|
|
47302
48475
|
case "importedMesh":
|
|
47303
48476
|
case "sdf":
|
|
47304
48477
|
case "fromSlices":
|
|
48478
|
+
case "fromSectionFrames":
|
|
47305
48479
|
case "analyticSurface":
|
|
47306
48480
|
case "nurbsSurface":
|
|
47307
48481
|
case "surfaceRuled":
|
|
@@ -47729,12 +48903,15 @@ function rootPlanPropagation(plan) {
|
|
|
47729
48903
|
case "variableSweep":
|
|
47730
48904
|
case "filletEdges":
|
|
47731
48905
|
case "cornerYBlend":
|
|
48906
|
+
case "faceFillet":
|
|
48907
|
+
case "fullRound":
|
|
47732
48908
|
case "chamferEdges":
|
|
47733
48909
|
case "draft":
|
|
47734
48910
|
case "offsetSolid":
|
|
47735
48911
|
case "importedMesh":
|
|
47736
48912
|
case "sdf":
|
|
47737
48913
|
case "fromSlices":
|
|
48914
|
+
case "fromSectionFrames":
|
|
47738
48915
|
case "analyticSurface":
|
|
47739
48916
|
case "nurbsSurface":
|
|
47740
48917
|
case "surfaceRuled":
|
|
@@ -48069,11 +49246,11 @@ function clusterTriangles(mesh) {
|
|
|
48069
49246
|
let nx = e1y * e2z - e1z * e2y;
|
|
48070
49247
|
let ny = e1z * e2x - e1x * e2z;
|
|
48071
49248
|
let nz = e1x * e2y - e1y * e2x;
|
|
48072
|
-
const
|
|
48073
|
-
if (
|
|
48074
|
-
nx /=
|
|
48075
|
-
ny /=
|
|
48076
|
-
nz /=
|
|
49249
|
+
const len2 = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
49250
|
+
if (len2 < 1e-10) continue;
|
|
49251
|
+
nx /= len2;
|
|
49252
|
+
ny /= len2;
|
|
49253
|
+
nz /= len2;
|
|
48077
49254
|
const normal2 = [nx, ny, nz];
|
|
48078
49255
|
const planeOffset = nx * v0x + ny * v0y + nz * v0z;
|
|
48079
49256
|
let matched = -1;
|
|
@@ -48161,10 +49338,10 @@ function extractEdgesWithFaces(mesh, triCluster) {
|
|
|
48161
49338
|
const nx = e1y * e2z - e1z * e2y;
|
|
48162
49339
|
const ny = e1z * e2x - e1x * e2z;
|
|
48163
49340
|
const nz = e1x * e2y - e1y * e2x;
|
|
48164
|
-
const
|
|
48165
|
-
faceNx[t] = nx /
|
|
48166
|
-
faceNy[t] = ny /
|
|
48167
|
-
faceNz[t] = nz /
|
|
49341
|
+
const len2 = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
|
|
49342
|
+
faceNx[t] = nx / len2;
|
|
49343
|
+
faceNy[t] = ny / len2;
|
|
49344
|
+
faceNz[t] = nz / len2;
|
|
48168
49345
|
}
|
|
48169
49346
|
const numVerts = vertProperties.length / numProp;
|
|
48170
49347
|
const canon = buildCanonicalMap$1(numVerts, mesh.mergeFromVert, mesh.mergeToVert);
|
|
@@ -48424,9 +49601,9 @@ function extractFaceVertices(mesh, faceNormal, faceCenter, tolerance = 0.5) {
|
|
|
48424
49601
|
return vertices;
|
|
48425
49602
|
}
|
|
48426
49603
|
function normalize(v) {
|
|
48427
|
-
const
|
|
48428
|
-
if (
|
|
48429
|
-
return [v[0] /
|
|
49604
|
+
const len2 = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
49605
|
+
if (len2 < 1e-12) throw new Error("Cannot normalize zero-length vector.");
|
|
49606
|
+
return [v[0] / len2, v[1] / len2, v[2] / len2];
|
|
48430
49607
|
}
|
|
48431
49608
|
function computeSeatTranslation(faceVertices, faceNormal, targetMesh, options = {}) {
|
|
48432
49609
|
const depth = options.depth ?? "full";
|
|
@@ -48713,10 +49890,10 @@ function rotationAroundAxisMatrix(axis, angleDeg, pivot) {
|
|
|
48713
49890
|
const rad = angleDeg * Math.PI / 180;
|
|
48714
49891
|
const cos2 = Math.cos(rad);
|
|
48715
49892
|
const sin2 = Math.sin(rad);
|
|
48716
|
-
const
|
|
48717
|
-
const ux = axis[0] /
|
|
48718
|
-
const uy = axis[1] /
|
|
48719
|
-
const uz = axis[2] /
|
|
49893
|
+
const len2 = Math.sqrt(axis[0] ** 2 + axis[1] ** 2 + axis[2] ** 2) || 1;
|
|
49894
|
+
const ux = axis[0] / len2;
|
|
49895
|
+
const uy = axis[1] / len2;
|
|
49896
|
+
const uz = axis[2] / len2;
|
|
48720
49897
|
const m00 = cos2 + ux * ux * (1 - cos2);
|
|
48721
49898
|
const m01 = ux * uy * (1 - cos2) - uz * sin2;
|
|
48722
49899
|
const m02 = ux * uz * (1 - cos2) + uy * sin2;
|
|
@@ -48733,11 +49910,11 @@ function rotationAroundAxisMatrix(axis, angleDeg, pivot) {
|
|
|
48733
49910
|
}
|
|
48734
49911
|
function mirrorMatrix(normal2) {
|
|
48735
49912
|
const [nx0, ny0, nz0] = normal2;
|
|
48736
|
-
const
|
|
48737
|
-
if (
|
|
48738
|
-
const nx = nx0 /
|
|
48739
|
-
const ny = ny0 /
|
|
48740
|
-
const nz = nz0 /
|
|
49913
|
+
const len2 = Math.hypot(nx0, ny0, nz0);
|
|
49914
|
+
if (len2 < 1e-12) return Transform.identity().toArray();
|
|
49915
|
+
const nx = nx0 / len2;
|
|
49916
|
+
const ny = ny0 / len2;
|
|
49917
|
+
const nz = nz0 / len2;
|
|
48741
49918
|
const m00 = 1 - 2 * nx * nx;
|
|
48742
49919
|
const m01 = -2 * nx * ny;
|
|
48743
49920
|
const m02 = -2 * nx * nz;
|
|
@@ -48821,6 +49998,7 @@ const _shapeTopology = /* @__PURE__ */ new WeakMap();
|
|
|
48821
49998
|
const _shapeLineageTokens = /* @__PURE__ */ new WeakMap();
|
|
48822
49999
|
const _shapeFaceLabels = /* @__PURE__ */ new WeakMap();
|
|
48823
50000
|
const _shapeReferenceNames = /* @__PURE__ */ new WeakMap();
|
|
50001
|
+
const _shapeCarrierFaceRefs = /* @__PURE__ */ new WeakMap();
|
|
48824
50002
|
const _shapeReferenceAliases = /* @__PURE__ */ new WeakMap();
|
|
48825
50003
|
const _edgeRefOwners = /* @__PURE__ */ new WeakMap();
|
|
48826
50004
|
const _faceRefOwners = /* @__PURE__ */ new WeakMap();
|
|
@@ -49034,6 +50212,32 @@ function getShapeRuntimeBackendInternal(shape) {
|
|
|
49034
50212
|
if (!backend) throw new Error("Runtime backend missing on Shape");
|
|
49035
50213
|
return backend;
|
|
49036
50214
|
}
|
|
50215
|
+
function withCarrierFaceIds(mesh, shape) {
|
|
50216
|
+
if (mesh.faceID && mesh.faceID.length > 0 && mesh.faceIdNames && mesh.faceIdNames.length > 0) return mesh;
|
|
50217
|
+
if (mesh.numTri === 0) return mesh;
|
|
50218
|
+
const carriers = gatherShapeCarriers(getShapeCompilePlanInternal(shape));
|
|
50219
|
+
if (carriers.length === 0) return mesh;
|
|
50220
|
+
const result = classifyMeshFaces(mesh, carriers);
|
|
50221
|
+
const { faceID, faceIdNames } = synthesizePerTriangleFaceIds(result, mesh.numTri);
|
|
50222
|
+
if (faceIdNames.length === 0) return mesh;
|
|
50223
|
+
const writable = mesh;
|
|
50224
|
+
writable.faceID = faceID;
|
|
50225
|
+
writable.faceIdNames = faceIdNames;
|
|
50226
|
+
return mesh;
|
|
50227
|
+
}
|
|
50228
|
+
function getCarrierFaceRefs(shape) {
|
|
50229
|
+
const cached = _shapeCarrierFaceRefs.get(shape);
|
|
50230
|
+
if (cached) return cached;
|
|
50231
|
+
let refs;
|
|
50232
|
+
try {
|
|
50233
|
+
const mesh = shape.getMesh();
|
|
50234
|
+
refs = buildCarrierFaceRefs(mesh, gatherShapeCarriers(getShapeCompilePlanInternal(shape)));
|
|
50235
|
+
} catch {
|
|
50236
|
+
refs = /* @__PURE__ */ new Map();
|
|
50237
|
+
}
|
|
50238
|
+
_shapeCarrierFaceRefs.set(shape, refs);
|
|
50239
|
+
return refs;
|
|
50240
|
+
}
|
|
49037
50241
|
function getShapeCompilePlanInternal(shape) {
|
|
49038
50242
|
const stored = _shapeCompilePlans.get(shape);
|
|
49039
50243
|
if (!stored) throw new Error("Shape has no compile plan — every Shape must have an explicit plan set via setShapeCompilePlanInternal()");
|
|
@@ -49791,9 +50995,9 @@ function withTransformedDimensions(source, out, m2) {
|
|
|
49791
50995
|
if (sourceHint) {
|
|
49792
50996
|
const tx = Transform.from(m2);
|
|
49793
50997
|
const transformed = tx.vector(sourceHint);
|
|
49794
|
-
const
|
|
49795
|
-
if (
|
|
49796
|
-
setShapeExplodeHintInternal(out, [transformed[0] /
|
|
50998
|
+
const len2 = Math.hypot(transformed[0], transformed[1], transformed[2]);
|
|
50999
|
+
if (len2 > 1e-10) {
|
|
51000
|
+
setShapeExplodeHintInternal(out, [transformed[0] / len2, transformed[1] / len2, transformed[2] / len2]);
|
|
49797
51001
|
}
|
|
49798
51002
|
}
|
|
49799
51003
|
if (source.materialProps) out.materialProps = { ...source.materialProps };
|
|
@@ -49874,6 +51078,14 @@ function setShapePlacementReferences(shape, refs, options = {}) {
|
|
|
49874
51078
|
function getShapeRuntimeBackend(shape) {
|
|
49875
51079
|
return getShapeRuntimeBackendInternal(shape);
|
|
49876
51080
|
}
|
|
51081
|
+
function getShapeCompilePlan(shape) {
|
|
51082
|
+
return getShapeCompilePlanInternal(shape);
|
|
51083
|
+
}
|
|
51084
|
+
function getShapeSourceSpans(shape) {
|
|
51085
|
+
const records = _shapeSourceSpans.get(shape);
|
|
51086
|
+
if (!records) return [];
|
|
51087
|
+
return [...records].map(([planCacheKey, sourceSpan]) => ({ planCacheKey, sourceSpan }));
|
|
51088
|
+
}
|
|
49877
51089
|
function setShapeCompilePlan(shape, plan) {
|
|
49878
51090
|
return setShapeCompilePlanInternal(shape, plan);
|
|
49879
51091
|
}
|
|
@@ -50539,6 +51751,10 @@ class Shape {
|
|
|
50539
51751
|
const detected = queryMeshFace(this, query);
|
|
50540
51752
|
if (detected) return rememberFaceRefOwner(this, detected);
|
|
50541
51753
|
}
|
|
51754
|
+
if (compilePlanName) {
|
|
51755
|
+
const carrierRef = getCarrierFaceRefs(this).get(compilePlanName);
|
|
51756
|
+
if (carrierRef) return rememberFaceRefOwner(this, { ...carrierRef }, compilePlanName);
|
|
51757
|
+
}
|
|
50542
51758
|
if (compilePlanName) {
|
|
50543
51759
|
const plan = getShapeCompilePlanInternal(this);
|
|
50544
51760
|
throw new Error(explainMissingShapeFace(plan, compilePlanName));
|
|
@@ -50564,6 +51780,7 @@ class Shape {
|
|
|
50564
51780
|
if (topo) {
|
|
50565
51781
|
for (const topoName of topo.faces.keys()) all.add(topoName);
|
|
50566
51782
|
}
|
|
51783
|
+
for (const carrierName of getCarrierFaceRefs(this).keys()) all.add(carrierName);
|
|
50567
51784
|
if (!labels || labels.size === 0) return Array.from(all).sort();
|
|
50568
51785
|
for (const userLabel of labels.keys()) all.add(userLabel);
|
|
50569
51786
|
return Array.from(all).sort();
|
|
@@ -50793,7 +52010,8 @@ class Shape {
|
|
|
50793
52010
|
if (!face) {
|
|
50794
52011
|
throw new Error(explainMissingShapeFace(plan, name));
|
|
50795
52012
|
}
|
|
50796
|
-
|
|
52013
|
+
const sourceSpans = new Map(getShapeSourceSpans(this).map((record) => [record.planCacheKey, record.sourceSpan]));
|
|
52014
|
+
return traceFaceTransformationHistory(plan, face, sourceSpans);
|
|
50797
52015
|
}
|
|
50798
52016
|
/**
|
|
50799
52017
|
* Translate the shape so the given anchor or reference lands on the target coordinate.
|
|
@@ -50995,8 +52213,8 @@ class Shape {
|
|
|
50995
52213
|
*/
|
|
50996
52214
|
pointAlong(direction) {
|
|
50997
52215
|
const [dx, dy, dz] = requireNonZeroFiniteVec3(direction, "Shape.pointAlong() direction");
|
|
50998
|
-
const
|
|
50999
|
-
const nx = dx /
|
|
52216
|
+
const len2 = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1;
|
|
52217
|
+
const nx = dx / len2, ny = dy / len2, nz = dz / len2;
|
|
51000
52218
|
const cx = -ny, cy = nx, cz = 0;
|
|
51001
52219
|
const sinA = Math.sqrt(cx * cx + cy * cy + cz * cz);
|
|
51002
52220
|
const cosA = nz;
|
|
@@ -51016,8 +52234,8 @@ class Shape {
|
|
|
51016
52234
|
const rotateAxis = requireNonZeroFiniteVec3(axis, "Shape.rotateAroundAxis() axis");
|
|
51017
52235
|
const degrees = requireFiniteNumber(angleDeg, "Shape.rotateAroundAxis() angleDeg");
|
|
51018
52236
|
const rotatePivot = requireFiniteVec3$1(pivot, "Shape.rotateAroundAxis() pivot");
|
|
51019
|
-
const
|
|
51020
|
-
const normalizedAxis = [rotateAxis[0] /
|
|
52237
|
+
const len2 = Math.sqrt(rotateAxis[0] ** 2 + rotateAxis[1] ** 2 + rotateAxis[2] ** 2) || 1;
|
|
52238
|
+
const normalizedAxis = [rotateAxis[0] / len2, rotateAxis[1] / len2, rotateAxis[2] / len2];
|
|
51021
52239
|
const matrix = rotationAroundAxisMatrix(normalizedAxis, degrees, rotatePivot);
|
|
51022
52240
|
const nextPlan = appendShapeCompileTransform(getShapeCompilePlanInternal(this), {
|
|
51023
52241
|
kind: "rotateAround",
|
|
@@ -51338,7 +52556,8 @@ class Shape {
|
|
|
51338
52556
|
}
|
|
51339
52557
|
/** Extract triangle mesh for Three.js rendering */
|
|
51340
52558
|
getMesh() {
|
|
51341
|
-
|
|
52559
|
+
const mesh = getShapeRuntimeBackendInternal(this).getMesh();
|
|
52560
|
+
return withCarrierFaceIds(mesh, this);
|
|
51342
52561
|
}
|
|
51343
52562
|
/** Slice the runtime solid by a plane normal to local Z at the given offset. */
|
|
51344
52563
|
slice(offset = 0) {
|
|
@@ -51558,9 +52777,9 @@ class Shape {
|
|
|
51558
52777
|
if (firstPair) {
|
|
51559
52778
|
const selfAxis = firstPair.selfPort.axis;
|
|
51560
52779
|
const transformed = tx.vector(selfAxis);
|
|
51561
|
-
const
|
|
51562
|
-
if (
|
|
51563
|
-
setShapeExplodeHintInternal(result, [transformed[0] /
|
|
52780
|
+
const len2 = Math.hypot(transformed[0], transformed[1], transformed[2]);
|
|
52781
|
+
if (len2 > 1e-10) {
|
|
52782
|
+
setShapeExplodeHintInternal(result, [transformed[0] / len2, transformed[1] / len2, transformed[2] / len2]);
|
|
51564
52783
|
}
|
|
51565
52784
|
}
|
|
51566
52785
|
markPortsUsed(
|
|
@@ -51680,6 +52899,257 @@ function normalizeShapeOperands(apiName, inputs, minCount, usage) {
|
|
|
51680
52899
|
coerce: (value) => unwrapShapeLike(value)
|
|
51681
52900
|
});
|
|
51682
52901
|
}
|
|
52902
|
+
const PARALLEL_DOT = Math.cos(0.5 * Math.PI / 180);
|
|
52903
|
+
function vertPos(mesh, vIdx) {
|
|
52904
|
+
const b = vIdx * mesh.numProp;
|
|
52905
|
+
return [mesh.vertProperties[b], mesh.vertProperties[b + 1], mesh.vertProperties[b + 2]];
|
|
52906
|
+
}
|
|
52907
|
+
const edgeKey = (a2, b) => a2 < b ? `${a2}_${b}` : `${b}_${a2}`;
|
|
52908
|
+
const pairKey = (a2, b) => JSON.stringify(a2 < b ? [a2, b] : [b, a2]);
|
|
52909
|
+
function buildCanonicalMap(mesh) {
|
|
52910
|
+
const numVerts = mesh.vertProperties.length / mesh.numProp;
|
|
52911
|
+
const canon = new Uint32Array(numVerts);
|
|
52912
|
+
for (let i = 0; i < numVerts; i++) canon[i] = i;
|
|
52913
|
+
const { mergeFromVert, mergeToVert } = mesh;
|
|
52914
|
+
if (mergeFromVert && mergeToVert) {
|
|
52915
|
+
for (let i = 0; i < mergeFromVert.length; i++) canon[mergeFromVert[i]] = mergeToVert[i];
|
|
52916
|
+
for (let i = 0; i < numVerts; i++) {
|
|
52917
|
+
let v = canon[i];
|
|
52918
|
+
while (canon[v] !== v) v = canon[v];
|
|
52919
|
+
canon[i] = v;
|
|
52920
|
+
}
|
|
52921
|
+
}
|
|
52922
|
+
return canon;
|
|
52923
|
+
}
|
|
52924
|
+
function buildEdgeClassification(mesh, carriers) {
|
|
52925
|
+
const ids = mesh.faceID;
|
|
52926
|
+
const names = mesh.faceIdNames;
|
|
52927
|
+
if (!ids || !names || ids.length === 0 || names.length === 0) return [];
|
|
52928
|
+
if (mesh.numTri === 0) return [];
|
|
52929
|
+
const surfaceByName = /* @__PURE__ */ new Map();
|
|
52930
|
+
const planePointByName = /* @__PURE__ */ new Map();
|
|
52931
|
+
for (const c2 of carriers) {
|
|
52932
|
+
surfaceByName.set(c2.name, c2.surface);
|
|
52933
|
+
if (c2.planePoint) planePointByName.set(c2.name, c2.planePoint);
|
|
52934
|
+
}
|
|
52935
|
+
const canon = buildCanonicalMap(mesh);
|
|
52936
|
+
const edgeOwner = /* @__PURE__ */ new Map();
|
|
52937
|
+
const byPair = /* @__PURE__ */ new Map();
|
|
52938
|
+
const triFaceName = (t) => {
|
|
52939
|
+
const id = ids[t];
|
|
52940
|
+
if (id < 0) return null;
|
|
52941
|
+
return names[id] ?? null;
|
|
52942
|
+
};
|
|
52943
|
+
for (let t = 0; t < mesh.numTri; t++) {
|
|
52944
|
+
const v0 = mesh.triVerts[t * 3];
|
|
52945
|
+
const v1 = mesh.triVerts[t * 3 + 1];
|
|
52946
|
+
const v2 = mesh.triVerts[t * 3 + 2];
|
|
52947
|
+
const ca = canon[v0];
|
|
52948
|
+
const cb = canon[v1];
|
|
52949
|
+
const cc = canon[v2];
|
|
52950
|
+
for (const [u2, v] of [
|
|
52951
|
+
[ca, cb],
|
|
52952
|
+
[cb, cc],
|
|
52953
|
+
[cc, ca]
|
|
52954
|
+
]) {
|
|
52955
|
+
const k2 = edgeKey(u2, v);
|
|
52956
|
+
const other = edgeOwner.get(k2);
|
|
52957
|
+
if (other === void 0) {
|
|
52958
|
+
edgeOwner.set(k2, t);
|
|
52959
|
+
continue;
|
|
52960
|
+
}
|
|
52961
|
+
const nameThis = triFaceName(t);
|
|
52962
|
+
const nameOther = triFaceName(other);
|
|
52963
|
+
if (nameThis === null || nameOther === null) continue;
|
|
52964
|
+
if (nameThis === nameOther) continue;
|
|
52965
|
+
const pk = pairKey(nameThis, nameOther);
|
|
52966
|
+
let bucket = byPair.get(pk);
|
|
52967
|
+
if (!bucket) {
|
|
52968
|
+
const [fa, fb] = nameThis < nameOther ? [nameThis, nameOther] : [nameOther, nameThis];
|
|
52969
|
+
bucket = { faceA: fa, faceB: fb, edges: [] };
|
|
52970
|
+
byPair.set(pk, bucket);
|
|
52971
|
+
}
|
|
52972
|
+
bucket.edges.push({ a: u2, b: v });
|
|
52973
|
+
}
|
|
52974
|
+
}
|
|
52975
|
+
const result = [];
|
|
52976
|
+
for (const bucket of byPair.values()) {
|
|
52977
|
+
const chains = splitIntoChains(bucket.edges);
|
|
52978
|
+
for (const chainVerts of chains) {
|
|
52979
|
+
const chain = chainVerts.map((vi) => vertPos(mesh, vi));
|
|
52980
|
+
if (chain.length < 2) continue;
|
|
52981
|
+
const { curve, length: length4 } = deriveCurve(bucket.faceA, bucket.faceB, chain, surfaceByName, planePointByName);
|
|
52982
|
+
result.push({ faceA: bucket.faceA, faceB: bucket.faceB, curve, length: length4, chain });
|
|
52983
|
+
}
|
|
52984
|
+
}
|
|
52985
|
+
return result;
|
|
52986
|
+
}
|
|
52987
|
+
function splitIntoChains(edges) {
|
|
52988
|
+
const adj = /* @__PURE__ */ new Map();
|
|
52989
|
+
const addAdj = (x2, y2) => {
|
|
52990
|
+
let l = adj.get(x2);
|
|
52991
|
+
if (!l) {
|
|
52992
|
+
l = [];
|
|
52993
|
+
adj.set(x2, l);
|
|
52994
|
+
}
|
|
52995
|
+
l.push(y2);
|
|
52996
|
+
};
|
|
52997
|
+
const seenEdge = /* @__PURE__ */ new Set();
|
|
52998
|
+
for (const e of edges) {
|
|
52999
|
+
const k2 = edgeKey(e.a, e.b);
|
|
53000
|
+
if (seenEdge.has(k2)) continue;
|
|
53001
|
+
seenEdge.add(k2);
|
|
53002
|
+
addAdj(e.a, e.b);
|
|
53003
|
+
addAdj(e.b, e.a);
|
|
53004
|
+
}
|
|
53005
|
+
const visited = /* @__PURE__ */ new Set();
|
|
53006
|
+
const chains = [];
|
|
53007
|
+
for (const startSeed of adj.keys()) {
|
|
53008
|
+
if (visited.has(startSeed)) continue;
|
|
53009
|
+
const compVerts = [];
|
|
53010
|
+
const stack = [startSeed];
|
|
53011
|
+
visited.add(startSeed);
|
|
53012
|
+
while (stack.length) {
|
|
53013
|
+
const v = stack.pop();
|
|
53014
|
+
compVerts.push(v);
|
|
53015
|
+
for (const w2 of adj.get(v) ?? []) {
|
|
53016
|
+
if (!visited.has(w2)) {
|
|
53017
|
+
visited.add(w2);
|
|
53018
|
+
stack.push(w2);
|
|
53019
|
+
}
|
|
53020
|
+
}
|
|
53021
|
+
}
|
|
53022
|
+
chains.push(orderComponent(compVerts, adj));
|
|
53023
|
+
}
|
|
53024
|
+
return chains;
|
|
53025
|
+
}
|
|
53026
|
+
function orderComponent(compVerts, adj, seenEdge) {
|
|
53027
|
+
var _a3, _b3;
|
|
53028
|
+
if (compVerts.length <= 2) return compVerts;
|
|
53029
|
+
let start = compVerts[0];
|
|
53030
|
+
for (const v of compVerts) {
|
|
53031
|
+
if ((((_a3 = adj.get(v)) == null ? void 0 : _a3.length) ?? 0) === 1) {
|
|
53032
|
+
start = v;
|
|
53033
|
+
break;
|
|
53034
|
+
}
|
|
53035
|
+
}
|
|
53036
|
+
const ordered = [];
|
|
53037
|
+
const usedEdge = /* @__PURE__ */ new Set();
|
|
53038
|
+
let prev = -1;
|
|
53039
|
+
let cur = start;
|
|
53040
|
+
for (let guard = 0; guard <= compVerts.length; guard++) {
|
|
53041
|
+
ordered.push(cur);
|
|
53042
|
+
let next = -1;
|
|
53043
|
+
for (const w2 of adj.get(cur) ?? []) {
|
|
53044
|
+
const k2 = edgeKey(cur, w2);
|
|
53045
|
+
if (usedEdge.has(k2)) continue;
|
|
53046
|
+
if (w2 === prev && (((_b3 = adj.get(cur)) == null ? void 0 : _b3.length) ?? 0) > 1) continue;
|
|
53047
|
+
next = w2;
|
|
53048
|
+
usedEdge.add(k2);
|
|
53049
|
+
break;
|
|
53050
|
+
}
|
|
53051
|
+
if (next === -1) break;
|
|
53052
|
+
prev = cur;
|
|
53053
|
+
cur = next;
|
|
53054
|
+
if (cur === start) {
|
|
53055
|
+
ordered.push(cur);
|
|
53056
|
+
break;
|
|
53057
|
+
}
|
|
53058
|
+
}
|
|
53059
|
+
return ordered;
|
|
53060
|
+
}
|
|
53061
|
+
function polylineLength(chain) {
|
|
53062
|
+
let L = 0;
|
|
53063
|
+
for (let i = 1; i < chain.length; i++) L += len(sub(chain[i], chain[i - 1]));
|
|
53064
|
+
return L;
|
|
53065
|
+
}
|
|
53066
|
+
function deriveCurve(faceA, faceB, chain, surfaceByName, planePointByName) {
|
|
53067
|
+
const sA = surfaceByName.get(faceA);
|
|
53068
|
+
const sB = surfaceByName.get(faceB);
|
|
53069
|
+
if ((sA == null ? void 0 : sA.kind) === "plane" && (sB == null ? void 0 : sB.kind) === "plane") {
|
|
53070
|
+
const { start, end } = chainExtremes(chain);
|
|
53071
|
+
return {
|
|
53072
|
+
curve: { kind: "line", start, end, faceName: faceA },
|
|
53073
|
+
length: len(sub(end, start))
|
|
53074
|
+
};
|
|
53075
|
+
}
|
|
53076
|
+
const planeFirst = (sA == null ? void 0 : sA.kind) === "plane";
|
|
53077
|
+
const plane = planeFirst ? sA : (sB == null ? void 0 : sB.kind) === "plane" ? sB : void 0;
|
|
53078
|
+
const planeName = planeFirst ? faceA : faceB;
|
|
53079
|
+
const curved = planeFirst ? sB : sA;
|
|
53080
|
+
const curvedName = planeFirst ? faceB : faceA;
|
|
53081
|
+
if (plane && curved) {
|
|
53082
|
+
const circle = circleFromPlaneAndCarrier(plane, planeName, curved, curvedName, chain, planePointByName);
|
|
53083
|
+
if (circle) {
|
|
53084
|
+
return { curve: circle, length: 2 * Math.PI * circle.radius };
|
|
53085
|
+
}
|
|
53086
|
+
}
|
|
53087
|
+
return { curve: { kind: "unidentified" }, length: polylineLength(chain) };
|
|
53088
|
+
}
|
|
53089
|
+
function circleFromPlaneAndCarrier(plane, planeName, curved, curvedName, chain, planePointByName) {
|
|
53090
|
+
const planePoint = planePointByName.get(planeName);
|
|
53091
|
+
if (!planePoint) return null;
|
|
53092
|
+
const n = normalize$1(plane.normal);
|
|
53093
|
+
const onEdge = chainCentroid(chain);
|
|
53094
|
+
switch (curved.kind) {
|
|
53095
|
+
case "cylinder": {
|
|
53096
|
+
const ax = normalize$1(curved.axis);
|
|
53097
|
+
if (Math.abs(dot(ax, n)) < PARALLEL_DOT) return null;
|
|
53098
|
+
const center2 = axisPointAtPlane(curved.origin, ax, onEdge);
|
|
53099
|
+
return { kind: "circle", center: center2, axis: ax, radius: curved.radius, faceName: curvedName };
|
|
53100
|
+
}
|
|
53101
|
+
case "cone": {
|
|
53102
|
+
const ax = normalize$1(curved.axis);
|
|
53103
|
+
if (Math.abs(dot(ax, n)) < PARALLEL_DOT) return null;
|
|
53104
|
+
const center2 = axisPointAtPlane(curved.origin, ax, onEdge);
|
|
53105
|
+
const t = dot(sub(center2, curved.origin), ax);
|
|
53106
|
+
const radius = curved.radiusBottom + (curved.radiusTop - curved.radiusBottom) * (t / curved.height);
|
|
53107
|
+
if (!(radius > DEGENERATE)) return null;
|
|
53108
|
+
return { kind: "circle", center: center2, axis: ax, radius, faceName: curvedName };
|
|
53109
|
+
}
|
|
53110
|
+
case "sphere": {
|
|
53111
|
+
const dPlane = dot(n, sub(curved.center, planePoint));
|
|
53112
|
+
const r2 = curved.radius * curved.radius - dPlane * dPlane;
|
|
53113
|
+
if (!(r2 > DEGENERATE)) return null;
|
|
53114
|
+
const center2 = sub(curved.center, scl(n, dPlane));
|
|
53115
|
+
return { kind: "circle", center: center2, axis: n, radius: Math.sqrt(r2), faceName: curvedName };
|
|
53116
|
+
}
|
|
53117
|
+
default:
|
|
53118
|
+
return null;
|
|
53119
|
+
}
|
|
53120
|
+
}
|
|
53121
|
+
function axisPointAtPlane(origin, ax, onEdge) {
|
|
53122
|
+
const t = dot(sub(onEdge, origin), ax);
|
|
53123
|
+
return add(origin, scl(ax, t));
|
|
53124
|
+
}
|
|
53125
|
+
function chainCentroid(chain) {
|
|
53126
|
+
let x2 = 0;
|
|
53127
|
+
let y2 = 0;
|
|
53128
|
+
let z2 = 0;
|
|
53129
|
+
for (const p2 of chain) {
|
|
53130
|
+
x2 += p2[0];
|
|
53131
|
+
y2 += p2[1];
|
|
53132
|
+
z2 += p2[2];
|
|
53133
|
+
}
|
|
53134
|
+
const k2 = 1 / chain.length;
|
|
53135
|
+
return [x2 * k2, y2 * k2, z2 * k2];
|
|
53136
|
+
}
|
|
53137
|
+
function chainExtremes(chain) {
|
|
53138
|
+
let best = -1;
|
|
53139
|
+
let si = 0;
|
|
53140
|
+
let ei = chain.length - 1;
|
|
53141
|
+
for (let i = 0; i < chain.length; i++) {
|
|
53142
|
+
for (let j = i + 1; j < chain.length; j++) {
|
|
53143
|
+
const d2 = len(sub(chain[j], chain[i]));
|
|
53144
|
+
if (d2 > best) {
|
|
53145
|
+
best = d2;
|
|
53146
|
+
si = i;
|
|
53147
|
+
ei = j;
|
|
53148
|
+
}
|
|
53149
|
+
}
|
|
53150
|
+
}
|
|
53151
|
+
return { start: chain[si], end: chain[ei] };
|
|
53152
|
+
}
|
|
51683
53153
|
function cloneSerializedFaceRef(face) {
|
|
51684
53154
|
return {
|
|
51685
53155
|
...face,
|
|
@@ -51713,6 +53183,7 @@ class FrozenShapeBackend {
|
|
|
51713
53183
|
edgePositions: this._data.geometryEdgePositions,
|
|
51714
53184
|
triangleFaceIds: this._data.geometryTriangleFaceIds,
|
|
51715
53185
|
faceIdNames: this._data.geometryFaceIdNames,
|
|
53186
|
+
forgeEdges: this._data.geometryEdges ?? [],
|
|
51716
53187
|
hasSmoothNormals: this._data.hasSmoothNormals ?? false,
|
|
51717
53188
|
uvs: this._data.geometryUvs
|
|
51718
53189
|
};
|
|
@@ -51881,249 +53352,25 @@ class FrozenShape extends Shape {
|
|
|
51881
53352
|
return history;
|
|
51882
53353
|
}
|
|
51883
53354
|
}
|
|
51884
|
-
|
|
51885
|
-
const
|
|
51886
|
-
|
|
51887
|
-
|
|
51888
|
-
if (numProp !== NUM_PROP_POSITION_ONLY && numProp !== NUM_PROP_WITH_NORMAL && numProp !== NUM_PROP_WITH_UV) {
|
|
51889
|
-
throw new Error(
|
|
51890
|
-
`computeGeometryArrays: illegal vertProperties numProp ${numProp}. Only ${NUM_PROP_POSITION_ONLY} (position), ${NUM_PROP_WITH_NORMAL} (position+normal), and ${NUM_PROP_WITH_UV} (position+normal+uv) are valid; ${numProp} (e.g. a truncated uv channel) is not.`
|
|
51891
|
-
);
|
|
51892
|
-
}
|
|
51893
|
-
const hasStoredNormals = numProp === NUM_PROP_WITH_NORMAL || numProp === NUM_PROP_WITH_UV;
|
|
51894
|
-
const hasStoredUvs = numProp === NUM_PROP_WITH_UV;
|
|
51895
|
-
const useCornerNormals = !!cornerNormals && cornerNormals.length === triCount * 9;
|
|
51896
|
-
const positions = new Float32Array(triCount * 9);
|
|
51897
|
-
const normals = new Float32Array(triCount * 9);
|
|
51898
|
-
const uvs = hasStoredUvs ? new Float32Array(triCount * 6) : void 0;
|
|
51899
|
-
const faceNx = new Float32Array(triCount);
|
|
51900
|
-
const faceNy = new Float32Array(triCount);
|
|
51901
|
-
const faceNz = new Float32Array(triCount);
|
|
51902
|
-
for (let t = 0; t < triCount; t++) {
|
|
51903
|
-
const i0 = triVerts[t * 3];
|
|
51904
|
-
const i1 = triVerts[t * 3 + 1];
|
|
51905
|
-
const i2 = triVerts[t * 3 + 2];
|
|
51906
|
-
const ax = vertProperties[i0 * numProp], ay = vertProperties[i0 * numProp + 1], az = vertProperties[i0 * numProp + 2];
|
|
51907
|
-
const bx = vertProperties[i1 * numProp], by = vertProperties[i1 * numProp + 1], bz = vertProperties[i1 * numProp + 2];
|
|
51908
|
-
const cx = vertProperties[i2 * numProp], cy = vertProperties[i2 * numProp + 1], cz = vertProperties[i2 * numProp + 2];
|
|
51909
|
-
const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
|
|
51910
|
-
const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;
|
|
51911
|
-
let fnx = e1y * e2z - e1z * e2y;
|
|
51912
|
-
let fny = e1z * e2x - e1x * e2z;
|
|
51913
|
-
let fnz = e1x * e2y - e1y * e2x;
|
|
51914
|
-
const len = Math.sqrt(fnx * fnx + fny * fny + fnz * fnz) || 1;
|
|
51915
|
-
fnx /= len;
|
|
51916
|
-
fny /= len;
|
|
51917
|
-
fnz /= len;
|
|
51918
|
-
const o = t * 9;
|
|
51919
|
-
positions[o] = ax;
|
|
51920
|
-
positions[o + 1] = ay;
|
|
51921
|
-
positions[o + 2] = az;
|
|
51922
|
-
positions[o + 3] = bx;
|
|
51923
|
-
positions[o + 4] = by;
|
|
51924
|
-
positions[o + 5] = bz;
|
|
51925
|
-
positions[o + 6] = cx;
|
|
51926
|
-
positions[o + 7] = cy;
|
|
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
|
-
}
|
|
51937
|
-
if (useCornerNormals) {
|
|
51938
|
-
for (let k2 = 0; k2 < 9; k2++) normals[o + k2] = cornerNormals[o + k2];
|
|
51939
|
-
} else if (vertNormals) {
|
|
51940
|
-
normals[o] = vertNormals[i0 * 3];
|
|
51941
|
-
normals[o + 1] = vertNormals[i0 * 3 + 1];
|
|
51942
|
-
normals[o + 2] = vertNormals[i0 * 3 + 2];
|
|
51943
|
-
normals[o + 3] = vertNormals[i1 * 3];
|
|
51944
|
-
normals[o + 4] = vertNormals[i1 * 3 + 1];
|
|
51945
|
-
normals[o + 5] = vertNormals[i1 * 3 + 2];
|
|
51946
|
-
normals[o + 6] = vertNormals[i2 * 3];
|
|
51947
|
-
normals[o + 7] = vertNormals[i2 * 3 + 1];
|
|
51948
|
-
normals[o + 8] = vertNormals[i2 * 3 + 2];
|
|
51949
|
-
} else if (hasStoredNormals) {
|
|
51950
|
-
const corners = [i0, i1, i2];
|
|
51951
|
-
for (let v = 0; v < 3; v++) {
|
|
51952
|
-
const base = corners[v] * numProp;
|
|
51953
|
-
const nx = vertProperties[base + NORMAL_OFFSET];
|
|
51954
|
-
const ny = vertProperties[base + NORMAL_OFFSET + 1];
|
|
51955
|
-
const nz = vertProperties[base + NORMAL_OFFSET + 2];
|
|
51956
|
-
const oc = o + v * 3;
|
|
51957
|
-
if (nx * nx + ny * ny + nz * nz > 1e-12) {
|
|
51958
|
-
normals[oc] = nx;
|
|
51959
|
-
normals[oc + 1] = ny;
|
|
51960
|
-
normals[oc + 2] = nz;
|
|
51961
|
-
} else {
|
|
51962
|
-
normals[oc] = fnx;
|
|
51963
|
-
normals[oc + 1] = fny;
|
|
51964
|
-
normals[oc + 2] = fnz;
|
|
51965
|
-
}
|
|
51966
|
-
}
|
|
51967
|
-
} else {
|
|
51968
|
-
normals[o] = fnx;
|
|
51969
|
-
normals[o + 1] = fny;
|
|
51970
|
-
normals[o + 2] = fnz;
|
|
51971
|
-
normals[o + 3] = fnx;
|
|
51972
|
-
normals[o + 4] = fny;
|
|
51973
|
-
normals[o + 5] = fnz;
|
|
51974
|
-
normals[o + 6] = fnx;
|
|
51975
|
-
normals[o + 7] = fny;
|
|
51976
|
-
normals[o + 8] = fnz;
|
|
51977
|
-
}
|
|
51978
|
-
faceNx[t] = fnx;
|
|
51979
|
-
faceNy[t] = fny;
|
|
51980
|
-
faceNz[t] = fnz;
|
|
51981
|
-
}
|
|
51982
|
-
if (!useCornerNormals && !vertNormals && !hasStoredNormals && triCount > 0) {
|
|
51983
|
-
computeAutoSmoothNormals(
|
|
51984
|
-
triVerts,
|
|
51985
|
-
vertProperties,
|
|
51986
|
-
numProp,
|
|
51987
|
-
triCount,
|
|
51988
|
-
faceNx,
|
|
51989
|
-
faceNy,
|
|
51990
|
-
faceNz,
|
|
51991
|
-
normals,
|
|
51992
|
-
mesh.mergeFromVert,
|
|
51993
|
-
mesh.mergeToVert
|
|
51994
|
-
);
|
|
51995
|
-
}
|
|
51996
|
-
const edgePositions = options.skipEdges ? new Float32Array(0) : computeSharpEdges(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, mesh.mergeFromVert, mesh.mergeToVert);
|
|
51997
|
-
return {
|
|
51998
|
-
positions,
|
|
51999
|
-
normals,
|
|
52000
|
-
edgePositions,
|
|
52001
|
-
hasSmoothNormals: triCount > 0,
|
|
52002
|
-
uvs
|
|
52003
|
-
};
|
|
52004
|
-
}
|
|
52005
|
-
function computeAutoSmoothNormals(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, normals, mergeFromVert, mergeToVert) {
|
|
52006
|
-
const numVerts = vertProperties.length / numProp;
|
|
52007
|
-
const canon = buildCanonicalMap(numVerts, mergeFromVert, mergeToVert);
|
|
52008
|
-
const vertToTris = /* @__PURE__ */ new Map();
|
|
52009
|
-
for (let t = 0; t < triCount; t++) {
|
|
52010
|
-
for (let v = 0; v < 3; v++) {
|
|
52011
|
-
const cv = canon[triVerts[t * 3 + v]];
|
|
52012
|
-
let list = vertToTris.get(cv);
|
|
52013
|
-
if (!list) {
|
|
52014
|
-
list = [];
|
|
52015
|
-
vertToTris.set(cv, list);
|
|
52016
|
-
}
|
|
52017
|
-
list.push(t);
|
|
52018
|
-
}
|
|
52019
|
-
}
|
|
52020
|
-
for (let t = 0; t < triCount; t++) {
|
|
52021
|
-
for (let v = 0; v < 3; v++) {
|
|
52022
|
-
const cv = canon[triVerts[t * 3 + v]];
|
|
52023
|
-
const adjacentTris = vertToTris.get(cv);
|
|
52024
|
-
if (!adjacentTris || adjacentTris.length <= 1) continue;
|
|
52025
|
-
let sx = 0, sy = 0, sz = 0;
|
|
52026
|
-
for (const adj of adjacentTris) {
|
|
52027
|
-
const dot2 = faceNx[t] * faceNx[adj] + faceNy[t] * faceNy[adj] + faceNz[t] * faceNz[adj];
|
|
52028
|
-
if (dot2 >= SMOOTH_THRESHOLD_DOT) {
|
|
52029
|
-
sx += faceNx[adj];
|
|
52030
|
-
sy += faceNy[adj];
|
|
52031
|
-
sz += faceNz[adj];
|
|
52032
|
-
}
|
|
52033
|
-
}
|
|
52034
|
-
const slen = Math.sqrt(sx * sx + sy * sy + sz * sz);
|
|
52035
|
-
if (slen > 1e-9) {
|
|
52036
|
-
sx /= slen;
|
|
52037
|
-
sy /= slen;
|
|
52038
|
-
sz /= slen;
|
|
52039
|
-
const o = t * 9 + v * 3;
|
|
52040
|
-
normals[o] = sx;
|
|
52041
|
-
normals[o + 1] = sy;
|
|
52042
|
-
normals[o + 2] = sz;
|
|
52043
|
-
}
|
|
52044
|
-
}
|
|
52045
|
-
}
|
|
52046
|
-
}
|
|
52047
|
-
function computeSharpEdges(triVerts, vertProperties, numProp, triCount, faceNx, faceNy, faceNz, mergeFromVert, mergeToVert) {
|
|
52048
|
-
const numVerts = vertProperties.length / numProp;
|
|
52049
|
-
const canon = buildCanonicalMap(numVerts, mergeFromVert, mergeToVert);
|
|
52050
|
-
const MAX_NUMERIC = 1 << 21;
|
|
52051
|
-
let maxCanon = 0;
|
|
52052
|
-
for (let i = 0; i < numVerts; i++) {
|
|
52053
|
-
if (canon[i] > maxCanon) maxCanon = canon[i];
|
|
52054
|
-
}
|
|
52055
|
-
const useNumeric = maxCanon < MAX_NUMERIC;
|
|
52056
|
-
const halfEdges = /* @__PURE__ */ new Map();
|
|
52057
|
-
const edgeList = [];
|
|
52058
|
-
for (let t = 0; t < triCount; t++) {
|
|
52059
|
-
const ca = canon[triVerts[t * 3]];
|
|
52060
|
-
const cb = canon[triVerts[t * 3 + 1]];
|
|
52061
|
-
const cc = canon[triVerts[t * 3 + 2]];
|
|
52062
|
-
const tv = [ca, cb, cc];
|
|
52063
|
-
for (let e = 0; e < 3; e++) {
|
|
52064
|
-
const va = tv[e], vb = tv[(e + 1) % 3];
|
|
52065
|
-
const fwdKey = useNumeric ? va * MAX_NUMERIC + vb : `${va},${vb}`;
|
|
52066
|
-
const revKey = useNumeric ? vb * MAX_NUMERIC + va : `${vb},${va}`;
|
|
52067
|
-
const adjTri = halfEdges.get(revKey);
|
|
52068
|
-
if (adjTri !== void 0) {
|
|
52069
|
-
const dot2 = faceNx[t] * faceNx[adjTri] + faceNy[t] * faceNy[adjTri] + faceNz[t] * faceNz[adjTri];
|
|
52070
|
-
if (dot2 <= EDGE_THRESHOLD_DOT) {
|
|
52071
|
-
const origVa = triVerts[t * 3 + e];
|
|
52072
|
-
const origVb = triVerts[t * 3 + (e + 1) % 3];
|
|
52073
|
-
edgeList.push(
|
|
52074
|
-
vertProperties[origVa * numProp],
|
|
52075
|
-
vertProperties[origVa * numProp + 1],
|
|
52076
|
-
vertProperties[origVa * numProp + 2],
|
|
52077
|
-
vertProperties[origVb * numProp],
|
|
52078
|
-
vertProperties[origVb * numProp + 1],
|
|
52079
|
-
vertProperties[origVb * numProp + 2]
|
|
52080
|
-
);
|
|
52081
|
-
}
|
|
52082
|
-
halfEdges.delete(revKey);
|
|
52083
|
-
} else {
|
|
52084
|
-
halfEdges.set(fwdKey, t);
|
|
52085
|
-
}
|
|
52086
|
-
}
|
|
52087
|
-
}
|
|
52088
|
-
for (const [key2, t] of halfEdges) {
|
|
52089
|
-
const ca = canon[triVerts[t * 3]];
|
|
52090
|
-
const cb = canon[triVerts[t * 3 + 1]];
|
|
52091
|
-
const cc = canon[triVerts[t * 3 + 2]];
|
|
52092
|
-
const tv = [ca, cb, cc];
|
|
52093
|
-
for (let e = 0; e < 3; e++) {
|
|
52094
|
-
const va = tv[e], vb = tv[(e + 1) % 3];
|
|
52095
|
-
const fwdKey = useNumeric ? va * MAX_NUMERIC + vb : `${va},${vb}`;
|
|
52096
|
-
if (fwdKey === key2) {
|
|
52097
|
-
const origVa = triVerts[t * 3 + e];
|
|
52098
|
-
const origVb = triVerts[t * 3 + (e + 1) % 3];
|
|
52099
|
-
edgeList.push(
|
|
52100
|
-
vertProperties[origVa * numProp],
|
|
52101
|
-
vertProperties[origVa * numProp + 1],
|
|
52102
|
-
vertProperties[origVa * numProp + 2],
|
|
52103
|
-
vertProperties[origVb * numProp],
|
|
52104
|
-
vertProperties[origVb * numProp + 1],
|
|
52105
|
-
vertProperties[origVb * numProp + 2]
|
|
52106
|
-
);
|
|
52107
|
-
break;
|
|
52108
|
-
}
|
|
52109
|
-
}
|
|
53355
|
+
function toBakedEdge(edge) {
|
|
53356
|
+
const points = [];
|
|
53357
|
+
for (const p2 of edge.chain) {
|
|
53358
|
+
points.push(p2[0], p2[1], p2[2]);
|
|
52110
53359
|
}
|
|
52111
|
-
return
|
|
53360
|
+
return { faceA: edge.faceA, faceB: edge.faceB, curve: edge.curve, length: edge.length, points };
|
|
52112
53361
|
}
|
|
52113
|
-
function
|
|
52114
|
-
|
|
52115
|
-
|
|
52116
|
-
|
|
52117
|
-
|
|
52118
|
-
|
|
52119
|
-
|
|
52120
|
-
|
|
52121
|
-
let v = canon[i];
|
|
52122
|
-
while (canon[v] !== v) v = canon[v];
|
|
52123
|
-
canon[i] = v;
|
|
52124
|
-
}
|
|
53362
|
+
function attachEdgeMetadata(geometry, mesh, shape) {
|
|
53363
|
+
let edges;
|
|
53364
|
+
try {
|
|
53365
|
+
const carriers = gatherShapeCarriers(getShapeCompilePlan(shape));
|
|
53366
|
+
if (carriers.length === 0) return;
|
|
53367
|
+
edges = buildEdgeClassification(mesh, carriers);
|
|
53368
|
+
} catch {
|
|
53369
|
+
return;
|
|
52125
53370
|
}
|
|
52126
|
-
return
|
|
53371
|
+
if (edges.length === 0) return;
|
|
53372
|
+
const userData = geometry.userData;
|
|
53373
|
+
userData.forgeEdges = edges.map(toBakedEdge);
|
|
52127
53374
|
}
|
|
52128
53375
|
function attachKernelFaceMetadata(geometry, triangleFaceIds, faceIdNames) {
|
|
52129
53376
|
if (!triangleFaceIds || triangleFaceIds.length === 0 || !faceIdNames || faceIdNames.length === 0) return;
|
|
@@ -52134,12 +53381,15 @@ function attachKernelFaceMetadata(geometry, triangleFaceIds, faceIdNames) {
|
|
|
52134
53381
|
}
|
|
52135
53382
|
function shapeToGeometry(shape) {
|
|
52136
53383
|
if (shape instanceof FrozenShape) {
|
|
52137
|
-
const { positions, normals, edgePositions, triangleFaceIds, faceIdNames, hasSmoothNormals, uvs } = shape.getPrecomputedGeometry();
|
|
53384
|
+
const { positions, normals, edgePositions, triangleFaceIds, faceIdNames, forgeEdges, hasSmoothNormals, uvs } = shape.getPrecomputedGeometry();
|
|
52138
53385
|
const solid = new BufferGeometry();
|
|
52139
53386
|
solid.setAttribute("position", new BufferAttribute(positions, 3));
|
|
52140
53387
|
solid.setAttribute("normal", new BufferAttribute(normals, 3));
|
|
52141
53388
|
if (uvs) solid.setAttribute("uv", new BufferAttribute(uvs, 2));
|
|
52142
53389
|
attachKernelFaceMetadata(solid, triangleFaceIds, faceIdNames);
|
|
53390
|
+
if (forgeEdges && forgeEdges.length > 0) {
|
|
53391
|
+
solid.userData.forgeEdges = forgeEdges;
|
|
53392
|
+
}
|
|
52143
53393
|
const edges = new BufferGeometry();
|
|
52144
53394
|
edges.setAttribute("position", new BufferAttribute(edgePositions, 3));
|
|
52145
53395
|
return { solid, edges, hasSmoothNormals };
|
|
@@ -52176,6 +53426,7 @@ function shapeToGeometryFallback(shape) {
|
|
|
52176
53426
|
solid.setAttribute("normal", new BufferAttribute(normals, 3));
|
|
52177
53427
|
if (uvs) solid.setAttribute("uv", new BufferAttribute(uvs, 2));
|
|
52178
53428
|
attachKernelFaceMetadata(solid, mesh.faceID, mesh.faceIdNames);
|
|
53429
|
+
attachEdgeMetadata(solid, mesh, shape);
|
|
52179
53430
|
const edges = new BufferGeometry();
|
|
52180
53431
|
edges.setAttribute("position", new BufferAttribute(edgePositions, 3));
|
|
52181
53432
|
return { solid, edges, hasSmoothNormals };
|