forgecad 0.9.4 → 0.9.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-jwoEgwE_.js → AdminPage-uTtcSXtn.js} +1 -1
- package/dist/assets/{BlogPage-Ck7g3ue2.js → BlogPage-DYJMjWx3.js} +1 -1
- package/dist/assets/{DocsPage-9WaRC14b.js → DocsPage-C58f0K5v.js} +1 -6
- package/dist/assets/{EditorApp-Dja2jMmW.js → EditorApp-DNH1TEz1.js} +282 -62
- package/dist/assets/{EmbedViewer-37_PfMwv.js → EmbedViewer-CMXWA2LX.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-CO8WL0CY.js → LandingPageProofDriven-CAu2OZFn.js} +1 -1
- package/dist/assets/{PricingPage-DADKGuOa.js → PricingPage-BIgW7m3X.js} +1 -1
- package/dist/assets/{SettingsPage-DKKI4W49.js → SettingsPage-N1l1tMXO.js} +1 -1
- package/dist/assets/{app-CwI02pTA.js → app-CFy7g5WP.js} +74 -12
- package/dist/assets/cli/{render-Kw5hLEcL.js → render-BrVVdj_T.js} +453 -41
- package/dist/assets/{evalWorker-D6ub3kfS.js → evalWorker-c_SB9gg3.js} +2057 -446
- package/dist/assets/{manifold-lru0jwVw.js → manifold-CRoBhJKH.js} +2 -2
- package/dist/assets/{manifold-CwDdMKyc.js → manifold-Cjk7WhRs.js} +1 -1
- package/dist/assets/{manifold-DTvmxSDf.js → manifold-Dp6pvFr6.js} +1 -1
- package/dist/assets/{renderSceneState-tvtNKNRi.js → renderSceneState-3DfsSASX.js} +1 -1
- package/dist/assets/{reportWorker-DeqktDGt.js → reportWorker-BLkuIoS8.js} +2052 -443
- package/dist/assets/{sectionPlaneMath-C8N0w8o3.js → sectionPlaneMath-CykEnkvQ.js} +2258 -518
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +0 -1
- package/dist/docs-raw/API/core/concepts.md +11 -1
- package/dist/docs-raw/CLI.md +64 -13
- package/dist/docs-raw/generated/assembly.md +8 -3
- package/dist/docs-raw/generated/concepts.md +44 -41
- package/dist/docs-raw/generated/core.md +97 -47
- package/dist/docs-raw/generated/curves.md +6 -580
- package/dist/docs-raw/generated/lib.md +40 -3
- package/dist/docs-raw/generated/output.md +6 -1
- package/dist/docs-raw/generated/sdf.md +50 -4
- package/dist/docs-raw/generated/viewport.md +1 -9
- package/dist/docs-raw/guides/inspection-bundles.md +31 -6
- package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -0
- package/dist/docs-raw/skills/forgecad-image-replicator.md +3 -1
- package/dist/docs-raw/skills/forgecad-make-a-model.md +48 -4
- package/dist/docs-raw/skills/forgecad-render-inspect.md +3 -1
- package/dist/docs-raw/skills/forgecad-visual-spec.md +2 -0
- package/dist/docs-raw/skills/forgecad.md +2 -1
- package/dist/docs-raw/skills/index.md +0 -1
- package/dist/index.html +1 -1
- package/dist/sitemap.xml +6 -6
- package/dist-cli/blender/render.py +43 -8
- package/dist-cli/forgecad.js +4941 -1758
- package/dist-cli/forgecad.js.map +1 -1
- package/dist-skill/CONTEXT.md +255 -656
- package/dist-skill/SKILL-dev.md +2 -1
- package/dist-skill/SKILL.md +2 -1
- package/dist-skill/docs/API/core/concepts.md +11 -1
- package/dist-skill/docs/CLI.md +64 -13
- package/dist-skill/docs/generated/assembly.md +8 -3
- package/dist-skill/docs/generated/core.md +97 -47
- package/dist-skill/docs/generated/curves.md +6 -580
- package/dist-skill/docs/generated/lib.md +40 -3
- package/dist-skill/docs/generated/output.md +6 -1
- package/dist-skill/docs/generated/sdf.md +50 -4
- package/dist-skill/docs/generated/viewport.md +1 -9
- package/dist-skill/docs/guides/inspection-bundles.md +31 -6
- package/dist-skill/docs-dev/API/core/concepts.md +11 -1
- package/dist-skill/docs-dev/CLI.md +64 -13
- package/dist-skill/docs-dev/generated/assembly.md +8 -3
- package/dist-skill/docs-dev/generated/core.md +97 -47
- package/dist-skill/docs-dev/generated/curves.md +6 -580
- package/dist-skill/docs-dev/generated/lib.md +40 -3
- package/dist-skill/docs-dev/generated/output.md +6 -1
- package/dist-skill/docs-dev/generated/sdf.md +50 -4
- package/dist-skill/docs-dev/generated/viewport.md +1 -9
- package/dist-skill/docs-dev/guides/inspection-bundles.md +31 -6
- package/dist-skill/library/README.md +0 -1
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +1 -0
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +3 -1
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +48 -4
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
- package/dist-skill/library/forgecad-visual-spec/SKILL.md +2 -0
- package/examples/api/drive-wheel-regions.forge.js +43 -0
- package/examples/api/sdf-circular-array-knurling.forge.js +19 -0
- package/examples/api/sdf-pattern2d-ceramic-ripple-set.forge.js +83 -0
- package/examples/api/sdf-pattern2d-grip-tread.forge.js +72 -0
- package/examples/api/sdf-pattern2d-orbital-jewelry.forge.js +62 -0
- package/examples/api/sdf-surface-basket-weave.forge.js +67 -0
- package/examples/api/sector-gear-body.forge.js +34 -0
- package/package.json +1 -1
- package/dist/docs-raw/skills/forgecad-api-dogfood.md +0 -130
- package/dist-skill/library/forgecad-api-dogfood/SKILL.md +0 -125
|
@@ -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, bH as initSolverWasm, bG as initKernel, S as Scene, bI as BoxGeometry, bd as MeshStandardMaterial, a4 as BackSide, b0 as PointLight, M as Mesh, aa as MeshBasicMaterial, bJ as localAabbPlaneRelation, h as Vector2, bK as ShapeUtils, g as Vector3, e as Color, aC as resolveForgeRenderStyle, b9 as getRenderStylePreset, ax as setParamOverrides, b6 as runScript,
|
|
5
|
-
import { m as mergeViewportRenderSceneStates, p as parseRenderSceneCliSpec } from "../renderSceneState-
|
|
4
|
+
import { D as DoubleSide, bH as initSolverWasm, bG as initKernel, S as Scene, bI as BoxGeometry, bd as MeshStandardMaterial, a4 as BackSide, b0 as PointLight, M as Mesh, aa as MeshBasicMaterial, bJ as localAabbPlaneRelation, h as Vector2, bK as ShapeUtils, a0 as MathUtils, g as Vector3, G as Box3, aU as BufferAttribute, e as Color, aC as resolveForgeRenderStyle, b9 as getRenderStylePreset, ax as setParamOverrides, b6 as runScript, bL as Group, b3 as shapeToGeometry, b7 as MeshPhysicalMaterial, ba as AdditiveBlending, aH as LineBasicMaterial, b8 as LineSegments, aG as BufferGeometry, P as PerspectiveCamera, k as ShaderMaterial, bM as intersectWithPlane, W as WebGLRenderer, A as ACESFilmicToneMapping, c as SRGBColorSpace, bN as parseCameraCliSpec, bO as PMREMGenerator, aV as CanvasTexture, aW as Object3D, aX as FogExp2, aY as Fog, aZ as AmbientLight, b1 as DirectionalLight, a_ as HemisphereLight, bz as findJointAnimationClip, p as Plane, Y as Vector4, $ as Matrix4, bg as SDF_RAYMARCH_PROXY_VERTEX_SHADER, bf as buildSdfRaymarchFragmentShader, O as OrthographicCamera, bA as resolveJointAnimation, bB as resolveJointViewValues, R as Raycaster, bP as worldAuthorPlaneToLocal, a$ as SpotLight } from "../sectionPlaneMath-CykEnkvQ.js";
|
|
5
|
+
import { m as mergeViewportRenderSceneStates, p as parseRenderSceneCliSpec } from "../renderSceneState-3DfsSASX.js";
|
|
6
6
|
const CAD_MATERIAL_PROPS = {
|
|
7
7
|
color: 6003669,
|
|
8
8
|
metalness: 0.05,
|
|
@@ -269,17 +269,17 @@ function stitchLoops(points, edges) {
|
|
|
269
269
|
const warnings = [];
|
|
270
270
|
const adjacency = /* @__PURE__ */ new Map();
|
|
271
271
|
const unusedEdges = /* @__PURE__ */ new Set();
|
|
272
|
-
const
|
|
272
|
+
const edgeKey2 = (a, b) => a < b ? `${a}:${b}` : `${b}:${a}`;
|
|
273
273
|
for (const [a, b] of edges) {
|
|
274
274
|
if (!adjacency.has(a)) adjacency.set(a, []);
|
|
275
275
|
if (!adjacency.has(b)) adjacency.set(b, []);
|
|
276
276
|
(_a = adjacency.get(a)) == null ? void 0 : _a.push(b);
|
|
277
277
|
(_b = adjacency.get(b)) == null ? void 0 : _b.push(a);
|
|
278
|
-
unusedEdges.add(
|
|
278
|
+
unusedEdges.add(edgeKey2(a, b));
|
|
279
279
|
}
|
|
280
280
|
const loops = [];
|
|
281
281
|
for (const [edgeA, edgeB] of edges) {
|
|
282
|
-
const firstKey =
|
|
282
|
+
const firstKey = edgeKey2(edgeA, edgeB);
|
|
283
283
|
if (!unusedEdges.has(firstKey)) continue;
|
|
284
284
|
const loop = [edgeA, edgeB];
|
|
285
285
|
unusedEdges.delete(firstKey);
|
|
@@ -288,12 +288,12 @@ function stitchLoops(points, edges) {
|
|
|
288
288
|
let closed = false;
|
|
289
289
|
for (let guard = 0; guard < points.length + edges.length + 8; guard += 1) {
|
|
290
290
|
const neighbors = adjacency.get(current) ?? [];
|
|
291
|
-
const next = neighbors.find((candidate) => candidate !== previous && unusedEdges.has(
|
|
291
|
+
const next = neighbors.find((candidate) => candidate !== previous && unusedEdges.has(edgeKey2(current, candidate)));
|
|
292
292
|
if (next === void 0) {
|
|
293
293
|
if (current === edgeA) closed = true;
|
|
294
294
|
break;
|
|
295
295
|
}
|
|
296
|
-
unusedEdges.delete(
|
|
296
|
+
unusedEdges.delete(edgeKey2(current, next));
|
|
297
297
|
if (next === edgeA) {
|
|
298
298
|
closed = true;
|
|
299
299
|
break;
|
|
@@ -1069,6 +1069,38 @@ function analyzeDistanceInspection(entries, rawOptions = {}) {
|
|
|
1069
1069
|
warnings: [...connectivity.warnings]
|
|
1070
1070
|
};
|
|
1071
1071
|
}
|
|
1072
|
+
const CAMERA_TOKEN_DIRECTIONS = {
|
|
1073
|
+
front: [0, -1, 0.2],
|
|
1074
|
+
back: [0, 1, 0.2],
|
|
1075
|
+
side: [1, 0, 0.2],
|
|
1076
|
+
right: [1, 0, 0.2],
|
|
1077
|
+
top: [0, -0.01, 1],
|
|
1078
|
+
iso: [0.6, -0.6, 0.4]
|
|
1079
|
+
};
|
|
1080
|
+
function normalizeCameraDirection(dir) {
|
|
1081
|
+
const len = Math.sqrt(dir[0] ** 2 + dir[1] ** 2 + dir[2] ** 2) || 1;
|
|
1082
|
+
return [dir[0] / len, dir[1] / len, dir[2] / len];
|
|
1083
|
+
}
|
|
1084
|
+
function sphericalToCameraDirection(azimuthDeg, elevationDeg) {
|
|
1085
|
+
const az = azimuthDeg * Math.PI / 180;
|
|
1086
|
+
const el = elevationDeg * Math.PI / 180;
|
|
1087
|
+
const cosEl = Math.cos(el);
|
|
1088
|
+
return [cosEl * Math.sin(az), -cosEl * Math.cos(az), Math.sin(el)];
|
|
1089
|
+
}
|
|
1090
|
+
function parseCameraToken(token) {
|
|
1091
|
+
const normalized = token.trim();
|
|
1092
|
+
const preset = CAMERA_TOKEN_DIRECTIONS[normalized];
|
|
1093
|
+
if (preset) return { label: normalized, dir: preset };
|
|
1094
|
+
const parts = normalized.split(":").map((s) => Number.parseFloat(s));
|
|
1095
|
+
if (parts.length >= 2 && parts.length <= 3 && parts.every((n) => Number.isFinite(n))) {
|
|
1096
|
+
const dir = sphericalToCameraDirection(parts[0], parts[1]);
|
|
1097
|
+
const label = `az${parts[0]}_el${parts[1]}`;
|
|
1098
|
+
return { label, dir, distance: parts[2] };
|
|
1099
|
+
}
|
|
1100
|
+
throw new Error(
|
|
1101
|
+
`Unknown camera "${token}". Use a preset (front, back, side, right, top, iso) or azimuth:elevation in degrees (e.g. 45:30).`
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1072
1104
|
function formatAvailableViews(views) {
|
|
1073
1105
|
const names = Object.keys(views ?? {}).sort();
|
|
1074
1106
|
if (names.length === 0) {
|
|
@@ -1302,6 +1334,244 @@ function summarizeThicknessSamples(samples, options) {
|
|
|
1302
1334
|
unresolvedAreaPercent: percent(unresolvedArea, totalArea)
|
|
1303
1335
|
};
|
|
1304
1336
|
}
|
|
1337
|
+
const DEFAULT_ROUGHNESS_INSPECTION_OPTIONS = {
|
|
1338
|
+
smoothAngleDeg: 5,
|
|
1339
|
+
sharpAngleDeg: 30,
|
|
1340
|
+
harshAngleDeg: 90
|
|
1341
|
+
};
|
|
1342
|
+
const ROUGHNESS_COLORS = {
|
|
1343
|
+
smooth: [62, 72, 84],
|
|
1344
|
+
moderate: [255, 214, 0],
|
|
1345
|
+
sharp: [255, 124, 34],
|
|
1346
|
+
harsh: [255, 42, 96]
|
|
1347
|
+
};
|
|
1348
|
+
function resolveRoughnessInspectionOptions(raw = {}) {
|
|
1349
|
+
const options = {
|
|
1350
|
+
smoothAngleDeg: raw.smoothAngleDeg ?? DEFAULT_ROUGHNESS_INSPECTION_OPTIONS.smoothAngleDeg,
|
|
1351
|
+
sharpAngleDeg: raw.sharpAngleDeg ?? DEFAULT_ROUGHNESS_INSPECTION_OPTIONS.sharpAngleDeg,
|
|
1352
|
+
harshAngleDeg: raw.harshAngleDeg ?? DEFAULT_ROUGHNESS_INSPECTION_OPTIONS.harshAngleDeg
|
|
1353
|
+
};
|
|
1354
|
+
if (!Number.isFinite(options.smoothAngleDeg) || options.smoothAngleDeg < 0) {
|
|
1355
|
+
throw new Error(`smoothAngleDeg must be a finite non-negative angle (got ${options.smoothAngleDeg}).`);
|
|
1356
|
+
}
|
|
1357
|
+
if (!Number.isFinite(options.sharpAngleDeg) || options.sharpAngleDeg <= options.smoothAngleDeg) {
|
|
1358
|
+
throw new Error(`sharpAngleDeg must be greater than smoothAngleDeg (got ${options.sharpAngleDeg}).`);
|
|
1359
|
+
}
|
|
1360
|
+
if (!Number.isFinite(options.harshAngleDeg) || options.harshAngleDeg <= options.sharpAngleDeg || options.harshAngleDeg > 180) {
|
|
1361
|
+
throw new Error(`harshAngleDeg must be greater than sharpAngleDeg and <= 180 (got ${options.harshAngleDeg}).`);
|
|
1362
|
+
}
|
|
1363
|
+
return options;
|
|
1364
|
+
}
|
|
1365
|
+
function roughnessClassForAngle(angleDeg, options) {
|
|
1366
|
+
if (angleDeg >= options.harshAngleDeg) return "harsh";
|
|
1367
|
+
if (angleDeg >= options.sharpAngleDeg) return "sharp";
|
|
1368
|
+
if (angleDeg >= options.smoothAngleDeg) return "moderate";
|
|
1369
|
+
return "smooth";
|
|
1370
|
+
}
|
|
1371
|
+
function roughnessScoreForAngle(angleDeg, options) {
|
|
1372
|
+
if (angleDeg < options.sharpAngleDeg) return 0;
|
|
1373
|
+
if (angleDeg < options.harshAngleDeg) {
|
|
1374
|
+
return MathUtils.lerp(0.48, 0.82, (angleDeg - options.sharpAngleDeg) / (options.harshAngleDeg - options.sharpAngleDeg));
|
|
1375
|
+
}
|
|
1376
|
+
return 1;
|
|
1377
|
+
}
|
|
1378
|
+
function roughnessColorForAngle(angleDeg, options) {
|
|
1379
|
+
const cls = roughnessClassForAngle(angleDeg, options);
|
|
1380
|
+
if (cls === "smooth" || cls === "harsh") return ROUGHNESS_COLORS[cls];
|
|
1381
|
+
if (cls === "moderate") {
|
|
1382
|
+
return lerpRgb(
|
|
1383
|
+
ROUGHNESS_COLORS.moderate,
|
|
1384
|
+
ROUGHNESS_COLORS.sharp,
|
|
1385
|
+
(angleDeg - options.smoothAngleDeg) / (options.sharpAngleDeg - options.smoothAngleDeg)
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
return lerpRgb(
|
|
1389
|
+
ROUGHNESS_COLORS.sharp,
|
|
1390
|
+
ROUGHNESS_COLORS.harsh,
|
|
1391
|
+
(angleDeg - options.sharpAngleDeg) / (options.harshAngleDeg - options.sharpAngleDeg)
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
function lerpRgb(a, b, t) {
|
|
1395
|
+
const clamped = MathUtils.clamp(t, 0, 1);
|
|
1396
|
+
return [
|
|
1397
|
+
Math.round(MathUtils.lerp(a[0], b[0], clamped)),
|
|
1398
|
+
Math.round(MathUtils.lerp(a[1], b[1], clamped)),
|
|
1399
|
+
Math.round(MathUtils.lerp(a[2], b[2], clamped))
|
|
1400
|
+
];
|
|
1401
|
+
}
|
|
1402
|
+
function emptyRoughnessSummary() {
|
|
1403
|
+
return {
|
|
1404
|
+
triangleCount: 0,
|
|
1405
|
+
edgeCount: 0,
|
|
1406
|
+
boundaryEdgeCount: 0,
|
|
1407
|
+
nonManifoldEdgeCount: 0,
|
|
1408
|
+
smoothAreaPercent: 0,
|
|
1409
|
+
moderateAreaPercent: 0,
|
|
1410
|
+
sharpAreaPercent: 0,
|
|
1411
|
+
harshAreaPercent: 0,
|
|
1412
|
+
roughAreaPercent: 0,
|
|
1413
|
+
meanAngleDeg: null,
|
|
1414
|
+
p50AngleDeg: null,
|
|
1415
|
+
p90AngleDeg: null,
|
|
1416
|
+
p95AngleDeg: null,
|
|
1417
|
+
p99AngleDeg: null,
|
|
1418
|
+
maxAngleDeg: null,
|
|
1419
|
+
qualityScore: 0
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
function summarizeRoughnessTriangles(triangles, edgeAngles, edgeCount, boundaryEdgeCount, nonManifoldEdgeCount, options) {
|
|
1423
|
+
const areaByClass = {
|
|
1424
|
+
smooth: 0,
|
|
1425
|
+
moderate: 0,
|
|
1426
|
+
sharp: 0,
|
|
1427
|
+
harsh: 0
|
|
1428
|
+
};
|
|
1429
|
+
let totalArea = 0;
|
|
1430
|
+
for (const tri of triangles) {
|
|
1431
|
+
const area = Number.isFinite(tri.area) ? tri.area : 0;
|
|
1432
|
+
totalArea += area;
|
|
1433
|
+
areaByClass[roughnessClassForAngle(tri.maxAngleDeg, options)] += area;
|
|
1434
|
+
}
|
|
1435
|
+
const sortedAngles = [...edgeAngles].sort((lhs, rhs) => lhs - rhs);
|
|
1436
|
+
const meanAngleDeg = sortedAngles.length > 0 ? Number((sortedAngles.reduce((sum, angle) => sum + angle, 0) / sortedAngles.length).toFixed(2)) : null;
|
|
1437
|
+
const qualityScore = totalArea > 0 ? Math.round(
|
|
1438
|
+
MathUtils.clamp(
|
|
1439
|
+
100 * (areaByClass.smooth + areaByClass.moderate * 0.9) / totalArea - 50 * areaByClass.harsh / totalArea,
|
|
1440
|
+
0,
|
|
1441
|
+
100
|
|
1442
|
+
)
|
|
1443
|
+
) : 0;
|
|
1444
|
+
const safePercent = (value) => totalArea > 0 ? Number((value / totalArea * 100).toFixed(2)) : 0;
|
|
1445
|
+
return {
|
|
1446
|
+
triangleCount: triangles.length,
|
|
1447
|
+
edgeCount,
|
|
1448
|
+
boundaryEdgeCount,
|
|
1449
|
+
nonManifoldEdgeCount,
|
|
1450
|
+
smoothAreaPercent: safePercent(areaByClass.smooth),
|
|
1451
|
+
moderateAreaPercent: safePercent(areaByClass.moderate),
|
|
1452
|
+
sharpAreaPercent: safePercent(areaByClass.sharp),
|
|
1453
|
+
harshAreaPercent: safePercent(areaByClass.harsh),
|
|
1454
|
+
roughAreaPercent: safePercent(areaByClass.sharp + areaByClass.harsh),
|
|
1455
|
+
meanAngleDeg,
|
|
1456
|
+
p50AngleDeg: percentile(sortedAngles, 0.5),
|
|
1457
|
+
p90AngleDeg: percentile(sortedAngles, 0.9),
|
|
1458
|
+
p95AngleDeg: percentile(sortedAngles, 0.95),
|
|
1459
|
+
p99AngleDeg: percentile(sortedAngles, 0.99),
|
|
1460
|
+
maxAngleDeg: sortedAngles.length > 0 ? Number(sortedAngles[sortedAngles.length - 1].toFixed(2)) : null,
|
|
1461
|
+
qualityScore
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
function percentile(sorted, q) {
|
|
1465
|
+
if (sorted.length === 0) return null;
|
|
1466
|
+
const index = MathUtils.clamp(Math.floor(sorted.length * q), 0, sorted.length - 1);
|
|
1467
|
+
return Number(sorted[index].toFixed(2));
|
|
1468
|
+
}
|
|
1469
|
+
const DEG_PER_RAD = 180 / Math.PI;
|
|
1470
|
+
function analyzeRoughnessGeometry(sourceGeometry, rawOptions = {}) {
|
|
1471
|
+
const options = resolveRoughnessInspectionOptions(rawOptions);
|
|
1472
|
+
const geometry = sourceGeometry.index ? sourceGeometry.toNonIndexed() : sourceGeometry.clone();
|
|
1473
|
+
const position = geometry.getAttribute("position");
|
|
1474
|
+
const warnings = [];
|
|
1475
|
+
if (!position || position.count < 3) {
|
|
1476
|
+
return { geometry, summary: emptyRoughnessSummary(), warnings: ["No triangle geometry."] };
|
|
1477
|
+
}
|
|
1478
|
+
const triangleCount = Math.floor(position.count / 3);
|
|
1479
|
+
const normals = new Array(triangleCount);
|
|
1480
|
+
const triangles = new Array(triangleCount);
|
|
1481
|
+
const edges = /* @__PURE__ */ new Map();
|
|
1482
|
+
const colors = new Float32Array(position.count * 3);
|
|
1483
|
+
const scores = new Float32Array(position.count);
|
|
1484
|
+
const a = new Vector3();
|
|
1485
|
+
const b = new Vector3();
|
|
1486
|
+
const c = new Vector3();
|
|
1487
|
+
const ac = new Vector3();
|
|
1488
|
+
const normal = new Vector3();
|
|
1489
|
+
const bbox = new Box3().setFromBufferAttribute(position);
|
|
1490
|
+
const snap = Math.max(1e-6, bbox.getSize(new Vector3()).length() * 1e-8);
|
|
1491
|
+
for (let tri = 0; tri < triangleCount; tri += 1) {
|
|
1492
|
+
const offset = tri * 3;
|
|
1493
|
+
readVertex(position, offset, a);
|
|
1494
|
+
readVertex(position, offset + 1, b);
|
|
1495
|
+
readVertex(position, offset + 2, c);
|
|
1496
|
+
normal.subVectors(b, a).cross(ac.subVectors(c, a));
|
|
1497
|
+
const areaTwice = normal.length();
|
|
1498
|
+
triangles[tri] = { area: areaTwice * 0.5, maxAngleDeg: 0 };
|
|
1499
|
+
normals[tri] = areaTwice > 1e-12 ? normal.multiplyScalar(1 / areaTwice).clone() : new Vector3(0, 0, 1);
|
|
1500
|
+
const keys = [vertexKey(a, snap), vertexKey(b, snap), vertexKey(c, snap)];
|
|
1501
|
+
for (let edge = 0; edge < 3; edge += 1) {
|
|
1502
|
+
const key = edgeKey(keys[edge], keys[(edge + 1) % 3]);
|
|
1503
|
+
let record = edges.get(key);
|
|
1504
|
+
if (!record) {
|
|
1505
|
+
record = { triangles: [] };
|
|
1506
|
+
edges.set(key, record);
|
|
1507
|
+
}
|
|
1508
|
+
record.triangles.push(tri);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
const { boundaryEdgeCount, nonManifoldEdgeCount, edgeAngles } = markTriangleRoughness(edges, triangles, normals);
|
|
1512
|
+
if (boundaryEdgeCount > 0) warnings.push(`${boundaryEdgeCount} boundary edge(s) were treated as harsh roughness.`);
|
|
1513
|
+
if (nonManifoldEdgeCount > 0) warnings.push(`${nonManifoldEdgeCount} non-manifold edge(s) were treated as harsh roughness.`);
|
|
1514
|
+
for (let tri = 0; tri < triangleCount; tri += 1) {
|
|
1515
|
+
const { maxAngleDeg } = triangles[tri];
|
|
1516
|
+
const color = roughnessColorForAngle(maxAngleDeg, options);
|
|
1517
|
+
const score = roughnessScoreForAngle(maxAngleDeg, options);
|
|
1518
|
+
const offset = tri * 3;
|
|
1519
|
+
for (let vertex = 0; vertex < 3; vertex += 1) {
|
|
1520
|
+
const colorOffset = (offset + vertex) * 3;
|
|
1521
|
+
colors[colorOffset] = color[0] / 255;
|
|
1522
|
+
colors[colorOffset + 1] = color[1] / 255;
|
|
1523
|
+
colors[colorOffset + 2] = color[2] / 255;
|
|
1524
|
+
scores[offset + vertex] = score;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
geometry.setAttribute("color", new BufferAttribute(colors, 3));
|
|
1528
|
+
geometry.setAttribute("roughnessScore", new BufferAttribute(scores, 1));
|
|
1529
|
+
geometry.computeBoundingBox();
|
|
1530
|
+
return {
|
|
1531
|
+
geometry,
|
|
1532
|
+
summary: summarizeRoughnessTriangles(triangles, edgeAngles, edges.size, boundaryEdgeCount, nonManifoldEdgeCount, options),
|
|
1533
|
+
warnings
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
function markTriangleRoughness(edges, triangles, normals) {
|
|
1537
|
+
const edgeAngles = [];
|
|
1538
|
+
let boundaryEdgeCount = 0;
|
|
1539
|
+
let nonManifoldEdgeCount = 0;
|
|
1540
|
+
for (const edge of edges.values()) {
|
|
1541
|
+
if (edge.triangles.length === 1) {
|
|
1542
|
+
boundaryEdgeCount += 1;
|
|
1543
|
+
markTriangles(edge.triangles, triangles, 180);
|
|
1544
|
+
edgeAngles.push(180);
|
|
1545
|
+
continue;
|
|
1546
|
+
}
|
|
1547
|
+
if (edge.triangles.length > 2) {
|
|
1548
|
+
nonManifoldEdgeCount += 1;
|
|
1549
|
+
markTriangles(edge.triangles, triangles, 180);
|
|
1550
|
+
edgeAngles.push(180);
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
1553
|
+
const [first, second] = edge.triangles;
|
|
1554
|
+
const dot = MathUtils.clamp(normals[first].dot(normals[second]), -1, 1);
|
|
1555
|
+
const angleDeg = Math.acos(dot) * DEG_PER_RAD;
|
|
1556
|
+
markTriangles(edge.triangles, triangles, angleDeg);
|
|
1557
|
+
edgeAngles.push(angleDeg);
|
|
1558
|
+
}
|
|
1559
|
+
return { boundaryEdgeCount, nonManifoldEdgeCount, edgeAngles };
|
|
1560
|
+
}
|
|
1561
|
+
function readVertex(position, index, target) {
|
|
1562
|
+
target.set(position.getX(index), position.getY(index), position.getZ(index));
|
|
1563
|
+
}
|
|
1564
|
+
function vertexKey(point, snap) {
|
|
1565
|
+
return `${Math.round(point.x / snap)},${Math.round(point.y / snap)},${Math.round(point.z / snap)}`;
|
|
1566
|
+
}
|
|
1567
|
+
function edgeKey(a, b) {
|
|
1568
|
+
return a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
1569
|
+
}
|
|
1570
|
+
function markTriangles(indices, triangles, angleDeg) {
|
|
1571
|
+
for (const index of indices) {
|
|
1572
|
+
triangles[index].maxAngleDeg = Math.max(triangles[index].maxAngleDeg, angleDeg);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1305
1575
|
const canvas = document.getElementById("canvas");
|
|
1306
1576
|
const exportCanvas = document.createElement("canvas");
|
|
1307
1577
|
const exportCtx = exportCanvas.getContext("2d");
|
|
@@ -1569,39 +1839,8 @@ function updateSdfRaymarchUniforms(session) {
|
|
|
1569
1839
|
sdf.material.uniforms.uIsOrthographic.value = isOrthographic;
|
|
1570
1840
|
}
|
|
1571
1841
|
}
|
|
1572
|
-
const ANGLE_DIRS = {
|
|
1573
|
-
front: [0, -1, 0.2],
|
|
1574
|
-
back: [0, 1, 0.2],
|
|
1575
|
-
side: [1, 0, 0.2],
|
|
1576
|
-
right: [1, 0, 0.2],
|
|
1577
|
-
top: [0, -0.01, 1],
|
|
1578
|
-
iso: [0.6, -0.6, 0.4]
|
|
1579
|
-
};
|
|
1580
|
-
function normalizeVector(dir) {
|
|
1581
|
-
const len = Math.sqrt(dir[0] ** 2 + dir[1] ** 2 + dir[2] ** 2) || 1;
|
|
1582
|
-
return [dir[0] / len, dir[1] / len, dir[2] / len];
|
|
1583
|
-
}
|
|
1584
|
-
function sphericalToDir(azimuthDeg, elevationDeg) {
|
|
1585
|
-
const az = azimuthDeg * Math.PI / 180;
|
|
1586
|
-
const el = elevationDeg * Math.PI / 180;
|
|
1587
|
-
const cosEl = Math.cos(el);
|
|
1588
|
-
return [cosEl * Math.sin(az), -cosEl * Math.cos(az), Math.sin(el)];
|
|
1589
|
-
}
|
|
1590
|
-
function parseCameraToken(token) {
|
|
1591
|
-
const preset = ANGLE_DIRS[token];
|
|
1592
|
-
if (preset) return { label: token, dir: preset };
|
|
1593
|
-
const parts = token.split(":").map((s) => Number.parseFloat(s));
|
|
1594
|
-
if (parts.length >= 2 && parts.length <= 3 && parts.every((n) => Number.isFinite(n))) {
|
|
1595
|
-
const dir = sphericalToDir(parts[0], parts[1]);
|
|
1596
|
-
const label = `az${parts[0]}_el${parts[1]}`;
|
|
1597
|
-
return { label, dir, distance: parts[2] };
|
|
1598
|
-
}
|
|
1599
|
-
throw new Error(
|
|
1600
|
-
`Unknown camera "${token}". Use a preset (front, back, side, right, top, iso) or azimuth:elevation in degrees (e.g. 45:30).`
|
|
1601
|
-
);
|
|
1602
|
-
}
|
|
1603
1842
|
function applyDirectionCamera(session, dir, distanceOverride) {
|
|
1604
|
-
const d =
|
|
1843
|
+
const d = normalizeCameraDirection(dir);
|
|
1605
1844
|
const dist = distanceOverride ?? session.distance;
|
|
1606
1845
|
session.camera.position.set(session.center.x + d[0] * dist, session.center.y + d[1] * dist, session.center.z + d[2] * dist);
|
|
1607
1846
|
session.camera.up.set(0, 0, 1);
|
|
@@ -2274,6 +2513,130 @@ function renderCurrentThickness(session, rawOptions) {
|
|
|
2274
2513
|
material.dispose();
|
|
2275
2514
|
}
|
|
2276
2515
|
}
|
|
2516
|
+
const ROUGHNESS_SMOOTH_OPACITY = 0.16;
|
|
2517
|
+
const ROUGHNESS_HARSH_OPACITY = 1;
|
|
2518
|
+
function createRoughnessMaterial(clippingPlanes) {
|
|
2519
|
+
const material = new ShaderMaterial({
|
|
2520
|
+
transparent: true,
|
|
2521
|
+
depthTest: true,
|
|
2522
|
+
depthWrite: true,
|
|
2523
|
+
clippingPlanes: clippingPlanes ?? void 0,
|
|
2524
|
+
uniforms: {
|
|
2525
|
+
smoothOpacity: { value: ROUGHNESS_SMOOTH_OPACITY },
|
|
2526
|
+
harshOpacity: { value: ROUGHNESS_HARSH_OPACITY }
|
|
2527
|
+
},
|
|
2528
|
+
vertexShader: `
|
|
2529
|
+
attribute vec3 color;
|
|
2530
|
+
attribute float roughnessScore;
|
|
2531
|
+
varying vec3 vColor;
|
|
2532
|
+
varying float vRoughnessScore;
|
|
2533
|
+
varying vec3 vViewNormal;
|
|
2534
|
+
|
|
2535
|
+
void main() {
|
|
2536
|
+
vColor = color;
|
|
2537
|
+
vRoughnessScore = roughnessScore;
|
|
2538
|
+
vViewNormal = normalize(normalMatrix * normal);
|
|
2539
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
2540
|
+
}
|
|
2541
|
+
`,
|
|
2542
|
+
fragmentShader: `
|
|
2543
|
+
precision highp float;
|
|
2544
|
+
uniform float smoothOpacity;
|
|
2545
|
+
uniform float harshOpacity;
|
|
2546
|
+
varying vec3 vColor;
|
|
2547
|
+
varying float vRoughnessScore;
|
|
2548
|
+
varying vec3 vViewNormal;
|
|
2549
|
+
|
|
2550
|
+
void main() {
|
|
2551
|
+
vec3 shadowLight = normalize(vec3(0.32, 0.42, 0.84));
|
|
2552
|
+
float shade = 0.34 + 0.48 * max(dot(normalize(vViewNormal), shadowLight), 0.0);
|
|
2553
|
+
vec3 smoothShadow = vec3(0.26, 0.30, 0.35) * shade;
|
|
2554
|
+
float colorMix = smoothstep(0.04, 0.35, vRoughnessScore);
|
|
2555
|
+
vec3 finalColor = mix(smoothShadow, vColor, colorMix);
|
|
2556
|
+
float alpha = mix(smoothOpacity, harshOpacity, smoothstep(0.02, 0.55, vRoughnessScore));
|
|
2557
|
+
gl_FragColor = vec4(finalColor, alpha);
|
|
2558
|
+
}
|
|
2559
|
+
`
|
|
2560
|
+
});
|
|
2561
|
+
material.toneMapped = false;
|
|
2562
|
+
return material;
|
|
2563
|
+
}
|
|
2564
|
+
function renderCurrentRoughness(session, rawOptions) {
|
|
2565
|
+
const r = getRenderer(session.size, session.pixelRatio);
|
|
2566
|
+
const options = resolveRoughnessInspectionOptions(rawOptions);
|
|
2567
|
+
const byId = new Map(session.objects.map((obj) => [obj.id, obj]));
|
|
2568
|
+
const warnings = [];
|
|
2569
|
+
const objects = [];
|
|
2570
|
+
const replacements = session.renderables.map((renderable, index) => {
|
|
2571
|
+
const previousMaterial = renderable.solid.material;
|
|
2572
|
+
const previousGeometry = renderable.solid.geometry;
|
|
2573
|
+
if (renderable.sdfRaymarch) {
|
|
2574
|
+
const material2 = new MeshBasicMaterial({ transparent: true, opacity: 0, depthWrite: false });
|
|
2575
|
+
material2.toneMapped = false;
|
|
2576
|
+
renderable.solid.material = material2;
|
|
2577
|
+
warnings.push(`${renderable.name}: SDF raymarch objects are not included in mesh roughness inspection.`);
|
|
2578
|
+
return { renderable, previousMaterial, previousGeometry, material: material2, geometry: null };
|
|
2579
|
+
}
|
|
2580
|
+
const analysis = analyzeRoughnessGeometry(previousGeometry, options);
|
|
2581
|
+
const bbox = analysis.geometry.boundingBox;
|
|
2582
|
+
const sourceObject = byId.get(renderable.id);
|
|
2583
|
+
if (analysis.warnings.length > 0) {
|
|
2584
|
+
analysis.warnings.forEach((warning) => warnings.push(`${renderable.name}: ${warning}`));
|
|
2585
|
+
}
|
|
2586
|
+
objects.push({
|
|
2587
|
+
index: index + 1,
|
|
2588
|
+
id: renderable.id,
|
|
2589
|
+
name: renderable.name,
|
|
2590
|
+
groupName: renderable.groupName,
|
|
2591
|
+
treePath: sourceObject == null ? void 0 : sourceObject.treePath,
|
|
2592
|
+
mock: (sourceObject == null ? void 0 : sourceObject.mock) === true,
|
|
2593
|
+
bbox: {
|
|
2594
|
+
min: bbox ? [bbox.min.x, bbox.min.y, bbox.min.z] : [0, 0, 0],
|
|
2595
|
+
max: bbox ? [bbox.max.x, bbox.max.y, bbox.max.z] : [0, 0, 0]
|
|
2596
|
+
},
|
|
2597
|
+
...analysis.summary
|
|
2598
|
+
});
|
|
2599
|
+
const material = createRoughnessMaterial(renderable.solidMaterial.clippingPlanes);
|
|
2600
|
+
renderable.solid.geometry = analysis.geometry;
|
|
2601
|
+
renderable.solid.material = material;
|
|
2602
|
+
return { renderable, previousMaterial, previousGeometry, material, geometry: analysis.geometry };
|
|
2603
|
+
});
|
|
2604
|
+
try {
|
|
2605
|
+
const png = withSolidOnlyVisibility(
|
|
2606
|
+
session,
|
|
2607
|
+
() => withTemporarySceneBackground(session, new Color(0), () => {
|
|
2608
|
+
updateSdfRaymarchUniforms(session);
|
|
2609
|
+
r.render(session.scene, session.camera);
|
|
2610
|
+
return captureRenderedPng(session.size);
|
|
2611
|
+
})
|
|
2612
|
+
);
|
|
2613
|
+
return {
|
|
2614
|
+
png,
|
|
2615
|
+
report: {
|
|
2616
|
+
method: "mesh-dihedral-angle",
|
|
2617
|
+
options,
|
|
2618
|
+
objectCount: objects.length,
|
|
2619
|
+
objects,
|
|
2620
|
+
warnings,
|
|
2621
|
+
style: {
|
|
2622
|
+
smoothColor: ROUGHNESS_COLORS.smooth,
|
|
2623
|
+
moderateColor: ROUGHNESS_COLORS.moderate,
|
|
2624
|
+
sharpColor: ROUGHNESS_COLORS.sharp,
|
|
2625
|
+
harshColor: ROUGHNESS_COLORS.harsh,
|
|
2626
|
+
smoothOpacity: ROUGHNESS_SMOOTH_OPACITY,
|
|
2627
|
+
harshOpacity: ROUGHNESS_HARSH_OPACITY
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
};
|
|
2631
|
+
} finally {
|
|
2632
|
+
replacements.forEach(({ renderable, previousMaterial, previousGeometry, material, geometry }) => {
|
|
2633
|
+
renderable.solid.geometry = previousGeometry;
|
|
2634
|
+
renderable.solid.material = previousMaterial;
|
|
2635
|
+
geometry == null ? void 0 : geometry.dispose();
|
|
2636
|
+
material.dispose();
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2277
2640
|
function emptySectionSvg() {
|
|
2278
2641
|
return {
|
|
2279
2642
|
svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="-1 -1 2 2" width="400" height="400"><rect x="-1" y="-1" width="2" height="2" fill="#2a2a2a"/></svg>',
|
|
@@ -2756,7 +3119,7 @@ function applyOrbitPose(session, turn, pitchDeg) {
|
|
|
2756
3119
|
const pitch = MathUtils.degToRad(clampedPitch);
|
|
2757
3120
|
const cosPitch = Math.cos(pitch);
|
|
2758
3121
|
const dir = [Math.sin(yaw) * cosPitch, -Math.cos(yaw) * cosPitch, Math.sin(pitch)];
|
|
2759
|
-
const d =
|
|
3122
|
+
const d = normalizeCameraDirection(dir);
|
|
2760
3123
|
session.camera.position.set(
|
|
2761
3124
|
session.orbitTarget.x + d[0] * session.orbitRadius,
|
|
2762
3125
|
session.orbitTarget.y + d[1] * session.orbitRadius,
|
|
@@ -3190,6 +3553,12 @@ function createSession(code, opts) {
|
|
|
3190
3553
|
const scene = new Scene();
|
|
3191
3554
|
const sceneConfig = result.sceneConfig ?? null;
|
|
3192
3555
|
try {
|
|
3556
|
+
if ((opts == null ? void 0 : opts.viewName) && (opts == null ? void 0 : opts.cameraToken)) {
|
|
3557
|
+
return {
|
|
3558
|
+
ok: false,
|
|
3559
|
+
error: "Cannot use --view with --camera. Choose either a model-declared view or an explicit camera."
|
|
3560
|
+
};
|
|
3561
|
+
}
|
|
3193
3562
|
if (opts == null ? void 0 : opts.viewName) {
|
|
3194
3563
|
if (requestedSceneState == null ? void 0 : requestedSceneState.camera) {
|
|
3195
3564
|
return {
|
|
@@ -3227,6 +3596,34 @@ function createSession(code, opts) {
|
|
|
3227
3596
|
const fov = 45;
|
|
3228
3597
|
const distance = maxDim / (2 * Math.tan(fov * Math.PI / 360)) * 1.6;
|
|
3229
3598
|
const cameraFov = ((_c = requestedSceneState == null ? void 0 : requestedSceneState.camera) == null ? void 0 : _c.fov) ?? ((_d = sceneConfig == null ? void 0 : sceneConfig.camera) == null ? void 0 : _d.fov) ?? fov;
|
|
3599
|
+
try {
|
|
3600
|
+
if (opts == null ? void 0 : opts.cameraToken) {
|
|
3601
|
+
if (requestedSceneState == null ? void 0 : requestedSceneState.camera) {
|
|
3602
|
+
return {
|
|
3603
|
+
ok: false,
|
|
3604
|
+
error: "Cannot use camera presets/angles with an explicit render camera. Remove --camera-json or the camera field from --scene."
|
|
3605
|
+
};
|
|
3606
|
+
}
|
|
3607
|
+
const parsed = parseCameraToken(opts.cameraToken);
|
|
3608
|
+
const dir = normalizeCameraDirection(parsed.dir);
|
|
3609
|
+
const tokenDistance = parsed.distance ?? distance;
|
|
3610
|
+
requestedSceneState = mergeViewportRenderSceneStates(requestedSceneState, {
|
|
3611
|
+
camera: {
|
|
3612
|
+
projectionMode: "perspective",
|
|
3613
|
+
position: [
|
|
3614
|
+
center.x + dir[0] * tokenDistance,
|
|
3615
|
+
center.y + dir[1] * tokenDistance,
|
|
3616
|
+
center.z + dir[2] * tokenDistance
|
|
3617
|
+
],
|
|
3618
|
+
target: [center.x, center.y, center.z],
|
|
3619
|
+
up: [0, 0, 1],
|
|
3620
|
+
fov: cameraFov
|
|
3621
|
+
}
|
|
3622
|
+
});
|
|
3623
|
+
}
|
|
3624
|
+
} catch (err) {
|
|
3625
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
3626
|
+
}
|
|
3230
3627
|
const joints = ((_e = result.jointsView) == null ? void 0 : _e.enabled) === false ? [] : ((_f = result.jointsView) == null ? void 0 : _f.joints) ?? [];
|
|
3231
3628
|
const jointCouplings = ((_g = result.jointsView) == null ? void 0 : _g.enabled) === false ? [] : ((_h = result.jointsView) == null ? void 0 : _h.couplings) ?? [];
|
|
3232
3629
|
const animationClips = ((_i = result.jointsView) == null ? void 0 : _i.enabled) === false ? [] : ((_j = result.jointsView) == null ? void 0 : _j.animations) ?? [];
|
|
@@ -3525,7 +3922,7 @@ window.__forgeRender = async (code, opts) => {
|
|
|
3525
3922
|
const session = built.session;
|
|
3526
3923
|
await emitInspectProgress(opts, { type: "session-done", objectCount: session.objects.length });
|
|
3527
3924
|
const renderMode = (opts == null ? void 0 : opts.renderMode) === "wireframe" ? "wireframe" : "solid";
|
|
3528
|
-
const edgePreset = (opts == null ? void 0 : opts.edges) ?? "
|
|
3925
|
+
const edgePreset = (opts == null ? void 0 : opts.edges) ?? "off";
|
|
3529
3926
|
setSessionMode(session, renderMode);
|
|
3530
3927
|
if (renderMode === "solid") {
|
|
3531
3928
|
session.renderables.forEach((r) => {
|
|
@@ -3547,11 +3944,13 @@ window.__forgeRender = async (code, opts) => {
|
|
|
3547
3944
|
const distanceRenders = {};
|
|
3548
3945
|
const collisionRenders = {};
|
|
3549
3946
|
const thicknessRenders = {};
|
|
3947
|
+
const roughnessRenders = {};
|
|
3550
3948
|
let maskObjects = [];
|
|
3551
3949
|
let connectivityReport = null;
|
|
3552
3950
|
let distanceReport = null;
|
|
3553
3951
|
let collisionReport = null;
|
|
3554
3952
|
let thicknessReport = null;
|
|
3953
|
+
let roughnessReport = null;
|
|
3555
3954
|
let sectionAtlas = null;
|
|
3556
3955
|
const channelViewCounts = /* @__PURE__ */ new Map();
|
|
3557
3956
|
const markChannelViewStart = async (channel, view) => {
|
|
@@ -3592,6 +3991,13 @@ window.__forgeRender = async (code, opts) => {
|
|
|
3592
3991
|
normalRenders[label] = renderCurrentNormals(session);
|
|
3593
3992
|
await markChannelViewDone("normals", label);
|
|
3594
3993
|
}
|
|
3994
|
+
if (requestedChannels.has("roughness")) {
|
|
3995
|
+
await markChannelViewStart("roughness", label);
|
|
3996
|
+
const roughness = renderCurrentRoughness(session, opts == null ? void 0 : opts.roughness);
|
|
3997
|
+
roughnessRenders[label] = roughness.png;
|
|
3998
|
+
roughnessReport = roughness.report;
|
|
3999
|
+
await markChannelViewDone("roughness", label);
|
|
4000
|
+
}
|
|
3595
4001
|
if (requestedChannels.has("mask")) {
|
|
3596
4002
|
await markChannelViewStart("mask", label);
|
|
3597
4003
|
const mask = renderCurrentMask(session);
|
|
@@ -3652,6 +4058,12 @@ window.__forgeRender = async (code, opts) => {
|
|
|
3652
4058
|
renders,
|
|
3653
4059
|
depth: depthRenders,
|
|
3654
4060
|
normals: normalRenders,
|
|
4061
|
+
roughness: roughnessReport ? {
|
|
4062
|
+
...roughnessReport,
|
|
4063
|
+
views: roughnessRenders
|
|
4064
|
+
} : {
|
|
4065
|
+
views: roughnessRenders
|
|
4066
|
+
},
|
|
3655
4067
|
mask: {
|
|
3656
4068
|
views: maskRenders,
|
|
3657
4069
|
objects: maskObjects
|