forgecad 0.9.7 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/assets/{AdminPage-DX0mpSZT.js → AdminPage-CNEvQM7c.js} +1 -1
- package/dist/assets/{BlogPage-CI_P0_Pf.js → BlogPage-Cc41PP4d.js} +1 -1
- package/dist/assets/{DocsPage-DLhIIZyJ.js → DocsPage-9U1hGjrg.js} +2 -2
- package/dist/assets/{EditorApp-DfFT2Dn8.css → EditorApp-D11wL4Qn.css} +51 -0
- package/dist/assets/{EditorApp-BujZvuwX.js → EditorApp-DnddQvBt.js} +151 -9
- package/dist/assets/{EmbedViewer-0S0qXKog.js → EmbedViewer-B2lhWTcd.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-O_yMtAri.js → LandingPageProofDriven-CFet-l3o.js} +1 -1
- package/dist/assets/{PricingPage-DGkX3Ahr.js → PricingPage-CPm8mQx3.js} +1 -1
- package/dist/assets/{SettingsPage-DBsqTB_y.js → SettingsPage-BarZonVZ.js} +1 -1
- package/dist/assets/__vite-browser-external-Dhvy_jtL.js +4 -0
- package/dist/assets/{app-BE2nD6Yz.js → app-BjSEB4sY.js} +1006 -526
- package/dist/assets/cli/{render-iP9qh475.js → render-CXVrHY8q.js} +647 -481
- package/dist/assets/constructionHistoryWorker-z9_LGiRd.js +42984 -0
- package/dist/assets/{evalWorker-Ds5U4xtN.js → evalWorker-CtO7GsJR.js} +42 -9
- package/dist/assets/{inspectWorker-Dll4eVyD.js → inspectWorker-BZ2CkQZr.js} +785 -111
- package/dist/assets/{manifold-DjYsd7A_.js → manifold-BRdhoQy5.js} +2 -2
- package/dist/assets/{manifold-sJ-axdXM.js → manifold-BVi4_OeB.js} +1 -1
- package/dist/assets/manifold-Cp_dCC7i.js +3018 -0
- package/dist/assets/{manifold-Bk26ViCr.js → manifold-DAzn2Fsa.js} +1 -1
- package/dist/assets/{renderSceneState-Bngp5MrQ.js → renderSceneState-CWO8rHlt.js} +1 -1
- package/dist/assets/{reportWorker-CU8RZ4O0.js → reportWorker-Bz9tGiHb.js} +42 -9
- package/dist/assets/{sectionPlaneMath-BdTjyVfs.js → scalar-sampling-budget-Bmewod18.js} +1339 -215
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +1 -1
- package/dist/docs-raw/CLI.md +10 -10
- package/dist/docs-raw/coding-best-practices.md +1 -1
- package/dist/docs-raw/guides/inspection-bundles.md +77 -19
- package/dist/docs-raw/guides/skill-maintenance.md +1 -1
- package/dist/docs-raw/runbook.md +2 -2
- package/dist/docs-raw/skills/forgecad-make-a-model.md +11 -0
- package/dist/docs-raw/skills/forgecad-render-inspect.md +12 -6
- package/dist/docs-raw/skills/index.md +1 -1
- package/dist/index.html +1 -1
- package/dist/sitemap.xml +6 -6
- package/dist-cli/forgecad.js +596 -354
- package/dist-cli/forgecad.js.map +1 -1
- package/dist-skill/CONTEXT.md +77 -19
- package/dist-skill/docs/CLI.md +10 -10
- package/dist-skill/docs/guides/inspection-bundles.md +77 -19
- package/dist-skill/docs-dev/CLI.md +10 -10
- package/dist-skill/docs-dev/coding-best-practices.md +1 -1
- package/dist-skill/docs-dev/guides/inspection-bundles.md +77 -19
- package/dist-skill/docs-dev/guides/skill-maintenance.md +1 -1
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +11 -0
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +12 -6
- package/package.json +6 -3
|
@@ -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,
|
|
5
|
-
import { m as mergeViewportRenderSceneStates, p as parseRenderSceneCliSpec } from "../renderSceneState-
|
|
4
|
+
import { D as DoubleSide, bX as initSolverWasm, bW as initKernel, S as Scene, bY as BoxGeometry, bs as MeshStandardMaterial, a4 as BackSide, b0 as PointLight, M as Mesh, aa as MeshBasicMaterial, bZ as localAabbPlaneRelation, h as Vector2, b_ as ShapeUtils, b$ as analyzePhysicalConnectivity, c0 as meshContactDataFor, c1 as AabbSpatialIndex, c2 as detectPhysicalContact, g as Vector3, c3 as resolveThicknessInspectionOptions, R as Raycaster, c4 as thicknessColor, c5 as thicknessClass, aU as BufferAttribute, c6 as roughnessClassForAngle, a0 as MathUtils, c7 as resolveRoughnessInspectionOptions, G as Box3, c8 as roughnessColorForAngle, c9 as roughnessScoreForAngle, e as Color, aC as resolveForgeRenderStyle, bb as getRenderStylePreset, ax as setParamOverrides, b7 as runScript, ca as Group, b3 as shapeToGeometry, b8 as MeshPhysicalMaterial, bc as AdditiveBlending, aH as LineBasicMaterial, b9 as LineSegments, aG as BufferGeometry, P as PerspectiveCamera, k as ShaderMaterial, bi as ZEBRA_STRIPE_FRAGMENT_SHADER, bj as ZEBRA_STRIPE_VERTEX_SHADER, bd as ZEBRA_STRIPE_SOFTNESS, be as ZEBRA_STRIPE_SCALE, bf as ZEBRA_LIGHT_COLOR, bg as ZEBRA_DARK_COLOR, bh as ZEBRA_ACCENT_COLOR, ba as geometryWithVisibleVertexColors, cb as intersectWithPlane, cc as setActiveBackend, W as WebGLRenderer, A as ACESFilmicToneMapping, c as SRGBColorSpace, cd as parseCameraCliSpec, ce as PMREMGenerator, aV as CanvasTexture, aW as Object3D, aX as FogExp2, aY as Fog, aZ as AmbientLight, b1 as DirectionalLight, a_ as HemisphereLight, bO as findJointAnimationClip, p as Plane, bk as SURFACE_FIELD_FRAGMENT_SHADER, bl as SURFACE_FIELD_VERTEX_SHADER, Y as Vector4, $ as Matrix4, bv as SDF_RAYMARCH_PROXY_VERTEX_SHADER, bu as buildSdfRaymarchFragmentShader, O as OrthographicCamera, bP as resolveJointAnimation, bQ as resolveJointViewValues, bo as ROUGHNESS_COLORS, cf as PointsMaterial, cg as Points, b2 as analyzeCollisionIntersections, ch as serializeCollisionFinding, ci as summarizeThicknessSamples, br as THICKNESS_COLORS, cj as worldAuthorPlaneToLocal, a$ as SpotLight, bV as resolveScalarSceneSampleBudget } from "../scalar-sampling-budget-Bmewod18.js";
|
|
5
|
+
import { m as mergeViewportRenderSceneStates, p as parseRenderSceneCliSpec } from "../renderSceneState-CWO8rHlt.js";
|
|
6
6
|
const CAD_MATERIAL_PROPS = {
|
|
7
7
|
color: 6003669,
|
|
8
8
|
metalness: 0.05,
|
|
@@ -457,234 +457,6 @@ function computeMeshSectionCap(mesh, planeInput) {
|
|
|
457
457
|
warnings: stitched.warnings.length > 0 ? stitched.warnings : void 0
|
|
458
458
|
};
|
|
459
459
|
}
|
|
460
|
-
const DEFAULT_PHYSICAL_CONNECTIVITY_OPTIONS = {
|
|
461
|
-
contactTolerance: 0.05,
|
|
462
|
-
minOverlapVolume: 0.1,
|
|
463
|
-
exactGeometry: true
|
|
464
|
-
};
|
|
465
|
-
const AXIS_NAMES = ["x", "y", "z"];
|
|
466
|
-
let UnionFind$1 = class UnionFind {
|
|
467
|
-
constructor(size) {
|
|
468
|
-
__publicField(this, "parent");
|
|
469
|
-
__publicField(this, "rank");
|
|
470
|
-
this.parent = Array.from({ length: size }, (_, index) => index);
|
|
471
|
-
this.rank = Array.from({ length: size }, () => 0);
|
|
472
|
-
}
|
|
473
|
-
find(value) {
|
|
474
|
-
const parent = this.parent[value];
|
|
475
|
-
if (parent === value) return value;
|
|
476
|
-
const root = this.find(parent);
|
|
477
|
-
this.parent[value] = root;
|
|
478
|
-
return root;
|
|
479
|
-
}
|
|
480
|
-
union(a, b) {
|
|
481
|
-
const rootA = this.find(a);
|
|
482
|
-
const rootB = this.find(b);
|
|
483
|
-
if (rootA === rootB) return;
|
|
484
|
-
if (this.rank[rootA] < this.rank[rootB]) {
|
|
485
|
-
this.parent[rootA] = rootB;
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
if (this.rank[rootA] > this.rank[rootB]) {
|
|
489
|
-
this.parent[rootB] = rootA;
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
this.parent[rootB] = rootA;
|
|
493
|
-
this.rank[rootA] += 1;
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
function cloneVec3(value) {
|
|
497
|
-
return [value[0], value[1], value[2]];
|
|
498
|
-
}
|
|
499
|
-
function emptyBBox$1() {
|
|
500
|
-
return {
|
|
501
|
-
min: [Infinity, Infinity, Infinity],
|
|
502
|
-
max: [-Infinity, -Infinity, -Infinity]
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
function expandBBox$1(target, min, max) {
|
|
506
|
-
for (let axis = 0; axis < 3; axis += 1) {
|
|
507
|
-
target.min[axis] = Math.min(target.min[axis], min[axis]);
|
|
508
|
-
target.max[axis] = Math.max(target.max[axis], max[axis]);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
function nearestBoundaryGap(a, b, axis) {
|
|
512
|
-
return Math.min(Math.abs(a.max[axis] - b.min[axis]), Math.abs(b.max[axis] - a.min[axis]));
|
|
513
|
-
}
|
|
514
|
-
function hasPositiveGap(gaps) {
|
|
515
|
-
return gaps[0] > 0 || gaps[1] > 0 || gaps[2] > 0;
|
|
516
|
-
}
|
|
517
|
-
function collectCandidatePairs(entries, tolerance) {
|
|
518
|
-
if (entries.length < 2) return [];
|
|
519
|
-
const index = new AabbSpatialIndex(entries);
|
|
520
|
-
const pairs = index.overlapPairs({ padding: tolerance }).pairs.map((pair) => ({
|
|
521
|
-
sourceIndex: pair.sourceIndex,
|
|
522
|
-
targetIndex: pair.targetIndex,
|
|
523
|
-
gaps: aabbGaps(entries[pair.sourceIndex], entries[pair.targetIndex])
|
|
524
|
-
})).filter((pair) => Math.max(pair.gaps[0], pair.gaps[1], pair.gaps[2]) <= tolerance);
|
|
525
|
-
pairs.sort((a, b) => a.sourceIndex - b.sourceIndex || a.targetIndex - b.targetIndex);
|
|
526
|
-
return pairs;
|
|
527
|
-
}
|
|
528
|
-
function contactFromBBoxes(a, b, tolerance) {
|
|
529
|
-
const gaps = aabbGaps(a, b);
|
|
530
|
-
const largestGap = Math.max(gaps[0], gaps[1], gaps[2]);
|
|
531
|
-
if (largestGap > tolerance) return { touching: false, gap: largestGap };
|
|
532
|
-
const separatedAxes = gaps.map((gap, axis) => ({ gap, axis })).filter((entry) => entry.gap > 0);
|
|
533
|
-
if (separatedAxes.length > 0) {
|
|
534
|
-
const nearest2 = separatedAxes.reduce((best, entry) => entry.gap > best.gap ? entry : best, separatedAxes[0]);
|
|
535
|
-
return { touching: true, gap: nearest2.gap, axis: AXIS_NAMES[nearest2.axis] };
|
|
536
|
-
}
|
|
537
|
-
const boundaryAxes = AXIS_NAMES.map((axisName, axis) => ({
|
|
538
|
-
axis,
|
|
539
|
-
axisName,
|
|
540
|
-
gap: nearestBoundaryGap(a, b, axis)
|
|
541
|
-
})).filter((entry) => entry.gap <= tolerance);
|
|
542
|
-
if (boundaryAxes.length === 0) return { touching: false, gap: 0 };
|
|
543
|
-
const nearest = boundaryAxes.reduce((best, entry) => entry.gap < best.gap ? entry : best, boundaryAxes[0]);
|
|
544
|
-
return { touching: true, gap: nearest.gap, axis: nearest.axisName };
|
|
545
|
-
}
|
|
546
|
-
function intersectionVolume(a, b) {
|
|
547
|
-
try {
|
|
548
|
-
const hit = a.shape.intersect(b.shape);
|
|
549
|
-
if (hit.isEmpty()) return { volume: 0 };
|
|
550
|
-
const volume = hit.volume();
|
|
551
|
-
return { volume: Number.isFinite(volume) ? volume : 0 };
|
|
552
|
-
} catch (err) {
|
|
553
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
554
|
-
return { volume: null, warning: `Could not boolean-test ${a.name} against ${b.name}: ${message}` };
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
function bodyCountForEntry(entry) {
|
|
558
|
-
if (typeof entry.bodyCount === "number" && Number.isFinite(entry.bodyCount)) {
|
|
559
|
-
return Math.max(0, Math.round(entry.bodyCount));
|
|
560
|
-
}
|
|
561
|
-
return 1;
|
|
562
|
-
}
|
|
563
|
-
function makeEdge(entries, sourceIndex, targetIndex, edge) {
|
|
564
|
-
const source = entries[sourceIndex];
|
|
565
|
-
const target = entries[targetIndex];
|
|
566
|
-
return {
|
|
567
|
-
sourceIndex,
|
|
568
|
-
targetIndex,
|
|
569
|
-
sourceId: source.id,
|
|
570
|
-
targetId: target.id,
|
|
571
|
-
sourceName: source.name,
|
|
572
|
-
targetName: target.name,
|
|
573
|
-
...edge
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
function analyzePhysicalConnectivity(entries, rawOptions = {}) {
|
|
577
|
-
const exactGeometry = rawOptions.exactGeometry ?? DEFAULT_PHYSICAL_CONNECTIVITY_OPTIONS.exactGeometry;
|
|
578
|
-
const options = {
|
|
579
|
-
contactTolerance: rawOptions.contactTolerance ?? DEFAULT_PHYSICAL_CONNECTIVITY_OPTIONS.contactTolerance,
|
|
580
|
-
minOverlapVolume: rawOptions.minOverlapVolume ?? DEFAULT_PHYSICAL_CONNECTIVITY_OPTIONS.minOverlapVolume,
|
|
581
|
-
exactGeometry,
|
|
582
|
-
mergeOverlappingBBoxes: rawOptions.mergeOverlappingBBoxes ?? !exactGeometry,
|
|
583
|
-
mergeTouchingBBoxes: rawOptions.mergeTouchingBBoxes ?? !exactGeometry
|
|
584
|
-
};
|
|
585
|
-
const warnings = [];
|
|
586
|
-
const edges = [];
|
|
587
|
-
const unionFind = new UnionFind$1(entries.length);
|
|
588
|
-
for (const pair of collectCandidatePairs(entries, options.contactTolerance)) {
|
|
589
|
-
const i = pair.sourceIndex;
|
|
590
|
-
const j = pair.targetIndex;
|
|
591
|
-
const a = entries[i];
|
|
592
|
-
const b = entries[j];
|
|
593
|
-
const bboxOverlaps = !hasPositiveGap(pair.gaps) && aabbInteriorOverlaps(a, b);
|
|
594
|
-
if (options.exactGeometry && bboxOverlaps) {
|
|
595
|
-
const overlap = intersectionVolume(a, b);
|
|
596
|
-
if (overlap.warning) warnings.push(overlap.warning);
|
|
597
|
-
if (overlap.volume != null && overlap.volume > options.minOverlapVolume) {
|
|
598
|
-
unionFind.union(i, j);
|
|
599
|
-
edges.push(
|
|
600
|
-
makeEdge(entries, i, j, {
|
|
601
|
-
kind: "overlap",
|
|
602
|
-
method: "boolean-intersection",
|
|
603
|
-
gap: 0,
|
|
604
|
-
overlapVolume: overlap.volume
|
|
605
|
-
})
|
|
606
|
-
);
|
|
607
|
-
continue;
|
|
608
|
-
}
|
|
609
|
-
continue;
|
|
610
|
-
}
|
|
611
|
-
if (bboxOverlaps && options.mergeOverlappingBBoxes) {
|
|
612
|
-
unionFind.union(i, j);
|
|
613
|
-
edges.push(
|
|
614
|
-
makeEdge(entries, i, j, {
|
|
615
|
-
kind: "overlap",
|
|
616
|
-
method: "bbox-overlap",
|
|
617
|
-
gap: 0,
|
|
618
|
-
overlapVolume: aabbOverlapVolume(a, b)
|
|
619
|
-
})
|
|
620
|
-
);
|
|
621
|
-
} else {
|
|
622
|
-
const contact = contactFromBBoxes(a, b, options.contactTolerance);
|
|
623
|
-
if (!contact.touching || !options.mergeTouchingBBoxes) continue;
|
|
624
|
-
unionFind.union(i, j);
|
|
625
|
-
edges.push(
|
|
626
|
-
makeEdge(entries, i, j, {
|
|
627
|
-
kind: "touching",
|
|
628
|
-
method: "bbox-contact",
|
|
629
|
-
gap: contact.gap,
|
|
630
|
-
axis: contact.axis
|
|
631
|
-
})
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
const objects = entries.map((entry, index) => ({
|
|
636
|
-
index,
|
|
637
|
-
id: entry.id,
|
|
638
|
-
name: entry.name,
|
|
639
|
-
groupName: entry.groupName,
|
|
640
|
-
treePath: entry.treePath,
|
|
641
|
-
mock: entry.mock === true,
|
|
642
|
-
bodyCount: bodyCountForEntry(entry),
|
|
643
|
-
bbox: {
|
|
644
|
-
min: cloneVec3(entry.min),
|
|
645
|
-
max: cloneVec3(entry.max)
|
|
646
|
-
},
|
|
647
|
-
componentIndex: 0
|
|
648
|
-
}));
|
|
649
|
-
const componentByRoot = /* @__PURE__ */ new Map();
|
|
650
|
-
const rootToComponentIndex = /* @__PURE__ */ new Map();
|
|
651
|
-
for (let objectIndex = 0; objectIndex < objects.length; objectIndex += 1) {
|
|
652
|
-
const root = unionFind.find(objectIndex);
|
|
653
|
-
let component = componentByRoot.get(root);
|
|
654
|
-
if (!component) {
|
|
655
|
-
component = {
|
|
656
|
-
index: componentByRoot.size + 1,
|
|
657
|
-
objectIndexes: [],
|
|
658
|
-
objectIds: [],
|
|
659
|
-
objectNames: [],
|
|
660
|
-
objectCount: 0,
|
|
661
|
-
bodyCount: 0,
|
|
662
|
-
bbox: emptyBBox$1()
|
|
663
|
-
};
|
|
664
|
-
componentByRoot.set(root, component);
|
|
665
|
-
rootToComponentIndex.set(root, component.index);
|
|
666
|
-
}
|
|
667
|
-
const object = objects[objectIndex];
|
|
668
|
-
object.componentIndex = rootToComponentIndex.get(root) ?? component.index;
|
|
669
|
-
component.objectIndexes.push(object.index);
|
|
670
|
-
component.objectIds.push(object.id);
|
|
671
|
-
component.objectNames.push(object.name);
|
|
672
|
-
component.objectCount += 1;
|
|
673
|
-
component.bodyCount += object.bodyCount;
|
|
674
|
-
expandBBox$1(component.bbox, object.bbox.min, object.bbox.max);
|
|
675
|
-
}
|
|
676
|
-
const components = [...componentByRoot.values()];
|
|
677
|
-
return {
|
|
678
|
-
method: options.exactGeometry ? "boolean-overlap-plus-bbox-contact" : "bbox-neighborhood",
|
|
679
|
-
options,
|
|
680
|
-
objectCount: objects.length,
|
|
681
|
-
componentCount: components.length,
|
|
682
|
-
objects,
|
|
683
|
-
components,
|
|
684
|
-
edges,
|
|
685
|
-
warnings
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
460
|
const EPSILON = 1e-9;
|
|
689
461
|
function intervalGap(aMin, aMax, bMin, bMax) {
|
|
690
462
|
if (aMax < bMin) return bMin - aMax;
|
|
@@ -876,6 +648,185 @@ function analyzeDistanceInspection(entries, rawOptions = {}) {
|
|
|
876
648
|
warnings: [...connectivity.warnings]
|
|
877
649
|
};
|
|
878
650
|
}
|
|
651
|
+
const DEFAULT_CONTACT_TOLERANCE = 0.05;
|
|
652
|
+
const DEFAULT_BED_TOLERANCE = 0.05;
|
|
653
|
+
const DEFAULT_GROUND_Z = 0;
|
|
654
|
+
const MIN_SHAPE_OVERLAP_VOLUME = 1e-9;
|
|
655
|
+
let UnionFind$1 = class UnionFind {
|
|
656
|
+
constructor(size) {
|
|
657
|
+
__publicField(this, "parent");
|
|
658
|
+
__publicField(this, "rank");
|
|
659
|
+
this.parent = Array.from({ length: size }, (_, index) => index);
|
|
660
|
+
this.rank = Array.from({ length: size }, () => 0);
|
|
661
|
+
}
|
|
662
|
+
find(value) {
|
|
663
|
+
const parent = this.parent[value];
|
|
664
|
+
if (parent === value) return value;
|
|
665
|
+
const root = this.find(parent);
|
|
666
|
+
this.parent[value] = root;
|
|
667
|
+
return root;
|
|
668
|
+
}
|
|
669
|
+
union(a, b) {
|
|
670
|
+
const rootA = this.find(a);
|
|
671
|
+
const rootB = this.find(b);
|
|
672
|
+
if (rootA === rootB) return;
|
|
673
|
+
if (this.rank[rootA] < this.rank[rootB]) {
|
|
674
|
+
this.parent[rootA] = rootB;
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (this.rank[rootA] > this.rank[rootB]) {
|
|
678
|
+
this.parent[rootB] = rootA;
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
this.parent[rootB] = rootA;
|
|
682
|
+
this.rank[rootA] += 1;
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
function cloneVec3(value) {
|
|
686
|
+
return [value[0], value[1], value[2]];
|
|
687
|
+
}
|
|
688
|
+
function emptyBBox$1() {
|
|
689
|
+
return {
|
|
690
|
+
min: [Infinity, Infinity, Infinity],
|
|
691
|
+
max: [-Infinity, -Infinity, -Infinity]
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function expandBBox$1(target, min, max) {
|
|
695
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
696
|
+
target.min[axis] = Math.min(target.min[axis], min[axis]);
|
|
697
|
+
target.max[axis] = Math.max(target.max[axis], max[axis]);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
function bodyCountForEntry(entry) {
|
|
701
|
+
if (typeof entry.bodyCount === "number" && Number.isFinite(entry.bodyCount)) {
|
|
702
|
+
return Math.max(0, Math.round(entry.bodyCount));
|
|
703
|
+
}
|
|
704
|
+
return 1;
|
|
705
|
+
}
|
|
706
|
+
function resolveNonNegative(value, fallback) {
|
|
707
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
708
|
+
}
|
|
709
|
+
function resolveFinite(value, fallback) {
|
|
710
|
+
return Number.isFinite(value) ? value : fallback;
|
|
711
|
+
}
|
|
712
|
+
function analyzeFloatingInspection(entries, rawOptions = {}) {
|
|
713
|
+
const options = {
|
|
714
|
+
contactTolerance: resolveNonNegative(rawOptions.contactTolerance, DEFAULT_CONTACT_TOLERANCE),
|
|
715
|
+
bedTolerance: resolveNonNegative(rawOptions.bedTolerance, DEFAULT_BED_TOLERANCE),
|
|
716
|
+
groundZ: resolveFinite(rawOptions.groundZ, DEFAULT_GROUND_Z)
|
|
717
|
+
};
|
|
718
|
+
const unionFind = new UnionFind$1(entries.length);
|
|
719
|
+
const contacts = [];
|
|
720
|
+
const warnings = [];
|
|
721
|
+
const meshDataByIndex = /* @__PURE__ */ new Map();
|
|
722
|
+
entries.forEach((entry, index2) => {
|
|
723
|
+
const meshData = meshContactDataFor(entry);
|
|
724
|
+
if (meshData) meshDataByIndex.set(index2, meshData);
|
|
725
|
+
});
|
|
726
|
+
const candidateTolerance = options.contactTolerance > 0 ? options.contactTolerance : Number.EPSILON;
|
|
727
|
+
const index = new AabbSpatialIndex(entries);
|
|
728
|
+
const contactOptions = {
|
|
729
|
+
contactTolerance: options.contactTolerance,
|
|
730
|
+
minOverlapVolume: MIN_SHAPE_OVERLAP_VOLUME,
|
|
731
|
+
exactGeometry: true,
|
|
732
|
+
mergeOverlappingBBoxes: false,
|
|
733
|
+
mergeTouchingBBoxes: false
|
|
734
|
+
};
|
|
735
|
+
for (const pair of index.overlapPairs({ padding: candidateTolerance }).pairs) {
|
|
736
|
+
const source = entries[pair.sourceIndex];
|
|
737
|
+
const target = entries[pair.targetIndex];
|
|
738
|
+
const detection = detectPhysicalContact(source, target, contactOptions, {
|
|
739
|
+
sourceMesh: meshDataByIndex.get(pair.sourceIndex) ?? null,
|
|
740
|
+
targetMesh: meshDataByIndex.get(pair.targetIndex) ?? null
|
|
741
|
+
});
|
|
742
|
+
if (detection.warning) warnings.push(detection.warning);
|
|
743
|
+
if (!detection.contact) continue;
|
|
744
|
+
unionFind.union(pair.sourceIndex, pair.targetIndex);
|
|
745
|
+
contacts.push({
|
|
746
|
+
sourceIndex: pair.sourceIndex,
|
|
747
|
+
targetIndex: pair.targetIndex,
|
|
748
|
+
sourceId: source.id,
|
|
749
|
+
targetId: target.id,
|
|
750
|
+
sourceName: source.name,
|
|
751
|
+
targetName: target.name,
|
|
752
|
+
gap: detection.contact.gap
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
const objects = entries.map((entry, entryIndex) => ({
|
|
756
|
+
index: entryIndex,
|
|
757
|
+
id: entry.id,
|
|
758
|
+
name: entry.name,
|
|
759
|
+
groupName: entry.groupName,
|
|
760
|
+
treePath: entry.treePath,
|
|
761
|
+
mock: entry.mock === true,
|
|
762
|
+
bodyCount: bodyCountForEntry(entry),
|
|
763
|
+
bbox: {
|
|
764
|
+
min: cloneVec3(entry.min),
|
|
765
|
+
max: cloneVec3(entry.max)
|
|
766
|
+
},
|
|
767
|
+
componentIndex: 0,
|
|
768
|
+
isRootComponent: false,
|
|
769
|
+
isBedSupported: entry.min[2] <= options.groundZ + options.bedTolerance,
|
|
770
|
+
isFloating: false
|
|
771
|
+
}));
|
|
772
|
+
const componentByRoot = /* @__PURE__ */ new Map();
|
|
773
|
+
const rootToComponentIndex = /* @__PURE__ */ new Map();
|
|
774
|
+
for (let objectIndex = 0; objectIndex < objects.length; objectIndex += 1) {
|
|
775
|
+
const root = unionFind.find(objectIndex);
|
|
776
|
+
let component = componentByRoot.get(root);
|
|
777
|
+
if (!component) {
|
|
778
|
+
component = {
|
|
779
|
+
index: componentByRoot.size + 1,
|
|
780
|
+
objectIndexes: [],
|
|
781
|
+
objectIds: [],
|
|
782
|
+
objectNames: [],
|
|
783
|
+
objectCount: 0,
|
|
784
|
+
bodyCount: 0,
|
|
785
|
+
bbox: emptyBBox$1(),
|
|
786
|
+
isRoot: false,
|
|
787
|
+
isBedSupported: false,
|
|
788
|
+
isFloating: false
|
|
789
|
+
};
|
|
790
|
+
componentByRoot.set(root, component);
|
|
791
|
+
rootToComponentIndex.set(root, component.index);
|
|
792
|
+
}
|
|
793
|
+
const object = objects[objectIndex];
|
|
794
|
+
object.componentIndex = rootToComponentIndex.get(root) ?? component.index;
|
|
795
|
+
component.objectIndexes.push(object.index);
|
|
796
|
+
component.objectIds.push(object.id);
|
|
797
|
+
component.objectNames.push(object.name);
|
|
798
|
+
component.objectCount += 1;
|
|
799
|
+
component.bodyCount += object.bodyCount;
|
|
800
|
+
component.isBedSupported || (component.isBedSupported = object.isBedSupported);
|
|
801
|
+
expandBBox$1(component.bbox, object.bbox.min, object.bbox.max);
|
|
802
|
+
}
|
|
803
|
+
const components = [...componentByRoot.values()];
|
|
804
|
+
for (const component of components) {
|
|
805
|
+
component.isRoot = false;
|
|
806
|
+
component.isFloating = !component.isBedSupported;
|
|
807
|
+
}
|
|
808
|
+
const componentByIndex = new Map(components.map((component) => [component.index, component]));
|
|
809
|
+
for (const object of objects) {
|
|
810
|
+
const component = componentByIndex.get(object.componentIndex);
|
|
811
|
+
object.isRootComponent = false;
|
|
812
|
+
object.isBedSupported = (component == null ? void 0 : component.isBedSupported) ?? object.isBedSupported;
|
|
813
|
+
object.isFloating = (component == null ? void 0 : component.isFloating) ?? false;
|
|
814
|
+
}
|
|
815
|
+
return {
|
|
816
|
+
method: "mesh-contact-ground-reachability",
|
|
817
|
+
options,
|
|
818
|
+
rootComponentIndex: null,
|
|
819
|
+
objectCount: objects.length,
|
|
820
|
+
componentCount: components.length,
|
|
821
|
+
floatingComponentCount: components.filter((component) => component.isFloating).length,
|
|
822
|
+
floatingObjectCount: objects.filter((object) => object.isFloating).length,
|
|
823
|
+
floatingBodyCount: components.filter((component) => component.isFloating).reduce((total, component) => total + component.bodyCount, 0),
|
|
824
|
+
contacts,
|
|
825
|
+
objects,
|
|
826
|
+
components,
|
|
827
|
+
warnings
|
|
828
|
+
};
|
|
829
|
+
}
|
|
879
830
|
const CAMERA_TOKEN_DIRECTIONS = {
|
|
880
831
|
front: [0, -1, 0.2],
|
|
881
832
|
back: [0, 1, 0.2],
|
|
@@ -908,6 +859,7 @@ function parseCameraToken(token) {
|
|
|
908
859
|
`Unknown camera "${token}". Use a preset (front, back, side, right, top, iso) or azimuth:elevation in degrees (e.g. 45:30).`
|
|
909
860
|
);
|
|
910
861
|
}
|
|
862
|
+
const CLI_DEFAULT_BACKEND = "manifold";
|
|
911
863
|
function formatAvailableViews(views) {
|
|
912
864
|
const names = Object.keys(views ?? {}).sort();
|
|
913
865
|
if (names.length === 0) {
|
|
@@ -936,14 +888,36 @@ function resolveNamedSceneViewCamera(sceneConfig, viewName) {
|
|
|
936
888
|
}
|
|
937
889
|
return sceneViewCameraToViewportCameraState(view.camera);
|
|
938
890
|
}
|
|
939
|
-
function
|
|
891
|
+
function paletteForTheme(theme) {
|
|
892
|
+
if (theme === "cad-section") {
|
|
893
|
+
return {
|
|
894
|
+
background: "#f7f7f4",
|
|
895
|
+
fill: "#e6e8eb",
|
|
896
|
+
stroke: "#20262e",
|
|
897
|
+
hatchStroke: "#a8afb7",
|
|
898
|
+
strokeThin: 0.22,
|
|
899
|
+
strokeBold: 0.55,
|
|
900
|
+
useHatch: true
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
background: "#2a2a2a",
|
|
905
|
+
fill: "#4488cc",
|
|
906
|
+
stroke: "#224466",
|
|
907
|
+
hatchStroke: "#224466",
|
|
908
|
+
strokeThin: 0.3,
|
|
909
|
+
strokeBold: 0.8,
|
|
910
|
+
useHatch: false
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
function edgesStrokeAttrs(preset, palette) {
|
|
940
914
|
switch (preset) {
|
|
941
915
|
case "off":
|
|
942
916
|
return 'stroke="none"';
|
|
943
917
|
case "bold":
|
|
944
|
-
return
|
|
918
|
+
return `stroke="${palette.stroke}" stroke-width="${palette.strokeBold}"`;
|
|
945
919
|
default:
|
|
946
|
-
return
|
|
920
|
+
return `stroke="${palette.stroke}" stroke-width="${palette.strokeThin}"`;
|
|
947
921
|
}
|
|
948
922
|
}
|
|
949
923
|
function combineBounds(left, right) {
|
|
@@ -958,6 +932,19 @@ function escapeAttribute(value) {
|
|
|
958
932
|
function polygonPath(poly) {
|
|
959
933
|
return `${poly.map((point, index) => `${index === 0 ? "M" : "L"}${point[0].toFixed(3)},${(-point[1]).toFixed(3)}`).join(" ")} Z`;
|
|
960
934
|
}
|
|
935
|
+
function compoundPolygonPath(polygons) {
|
|
936
|
+
return polygons.map(polygonPath).join(" ");
|
|
937
|
+
}
|
|
938
|
+
function hatchDefs(palette) {
|
|
939
|
+
if (!palette.useHatch) return "";
|
|
940
|
+
return ` <defs>
|
|
941
|
+
<pattern id="forge-section-hatch" patternUnits="userSpaceOnUse" width="4" height="4" patternTransform="rotate(45)">
|
|
942
|
+
<rect width="4" height="4" fill="${palette.fill}"/>
|
|
943
|
+
<line x1="0" y1="0" x2="0" y2="4" stroke="${palette.hatchStroke}" stroke-width="0.28"/>
|
|
944
|
+
</pattern>
|
|
945
|
+
</defs>
|
|
946
|
+
`;
|
|
947
|
+
}
|
|
961
948
|
function prepareEntry(entry) {
|
|
962
949
|
const polygons = entry.sketch.toPolygons();
|
|
963
950
|
if (polygons.length === 0) {
|
|
@@ -979,7 +966,9 @@ function buildSketchSvgDocument(entries, options = {}) {
|
|
|
979
966
|
throw new Error("Sketch SVG export requires at least one sketch payload.");
|
|
980
967
|
}
|
|
981
968
|
const edges = options.edges ?? "thin";
|
|
982
|
-
const
|
|
969
|
+
const palette = paletteForTheme(options.theme ?? "debug");
|
|
970
|
+
const strokeAttrs = edgesStrokeAttrs(edges, palette);
|
|
971
|
+
const fill = palette.useHatch ? "url(#forge-section-hatch)" : palette.fill;
|
|
983
972
|
const prepared = entries.map(prepareEntry);
|
|
984
973
|
const combinedBounds = prepared.reduce((acc, entry) => combineBounds(acc, entry.bounds), prepared[0].bounds);
|
|
985
974
|
const margin = 2;
|
|
@@ -992,17 +981,15 @@ function buildSketchSvgDocument(entries, options = {}) {
|
|
|
992
981
|
let pathCount = 0;
|
|
993
982
|
const body = prepared.map((entry) => {
|
|
994
983
|
const label = entry.name ? ` data-name="${escapeAttribute(entry.name)}"` : "";
|
|
995
|
-
|
|
996
|
-
pathCount += 1;
|
|
997
|
-
return ` <path d="${polygonPath(poly)}" fill="#4488cc" ${strokeAttrs}/>`;
|
|
998
|
-
}).join("\n");
|
|
984
|
+
pathCount += entry.polygons.length;
|
|
999
985
|
return ` <g${label}>
|
|
1000
|
-
${
|
|
986
|
+
<path d="${compoundPolygonPath(entry.polygons)}" fill="${fill}" fill-rule="evenodd" ${strokeAttrs}/>
|
|
1001
987
|
</g>`;
|
|
1002
988
|
}).join("\n");
|
|
1003
989
|
return {
|
|
1004
990
|
svg: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${minX.toFixed(1)} ${(-maxY).toFixed(1)} ${width.toFixed(1)} ${height.toFixed(1)}" width="${Math.max(width * 4, 400)}" height="${Math.max(height * 4, 400)}">
|
|
1005
|
-
<rect x="${minX.toFixed(1)}" y="${(-maxY).toFixed(1)}" width="${width.toFixed(1)}" height="${height.toFixed(1)}" fill="
|
|
991
|
+
<rect x="${minX.toFixed(1)}" y="${(-maxY).toFixed(1)}" width="${width.toFixed(1)}" height="${height.toFixed(1)}" fill="${palette.background}"/>
|
|
992
|
+
${hatchDefs(palette)}
|
|
1006
993
|
${body}
|
|
1007
994
|
</svg>`,
|
|
1008
995
|
width,
|
|
@@ -1011,136 +998,6 @@ ${body}
|
|
|
1011
998
|
pathCount
|
|
1012
999
|
};
|
|
1013
1000
|
}
|
|
1014
|
-
const DEFAULT_THICKNESS_INSPECTION_OPTIONS = {
|
|
1015
|
-
minThickness: 1.2,
|
|
1016
|
-
warnThickness: 2,
|
|
1017
|
-
maxThickness: 6,
|
|
1018
|
-
maxSamplesPerObject: 5e3
|
|
1019
|
-
};
|
|
1020
|
-
const THICKNESS_COLORS = {
|
|
1021
|
-
critical: [255, 28, 28],
|
|
1022
|
-
warning: [255, 150, 0],
|
|
1023
|
-
ok: [60, 220, 90],
|
|
1024
|
-
thick: [70, 145, 255],
|
|
1025
|
-
unknown: [90, 90, 90]
|
|
1026
|
-
};
|
|
1027
|
-
function finitePositive(value, fallback, label) {
|
|
1028
|
-
if (value === void 0) return fallback;
|
|
1029
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
1030
|
-
throw new Error(`${label} must be a positive finite number.`);
|
|
1031
|
-
}
|
|
1032
|
-
return value;
|
|
1033
|
-
}
|
|
1034
|
-
function resolveThicknessInspectionOptions(raw = {}) {
|
|
1035
|
-
const minThickness = finitePositive(raw.minThickness, DEFAULT_THICKNESS_INSPECTION_OPTIONS.minThickness, "minThickness");
|
|
1036
|
-
const warnThickness = finitePositive(raw.warnThickness, DEFAULT_THICKNESS_INSPECTION_OPTIONS.warnThickness, "warnThickness");
|
|
1037
|
-
const maxThickness = finitePositive(raw.maxThickness, DEFAULT_THICKNESS_INSPECTION_OPTIONS.maxThickness, "maxThickness");
|
|
1038
|
-
const maxSamplesPerObject = finitePositive(
|
|
1039
|
-
raw.maxSamplesPerObject,
|
|
1040
|
-
DEFAULT_THICKNESS_INSPECTION_OPTIONS.maxSamplesPerObject,
|
|
1041
|
-
"maxSamplesPerObject"
|
|
1042
|
-
);
|
|
1043
|
-
if (minThickness > warnThickness) {
|
|
1044
|
-
throw new Error("minThickness must be less than or equal to warnThickness.");
|
|
1045
|
-
}
|
|
1046
|
-
if (warnThickness > maxThickness) {
|
|
1047
|
-
throw new Error("warnThickness must be less than or equal to maxThickness.");
|
|
1048
|
-
}
|
|
1049
|
-
return {
|
|
1050
|
-
minThickness,
|
|
1051
|
-
warnThickness,
|
|
1052
|
-
maxThickness,
|
|
1053
|
-
maxSamplesPerObject: Math.max(1, Math.floor(maxSamplesPerObject))
|
|
1054
|
-
};
|
|
1055
|
-
}
|
|
1056
|
-
function lerp(a, b, t) {
|
|
1057
|
-
return a + (b - a) * Math.max(0, Math.min(1, t));
|
|
1058
|
-
}
|
|
1059
|
-
function lerpColor$1(a, b, t) {
|
|
1060
|
-
return [Math.round(lerp(a[0], b[0], t)), Math.round(lerp(a[1], b[1], t)), Math.round(lerp(a[2], b[2], t))];
|
|
1061
|
-
}
|
|
1062
|
-
function thicknessClass(thickness, options) {
|
|
1063
|
-
if (thickness == null || !Number.isFinite(thickness) || thickness <= 0) return "unknown";
|
|
1064
|
-
if (thickness <= options.minThickness) return "critical";
|
|
1065
|
-
if (thickness <= options.warnThickness) return "warning";
|
|
1066
|
-
if (thickness <= options.maxThickness) return "ok";
|
|
1067
|
-
return "thick";
|
|
1068
|
-
}
|
|
1069
|
-
function thicknessColor(thickness, options) {
|
|
1070
|
-
const cls = thicknessClass(thickness, options);
|
|
1071
|
-
if (cls === "unknown") return THICKNESS_COLORS.unknown;
|
|
1072
|
-
if (cls === "critical") return THICKNESS_COLORS.critical;
|
|
1073
|
-
if (cls === "warning") {
|
|
1074
|
-
const span = Math.max(1e-9, options.warnThickness - options.minThickness);
|
|
1075
|
-
return lerpColor$1(THICKNESS_COLORS.critical, THICKNESS_COLORS.warning, ((thickness ?? 0) - options.minThickness) / span);
|
|
1076
|
-
}
|
|
1077
|
-
if (cls === "ok") {
|
|
1078
|
-
const span = Math.max(1e-9, options.maxThickness - options.warnThickness);
|
|
1079
|
-
return lerpColor$1(THICKNESS_COLORS.ok, THICKNESS_COLORS.thick, ((thickness ?? 0) - options.warnThickness) / span);
|
|
1080
|
-
}
|
|
1081
|
-
return THICKNESS_COLORS.thick;
|
|
1082
|
-
}
|
|
1083
|
-
function sampleArea(sample) {
|
|
1084
|
-
const area = sample.area ?? 1;
|
|
1085
|
-
return Number.isFinite(area) && area > 0 ? area : 1;
|
|
1086
|
-
}
|
|
1087
|
-
function weightedQuantile(samples, q) {
|
|
1088
|
-
if (samples.length === 0) return null;
|
|
1089
|
-
const sorted = [...samples].sort((a, b) => a.thickness - b.thickness);
|
|
1090
|
-
const totalArea = sorted.reduce((sum, sample) => sum + sample.area, 0);
|
|
1091
|
-
const target = totalArea * Math.max(0, Math.min(1, q));
|
|
1092
|
-
let cumulative = 0;
|
|
1093
|
-
for (const sample of sorted) {
|
|
1094
|
-
cumulative += sample.area;
|
|
1095
|
-
if (cumulative >= target) return sample.thickness;
|
|
1096
|
-
}
|
|
1097
|
-
return sorted[sorted.length - 1].thickness;
|
|
1098
|
-
}
|
|
1099
|
-
function percent(part, total) {
|
|
1100
|
-
if (total <= 0) return 0;
|
|
1101
|
-
return part / total * 100;
|
|
1102
|
-
}
|
|
1103
|
-
function summarizeThicknessSamples(samples, options) {
|
|
1104
|
-
const resolved = [];
|
|
1105
|
-
let totalArea = 0;
|
|
1106
|
-
let resolvedArea = 0;
|
|
1107
|
-
let unresolvedArea = 0;
|
|
1108
|
-
let criticalArea = 0;
|
|
1109
|
-
let warningArea = 0;
|
|
1110
|
-
let weightedSum = 0;
|
|
1111
|
-
for (const sample of samples) {
|
|
1112
|
-
const area = sampleArea(sample);
|
|
1113
|
-
totalArea += area;
|
|
1114
|
-
const value = sample.thickness;
|
|
1115
|
-
if (value == null || !Number.isFinite(value) || value <= 0) {
|
|
1116
|
-
unresolvedArea += area;
|
|
1117
|
-
continue;
|
|
1118
|
-
}
|
|
1119
|
-
resolved.push({ thickness: value, area });
|
|
1120
|
-
resolvedArea += area;
|
|
1121
|
-
weightedSum += value * area;
|
|
1122
|
-
if (value <= options.minThickness) {
|
|
1123
|
-
criticalArea += area;
|
|
1124
|
-
} else if (value <= options.warnThickness) {
|
|
1125
|
-
warningArea += area;
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
const values = resolved.map((sample) => sample.thickness);
|
|
1129
|
-
return {
|
|
1130
|
-
sampleCount: samples.length,
|
|
1131
|
-
resolvedCount: resolved.length,
|
|
1132
|
-
unresolvedCount: samples.length - resolved.length,
|
|
1133
|
-
minThickness: values.length > 0 ? Math.min(...values) : null,
|
|
1134
|
-
p05Thickness: weightedQuantile(resolved, 0.05),
|
|
1135
|
-
medianThickness: weightedQuantile(resolved, 0.5),
|
|
1136
|
-
meanThickness: resolvedArea > 0 ? weightedSum / resolvedArea : null,
|
|
1137
|
-
maxThickness: values.length > 0 ? Math.max(...values) : null,
|
|
1138
|
-
criticalAreaPercent: percent(criticalArea, resolvedArea),
|
|
1139
|
-
warningAreaPercent: percent(warningArea, resolvedArea),
|
|
1140
|
-
belowWarnAreaPercent: percent(criticalArea + warningArea, resolvedArea),
|
|
1141
|
-
unresolvedAreaPercent: percent(unresolvedArea, totalArea)
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
1001
|
const MIN_TRIANGLE_AREA = 1e-12;
|
|
1145
1002
|
const R2_ALPHA = 0.7548776662466927;
|
|
1146
1003
|
const R2_BETA = 0.5698402909980532;
|
|
@@ -1249,22 +1106,45 @@ function geometryMaxDimension(geometry) {
|
|
|
1249
1106
|
box.getSize(size);
|
|
1250
1107
|
return Math.max(1, size.x, size.y, size.z);
|
|
1251
1108
|
}
|
|
1252
|
-
function firstOppositeSurfaceDistance(raycaster,
|
|
1109
|
+
function firstOppositeSurfaceDistance(raycaster, rayTargetMeshes, jumpableMeshes, point, direction, epsilon, far, contactTolerance) {
|
|
1253
1110
|
const origin = point.clone().addScaledVector(direction, epsilon);
|
|
1254
1111
|
raycaster.set(origin, direction);
|
|
1255
1112
|
raycaster.near = epsilon;
|
|
1256
1113
|
raycaster.far = far;
|
|
1257
|
-
const
|
|
1258
|
-
|
|
1114
|
+
const hits = raycaster.intersectObjects(rayTargetMeshes, false);
|
|
1115
|
+
for (const hit of hits) {
|
|
1116
|
+
if (hit.distance <= epsilon) continue;
|
|
1117
|
+
if (hit.distance <= contactTolerance && jumpableMeshes.has(hit.object)) continue;
|
|
1118
|
+
return hit.distance + epsilon;
|
|
1119
|
+
}
|
|
1120
|
+
return null;
|
|
1259
1121
|
}
|
|
1260
|
-
function triangleThickness(raycaster,
|
|
1261
|
-
const forward = firstOppositeSurfaceDistance(
|
|
1262
|
-
|
|
1122
|
+
function triangleThickness(raycaster, rayTargetMeshes, jumpableMeshes, centroid, normal, epsilon, far, contactTolerance) {
|
|
1123
|
+
const forward = firstOppositeSurfaceDistance(
|
|
1124
|
+
raycaster,
|
|
1125
|
+
rayTargetMeshes,
|
|
1126
|
+
jumpableMeshes,
|
|
1127
|
+
centroid,
|
|
1128
|
+
normal,
|
|
1129
|
+
epsilon,
|
|
1130
|
+
far,
|
|
1131
|
+
contactTolerance
|
|
1132
|
+
);
|
|
1133
|
+
const backward = firstOppositeSurfaceDistance(
|
|
1134
|
+
raycaster,
|
|
1135
|
+
rayTargetMeshes,
|
|
1136
|
+
jumpableMeshes,
|
|
1137
|
+
centroid,
|
|
1138
|
+
normal.clone().negate(),
|
|
1139
|
+
epsilon,
|
|
1140
|
+
far,
|
|
1141
|
+
contactTolerance
|
|
1142
|
+
);
|
|
1263
1143
|
if (forward == null) return backward;
|
|
1264
1144
|
if (backward == null) return forward;
|
|
1265
1145
|
return Math.min(forward, backward);
|
|
1266
1146
|
}
|
|
1267
|
-
function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}) {
|
|
1147
|
+
function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}, context = {}) {
|
|
1268
1148
|
const options = resolveThicknessInspectionOptions(rawOptions);
|
|
1269
1149
|
const geometry = cloneGeometryForFaceColors(sourceGeometry);
|
|
1270
1150
|
const position = geometry.getAttribute("position");
|
|
@@ -1282,7 +1162,8 @@ function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}) {
|
|
|
1282
1162
|
const triangleCount = Math.floor(position.count / 3);
|
|
1283
1163
|
const surfaceTriangles = readSurfaceTriangles(position);
|
|
1284
1164
|
const surfaceSamples = sampleSurfaceTriangles(surfaceTriangles, options.maxSamplesPerObject);
|
|
1285
|
-
const
|
|
1165
|
+
const connectedGeometries = context.connectedGeometries ?? [];
|
|
1166
|
+
const maxDim = Math.max(geometryMaxDimension(geometry), ...connectedGeometries.map(geometryMaxDimension));
|
|
1286
1167
|
const epsilon = Math.max(1e-4, maxDim * 1e-6);
|
|
1287
1168
|
const far = Math.max(maxDim * 4, options.maxThickness * 4, 1);
|
|
1288
1169
|
const colors = new Float32Array(position.count * 3);
|
|
@@ -1291,7 +1172,15 @@ function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}) {
|
|
|
1291
1172
|
const pointSamples = [];
|
|
1292
1173
|
const warnings = [];
|
|
1293
1174
|
const rayMaterial = new MeshBasicMaterial({ side: DoubleSide });
|
|
1294
|
-
const
|
|
1175
|
+
const rayTargets = [
|
|
1176
|
+
{ mesh: new Mesh(geometry, rayMaterial), jumpable: false },
|
|
1177
|
+
...connectedGeometries.map((connectedGeometry) => ({
|
|
1178
|
+
mesh: new Mesh(connectedGeometry, rayMaterial),
|
|
1179
|
+
jumpable: true
|
|
1180
|
+
}))
|
|
1181
|
+
];
|
|
1182
|
+
const rayTargetMeshes = rayTargets.map((target) => target.mesh);
|
|
1183
|
+
const jumpableMeshes = new Set(rayTargets.filter((target) => target.jumpable).map((target) => target.mesh));
|
|
1295
1184
|
const raycaster = new Raycaster();
|
|
1296
1185
|
if (surfaceTriangles.length === 0) {
|
|
1297
1186
|
warnings.push("No non-degenerate triangle surface was available for thickness sampling.");
|
|
@@ -1303,7 +1192,16 @@ function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}) {
|
|
|
1303
1192
|
const sampledTriangleIndexes = /* @__PURE__ */ new Set();
|
|
1304
1193
|
for (const sample of surfaceSamples) {
|
|
1305
1194
|
sampledTriangleIndexes.add(sample.triangle.index);
|
|
1306
|
-
const thickness = triangleThickness(
|
|
1195
|
+
const thickness = triangleThickness(
|
|
1196
|
+
raycaster,
|
|
1197
|
+
rayTargetMeshes,
|
|
1198
|
+
jumpableMeshes,
|
|
1199
|
+
sample.position,
|
|
1200
|
+
sample.normal,
|
|
1201
|
+
epsilon,
|
|
1202
|
+
far,
|
|
1203
|
+
options.contactTolerance
|
|
1204
|
+
);
|
|
1307
1205
|
samples.push({ thickness, area: sample.area });
|
|
1308
1206
|
const previous = triangleThicknessValues[sample.triangle.index];
|
|
1309
1207
|
if (previous === void 0 || previous == null || thickness != null && thickness < previous) {
|
|
@@ -1340,79 +1238,6 @@ function analyzeThicknessGeometry(sourceGeometry, rawOptions = {}) {
|
|
|
1340
1238
|
warnings
|
|
1341
1239
|
};
|
|
1342
1240
|
}
|
|
1343
|
-
const DEFAULT_ROUGHNESS_INSPECTION_OPTIONS = {
|
|
1344
|
-
smoothAngleDeg: 5,
|
|
1345
|
-
sharpAngleDeg: 30,
|
|
1346
|
-
harshAngleDeg: 90,
|
|
1347
|
-
maxSamplesPerObject: 5e3
|
|
1348
|
-
};
|
|
1349
|
-
const ROUGHNESS_COLORS = {
|
|
1350
|
-
smooth: [62, 72, 84],
|
|
1351
|
-
moderate: [255, 214, 0],
|
|
1352
|
-
sharp: [255, 124, 34],
|
|
1353
|
-
harsh: [255, 42, 96]
|
|
1354
|
-
};
|
|
1355
|
-
function resolveRoughnessInspectionOptions(raw = {}) {
|
|
1356
|
-
const options = {
|
|
1357
|
-
smoothAngleDeg: raw.smoothAngleDeg ?? DEFAULT_ROUGHNESS_INSPECTION_OPTIONS.smoothAngleDeg,
|
|
1358
|
-
sharpAngleDeg: raw.sharpAngleDeg ?? DEFAULT_ROUGHNESS_INSPECTION_OPTIONS.sharpAngleDeg,
|
|
1359
|
-
harshAngleDeg: raw.harshAngleDeg ?? DEFAULT_ROUGHNESS_INSPECTION_OPTIONS.harshAngleDeg,
|
|
1360
|
-
maxSamplesPerObject: raw.maxSamplesPerObject ?? DEFAULT_ROUGHNESS_INSPECTION_OPTIONS.maxSamplesPerObject
|
|
1361
|
-
};
|
|
1362
|
-
if (!Number.isFinite(options.smoothAngleDeg) || options.smoothAngleDeg < 0) {
|
|
1363
|
-
throw new Error(`smoothAngleDeg must be a finite non-negative angle (got ${options.smoothAngleDeg}).`);
|
|
1364
|
-
}
|
|
1365
|
-
if (!Number.isFinite(options.sharpAngleDeg) || options.sharpAngleDeg <= options.smoothAngleDeg) {
|
|
1366
|
-
throw new Error(`sharpAngleDeg must be greater than smoothAngleDeg (got ${options.sharpAngleDeg}).`);
|
|
1367
|
-
}
|
|
1368
|
-
if (!Number.isFinite(options.harshAngleDeg) || options.harshAngleDeg <= options.sharpAngleDeg || options.harshAngleDeg > 180) {
|
|
1369
|
-
throw new Error(`harshAngleDeg must be greater than sharpAngleDeg and <= 180 (got ${options.harshAngleDeg}).`);
|
|
1370
|
-
}
|
|
1371
|
-
if (!Number.isFinite(options.maxSamplesPerObject) || options.maxSamplesPerObject <= 0) {
|
|
1372
|
-
throw new Error(`maxSamplesPerObject must be a positive finite number (got ${options.maxSamplesPerObject}).`);
|
|
1373
|
-
}
|
|
1374
|
-
return {
|
|
1375
|
-
...options,
|
|
1376
|
-
maxSamplesPerObject: Math.max(1, Math.floor(options.maxSamplesPerObject))
|
|
1377
|
-
};
|
|
1378
|
-
}
|
|
1379
|
-
function roughnessClassForAngle(angleDeg, options) {
|
|
1380
|
-
if (angleDeg >= options.harshAngleDeg) return "harsh";
|
|
1381
|
-
if (angleDeg >= options.sharpAngleDeg) return "sharp";
|
|
1382
|
-
if (angleDeg >= options.smoothAngleDeg) return "moderate";
|
|
1383
|
-
return "smooth";
|
|
1384
|
-
}
|
|
1385
|
-
function roughnessScoreForAngle(angleDeg, options) {
|
|
1386
|
-
if (angleDeg < options.sharpAngleDeg) return 0;
|
|
1387
|
-
if (angleDeg < options.harshAngleDeg) {
|
|
1388
|
-
return MathUtils.lerp(0.48, 0.82, (angleDeg - options.sharpAngleDeg) / (options.harshAngleDeg - options.sharpAngleDeg));
|
|
1389
|
-
}
|
|
1390
|
-
return 1;
|
|
1391
|
-
}
|
|
1392
|
-
function roughnessColorForAngle(angleDeg, options) {
|
|
1393
|
-
const cls = roughnessClassForAngle(angleDeg, options);
|
|
1394
|
-
if (cls === "smooth" || cls === "harsh") return ROUGHNESS_COLORS[cls];
|
|
1395
|
-
if (cls === "moderate") {
|
|
1396
|
-
return lerpRgb(
|
|
1397
|
-
ROUGHNESS_COLORS.moderate,
|
|
1398
|
-
ROUGHNESS_COLORS.sharp,
|
|
1399
|
-
(angleDeg - options.smoothAngleDeg) / (options.sharpAngleDeg - options.smoothAngleDeg)
|
|
1400
|
-
);
|
|
1401
|
-
}
|
|
1402
|
-
return lerpRgb(
|
|
1403
|
-
ROUGHNESS_COLORS.sharp,
|
|
1404
|
-
ROUGHNESS_COLORS.harsh,
|
|
1405
|
-
(angleDeg - options.sharpAngleDeg) / (options.harshAngleDeg - options.sharpAngleDeg)
|
|
1406
|
-
);
|
|
1407
|
-
}
|
|
1408
|
-
function lerpRgb(a, b, t) {
|
|
1409
|
-
const clamped = MathUtils.clamp(t, 0, 1);
|
|
1410
|
-
return [
|
|
1411
|
-
Math.round(MathUtils.lerp(a[0], b[0], clamped)),
|
|
1412
|
-
Math.round(MathUtils.lerp(a[1], b[1], clamped)),
|
|
1413
|
-
Math.round(MathUtils.lerp(a[2], b[2], clamped))
|
|
1414
|
-
];
|
|
1415
|
-
}
|
|
1416
1241
|
function emptyRoughnessSummary() {
|
|
1417
1242
|
return {
|
|
1418
1243
|
triangleCount: 0,
|
|
@@ -1747,12 +1572,25 @@ function objectConnectivityEntry(object) {
|
|
|
1747
1572
|
shape: object.shape,
|
|
1748
1573
|
min: object.min,
|
|
1749
1574
|
max: object.max,
|
|
1575
|
+
positions: object.positions,
|
|
1750
1576
|
groupName: object.groupName,
|
|
1751
1577
|
treePath: object.treePath,
|
|
1752
1578
|
mock: object.mock,
|
|
1753
1579
|
bodyCount: 1
|
|
1754
1580
|
};
|
|
1755
1581
|
}
|
|
1582
|
+
function componentPositions(sourcePositions, component) {
|
|
1583
|
+
const out = new Float32Array(component.triangleIndexes.length * 9);
|
|
1584
|
+
let outOffset = 0;
|
|
1585
|
+
for (const triangleIndex of component.triangleIndexes) {
|
|
1586
|
+
const sourceOffset = triangleIndex * 9;
|
|
1587
|
+
for (let offset = 0; offset < 9; offset += 1) {
|
|
1588
|
+
out[outOffset + offset] = sourcePositions[sourceOffset + offset];
|
|
1589
|
+
}
|
|
1590
|
+
outOffset += 9;
|
|
1591
|
+
}
|
|
1592
|
+
return out;
|
|
1593
|
+
}
|
|
1756
1594
|
function buildMeshBodyConnectivityInput(objects, options) {
|
|
1757
1595
|
const entries = [];
|
|
1758
1596
|
const bodyIdsByObjectId = /* @__PURE__ */ new Map();
|
|
@@ -1776,6 +1614,7 @@ function buildMeshBodyConnectivityInput(objects, options) {
|
|
|
1776
1614
|
shape: options.bodyShape(object, component),
|
|
1777
1615
|
min: component.bbox.min,
|
|
1778
1616
|
max: component.bbox.max,
|
|
1617
|
+
positions: componentPositions(object.positions, component),
|
|
1779
1618
|
groupName: object.groupName,
|
|
1780
1619
|
treePath: object.treePath,
|
|
1781
1620
|
mock: object.mock,
|
|
@@ -1854,6 +1693,9 @@ class EmptyInspectionShape {
|
|
|
1854
1693
|
const COLLISION_SOURCE_OPACITY = 0.22;
|
|
1855
1694
|
const COLLISION_SOURCE_COLOR = [180, 200, 220];
|
|
1856
1695
|
const COLLISION_HIGHLIGHT_COLOR = [255, 68, 16];
|
|
1696
|
+
const FLOATING_HIGHLIGHT_COLOR = [255, 68, 16];
|
|
1697
|
+
const FLOATING_CONTEXT_COLOR = [38, 49, 58];
|
|
1698
|
+
const FLOATING_HIDDEN_COLOR = [0, 0, 0];
|
|
1857
1699
|
const COLLISION_PALETTE = [
|
|
1858
1700
|
COLLISION_HIGHLIGHT_COLOR,
|
|
1859
1701
|
[0, 204, 255],
|
|
@@ -1894,18 +1736,13 @@ const MASK_PALETTE = [
|
|
|
1894
1736
|
[0, 0, 128],
|
|
1895
1737
|
[128, 128, 128]
|
|
1896
1738
|
];
|
|
1897
|
-
function
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
return new Float32Array(position.array);
|
|
1903
|
-
} finally {
|
|
1904
|
-
geometry.solid.dispose();
|
|
1905
|
-
geometry.edges.dispose();
|
|
1906
|
-
}
|
|
1739
|
+
function cloneGeometryPositions(geometry) {
|
|
1740
|
+
if (!geometry) return void 0;
|
|
1741
|
+
const position = geometry.getAttribute("position");
|
|
1742
|
+
if (!position) return void 0;
|
|
1743
|
+
return new Float32Array(position.array);
|
|
1907
1744
|
}
|
|
1908
|
-
function meshConnectivitySource(entry) {
|
|
1745
|
+
function meshConnectivitySource(entry, positions) {
|
|
1909
1746
|
const bb = entry.shape.boundingBox();
|
|
1910
1747
|
return {
|
|
1911
1748
|
id: entry.source.id,
|
|
@@ -1916,7 +1753,7 @@ function meshConnectivitySource(entry) {
|
|
|
1916
1753
|
groupName: entry.source.groupName,
|
|
1917
1754
|
treePath: entry.source.treePath,
|
|
1918
1755
|
mock: entry.source.mock,
|
|
1919
|
-
positions
|
|
1756
|
+
positions
|
|
1920
1757
|
};
|
|
1921
1758
|
}
|
|
1922
1759
|
function summarizeSceneGeometry(entries) {
|
|
@@ -2268,6 +2105,37 @@ function renderCurrentNormals(session) {
|
|
|
2268
2105
|
normalMaterial.dispose();
|
|
2269
2106
|
}
|
|
2270
2107
|
}
|
|
2108
|
+
function renderCurrentZebra(session) {
|
|
2109
|
+
const r = getRenderer(session.size, session.pixelRatio);
|
|
2110
|
+
const zebraMaterial = new ShaderMaterial({
|
|
2111
|
+
uniforms: {
|
|
2112
|
+
uAccentColor: { value: new Color(ZEBRA_ACCENT_COLOR) },
|
|
2113
|
+
uDarkColor: { value: new Color(ZEBRA_DARK_COLOR) },
|
|
2114
|
+
uLightColor: { value: new Color(ZEBRA_LIGHT_COLOR) },
|
|
2115
|
+
uStripeScale: { value: ZEBRA_STRIPE_SCALE },
|
|
2116
|
+
uStripeSoftness: { value: ZEBRA_STRIPE_SOFTNESS }
|
|
2117
|
+
},
|
|
2118
|
+
vertexShader: ZEBRA_STRIPE_VERTEX_SHADER,
|
|
2119
|
+
fragmentShader: ZEBRA_STRIPE_FRAGMENT_SHADER,
|
|
2120
|
+
side: DoubleSide
|
|
2121
|
+
});
|
|
2122
|
+
zebraMaterial.toneMapped = false;
|
|
2123
|
+
const prevOverride = session.scene.overrideMaterial;
|
|
2124
|
+
session.scene.overrideMaterial = zebraMaterial;
|
|
2125
|
+
try {
|
|
2126
|
+
return withSolidOnlyVisibility(
|
|
2127
|
+
session,
|
|
2128
|
+
() => withTemporarySceneBackground(session, new Color(0), () => {
|
|
2129
|
+
updateSdfRaymarchUniforms(session);
|
|
2130
|
+
r.render(session.scene, session.camera);
|
|
2131
|
+
return captureRenderedPng(session.size);
|
|
2132
|
+
})
|
|
2133
|
+
);
|
|
2134
|
+
} finally {
|
|
2135
|
+
session.scene.overrideMaterial = prevOverride;
|
|
2136
|
+
zebraMaterial.dispose();
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2271
2139
|
function maskColorForIndex(index) {
|
|
2272
2140
|
return MASK_PALETTE[(index - 1) % MASK_PALETTE.length];
|
|
2273
2141
|
}
|
|
@@ -2371,6 +2239,47 @@ function analyzeSessionConnectivity(session) {
|
|
|
2371
2239
|
}
|
|
2372
2240
|
return session.physicalConnectivity;
|
|
2373
2241
|
}
|
|
2242
|
+
function addContactNeighbor(target, sourceId, targetId) {
|
|
2243
|
+
let neighbors = target.get(sourceId);
|
|
2244
|
+
if (!neighbors) {
|
|
2245
|
+
neighbors = /* @__PURE__ */ new Set();
|
|
2246
|
+
target.set(sourceId, neighbors);
|
|
2247
|
+
}
|
|
2248
|
+
neighbors.add(targetId);
|
|
2249
|
+
}
|
|
2250
|
+
function baseObjectIdForConnectivityEntry(entryId, renderableIds) {
|
|
2251
|
+
if (renderableIds.has(entryId)) return entryId;
|
|
2252
|
+
const bodySuffixIndex = entryId.lastIndexOf(":body:");
|
|
2253
|
+
if (bodySuffixIndex <= 0) return null;
|
|
2254
|
+
const baseId = entryId.slice(0, bodySuffixIndex);
|
|
2255
|
+
return renderableIds.has(baseId) ? baseId : null;
|
|
2256
|
+
}
|
|
2257
|
+
function buildThicknessRaycastConnectivityContext(session) {
|
|
2258
|
+
const report = analyzeSessionConnectivity(session);
|
|
2259
|
+
const renderableById = new Map(
|
|
2260
|
+
session.renderables.filter((renderable) => !renderable.sdfRaymarch).map((renderable) => [renderable.id, renderable])
|
|
2261
|
+
);
|
|
2262
|
+
const renderableIds = new Set(renderableById.keys());
|
|
2263
|
+
const neighborIdsByObjectId = /* @__PURE__ */ new Map();
|
|
2264
|
+
for (const edge of report.edges) {
|
|
2265
|
+
const sourceId = baseObjectIdForConnectivityEntry(edge.sourceId, renderableIds);
|
|
2266
|
+
const targetId = baseObjectIdForConnectivityEntry(edge.targetId, renderableIds);
|
|
2267
|
+
if (!sourceId || !targetId || sourceId === targetId) continue;
|
|
2268
|
+
addContactNeighbor(neighborIdsByObjectId, sourceId, targetId);
|
|
2269
|
+
addContactNeighbor(neighborIdsByObjectId, targetId, sourceId);
|
|
2270
|
+
}
|
|
2271
|
+
return { neighborIdsByObjectId, renderableById };
|
|
2272
|
+
}
|
|
2273
|
+
function connectedThicknessGeometriesFor(context, source) {
|
|
2274
|
+
const neighborIds = context.neighborIdsByObjectId.get(source.id);
|
|
2275
|
+
if (!neighborIds) return [];
|
|
2276
|
+
const geometries = [];
|
|
2277
|
+
for (const neighborId of neighborIds) {
|
|
2278
|
+
const renderable = context.renderableById.get(neighborId);
|
|
2279
|
+
if (renderable) geometries.push(renderable.solid.geometry);
|
|
2280
|
+
}
|
|
2281
|
+
return geometries;
|
|
2282
|
+
}
|
|
2374
2283
|
function decorateConnectivityReport(report) {
|
|
2375
2284
|
const components = report.components.map((component) => {
|
|
2376
2285
|
const color = maskColorForIndex(component.index);
|
|
@@ -2539,6 +2448,151 @@ function renderCurrentDistance(session) {
|
|
|
2539
2448
|
});
|
|
2540
2449
|
}
|
|
2541
2450
|
}
|
|
2451
|
+
function analyzeSessionFloating(session) {
|
|
2452
|
+
if (!session.floatingReport) {
|
|
2453
|
+
session.floatingReport = analyzeFloatingInspection(session.connectivityEntries, { groundZ: session.floatingGroundZ });
|
|
2454
|
+
}
|
|
2455
|
+
return session.floatingReport;
|
|
2456
|
+
}
|
|
2457
|
+
function decorateFloatingReport(report) {
|
|
2458
|
+
const components = report.components.map((component) => {
|
|
2459
|
+
const color = component.isFloating ? FLOATING_HIGHLIGHT_COLOR : FLOATING_HIDDEN_COLOR;
|
|
2460
|
+
return {
|
|
2461
|
+
...component,
|
|
2462
|
+
color,
|
|
2463
|
+
hex: colorHex(color)
|
|
2464
|
+
};
|
|
2465
|
+
});
|
|
2466
|
+
const objectByComponentIndex = new Map(components.map((component) => [component.index, component]));
|
|
2467
|
+
const objects = report.objects.map((object) => {
|
|
2468
|
+
var _a;
|
|
2469
|
+
const color = ((_a = objectByComponentIndex.get(object.componentIndex)) == null ? void 0 : _a.color) ?? FLOATING_HIDDEN_COLOR;
|
|
2470
|
+
return {
|
|
2471
|
+
...object,
|
|
2472
|
+
color,
|
|
2473
|
+
hex: colorHex(color)
|
|
2474
|
+
};
|
|
2475
|
+
});
|
|
2476
|
+
return {
|
|
2477
|
+
method: report.method,
|
|
2478
|
+
options: report.options,
|
|
2479
|
+
rootComponentIndex: report.rootComponentIndex,
|
|
2480
|
+
objectCount: report.objectCount,
|
|
2481
|
+
componentCount: report.componentCount,
|
|
2482
|
+
floatingComponentCount: report.floatingComponentCount,
|
|
2483
|
+
floatingObjectCount: report.floatingObjectCount,
|
|
2484
|
+
floatingBodyCount: report.floatingBodyCount,
|
|
2485
|
+
objects,
|
|
2486
|
+
components,
|
|
2487
|
+
contacts: report.contacts,
|
|
2488
|
+
warnings: report.warnings,
|
|
2489
|
+
style: {
|
|
2490
|
+
highlightColor: FLOATING_HIGHLIGHT_COLOR,
|
|
2491
|
+
highlightHex: colorHex(FLOATING_HIGHLIGHT_COLOR),
|
|
2492
|
+
contextColor: FLOATING_CONTEXT_COLOR,
|
|
2493
|
+
contextHex: colorHex(FLOATING_CONTEXT_COLOR),
|
|
2494
|
+
hiddenColor: FLOATING_HIDDEN_COLOR,
|
|
2495
|
+
hiddenHex: colorHex(FLOATING_HIDDEN_COLOR)
|
|
2496
|
+
}
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
function renderCurrentFloating(session) {
|
|
2500
|
+
const r = getRenderer(session.size, session.pixelRatio);
|
|
2501
|
+
const report = decorateFloatingReport(analyzeSessionFloating(session));
|
|
2502
|
+
const byId = new Map(report.objects.map((object) => [object.id, object]));
|
|
2503
|
+
const vertexColorsById = session.connectivityBodyInput ? meshVertexColorBuffersFor(session.connectivityBodyInput, (entryId) => {
|
|
2504
|
+
var _a;
|
|
2505
|
+
return rgbFloats(((_a = byId.get(entryId)) == null ? void 0 : _a.color) ?? FLOATING_HIDDEN_COLOR);
|
|
2506
|
+
}) : /* @__PURE__ */ new Map();
|
|
2507
|
+
const replacements = session.renderables.map((renderable) => {
|
|
2508
|
+
var _a;
|
|
2509
|
+
const object = byId.get(renderable.id);
|
|
2510
|
+
const colors = vertexColorsById.get(renderable.id);
|
|
2511
|
+
const vertexGeometry = colors ? geometryWithVisibleVertexColors(renderable.solid.geometry, colors) : null;
|
|
2512
|
+
const previousMaterial = renderable.solid.material;
|
|
2513
|
+
const previousGeometry = renderable.solid.geometry;
|
|
2514
|
+
const previousVisible = renderable.solid.visible;
|
|
2515
|
+
const previousWireVisible = renderable.wire.visible;
|
|
2516
|
+
const previousShellVisible = (_a = renderable.shell) == null ? void 0 : _a.visible;
|
|
2517
|
+
const contextMaterial = new MeshBasicMaterial({
|
|
2518
|
+
color: colorHex(FLOATING_CONTEXT_COLOR),
|
|
2519
|
+
transparent: true,
|
|
2520
|
+
opacity: 0.18,
|
|
2521
|
+
side: DoubleSide,
|
|
2522
|
+
depthWrite: false,
|
|
2523
|
+
clippingPlanes: renderable.solidMaterial.clippingPlanes ?? null
|
|
2524
|
+
});
|
|
2525
|
+
contextMaterial.toneMapped = false;
|
|
2526
|
+
const highlightMaterial = new MeshBasicMaterial({
|
|
2527
|
+
color: vertexGeometry ? 16777215 : colorHex(FLOATING_HIGHLIGHT_COLOR),
|
|
2528
|
+
vertexColors: Boolean(vertexGeometry),
|
|
2529
|
+
side: DoubleSide,
|
|
2530
|
+
depthWrite: false,
|
|
2531
|
+
clippingPlanes: renderable.solidMaterial.clippingPlanes ?? null,
|
|
2532
|
+
polygonOffset: true,
|
|
2533
|
+
polygonOffsetFactor: -1,
|
|
2534
|
+
polygonOffsetUnits: -1
|
|
2535
|
+
});
|
|
2536
|
+
highlightMaterial.toneMapped = false;
|
|
2537
|
+
renderable.solid.material = contextMaterial;
|
|
2538
|
+
renderable.solid.geometry = previousGeometry;
|
|
2539
|
+
renderable.solid.visible = true;
|
|
2540
|
+
const highlightGeometry = vertexGeometry ?? ((object == null ? void 0 : object.isFloating) === true ? previousGeometry : null);
|
|
2541
|
+
const highlightMesh = highlightGeometry ? new Mesh(highlightGeometry, highlightMaterial) : null;
|
|
2542
|
+
if (highlightMesh) {
|
|
2543
|
+
highlightMesh.renderOrder = 5;
|
|
2544
|
+
highlightMesh.raycast = () => null;
|
|
2545
|
+
renderable.root.add(highlightMesh);
|
|
2546
|
+
}
|
|
2547
|
+
renderable.wire.visible = false;
|
|
2548
|
+
if (renderable.shell) renderable.shell.visible = false;
|
|
2549
|
+
return {
|
|
2550
|
+
renderable,
|
|
2551
|
+
previousMaterial,
|
|
2552
|
+
previousGeometry,
|
|
2553
|
+
previousVisible,
|
|
2554
|
+
previousWireVisible,
|
|
2555
|
+
previousShellVisible,
|
|
2556
|
+
vertexGeometry,
|
|
2557
|
+
contextMaterial,
|
|
2558
|
+
highlightMaterial,
|
|
2559
|
+
highlightMesh
|
|
2560
|
+
};
|
|
2561
|
+
});
|
|
2562
|
+
try {
|
|
2563
|
+
const png = withTemporarySceneBackground(session, new Color(0), () => {
|
|
2564
|
+
updateSdfRaymarchUniforms(session);
|
|
2565
|
+
r.render(session.scene, session.camera);
|
|
2566
|
+
return captureRenderedPng(session.size);
|
|
2567
|
+
});
|
|
2568
|
+
return { png, report };
|
|
2569
|
+
} finally {
|
|
2570
|
+
replacements.forEach(
|
|
2571
|
+
({
|
|
2572
|
+
renderable,
|
|
2573
|
+
previousMaterial,
|
|
2574
|
+
previousGeometry,
|
|
2575
|
+
previousVisible,
|
|
2576
|
+
previousWireVisible,
|
|
2577
|
+
previousShellVisible,
|
|
2578
|
+
vertexGeometry,
|
|
2579
|
+
contextMaterial,
|
|
2580
|
+
highlightMaterial,
|
|
2581
|
+
highlightMesh
|
|
2582
|
+
}) => {
|
|
2583
|
+
if (highlightMesh) renderable.root.remove(highlightMesh);
|
|
2584
|
+
renderable.solid.material = previousMaterial;
|
|
2585
|
+
renderable.solid.geometry = previousGeometry;
|
|
2586
|
+
renderable.solid.visible = previousVisible;
|
|
2587
|
+
renderable.wire.visible = previousWireVisible;
|
|
2588
|
+
if (renderable.shell && previousShellVisible !== void 0) renderable.shell.visible = previousShellVisible;
|
|
2589
|
+
vertexGeometry == null ? void 0 : vertexGeometry.dispose();
|
|
2590
|
+
contextMaterial.dispose();
|
|
2591
|
+
highlightMaterial.dispose();
|
|
2592
|
+
}
|
|
2593
|
+
);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2542
2596
|
function analyzeSessionCollisions(session) {
|
|
2543
2597
|
if (!session.collisionReport) {
|
|
2544
2598
|
session.collisionReport = analyzeCollisionIntersections(session.collisionEntries);
|
|
@@ -2549,7 +2603,15 @@ function decorateCollisionReport(report) {
|
|
|
2549
2603
|
return {
|
|
2550
2604
|
method: report.method,
|
|
2551
2605
|
options: report.options,
|
|
2606
|
+
broadphase: report.broadphase,
|
|
2552
2607
|
objectCount: report.objectCount,
|
|
2608
|
+
candidatePairCount: report.candidatePairCount,
|
|
2609
|
+
bboxVolumePrunedPairCount: report.bboxVolumePrunedPairCount,
|
|
2610
|
+
testedPairCount: report.testedPairCount,
|
|
2611
|
+
skippedPairCount: report.skippedPairCount,
|
|
2612
|
+
pairLimitSkippedPairCount: report.pairLimitSkippedPairCount,
|
|
2613
|
+
timeBudgetSkippedPairCount: report.timeBudgetSkippedPairCount,
|
|
2614
|
+
exactCheckMs: report.exactCheckMs,
|
|
2553
2615
|
collisionCount: report.collisionCount,
|
|
2554
2616
|
objects: report.objects,
|
|
2555
2617
|
collisions: report.collisions.map((finding) => {
|
|
@@ -2637,6 +2699,26 @@ function renderCurrentCollisions(session) {
|
|
|
2637
2699
|
function inspectionOptionsKey(value) {
|
|
2638
2700
|
return JSON.stringify(value ?? {});
|
|
2639
2701
|
}
|
|
2702
|
+
function scalarInspectableObjectCount(session) {
|
|
2703
|
+
return session.renderables.filter((renderable) => !renderable.sdfRaymarch).length;
|
|
2704
|
+
}
|
|
2705
|
+
function withSceneSampleBudget(session, options, explicitMaxSamplesPerObject) {
|
|
2706
|
+
const sampleBudget = resolveScalarSceneSampleBudget({
|
|
2707
|
+
objectCount: scalarInspectableObjectCount(session),
|
|
2708
|
+
maxSamplesPerObject: options.maxSamplesPerObject,
|
|
2709
|
+
explicitMaxSamplesPerObject
|
|
2710
|
+
});
|
|
2711
|
+
return {
|
|
2712
|
+
options: { ...options, maxSamplesPerObject: sampleBudget.effectiveMaxSamplesPerObject },
|
|
2713
|
+
sampleBudget
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
function maybePushSceneSampleBudgetWarning(warnings, channel, sampleBudget) {
|
|
2717
|
+
if (!sampleBudget.capped) return;
|
|
2718
|
+
warnings.push(
|
|
2719
|
+
`${channel} inspection scene budget lowered maxSamplesPerObject from ${sampleBudget.requestedMaxSamplesPerObject} to ${sampleBudget.effectiveMaxSamplesPerObject} across ${sampleBudget.objectCount} mesh object(s); pass the sample-count flag to override.`
|
|
2720
|
+
);
|
|
2721
|
+
}
|
|
2640
2722
|
function bboxFromGeometry(geometry) {
|
|
2641
2723
|
geometry.computeBoundingBox();
|
|
2642
2724
|
const bbox = geometry.boundingBox;
|
|
@@ -2717,22 +2799,27 @@ function renderScalarPointOverlays(session, overlays) {
|
|
|
2717
2799
|
}
|
|
2718
2800
|
function getSessionThicknessInspection(session, rawOptions) {
|
|
2719
2801
|
var _a;
|
|
2720
|
-
const
|
|
2721
|
-
const
|
|
2802
|
+
const resolvedOptions = resolveThicknessInspectionOptions(rawOptions);
|
|
2803
|
+
const { options, sampleBudget } = withSceneSampleBudget(session, resolvedOptions, (rawOptions == null ? void 0 : rawOptions.maxSamplesPerObject) !== void 0);
|
|
2804
|
+
const optionsKey = inspectionOptionsKey({ options, sampleBudget });
|
|
2722
2805
|
if (((_a = session.thicknessInspection) == null ? void 0 : _a.optionsKey) === optionsKey) return session.thicknessInspection;
|
|
2723
2806
|
const byId = new Map(session.objects.map((obj) => [obj.id, obj]));
|
|
2724
2807
|
const warnings = [];
|
|
2808
|
+
maybePushSceneSampleBudgetWarning(warnings, "Thickness", sampleBudget);
|
|
2725
2809
|
const objects = [];
|
|
2726
2810
|
const cloudObjects = [];
|
|
2727
2811
|
const points = [];
|
|
2728
2812
|
const overlays = [];
|
|
2813
|
+
const raycastConnectivity = buildThicknessRaycastConnectivityContext(session);
|
|
2729
2814
|
session.renderables.forEach((renderable, index) => {
|
|
2730
2815
|
const sourceObject = byId.get(renderable.id);
|
|
2731
2816
|
if (renderable.sdfRaymarch) {
|
|
2732
2817
|
warnings.push(`${renderable.name}: SDF raymarch objects are not included in mesh thickness inspection.`);
|
|
2733
2818
|
return;
|
|
2734
2819
|
}
|
|
2735
|
-
const analysis = analyzeThicknessGeometry(renderable.solid.geometry, options
|
|
2820
|
+
const analysis = analyzeThicknessGeometry(renderable.solid.geometry, options, {
|
|
2821
|
+
connectedGeometries: connectedThicknessGeometriesFor(raycastConnectivity, renderable)
|
|
2822
|
+
});
|
|
2736
2823
|
const bbox = bboxFromGeometry(analysis.geometry);
|
|
2737
2824
|
const summary = summarizeThicknessSamples(analysis.samples, options);
|
|
2738
2825
|
if (analysis.warnings.length > 0) {
|
|
@@ -2786,8 +2873,9 @@ function getSessionThicknessInspection(session, rawOptions) {
|
|
|
2786
2873
|
points
|
|
2787
2874
|
},
|
|
2788
2875
|
report: {
|
|
2789
|
-
method: "mesh-normal-raycast",
|
|
2876
|
+
method: "mesh-normal-raycast-contact-aware",
|
|
2790
2877
|
options,
|
|
2878
|
+
sampleBudget,
|
|
2791
2879
|
objectCount: objects.length,
|
|
2792
2880
|
objects,
|
|
2793
2881
|
warnings,
|
|
@@ -2815,11 +2903,13 @@ function renderCurrentRoughness(session, rawOptions) {
|
|
|
2815
2903
|
}
|
|
2816
2904
|
function getSessionRoughnessInspection(session, rawOptions) {
|
|
2817
2905
|
var _a;
|
|
2818
|
-
const
|
|
2819
|
-
const
|
|
2906
|
+
const resolvedOptions = resolveRoughnessInspectionOptions(rawOptions);
|
|
2907
|
+
const { options, sampleBudget } = withSceneSampleBudget(session, resolvedOptions, (rawOptions == null ? void 0 : rawOptions.maxSamplesPerObject) !== void 0);
|
|
2908
|
+
const optionsKey = inspectionOptionsKey({ options, sampleBudget });
|
|
2820
2909
|
if (((_a = session.roughnessInspection) == null ? void 0 : _a.optionsKey) === optionsKey) return session.roughnessInspection;
|
|
2821
2910
|
const byId = new Map(session.objects.map((obj) => [obj.id, obj]));
|
|
2822
2911
|
const warnings = [];
|
|
2912
|
+
maybePushSceneSampleBudgetWarning(warnings, "Roughness", sampleBudget);
|
|
2823
2913
|
const objects = [];
|
|
2824
2914
|
const cloudObjects = [];
|
|
2825
2915
|
const points = [];
|
|
@@ -2882,6 +2972,7 @@ function getSessionRoughnessInspection(session, rawOptions) {
|
|
|
2882
2972
|
report: {
|
|
2883
2973
|
method: "mesh-dihedral-angle",
|
|
2884
2974
|
options,
|
|
2975
|
+
sampleBudget,
|
|
2885
2976
|
objectCount: objects.length,
|
|
2886
2977
|
objects,
|
|
2887
2978
|
warnings,
|
|
@@ -2900,14 +2991,14 @@ function getSessionRoughnessInspection(session, rawOptions) {
|
|
|
2900
2991
|
}
|
|
2901
2992
|
function emptySectionSvg() {
|
|
2902
2993
|
return {
|
|
2903
|
-
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="#
|
|
2994
|
+
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="#f7f7f4"/></svg>',
|
|
2904
2995
|
width: 0,
|
|
2905
2996
|
height: 0,
|
|
2906
2997
|
area: 0,
|
|
2907
2998
|
pathCount: 0
|
|
2908
2999
|
};
|
|
2909
3000
|
}
|
|
2910
|
-
async function svgToPngDataUrl(svg, size, background = "#
|
|
3001
|
+
async function svgToPngDataUrl(svg, size, background = "#f7f7f4") {
|
|
2911
3002
|
if (!exportCtx) {
|
|
2912
3003
|
throw new Error("Could not create export canvas context.");
|
|
2913
3004
|
}
|
|
@@ -2965,7 +3056,7 @@ async function renderSectionAtlas(session, opts) {
|
|
|
2965
3056
|
sectionSketches.push({ name: entry.name, sketch });
|
|
2966
3057
|
}
|
|
2967
3058
|
}
|
|
2968
|
-
const svgDocument = sectionSketches.length > 0 ? buildSketchSvgDocument(sectionSketches, { edges: "thin" }) : emptySectionSvg();
|
|
3059
|
+
const svgDocument = sectionSketches.length > 0 ? buildSketchSvgDocument(sectionSketches, { edges: "thin", theme: "cad-section" }) : emptySectionSvg();
|
|
2969
3060
|
slices.push({
|
|
2970
3061
|
index,
|
|
2971
3062
|
offset,
|
|
@@ -3015,6 +3106,37 @@ function addRenderStyleLights(scene, style) {
|
|
|
3015
3106
|
new HemisphereLight(new Color(lights.hemisphereSky), new Color(lights.hemisphereGround), lights.hemisphereIntensity)
|
|
3016
3107
|
);
|
|
3017
3108
|
}
|
|
3109
|
+
function fieldKindUniform(kind) {
|
|
3110
|
+
return kind === "hybrid" ? 1 : 0;
|
|
3111
|
+
}
|
|
3112
|
+
function createSurfaceFieldMaterial({
|
|
3113
|
+
field,
|
|
3114
|
+
objectColor,
|
|
3115
|
+
clippingPlanes,
|
|
3116
|
+
fieldScale
|
|
3117
|
+
}) {
|
|
3118
|
+
return new ShaderMaterial({
|
|
3119
|
+
vertexShader: SURFACE_FIELD_VERTEX_SHADER,
|
|
3120
|
+
fragmentShader: SURFACE_FIELD_FRAGMENT_SHADER,
|
|
3121
|
+
uniforms: {
|
|
3122
|
+
uAccentColor: { value: new Color(field.accentColor) },
|
|
3123
|
+
uBaseColor: { value: new Color(field.baseColor) },
|
|
3124
|
+
uDarkColor: { value: new Color(field.darkColor) },
|
|
3125
|
+
uFieldKind: { value: fieldKindUniform(field.kind) },
|
|
3126
|
+
uFieldScale: { value: fieldScale },
|
|
3127
|
+
uGlow: { value: field.glow },
|
|
3128
|
+
uLineColor: { value: new Color(field.lineColor) },
|
|
3129
|
+
uLineWidth: { value: field.lineWidth },
|
|
3130
|
+
uNodeColor: { value: new Color(field.nodeColor) },
|
|
3131
|
+
uObjectColor: { value: objectColor },
|
|
3132
|
+
uObjectColorMix: { value: field.objectColorMix },
|
|
3133
|
+
uSpacing: { value: field.spacing }
|
|
3134
|
+
},
|
|
3135
|
+
side: DoubleSide,
|
|
3136
|
+
toneMapped: false,
|
|
3137
|
+
clippingPlanes
|
|
3138
|
+
});
|
|
3139
|
+
}
|
|
3018
3140
|
function createSceneLight(def) {
|
|
3019
3141
|
const color = def.color ? new Color(def.color) : new Color(16777215);
|
|
3020
3142
|
const intensity = def.intensity ?? 1;
|
|
@@ -3703,7 +3825,7 @@ function isFocusVisible(obj, focus, hide) {
|
|
|
3703
3825
|
return true;
|
|
3704
3826
|
}
|
|
3705
3827
|
function createSession(code, opts) {
|
|
3706
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
3828
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
3707
3829
|
const size = (opts == null ? void 0 : opts.size) ?? 1024;
|
|
3708
3830
|
const pixelRatio = (opts == null ? void 0 : opts.pixelRatio) ?? 1;
|
|
3709
3831
|
const debug = createCaptureDebugLogger(opts == null ? void 0 : opts.debug);
|
|
@@ -3785,8 +3907,6 @@ function createSession(code, opts) {
|
|
|
3785
3907
|
name: entry.source.name,
|
|
3786
3908
|
shape: entry.shape
|
|
3787
3909
|
}));
|
|
3788
|
-
const connectivityBodyInput = (opts == null ? void 0 : opts.includeConnectivity) ? buildMeshBodyConnectivityInput(shapeVisibleObjs.map(meshConnectivitySource), { bodyShape: () => new EmptyInspectionShape() }) : null;
|
|
3789
|
-
const connectivityEntries = (connectivityBodyInput == null ? void 0 : connectivityBodyInput.entries) ?? [];
|
|
3790
3910
|
const collisionEntries = (opts == null ? void 0 : opts.includeCollisions) ? shapeVisibleObjs.map((entry) => {
|
|
3791
3911
|
const bb2 = entry.shape.boundingBox();
|
|
3792
3912
|
return {
|
|
@@ -3843,6 +3963,7 @@ function createSession(code, opts) {
|
|
|
3843
3963
|
const bsize = new Vector3();
|
|
3844
3964
|
bb.getSize(bsize);
|
|
3845
3965
|
const maxDim = Math.max(1, bsize.x, bsize.y, bsize.z);
|
|
3966
|
+
const surfaceFieldScale = maxDim / 5;
|
|
3846
3967
|
const fov = 45;
|
|
3847
3968
|
const distance = maxDim / (2 * Math.tan(fov * Math.PI / 360)) * 1.6;
|
|
3848
3969
|
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;
|
|
@@ -3949,15 +4070,17 @@ function createSession(code, opts) {
|
|
|
3949
4070
|
edgeSegments: Math.floor((((_m = geo.edges.getAttribute("position")) == null ? void 0 : _m.count) ?? 0) / 2)
|
|
3950
4071
|
});
|
|
3951
4072
|
const materialDefaults = renderStylePreset.material;
|
|
4073
|
+
const surfaceField = renderStylePreset.surfaceField;
|
|
3952
4074
|
const authoredMaterialOpacity = mp == null ? void 0 : mp.opacity;
|
|
3953
4075
|
const authoredMaterialTransmission = mp == null ? void 0 : mp.transmission;
|
|
3954
4076
|
const hasAuthoredTransparency = authoredMaterialOpacity !== void 0 && authoredMaterialOpacity < 0.99 || authoredMaterialTransmission !== void 0 && authoredMaterialTransmission > 0;
|
|
3955
4077
|
const transparentDefaults = hasAuthoredTransparency ? materialDefaults.authoredTransparent : materialDefaults;
|
|
3956
4078
|
const materialOpacity = Math.min(obj.opacity, authoredMaterialOpacity ?? materialDefaults.opacity);
|
|
3957
4079
|
const materialTransmission = authoredMaterialTransmission ?? transparentDefaults.transmission;
|
|
4080
|
+
const objectColor = parseColor(obj.color, CAD_MATERIAL_PROPS.color);
|
|
3958
4081
|
const solidMaterialProps = {
|
|
3959
4082
|
...CAD_MATERIAL_PROPS,
|
|
3960
|
-
color:
|
|
4083
|
+
color: objectColor,
|
|
3961
4084
|
metalness: (mp == null ? void 0 : mp.metalness) ?? materialDefaults.metalness,
|
|
3962
4085
|
roughness: (mp == null ? void 0 : mp.roughness) ?? transparentDefaults.roughness,
|
|
3963
4086
|
clearcoat: (mp == null ? void 0 : mp.clearcoat) ?? transparentDefaults.clearcoat,
|
|
@@ -3973,7 +4096,12 @@ function createSession(code, opts) {
|
|
|
3973
4096
|
...(mp == null ? void 0 : mp.emissiveIntensity) !== void 0 && { emissiveIntensity: mp.emissiveIntensity },
|
|
3974
4097
|
...(mp == null ? void 0 : mp.wireframe) && { wireframe: true }
|
|
3975
4098
|
};
|
|
3976
|
-
solidMaterial =
|
|
4099
|
+
solidMaterial = surfaceField.enabled ? createSurfaceFieldMaterial({
|
|
4100
|
+
field: surfaceField,
|
|
4101
|
+
objectColor,
|
|
4102
|
+
clippingPlanes: applicableCutPlanes,
|
|
4103
|
+
fieldScale: surfaceFieldScale
|
|
4104
|
+
}) : new MeshPhysicalMaterial({
|
|
3977
4105
|
...solidMaterialProps,
|
|
3978
4106
|
transparent: materialOpacity < 1 || materialTransmission > 0,
|
|
3979
4107
|
opacity: materialOpacity,
|
|
@@ -4051,6 +4179,19 @@ function createSession(code, opts) {
|
|
|
4051
4179
|
...sdfRaymarch ? { sdfRaymarch } : {}
|
|
4052
4180
|
});
|
|
4053
4181
|
}
|
|
4182
|
+
const renderableById = new Map(renderables.map((renderable) => [renderable.id, renderable]));
|
|
4183
|
+
const connectivityBodyInput = (opts == null ? void 0 : opts.includeConnectivity) ? buildMeshBodyConnectivityInput(
|
|
4184
|
+
shapeVisibleObjs.map(
|
|
4185
|
+
(entry) => {
|
|
4186
|
+
var _a2;
|
|
4187
|
+
return meshConnectivitySource(entry, cloneGeometryPositions((_a2 = renderableById.get(entry.source.id)) == null ? void 0 : _a2.solid.geometry));
|
|
4188
|
+
}
|
|
4189
|
+
),
|
|
4190
|
+
{ bodyShape: () => new EmptyInspectionShape() }
|
|
4191
|
+
) : null;
|
|
4192
|
+
const connectivityEntries = (connectivityBodyInput == null ? void 0 : connectivityBodyInput.entries) ?? [];
|
|
4193
|
+
const groundOffset = Number.isFinite((_n = sceneConfig == null ? void 0 : sceneConfig.ground) == null ? void 0 : _n.offset) ? sceneConfig.ground.offset : 0;
|
|
4194
|
+
const floatingGroundZ = bbox.min[2] - groundOffset;
|
|
4054
4195
|
let sceneConfigCameraState = null;
|
|
4055
4196
|
if ((sceneConfig == null ? void 0 : sceneConfig.camera) && !(requestedSceneState == null ? void 0 : requestedSceneState.camera)) {
|
|
4056
4197
|
const cam = sceneConfig.camera;
|
|
@@ -4068,7 +4209,7 @@ function createSession(code, opts) {
|
|
|
4068
4209
|
}
|
|
4069
4210
|
const cameraSpec = sceneConfigCameraState && (opts == null ? void 0 : opts.capture) === "section-sweep" ? fitCameraStateToBounds(sceneConfigCameraState, bbox, cameraFov) : (requestedSceneState == null ? void 0 : requestedSceneState.camera) ?? sceneConfigCameraState;
|
|
4070
4211
|
const cameraRig = buildCameraRig(center, distance, maxDim, cameraSpec, cameraFov);
|
|
4071
|
-
const explicitCameraFov = (cameraSpec == null ? void 0 : cameraSpec.fov) ?? ((
|
|
4212
|
+
const explicitCameraFov = (cameraSpec == null ? void 0 : cameraSpec.fov) ?? ((_o = sceneConfig == null ? void 0 : sceneConfig.camera) == null ? void 0 : _o.fov);
|
|
4072
4213
|
if (explicitCameraFov && cameraRig.camera instanceof PerspectiveCamera) {
|
|
4073
4214
|
cameraRig.camera.fov = explicitCameraFov;
|
|
4074
4215
|
cameraRig.camera.updateProjectionMatrix();
|
|
@@ -4090,8 +4231,10 @@ function createSession(code, opts) {
|
|
|
4090
4231
|
sectionShapes,
|
|
4091
4232
|
connectivityEntries,
|
|
4092
4233
|
connectivityBodyInput,
|
|
4234
|
+
floatingGroundZ,
|
|
4093
4235
|
physicalConnectivity: null,
|
|
4094
4236
|
distanceReport: null,
|
|
4237
|
+
floatingReport: null,
|
|
4095
4238
|
collisionEntries,
|
|
4096
4239
|
collisionReport: null,
|
|
4097
4240
|
thicknessInspection: null,
|
|
@@ -4121,6 +4264,7 @@ function createSession(code, opts) {
|
|
|
4121
4264
|
}
|
|
4122
4265
|
async function setup() {
|
|
4123
4266
|
await init();
|
|
4267
|
+
setActiveBackend(CLI_DEFAULT_BACKEND);
|
|
4124
4268
|
window.__forgeCaptureCapabilities = CAPTURE_RUNTIME_CAPABILITIES;
|
|
4125
4269
|
window.__forgeReady = true;
|
|
4126
4270
|
}
|
|
@@ -4161,7 +4305,7 @@ window.__forgeRender = async (code, opts) => {
|
|
|
4161
4305
|
hide: opts == null ? void 0 : opts.hide,
|
|
4162
4306
|
paramOverrides: opts == null ? void 0 : opts.paramOverrides,
|
|
4163
4307
|
renderStyle: opts == null ? void 0 : opts.renderStyle,
|
|
4164
|
-
includeConnectivity: requestedChannels.has("connectivity") || requestedChannels.has("distance"),
|
|
4308
|
+
includeConnectivity: requestedChannels.has("connectivity") || requestedChannels.has("floating") || requestedChannels.has("distance") || requestedChannels.has("thickness"),
|
|
4165
4309
|
includeCollisions: requestedChannels.has("collisions"),
|
|
4166
4310
|
capture: "orbit"
|
|
4167
4311
|
});
|
|
@@ -4188,14 +4332,17 @@ window.__forgeRender = async (code, opts) => {
|
|
|
4188
4332
|
const renders = {};
|
|
4189
4333
|
const depthRenders = {};
|
|
4190
4334
|
const normalRenders = {};
|
|
4335
|
+
const zebraRenders = {};
|
|
4191
4336
|
const maskRenders = {};
|
|
4192
4337
|
const connectivityRenders = {};
|
|
4338
|
+
const floatingRenders = {};
|
|
4193
4339
|
const distanceRenders = {};
|
|
4194
4340
|
const collisionRenders = {};
|
|
4195
4341
|
const thicknessRenders = {};
|
|
4196
4342
|
const roughnessRenders = {};
|
|
4197
4343
|
let maskObjects = [];
|
|
4198
4344
|
let connectivityReport = null;
|
|
4345
|
+
let floatingReport = null;
|
|
4199
4346
|
let distanceReport = null;
|
|
4200
4347
|
let collisionReport = null;
|
|
4201
4348
|
let thicknessReport = null;
|
|
@@ -4242,6 +4389,11 @@ window.__forgeRender = async (code, opts) => {
|
|
|
4242
4389
|
normalRenders[label] = renderCurrentNormals(session);
|
|
4243
4390
|
await markChannelViewDone("normals", label);
|
|
4244
4391
|
}
|
|
4392
|
+
if (requestedChannels.has("zebra")) {
|
|
4393
|
+
await markChannelViewStart("zebra", label);
|
|
4394
|
+
zebraRenders[label] = renderCurrentZebra(session);
|
|
4395
|
+
await markChannelViewDone("zebra", label);
|
|
4396
|
+
}
|
|
4245
4397
|
if (requestedChannels.has("roughness")) {
|
|
4246
4398
|
await markChannelViewStart("roughness", label);
|
|
4247
4399
|
const roughness = renderCurrentRoughness(session, opts == null ? void 0 : opts.roughness);
|
|
@@ -4267,6 +4419,13 @@ window.__forgeRender = async (code, opts) => {
|
|
|
4267
4419
|
connectivityReport = connectivity.report;
|
|
4268
4420
|
await markChannelViewDone("connectivity", label);
|
|
4269
4421
|
}
|
|
4422
|
+
if (requestedChannels.has("floating")) {
|
|
4423
|
+
await markChannelViewStart("floating", label);
|
|
4424
|
+
const floating = renderCurrentFloating(session);
|
|
4425
|
+
floatingRenders[label] = floating.png;
|
|
4426
|
+
floatingReport = floating.report;
|
|
4427
|
+
await markChannelViewDone("floating", label);
|
|
4428
|
+
}
|
|
4270
4429
|
if (requestedChannels.has("distance")) {
|
|
4271
4430
|
await markChannelViewStart("distance", label);
|
|
4272
4431
|
const distance = renderCurrentDistance(session);
|
|
@@ -4311,6 +4470,7 @@ window.__forgeRender = async (code, opts) => {
|
|
|
4311
4470
|
renders,
|
|
4312
4471
|
depth: depthRenders,
|
|
4313
4472
|
normals: normalRenders,
|
|
4473
|
+
zebra: zebraRenders,
|
|
4314
4474
|
roughness: roughnessReport ? {
|
|
4315
4475
|
...roughnessReport,
|
|
4316
4476
|
pointCloud: roughnessPointCloud,
|
|
@@ -4328,6 +4488,12 @@ window.__forgeRender = async (code, opts) => {
|
|
|
4328
4488
|
} : {
|
|
4329
4489
|
views: connectivityRenders
|
|
4330
4490
|
},
|
|
4491
|
+
floating: floatingReport ? {
|
|
4492
|
+
...floatingReport,
|
|
4493
|
+
views: floatingRenders
|
|
4494
|
+
} : {
|
|
4495
|
+
views: floatingRenders
|
|
4496
|
+
},
|
|
4331
4497
|
distance: distanceReport ? {
|
|
4332
4498
|
...distanceReport,
|
|
4333
4499
|
views: distanceRenders
|