forgecad 0.10.3 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{AdminPage-CK7ObBz3.js → AdminPage-B3L3W1Uo.js} +1 -1
- package/dist/assets/{BenchmarkPage-Ds7Z2doN.js → BenchmarkPage-DXKVXMrJ.js} +2 -2
- package/dist/assets/{BlogPage-DlPbpt6A.js → BlogPage-B7BWxOCg.js} +1 -1
- package/dist/assets/{DocsPage-vZb3b3Y0.js → DocsPage-BPGGwht1.js} +28 -43
- package/dist/assets/{EditorApp-HLoKfe15.js → EditorApp-BWUGCdD5.js} +49 -16
- package/dist/assets/{EmbedViewer--KnqBKrJ.js → EmbedViewer-DygByZS2.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-C_LssmnA.js → LandingPageProofDriven-BoVE7JGY.js} +54 -36
- package/dist/assets/{LegalPage-DGsyo4n1.js → LegalPage-Din8wv8d.js} +2 -2
- package/dist/assets/{PricingPage-BOE27B-R.js → PricingPage-C2PMzmDc.js} +2 -2
- package/dist/assets/{SettingsPage-f47cnk39.js → SettingsPage-BlJDCRe8.js} +1 -1
- package/dist/assets/{app-D6ccu2Xx.js → app-BsRYSfxY.js} +238 -3714
- package/dist/assets/{backendInit-DbTkQN9J.js → backendInit-6C0DLgH0.js} +5972 -1566
- package/dist/assets/cli/{render-BsngirjC.js → render-XXol_ET7.js} +724 -112
- package/dist/assets/{constructionHistoryWorker-PCwXrTDB.js → constructionHistoryWorker-cTHWRJEi.js} +528 -252
- package/dist/assets/{evalWorker-CS63PfZu.js → evalWorker-BssDYW9u.js} +1453 -902
- package/dist/assets/{inspectWorker-Y4cOzNyA.js → inspectWorker-ymhBV4Ll.js} +2635 -1024
- package/dist/assets/{jointPose-AMvCywzS.js → jointPose-B0blBj9A.js} +1 -1
- package/dist/assets/{landing-proof-driven-ORyigZ6p.css → landing-proof-driven-Cpf-MIbI.css} +73 -13
- package/dist/assets/{manifold-Crd_F2qx.js → manifold-B_7QXpGB.js} +1 -1
- package/dist/assets/{manifold-k2kRcc85.js → manifold-CNShmpEJ.js} +1 -1
- package/dist/assets/{manifold-CBry38ly.js → manifold-CYlIm-M6.js} +2 -2
- package/dist/assets/{reportWorker-CWvn0CEv.js → reportWorker-Cb5eyM7D.js} +1407 -892
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +17 -15
- package/dist/docs-raw/component-model.md +2 -2
- package/dist/docs-raw/generated/concepts.md +5 -1
- package/dist/docs-raw/generated/core.md +26 -0
- package/dist/docs-raw/generated/runtime-names.md +1 -1
- package/dist/docs-raw/guides/inspection-bundles.md +1 -1
- package/dist/docs-raw/simulation-workflow.md +1 -1
- package/dist/docs-raw/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
- package/dist/docs-raw/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
- package/dist/docs-raw/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
- package/{dist-skill/website/skills/forgecad-visual-spec.md → dist/docs-raw/skills/forgecad-image-prompt.md} +7 -7
- package/dist/docs-raw/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
- package/{dist-skill/website/skills/forgecad-project.md → dist/docs-raw/skills/forgecad-project-sync.md} +5 -5
- package/dist/docs-raw/skills/{forgecad-3d-reconstruction.md → forgecad-reconstruct-cad-file.md} +7 -7
- package/dist/docs-raw/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
- package/dist/docs-raw/skills/{forgecad-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
- package/dist/docs-raw/skills/index.md +9 -12
- package/dist/index.html +9 -9
- package/dist/llms.txt +7 -7
- package/dist/sitemap.xml +16 -16
- package/dist-cli/{check-compiler-HPF2T2FS.js → check-compiler-4RPB6SB5.js} +1 -1
- package/dist-cli/{check-query-propagation-HYSLTXAB.js → check-query-propagation-KN3DFQTX.js} +1 -1
- package/dist-cli/{chunk-WLUKAW3H.js → chunk-UHBRMYA6.js} +28802 -28152
- package/dist-cli/forgecad.js +660 -9
- package/dist-skill/CONTEXT.md +27 -1
- package/dist-skill/docs/generated/core.md +26 -0
- package/dist-skill/docs/generated/runtime-names.md +1 -1
- package/dist-skill/docs/guides/inspection-bundles.md +1 -1
- package/dist-skill/library/README.md +9 -12
- package/dist-skill/library/{forgecad-make-a-model → forgecad-build-model}/SKILL.md +16 -6
- package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/SKILL.md +4 -4
- package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/references/master-prompt.md +1 -1
- package/dist-skill/library/{forgecad-model-grader → forgecad-grade-model}/SKILL.md +6 -4
- package/dist-skill/library/forgecad-grade-model/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-visual-spec → forgecad-image-prompt}/SKILL.md +5 -5
- package/dist-skill/library/forgecad-image-prompt/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/SKILL.md +4 -4
- package/dist-skill/library/{forgecad-project → forgecad-project-sync}/SKILL.md +3 -3
- package/dist-skill/library/{forgecad-3d-reconstruction → forgecad-reconstruct-cad-file}/SKILL.md +5 -5
- package/dist-skill/library/forgecad-reconstruct-cad-file/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/SKILL.md +10 -10
- package/dist-skill/library/forgecad-reconstruct-from-images/agents/openai.yaml +4 -0
- package/dist-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/SKILL.md +4 -4
- package/dist-skill/website/skills/{forgecad-make-a-model.md → forgecad-build-model.md} +18 -8
- package/dist-skill/website/skills/{forgecad-spec-by-walking-through-it.md → forgecad-design-spec.md} +6 -6
- package/dist-skill/website/skills/{forgecad-model-grader.md → forgecad-grade-model.md} +8 -6
- package/{dist/docs-raw/skills/forgecad-visual-spec.md → dist-skill/website/skills/forgecad-image-prompt.md} +7 -7
- package/dist-skill/website/skills/{forgecad-render-inspect.md → forgecad-inspect-model.md} +6 -6
- package/{dist/docs-raw/skills/forgecad-project.md → dist-skill/website/skills/forgecad-project-sync.md} +5 -5
- package/dist-skill/website/skills/{forgecad-3d-reconstruction.md → forgecad-reconstruct-cad-file.md} +7 -7
- package/dist-skill/website/skills/{forgecad-image-replicator.md → forgecad-reconstruct-from-images.md} +12 -12
- package/dist-skill/website/skills/{forgecad-mujoco-verify.md → forgecad-verify-mujoco.md} +6 -6
- package/dist-skill/website/skills/index.md +9 -12
- package/examples/api/texture-projection.forge.js +75 -0
- package/examples/assets/uv-grid.png +0 -0
- package/package.json +1 -1
- package/dist/docs-raw/skills/forgecad-blockout-model.md +0 -49
- package/dist/docs-raw/skills/forgecad-component-model.md +0 -53
- package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +0 -60
- package/dist-skill/library/forgecad-3d-reconstruction/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +0 -42
- package/dist-skill/library/forgecad-component-model/SKILL.md +0 -46
- package/dist-skill/library/forgecad-image-replicator/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-model-grader/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +0 -48
- package/dist-skill/library/forgecad-reconstruction-benchmark/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-visual-spec/agents/openai.yaml +0 -4
- package/dist-skill/website/skills/forgecad-blockout-model.md +0 -49
- package/dist-skill/website/skills/forgecad-component-model.md +0 -53
- package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +0 -60
- /package/dist/assets/{landing-proof-driven-DiGqdtWa.js → landing-proof-driven-BxZZh5r5.js} +0 -0
- /package/dist-skill/library/{forgecad-spec-by-walking-through-it → forgecad-design-spec}/references/default-profiles.md +0 -0
- /package/dist-skill/library/{forgecad-render-inspect → forgecad-inspect-model}/summarize_manifest.py +0 -0
- /package/dist-skill/library/{forgecad-image-replicator → forgecad-reconstruct-from-images}/scripts/compare_images.py +0 -0
- /package/dist-skill/library/{forgecad-mujoco-verify → forgecad-verify-mujoco}/scripts/mujoco_verify.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import { D as DoubleSide, a as Scene,
|
|
5
|
-
import { m as mergeViewportRenderSceneStates, v as validateJointOverrides, b as buildBaseJointValues, p as parseRenderSceneCliSpec, g as getSceneObjectTreePath } from "../jointPose-
|
|
4
|
+
import { D as DoubleSide, a as Scene, bH as BoxGeometry, cm as MeshStandardMaterial, a5 as BackSide, bd as PointLight, M as Mesh, ab as MeshBasicMaterial, c$ as localAabbPlaneRelation, i as Vector2, d0 as ShapeUtils, d1 as analyzePhysicalConnectivity, h as Vector3, a0 as Matrix4, d2 as Frustum, J as Box3, a1 as MathUtils, d3 as meshContactDataFor, d4 as AabbSpatialIndex, d5 as detectPhysicalContact, d6 as resolveThicknessInspectionOptions, R as Raycaster, d7 as thicknessColor, d8 as thicknessClass, bf as BufferAttribute, bQ as MeshBVH, b_ as acceleratedRaycast, d9 as requireFiniteNumber, da as requireIntegerAtLeast, db as requirePositiveFiniteNumber, c_ as initBackendForEvaluation, f as Color, bh as COMPARISON_COLORS, aB as resolveForgeRenderStyle, bX as getRenderStylePreset, az as setParamOverrides, bw as runScript, cK as scanProxyGridForBounds, dc as Group, bl as shapeToGeometry, bx as MeshPhysicalMaterial, bO as AdditiveBlending, c1 as scanMaterialShellColor, bV as descriptorToThreeTexture, bW as applyProjectedTexture, dd as createScanProxyGeometry, b3 as LineBasicMaterial, bZ as NormalBlending, by as LineSegments, P as PerspectiveCamera, cH as DEFAULT_VIEW_CONFIG, bD as worldAuthorPlaneToLocal, de as resolveSectionHatchMetrics, cQ as buildGeometryComparisonPointCloud, cO as triangleSoupFromMeshes, O as OrthographicCamera, l as ShaderMaterial, c7 as ZEBRA_STRIPE_FRAGMENT_SHADER, c8 as ZEBRA_STRIPE_VERTEX_SHADER, c2 as ZEBRA_STRIPE_SOFTNESS, c3 as ZEBRA_STRIPE_SCALE, c4 as ZEBRA_LIGHT_COLOR, c5 as ZEBRA_DARK_COLOR, c6 as ZEBRA_ACCENT_COLOR, bP as geometryWithVisibleVertexColors, df as intersectWithPlane, dg as setActiveBackend, W as WebGLRenderer, A as ACESFilmicToneMapping, d as SRGBColorSpace, dh as parseCameraCliSpec, di as PMREMGenerator, b6 as CanvasTexture, b7 as Object3D, b8 as FogExp2, b9 as Fog, ba as AmbientLight, be as DirectionalLight, bb as HemisphereLight, aW as findJointAnimationClip, q as Plane, cb as SURFACE_FIELD_FRAGMENT_SHADER, cc as SURFACE_FIELD_VERTEX_SHADER, ca as scanMaterialLayerStyles, c9 as SCAN_PROXY_LAYER_STYLES, aX as resolveJointAnimation, aY as resolveJointViewValues, b2 as BufferGeometry, dj as DEFAULT_ROUGHNESS_COLOR_SCALE, bR as makeColorScaleTexture, bS as colorScaleLUT, bT as makeInspectScalarUniforms, b$ as INSPECT_SCALAR_FRAGMENT_SHADER, c0 as INSPECT_SCALAR_VERTEX_SHADER, bg as heatPointsForSide, dk as analyzeCollisionIntersections, dl as serializeCollisionFinding, dm as summarizeThicknessSamples, dn as THICKNESS_COLORS, dp as DEFAULT_THICKNESS_COLOR_SCALE, bc as SpotLight, cq as CylinderGeometry, dq as TorusGeometry, ce as CatmullRomCurve3, cf as TubeGeometry, cU as resolveScalarSceneSampleBudget, dr as DEFAULT_INSPECT_ISOLINE_SPACING, ch as DEFAULT_COLORMAP, bm as buildComparisonHeatPatchGeometry, bn as EdgesGeometry, ds as SphereGeometry, dt as ConeGeometry, bi as comparisonHeatDepthTest, bj as comparisonHeatEdgeOpacity, bk as comparisonHeatPatchOpacity, cY as comparisonCandidateContextOpacity, du as DEFAULT_COMPARISON_CANDIDATE_OPACITY } from "../backendInit-6C0DLgH0.js";
|
|
5
|
+
import { m as mergeViewportRenderSceneStates, v as validateJointOverrides, b as buildBaseJointValues, p as parseRenderSceneCliSpec, g as getSceneObjectTreePath } from "../jointPose-B0blBj9A.js";
|
|
6
6
|
const CAD_MATERIAL_PROPS = {
|
|
7
7
|
color: 6003669,
|
|
8
8
|
metalness: 0.05,
|
|
@@ -1324,6 +1324,13 @@ function clampUnit(value) {
|
|
|
1324
1324
|
function cloneGeometryForFaceColors(geometry) {
|
|
1325
1325
|
return geometry.index ? geometry.toNonIndexed() : geometry.clone();
|
|
1326
1326
|
}
|
|
1327
|
+
function makeThicknessRaycastTarget(sourceGeometry, rayMaterial, jumpable) {
|
|
1328
|
+
const geometry = sourceGeometry.clone();
|
|
1329
|
+
geometry.boundsTree = new MeshBVH(geometry);
|
|
1330
|
+
const mesh = new Mesh(geometry, rayMaterial);
|
|
1331
|
+
mesh.raycast = acceleratedRaycast;
|
|
1332
|
+
return { mesh, jumpable, geometry };
|
|
1333
|
+
}
|
|
1327
1334
|
function geometryMaxDimension(geometry) {
|
|
1328
1335
|
geometry.computeBoundingBox();
|
|
1329
1336
|
const box = geometry.boundingBox;
|
|
@@ -1399,69 +1406,454 @@ function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}, context = {})
|
|
|
1399
1406
|
const warnings = [];
|
|
1400
1407
|
const rayMaterial = new MeshBasicMaterial({ side: DoubleSide });
|
|
1401
1408
|
const rayTargets = [
|
|
1402
|
-
|
|
1403
|
-
...connectedGeometries.map((connectedGeometry) => (
|
|
1404
|
-
mesh: new Mesh(connectedGeometry, rayMaterial),
|
|
1405
|
-
jumpable: true
|
|
1406
|
-
}))
|
|
1409
|
+
makeThicknessRaycastTarget(geometry, rayMaterial, false),
|
|
1410
|
+
...connectedGeometries.map((connectedGeometry) => makeThicknessRaycastTarget(connectedGeometry, rayMaterial, true))
|
|
1407
1411
|
];
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1412
|
+
try {
|
|
1413
|
+
const rayTargetMeshes = rayTargets.map((target) => target.mesh);
|
|
1414
|
+
const jumpableMeshes = new Set(rayTargets.filter((target) => target.jumpable).map((target) => target.mesh));
|
|
1415
|
+
const raycaster = new Raycaster();
|
|
1416
|
+
if (surfaceTriangles.length === 0) {
|
|
1417
|
+
warnings.push("No non-degenerate triangle surface was available for thickness sampling.");
|
|
1418
|
+
} else if (surfaceSamples.length < surfaceTriangles.length) {
|
|
1419
|
+
warnings.push(
|
|
1420
|
+
`Area sampling budget ${surfaceSamples.length} covers ${surfaceTriangles.length} surface triangles; increase --thickness-samples for denser analysis.`
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
const sampledTriangleIndexes = /* @__PURE__ */ new Set();
|
|
1424
|
+
for (const sample of surfaceSamples) {
|
|
1425
|
+
sampledTriangleIndexes.add(sample.triangle.index);
|
|
1426
|
+
const thickness = triangleThickness(
|
|
1427
|
+
raycaster,
|
|
1428
|
+
rayTargetMeshes,
|
|
1429
|
+
jumpableMeshes,
|
|
1430
|
+
sample.position,
|
|
1431
|
+
sample.normal,
|
|
1432
|
+
epsilon,
|
|
1433
|
+
far,
|
|
1434
|
+
options.contactTolerance
|
|
1435
|
+
);
|
|
1436
|
+
samples.push({ thickness, area: sample.area });
|
|
1437
|
+
const previous = triangleThicknessValues[sample.triangle.index];
|
|
1438
|
+
if (previous === void 0 || previous == null || thickness != null && thickness < previous) {
|
|
1439
|
+
triangleThicknessValues[sample.triangle.index] = thickness;
|
|
1440
|
+
}
|
|
1441
|
+
pointSamples.push({
|
|
1442
|
+
position: [sample.position.x, sample.position.y, sample.position.z],
|
|
1443
|
+
normal: [sample.normal.x, sample.normal.y, sample.normal.z],
|
|
1444
|
+
value: thickness,
|
|
1445
|
+
className: thicknessClass(thickness, options),
|
|
1446
|
+
color: thicknessColor(thickness, options),
|
|
1447
|
+
area: sample.area
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
for (let tri = 0; tri < triangleCount; tri += 1) {
|
|
1451
|
+
const color = thicknessColor(triangleThicknessValues[tri], options);
|
|
1452
|
+
const offset = tri * 3;
|
|
1453
|
+
for (let vertex = 0; vertex < 3; vertex += 1) {
|
|
1454
|
+
const colorOffset = (offset + vertex) * 3;
|
|
1455
|
+
colors[colorOffset] = color[0] / 255;
|
|
1456
|
+
colors[colorOffset + 1] = color[1] / 255;
|
|
1457
|
+
colors[colorOffset + 2] = color[2] / 255;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
geometry.setAttribute("color", new BufferAttribute(colors, 3));
|
|
1461
|
+
return {
|
|
1462
|
+
geometry,
|
|
1463
|
+
samples,
|
|
1464
|
+
pointSamples,
|
|
1465
|
+
triangleCount,
|
|
1466
|
+
sampledTriangleCount: sampledTriangleIndexes.size,
|
|
1467
|
+
sampleStride: Math.max(1, Math.ceil(Math.max(1, surfaceTriangles.length) / Math.max(1, sampledTriangleIndexes.size))),
|
|
1468
|
+
warnings
|
|
1469
|
+
};
|
|
1470
|
+
} finally {
|
|
1471
|
+
rayTargets.forEach((target) => target.geometry.dispose());
|
|
1472
|
+
rayMaterial.dispose();
|
|
1417
1473
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1474
|
+
}
|
|
1475
|
+
const DEFAULT_VERTEX_CAP = 2e6;
|
|
1476
|
+
const DEFAULT_TARGET_EDGE_SPACING_FACTOR = 2;
|
|
1477
|
+
const DEFAULT_K = 8;
|
|
1478
|
+
const DEFAULT_GATE_DOT = 0.3;
|
|
1479
|
+
const SCATTER_CELL_SPACING_FACTOR = 1.5;
|
|
1480
|
+
const SCATTER_RADIUS_SPACING_FACTOR = 3;
|
|
1481
|
+
const IDW_DISTANCE_FLOOR = 1e-9;
|
|
1482
|
+
const MAX_SUBDIVISION_PASSES = 24;
|
|
1483
|
+
function subVec(a, b) {
|
|
1484
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
1485
|
+
}
|
|
1486
|
+
function crossVec(a, b) {
|
|
1487
|
+
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
1488
|
+
}
|
|
1489
|
+
function lenVec(a) {
|
|
1490
|
+
return Math.hypot(a[0], a[1], a[2]);
|
|
1491
|
+
}
|
|
1492
|
+
function getVert(positions, i) {
|
|
1493
|
+
const o = i * 3;
|
|
1494
|
+
return [positions[o], positions[o + 1], positions[o + 2]];
|
|
1495
|
+
}
|
|
1496
|
+
function triArea(positions, a, b, c) {
|
|
1497
|
+
const va = getVert(positions, a);
|
|
1498
|
+
return 0.5 * lenVec(crossVec(subVec(getVert(positions, b), va), subVec(getVert(positions, c), va)));
|
|
1499
|
+
}
|
|
1500
|
+
function triNormal(positions, a, b, c) {
|
|
1501
|
+
const va = getVert(positions, a);
|
|
1502
|
+
const n = crossVec(subVec(getVert(positions, b), va), subVec(getVert(positions, c), va));
|
|
1503
|
+
const l = lenVec(n) || 1;
|
|
1504
|
+
return [n[0] / l, n[1] / l, n[2] / l];
|
|
1505
|
+
}
|
|
1506
|
+
function edgeLen(positions, a, b) {
|
|
1507
|
+
return lenVec(subVec(getVert(positions, a), getVert(positions, b)));
|
|
1508
|
+
}
|
|
1509
|
+
function maxEdge(positions, a, b, c) {
|
|
1510
|
+
return Math.max(edgeLen(positions, a, b), edgeLen(positions, b, c), edgeLen(positions, c, a));
|
|
1511
|
+
}
|
|
1512
|
+
function weld(positions) {
|
|
1513
|
+
if (positions.length % 9 !== 0) {
|
|
1514
|
+
throw new Error(
|
|
1515
|
+
`weld: positions length must be a multiple of 9 (9 floats per triangle), got ${positions.length}`
|
|
1430
1516
|
);
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1517
|
+
}
|
|
1518
|
+
positions.length / 3;
|
|
1519
|
+
let minX = Infinity;
|
|
1520
|
+
let minY = Infinity;
|
|
1521
|
+
let minZ = Infinity;
|
|
1522
|
+
let maxX = -Infinity;
|
|
1523
|
+
let maxY = -Infinity;
|
|
1524
|
+
let maxZ = -Infinity;
|
|
1525
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
1526
|
+
const x = positions[i];
|
|
1527
|
+
const y = positions[i + 1];
|
|
1528
|
+
const z = positions[i + 2];
|
|
1529
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
|
|
1530
|
+
throw new Error(`weld: non-finite vertex position at float index ${i}`);
|
|
1531
|
+
}
|
|
1532
|
+
if (x < minX) minX = x;
|
|
1533
|
+
if (y < minY) minY = y;
|
|
1534
|
+
if (z < minZ) minZ = z;
|
|
1535
|
+
if (x > maxX) maxX = x;
|
|
1536
|
+
if (y > maxY) maxY = y;
|
|
1537
|
+
if (z > maxZ) maxZ = z;
|
|
1538
|
+
}
|
|
1539
|
+
const diagonal = Math.hypot(maxX - minX, maxY - minY, maxZ - minZ);
|
|
1540
|
+
const tolerance = Math.max(diagonal * 1e-5, Number.EPSILON);
|
|
1541
|
+
const remap = /* @__PURE__ */ new Map();
|
|
1542
|
+
const out = [];
|
|
1543
|
+
const tris = [];
|
|
1544
|
+
const inv = 1 / tolerance;
|
|
1545
|
+
const quantKey = (i) => {
|
|
1546
|
+
const qx = Math.round(positions[i] * inv);
|
|
1547
|
+
const qy = Math.round(positions[i + 1] * inv);
|
|
1548
|
+
const qz = Math.round(positions[i + 2] * inv);
|
|
1549
|
+
return `${qx},${qy},${qz}`;
|
|
1550
|
+
};
|
|
1551
|
+
for (let tri = 0; tri < positions.length; tri += 9) {
|
|
1552
|
+
const idx = [];
|
|
1553
|
+
for (let corner = 0; corner < 3; corner += 1) {
|
|
1554
|
+
const o = tri + corner * 3;
|
|
1555
|
+
const key = quantKey(o);
|
|
1556
|
+
let vi = remap.get(key);
|
|
1557
|
+
if (vi === void 0) {
|
|
1558
|
+
vi = out.length / 3;
|
|
1559
|
+
out.push(positions[o], positions[o + 1], positions[o + 2]);
|
|
1560
|
+
remap.set(key, vi);
|
|
1561
|
+
}
|
|
1562
|
+
idx.push(vi);
|
|
1563
|
+
}
|
|
1564
|
+
if (idx[0] !== idx[1] && idx[1] !== idx[2] && idx[2] !== idx[0]) {
|
|
1565
|
+
tris.push(idx[0], idx[1], idx[2]);
|
|
1435
1566
|
}
|
|
1436
|
-
pointSamples.push({
|
|
1437
|
-
position: [sample.position.x, sample.position.y, sample.position.z],
|
|
1438
|
-
normal: [sample.normal.x, sample.normal.y, sample.normal.z],
|
|
1439
|
-
value: thickness,
|
|
1440
|
-
className: thicknessClass(thickness, options),
|
|
1441
|
-
color: thicknessColor(thickness, options),
|
|
1442
|
-
area: sample.area
|
|
1443
|
-
});
|
|
1444
1567
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1568
|
+
const distinctVertexCount = out.length / 3;
|
|
1569
|
+
const degenerate = distinctVertexCount < 3 || tris.length === 0;
|
|
1570
|
+
return { positions: out, tris, degenerate };
|
|
1571
|
+
}
|
|
1572
|
+
function adaptiveSubdivide(mesh, targetEdge, vertexCap) {
|
|
1573
|
+
requirePositiveFiniteNumber(targetEdge, "adaptiveSubdivide targetEdge");
|
|
1574
|
+
requireIntegerAtLeast(vertexCap, "adaptiveSubdivide vertexCap", 3);
|
|
1575
|
+
const positions = mesh.positions.slice();
|
|
1576
|
+
let tris = mesh.tris.slice();
|
|
1577
|
+
let capped = false;
|
|
1578
|
+
const keyOf = (a, b) => a < b ? `${a}_${b}` : `${b}_${a}`;
|
|
1579
|
+
for (let pass = 0; pass < MAX_SUBDIVISION_PASSES; pass += 1) {
|
|
1580
|
+
const next = [];
|
|
1581
|
+
const midCache = /* @__PURE__ */ new Map();
|
|
1582
|
+
let changed = false;
|
|
1583
|
+
const getMid = (a, b) => {
|
|
1584
|
+
const key = keyOf(a, b);
|
|
1585
|
+
let mi = midCache.get(key);
|
|
1586
|
+
if (mi === void 0) {
|
|
1587
|
+
mi = positions.length / 3;
|
|
1588
|
+
const ao = a * 3;
|
|
1589
|
+
const bo = b * 3;
|
|
1590
|
+
positions.push(
|
|
1591
|
+
(positions[ao] + positions[bo]) / 2,
|
|
1592
|
+
(positions[ao + 1] + positions[bo + 1]) / 2,
|
|
1593
|
+
(positions[ao + 2] + positions[bo + 2]) / 2
|
|
1594
|
+
);
|
|
1595
|
+
midCache.set(key, mi);
|
|
1596
|
+
}
|
|
1597
|
+
return mi;
|
|
1598
|
+
};
|
|
1599
|
+
for (let t = 0; t < tris.length; t += 3) {
|
|
1600
|
+
const a = tris[t];
|
|
1601
|
+
const b = tris[t + 1];
|
|
1602
|
+
const c = tris[t + 2];
|
|
1603
|
+
const vertexCount = positions.length / 3;
|
|
1604
|
+
if (maxEdge(positions, a, b, c) > targetEdge && vertexCount < vertexCap) {
|
|
1605
|
+
const ab = getMid(a, b);
|
|
1606
|
+
const bc = getMid(b, c);
|
|
1607
|
+
const ca = getMid(c, a);
|
|
1608
|
+
next.push(a, ab, ca, ab, b, bc, ca, bc, c, ab, bc, ca);
|
|
1609
|
+
changed = true;
|
|
1610
|
+
} else {
|
|
1611
|
+
if (positions.length / 3 >= vertexCap) capped = true;
|
|
1612
|
+
next.push(a, b, c);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
tris = next;
|
|
1616
|
+
if (!changed) break;
|
|
1617
|
+
}
|
|
1618
|
+
return { positions, tris, capped };
|
|
1619
|
+
}
|
|
1620
|
+
function vertexNormals(mesh) {
|
|
1621
|
+
const vertexCount = mesh.positions.length / 3;
|
|
1622
|
+
const acc = new Float32Array(vertexCount * 3);
|
|
1623
|
+
for (let t = 0; t < mesh.tris.length; t += 3) {
|
|
1624
|
+
const a = mesh.tris[t];
|
|
1625
|
+
const b = mesh.tris[t + 1];
|
|
1626
|
+
const c = mesh.tris[t + 2];
|
|
1627
|
+
const n = triNormal(mesh.positions, a, b, c);
|
|
1628
|
+
const area = triArea(mesh.positions, a, b, c);
|
|
1629
|
+
const nx = n[0] * area;
|
|
1630
|
+
const ny = n[1] * area;
|
|
1631
|
+
const nz = n[2] * area;
|
|
1632
|
+
for (const vi of [a, b, c]) {
|
|
1633
|
+
acc[vi * 3] += nx;
|
|
1634
|
+
acc[vi * 3 + 1] += ny;
|
|
1635
|
+
acc[vi * 3 + 2] += nz;
|
|
1453
1636
|
}
|
|
1454
1637
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1638
|
+
for (let i = 0; i < vertexCount; i += 1) {
|
|
1639
|
+
const o = i * 3;
|
|
1640
|
+
const l = Math.hypot(acc[o], acc[o + 1], acc[o + 2]) || 1;
|
|
1641
|
+
acc[o] /= l;
|
|
1642
|
+
acc[o + 1] /= l;
|
|
1643
|
+
acc[o + 2] /= l;
|
|
1644
|
+
}
|
|
1645
|
+
return acc;
|
|
1646
|
+
}
|
|
1647
|
+
function buildSampleGrid(positions, cell) {
|
|
1648
|
+
const grid = /* @__PURE__ */ new Map();
|
|
1649
|
+
const sampleCount = positions.length / 3;
|
|
1650
|
+
for (let i = 0; i < sampleCount; i += 1) {
|
|
1651
|
+
const o = i * 3;
|
|
1652
|
+
const key = `${Math.floor(positions[o] / cell)},${Math.floor(positions[o + 1] / cell)},${Math.floor(positions[o + 2] / cell)}`;
|
|
1653
|
+
let arr = grid.get(key);
|
|
1654
|
+
if (!arr) {
|
|
1655
|
+
arr = [];
|
|
1656
|
+
grid.set(key, arr);
|
|
1657
|
+
}
|
|
1658
|
+
arr.push(i);
|
|
1659
|
+
}
|
|
1660
|
+
return { grid, cell };
|
|
1661
|
+
}
|
|
1662
|
+
function gatherSamples(sampleGrid, p, rings, out) {
|
|
1663
|
+
out.length = 0;
|
|
1664
|
+
const { cell, grid } = sampleGrid;
|
|
1665
|
+
const bx = Math.floor(p[0] / cell);
|
|
1666
|
+
const by = Math.floor(p[1] / cell);
|
|
1667
|
+
const bz = Math.floor(p[2] / cell);
|
|
1668
|
+
for (let dx = -rings; dx <= rings; dx += 1) {
|
|
1669
|
+
for (let dy = -rings; dy <= rings; dy += 1) {
|
|
1670
|
+
for (let dz = -rings; dz <= rings; dz += 1) {
|
|
1671
|
+
const arr = grid.get(`${bx + dx},${by + dy},${bz + dz}`);
|
|
1672
|
+
if (arr) for (const i of arr) out.push(i);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
function scatterToVertices(positions, vnormals, samples, spacing, k, gateDot) {
|
|
1678
|
+
const cell = spacing * SCATTER_CELL_SPACING_FACTOR;
|
|
1679
|
+
const sampleGrid = buildSampleGrid(samples.positions, cell);
|
|
1680
|
+
const radius = spacing * SCATTER_RADIUS_SPACING_FACTOR;
|
|
1681
|
+
const radius2 = radius * radius;
|
|
1682
|
+
const vertexCount = positions.length / 3;
|
|
1683
|
+
const values = new Float32Array(vertexCount);
|
|
1684
|
+
let holeCount = 0;
|
|
1685
|
+
const candidates = [];
|
|
1686
|
+
const scratchP = [0, 0, 0];
|
|
1687
|
+
const bestD2 = new Float64Array(k);
|
|
1688
|
+
const bestVal = new Float64Array(k);
|
|
1689
|
+
for (let vi = 0; vi < vertexCount; vi += 1) {
|
|
1690
|
+
const pi = vi * 3;
|
|
1691
|
+
const px = positions[pi];
|
|
1692
|
+
const py = positions[pi + 1];
|
|
1693
|
+
const pz = positions[pi + 2];
|
|
1694
|
+
const nx = vnormals[pi];
|
|
1695
|
+
const ny = vnormals[pi + 1];
|
|
1696
|
+
const nz = vnormals[pi + 2];
|
|
1697
|
+
scratchP[0] = px;
|
|
1698
|
+
scratchP[1] = py;
|
|
1699
|
+
scratchP[2] = pz;
|
|
1700
|
+
gatherSamples(sampleGrid, scratchP, 3, candidates);
|
|
1701
|
+
let count = 0;
|
|
1702
|
+
let worst = 0;
|
|
1703
|
+
let worstD2 = -Infinity;
|
|
1704
|
+
for (let c = 0; c < candidates.length; c += 1) {
|
|
1705
|
+
const si = candidates[c];
|
|
1706
|
+
const so = si * 3;
|
|
1707
|
+
const dx = samples.positions[so] - px;
|
|
1708
|
+
const dy = samples.positions[so + 1] - py;
|
|
1709
|
+
const dz = samples.positions[so + 2] - pz;
|
|
1710
|
+
const d2 = dx * dx + dy * dy + dz * dz;
|
|
1711
|
+
if (d2 > radius2) continue;
|
|
1712
|
+
if (samples.normals[so] * nx + samples.normals[so + 1] * ny + samples.normals[so + 2] * nz < gateDot) continue;
|
|
1713
|
+
const val = samples.values[si];
|
|
1714
|
+
if (count < k) {
|
|
1715
|
+
bestD2[count] = d2;
|
|
1716
|
+
bestVal[count] = val;
|
|
1717
|
+
if (d2 > worstD2) {
|
|
1718
|
+
worstD2 = d2;
|
|
1719
|
+
worst = count;
|
|
1720
|
+
}
|
|
1721
|
+
count += 1;
|
|
1722
|
+
} else if (d2 < worstD2) {
|
|
1723
|
+
bestD2[worst] = d2;
|
|
1724
|
+
bestVal[worst] = val;
|
|
1725
|
+
worstD2 = -Infinity;
|
|
1726
|
+
for (let t = 0; t < k; t += 1) {
|
|
1727
|
+
if (bestD2[t] > worstD2) {
|
|
1728
|
+
worstD2 = bestD2[t];
|
|
1729
|
+
worst = t;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
if (count === 0) {
|
|
1735
|
+
holeCount += 1;
|
|
1736
|
+
values[vi] = NaN;
|
|
1737
|
+
continue;
|
|
1738
|
+
}
|
|
1739
|
+
let w = 0;
|
|
1740
|
+
let acc = 0;
|
|
1741
|
+
for (let t = 0; t < count; t += 1) {
|
|
1742
|
+
const ww = 1 / Math.max(bestD2[t], IDW_DISTANCE_FLOOR);
|
|
1743
|
+
w += ww;
|
|
1744
|
+
acc += ww * bestVal[t];
|
|
1745
|
+
}
|
|
1746
|
+
values[vi] = acc / w;
|
|
1747
|
+
}
|
|
1748
|
+
return { values, holeCount };
|
|
1749
|
+
}
|
|
1750
|
+
function backfillHoles(values, mesh) {
|
|
1751
|
+
const vertexCount = values.length;
|
|
1752
|
+
const adjacency = Array.from({ length: vertexCount }, () => []);
|
|
1753
|
+
for (let t = 0; t < mesh.tris.length; t += 3) {
|
|
1754
|
+
const a = mesh.tris[t];
|
|
1755
|
+
const b = mesh.tris[t + 1];
|
|
1756
|
+
const c = mesh.tris[t + 2];
|
|
1757
|
+
adjacency[a].push(b, c);
|
|
1758
|
+
adjacency[b].push(a, c);
|
|
1759
|
+
adjacency[c].push(a, b);
|
|
1760
|
+
}
|
|
1761
|
+
let frontier = [];
|
|
1762
|
+
for (let i = 0; i < vertexCount; i += 1) {
|
|
1763
|
+
if (Number.isFinite(values[i])) frontier.push(i);
|
|
1764
|
+
}
|
|
1765
|
+
if (frontier.length === 0) {
|
|
1766
|
+
values.fill(0);
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
while (frontier.length > 0) {
|
|
1770
|
+
const nextFrontier = [];
|
|
1771
|
+
for (const v of frontier) {
|
|
1772
|
+
const val = values[v];
|
|
1773
|
+
for (const n of adjacency[v]) {
|
|
1774
|
+
if (!Number.isFinite(values[n])) {
|
|
1775
|
+
values[n] = val;
|
|
1776
|
+
nextFrontier.push(n);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
frontier = nextFrontier;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
function reconstructSurfaceScalarField(trianglePositions, samples, options = {}) {
|
|
1784
|
+
if (!(trianglePositions instanceof Float32Array)) {
|
|
1785
|
+
throw new Error("reconstructSurfaceScalarField: trianglePositions must be a Float32Array");
|
|
1786
|
+
}
|
|
1787
|
+
if (trianglePositions.length === 0 || trianglePositions.length % 9 !== 0) {
|
|
1788
|
+
throw new Error(
|
|
1789
|
+
`reconstructSurfaceScalarField: trianglePositions length must be a positive multiple of 9, got ${trianglePositions.length}`
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
if (!(samples.positions instanceof Float32Array) || !(samples.values instanceof Float32Array) || !(samples.normals instanceof Float32Array)) {
|
|
1793
|
+
throw new Error("reconstructSurfaceScalarField: samples positions/values/normals must be Float32Arrays");
|
|
1794
|
+
}
|
|
1795
|
+
const sampleCount = samples.values.length;
|
|
1796
|
+
if (sampleCount === 0) {
|
|
1797
|
+
throw new Error("reconstructSurfaceScalarField: samples must be non-empty");
|
|
1798
|
+
}
|
|
1799
|
+
if (samples.positions.length !== sampleCount * 3) {
|
|
1800
|
+
throw new Error(
|
|
1801
|
+
`reconstructSurfaceScalarField: samples.positions length ${samples.positions.length} must equal values*3 (${sampleCount * 3})`
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
if (samples.normals.length !== sampleCount * 3) {
|
|
1805
|
+
throw new Error(
|
|
1806
|
+
`reconstructSurfaceScalarField: samples.normals length ${samples.normals.length} must equal values*3 (${sampleCount * 3})`
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
for (let i = 0; i < sampleCount; i += 1) {
|
|
1810
|
+
requireFiniteNumber(samples.values[i], `samples.values[${i}]`);
|
|
1811
|
+
}
|
|
1812
|
+
const vertexCap = options.vertexCap === void 0 ? DEFAULT_VERTEX_CAP : requireIntegerAtLeast(options.vertexCap, "vertexCap", 3);
|
|
1813
|
+
const targetEdgeSpacingFactor = options.targetEdgeSpacingFactor === void 0 ? DEFAULT_TARGET_EDGE_SPACING_FACTOR : requirePositiveFiniteNumber(options.targetEdgeSpacingFactor, "targetEdgeSpacingFactor");
|
|
1814
|
+
const k = options.k === void 0 ? DEFAULT_K : requireIntegerAtLeast(options.k, "k", 1);
|
|
1815
|
+
const gateDot = options.gateDot === void 0 ? DEFAULT_GATE_DOT : requireFiniteNumber(options.gateDot, "gateDot");
|
|
1816
|
+
const welded = weld(trianglePositions);
|
|
1817
|
+
let surfaceArea = 0;
|
|
1818
|
+
for (let t = 0; t < welded.tris.length; t += 3) {
|
|
1819
|
+
surfaceArea += triArea(welded.positions, welded.tris[t], welded.tris[t + 1], welded.tris[t + 2]);
|
|
1820
|
+
}
|
|
1821
|
+
const sampleSpacing = surfaceArea > 0 ? Math.sqrt(surfaceArea / sampleCount) : 0;
|
|
1822
|
+
let subdivided;
|
|
1823
|
+
if (welded.degenerate || !(sampleSpacing > 0)) {
|
|
1824
|
+
subdivided = { positions: welded.positions, tris: welded.tris, capped: false };
|
|
1825
|
+
} else {
|
|
1826
|
+
const targetEdge = targetEdgeSpacingFactor * sampleSpacing;
|
|
1827
|
+
subdivided = adaptiveSubdivide(welded, targetEdge, vertexCap);
|
|
1828
|
+
}
|
|
1829
|
+
const normals = vertexNormals(subdivided);
|
|
1830
|
+
const effectiveSpacing = sampleSpacing > 0 ? sampleSpacing : 1;
|
|
1831
|
+
const scatter = scatterToVertices(subdivided.positions, normals, samples, effectiveSpacing, k, gateDot);
|
|
1832
|
+
let valueMin = Infinity;
|
|
1833
|
+
let valueMax = -Infinity;
|
|
1834
|
+
for (let i = 0; i < scatter.values.length; i += 1) {
|
|
1835
|
+
const v = scatter.values[i];
|
|
1836
|
+
if (!Number.isFinite(v)) continue;
|
|
1837
|
+
if (v < valueMin) valueMin = v;
|
|
1838
|
+
if (v > valueMax) valueMax = v;
|
|
1839
|
+
}
|
|
1840
|
+
if (!Number.isFinite(valueMin) || !Number.isFinite(valueMax)) {
|
|
1841
|
+
valueMin = 0;
|
|
1842
|
+
valueMax = 0;
|
|
1843
|
+
}
|
|
1844
|
+
backfillHoles(scatter.values, subdivided);
|
|
1845
|
+
const vertexCount = subdivided.positions.length / 3;
|
|
1457
1846
|
return {
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1847
|
+
positions: Float32Array.from(subdivided.positions),
|
|
1848
|
+
normals,
|
|
1849
|
+
index: Uint32Array.from(subdivided.tris),
|
|
1850
|
+
values: scatter.values,
|
|
1851
|
+
valueMin,
|
|
1852
|
+
valueMax,
|
|
1853
|
+
capped: subdivided.capped,
|
|
1854
|
+
degenerate: welded.degenerate,
|
|
1855
|
+
holeCount: scatter.holeCount,
|
|
1856
|
+
vertexCount
|
|
1465
1857
|
};
|
|
1466
1858
|
}
|
|
1467
1859
|
const DEFAULT_ROUGHNESS_INSPECTION_OPTIONS = {
|
|
@@ -2001,6 +2393,16 @@ function createCaptureDebugLogger(enabled) {
|
|
|
2001
2393
|
console.info(`[forge-capture:debug] +${sinceStart}ms Δ${delta}ms ${phase}${detailText}`);
|
|
2002
2394
|
};
|
|
2003
2395
|
}
|
|
2396
|
+
function setInspectProfileEnabled(enabled) {
|
|
2397
|
+
window.__forgeInspectProfile = enabled === true;
|
|
2398
|
+
}
|
|
2399
|
+
function inspectProfileEnabled() {
|
|
2400
|
+
return window.__forgeInspectProfile === true;
|
|
2401
|
+
}
|
|
2402
|
+
function inspectProfileLog(phase, detail) {
|
|
2403
|
+
if (!inspectProfileEnabled()) return;
|
|
2404
|
+
console.info(`[forge-inspect-profile] ${phase} ${JSON.stringify(detail)}`);
|
|
2405
|
+
}
|
|
2004
2406
|
class EmptyInspectionShape {
|
|
2005
2407
|
intersect() {
|
|
2006
2408
|
return {
|
|
@@ -3675,28 +4077,100 @@ function bboxFromGeometry(geometry) {
|
|
|
3675
4077
|
max: bbox ? [bbox.max.x, bbox.max.y, bbox.max.z] : [0, 0, 0]
|
|
3676
4078
|
};
|
|
3677
4079
|
}
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
samples.
|
|
4080
|
+
const SCALAR_SURFACE_VERTEX_CAP = 2e6;
|
|
4081
|
+
const SCALAR_SURFACE_TARGET_EDGE_SPACING_FACTOR = 8;
|
|
4082
|
+
function scalarSamplesFromPointSamples(samples) {
|
|
4083
|
+
const finite = samples.filter((sample) => sample.value != null && Number.isFinite(sample.value));
|
|
4084
|
+
const positions = new Float32Array(finite.length * 3);
|
|
4085
|
+
const values = new Float32Array(finite.length);
|
|
4086
|
+
const normals = new Float32Array(finite.length * 3);
|
|
4087
|
+
finite.forEach((sample, index) => {
|
|
3682
4088
|
const base = index * 3;
|
|
3683
|
-
positions[base] = sample.position[0]
|
|
3684
|
-
positions[base + 1] = sample.position[1]
|
|
3685
|
-
positions[base + 2] = sample.position[2]
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
4089
|
+
positions[base] = sample.position[0];
|
|
4090
|
+
positions[base + 1] = sample.position[1];
|
|
4091
|
+
positions[base + 2] = sample.position[2];
|
|
4092
|
+
normals[base] = sample.normal[0];
|
|
4093
|
+
normals[base + 1] = sample.normal[1];
|
|
4094
|
+
normals[base + 2] = sample.normal[2];
|
|
4095
|
+
values[index] = sample.value;
|
|
4096
|
+
});
|
|
4097
|
+
return { positions, values, normals };
|
|
4098
|
+
}
|
|
4099
|
+
const DEFAULT_SCALAR_FIELD_PARAMS = {
|
|
4100
|
+
quantizeBands: 0,
|
|
4101
|
+
isoEnabled: false,
|
|
4102
|
+
isoSpacing: 0,
|
|
4103
|
+
isoLineWidthPx: 1.5,
|
|
4104
|
+
criticalEnabled: false,
|
|
4105
|
+
criticalThreshold: 0,
|
|
4106
|
+
shadingEnabled: true
|
|
4107
|
+
};
|
|
4108
|
+
function mergeScalarFieldParams(override) {
|
|
4109
|
+
if (!override) return DEFAULT_SCALAR_FIELD_PARAMS;
|
|
4110
|
+
const merged = { ...DEFAULT_SCALAR_FIELD_PARAMS };
|
|
4111
|
+
if (override.quantizeBands !== void 0) {
|
|
4112
|
+
const bands = override.quantizeBands;
|
|
4113
|
+
if (!Number.isInteger(bands) || bands < 0) {
|
|
4114
|
+
throw new RangeError(`Heatmap band count must be a non-negative integer, got ${bands}.`);
|
|
3695
4115
|
}
|
|
4116
|
+
merged.quantizeBands = bands;
|
|
4117
|
+
}
|
|
4118
|
+
if (override.isoEnabled !== void 0) {
|
|
4119
|
+
merged.isoEnabled = override.isoEnabled;
|
|
4120
|
+
}
|
|
4121
|
+
if (merged.isoEnabled && merged.isoSpacing <= 0) {
|
|
4122
|
+
merged.isoSpacing = DEFAULT_INSPECT_ISOLINE_SPACING;
|
|
4123
|
+
}
|
|
4124
|
+
if (override.isoLineWidthPx !== void 0) {
|
|
4125
|
+
const width = override.isoLineWidthPx;
|
|
4126
|
+
if (!Number.isFinite(width) || width <= 0) {
|
|
4127
|
+
throw new RangeError(`Heatmap isoline width must be a finite positive number of pixels, got ${width}.`);
|
|
4128
|
+
}
|
|
4129
|
+
merged.isoLineWidthPx = width;
|
|
4130
|
+
}
|
|
4131
|
+
return merged;
|
|
4132
|
+
}
|
|
4133
|
+
function buildScalarSurfaceOverlay(renderable, trianglePositions, samples, domain, fieldParams) {
|
|
4134
|
+
const started = performance.now();
|
|
4135
|
+
const scalarSamples = scalarSamplesFromPointSamples(samples);
|
|
4136
|
+
if (scalarSamples.values.length === 0) return null;
|
|
4137
|
+
const reconstructStarted = performance.now();
|
|
4138
|
+
const field = reconstructSurfaceScalarField(trianglePositions, scalarSamples, {
|
|
4139
|
+
vertexCap: SCALAR_SURFACE_VERTEX_CAP,
|
|
4140
|
+
targetEdgeSpacingFactor: SCALAR_SURFACE_TARGET_EDGE_SPACING_FACTOR
|
|
4141
|
+
});
|
|
4142
|
+
const reconstructMs = performance.now() - reconstructStarted;
|
|
4143
|
+
inspectProfileLog("scalar-overlay", {
|
|
4144
|
+
object: renderable.name,
|
|
4145
|
+
samples: scalarSamples.values.length,
|
|
4146
|
+
inputTriangles: trianglePositions.length / 9,
|
|
4147
|
+
vertices: field.vertexCount,
|
|
4148
|
+
capped: field.capped,
|
|
4149
|
+
holes: field.holeCount,
|
|
4150
|
+
reconstructMs: Number(reconstructMs.toFixed(1)),
|
|
4151
|
+
totalMs: Number((performance.now() - started).toFixed(1))
|
|
3696
4152
|
});
|
|
3697
|
-
|
|
4153
|
+
const colorScale = domain ? { colormap: DEFAULT_COLORMAP, domainMin: domain.min, domainMax: domain.max } : {
|
|
4154
|
+
colormap: DEFAULT_COLORMAP,
|
|
4155
|
+
domainMin: field.valueMin,
|
|
4156
|
+
domainMax: field.valueMax > field.valueMin ? field.valueMax : field.valueMin + 1
|
|
4157
|
+
};
|
|
4158
|
+
return {
|
|
4159
|
+
overlay: {
|
|
4160
|
+
renderable,
|
|
4161
|
+
positions: field.positions,
|
|
4162
|
+
normals: field.normals,
|
|
4163
|
+
index: field.index,
|
|
4164
|
+
aValue: field.values,
|
|
4165
|
+
colorScale,
|
|
4166
|
+
fieldParams
|
|
4167
|
+
},
|
|
4168
|
+
capped: field.capped,
|
|
4169
|
+
holeCount: field.holeCount
|
|
4170
|
+
};
|
|
3698
4171
|
}
|
|
3699
|
-
function
|
|
4172
|
+
function renderScalarSurfaceOverlays(session, overlays) {
|
|
4173
|
+
const started = performance.now();
|
|
3700
4174
|
const r = getRenderer(session.size, session.pixelRatio);
|
|
3701
4175
|
const overlayById = new Map(overlays.map((overlay) => [overlay.renderable.id, overlay]));
|
|
3702
4176
|
const replacements = session.renderables.map((renderable) => {
|
|
@@ -3713,49 +4187,77 @@ function renderScalarPointOverlays(session, overlays) {
|
|
|
3713
4187
|
ghostMaterial.toneMapped = false;
|
|
3714
4188
|
renderable.solid.material = ghostMaterial;
|
|
3715
4189
|
const overlay = overlayById.get(renderable.id);
|
|
3716
|
-
if (!overlay || overlay.positions.length === 0)
|
|
3717
|
-
return { renderable, previousMaterial, ghostMaterial,
|
|
4190
|
+
if (!overlay || overlay.positions.length === 0) {
|
|
4191
|
+
return { renderable, previousMaterial, ghostMaterial, mesh: null, geometry: null, material: null, lut: null };
|
|
4192
|
+
}
|
|
3718
4193
|
const geometry = new BufferGeometry();
|
|
3719
4194
|
geometry.setAttribute("position", new BufferAttribute(overlay.positions, 3));
|
|
3720
|
-
geometry.setAttribute("
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
4195
|
+
geometry.setAttribute("normal", new BufferAttribute(overlay.normals, 3));
|
|
4196
|
+
geometry.setAttribute("aValue", new BufferAttribute(overlay.aValue, 1));
|
|
4197
|
+
geometry.setIndex(new BufferAttribute(overlay.index, 1));
|
|
4198
|
+
const lut = makeColorScaleTexture(colorScaleLUT(overlay.colorScale.colormap));
|
|
4199
|
+
trackTextureDecode(lut);
|
|
4200
|
+
const uniforms = makeInspectScalarUniforms({
|
|
4201
|
+
colorScale: lut,
|
|
4202
|
+
domainMin: overlay.colorScale.domainMin,
|
|
4203
|
+
domainMax: overlay.colorScale.domainMax,
|
|
4204
|
+
quantizeBands: overlay.fieldParams.quantizeBands,
|
|
4205
|
+
isoEnabled: overlay.fieldParams.isoEnabled,
|
|
4206
|
+
isoSpacing: overlay.fieldParams.isoSpacing,
|
|
4207
|
+
isoLineWidthPx: overlay.fieldParams.isoLineWidthPx,
|
|
4208
|
+
criticalEnabled: overlay.fieldParams.criticalEnabled,
|
|
4209
|
+
criticalThreshold: overlay.fieldParams.criticalThreshold,
|
|
4210
|
+
shadingEnabled: overlay.fieldParams.shadingEnabled
|
|
4211
|
+
});
|
|
4212
|
+
const clippingPlanes = renderable.solidMaterial.clippingPlanes ?? void 0;
|
|
4213
|
+
const material = new ShaderMaterial({
|
|
4214
|
+
vertexShader: INSPECT_SCALAR_VERTEX_SHADER,
|
|
4215
|
+
fragmentShader: INSPECT_SCALAR_FRAGMENT_SHADER,
|
|
4216
|
+
uniforms,
|
|
4217
|
+
side: DoubleSide,
|
|
4218
|
+
clippingPlanes,
|
|
4219
|
+
// Engage the shader's `#include <clipping_planes_*>` chunks only when section
|
|
4220
|
+
// planes are actually present (NUM_CLIPPING_PLANES define is gated on this),
|
|
4221
|
+
// so heatmap surfaces honor section cuts the same as the ghosted solid does.
|
|
4222
|
+
clipping: Boolean(clippingPlanes && clippingPlanes.length > 0)
|
|
3728
4223
|
});
|
|
3729
4224
|
material.toneMapped = false;
|
|
3730
|
-
const
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
renderable.root.add(
|
|
3734
|
-
return { renderable, previousMaterial, ghostMaterial,
|
|
4225
|
+
const mesh = new Mesh(geometry, material);
|
|
4226
|
+
mesh.renderOrder = 5;
|
|
4227
|
+
mesh.raycast = () => null;
|
|
4228
|
+
renderable.root.add(mesh);
|
|
4229
|
+
return { renderable, previousMaterial, ghostMaterial, mesh, geometry, material, lut };
|
|
3735
4230
|
});
|
|
3736
4231
|
try {
|
|
3737
|
-
|
|
4232
|
+
const png = withSolidOnlyVisibility(
|
|
3738
4233
|
session,
|
|
3739
4234
|
() => withTemporarySceneBackground(session, new Color(0), () => {
|
|
3740
4235
|
r.render(session.scene, session.camera);
|
|
3741
4236
|
return captureRenderedPng(session.size);
|
|
3742
4237
|
})
|
|
3743
4238
|
);
|
|
4239
|
+
inspectProfileLog("scalar-render", {
|
|
4240
|
+
overlays: overlays.length,
|
|
4241
|
+
totalMs: Number((performance.now() - started).toFixed(1))
|
|
4242
|
+
});
|
|
4243
|
+
return png;
|
|
3744
4244
|
} finally {
|
|
3745
|
-
replacements.forEach(({ renderable, previousMaterial, ghostMaterial,
|
|
3746
|
-
if (
|
|
4245
|
+
replacements.forEach(({ renderable, previousMaterial, ghostMaterial, mesh, geometry, material, lut }) => {
|
|
4246
|
+
if (mesh) renderable.root.remove(mesh);
|
|
3747
4247
|
renderable.solid.material = previousMaterial;
|
|
3748
4248
|
ghostMaterial.dispose();
|
|
3749
4249
|
geometry == null ? void 0 : geometry.dispose();
|
|
3750
4250
|
material == null ? void 0 : material.dispose();
|
|
4251
|
+
lut == null ? void 0 : lut.dispose();
|
|
3751
4252
|
});
|
|
3752
4253
|
}
|
|
3753
4254
|
}
|
|
3754
|
-
function getSessionThicknessInspection(session, rawOptions) {
|
|
4255
|
+
function getSessionThicknessInspection(session, rawOptions, fieldOverride) {
|
|
3755
4256
|
var _a;
|
|
3756
4257
|
const resolvedOptions = resolveThicknessInspectionOptions(rawOptions);
|
|
3757
4258
|
const { options, sampleBudget } = withSceneSampleBudget(session, resolvedOptions, (rawOptions == null ? void 0 : rawOptions.maxSamplesPerObject) !== void 0);
|
|
3758
|
-
const
|
|
4259
|
+
const fieldParams = mergeScalarFieldParams(fieldOverride);
|
|
4260
|
+
const optionsKey = inspectionOptionsKey({ options, sampleBudget, fieldParams });
|
|
3759
4261
|
if (((_a = session.thicknessInspection) == null ? void 0 : _a.optionsKey) === optionsKey) return session.thicknessInspection;
|
|
3760
4262
|
const byId = new Map(session.objects.map((obj) => [obj.id, obj]));
|
|
3761
4263
|
const warnings = [];
|
|
@@ -3769,11 +4271,24 @@ function getSessionThicknessInspection(session, rawOptions) {
|
|
|
3769
4271
|
const cloudObjects = [];
|
|
3770
4272
|
const points = [];
|
|
3771
4273
|
const overlays = [];
|
|
4274
|
+
const connectivityStarted = performance.now();
|
|
3772
4275
|
const raycastConnectivity = buildThicknessRaycastConnectivityContext(session);
|
|
4276
|
+
inspectProfileLog("thickness-connectivity", {
|
|
4277
|
+
ms: Number((performance.now() - connectivityStarted).toFixed(1)),
|
|
4278
|
+
neighborSets: raycastConnectivity.neighborIdsByObjectId.size
|
|
4279
|
+
});
|
|
3773
4280
|
session.renderables.forEach((renderable, index) => {
|
|
4281
|
+
var _a2;
|
|
3774
4282
|
const sourceObject = byId.get(renderable.id);
|
|
3775
|
-
const
|
|
3776
|
-
|
|
4283
|
+
const connectedGeometries = connectedThicknessGeometriesFor(raycastConnectivity, renderable);
|
|
4284
|
+
const analysisStarted = performance.now();
|
|
4285
|
+
const analysis = analyzeThicknessGeometry(renderable.solid.geometry, options, { connectedGeometries });
|
|
4286
|
+
inspectProfileLog("thickness-analysis", {
|
|
4287
|
+
object: renderable.name,
|
|
4288
|
+
samples: analysis.pointSamples.length,
|
|
4289
|
+
triangles: analysis.triangleCount,
|
|
4290
|
+
connectedGeometries: connectedGeometries.length,
|
|
4291
|
+
ms: Number((performance.now() - analysisStarted).toFixed(1))
|
|
3777
4292
|
});
|
|
3778
4293
|
const bbox = bboxFromGeometry(analysis.geometry);
|
|
3779
4294
|
const summary = summarizeThicknessSamples(analysis.samples, options);
|
|
@@ -3812,7 +4327,27 @@ function getSessionThicknessInspection(session, rawOptions) {
|
|
|
3812
4327
|
...sample
|
|
3813
4328
|
});
|
|
3814
4329
|
});
|
|
3815
|
-
|
|
4330
|
+
const trianglePositions = (_a2 = analysis.geometry.getAttribute("position")) == null ? void 0 : _a2.array;
|
|
4331
|
+
if (trianglePositions instanceof Float32Array) {
|
|
4332
|
+
const built = buildScalarSurfaceOverlay(
|
|
4333
|
+
renderable,
|
|
4334
|
+
trianglePositions,
|
|
4335
|
+
analysis.pointSamples,
|
|
4336
|
+
{ min: thicknessColorScale.domainMin, max: thicknessColorScale.domainMax },
|
|
4337
|
+
fieldParams
|
|
4338
|
+
);
|
|
4339
|
+
if (built) {
|
|
4340
|
+
overlays.push(built.overlay);
|
|
4341
|
+
if (built.capped) {
|
|
4342
|
+
warnings.push(
|
|
4343
|
+
`${renderable.name}: scalar surface hit the ${SCALAR_SURFACE_VERTEX_CAP.toLocaleString()} vertex cap; raise the sample budget or reduce object size for full resolution.`
|
|
4344
|
+
);
|
|
4345
|
+
}
|
|
4346
|
+
if (built.holeCount > 0) {
|
|
4347
|
+
warnings.push(`${renderable.name}: ${built.holeCount} surface vertices had no in-gate sample (filled by nearest neighbor).`);
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
3816
4351
|
analysis.geometry.dispose();
|
|
3817
4352
|
});
|
|
3818
4353
|
const state = {
|
|
@@ -3835,9 +4370,6 @@ function getSessionThicknessInspection(session, rawOptions) {
|
|
|
3835
4370
|
objects,
|
|
3836
4371
|
warnings,
|
|
3837
4372
|
style: {
|
|
3838
|
-
// gradientColors is the legacy 'thickness-classic' rainbow, kept for
|
|
3839
|
-
// back-compat. colorScale is the truth the viewport/CLI now render with.
|
|
3840
|
-
gradientColors: THICKNESS_GRADIENT_COLORS.map((color) => [...color]),
|
|
3841
4373
|
colorScale: thicknessColorScale,
|
|
3842
4374
|
colorMinThickness: options.colorMinThickness,
|
|
3843
4375
|
colorMaxThickness: options.colorMaxThickness,
|
|
@@ -3848,21 +4380,22 @@ function getSessionThicknessInspection(session, rawOptions) {
|
|
|
3848
4380
|
session.thicknessInspection = state;
|
|
3849
4381
|
return state;
|
|
3850
4382
|
}
|
|
3851
|
-
function renderCurrentThickness(session, rawOptions) {
|
|
3852
|
-
const state = getSessionThicknessInspection(session, rawOptions);
|
|
3853
|
-
return { png:
|
|
4383
|
+
function renderCurrentThickness(session, rawOptions, fieldOverride) {
|
|
4384
|
+
const state = getSessionThicknessInspection(session, rawOptions, fieldOverride);
|
|
4385
|
+
return { png: renderScalarSurfaceOverlays(session, state.overlays), report: state.report, pointCloud: state.pointCloud };
|
|
3854
4386
|
}
|
|
3855
4387
|
const ROUGHNESS_SMOOTH_OPACITY = 0.16;
|
|
3856
4388
|
const ROUGHNESS_HARSH_OPACITY = 1;
|
|
3857
|
-
function renderCurrentRoughness(session, rawOptions) {
|
|
3858
|
-
const state = getSessionRoughnessInspection(session, rawOptions);
|
|
3859
|
-
return { png:
|
|
4389
|
+
function renderCurrentRoughness(session, rawOptions, fieldOverride) {
|
|
4390
|
+
const state = getSessionRoughnessInspection(session, rawOptions, fieldOverride);
|
|
4391
|
+
return { png: renderScalarSurfaceOverlays(session, state.overlays), report: state.report, pointCloud: state.pointCloud };
|
|
3860
4392
|
}
|
|
3861
|
-
function getSessionRoughnessInspection(session, rawOptions) {
|
|
4393
|
+
function getSessionRoughnessInspection(session, rawOptions, fieldOverride) {
|
|
3862
4394
|
var _a;
|
|
3863
4395
|
const resolvedOptions = resolveRoughnessInspectionOptions(rawOptions);
|
|
3864
4396
|
const { options, sampleBudget } = withSceneSampleBudget(session, resolvedOptions, (rawOptions == null ? void 0 : rawOptions.maxSamplesPerObject) !== void 0);
|
|
3865
|
-
const
|
|
4397
|
+
const fieldParams = mergeScalarFieldParams(fieldOverride);
|
|
4398
|
+
const optionsKey = inspectionOptionsKey({ options, sampleBudget, fieldParams });
|
|
3866
4399
|
if (((_a = session.roughnessInspection) == null ? void 0 : _a.optionsKey) === optionsKey) return session.roughnessInspection;
|
|
3867
4400
|
const byId = new Map(session.objects.map((obj) => [obj.id, obj]));
|
|
3868
4401
|
const warnings = [];
|
|
@@ -3886,6 +4419,7 @@ function getSessionRoughnessInspection(session, rawOptions) {
|
|
|
3886
4419
|
});
|
|
3887
4420
|
const roughnessColorScale = Number.isFinite(roughnessMin) && roughnessMax > roughnessMin ? { colormap: DEFAULT_ROUGHNESS_COLOR_SCALE.colormap, domainMin: roughnessMin, domainMax: roughnessMax } : DEFAULT_ROUGHNESS_COLOR_SCALE;
|
|
3888
4421
|
session.renderables.forEach((renderable, index) => {
|
|
4422
|
+
var _a2;
|
|
3889
4423
|
const analysis = analysesByIndex[index];
|
|
3890
4424
|
const bbox = bboxFromGeometry(analysis.geometry);
|
|
3891
4425
|
const sourceObject = byId.get(renderable.id);
|
|
@@ -3921,7 +4455,21 @@ function getSessionRoughnessInspection(session, rawOptions) {
|
|
|
3921
4455
|
...sample
|
|
3922
4456
|
});
|
|
3923
4457
|
});
|
|
3924
|
-
|
|
4458
|
+
const trianglePositions = (_a2 = analysis.geometry.getAttribute("position")) == null ? void 0 : _a2.array;
|
|
4459
|
+
if (trianglePositions instanceof Float32Array) {
|
|
4460
|
+
const built = buildScalarSurfaceOverlay(renderable, trianglePositions, analysis.pointSamples, null, fieldParams);
|
|
4461
|
+
if (built) {
|
|
4462
|
+
overlays.push(built.overlay);
|
|
4463
|
+
if (built.capped) {
|
|
4464
|
+
warnings.push(
|
|
4465
|
+
`${renderable.name}: scalar surface hit the ${SCALAR_SURFACE_VERTEX_CAP.toLocaleString()} vertex cap; raise the sample budget or reduce object size for full resolution.`
|
|
4466
|
+
);
|
|
4467
|
+
}
|
|
4468
|
+
if (built.holeCount > 0) {
|
|
4469
|
+
warnings.push(`${renderable.name}: ${built.holeCount} surface vertices had no in-gate sample (filled by nearest neighbor).`);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
3925
4473
|
analysis.geometry.dispose();
|
|
3926
4474
|
});
|
|
3927
4475
|
const state = {
|
|
@@ -5073,6 +5621,60 @@ Available renderable objects: ${available}`;
|
|
|
5073
5621
|
}
|
|
5074
5622
|
return "No visible renderable objects found.";
|
|
5075
5623
|
}
|
|
5624
|
+
const PENDING_TEXTURE_DECODES = /* @__PURE__ */ new Set();
|
|
5625
|
+
function trackTextureDecode(texture) {
|
|
5626
|
+
PENDING_TEXTURE_DECODES.add(textureDecoded(texture));
|
|
5627
|
+
}
|
|
5628
|
+
function textureDecoded(texture) {
|
|
5629
|
+
return new Promise((resolve) => {
|
|
5630
|
+
var _a;
|
|
5631
|
+
const finish = () => {
|
|
5632
|
+
texture.needsUpdate = true;
|
|
5633
|
+
resolve();
|
|
5634
|
+
};
|
|
5635
|
+
const forgeDecoded = (_a = texture.userData) == null ? void 0 : _a.forgeDecoded;
|
|
5636
|
+
if (forgeDecoded) {
|
|
5637
|
+
forgeDecoded.then(
|
|
5638
|
+
() => finish(),
|
|
5639
|
+
() => finish()
|
|
5640
|
+
);
|
|
5641
|
+
return;
|
|
5642
|
+
}
|
|
5643
|
+
const image = texture.image;
|
|
5644
|
+
if (image && typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) {
|
|
5645
|
+
finish();
|
|
5646
|
+
return;
|
|
5647
|
+
}
|
|
5648
|
+
if (image && typeof image.decode === "function") {
|
|
5649
|
+
image.decode().then(finish, finish);
|
|
5650
|
+
return;
|
|
5651
|
+
}
|
|
5652
|
+
if (image && (image.naturalWidth > 0 || image.complete && image.width > 0)) {
|
|
5653
|
+
finish();
|
|
5654
|
+
return;
|
|
5655
|
+
}
|
|
5656
|
+
if (image && "onload" in image) {
|
|
5657
|
+
const prevLoad = image.onload;
|
|
5658
|
+
const prevError = image.onerror;
|
|
5659
|
+
image.onload = (ev) => {
|
|
5660
|
+
if (typeof prevLoad === "function") prevLoad.call(image, ev);
|
|
5661
|
+
finish();
|
|
5662
|
+
};
|
|
5663
|
+
image.onerror = (ev) => {
|
|
5664
|
+
if (typeof prevError === "function") prevError.call(image, ev);
|
|
5665
|
+
finish();
|
|
5666
|
+
};
|
|
5667
|
+
return;
|
|
5668
|
+
}
|
|
5669
|
+
finish();
|
|
5670
|
+
});
|
|
5671
|
+
}
|
|
5672
|
+
async function awaitPendingTextureDecodes() {
|
|
5673
|
+
if (PENDING_TEXTURE_DECODES.size === 0) return;
|
|
5674
|
+
const pending = Array.from(PENDING_TEXTURE_DECODES);
|
|
5675
|
+
PENDING_TEXTURE_DECODES.clear();
|
|
5676
|
+
await Promise.all(pending);
|
|
5677
|
+
}
|
|
5076
5678
|
function createSession(code, opts) {
|
|
5077
5679
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
|
|
5078
5680
|
const size = (opts == null ? void 0 : opts.size) ?? 1024;
|
|
@@ -5426,6 +6028,12 @@ Fix one:
|
|
|
5426
6028
|
depthWrite: materialOpacity >= 0.99 && materialTransmission <= 0,
|
|
5427
6029
|
clippingPlanes: applicableCutPlanes
|
|
5428
6030
|
});
|
|
6031
|
+
const textureDescriptor = mp == null ? void 0 : mp.texture;
|
|
6032
|
+
if (textureDescriptor && solidMaterial instanceof MeshPhysicalMaterial) {
|
|
6033
|
+
const projectedTexture = descriptorToThreeTexture(textureDescriptor.image, { colorSpace: "srgb" });
|
|
6034
|
+
applyProjectedTexture(solidMaterial, textureDescriptor.projection, projectedTexture);
|
|
6035
|
+
trackTextureDecode(projectedTexture);
|
|
6036
|
+
}
|
|
5429
6037
|
solid = new Mesh(geo.solid, solidMaterial);
|
|
5430
6038
|
if (isScanRenderStyle) {
|
|
5431
6039
|
const scanProxyGeometry = createScanProxyGeometry(geo.solid, { grid: scanProxyGrid ?? void 0 });
|
|
@@ -5618,6 +6226,7 @@ async function emitInspectProgress(opts, event) {
|
|
|
5618
6226
|
}
|
|
5619
6227
|
window.__forgeRender = async (code, opts) => {
|
|
5620
6228
|
var _a, _b, _c;
|
|
6229
|
+
setInspectProfileEnabled(opts == null ? void 0 : opts.debug);
|
|
5621
6230
|
const requestedCameraTokens = (opts == null ? void 0 : opts.cameras) ?? (opts == null ? void 0 : opts.angles);
|
|
5622
6231
|
const hasDirectionalCameraTokens = Array.isArray(requestedCameraTokens) && requestedCameraTokens.length > 0;
|
|
5623
6232
|
if ((opts == null ? void 0 : opts.viewName) && hasDirectionalCameraTokens) {
|
|
@@ -5635,6 +6244,7 @@ window.__forgeRender = async (code, opts) => {
|
|
|
5635
6244
|
const built = createSession(code, {
|
|
5636
6245
|
size: (opts == null ? void 0 : opts.size) || 1024,
|
|
5637
6246
|
pixelRatio: (opts == null ? void 0 : opts.pixelRatio) || 1,
|
|
6247
|
+
debug: opts == null ? void 0 : opts.debug,
|
|
5638
6248
|
quality: opts == null ? void 0 : opts.quality,
|
|
5639
6249
|
allFiles: opts == null ? void 0 : opts.allFiles,
|
|
5640
6250
|
binaryFiles: opts == null ? void 0 : opts.binaryFiles,
|
|
@@ -5697,6 +6307,7 @@ window.__forgeRender = async (code, opts) => {
|
|
|
5697
6307
|
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
5698
6308
|
}
|
|
5699
6309
|
}
|
|
6310
|
+
await awaitPendingTextureDecodes();
|
|
5700
6311
|
await emitInspectProgress(opts, { type: "session-done", objectCount: session.objects.length });
|
|
5701
6312
|
const renderMode = (opts == null ? void 0 : opts.renderMode) === "wireframe" ? "wireframe" : "solid";
|
|
5702
6313
|
const edgePreset = (opts == null ? void 0 : opts.edges) ?? "off";
|
|
@@ -5822,7 +6433,7 @@ window.__forgeRender = async (code, opts) => {
|
|
|
5822
6433
|
}
|
|
5823
6434
|
if (requestedChannels.has("roughness")) {
|
|
5824
6435
|
await markChannelViewStart("roughness", label);
|
|
5825
|
-
const roughness = renderCurrentRoughness(session, opts == null ? void 0 : opts.roughness);
|
|
6436
|
+
const roughness = renderCurrentRoughness(session, opts == null ? void 0 : opts.roughness, opts == null ? void 0 : opts.scalarFieldParams);
|
|
5826
6437
|
roughnessRenders[label] = roughness.png;
|
|
5827
6438
|
roughnessReport = roughness.report;
|
|
5828
6439
|
roughnessPointCloud = roughness.pointCloud;
|
|
@@ -5877,7 +6488,7 @@ window.__forgeRender = async (code, opts) => {
|
|
|
5877
6488
|
}
|
|
5878
6489
|
if (requestedChannels.has("thickness")) {
|
|
5879
6490
|
await markChannelViewStart("thickness", label);
|
|
5880
|
-
const thickness = renderCurrentThickness(session, opts == null ? void 0 : opts.thickness);
|
|
6491
|
+
const thickness = renderCurrentThickness(session, opts == null ? void 0 : opts.thickness, opts == null ? void 0 : opts.scalarFieldParams);
|
|
5881
6492
|
thicknessRenders[label] = thickness.png;
|
|
5882
6493
|
thicknessReport = thickness.report;
|
|
5883
6494
|
thicknessPointCloud = thickness.pointCloud;
|
|
@@ -6022,6 +6633,7 @@ window.__forgeCaptureInit = async (code, opts) => {
|
|
|
6022
6633
|
if (!built.ok) {
|
|
6023
6634
|
return built;
|
|
6024
6635
|
}
|
|
6636
|
+
await awaitPendingTextureDecodes();
|
|
6025
6637
|
captureSession = built.session;
|
|
6026
6638
|
return {
|
|
6027
6639
|
ok: true,
|