forgecad 0.10.0 → 0.10.2
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-DwYHz72L.js → AdminPage-CHY6ZN-p.js} +1 -1
- package/dist/assets/{BenchmarkPage-a9_f-1US.js → BenchmarkPage-BcRT5iGN.js} +1 -1
- package/dist/assets/{BlogPage-DodHpvmf.js → BlogPage-BssBbnb-.js} +1 -1
- package/dist/assets/{DocsPage-B5LePEuj.js → DocsPage-DsvdiRNK.js} +33 -2
- package/dist/assets/{EditorApp-QXsAISLR.js → EditorApp-Bfd3jbtC.js} +185 -44
- package/dist/assets/{EmbedViewer-DdEHGUMU.js → EmbedViewer-D5t8WamV.js} +3 -3
- package/dist/assets/{LandingPageProofDriven-yhhOodbf.js → LandingPageProofDriven-DbN7o-Be.js} +1 -1
- package/dist/assets/{LegalPage-5RbKRGYK.js → LegalPage-DNGrrY0p.js} +1 -1
- package/dist/assets/{PricingPage-E3Rma7aV.js → PricingPage-Nczr3pRz.js} +1 -1
- package/dist/assets/{SettingsPage-BJZcM97j.js → SettingsPage-DZlyu4d4.js} +1 -1
- package/dist/assets/{app-DSYrDg0V.js → app-C9ct2hRD.js} +1752 -474
- package/dist/assets/{app-CE3sYcV7.css → app-CjsbDlb7.css} +143 -0
- package/dist/assets/{scalar-sampling-budget-o90NSNmF.js → backendInit-ymjonyQp.js} +85756 -78750
- package/dist/assets/cli/{render-ZMHR9HkV.js → render-B_0lQwKU.js} +71 -193
- package/dist/assets/{constructionHistoryWorker-AwMMWSxg.js → constructionHistoryWorker-CZ42Dksy.js} +8058 -1225
- package/dist/assets/{evalWorker-DbNs7Dkp.js → evalWorker-C2pm8LHP.js} +23037 -15821
- package/dist/assets/{forgecad_geometry-Dgceylq9.js → forgecad_geometry-BlMtqluF.js} +120 -1
- package/dist/assets/{forgecad_geometry_bg-dD4RNQF1.wasm → forgecad_geometry_bg-BllP_WiL.wasm} +0 -0
- package/dist/assets/{inspectWorker-CZsCFtQT.js → inspectWorker-D5T5VbfK.js} +31375 -32603
- package/dist/assets/{jointPose-DO6mnXn_.js → jointPose-4r8ed8_5.js} +1 -1
- package/dist/assets/{manifold-BU-tJwQh.js → manifold-5PP1eGLN.js} +1 -1
- package/dist/assets/{manifold-fy2MV7K1.js → manifold-C4r6B-XY.js} +2 -2
- package/dist/assets/{manifold-BGlQBBH9.js → manifold-DjBkyIc8.js} +1 -1
- package/dist/assets/{reportWorker-DO6hcQbh.js → reportWorker-CwenM7wB.js} +46620 -44936
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/CLI.md +43 -16
- package/dist/docs-raw/generated/assembly.md +71 -6
- package/dist/docs-raw/generated/concepts.md +17 -3
- package/dist/docs-raw/generated/core.md +10 -3
- package/dist/docs-raw/generated/output.md +14 -43
- package/dist/docs-raw/generated/runtime-names.md +4 -4
- package/dist/docs-raw/generated/sdf.md +2 -2
- package/dist/docs-raw/guides/simready-quickstart.md +173 -0
- package/dist/docs-raw/simulation-workflow.md +273 -0
- package/dist/index.html +2 -2
- package/dist/sitemap.xml +25 -13
- package/dist-cli/{check-compiler-JTVBITCR.js → check-compiler-SP7FAL7R.js} +1 -1
- package/dist-cli/{check-query-propagation-3FFLSMVN.js → check-query-propagation-BRLSHP22.js} +1 -1
- package/dist-cli/{chunk-OAN5T4XD.js → chunk-RQQ42YCP.js} +51209 -43456
- package/dist-cli/forgecad.js +5783 -1691
- package/dist-cli/{forgecad_geometry-QOQIIP53.js → forgecad_geometry-7TVSNVUB.js} +119 -0
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +107 -68
- package/dist-skill/docs/API/core/concepts.md +2 -2
- package/dist-skill/docs/CLI.md +43 -16
- package/dist-skill/docs/generated/assembly.md +67 -6
- package/dist-skill/docs/generated/core.md +10 -3
- package/dist-skill/docs/generated/output.md +14 -43
- package/dist-skill/docs/generated/runtime-names.md +4 -4
- package/dist-skill/docs/generated/sdf.md +2 -2
- package/examples/api/gyroid-voronoi-blend.forge.js +1 -1
- package/examples/api/organic-noise-sculpture.forge.js +1 -1
- package/examples/api/sdf-circular-array-knurling.forge.js +1 -1
- package/examples/api/{sdf-custom-raymarch.forge.js → sdf-custom-field-mesh-preview.forge.js} +3 -4
- package/examples/api/sdf-materialize-tree.forge.js +2 -2
- package/examples/api/sdf-plain-return.forge.js +3 -2
- package/examples/api/sdf-shapes.forge.js +2 -2
- package/examples/api/sdf-surface-basket-weave.forge.js +2 -2
- package/examples/generative/twisted-lattice-tower.forge.js +1 -1
- package/examples/generative/voronoi-lampshade.forge.js +1 -1
- package/examples/robotics/README.md +46 -0
- package/examples/robotics/scout-cam-rover-simready/README.md +119 -0
- package/examples/robotics/scout-cam-rover-simready/lib/dims.js +140 -0
- package/examples/robotics/scout-cam-rover-simready/main.forge.js +343 -0
- package/examples/robotics/scout-cam-rover-simready/parts/body.forge.js +304 -0
- package/examples/robotics/scout-cam-rover-simready/parts/chassis.forge.js +320 -0
- package/examples/robotics/scout-cam-rover-simready/parts/hardware.forge.js +21 -0
- package/examples/robotics/scout-cam-rover-simready/parts/turret.forge.js +70 -0
- package/examples/robotics/scout-cam-rover-simready/parts/wheel.forge.js +116 -0
- package/examples/robotics/simready-asset-crate.forge.js +79 -0
- package/examples/robotics/simready-diff-drive-rover.forge.js +141 -0
- package/examples/robotics/simready-parallel-gripper.forge.js +102 -0
- package/package.json +2 -2
- package/dist/assets/manifold-CzYf_iub.js +0 -3023
|
@@ -4,7 +4,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
var _a2;
|
|
6
6
|
import { c as create, j as jsxRuntimeExports, r as reactExports, a as createWithEqualityFn, R as React, T as Tb, s as schedulerExports, b as clientExports, d as reactDomExports, u as useParams, e as useSearchParams, f as useNavigate, L as Link, g as useLocation, B as BrowserRouter, h as Routes, i as Route, N as Navigate } from "./vendor-react-6j1Kke-Y.js";
|
|
7
|
-
import { _ as __vitePreload, z as zipSync, s as strToU8, W as WebGLRenderer, R as Raycaster, O as OrthographicCamera$1, P as PerspectiveCamera$1,
|
|
7
|
+
import { _ as __vitePreload, S as SDF_PROGRAM_OP, z as zipSync, s as strToU8, W as WebGLRenderer, R as Raycaster, O as OrthographicCamera$1, P as PerspectiveCamera$1, a as Scene, b as PCFSoftShadowMap, V as VSMShadowMap, c as PCFShadowMap, B as BasicShadowMap, C as ColorManagement, L as LinearSRGBColorSpace, d as SRGBColorSpace, N as NoToneMapping, A as ACESFilmicToneMapping, e as Layers, f as Color, g as RGBAFormat, U as UnsignedByteType, h as Vector3, i as Vector2, j as Clock, T as THREE, D as DoubleSide, k as REVISION, M as Mesh, I as IcosahedronGeometry, l as ShaderMaterial, m as Spherical, Q as Quaternion, n as MOUSE, o as TOUCH, p as Ray, q as Plane, r as DataTextureLoader, H as HalfFloatType, F as FloatType, t as DataUtils, u as LinearFilter, v as RedFormat, w as InstancedBufferGeometry, x as Float32BufferAttribute, y as InstancedInterleavedBuffer, E as InterleavedBufferAttribute, G as WireframeGeometry, J as Box3, K as Sphere, X as UniformsUtils, Y as UniformsLib, Z as Vector4, $ as Line3, a0 as Matrix4, a1 as MathUtils, a2 as Uniform, a3 as WebGLRenderTarget, a4 as DepthTexture, a5 as BackSide, a6 as ClampToEdgeWrapping, a7 as PlaneGeometry, a8 as UVMapping, a9 as DataTexture, aa as Texture, ab as MeshBasicMaterial, ac as IntType, ad as ShortType, ae as ByteType, af as UnsignedIntType, ag as Loader, ah as LoadingManager, ai as LinearMipMapLinearFilter, aj as FileLoader, ak as NoBlending, al as CubeReflectionMapping, am as EquirectangularReflectionMapping, an as CubeTextureLoader, ao as WebGLCubeRenderTarget, ap as ConstraintSketch, aq as setSketchPlacement3D, ar as Sketch, as as PROFILE_BACKEND_MARKER, at as FrozenShape, au as setShapeCompilePlan, av as hasAnyPorts, aw as setShapePortsInternal, ax as markShapePortsUsed, ay as setParamOverrides, az as resolveForgeRenderStyle, aA as DEFAULT_ACTIVE_BACKEND, aB as isConstraintSketch, aC as updateConstraintValue, aD as linearizeMultiObjectSteps, aE as getShapeCompilePlan, aF as SCAN_PROXY_GRANULARITY_DEFAULT, aG as publishSolverWasmRunDebug, aH as resolveForgeQualityPreset, aI as SCAN_PROXY_MATRIX_GRANULARITY_MAX, aJ as findJointAnimationClip, aK as resolveJointAnimation, aL as resolveJointViewValues, aM as resolveImportPath, aN as resolveScanProxyGranularity, aO as BufferGeometry, aP as LineBasicMaterial, aQ as Line$1, aR as LineDashedMaterial, aS as DepthStencilFormat, aT as UnsignedInt248Type, aU as MeshNormalMaterial, aV as NearestFilter, aW as BasicDepthPacking, aX as EventDispatcher$1, aY as NoColorSpace, aZ as FrontSide, a_ as Material, a$ as AlwaysDepth, b0 as BufferAttribute, b1 as CanvasTexture, b2 as Object3D, b3 as FogExp2, b4 as Fog, b5 as AmbientLight, b6 as HemisphereLight, b7 as SpotLight, b8 as PointLight, b9 as DirectionalLight, ba as heatPointsForSide, bb as COMPARISON_COLORS, bc as comparisonHeatDepthTest, bd as comparisonHeatEdgeOpacity, be as comparisonHeatPatchOpacity, bf as shapeToGeometry, bg as buildComparisonHeatPatchGeometry, bh as EdgesGeometry, bi as buildShapeFromCompilePlan, bj as buildVisibleHistoryStacks, bk as sketchToSvg, bl as sketchToDxf, bm as runScript, bn as MeshPhysicalMaterial, bo as LineSegments, bp as findDesignTraceNodeForConstructionStep, bq as formatDesignTraceAnchor, br as waitForAnimationFrame, bs as selectBuildLedgerNodes, bt as worldAuthorPlaneToLocal, bu as compileSdfProgramEvaluator3, bv as SDF_LINEAR_OUTPUT_COLOR_GLSL, bw as GLSL3, bx as BoxGeometry, by as Data3DTexture, bz as buildSdfRaymarchFragmentShader, bA as SDF_RAYMARCH_PROXY_VERTEX_SHADER, bB as scanProxySourceBytes, bC as disposeScanProxyGeometry, bD as scanProxyGeometryFromPayload, bE as AdditiveBlending, bF as geometryWithVisibleVertexColors, bG as getRenderStylePreset, bH as SCAN_RENDER_COLORS, bI as NormalBlending, bJ as scanMaterialShellColor, bK as ZEBRA_STRIPE_SOFTNESS, bL as ZEBRA_STRIPE_SCALE, bM as ZEBRA_LIGHT_COLOR, bN as ZEBRA_DARK_COLOR, bO as ZEBRA_ACCENT_COLOR, bP as ZEBRA_STRIPE_FRAGMENT_SHADER, bQ as ZEBRA_STRIPE_VERTEX_SHADER, bR as SCAN_PROXY_LAYER_STYLES, bS as scanMaterialLayerStyles, bT as SURFACE_FIELD_FRAGMENT_SHADER, bU as SURFACE_FIELD_VERTEX_SHADER, bV as WORLD_UP, bW as CatmullRomCurve3, bX as TubeGeometry, bY as THICKNESS_GRADIENT_COLORS, bZ as ROUGHNESS_COLORS, b_ as DEFAULT_ROUGHNESS_INSPECTION_OPTIONS, b$ as PERFORMANCE_SAMPLE_INTERVAL_SEC, c0 as formatPerformanceCount, c1 as NON_TEXT_INPUT_TYPES, c2 as MeshStandardMaterial, c3 as Shape, c4 as ShapeGeometry, c5 as ShaderLib, c6 as CylinderGeometry, c7 as VIEWPORT_CAMERA_STORAGE_KEY, c8 as parseViewportCameraState, c9 as createResolvedExplodeConfig, ca as explodeBoundsCenter, cb as explodeMergeBounds, cc as resolveExplodeDirective, cd as computeExplodeMotion, ce as getSketchWorldMatrix, cf as explodeAdd, cg as hasExplodeOverride, ch as resolveExplodeLocalFanDirection, ci as explodeMul, cj as explodeLeafFanStage, ck as normalizeCutPlane, cl as toClippingPlane, cm as isObjectExcludedFromCutPlane, cn as getShapePorts, co as getShapeUsedPorts, cp as DEFAULT_VIEW_CONFIG, cq as SECTION_EXPLORER_PLANE_NAME, cr as ZERO_OFFSET, cs as scanProxyGridForBounds, ct as IDENTITY_MATRIX$2, cu as OBJECT_CONTEXT_MENU_MARGIN, cv as OBJECT_CONTEXT_MENU_WIDTH, cw as OBJECT_CONTEXT_MENU_HEIGHT, cx as getKernelFaceNameForTriangle, cy as triangleSoupFromMeshes, cz as compareTriangleSoups, cA as buildGeometryComparisonPointCloud, cB as aabbOverlaps, cC as aabbOverlapVolume, cD as DEFAULT_COLLISION_INSPECTION_OPTIONS, cE as resolveScalarSceneSampleBudget, cF as FOCUS_MODE_DIM_OPACITY, cG as comparisonCandidateContextOpacity, cH as Matrix3, cI as initBackendForEvaluation } from "./backendInit-ymjonyQp.js";
|
|
8
8
|
function getCsrfToken() {
|
|
9
9
|
const match = document.cookie.match(/(?:^|;\s*)fc-csrf-token=([^;]+)/);
|
|
10
10
|
return match == null ? void 0 : match[1];
|
|
@@ -880,6 +880,136 @@ function detectMobile() {
|
|
|
880
880
|
return hasTouch && isNarrow;
|
|
881
881
|
}
|
|
882
882
|
const isMobile = detectMobile();
|
|
883
|
+
function buildSdfProgramWgslFunction(program, options = {}) {
|
|
884
|
+
const functionName = requireWgslIdentifier(options.functionName ?? "sdfProgram", "functionName");
|
|
885
|
+
const lines = [`fn ${functionName}(p: vec3f) -> f32 {`, " var v0: f32 = p.x;", " var v1: f32 = p.y;", " var v2: f32 = p.z;"];
|
|
886
|
+
const { opcodes, argA, argB, argC, constants, output } = program;
|
|
887
|
+
for (let index = 0; index < opcodes.length; index += 1) {
|
|
888
|
+
const slot = `v${index + 3}`;
|
|
889
|
+
const a2 = `v${argA[index]}`;
|
|
890
|
+
const b2 = `v${argB[index]}`;
|
|
891
|
+
switch (opcodes[index]) {
|
|
892
|
+
case SDF_PROGRAM_OP.Const:
|
|
893
|
+
lines.push(` var ${slot}: f32 = ${wgslNumber(constants[argC[index]] ?? 0)};`);
|
|
894
|
+
break;
|
|
895
|
+
case SDF_PROGRAM_OP.Neg:
|
|
896
|
+
lines.push(` var ${slot}: f32 = -${a2};`);
|
|
897
|
+
break;
|
|
898
|
+
case SDF_PROGRAM_OP.Abs:
|
|
899
|
+
lines.push(` var ${slot}: f32 = abs(${a2});`);
|
|
900
|
+
break;
|
|
901
|
+
case SDF_PROGRAM_OP.Sqrt:
|
|
902
|
+
lines.push(` var ${slot}: f32 = sqrt(${a2});`);
|
|
903
|
+
break;
|
|
904
|
+
case SDF_PROGRAM_OP.Sin:
|
|
905
|
+
lines.push(` var ${slot}: f32 = sin(${a2});`);
|
|
906
|
+
break;
|
|
907
|
+
case SDF_PROGRAM_OP.Cos:
|
|
908
|
+
lines.push(` var ${slot}: f32 = cos(${a2});`);
|
|
909
|
+
break;
|
|
910
|
+
case SDF_PROGRAM_OP.Round:
|
|
911
|
+
lines.push(` var ${slot}: f32 = round(${a2});`);
|
|
912
|
+
break;
|
|
913
|
+
case SDF_PROGRAM_OP.Add:
|
|
914
|
+
lines.push(` var ${slot}: f32 = ${a2} + ${b2};`);
|
|
915
|
+
break;
|
|
916
|
+
case SDF_PROGRAM_OP.Sub:
|
|
917
|
+
lines.push(` var ${slot}: f32 = ${a2} - ${b2};`);
|
|
918
|
+
break;
|
|
919
|
+
case SDF_PROGRAM_OP.Mul:
|
|
920
|
+
lines.push(` var ${slot}: f32 = ${a2} * ${b2};`);
|
|
921
|
+
break;
|
|
922
|
+
case SDF_PROGRAM_OP.Div:
|
|
923
|
+
lines.push(` var ${slot}: f32 = ${a2} / ${b2};`);
|
|
924
|
+
break;
|
|
925
|
+
case SDF_PROGRAM_OP.Min:
|
|
926
|
+
lines.push(` var ${slot}: f32 = min(${a2}, ${b2});`);
|
|
927
|
+
break;
|
|
928
|
+
case SDF_PROGRAM_OP.Max:
|
|
929
|
+
lines.push(` var ${slot}: f32 = max(${a2}, ${b2});`);
|
|
930
|
+
break;
|
|
931
|
+
case SDF_PROGRAM_OP.Atan2:
|
|
932
|
+
lines.push(` var ${slot}: f32 = atan2(${a2}, ${b2});`);
|
|
933
|
+
break;
|
|
934
|
+
case SDF_PROGRAM_OP.LessThan:
|
|
935
|
+
lines.push(` var ${slot}: f32 = select(0.0, 1.0, ${a2} < ${b2});`);
|
|
936
|
+
break;
|
|
937
|
+
case SDF_PROGRAM_OP.Mod:
|
|
938
|
+
lines.push(` var ${slot}: f32 = ${a2} % ${b2};`);
|
|
939
|
+
break;
|
|
940
|
+
default:
|
|
941
|
+
throw new Error(`Unknown SdfProgram opcode ${opcodes[index]} at instruction ${index}.`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
lines.push(` return v${output};`);
|
|
945
|
+
lines.push("}");
|
|
946
|
+
return lines.join("\n");
|
|
947
|
+
}
|
|
948
|
+
function buildSdfProgramBrickComputeWgsl(program, options = {}) {
|
|
949
|
+
const functionName = requireWgslIdentifier(options.functionName ?? "sdfProgram", "functionName");
|
|
950
|
+
const entryPoint = requireWgslIdentifier(options.entryPoint ?? "sampleSdfBrick", "entryPoint");
|
|
951
|
+
const [workgroupX, workgroupY, workgroupZ] = requireWorkgroupSize(options.workgroupSize ?? [4, 4, 4]);
|
|
952
|
+
const bindingGroup = requireBindingIndex(options.bindingGroup ?? 0, "bindingGroup");
|
|
953
|
+
const paramsBinding = requireBindingIndex(options.paramsBinding ?? 0, "paramsBinding");
|
|
954
|
+
const distancesBinding = requireBindingIndex(options.distancesBinding ?? 1, "distancesBinding");
|
|
955
|
+
return [
|
|
956
|
+
buildSdfProgramWgslFunction(program, { functionName }),
|
|
957
|
+
"",
|
|
958
|
+
"struct SdfBrickParams {",
|
|
959
|
+
" origin: vec4f,",
|
|
960
|
+
" step: vec4f,",
|
|
961
|
+
" dims: vec4u,",
|
|
962
|
+
"};",
|
|
963
|
+
"",
|
|
964
|
+
`@group(${bindingGroup}) @binding(${paramsBinding}) var<uniform> brick: SdfBrickParams;`,
|
|
965
|
+
`@group(${bindingGroup}) @binding(${distancesBinding}) var<storage, read_write> distances: array<f32>;`,
|
|
966
|
+
"",
|
|
967
|
+
`@compute @workgroup_size(${workgroupX}, ${workgroupY}, ${workgroupZ})`,
|
|
968
|
+
`fn ${entryPoint}(@builtin(global_invocation_id) gid: vec3u) {`,
|
|
969
|
+
" let dims = brick.dims.xyz;",
|
|
970
|
+
" if (any(gid >= dims)) {",
|
|
971
|
+
" return;",
|
|
972
|
+
" }",
|
|
973
|
+
" let index = gid.x + dims.x * (gid.y + dims.y * gid.z);",
|
|
974
|
+
" let voxel = vec3f(f32(gid.x), f32(gid.y), f32(gid.z));",
|
|
975
|
+
" let p = brick.origin.xyz + voxel * brick.step.xyz;",
|
|
976
|
+
` distances[index] = ${functionName}(p);`,
|
|
977
|
+
"}"
|
|
978
|
+
].join("\n");
|
|
979
|
+
}
|
|
980
|
+
function wgslNumber(value) {
|
|
981
|
+
if (!Number.isFinite(value)) return "0.0";
|
|
982
|
+
const text = Number(value.toPrecision(9)).toString();
|
|
983
|
+
return text.includes(".") || text.includes("e") ? text : `${text}.0`;
|
|
984
|
+
}
|
|
985
|
+
function requireWgslIdentifier(value, name) {
|
|
986
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
|
|
987
|
+
throw new Error(`${name} must be a valid WGSL identifier.`);
|
|
988
|
+
}
|
|
989
|
+
return value;
|
|
990
|
+
}
|
|
991
|
+
function requireWorkgroupSize(value) {
|
|
992
|
+
const [x, y, z] = value;
|
|
993
|
+
for (const [name, size] of [
|
|
994
|
+
["x", x],
|
|
995
|
+
["y", y],
|
|
996
|
+
["z", z]
|
|
997
|
+
]) {
|
|
998
|
+
if (!Number.isInteger(size) || size < 1 || size > 256) {
|
|
999
|
+
throw new Error(`workgroupSize.${name} must be an integer from 1 to 256.`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
if (x * y * z > 256) {
|
|
1003
|
+
throw new Error("workgroupSize product must be at most 256 for portable WebGPU compute.");
|
|
1004
|
+
}
|
|
1005
|
+
return [x, y, z];
|
|
1006
|
+
}
|
|
1007
|
+
function requireBindingIndex(value, name) {
|
|
1008
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
1009
|
+
throw new Error(`${name} must be a non-negative integer.`);
|
|
1010
|
+
}
|
|
1011
|
+
return value;
|
|
1012
|
+
}
|
|
883
1013
|
function escapeXml(value) {
|
|
884
1014
|
return value.replace(/[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]/g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
885
1015
|
}
|
|
@@ -15157,8 +15287,12 @@ const runResultDeserializer = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Obje
|
|
|
15157
15287
|
__proto__: null,
|
|
15158
15288
|
deserializeRunResult
|
|
15159
15289
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
15290
|
+
function compilePlanNeedsNativeShapeLowering(plan) {
|
|
15291
|
+
if (plan.kind === "queryOwner") return compilePlanNeedsNativeShapeLowering(plan.base);
|
|
15292
|
+
return plan.kind !== "sdf";
|
|
15293
|
+
}
|
|
15160
15294
|
function serializedSceneObjectNeedsNativeShapeLowering(object) {
|
|
15161
|
-
return object.compilePlan !== null && object.compilePlan !== void 0;
|
|
15295
|
+
return object.compilePlan !== null && object.compilePlan !== void 0 && compilePlanNeedsNativeShapeLowering(object.compilePlan);
|
|
15162
15296
|
}
|
|
15163
15297
|
function serializedRunResultNeedsNativeOcctLowering(result) {
|
|
15164
15298
|
return result.objects.some(serializedSceneObjectNeedsNativeShapeLowering);
|
|
@@ -15753,7 +15887,7 @@ function computeServerSnapshot$1(state2, serverFiles, serverFolders, sharedModel
|
|
|
15753
15887
|
const isMeshHash = startupHashFile && MESH_EXTS.some((ext) => startupHashFile.toLowerCase().endsWith(ext));
|
|
15754
15888
|
const isBinaryHash = startupHashFile && BINARY_IMPORT_EXTS.some((ext) => startupHashFile.toLowerCase().endsWith(ext));
|
|
15755
15889
|
const meshPreviewFile = isMeshHash && startupHashFile && nextFiles[startupHashFile] !== void 0 ? startupHashFile : null;
|
|
15756
|
-
const newActiveFile = sharedBundle2 ? sharedBundle2.entry : sharedModel2 ? sharedModel2.filename : startupHashFile && !isBinaryHash && nextFiles[startupHashFile] !== void 0 ? startupHashFile : initialFile && nextFiles[initialFile] !== void 0 ? initialFile : activeFile && nextFiles[activeFile] ? activeFile : findPreferredEntryFile(availableFiles) || availableFiles.find((n) => n.endsWith(".js")) || availableFiles[0];
|
|
15890
|
+
const newActiveFile = sharedBundle2 ? sharedBundle2.entry : sharedModel2 ? sharedModel2.filename : startupHashFile && !isBinaryHash && nextFiles[startupHashFile] !== void 0 ? startupHashFile : initialFile && nextFiles[initialFile] !== void 0 ? initialFile : activeFile && nextFiles[activeFile] !== void 0 ? activeFile : findPreferredEntryFile(availableFiles) || availableFiles.find((n) => n.endsWith(".js")) || availableFiles[0] || "";
|
|
15757
15891
|
const nextDirty = Object.keys(nextFiles).some((p2) => nextSaved[p2] !== nextFiles[p2]);
|
|
15758
15892
|
const nextObjectSettingsByFile = Object.fromEntries(Object.entries(objectSettingsByFile).filter(([f2]) => f2 in nextFiles));
|
|
15759
15893
|
return {
|
|
@@ -15798,7 +15932,7 @@ function computeServerFileDelete$1(state2, filename) {
|
|
|
15798
15932
|
const newFolders = /* @__PURE__ */ new Set();
|
|
15799
15933
|
Object.keys(nextFiles).forEach((p2) => collectParentPaths(p2).forEach((f2) => newFolders.add(f2)));
|
|
15800
15934
|
const availableFiles = Object.keys(nextFiles);
|
|
15801
|
-
const newActiveFile = activeFile === filename ? findPreferredEntryFile(availableFiles) || availableFiles.find((n) => n.endsWith(".js")) || availableFiles[0] : activeFile;
|
|
15935
|
+
const newActiveFile = activeFile === filename ? findPreferredEntryFile(availableFiles) || availableFiles.find((n) => n.endsWith(".js")) || availableFiles[0] || "" : activeFile;
|
|
15802
15936
|
const nextObjectSettingsByFile = Object.fromEntries(Object.entries(objectSettingsByFile).filter(([f2]) => f2 in nextFiles));
|
|
15803
15937
|
return {
|
|
15804
15938
|
files: nextFiles,
|
|
@@ -15841,7 +15975,7 @@ const CRASH_COOLDOWN_MS = 2e3;
|
|
|
15841
15975
|
class EvalWorkerClient {
|
|
15842
15976
|
constructor(workerFactory = () => new Worker(new URL(
|
|
15843
15977
|
/* @vite-ignore */
|
|
15844
|
-
"/assets/evalWorker-
|
|
15978
|
+
"/assets/evalWorker-C2pm8LHP.js",
|
|
15845
15979
|
import.meta.url
|
|
15846
15980
|
), { type: "module" })) {
|
|
15847
15981
|
__publicField(this, "worker", null);
|
|
@@ -16332,6 +16466,7 @@ function projectTextFileTemplate(path) {
|
|
|
16332
16466
|
return "";
|
|
16333
16467
|
}
|
|
16334
16468
|
function monacoLanguageForProjectFile(path) {
|
|
16469
|
+
if (!path) return "plaintext";
|
|
16335
16470
|
const lower = path.toLowerCase();
|
|
16336
16471
|
if (lower.endsWith(".md")) return "markdown";
|
|
16337
16472
|
if (lower.endsWith(".json")) return "json";
|
|
@@ -16344,6 +16479,7 @@ function monacoLanguageForProjectFile(path) {
|
|
|
16344
16479
|
return "plaintext";
|
|
16345
16480
|
}
|
|
16346
16481
|
function highlightLanguageForProjectFile(path) {
|
|
16482
|
+
if (!path) return null;
|
|
16347
16483
|
const lower = path.toLowerCase();
|
|
16348
16484
|
if (lower.endsWith(".md")) return "markdown";
|
|
16349
16485
|
if (lower.endsWith(".json")) return "json";
|
|
@@ -17064,7 +17200,12 @@ const INITIAL_FOLDERS = collectInitialFolders(INITIAL_FILES);
|
|
|
17064
17200
|
const getActiveFileFromHash = () => {
|
|
17065
17201
|
const hash = window.location.hash.slice(1);
|
|
17066
17202
|
if (hash.startsWith("code/") || hash.startsWith("bundle/")) return null;
|
|
17067
|
-
|
|
17203
|
+
if (!hash) return null;
|
|
17204
|
+
try {
|
|
17205
|
+
return decodeURIComponent(hash);
|
|
17206
|
+
} catch {
|
|
17207
|
+
return hash;
|
|
17208
|
+
}
|
|
17068
17209
|
};
|
|
17069
17210
|
const STARTUP_HASH_FILE = getActiveFileFromHash();
|
|
17070
17211
|
const sharedModel = decodeSharedHash(window.location.hash);
|
|
@@ -17320,6 +17461,8 @@ function formatComputeBackendLabel(backend) {
|
|
|
17320
17461
|
return "Local OCCT";
|
|
17321
17462
|
case "truck":
|
|
17322
17463
|
return "Local Truck";
|
|
17464
|
+
case "sdf":
|
|
17465
|
+
return "Local SDF";
|
|
17323
17466
|
case "server-occt":
|
|
17324
17467
|
return "Server OCCT";
|
|
17325
17468
|
}
|
|
@@ -17523,7 +17666,7 @@ const initialActive = (() => {
|
|
|
17523
17666
|
}
|
|
17524
17667
|
const names = Object.keys(INITIAL_FILES);
|
|
17525
17668
|
const fallback = findPreferredEntryFile(names) || names.find((n) => n.endsWith(".js")) || names[0];
|
|
17526
|
-
return fallback;
|
|
17669
|
+
return fallback || "";
|
|
17527
17670
|
})();
|
|
17528
17671
|
const INITIAL_SAVED = {};
|
|
17529
17672
|
const MAX_RECENT_FILES = 200;
|
|
@@ -17606,6 +17749,19 @@ function isScalarInspectChannel(channel) {
|
|
|
17606
17749
|
function shouldUseScanRenderStyle(channel, displayMode) {
|
|
17607
17750
|
return isScalarInspectChannel(channel) && displayMode === "scan";
|
|
17608
17751
|
}
|
|
17752
|
+
function recordViewCommandDebug(command) {
|
|
17753
|
+
if (typeof window === "undefined") return;
|
|
17754
|
+
if (window.localStorage.getItem("forgecad:debugCamera") !== "1") return;
|
|
17755
|
+
const entry = {
|
|
17756
|
+
id: command.id,
|
|
17757
|
+
type: command.type,
|
|
17758
|
+
view: command.view,
|
|
17759
|
+
targetId: command.targetId,
|
|
17760
|
+
camera: command.camera
|
|
17761
|
+
};
|
|
17762
|
+
window.__forgecadViewCommandDebug = [...window.__forgecadViewCommandDebug ?? [], entry].slice(-50);
|
|
17763
|
+
console.debug("[forgecad view-command]", entry);
|
|
17764
|
+
}
|
|
17609
17765
|
const initialViewPreferences = readViewPreferences();
|
|
17610
17766
|
const initialInspectChannel = resolveInitialViewInspectChannel(initialViewPreferences.inspectChannel);
|
|
17611
17767
|
const initialInspectDisplayMode = resolveInspectDisplayMode(initialViewPreferences.inspectDisplayMode);
|
|
@@ -17620,6 +17776,28 @@ function resolveViewportScanGranularity(granularity, renderStyle) {
|
|
|
17620
17776
|
return renderStyle === "matrix" ? Math.min(next, SCAN_PROXY_MATRIX_GRANULARITY_MAX) : next;
|
|
17621
17777
|
}
|
|
17622
17778
|
const initialPreviewFile = resolvePreviewFile(initialActive, INITIAL_FILES);
|
|
17779
|
+
function openCodeEditorPatch() {
|
|
17780
|
+
writeViewPreferences({ codeEditorOpen: true });
|
|
17781
|
+
return { codeEditorOpen: true };
|
|
17782
|
+
}
|
|
17783
|
+
function activeFileHasViewportPreview(activeFile, files, meshPreviewFile) {
|
|
17784
|
+
if (meshPreviewFile) return true;
|
|
17785
|
+
if (!activeFile || !(activeFile in files)) return false;
|
|
17786
|
+
if (resolvePreviewFile(activeFile, files)) return true;
|
|
17787
|
+
return activeFile.toLowerCase().endsWith(".svg");
|
|
17788
|
+
}
|
|
17789
|
+
function codeEditorPatchForActiveFile(activeFile, files, meshPreviewFile) {
|
|
17790
|
+
if (!activeFile || !(activeFile in files)) return {};
|
|
17791
|
+
return activeFileHasViewportPreview(activeFile, files, meshPreviewFile) ? {} : openCodeEditorPatch();
|
|
17792
|
+
}
|
|
17793
|
+
function runResultHasViewportContent(result) {
|
|
17794
|
+
var _a3, _b2, _c, _d;
|
|
17795
|
+
return result.objects.length > 0 || !!result.assemblyKinematics || (((_a3 = result.jointsView) == null ? void 0 : _a3.joints.length) ?? 0) > 0 && ((_b2 = result.jointsView) == null ? void 0 : _b2.enabled) !== false || (((_c = result.renderLabels) == null ? void 0 : _c.length) ?? 0) > 0 || (((_d = result.debugHighlights3D) == null ? void 0 : _d.length) ?? 0) > 0;
|
|
17796
|
+
}
|
|
17797
|
+
function codeEditorPatchForRunResult(result, meshPreviewFile) {
|
|
17798
|
+
if (meshPreviewFile || runResultHasViewportContent(result)) return {};
|
|
17799
|
+
return openCodeEditorPatch();
|
|
17800
|
+
}
|
|
17623
17801
|
const initialObjectSettingsByFile = (() => {
|
|
17624
17802
|
const viewPreferences = initialViewPreferences;
|
|
17625
17803
|
if (viewPreferences.objectSettingsByFile && typeof viewPreferences.objectSettingsByFile === "object") {
|
|
@@ -17799,6 +17977,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17799
17977
|
const restored = targetPreviewFile && nextByFile[targetPreviewFile] ? nextByFile[targetPreviewFile] : {};
|
|
17800
17978
|
set({
|
|
17801
17979
|
activeFile: name,
|
|
17980
|
+
...codeEditorPatchForActiveFile(name, files, null),
|
|
17802
17981
|
recentFiles: touchAndPersistRecentFile(get().recentFiles, files, name),
|
|
17803
17982
|
meshPreviewFile: null,
|
|
17804
17983
|
// Clear mesh preview when switching files
|
|
@@ -17849,11 +18028,13 @@ const useForgeStore = create((set, get) => ({
|
|
|
17849
18028
|
if (get().files[normalized] !== void 0) return;
|
|
17850
18029
|
if (get().folders.includes(normalized)) return;
|
|
17851
18030
|
const template = projectTextFileTemplate(normalized);
|
|
18031
|
+
const nextFilesForEditor = { ...get().files, [normalized]: template };
|
|
17852
18032
|
const newFolders = Array.from(/* @__PURE__ */ new Set([...get().folders, ...collectParentPaths(normalized)])).sort();
|
|
17853
18033
|
set((s) => ({
|
|
17854
18034
|
files: { ...s.files, [normalized]: template },
|
|
17855
18035
|
savedFiles: { ...s.savedFiles, [normalized]: template },
|
|
17856
18036
|
activeFile: normalized,
|
|
18037
|
+
...codeEditorPatchForActiveFile(normalized, nextFilesForEditor, null),
|
|
17857
18038
|
recentFiles: touchAndPersistRecentFile(s.recentFiles, { ...s.files, [normalized]: template }, normalized),
|
|
17858
18039
|
paramOverrides: {},
|
|
17859
18040
|
jointValues: {},
|
|
@@ -17892,6 +18073,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17892
18073
|
files: remaining,
|
|
17893
18074
|
savedFiles: remainingSaved,
|
|
17894
18075
|
activeFile: newActive,
|
|
18076
|
+
...codeEditorPatchForActiveFile(newActive, remaining, null),
|
|
17895
18077
|
recentFiles: touchAndPersistRecentFile(get().recentFiles, remaining, newActive),
|
|
17896
18078
|
objectSettingsByFile: nextObjectSettingsByFile,
|
|
17897
18079
|
paramSnapshotsByFile: nextParamSnapshotsByFile,
|
|
@@ -17922,10 +18104,12 @@ const useForgeStore = create((set, get) => ({
|
|
|
17922
18104
|
const nextObjectSettingsByFile = remapObjectSettingsByFile(objectSettingsByFile, oldName, normalized);
|
|
17923
18105
|
const nextParamSnapshotsByFile = remapParamSnapshotsByFile(paramSnapshotsByFile, oldName, normalized);
|
|
17924
18106
|
writeViewPreferences({ objectSettingsByFile: nextObjectSettingsByFile, paramSnapshotsByFile: nextParamSnapshotsByFile });
|
|
18107
|
+
const nextActive = oldName === activeFile ? normalized : activeFile;
|
|
17925
18108
|
set({
|
|
17926
18109
|
files: remaining,
|
|
17927
18110
|
savedFiles: remainingSaved,
|
|
17928
|
-
activeFile:
|
|
18111
|
+
activeFile: nextActive,
|
|
18112
|
+
...codeEditorPatchForActiveFile(nextActive, remaining, null),
|
|
17929
18113
|
recentFiles: remapAndPersistRecentFiles(get().recentFiles, remaining, oldName, normalized),
|
|
17930
18114
|
objectSettingsByFile: nextObjectSettingsByFile,
|
|
17931
18115
|
paramSnapshotsByFile: nextParamSnapshotsByFile,
|
|
@@ -17983,6 +18167,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17983
18167
|
savedFiles: updatedSaved,
|
|
17984
18168
|
folders: updatedFolders,
|
|
17985
18169
|
activeFile: nextActive,
|
|
18170
|
+
...codeEditorPatchForActiveFile(nextActive, updatedFiles, null),
|
|
17986
18171
|
recentFiles: remapAndPersistRecentFiles(get().recentFiles, updatedFiles, normalizedOld, normalizedNew),
|
|
17987
18172
|
objectSettingsByFile: nextObjectSettingsByFile,
|
|
17988
18173
|
paramSnapshotsByFile: nextParamSnapshotsByFile,
|
|
@@ -18041,6 +18226,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
18041
18226
|
savedFiles: remainingSaved,
|
|
18042
18227
|
folders: remainingFolders,
|
|
18043
18228
|
activeFile: newActive,
|
|
18229
|
+
...codeEditorPatchForActiveFile(newActive, remainingFiles, null),
|
|
18044
18230
|
recentFiles: touchAndPersistRecentFile(get().recentFiles, remainingFiles, newActive),
|
|
18045
18231
|
objectSettingsByFile: nextObjectSettingsByFile,
|
|
18046
18232
|
paramSnapshotsByFile: nextParamSnapshotsByFile,
|
|
@@ -18068,7 +18254,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
18068
18254
|
}
|
|
18069
18255
|
},
|
|
18070
18256
|
dirty: false,
|
|
18071
|
-
filesLoading:
|
|
18257
|
+
filesLoading: false,
|
|
18072
18258
|
fileHandle: null,
|
|
18073
18259
|
setFileHandle: (fileHandle) => set({ fileHandle }),
|
|
18074
18260
|
result: null,
|
|
@@ -18195,7 +18381,14 @@ const useForgeStore = create((set, get) => ({
|
|
|
18195
18381
|
if (cached) {
|
|
18196
18382
|
const applied = buildRunState(previewFile, cached, get());
|
|
18197
18383
|
rememberAcceptedAssemblyPoseState(cached, readAssemblyPoseControlState(applied.nextState));
|
|
18198
|
-
set({
|
|
18384
|
+
set({
|
|
18385
|
+
...applied.nextState,
|
|
18386
|
+
...codeEditorPatchForRunResult(cached, meshPreviewFile),
|
|
18387
|
+
lastValidResult: cached,
|
|
18388
|
+
isEvaluating: false,
|
|
18389
|
+
evaluationPhase: "idle",
|
|
18390
|
+
buildLedgerEvents: []
|
|
18391
|
+
});
|
|
18199
18392
|
writeViewPreferences({ objectSettingsByFile: applied.nextObjectSettingsByFile, cutPlaneEnabled: applied.nextCutPlaneEnabled });
|
|
18200
18393
|
return;
|
|
18201
18394
|
}
|
|
@@ -18247,10 +18440,12 @@ const useForgeStore = create((set, get) => ({
|
|
|
18247
18440
|
const message = serverError instanceof Error ? serverError.message : String(serverError);
|
|
18248
18441
|
if (execId !== currentExecutionId) return;
|
|
18249
18442
|
rememberAcceptedAssemblyPoseState(null, EMPTY_ASSEMBLY_POSE_CONTROL_STATE);
|
|
18443
|
+
const failedResult = errorRunResult(`Server compute failed: ${message}`, performance.now() - tDispatch);
|
|
18250
18444
|
set({
|
|
18251
|
-
result:
|
|
18445
|
+
result: failedResult,
|
|
18252
18446
|
consoleLogs: [],
|
|
18253
18447
|
previewFile,
|
|
18448
|
+
...codeEditorPatchForRunResult(failedResult, meshPreviewFile),
|
|
18254
18449
|
isEvaluating: false,
|
|
18255
18450
|
evaluationPhase: "idle",
|
|
18256
18451
|
buildLedgerEvents: []
|
|
@@ -18281,12 +18476,25 @@ const useForgeStore = create((set, get) => ({
|
|
|
18281
18476
|
);
|
|
18282
18477
|
if (runResult.error) {
|
|
18283
18478
|
rememberAcceptedAssemblyPoseState(null, EMPTY_ASSEMBLY_POSE_CONTROL_STATE);
|
|
18284
|
-
set({
|
|
18479
|
+
set({
|
|
18480
|
+
result: runResult,
|
|
18481
|
+
consoleLogs: runResult.logs,
|
|
18482
|
+
previewFile,
|
|
18483
|
+
...codeEditorPatchForRunResult(runResult, meshPreviewFile),
|
|
18484
|
+
isEvaluating: false,
|
|
18485
|
+
evaluationPhase: "idle"
|
|
18486
|
+
});
|
|
18285
18487
|
} else {
|
|
18286
18488
|
storeCache(previewFile, code, files, paramOverrides, assemblyState, runQuality, executionBackend, runResult, serialized);
|
|
18287
18489
|
const applied = buildRunState(previewFile, runResult, get());
|
|
18288
18490
|
rememberAcceptedAssemblyPoseState(runResult, readAssemblyPoseControlState(applied.nextState));
|
|
18289
|
-
set({
|
|
18491
|
+
set({
|
|
18492
|
+
...applied.nextState,
|
|
18493
|
+
...codeEditorPatchForRunResult(runResult, meshPreviewFile),
|
|
18494
|
+
lastValidResult: runResult,
|
|
18495
|
+
isEvaluating: false,
|
|
18496
|
+
evaluationPhase: "idle"
|
|
18497
|
+
});
|
|
18290
18498
|
writeViewPreferences({ objectSettingsByFile: applied.nextObjectSettingsByFile, cutPlaneEnabled: applied.nextCutPlaneEnabled });
|
|
18291
18499
|
}
|
|
18292
18500
|
} catch (error) {
|
|
@@ -18295,7 +18503,14 @@ const useForgeStore = create((set, get) => ({
|
|
|
18295
18503
|
const message = error instanceof Error ? error.message : String(error);
|
|
18296
18504
|
const errResult = createErrorRunResult(message, runQuality);
|
|
18297
18505
|
rememberAcceptedAssemblyPoseState(null, EMPTY_ASSEMBLY_POSE_CONTROL_STATE);
|
|
18298
|
-
set({
|
|
18506
|
+
set({
|
|
18507
|
+
result: errResult,
|
|
18508
|
+
consoleLogs: errResult.logs,
|
|
18509
|
+
previewFile,
|
|
18510
|
+
...codeEditorPatchForRunResult(errResult, meshPreviewFile),
|
|
18511
|
+
isEvaluating: false,
|
|
18512
|
+
evaluationPhase: "idle"
|
|
18513
|
+
});
|
|
18299
18514
|
}
|
|
18300
18515
|
},
|
|
18301
18516
|
updateAssemblyPose: async () => {
|
|
@@ -18657,6 +18872,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
18657
18872
|
},
|
|
18658
18873
|
gridEnabled: initialViewPreferences.gridEnabled ?? true,
|
|
18659
18874
|
axesVisible: initialViewPreferences.axesVisible ?? true,
|
|
18875
|
+
viewCubeVisible: initialViewPreferences.viewCubeVisible ?? true,
|
|
18660
18876
|
gridSize: initialViewPreferences.gridSize ?? 10,
|
|
18661
18877
|
setGridEnabled: (enabled) => {
|
|
18662
18878
|
writeViewPreferences({ gridEnabled: enabled });
|
|
@@ -18666,6 +18882,10 @@ const useForgeStore = create((set, get) => ({
|
|
|
18666
18882
|
writeViewPreferences({ axesVisible: visible });
|
|
18667
18883
|
set({ axesVisible: visible });
|
|
18668
18884
|
},
|
|
18885
|
+
setViewCubeVisible: (visible) => {
|
|
18886
|
+
writeViewPreferences({ viewCubeVisible: visible });
|
|
18887
|
+
set({ viewCubeVisible: visible });
|
|
18888
|
+
},
|
|
18669
18889
|
setGridSize: (size) => {
|
|
18670
18890
|
writeViewPreferences({ gridSize: size });
|
|
18671
18891
|
set({ gridSize: size });
|
|
@@ -19002,7 +19222,11 @@ const useForgeStore = create((set, get) => ({
|
|
|
19002
19222
|
set({ objectPickSyncEnabled: enabled });
|
|
19003
19223
|
},
|
|
19004
19224
|
viewCommand: null,
|
|
19005
|
-
requestViewCommand: (command) =>
|
|
19225
|
+
requestViewCommand: (command) => {
|
|
19226
|
+
const viewCommand = { ...command, id: Date.now() };
|
|
19227
|
+
recordViewCommandDebug(viewCommand);
|
|
19228
|
+
set({ viewCommand });
|
|
19229
|
+
},
|
|
19006
19230
|
clearViewCommand: () => set({ viewCommand: null }),
|
|
19007
19231
|
viewportCameraState: null,
|
|
19008
19232
|
setViewportCameraState: (state2) => set({ viewportCameraState: state2 }),
|
|
@@ -19275,10 +19499,12 @@ const useForgeStore = create((set, get) => ({
|
|
|
19275
19499
|
loadFromText: (text, name) => {
|
|
19276
19500
|
const normalized = normalizePath(name);
|
|
19277
19501
|
const newFolders = Array.from(/* @__PURE__ */ new Set([...get().folders, ...collectParentPaths(normalized)])).sort();
|
|
19502
|
+
const nextFilesForEditor = { ...get().files, [normalized]: text };
|
|
19278
19503
|
set((s) => ({
|
|
19279
19504
|
files: { ...s.files, [normalized]: text },
|
|
19280
19505
|
savedFiles: { ...s.savedFiles, [normalized]: text },
|
|
19281
19506
|
activeFile: normalized,
|
|
19507
|
+
...codeEditorPatchForActiveFile(normalized, nextFilesForEditor, null),
|
|
19282
19508
|
recentFiles: touchAndPersistRecentFile(s.recentFiles, { ...s.files, [normalized]: text }, normalized),
|
|
19283
19509
|
fileHandle: null,
|
|
19284
19510
|
dirty: false,
|
|
@@ -19338,6 +19564,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
19338
19564
|
return {
|
|
19339
19565
|
files: nextFiles,
|
|
19340
19566
|
activeFile: nextActive,
|
|
19567
|
+
...codeEditorPatchForActiveFile(nextActive, nextFiles, nextMeshPreview),
|
|
19341
19568
|
recentFiles: touchAndPersistRecentFile(s.recentFiles, nextFiles, nextActive),
|
|
19342
19569
|
fileHandle: null,
|
|
19343
19570
|
dirty: true,
|
|
@@ -19396,6 +19623,12 @@ const useForgeStore = create((set, get) => ({
|
|
|
19396
19623
|
writeViewPreferences({ fileExplorerOpen: nextFileExplorerOpen });
|
|
19397
19624
|
return { fileExplorerOpen: nextFileExplorerOpen };
|
|
19398
19625
|
}),
|
|
19626
|
+
codeEditorOpen: initialViewPreferences.codeEditorOpen ?? true,
|
|
19627
|
+
toggleCodeEditor: () => set((s) => {
|
|
19628
|
+
const nextCodeEditorOpen = !s.codeEditorOpen;
|
|
19629
|
+
writeViewPreferences({ codeEditorOpen: nextCodeEditorOpen });
|
|
19630
|
+
return { codeEditorOpen: nextCodeEditorOpen };
|
|
19631
|
+
}),
|
|
19399
19632
|
viewPanelOpen: initialViewPreferences.viewPanelOpen ?? true,
|
|
19400
19633
|
toggleViewPanel: () => set((s) => {
|
|
19401
19634
|
const nextViewPanelOpen = !s.viewPanelOpen;
|
|
@@ -19423,6 +19656,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
19423
19656
|
set({
|
|
19424
19657
|
...nextState,
|
|
19425
19658
|
activeFile: newActiveFile,
|
|
19659
|
+
...codeEditorPatchForActiveFile(newActiveFile, nextFiles, nextState.meshPreviewFile),
|
|
19426
19660
|
recentFiles: touchAndPersistRecentFile(baseRecentFiles, nextFiles, newActiveFile),
|
|
19427
19661
|
filesLoading: false
|
|
19428
19662
|
});
|
|
@@ -19459,6 +19693,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
19459
19693
|
set({
|
|
19460
19694
|
...nextState,
|
|
19461
19695
|
activeFile: newActiveFile,
|
|
19696
|
+
...codeEditorPatchForActiveFile(newActiveFile, nextState.files, null),
|
|
19462
19697
|
recentFiles: touchAndPersistRecentFile(state2.recentFiles, nextState.files, newActiveFile)
|
|
19463
19698
|
});
|
|
19464
19699
|
if (newActiveFile && newActiveFile !== prevActiveFile) {
|
|
@@ -19500,10 +19735,13 @@ const useForgeStore = create((set, get) => ({
|
|
|
19500
19735
|
openAISkill: () => set({ aiSkillOpen: true }),
|
|
19501
19736
|
closeAISkill: () => set({ aiSkillOpen: false }),
|
|
19502
19737
|
editorNavigate: null,
|
|
19503
|
-
requestEditorNavigate: (line) =>
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
19738
|
+
requestEditorNavigate: (line) => {
|
|
19739
|
+
writeViewPreferences({ codeEditorOpen: true });
|
|
19740
|
+
set((s) => {
|
|
19741
|
+
var _a3;
|
|
19742
|
+
return { codeEditorOpen: true, editorNavigate: { line, id: (((_a3 = s.editorNavigate) == null ? void 0 : _a3.id) ?? 0) + 1 } };
|
|
19743
|
+
});
|
|
19744
|
+
},
|
|
19507
19745
|
clearEditorNavigate: () => set({ editorNavigate: null }),
|
|
19508
19746
|
disableRunCache: initialViewPreferences.disableRunCache ?? false,
|
|
19509
19747
|
setDisableRunCache: (disabled) => {
|
|
@@ -27622,7 +27860,7 @@ function ConstructionGhostOverlay({ matrix }) {
|
|
|
27622
27860
|
class ConstructionHistoryWorkerClient {
|
|
27623
27861
|
constructor(workerFactory = () => new Worker(new URL(
|
|
27624
27862
|
/* @vite-ignore */
|
|
27625
|
-
"/assets/constructionHistoryWorker-
|
|
27863
|
+
"/assets/constructionHistoryWorker-CZ42Dksy.js",
|
|
27626
27864
|
import.meta.url
|
|
27627
27865
|
), { type: "module" })) {
|
|
27628
27866
|
__publicField(this, "worker", null);
|
|
@@ -30134,7 +30372,7 @@ function generateReportInWorker(options) {
|
|
|
30134
30372
|
return new Promise((resolve2, reject) => {
|
|
30135
30373
|
const worker = new Worker(new URL(
|
|
30136
30374
|
/* @vite-ignore */
|
|
30137
|
-
"/assets/reportWorker-
|
|
30375
|
+
"/assets/reportWorker-CwenM7wB.js",
|
|
30138
30376
|
import.meta.url
|
|
30139
30377
|
), { type: "module" });
|
|
30140
30378
|
const cleanup = () => {
|
|
@@ -30339,7 +30577,7 @@ const SURFACE_PALETTE = ["#4488cc", "#44cc88", "#cc8844", "#cc44aa", "#88cc44",
|
|
|
30339
30577
|
function toPage(tx, x, y) {
|
|
30340
30578
|
return [x * tx.scale + tx.offsetX, y * tx.scale + tx.offsetY];
|
|
30341
30579
|
}
|
|
30342
|
-
function computeBounds
|
|
30580
|
+
function computeBounds(meta) {
|
|
30343
30581
|
const bounds = { min: [Infinity, Infinity], max: [-Infinity, -Infinity] };
|
|
30344
30582
|
function expand(x, y) {
|
|
30345
30583
|
bounds.min[0] = Math.min(bounds.min[0], x);
|
|
@@ -30405,7 +30643,7 @@ function computeBounds$1(meta) {
|
|
|
30405
30643
|
return bounds;
|
|
30406
30644
|
}
|
|
30407
30645
|
function computeTransform(meta, ptsPerMm, marginPts) {
|
|
30408
|
-
const bounds = computeBounds
|
|
30646
|
+
const bounds = computeBounds(meta);
|
|
30409
30647
|
const sketchW = bounds.max[0] - bounds.min[0];
|
|
30410
30648
|
const sketchH = bounds.max[1] - bounds.min[1];
|
|
30411
30649
|
return {
|
|
@@ -30701,7 +30939,7 @@ function generateSketchPdf(meta, options) {
|
|
|
30701
30939
|
const tx = computeTransform(meta, POINTS_PER_MM, MARGIN);
|
|
30702
30940
|
tx.pageWidth = Math.max(tx.pageWidth, 600);
|
|
30703
30941
|
tx.pageHeight = Math.max(tx.pageHeight, 400);
|
|
30704
|
-
const bounds = computeBounds
|
|
30942
|
+
const bounds = computeBounds(meta);
|
|
30705
30943
|
const extent = Math.max(bounds.max[0] - bounds.min[0], bounds.max[1] - bounds.min[1], 1);
|
|
30706
30944
|
const baseExtent = 100;
|
|
30707
30945
|
const autoFontScale = Math.max(1, extent / baseExtent);
|
|
@@ -30881,9 +31119,14 @@ async function exportMeshFromStore(format, preferredStem, options = {}) {
|
|
|
30881
31119
|
downloadExportArtifact(await buildMeshExportArtifact(format, stem, runResult, objectSettings));
|
|
30882
31120
|
}
|
|
30883
31121
|
async function exportExactFromStore(format, preferredStem) {
|
|
30884
|
-
const { result, activeFile, files, paramOverrides, runQuality } = useForgeStore.getState();
|
|
31122
|
+
const { result, activeFile, files, paramOverrides, runQuality, activeBackend } = useForgeStore.getState();
|
|
30885
31123
|
const assemblyState = resolveAssemblyStateForExport();
|
|
30886
31124
|
requireSuccessfulRunResult(result);
|
|
31125
|
+
if (activeBackend === "sdf") {
|
|
31126
|
+
throw new Error(
|
|
31127
|
+
"STEP/BREP exact export is not available from the SDF backend. Use 3MF, OBJ, or STL mesh export, or switch to OCCT for exact B-rep export."
|
|
31128
|
+
);
|
|
31129
|
+
}
|
|
30887
31130
|
const stem = sanitizeExportStem(preferredStem ?? deriveExportStem(activeFile));
|
|
30888
31131
|
const code = files[activeFile];
|
|
30889
31132
|
if (!code) throw new Error(`Active file "${activeFile}" is missing.`);
|
|
@@ -30895,7 +31138,8 @@ async function exportExactFromStore(format, preferredStem) {
|
|
|
30895
31138
|
quality: runQuality,
|
|
30896
31139
|
paramOverrides,
|
|
30897
31140
|
assemblyState,
|
|
30898
|
-
isNotebook: false
|
|
31141
|
+
isNotebook: false,
|
|
31142
|
+
activeBackend
|
|
30899
31143
|
});
|
|
30900
31144
|
triggerDownload(blob, `${stem}.${format}`);
|
|
30901
31145
|
} finally {
|
|
@@ -33586,7 +33830,7 @@ const PHASE_CONFIG = {
|
|
|
33586
33830
|
};
|
|
33587
33831
|
const PHASE_ORDER = ["kernel-init", "evaluating", "serializing"];
|
|
33588
33832
|
function formatEvaluationBackendLabel(activeBackend, computeTarget) {
|
|
33589
|
-
const backend = activeBackend === "occt" ? "OCCT" : activeBackend === "manifold" ? "Manifold" : activeBackend === "truck" ? "Truck" : "kernel";
|
|
33833
|
+
const backend = activeBackend === "occt" ? "OCCT" : activeBackend === "manifold" ? "Manifold" : activeBackend === "truck" ? "Truck" : activeBackend === "sdf" ? "SDF" : "kernel";
|
|
33590
33834
|
return computeTarget === "server" ? "Server OCCT" : `Local ${backend}`;
|
|
33591
33835
|
}
|
|
33592
33836
|
function EvaluationIndicator({
|
|
@@ -34180,6 +34424,712 @@ function SectionCapPreview({
|
|
|
34180
34424
|
if (previewPlanes.length === 0) return null;
|
|
34181
34425
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: previewPlanes.map((plane) => /* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { geometry: plane.geometry, renderOrder: plane.renderOrder, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(PreviewCapMaterial, { color: color2, opacity, plane }) }, plane.planeId)) });
|
|
34182
34426
|
}
|
|
34427
|
+
const DEFAULT_TARGET_LONGEST_AXIS_SAMPLES = 96;
|
|
34428
|
+
const DEFAULT_MAX_VOXELS = 18e4;
|
|
34429
|
+
const DEFAULT_MIN_AXIS_SAMPLES = 8;
|
|
34430
|
+
const BRICK_PROGRAM_OPCODE_THRESHOLD = 768;
|
|
34431
|
+
function compiledSdfProgramFromSceneData(program) {
|
|
34432
|
+
return {
|
|
34433
|
+
opcodes: Uint8Array.from(program.opcodes),
|
|
34434
|
+
argA: Int32Array.from(program.argA),
|
|
34435
|
+
argB: Int32Array.from(program.argB),
|
|
34436
|
+
argC: Int32Array.from(program.argC),
|
|
34437
|
+
constants: Float64Array.from(program.constants),
|
|
34438
|
+
output: program.output,
|
|
34439
|
+
slotCount: program.slotCount
|
|
34440
|
+
};
|
|
34441
|
+
}
|
|
34442
|
+
function shouldUseSdfDistanceBrick(data, opcodeThreshold = BRICK_PROGRAM_OPCODE_THRESHOLD) {
|
|
34443
|
+
return Boolean((data == null ? void 0 : data.program) && data.program.opcodes.length >= opcodeThreshold && finitePositiveBounds(data.bounds));
|
|
34444
|
+
}
|
|
34445
|
+
function chooseSdfDistanceBrickResolution(bounds, options = {}) {
|
|
34446
|
+
const targetLongestAxisSamples = clampInteger(options.targetLongestAxisSamples ?? DEFAULT_TARGET_LONGEST_AXIS_SAMPLES, 4, 256);
|
|
34447
|
+
const maxVoxels = clampInteger(options.maxVoxels ?? DEFAULT_MAX_VOXELS, 512, 16e6);
|
|
34448
|
+
const minAxisSamples = clampInteger(options.minAxisSamples ?? DEFAULT_MIN_AXIS_SAMPLES, 2, 64);
|
|
34449
|
+
const size = boundsSize(bounds);
|
|
34450
|
+
const longest = Math.max(...size);
|
|
34451
|
+
if (!Number.isFinite(longest) || longest <= 0) {
|
|
34452
|
+
return { dims: [minAxisSamples, minAxisSamples, minAxisSamples], cellSize: 1, voxelCount: minAxisSamples ** 3 };
|
|
34453
|
+
}
|
|
34454
|
+
let samples = targetLongestAxisSamples;
|
|
34455
|
+
while (samples >= minAxisSamples) {
|
|
34456
|
+
const cellSize = longest / Math.max(1, samples - 1);
|
|
34457
|
+
const dims = size.map((axis) => Math.max(minAxisSamples, Math.ceil(axis / cellSize) + 1));
|
|
34458
|
+
const voxelCount = dims[0] * dims[1] * dims[2];
|
|
34459
|
+
if (voxelCount <= maxVoxels || samples === minAxisSamples) return { dims, cellSize, voxelCount };
|
|
34460
|
+
samples = Math.max(minAxisSamples, Math.floor(samples * 0.9));
|
|
34461
|
+
}
|
|
34462
|
+
return {
|
|
34463
|
+
dims: [minAxisSamples, minAxisSamples, minAxisSamples],
|
|
34464
|
+
cellSize: longest / Math.max(1, minAxisSamples - 1),
|
|
34465
|
+
voxelCount: minAxisSamples ** 3
|
|
34466
|
+
};
|
|
34467
|
+
}
|
|
34468
|
+
function buildSdfDistanceBrick(data, options = {}) {
|
|
34469
|
+
const startedAt = performance.now();
|
|
34470
|
+
const resolution = chooseSdfDistanceBrickResolution(data.bounds, options);
|
|
34471
|
+
const program = compiledSdfProgramFromSceneData(data.program);
|
|
34472
|
+
const evalSdf = compileSdfProgramEvaluator3(program);
|
|
34473
|
+
const distances = new Float32Array(resolution.voxelCount);
|
|
34474
|
+
const min = [...data.bounds.min];
|
|
34475
|
+
const max2 = [...data.bounds.max];
|
|
34476
|
+
const size = boundsSize(data.bounds);
|
|
34477
|
+
const [nx, ny, nz] = resolution.dims;
|
|
34478
|
+
let offset = 0;
|
|
34479
|
+
for (let z = 0; z < nz; z += 1) {
|
|
34480
|
+
const pz = coordinateAt(min[2], size[2], z, nz);
|
|
34481
|
+
for (let y = 0; y < ny; y += 1) {
|
|
34482
|
+
const py = coordinateAt(min[1], size[1], y, ny);
|
|
34483
|
+
for (let x = 0; x < nx; x += 1) {
|
|
34484
|
+
const px = coordinateAt(min[0], size[0], x, nx);
|
|
34485
|
+
const d = evalSdf(px, py, pz);
|
|
34486
|
+
distances[offset] = Number.isFinite(d) ? d : 1e9;
|
|
34487
|
+
offset += 1;
|
|
34488
|
+
}
|
|
34489
|
+
}
|
|
34490
|
+
}
|
|
34491
|
+
return {
|
|
34492
|
+
...resolution,
|
|
34493
|
+
min,
|
|
34494
|
+
max: max2,
|
|
34495
|
+
distances,
|
|
34496
|
+
buildMs: performance.now() - startedAt,
|
|
34497
|
+
buildSource: "cpu"
|
|
34498
|
+
};
|
|
34499
|
+
}
|
|
34500
|
+
function finitePositiveBounds(bounds) {
|
|
34501
|
+
const size = boundsSize(bounds);
|
|
34502
|
+
return [...bounds.min, ...bounds.max, ...size].every(Number.isFinite) && size.every((axis) => axis > 0);
|
|
34503
|
+
}
|
|
34504
|
+
function boundsSize(bounds) {
|
|
34505
|
+
return [bounds.max[0] - bounds.min[0], bounds.max[1] - bounds.min[1], bounds.max[2] - bounds.min[2]];
|
|
34506
|
+
}
|
|
34507
|
+
function coordinateAt(min, size, index, count) {
|
|
34508
|
+
if (count <= 1) return min;
|
|
34509
|
+
return min + size * (index / (count - 1));
|
|
34510
|
+
}
|
|
34511
|
+
function clampInteger(value, min, max2) {
|
|
34512
|
+
if (!Number.isFinite(value)) return min;
|
|
34513
|
+
return Math.max(min, Math.min(max2, Math.round(value)));
|
|
34514
|
+
}
|
|
34515
|
+
const DEFAULT_WORKGROUP_SIZE = [4, 4, 4];
|
|
34516
|
+
const BRICK_PARAMS_BYTE_LENGTH = 48;
|
|
34517
|
+
async function buildSdfDistanceBrickWebGpu(data, options = {}) {
|
|
34518
|
+
var _a3, _b2, _c, _d;
|
|
34519
|
+
const gpu = options.gpu ?? currentBrowserGpu();
|
|
34520
|
+
if (!gpu) return null;
|
|
34521
|
+
const adapter = await gpu.requestAdapter();
|
|
34522
|
+
if (!adapter) return null;
|
|
34523
|
+
const startedAt = performance.now();
|
|
34524
|
+
const device = await adapter.requestDevice();
|
|
34525
|
+
const buffers = [];
|
|
34526
|
+
try {
|
|
34527
|
+
const resolution = chooseSdfDistanceBrickResolution(data.bounds, options);
|
|
34528
|
+
const min = [...data.bounds.min];
|
|
34529
|
+
const max2 = [...data.bounds.max];
|
|
34530
|
+
const step = brickStep(data.bounds, resolution.dims);
|
|
34531
|
+
const byteLength2 = resolution.voxelCount * Float32Array.BYTES_PER_ELEMENT;
|
|
34532
|
+
const workgroupSize = options.workgroupSize ?? DEFAULT_WORKGROUP_SIZE;
|
|
34533
|
+
const entryPoint = "sampleSdfBrick";
|
|
34534
|
+
const module = device.createShaderModule({
|
|
34535
|
+
label: "ForgeCAD SDF distance brick compute shader",
|
|
34536
|
+
code: buildSdfProgramBrickComputeWgsl(compiledSdfProgramFromSceneData(data.program), { entryPoint, workgroupSize })
|
|
34537
|
+
});
|
|
34538
|
+
const pipeline = device.createComputePipeline({
|
|
34539
|
+
label: "ForgeCAD SDF distance brick compute pipeline",
|
|
34540
|
+
layout: "auto",
|
|
34541
|
+
compute: { module, entryPoint }
|
|
34542
|
+
});
|
|
34543
|
+
const usage = webGpuUsage();
|
|
34544
|
+
const paramsBuffer = device.createBuffer({
|
|
34545
|
+
label: "ForgeCAD SDF distance brick params",
|
|
34546
|
+
size: BRICK_PARAMS_BYTE_LENGTH,
|
|
34547
|
+
usage: usage.UNIFORM | usage.COPY_DST
|
|
34548
|
+
});
|
|
34549
|
+
const distancesBuffer = device.createBuffer({
|
|
34550
|
+
label: "ForgeCAD SDF distance brick distances",
|
|
34551
|
+
size: byteLength2,
|
|
34552
|
+
usage: usage.STORAGE | usage.COPY_SRC
|
|
34553
|
+
});
|
|
34554
|
+
const readBuffer = device.createBuffer({
|
|
34555
|
+
label: "ForgeCAD SDF distance brick readback",
|
|
34556
|
+
size: byteLength2,
|
|
34557
|
+
usage: usage.MAP_READ | usage.COPY_DST
|
|
34558
|
+
});
|
|
34559
|
+
buffers.push(paramsBuffer, distancesBuffer, readBuffer);
|
|
34560
|
+
device.queue.writeBuffer(paramsBuffer, 0, packSdfDistanceBrickWebGpuParams(min, step, resolution.dims));
|
|
34561
|
+
const bindGroup = device.createBindGroup({
|
|
34562
|
+
label: "ForgeCAD SDF distance brick bind group",
|
|
34563
|
+
layout: pipeline.getBindGroupLayout(0),
|
|
34564
|
+
entries: [
|
|
34565
|
+
{ binding: 0, resource: { buffer: paramsBuffer } },
|
|
34566
|
+
{ binding: 1, resource: { buffer: distancesBuffer } }
|
|
34567
|
+
]
|
|
34568
|
+
});
|
|
34569
|
+
const encoder2 = device.createCommandEncoder({ label: "ForgeCAD SDF distance brick command encoder" });
|
|
34570
|
+
const pass = encoder2.beginComputePass({ label: "ForgeCAD SDF distance brick compute pass" });
|
|
34571
|
+
pass.setPipeline(pipeline);
|
|
34572
|
+
pass.setBindGroup(0, bindGroup);
|
|
34573
|
+
pass.dispatchWorkgroups(
|
|
34574
|
+
Math.ceil(resolution.dims[0] / workgroupSize[0]),
|
|
34575
|
+
Math.ceil(resolution.dims[1] / workgroupSize[1]),
|
|
34576
|
+
Math.ceil(resolution.dims[2] / workgroupSize[2])
|
|
34577
|
+
);
|
|
34578
|
+
pass.end();
|
|
34579
|
+
encoder2.copyBufferToBuffer(distancesBuffer, 0, readBuffer, 0, byteLength2);
|
|
34580
|
+
device.queue.submit([encoder2.finish()]);
|
|
34581
|
+
await ((_b2 = (_a3 = device.queue).onSubmittedWorkDone) == null ? void 0 : _b2.call(_a3));
|
|
34582
|
+
await readBuffer.mapAsync(webGpuMapModeRead());
|
|
34583
|
+
const mapped = readBuffer.getMappedRange();
|
|
34584
|
+
const distances = new Float32Array(mapped.slice(0, byteLength2));
|
|
34585
|
+
readBuffer.unmap();
|
|
34586
|
+
return {
|
|
34587
|
+
...resolution,
|
|
34588
|
+
min,
|
|
34589
|
+
max: max2,
|
|
34590
|
+
distances,
|
|
34591
|
+
buildMs: performance.now() - startedAt,
|
|
34592
|
+
buildSource: "webgpu"
|
|
34593
|
+
};
|
|
34594
|
+
} finally {
|
|
34595
|
+
for (const buffer of buffers) (_c = buffer.destroy) == null ? void 0 : _c.call(buffer);
|
|
34596
|
+
(_d = device.destroy) == null ? void 0 : _d.call(device);
|
|
34597
|
+
}
|
|
34598
|
+
}
|
|
34599
|
+
function isSdfDistanceBrickWebGpuEnabled() {
|
|
34600
|
+
if (typeof window === "undefined") return false;
|
|
34601
|
+
const params = new URLSearchParams(window.location.search);
|
|
34602
|
+
return params.has("sdf-webgpu-bricks") || window.localStorage.getItem("fc-sdf-webgpu-bricks") === "1";
|
|
34603
|
+
}
|
|
34604
|
+
function packSdfDistanceBrickWebGpuParams(origin, step, dims) {
|
|
34605
|
+
const buffer = new ArrayBuffer(BRICK_PARAMS_BYTE_LENGTH);
|
|
34606
|
+
const floats = new Float32Array(buffer, 0, 8);
|
|
34607
|
+
floats[0] = origin[0];
|
|
34608
|
+
floats[1] = origin[1];
|
|
34609
|
+
floats[2] = origin[2];
|
|
34610
|
+
floats[3] = 0;
|
|
34611
|
+
floats[4] = step[0];
|
|
34612
|
+
floats[5] = step[1];
|
|
34613
|
+
floats[6] = step[2];
|
|
34614
|
+
floats[7] = 0;
|
|
34615
|
+
const uints = new Uint32Array(buffer, 32, 4);
|
|
34616
|
+
uints[0] = dims[0];
|
|
34617
|
+
uints[1] = dims[1];
|
|
34618
|
+
uints[2] = dims[2];
|
|
34619
|
+
uints[3] = 0;
|
|
34620
|
+
return buffer;
|
|
34621
|
+
}
|
|
34622
|
+
function brickStep(bounds, dims) {
|
|
34623
|
+
return [
|
|
34624
|
+
axisStep(bounds.min[0], bounds.max[0], dims[0]),
|
|
34625
|
+
axisStep(bounds.min[1], bounds.max[1], dims[1]),
|
|
34626
|
+
axisStep(bounds.min[2], bounds.max[2], dims[2])
|
|
34627
|
+
];
|
|
34628
|
+
}
|
|
34629
|
+
function axisStep(min, max2, count) {
|
|
34630
|
+
if (count <= 1) return 0;
|
|
34631
|
+
return (max2 - min) / (count - 1);
|
|
34632
|
+
}
|
|
34633
|
+
function currentBrowserGpu() {
|
|
34634
|
+
const nav = globalThis.navigator;
|
|
34635
|
+
return (nav == null ? void 0 : nav.gpu) ?? null;
|
|
34636
|
+
}
|
|
34637
|
+
function webGpuUsage() {
|
|
34638
|
+
return globalThis.GPUBufferUsage ?? {
|
|
34639
|
+
MAP_READ: 1,
|
|
34640
|
+
COPY_SRC: 4,
|
|
34641
|
+
COPY_DST: 8,
|
|
34642
|
+
UNIFORM: 64,
|
|
34643
|
+
STORAGE: 128
|
|
34644
|
+
};
|
|
34645
|
+
}
|
|
34646
|
+
function webGpuMapModeRead() {
|
|
34647
|
+
var _a3;
|
|
34648
|
+
return ((_a3 = globalThis.GPUMapMode) == null ? void 0 : _a3.READ) ?? 1;
|
|
34649
|
+
}
|
|
34650
|
+
const MAX_CLIP_PLANES$1 = 8;
|
|
34651
|
+
const SDF_BRICK_VERTEX_SHADER = `precision highp float;
|
|
34652
|
+
|
|
34653
|
+
out vec3 vLocalPosition;
|
|
34654
|
+
|
|
34655
|
+
void main() {
|
|
34656
|
+
vLocalPosition = position;
|
|
34657
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
34658
|
+
}
|
|
34659
|
+
`;
|
|
34660
|
+
const SDF_BRICK_FRAGMENT_SHADER = `precision highp float;
|
|
34661
|
+
precision highp sampler3D;
|
|
34662
|
+
|
|
34663
|
+
in vec3 vLocalPosition;
|
|
34664
|
+
out vec4 outColor;
|
|
34665
|
+
|
|
34666
|
+
${SDF_LINEAR_OUTPUT_COLOR_GLSL}
|
|
34667
|
+
|
|
34668
|
+
uniform sampler3D uDistanceTexture;
|
|
34669
|
+
uniform mat4 uCameraMatrixWorld;
|
|
34670
|
+
uniform mat4 uLocalToWorld;
|
|
34671
|
+
uniform mat4 uWorldToLocal;
|
|
34672
|
+
uniform mat4 uViewProjectionMatrix;
|
|
34673
|
+
uniform vec3 uBrickMin;
|
|
34674
|
+
uniform vec3 uBrickMax;
|
|
34675
|
+
uniform vec3 uColor;
|
|
34676
|
+
uniform vec3 uHoverColor;
|
|
34677
|
+
uniform float uAlpha;
|
|
34678
|
+
uniform float uHoverIntensity;
|
|
34679
|
+
uniform float uRayStep;
|
|
34680
|
+
uniform float uIsoEps;
|
|
34681
|
+
uniform float uNormalStep;
|
|
34682
|
+
uniform float uIsOrthographic;
|
|
34683
|
+
uniform int uClipPlaneCount;
|
|
34684
|
+
uniform vec4 uClipPlanes[8];
|
|
34685
|
+
|
|
34686
|
+
const int MAX_STEPS = 420;
|
|
34687
|
+
const int REFINE_STEPS = 7;
|
|
34688
|
+
|
|
34689
|
+
bool intersectAabb(vec3 ro, vec3 rd, vec3 bmin, vec3 bmax, out float tNear, out float tFar) {
|
|
34690
|
+
vec3 inv = 1.0 / rd;
|
|
34691
|
+
vec3 t0 = (bmin - ro) * inv;
|
|
34692
|
+
vec3 t1 = (bmax - ro) * inv;
|
|
34693
|
+
vec3 tmin = min(t0, t1);
|
|
34694
|
+
vec3 tmax = max(t0, t1);
|
|
34695
|
+
tNear = max(max(tmin.x, tmin.y), tmin.z);
|
|
34696
|
+
tFar = min(min(tmax.x, tmax.y), tmax.z);
|
|
34697
|
+
return tFar >= max(tNear, 0.0);
|
|
34698
|
+
}
|
|
34699
|
+
|
|
34700
|
+
float brickSdf(vec3 p) {
|
|
34701
|
+
vec3 uv = clamp((p - uBrickMin) / max(uBrickMax - uBrickMin, vec3(1e-6)), vec3(0.0), vec3(1.0));
|
|
34702
|
+
return texture(uDistanceTexture, uv).r;
|
|
34703
|
+
}
|
|
34704
|
+
|
|
34705
|
+
float clippedBrickSdf(vec3 p) {
|
|
34706
|
+
float d = brickSdf(p);
|
|
34707
|
+
for (int i = 0; i < 8; i++) {
|
|
34708
|
+
if (i >= uClipPlaneCount) break;
|
|
34709
|
+
vec4 plane = uClipPlanes[i];
|
|
34710
|
+
d = max(d, dot(plane.xyz, p) + plane.w);
|
|
34711
|
+
}
|
|
34712
|
+
return d;
|
|
34713
|
+
}
|
|
34714
|
+
|
|
34715
|
+
bool crossedSurface(float a, float b) {
|
|
34716
|
+
return (a < 0.0 && b > 0.0) || (a > 0.0 && b < 0.0);
|
|
34717
|
+
}
|
|
34718
|
+
|
|
34719
|
+
float refineSurface(vec3 ro, vec3 rd, float lo, float hi) {
|
|
34720
|
+
float dLo = clippedBrickSdf(ro + rd * lo);
|
|
34721
|
+
for (int i = 0; i < REFINE_STEPS; i++) {
|
|
34722
|
+
float mid = 0.5 * (lo + hi);
|
|
34723
|
+
float dMid = clippedBrickSdf(ro + rd * mid);
|
|
34724
|
+
if (abs(dMid) < uIsoEps) return mid;
|
|
34725
|
+
if (crossedSurface(dLo, dMid)) {
|
|
34726
|
+
hi = mid;
|
|
34727
|
+
} else {
|
|
34728
|
+
lo = mid;
|
|
34729
|
+
dLo = dMid;
|
|
34730
|
+
}
|
|
34731
|
+
}
|
|
34732
|
+
return 0.5 * (lo + hi);
|
|
34733
|
+
}
|
|
34734
|
+
|
|
34735
|
+
vec3 estimateNormal(vec3 p) {
|
|
34736
|
+
vec2 e = vec2(uNormalStep, 0.0);
|
|
34737
|
+
return normalize(vec3(
|
|
34738
|
+
brickSdf(p + e.xyy) - brickSdf(p - e.xyy),
|
|
34739
|
+
brickSdf(p + e.yxy) - brickSdf(p - e.yxy),
|
|
34740
|
+
brickSdf(p + e.yyx) - brickSdf(p - e.yyx)
|
|
34741
|
+
));
|
|
34742
|
+
}
|
|
34743
|
+
|
|
34744
|
+
void writeFragmentDepth(vec3 pLocal) {
|
|
34745
|
+
vec4 world = uLocalToWorld * vec4(pLocal, 1.0);
|
|
34746
|
+
vec4 clip = uViewProjectionMatrix * world;
|
|
34747
|
+
float ndcDepth = clip.z / clip.w;
|
|
34748
|
+
gl_FragDepth = ndcDepth * 0.5 + 0.5;
|
|
34749
|
+
}
|
|
34750
|
+
|
|
34751
|
+
void main() {
|
|
34752
|
+
float sceneDiag = max(length(uBrickMax - uBrickMin), 1.0);
|
|
34753
|
+
vec3 roWorld = uCameraMatrixWorld[3].xyz;
|
|
34754
|
+
vec3 ro = (uWorldToLocal * vec4(roWorld, 1.0)).xyz;
|
|
34755
|
+
vec3 rd = normalize(vLocalPosition - ro);
|
|
34756
|
+
if (uIsOrthographic > 0.5) {
|
|
34757
|
+
vec3 rdWorld = normalize((uCameraMatrixWorld * vec4(0.0, 0.0, -1.0, 0.0)).xyz);
|
|
34758
|
+
rd = normalize((uWorldToLocal * vec4(rdWorld, 0.0)).xyz);
|
|
34759
|
+
ro = vLocalPosition - rd * sceneDiag * 2.0;
|
|
34760
|
+
}
|
|
34761
|
+
|
|
34762
|
+
float tNear = 0.0;
|
|
34763
|
+
float tFar = 0.0;
|
|
34764
|
+
if (!intersectAabb(ro, rd, uBrickMin, uBrickMax, tNear, tFar)) discard;
|
|
34765
|
+
|
|
34766
|
+
float t = max(tNear, 0.0);
|
|
34767
|
+
float prevT = t;
|
|
34768
|
+
float prevD = clippedBrickSdf(ro + rd * t);
|
|
34769
|
+
bool hit = prevD <= 0.0 && abs(prevD) < uIsoEps;
|
|
34770
|
+
|
|
34771
|
+
for (int i = 0; i < MAX_STEPS; i++) {
|
|
34772
|
+
if (hit) break;
|
|
34773
|
+
t += clamp(abs(prevD) * 0.8, uRayStep, uRayStep * 4.0);
|
|
34774
|
+
if (t > tFar) break;
|
|
34775
|
+
float d = clippedBrickSdf(ro + rd * t);
|
|
34776
|
+
float absD = abs(d);
|
|
34777
|
+
if (d <= 0.0 && absD < uIsoEps) {
|
|
34778
|
+
hit = true;
|
|
34779
|
+
break;
|
|
34780
|
+
}
|
|
34781
|
+
if (crossedSurface(prevD, d)) {
|
|
34782
|
+
t = refineSurface(ro, rd, prevT, t);
|
|
34783
|
+
hit = true;
|
|
34784
|
+
break;
|
|
34785
|
+
}
|
|
34786
|
+
prevT = t;
|
|
34787
|
+
prevD = d;
|
|
34788
|
+
}
|
|
34789
|
+
|
|
34790
|
+
if (!hit) discard;
|
|
34791
|
+
|
|
34792
|
+
vec3 p = ro + rd * t;
|
|
34793
|
+
writeFragmentDepth(p);
|
|
34794
|
+
vec3 pWorld = (uLocalToWorld * vec4(p, 1.0)).xyz;
|
|
34795
|
+
vec3 normal = normalize(mat3(transpose(uWorldToLocal)) * estimateNormal(p));
|
|
34796
|
+
vec3 viewDir = normalize(roWorld - pWorld);
|
|
34797
|
+
vec3 keyDir = normalize(vec3(0.45, 0.62, 0.64));
|
|
34798
|
+
vec3 fillDir = normalize(vec3(-0.68, -0.18, 0.42));
|
|
34799
|
+
float diffuse = max(dot(normal, keyDir), 0.0) * 0.72 + max(dot(normal, fillDir), 0.0) * 0.22 + 0.16;
|
|
34800
|
+
float fresnel = pow(1.0 - max(dot(normal, viewDir), 0.0), 3.0);
|
|
34801
|
+
vec3 base = mix(uColor, uHoverColor, uHoverIntensity);
|
|
34802
|
+
vec3 shaded = base * diffuse + base * fresnel * 0.18;
|
|
34803
|
+
outColor = forgeLinearToOutputColor(shaded, uAlpha);
|
|
34804
|
+
}
|
|
34805
|
+
`;
|
|
34806
|
+
function colorToRgb$1(color2, fallback) {
|
|
34807
|
+
const resolved = new Color(color2 ?? fallback);
|
|
34808
|
+
return [resolved.r, resolved.g, resolved.b];
|
|
34809
|
+
}
|
|
34810
|
+
function createProxyGeometry$1(data) {
|
|
34811
|
+
const { min, max: max2 } = data.bounds;
|
|
34812
|
+
const size = [max2[0] - min[0], max2[1] - min[1], max2[2] - min[2]];
|
|
34813
|
+
if (!size.every((value) => Number.isFinite(value) && value > 0)) return null;
|
|
34814
|
+
const geometry = new BoxGeometry(size[0], size[1], size[2]);
|
|
34815
|
+
geometry.translate((min[0] + max2[0]) / 2, (min[1] + max2[1]) / 2, (min[2] + max2[2]) / 2);
|
|
34816
|
+
return geometry;
|
|
34817
|
+
}
|
|
34818
|
+
function createClipPlaneUniforms$1() {
|
|
34819
|
+
return Array.from({ length: MAX_CLIP_PLANES$1 }, () => new Vector4(0, 0, 0, 0));
|
|
34820
|
+
}
|
|
34821
|
+
function createDistanceTexture(brick) {
|
|
34822
|
+
const texture = new Data3DTexture(brick.distances, brick.dims[0], brick.dims[1], brick.dims[2]);
|
|
34823
|
+
texture.format = RedFormat;
|
|
34824
|
+
texture.type = FloatType;
|
|
34825
|
+
texture.minFilter = LinearFilter;
|
|
34826
|
+
texture.magFilter = LinearFilter;
|
|
34827
|
+
texture.wrapS = ClampToEdgeWrapping;
|
|
34828
|
+
texture.wrapT = ClampToEdgeWrapping;
|
|
34829
|
+
texture.wrapR = ClampToEdgeWrapping;
|
|
34830
|
+
texture.unpackAlignment = 1;
|
|
34831
|
+
texture.needsUpdate = true;
|
|
34832
|
+
return texture;
|
|
34833
|
+
}
|
|
34834
|
+
async function buildViewportDistanceBrick(sdf) {
|
|
34835
|
+
if (!shouldUseSdfDistanceBrick(sdf)) return null;
|
|
34836
|
+
if (isSdfDistanceBrickWebGpuEnabled()) {
|
|
34837
|
+
const webGpuBrick = await buildSdfDistanceBrickWebGpu(sdf);
|
|
34838
|
+
if (webGpuBrick) return webGpuBrick;
|
|
34839
|
+
}
|
|
34840
|
+
return buildSdfDistanceBrick(sdf);
|
|
34841
|
+
}
|
|
34842
|
+
function SdfDistanceBrickObject({
|
|
34843
|
+
sdf,
|
|
34844
|
+
settings,
|
|
34845
|
+
matrix,
|
|
34846
|
+
materialProps,
|
|
34847
|
+
isHovered,
|
|
34848
|
+
clippingPlanes = [],
|
|
34849
|
+
onPointerEnter,
|
|
34850
|
+
onPointerMove,
|
|
34851
|
+
onPointerLeave,
|
|
34852
|
+
onClick,
|
|
34853
|
+
onDoubleClick,
|
|
34854
|
+
onContextMenu
|
|
34855
|
+
}) {
|
|
34856
|
+
const meshRef = reactExports.useRef(null);
|
|
34857
|
+
const materialRef = reactExports.useRef(null);
|
|
34858
|
+
const { camera } = useThree();
|
|
34859
|
+
const viewProjection = reactExports.useMemo(() => new Matrix4(), []);
|
|
34860
|
+
const worldToLocal = reactExports.useMemo(() => new Matrix4(), []);
|
|
34861
|
+
const localClipPlane = reactExports.useMemo(() => new Plane(), []);
|
|
34862
|
+
const [brick, setBrick] = reactExports.useState(null);
|
|
34863
|
+
const [brickError, setBrickError] = reactExports.useState(null);
|
|
34864
|
+
reactExports.useEffect(() => {
|
|
34865
|
+
let cancelled = false;
|
|
34866
|
+
setBrick(null);
|
|
34867
|
+
setBrickError(null);
|
|
34868
|
+
buildViewportDistanceBrick(sdf).then((nextBrick) => {
|
|
34869
|
+
if (!cancelled) setBrick(nextBrick);
|
|
34870
|
+
}).catch((error) => {
|
|
34871
|
+
const nextError = error instanceof Error ? error : new Error(String(error));
|
|
34872
|
+
console.error("ForgeCAD SDF distance brick build failed.", nextError);
|
|
34873
|
+
if (!cancelled) setBrickError(nextError);
|
|
34874
|
+
});
|
|
34875
|
+
return () => {
|
|
34876
|
+
cancelled = true;
|
|
34877
|
+
};
|
|
34878
|
+
}, [sdf]);
|
|
34879
|
+
const geometry = reactExports.useMemo(() => createProxyGeometry$1(sdf), [sdf]);
|
|
34880
|
+
const texture = reactExports.useMemo(() => brick ? createDistanceTexture(brick) : null, [brick]);
|
|
34881
|
+
const opacity = Math.min(settings.opacity, (materialProps == null ? void 0 : materialProps.opacity) ?? 1);
|
|
34882
|
+
const color2 = reactExports.useMemo(() => colorToRgb$1(settings.color ?? sdf.colorHex, "#5b9bd5"), [settings.color, sdf.colorHex]);
|
|
34883
|
+
const uniforms = reactExports.useMemo(
|
|
34884
|
+
() => ({
|
|
34885
|
+
uDistanceTexture: { value: texture },
|
|
34886
|
+
uCameraMatrixWorld: { value: new Matrix4() },
|
|
34887
|
+
uLocalToWorld: { value: new Matrix4() },
|
|
34888
|
+
uWorldToLocal: { value: new Matrix4() },
|
|
34889
|
+
uViewProjectionMatrix: { value: new Matrix4() },
|
|
34890
|
+
uBrickMin: { value: new Vector3((brick == null ? void 0 : brick.min[0]) ?? 0, (brick == null ? void 0 : brick.min[1]) ?? 0, (brick == null ? void 0 : brick.min[2]) ?? 0) },
|
|
34891
|
+
uBrickMax: { value: new Vector3((brick == null ? void 0 : brick.max[0]) ?? 1, (brick == null ? void 0 : brick.max[1]) ?? 1, (brick == null ? void 0 : brick.max[2]) ?? 1) },
|
|
34892
|
+
uColor: { value: new Vector3(...color2) },
|
|
34893
|
+
uHoverColor: { value: new Color(settings.color) },
|
|
34894
|
+
uAlpha: { value: opacity },
|
|
34895
|
+
uHoverIntensity: { value: 0 },
|
|
34896
|
+
uRayStep: { value: Math.max(0.05, ((brick == null ? void 0 : brick.cellSize) ?? 1) * 0.65) },
|
|
34897
|
+
uIsoEps: { value: Math.max(0.05, ((brick == null ? void 0 : brick.cellSize) ?? 1) * 0.55) },
|
|
34898
|
+
uNormalStep: { value: Math.max(0.05, (brick == null ? void 0 : brick.cellSize) ?? 1) },
|
|
34899
|
+
uIsOrthographic: { value: 0 },
|
|
34900
|
+
uClipPlaneCount: { value: 0 },
|
|
34901
|
+
uClipPlanes: { value: createClipPlaneUniforms$1() }
|
|
34902
|
+
}),
|
|
34903
|
+
[brick, color2, opacity, settings.color, texture]
|
|
34904
|
+
);
|
|
34905
|
+
useFrame(() => {
|
|
34906
|
+
const material = materialRef.current;
|
|
34907
|
+
const mesh = meshRef.current;
|
|
34908
|
+
if (!material || !mesh) return;
|
|
34909
|
+
mesh.updateMatrixWorld();
|
|
34910
|
+
camera.updateMatrixWorld();
|
|
34911
|
+
viewProjection.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
34912
|
+
worldToLocal.copy(mesh.matrixWorld).invert();
|
|
34913
|
+
material.uniforms.uCameraMatrixWorld.value.copy(camera.matrixWorld);
|
|
34914
|
+
material.uniforms.uLocalToWorld.value.copy(mesh.matrixWorld);
|
|
34915
|
+
material.uniforms.uWorldToLocal.value.copy(worldToLocal);
|
|
34916
|
+
material.uniforms.uViewProjectionMatrix.value.copy(viewProjection);
|
|
34917
|
+
material.uniforms.uIsOrthographic.value = camera.isOrthographicCamera ? 1 : 0;
|
|
34918
|
+
material.uniforms.uHoverColor.value.set(settings.color);
|
|
34919
|
+
material.uniforms.uHoverIntensity.value = isHovered ? 0.3 : 0;
|
|
34920
|
+
material.uniforms.uClipPlaneCount.value = Math.min(clippingPlanes.length, MAX_CLIP_PLANES$1);
|
|
34921
|
+
const clipUniforms = material.uniforms.uClipPlanes.value;
|
|
34922
|
+
for (let i = 0; i < MAX_CLIP_PLANES$1; i += 1) {
|
|
34923
|
+
const source = clippingPlanes[i];
|
|
34924
|
+
if (!source) {
|
|
34925
|
+
clipUniforms[i].set(0, 0, 0, 0);
|
|
34926
|
+
continue;
|
|
34927
|
+
}
|
|
34928
|
+
localClipPlane.copy(source).applyMatrix4(worldToLocal);
|
|
34929
|
+
clipUniforms[i].set(localClipPlane.normal.x, localClipPlane.normal.y, localClipPlane.normal.z, localClipPlane.constant);
|
|
34930
|
+
}
|
|
34931
|
+
});
|
|
34932
|
+
reactExports.useEffect(() => {
|
|
34933
|
+
return () => {
|
|
34934
|
+
geometry == null ? void 0 : geometry.dispose();
|
|
34935
|
+
};
|
|
34936
|
+
}, [geometry]);
|
|
34937
|
+
reactExports.useEffect(() => {
|
|
34938
|
+
return () => {
|
|
34939
|
+
texture == null ? void 0 : texture.dispose();
|
|
34940
|
+
};
|
|
34941
|
+
}, [texture]);
|
|
34942
|
+
if (brickError || !brick || !geometry || !texture) return null;
|
|
34943
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34944
|
+
"group",
|
|
34945
|
+
{
|
|
34946
|
+
matrixAutoUpdate: false,
|
|
34947
|
+
matrix,
|
|
34948
|
+
onPointerEnter,
|
|
34949
|
+
onPointerMove,
|
|
34950
|
+
onPointerLeave,
|
|
34951
|
+
onClick,
|
|
34952
|
+
onDoubleClick,
|
|
34953
|
+
onContextMenu,
|
|
34954
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34955
|
+
"mesh",
|
|
34956
|
+
{
|
|
34957
|
+
ref: meshRef,
|
|
34958
|
+
geometry,
|
|
34959
|
+
userData: {
|
|
34960
|
+
forgeMesh: true,
|
|
34961
|
+
forgeSdfDirect: true,
|
|
34962
|
+
forgeSdfRenderer: "distance-brick",
|
|
34963
|
+
forgeSdfDistanceBrick: true,
|
|
34964
|
+
forgeSdfDistanceBrickBuildSource: brick.buildSource,
|
|
34965
|
+
forgeSdfDistanceBrickBuildMs: brick.buildMs,
|
|
34966
|
+
forgeSdfDistanceBrickVoxels: brick.voxelCount
|
|
34967
|
+
},
|
|
34968
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34969
|
+
"shaderMaterial",
|
|
34970
|
+
{
|
|
34971
|
+
ref: materialRef,
|
|
34972
|
+
glslVersion: GLSL3,
|
|
34973
|
+
vertexShader: SDF_BRICK_VERTEX_SHADER,
|
|
34974
|
+
fragmentShader: SDF_BRICK_FRAGMENT_SHADER,
|
|
34975
|
+
uniforms,
|
|
34976
|
+
side: DoubleSide,
|
|
34977
|
+
transparent: opacity < 1 || ((materialProps == null ? void 0 : materialProps.transmission) ?? 0) > 0,
|
|
34978
|
+
depthWrite: opacity >= 1 && ((materialProps == null ? void 0 : materialProps.transmission) ?? 0) <= 0,
|
|
34979
|
+
toneMapped: false
|
|
34980
|
+
}
|
|
34981
|
+
)
|
|
34982
|
+
}
|
|
34983
|
+
)
|
|
34984
|
+
}
|
|
34985
|
+
);
|
|
34986
|
+
}
|
|
34987
|
+
const MAX_CLIP_PLANES = 8;
|
|
34988
|
+
function colorToRgb(color2, fallback) {
|
|
34989
|
+
const resolved = new Color(color2 ?? fallback);
|
|
34990
|
+
return [resolved.r, resolved.g, resolved.b];
|
|
34991
|
+
}
|
|
34992
|
+
function sdfLeaf(data, settings, materialProps) {
|
|
34993
|
+
const opacity = Math.min(settings.opacity, (materialProps == null ? void 0 : materialProps.opacity) ?? 1);
|
|
34994
|
+
return {
|
|
34995
|
+
node: data.node,
|
|
34996
|
+
...data.program ? { program: data.program } : {},
|
|
34997
|
+
color: colorToRgb(settings.color ?? data.colorHex, "#5b9bd5"),
|
|
34998
|
+
alpha: opacity,
|
|
34999
|
+
emissive: colorToRgb(materialProps == null ? void 0 : materialProps.emissive, "#000000"),
|
|
35000
|
+
emissiveIntensity: (materialProps == null ? void 0 : materialProps.emissiveIntensity) ?? 0,
|
|
35001
|
+
metalness: (materialProps == null ? void 0 : materialProps.metalness) ?? 0.05,
|
|
35002
|
+
roughness: (materialProps == null ? void 0 : materialProps.roughness) ?? 0.35,
|
|
35003
|
+
clearcoat: (materialProps == null ? void 0 : materialProps.clearcoat) ?? 0.1,
|
|
35004
|
+
clearcoatRoughness: (materialProps == null ? void 0 : materialProps.clearcoatRoughness) ?? 0.4,
|
|
35005
|
+
transmission: (materialProps == null ? void 0 : materialProps.transmission) ?? 0,
|
|
35006
|
+
reflectivity: (materialProps == null ? void 0 : materialProps.reflectivity) ?? 0.5
|
|
35007
|
+
};
|
|
35008
|
+
}
|
|
35009
|
+
function canRenderSdfDirectly(data) {
|
|
35010
|
+
return Boolean(data && data.preview.mode === "raymarch");
|
|
35011
|
+
}
|
|
35012
|
+
function createProxyGeometry(data) {
|
|
35013
|
+
const { min, max: max2 } = data.bounds;
|
|
35014
|
+
const size = [max2[0] - min[0], max2[1] - min[1], max2[2] - min[2]];
|
|
35015
|
+
if (!size.every((value) => Number.isFinite(value) && value > 0)) return null;
|
|
35016
|
+
const geometry = new BoxGeometry(size[0], size[1], size[2]);
|
|
35017
|
+
geometry.translate((min[0] + max2[0]) / 2, (min[1] + max2[1]) / 2, (min[2] + max2[2]) / 2);
|
|
35018
|
+
return geometry;
|
|
35019
|
+
}
|
|
35020
|
+
function createClipPlaneUniforms() {
|
|
35021
|
+
return Array.from({ length: MAX_CLIP_PLANES }, () => new Vector4(0, 0, 0, 0));
|
|
35022
|
+
}
|
|
35023
|
+
function SdfDirectObject(props) {
|
|
35024
|
+
const gl = useThree((state2) => state2.gl);
|
|
35025
|
+
if (shouldUseSdfDistanceBrick(props.sdf) && gl.capabilities.isWebGL2) {
|
|
35026
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(SdfDistanceBrickObject, { ...props });
|
|
35027
|
+
}
|
|
35028
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(SdfRaymarchObject, { ...props });
|
|
35029
|
+
}
|
|
35030
|
+
function SdfRaymarchObject({
|
|
35031
|
+
sdf,
|
|
35032
|
+
settings,
|
|
35033
|
+
matrix,
|
|
35034
|
+
materialProps,
|
|
35035
|
+
isHovered,
|
|
35036
|
+
clippingPlanes = [],
|
|
35037
|
+
onPointerEnter,
|
|
35038
|
+
onPointerMove,
|
|
35039
|
+
onPointerLeave,
|
|
35040
|
+
onClick,
|
|
35041
|
+
onDoubleClick,
|
|
35042
|
+
onContextMenu
|
|
35043
|
+
}) {
|
|
35044
|
+
const meshRef = reactExports.useRef(null);
|
|
35045
|
+
const materialRef = reactExports.useRef(null);
|
|
35046
|
+
const { camera } = useThree();
|
|
35047
|
+
const viewProjection = reactExports.useMemo(() => new Matrix4(), []);
|
|
35048
|
+
const worldToLocal = reactExports.useMemo(() => new Matrix4(), []);
|
|
35049
|
+
const localClipPlane = reactExports.useMemo(() => new Plane(), []);
|
|
35050
|
+
const geometry = reactExports.useMemo(() => createProxyGeometry(sdf), [sdf]);
|
|
35051
|
+
const fragmentShader2 = reactExports.useMemo(
|
|
35052
|
+
() => buildSdfRaymarchFragmentShader([sdfLeaf(sdf, settings, materialProps)]),
|
|
35053
|
+
[materialProps, settings, sdf]
|
|
35054
|
+
);
|
|
35055
|
+
const uniforms = reactExports.useMemo(
|
|
35056
|
+
() => ({
|
|
35057
|
+
uCameraMatrixWorld: { value: new Matrix4() },
|
|
35058
|
+
uLocalToWorld: { value: new Matrix4() },
|
|
35059
|
+
uWorldToLocal: { value: new Matrix4() },
|
|
35060
|
+
uViewProjectionMatrix: { value: new Matrix4() },
|
|
35061
|
+
uSceneMin: { value: new Vector3(sdf.bounds.min[0], sdf.bounds.min[1], sdf.bounds.min[2]) },
|
|
35062
|
+
uSceneMax: { value: new Vector3(sdf.bounds.max[0], sdf.bounds.max[1], sdf.bounds.max[2]) },
|
|
35063
|
+
uClipPlaneCount: { value: 0 },
|
|
35064
|
+
uClipPlanes: { value: createClipPlaneUniforms() },
|
|
35065
|
+
uHoverLeafIndex: { value: -1 },
|
|
35066
|
+
uHoverColor: { value: new Color(settings.color) },
|
|
35067
|
+
uHoverIntensity: { value: 0 },
|
|
35068
|
+
uIsOrthographic: { value: 0 }
|
|
35069
|
+
}),
|
|
35070
|
+
[sdf.bounds.max, sdf.bounds.min, settings.color]
|
|
35071
|
+
);
|
|
35072
|
+
useFrame(() => {
|
|
35073
|
+
const material = materialRef.current;
|
|
35074
|
+
const mesh = meshRef.current;
|
|
35075
|
+
if (!material || !mesh) return;
|
|
35076
|
+
mesh.updateMatrixWorld();
|
|
35077
|
+
camera.updateMatrixWorld();
|
|
35078
|
+
viewProjection.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
35079
|
+
worldToLocal.copy(mesh.matrixWorld).invert();
|
|
35080
|
+
material.uniforms.uCameraMatrixWorld.value.copy(camera.matrixWorld);
|
|
35081
|
+
material.uniforms.uLocalToWorld.value.copy(mesh.matrixWorld);
|
|
35082
|
+
material.uniforms.uWorldToLocal.value.copy(worldToLocal);
|
|
35083
|
+
material.uniforms.uViewProjectionMatrix.value.copy(viewProjection);
|
|
35084
|
+
material.uniforms.uIsOrthographic.value = camera.isOrthographicCamera ? 1 : 0;
|
|
35085
|
+
material.uniforms.uHoverLeafIndex.value = isHovered ? 0 : -1;
|
|
35086
|
+
material.uniforms.uHoverColor.value.set(settings.color);
|
|
35087
|
+
material.uniforms.uHoverIntensity.value = isHovered ? 0.3 : 0;
|
|
35088
|
+
material.uniforms.uClipPlaneCount.value = Math.min(clippingPlanes.length, MAX_CLIP_PLANES);
|
|
35089
|
+
const clipUniforms = material.uniforms.uClipPlanes.value;
|
|
35090
|
+
for (let i = 0; i < MAX_CLIP_PLANES; i += 1) {
|
|
35091
|
+
const source = clippingPlanes[i];
|
|
35092
|
+
if (!source) {
|
|
35093
|
+
clipUniforms[i].set(0, 0, 0, 0);
|
|
35094
|
+
continue;
|
|
35095
|
+
}
|
|
35096
|
+
localClipPlane.copy(source).applyMatrix4(worldToLocal);
|
|
35097
|
+
clipUniforms[i].set(localClipPlane.normal.x, localClipPlane.normal.y, localClipPlane.normal.z, localClipPlane.constant);
|
|
35098
|
+
}
|
|
35099
|
+
});
|
|
35100
|
+
reactExports.useEffect(() => {
|
|
35101
|
+
return () => geometry == null ? void 0 : geometry.dispose();
|
|
35102
|
+
}, [geometry]);
|
|
35103
|
+
if (!geometry) return null;
|
|
35104
|
+
const opacity = Math.min(settings.opacity, (materialProps == null ? void 0 : materialProps.opacity) ?? 1);
|
|
35105
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35106
|
+
"group",
|
|
35107
|
+
{
|
|
35108
|
+
matrixAutoUpdate: false,
|
|
35109
|
+
matrix,
|
|
35110
|
+
onPointerEnter,
|
|
35111
|
+
onPointerMove,
|
|
35112
|
+
onPointerLeave,
|
|
35113
|
+
onClick,
|
|
35114
|
+
onDoubleClick,
|
|
35115
|
+
onContextMenu,
|
|
35116
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { ref: meshRef, geometry, userData: { forgeMesh: true, forgeSdfDirect: true, forgeSdfRenderer: "raymarch" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35117
|
+
"shaderMaterial",
|
|
35118
|
+
{
|
|
35119
|
+
ref: materialRef,
|
|
35120
|
+
vertexShader: SDF_RAYMARCH_PROXY_VERTEX_SHADER,
|
|
35121
|
+
fragmentShader: fragmentShader2,
|
|
35122
|
+
uniforms,
|
|
35123
|
+
side: DoubleSide,
|
|
35124
|
+
transparent: opacity < 1 || ((materialProps == null ? void 0 : materialProps.transmission) ?? 0) > 0,
|
|
35125
|
+
depthWrite: opacity >= 1 && ((materialProps == null ? void 0 : materialProps.transmission) ?? 0) <= 0,
|
|
35126
|
+
toneMapped: false,
|
|
35127
|
+
extensions: { fragDepth: true }
|
|
35128
|
+
}
|
|
35129
|
+
) })
|
|
35130
|
+
}
|
|
35131
|
+
);
|
|
35132
|
+
}
|
|
34183
35133
|
class ScanProxyWorkerClient {
|
|
34184
35134
|
constructor(workerFactory = () => new Worker(new URL(
|
|
34185
35135
|
/* @vite-ignore */
|
|
@@ -34815,7 +35765,17 @@ function ForgeObject({
|
|
|
34815
35765
|
onContextMenu
|
|
34816
35766
|
}) {
|
|
34817
35767
|
var _a3, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
|
|
35768
|
+
const wantsDirectSdf = Boolean(
|
|
35769
|
+
canRenderSdfDirectly(obj.sdf) && inspectChannel === "none" && renderStyle !== "scan" && renderStyle !== "matrix" && renderMode !== "wireframe" && !debugHighlightColor
|
|
35770
|
+
);
|
|
34818
35771
|
const { solidGeo, edgesGeo, hasSmoothNormals } = reactExports.useMemo(() => {
|
|
35772
|
+
if (wantsDirectSdf) {
|
|
35773
|
+
return {
|
|
35774
|
+
solidGeo: null,
|
|
35775
|
+
edgesGeo: null,
|
|
35776
|
+
hasSmoothNormals: false
|
|
35777
|
+
};
|
|
35778
|
+
}
|
|
34819
35779
|
if (!obj.shape) {
|
|
34820
35780
|
return {
|
|
34821
35781
|
solidGeo: null,
|
|
@@ -34837,7 +35797,7 @@ function ForgeObject({
|
|
|
34837
35797
|
hasSmoothNormals: false
|
|
34838
35798
|
};
|
|
34839
35799
|
}
|
|
34840
|
-
}, [obj.shape]);
|
|
35800
|
+
}, [obj.shape, wantsDirectSdf]);
|
|
34841
35801
|
const isScalarInspect = inspectChannel === "thickness" || inspectChannel === "roughness";
|
|
34842
35802
|
const isScalarScan = isScalarInspect && inspectDisplayMode === "scan";
|
|
34843
35803
|
const inspectPointGeo = reactExports.useMemo(() => {
|
|
@@ -34896,7 +35856,7 @@ function ForgeObject({
|
|
|
34896
35856
|
Object.values(scanAnalysisGeometries ?? {}).forEach((geometry) => geometry == null ? void 0 : geometry.dispose());
|
|
34897
35857
|
};
|
|
34898
35858
|
}, [scanAnalysisGeometries]);
|
|
34899
|
-
if (!
|
|
35859
|
+
if (!settings.visible) return null;
|
|
34900
35860
|
const effectiveRenderMode = isInteracting && renderMode === "overlay" ? "solid" : renderMode;
|
|
34901
35861
|
const renderStylePreset = getRenderStylePreset(renderStyle);
|
|
34902
35862
|
const materialDefaults = renderStylePreset.material;
|
|
@@ -34916,7 +35876,6 @@ function ForgeObject({
|
|
|
34916
35876
|
const showWire = !isInspecting && effectiveRenderMode === "wireframe";
|
|
34917
35877
|
const effectiveClippingPlanes = clippingPlanes ?? EMPTY_CLIPPING_PLANES$1;
|
|
34918
35878
|
const effectiveSectionPlanes = sectionPlanes ?? EMPTY_SECTION_PLANES$1;
|
|
34919
|
-
const inspectSolidGeo = inspectMeshColorGeo ?? solidGeo;
|
|
34920
35879
|
const hasInspectMeshColors = inspectMeshColorGeo !== null;
|
|
34921
35880
|
const showFloatingContext = showSolid && inspectChannel === "floating";
|
|
34922
35881
|
const showFloatingObject = inspectChannel !== "floating" || hasInspectMeshColors || inspectColor !== "#000000";
|
|
@@ -34938,6 +35897,27 @@ function ForgeObject({
|
|
|
34938
35897
|
const showSectionCapPreview = Boolean(
|
|
34939
35898
|
isInteracting && showSolid && effectiveRenderMode !== "wireframe" && effectiveSectionPlanes.length > 0
|
|
34940
35899
|
);
|
|
35900
|
+
if (wantsDirectSdf && obj.sdf) {
|
|
35901
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35902
|
+
SdfDirectObject,
|
|
35903
|
+
{
|
|
35904
|
+
sdf: obj.sdf,
|
|
35905
|
+
settings,
|
|
35906
|
+
matrix,
|
|
35907
|
+
materialProps: obj.materialProps,
|
|
35908
|
+
isHovered,
|
|
35909
|
+
clippingPlanes: effectiveClippingPlanes,
|
|
35910
|
+
onPointerEnter,
|
|
35911
|
+
onPointerMove,
|
|
35912
|
+
onPointerLeave,
|
|
35913
|
+
onClick,
|
|
35914
|
+
onDoubleClick,
|
|
35915
|
+
onContextMenu
|
|
35916
|
+
}
|
|
35917
|
+
);
|
|
35918
|
+
}
|
|
35919
|
+
if (!solidGeo) return null;
|
|
35920
|
+
const inspectSolidGeo = inspectMeshColorGeo ?? solidGeo;
|
|
34941
35921
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
34942
35922
|
"group",
|
|
34943
35923
|
{
|
|
@@ -37083,6 +38063,7 @@ function MeasureInfoPanel() {
|
|
|
37083
38063
|
}
|
|
37084
38064
|
return null;
|
|
37085
38065
|
}
|
|
38066
|
+
const MAX_VIEWPORT_PERFORMANCE_SAMPLES = 240;
|
|
37086
38067
|
const getProgramCount = (gl) => {
|
|
37087
38068
|
const info = gl.info;
|
|
37088
38069
|
return Array.isArray(info.programs) ? info.programs.length : 0;
|
|
@@ -37123,6 +38104,26 @@ function collectScanProxyPerformanceInfo(scene) {
|
|
|
37123
38104
|
performanceReplacementCount
|
|
37124
38105
|
} : null;
|
|
37125
38106
|
}
|
|
38107
|
+
function collectSdfDistanceBrickPerformanceInfo(scene) {
|
|
38108
|
+
let count = 0;
|
|
38109
|
+
let voxels = 0;
|
|
38110
|
+
let buildMs = 0;
|
|
38111
|
+
let cpuCount = 0;
|
|
38112
|
+
let webGpuCount = 0;
|
|
38113
|
+
scene.traverse((object) => {
|
|
38114
|
+
const userData = object.userData;
|
|
38115
|
+
if (!userData.forgeSdfDistanceBrick) return;
|
|
38116
|
+
count += 1;
|
|
38117
|
+
voxels += userData.forgeSdfDistanceBrickVoxels ?? 0;
|
|
38118
|
+
buildMs += userData.forgeSdfDistanceBrickBuildMs ?? 0;
|
|
38119
|
+
if (userData.forgeSdfDistanceBrickBuildSource === "webgpu") {
|
|
38120
|
+
webGpuCount += 1;
|
|
38121
|
+
} else {
|
|
38122
|
+
cpuCount += 1;
|
|
38123
|
+
}
|
|
38124
|
+
});
|
|
38125
|
+
return count > 0 ? { count, voxels, buildMs, cpuCount, webGpuCount } : null;
|
|
38126
|
+
}
|
|
37126
38127
|
const TREND_WINDOW = 20;
|
|
37127
38128
|
class TrendTracker {
|
|
37128
38129
|
constructor(size) {
|
|
@@ -37174,6 +38175,88 @@ const formatWorkerStatus = (message) => {
|
|
|
37174
38175
|
if (/timed out/i.test(message)) return "restarted after timeout";
|
|
37175
38176
|
return "restarted after worker error";
|
|
37176
38177
|
};
|
|
38178
|
+
function summarizeViewportPerformanceSamples(samples) {
|
|
38179
|
+
var _a3, _b2, _c, _d;
|
|
38180
|
+
if (samples.length === 0) {
|
|
38181
|
+
return {
|
|
38182
|
+
samples: 0,
|
|
38183
|
+
latestFps: null,
|
|
38184
|
+
averageFps: null,
|
|
38185
|
+
minFps: null,
|
|
38186
|
+
p10Fps: null,
|
|
38187
|
+
averageFrameTimeMs: null,
|
|
38188
|
+
latestDrawCalls: null,
|
|
38189
|
+
latestRenderTriangles: null,
|
|
38190
|
+
latestModelTriangles: null,
|
|
38191
|
+
latestProgramCount: null,
|
|
38192
|
+
latestSdfDistanceBrickCount: null,
|
|
38193
|
+
latestSdfDistanceBrickVoxels: null,
|
|
38194
|
+
latestSdfDistanceBrickCpuCount: null,
|
|
38195
|
+
latestSdfDistanceBrickWebGpuCount: null
|
|
38196
|
+
};
|
|
38197
|
+
}
|
|
38198
|
+
const latest = samples[samples.length - 1];
|
|
38199
|
+
const fpsValues = samples.map((sample) => sample.fps).sort((a2, b2) => a2 - b2);
|
|
38200
|
+
const fpsTotal = samples.reduce((sum, sample) => sum + sample.fps, 0);
|
|
38201
|
+
const frameTimeTotal = samples.reduce((sum, sample) => sum + sample.frameTimeMs, 0);
|
|
38202
|
+
const p10Index = Math.floor((fpsValues.length - 1) * 0.1);
|
|
38203
|
+
return {
|
|
38204
|
+
samples: samples.length,
|
|
38205
|
+
latestFps: latest.fps,
|
|
38206
|
+
averageFps: fpsTotal / samples.length,
|
|
38207
|
+
minFps: fpsValues[0],
|
|
38208
|
+
p10Fps: fpsValues[p10Index],
|
|
38209
|
+
averageFrameTimeMs: frameTimeTotal / samples.length,
|
|
38210
|
+
latestDrawCalls: latest.drawCalls,
|
|
38211
|
+
latestRenderTriangles: latest.renderTriangles,
|
|
38212
|
+
latestModelTriangles: latest.modelTriangles,
|
|
38213
|
+
latestProgramCount: latest.programCount,
|
|
38214
|
+
latestSdfDistanceBrickCount: ((_a3 = latest.sdfDistanceBricks) == null ? void 0 : _a3.count) ?? 0,
|
|
38215
|
+
latestSdfDistanceBrickVoxels: ((_b2 = latest.sdfDistanceBricks) == null ? void 0 : _b2.voxels) ?? 0,
|
|
38216
|
+
latestSdfDistanceBrickCpuCount: ((_c = latest.sdfDistanceBricks) == null ? void 0 : _c.cpuCount) ?? 0,
|
|
38217
|
+
latestSdfDistanceBrickWebGpuCount: ((_d = latest.sdfDistanceBricks) == null ? void 0 : _d.webGpuCount) ?? 0
|
|
38218
|
+
};
|
|
38219
|
+
}
|
|
38220
|
+
function getViewportPerformanceProbe() {
|
|
38221
|
+
if (typeof window === "undefined") return null;
|
|
38222
|
+
if (!window.__forgeViewportPerformance) {
|
|
38223
|
+
window.__forgeViewportPerformance = {
|
|
38224
|
+
latest: null,
|
|
38225
|
+
samples: [],
|
|
38226
|
+
reset() {
|
|
38227
|
+
this.latest = null;
|
|
38228
|
+
this.samples = [];
|
|
38229
|
+
},
|
|
38230
|
+
summary(sampleLimit) {
|
|
38231
|
+
const selected = typeof sampleLimit === "number" && Number.isFinite(sampleLimit) && sampleLimit > 0 ? this.samples.slice(-Math.round(sampleLimit)) : this.samples;
|
|
38232
|
+
return summarizeViewportPerformanceSamples(selected);
|
|
38233
|
+
}
|
|
38234
|
+
};
|
|
38235
|
+
}
|
|
38236
|
+
return window.__forgeViewportPerformance;
|
|
38237
|
+
}
|
|
38238
|
+
function publishViewportPerformanceSample(stats) {
|
|
38239
|
+
const probe = getViewportPerformanceProbe();
|
|
38240
|
+
if (!probe) return;
|
|
38241
|
+
if (!stats) {
|
|
38242
|
+
probe.latest = null;
|
|
38243
|
+
return;
|
|
38244
|
+
}
|
|
38245
|
+
const sample = {
|
|
38246
|
+
...stats,
|
|
38247
|
+
sampledAtMs: performance.now()
|
|
38248
|
+
};
|
|
38249
|
+
probe.latest = sample;
|
|
38250
|
+
probe.samples.push(sample);
|
|
38251
|
+
if (probe.samples.length > MAX_VIEWPORT_PERFORMANCE_SAMPLES) {
|
|
38252
|
+
probe.samples.splice(0, probe.samples.length - MAX_VIEWPORT_PERFORMANCE_SAMPLES);
|
|
38253
|
+
}
|
|
38254
|
+
}
|
|
38255
|
+
function isViewportPerformanceProbeEnabled() {
|
|
38256
|
+
if (typeof window === "undefined") return false;
|
|
38257
|
+
const params = new URLSearchParams(window.location.search);
|
|
38258
|
+
return params.has("viewport-fps") || params.has("viewportPerf") || window.localStorage.getItem("fc-viewport-performance-probe") === "1";
|
|
38259
|
+
}
|
|
37177
38260
|
function PerformanceInfoSampler({
|
|
37178
38261
|
enabled,
|
|
37179
38262
|
modelTriangles,
|
|
@@ -37198,7 +38281,10 @@ function PerformanceInfoSampler({
|
|
|
37198
38281
|
sinceEmitSec: 0,
|
|
37199
38282
|
reactRenderCountAtLastEmit: reactRenderCountRef.current
|
|
37200
38283
|
};
|
|
37201
|
-
if (!enabled)
|
|
38284
|
+
if (!enabled) {
|
|
38285
|
+
publishViewportPerformanceSample(null);
|
|
38286
|
+
onStatsChange(null);
|
|
38287
|
+
}
|
|
37202
38288
|
}, [enabled, modelTriangles, onStatsChange, reactRenderCountRef, sceneObjects]);
|
|
37203
38289
|
useFrame((_state, delta) => {
|
|
37204
38290
|
var _a3, _b2, _c, _d;
|
|
@@ -37213,7 +38299,7 @@ function PerformanceInfoSampler({
|
|
|
37213
38299
|
const reactRendersDelta = reactRenderCountRef.current - sample.reactRenderCountAtLastEmit;
|
|
37214
38300
|
const mem = performance.memory;
|
|
37215
38301
|
const cacheStats = getRunResultCacheStats();
|
|
37216
|
-
|
|
38302
|
+
const stats = {
|
|
37217
38303
|
fps: frameCount / Math.max(sample.elapsedSec, 1e-6),
|
|
37218
38304
|
frameTimeMs: sample.frameTimeMsTotal / frameCount,
|
|
37219
38305
|
sceneObjects,
|
|
@@ -37236,8 +38322,11 @@ function PerformanceInfoSampler({
|
|
|
37236
38322
|
wasmHeapTruckMB: toMB(((_d = evalWorkerClient.workerWasmHeap) == null ? void 0 : _d.truckBytes) ?? null),
|
|
37237
38323
|
workerFailure: evalWorkerClient.lastWorkerFailure,
|
|
37238
38324
|
reactRendersPerSec: reactRendersDelta / Math.max(sample.sinceEmitSec, 1e-6),
|
|
37239
|
-
scanProxy: collectScanProxyPerformanceInfo(scene)
|
|
37240
|
-
|
|
38325
|
+
scanProxy: collectScanProxyPerformanceInfo(scene),
|
|
38326
|
+
sdfDistanceBricks: collectSdfDistanceBrickPerformanceInfo(scene)
|
|
38327
|
+
};
|
|
38328
|
+
publishViewportPerformanceSample(stats);
|
|
38329
|
+
onStatsChange(stats);
|
|
37241
38330
|
sample.frames = 0;
|
|
37242
38331
|
sample.elapsedSec = 0;
|
|
37243
38332
|
sample.frameTimeMsTotal = 0;
|
|
@@ -37764,6 +38853,13 @@ function computeSceneObjectBounds(obj, objectMatrices) {
|
|
|
37764
38853
|
}
|
|
37765
38854
|
return isFiniteBox3(out) ? out : null;
|
|
37766
38855
|
}
|
|
38856
|
+
if (obj.sdf) {
|
|
38857
|
+
const out = new Box3();
|
|
38858
|
+
if (!expandBoundsByTransformedAabb(out, obj.sdf.bounds.min, obj.sdf.bounds.max, matrix)) {
|
|
38859
|
+
return null;
|
|
38860
|
+
}
|
|
38861
|
+
return isFiniteBox3(out) ? out : null;
|
|
38862
|
+
}
|
|
37767
38863
|
if (obj.curve3d) {
|
|
37768
38864
|
const b2 = obj.curve3d.bounds;
|
|
37769
38865
|
const out = new Box3();
|
|
@@ -37776,12 +38872,6 @@ function computeSceneObjectBounds(obj, objectMatrices) {
|
|
|
37776
38872
|
if (!expandBoundsByTransformedAabb(out, b2.min, b2.max, matrix)) return null;
|
|
37777
38873
|
return isFiniteBox3(out) ? out : null;
|
|
37778
38874
|
}
|
|
37779
|
-
if (obj.sdf) {
|
|
37780
|
-
const b2 = obj.sdf.bounds;
|
|
37781
|
-
const out = new Box3();
|
|
37782
|
-
if (!expandBoundsByTransformedAabb(out, b2.min, b2.max, matrix)) return null;
|
|
37783
|
-
return isFiniteBox3(out) ? out : null;
|
|
37784
|
-
}
|
|
37785
38875
|
return null;
|
|
37786
38876
|
}
|
|
37787
38877
|
function computeSceneBounds(objects, objectMatrices) {
|
|
@@ -38221,290 +39311,6 @@ function SectionCaps({
|
|
|
38221
39311
|
);
|
|
38222
39312
|
}) });
|
|
38223
39313
|
}
|
|
38224
|
-
const DEFAULT_COLOR = "#5b9bd5";
|
|
38225
|
-
const BLACK = [0, 0, 0];
|
|
38226
|
-
const MAX_SDF_CLIP_PLANES = 8;
|
|
38227
|
-
const PICK_MAX_STEPS = 220;
|
|
38228
|
-
const PICK_MIN_STEP = 0.012;
|
|
38229
|
-
const PICK_HIT_EPS = 0.05;
|
|
38230
|
-
function colorToRgb(value, fallback = DEFAULT_COLOR) {
|
|
38231
|
-
const color2 = new Color(value || fallback);
|
|
38232
|
-
return [color2.r, color2.g, color2.b];
|
|
38233
|
-
}
|
|
38234
|
-
function clamp01(value) {
|
|
38235
|
-
return Math.min(1, Math.max(0, value));
|
|
38236
|
-
}
|
|
38237
|
-
function computeBounds(leaves) {
|
|
38238
|
-
if (leaves.length === 0) return null;
|
|
38239
|
-
const min = [Infinity, Infinity, Infinity];
|
|
38240
|
-
const max2 = [-Infinity, -Infinity, -Infinity];
|
|
38241
|
-
for (const leaf of leaves) {
|
|
38242
|
-
for (let i = 0; i < 3; i++) {
|
|
38243
|
-
min[i] = Math.min(min[i], leaf.bounds.min[i]);
|
|
38244
|
-
max2[i] = Math.max(max2[i], leaf.bounds.max[i]);
|
|
38245
|
-
}
|
|
38246
|
-
}
|
|
38247
|
-
if (!min.every(Number.isFinite) || !max2.every(Number.isFinite)) return null;
|
|
38248
|
-
const diagonal = Math.hypot(max2[0] - min[0], max2[1] - min[1], max2[2] - min[2]);
|
|
38249
|
-
const pad = Math.max(1, diagonal * 0.04);
|
|
38250
|
-
return {
|
|
38251
|
-
min: [min[0] - pad, min[1] - pad, min[2] - pad],
|
|
38252
|
-
max: [max2[0] + pad, max2[1] + pad, max2[2] + pad]
|
|
38253
|
-
};
|
|
38254
|
-
}
|
|
38255
|
-
function setClipPlaneUniforms(target, clippingPlanes) {
|
|
38256
|
-
const count = Math.min(clippingPlanes.length, MAX_SDF_CLIP_PLANES);
|
|
38257
|
-
for (let i = 0; i < MAX_SDF_CLIP_PLANES; i++) {
|
|
38258
|
-
const plane = clippingPlanes[i];
|
|
38259
|
-
target[i].set((plane == null ? void 0 : plane.normal.x) ?? 0, (plane == null ? void 0 : plane.normal.y) ?? 0, (plane == null ? void 0 : plane.normal.z) ?? 0, (plane == null ? void 0 : plane.constant) ?? 0);
|
|
38260
|
-
}
|
|
38261
|
-
return count;
|
|
38262
|
-
}
|
|
38263
|
-
function clipPlaneDistance(point, clippingPlanes) {
|
|
38264
|
-
let distance = -Infinity;
|
|
38265
|
-
const count = Math.min(clippingPlanes.length, MAX_SDF_CLIP_PLANES);
|
|
38266
|
-
for (let i = 0; i < count; i++) {
|
|
38267
|
-
const plane = clippingPlanes[i];
|
|
38268
|
-
distance = Math.max(distance, -plane.distanceToPoint(point));
|
|
38269
|
-
}
|
|
38270
|
-
return distance;
|
|
38271
|
-
}
|
|
38272
|
-
function clippedDistance(fn, point, clippingPlanes) {
|
|
38273
|
-
return Math.max(fn(point.x, point.y, point.z), clipPlaneDistance(point, clippingPlanes));
|
|
38274
|
-
}
|
|
38275
|
-
function intersectRayBoxRange(ray, box2) {
|
|
38276
|
-
let tNear = -Infinity;
|
|
38277
|
-
let tFar = Infinity;
|
|
38278
|
-
const origin = ray.origin;
|
|
38279
|
-
const direction = ray.direction;
|
|
38280
|
-
for (const axis of ["x", "y", "z"]) {
|
|
38281
|
-
const o2 = origin[axis];
|
|
38282
|
-
const d = direction[axis];
|
|
38283
|
-
const min = box2.min[axis];
|
|
38284
|
-
const max2 = box2.max[axis];
|
|
38285
|
-
if (Math.abs(d) < 1e-10) {
|
|
38286
|
-
if (o2 < min || o2 > max2) return null;
|
|
38287
|
-
continue;
|
|
38288
|
-
}
|
|
38289
|
-
const inv = 1 / d;
|
|
38290
|
-
let t02 = (min - o2) * inv;
|
|
38291
|
-
let t1 = (max2 - o2) * inv;
|
|
38292
|
-
if (t02 > t1) [t02, t1] = [t1, t02];
|
|
38293
|
-
tNear = Math.max(tNear, t02);
|
|
38294
|
-
tFar = Math.min(tFar, t1);
|
|
38295
|
-
if (tFar < tNear) return null;
|
|
38296
|
-
}
|
|
38297
|
-
if (tFar < 0) return null;
|
|
38298
|
-
return { near: Math.max(0, tNear), far: tFar };
|
|
38299
|
-
}
|
|
38300
|
-
function raymarchHit(ray, box2, diagonal, sdfFn, clippingPlanes) {
|
|
38301
|
-
const range = intersectRayBoxRange(ray, box2);
|
|
38302
|
-
if (!range) return null;
|
|
38303
|
-
const maxStep = Math.max(PICK_MIN_STEP, diagonal / 180);
|
|
38304
|
-
const point = new Vector3();
|
|
38305
|
-
let t2 = range.near;
|
|
38306
|
-
let prevT = t2;
|
|
38307
|
-
let prevDist = Number.POSITIVE_INFINITY;
|
|
38308
|
-
let bestT = t2;
|
|
38309
|
-
let bestAbsDist = Number.POSITIVE_INFINITY;
|
|
38310
|
-
for (let i = 0; i < PICK_MAX_STEPS; i++) {
|
|
38311
|
-
point.copy(ray.origin).addScaledVector(ray.direction, t2);
|
|
38312
|
-
const dist = clippedDistance(sdfFn, point, clippingPlanes);
|
|
38313
|
-
const absDist = Math.abs(dist);
|
|
38314
|
-
if (absDist < bestAbsDist) {
|
|
38315
|
-
bestAbsDist = absDist;
|
|
38316
|
-
bestT = t2;
|
|
38317
|
-
}
|
|
38318
|
-
if (absDist < PICK_HIT_EPS) return point;
|
|
38319
|
-
if (Number.isFinite(prevDist) && (prevDist < 0 && dist > 0 || prevDist > 0 && dist < 0)) {
|
|
38320
|
-
let lo = prevT;
|
|
38321
|
-
let hi = t2;
|
|
38322
|
-
const midPoint = point.clone();
|
|
38323
|
-
let dLo = prevDist;
|
|
38324
|
-
for (let step = 0; step < 8; step++) {
|
|
38325
|
-
const mid = (lo + hi) * 0.5;
|
|
38326
|
-
midPoint.copy(ray.origin).addScaledVector(ray.direction, mid);
|
|
38327
|
-
const dMid = clippedDistance(sdfFn, midPoint, clippingPlanes);
|
|
38328
|
-
if (Math.abs(dMid) < PICK_HIT_EPS) return midPoint;
|
|
38329
|
-
if (dLo < 0 && dMid > 0 || dLo > 0 && dMid < 0) {
|
|
38330
|
-
hi = mid;
|
|
38331
|
-
} else {
|
|
38332
|
-
lo = mid;
|
|
38333
|
-
dLo = dMid;
|
|
38334
|
-
}
|
|
38335
|
-
}
|
|
38336
|
-
return midPoint;
|
|
38337
|
-
}
|
|
38338
|
-
prevT = t2;
|
|
38339
|
-
prevDist = dist;
|
|
38340
|
-
t2 += MathUtils.clamp(absDist * 0.5, PICK_MIN_STEP, maxStep);
|
|
38341
|
-
if (t2 > range.far) break;
|
|
38342
|
-
}
|
|
38343
|
-
return bestAbsDist < PICK_HIT_EPS * 2 ? ray.origin.clone().addScaledVector(ray.direction, bestT) : null;
|
|
38344
|
-
}
|
|
38345
|
-
function SdfRaymarchObject({
|
|
38346
|
-
obj,
|
|
38347
|
-
settings,
|
|
38348
|
-
clippingPlanes,
|
|
38349
|
-
isHovered,
|
|
38350
|
-
onPointerEnter,
|
|
38351
|
-
onPointerMove,
|
|
38352
|
-
onPointerLeave,
|
|
38353
|
-
onClick,
|
|
38354
|
-
onDoubleClick,
|
|
38355
|
-
onContextMenu
|
|
38356
|
-
}) {
|
|
38357
|
-
const meshRef = reactExports.useRef(null);
|
|
38358
|
-
const sdf = obj.sdf;
|
|
38359
|
-
const bounds = reactExports.useMemo(() => sdf ? computeBounds([sdf]) : null, [sdf]);
|
|
38360
|
-
const boundsBox = reactExports.useMemo(
|
|
38361
|
-
() => bounds ? new Box3(new Vector3(...bounds.min), new Vector3(...bounds.max)) : null,
|
|
38362
|
-
[bounds]
|
|
38363
|
-
);
|
|
38364
|
-
const boundsDiagonal = reactExports.useMemo(() => boundsBox ? boundsBox.getSize(new Vector3()).length() : 0, [boundsBox]);
|
|
38365
|
-
const sdfFn = reactExports.useMemo(() => sdf ? compileSdfNode3(sdf.node) : null, [sdf]);
|
|
38366
|
-
const color2 = reactExports.useMemo(() => colorToRgb((settings == null ? void 0 : settings.color) ?? (sdf == null ? void 0 : sdf.colorHex)), [sdf == null ? void 0 : sdf.colorHex, settings == null ? void 0 : settings.color]);
|
|
38367
|
-
const leaf = reactExports.useMemo(() => {
|
|
38368
|
-
var _a3, _b2, _c, _d, _e, _f, _g, _h, _i;
|
|
38369
|
-
if (!sdf) return null;
|
|
38370
|
-
const materialAlpha = ((_a3 = sdf.materialProps) == null ? void 0 : _a3.opacity) ?? 1;
|
|
38371
|
-
return {
|
|
38372
|
-
node: sdf.node,
|
|
38373
|
-
color: color2,
|
|
38374
|
-
alpha: clamp01(Math.min((settings == null ? void 0 : settings.opacity) ?? 1, materialAlpha)),
|
|
38375
|
-
emissive: ((_b2 = sdf.materialProps) == null ? void 0 : _b2.emissive) ? colorToRgb(sdf.materialProps.emissive, "#000000") : BLACK,
|
|
38376
|
-
emissiveIntensity: ((_c = sdf.materialProps) == null ? void 0 : _c.emissiveIntensity) ?? 0,
|
|
38377
|
-
metalness: clamp01(((_d = sdf.materialProps) == null ? void 0 : _d.metalness) ?? 0.05),
|
|
38378
|
-
roughness: clamp01(((_e = sdf.materialProps) == null ? void 0 : _e.roughness) ?? 0.35),
|
|
38379
|
-
clearcoat: clamp01(((_f = sdf.materialProps) == null ? void 0 : _f.clearcoat) ?? 0.1),
|
|
38380
|
-
clearcoatRoughness: clamp01(((_g = sdf.materialProps) == null ? void 0 : _g.clearcoatRoughness) ?? 0.4),
|
|
38381
|
-
transmission: clamp01(((_h = sdf.materialProps) == null ? void 0 : _h.transmission) ?? 0),
|
|
38382
|
-
reflectivity: clamp01(((_i = sdf.materialProps) == null ? void 0 : _i.reflectivity) ?? 0.5)
|
|
38383
|
-
};
|
|
38384
|
-
}, [color2, sdf, settings == null ? void 0 : settings.opacity]);
|
|
38385
|
-
const isTransparent = ((leaf == null ? void 0 : leaf.alpha) ?? 1) < 1 || ((leaf == null ? void 0 : leaf.transmission) ?? 0) > 0;
|
|
38386
|
-
const fragmentShader2 = reactExports.useMemo(() => leaf ? buildSdfRaymarchFragmentShader([leaf]) : null, [leaf]);
|
|
38387
|
-
const clipPlaneUniforms = reactExports.useMemo(() => Array.from({ length: MAX_SDF_CLIP_PLANES }, () => new Vector4()), []);
|
|
38388
|
-
const material = reactExports.useMemo(() => {
|
|
38389
|
-
if (!fragmentShader2) return null;
|
|
38390
|
-
return new ShaderMaterial({
|
|
38391
|
-
vertexShader: SDF_RAYMARCH_PROXY_VERTEX_SHADER,
|
|
38392
|
-
fragmentShader: fragmentShader2,
|
|
38393
|
-
side: BackSide,
|
|
38394
|
-
transparent: isTransparent,
|
|
38395
|
-
depthTest: true,
|
|
38396
|
-
// SDF previews render a proxy box, but the shader writes the actual
|
|
38397
|
-
// raymarched hit depth. Keep depth writes on for transparent SDFs so
|
|
38398
|
-
// overlapping leaves sort by their real surface depth instead of by the
|
|
38399
|
-
// proxy box centers used by Three's transparent object sorter.
|
|
38400
|
-
depthWrite: true,
|
|
38401
|
-
uniforms: {
|
|
38402
|
-
uCameraMatrixWorld: { value: new Matrix4() },
|
|
38403
|
-
uViewProjectionMatrix: { value: new Matrix4() },
|
|
38404
|
-
uSceneMin: { value: new Vector3() },
|
|
38405
|
-
uSceneMax: { value: new Vector3() },
|
|
38406
|
-
uClipPlaneCount: { value: 0 },
|
|
38407
|
-
uClipPlanes: { value: clipPlaneUniforms },
|
|
38408
|
-
uHoverLeafIndex: { value: -1 },
|
|
38409
|
-
uHoverColor: { value: new Vector3() },
|
|
38410
|
-
uHoverIntensity: { value: 0 },
|
|
38411
|
-
uIsOrthographic: { value: 0 }
|
|
38412
|
-
}
|
|
38413
|
-
});
|
|
38414
|
-
}, [clipPlaneUniforms, fragmentShader2, isTransparent]);
|
|
38415
|
-
reactExports.useEffect(() => () => material == null ? void 0 : material.dispose(), [material]);
|
|
38416
|
-
useFrame(({ camera }) => {
|
|
38417
|
-
if (!bounds || !material) return;
|
|
38418
|
-
material.uniforms.uCameraMatrixWorld.value.copy(camera.matrixWorld);
|
|
38419
|
-
material.uniforms.uViewProjectionMatrix.value.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
38420
|
-
material.uniforms.uSceneMin.value.set(bounds.min[0], bounds.min[1], bounds.min[2]);
|
|
38421
|
-
material.uniforms.uSceneMax.value.set(bounds.max[0], bounds.max[1], bounds.max[2]);
|
|
38422
|
-
material.uniforms.uClipPlaneCount.value = setClipPlaneUniforms(clipPlaneUniforms, clippingPlanes);
|
|
38423
|
-
material.uniforms.uHoverLeafIndex.value = isHovered ? 0 : -1;
|
|
38424
|
-
material.uniforms.uHoverColor.value.set(color2[0], color2[1], color2[2]);
|
|
38425
|
-
material.uniforms.uHoverIntensity.value = isHovered ? 0.3 : 0;
|
|
38426
|
-
material.uniforms.uIsOrthographic.value = camera.isOrthographicCamera ? 1 : 0;
|
|
38427
|
-
});
|
|
38428
|
-
const raycast = reactExports.useCallback(
|
|
38429
|
-
(raycaster, intersections) => {
|
|
38430
|
-
const object = meshRef.current;
|
|
38431
|
-
if (!object || !boundsBox || !sdfFn) return;
|
|
38432
|
-
const point = raymarchHit(raycaster.ray, boundsBox, boundsDiagonal, sdfFn, clippingPlanes);
|
|
38433
|
-
if (!point) return;
|
|
38434
|
-
const distance = raycaster.ray.origin.distanceTo(point);
|
|
38435
|
-
if (distance < raycaster.near || distance > raycaster.far) return;
|
|
38436
|
-
intersections.push({ distance, point, object });
|
|
38437
|
-
},
|
|
38438
|
-
[boundsBox, boundsDiagonal, clippingPlanes, sdfFn]
|
|
38439
|
-
);
|
|
38440
|
-
if (!bounds || !material || !sdfFn) return null;
|
|
38441
|
-
const size = [
|
|
38442
|
-
Math.max(bounds.max[0] - bounds.min[0], 1e-3),
|
|
38443
|
-
Math.max(bounds.max[1] - bounds.min[1], 1e-3),
|
|
38444
|
-
Math.max(bounds.max[2] - bounds.min[2], 1e-3)
|
|
38445
|
-
];
|
|
38446
|
-
const center = [
|
|
38447
|
-
(bounds.min[0] + bounds.max[0]) * 0.5,
|
|
38448
|
-
(bounds.min[1] + bounds.max[1]) * 0.5,
|
|
38449
|
-
(bounds.min[2] + bounds.max[2]) * 0.5
|
|
38450
|
-
];
|
|
38451
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
38452
|
-
"mesh",
|
|
38453
|
-
{
|
|
38454
|
-
ref: meshRef,
|
|
38455
|
-
position: center,
|
|
38456
|
-
frustumCulled: false,
|
|
38457
|
-
raycast,
|
|
38458
|
-
onPointerEnter,
|
|
38459
|
-
onPointerMove,
|
|
38460
|
-
onPointerLeave,
|
|
38461
|
-
onClick,
|
|
38462
|
-
onDoubleClick,
|
|
38463
|
-
onContextMenu,
|
|
38464
|
-
children: [
|
|
38465
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("boxGeometry", { args: size }),
|
|
38466
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("primitive", { object: material, attach: "material" })
|
|
38467
|
-
]
|
|
38468
|
-
}
|
|
38469
|
-
);
|
|
38470
|
-
}
|
|
38471
|
-
function SdfRaymarchLayer({
|
|
38472
|
-
objects,
|
|
38473
|
-
objectSettings,
|
|
38474
|
-
clippingPlanesById,
|
|
38475
|
-
hoveredObjectId,
|
|
38476
|
-
onPointerEnter,
|
|
38477
|
-
onPointerMove,
|
|
38478
|
-
onPointerLeave,
|
|
38479
|
-
onClick,
|
|
38480
|
-
onDoubleClick,
|
|
38481
|
-
onContextMenu
|
|
38482
|
-
}) {
|
|
38483
|
-
const visibleSdfObjects = reactExports.useMemo(
|
|
38484
|
-
() => objects.filter((obj) => {
|
|
38485
|
-
var _a3;
|
|
38486
|
-
return obj.sdf && (((_a3 = objectSettings[obj.id]) == null ? void 0 : _a3.visible) ?? true);
|
|
38487
|
-
}),
|
|
38488
|
-
[objectSettings, objects]
|
|
38489
|
-
);
|
|
38490
|
-
if (visibleSdfObjects.length === 0) return null;
|
|
38491
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("group", { children: visibleSdfObjects.map((obj) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
38492
|
-
SdfRaymarchObject,
|
|
38493
|
-
{
|
|
38494
|
-
obj,
|
|
38495
|
-
settings: objectSettings[obj.id],
|
|
38496
|
-
clippingPlanes: clippingPlanesById[obj.id] ?? EMPTY_CLIPPING_PLANES,
|
|
38497
|
-
isHovered: hoveredObjectId === obj.id,
|
|
38498
|
-
onPointerEnter: onPointerEnter ? (event) => onPointerEnter(obj, event) : void 0,
|
|
38499
|
-
onPointerMove: onPointerMove ? (event) => onPointerMove(obj, event) : void 0,
|
|
38500
|
-
onPointerLeave: onPointerLeave ? (event) => onPointerLeave(obj, event) : void 0,
|
|
38501
|
-
onClick: onClick ? (event) => onClick(obj, event) : void 0,
|
|
38502
|
-
onDoubleClick: onDoubleClick ? (event) => onDoubleClick(obj, event) : void 0,
|
|
38503
|
-
onContextMenu: onContextMenu ? (event) => onContextMenu(obj, event) : void 0
|
|
38504
|
-
},
|
|
38505
|
-
obj.id
|
|
38506
|
-
)) });
|
|
38507
|
-
}
|
|
38508
39314
|
function findHoveredSurface(x, y, meta) {
|
|
38509
39315
|
for (let i = meta.surfaces.length - 1; i >= 0; i--) {
|
|
38510
39316
|
const s = meta.surfaces[i];
|
|
@@ -40846,6 +41652,37 @@ function ZoomIndicatorPanel({ mmPerPx }) {
|
|
|
40846
41652
|
}
|
|
40847
41653
|
);
|
|
40848
41654
|
}
|
|
41655
|
+
function roundDebugNumber(value) {
|
|
41656
|
+
return Number.isFinite(value) ? Math.round(value * 1e3) / 1e3 : value;
|
|
41657
|
+
}
|
|
41658
|
+
function vectorTuple(vector) {
|
|
41659
|
+
return [roundDebugNumber(vector.x), roundDebugNumber(vector.y), roundDebugNumber(vector.z)];
|
|
41660
|
+
}
|
|
41661
|
+
function snapshotViewportCamera(camera, target) {
|
|
41662
|
+
const ortho = camera;
|
|
41663
|
+
const perspective = camera;
|
|
41664
|
+
const isOrtho = ortho.isOrthographicCamera === true;
|
|
41665
|
+
return {
|
|
41666
|
+
position: vectorTuple(camera.position),
|
|
41667
|
+
target: target ? vectorTuple(target) : null,
|
|
41668
|
+
up: vectorTuple(camera.up),
|
|
41669
|
+
projection: isOrtho ? "orthographic" : "perspective",
|
|
41670
|
+
fov: isOrtho ? void 0 : roundDebugNumber(perspective.fov),
|
|
41671
|
+
zoom: isOrtho ? roundDebugNumber(ortho.zoom) : void 0
|
|
41672
|
+
};
|
|
41673
|
+
}
|
|
41674
|
+
function recordViewportCameraDebug(entry) {
|
|
41675
|
+
if (typeof window === "undefined") return;
|
|
41676
|
+
if (window.localStorage.getItem("forgecad:debugCamera") !== "1") return;
|
|
41677
|
+
window.__forgecadCameraDebug = [...window.__forgecadCameraDebug ?? [], entry].slice(-50);
|
|
41678
|
+
console.debug("[forgecad camera]", entry);
|
|
41679
|
+
}
|
|
41680
|
+
function recordViewportCameraLifecycleDebug(entry) {
|
|
41681
|
+
if (typeof window === "undefined") return;
|
|
41682
|
+
if (window.localStorage.getItem("forgecad:debugCamera") !== "1") return;
|
|
41683
|
+
window.__forgecadCameraLifecycleDebug = [...window.__forgecadCameraLifecycleDebug ?? [], entry].slice(-50);
|
|
41684
|
+
console.debug("[forgecad view-controller]", entry);
|
|
41685
|
+
}
|
|
40849
41686
|
const readPersistedViewportCameraState = () => {
|
|
40850
41687
|
if (typeof window === "undefined") return null;
|
|
40851
41688
|
try {
|
|
@@ -40869,19 +41706,117 @@ const resolveHoverObjectName = (name, knownFileNames) => {
|
|
|
40869
41706
|
if (knownFileNames.has(trimmed)) return null;
|
|
40870
41707
|
return trimmed;
|
|
40871
41708
|
};
|
|
41709
|
+
const SNAP_VIEW_DIRECTIONS = {
|
|
41710
|
+
front: new Vector3(0, -1, 0),
|
|
41711
|
+
back: new Vector3(0, 1, 0),
|
|
41712
|
+
right: new Vector3(1, 0, 0),
|
|
41713
|
+
left: new Vector3(-1, 0, 0),
|
|
41714
|
+
top: new Vector3(0, 0, 1),
|
|
41715
|
+
bottom: new Vector3(0, 0, -1),
|
|
41716
|
+
iso: new Vector3(1, -1, 1)
|
|
41717
|
+
};
|
|
41718
|
+
const DEFAULT_CAMERA_UP = new Vector3(0, 0, 1);
|
|
41719
|
+
const SNAP_VIEW_UP = {
|
|
41720
|
+
top: new Vector3(0, 1, 0),
|
|
41721
|
+
bottom: new Vector3(0, -1, 0)
|
|
41722
|
+
};
|
|
41723
|
+
function isFiniteVector$1(vector) {
|
|
41724
|
+
return Number.isFinite(vector.x) && Number.isFinite(vector.y) && Number.isFinite(vector.z);
|
|
41725
|
+
}
|
|
41726
|
+
function computeBoundsFraming(bounds) {
|
|
41727
|
+
if (bounds.isEmpty()) return null;
|
|
41728
|
+
const target = new Vector3();
|
|
41729
|
+
bounds.getCenter(target);
|
|
41730
|
+
const size = new Vector3();
|
|
41731
|
+
bounds.getSize(size);
|
|
41732
|
+
if (!isFiniteVector$1(target) || !isFiniteVector$1(size)) return null;
|
|
41733
|
+
const reach = Math.max(size.x, size.y, size.z, 1);
|
|
41734
|
+
if (!Number.isFinite(reach) || reach <= 0) return null;
|
|
41735
|
+
return { target, size, reach };
|
|
41736
|
+
}
|
|
41737
|
+
function snapViewDirection(view2) {
|
|
41738
|
+
return SNAP_VIEW_DIRECTIONS[view2 ?? "iso"].clone().normalize();
|
|
41739
|
+
}
|
|
41740
|
+
function snapViewUp(view2) {
|
|
41741
|
+
return (SNAP_VIEW_UP[view2 ?? "iso"] ?? DEFAULT_CAMERA_UP).clone();
|
|
41742
|
+
}
|
|
41743
|
+
function computeSnapOrbitPose({
|
|
41744
|
+
position,
|
|
41745
|
+
target,
|
|
41746
|
+
up,
|
|
41747
|
+
view: view2
|
|
41748
|
+
}) {
|
|
41749
|
+
if (!isFiniteVector$1(position) || !isFiniteVector$1(target) || !isFiniteVector$1(up)) return null;
|
|
41750
|
+
const distance = position.distanceTo(target);
|
|
41751
|
+
if (!Number.isFinite(distance) || distance <= 1e-6) return null;
|
|
41752
|
+
const nextPosition = target.clone().add(snapViewDirection(view2).multiplyScalar(distance));
|
|
41753
|
+
if (!isFiniteVector$1(nextPosition)) return null;
|
|
41754
|
+
return {
|
|
41755
|
+
position: nextPosition,
|
|
41756
|
+
target: target.clone(),
|
|
41757
|
+
up: up.clone()
|
|
41758
|
+
};
|
|
41759
|
+
}
|
|
41760
|
+
function ActiveCameraBridge({ onCameraChange }) {
|
|
41761
|
+
const camera = useThree((state2) => state2.camera);
|
|
41762
|
+
reactExports.useEffect(() => {
|
|
41763
|
+
onCameraChange(camera);
|
|
41764
|
+
recordViewportCameraLifecycleDebug({
|
|
41765
|
+
path: "active-camera",
|
|
41766
|
+
snapshot: snapshotViewportCamera(camera, null)
|
|
41767
|
+
});
|
|
41768
|
+
}, [camera, onCameraChange]);
|
|
41769
|
+
reactExports.useEffect(() => {
|
|
41770
|
+
return () => {
|
|
41771
|
+
onCameraChange(null);
|
|
41772
|
+
recordViewportCameraLifecycleDebug({ path: "active-camera-cleared" });
|
|
41773
|
+
};
|
|
41774
|
+
}, [onCameraChange]);
|
|
41775
|
+
return null;
|
|
41776
|
+
}
|
|
40872
41777
|
function ViewController({
|
|
41778
|
+
camera,
|
|
40873
41779
|
controlsRef,
|
|
40874
|
-
|
|
41780
|
+
viewportRef,
|
|
40875
41781
|
objects,
|
|
40876
41782
|
objectMatrices,
|
|
40877
41783
|
fallbackBounds,
|
|
40878
41784
|
settings,
|
|
40879
|
-
focusedObjectIds
|
|
40880
|
-
clearCommand
|
|
41785
|
+
focusedObjectIds
|
|
40881
41786
|
}) {
|
|
40882
|
-
const
|
|
41787
|
+
const command = useForgeStore((s) => s.viewCommand);
|
|
41788
|
+
const clearCommand = useForgeStore((s) => s.clearViewCommand);
|
|
41789
|
+
const getActiveCamera = reactExports.useCallback(() => {
|
|
41790
|
+
var _a3;
|
|
41791
|
+
return camera ?? ((_a3 = controlsRef.current) == null ? void 0 : _a3.object) ?? null;
|
|
41792
|
+
}, [camera, controlsRef]);
|
|
41793
|
+
reactExports.useEffect(() => {
|
|
41794
|
+
recordViewportCameraLifecycleDebug({ path: "mounted" });
|
|
41795
|
+
return () => {
|
|
41796
|
+
recordViewportCameraLifecycleDebug({ path: "unmounted" });
|
|
41797
|
+
};
|
|
41798
|
+
}, []);
|
|
40883
41799
|
reactExports.useEffect(() => {
|
|
41800
|
+
var _a3;
|
|
40884
41801
|
if (!command) return;
|
|
41802
|
+
const activeCamera = getActiveCamera();
|
|
41803
|
+
if (!activeCamera) {
|
|
41804
|
+
recordViewportCameraLifecycleDebug({ path: "command-no-camera" });
|
|
41805
|
+
return;
|
|
41806
|
+
}
|
|
41807
|
+
recordViewportCameraLifecycleDebug({
|
|
41808
|
+
path: "command-change",
|
|
41809
|
+
snapshot: snapshotViewportCamera(activeCamera, ((_a3 = controlsRef.current) == null ? void 0 : _a3.target) ?? null)
|
|
41810
|
+
});
|
|
41811
|
+
}, [command, controlsRef, getActiveCamera]);
|
|
41812
|
+
reactExports.useEffect(() => {
|
|
41813
|
+
var _a3;
|
|
41814
|
+
if (!command) return;
|
|
41815
|
+
const camera2 = getActiveCamera();
|
|
41816
|
+
if (!camera2) {
|
|
41817
|
+
recordViewportCameraLifecycleDebug({ path: "command-no-camera-handler" });
|
|
41818
|
+
return;
|
|
41819
|
+
}
|
|
40885
41820
|
if (command.type === "sceneCamera") return;
|
|
40886
41821
|
if (command.type === "camera") {
|
|
40887
41822
|
const state2 = command.camera;
|
|
@@ -40889,17 +41824,17 @@ function ViewController({
|
|
|
40889
41824
|
clearCommand();
|
|
40890
41825
|
return;
|
|
40891
41826
|
}
|
|
40892
|
-
|
|
40893
|
-
|
|
40894
|
-
if ("fov" in
|
|
40895
|
-
|
|
41827
|
+
camera2.position.set(state2.position[0], state2.position[1], state2.position[2]);
|
|
41828
|
+
camera2.up.set(state2.up[0], state2.up[1], state2.up[2]);
|
|
41829
|
+
if ("fov" in camera2 && state2.fov !== void 0) {
|
|
41830
|
+
camera2.fov = state2.fov;
|
|
40896
41831
|
}
|
|
40897
|
-
if (
|
|
40898
|
-
|
|
41832
|
+
if (camera2.isOrthographicCamera && state2.orthoZoom !== void 0) {
|
|
41833
|
+
camera2.zoom = state2.orthoZoom;
|
|
40899
41834
|
}
|
|
40900
|
-
|
|
40901
|
-
if ("updateProjectionMatrix" in
|
|
40902
|
-
|
|
41835
|
+
camera2.lookAt(state2.target[0], state2.target[1], state2.target[2]);
|
|
41836
|
+
if ("updateProjectionMatrix" in camera2) {
|
|
41837
|
+
camera2.updateProjectionMatrix();
|
|
40903
41838
|
}
|
|
40904
41839
|
const controls2 = controlsRef.current;
|
|
40905
41840
|
if (controls2) {
|
|
@@ -40909,9 +41844,43 @@ function ViewController({
|
|
|
40909
41844
|
clearCommand();
|
|
40910
41845
|
return;
|
|
40911
41846
|
}
|
|
41847
|
+
const controls = controlsRef.current;
|
|
41848
|
+
const commandSnapshot = snapshotViewportCamera(camera2, (controls == null ? void 0 : controls.target) ?? null);
|
|
41849
|
+
recordViewportCameraDebug({
|
|
41850
|
+
path: "received",
|
|
41851
|
+
command: { type: command.type, view: command.view, targetId: command.targetId },
|
|
41852
|
+
before: commandSnapshot,
|
|
41853
|
+
after: commandSnapshot
|
|
41854
|
+
});
|
|
41855
|
+
if (command.type === "snap" && controls) {
|
|
41856
|
+
const before2 = commandSnapshot;
|
|
41857
|
+
const snapPose = computeSnapOrbitPose({
|
|
41858
|
+
position: camera2.position,
|
|
41859
|
+
target: controls.target,
|
|
41860
|
+
up: camera2.up,
|
|
41861
|
+
view: command.view
|
|
41862
|
+
});
|
|
41863
|
+
if (snapPose) {
|
|
41864
|
+
camera2.position.copy(snapPose.position);
|
|
41865
|
+
camera2.up.copy(snapPose.up);
|
|
41866
|
+
if ("updateProjectionMatrix" in camera2) {
|
|
41867
|
+
camera2.updateProjectionMatrix();
|
|
41868
|
+
}
|
|
41869
|
+
controls.target.copy(snapPose.target);
|
|
41870
|
+
controls.update();
|
|
41871
|
+
recordViewportCameraDebug({
|
|
41872
|
+
path: "snap-orbit",
|
|
41873
|
+
command: { type: command.type, view: command.view, targetId: command.targetId },
|
|
41874
|
+
before: before2,
|
|
41875
|
+
after: snapshotViewportCamera(camera2, controls.target)
|
|
41876
|
+
});
|
|
41877
|
+
clearCommand();
|
|
41878
|
+
return;
|
|
41879
|
+
}
|
|
41880
|
+
}
|
|
40912
41881
|
const visibleObjects = objects.filter((obj) => {
|
|
40913
|
-
var
|
|
40914
|
-
return ((
|
|
41882
|
+
var _a4;
|
|
41883
|
+
return ((_a4 = settings[obj.id]) == null ? void 0 : _a4.visible) !== false;
|
|
40915
41884
|
});
|
|
40916
41885
|
const focusedIdSet = new Set(focusedObjectIds);
|
|
40917
41886
|
const focusedVisibleObjects = focusedIdSet.size > 0 ? visibleObjects.filter((obj) => focusedIdSet.has(obj.id)) : [];
|
|
@@ -40920,46 +41889,34 @@ function ViewController({
|
|
|
40920
41889
|
const canUseFallbackBounds = !command.targetId && !useFocusedScope;
|
|
40921
41890
|
const bounds = computeSceneBounds(targetObjects, objectMatrices) ?? (canUseFallbackBounds ? fallbackBounds : null);
|
|
40922
41891
|
if (!bounds) {
|
|
41892
|
+
recordViewportCameraDebug({
|
|
41893
|
+
path: "no-bounds",
|
|
41894
|
+
command: { type: command.type, view: command.view, targetId: command.targetId },
|
|
41895
|
+
before: commandSnapshot,
|
|
41896
|
+
after: snapshotViewportCamera(camera2, (controls == null ? void 0 : controls.target) ?? null)
|
|
41897
|
+
});
|
|
40923
41898
|
clearCommand();
|
|
40924
41899
|
return;
|
|
40925
41900
|
}
|
|
40926
|
-
const
|
|
40927
|
-
|
|
40928
|
-
|
|
40929
|
-
|
|
40930
|
-
|
|
40931
|
-
|
|
40932
|
-
|
|
40933
|
-
|
|
40934
|
-
const maxDim = Math.max(sizeVec.x, sizeVec.y, sizeVec.z, 1);
|
|
40935
|
-
const snapUsesScopedCenter = command.type === "snap" && useFocusedScope;
|
|
40936
|
-
const target = command.type === "snap" && !snapUsesScopedCenter ? new Vector3(0, 0, 0) : center;
|
|
40937
|
-
const maxReach = command.type === "snap" && !snapUsesScopedCenter ? Math.max(sizeVec.x / 2 + Math.abs(center.x), sizeVec.y / 2 + Math.abs(center.y), sizeVec.z / 2 + Math.abs(center.z)) * 2 : maxDim;
|
|
40938
|
-
if (!Number.isFinite(target.x) || !Number.isFinite(target.y) || !Number.isFinite(target.z) || !Number.isFinite(maxReach) || maxReach <= 0) {
|
|
41901
|
+
const framing = computeBoundsFraming(bounds);
|
|
41902
|
+
if (!framing) {
|
|
41903
|
+
recordViewportCameraDebug({
|
|
41904
|
+
path: "invalid-bounds",
|
|
41905
|
+
command: { type: command.type, view: command.view, targetId: command.targetId },
|
|
41906
|
+
before: commandSnapshot,
|
|
41907
|
+
after: snapshotViewportCamera(camera2, (controls == null ? void 0 : controls.target) ?? null)
|
|
41908
|
+
});
|
|
40939
41909
|
clearCommand();
|
|
40940
41910
|
return;
|
|
40941
41911
|
}
|
|
40942
|
-
const
|
|
41912
|
+
const { target, reach: maxReach } = framing;
|
|
41913
|
+
const before = snapshotViewportCamera(camera2, (controls == null ? void 0 : controls.target) ?? null);
|
|
40943
41914
|
const camDir = new Vector3();
|
|
40944
41915
|
if (command.type === "snap") {
|
|
40945
|
-
|
|
40946
|
-
|
|
40947
|
-
back: new Vector3(0, 1, 0),
|
|
40948
|
-
right: new Vector3(1, 0, 0),
|
|
40949
|
-
left: new Vector3(-1, 0, 0),
|
|
40950
|
-
top: new Vector3(0, 0, 1),
|
|
40951
|
-
bottom: new Vector3(0, 0, -1),
|
|
40952
|
-
iso: new Vector3(1, -1, 1)
|
|
40953
|
-
};
|
|
40954
|
-
const upMap = {
|
|
40955
|
-
top: new Vector3(0, 1, 0),
|
|
40956
|
-
bottom: new Vector3(0, -1, 0)
|
|
40957
|
-
};
|
|
40958
|
-
camDir.copy(viewMap[command.view ?? "iso"]).normalize();
|
|
40959
|
-
const up = upMap[command.view ?? ""] ?? new Vector3(0, 0, 1);
|
|
40960
|
-
camera.up.copy(up);
|
|
41916
|
+
camDir.copy(snapViewDirection(command.view));
|
|
41917
|
+
camera2.up.copy(snapViewUp(command.view));
|
|
40961
41918
|
} else if (controls) {
|
|
40962
|
-
camDir.subVectors(
|
|
41919
|
+
camDir.subVectors(camera2.position, controls.target).normalize();
|
|
40963
41920
|
if (camDir.lengthSq() === 0) camDir.set(1, 1, 1).normalize();
|
|
40964
41921
|
} else {
|
|
40965
41922
|
camDir.set(1, 1, 1).normalize();
|
|
@@ -40968,10 +41925,13 @@ function ViewController({
|
|
|
40968
41925
|
clearCommand();
|
|
40969
41926
|
return;
|
|
40970
41927
|
}
|
|
40971
|
-
const isOrtho =
|
|
41928
|
+
const isOrtho = camera2.isOrthographicCamera;
|
|
41929
|
+
const viewportRect = (_a3 = viewportRef.current) == null ? void 0 : _a3.getBoundingClientRect();
|
|
41930
|
+
const viewportWidth = (viewportRect == null ? void 0 : viewportRect.width) ?? 800;
|
|
41931
|
+
const viewportHeight = (viewportRect == null ? void 0 : viewportRect.height) ?? 600;
|
|
40972
41932
|
if (isOrtho) {
|
|
40973
|
-
const ortho =
|
|
40974
|
-
const zoom = Math.min(
|
|
41933
|
+
const ortho = camera2;
|
|
41934
|
+
const zoom = Math.min(viewportWidth, viewportHeight) / maxReach / 2.2;
|
|
40975
41935
|
if (!Number.isFinite(zoom) || zoom <= 0) {
|
|
40976
41936
|
clearCommand();
|
|
40977
41937
|
return;
|
|
@@ -40985,7 +41945,7 @@ function ViewController({
|
|
|
40985
41945
|
ortho.position.copy(nextPosition);
|
|
40986
41946
|
ortho.updateProjectionMatrix();
|
|
40987
41947
|
} else {
|
|
40988
|
-
const persp =
|
|
41948
|
+
const persp = camera2;
|
|
40989
41949
|
const dist = maxReach / (2 * Math.tan(persp.fov * Math.PI / 360)) * 1.4;
|
|
40990
41950
|
if (!Number.isFinite(dist) || dist <= 0) {
|
|
40991
41951
|
clearCommand();
|
|
@@ -41003,21 +41963,26 @@ function ViewController({
|
|
|
41003
41963
|
controls.target.copy(target);
|
|
41004
41964
|
controls.update();
|
|
41005
41965
|
} else {
|
|
41006
|
-
|
|
41966
|
+
camera2.lookAt(target);
|
|
41007
41967
|
}
|
|
41968
|
+
recordViewportCameraDebug({
|
|
41969
|
+
path: command.type === "snap" ? "snap-framing-fallback" : "framing",
|
|
41970
|
+
command: { type: command.type, view: command.view, targetId: command.targetId },
|
|
41971
|
+
before,
|
|
41972
|
+
after: snapshotViewportCamera(camera2, (controls == null ? void 0 : controls.target) ?? null)
|
|
41973
|
+
});
|
|
41008
41974
|
clearCommand();
|
|
41009
41975
|
}, [
|
|
41010
|
-
camera,
|
|
41011
41976
|
clearCommand,
|
|
41012
41977
|
command,
|
|
41013
41978
|
controlsRef,
|
|
41014
41979
|
fallbackBounds,
|
|
41015
41980
|
focusedObjectIds,
|
|
41981
|
+
getActiveCamera,
|
|
41016
41982
|
objectMatrices,
|
|
41017
41983
|
objects,
|
|
41018
41984
|
settings,
|
|
41019
|
-
|
|
41020
|
-
size.width
|
|
41985
|
+
viewportRef
|
|
41021
41986
|
]);
|
|
41022
41987
|
return null;
|
|
41023
41988
|
}
|
|
@@ -41151,6 +42116,293 @@ function ViewPersistence({
|
|
|
41151
42116
|
}, [camera, controlsRef, isSketchOnly, projectionMode, setViewportCameraState]);
|
|
41152
42117
|
return null;
|
|
41153
42118
|
}
|
|
42119
|
+
const CUBE_SIZE = 116;
|
|
42120
|
+
const CUBE_CENTER = CUBE_SIZE / 2;
|
|
42121
|
+
const CUBE_SCALE = 26;
|
|
42122
|
+
const DEFAULT_CAMERA_STATE = {
|
|
42123
|
+
position: [120, 80, 120],
|
|
42124
|
+
target: [0, 0, 0],
|
|
42125
|
+
up: [0, 0, 1]
|
|
42126
|
+
};
|
|
42127
|
+
const FACE_DEFS = [
|
|
42128
|
+
{
|
|
42129
|
+
view: "right",
|
|
42130
|
+
label: "RIGHT",
|
|
42131
|
+
normal: new Vector3(1, 0, 0),
|
|
42132
|
+
corners: [new Vector3(1, -1, -1), new Vector3(1, 1, -1), new Vector3(1, 1, 1), new Vector3(1, -1, 1)]
|
|
42133
|
+
},
|
|
42134
|
+
{
|
|
42135
|
+
view: "left",
|
|
42136
|
+
label: "LEFT",
|
|
42137
|
+
normal: new Vector3(-1, 0, 0),
|
|
42138
|
+
corners: [new Vector3(-1, 1, -1), new Vector3(-1, -1, -1), new Vector3(-1, -1, 1), new Vector3(-1, 1, 1)]
|
|
42139
|
+
},
|
|
42140
|
+
{
|
|
42141
|
+
view: "back",
|
|
42142
|
+
label: "BACK",
|
|
42143
|
+
normal: new Vector3(0, 1, 0),
|
|
42144
|
+
corners: [new Vector3(1, 1, -1), new Vector3(-1, 1, -1), new Vector3(-1, 1, 1), new Vector3(1, 1, 1)]
|
|
42145
|
+
},
|
|
42146
|
+
{
|
|
42147
|
+
view: "front",
|
|
42148
|
+
label: "FRONT",
|
|
42149
|
+
normal: new Vector3(0, -1, 0),
|
|
42150
|
+
corners: [new Vector3(-1, -1, -1), new Vector3(1, -1, -1), new Vector3(1, -1, 1), new Vector3(-1, -1, 1)]
|
|
42151
|
+
},
|
|
42152
|
+
{
|
|
42153
|
+
view: "top",
|
|
42154
|
+
label: "TOP",
|
|
42155
|
+
normal: new Vector3(0, 0, 1),
|
|
42156
|
+
corners: [new Vector3(-1, -1, 1), new Vector3(1, -1, 1), new Vector3(1, 1, 1), new Vector3(-1, 1, 1)]
|
|
42157
|
+
},
|
|
42158
|
+
{
|
|
42159
|
+
view: "bottom",
|
|
42160
|
+
label: "BOTTOM",
|
|
42161
|
+
normal: new Vector3(0, 0, -1),
|
|
42162
|
+
corners: [new Vector3(-1, 1, -1), new Vector3(1, 1, -1), new Vector3(1, -1, -1), new Vector3(-1, -1, -1)]
|
|
42163
|
+
}
|
|
42164
|
+
];
|
|
42165
|
+
const SNAP_VIEWS = /* @__PURE__ */ new Set(["front", "back", "left", "right", "top", "bottom", "iso"]);
|
|
42166
|
+
function vectorFromTuple(tuple) {
|
|
42167
|
+
return new Vector3(tuple[0], tuple[1], tuple[2]);
|
|
42168
|
+
}
|
|
42169
|
+
function normalizedOr(vector, fallback) {
|
|
42170
|
+
return Number.isFinite(vector.x) && Number.isFinite(vector.y) && Number.isFinite(vector.z) && vector.lengthSq() > 1e-10 ? vector.clone().normalize() : fallback.clone().normalize();
|
|
42171
|
+
}
|
|
42172
|
+
function buildViewBasisFromCameraState(cameraState) {
|
|
42173
|
+
const position = vectorFromTuple(cameraState.position);
|
|
42174
|
+
const target = vectorFromTuple(cameraState.target);
|
|
42175
|
+
const up = normalizedOr(vectorFromTuple(cameraState.up), new Vector3(0, 0, 1));
|
|
42176
|
+
const viewer = normalizedOr(position.clone().sub(target), new Vector3(1, -1, 1));
|
|
42177
|
+
const forward = viewer.clone().negate();
|
|
42178
|
+
let right = forward.clone().cross(up);
|
|
42179
|
+
if (right.lengthSq() <= 1e-10) {
|
|
42180
|
+
right = Math.abs(forward.z) > 0.9 ? new Vector3(1, 0, 0) : forward.clone().cross(new Vector3(0, 0, 1));
|
|
42181
|
+
}
|
|
42182
|
+
right.normalize();
|
|
42183
|
+
const screenUp = normalizedOr(right.clone().cross(forward), new Vector3(0, 0, 1));
|
|
42184
|
+
return { viewer, right, screenUp };
|
|
42185
|
+
}
|
|
42186
|
+
const DEFAULT_VIEW_BASIS = buildViewBasisFromCameraState(DEFAULT_CAMERA_STATE);
|
|
42187
|
+
function buildViewBasisFromLiveCamera(camera) {
|
|
42188
|
+
camera.updateMatrixWorld();
|
|
42189
|
+
const quaternion = camera.getWorldQuaternion(new Quaternion());
|
|
42190
|
+
const viewer = normalizedOr(new Vector3(0, 0, 1).applyQuaternion(quaternion), DEFAULT_VIEW_BASIS.viewer);
|
|
42191
|
+
const right = normalizedOr(new Vector3(1, 0, 0).applyQuaternion(quaternion), DEFAULT_VIEW_BASIS.right);
|
|
42192
|
+
const screenUp = normalizedOr(new Vector3(0, 1, 0).applyQuaternion(quaternion), DEFAULT_VIEW_BASIS.screenUp);
|
|
42193
|
+
return { viewer, right, screenUp };
|
|
42194
|
+
}
|
|
42195
|
+
function faceFill(brightness) {
|
|
42196
|
+
const litPercent = Math.round(42 + brightness * 46);
|
|
42197
|
+
return `color-mix(in srgb, var(--fc-view-cube-face-lit) ${litPercent}%, var(--fc-view-cube-face-dim))`;
|
|
42198
|
+
}
|
|
42199
|
+
function projectVector(vector, right, screenUp, viewer) {
|
|
42200
|
+
return {
|
|
42201
|
+
x: CUBE_CENTER + vector.dot(right) * CUBE_SCALE,
|
|
42202
|
+
y: CUBE_CENTER - vector.dot(screenUp) * CUBE_SCALE,
|
|
42203
|
+
depth: vector.dot(viewer)
|
|
42204
|
+
};
|
|
42205
|
+
}
|
|
42206
|
+
function polygonPoints(points) {
|
|
42207
|
+
return points.map((point) => `${point.x.toFixed(1)},${point.y.toFixed(1)}`).join(" ");
|
|
42208
|
+
}
|
|
42209
|
+
function snapViewFromEventTarget(target) {
|
|
42210
|
+
var _a3;
|
|
42211
|
+
if (!(target instanceof Element)) return null;
|
|
42212
|
+
const view2 = (_a3 = target.closest(".fc-view-cube-face")) == null ? void 0 : _a3.dataset.view;
|
|
42213
|
+
return view2 && SNAP_VIEWS.has(view2) ? view2 : null;
|
|
42214
|
+
}
|
|
42215
|
+
function cameraRightVector(camera) {
|
|
42216
|
+
return buildViewBasisFromLiveCamera(camera).right;
|
|
42217
|
+
}
|
|
42218
|
+
function rotateCameraFromDrag(camera, controls, drag, clientX, clientY) {
|
|
42219
|
+
const dx = clientX - drag.startX;
|
|
42220
|
+
const dy = clientY - drag.startY;
|
|
42221
|
+
if (Math.hypot(dx, dy) > 3) drag.moved = true;
|
|
42222
|
+
const sensitivity = 8e-3;
|
|
42223
|
+
const startOffset = drag.startPosition.clone().sub(drag.target);
|
|
42224
|
+
const yaw = new Quaternion().setFromAxisAngle(drag.up, -dx * sensitivity);
|
|
42225
|
+
const pitchAxis = drag.right.clone().applyQuaternion(yaw).normalize();
|
|
42226
|
+
const pitch = new Quaternion().setFromAxisAngle(pitchAxis, -dy * sensitivity);
|
|
42227
|
+
const nextOffset = startOffset.applyQuaternion(yaw).applyQuaternion(pitch);
|
|
42228
|
+
camera.position.copy(drag.target).add(nextOffset);
|
|
42229
|
+
camera.up.copy(drag.up);
|
|
42230
|
+
controls.target.copy(drag.target);
|
|
42231
|
+
if ("updateProjectionMatrix" in camera) {
|
|
42232
|
+
camera.updateProjectionMatrix();
|
|
42233
|
+
}
|
|
42234
|
+
controls.update();
|
|
42235
|
+
}
|
|
42236
|
+
function ViewCube({ camera, controlsRef, onSnap }) {
|
|
42237
|
+
const [viewBasis, setViewBasis] = reactExports.useState(null);
|
|
42238
|
+
const [dragging, setDragging] = reactExports.useState(false);
|
|
42239
|
+
const [pressedView, setPressedView] = reactExports.useState(null);
|
|
42240
|
+
const dragInfoRef = reactExports.useRef(null);
|
|
42241
|
+
const pressedTimeoutRef = reactExports.useRef(null);
|
|
42242
|
+
const getLiveCamera = reactExports.useCallback(() => {
|
|
42243
|
+
var _a3;
|
|
42244
|
+
return camera ?? ((_a3 = controlsRef.current) == null ? void 0 : _a3.object) ?? null;
|
|
42245
|
+
}, [camera, controlsRef]);
|
|
42246
|
+
const triggerSnap = reactExports.useCallback(
|
|
42247
|
+
(view2) => {
|
|
42248
|
+
setPressedView(view2);
|
|
42249
|
+
if (pressedTimeoutRef.current !== null) {
|
|
42250
|
+
window.clearTimeout(pressedTimeoutRef.current);
|
|
42251
|
+
}
|
|
42252
|
+
pressedTimeoutRef.current = window.setTimeout(() => {
|
|
42253
|
+
pressedTimeoutRef.current = null;
|
|
42254
|
+
setPressedView(null);
|
|
42255
|
+
}, 180);
|
|
42256
|
+
onSnap(view2);
|
|
42257
|
+
},
|
|
42258
|
+
[onSnap]
|
|
42259
|
+
);
|
|
42260
|
+
const syncCameraState = reactExports.useCallback(() => {
|
|
42261
|
+
const controls = controlsRef.current;
|
|
42262
|
+
const liveCamera = getLiveCamera();
|
|
42263
|
+
if (!liveCamera || !controls) return;
|
|
42264
|
+
setViewBasis(buildViewBasisFromLiveCamera(liveCamera));
|
|
42265
|
+
}, [controlsRef, getLiveCamera]);
|
|
42266
|
+
reactExports.useEffect(() => {
|
|
42267
|
+
let disposed = false;
|
|
42268
|
+
let retryFrame = 0;
|
|
42269
|
+
let controls = null;
|
|
42270
|
+
const attach2 = () => {
|
|
42271
|
+
if (disposed) return;
|
|
42272
|
+
controls = controlsRef.current;
|
|
42273
|
+
if (!getLiveCamera() || !controls) {
|
|
42274
|
+
retryFrame = window.requestAnimationFrame(attach2);
|
|
42275
|
+
return;
|
|
42276
|
+
}
|
|
42277
|
+
syncCameraState();
|
|
42278
|
+
controls.addEventListener("change", syncCameraState);
|
|
42279
|
+
};
|
|
42280
|
+
attach2();
|
|
42281
|
+
return () => {
|
|
42282
|
+
disposed = true;
|
|
42283
|
+
if (retryFrame) window.cancelAnimationFrame(retryFrame);
|
|
42284
|
+
controls == null ? void 0 : controls.removeEventListener("change", syncCameraState);
|
|
42285
|
+
};
|
|
42286
|
+
}, [controlsRef, getLiveCamera, syncCameraState]);
|
|
42287
|
+
reactExports.useEffect(() => {
|
|
42288
|
+
return () => {
|
|
42289
|
+
if (pressedTimeoutRef.current !== null) {
|
|
42290
|
+
window.clearTimeout(pressedTimeoutRef.current);
|
|
42291
|
+
}
|
|
42292
|
+
};
|
|
42293
|
+
}, []);
|
|
42294
|
+
const faces = reactExports.useMemo(() => {
|
|
42295
|
+
const { viewer, right, screenUp } = viewBasis ?? DEFAULT_VIEW_BASIS;
|
|
42296
|
+
const projectedFaces = FACE_DEFS.map((face) => {
|
|
42297
|
+
const visibility = face.normal.dot(viewer);
|
|
42298
|
+
if (visibility <= 0.02) return null;
|
|
42299
|
+
const points = face.corners.map((corner) => projectVector(corner, right, screenUp, viewer));
|
|
42300
|
+
const center = projectVector(face.normal, right, screenUp, viewer);
|
|
42301
|
+
const depth = points.reduce((sum, point) => sum + point.depth, 0) / points.length;
|
|
42302
|
+
return {
|
|
42303
|
+
view: face.view,
|
|
42304
|
+
label: face.label,
|
|
42305
|
+
points,
|
|
42306
|
+
center,
|
|
42307
|
+
depth,
|
|
42308
|
+
brightness: MathUtils.clamp(visibility, 0, 1)
|
|
42309
|
+
};
|
|
42310
|
+
}).filter((face) => face !== null).sort((a2, b2) => a2.depth - b2.depth);
|
|
42311
|
+
return projectedFaces;
|
|
42312
|
+
}, [viewBasis]);
|
|
42313
|
+
const handlePointerDown = reactExports.useCallback(
|
|
42314
|
+
(event) => {
|
|
42315
|
+
if (event.button !== 0) return;
|
|
42316
|
+
const liveCamera = getLiveCamera();
|
|
42317
|
+
const clickView = snapViewFromEventTarget(event.target);
|
|
42318
|
+
const controls = controlsRef.current;
|
|
42319
|
+
if (!clickView && (!liveCamera || !controls)) return;
|
|
42320
|
+
const target = (controls == null ? void 0 : controls.target.clone()) ?? new Vector3();
|
|
42321
|
+
const up = liveCamera ? normalizedOr(liveCamera.up, new Vector3(0, 0, 1)) : new Vector3(0, 0, 1);
|
|
42322
|
+
dragInfoRef.current = {
|
|
42323
|
+
pointerId: event.pointerId,
|
|
42324
|
+
startX: event.clientX,
|
|
42325
|
+
startY: event.clientY,
|
|
42326
|
+
startPosition: (liveCamera == null ? void 0 : liveCamera.position.clone()) ?? new Vector3(),
|
|
42327
|
+
target,
|
|
42328
|
+
up,
|
|
42329
|
+
right: liveCamera && controls ? cameraRightVector(liveCamera) : new Vector3(1, 0, 0),
|
|
42330
|
+
moved: false,
|
|
42331
|
+
clickView
|
|
42332
|
+
};
|
|
42333
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
42334
|
+
setDragging(true);
|
|
42335
|
+
},
|
|
42336
|
+
[controlsRef, getLiveCamera]
|
|
42337
|
+
);
|
|
42338
|
+
const handlePointerMove = reactExports.useCallback(
|
|
42339
|
+
(event) => {
|
|
42340
|
+
const drag = dragInfoRef.current;
|
|
42341
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
42342
|
+
if (Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) > 3) {
|
|
42343
|
+
drag.moved = true;
|
|
42344
|
+
}
|
|
42345
|
+
const controls = controlsRef.current;
|
|
42346
|
+
const liveCamera = getLiveCamera();
|
|
42347
|
+
if (!liveCamera || !controls) return;
|
|
42348
|
+
event.preventDefault();
|
|
42349
|
+
rotateCameraFromDrag(liveCamera, controls, drag, event.clientX, event.clientY);
|
|
42350
|
+
syncCameraState();
|
|
42351
|
+
},
|
|
42352
|
+
[controlsRef, getLiveCamera, syncCameraState]
|
|
42353
|
+
);
|
|
42354
|
+
const finishPointerDrag = reactExports.useCallback(
|
|
42355
|
+
(event) => {
|
|
42356
|
+
const drag = dragInfoRef.current;
|
|
42357
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
42358
|
+
dragInfoRef.current = null;
|
|
42359
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
42360
|
+
setDragging(false);
|
|
42361
|
+
if (!drag.moved && drag.clickView) {
|
|
42362
|
+
triggerSnap(drag.clickView);
|
|
42363
|
+
}
|
|
42364
|
+
},
|
|
42365
|
+
[triggerSnap]
|
|
42366
|
+
);
|
|
42367
|
+
const handleFaceKeyDown = reactExports.useCallback(
|
|
42368
|
+
(event, view2) => {
|
|
42369
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
42370
|
+
event.preventDefault();
|
|
42371
|
+
triggerSnap(view2);
|
|
42372
|
+
},
|
|
42373
|
+
[triggerSnap]
|
|
42374
|
+
);
|
|
42375
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42376
|
+
"div",
|
|
42377
|
+
{
|
|
42378
|
+
className: `fc-view-cube${dragging ? " dragging" : ""}`,
|
|
42379
|
+
"aria-label": "View cube",
|
|
42380
|
+
onPointerDown: handlePointerDown,
|
|
42381
|
+
onPointerMove: handlePointerMove,
|
|
42382
|
+
onPointerUp: finishPointerDrag,
|
|
42383
|
+
onPointerCancel: finishPointerDrag,
|
|
42384
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { className: "fc-view-cube-svg", viewBox: `0 0 ${CUBE_SIZE} ${CUBE_SIZE}`, role: "img", "aria-label": "Viewport orientation", children: [
|
|
42385
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("defs", { children: /* @__PURE__ */ jsxRuntimeExports.jsx("filter", { id: "fc-view-cube-shadow", x: "-20%", y: "-20%", width: "140%", height: "140%", children: /* @__PURE__ */ jsxRuntimeExports.jsx("feDropShadow", { dx: "0", dy: "2", stdDeviation: "2", floodColor: "#07101a", floodOpacity: "0.28" }) }) }),
|
|
42386
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("g", { filter: "url(#fc-view-cube-shadow)", children: faces.map((face) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42387
|
+
"g",
|
|
42388
|
+
{
|
|
42389
|
+
className: `fc-view-cube-face${pressedView === face.view ? " active" : ""}`,
|
|
42390
|
+
role: "button",
|
|
42391
|
+
tabIndex: 0,
|
|
42392
|
+
"data-view": face.view,
|
|
42393
|
+
"aria-label": `View ${face.label.toLowerCase()}`,
|
|
42394
|
+
onKeyDown: (event) => handleFaceKeyDown(event, face.view),
|
|
42395
|
+
children: [
|
|
42396
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("polygon", { points: polygonPoints(face.points), fill: faceFill(face.brightness) }),
|
|
42397
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("text", { x: face.center.x, y: face.center.y, className: "fc-view-cube-face-label", children: face.label })
|
|
42398
|
+
]
|
|
42399
|
+
},
|
|
42400
|
+
face.view
|
|
42401
|
+
)) })
|
|
42402
|
+
] })
|
|
42403
|
+
}
|
|
42404
|
+
);
|
|
42405
|
+
}
|
|
41154
42406
|
const FLAG_DEFINITIONS = {
|
|
41155
42407
|
drawMode: {
|
|
41156
42408
|
label: "Draw Mode (interactive sketch editor)",
|
|
@@ -41763,6 +43015,7 @@ function useViewportState() {
|
|
|
41763
43015
|
const projectionMode = useForgeStore((s) => s.projectionMode);
|
|
41764
43016
|
const gridEnabled = useForgeStore((s) => s.gridEnabled);
|
|
41765
43017
|
const axesVisible = useForgeStore((s) => s.axesVisible);
|
|
43018
|
+
const viewCubeVisible = useForgeStore((s) => s.viewCubeVisible);
|
|
41766
43019
|
const gridSize = useForgeStore((s) => s.gridSize);
|
|
41767
43020
|
const showPerformanceInfo = useForgeStore((s) => s.showPerformanceInfo);
|
|
41768
43021
|
const objectSettings = useForgeStore((s) => s.objectSettings);
|
|
@@ -41776,9 +43029,7 @@ function useViewportState() {
|
|
|
41776
43029
|
const clearFocusedObject = useForgeStore((s) => s.clearFocusedObject);
|
|
41777
43030
|
const objectPickSyncEnabled = useForgeStore((s) => s.objectPickSyncEnabled);
|
|
41778
43031
|
const explodeAmount = useForgeStore((s) => s.explodeAmount);
|
|
41779
|
-
const viewCommand = useForgeStore((s) => s.viewCommand);
|
|
41780
43032
|
const requestViewCommand = useForgeStore((s) => s.requestViewCommand);
|
|
41781
|
-
const clearViewCommand = useForgeStore((s) => s.clearViewCommand);
|
|
41782
43033
|
const jointValues = useForgeStore((s) => s.jointValues);
|
|
41783
43034
|
const jointAnimationClip = useForgeStore((s) => s.jointAnimationClip);
|
|
41784
43035
|
const jointAnimationProgress = useForgeStore((s) => s.jointAnimationProgress);
|
|
@@ -41912,6 +43163,10 @@ function useViewportState() {
|
|
|
41912
43163
|
}
|
|
41913
43164
|
return;
|
|
41914
43165
|
}
|
|
43166
|
+
if (obj.sdf) {
|
|
43167
|
+
if (expandBoundsByTransformedAabb(bounds, obj.sdf.bounds.min, obj.sdf.bounds.max, matrix)) hasBounds = true;
|
|
43168
|
+
return;
|
|
43169
|
+
}
|
|
41915
43170
|
if (obj.sketch) {
|
|
41916
43171
|
try {
|
|
41917
43172
|
const bb = obj.sketch.bounds();
|
|
@@ -41920,15 +43175,16 @@ function useViewportState() {
|
|
|
41920
43175
|
}
|
|
41921
43176
|
return;
|
|
41922
43177
|
}
|
|
43178
|
+
if (obj.curve3d) {
|
|
43179
|
+
const cb = obj.curve3d.bounds;
|
|
43180
|
+
if (expandBoundsByTransformedAabb(bounds, cb.min, cb.max, matrix)) hasBounds = true;
|
|
43181
|
+
return;
|
|
43182
|
+
}
|
|
41923
43183
|
if (obj.toolpath) {
|
|
41924
43184
|
const tb = obj.toolpath.bounds;
|
|
41925
43185
|
if (expandBoundsByTransformedAabb(bounds, tb.min, tb.max, matrix)) hasBounds = true;
|
|
41926
43186
|
return;
|
|
41927
43187
|
}
|
|
41928
|
-
if (obj.sdf) {
|
|
41929
|
-
const sb = obj.sdf.bounds;
|
|
41930
|
-
if (expandBoundsByTransformedAabb(bounds, sb.min, sb.max, matrix)) hasBounds = true;
|
|
41931
|
-
}
|
|
41932
43188
|
});
|
|
41933
43189
|
return hasBounds ? bounds.min.z : null;
|
|
41934
43190
|
}, [objects, objectMatrices]);
|
|
@@ -41945,6 +43201,10 @@ function useViewportState() {
|
|
|
41945
43201
|
if (expandBoundsByTransformedAabb(bounds, bb.min, bb.max, matrix)) hasBounds = true;
|
|
41946
43202
|
} catch {
|
|
41947
43203
|
}
|
|
43204
|
+
return;
|
|
43205
|
+
}
|
|
43206
|
+
if (obj.sdf) {
|
|
43207
|
+
if (expandBoundsByTransformedAabb(bounds, obj.sdf.bounds.min, obj.sdf.bounds.max, matrix)) hasBounds = true;
|
|
41948
43208
|
}
|
|
41949
43209
|
});
|
|
41950
43210
|
if (!hasBounds) return { surfaceFieldScale: 1, scanProxyGrid: null };
|
|
@@ -41972,6 +43232,10 @@ function useViewportState() {
|
|
|
41972
43232
|
}
|
|
41973
43233
|
return;
|
|
41974
43234
|
}
|
|
43235
|
+
if (obj.sdf) {
|
|
43236
|
+
if (expandBoundsByTransformedAabb(bounds, obj.sdf.bounds.min, obj.sdf.bounds.max, matrix)) hasBounds = true;
|
|
43237
|
+
return;
|
|
43238
|
+
}
|
|
41975
43239
|
if (obj.sketch) {
|
|
41976
43240
|
try {
|
|
41977
43241
|
const bb = obj.sketch.bounds();
|
|
@@ -41980,15 +43244,16 @@ function useViewportState() {
|
|
|
41980
43244
|
}
|
|
41981
43245
|
return;
|
|
41982
43246
|
}
|
|
43247
|
+
if (obj.curve3d) {
|
|
43248
|
+
const cb = obj.curve3d.bounds;
|
|
43249
|
+
if (expandBoundsByTransformedAabb(bounds, cb.min, cb.max, matrix)) hasBounds = true;
|
|
43250
|
+
return;
|
|
43251
|
+
}
|
|
41983
43252
|
if (obj.toolpath) {
|
|
41984
43253
|
const tb = obj.toolpath.bounds;
|
|
41985
43254
|
if (expandBoundsByTransformedAabb(bounds, tb.min, tb.max, matrix)) hasBounds = true;
|
|
41986
43255
|
return;
|
|
41987
43256
|
}
|
|
41988
|
-
if (obj.sdf) {
|
|
41989
|
-
const sb = obj.sdf.bounds;
|
|
41990
|
-
if (expandBoundsByTransformedAabb(bounds, sb.min, sb.max, matrix)) hasBounds = true;
|
|
41991
|
-
}
|
|
41992
43257
|
});
|
|
41993
43258
|
if (!hasBounds) return Math.max(60, gridSize * 8);
|
|
41994
43259
|
const size = new Vector3();
|
|
@@ -42008,6 +43273,10 @@ function useViewportState() {
|
|
|
42008
43273
|
}
|
|
42009
43274
|
return;
|
|
42010
43275
|
}
|
|
43276
|
+
if (obj.sdf) {
|
|
43277
|
+
if (expandBoundsByTransformedAabb(bounds, obj.sdf.bounds.min, obj.sdf.bounds.max, IDENTITY_MATRIX$2)) hasBounds = true;
|
|
43278
|
+
return;
|
|
43279
|
+
}
|
|
42011
43280
|
if (obj.sketch) {
|
|
42012
43281
|
try {
|
|
42013
43282
|
const bb = obj.sketch.bounds();
|
|
@@ -42023,15 +43292,16 @@ function useViewportState() {
|
|
|
42023
43292
|
}
|
|
42024
43293
|
return;
|
|
42025
43294
|
}
|
|
43295
|
+
if (obj.curve3d) {
|
|
43296
|
+
const cb = obj.curve3d.bounds;
|
|
43297
|
+
if (expandBoundsByTransformedAabb(bounds, cb.min, cb.max, IDENTITY_MATRIX$2)) hasBounds = true;
|
|
43298
|
+
return;
|
|
43299
|
+
}
|
|
42026
43300
|
if (obj.toolpath) {
|
|
42027
43301
|
const tb = obj.toolpath.bounds;
|
|
42028
43302
|
if (expandBoundsByTransformedAabb(bounds, tb.min, tb.max, IDENTITY_MATRIX$2)) hasBounds = true;
|
|
42029
43303
|
return;
|
|
42030
43304
|
}
|
|
42031
|
-
if (obj.sdf) {
|
|
42032
|
-
const sb = obj.sdf.bounds;
|
|
42033
|
-
if (expandBoundsByTransformedAabb(bounds, sb.min, sb.max, IDENTITY_MATRIX$2)) hasBounds = true;
|
|
42034
|
-
}
|
|
42035
43305
|
});
|
|
42036
43306
|
if (!hasBounds) return Math.max(60, gridSize * 8);
|
|
42037
43307
|
const size = new Vector3();
|
|
@@ -42104,8 +43374,7 @@ function useViewportState() {
|
|
|
42104
43374
|
]);
|
|
42105
43375
|
const rigInspectionBounds = rigInspectionOverlay.bounds;
|
|
42106
43376
|
const hasShape = objects.some((obj) => obj.shape);
|
|
42107
|
-
const
|
|
42108
|
-
const isSketchOnly = !hasShape && !hasSdf && objects.some((obj) => obj.sketch);
|
|
43377
|
+
const isSketchOnly = !hasShape && objects.some((obj) => obj.sketch);
|
|
42109
43378
|
const knownFileNames = reactExports.useMemo(() => new Set(Object.keys(files)), [files]);
|
|
42110
43379
|
const focusedObjectIdSet = reactExports.useMemo(() => new Set(focusedObjectIds), [focusedObjectIds]);
|
|
42111
43380
|
const { visibleSceneObjectCount, visibleModelTriangles } = reactExports.useMemo(() => {
|
|
@@ -42156,6 +43425,7 @@ function useViewportState() {
|
|
|
42156
43425
|
projectionMode,
|
|
42157
43426
|
gridEnabled,
|
|
42158
43427
|
axesVisible,
|
|
43428
|
+
viewCubeVisible,
|
|
42159
43429
|
gridSize,
|
|
42160
43430
|
showPerformanceInfo,
|
|
42161
43431
|
objectSettings,
|
|
@@ -42169,9 +43439,7 @@ function useViewportState() {
|
|
|
42169
43439
|
clearFocusedObject,
|
|
42170
43440
|
objectPickSyncEnabled,
|
|
42171
43441
|
explodeAmount,
|
|
42172
|
-
viewCommand,
|
|
42173
43442
|
requestViewCommand,
|
|
42174
|
-
clearViewCommand,
|
|
42175
43443
|
jointValues,
|
|
42176
43444
|
jointAnimationClip,
|
|
42177
43445
|
jointAnimationProgress,
|
|
@@ -43791,7 +45059,7 @@ function useGeometryComparison(args) {
|
|
|
43791
45059
|
class InspectWorkerClient {
|
|
43792
45060
|
constructor(workerFactory = () => new Worker(new URL(
|
|
43793
45061
|
/* @vite-ignore */
|
|
43794
|
-
"/assets/inspectWorker-
|
|
45062
|
+
"/assets/inspectWorker-D5T5VbfK.js",
|
|
43795
45063
|
import.meta.url
|
|
43796
45064
|
), { type: "module" })) {
|
|
43797
45065
|
__publicField(this, "worker", null);
|
|
@@ -44532,6 +45800,7 @@ function VoxelIntentOverlay() {
|
|
|
44532
45800
|
))
|
|
44533
45801
|
] });
|
|
44534
45802
|
}
|
|
45803
|
+
const DEFAULT_OBJECT_SETTINGS = { visible: true, opacity: 1, color: "#5b9bd5" };
|
|
44535
45804
|
function buildManualSceneConfig(base, manualScene, renderStylePreset) {
|
|
44536
45805
|
var _a3, _b2, _c;
|
|
44537
45806
|
const lights = [
|
|
@@ -44876,6 +46145,7 @@ function Viewport() {
|
|
|
44876
46145
|
projectionMode,
|
|
44877
46146
|
gridEnabled,
|
|
44878
46147
|
axesVisible,
|
|
46148
|
+
viewCubeVisible,
|
|
44879
46149
|
gridSize,
|
|
44880
46150
|
showPerformanceInfo,
|
|
44881
46151
|
objectSettings,
|
|
@@ -44888,9 +46158,7 @@ function Viewport() {
|
|
|
44888
46158
|
focusObject,
|
|
44889
46159
|
clearFocusedObject,
|
|
44890
46160
|
objectPickSyncEnabled,
|
|
44891
|
-
viewCommand,
|
|
44892
46161
|
requestViewCommand,
|
|
44893
|
-
clearViewCommand,
|
|
44894
46162
|
lengthUnit,
|
|
44895
46163
|
constructionGhost,
|
|
44896
46164
|
objects,
|
|
@@ -44943,6 +46211,7 @@ function Viewport() {
|
|
|
44943
46211
|
previewFile,
|
|
44944
46212
|
knownFileNames
|
|
44945
46213
|
} = state2;
|
|
46214
|
+
const viewportPerformanceProbeEnabled = reactExports.useMemo(() => isViewportPerformanceProbeEnabled(), []);
|
|
44946
46215
|
const voxelContextFile = previewFile ?? ((activeFile == null ? void 0 : activeFile.toLowerCase().endsWith(".forge.js")) ? activeFile : null);
|
|
44947
46216
|
const historyMode = useForgeStore((s) => s.historyMode);
|
|
44948
46217
|
const historyObjectIds = useForgeStore((s) => s.historyObjectIds);
|
|
@@ -44961,10 +46230,17 @@ function Viewport() {
|
|
|
44961
46230
|
const vRulerRef = reactExports.useRef(null);
|
|
44962
46231
|
const adaptiveGrid = useAdaptiveGrid(zoomMmPerPx, isSketchOnly && gridEnabled);
|
|
44963
46232
|
const controlsRef = reactExports.useRef(null);
|
|
46233
|
+
const [activeCanvasCamera, setActiveCanvasCamera] = reactExports.useState(null);
|
|
44964
46234
|
const initialFitRequestedRef = reactExports.useRef(false);
|
|
44965
46235
|
const prevPreviewFileRef = reactExports.useRef(void 0);
|
|
44966
46236
|
const containerRef = reactExports.useRef(null);
|
|
44967
46237
|
const contextMenuRef = reactExports.useRef(null);
|
|
46238
|
+
const handleActiveCameraChange = reactExports.useCallback((camera) => {
|
|
46239
|
+
setActiveCanvasCamera(camera);
|
|
46240
|
+
}, []);
|
|
46241
|
+
const handleCanvasCreated = reactExports.useCallback((state22) => {
|
|
46242
|
+
setActiveCanvasCamera(state22.camera);
|
|
46243
|
+
}, []);
|
|
44968
46244
|
const t2 = themes[themeName];
|
|
44969
46245
|
const renderStylePreset = reactExports.useMemo(() => getRenderStylePreset(renderStyle), [renderStyle]);
|
|
44970
46246
|
const effectiveSceneConfig = reactExports.useMemo(
|
|
@@ -44976,10 +46252,6 @@ function Viewport() {
|
|
|
44976
46252
|
const inspectChannelActive = inspectChannel !== "none";
|
|
44977
46253
|
const comparisonInspectActive = inspectChannel === "comparison";
|
|
44978
46254
|
const rigInspectActive = inspectChannel === "rig";
|
|
44979
|
-
const hasVisibleSdfObjects = objects.some((obj) => {
|
|
44980
|
-
var _a4;
|
|
44981
|
-
return obj.sdf && (((_a4 = objectSettings[obj.id]) == null ? void 0 : _a4.visible) ?? true);
|
|
44982
|
-
});
|
|
44983
46255
|
const viewportDisabledMessage = reactExports.useMemo(() => {
|
|
44984
46256
|
if (activeFile && !isModelFile(activeFile) && !isSvgActive && !meshPreviewFile) {
|
|
44985
46257
|
return {
|
|
@@ -44995,15 +46267,12 @@ function Viewport() {
|
|
|
44995
46267
|
}
|
|
44996
46268
|
return null;
|
|
44997
46269
|
}, [activeFile, isSvgActive, meshPreviewFile, objects.length, result, rigInspectActive]);
|
|
44998
|
-
const canvasDpr =
|
|
44999
|
-
|
|
45000
|
-
return [1, renderStylePreset.canvasDpr.idleMax];
|
|
45001
|
-
}, [hasVisibleSdfObjects, renderStylePreset.canvasDpr.idleMax, renderStylePreset.canvasDpr.live]);
|
|
45002
|
-
const effectiveSdfObjectSettings = reactExports.useMemo(() => {
|
|
46270
|
+
const canvasDpr = isViewportInteracting ? renderStylePreset.canvasDpr.live : [1, renderStylePreset.canvasDpr.idleMax];
|
|
46271
|
+
const sdfObjectSettings = reactExports.useMemo(() => {
|
|
45003
46272
|
const next = {};
|
|
45004
46273
|
for (const obj of objects) {
|
|
45005
46274
|
if (!obj.sdf) continue;
|
|
45006
|
-
const settings = objectSettings[obj.id] ??
|
|
46275
|
+
const settings = objectSettings[obj.id] ?? DEFAULT_OBJECT_SETTINGS;
|
|
45007
46276
|
const isDimmedByFocus = focusedObjectIdSet.size > 0 && !focusedObjectIdSet.has(obj.id);
|
|
45008
46277
|
const isDimmedByGhost = constructionGhost !== null && obj.id !== constructionGhost.objectId;
|
|
45009
46278
|
next[obj.id] = isDimmedByFocus || isDimmedByGhost ? { ...settings, opacity: Math.min(settings.opacity, FOCUS_MODE_DIM_OPACITY) } : settings;
|
|
@@ -45325,7 +46594,9 @@ function Viewport() {
|
|
|
45325
46594
|
raycaster: { params: { Line: { threshold: 0.5 } } },
|
|
45326
46595
|
camera: { up: [0, 0, 1] },
|
|
45327
46596
|
onPointerMissed: handleViewportPointerMissed,
|
|
46597
|
+
onCreated: handleCanvasCreated,
|
|
45328
46598
|
children: [
|
|
46599
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ActiveCameraBridge, { onCameraChange: handleActiveCameraChange }),
|
|
45329
46600
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(reactExports.Suspense, { fallback: null, children: [
|
|
45330
46601
|
projectionMode === "orthographic" ? /* @__PURE__ */ jsxRuntimeExports.jsx(OrthographicCamera, { makeDefault: true, position: [120, 80, 120], zoom: 2, near: -5e4, far: 5e4, up: [0, 0, 1] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(PerspectiveCamera, { makeDefault: true, position: [120, 80, 120], fov: 45, near: 0.1, far: 1e5, up: [0, 0, 1] }),
|
|
45331
46602
|
((_d = effectiveSceneConfig == null ? void 0 : effectiveSceneConfig.postProcessing) == null ? void 0 : _d.toneMappingExposure) === void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(RenderStyleExposure, { exposure: renderStylePreset.toneMappingExposure }),
|
|
@@ -45396,7 +46667,7 @@ function Viewport() {
|
|
|
45396
46667
|
}
|
|
45397
46668
|
),
|
|
45398
46669
|
objects.map((obj, objIndex) => {
|
|
45399
|
-
const settings = objectSettings[obj.id] ??
|
|
46670
|
+
const settings = objectSettings[obj.id] ?? DEFAULT_OBJECT_SETTINGS;
|
|
45400
46671
|
const isDimmedByFocus = focusedObjectIdSet.size > 0 && !focusedObjectIdSet.has(obj.id);
|
|
45401
46672
|
const isDimmedByGhost = constructionGhost !== null && obj.id !== constructionGhost.objectId;
|
|
45402
46673
|
const isDimmedByVoxel = voxelIntentMode && !!obj.shape;
|
|
@@ -45446,6 +46717,26 @@ function Viewport() {
|
|
|
45446
46717
|
obj.id
|
|
45447
46718
|
);
|
|
45448
46719
|
}
|
|
46720
|
+
if (canRenderSdfDirectly(obj.sdf)) {
|
|
46721
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
46722
|
+
SdfDirectObject,
|
|
46723
|
+
{
|
|
46724
|
+
sdf: obj.sdf,
|
|
46725
|
+
settings: sdfObjectSettings[obj.id] ?? DEFAULT_OBJECT_SETTINGS,
|
|
46726
|
+
matrix,
|
|
46727
|
+
materialProps: obj.materialProps,
|
|
46728
|
+
isHovered,
|
|
46729
|
+
clippingPlanes: objectClippingPlanes,
|
|
46730
|
+
onPointerEnter: (event) => updateHoverLabel(obj, event),
|
|
46731
|
+
onPointerMove: (event) => updateHoverLabel(obj, event),
|
|
46732
|
+
onPointerLeave: (event) => clearHoverLabel(obj, event),
|
|
46733
|
+
onClick: (event) => handleObjectClick(obj, event),
|
|
46734
|
+
onDoubleClick: (event) => handleObjectDoubleClick(obj, event),
|
|
46735
|
+
onContextMenu: (event) => handleObjectContextMenu(obj, event)
|
|
46736
|
+
},
|
|
46737
|
+
obj.id
|
|
46738
|
+
);
|
|
46739
|
+
}
|
|
45449
46740
|
if (obj.sketch) {
|
|
45450
46741
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45451
46742
|
SketchObject,
|
|
@@ -45559,7 +46850,7 @@ function Viewport() {
|
|
|
45559
46850
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45560
46851
|
PerformanceInfoSampler,
|
|
45561
46852
|
{
|
|
45562
|
-
enabled: showPerformanceInfo,
|
|
46853
|
+
enabled: showPerformanceInfo || viewportPerformanceProbeEnabled,
|
|
45563
46854
|
sceneObjects: visibleSceneObjectCount,
|
|
45564
46855
|
modelTriangles: visibleModelTriangles,
|
|
45565
46856
|
reactRenderCountRef,
|
|
@@ -45582,21 +46873,6 @@ function Viewport() {
|
|
|
45582
46873
|
infiniteGrid: true
|
|
45583
46874
|
}
|
|
45584
46875
|
),
|
|
45585
|
-
!isSketchOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45586
|
-
SdfRaymarchLayer,
|
|
45587
|
-
{
|
|
45588
|
-
objects,
|
|
45589
|
-
objectSettings: effectiveSdfObjectSettings,
|
|
45590
|
-
clippingPlanesById: objectClippingPlanesById,
|
|
45591
|
-
hoveredObjectId,
|
|
45592
|
-
onPointerEnter: (obj, event) => updateHoverLabel(obj, event),
|
|
45593
|
-
onPointerMove: (obj, event) => updateHoverLabel(obj, event),
|
|
45594
|
-
onPointerLeave: (obj, event) => clearHoverLabel(obj, event),
|
|
45595
|
-
onClick: (obj, event) => voxelIntentMode ? handleVoxelObjectSurfaceClick(event, objectMatrices[obj.id] ?? new Matrix4()) : handleObjectClick(obj, event),
|
|
45596
|
-
onDoubleClick: (obj, event) => handleObjectDoubleClick(obj, event),
|
|
45597
|
-
onContextMenu: (obj, event) => handleObjectContextMenu(obj, event)
|
|
45598
|
-
}
|
|
45599
|
-
),
|
|
45600
46876
|
axesVisible && !isSketchOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(LabeledAxes, {}),
|
|
45601
46877
|
axesVisible && isSketchOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(SketchAxes, {}),
|
|
45602
46878
|
isSketchOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(CursorTracker, { onMove: (x, y) => setCursorPos({ x, y }) }),
|
|
@@ -45639,23 +46915,24 @@ function Viewport() {
|
|
|
45639
46915
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ViewportRecordingBridge, { controlsRef }),
|
|
45640
46916
|
historyMode && /* @__PURE__ */ jsxRuntimeExports.jsx(HistoryRecorderBridge, {}),
|
|
45641
46917
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TrajectoryRecorderBridge, { controlsRef })
|
|
45642
|
-
] })
|
|
45643
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45644
|
-
ViewController,
|
|
45645
|
-
{
|
|
45646
|
-
controlsRef,
|
|
45647
|
-
command: viewCommand,
|
|
45648
|
-
objects,
|
|
45649
|
-
objectMatrices,
|
|
45650
|
-
fallbackBounds: rigInspectActive ? rigInspectionBounds : null,
|
|
45651
|
-
settings: objectSettings,
|
|
45652
|
-
focusedObjectIds,
|
|
45653
|
-
clearCommand: clearViewCommand
|
|
45654
|
-
}
|
|
45655
|
-
)
|
|
46918
|
+
] })
|
|
45656
46919
|
]
|
|
45657
46920
|
}
|
|
45658
46921
|
),
|
|
46922
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
46923
|
+
ViewController,
|
|
46924
|
+
{
|
|
46925
|
+
camera: activeCanvasCamera,
|
|
46926
|
+
controlsRef,
|
|
46927
|
+
viewportRef: containerRef,
|
|
46928
|
+
objects,
|
|
46929
|
+
objectMatrices,
|
|
46930
|
+
fallbackBounds: rigInspectActive ? rigInspectionBounds : null,
|
|
46931
|
+
settings: objectSettings,
|
|
46932
|
+
focusedObjectIds
|
|
46933
|
+
}
|
|
46934
|
+
),
|
|
46935
|
+
viewCubeVisible && !isSketchOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(ViewCube, { camera: activeCanvasCamera, controlsRef, onSnap: (view2) => requestViewCommand({ type: "snap", view: view2 }) }),
|
|
45659
46936
|
(() => {
|
|
45660
46937
|
const toolpathObj = objects.find((o2) => o2.toolpath && o2.toolpath.segments.length > 0);
|
|
45661
46938
|
if (!(toolpathObj == null ? void 0 : toolpathObj.toolpath)) return null;
|
|
@@ -46211,7 +47488,7 @@ function Viewport() {
|
|
|
46211
47488
|
}
|
|
46212
47489
|
);
|
|
46213
47490
|
}
|
|
46214
|
-
const EditorApp$1 = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-
|
|
47491
|
+
const EditorApp$1 = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-Bfd3jbtC.js"), true ? __vite__mapDeps([0]) : void 0).then((m2) => ({ default: m2.EditorApp })));
|
|
46215
47492
|
const PENDING_SHARE_COPY_KEY = "fc-pending-share-copy";
|
|
46216
47493
|
function storePendingShareCopy(shareId) {
|
|
46217
47494
|
sessionStorage.setItem(PENDING_SHARE_COPY_KEY, shareId);
|
|
@@ -46245,7 +47522,7 @@ function PublishedModelPage() {
|
|
|
46245
47522
|
checkSession();
|
|
46246
47523
|
}, [checkSession]);
|
|
46247
47524
|
reactExports.useEffect(() => {
|
|
46248
|
-
|
|
47525
|
+
initBackendForEvaluation(useForgeStore.getState().activeBackend).then(() => {
|
|
46249
47526
|
setKernelReady(true);
|
|
46250
47527
|
});
|
|
46251
47528
|
}, []);
|
|
@@ -46400,7 +47677,7 @@ const PublishedModelPage$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Objec
|
|
|
46400
47677
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
46401
47678
|
const siteUrl = "https://forgecad.io";
|
|
46402
47679
|
const defaultImage = "https://forgecad.io/brand/social-card.png";
|
|
46403
|
-
const pages$1 = [{ "path": "/", "title": "ForgeCAD - AI-Native CAD for Manufacturable Models", "description": "ForgeCAD turns prompts, JavaScript, and product ideas into editable parametric CAD with agent validation, manufacturing feedback, and STEP/STL exports.", "priority": "1.0", "changefreq": "weekly" }, { "path": "/docs", "title": "ForgeCAD Documentation - AI-Native Parametric CAD", "description": "Learn ForgeCAD's JavaScript CAD API, agent workflow, validation loop, exports, assemblies, sketches, and manufacturing-ready model checks.", "priority": "0.8", "changefreq": "weekly" }, { "path": "/docs/ai-native-cad", "title": "AI-Native CAD Workflow - ForgeCAD", "description": "How ForgeCAD supports AI-driven CAD: editable code, validation evidence, manufacturing feedback, and parallel product idea iteration.", "priority": "0.9", "changefreq": "weekly" }, { "path": "/docs/ai-usage", "title": "AI CAD Agent Setup - ForgeCAD", "description": "Set up Codex, Claude Code, OpenCode, or another agent to build, validate, inspect, export, and publish ForgeCAD models.", "priority": "0.8", "changefreq": "weekly" }, { "path": "/docs/cli", "title": "ForgeCAD CLI - Validate, Inspect, Export, Publish", "description": "Use the ForgeCAD CLI to run .forge.js models, render evidence, inspect manufacturability, export STEP/STL/3MF, and sync projects.", "priority": "0.7", "changefreq": "weekly" }, { "path": "/docs/skills/forgecad-make-a-model", "title": "AI CAD Model-Making Skill - ForgeCAD", "description": "A workflow for AI agents to create manufacture-realistic ForgeCAD models with validation, renders, inspection evidence, and exports.", "priority": "0.7", "changefreq": "weekly" }, { "path": "/benchmark", "title": "ForgeCAD Benchmark - AI CAD Model Generation", "description": "Benchmark AI agents generating real ForgeCAD models with executable code, visual evidence, validation output, and geometry checks.", "priority": "0.7", "changefreq": "weekly" }, { "path": "/blog", "title": "ForgeCAD Blog - AI-Native CAD and Manufacturing Workflows", "description": "Product notes, tutorials, examples, and technical updates on AI-native CAD, parametric modeling, validation, and manufacturing exports.", "priority": "0.6", "changefreq": "weekly" }, { "path": "/pricing", "title": "ForgeCAD Pricing", "description": "ForgeCAD plans for browser CAD, local CLI workflows, AI agent model generation, project publishing, and commercial use.", "priority": "0.5", "changefreq": "monthly" }, { "path": "/terms", "title": "ForgeCAD Terms of Service", "description": "Terms for ForgeCAD accounts, projects, hosted services, CLI use, commercial use, automation, and AI-assisted workflows.", "priority": "0.3", "changefreq": "yearly" }, { "path": "/privacy", "title": "ForgeCAD Privacy Policy", "description": "How ForgeCAD handles account, project, billing, analytics, and service data for the website, CLI, and hosted app.", "priority": "0.3", "changefreq": "yearly" }, { "path": "/license", "title": "ForgeCAD Software License", "description": "ForgeCAD license terms for Free, Pro, Enterprise, embedded, backend, local CLI, and AI workflow usage.", "priority": "0.3", "changefreq": "yearly" }];
|
|
47680
|
+
const pages$1 = [{ "path": "/", "title": "ForgeCAD - AI-Native CAD for Manufacturable Models", "description": "ForgeCAD turns prompts, JavaScript, and product ideas into editable parametric CAD with agent validation, manufacturing feedback, and STEP/STL exports.", "priority": "1.0", "changefreq": "weekly" }, { "path": "/docs", "title": "ForgeCAD Documentation - AI-Native Parametric CAD", "description": "Learn ForgeCAD's JavaScript CAD API, agent workflow, validation loop, exports, assemblies, sketches, and manufacturing-ready model checks.", "priority": "0.8", "changefreq": "weekly" }, { "path": "/docs/ai-native-cad", "title": "AI-Native CAD Workflow - ForgeCAD", "description": "How ForgeCAD supports AI-driven CAD: editable code, validation evidence, manufacturing feedback, and parallel product idea iteration.", "priority": "0.9", "changefreq": "weekly" }, { "path": "/docs/ai-usage", "title": "AI CAD Agent Setup - ForgeCAD", "description": "Set up Codex, Claude Code, OpenCode, or another agent to build, validate, inspect, export, and publish ForgeCAD models.", "priority": "0.8", "changefreq": "weekly" }, { "path": "/docs/cli", "title": "ForgeCAD CLI - Validate, Inspect, Export, Publish", "description": "Use the ForgeCAD CLI to run .forge.js models, render evidence, inspect manufacturability, export STEP/STL/3MF, and sync projects.", "priority": "0.7", "changefreq": "weekly" }, { "path": "/docs/simready-quickstart", "title": "ForgeCAD SimReady Quickstart - Source-Authored Simulation Metadata", "description": "Author ForgeCAD assemblies with physical bodies, materials, colliders, joints, controllers, offline SimReady checks, and simulator exports.", "priority": "0.8", "changefreq": "weekly" }, { "path": "/docs/simulation-workflow", "title": "ForgeCAD Simulation Workflow - MuJoCo, Gym, and Isaac Lab Handoff", "description": "Take a ForgeCAD robot through MJCF export, MuJoCo playback, Gymnasium training, force-push interaction tests, and Isaac Lab integration planning.", "priority": "0.8", "changefreq": "weekly" }, { "path": "/docs/skills/forgecad-make-a-model", "title": "AI CAD Model-Making Skill - ForgeCAD", "description": "A workflow for AI agents to create manufacture-realistic ForgeCAD models with validation, renders, inspection evidence, and exports.", "priority": "0.7", "changefreq": "weekly" }, { "path": "/benchmark", "title": "ForgeCAD Benchmark - AI CAD Model Generation", "description": "Benchmark AI agents generating real ForgeCAD models with executable code, visual evidence, validation output, and geometry checks.", "priority": "0.7", "changefreq": "weekly" }, { "path": "/blog", "title": "ForgeCAD Blog - AI-Native CAD and Manufacturing Workflows", "description": "Product notes, tutorials, examples, and technical updates on AI-native CAD, parametric modeling, validation, and manufacturing exports.", "priority": "0.6", "changefreq": "weekly" }, { "path": "/pricing", "title": "ForgeCAD Pricing", "description": "ForgeCAD plans for browser CAD, local CLI workflows, AI agent model generation, project publishing, and commercial use.", "priority": "0.5", "changefreq": "monthly" }, { "path": "/terms", "title": "ForgeCAD Terms of Service", "description": "Terms for ForgeCAD accounts, projects, hosted services, CLI use, commercial use, automation, and AI-assisted workflows.", "priority": "0.3", "changefreq": "yearly" }, { "path": "/privacy", "title": "ForgeCAD Privacy Policy", "description": "How ForgeCAD handles account, project, billing, analytics, and service data for the website, CLI, and hosted app.", "priority": "0.3", "changefreq": "yearly" }, { "path": "/license", "title": "ForgeCAD Software License", "description": "ForgeCAD license terms for Free, Pro, Enterprise, embedded, backend, local CLI, and AI workflow usage.", "priority": "0.3", "changefreq": "yearly" }];
|
|
46404
47681
|
const seoConfig = {
|
|
46405
47682
|
siteUrl,
|
|
46406
47683
|
defaultImage,
|
|
@@ -46477,17 +47754,17 @@ function SeoMetadata() {
|
|
|
46477
47754
|
}, [location.pathname]);
|
|
46478
47755
|
return null;
|
|
46479
47756
|
}
|
|
46480
|
-
reactExports.lazy(() => __vitePreload(() => import("./LandingPageProofDriven-
|
|
46481
|
-
const DocsPage = reactExports.lazy(() => __vitePreload(() => import("./DocsPage-
|
|
46482
|
-
reactExports.lazy(() => __vitePreload(() => import("./BlogPage-
|
|
46483
|
-
reactExports.lazy(() => __vitePreload(() => import("./BenchmarkPage-
|
|
46484
|
-
reactExports.lazy(() => __vitePreload(() => import("./AdminPage-
|
|
47757
|
+
reactExports.lazy(() => __vitePreload(() => import("./LandingPageProofDriven-DbN7o-Be.js"), true ? __vite__mapDeps([1]) : void 0).then((m2) => ({ default: m2.LandingPageProofDriven })));
|
|
47758
|
+
const DocsPage = reactExports.lazy(() => __vitePreload(() => import("./DocsPage-DsvdiRNK.js"), true ? [] : void 0).then((m2) => ({ default: m2.DocsPage })));
|
|
47759
|
+
reactExports.lazy(() => __vitePreload(() => import("./BlogPage-BssBbnb-.js"), true ? [] : void 0).then((m2) => ({ default: m2.BlogPage })));
|
|
47760
|
+
reactExports.lazy(() => __vitePreload(() => import("./BenchmarkPage-BcRT5iGN.js"), true ? __vite__mapDeps([1,2]) : void 0).then((m2) => ({ default: m2.BenchmarkPage })));
|
|
47761
|
+
reactExports.lazy(() => __vitePreload(() => import("./AdminPage-CHY6ZN-p.js"), true ? [] : void 0).then((m2) => ({ default: m2.AdminPage })));
|
|
46485
47762
|
reactExports.lazy(() => __vitePreload(() => Promise.resolve().then(() => PublishedModelPage$1), true ? void 0 : void 0).then((m2) => ({ default: m2.PublishedModelPage })));
|
|
46486
|
-
reactExports.lazy(() => __vitePreload(() => import("./SettingsPage-
|
|
46487
|
-
reactExports.lazy(() => __vitePreload(() => import("./PricingPage-
|
|
46488
|
-
reactExports.lazy(() => __vitePreload(() => import("./LegalPage-
|
|
46489
|
-
const EditorApp = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-
|
|
46490
|
-
const EmbedViewer = reactExports.lazy(() => __vitePreload(() => import("./EmbedViewer-
|
|
47763
|
+
reactExports.lazy(() => __vitePreload(() => import("./SettingsPage-DZlyu4d4.js"), true ? [] : void 0).then((m2) => ({ default: m2.SettingsPage })));
|
|
47764
|
+
reactExports.lazy(() => __vitePreload(() => import("./PricingPage-Nczr3pRz.js"), true ? __vite__mapDeps([1,3]) : void 0).then((m2) => ({ default: m2.PricingPage })));
|
|
47765
|
+
reactExports.lazy(() => __vitePreload(() => import("./LegalPage-DNGrrY0p.js"), true ? __vite__mapDeps([1,4]) : void 0).then((m2) => ({ default: m2.LegalPage })));
|
|
47766
|
+
const EditorApp = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-Bfd3jbtC.js"), true ? __vite__mapDeps([0]) : void 0).then((m2) => ({ default: m2.EditorApp })));
|
|
47767
|
+
const EmbedViewer = reactExports.lazy(() => __vitePreload(() => import("./EmbedViewer-D5t8WamV.js"), true ? [] : void 0).then((m2) => ({ default: m2.EmbedViewer })));
|
|
46491
47768
|
const embedMode = isEmbedMode() && !window.location.pathname.startsWith("/m/");
|
|
46492
47769
|
const EDITABLE_CRASH_FILE = /\.(?:forge\.js|[cm]?[jt]sx?|json|md|txt|svg|dxf)$/i;
|
|
46493
47770
|
function firstMeaningfulLine(text) {
|
|
@@ -46745,11 +48022,12 @@ export {
|
|
|
46745
48022
|
hasProjectTextFileExtension as a6,
|
|
46746
48023
|
expandBoundsByTransformedAabb as a7,
|
|
46747
48024
|
Canvas as a8,
|
|
46748
|
-
|
|
46749
|
-
|
|
46750
|
-
|
|
46751
|
-
|
|
46752
|
-
|
|
48025
|
+
ActiveCameraBridge as a9,
|
|
48026
|
+
storePendingShareCopy as aA,
|
|
48027
|
+
buildShareUrl as aB,
|
|
48028
|
+
share as aC,
|
|
48029
|
+
PerspectiveCamera as aa,
|
|
48030
|
+
ControlsInteractionBridge as ab,
|
|
46753
48031
|
SceneConfigurator as ac,
|
|
46754
48032
|
LocalEnvironment as ad,
|
|
46755
48033
|
ForgeObject as ae,
|
|
@@ -46758,22 +48036,22 @@ export {
|
|
|
46758
48036
|
OrbitControls2 as ah,
|
|
46759
48037
|
TOUCH_GESTURES_3D as ai,
|
|
46760
48038
|
MOUSE_BUTTONS_3D as aj,
|
|
46761
|
-
|
|
46762
|
-
|
|
46763
|
-
|
|
46764
|
-
|
|
46765
|
-
|
|
46766
|
-
|
|
46767
|
-
|
|
46768
|
-
|
|
46769
|
-
|
|
46770
|
-
|
|
46771
|
-
|
|
46772
|
-
|
|
46773
|
-
|
|
46774
|
-
|
|
46775
|
-
|
|
46776
|
-
|
|
48039
|
+
ViewController as ak,
|
|
48040
|
+
ModelJourneyBar as al,
|
|
48041
|
+
useJointAnimationLoop as am,
|
|
48042
|
+
computeJointNodeMatrices as an,
|
|
48043
|
+
computeObjectJointMatrices as ao,
|
|
48044
|
+
readLastActiveFileForUser as ap,
|
|
48045
|
+
ToastContainer as aq,
|
|
48046
|
+
isMobile as ar,
|
|
48047
|
+
useFeatureFlag as as,
|
|
48048
|
+
decodeSharedHash as at,
|
|
48049
|
+
decodeSharedBundle as au,
|
|
48050
|
+
getExternalUrl as av,
|
|
48051
|
+
getGistId as aw,
|
|
48052
|
+
Viewport as ax,
|
|
48053
|
+
shouldBlockBrowserShortcut as ay,
|
|
48054
|
+
useDrawStore as az,
|
|
46777
48055
|
authFetch as b,
|
|
46778
48056
|
authApi as c,
|
|
46779
48057
|
showToast as d,
|