forgecad 0.9.15 → 0.10.0
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-CDyGUinA.js → AdminPage-DwYHz72L.js} +1 -1
- package/dist/assets/{BenchmarkPage-DfPMY_-d.js → BenchmarkPage-a9_f-1US.js} +1 -1
- package/dist/assets/{BlogPage-kF0fkdJT.js → BlogPage-DodHpvmf.js} +1 -1
- package/dist/assets/{DocsPage-B954L3YN.js → DocsPage-B5LePEuj.js} +8 -858
- package/dist/assets/{EditorApp-CuDLxKqL.css → EditorApp-BpjZgzk0.css} +148 -0
- package/dist/assets/EditorApp-QXsAISLR.js +16307 -0
- package/dist/assets/{EmbedViewer-C77B-TrF.js → EmbedViewer-DdEHGUMU.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-Cr6fXMDj.js → LandingPageProofDriven-yhhOodbf.js} +2 -2
- package/dist/assets/{LegalPage-Dzklqmmg.js → LegalPage-5RbKRGYK.js} +1 -1
- package/dist/assets/{PricingPage-zWXkvlwl.js → PricingPage-E3Rma7aV.js} +1 -1
- package/dist/assets/{SettingsPage-Bz0of4KQ.js → SettingsPage-BJZcM97j.js} +1 -1
- package/dist/assets/{app-D3kDkggg.js → app-DSYrDg0V.js} +1846 -352
- package/dist/assets/cli/{render-DSY3mMQa.js → render-ZMHR9HkV.js} +161 -70
- package/dist/assets/{constructionHistoryWorker-gpDo-uH2.js → constructionHistoryWorker-AwMMWSxg.js} +1104 -349
- package/dist/assets/{evalWorker-CU0Ke6DP.js → evalWorker-DbNs7Dkp.js} +5155 -3772
- package/dist/assets/{inspectWorker-COyp8XXA.js → inspectWorker-CZsCFtQT.js} +1415 -439
- package/dist/assets/{targets-B9sGB5nB.js → jointPose-DO6mnXn_.js} +71 -3
- package/dist/assets/{manifold-DNkrUWpA.js → manifold-BGlQBBH9.js} +1 -1
- package/dist/assets/{manifold-BRI5prcH.js → manifold-BU-tJwQh.js} +1 -1
- package/dist/assets/{manifold-C-3h2M7p.js → manifold-fy2MV7K1.js} +2 -2
- package/dist/assets/{reportWorker-CdBz5bNg.js → reportWorker-DO6hcQbh.js} +8474 -4549
- package/dist/assets/{scalar-sampling-budget-wJF98aY9.js → scalar-sampling-budget-o90NSNmF.js} +5347 -3906
- package/dist/assets/{scanProxyWorker-B-9VbLIs.js → scanProxyWorker-2GtDLk-R.js} +19 -6
- package/dist/assets/{javascript-1kQXfVaz.js → typescript-DBQ6RN5l.js} +874 -22
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +3 -3
- package/dist/docs-raw/AI/usage.md +3 -1
- package/dist/docs-raw/CLI.md +65 -239
- package/dist/docs-raw/README.md +6 -0
- package/dist/docs-raw/component-model.md +17 -150
- package/dist/docs-raw/generated/assembly.md +159 -520
- package/dist/docs-raw/generated/concepts.md +245 -3491
- package/dist/docs-raw/generated/core.md +277 -1251
- package/dist/docs-raw/generated/curves.md +387 -1608
- package/dist/docs-raw/generated/legacy.md +162 -0
- package/dist/docs-raw/generated/lib.md +238 -112
- package/dist/docs-raw/generated/output.md +51 -76
- package/dist/docs-raw/generated/runtime-names.md +30 -22
- package/dist/docs-raw/generated/sdf.md +68 -284
- package/dist/docs-raw/generated/sheet-metal.md +68 -335
- package/dist/docs-raw/generated/sketch.md +240 -1161
- package/dist/docs-raw/generated/viewport.md +75 -316
- package/dist/docs-raw/generated/wood.md +21 -49
- package/dist/docs-raw/guides/coordinate-system.md +4 -42
- package/dist/docs-raw/guides/inspection-bundles.md +44 -442
- package/dist/docs-raw/guides/joint-design.md +18 -79
- package/dist/docs-raw/guides/positioning.md +21 -143
- package/dist/docs-raw/guides/scene-presentation.md +89 -0
- package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +25 -111
- package/dist/docs-raw/skills/forgecad-blockout-model.md +20 -117
- package/dist/docs-raw/skills/forgecad-component-model.md +23 -107
- package/dist/docs-raw/skills/forgecad-high-level-spec.md +47 -155
- package/dist/docs-raw/skills/forgecad-image-replicator.md +26 -143
- package/dist/docs-raw/skills/forgecad-lld.md +19 -113
- package/dist/docs-raw/skills/forgecad-make-a-model.md +113 -532
- package/dist/docs-raw/skills/forgecad-model-grader.md +38 -108
- package/dist/docs-raw/skills/forgecad-prepare-prompt.md +24 -211
- package/dist/docs-raw/skills/forgecad-project.md +13 -129
- package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +42 -134
- package/dist/docs-raw/skills/forgecad-render-inspect.md +27 -174
- package/dist/docs-raw/skills/forgecad-visual-spec.md +32 -112
- package/dist/docs-raw/skills/forgecad.md +19 -18
- package/dist/docs-raw/skills/index.md +2 -0
- package/dist/docs-raw/welcome.md +4 -2
- package/dist/index.html +1 -1
- package/dist/llms.txt +1 -2
- package/dist/sitemap.xml +13 -13
- package/dist-cli/{check-compiler-SDX5QIXI.js → check-compiler-JTVBITCR.js} +1 -1
- package/dist-cli/{check-query-propagation-EAYEFT77.js → check-query-propagation-3FFLSMVN.js} +1 -1
- package/dist-cli/{chunk-N4O47JLF.js → chunk-OAN5T4XD.js} +5722 -4287
- package/dist-cli/forgecad.js +2195 -656
- package/dist-skill/CONTEXT.md +1778 -7912
- package/dist-skill/SKILL.md +15 -15
- package/dist-skill/docs/API/core/concepts.md +27 -157
- package/dist-skill/docs/CLI.md +65 -239
- package/dist-skill/docs/generated/assembly.md +160 -493
- package/dist-skill/docs/generated/core.md +277 -1251
- package/dist-skill/docs/generated/curves.md +387 -1609
- package/dist-skill/docs/generated/lib.md +238 -112
- package/dist-skill/docs/generated/output.md +51 -76
- package/dist-skill/docs/generated/runtime-names.md +16 -22
- package/dist-skill/docs/generated/sdf.md +68 -284
- package/dist-skill/docs/generated/sheet-metal.md +68 -335
- package/dist-skill/docs/generated/sketch.md +240 -1160
- package/dist-skill/docs/generated/viewport.md +75 -223
- package/dist-skill/docs/generated/wood.md +21 -49
- package/dist-skill/docs/guides/coordinate-system.md +4 -42
- package/dist-skill/docs/guides/inspection-bundles.md +44 -442
- package/dist-skill/docs/guides/joint-design.md +18 -79
- package/dist-skill/docs/guides/positioning.md +21 -143
- package/dist-skill/docs/guides/scene-presentation.md +89 -0
- package/dist-skill/docs/guides/surface-members.md +26 -0
- package/dist-skill/library/forgecad-3d-reconstruction/SKILL.md +23 -111
- package/dist-skill/library/forgecad-blockout-model/SKILL.md +18 -117
- package/dist-skill/library/forgecad-component-model/SKILL.md +21 -107
- package/dist-skill/library/forgecad-high-level-spec/SKILL.md +45 -155
- package/dist-skill/library/forgecad-image-replicator/SKILL.md +24 -143
- package/dist-skill/library/forgecad-lld/SKILL.md +17 -113
- package/dist-skill/library/forgecad-make-a-model/SKILL.md +111 -532
- package/dist-skill/library/forgecad-model-grader/SKILL.md +36 -108
- package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +35 -224
- package/dist-skill/library/forgecad-prepare-prompt/references/default-profiles.md +43 -271
- package/dist-skill/library/forgecad-prepare-prompt/references/master-prompt.md +30 -99
- package/dist-skill/library/forgecad-project/SKILL.md +13 -131
- package/dist-skill/library/forgecad-reconstruction-benchmark/SKILL.md +29 -123
- package/dist-skill/library/forgecad-render-inspect/SKILL.md +25 -174
- package/dist-skill/library/forgecad-visual-spec/SKILL.md +30 -111
- package/dist-skill/website/skills/forgecad-3d-reconstruction.md +58 -0
- package/dist-skill/website/skills/forgecad-blockout-model.md +49 -0
- package/dist-skill/website/skills/forgecad-component-model.md +53 -0
- package/dist-skill/website/skills/forgecad-high-level-spec.md +101 -0
- package/dist-skill/website/skills/forgecad-image-replicator.md +63 -0
- package/dist-skill/website/skills/forgecad-lld.md +41 -0
- package/dist-skill/website/skills/forgecad-make-a-model.md +186 -0
- package/dist-skill/website/skills/forgecad-model-grader.md +82 -0
- package/dist-skill/website/skills/forgecad-prepare-prompt.md +63 -0
- package/dist-skill/website/skills/forgecad-project.md +26 -0
- package/dist-skill/website/skills/forgecad-reconstruction-benchmark.md +60 -0
- package/dist-skill/website/skills/forgecad-render-inspect.md +80 -0
- package/dist-skill/website/skills/forgecad-visual-spec.md +71 -0
- package/dist-skill/website/skills/forgecad.md +122 -0
- package/dist-skill/website/skills/index.md +26 -0
- package/examples/api/comparison-imported-sphere-candidate.forge.js +1 -1
- package/examples/api/conformal-product-ribbon.forge.js +1 -1
- package/examples/api/exact-sheet-shell-assembly.forge.js +1 -1
- package/examples/api/extrude-options.forge.js +4 -2
- package/examples/api/field-loft-drive-tip.forge.js +40 -0
- package/examples/api/guided-loft-olive-oil-bottle.forge.js +1 -1
- package/examples/api/helix-basics.forge.js +2 -2
- package/examples/api/highlight-debug.forge.js +10 -10
- package/examples/api/mesh-import-slats.forge.js +1 -1
- package/examples/api/real-product-curves.forge.js +1 -1
- package/examples/api/route3d-elbow.forge.js +3 -0
- package/examples/api/sculpt-box-circle-booleans.forge.js +1 -1
- package/examples/api/sdf-shapes.forge.js +2 -5
- package/examples/api/sketch-rounding-strategies.forge.js +6 -6
- package/examples/api/surface-member-bottle-cage.forge.js +3 -3
- package/examples/api/surface-member-conformal-product-ribbon.forge.js +3 -3
- package/examples/api/surface-member-razor-inlay.forge.js +1 -1
- package/examples/api/variable-sweep-test.forge.js +4 -2
- package/examples/mechanical/airplane-propeller.forge.js +74 -39
- package/examples/nurbs-surface.forge.js +1 -1
- package/examples/products/iphone.forge.js +1 -1
- package/package.json +4 -1
- package/dist/assets/EditorApp-Beb-IZ0y.js +0 -14014
- package/dist/docs-raw/guides/geometry-conventions.md +0 -52
- package/dist/docs-raw/guides/modeling-recipes.md +0 -78
- package/dist-skill/docs/guides/geometry-conventions.md +0 -52
- package/dist-skill/docs/guides/modeling-recipes.md +0 -78
- package/dist-skill/library/forgecad-visual-spec/references/prompt-template.md +0 -79
- package/examples/api/bolted-service-cover.forge.js +0 -17
- package/examples/api/cable-gland-anchor.forge.js +0 -14
- package/examples/api/captured-cartridge-guide.forge.js +0 -14
- package/examples/api/captured-linear-slide.forge.js +0 -13
- package/examples/api/clevis-pin-joint.forge.js +0 -13
- package/examples/api/datum-enclosure.forge.js +0 -16
- package/examples/api/hose-barb-port.forge.js +0 -14
- package/examples/api/knuckled-hinge-assembly.forge.js +0 -15
- package/examples/api/living-hinge-cover.forge.js +0 -14
- package/examples/api/pcb-terminal-block.forge.js +0 -22
- package/examples/api/pinned-lever-pivot-stack.forge.js +0 -14
- package/examples/api/retained-shaft-knob-stack.forge.js +0 -15
- package/examples/api/routed-tube-clip.forge.js +0 -15
- package/examples/api/seated-bearing-stack.forge.js +0 -30
- package/examples/api/snap-latch-cover.forge.js +0 -14
- package/examples/api/thumb-screw-clamp.forge.js +0 -15
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/EditorApp-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/EditorApp-BpjZgzk0.css","assets/landing-proof-driven-ORyigZ6p.css","assets/BenchmarkPage-BAbsyMaF.css","assets/PricingPage-BPF6HKyO.css","assets/LegalPage-BRlScr9A.css"])))=>i.map(i=>d[i]);
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
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, S as Scene, a as PCFSoftShadowMap, V as VSMShadowMap, b as PCFShadowMap, B as BasicShadowMap, C as ColorManagement, L as LinearSRGBColorSpace, c as SRGBColorSpace, N as NoToneMapping, A as ACESFilmicToneMapping, d as Layers, e as Color, f as RGBAFormat, U as UnsignedByteType, g as Vector3, h as Vector2, i as Clock, T as THREE, D as DoubleSide, j as REVISION, M as Mesh, I as IcosahedronGeometry, k as ShaderMaterial, l as Spherical, Q as Quaternion, m as MOUSE, n as TOUCH, o as Ray, p as Plane, q as DataTextureLoader, H as HalfFloatType, F as FloatType, r as DataUtils, t as LinearFilter, u as RedFormat, v as InstancedBufferGeometry, w as Float32BufferAttribute, x as InstancedInterleavedBuffer, y as InterleavedBufferAttribute, E as WireframeGeometry, G as Box3, J as Sphere, K as UniformsUtils, X as UniformsLib, Y as Vector4, Z as Line3, $ as Matrix4, a0 as MathUtils, a1 as Uniform, a2 as WebGLRenderTarget, a3 as DepthTexture, a4 as BackSide, a5 as ClampToEdgeWrapping, a6 as PlaneGeometry, a7 as UVMapping, a8 as DataTexture, a9 as Texture, aa as MeshBasicMaterial, ab as IntType, ac as ShortType, ad as ByteType, ae as UnsignedIntType, af as Loader, ag as LoadingManager, ah as LinearMipMapLinearFilter, ai as FileLoader, aj as NoBlending, ak as CubeReflectionMapping, al as EquirectangularReflectionMapping, am as CubeTextureLoader, an as WebGLCubeRenderTarget, ao as ConstraintSketch, ap as setSketchPlacement3D, aq as Sketch, ar as PROFILE_BACKEND_MARKER, as as FrozenShape, at as setShapeCompilePlan, au as hasAnyPorts, av as setShapePortsInternal, aw as markShapePortsUsed, ax as setParamOverrides, ay as resolveForgeRenderStyle, az as DEFAULT_ACTIVE_BACKEND, aA as isConstraintSketch, aB as updateConstraintValue, aC as linearizeMultiObjectSteps, aD as getShapeCompilePlan, aE as SCAN_PROXY_GRANULARITY_DEFAULT, aF as publishSolverWasmRunDebug, aG as resolveForgeQualityPreset, aH as SCAN_PROXY_MATRIX_GRANULARITY_MAX, aI as findJointAnimationClip, aJ as resolveJointAnimation, aK as resolveJointViewValues, aL as resolveImportPath, aM as resolveScanProxyGranularity, aN as BufferGeometry, aO as LineBasicMaterial, aP as Line$1, aQ as LineDashedMaterial, aR as DepthStencilFormat, aS as UnsignedInt248Type, aT as MeshNormalMaterial, aU as NearestFilter, aV as BasicDepthPacking, aW as EventDispatcher$1, aX as NoColorSpace, aY as FrontSide, aZ as Material, a_ as AlwaysDepth, a$ as BufferAttribute, b0 as CanvasTexture, b1 as Object3D, b2 as FogExp2, b3 as Fog, b4 as AmbientLight, b5 as HemisphereLight, b6 as SpotLight, b7 as PointLight, b8 as DirectionalLight, b9 as heatPointsForSide, ba as COMPARISON_COLORS, bb as comparisonHeatDepthTest, bc as comparisonHeatEdgeOpacity, bd as comparisonHeatPatchOpacity, be as shapeToGeometry, bf as buildComparisonHeatPatchGeometry, bg as EdgesGeometry, bh as buildShapeFromCompilePlan, bi as buildVisibleHistoryStacks, bj as sketchToSvg, bk as sketchToDxf, bl as runScript, bm as MeshPhysicalMaterial, bn as LineSegments, bo as findDesignTraceNodeForConstructionStep, bp as formatDesignTraceAnchor, bq as waitForAnimationFrame, br as selectBuildLedgerNodes, bs as worldAuthorPlaneToLocal, bt as scanProxySourceBytes, bu as disposeScanProxyGeometry, bv as scanProxyGeometryFromPayload, bw as AdditiveBlending, bx as geometryWithVisibleVertexColors, by as getRenderStylePreset, bz as SCAN_RENDER_COLORS, bA as NormalBlending, bB as
|
|
7
|
+
import { _ as __vitePreload, z as zipSync, s as strToU8, W as WebGLRenderer, R as Raycaster, O as OrthographicCamera$1, P as PerspectiveCamera$1, S as Scene, a as PCFSoftShadowMap, V as VSMShadowMap, b as PCFShadowMap, B as BasicShadowMap, C as ColorManagement, L as LinearSRGBColorSpace, c as SRGBColorSpace, N as NoToneMapping, A as ACESFilmicToneMapping, d as Layers, e as Color, f as RGBAFormat, U as UnsignedByteType, g as Vector3, h as Vector2, i as Clock, T as THREE, D as DoubleSide, j as REVISION, M as Mesh, I as IcosahedronGeometry, k as ShaderMaterial, l as Spherical, Q as Quaternion, m as MOUSE, n as TOUCH, o as Ray, p as Plane, q as DataTextureLoader, H as HalfFloatType, F as FloatType, r as DataUtils, t as LinearFilter, u as RedFormat, v as InstancedBufferGeometry, w as Float32BufferAttribute, x as InstancedInterleavedBuffer, y as InterleavedBufferAttribute, E as WireframeGeometry, G as Box3, J as Sphere, K as UniformsUtils, X as UniformsLib, Y as Vector4, Z as Line3, $ as Matrix4, a0 as MathUtils, a1 as Uniform, a2 as WebGLRenderTarget, a3 as DepthTexture, a4 as BackSide, a5 as ClampToEdgeWrapping, a6 as PlaneGeometry, a7 as UVMapping, a8 as DataTexture, a9 as Texture, aa as MeshBasicMaterial, ab as IntType, ac as ShortType, ad as ByteType, ae as UnsignedIntType, af as Loader, ag as LoadingManager, ah as LinearMipMapLinearFilter, ai as FileLoader, aj as NoBlending, ak as CubeReflectionMapping, al as EquirectangularReflectionMapping, am as CubeTextureLoader, an as WebGLCubeRenderTarget, ao as ConstraintSketch, ap as setSketchPlacement3D, aq as Sketch, ar as PROFILE_BACKEND_MARKER, as as FrozenShape, at as setShapeCompilePlan, au as hasAnyPorts, av as setShapePortsInternal, aw as markShapePortsUsed, ax as setParamOverrides, ay as resolveForgeRenderStyle, az as DEFAULT_ACTIVE_BACKEND, aA as isConstraintSketch, aB as updateConstraintValue, aC as linearizeMultiObjectSteps, aD as getShapeCompilePlan, aE as SCAN_PROXY_GRANULARITY_DEFAULT, aF as publishSolverWasmRunDebug, aG as resolveForgeQualityPreset, aH as SCAN_PROXY_MATRIX_GRANULARITY_MAX, aI as findJointAnimationClip, aJ as resolveJointAnimation, aK as resolveJointViewValues, aL as resolveImportPath, aM as resolveScanProxyGranularity, aN as BufferGeometry, aO as LineBasicMaterial, aP as Line$1, aQ as LineDashedMaterial, aR as DepthStencilFormat, aS as UnsignedInt248Type, aT as MeshNormalMaterial, aU as NearestFilter, aV as BasicDepthPacking, aW as EventDispatcher$1, aX as NoColorSpace, aY as FrontSide, aZ as Material, a_ as AlwaysDepth, a$ as BufferAttribute, b0 as CanvasTexture, b1 as Object3D, b2 as FogExp2, b3 as Fog, b4 as AmbientLight, b5 as HemisphereLight, b6 as SpotLight, b7 as PointLight, b8 as DirectionalLight, b9 as heatPointsForSide, ba as COMPARISON_COLORS, bb as comparisonHeatDepthTest, bc as comparisonHeatEdgeOpacity, bd as comparisonHeatPatchOpacity, be as shapeToGeometry, bf as buildComparisonHeatPatchGeometry, bg as EdgesGeometry, bh as buildShapeFromCompilePlan, bi as buildVisibleHistoryStacks, bj as sketchToSvg, bk as sketchToDxf, bl as runScript, bm as MeshPhysicalMaterial, bn as LineSegments, bo as findDesignTraceNodeForConstructionStep, bp as formatDesignTraceAnchor, bq as waitForAnimationFrame, br as selectBuildLedgerNodes, bs as worldAuthorPlaneToLocal, bt as scanProxySourceBytes, bu as disposeScanProxyGeometry, bv as scanProxyGeometryFromPayload, bw as AdditiveBlending, bx as geometryWithVisibleVertexColors, by as getRenderStylePreset, bz as SCAN_RENDER_COLORS, bA as NormalBlending, bB as scanMaterialShellColor, bC as ZEBRA_STRIPE_SOFTNESS, bD as ZEBRA_STRIPE_SCALE, bE as ZEBRA_LIGHT_COLOR, bF as ZEBRA_DARK_COLOR, bG as ZEBRA_ACCENT_COLOR, bH as ZEBRA_STRIPE_FRAGMENT_SHADER, bI as ZEBRA_STRIPE_VERTEX_SHADER, bJ as SCAN_PROXY_LAYER_STYLES, bK as scanMaterialLayerStyles, bL as SURFACE_FIELD_FRAGMENT_SHADER, bM as SURFACE_FIELD_VERTEX_SHADER, bN as WORLD_UP, bO as CatmullRomCurve3, bP as TubeGeometry, bQ as THICKNESS_GRADIENT_COLORS, bR as ROUGHNESS_COLORS, bS as DEFAULT_ROUGHNESS_INSPECTION_OPTIONS, bT as PERFORMANCE_SAMPLE_INTERVAL_SEC, bU as formatPerformanceCount, bV as NON_TEXT_INPUT_TYPES, bW as MeshStandardMaterial, bX as compileSdfNode3, bY as buildSdfRaymarchFragmentShader, bZ as SDF_RAYMARCH_PROXY_VERTEX_SHADER, b_ as Shape, b$ as ShapeGeometry, c0 as ShaderLib, c1 as CylinderGeometry, c2 as VIEWPORT_CAMERA_STORAGE_KEY, c3 as parseViewportCameraState, c4 as createResolvedExplodeConfig, c5 as explodeBoundsCenter, c6 as explodeMergeBounds, c7 as resolveExplodeDirective, c8 as computeExplodeMotion, c9 as getSketchWorldMatrix, ca as explodeAdd, cb as hasExplodeOverride, cc as resolveExplodeLocalFanDirection, cd as explodeMul, ce as explodeLeafFanStage, cf as normalizeCutPlane, cg as toClippingPlane, ch as isObjectExcludedFromCutPlane, ci as getShapePorts, cj as getShapeUsedPorts, ck as DEFAULT_VIEW_CONFIG, cl as SECTION_EXPLORER_PLANE_NAME, cm as ZERO_OFFSET, cn as scanProxyGridForBounds, co as IDENTITY_MATRIX$2, cp as OBJECT_CONTEXT_MENU_MARGIN, cq as OBJECT_CONTEXT_MENU_WIDTH, cr as OBJECT_CONTEXT_MENU_HEIGHT, cs as getKernelFaceNameForTriangle, ct as triangleSoupFromMeshes, cu as compareTriangleSoups, cv as buildGeometryComparisonPointCloud, cw as aabbOverlaps, cx as aabbOverlapVolume, cy as DEFAULT_COLLISION_INSPECTION_OPTIONS, cz as resolveScalarSceneSampleBudget, cA as FOCUS_MODE_DIM_OPACITY, cB as comparisonCandidateContextOpacity, cC as Matrix3, cD as initKernel, cE as initSolverWasm } from "./scalar-sampling-budget-o90NSNmF.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];
|
|
@@ -39,7 +39,15 @@ async function doFetch(url2, init) {
|
|
|
39
39
|
credentials: "include",
|
|
40
40
|
headers: { ...headers, ...init == null ? void 0 : init.headers }
|
|
41
41
|
});
|
|
42
|
-
const
|
|
42
|
+
const text = await res.text();
|
|
43
|
+
let body = {};
|
|
44
|
+
if (text.trim().length > 0) {
|
|
45
|
+
try {
|
|
46
|
+
body = JSON.parse(text);
|
|
47
|
+
} catch {
|
|
48
|
+
body = { error: text };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
43
51
|
return { res, body };
|
|
44
52
|
}
|
|
45
53
|
async function authFetch(url2, init) {
|
|
@@ -288,6 +296,8 @@ function lookupCache(filePath, code, files, paramOverrides, assemblyState, quali
|
|
|
288
296
|
if (!entry) return null;
|
|
289
297
|
if (entry.code !== code || entry.quality !== quality || JSON.stringify(entry.paramOverrides) !== JSON.stringify(paramOverrides) || JSON.stringify(entry.assemblyState) !== JSON.stringify(assemblyState) || JSON.stringify(entry.files) !== JSON.stringify(files))
|
|
290
298
|
return null;
|
|
299
|
+
runResultCache.delete(key);
|
|
300
|
+
runResultCache.set(key, entry);
|
|
291
301
|
return entry.result;
|
|
292
302
|
}
|
|
293
303
|
function storeCache(filePath, code, files, paramOverrides, assemblyState, quality, backend, result, serialized) {
|
|
@@ -14721,6 +14731,7 @@ const MOUSE_BUTTONS_DRAW = {
|
|
|
14721
14731
|
MIDDLE: MOUSE.PAN,
|
|
14722
14732
|
RIGHT: MOUSE.PAN
|
|
14723
14733
|
};
|
|
14734
|
+
const MOUSE_BUTTONS_VOXEL = MOUSE_BUTTONS_3D;
|
|
14724
14735
|
const dark = {
|
|
14725
14736
|
bg: "#0d1117",
|
|
14726
14737
|
bgPanel: "#161b22",
|
|
@@ -15574,7 +15585,7 @@ const movePath = (value, from, to) => {
|
|
|
15574
15585
|
if (value.startsWith(`${from}/`)) return `${to}${value.slice(from.length)}`;
|
|
15575
15586
|
return value;
|
|
15576
15587
|
};
|
|
15577
|
-
const FORGE_IMPORT_RE = /\b(?:importMesh|importStep|importSvgSketch|Import\.dxfSketch|compareWith)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
15588
|
+
const FORGE_IMPORT_RE = /\b(?:importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch|compareWith)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
15578
15589
|
const REQUIRE_RE = /\brequire\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
15579
15590
|
const ES_IMPORT_RE = /\bfrom\s+(?:"([^"]+)"|'([^']+)')/g;
|
|
15580
15591
|
const VIRTUAL_MODULES = /* @__PURE__ */ new Set(["forgecad", "@forge/runtime", "@forgecad/runtime"]);
|
|
@@ -15592,6 +15603,9 @@ function extractImports(code) {
|
|
|
15592
15603
|
importMesh: "forgeMesh",
|
|
15593
15604
|
importStep: "forgeMesh",
|
|
15594
15605
|
importSvgSketch: "forgeSvg",
|
|
15606
|
+
"Import.mesh": "forgeMesh",
|
|
15607
|
+
"Import.step": "forgeMesh",
|
|
15608
|
+
"Import.svgSketch": "forgeSvg",
|
|
15595
15609
|
"Import.dxfSketch": "forgeDxf",
|
|
15596
15610
|
compareWith: "compareWith"
|
|
15597
15611
|
};
|
|
@@ -15599,7 +15613,9 @@ function extractImports(code) {
|
|
|
15599
15613
|
const forgeRe = new RegExp(FORGE_IMPORT_RE.source, FORGE_IMPORT_RE.flags);
|
|
15600
15614
|
while ((m2 = forgeRe.exec(code)) !== null) {
|
|
15601
15615
|
const path = m2[1] ?? m2[2];
|
|
15602
|
-
const fn = (_a3 = m2[0].match(
|
|
15616
|
+
const fn = (_a3 = m2[0].match(
|
|
15617
|
+
/\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch|compareWith)/
|
|
15618
|
+
)) == null ? void 0 : _a3[1];
|
|
15603
15619
|
add(path, kindMap[fn] ?? "forgeMesh");
|
|
15604
15620
|
}
|
|
15605
15621
|
const reqRe = new RegExp(REQUIRE_RE.source, REQUIRE_RE.flags);
|
|
@@ -15825,7 +15841,7 @@ const CRASH_COOLDOWN_MS = 2e3;
|
|
|
15825
15841
|
class EvalWorkerClient {
|
|
15826
15842
|
constructor(workerFactory = () => new Worker(new URL(
|
|
15827
15843
|
/* @vite-ignore */
|
|
15828
|
-
"/assets/evalWorker-
|
|
15844
|
+
"/assets/evalWorker-DbNs7Dkp.js",
|
|
15829
15845
|
import.meta.url
|
|
15830
15846
|
), { type: "module" })) {
|
|
15831
15847
|
__publicField(this, "worker", null);
|
|
@@ -16070,6 +16086,10 @@ class EvalWorkerClient {
|
|
|
16070
16086
|
runIrPlan(options) {
|
|
16071
16087
|
return this.startCancellableRun(options, "run-ir-plan");
|
|
16072
16088
|
}
|
|
16089
|
+
clearCaches() {
|
|
16090
|
+
var _a3;
|
|
16091
|
+
(_a3 = this.worker) == null ? void 0 : _a3.postMessage({ type: "clear-caches" });
|
|
16092
|
+
}
|
|
16073
16093
|
startCancellableRun(options, type) {
|
|
16074
16094
|
if (this.shouldReplaceWorkerForSupersedingRun()) {
|
|
16075
16095
|
this.replaceWorkerForSupersedingRun();
|
|
@@ -16262,7 +16282,89 @@ const removeParamSnapshotsForFile = (snapshotsByFile, file) => {
|
|
|
16262
16282
|
delete next[file];
|
|
16263
16283
|
return next;
|
|
16264
16284
|
};
|
|
16265
|
-
const
|
|
16285
|
+
const EDITABLE_PROJECT_TEXT_FILE_EXTS = [
|
|
16286
|
+
".forge.js",
|
|
16287
|
+
".sketch.js",
|
|
16288
|
+
".js",
|
|
16289
|
+
".mjs",
|
|
16290
|
+
".cjs",
|
|
16291
|
+
".jsx",
|
|
16292
|
+
".ts",
|
|
16293
|
+
".tsx",
|
|
16294
|
+
".json",
|
|
16295
|
+
".md",
|
|
16296
|
+
".txt",
|
|
16297
|
+
".svg",
|
|
16298
|
+
".dxf",
|
|
16299
|
+
".html",
|
|
16300
|
+
".css",
|
|
16301
|
+
".scss",
|
|
16302
|
+
".yml",
|
|
16303
|
+
".yaml",
|
|
16304
|
+
".toml",
|
|
16305
|
+
".xml",
|
|
16306
|
+
".csv",
|
|
16307
|
+
".ini"
|
|
16308
|
+
];
|
|
16309
|
+
function hasProjectTextFileExtension(path) {
|
|
16310
|
+
const lower = path.toLowerCase();
|
|
16311
|
+
return EDITABLE_PROJECT_TEXT_FILE_EXTS.some((ext) => lower.endsWith(ext));
|
|
16312
|
+
}
|
|
16313
|
+
function projectTextFileTemplate(path) {
|
|
16314
|
+
const lower = path.toLowerCase();
|
|
16315
|
+
if (lower.endsWith(".svg")) {
|
|
16316
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
16317
|
+
<path d="M 10 10 L 90 10 L 90 90 L 10 90 Z" fill="none" stroke="#000" stroke-width="4" />
|
|
16318
|
+
</svg>
|
|
16319
|
+
`;
|
|
16320
|
+
}
|
|
16321
|
+
if (lower.endsWith(".dxf")) {
|
|
16322
|
+
return "0\nSECTION\n2\nENTITIES\n0\nLWPOLYLINE\n8\n0\n90\n4\n70\n1\n10\n0\n20\n0\n10\n100\n20\n0\n10\n100\n20\n60\n10\n0\n20\n60\n0\nENDSEC\n0\nEOF\n";
|
|
16323
|
+
}
|
|
16324
|
+
if (lower.endsWith(".forge.js") || lower.endsWith(".sketch.js")) {
|
|
16325
|
+
return "// 3D Part\n\nreturn box(50, 30, 10);\n";
|
|
16326
|
+
}
|
|
16327
|
+
if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs")) {
|
|
16328
|
+
return "// Shared JS utilities for ForgeCAD.\n\nexport function exampleValue() {\n return 42;\n}\n";
|
|
16329
|
+
}
|
|
16330
|
+
if (lower.endsWith(".md")) return "# Notes\n\n";
|
|
16331
|
+
if (lower.endsWith(".json")) return "{\n \n}\n";
|
|
16332
|
+
return "";
|
|
16333
|
+
}
|
|
16334
|
+
function monacoLanguageForProjectFile(path) {
|
|
16335
|
+
const lower = path.toLowerCase();
|
|
16336
|
+
if (lower.endsWith(".md")) return "markdown";
|
|
16337
|
+
if (lower.endsWith(".json")) return "json";
|
|
16338
|
+
if (lower.endsWith(".ts") || lower.endsWith(".tsx")) return "typescript";
|
|
16339
|
+
if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs") || lower.endsWith(".jsx")) return "javascript";
|
|
16340
|
+
if (lower.endsWith(".html")) return "html";
|
|
16341
|
+
if (lower.endsWith(".css") || lower.endsWith(".scss")) return "css";
|
|
16342
|
+
if (lower.endsWith(".svg") || lower.endsWith(".xml")) return "xml";
|
|
16343
|
+
if (lower.endsWith(".yml") || lower.endsWith(".yaml")) return "yaml";
|
|
16344
|
+
return "plaintext";
|
|
16345
|
+
}
|
|
16346
|
+
function highlightLanguageForProjectFile(path) {
|
|
16347
|
+
const lower = path.toLowerCase();
|
|
16348
|
+
if (lower.endsWith(".md")) return "markdown";
|
|
16349
|
+
if (lower.endsWith(".json")) return "json";
|
|
16350
|
+
if (lower.endsWith(".ts") || lower.endsWith(".tsx")) return "typescript";
|
|
16351
|
+
if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs") || lower.endsWith(".jsx")) return "javascript";
|
|
16352
|
+
if (lower.endsWith(".html") || lower.endsWith(".svg") || lower.endsWith(".xml")) return "xml";
|
|
16353
|
+
if (lower.endsWith(".css") || lower.endsWith(".scss")) return "css";
|
|
16354
|
+
return null;
|
|
16355
|
+
}
|
|
16356
|
+
function projectTextFileMimeType(path) {
|
|
16357
|
+
const lower = path.toLowerCase();
|
|
16358
|
+
if (lower.endsWith(".svg")) return "image/svg+xml";
|
|
16359
|
+
if (lower.endsWith(".dxf")) return "application/dxf";
|
|
16360
|
+
if (lower.endsWith(".json")) return "application/json";
|
|
16361
|
+
if (lower.endsWith(".md")) return "text/markdown";
|
|
16362
|
+
if (lower.endsWith(".html")) return "text/html";
|
|
16363
|
+
if (lower.endsWith(".css") || lower.endsWith(".scss")) return "text/css";
|
|
16364
|
+
if (lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs") || lower.endsWith(".jsx")) return "text/javascript";
|
|
16365
|
+
return "text/plain";
|
|
16366
|
+
}
|
|
16367
|
+
const IMPORTABLE_PROJECT_TEXT_FILE_EXTS = EDITABLE_PROJECT_TEXT_FILE_EXTS;
|
|
16266
16368
|
const IMPORTABLE_PROJECT_MESH_FILE_EXTS = [".stl", ".obj", ".3mf"];
|
|
16267
16369
|
const IMPORTABLE_PROJECT_EXACT_FILE_EXTS = [".step", ".stp"];
|
|
16268
16370
|
const IMPORTABLE_PROJECT_BINARY_FILE_EXTS = [...IMPORTABLE_PROJECT_MESH_FILE_EXTS, ...IMPORTABLE_PROJECT_EXACT_FILE_EXTS];
|
|
@@ -16273,7 +16375,7 @@ function hasImportableExt(path, extensions) {
|
|
|
16273
16375
|
return extensions.some((ext) => lower.endsWith(ext));
|
|
16274
16376
|
}
|
|
16275
16377
|
function isImportableProjectTextFile(path) {
|
|
16276
|
-
return
|
|
16378
|
+
return hasProjectTextFileExtension(path);
|
|
16277
16379
|
}
|
|
16278
16380
|
function isImportableProjectMeshFile(path) {
|
|
16279
16381
|
return hasImportableExt(path, IMPORTABLE_PROJECT_MESH_FILE_EXTS);
|
|
@@ -16976,7 +17078,9 @@ if (sharedBundle) {
|
|
|
16976
17078
|
}
|
|
16977
17079
|
}
|
|
16978
17080
|
const LAST_ACTIVE_FILE_KEY_BASE = "fc-last-active-file";
|
|
17081
|
+
const RECENT_FILES_KEY_BASE = "fc-recent-files";
|
|
16979
17082
|
const lastActiveFileKey = () => userScopedKey(LAST_ACTIVE_FILE_KEY_BASE);
|
|
17083
|
+
const recentFilesKey = () => userScopedKey(RECENT_FILES_KEY_BASE);
|
|
16980
17084
|
const readLastActiveFileForUser = (userId) => {
|
|
16981
17085
|
try {
|
|
16982
17086
|
return localStorage.getItem(`${LAST_ACTIVE_FILE_KEY_BASE}-${userId}`);
|
|
@@ -16984,6 +17088,25 @@ const readLastActiveFileForUser = (userId) => {
|
|
|
16984
17088
|
return null;
|
|
16985
17089
|
}
|
|
16986
17090
|
};
|
|
17091
|
+
const readRecentFiles = () => {
|
|
17092
|
+
try {
|
|
17093
|
+
if (typeof localStorage === "undefined") return [];
|
|
17094
|
+
const raw = localStorage.getItem(recentFilesKey());
|
|
17095
|
+
if (!raw) return [];
|
|
17096
|
+
const parsed = JSON.parse(raw);
|
|
17097
|
+
if (!Array.isArray(parsed)) return [];
|
|
17098
|
+
return parsed.filter((value) => typeof value === "string" && value.length > 0);
|
|
17099
|
+
} catch {
|
|
17100
|
+
return [];
|
|
17101
|
+
}
|
|
17102
|
+
};
|
|
17103
|
+
const writeRecentFiles = (files) => {
|
|
17104
|
+
try {
|
|
17105
|
+
if (typeof localStorage === "undefined") return;
|
|
17106
|
+
localStorage.setItem(recentFilesKey(), JSON.stringify(files));
|
|
17107
|
+
} catch {
|
|
17108
|
+
}
|
|
17109
|
+
};
|
|
16987
17110
|
const clampJointValue = (value, min, max2) => {
|
|
16988
17111
|
let next = Number.isFinite(value) ? value : 0;
|
|
16989
17112
|
if (min !== void 0) next = Math.max(min, next);
|
|
@@ -17208,6 +17331,13 @@ const DEFAULT_INSPECT_POINT_SAMPLE_COUNT = 2e3;
|
|
|
17208
17331
|
const DEFAULT_COMPARISON_INSPECT_MODE = "difference-only";
|
|
17209
17332
|
const DEFAULT_COMPARISON_CANDIDATE_OPACITY = 0.42;
|
|
17210
17333
|
const DEFAULT_COMPARISON_REFERENCE_OPACITY = 0.2;
|
|
17334
|
+
const THICKNESS_COLOR_RANGE_MIN = 0;
|
|
17335
|
+
const THICKNESS_COLOR_RANGE_MAX = 1e3;
|
|
17336
|
+
const THICKNESS_COLOR_RANGE_MIN_SPAN = 1e-3;
|
|
17337
|
+
const DEFAULT_THICKNESS_COLOR_RANGE = {
|
|
17338
|
+
min: 0,
|
|
17339
|
+
max: 6
|
|
17340
|
+
};
|
|
17211
17341
|
const DEFAULT_MANUAL_SCENE_SETTINGS = {
|
|
17212
17342
|
enabled: false,
|
|
17213
17343
|
backgroundColor: "#f6f7f8",
|
|
@@ -17235,6 +17365,21 @@ const resolveClampedNumber = (value, fallback, min, max2) => {
|
|
|
17235
17365
|
return Math.max(min, Math.min(max2, numeric));
|
|
17236
17366
|
};
|
|
17237
17367
|
const resolveComparisonOpacity = (value, fallback) => resolveClampedNumber(value, fallback, 0, 1);
|
|
17368
|
+
const resolveThicknessColorRange = (value) => {
|
|
17369
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
17370
|
+
return DEFAULT_THICKNESS_COLOR_RANGE;
|
|
17371
|
+
}
|
|
17372
|
+
const raw = value;
|
|
17373
|
+
const min = resolveClampedNumber(raw.min, DEFAULT_THICKNESS_COLOR_RANGE.min, THICKNESS_COLOR_RANGE_MIN, THICKNESS_COLOR_RANGE_MAX);
|
|
17374
|
+
const max2 = resolveClampedNumber(raw.max, DEFAULT_THICKNESS_COLOR_RANGE.max, THICKNESS_COLOR_RANGE_MIN, THICKNESS_COLOR_RANGE_MAX);
|
|
17375
|
+
if (max2 - min >= THICKNESS_COLOR_RANGE_MIN_SPAN) return { min, max: max2 };
|
|
17376
|
+
const defaultSpan = DEFAULT_THICKNESS_COLOR_RANGE.max - DEFAULT_THICKNESS_COLOR_RANGE.min;
|
|
17377
|
+
const expandedMax = Math.min(THICKNESS_COLOR_RANGE_MAX, min + defaultSpan);
|
|
17378
|
+
if (expandedMax - min >= THICKNESS_COLOR_RANGE_MIN_SPAN) return { min, max: expandedMax };
|
|
17379
|
+
const expandedMin = Math.max(THICKNESS_COLOR_RANGE_MIN, max2 - defaultSpan);
|
|
17380
|
+
if (max2 - expandedMin >= THICKNESS_COLOR_RANGE_MIN_SPAN) return { min: expandedMin, max: max2 };
|
|
17381
|
+
return DEFAULT_THICKNESS_COLOR_RANGE;
|
|
17382
|
+
};
|
|
17238
17383
|
const resolveManualSceneSettings = (value) => {
|
|
17239
17384
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
17240
17385
|
return DEFAULT_MANUAL_SCENE_SETTINGS;
|
|
@@ -17381,6 +17526,47 @@ const initialActive = (() => {
|
|
|
17381
17526
|
return fallback;
|
|
17382
17527
|
})();
|
|
17383
17528
|
const INITIAL_SAVED = {};
|
|
17529
|
+
const MAX_RECENT_FILES = 200;
|
|
17530
|
+
function touchRecentFile(recentFiles, files, file) {
|
|
17531
|
+
const availableFiles = new Set(Object.keys(files));
|
|
17532
|
+
const next = [];
|
|
17533
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17534
|
+
if (file && availableFiles.has(file)) {
|
|
17535
|
+
next.push(file);
|
|
17536
|
+
seen.add(file);
|
|
17537
|
+
}
|
|
17538
|
+
for (const recentFile of recentFiles) {
|
|
17539
|
+
if (!availableFiles.has(recentFile) || seen.has(recentFile)) continue;
|
|
17540
|
+
next.push(recentFile);
|
|
17541
|
+
seen.add(recentFile);
|
|
17542
|
+
if (next.length >= MAX_RECENT_FILES) break;
|
|
17543
|
+
}
|
|
17544
|
+
return next;
|
|
17545
|
+
}
|
|
17546
|
+
function pruneRecentFiles(recentFiles, files) {
|
|
17547
|
+
return touchRecentFile(recentFiles, files, "");
|
|
17548
|
+
}
|
|
17549
|
+
function remapRecentFiles(recentFiles, files, oldPath, newPath) {
|
|
17550
|
+
const remapped = recentFiles.map((file) => movePath(file, oldPath, newPath));
|
|
17551
|
+
return pruneRecentFiles(remapped, files);
|
|
17552
|
+
}
|
|
17553
|
+
function persistRecentFiles(recentFiles) {
|
|
17554
|
+
writeRecentFiles(recentFiles);
|
|
17555
|
+
return recentFiles;
|
|
17556
|
+
}
|
|
17557
|
+
function touchAndPersistRecentFile(recentFiles, files, file) {
|
|
17558
|
+
return persistRecentFiles(touchRecentFile(recentFiles, files, file));
|
|
17559
|
+
}
|
|
17560
|
+
function pruneAndPersistRecentFiles(recentFiles, files) {
|
|
17561
|
+
return persistRecentFiles(pruneRecentFiles(recentFiles, files));
|
|
17562
|
+
}
|
|
17563
|
+
function remapAndPersistRecentFiles(recentFiles, files, oldPath, newPath) {
|
|
17564
|
+
return persistRecentFiles(remapRecentFiles(recentFiles, files, oldPath, newPath));
|
|
17565
|
+
}
|
|
17566
|
+
function readRecentFilesOrFallback(fallback) {
|
|
17567
|
+
const stored = readRecentFiles();
|
|
17568
|
+
return stored.length > 0 ? stored : fallback;
|
|
17569
|
+
}
|
|
17384
17570
|
const VIEW_INSPECT_CHANNELS = /* @__PURE__ */ new Set([
|
|
17385
17571
|
"none",
|
|
17386
17572
|
"mask",
|
|
@@ -17396,6 +17582,9 @@ const VIEW_INSPECT_CHANNELS = /* @__PURE__ */ new Set([
|
|
|
17396
17582
|
"roughness"
|
|
17397
17583
|
]);
|
|
17398
17584
|
const INSPECT_DISPLAY_MODES = /* @__PURE__ */ new Set(["heatmap", "points", "both", "scan"]);
|
|
17585
|
+
const SAVE_PICKER_TEXT_FILE_EXTS = EDITABLE_PROJECT_TEXT_FILE_EXTS.filter(
|
|
17586
|
+
(ext) => ![".forge.js", ".sketch.js", ".js", ".svg", ".dxf", ".md"].includes(ext)
|
|
17587
|
+
);
|
|
17399
17588
|
function resolveViewInspectChannel(value) {
|
|
17400
17589
|
return typeof value === "string" && VIEW_INSPECT_CHANNELS.has(value) ? value : "none";
|
|
17401
17590
|
}
|
|
@@ -17411,8 +17600,17 @@ function resolveInitialViewInspectChannel(value) {
|
|
|
17411
17600
|
function resolveInspectDisplayMode(value) {
|
|
17412
17601
|
return typeof value === "string" && INSPECT_DISPLAY_MODES.has(value) ? value : "heatmap";
|
|
17413
17602
|
}
|
|
17603
|
+
function isScalarInspectChannel(channel) {
|
|
17604
|
+
return channel === "thickness" || channel === "roughness";
|
|
17605
|
+
}
|
|
17606
|
+
function shouldUseScanRenderStyle(channel, displayMode) {
|
|
17607
|
+
return isScalarInspectChannel(channel) && displayMode === "scan";
|
|
17608
|
+
}
|
|
17414
17609
|
const initialViewPreferences = readViewPreferences();
|
|
17415
|
-
const
|
|
17610
|
+
const initialInspectChannel = resolveInitialViewInspectChannel(initialViewPreferences.inspectChannel);
|
|
17611
|
+
const initialInspectDisplayMode = resolveInspectDisplayMode(initialViewPreferences.inspectDisplayMode);
|
|
17612
|
+
const initialStoredRenderStyle = resolveForgeRenderStyle(initialViewPreferences.renderStyle);
|
|
17613
|
+
const initialRenderStyle = shouldUseScanRenderStyle(initialInspectChannel, initialInspectDisplayMode) ? "scan" : initialStoredRenderStyle;
|
|
17416
17614
|
const resolveFiniteNumber = (value, fallback) => {
|
|
17417
17615
|
const numeric = typeof value === "number" ? value : Number(value);
|
|
17418
17616
|
return Number.isFinite(numeric) ? numeric : fallback;
|
|
@@ -17497,6 +17695,10 @@ function abortServerCompute() {
|
|
|
17497
17695
|
function canUseServerCompute() {
|
|
17498
17696
|
return true;
|
|
17499
17697
|
}
|
|
17698
|
+
function canHaveProjectImports(filename) {
|
|
17699
|
+
const lower = filename.toLowerCase();
|
|
17700
|
+
return lower.endsWith(".forge.js") || lower.endsWith(".sketch.js") || lower.endsWith(".js") || lower.endsWith(".mjs") || lower.endsWith(".cjs") || lower.endsWith(".jsx") || lower.endsWith(".ts") || lower.endsWith(".tsx");
|
|
17701
|
+
}
|
|
17500
17702
|
const PARAM_DEBOUNCE_MS = navigator.maxTouchPoints > 0 && window.innerWidth < 768 ? 250 : 80;
|
|
17501
17703
|
async function lazyLoadFileAndDeps(filename, get, set) {
|
|
17502
17704
|
if (!fileSystem.fetchFileContent) return;
|
|
@@ -17519,6 +17721,7 @@ async function lazyLoadFileAndDeps(filename, get, set) {
|
|
|
17519
17721
|
}
|
|
17520
17722
|
}
|
|
17521
17723
|
if (!content) continue;
|
|
17724
|
+
if (!canHaveProjectImports(file)) continue;
|
|
17522
17725
|
for (const ref of extractImports(content)) {
|
|
17523
17726
|
const resolved = resolveImportPath(file, ref.raw);
|
|
17524
17727
|
const matchKey = Object.keys(storeFiles).find((k2) => k2 === resolved || k2.replace(/^\.\//, "") === resolved.replace(/^\.\//, ""));
|
|
@@ -17572,6 +17775,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17572
17775
|
savedFiles: { ...INITIAL_SAVED },
|
|
17573
17776
|
folders: [...INITIAL_FOLDERS],
|
|
17574
17777
|
activeFile: initialActive,
|
|
17778
|
+
recentFiles: touchRecentFile(readRecentFiles(), INITIAL_FILES, initialActive),
|
|
17575
17779
|
setActiveFile: (name) => {
|
|
17576
17780
|
abortServerCompute();
|
|
17577
17781
|
if (name) {
|
|
@@ -17595,6 +17799,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17595
17799
|
const restored = targetPreviewFile && nextByFile[targetPreviewFile] ? nextByFile[targetPreviewFile] : {};
|
|
17596
17800
|
set({
|
|
17597
17801
|
activeFile: name,
|
|
17802
|
+
recentFiles: touchAndPersistRecentFile(get().recentFiles, files, name),
|
|
17598
17803
|
meshPreviewFile: null,
|
|
17599
17804
|
// Clear mesh preview when switching files
|
|
17600
17805
|
lastValidResult: null,
|
|
@@ -17643,15 +17848,13 @@ const useForgeStore = create((set, get) => ({
|
|
|
17643
17848
|
if (!normalized) return;
|
|
17644
17849
|
if (get().files[normalized] !== void 0) return;
|
|
17645
17850
|
if (get().folders.includes(normalized)) return;
|
|
17646
|
-
const template = normalized
|
|
17647
|
-
<path d="M 10 10 L 90 10 L 90 90 L 10 90 Z" fill="none" stroke="#000" stroke-width="4" />
|
|
17648
|
-
</svg>
|
|
17649
|
-
` : normalized.endsWith(".dxf") ? "0\nSECTION\n2\nENTITIES\n0\nLWPOLYLINE\n8\n0\n90\n4\n70\n1\n10\n0\n20\n0\n10\n100\n20\n0\n10\n100\n20\n60\n10\n0\n20\n60\n0\nENDSEC\n0\nEOF\n" : normalized.endsWith(".forge.js") ? "// 3D Part\n\nreturn box(50, 30, 10);\n" : "// Shared JS utilities for ForgeCAD.\n\nexport function exampleValue() {\n return 42;\n}\n";
|
|
17851
|
+
const template = projectTextFileTemplate(normalized);
|
|
17650
17852
|
const newFolders = Array.from(/* @__PURE__ */ new Set([...get().folders, ...collectParentPaths(normalized)])).sort();
|
|
17651
17853
|
set((s) => ({
|
|
17652
17854
|
files: { ...s.files, [normalized]: template },
|
|
17653
17855
|
savedFiles: { ...s.savedFiles, [normalized]: template },
|
|
17654
17856
|
activeFile: normalized,
|
|
17857
|
+
recentFiles: touchAndPersistRecentFile(s.recentFiles, { ...s.files, [normalized]: template }, normalized),
|
|
17655
17858
|
paramOverrides: {},
|
|
17656
17859
|
jointValues: {},
|
|
17657
17860
|
jointAnimationClip: null,
|
|
@@ -17689,6 +17892,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17689
17892
|
files: remaining,
|
|
17690
17893
|
savedFiles: remainingSaved,
|
|
17691
17894
|
activeFile: newActive,
|
|
17895
|
+
recentFiles: touchAndPersistRecentFile(get().recentFiles, remaining, newActive),
|
|
17692
17896
|
objectSettingsByFile: nextObjectSettingsByFile,
|
|
17693
17897
|
paramSnapshotsByFile: nextParamSnapshotsByFile,
|
|
17694
17898
|
paramOverrides: {},
|
|
@@ -17722,6 +17926,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17722
17926
|
files: remaining,
|
|
17723
17927
|
savedFiles: remainingSaved,
|
|
17724
17928
|
activeFile: oldName === activeFile ? normalized : activeFile,
|
|
17929
|
+
recentFiles: remapAndPersistRecentFiles(get().recentFiles, remaining, oldName, normalized),
|
|
17725
17930
|
objectSettingsByFile: nextObjectSettingsByFile,
|
|
17726
17931
|
paramSnapshotsByFile: nextParamSnapshotsByFile,
|
|
17727
17932
|
folders: Array.from(/* @__PURE__ */ new Set([...get().folders, ...collectParentPaths(normalized)])).sort()
|
|
@@ -17778,6 +17983,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17778
17983
|
savedFiles: updatedSaved,
|
|
17779
17984
|
folders: updatedFolders,
|
|
17780
17985
|
activeFile: nextActive,
|
|
17986
|
+
recentFiles: remapAndPersistRecentFiles(get().recentFiles, updatedFiles, normalizedOld, normalizedNew),
|
|
17781
17987
|
objectSettingsByFile: nextObjectSettingsByFile,
|
|
17782
17988
|
paramSnapshotsByFile: nextParamSnapshotsByFile,
|
|
17783
17989
|
paramOverrides: {},
|
|
@@ -17800,22 +18006,29 @@ const useForgeStore = create((set, get) => ({
|
|
|
17800
18006
|
const prefix = `${normalized}/`;
|
|
17801
18007
|
const remainingFiles = {};
|
|
17802
18008
|
const remainingSaved = {};
|
|
17803
|
-
const deletedFiles =
|
|
18009
|
+
const deletedFiles = /* @__PURE__ */ new Set();
|
|
17804
18010
|
for (const key of Object.keys(files)) {
|
|
17805
18011
|
if (key.startsWith(prefix)) {
|
|
17806
|
-
deletedFiles.
|
|
18012
|
+
deletedFiles.add(key);
|
|
17807
18013
|
} else {
|
|
17808
18014
|
remainingFiles[key] = files[key];
|
|
17809
18015
|
}
|
|
17810
18016
|
}
|
|
17811
18017
|
for (const key of Object.keys(savedFiles)) {
|
|
17812
|
-
if (
|
|
18018
|
+
if (key.startsWith(prefix)) {
|
|
18019
|
+
deletedFiles.add(key);
|
|
18020
|
+
} else {
|
|
17813
18021
|
remainingSaved[key] = savedFiles[key];
|
|
17814
18022
|
}
|
|
17815
18023
|
}
|
|
17816
|
-
|
|
18024
|
+
const replacementFile = Object.keys(remainingFiles).length === 0 ? "main.forge.js" : null;
|
|
18025
|
+
if (replacementFile) {
|
|
18026
|
+
const template = projectTextFileTemplate(replacementFile);
|
|
18027
|
+
remainingFiles[replacementFile] = template;
|
|
18028
|
+
remainingSaved[replacementFile] = template;
|
|
18029
|
+
}
|
|
17817
18030
|
const remainingFolders = folders.filter((f2) => f2 !== normalized && !f2.startsWith(prefix));
|
|
17818
|
-
const newActive = (activeFile == null ? void 0 : activeFile.startsWith(prefix)) ? Object.keys(remainingFiles)[0] : activeFile;
|
|
18031
|
+
const newActive = (activeFile == null ? void 0 : activeFile.startsWith(prefix)) || !activeFile ? Object.keys(remainingFiles)[0] : activeFile;
|
|
17819
18032
|
let nextObjectSettingsByFile = objectSettingsByFile;
|
|
17820
18033
|
let nextParamSnapshotsByFile = paramSnapshotsByFile;
|
|
17821
18034
|
for (const f2 of deletedFiles) {
|
|
@@ -17828,6 +18041,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17828
18041
|
savedFiles: remainingSaved,
|
|
17829
18042
|
folders: remainingFolders,
|
|
17830
18043
|
activeFile: newActive,
|
|
18044
|
+
recentFiles: touchAndPersistRecentFile(get().recentFiles, remainingFiles, newActive),
|
|
17831
18045
|
objectSettingsByFile: nextObjectSettingsByFile,
|
|
17832
18046
|
paramSnapshotsByFile: nextParamSnapshotsByFile,
|
|
17833
18047
|
paramOverrides: {},
|
|
@@ -17837,7 +18051,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
17837
18051
|
jointAnimationPlaying: false,
|
|
17838
18052
|
hoveredJointName: null
|
|
17839
18053
|
});
|
|
17840
|
-
Promise.all(deletedFiles.map((f2) => fileSystem.delete(f2))).then(() => fileSystem.deleteFolder(normalized)).catch((e2) => console.error("Delete folder failed:", e2));
|
|
18054
|
+
Promise.all(Array.from(deletedFiles).map((f2) => fileSystem.delete(f2))).then(() => replacementFile ? fileSystem.save(replacementFile, remainingFiles[replacementFile]) : void 0).then(() => fileSystem.deleteFolder(normalized)).catch((e2) => console.error("Delete folder failed:", e2));
|
|
17841
18055
|
setTimeout(() => get().execute(), 0);
|
|
17842
18056
|
},
|
|
17843
18057
|
moveEntry: (oldPath, newPath) => {
|
|
@@ -18003,7 +18217,8 @@ const useForgeStore = create((set, get) => ({
|
|
|
18003
18217
|
assemblyState,
|
|
18004
18218
|
...useServer ? {} : { assemblyDisplayMode: "instanceTransforms" },
|
|
18005
18219
|
isNotebook: false,
|
|
18006
|
-
activeBackend: executionBackend
|
|
18220
|
+
activeBackend: executionBackend,
|
|
18221
|
+
disableShapeBuildCache: get().disableRunCache
|
|
18007
18222
|
};
|
|
18008
18223
|
let serialized;
|
|
18009
18224
|
if (useServer) {
|
|
@@ -18254,6 +18469,19 @@ const useForgeStore = create((set, get) => ({
|
|
|
18254
18469
|
});
|
|
18255
18470
|
if (isAssemblyDrivenResult(state2.lastValidResult)) void get().updateAssemblyPose();
|
|
18256
18471
|
},
|
|
18472
|
+
resetJointValues: () => {
|
|
18473
|
+
const state2 = get();
|
|
18474
|
+
const hasPoseState = Object.keys(state2.jointValues).length > 0 || state2.jointAnimationClip !== null || state2.jointAnimationProgress !== 0 || state2.jointAnimationPlaying;
|
|
18475
|
+
if (!hasPoseState) return;
|
|
18476
|
+
const assemblyDriven = isAssemblyDrivenResult(state2.lastValidResult);
|
|
18477
|
+
set({
|
|
18478
|
+
jointValues: {},
|
|
18479
|
+
jointAnimationClip: null,
|
|
18480
|
+
jointAnimationProgress: 0,
|
|
18481
|
+
jointAnimationPlaying: false
|
|
18482
|
+
});
|
|
18483
|
+
if (assemblyDriven) void get().updateAssemblyPose();
|
|
18484
|
+
},
|
|
18257
18485
|
setJointAnimationClip: (name) => {
|
|
18258
18486
|
const assemblyDriven = isAssemblyDrivenResult(get().lastValidResult);
|
|
18259
18487
|
set((state2) => {
|
|
@@ -18347,17 +18575,31 @@ const useForgeStore = create((set, get) => ({
|
|
|
18347
18575
|
writeViewPreferences({ manualScene: DEFAULT_MANUAL_SCENE_SETTINGS });
|
|
18348
18576
|
set({ manualScene: DEFAULT_MANUAL_SCENE_SETTINGS });
|
|
18349
18577
|
},
|
|
18350
|
-
inspectChannel:
|
|
18578
|
+
inspectChannel: initialInspectChannel,
|
|
18351
18579
|
setInspectChannel: (channel) => {
|
|
18352
18580
|
const next = resolveViewInspectChannel(channel);
|
|
18353
|
-
|
|
18354
|
-
|
|
18581
|
+
set((state2) => {
|
|
18582
|
+
const renderStyle = shouldUseScanRenderStyle(next, state2.inspectDisplayMode) ? "scan" : state2.renderStyle;
|
|
18583
|
+
const scanGranularity = resolveViewportScanGranularity(state2.scanGranularity, renderStyle);
|
|
18584
|
+
writeViewPreferences({
|
|
18585
|
+
inspectChannel: next === "collisions" ? "none" : next,
|
|
18586
|
+
...renderStyle !== state2.renderStyle ? { renderStyle, scanGranularity } : {}
|
|
18587
|
+
});
|
|
18588
|
+
return { inspectChannel: next, renderStyle, scanGranularity };
|
|
18589
|
+
});
|
|
18355
18590
|
},
|
|
18356
|
-
inspectDisplayMode:
|
|
18591
|
+
inspectDisplayMode: initialInspectDisplayMode,
|
|
18357
18592
|
setInspectDisplayMode: (mode) => {
|
|
18358
18593
|
const next = resolveInspectDisplayMode(mode);
|
|
18359
|
-
|
|
18360
|
-
|
|
18594
|
+
set((state2) => {
|
|
18595
|
+
const renderStyle = shouldUseScanRenderStyle(state2.inspectChannel, next) ? "scan" : state2.renderStyle;
|
|
18596
|
+
const scanGranularity = resolveViewportScanGranularity(state2.scanGranularity, renderStyle);
|
|
18597
|
+
writeViewPreferences({
|
|
18598
|
+
inspectDisplayMode: next,
|
|
18599
|
+
...renderStyle !== state2.renderStyle ? { renderStyle, scanGranularity } : {}
|
|
18600
|
+
});
|
|
18601
|
+
return { inspectDisplayMode: next, renderStyle, scanGranularity };
|
|
18602
|
+
});
|
|
18361
18603
|
},
|
|
18362
18604
|
scanGranularity: resolveViewportScanGranularity(
|
|
18363
18605
|
initialViewPreferences.scanGranularity ?? SCAN_PROXY_GRANULARITY_DEFAULT,
|
|
@@ -18376,6 +18618,14 @@ const useForgeStore = create((set, get) => ({
|
|
|
18376
18618
|
writeViewPreferences({ inspectPointSampleCount: next });
|
|
18377
18619
|
set({ inspectPointSampleCount: next });
|
|
18378
18620
|
},
|
|
18621
|
+
thicknessColorRange: resolveThicknessColorRange(initialViewPreferences.thicknessColorRange),
|
|
18622
|
+
setThicknessColorRange: (range) => {
|
|
18623
|
+
set((state2) => {
|
|
18624
|
+
const next = resolveThicknessColorRange({ ...state2.thicknessColorRange, ...range });
|
|
18625
|
+
writeViewPreferences({ thicknessColorRange: next });
|
|
18626
|
+
return { thicknessColorRange: next };
|
|
18627
|
+
});
|
|
18628
|
+
},
|
|
18379
18629
|
comparisonInspectMode: resolveComparisonInspectMode(initialViewPreferences.comparisonInspectMode),
|
|
18380
18630
|
setComparisonInspectMode: (mode) => {
|
|
18381
18631
|
const next = resolveComparisonInspectMode(mode);
|
|
@@ -18763,7 +19013,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
18763
19013
|
if (!next) {
|
|
18764
19014
|
return { measureMode: false, measureSelections: [], measurements: [] };
|
|
18765
19015
|
}
|
|
18766
|
-
return { measureMode: true };
|
|
19016
|
+
return { measureMode: true, voxelIntentMode: false };
|
|
18767
19017
|
});
|
|
18768
19018
|
},
|
|
18769
19019
|
measureSelections: [],
|
|
@@ -18797,6 +19047,73 @@ const useForgeStore = create((set, get) => ({
|
|
|
18797
19047
|
writeViewPreferences({ measureSnapPx: value });
|
|
18798
19048
|
set({ measureSnapPx: value });
|
|
18799
19049
|
},
|
|
19050
|
+
voxelIntentMode: false,
|
|
19051
|
+
toggleVoxelIntentMode: () => set((s) => {
|
|
19052
|
+
const next = !s.voxelIntentMode;
|
|
19053
|
+
if (!next) return { voxelIntentMode: false };
|
|
19054
|
+
return {
|
|
19055
|
+
voxelIntentMode: true,
|
|
19056
|
+
measureMode: false,
|
|
19057
|
+
measureSelections: [],
|
|
19058
|
+
measurements: [],
|
|
19059
|
+
constructionGhost: null
|
|
19060
|
+
};
|
|
19061
|
+
}),
|
|
19062
|
+
setVoxelIntentMode: (enabled) => set(
|
|
19063
|
+
() => enabled ? {
|
|
19064
|
+
voxelIntentMode: true,
|
|
19065
|
+
measureMode: false,
|
|
19066
|
+
measureSelections: [],
|
|
19067
|
+
measurements: [],
|
|
19068
|
+
constructionGhost: null
|
|
19069
|
+
} : { voxelIntentMode: false }
|
|
19070
|
+
),
|
|
19071
|
+
voxelIntentTool: "red",
|
|
19072
|
+
setVoxelIntentTool: (tool) => set({ voxelIntentTool: tool }),
|
|
19073
|
+
voxelIntentPlacement: "surface",
|
|
19074
|
+
setVoxelIntentPlacement: (placement) => set({ voxelIntentPlacement: placement }),
|
|
19075
|
+
voxelIntentCellSize: 10,
|
|
19076
|
+
setVoxelIntentCellSize: (cellSize) => set((s) => {
|
|
19077
|
+
if (Object.keys(s.voxelIntentBlocks).length > 0) return {};
|
|
19078
|
+
const nextCellSize = Math.max(1, Math.min(200, Number.isFinite(cellSize) ? Math.round(cellSize) : 10));
|
|
19079
|
+
return { voxelIntentCellSize: nextCellSize };
|
|
19080
|
+
}),
|
|
19081
|
+
voxelIntentCellSizePreviewVisible: false,
|
|
19082
|
+
setVoxelIntentCellSizePreviewVisible: (visible) => set({ voxelIntentCellSizePreviewVisible: visible }),
|
|
19083
|
+
voxelIntentGhostOpacity: 0.24,
|
|
19084
|
+
setVoxelIntentGhostOpacity: (opacity) => set({ voxelIntentGhostOpacity: Math.max(0.05, Math.min(0.75, opacity)) }),
|
|
19085
|
+
voxelIntentContextFile: null,
|
|
19086
|
+
setVoxelIntentContextFile: (filePath) => set((s) => {
|
|
19087
|
+
if (s.voxelIntentContextFile === filePath) return {};
|
|
19088
|
+
return {
|
|
19089
|
+
voxelIntentContextFile: filePath,
|
|
19090
|
+
voxelIntentBlocks: filePath ? s.voxelIntentBlocksByFile[filePath] ?? {} : {}
|
|
19091
|
+
};
|
|
19092
|
+
}),
|
|
19093
|
+
voxelIntentBlocksByFile: {},
|
|
19094
|
+
voxelIntentBlocks: {},
|
|
19095
|
+
setVoxelIntentBlock: (cell, block) => set((s) => {
|
|
19096
|
+
const key = `${cell.x},${cell.y},${cell.z}`;
|
|
19097
|
+
const voxelIntentBlocks = { ...s.voxelIntentBlocks, [key]: block };
|
|
19098
|
+
return {
|
|
19099
|
+
voxelIntentBlocks,
|
|
19100
|
+
voxelIntentBlocksByFile: s.voxelIntentContextFile ? { ...s.voxelIntentBlocksByFile, [s.voxelIntentContextFile]: voxelIntentBlocks } : s.voxelIntentBlocksByFile
|
|
19101
|
+
};
|
|
19102
|
+
}),
|
|
19103
|
+
eraseVoxelIntentBlock: (cell) => set((s) => {
|
|
19104
|
+
const key = `${cell.x},${cell.y},${cell.z}`;
|
|
19105
|
+
if (!(key in s.voxelIntentBlocks)) return {};
|
|
19106
|
+
const next = { ...s.voxelIntentBlocks };
|
|
19107
|
+
delete next[key];
|
|
19108
|
+
return {
|
|
19109
|
+
voxelIntentBlocks: next,
|
|
19110
|
+
voxelIntentBlocksByFile: s.voxelIntentContextFile ? { ...s.voxelIntentBlocksByFile, [s.voxelIntentContextFile]: next } : s.voxelIntentBlocksByFile
|
|
19111
|
+
};
|
|
19112
|
+
}),
|
|
19113
|
+
clearVoxelIntentBlocks: () => set((s) => ({
|
|
19114
|
+
voxelIntentBlocks: {},
|
|
19115
|
+
voxelIntentBlocksByFile: s.voxelIntentContextFile ? { ...s.voxelIntentBlocksByFile, [s.voxelIntentContextFile]: {} } : s.voxelIntentBlocksByFile
|
|
19116
|
+
})),
|
|
18800
19117
|
dimensionsVisible: initialViewPreferences.dimensionsVisible ?? true,
|
|
18801
19118
|
toggleDimensions: () => set((s) => {
|
|
18802
19119
|
const nextDimensionsVisible = !s.dimensionsVisible;
|
|
@@ -18885,6 +19202,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
18885
19202
|
savedFiles: { ...INITIAL_SAVED },
|
|
18886
19203
|
folders: [],
|
|
18887
19204
|
activeFile: initialActive,
|
|
19205
|
+
recentFiles: touchAndPersistRecentFile([], INITIAL_FILES, initialActive),
|
|
18888
19206
|
previewFile: initialPreviewFile,
|
|
18889
19207
|
objectSettings: getObjectSettingsForPreviewFile(initialObjectSettingsByFile, initialPreviewFile),
|
|
18890
19208
|
objectSettingsByFile: initialObjectSettingsByFile,
|
|
@@ -18925,7 +19243,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18925
19243
|
const handle = await window.showSaveFilePicker({
|
|
18926
19244
|
suggestedName: activeFile,
|
|
18927
19245
|
types: [
|
|
18928
|
-
{ description: "ForgeCAD scripts", accept: { "text/javascript": [".forge.js", ".js"] } },
|
|
19246
|
+
{ description: "ForgeCAD scripts", accept: { "text/javascript": [".forge.js", ".sketch.js", ".js"] } },
|
|
19247
|
+
{ description: "Text files", accept: { "text/plain": SAVE_PICKER_TEXT_FILE_EXTS } },
|
|
19248
|
+
{ description: "Markdown", accept: { "text/markdown": [".md"] } },
|
|
18929
19249
|
{ description: "SVG", accept: { "image/svg+xml": [".svg"] } },
|
|
18930
19250
|
{ description: "DXF", accept: { "application/dxf": [".dxf"] } }
|
|
18931
19251
|
]
|
|
@@ -18940,7 +19260,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
18940
19260
|
// This assumes we are satisfied with current content being "saved"
|
|
18941
19261
|
}));
|
|
18942
19262
|
} else {
|
|
18943
|
-
const mime =
|
|
19263
|
+
const mime = projectTextFileMimeType(activeFile);
|
|
18944
19264
|
const blob = new Blob([code], { type: mime });
|
|
18945
19265
|
triggerDownload(blob, activeFile);
|
|
18946
19266
|
set((s) => ({
|
|
@@ -18959,6 +19279,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
18959
19279
|
files: { ...s.files, [normalized]: text },
|
|
18960
19280
|
savedFiles: { ...s.savedFiles, [normalized]: text },
|
|
18961
19281
|
activeFile: normalized,
|
|
19282
|
+
recentFiles: touchAndPersistRecentFile(s.recentFiles, { ...s.files, [normalized]: text }, normalized),
|
|
18962
19283
|
fileHandle: null,
|
|
18963
19284
|
dirty: false,
|
|
18964
19285
|
previewFile: null,
|
|
@@ -19017,6 +19338,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
19017
19338
|
return {
|
|
19018
19339
|
files: nextFiles,
|
|
19019
19340
|
activeFile: nextActive,
|
|
19341
|
+
recentFiles: touchAndPersistRecentFile(s.recentFiles, nextFiles, nextActive),
|
|
19020
19342
|
fileHandle: null,
|
|
19021
19343
|
dirty: true,
|
|
19022
19344
|
previewFile: null,
|
|
@@ -19095,9 +19417,15 @@ const useForgeStore = create((set, get) => ({
|
|
|
19095
19417
|
const prevFiles = state2.files;
|
|
19096
19418
|
const prevActiveFile = state2.activeFile;
|
|
19097
19419
|
const nextState = computeServerSnapshot(state2, serverFiles, serverFolders, sharedModel, sharedBundle, initialFile);
|
|
19098
|
-
set({ ...nextState, filesLoading: false });
|
|
19099
19420
|
const nextFiles = nextState.files;
|
|
19100
|
-
const newActiveFile = nextState.activeFile;
|
|
19421
|
+
const newActiveFile = nextState.activeFile ?? state2.activeFile;
|
|
19422
|
+
const baseRecentFiles = readRecentFilesOrFallback(state2.recentFiles);
|
|
19423
|
+
set({
|
|
19424
|
+
...nextState,
|
|
19425
|
+
activeFile: newActiveFile,
|
|
19426
|
+
recentFiles: touchAndPersistRecentFile(baseRecentFiles, nextFiles, newActiveFile),
|
|
19427
|
+
filesLoading: false
|
|
19428
|
+
});
|
|
19101
19429
|
const needsLazyLoad = newActiveFile && newActiveFile in nextFiles && nextFiles[newActiveFile] === "" && fileSystem.fetchFileContent;
|
|
19102
19430
|
postApplyServerSnapshot(
|
|
19103
19431
|
prevActiveFile,
|
|
@@ -19118,7 +19446,7 @@ const useForgeStore = create((set, get) => ({
|
|
|
19118
19446
|
const state2 = get();
|
|
19119
19447
|
const nextState = computeServerFileChange(state2, filename, content);
|
|
19120
19448
|
if (!nextState) return;
|
|
19121
|
-
set(nextState);
|
|
19449
|
+
set({ ...nextState, recentFiles: pruneAndPersistRecentFiles(state2.recentFiles, nextState.files) });
|
|
19122
19450
|
const previewFile = resolvePreviewFile(state2.activeFile, nextState.files);
|
|
19123
19451
|
if (previewFile === filename) setTimeout(() => get().execute(), 0);
|
|
19124
19452
|
},
|
|
@@ -19127,13 +19455,18 @@ const useForgeStore = create((set, get) => ({
|
|
|
19127
19455
|
const prevActiveFile = state2.activeFile;
|
|
19128
19456
|
const nextState = computeServerFileDelete(state2, filename);
|
|
19129
19457
|
if (!nextState) return;
|
|
19130
|
-
|
|
19131
|
-
|
|
19458
|
+
const newActiveFile = nextState.activeFile ?? "";
|
|
19459
|
+
set({
|
|
19460
|
+
...nextState,
|
|
19461
|
+
activeFile: newActiveFile,
|
|
19462
|
+
recentFiles: touchAndPersistRecentFile(state2.recentFiles, nextState.files, newActiveFile)
|
|
19463
|
+
});
|
|
19464
|
+
if (newActiveFile && newActiveFile !== prevActiveFile) {
|
|
19132
19465
|
set({ paramOverrides: {}, lastValidResult: null });
|
|
19133
19466
|
setParamOverrides({});
|
|
19134
|
-
window.history.replaceState(null, "", `#${
|
|
19467
|
+
window.history.replaceState(null, "", `#${newActiveFile}`);
|
|
19135
19468
|
try {
|
|
19136
|
-
localStorage.setItem(lastActiveFileKey(),
|
|
19469
|
+
localStorage.setItem(lastActiveFileKey(), newActiveFile);
|
|
19137
19470
|
} catch {
|
|
19138
19471
|
}
|
|
19139
19472
|
setTimeout(() => get().execute(), 0);
|
|
@@ -19160,6 +19493,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
19160
19493
|
shareOpen: false,
|
|
19161
19494
|
openShare: () => set({ shareOpen: true }),
|
|
19162
19495
|
closeShare: () => set({ shareOpen: false }),
|
|
19496
|
+
captureOpen: false,
|
|
19497
|
+
openCapture: () => set({ captureOpen: true }),
|
|
19498
|
+
closeCapture: () => set({ captureOpen: false }),
|
|
19163
19499
|
aiSkillOpen: false,
|
|
19164
19500
|
openAISkill: () => set({ aiSkillOpen: true }),
|
|
19165
19501
|
closeAISkill: () => set({ aiSkillOpen: false }),
|
|
@@ -19172,7 +19508,10 @@ const useForgeStore = create((set, get) => ({
|
|
|
19172
19508
|
disableRunCache: initialViewPreferences.disableRunCache ?? false,
|
|
19173
19509
|
setDisableRunCache: (disabled) => {
|
|
19174
19510
|
writeViewPreferences({ disableRunCache: disabled });
|
|
19175
|
-
if (disabled)
|
|
19511
|
+
if (disabled) {
|
|
19512
|
+
clearRunResultCache();
|
|
19513
|
+
evalWorkerClient.clearCaches();
|
|
19514
|
+
}
|
|
19176
19515
|
set({ disableRunCache: disabled });
|
|
19177
19516
|
}
|
|
19178
19517
|
}));
|
|
@@ -26998,12 +27337,15 @@ const COMPARISON_REFERENCE_COLOR = [32, 199, 255];
|
|
|
26998
27337
|
const COMPARISON_CONTEXT_COLOR = [174, 184, 194];
|
|
26999
27338
|
const SCAN_CONTEXT_COLOR = [19, 201, 255];
|
|
27000
27339
|
const SCAN_CONTEXT_EDGE_COLOR = [145, 231, 255];
|
|
27001
|
-
const SCAN_RISK_MID_COLOR = [255, 193, 77];
|
|
27002
|
-
const SCAN_RISK_HOT_COLOR = [255, 138, 31];
|
|
27003
27340
|
const FLOATING_HIGHLIGHT_COLOR = [255, 68, 16];
|
|
27004
27341
|
const FLOATING_CONTEXT_COLOR = [38, 49, 58];
|
|
27005
27342
|
const RIG_SHADOW_COLOR = [6, 10, 18];
|
|
27006
|
-
const RIG_LINK_COLOR = [
|
|
27343
|
+
const RIG_LINK_COLOR = [142, 106, 255];
|
|
27344
|
+
const RIG_KINEMATIC_LINK_COLOR = [255, 226, 106];
|
|
27345
|
+
const RIG_KINEMATIC_EDGE_COLOR = [20, 220, 255];
|
|
27346
|
+
const RIG_FIXED_LINK_COLOR = [240, 253, 255];
|
|
27347
|
+
const RIG_DERIVED_LINK_COLOR = [255, 76, 196];
|
|
27348
|
+
const RIG_CONTROL_LINK_COLOR = [255, 122, 26];
|
|
27007
27349
|
const RIG_PART_LINK_COLOR = [174, 255, 92];
|
|
27008
27350
|
const RIG_HIDDEN_LINK_COLOR = [116, 136, 148];
|
|
27009
27351
|
const COLLISION_PALETTE = [
|
|
@@ -27280,7 +27622,7 @@ function ConstructionGhostOverlay({ matrix }) {
|
|
|
27280
27622
|
class ConstructionHistoryWorkerClient {
|
|
27281
27623
|
constructor(workerFactory = () => new Worker(new URL(
|
|
27282
27624
|
/* @vite-ignore */
|
|
27283
|
-
"/assets/constructionHistoryWorker-
|
|
27625
|
+
"/assets/constructionHistoryWorker-AwMMWSxg.js",
|
|
27284
27626
|
import.meta.url
|
|
27285
27627
|
), { type: "module" })) {
|
|
27286
27628
|
__publicField(this, "worker", null);
|
|
@@ -29436,14 +29778,89 @@ ensureNotFinalized_fn = function() {
|
|
|
29436
29778
|
throw new Error("Cannot add new video or audio chunks after the file has been finalized.");
|
|
29437
29779
|
}
|
|
29438
29780
|
};
|
|
29781
|
+
const H264_HIGH_LEVEL_4_CODEC = "avc1.640028";
|
|
29782
|
+
const H264_LEVEL_4_MAX_CODED_AREA = 2097152;
|
|
29783
|
+
const H264_MACROBLOCK_SIZE = 16;
|
|
29439
29784
|
function supportsMP4Recording() {
|
|
29440
29785
|
return typeof VideoEncoder !== "undefined" && typeof VideoFrame !== "undefined";
|
|
29441
29786
|
}
|
|
29787
|
+
function evenFloor(value) {
|
|
29788
|
+
return Math.max(2, Math.floor(value) & -2);
|
|
29789
|
+
}
|
|
29790
|
+
function codedDimension(value) {
|
|
29791
|
+
return Math.ceil(value / H264_MACROBLOCK_SIZE) * H264_MACROBLOCK_SIZE;
|
|
29792
|
+
}
|
|
29793
|
+
function codedArea(width, height) {
|
|
29794
|
+
return codedDimension(width) * codedDimension(height);
|
|
29795
|
+
}
|
|
29796
|
+
function resolveCanvasBackground(canvas) {
|
|
29797
|
+
const transparent = /* @__PURE__ */ new Set(["transparent", "rgba(0, 0, 0, 0)"]);
|
|
29798
|
+
let node = canvas;
|
|
29799
|
+
while (node) {
|
|
29800
|
+
const backgroundColor = getComputedStyle(node).backgroundColor;
|
|
29801
|
+
if (backgroundColor && !transparent.has(backgroundColor)) return backgroundColor;
|
|
29802
|
+
node = node.parentElement;
|
|
29803
|
+
}
|
|
29804
|
+
return "#252526";
|
|
29805
|
+
}
|
|
29806
|
+
function fitH264Level4RecordingSize(sourceWidth, sourceHeight) {
|
|
29807
|
+
if (!Number.isFinite(sourceWidth) || !Number.isFinite(sourceHeight) || sourceWidth <= 0 || sourceHeight <= 0) {
|
|
29808
|
+
throw new Error(`Cannot record MP4 from invalid canvas size ${sourceWidth}x${sourceHeight}.`);
|
|
29809
|
+
}
|
|
29810
|
+
const evenSourceWidth = evenFloor(sourceWidth);
|
|
29811
|
+
const evenSourceHeight = evenFloor(sourceHeight);
|
|
29812
|
+
let width = evenSourceWidth;
|
|
29813
|
+
let height = evenSourceHeight;
|
|
29814
|
+
if (codedArea(width, height) > H264_LEVEL_4_MAX_CODED_AREA) {
|
|
29815
|
+
let low = 0;
|
|
29816
|
+
let high = 1;
|
|
29817
|
+
let bestWidth = 2;
|
|
29818
|
+
let bestHeight = 2;
|
|
29819
|
+
for (let i = 0; i < 32; i += 1) {
|
|
29820
|
+
const scale = (low + high) / 2;
|
|
29821
|
+
const candidateWidth = evenFloor(evenSourceWidth * scale);
|
|
29822
|
+
const candidateHeight = evenFloor(evenSourceHeight * scale);
|
|
29823
|
+
if (codedArea(candidateWidth, candidateHeight) <= H264_LEVEL_4_MAX_CODED_AREA) {
|
|
29824
|
+
bestWidth = candidateWidth;
|
|
29825
|
+
bestHeight = candidateHeight;
|
|
29826
|
+
low = scale;
|
|
29827
|
+
} else {
|
|
29828
|
+
high = scale;
|
|
29829
|
+
}
|
|
29830
|
+
}
|
|
29831
|
+
width = bestWidth;
|
|
29832
|
+
height = bestHeight;
|
|
29833
|
+
}
|
|
29834
|
+
const codedWidth = codedDimension(width);
|
|
29835
|
+
const codedHeight = codedDimension(height);
|
|
29836
|
+
if (codedWidth * codedHeight > H264_LEVEL_4_MAX_CODED_AREA) {
|
|
29837
|
+
throw new Error(`Could not fit ${sourceWidth}x${sourceHeight} into the MP4 encoder limit.`);
|
|
29838
|
+
}
|
|
29839
|
+
return {
|
|
29840
|
+
sourceWidth: evenSourceWidth,
|
|
29841
|
+
sourceHeight: evenSourceHeight,
|
|
29842
|
+
width,
|
|
29843
|
+
height,
|
|
29844
|
+
codedWidth,
|
|
29845
|
+
codedHeight,
|
|
29846
|
+
scale: Math.min(width / evenSourceWidth, height / evenSourceHeight)
|
|
29847
|
+
};
|
|
29848
|
+
}
|
|
29849
|
+
async function requireSupportedEncoderConfig(config) {
|
|
29850
|
+
if (typeof VideoEncoder.isConfigSupported !== "function") return config;
|
|
29851
|
+
const support = await VideoEncoder.isConfigSupported(config);
|
|
29852
|
+
if (!support.supported) {
|
|
29853
|
+
throw new Error(`MP4 recording is not supported at ${config.width}x${config.height} in this browser.`);
|
|
29854
|
+
}
|
|
29855
|
+
return support.config ?? config;
|
|
29856
|
+
}
|
|
29442
29857
|
class HistoryRecorder {
|
|
29443
29858
|
constructor(canvas, options) {
|
|
29444
29859
|
__publicField(this, "encoder", null);
|
|
29445
29860
|
__publicField(this, "muxer", null);
|
|
29446
29861
|
__publicField(this, "target", null);
|
|
29862
|
+
__publicField(this, "captureCanvas", null);
|
|
29863
|
+
__publicField(this, "captureContext", null);
|
|
29447
29864
|
__publicField(this, "frameCount", 0);
|
|
29448
29865
|
__publicField(this, "recording", false);
|
|
29449
29866
|
__publicField(this, "startTime", 0);
|
|
@@ -29460,37 +29877,71 @@ class HistoryRecorder {
|
|
|
29460
29877
|
/** Start recording. Must be called before captureFrame(). */
|
|
29461
29878
|
async start() {
|
|
29462
29879
|
if (this.recording) return;
|
|
29463
|
-
|
|
29464
|
-
|
|
29465
|
-
|
|
29466
|
-
|
|
29467
|
-
|
|
29468
|
-
|
|
29469
|
-
codec: "avc",
|
|
29470
|
-
width,
|
|
29471
|
-
height
|
|
29472
|
-
},
|
|
29473
|
-
fastStart: "in-memory",
|
|
29474
|
-
firstTimestampBehavior: "offset"
|
|
29475
|
-
});
|
|
29476
|
-
this.encoder = new VideoEncoder({
|
|
29477
|
-
output: (chunk, meta) => {
|
|
29478
|
-
this.muxer.addVideoChunk(chunk, meta);
|
|
29479
|
-
},
|
|
29480
|
-
error: (e2) => console.error("VideoEncoder error:", e2)
|
|
29481
|
-
});
|
|
29482
|
-
this.encoder.configure({
|
|
29483
|
-
codec: "avc1.640028",
|
|
29880
|
+
if (!supportsMP4Recording()) {
|
|
29881
|
+
throw new Error("MP4 recording requires WebCodecs VideoEncoder and VideoFrame support.");
|
|
29882
|
+
}
|
|
29883
|
+
const size = fitH264Level4RecordingSize(this.canvas.width, this.canvas.height);
|
|
29884
|
+
const config = await requireSupportedEncoderConfig({
|
|
29885
|
+
codec: H264_HIGH_LEVEL_4_CODEC,
|
|
29484
29886
|
// H.264 High Profile Level 4.0
|
|
29485
|
-
width,
|
|
29486
|
-
height,
|
|
29887
|
+
width: size.width,
|
|
29888
|
+
height: size.height,
|
|
29487
29889
|
bitrate: this.bitrate,
|
|
29488
29890
|
framerate: this.fps
|
|
29489
29891
|
});
|
|
29892
|
+
try {
|
|
29893
|
+
this.prepareCaptureSurface(size);
|
|
29894
|
+
this.target = new ArrayBufferTarget();
|
|
29895
|
+
this.muxer = new Muxer({
|
|
29896
|
+
target: this.target,
|
|
29897
|
+
video: {
|
|
29898
|
+
codec: "avc",
|
|
29899
|
+
width: size.width,
|
|
29900
|
+
height: size.height
|
|
29901
|
+
},
|
|
29902
|
+
fastStart: "in-memory",
|
|
29903
|
+
firstTimestampBehavior: "offset"
|
|
29904
|
+
});
|
|
29905
|
+
this.encoder = new VideoEncoder({
|
|
29906
|
+
output: (chunk, meta) => {
|
|
29907
|
+
this.muxer.addVideoChunk(chunk, meta);
|
|
29908
|
+
},
|
|
29909
|
+
error: (e2) => {
|
|
29910
|
+
this.recording = false;
|
|
29911
|
+
console.error("VideoEncoder error:", e2);
|
|
29912
|
+
}
|
|
29913
|
+
});
|
|
29914
|
+
this.encoder.configure(config);
|
|
29915
|
+
} catch (err) {
|
|
29916
|
+
this.abort();
|
|
29917
|
+
throw err;
|
|
29918
|
+
}
|
|
29490
29919
|
this.frameCount = 0;
|
|
29491
29920
|
this.startTime = performance.now();
|
|
29492
29921
|
this.recording = true;
|
|
29493
29922
|
}
|
|
29923
|
+
prepareCaptureSurface(size) {
|
|
29924
|
+
this.captureCanvas = null;
|
|
29925
|
+
this.captureContext = null;
|
|
29926
|
+
const captureCanvas = document.createElement("canvas");
|
|
29927
|
+
captureCanvas.width = size.width;
|
|
29928
|
+
captureCanvas.height = size.height;
|
|
29929
|
+
const captureContext = captureCanvas.getContext("2d", { alpha: false });
|
|
29930
|
+
if (!captureContext) {
|
|
29931
|
+
throw new Error("Could not create MP4 recording capture canvas.");
|
|
29932
|
+
}
|
|
29933
|
+
this.captureCanvas = captureCanvas;
|
|
29934
|
+
this.captureContext = captureContext;
|
|
29935
|
+
}
|
|
29936
|
+
frameSourceCanvas() {
|
|
29937
|
+
if (!this.captureCanvas || !this.captureContext) {
|
|
29938
|
+
throw new Error("MP4 recording capture surface is not initialized.");
|
|
29939
|
+
}
|
|
29940
|
+
this.captureContext.fillStyle = resolveCanvasBackground(this.canvas);
|
|
29941
|
+
this.captureContext.fillRect(0, 0, this.captureCanvas.width, this.captureCanvas.height);
|
|
29942
|
+
this.captureContext.drawImage(this.canvas, 0, 0, this.captureCanvas.width, this.captureCanvas.height);
|
|
29943
|
+
return this.captureCanvas;
|
|
29944
|
+
}
|
|
29494
29945
|
/**
|
|
29495
29946
|
* Capture the current canvas frame. Forces a render so the drawing buffer
|
|
29496
29947
|
* has fresh content — this avoids needing `preserveDrawingBuffer: true` on
|
|
@@ -29521,7 +29972,11 @@ class HistoryRecorder {
|
|
|
29521
29972
|
this.encodeFrame(timestampMicros);
|
|
29522
29973
|
}
|
|
29523
29974
|
encodeFrame(timestampMicros) {
|
|
29524
|
-
|
|
29975
|
+
if (!this.encoder || this.encoder.state !== "configured") {
|
|
29976
|
+
this.recording = false;
|
|
29977
|
+
return;
|
|
29978
|
+
}
|
|
29979
|
+
const frame2 = new VideoFrame(this.frameSourceCanvas(), {
|
|
29525
29980
|
timestamp: timestampMicros
|
|
29526
29981
|
});
|
|
29527
29982
|
const keyFrame = this.frameCount % (this.fps * 2) === 0;
|
|
@@ -29529,20 +29984,32 @@ class HistoryRecorder {
|
|
|
29529
29984
|
frame2.close();
|
|
29530
29985
|
this.frameCount++;
|
|
29531
29986
|
}
|
|
29987
|
+
reset() {
|
|
29988
|
+
this.encoder = null;
|
|
29989
|
+
this.muxer = null;
|
|
29990
|
+
this.target = null;
|
|
29991
|
+
this.captureCanvas = null;
|
|
29992
|
+
this.captureContext = null;
|
|
29993
|
+
this.frameCount = 0;
|
|
29994
|
+
this.startTime = 0;
|
|
29995
|
+
}
|
|
29532
29996
|
/** Stop recording and return the MP4 blob. */
|
|
29533
29997
|
async stop() {
|
|
29534
29998
|
if (!this.encoder || !this.muxer || !this.target) {
|
|
29535
29999
|
throw new Error("Not recording");
|
|
29536
30000
|
}
|
|
30001
|
+
const encoder2 = this.encoder;
|
|
30002
|
+
const muxer = this.muxer;
|
|
30003
|
+
const target = this.target;
|
|
29537
30004
|
this.recording = false;
|
|
29538
|
-
|
|
29539
|
-
|
|
29540
|
-
|
|
29541
|
-
|
|
29542
|
-
|
|
29543
|
-
|
|
29544
|
-
|
|
29545
|
-
|
|
30005
|
+
try {
|
|
30006
|
+
await encoder2.flush();
|
|
30007
|
+
if (encoder2.state !== "closed") encoder2.close();
|
|
30008
|
+
muxer.finalize();
|
|
30009
|
+
return new Blob([target.buffer], { type: "video/mp4" });
|
|
30010
|
+
} finally {
|
|
30011
|
+
this.reset();
|
|
30012
|
+
}
|
|
29546
30013
|
}
|
|
29547
30014
|
/** Abort recording without producing output. */
|
|
29548
30015
|
abort() {
|
|
@@ -29554,10 +30021,7 @@ class HistoryRecorder {
|
|
|
29554
30021
|
} catch {
|
|
29555
30022
|
}
|
|
29556
30023
|
}
|
|
29557
|
-
this.
|
|
29558
|
-
this.muxer = null;
|
|
29559
|
-
this.target = null;
|
|
29560
|
-
this.frameCount = 0;
|
|
30024
|
+
this.reset();
|
|
29561
30025
|
}
|
|
29562
30026
|
}
|
|
29563
30027
|
let registeredStart$2 = null;
|
|
@@ -29613,6 +30077,12 @@ function HistoryRecorderBridge() {
|
|
|
29613
30077
|
requestAnimationFrame(() => {
|
|
29614
30078
|
useForgeStore.getState().setHistoryPlaying(true);
|
|
29615
30079
|
});
|
|
30080
|
+
}).catch((err) => {
|
|
30081
|
+
recorderRef.current = null;
|
|
30082
|
+
state2.setHistoryRecording(false);
|
|
30083
|
+
state2.setHistoryPlaying(false);
|
|
30084
|
+
showToast("Recording failed", "error");
|
|
30085
|
+
console.error("History recording failed:", err);
|
|
29616
30086
|
});
|
|
29617
30087
|
}, [gl]);
|
|
29618
30088
|
const stop = reactExports.useCallback(async () => {
|
|
@@ -29664,7 +30134,7 @@ function generateReportInWorker(options) {
|
|
|
29664
30134
|
return new Promise((resolve2, reject) => {
|
|
29665
30135
|
const worker = new Worker(new URL(
|
|
29666
30136
|
/* @vite-ignore */
|
|
29667
|
-
"/assets/reportWorker-
|
|
30137
|
+
"/assets/reportWorker-DO6hcQbh.js",
|
|
29668
30138
|
import.meta.url
|
|
29669
30139
|
), { type: "module" });
|
|
29670
30140
|
const cleanup = () => {
|
|
@@ -30347,9 +30817,13 @@ function buildSketchExportArtifacts(format, stem, runResult) {
|
|
|
30347
30817
|
});
|
|
30348
30818
|
}
|
|
30349
30819
|
let orbitVideoExporter = null;
|
|
30820
|
+
let viewportImageExporter = null;
|
|
30350
30821
|
function registerOrbitVideoExporter(exporter) {
|
|
30351
30822
|
orbitVideoExporter = exporter;
|
|
30352
30823
|
}
|
|
30824
|
+
function registerViewportImageExporter(exporter) {
|
|
30825
|
+
viewportImageExporter = exporter;
|
|
30826
|
+
}
|
|
30353
30827
|
function rerunActiveScriptForQuality(quality) {
|
|
30354
30828
|
const { files, activeFile, paramOverrides } = useForgeStore.getState();
|
|
30355
30829
|
const assemblyState = resolveAssemblyStateForExport();
|
|
@@ -30475,6 +30949,23 @@ async function exportOrbitVideoFromStore(preferredStem, options) {
|
|
|
30475
30949
|
const ext = blob.type === "video/mp4" ? "mp4" : "gif";
|
|
30476
30950
|
triggerDownload(blob, `${stem}.orbit.${ext}`);
|
|
30477
30951
|
}
|
|
30952
|
+
async function captureViewportImageBlobFromStore(options) {
|
|
30953
|
+
if (!viewportImageExporter) {
|
|
30954
|
+
throw new Error("Viewport is not ready for image capture. Try again in a moment.");
|
|
30955
|
+
}
|
|
30956
|
+
const { activeFile, theme } = useForgeStore.getState();
|
|
30957
|
+
return viewportImageExporter({
|
|
30958
|
+
title: deriveExportStem(activeFile),
|
|
30959
|
+
themeName: theme,
|
|
30960
|
+
...options ?? {}
|
|
30961
|
+
});
|
|
30962
|
+
}
|
|
30963
|
+
async function exportViewportImageFromStore(preferredStem, options) {
|
|
30964
|
+
const { activeFile } = useForgeStore.getState();
|
|
30965
|
+
const stem = sanitizeExportStem(deriveExportStem(activeFile));
|
|
30966
|
+
const blob = await captureViewportImageBlobFromStore(options);
|
|
30967
|
+
triggerDownload(blob, `${stem}.png`);
|
|
30968
|
+
}
|
|
30478
30969
|
function exportSketchFromStore(format, preferredStem) {
|
|
30479
30970
|
const { result, activeFile } = useForgeStore.getState();
|
|
30480
30971
|
const stem = sanitizeExportStem(preferredStem ?? deriveExportStem(activeFile));
|
|
@@ -30735,6 +31226,11 @@ function ViewportRecordingBridge({ controlsRef }) {
|
|
|
30735
31226
|
frameIntervalRef.current = 1e3 / TARGET_FPS;
|
|
30736
31227
|
void recorder.start().then(() => {
|
|
30737
31228
|
useForgeStore.getState().setAnimationRecording(true);
|
|
31229
|
+
}).catch((err) => {
|
|
31230
|
+
recorderRef.current = null;
|
|
31231
|
+
useForgeStore.getState().setAnimationRecording(false);
|
|
31232
|
+
showToast("Recording failed", "error");
|
|
31233
|
+
console.error("Viewport recording failed:", err);
|
|
30738
31234
|
});
|
|
30739
31235
|
}, [gl, snapshotOrbitTarget]);
|
|
30740
31236
|
const stop = reactExports.useCallback(async () => {
|
|
@@ -31570,6 +32066,14 @@ function TrajectoryRecorderBridge({ controlsRef }) {
|
|
|
31570
32066
|
state2.setTrajectoryProgress(0);
|
|
31571
32067
|
void recorder.start().then(() => {
|
|
31572
32068
|
state2.setTrajectoryRecording(true);
|
|
32069
|
+
}).catch((err) => {
|
|
32070
|
+
recorderRef.current = null;
|
|
32071
|
+
trajectoryRef.current = null;
|
|
32072
|
+
lastFrameRef.current = 0;
|
|
32073
|
+
manualStartRef.current = 0;
|
|
32074
|
+
state2.setTrajectoryRecording(false);
|
|
32075
|
+
showToast("Recording failed", "error");
|
|
32076
|
+
console.error("Trajectory recording failed:", err);
|
|
31573
32077
|
});
|
|
31574
32078
|
}, [gl]);
|
|
31575
32079
|
const stop = reactExports.useCallback(async () => {
|
|
@@ -32526,6 +33030,164 @@ function OrbitExporterBridge({ controlsRef }) {
|
|
|
32526
33030
|
}, [exportOrbitVideo]);
|
|
32527
33031
|
return null;
|
|
32528
33032
|
}
|
|
33033
|
+
function clampDimension(value, fallback) {
|
|
33034
|
+
const numeric = Number.isFinite(value) ? value : fallback;
|
|
33035
|
+
return Math.max(96, Math.min(4096, Math.round(numeric ?? 96)));
|
|
33036
|
+
}
|
|
33037
|
+
function computeFrameLayout(width, height, includeFrame) {
|
|
33038
|
+
if (!includeFrame) return { x: 0, y: 0, width, height, headerHeight: 0, pad: 0 };
|
|
33039
|
+
const pad = Math.max(14, Math.round(Math.min(width, height) * 0.035));
|
|
33040
|
+
const headerHeight = Math.max(50, Math.min(96, Math.round(height * 0.105)));
|
|
33041
|
+
return {
|
|
33042
|
+
x: pad,
|
|
33043
|
+
y: headerHeight,
|
|
33044
|
+
width: Math.max(1, width - pad * 2),
|
|
33045
|
+
height: Math.max(1, height - headerHeight - pad),
|
|
33046
|
+
headerHeight,
|
|
33047
|
+
pad
|
|
33048
|
+
};
|
|
33049
|
+
}
|
|
33050
|
+
function cssVar(name, fallback) {
|
|
33051
|
+
if (typeof document === "undefined") return fallback;
|
|
33052
|
+
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
33053
|
+
return value || fallback;
|
|
33054
|
+
}
|
|
33055
|
+
function canvasBlob(canvas) {
|
|
33056
|
+
return new Promise((resolve2, reject) => {
|
|
33057
|
+
canvas.toBlob((blob) => blob ? resolve2(blob) : reject(new Error("Could not encode PNG capture.")), "image/png");
|
|
33058
|
+
});
|
|
33059
|
+
}
|
|
33060
|
+
function drawCorner(ctx, x, y, dx, dy, size) {
|
|
33061
|
+
ctx.beginPath();
|
|
33062
|
+
ctx.moveTo(x, y + dy * size);
|
|
33063
|
+
ctx.lineTo(x, y);
|
|
33064
|
+
ctx.lineTo(x + dx * size, y);
|
|
33065
|
+
ctx.stroke();
|
|
33066
|
+
}
|
|
33067
|
+
function composeFrame(source, outputWidth, outputHeight, layout, options) {
|
|
33068
|
+
var _a3;
|
|
33069
|
+
const canvas = document.createElement("canvas");
|
|
33070
|
+
canvas.width = outputWidth;
|
|
33071
|
+
canvas.height = outputHeight;
|
|
33072
|
+
const ctx = canvas.getContext("2d");
|
|
33073
|
+
if (!ctx) throw new Error("Could not create PNG capture context.");
|
|
33074
|
+
const bg = cssVar("--fc-bg", "#02070a");
|
|
33075
|
+
const surface = cssVar("--fc-bgSurface", "#0a2028");
|
|
33076
|
+
const border = cssVar("--fc-border", "#165363");
|
|
33077
|
+
const accent = cssVar("--fc-accent", "#36e8ff");
|
|
33078
|
+
const text = cssVar("--fc-text", "#d9fbff");
|
|
33079
|
+
const muted = cssVar("--fc-textMuted", "#8ebdc6");
|
|
33080
|
+
const dim = cssVar("--fc-textDim", "#4e7078");
|
|
33081
|
+
const title = ((_a3 = options.title) == null ? void 0 : _a3.trim()) || "ForgeCAD model";
|
|
33082
|
+
const themeLabel = options.themeName ? `${options.themeName} theme` : "current theme";
|
|
33083
|
+
ctx.fillStyle = bg;
|
|
33084
|
+
ctx.fillRect(0, 0, outputWidth, outputHeight);
|
|
33085
|
+
ctx.fillStyle = surface;
|
|
33086
|
+
ctx.fillRect(0, 0, outputWidth, layout.headerHeight);
|
|
33087
|
+
ctx.strokeStyle = border;
|
|
33088
|
+
ctx.lineWidth = Math.max(1, Math.round(outputWidth / 900));
|
|
33089
|
+
ctx.strokeRect(layout.x - 1, layout.y - 1, layout.width + 2, layout.height + 2);
|
|
33090
|
+
ctx.drawImage(source, layout.x, layout.y, layout.width, layout.height);
|
|
33091
|
+
ctx.strokeStyle = accent;
|
|
33092
|
+
ctx.globalAlpha = 0.85;
|
|
33093
|
+
ctx.beginPath();
|
|
33094
|
+
ctx.moveTo(layout.pad, layout.headerHeight - 1);
|
|
33095
|
+
ctx.lineTo(outputWidth - layout.pad, layout.headerHeight - 1);
|
|
33096
|
+
ctx.stroke();
|
|
33097
|
+
const corner = Math.max(18, Math.min(42, Math.round(Math.min(outputWidth, outputHeight) * 0.035)));
|
|
33098
|
+
drawCorner(ctx, layout.pad, layout.y, 1, 1, corner);
|
|
33099
|
+
drawCorner(ctx, outputWidth - layout.pad, layout.y, -1, 1, corner);
|
|
33100
|
+
drawCorner(ctx, layout.pad, outputHeight - layout.pad, 1, -1, corner);
|
|
33101
|
+
drawCorner(ctx, outputWidth - layout.pad, outputHeight - layout.pad, -1, -1, corner);
|
|
33102
|
+
ctx.globalAlpha = 1;
|
|
33103
|
+
const mono = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
|
|
33104
|
+
ctx.fillStyle = accent;
|
|
33105
|
+
ctx.font = `700 ${Math.max(11, Math.round(outputHeight * 0.012))}px ${mono}`;
|
|
33106
|
+
ctx.fillText("FORGECAD CAPTURE", layout.pad, Math.round(layout.headerHeight * 0.35));
|
|
33107
|
+
ctx.fillStyle = text;
|
|
33108
|
+
ctx.font = `700 ${Math.max(16, Math.round(outputHeight * 0.021))}px Inter, system-ui, sans-serif`;
|
|
33109
|
+
ctx.fillText(title.slice(0, 64), layout.pad, Math.round(layout.headerHeight * 0.72));
|
|
33110
|
+
ctx.fillStyle = muted;
|
|
33111
|
+
ctx.font = `500 ${Math.max(11, Math.round(outputHeight * 0.012))}px ${mono}`;
|
|
33112
|
+
ctx.textAlign = "right";
|
|
33113
|
+
ctx.fillText(`${outputWidth}x${outputHeight}`, outputWidth - layout.pad, Math.round(layout.headerHeight * 0.35));
|
|
33114
|
+
ctx.fillStyle = dim;
|
|
33115
|
+
ctx.fillText(themeLabel, outputWidth - layout.pad, Math.round(layout.headerHeight * 0.72));
|
|
33116
|
+
ctx.textAlign = "left";
|
|
33117
|
+
return canvas;
|
|
33118
|
+
}
|
|
33119
|
+
function copyRenderedCanvas(source, width, height) {
|
|
33120
|
+
const canvas = document.createElement("canvas");
|
|
33121
|
+
canvas.width = width;
|
|
33122
|
+
canvas.height = height;
|
|
33123
|
+
const ctx = canvas.getContext("2d");
|
|
33124
|
+
if (!ctx) throw new Error("Could not create PNG capture context.");
|
|
33125
|
+
ctx.drawImage(source, 0, 0, width, height);
|
|
33126
|
+
return canvas;
|
|
33127
|
+
}
|
|
33128
|
+
function withCameraAspect(camera, width, height, render) {
|
|
33129
|
+
if (camera instanceof PerspectiveCamera$1) {
|
|
33130
|
+
const previousAspect = camera.aspect;
|
|
33131
|
+
camera.aspect = width / height;
|
|
33132
|
+
camera.updateProjectionMatrix();
|
|
33133
|
+
try {
|
|
33134
|
+
render();
|
|
33135
|
+
} finally {
|
|
33136
|
+
camera.aspect = previousAspect;
|
|
33137
|
+
camera.updateProjectionMatrix();
|
|
33138
|
+
}
|
|
33139
|
+
return;
|
|
33140
|
+
}
|
|
33141
|
+
if (camera instanceof OrthographicCamera$1) {
|
|
33142
|
+
const previous = { left: camera.left, right: camera.right, top: camera.top, bottom: camera.bottom };
|
|
33143
|
+
const midX = (camera.left + camera.right) / 2;
|
|
33144
|
+
const halfHeight = (camera.top - camera.bottom) / 2;
|
|
33145
|
+
const halfWidth = halfHeight * (width / height);
|
|
33146
|
+
camera.left = midX - halfWidth;
|
|
33147
|
+
camera.right = midX + halfWidth;
|
|
33148
|
+
camera.updateProjectionMatrix();
|
|
33149
|
+
try {
|
|
33150
|
+
render();
|
|
33151
|
+
} finally {
|
|
33152
|
+
Object.assign(camera, previous);
|
|
33153
|
+
camera.updateProjectionMatrix();
|
|
33154
|
+
}
|
|
33155
|
+
return;
|
|
33156
|
+
}
|
|
33157
|
+
render();
|
|
33158
|
+
}
|
|
33159
|
+
function ViewportImageCaptureBridge() {
|
|
33160
|
+
const { camera, gl, scene } = useThree();
|
|
33161
|
+
const capture = reactExports.useCallback(
|
|
33162
|
+
async (options) => {
|
|
33163
|
+
const currentSize = gl.getSize(new Vector2());
|
|
33164
|
+
const outputWidth = clampDimension(options == null ? void 0 : options.width, currentSize.x);
|
|
33165
|
+
const outputHeight = clampDimension(options == null ? void 0 : options.height, currentSize.y);
|
|
33166
|
+
const layout = computeFrameLayout(outputWidth, outputHeight, (options == null ? void 0 : options.includeFrame) ?? true);
|
|
33167
|
+
const previousSize = currentSize.clone();
|
|
33168
|
+
const previousPixelRatio = gl.getPixelRatio();
|
|
33169
|
+
try {
|
|
33170
|
+
await waitForAnimationFrame();
|
|
33171
|
+
gl.setPixelRatio(1);
|
|
33172
|
+
gl.setSize(layout.width, layout.height, false);
|
|
33173
|
+
withCameraAspect(camera, layout.width, layout.height, () => gl.render(scene, camera));
|
|
33174
|
+
const rendered = copyRenderedCanvas(gl.domElement, layout.width, layout.height);
|
|
33175
|
+
const finalCanvas = (options == null ? void 0 : options.includeFrame) === false ? rendered : composeFrame(rendered, outputWidth, outputHeight, layout, options ?? {});
|
|
33176
|
+
return await canvasBlob(finalCanvas);
|
|
33177
|
+
} finally {
|
|
33178
|
+
gl.setPixelRatio(previousPixelRatio);
|
|
33179
|
+
gl.setSize(previousSize.x, previousSize.y, false);
|
|
33180
|
+
gl.render(scene, camera);
|
|
33181
|
+
}
|
|
33182
|
+
},
|
|
33183
|
+
[camera, gl, scene]
|
|
33184
|
+
);
|
|
33185
|
+
reactExports.useEffect(() => {
|
|
33186
|
+
registerViewportImageExporter(capture);
|
|
33187
|
+
return () => registerViewportImageExporter(null);
|
|
33188
|
+
}, [capture]);
|
|
33189
|
+
return null;
|
|
33190
|
+
}
|
|
32529
33191
|
const DEBUG_HL_DEFAULT_COLOR = "#ff00ff";
|
|
32530
33192
|
const DEBUG_HL_DEFAULT_POINT_SIZE = 3;
|
|
32531
33193
|
const DEBUG_HL_DEFAULT_PLANE_SIZE = 50;
|
|
@@ -33521,7 +34183,7 @@ function SectionCapPreview({
|
|
|
33521
34183
|
class ScanProxyWorkerClient {
|
|
33522
34184
|
constructor(workerFactory = () => new Worker(new URL(
|
|
33523
34185
|
/* @vite-ignore */
|
|
33524
|
-
"/assets/scanProxyWorker-
|
|
34186
|
+
"/assets/scanProxyWorker-2GtDLk-R.js",
|
|
33525
34187
|
import.meta.url
|
|
33526
34188
|
), { type: "module" })) {
|
|
33527
34189
|
__publicField(this, "worker", null);
|
|
@@ -33897,35 +34559,6 @@ function MatrixGlyphVolume({
|
|
|
33897
34559
|
)
|
|
33898
34560
|
] });
|
|
33899
34561
|
}
|
|
33900
|
-
const SCAN_COLD_RGB = normalizedRgb(SCAN_CONTEXT_EDGE_COLOR);
|
|
33901
|
-
const SCAN_MID_RGB = normalizedRgb(SCAN_RISK_MID_COLOR);
|
|
33902
|
-
const SCAN_HOT_RGB = normalizedRgb(SCAN_RISK_HOT_COLOR);
|
|
33903
|
-
function normalizedRgb([r2, g2, b2]) {
|
|
33904
|
-
return [r2 / 255, g2 / 255, b2 / 255];
|
|
33905
|
-
}
|
|
33906
|
-
function clamp01$1(value) {
|
|
33907
|
-
return Math.max(0, Math.min(1, value));
|
|
33908
|
-
}
|
|
33909
|
-
function mix(a2, b2, t2) {
|
|
33910
|
-
return a2 + (b2 - a2) * clamp01$1(t2);
|
|
33911
|
-
}
|
|
33912
|
-
function scanRiskFromColor(r2, g2, b2) {
|
|
33913
|
-
return clamp01$1(r2 * 1.08 + g2 * 0.48 - b2 * 0.62 - 0.38);
|
|
33914
|
-
}
|
|
33915
|
-
function scanColorBuffer(source) {
|
|
33916
|
-
const out = new Float32Array(source.length);
|
|
33917
|
-
for (let index = 0; index < source.length; index += 3) {
|
|
33918
|
-
const risk = scanRiskFromColor(source[index] ?? 0, source[index + 1] ?? 0, source[index + 2] ?? 0);
|
|
33919
|
-
const lowBand = risk < 0.55;
|
|
33920
|
-
const t2 = lowBand ? risk / 0.55 : (risk - 0.55) / 0.45;
|
|
33921
|
-
const from = lowBand ? SCAN_COLD_RGB : SCAN_MID_RGB;
|
|
33922
|
-
const to = lowBand ? SCAN_MID_RGB : SCAN_HOT_RGB;
|
|
33923
|
-
out[index] = mix(from[0], to[0], t2);
|
|
33924
|
-
out[index + 1] = mix(from[1], to[1], t2);
|
|
33925
|
-
out[index + 2] = mix(from[2], to[2], t2);
|
|
33926
|
-
}
|
|
33927
|
-
return out;
|
|
33928
|
-
}
|
|
33929
34562
|
function SurfaceFieldMaterial({
|
|
33930
34563
|
field,
|
|
33931
34564
|
color: color2,
|
|
@@ -33985,64 +34618,174 @@ function ZebraInspectionMaterial({ clippingPlanes }) {
|
|
|
33985
34618
|
}
|
|
33986
34619
|
const EMPTY_CLIPPING_PLANES$1 = [];
|
|
33987
34620
|
const EMPTY_SECTION_PLANES$1 = [];
|
|
34621
|
+
function scanCellKey(ix, iy, iz) {
|
|
34622
|
+
return `${ix}:${iy}:${iz}`;
|
|
34623
|
+
}
|
|
34624
|
+
function buildDirectScanCellColors(pointCloud, grid, matrix) {
|
|
34625
|
+
const accumulators = /* @__PURE__ */ new Map();
|
|
34626
|
+
const point = new Vector3();
|
|
34627
|
+
const [originX, originY, originZ] = grid.origin;
|
|
34628
|
+
const sampleCount = Math.floor(pointCloud.positions.length / 3);
|
|
34629
|
+
for (let sample = 0; sample < sampleCount; sample += 1) {
|
|
34630
|
+
const offset = sample * 3;
|
|
34631
|
+
point.set(pointCloud.positions[offset], pointCloud.positions[offset + 1], pointCloud.positions[offset + 2]).applyMatrix4(matrix);
|
|
34632
|
+
const ix = Math.floor((point.x - originX) / grid.cellSize);
|
|
34633
|
+
const iy = Math.floor((point.y - originY) / grid.cellSize);
|
|
34634
|
+
const iz = Math.floor((point.z - originZ) / grid.cellSize);
|
|
34635
|
+
const key = scanCellKey(ix, iy, iz);
|
|
34636
|
+
const accumulator = accumulators.get(key) ?? { ix, iy, iz, r: 0, g: 0, b: 0, count: 0 };
|
|
34637
|
+
accumulator.r += pointCloud.colors[offset] ?? 0.35;
|
|
34638
|
+
accumulator.g += pointCloud.colors[offset + 1] ?? 0.35;
|
|
34639
|
+
accumulator.b += pointCloud.colors[offset + 2] ?? 0.35;
|
|
34640
|
+
accumulator.count += 1;
|
|
34641
|
+
accumulators.set(key, accumulator);
|
|
34642
|
+
}
|
|
34643
|
+
const directColors = /* @__PURE__ */ new Map();
|
|
34644
|
+
for (const [key, accumulator] of accumulators) {
|
|
34645
|
+
directColors.set(key, {
|
|
34646
|
+
ix: accumulator.ix,
|
|
34647
|
+
iy: accumulator.iy,
|
|
34648
|
+
iz: accumulator.iz,
|
|
34649
|
+
color: [accumulator.r / accumulator.count, accumulator.g / accumulator.count, accumulator.b / accumulator.count]
|
|
34650
|
+
});
|
|
34651
|
+
}
|
|
34652
|
+
return directColors;
|
|
34653
|
+
}
|
|
34654
|
+
function nearestScanCellColor(ix, iy, iz, coloredCells) {
|
|
34655
|
+
let best = coloredCells[0];
|
|
34656
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
34657
|
+
for (const candidate of coloredCells) {
|
|
34658
|
+
const distance = (candidate.ix - ix) ** 2 + (candidate.iy - iy) ** 2 + (candidate.iz - iz) ** 2;
|
|
34659
|
+
if (distance >= bestDistance) continue;
|
|
34660
|
+
best = candidate;
|
|
34661
|
+
bestDistance = distance;
|
|
34662
|
+
}
|
|
34663
|
+
return (best == null ? void 0 : best.color) ?? [0.35, 0.35, 0.35];
|
|
34664
|
+
}
|
|
34665
|
+
function createScanAnalysisColorGeometry(geometry, pointCloud, grid, matrix) {
|
|
34666
|
+
var _a3;
|
|
34667
|
+
if (!geometry) return null;
|
|
34668
|
+
const position = geometry.getAttribute("position");
|
|
34669
|
+
const scanCell = geometry.getAttribute("scanCell");
|
|
34670
|
+
if (!position || !scanCell || position.count !== scanCell.count) return null;
|
|
34671
|
+
const directColors = buildDirectScanCellColors(pointCloud, grid, matrix);
|
|
34672
|
+
const coloredCells = [...directColors.values()];
|
|
34673
|
+
if (coloredCells.length === 0) return null;
|
|
34674
|
+
const resolvedColors = /* @__PURE__ */ new Map();
|
|
34675
|
+
const colors = new Float32Array(position.count * 3);
|
|
34676
|
+
for (let vertex = 0; vertex < position.count; vertex += 1) {
|
|
34677
|
+
const ix = Math.round(scanCell.getX(vertex));
|
|
34678
|
+
const iy = Math.round(scanCell.getY(vertex));
|
|
34679
|
+
const iz = Math.round(scanCell.getZ(vertex));
|
|
34680
|
+
const key = scanCellKey(ix, iy, iz);
|
|
34681
|
+
let color2 = resolvedColors.get(key);
|
|
34682
|
+
if (!color2) {
|
|
34683
|
+
color2 = ((_a3 = directColors.get(key)) == null ? void 0 : _a3.color) ?? nearestScanCellColor(ix, iy, iz, coloredCells);
|
|
34684
|
+
resolvedColors.set(key, color2);
|
|
34685
|
+
}
|
|
34686
|
+
const offset = vertex * 3;
|
|
34687
|
+
colors[offset] = color2[0];
|
|
34688
|
+
colors[offset + 1] = color2[1];
|
|
34689
|
+
colors[offset + 2] = color2[2];
|
|
34690
|
+
}
|
|
34691
|
+
const coloredGeometry = geometry.clone();
|
|
34692
|
+
coloredGeometry.setAttribute("color", new BufferAttribute(colors, 3));
|
|
34693
|
+
return coloredGeometry;
|
|
34694
|
+
}
|
|
33988
34695
|
function ScanProxyLayer({
|
|
33989
34696
|
geometry,
|
|
33990
34697
|
color: color2,
|
|
33991
34698
|
fillOpacity,
|
|
33992
34699
|
wireOpacity,
|
|
33993
34700
|
clippingPlanes,
|
|
33994
|
-
|
|
34701
|
+
volumeFill = false,
|
|
34702
|
+
showWire = true,
|
|
34703
|
+
analysisGeometry
|
|
33995
34704
|
}) {
|
|
33996
|
-
|
|
34705
|
+
const fillGeometry = analysisGeometry ?? geometry;
|
|
34706
|
+
if (!geometry || !fillGeometry) return null;
|
|
33997
34707
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
33998
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { geometry, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34708
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { geometry: fillGeometry, raycast: () => null, children: volumeFill ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
33999
34709
|
"meshBasicMaterial",
|
|
34000
34710
|
{
|
|
34001
34711
|
color: color2,
|
|
34002
34712
|
transparent: true,
|
|
34003
|
-
opacity: fillOpacity,
|
|
34713
|
+
opacity: Math.min(0.1, fillOpacity * 0.2),
|
|
34004
34714
|
side: DoubleSide,
|
|
34005
34715
|
depthWrite: false,
|
|
34716
|
+
blending: AdditiveBlending,
|
|
34717
|
+
toneMapped: false,
|
|
34718
|
+
clippingPlanes
|
|
34719
|
+
}
|
|
34720
|
+
) : analysisGeometry ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34721
|
+
"meshBasicMaterial",
|
|
34722
|
+
{
|
|
34723
|
+
vertexColors: true,
|
|
34724
|
+
opacity: fillOpacity,
|
|
34725
|
+
side: DoubleSide,
|
|
34726
|
+
alphaHash: true,
|
|
34727
|
+
alphaToCoverage: true,
|
|
34728
|
+
depthWrite: true,
|
|
34729
|
+
toneMapped: false,
|
|
34730
|
+
clippingPlanes
|
|
34731
|
+
}
|
|
34732
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34733
|
+
"meshBasicMaterial",
|
|
34734
|
+
{
|
|
34735
|
+
color: color2,
|
|
34736
|
+
opacity: fillOpacity,
|
|
34737
|
+
side: DoubleSide,
|
|
34738
|
+
alphaHash: true,
|
|
34739
|
+
alphaToCoverage: true,
|
|
34740
|
+
depthWrite: true,
|
|
34006
34741
|
toneMapped: false,
|
|
34007
34742
|
clippingPlanes
|
|
34008
34743
|
}
|
|
34009
34744
|
) }),
|
|
34010
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { geometry, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34745
|
+
showWire && /* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { geometry, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34011
34746
|
"meshBasicMaterial",
|
|
34012
34747
|
{
|
|
34013
34748
|
color: color2,
|
|
34014
|
-
transparent: true,
|
|
34015
34749
|
opacity: wireOpacity,
|
|
34016
34750
|
wireframe: true,
|
|
34017
34751
|
side: DoubleSide,
|
|
34018
|
-
|
|
34019
|
-
|
|
34752
|
+
alphaHash: true,
|
|
34753
|
+
alphaToCoverage: true,
|
|
34754
|
+
depthWrite: true,
|
|
34020
34755
|
toneMapped: false,
|
|
34021
34756
|
clippingPlanes
|
|
34022
34757
|
}
|
|
34023
34758
|
) })
|
|
34024
34759
|
] });
|
|
34025
34760
|
}
|
|
34026
|
-
function objectColorScanLayerStyles(color2) {
|
|
34027
|
-
return SCAN_PROXY_LAYER_STYLES.map((layer) => ({ ...layer, color: color2 }));
|
|
34028
|
-
}
|
|
34029
34761
|
function ScanProxyVolume({
|
|
34030
34762
|
proxy,
|
|
34031
34763
|
clippingPlanes,
|
|
34032
|
-
objectColor
|
|
34764
|
+
objectColor,
|
|
34765
|
+
volumeFill = false,
|
|
34766
|
+
showWire = true,
|
|
34767
|
+
analysisGeometries
|
|
34033
34768
|
}) {
|
|
34034
|
-
const layers = objectColor ?
|
|
34035
|
-
|
|
34036
|
-
|
|
34037
|
-
|
|
34038
|
-
|
|
34039
|
-
|
|
34040
|
-
|
|
34041
|
-
|
|
34042
|
-
|
|
34043
|
-
|
|
34044
|
-
|
|
34045
|
-
|
|
34769
|
+
const layers = reactExports.useMemo(() => objectColor ? scanMaterialLayerStyles(objectColor) : SCAN_PROXY_LAYER_STYLES, [objectColor]);
|
|
34770
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("group", { userData: { scanProxyTelemetry: proxy.telemetry }, children: layers.map((layer) => {
|
|
34771
|
+
const analysisGeometry = (analysisGeometries == null ? void 0 : analysisGeometries[layer.material]) ?? null;
|
|
34772
|
+
const fillOpacity = analysisGeometry ? Math.max(layer.fillOpacity, 0.78) : layer.fillOpacity;
|
|
34773
|
+
const wireOpacity = analysisGeometry ? Math.max(layer.wireOpacity, 0.34) : layer.wireOpacity;
|
|
34774
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34775
|
+
ScanProxyLayer,
|
|
34776
|
+
{
|
|
34777
|
+
geometry: proxy.geometries[layer.material],
|
|
34778
|
+
analysisGeometry,
|
|
34779
|
+
clippingPlanes,
|
|
34780
|
+
volumeFill,
|
|
34781
|
+
showWire,
|
|
34782
|
+
...layer,
|
|
34783
|
+
fillOpacity,
|
|
34784
|
+
wireOpacity
|
|
34785
|
+
},
|
|
34786
|
+
layer.material
|
|
34787
|
+
);
|
|
34788
|
+
}) });
|
|
34046
34789
|
}
|
|
34047
34790
|
function ForgeObject({
|
|
34048
34791
|
obj,
|
|
@@ -34100,11 +34843,10 @@ function ForgeObject({
|
|
|
34100
34843
|
const inspectPointGeo = reactExports.useMemo(() => {
|
|
34101
34844
|
if (!inspectPointCloud) return null;
|
|
34102
34845
|
const geometry = new BufferGeometry();
|
|
34103
|
-
const colors = isScalarScan ? scanColorBuffer(inspectPointCloud.colors) : inspectPointCloud.colors;
|
|
34104
34846
|
geometry.setAttribute("position", new BufferAttribute(inspectPointCloud.positions, 3));
|
|
34105
|
-
geometry.setAttribute("color", new BufferAttribute(colors, 3));
|
|
34847
|
+
geometry.setAttribute("color", new BufferAttribute(inspectPointCloud.colors, 3));
|
|
34106
34848
|
return geometry;
|
|
34107
|
-
}, [inspectPointCloud
|
|
34849
|
+
}, [inspectPointCloud]);
|
|
34108
34850
|
const inspectMeshColorGeo = reactExports.useMemo(() => {
|
|
34109
34851
|
if (!solidGeo || !inspectMeshColors) return null;
|
|
34110
34852
|
const position = solidGeo.getAttribute("position");
|
|
@@ -34123,6 +34865,11 @@ function ForgeObject({
|
|
|
34123
34865
|
);
|
|
34124
34866
|
const scanProxyState = useScanProxyWorkerGeometry(solidGeo, wantsScanProxy, scanProxyGrid, matrix);
|
|
34125
34867
|
const scanProxy = scanProxyState.proxy;
|
|
34868
|
+
const scanAnalysisGeometries = reactExports.useMemo(() => {
|
|
34869
|
+
if (!isScalarScan || !scanProxy || !inspectPointCloud) return null;
|
|
34870
|
+
const shell = createScanAnalysisColorGeometry(scanProxy.geometries.shell, inspectPointCloud, scanProxy.grid, matrix);
|
|
34871
|
+
return shell ? { shell } : null;
|
|
34872
|
+
}, [inspectPointCloud, isScalarScan, matrix, scanProxy]);
|
|
34126
34873
|
reactExports.useEffect(() => {
|
|
34127
34874
|
return () => {
|
|
34128
34875
|
solidGeo == null ? void 0 : solidGeo.dispose();
|
|
@@ -34144,16 +34891,13 @@ function ForgeObject({
|
|
|
34144
34891
|
inspectHeatmapField == null ? void 0 : inspectHeatmapField.texture.dispose();
|
|
34145
34892
|
};
|
|
34146
34893
|
}, [inspectHeatmapField]);
|
|
34894
|
+
reactExports.useEffect(() => {
|
|
34895
|
+
return () => {
|
|
34896
|
+
Object.values(scanAnalysisGeometries ?? {}).forEach((geometry) => geometry == null ? void 0 : geometry.dispose());
|
|
34897
|
+
};
|
|
34898
|
+
}, [scanAnalysisGeometries]);
|
|
34147
34899
|
if (!solidGeo || !settings.visible) return null;
|
|
34148
34900
|
const effectiveRenderMode = isInteracting && renderMode === "overlay" ? "solid" : renderMode;
|
|
34149
|
-
const isInspecting = inspectChannel !== "none";
|
|
34150
|
-
const showInspectHeatmap = Boolean(
|
|
34151
|
-
isScalarInspect && !isScalarScan && inspectHeatmapField && (inspectDisplayMode === "heatmap" || inspectDisplayMode === "both")
|
|
34152
|
-
);
|
|
34153
|
-
const showScalarContextMesh = Boolean(isScalarInspect && (isScalarScan || !showInspectHeatmap));
|
|
34154
|
-
const showInspectPoints = Boolean(
|
|
34155
|
-
isScalarInspect && inspectPointGeo && (inspectDisplayMode === "points" || inspectDisplayMode === "both" || isScalarScan)
|
|
34156
|
-
);
|
|
34157
34901
|
const renderStylePreset = getRenderStylePreset(renderStyle);
|
|
34158
34902
|
const materialDefaults = renderStylePreset.material;
|
|
34159
34903
|
const surfaceField = renderStylePreset.surfaceField;
|
|
@@ -34166,6 +34910,7 @@ function ForgeObject({
|
|
|
34166
34910
|
const materialOpacity = Math.min(meshOpacity, styleOpacity);
|
|
34167
34911
|
const transmission = authoredMaterialTransmission ?? transparentDefaults.transmission;
|
|
34168
34912
|
const isTransparent = materialOpacity < 1 || transmission > 0;
|
|
34913
|
+
const isInspecting = inspectChannel !== "none";
|
|
34169
34914
|
const showSolid = isInspecting || effectiveRenderMode !== "wireframe";
|
|
34170
34915
|
const showEdges = !isInspecting && effectiveRenderMode === "overlay" && renderStyle !== "scan" && renderStyle !== "matrix";
|
|
34171
34916
|
const showWire = !isInspecting && effectiveRenderMode === "wireframe";
|
|
@@ -34177,7 +34922,16 @@ function ForgeObject({
|
|
|
34177
34922
|
const showFloatingObject = inspectChannel !== "floating" || hasInspectMeshColors || inspectColor !== "#000000";
|
|
34178
34923
|
const showScanRenderStyle = showSolid && inspectChannel === "none" && renderStyle === "scan";
|
|
34179
34924
|
const showMatrixRenderStyle = showSolid && inspectChannel === "none" && renderStyle === "matrix";
|
|
34180
|
-
const showScalarScanProxy = showSolid && isScalarScan && scanProxy !== null;
|
|
34925
|
+
const showScalarScanProxy = showSolid && isScalarScan && scanProxy !== null && (scanAnalysisGeometries == null ? void 0 : scanAnalysisGeometries.shell) != null;
|
|
34926
|
+
const showInspectHeatmap = Boolean(
|
|
34927
|
+
isScalarInspect && inspectHeatmapField && (!isScalarScan && (inspectDisplayMode === "heatmap" || inspectDisplayMode === "both") || isScalarScan && !showScalarScanProxy)
|
|
34928
|
+
);
|
|
34929
|
+
const showScalarContextMesh = Boolean(
|
|
34930
|
+
isScalarInspect && (!isScalarScan && !showInspectHeatmap || isScalarScan && !showScalarScanProxy && !showInspectHeatmap)
|
|
34931
|
+
);
|
|
34932
|
+
const showInspectPoints = Boolean(
|
|
34933
|
+
isScalarInspect && inspectPointGeo && (inspectDisplayMode === "points" || inspectDisplayMode === "both")
|
|
34934
|
+
);
|
|
34181
34935
|
const showScanFallbackMesh = showScanRenderStyle && scanProxy === null;
|
|
34182
34936
|
const showMatrixFallbackMesh = showMatrixRenderStyle && scanProxy === null;
|
|
34183
34937
|
const showHiddenPickMesh = showScanRenderStyle || showMatrixRenderStyle || showScalarScanProxy;
|
|
@@ -34256,7 +35010,7 @@ function ForgeObject({
|
|
|
34256
35010
|
clippingPlanes: effectiveClippingPlanes
|
|
34257
35011
|
}
|
|
34258
35012
|
) }),
|
|
34259
|
-
showScalarScanProxy && /* @__PURE__ */ jsxRuntimeExports.jsx(ScanProxyVolume, { proxy: scanProxy, clippingPlanes: effectiveClippingPlanes }),
|
|
35013
|
+
showScalarScanProxy && scanProxy && scanAnalysisGeometries && /* @__PURE__ */ jsxRuntimeExports.jsx(ScanProxyVolume, { proxy: scanProxy, clippingPlanes: effectiveClippingPlanes, analysisGeometries: scanAnalysisGeometries }),
|
|
34260
35014
|
showSolid && showScalarContextMesh && !showScalarScanProxy && /* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { geometry: solidGeo, userData: { forgeMesh: true }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34261
35015
|
"meshBasicMaterial",
|
|
34262
35016
|
{
|
|
@@ -34295,7 +35049,8 @@ function ForgeObject({
|
|
|
34295
35049
|
side: DoubleSide,
|
|
34296
35050
|
toneMapped: false,
|
|
34297
35051
|
clippingPlanes: effectiveClippingPlanes
|
|
34298
|
-
}
|
|
35052
|
+
},
|
|
35053
|
+
inspectHeatmapField.texture.uuid
|
|
34299
35054
|
) }),
|
|
34300
35055
|
showInspectPoints && inspectPointGeo && /* @__PURE__ */ jsxRuntimeExports.jsx("points", { geometry: inspectPointGeo, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34301
35056
|
"pointsMaterial",
|
|
@@ -34340,32 +35095,28 @@ function ForgeObject({
|
|
|
34340
35095
|
) }),
|
|
34341
35096
|
edgesGeo && /* @__PURE__ */ jsxRuntimeExports.jsx("lineSegments", { geometry: edgesGeo, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx("lineBasicMaterial", { color: "#d7e1eb", transparent: true, opacity: 0.36, depthWrite: false, clippingPlanes: effectiveClippingPlanes }) })
|
|
34342
35097
|
] }),
|
|
34343
|
-
showScanRenderStyle && (scanProxy ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34344
|
-
|
|
34345
|
-
|
|
34346
|
-
|
|
34347
|
-
|
|
34348
|
-
|
|
34349
|
-
|
|
34350
|
-
|
|
34351
|
-
|
|
34352
|
-
|
|
34353
|
-
|
|
34354
|
-
|
|
34355
|
-
|
|
34356
|
-
|
|
34357
|
-
|
|
34358
|
-
|
|
34359
|
-
|
|
34360
|
-
|
|
34361
|
-
|
|
34362
|
-
|
|
34363
|
-
|
|
34364
|
-
|
|
34365
|
-
clippingPlanes: effectiveClippingPlanes
|
|
34366
|
-
}
|
|
34367
|
-
) })
|
|
34368
|
-
] })),
|
|
35098
|
+
showScanRenderStyle && (scanProxy ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35099
|
+
ScanProxyVolume,
|
|
35100
|
+
{
|
|
35101
|
+
proxy: scanProxy,
|
|
35102
|
+
clippingPlanes: effectiveClippingPlanes,
|
|
35103
|
+
objectColor: settings.color,
|
|
35104
|
+
volumeFill: true,
|
|
35105
|
+
showWire: false
|
|
35106
|
+
}
|
|
35107
|
+
) : showScanFallbackMesh && /* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { geometry: solidGeo, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35108
|
+
"meshBasicMaterial",
|
|
35109
|
+
{
|
|
35110
|
+
color: scanMaterialShellColor(settings.color),
|
|
35111
|
+
transparent: true,
|
|
35112
|
+
opacity: 0.08,
|
|
35113
|
+
side: DoubleSide,
|
|
35114
|
+
depthWrite: false,
|
|
35115
|
+
blending: AdditiveBlending,
|
|
35116
|
+
toneMapped: false,
|
|
35117
|
+
clippingPlanes: effectiveClippingPlanes
|
|
35118
|
+
}
|
|
35119
|
+
) })),
|
|
34369
35120
|
showMatrixRenderStyle && (scanProxy ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
34370
35121
|
MatrixGlyphVolume,
|
|
34371
35122
|
{
|
|
@@ -34782,9 +35533,12 @@ function colorToHex(color2) {
|
|
|
34782
35533
|
return rgbToHex(color2);
|
|
34783
35534
|
}
|
|
34784
35535
|
function fmt(value) {
|
|
34785
|
-
|
|
35536
|
+
if (!Number.isFinite(value)) return "0";
|
|
35537
|
+
if (Number.isInteger(value)) return String(value);
|
|
35538
|
+
const fixed = Math.abs(value) < 10 ? value.toFixed(2) : value.toFixed(1);
|
|
35539
|
+
return fixed.replace(/\.?0+$/, "");
|
|
34786
35540
|
}
|
|
34787
|
-
function inspectionLegendDefinitionFor(channel, displayMode) {
|
|
35541
|
+
function inspectionLegendDefinitionFor(channel, displayMode, legendOptions = {}) {
|
|
34788
35542
|
switch (channel) {
|
|
34789
35543
|
case "mask":
|
|
34790
35544
|
return {
|
|
@@ -34810,8 +35564,11 @@ function inspectionLegendDefinitionFor(channel, displayMode) {
|
|
|
34810
35564
|
case "rig":
|
|
34811
35565
|
return {
|
|
34812
35566
|
title: "Rig Skeleton",
|
|
34813
|
-
summary: "Geometry is ghosted while joint axes,
|
|
35567
|
+
summary: "Geometry is ghosted while joint axes, link pins, and rigid edges stay bright.",
|
|
34814
35568
|
swatches: [
|
|
35569
|
+
{ label: "Link pin", detail: "kinematic node", color: rgbToHex(RIG_KINEMATIC_LINK_COLOR) },
|
|
35570
|
+
{ label: "Fixed link", detail: "anchored node", color: rgbToHex(RIG_FIXED_LINK_COLOR) },
|
|
35571
|
+
{ label: "Rigid edge", detail: "distance member", color: rgbToHex(RIG_KINEMATIC_EDGE_COLOR) },
|
|
34815
35572
|
{ label: "Joint link", detail: "parent-child chain", color: rgbToHex(RIG_LINK_COLOR) },
|
|
34816
35573
|
{ label: "Part link", detail: "joint to moving body", color: rgbToHex(RIG_PART_LINK_COLOR) },
|
|
34817
35574
|
{ label: "Hidden joint", detail: "fixed follower", color: rgbToHex(RIG_HIDDEN_LINK_COLOR) },
|
|
@@ -34869,33 +35626,25 @@ function inspectionLegendDefinitionFor(channel, displayMode) {
|
|
|
34869
35626
|
]
|
|
34870
35627
|
};
|
|
34871
35628
|
case "thickness": {
|
|
34872
|
-
const
|
|
35629
|
+
const colorRange = legendOptions.thicknessColorRange ?? DEFAULT_THICKNESS_COLOR_RANGE;
|
|
35630
|
+
const midpoint = colorRange.min + (colorRange.max - colorRange.min) / 2;
|
|
35631
|
+
const ramp = {
|
|
35632
|
+
colors: THICKNESS_GRADIENT_COLORS.map(colorToHex),
|
|
35633
|
+
leftLabel: `${fmt(colorRange.min)} mm`,
|
|
35634
|
+
centerLabel: `${fmt(midpoint)} mm`,
|
|
35635
|
+
rightLabel: `${fmt(colorRange.max)} mm`
|
|
35636
|
+
};
|
|
34873
35637
|
if (displayMode === "scan") {
|
|
34874
35638
|
return {
|
|
34875
35639
|
title: "Thickness Scan",
|
|
34876
|
-
summary: "
|
|
34877
|
-
|
|
34878
|
-
{ label: "Shell", detail: "context", color: colorToHex(SCAN_CONTEXT_COLOR) },
|
|
34879
|
-
{ label: "Trace", detail: "surface edge", color: colorToHex(SCAN_CONTEXT_EDGE_COLOR) },
|
|
34880
|
-
{ label: "Warn", detail: `<= ${fmt(options.warnThickness)} mm`, color: colorToHex(SCAN_RISK_MID_COLOR) },
|
|
34881
|
-
{ label: "Critical", detail: `<= ${fmt(options.minThickness)} mm`, color: colorToHex(SCAN_RISK_HOT_COLOR) }
|
|
34882
|
-
]
|
|
35640
|
+
summary: "Each scan box uses the same continuous wall-thickness color ramp as the heatmap.",
|
|
35641
|
+
ramp
|
|
34883
35642
|
};
|
|
34884
35643
|
}
|
|
34885
35644
|
return {
|
|
34886
35645
|
title: "Wall Thickness",
|
|
34887
|
-
summary: "
|
|
34888
|
-
ramp
|
|
34889
|
-
colors: [
|
|
34890
|
-
colorToHex(THICKNESS_COLORS.critical),
|
|
34891
|
-
colorToHex(THICKNESS_COLORS.warning),
|
|
34892
|
-
colorToHex(THICKNESS_COLORS.ok),
|
|
34893
|
-
colorToHex(THICKNESS_COLORS.thick)
|
|
34894
|
-
],
|
|
34895
|
-
leftLabel: `<= ${fmt(options.minThickness)} mm`,
|
|
34896
|
-
centerLabel: `${fmt(options.warnThickness)} mm warn`,
|
|
34897
|
-
rightLabel: `>= ${fmt(options.maxThickness)} mm`
|
|
34898
|
-
}
|
|
35646
|
+
summary: "Color maps continuously from thinner to thicker material across the selected range.",
|
|
35647
|
+
ramp
|
|
34899
35648
|
};
|
|
34900
35649
|
}
|
|
34901
35650
|
case "roughness": {
|
|
@@ -34903,12 +35652,20 @@ function inspectionLegendDefinitionFor(channel, displayMode) {
|
|
|
34903
35652
|
if (displayMode === "scan") {
|
|
34904
35653
|
return {
|
|
34905
35654
|
title: "Roughness Scan",
|
|
34906
|
-
summary: "
|
|
35655
|
+
summary: "Each scan box is colored from the same roughness analysis field as the heatmap.",
|
|
34907
35656
|
swatches: [
|
|
34908
|
-
{ label: "
|
|
34909
|
-
{
|
|
34910
|
-
|
|
34911
|
-
|
|
35657
|
+
{ label: "Smooth", detail: `< ${fmt(options.smoothAngleDeg)} deg`, color: colorToHex(ROUGHNESS_COLORS.smooth) },
|
|
35658
|
+
{
|
|
35659
|
+
label: "Moderate",
|
|
35660
|
+
detail: `${fmt(options.smoothAngleDeg)}-${fmt(options.sharpAngleDeg)} deg`,
|
|
35661
|
+
color: colorToHex(ROUGHNESS_COLORS.moderate)
|
|
35662
|
+
},
|
|
35663
|
+
{
|
|
35664
|
+
label: "Sharp",
|
|
35665
|
+
detail: `${fmt(options.sharpAngleDeg)}-${fmt(options.harshAngleDeg)} deg`,
|
|
35666
|
+
color: colorToHex(ROUGHNESS_COLORS.sharp)
|
|
35667
|
+
},
|
|
35668
|
+
{ label: "Harsh", detail: `>= ${fmt(options.harshAngleDeg)} deg`, color: colorToHex(ROUGHNESS_COLORS.harsh) }
|
|
34912
35669
|
]
|
|
34913
35670
|
};
|
|
34914
35671
|
}
|
|
@@ -34968,6 +35725,78 @@ const swatchGridStyle = {
|
|
|
34968
35725
|
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
34969
35726
|
gap: 6
|
|
34970
35727
|
};
|
|
35728
|
+
const rangeControlsStyle = {
|
|
35729
|
+
display: "grid",
|
|
35730
|
+
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
35731
|
+
gap: 7,
|
|
35732
|
+
marginTop: 7
|
|
35733
|
+
};
|
|
35734
|
+
const rangeLabelStyle = {
|
|
35735
|
+
display: "grid",
|
|
35736
|
+
gap: 3,
|
|
35737
|
+
minWidth: 0,
|
|
35738
|
+
color: "var(--fc-textDim)",
|
|
35739
|
+
fontSize: 10
|
|
35740
|
+
};
|
|
35741
|
+
const rangeInputStyle = {
|
|
35742
|
+
width: "100%",
|
|
35743
|
+
minWidth: 0,
|
|
35744
|
+
boxSizing: "border-box",
|
|
35745
|
+
height: 24,
|
|
35746
|
+
borderRadius: 6,
|
|
35747
|
+
border: "1px solid var(--fc-border)",
|
|
35748
|
+
background: "var(--fc-bg)",
|
|
35749
|
+
color: "var(--fc-text)",
|
|
35750
|
+
font: "inherit",
|
|
35751
|
+
fontSize: 11,
|
|
35752
|
+
padding: "2px 6px",
|
|
35753
|
+
outline: "none"
|
|
35754
|
+
};
|
|
35755
|
+
const dualSliderStyle = {
|
|
35756
|
+
position: "relative",
|
|
35757
|
+
height: 28,
|
|
35758
|
+
marginTop: 9,
|
|
35759
|
+
touchAction: "none"
|
|
35760
|
+
};
|
|
35761
|
+
const sliderTrackStyle = {
|
|
35762
|
+
position: "absolute",
|
|
35763
|
+
left: 0,
|
|
35764
|
+
right: 0,
|
|
35765
|
+
top: 12,
|
|
35766
|
+
height: 5,
|
|
35767
|
+
borderRadius: 999,
|
|
35768
|
+
background: "color-mix(in srgb, var(--fc-border) 76%, transparent)"
|
|
35769
|
+
};
|
|
35770
|
+
const sliderActiveStyle = {
|
|
35771
|
+
position: "absolute",
|
|
35772
|
+
top: 12,
|
|
35773
|
+
height: 5,
|
|
35774
|
+
borderRadius: 999,
|
|
35775
|
+
background: "linear-gradient(90deg, #ff1c1c, #ffde00, #3cdc5a, #4691ff)"
|
|
35776
|
+
};
|
|
35777
|
+
const sliderThumbStyle = {
|
|
35778
|
+
position: "absolute",
|
|
35779
|
+
top: 5,
|
|
35780
|
+
width: 18,
|
|
35781
|
+
height: 18,
|
|
35782
|
+
borderRadius: "50%",
|
|
35783
|
+
border: "2px solid var(--fc-bgPanel)",
|
|
35784
|
+
background: "var(--fc-text)",
|
|
35785
|
+
boxShadow: "0 1px 5px rgba(0, 0, 0, 0.3)",
|
|
35786
|
+
padding: 0,
|
|
35787
|
+
cursor: "grab",
|
|
35788
|
+
touchAction: "none"
|
|
35789
|
+
};
|
|
35790
|
+
const sliderScaleStyle = {
|
|
35791
|
+
display: "grid",
|
|
35792
|
+
gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
|
|
35793
|
+
gap: 6,
|
|
35794
|
+
marginTop: 1,
|
|
35795
|
+
color: "var(--fc-textDim)",
|
|
35796
|
+
fontSize: 9
|
|
35797
|
+
};
|
|
35798
|
+
const SLIDER_STEP = 0.01;
|
|
35799
|
+
const MIN_RANGE_SPAN = 1e-3;
|
|
34971
35800
|
function Ramp({ compact, ramp }) {
|
|
34972
35801
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { minWidth: 0 }, children: [
|
|
34973
35802
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -35024,15 +35853,272 @@ function Swatch({ item }) {
|
|
|
35024
35853
|
] })
|
|
35025
35854
|
] });
|
|
35026
35855
|
}
|
|
35856
|
+
function parseRangeInput(value, fallback) {
|
|
35857
|
+
const parsed = Number(value);
|
|
35858
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
35859
|
+
}
|
|
35860
|
+
function rangeDraftValue(value) {
|
|
35861
|
+
return Number.isFinite(value) ? String(value) : "";
|
|
35862
|
+
}
|
|
35863
|
+
function rangeKey(range) {
|
|
35864
|
+
return `${range.min}:${range.max}`;
|
|
35865
|
+
}
|
|
35866
|
+
function clamp(value, min, max2) {
|
|
35867
|
+
return Math.max(min, Math.min(max2, value));
|
|
35868
|
+
}
|
|
35869
|
+
function snapSliderValue(value) {
|
|
35870
|
+
return Number((Math.round(value / SLIDER_STEP) * SLIDER_STEP).toFixed(2));
|
|
35871
|
+
}
|
|
35872
|
+
function formatSliderTick(value) {
|
|
35873
|
+
if (!Number.isFinite(value)) return "0";
|
|
35874
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(1).replace(/\.0$/, "");
|
|
35875
|
+
}
|
|
35876
|
+
function sliderMaxFor(range) {
|
|
35877
|
+
const target = Math.max(6, range.max * 1.25, range.min + 1);
|
|
35878
|
+
if (target <= 10) return Math.ceil(target);
|
|
35879
|
+
if (target <= 50) return Math.ceil(target / 5) * 5;
|
|
35880
|
+
if (target <= 100) return Math.ceil(target / 10) * 10;
|
|
35881
|
+
return Math.ceil(target / 50) * 50;
|
|
35882
|
+
}
|
|
35883
|
+
function DualRangeSlider({
|
|
35884
|
+
range,
|
|
35885
|
+
sliderMax,
|
|
35886
|
+
onDraftChange,
|
|
35887
|
+
onCommit
|
|
35888
|
+
}) {
|
|
35889
|
+
const trackRef = reactExports.useRef(null);
|
|
35890
|
+
const minPercent = clamp(range.min, 0, sliderMax) / sliderMax * 100;
|
|
35891
|
+
const maxPercent = clamp(range.max, 0, sliderMax) / sliderMax * 100;
|
|
35892
|
+
const valueFromPointer = (event) => {
|
|
35893
|
+
var _a3;
|
|
35894
|
+
const rect = (_a3 = trackRef.current) == null ? void 0 : _a3.getBoundingClientRect();
|
|
35895
|
+
if (!rect || rect.width <= 0) return null;
|
|
35896
|
+
const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1);
|
|
35897
|
+
return snapSliderValue(ratio * sliderMax);
|
|
35898
|
+
};
|
|
35899
|
+
const updateThumb = (thumb, value) => {
|
|
35900
|
+
if (thumb === "min") {
|
|
35901
|
+
onDraftChange({ min: clamp(value, 0, range.max - MIN_RANGE_SPAN), max: range.max });
|
|
35902
|
+
} else {
|
|
35903
|
+
onDraftChange({ min: range.min, max: Math.max(range.min + MIN_RANGE_SPAN, clamp(value, MIN_RANGE_SPAN, sliderMax)) });
|
|
35904
|
+
}
|
|
35905
|
+
};
|
|
35906
|
+
const handlePointerMove = (thumb, event) => {
|
|
35907
|
+
if ((event.buttons & 1) !== 1) return;
|
|
35908
|
+
const value = valueFromPointer(event);
|
|
35909
|
+
if (value == null) return;
|
|
35910
|
+
updateThumb(thumb, value);
|
|
35911
|
+
};
|
|
35912
|
+
const handlePointerDown = (thumb, event) => {
|
|
35913
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
35914
|
+
const value = valueFromPointer(event);
|
|
35915
|
+
if (value != null) updateThumb(thumb, value);
|
|
35916
|
+
};
|
|
35917
|
+
const handleKeyDown = (thumb, event) => {
|
|
35918
|
+
let next = null;
|
|
35919
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowDown") next = range[thumb] - SLIDER_STEP;
|
|
35920
|
+
if (event.key === "ArrowRight" || event.key === "ArrowUp") next = range[thumb] + SLIDER_STEP;
|
|
35921
|
+
if (event.key === "Home") next = thumb === "min" ? 0 : range.min + MIN_RANGE_SPAN;
|
|
35922
|
+
if (event.key === "End") next = thumb === "min" ? range.max - MIN_RANGE_SPAN : sliderMax;
|
|
35923
|
+
if (event.key === "Enter") {
|
|
35924
|
+
event.preventDefault();
|
|
35925
|
+
event.currentTarget.blur();
|
|
35926
|
+
return;
|
|
35927
|
+
}
|
|
35928
|
+
if (next == null) return;
|
|
35929
|
+
event.preventDefault();
|
|
35930
|
+
updateThumb(thumb, snapSliderValue(next));
|
|
35931
|
+
};
|
|
35932
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
35933
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: trackRef, style: dualSliderStyle, children: [
|
|
35934
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: sliderTrackStyle }),
|
|
35935
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35936
|
+
"div",
|
|
35937
|
+
{
|
|
35938
|
+
style: {
|
|
35939
|
+
...sliderActiveStyle,
|
|
35940
|
+
left: `${minPercent}%`,
|
|
35941
|
+
width: `${Math.max(0, maxPercent - minPercent)}%`
|
|
35942
|
+
}
|
|
35943
|
+
}
|
|
35944
|
+
),
|
|
35945
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35946
|
+
"button",
|
|
35947
|
+
{
|
|
35948
|
+
type: "button",
|
|
35949
|
+
role: "slider",
|
|
35950
|
+
"aria-label": "Minimum thickness color",
|
|
35951
|
+
"aria-valuemin": 0,
|
|
35952
|
+
"aria-valuemax": range.max,
|
|
35953
|
+
"aria-valuenow": range.min,
|
|
35954
|
+
onPointerDown: (event) => handlePointerDown("min", event),
|
|
35955
|
+
onPointerMove: (event) => handlePointerMove("min", event),
|
|
35956
|
+
onPointerUp: onCommit,
|
|
35957
|
+
onLostPointerCapture: onCommit,
|
|
35958
|
+
onBlur: onCommit,
|
|
35959
|
+
onKeyDown: (event) => handleKeyDown("min", event),
|
|
35960
|
+
style: {
|
|
35961
|
+
...sliderThumbStyle,
|
|
35962
|
+
left: `${minPercent}%`,
|
|
35963
|
+
transform: "translateX(-50%)",
|
|
35964
|
+
zIndex: range.min >= range.max - SLIDER_STEP ? 3 : 2
|
|
35965
|
+
}
|
|
35966
|
+
}
|
|
35967
|
+
),
|
|
35968
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35969
|
+
"button",
|
|
35970
|
+
{
|
|
35971
|
+
type: "button",
|
|
35972
|
+
role: "slider",
|
|
35973
|
+
"aria-label": "Maximum thickness color",
|
|
35974
|
+
"aria-valuemin": range.min,
|
|
35975
|
+
"aria-valuemax": sliderMax,
|
|
35976
|
+
"aria-valuenow": range.max,
|
|
35977
|
+
onPointerDown: (event) => handlePointerDown("max", event),
|
|
35978
|
+
onPointerMove: (event) => handlePointerMove("max", event),
|
|
35979
|
+
onPointerUp: onCommit,
|
|
35980
|
+
onLostPointerCapture: onCommit,
|
|
35981
|
+
onBlur: onCommit,
|
|
35982
|
+
onKeyDown: (event) => handleKeyDown("max", event),
|
|
35983
|
+
style: {
|
|
35984
|
+
...sliderThumbStyle,
|
|
35985
|
+
left: `${maxPercent}%`,
|
|
35986
|
+
transform: "translateX(-50%)",
|
|
35987
|
+
zIndex: 2,
|
|
35988
|
+
background: "var(--fc-accent, #4f8cff)"
|
|
35989
|
+
}
|
|
35990
|
+
}
|
|
35991
|
+
)
|
|
35992
|
+
] }),
|
|
35993
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: sliderScaleStyle, children: [
|
|
35994
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "0" }),
|
|
35995
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { textAlign: "center" }, children: formatSliderTick(sliderMax / 2) }),
|
|
35996
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { textAlign: "right" }, children: formatSliderTick(sliderMax) })
|
|
35997
|
+
] })
|
|
35998
|
+
] });
|
|
35999
|
+
}
|
|
36000
|
+
function ThicknessRangeControls({
|
|
36001
|
+
compact,
|
|
36002
|
+
range,
|
|
36003
|
+
onChange
|
|
36004
|
+
}) {
|
|
36005
|
+
const [minDraft, setMinDraft] = reactExports.useState(() => rangeDraftValue(range.min));
|
|
36006
|
+
const [maxDraft, setMaxDraft] = reactExports.useState(() => rangeDraftValue(range.max));
|
|
36007
|
+
const [sliderDraft, setSliderDraft] = reactExports.useState(range);
|
|
36008
|
+
const [sliderMax, setSliderMax] = reactExports.useState(() => sliderMaxFor(range));
|
|
36009
|
+
const sliderDraftRef = reactExports.useRef(range);
|
|
36010
|
+
const committedSliderKeyRef = reactExports.useRef(rangeKey(range));
|
|
36011
|
+
const span = Math.max(1, range.max - range.min);
|
|
36012
|
+
reactExports.useEffect(() => {
|
|
36013
|
+
setMinDraft(rangeDraftValue(range.min));
|
|
36014
|
+
setMaxDraft(rangeDraftValue(range.max));
|
|
36015
|
+
setSliderDraft(range);
|
|
36016
|
+
sliderDraftRef.current = range;
|
|
36017
|
+
committedSliderKeyRef.current = rangeKey(range);
|
|
36018
|
+
}, [range.max, range.min]);
|
|
36019
|
+
const updateSliderDraft = (next) => {
|
|
36020
|
+
sliderDraftRef.current = next;
|
|
36021
|
+
setSliderDraft(next);
|
|
36022
|
+
setMinDraft(rangeDraftValue(next.min));
|
|
36023
|
+
setMaxDraft(rangeDraftValue(next.max));
|
|
36024
|
+
};
|
|
36025
|
+
const commitSliderDraft = () => {
|
|
36026
|
+
const next = sliderDraftRef.current;
|
|
36027
|
+
const nextKey = rangeKey(next);
|
|
36028
|
+
if (nextKey === committedSliderKeyRef.current) return;
|
|
36029
|
+
committedSliderKeyRef.current = nextKey;
|
|
36030
|
+
onChange(next);
|
|
36031
|
+
};
|
|
36032
|
+
const commitMin = () => {
|
|
36033
|
+
const value = minDraft.trim();
|
|
36034
|
+
if (value === "") {
|
|
36035
|
+
setMinDraft(rangeDraftValue(range.min));
|
|
36036
|
+
return;
|
|
36037
|
+
}
|
|
36038
|
+
const min = Math.max(0, parseRangeInput(value, range.min));
|
|
36039
|
+
const max2 = range.max <= min ? min + span : range.max;
|
|
36040
|
+
const next = { min, max: max2 };
|
|
36041
|
+
setSliderMax(sliderMaxFor(next));
|
|
36042
|
+
onChange(next);
|
|
36043
|
+
};
|
|
36044
|
+
const commitMax = () => {
|
|
36045
|
+
const value = maxDraft.trim();
|
|
36046
|
+
if (value === "") {
|
|
36047
|
+
setMaxDraft(rangeDraftValue(range.max));
|
|
36048
|
+
return;
|
|
36049
|
+
}
|
|
36050
|
+
const max2 = Math.max(1e-3, parseRangeInput(value, range.max));
|
|
36051
|
+
const min = range.min >= max2 ? Math.max(0, max2 - span) : range.min;
|
|
36052
|
+
const next = { min, max: max2 };
|
|
36053
|
+
setSliderMax(sliderMaxFor(next));
|
|
36054
|
+
onChange(next);
|
|
36055
|
+
};
|
|
36056
|
+
const handleKeyDown = (event, reset) => {
|
|
36057
|
+
if (event.key === "Enter") {
|
|
36058
|
+
event.preventDefault();
|
|
36059
|
+
event.currentTarget.blur();
|
|
36060
|
+
} else if (event.key === "Escape") {
|
|
36061
|
+
event.preventDefault();
|
|
36062
|
+
reset();
|
|
36063
|
+
event.currentTarget.blur();
|
|
36064
|
+
}
|
|
36065
|
+
};
|
|
36066
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
36067
|
+
"div",
|
|
36068
|
+
{
|
|
36069
|
+
style: {
|
|
36070
|
+
...rangeControlsStyle,
|
|
36071
|
+
gridTemplateColumns: compact ? "minmax(0, 1fr)" : rangeControlsStyle.gridTemplateColumns
|
|
36072
|
+
},
|
|
36073
|
+
children: [
|
|
36074
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { gridColumn: "1 / -1" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(DualRangeSlider, { range: sliderDraft, sliderMax, onDraftChange: updateSliderDraft, onCommit: commitSliderDraft }) }),
|
|
36075
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { style: rangeLabelStyle, children: [
|
|
36076
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Min" }),
|
|
36077
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
36078
|
+
"input",
|
|
36079
|
+
{
|
|
36080
|
+
type: "number",
|
|
36081
|
+
min: 0,
|
|
36082
|
+
step: 0.1,
|
|
36083
|
+
value: minDraft,
|
|
36084
|
+
onChange: (event) => setMinDraft(event.currentTarget.value),
|
|
36085
|
+
onBlur: commitMin,
|
|
36086
|
+
onKeyDown: (event) => handleKeyDown(event, () => setMinDraft(rangeDraftValue(range.min))),
|
|
36087
|
+
style: rangeInputStyle
|
|
36088
|
+
}
|
|
36089
|
+
)
|
|
36090
|
+
] }),
|
|
36091
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { style: rangeLabelStyle, children: [
|
|
36092
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Max" }),
|
|
36093
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
36094
|
+
"input",
|
|
36095
|
+
{
|
|
36096
|
+
type: "number",
|
|
36097
|
+
min: 1e-3,
|
|
36098
|
+
step: 0.1,
|
|
36099
|
+
value: maxDraft,
|
|
36100
|
+
onChange: (event) => setMaxDraft(event.currentTarget.value),
|
|
36101
|
+
onBlur: commitMax,
|
|
36102
|
+
onKeyDown: (event) => handleKeyDown(event, () => setMaxDraft(rangeDraftValue(range.max))),
|
|
36103
|
+
style: rangeInputStyle
|
|
36104
|
+
}
|
|
36105
|
+
)
|
|
36106
|
+
] })
|
|
36107
|
+
]
|
|
36108
|
+
}
|
|
36109
|
+
);
|
|
36110
|
+
}
|
|
35027
36111
|
function InspectionLegend({
|
|
35028
36112
|
channel,
|
|
35029
36113
|
displayMode,
|
|
35030
36114
|
warnings,
|
|
35031
|
-
details
|
|
36115
|
+
details,
|
|
36116
|
+
thicknessColorRange,
|
|
36117
|
+
onThicknessColorRangeChange
|
|
35032
36118
|
}) {
|
|
35033
36119
|
const panelRef = reactExports.useRef(null);
|
|
35034
36120
|
const [compact, setCompact] = reactExports.useState(false);
|
|
35035
|
-
const definition = inspectionLegendDefinitionFor(channel, displayMode);
|
|
36121
|
+
const definition = inspectionLegendDefinitionFor(channel, displayMode, { thicknessColorRange });
|
|
35036
36122
|
reactExports.useEffect(() => {
|
|
35037
36123
|
var _a3;
|
|
35038
36124
|
const parent = (_a3 = panelRef.current) == null ? void 0 : _a3.parentElement;
|
|
@@ -35045,7 +36131,12 @@ function InspectionLegend({
|
|
|
35045
36131
|
}, [channel]);
|
|
35046
36132
|
if (!definition) return null;
|
|
35047
36133
|
const warning = warnings[0];
|
|
35048
|
-
const
|
|
36134
|
+
const showThicknessControls = channel === "thickness" && thicknessColorRange !== void 0 && onThicknessColorRangeChange !== void 0;
|
|
36135
|
+
const effectivePanelStyle = {
|
|
36136
|
+
...panelStyle,
|
|
36137
|
+
...compact ? { padding: "7px 8px" } : {},
|
|
36138
|
+
...showThicknessControls ? { pointerEvents: "auto" } : {}
|
|
36139
|
+
};
|
|
35049
36140
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: panelRef, className: "fc-viewport-floating-panel fc-inspection-legend", style: effectivePanelStyle, children: [
|
|
35050
36141
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: titleStyle, children: [
|
|
35051
36142
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: definition.title }),
|
|
@@ -35053,6 +36144,7 @@ function InspectionLegend({
|
|
|
35053
36144
|
] }),
|
|
35054
36145
|
!compact && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: summaryStyle, children: definition.summary }),
|
|
35055
36146
|
definition.ramp && /* @__PURE__ */ jsxRuntimeExports.jsx(Ramp, { compact, ramp: definition.ramp }),
|
|
36147
|
+
showThicknessControls && /* @__PURE__ */ jsxRuntimeExports.jsx(ThicknessRangeControls, { compact, range: thicknessColorRange, onChange: onThicknessColorRangeChange }),
|
|
35056
36148
|
details && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35057
36149
|
"div",
|
|
35058
36150
|
{
|
|
@@ -36672,6 +37764,12 @@ function computeSceneObjectBounds(obj, objectMatrices) {
|
|
|
36672
37764
|
}
|
|
36673
37765
|
return isFiniteBox3(out) ? out : null;
|
|
36674
37766
|
}
|
|
37767
|
+
if (obj.curve3d) {
|
|
37768
|
+
const b2 = obj.curve3d.bounds;
|
|
37769
|
+
const out = new Box3();
|
|
37770
|
+
if (!expandBoundsByTransformedAabb(out, b2.min, b2.max, matrix)) return null;
|
|
37771
|
+
return isFiniteBox3(out) ? out : null;
|
|
37772
|
+
}
|
|
36675
37773
|
if (obj.toolpath) {
|
|
36676
37774
|
const b2 = obj.toolpath.bounds;
|
|
36677
37775
|
const out = new Box3();
|
|
@@ -40322,8 +41420,8 @@ const computeExplodeTreeOffsets = (root, explodeAmount, explodeConfig) => {
|
|
|
40322
41420
|
};
|
|
40323
41421
|
const EMPTY_RIG_INSPECTION_OVERLAY_STATE = {
|
|
40324
41422
|
joints: [],
|
|
40325
|
-
|
|
40326
|
-
|
|
41423
|
+
edges: [],
|
|
41424
|
+
kinematicLinks: [],
|
|
40327
41425
|
bounds: null
|
|
40328
41426
|
};
|
|
40329
41427
|
function isFiniteVector(value) {
|
|
@@ -40388,18 +41486,20 @@ function buildRigInspectionOverlayState(args) {
|
|
|
40388
41486
|
joints.forEach((joint) => {
|
|
40389
41487
|
byChild.set(joint.joint.child, joint);
|
|
40390
41488
|
});
|
|
40391
|
-
const
|
|
40392
|
-
const
|
|
41489
|
+
const edges = [];
|
|
41490
|
+
const kinematicLinks = [];
|
|
40393
41491
|
if (args.assemblyKinematics) {
|
|
40394
|
-
const
|
|
41492
|
+
const derivedNames = new Set(args.assemblyKinematics.derivedLinks.map((link) => link.name));
|
|
41493
|
+
const controlledNames = new Set(args.assemblyKinematics.angles.filter((angle) => angle.control).map((angle) => angle.c));
|
|
40395
41494
|
const connectedLinkNames = /* @__PURE__ */ new Set();
|
|
41495
|
+
const positions = new Map(args.assemblyKinematics.links.map((link) => [link.name, new Vector3(...link.position)]));
|
|
40396
41496
|
for (const edge of args.assemblyKinematics.edges) {
|
|
40397
41497
|
const start = positions.get(edge.a);
|
|
40398
41498
|
const end = positions.get(edge.b);
|
|
40399
41499
|
if (!start || !end || start.distanceToSquared(end) <= 1e-8) continue;
|
|
40400
41500
|
connectedLinkNames.add(edge.a);
|
|
40401
41501
|
connectedLinkNames.add(edge.b);
|
|
40402
|
-
|
|
41502
|
+
edges.push({
|
|
40403
41503
|
id: `kinematic:${edge.name}`,
|
|
40404
41504
|
kind: "kinematic",
|
|
40405
41505
|
start,
|
|
@@ -40407,22 +41507,36 @@ function buildRigInspectionOverlayState(args) {
|
|
|
40407
41507
|
hidden: false
|
|
40408
41508
|
});
|
|
40409
41509
|
}
|
|
41510
|
+
for (const edge of args.assemblyKinematics.frameEdges ?? []) {
|
|
41511
|
+
const start = new Vector3(...edge.start);
|
|
41512
|
+
const end = new Vector3(...edge.end);
|
|
41513
|
+
if (!isFiniteVector(start) || !isFiniteVector(end) || start.distanceToSquared(end) <= 1e-8) continue;
|
|
41514
|
+
edges.push({
|
|
41515
|
+
id: `frame-edge:${edge.name}`,
|
|
41516
|
+
kind: "kinematic",
|
|
41517
|
+
start,
|
|
41518
|
+
end,
|
|
41519
|
+
hidden: false
|
|
41520
|
+
});
|
|
41521
|
+
}
|
|
40410
41522
|
for (const link of args.assemblyKinematics.links) {
|
|
40411
41523
|
const position = positions.get(link.name);
|
|
40412
41524
|
if (!position || !isFiniteVector(position)) continue;
|
|
40413
|
-
|
|
41525
|
+
kinematicLinks.push({
|
|
40414
41526
|
id: `kinematic-link:${link.name}`,
|
|
40415
41527
|
name: link.name,
|
|
41528
|
+
kind: derivedNames.has(link.name) ? "derived" : link.fixed ? "fixed" : link.gaugeFixed ? "gauge" : "moving",
|
|
40416
41529
|
position,
|
|
40417
|
-
|
|
40418
|
-
connected: connectedLinkNames.has(link.name)
|
|
41530
|
+
controlled: controlledNames.has(link.name),
|
|
41531
|
+
connected: connectedLinkNames.has(link.name),
|
|
41532
|
+
hidden: false
|
|
40419
41533
|
});
|
|
40420
41534
|
}
|
|
40421
41535
|
}
|
|
40422
41536
|
for (const joint of joints) {
|
|
40423
41537
|
const parentJoint = joint.joint.parent ? byChild.get(joint.joint.parent) : null;
|
|
40424
41538
|
if (parentJoint && parentJoint.pivotWorld.distanceToSquared(joint.pivotWorld) > 1e-8) {
|
|
40425
|
-
|
|
41539
|
+
edges.push({
|
|
40426
41540
|
id: `${joint.joint.name}:hierarchy`,
|
|
40427
41541
|
kind: "hierarchy",
|
|
40428
41542
|
start: parentJoint.pivotWorld,
|
|
@@ -40432,7 +41546,7 @@ function buildRigInspectionOverlayState(args) {
|
|
|
40432
41546
|
}
|
|
40433
41547
|
const center = childCenter(args, joint.joint.child);
|
|
40434
41548
|
if (center && center.distanceToSquared(joint.pivotWorld) > 1e-8) {
|
|
40435
|
-
|
|
41549
|
+
edges.push({
|
|
40436
41550
|
id: `${joint.joint.name}:part`,
|
|
40437
41551
|
kind: "part",
|
|
40438
41552
|
start: joint.pivotWorld,
|
|
@@ -40443,12 +41557,12 @@ function buildRigInspectionOverlayState(args) {
|
|
|
40443
41557
|
}
|
|
40444
41558
|
const bounds = new Box3();
|
|
40445
41559
|
let hasBounds = false;
|
|
40446
|
-
for (const
|
|
40447
|
-
if (expandFinitePoint(bounds,
|
|
41560
|
+
for (const link of kinematicLinks) {
|
|
41561
|
+
if (expandFinitePoint(bounds, link.position)) hasBounds = true;
|
|
40448
41562
|
}
|
|
40449
|
-
for (const
|
|
40450
|
-
if (expandFinitePoint(bounds,
|
|
40451
|
-
if (expandFinitePoint(bounds,
|
|
41563
|
+
for (const edge of edges) {
|
|
41564
|
+
if (expandFinitePoint(bounds, edge.start)) hasBounds = true;
|
|
41565
|
+
if (expandFinitePoint(bounds, edge.end)) hasBounds = true;
|
|
40452
41566
|
}
|
|
40453
41567
|
for (const joint of joints) {
|
|
40454
41568
|
if (expandFinitePoint(bounds, joint.pivotWorld)) hasBounds = true;
|
|
@@ -40459,7 +41573,7 @@ function buildRigInspectionOverlayState(args) {
|
|
|
40459
41573
|
if (hasBounds) {
|
|
40460
41574
|
bounds.expandByScalar(Math.max(2, args.axisLength * 0.12));
|
|
40461
41575
|
}
|
|
40462
|
-
return { joints,
|
|
41576
|
+
return { joints, edges, kinematicLinks, bounds: hasBounds ? bounds : null };
|
|
40463
41577
|
}
|
|
40464
41578
|
const EMPTY_PLANE_RECORD = Object.freeze({});
|
|
40465
41579
|
const EMPTY_CLIPPING_RECORD = Object.freeze({});
|
|
@@ -40641,6 +41755,8 @@ function useViewportState() {
|
|
|
40641
41755
|
const inspectDisplayMode = useForgeStore((s) => s.inspectDisplayMode);
|
|
40642
41756
|
const scanGranularity = useForgeStore((s) => s.scanGranularity);
|
|
40643
41757
|
const inspectPointSampleCount = useForgeStore((s) => s.inspectPointSampleCount);
|
|
41758
|
+
const thicknessColorRange = useForgeStore((s) => s.thicknessColorRange);
|
|
41759
|
+
const setThicknessColorRange = useForgeStore((s) => s.setThicknessColorRange);
|
|
40644
41760
|
const comparisonInspectMode = useForgeStore((s) => s.comparisonInspectMode);
|
|
40645
41761
|
const comparisonCandidateOpacity = useForgeStore((s) => s.comparisonCandidateOpacity);
|
|
40646
41762
|
const comparisonReferenceOpacity = useForgeStore((s) => s.comparisonReferenceOpacity);
|
|
@@ -41032,6 +42148,8 @@ function useViewportState() {
|
|
|
41032
42148
|
inspectChannel,
|
|
41033
42149
|
inspectDisplayMode,
|
|
41034
42150
|
inspectPointSampleCount,
|
|
42151
|
+
thicknessColorRange,
|
|
42152
|
+
setThicknessColorRange,
|
|
41035
42153
|
comparisonInspectMode,
|
|
41036
42154
|
comparisonCandidateOpacity,
|
|
41037
42155
|
comparisonReferenceOpacity,
|
|
@@ -42088,12 +43206,12 @@ const HoverTooltipLayer = reactExports.forwardRef(function HoverTooltipLayer2({
|
|
|
42088
43206
|
top: 0,
|
|
42089
43207
|
display: "none",
|
|
42090
43208
|
zIndex: 15,
|
|
42091
|
-
background: "var(--fc-floating-panel-bg, #111111d9)",
|
|
43209
|
+
background: "var(--fc-floating-panel-bg, var(--fc-bgPanel, #111111d9))",
|
|
42092
43210
|
color: "var(--fc-text, #f2f2f2)",
|
|
42093
43211
|
padding: "3px 7px",
|
|
42094
43212
|
borderRadius: "var(--fc-floating-panel-radius, 4px)",
|
|
42095
|
-
border: "1px solid var(--fc-floating-panel-border, #2a2a2a)",
|
|
42096
|
-
boxShadow: "var(--fc-floating-panel-shadow,
|
|
43213
|
+
border: "1px solid var(--fc-floating-panel-border, var(--fc-border, #2a2a2a))",
|
|
43214
|
+
boxShadow: "var(--fc-floating-panel-shadow, 0 8px 20px rgba(0, 0, 0, 0.18))",
|
|
42097
43215
|
fontFamily: "var(--fc-hud-font, inherit)",
|
|
42098
43216
|
fontSize: 11,
|
|
42099
43217
|
fontWeight: 600,
|
|
@@ -42424,10 +43542,12 @@ function RenderLabelsOverlay({ labels }) {
|
|
|
42424
43542
|
if (labels.length === 0) return null;
|
|
42425
43543
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("group", { renderOrder: 12, children: labels.map((label) => /* @__PURE__ */ jsxRuntimeExports.jsx(RenderLabelItem, { label }, label.id)) });
|
|
42426
43544
|
}
|
|
42427
|
-
function
|
|
42428
|
-
const segment = reactExports.useMemo(() => resolveSegmentMeshTransform(
|
|
43545
|
+
function RigEdge({ edge, radius }) {
|
|
43546
|
+
const segment = reactExports.useMemo(() => resolveSegmentMeshTransform(edge.start, edge.end), [edge.end, edge.start]);
|
|
42429
43547
|
if (!segment) return null;
|
|
42430
|
-
const color2 = rgbToHex(
|
|
43548
|
+
const color2 = rgbToHex(
|
|
43549
|
+
edge.hidden ? RIG_HIDDEN_LINK_COLOR : edge.kind === "part" ? RIG_PART_LINK_COLOR : edge.kind === "kinematic" ? RIG_KINEMATIC_EDGE_COLOR : RIG_LINK_COLOR
|
|
43550
|
+
);
|
|
42431
43551
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42432
43552
|
"mesh",
|
|
42433
43553
|
{
|
|
@@ -42437,16 +43557,35 @@ function RigLink({ link, radius }) {
|
|
|
42437
43557
|
userData: { measureHelper: true },
|
|
42438
43558
|
children: [
|
|
42439
43559
|
/* @__PURE__ */ jsxRuntimeExports.jsx("cylinderGeometry", { args: [radius, radius, segment.length, 16] }),
|
|
42440
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("meshBasicMaterial", { color: color2, depthTest: false, transparent: true, opacity:
|
|
43560
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("meshBasicMaterial", { color: color2, depthTest: false, transparent: true, opacity: edge.hidden ? 0.72 : 0.96, toneMapped: false })
|
|
42441
43561
|
]
|
|
42442
43562
|
}
|
|
42443
43563
|
);
|
|
42444
43564
|
}
|
|
42445
|
-
function
|
|
42446
|
-
|
|
42447
|
-
|
|
42448
|
-
|
|
42449
|
-
|
|
43565
|
+
function colorForKinematicLink(link) {
|
|
43566
|
+
if (link.kind === "fixed" || link.kind === "gauge") return rgbToHex(RIG_FIXED_LINK_COLOR);
|
|
43567
|
+
if (link.kind === "derived") return rgbToHex(RIG_DERIVED_LINK_COLOR);
|
|
43568
|
+
return rgbToHex(link.connected ? RIG_KINEMATIC_LINK_COLOR : RIG_PART_LINK_COLOR);
|
|
43569
|
+
}
|
|
43570
|
+
function RigKinematicLinkPin({ link, radius }) {
|
|
43571
|
+
const color2 = colorForKinematicLink(link);
|
|
43572
|
+
const position = [link.position.x, link.position.y, link.position.z];
|
|
43573
|
+
const markerRadius = link.kind === "derived" || !link.connected ? radius * 0.72 : radius;
|
|
43574
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("group", { position, renderOrder: 94, userData: { measureHelper: true }, children: [
|
|
43575
|
+
link.kind === "fixed" || link.kind === "gauge" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("mesh", { renderOrder: 94, userData: { measureHelper: true }, children: [
|
|
43576
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("boxGeometry", { args: [radius * 1.75, radius * 1.75, radius * 1.05] }),
|
|
43577
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("meshBasicMaterial", { color: color2, depthTest: false, transparent: true, opacity: 0.98, toneMapped: false })
|
|
43578
|
+
] }) : link.kind === "derived" || !link.connected ? /* @__PURE__ */ jsxRuntimeExports.jsxs("mesh", { renderOrder: 94, userData: { measureHelper: true }, children: [
|
|
43579
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("torusGeometry", { args: [markerRadius * 0.82, markerRadius * 0.18, 8, 24] }),
|
|
43580
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("meshBasicMaterial", { color: color2, depthTest: false, transparent: true, opacity: 0.98, toneMapped: false })
|
|
43581
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("mesh", { renderOrder: 94, userData: { measureHelper: true }, children: [
|
|
43582
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("sphereGeometry", { args: [markerRadius, 18, 18] }),
|
|
43583
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("meshBasicMaterial", { color: color2, depthTest: false, transparent: true, opacity: 0.98, toneMapped: false })
|
|
43584
|
+
] }),
|
|
43585
|
+
link.controlled && /* @__PURE__ */ jsxRuntimeExports.jsxs("mesh", { renderOrder: 95, userData: { measureHelper: true }, children: [
|
|
43586
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("torusGeometry", { args: [radius * 1.32, radius * 0.12, 8, 28] }),
|
|
43587
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("meshBasicMaterial", { color: rgbToHex(RIG_CONTROL_LINK_COLOR), depthTest: false, transparent: true, opacity: 0.98, toneMapped: false })
|
|
43588
|
+
] })
|
|
42450
43589
|
] });
|
|
42451
43590
|
}
|
|
42452
43591
|
function RigInspectionOverlay({ state: state2, config }) {
|
|
@@ -42462,16 +43601,21 @@ function RigInspectionOverlay({ state: state2, config }) {
|
|
|
42462
43601
|
[config, hiddenColor]
|
|
42463
43602
|
);
|
|
42464
43603
|
const maxAxisLength = reactExports.useMemo(() => state2.joints.reduce((max2, joint) => Math.max(max2, joint.axisLength), 0), [state2.joints]);
|
|
42465
|
-
const
|
|
42466
|
-
|
|
42467
|
-
config.
|
|
42468
|
-
config.
|
|
43604
|
+
const scaleBasis = Math.max(maxAxisLength, config.axisLengthMin);
|
|
43605
|
+
const edgeRadius = MathUtils.clamp(
|
|
43606
|
+
scaleBasis * config.axisLineRadiusScale * 1.15,
|
|
43607
|
+
config.axisLineRadiusMin * 1.15,
|
|
43608
|
+
config.axisLineRadiusMax * 1.75
|
|
43609
|
+
);
|
|
43610
|
+
const linkPinRadius = MathUtils.clamp(
|
|
43611
|
+
scaleBasis * config.axisDotRadiusScale * 3.2,
|
|
43612
|
+
config.axisDotRadiusMin * 2.2,
|
|
43613
|
+
config.axisLineRadiusMax * 3.2
|
|
42469
43614
|
);
|
|
42470
|
-
|
|
42471
|
-
if (state2.joints.length === 0 && state2.links.length === 0 && state2.points.length === 0) return null;
|
|
43615
|
+
if (state2.joints.length === 0 && state2.edges.length === 0 && state2.kinematicLinks.length === 0) return null;
|
|
42472
43616
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("group", { children: [
|
|
42473
|
-
state2.
|
|
42474
|
-
state2.
|
|
43617
|
+
state2.edges.map((edge) => /* @__PURE__ */ jsxRuntimeExports.jsx(RigEdge, { edge, radius: edgeRadius }, edge.id)),
|
|
43618
|
+
state2.kinematicLinks.map((link) => /* @__PURE__ */ jsxRuntimeExports.jsx(RigKinematicLinkPin, { link, radius: linkPinRadius }, link.id)),
|
|
42475
43619
|
state2.joints.map((joint) => /* @__PURE__ */ jsxRuntimeExports.jsx(HoveredJointOverlay, { state: joint, config: joint.hidden ? hiddenConfig : config }, joint.joint.name))
|
|
42476
43620
|
] });
|
|
42477
43621
|
}
|
|
@@ -42493,7 +43637,7 @@ function directCadKindForPath(path) {
|
|
|
42493
43637
|
return null;
|
|
42494
43638
|
}
|
|
42495
43639
|
function directCadReferenceCode(path, kind) {
|
|
42496
|
-
const fn = kind === "step" ? "
|
|
43640
|
+
const fn = kind === "step" ? "Import.step" : "Import.mesh";
|
|
42497
43641
|
const name = basename(path);
|
|
42498
43642
|
return [`const imported = ${fn}(${JSON.stringify(path)});`, `return [{ name: ${JSON.stringify(name)}, shape: imported }];`].join("\n");
|
|
42499
43643
|
}
|
|
@@ -42647,12 +43791,12 @@ function useGeometryComparison(args) {
|
|
|
42647
43791
|
class InspectWorkerClient {
|
|
42648
43792
|
constructor(workerFactory = () => new Worker(new URL(
|
|
42649
43793
|
/* @vite-ignore */
|
|
42650
|
-
"/assets/inspectWorker-
|
|
43794
|
+
"/assets/inspectWorker-CZsCFtQT.js",
|
|
42651
43795
|
import.meta.url
|
|
42652
43796
|
), { type: "module" })) {
|
|
42653
43797
|
__publicField(this, "worker", null);
|
|
42654
43798
|
__publicField(this, "reqId", 0);
|
|
42655
|
-
__publicField(this, "pending",
|
|
43799
|
+
__publicField(this, "pending", /* @__PURE__ */ new Map());
|
|
42656
43800
|
this.workerFactory = workerFactory;
|
|
42657
43801
|
}
|
|
42658
43802
|
getWorker() {
|
|
@@ -42660,9 +43804,9 @@ class InspectWorkerClient {
|
|
|
42660
43804
|
this.worker = this.workerFactory();
|
|
42661
43805
|
this.worker.onmessage = (event) => {
|
|
42662
43806
|
const data = event.data;
|
|
42663
|
-
const pending = this.pending;
|
|
43807
|
+
const pending = this.pending.get(data.payload.reqId);
|
|
42664
43808
|
if (!pending) return;
|
|
42665
|
-
this.pending
|
|
43809
|
+
this.pending.delete(data.payload.reqId);
|
|
42666
43810
|
if (data.type === "inspect-error") {
|
|
42667
43811
|
pending.reject(new Error(data.payload.message));
|
|
42668
43812
|
} else {
|
|
@@ -42670,22 +43814,28 @@ class InspectWorkerClient {
|
|
|
42670
43814
|
}
|
|
42671
43815
|
};
|
|
42672
43816
|
this.worker.onerror = (event) => {
|
|
42673
|
-
var _a3
|
|
43817
|
+
var _a3;
|
|
42674
43818
|
const error = new Error(event.message || "Inspect worker failed unexpectedly.");
|
|
42675
|
-
|
|
42676
|
-
this.pending
|
|
42677
|
-
(
|
|
43819
|
+
this.pending.forEach((pending) => pending.reject(error));
|
|
43820
|
+
this.pending.clear();
|
|
43821
|
+
(_a3 = this.worker) == null ? void 0 : _a3.terminate();
|
|
42678
43822
|
this.worker = null;
|
|
42679
43823
|
};
|
|
42680
43824
|
return this.worker;
|
|
42681
43825
|
}
|
|
42682
43826
|
replaceActiveWorker() {
|
|
42683
|
-
var _a3
|
|
42684
|
-
|
|
42685
|
-
this.pending
|
|
42686
|
-
(
|
|
43827
|
+
var _a3;
|
|
43828
|
+
this.pending.forEach((pending) => pending.reject(new Error("cancelled")));
|
|
43829
|
+
this.pending.clear();
|
|
43830
|
+
(_a3 = this.worker) == null ? void 0 : _a3.terminate();
|
|
42687
43831
|
this.worker = null;
|
|
42688
43832
|
}
|
|
43833
|
+
postRequest(request, transfers = []) {
|
|
43834
|
+
return new Promise((resolve2, reject) => {
|
|
43835
|
+
this.pending.set(request.payload.reqId, { resolve: resolve2, reject });
|
|
43836
|
+
this.getWorker().postMessage(request, transfers);
|
|
43837
|
+
});
|
|
43838
|
+
}
|
|
42689
43839
|
analyze(payload) {
|
|
42690
43840
|
if (this.pending) this.replaceActiveWorker();
|
|
42691
43841
|
const reqId = ++this.reqId;
|
|
@@ -42705,10 +43855,18 @@ class InspectWorkerClient {
|
|
|
42705
43855
|
object.collisionShape.mergeToVert.buffer
|
|
42706
43856
|
] : []
|
|
42707
43857
|
]);
|
|
42708
|
-
return
|
|
42709
|
-
|
|
42710
|
-
|
|
42711
|
-
|
|
43858
|
+
return this.postRequest(request, transfers);
|
|
43859
|
+
}
|
|
43860
|
+
colorizeThickness(payload) {
|
|
43861
|
+
const reqId = ++this.reqId;
|
|
43862
|
+
const request = {
|
|
43863
|
+
type: "colorize-thickness",
|
|
43864
|
+
payload: {
|
|
43865
|
+
reqId,
|
|
43866
|
+
...payload
|
|
43867
|
+
}
|
|
43868
|
+
};
|
|
43869
|
+
return this.postRequest(request);
|
|
42712
43870
|
}
|
|
42713
43871
|
dispose() {
|
|
42714
43872
|
this.replaceActiveWorker();
|
|
@@ -43039,6 +44197,7 @@ function analyzePayloadFor(channel, objects, inspectPointSampleCount, groundZ, w
|
|
|
43039
44197
|
}
|
|
43040
44198
|
function resultToState(channel, result) {
|
|
43041
44199
|
return {
|
|
44200
|
+
analysisId: result.analysisId,
|
|
43042
44201
|
status: "ready",
|
|
43043
44202
|
channel,
|
|
43044
44203
|
objectColors: result.objectColors,
|
|
@@ -43061,7 +44220,8 @@ function resultToState(channel, result) {
|
|
|
43061
44220
|
{
|
|
43062
44221
|
sampleCount: object.sampleCount,
|
|
43063
44222
|
positions: object.positions,
|
|
43064
|
-
colors: object.colors
|
|
44223
|
+
colors: object.colors,
|
|
44224
|
+
values: object.values
|
|
43065
44225
|
}
|
|
43066
44226
|
])
|
|
43067
44227
|
),
|
|
@@ -43069,8 +44229,40 @@ function resultToState(channel, result) {
|
|
|
43069
44229
|
error: null
|
|
43070
44230
|
};
|
|
43071
44231
|
}
|
|
44232
|
+
function applyThicknessColorizeResult(state2, result) {
|
|
44233
|
+
if (state2.channel !== "thickness" || state2.status !== "ready") return state2;
|
|
44234
|
+
if (state2.analysisId !== result.analysisId) return state2;
|
|
44235
|
+
const colorsByObjectId = new Map(result.pointObjects.map((object) => [object.objectId, object.colors]));
|
|
44236
|
+
const pointClouds = Object.fromEntries(
|
|
44237
|
+
Object.entries(state2.pointClouds).map(([objectId, pointCloud]) => [
|
|
44238
|
+
objectId,
|
|
44239
|
+
{
|
|
44240
|
+
...pointCloud,
|
|
44241
|
+
colors: colorsByObjectId.get(objectId) ?? pointCloud.colors
|
|
44242
|
+
}
|
|
44243
|
+
])
|
|
44244
|
+
);
|
|
44245
|
+
return {
|
|
44246
|
+
...state2,
|
|
44247
|
+
pointClouds,
|
|
44248
|
+
heatmapFields: Object.fromEntries(
|
|
44249
|
+
result.heatmapFieldObjects.map((object) => [
|
|
44250
|
+
object.objectId,
|
|
44251
|
+
{
|
|
44252
|
+
data: object.data,
|
|
44253
|
+
boundsMin: object.boundsMin,
|
|
44254
|
+
boundsSize: object.boundsSize,
|
|
44255
|
+
gridSize: object.gridSize
|
|
44256
|
+
}
|
|
44257
|
+
])
|
|
44258
|
+
)
|
|
44259
|
+
};
|
|
44260
|
+
}
|
|
44261
|
+
function thicknessColorRangeKey(range) {
|
|
44262
|
+
return `${range.min}:${range.max}`;
|
|
44263
|
+
}
|
|
43072
44264
|
function useInspectWorkerAnalysis(args) {
|
|
43073
|
-
const [
|
|
44265
|
+
const [rawState, setRawState] = reactExports.useState({
|
|
43074
44266
|
status: "idle",
|
|
43075
44267
|
channel: "none",
|
|
43076
44268
|
objectColors: {},
|
|
@@ -43081,10 +44273,11 @@ function useInspectWorkerAnalysis(args) {
|
|
|
43081
44273
|
warnings: [],
|
|
43082
44274
|
error: null
|
|
43083
44275
|
});
|
|
44276
|
+
const [colorizedState, setColorizedState] = reactExports.useState(null);
|
|
43084
44277
|
reactExports.useEffect(() => {
|
|
43085
44278
|
if (!WORKER_CHANNELS.has(args.inspectChannel)) {
|
|
43086
44279
|
inspectWorkerClient.dispose();
|
|
43087
|
-
|
|
44280
|
+
setRawState({
|
|
43088
44281
|
status: "idle",
|
|
43089
44282
|
channel: args.inspectChannel,
|
|
43090
44283
|
objectColors: {},
|
|
@@ -43099,7 +44292,7 @@ function useInspectWorkerAnalysis(args) {
|
|
|
43099
44292
|
}
|
|
43100
44293
|
let cancelled = false;
|
|
43101
44294
|
const channel = args.inspectChannel;
|
|
43102
|
-
|
|
44295
|
+
setRawState({
|
|
43103
44296
|
status: "loading",
|
|
43104
44297
|
channel: args.inspectChannel,
|
|
43105
44298
|
objectColors: {},
|
|
@@ -43133,12 +44326,13 @@ function useInspectWorkerAnalysis(args) {
|
|
|
43133
44326
|
)
|
|
43134
44327
|
);
|
|
43135
44328
|
if (cancelled) return;
|
|
43136
|
-
|
|
44329
|
+
setRawState(resultToState(args.inspectChannel, result));
|
|
44330
|
+
setColorizedState(null);
|
|
43137
44331
|
} catch (error) {
|
|
43138
44332
|
if (cancelled || error instanceof InspectBuildCancelledError || error instanceof Error && error.message === "cancelled") {
|
|
43139
44333
|
return;
|
|
43140
44334
|
}
|
|
43141
|
-
|
|
44335
|
+
setRawState({
|
|
43142
44336
|
status: "error",
|
|
43143
44337
|
channel: args.inspectChannel,
|
|
43144
44338
|
objectColors: {},
|
|
@@ -43164,7 +44358,179 @@ function useInspectWorkerAnalysis(args) {
|
|
|
43164
44358
|
args.objectMatrices,
|
|
43165
44359
|
args.groundZ
|
|
43166
44360
|
]);
|
|
43167
|
-
|
|
44361
|
+
reactExports.useEffect(() => {
|
|
44362
|
+
if (rawState.status !== "ready" || rawState.channel !== "thickness" || rawState.analysisId === void 0) {
|
|
44363
|
+
setColorizedState(null);
|
|
44364
|
+
return;
|
|
44365
|
+
}
|
|
44366
|
+
let cancelled = false;
|
|
44367
|
+
const analysisId = rawState.analysisId;
|
|
44368
|
+
const rangeKey22 = thicknessColorRangeKey(args.thicknessColorRange);
|
|
44369
|
+
void inspectWorkerClient.colorizeThickness({
|
|
44370
|
+
analysisId,
|
|
44371
|
+
colorMinThickness: args.thicknessColorRange.min,
|
|
44372
|
+
colorMaxThickness: args.thicknessColorRange.max
|
|
44373
|
+
}).then((result) => {
|
|
44374
|
+
if (cancelled) return;
|
|
44375
|
+
setColorizedState({
|
|
44376
|
+
analysisId,
|
|
44377
|
+
rangeKey: rangeKey22,
|
|
44378
|
+
state: applyThicknessColorizeResult(rawState, result)
|
|
44379
|
+
});
|
|
44380
|
+
}).catch((error) => {
|
|
44381
|
+
if (cancelled || error instanceof Error && error.message === "cancelled") return;
|
|
44382
|
+
setColorizedState(null);
|
|
44383
|
+
});
|
|
44384
|
+
return () => {
|
|
44385
|
+
cancelled = true;
|
|
44386
|
+
};
|
|
44387
|
+
}, [args.thicknessColorRange, rawState]);
|
|
44388
|
+
const rangeKey2 = thicknessColorRangeKey(args.thicknessColorRange);
|
|
44389
|
+
if (rawState.status === "ready" && rawState.channel === "thickness" && rawState.analysisId !== void 0 && (colorizedState == null ? void 0 : colorizedState.analysisId) === rawState.analysisId && colorizedState.rangeKey === rangeKey2) {
|
|
44390
|
+
return colorizedState.state;
|
|
44391
|
+
}
|
|
44392
|
+
return rawState;
|
|
44393
|
+
}
|
|
44394
|
+
function voxelCellKey(cell) {
|
|
44395
|
+
return `${cell.x},${cell.y},${cell.z}`;
|
|
44396
|
+
}
|
|
44397
|
+
function resolveVoxelIntentCellSize(cellSize) {
|
|
44398
|
+
return Math.max(1, Number.isFinite(cellSize) ? cellSize : 10);
|
|
44399
|
+
}
|
|
44400
|
+
function voxelCellFromPoint(point, cellSize) {
|
|
44401
|
+
return {
|
|
44402
|
+
x: Math.floor(point.x / cellSize),
|
|
44403
|
+
y: Math.floor(point.y / cellSize),
|
|
44404
|
+
z: Math.floor(point.z / cellSize)
|
|
44405
|
+
};
|
|
44406
|
+
}
|
|
44407
|
+
function dominantAxis(normal) {
|
|
44408
|
+
const ax = Math.abs(normal.x);
|
|
44409
|
+
const ay = Math.abs(normal.y);
|
|
44410
|
+
const az = Math.abs(normal.z);
|
|
44411
|
+
if (ax >= ay && ax >= az) return "x";
|
|
44412
|
+
if (ay >= ax && ay >= az) return "y";
|
|
44413
|
+
return "z";
|
|
44414
|
+
}
|
|
44415
|
+
function anchoredCellIndex(value, cellSize, direction) {
|
|
44416
|
+
const scaled = value / cellSize;
|
|
44417
|
+
const base = Math.floor(scaled);
|
|
44418
|
+
const onBoundary = Math.abs(scaled - Math.round(scaled)) < 1e-7;
|
|
44419
|
+
return onBoundary && direction < 0 ? base - 1 : base;
|
|
44420
|
+
}
|
|
44421
|
+
function voxelCellFromSurfaceAnchor(point, normal, cellSize, side) {
|
|
44422
|
+
const cell = voxelCellFromPoint(point, cellSize);
|
|
44423
|
+
const axis = dominantAxis(normal);
|
|
44424
|
+
const normalValue = normal[axis];
|
|
44425
|
+
const outsideDirection = normalValue < 0 ? -1 : 1;
|
|
44426
|
+
const direction = side === "outside" ? outsideDirection : -outsideDirection;
|
|
44427
|
+
cell[axis] = anchoredCellIndex(point[axis], cellSize, direction);
|
|
44428
|
+
return cell;
|
|
44429
|
+
}
|
|
44430
|
+
function parseVoxelCellKey(key) {
|
|
44431
|
+
const [x, y, z] = key.split(",").map(Number);
|
|
44432
|
+
return { x, y, z };
|
|
44433
|
+
}
|
|
44434
|
+
function addVoxelCells(a2, b2) {
|
|
44435
|
+
return { x: a2.x + b2.x, y: a2.y + b2.y, z: a2.z + b2.z };
|
|
44436
|
+
}
|
|
44437
|
+
function voxelCellCenter(cell, cellSize) {
|
|
44438
|
+
return [(cell.x + 0.5) * cellSize, (cell.y + 0.5) * cellSize, (cell.z + 0.5) * cellSize];
|
|
44439
|
+
}
|
|
44440
|
+
const VOXEL_INTENT_TOOL_COLORS = {
|
|
44441
|
+
red: "#ef4444",
|
|
44442
|
+
green: "#22c55e",
|
|
44443
|
+
blue: "#3b82f6",
|
|
44444
|
+
yellow: "#facc15",
|
|
44445
|
+
white: "#f8fafc",
|
|
44446
|
+
erase: "#a855f7"
|
|
44447
|
+
};
|
|
44448
|
+
const VOXEL_INTENT_TOOL_LABELS = {
|
|
44449
|
+
red: { label: "Red", detail: "mark a red thing to explain in chat" },
|
|
44450
|
+
green: { label: "Green", detail: "mark a green thing to explain in chat" },
|
|
44451
|
+
blue: { label: "Blue", detail: "mark a blue thing to explain in chat" },
|
|
44452
|
+
yellow: { label: "Yellow", detail: "mark a yellow thing to explain in chat" },
|
|
44453
|
+
white: { label: "White", detail: "mark a neutral thing to explain in chat" },
|
|
44454
|
+
erase: { label: "Eraser", detail: "delete the first marker block clicked" }
|
|
44455
|
+
};
|
|
44456
|
+
const VOXEL_INTENT_TOOL_ORDER = ["red", "green", "blue", "yellow", "white", "erase"];
|
|
44457
|
+
const VOXEL_INTENT_PLACEMENT_LABELS = {
|
|
44458
|
+
surface: { label: "Surface", detail: "place outside the clicked face" },
|
|
44459
|
+
inside: { label: "Inside", detail: "place just under the first clicked surface" },
|
|
44460
|
+
through: { label: "Through", detail: "place between the first and second surface hits" },
|
|
44461
|
+
grid: { label: "Grid", detail: "place from the click point in the global grid" }
|
|
44462
|
+
};
|
|
44463
|
+
const VOXEL_INTENT_PLACEMENT_ORDER = ["surface", "inside", "through", "grid"];
|
|
44464
|
+
function directionFromFace(event) {
|
|
44465
|
+
var _a3;
|
|
44466
|
+
const normal = (_a3 = event.face) == null ? void 0 : _a3.normal;
|
|
44467
|
+
if (!normal) return { x: 0, y: 0, z: 1 };
|
|
44468
|
+
const values = [
|
|
44469
|
+
{ axis: "x", value: normal.x },
|
|
44470
|
+
{ axis: "y", value: normal.y },
|
|
44471
|
+
{ axis: "z", value: normal.z }
|
|
44472
|
+
];
|
|
44473
|
+
values.sort((a2, b2) => Math.abs(b2.value) - Math.abs(a2.value));
|
|
44474
|
+
const strongest = values[0];
|
|
44475
|
+
return {
|
|
44476
|
+
x: strongest.axis === "x" ? Math.sign(strongest.value) || 1 : 0,
|
|
44477
|
+
y: strongest.axis === "y" ? Math.sign(strongest.value) || 1 : 0,
|
|
44478
|
+
z: strongest.axis === "z" ? Math.sign(strongest.value) || 1 : 0
|
|
44479
|
+
};
|
|
44480
|
+
}
|
|
44481
|
+
function VoxelIntentCellMesh({
|
|
44482
|
+
cell,
|
|
44483
|
+
color: color2,
|
|
44484
|
+
cellSize,
|
|
44485
|
+
opacity,
|
|
44486
|
+
onClick
|
|
44487
|
+
}) {
|
|
44488
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("mesh", { position: voxelCellCenter(cell, cellSize), onClick, renderOrder: 60, userData: { voxelIntentCell: cell }, children: [
|
|
44489
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("boxGeometry", { args: [cellSize * 0.96, cellSize * 0.96, cellSize * 0.96] }),
|
|
44490
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("meshStandardMaterial", { color: color2, roughness: 0.62, metalness: 0.04, transparent: true, opacity, depthWrite: opacity > 0.55 })
|
|
44491
|
+
] });
|
|
44492
|
+
}
|
|
44493
|
+
function VoxelIntentOverlay() {
|
|
44494
|
+
const tool = useForgeStore((s) => s.voxelIntentTool);
|
|
44495
|
+
const placement = useForgeStore((s) => s.voxelIntentPlacement);
|
|
44496
|
+
const cellSize = useForgeStore((s) => s.voxelIntentCellSize);
|
|
44497
|
+
const blocks = useForgeStore((s) => s.voxelIntentBlocks);
|
|
44498
|
+
const cellSizePreviewVisible = useForgeStore((s) => s.voxelIntentCellSizePreviewVisible);
|
|
44499
|
+
const setBlock = useForgeStore((s) => s.setVoxelIntentBlock);
|
|
44500
|
+
const eraseBlock = useForgeStore((s) => s.eraseVoxelIntentBlock);
|
|
44501
|
+
const resolvedCellSize = resolveVoxelIntentCellSize(cellSize);
|
|
44502
|
+
const blockEntries = reactExports.useMemo(() => Object.entries(blocks).map(([key, block]) => ({ cell: parseVoxelCellKey(key), block })), [blocks]);
|
|
44503
|
+
const occupied = (cell) => voxelCellKey(cell) in blocks;
|
|
44504
|
+
const placeAdjacentTo = (blockCell, event) => {
|
|
44505
|
+
event.stopPropagation();
|
|
44506
|
+
if (tool === "erase") {
|
|
44507
|
+
const clickedCell = event.object.userData.voxelIntentCell ?? blockCell;
|
|
44508
|
+
eraseBlock(clickedCell);
|
|
44509
|
+
return;
|
|
44510
|
+
}
|
|
44511
|
+
const faceCandidate = addVoxelCells(blockCell, directionFromFace(event));
|
|
44512
|
+
if (!occupied(faceCandidate)) {
|
|
44513
|
+
setBlock(faceCandidate, { groupId: tool, placement });
|
|
44514
|
+
return;
|
|
44515
|
+
}
|
|
44516
|
+
};
|
|
44517
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("group", { children: [
|
|
44518
|
+
cellSizePreviewVisible && /* @__PURE__ */ jsxRuntimeExports.jsxs("mesh", { position: voxelCellCenter({ x: 0, y: 0, z: 0 }, resolvedCellSize), renderOrder: 55, raycast: () => null, children: [
|
|
44519
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("boxGeometry", { args: [resolvedCellSize, resolvedCellSize, resolvedCellSize] }),
|
|
44520
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("meshStandardMaterial", { color: "#22d3ee", emissive: "#0891b2", emissiveIntensity: 0.42, roughness: 0.28, metalness: 0.02 })
|
|
44521
|
+
] }),
|
|
44522
|
+
blockEntries.map(({ cell, block }) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44523
|
+
VoxelIntentCellMesh,
|
|
44524
|
+
{
|
|
44525
|
+
cell,
|
|
44526
|
+
color: VOXEL_INTENT_TOOL_COLORS[block.groupId] ?? VOXEL_INTENT_TOOL_COLORS.white,
|
|
44527
|
+
cellSize: resolvedCellSize,
|
|
44528
|
+
opacity: 0.92,
|
|
44529
|
+
onClick: (event) => placeAdjacentTo(cell, event)
|
|
44530
|
+
},
|
|
44531
|
+
`block:${voxelCellKey(cell)}`
|
|
44532
|
+
))
|
|
44533
|
+
] });
|
|
43168
44534
|
}
|
|
43169
44535
|
function buildManualSceneConfig(base, manualScene, renderStylePreset) {
|
|
43170
44536
|
var _a3, _b2, _c;
|
|
@@ -43480,6 +44846,15 @@ function Viewport() {
|
|
|
43480
44846
|
const buildLedgerEvents = useForgeStore((s) => s.buildLedgerEvents);
|
|
43481
44847
|
const measureSelections = useForgeStore((s) => s.measureSelections);
|
|
43482
44848
|
const meshPreviewFile = useForgeStore((s) => s.meshPreviewFile);
|
|
44849
|
+
const voxelIntentMode = useForgeStore((s) => s.voxelIntentMode);
|
|
44850
|
+
const voxelIntentTool = useForgeStore((s) => s.voxelIntentTool);
|
|
44851
|
+
const voxelIntentPlacement = useForgeStore((s) => s.voxelIntentPlacement);
|
|
44852
|
+
const voxelIntentCellSizeValue = useForgeStore((s) => s.voxelIntentCellSize);
|
|
44853
|
+
const voxelIntentGhostOpacity = useForgeStore((s) => s.voxelIntentGhostOpacity);
|
|
44854
|
+
const voxelIntentBlocks = useForgeStore((s) => s.voxelIntentBlocks);
|
|
44855
|
+
const setVoxelIntentBlock = useForgeStore((s) => s.setVoxelIntentBlock);
|
|
44856
|
+
const eraseVoxelIntentBlock = useForgeStore((s) => s.eraseVoxelIntentBlock);
|
|
44857
|
+
const setVoxelIntentContextFile = useForgeStore((s) => s.setVoxelIntentContextFile);
|
|
43483
44858
|
const isSvgActive = !!activeFile && activeFile.toLowerCase().endsWith(".svg");
|
|
43484
44859
|
const state2 = useViewportState();
|
|
43485
44860
|
const {
|
|
@@ -43494,6 +44869,8 @@ function Viewport() {
|
|
|
43494
44869
|
inspectChannel,
|
|
43495
44870
|
inspectDisplayMode,
|
|
43496
44871
|
inspectPointSampleCount,
|
|
44872
|
+
thicknessColorRange,
|
|
44873
|
+
setThicknessColorRange,
|
|
43497
44874
|
comparisonInspectMode,
|
|
43498
44875
|
comparisonCandidateOpacity,
|
|
43499
44876
|
projectionMode,
|
|
@@ -43566,6 +44943,7 @@ function Viewport() {
|
|
|
43566
44943
|
previewFile,
|
|
43567
44944
|
knownFileNames
|
|
43568
44945
|
} = state2;
|
|
44946
|
+
const voxelContextFile = previewFile ?? ((activeFile == null ? void 0 : activeFile.toLowerCase().endsWith(".forge.js")) ? activeFile : null);
|
|
43569
44947
|
const historyMode = useForgeStore((s) => s.historyMode);
|
|
43570
44948
|
const historyObjectIds = useForgeStore((s) => s.historyObjectIds);
|
|
43571
44949
|
useHistoryPlaybackLoop();
|
|
@@ -43606,7 +44984,7 @@ function Viewport() {
|
|
|
43606
44984
|
if (activeFile && !isModelFile(activeFile) && !isSvgActive && !meshPreviewFile) {
|
|
43607
44985
|
return {
|
|
43608
44986
|
title: "Viewport disabled",
|
|
43609
|
-
body: `${activeFile} is a
|
|
44987
|
+
body: `${activeFile} is a text file, not a .forge.js or .sketch.js model. Open a model file to render geometry.`
|
|
43610
44988
|
};
|
|
43611
44989
|
}
|
|
43612
44990
|
if (!(result == null ? void 0 : result.error) && (result == null ? void 0 : result.assemblyKinematics) && objects.length === 0 && !rigInspectActive) {
|
|
@@ -43617,7 +44995,10 @@ function Viewport() {
|
|
|
43617
44995
|
}
|
|
43618
44996
|
return null;
|
|
43619
44997
|
}, [activeFile, isSvgActive, meshPreviewFile, objects.length, result, rigInspectActive]);
|
|
43620
|
-
const canvasDpr =
|
|
44998
|
+
const canvasDpr = reactExports.useMemo(() => {
|
|
44999
|
+
if (hasVisibleSdfObjects) return renderStylePreset.canvasDpr.live;
|
|
45000
|
+
return [1, renderStylePreset.canvasDpr.idleMax];
|
|
45001
|
+
}, [hasVisibleSdfObjects, renderStylePreset.canvasDpr.idleMax, renderStylePreset.canvasDpr.live]);
|
|
43621
45002
|
const effectiveSdfObjectSettings = reactExports.useMemo(() => {
|
|
43622
45003
|
const next = {};
|
|
43623
45004
|
for (const obj of objects) {
|
|
@@ -43632,6 +45013,7 @@ function Viewport() {
|
|
|
43632
45013
|
const inspectAnalysis = useInspectWorkerAnalysis({
|
|
43633
45014
|
inspectChannel: comparisonInspectActive ? "none" : inspectChannel,
|
|
43634
45015
|
inspectPointSampleCount,
|
|
45016
|
+
thicknessColorRange,
|
|
43635
45017
|
sceneVersion,
|
|
43636
45018
|
objects,
|
|
43637
45019
|
objectSettings,
|
|
@@ -43656,7 +45038,7 @@ function Viewport() {
|
|
|
43656
45038
|
const handlers = useViewportHandlers({
|
|
43657
45039
|
containerRef,
|
|
43658
45040
|
contextMenuRef,
|
|
43659
|
-
measureMode,
|
|
45041
|
+
measureMode: measureMode || voxelIntentMode,
|
|
43660
45042
|
isViewportInteracting,
|
|
43661
45043
|
objectPickSyncEnabled,
|
|
43662
45044
|
hoveredObjectId,
|
|
@@ -43701,6 +45083,100 @@ function Viewport() {
|
|
|
43701
45083
|
handlePerformanceInfoChange,
|
|
43702
45084
|
handleViewPersistenceResolved: handleViewPersistenceResolvedFromHandlers
|
|
43703
45085
|
} = handlers;
|
|
45086
|
+
reactExports.useEffect(() => {
|
|
45087
|
+
setVoxelIntentContextFile(voxelContextFile);
|
|
45088
|
+
}, [setVoxelIntentContextFile, voxelContextFile]);
|
|
45089
|
+
const voxelCellKey2 = (cell) => `${cell.x},${cell.y},${cell.z}`;
|
|
45090
|
+
const addVoxelCells2 = (a2, b2) => ({ x: a2.x + b2.x, y: a2.y + b2.y, z: a2.z + b2.z });
|
|
45091
|
+
const voxelNormalFromBoxHit = (point, bounds) => {
|
|
45092
|
+
const candidates = [
|
|
45093
|
+
{ normal: { x: -1, y: 0, z: 0 }, distance: Math.abs(point.x - bounds.min.x) },
|
|
45094
|
+
{ normal: { x: 1, y: 0, z: 0 }, distance: Math.abs(point.x - bounds.max.x) },
|
|
45095
|
+
{ normal: { x: 0, y: -1, z: 0 }, distance: Math.abs(point.y - bounds.min.y) },
|
|
45096
|
+
{ normal: { x: 0, y: 1, z: 0 }, distance: Math.abs(point.y - bounds.max.y) },
|
|
45097
|
+
{ normal: { x: 0, y: 0, z: -1 }, distance: Math.abs(point.z - bounds.min.z) },
|
|
45098
|
+
{ normal: { x: 0, y: 0, z: 1 }, distance: Math.abs(point.z - bounds.max.z) }
|
|
45099
|
+
];
|
|
45100
|
+
candidates.sort((a2, b2) => a2.distance - b2.distance);
|
|
45101
|
+
return candidates[0].normal;
|
|
45102
|
+
};
|
|
45103
|
+
const voxelBlockHitsOnRay = (ray, cellSize) => {
|
|
45104
|
+
const hits = [];
|
|
45105
|
+
const halfExtent = cellSize * 0.55;
|
|
45106
|
+
const hitPoint = new Vector3();
|
|
45107
|
+
for (const key of Object.keys(voxelIntentBlocks)) {
|
|
45108
|
+
const [x, y, z] = key.split(",").map(Number);
|
|
45109
|
+
const center = new Vector3((x + 0.5) * cellSize, (y + 0.5) * cellSize, (z + 0.5) * cellSize);
|
|
45110
|
+
const bounds = new Box3(
|
|
45111
|
+
new Vector3(center.x - halfExtent, center.y - halfExtent, center.z - halfExtent),
|
|
45112
|
+
new Vector3(center.x + halfExtent, center.y + halfExtent, center.z + halfExtent)
|
|
45113
|
+
);
|
|
45114
|
+
const intersection = ray.intersectBox(bounds, hitPoint);
|
|
45115
|
+
if (!intersection) continue;
|
|
45116
|
+
const alongRay = intersection.clone().sub(ray.origin).dot(ray.direction);
|
|
45117
|
+
hits.push({ cell: { x, y, z }, normal: voxelNormalFromBoxHit(intersection, bounds), alongRay });
|
|
45118
|
+
}
|
|
45119
|
+
hits.sort((a2, b2) => a2.alongRay - b2.alongRay);
|
|
45120
|
+
return hits;
|
|
45121
|
+
};
|
|
45122
|
+
const rayExitPointForObjectClick = (event, cellSize) => {
|
|
45123
|
+
var _a4;
|
|
45124
|
+
const hitObject = event.object;
|
|
45125
|
+
hitObject.updateWorldMatrix(true, false);
|
|
45126
|
+
const raycaster = new Raycaster(event.ray.origin.clone(), event.ray.direction.clone(), 0, 1e5);
|
|
45127
|
+
const hits = raycaster.intersectObject(hitObject, false).sort((a2, b2) => a2.distance - b2.distance);
|
|
45128
|
+
const minGap = Math.max(1e-3, cellSize * 0.08);
|
|
45129
|
+
const distinctHits = [];
|
|
45130
|
+
for (const hit of hits) {
|
|
45131
|
+
if (hit.distance < event.distance - minGap) continue;
|
|
45132
|
+
const previousHit = distinctHits[distinctHits.length - 1];
|
|
45133
|
+
if (!previousHit || Math.abs(hit.distance - previousHit.distance) > minGap) distinctHits.push(hit);
|
|
45134
|
+
}
|
|
45135
|
+
return ((_a4 = distinctHits[1]) == null ? void 0 : _a4.point) ?? null;
|
|
45136
|
+
};
|
|
45137
|
+
const handleVoxelObjectSurfaceClick = (event, matrix) => {
|
|
45138
|
+
var _a4;
|
|
45139
|
+
event.stopPropagation();
|
|
45140
|
+
if ((event.delta ?? 0) > 4) return;
|
|
45141
|
+
const cellSize = resolveVoxelIntentCellSize(voxelIntentCellSizeValue);
|
|
45142
|
+
const markerHits = voxelBlockHitsOnRay(event.ray, cellSize);
|
|
45143
|
+
if (voxelIntentTool === "erase") {
|
|
45144
|
+
const markerHit2 = markerHits[0];
|
|
45145
|
+
if (markerHit2) eraseVoxelIntentBlock(markerHit2.cell);
|
|
45146
|
+
return;
|
|
45147
|
+
}
|
|
45148
|
+
const markerHit = markerHits[0];
|
|
45149
|
+
if (markerHit) {
|
|
45150
|
+
const candidate = addVoxelCells2(markerHit.cell, markerHit.normal);
|
|
45151
|
+
if (!(voxelCellKey2(candidate) in voxelIntentBlocks)) {
|
|
45152
|
+
setVoxelIntentBlock(candidate, { groupId: voxelIntentTool, placement: voxelIntentPlacement });
|
|
45153
|
+
}
|
|
45154
|
+
return;
|
|
45155
|
+
}
|
|
45156
|
+
const worldNormal = ((_a4 = event.face) == null ? void 0 : _a4.normal) ? event.face.normal.clone().applyNormalMatrix(new Matrix3().getNormalMatrix(matrix)).normalize() : new Vector3(0, 0, 1);
|
|
45157
|
+
if (worldNormal.lengthSq() === 0) worldNormal.set(0, 0, 1);
|
|
45158
|
+
if (voxelIntentPlacement === "through") {
|
|
45159
|
+
const exitPoint = rayExitPointForObjectClick(event, cellSize);
|
|
45160
|
+
const cutPoint = exitPoint ? event.point.clone().lerp(exitPoint, 0.5) : event.point.clone().sub(worldNormal.clone().multiplyScalar(cellSize * 0.5));
|
|
45161
|
+
setVoxelIntentBlock(voxelCellFromPoint(cutPoint, cellSize), { groupId: voxelIntentTool, placement: voxelIntentPlacement });
|
|
45162
|
+
return;
|
|
45163
|
+
}
|
|
45164
|
+
if (voxelIntentPlacement === "inside") {
|
|
45165
|
+
setVoxelIntentBlock(voxelCellFromSurfaceAnchor(event.point, worldNormal, cellSize, "inside"), {
|
|
45166
|
+
groupId: voxelIntentTool,
|
|
45167
|
+
placement: voxelIntentPlacement
|
|
45168
|
+
});
|
|
45169
|
+
return;
|
|
45170
|
+
}
|
|
45171
|
+
if (voxelIntentPlacement === "grid") {
|
|
45172
|
+
setVoxelIntentBlock(voxelCellFromPoint(event.point, cellSize), { groupId: voxelIntentTool, placement: voxelIntentPlacement });
|
|
45173
|
+
return;
|
|
45174
|
+
}
|
|
45175
|
+
setVoxelIntentBlock(voxelCellFromSurfaceAnchor(event.point, worldNormal, cellSize, "outside"), {
|
|
45176
|
+
groupId: voxelIntentTool,
|
|
45177
|
+
placement: voxelIntentPlacement
|
|
45178
|
+
});
|
|
45179
|
+
};
|
|
43704
45180
|
const overlayEntries = [];
|
|
43705
45181
|
if (showPerformanceInfo) {
|
|
43706
45182
|
overlayEntries.push({
|
|
@@ -43755,6 +45231,8 @@ function Viewport() {
|
|
|
43755
45231
|
channel: inspectChannel,
|
|
43756
45232
|
displayMode: inspectDisplayMode,
|
|
43757
45233
|
warnings: inspectWarnings,
|
|
45234
|
+
thicknessColorRange,
|
|
45235
|
+
onThicknessColorRangeChange: setThicknessColorRange,
|
|
43758
45236
|
details: comparisonInspectActive ? /* @__PURE__ */ jsxRuntimeExports.jsx(ComparisonLegendDetails, { metrics: comparisonAnalysis.metrics, mode: comparisonInspectMode }) : void 0
|
|
43759
45237
|
}
|
|
43760
45238
|
)
|
|
@@ -43834,7 +45312,7 @@ function Viewport() {
|
|
|
43834
45312
|
{
|
|
43835
45313
|
style: {
|
|
43836
45314
|
background: renderStyle === "classic" ? t2.viewportBg : renderStylePreset.background,
|
|
43837
|
-
cursor: measureMode ? "crosshair" : "default"
|
|
45315
|
+
cursor: measureMode || voxelIntentMode ? "crosshair" : "default"
|
|
43838
45316
|
},
|
|
43839
45317
|
dpr: canvasDpr,
|
|
43840
45318
|
gl: {
|
|
@@ -43921,14 +45399,16 @@ function Viewport() {
|
|
|
43921
45399
|
const settings = objectSettings[obj.id] ?? { visible: true, opacity: 1, color: "#5b9bd5" };
|
|
43922
45400
|
const isDimmedByFocus = focusedObjectIdSet.size > 0 && !focusedObjectIdSet.has(obj.id);
|
|
43923
45401
|
const isDimmedByGhost = constructionGhost !== null && obj.id !== constructionGhost.objectId;
|
|
45402
|
+
const isDimmedByVoxel = voxelIntentMode && !!obj.shape;
|
|
43924
45403
|
const isHiddenByHistory = historyMode && historyObjectIds.includes(obj.id);
|
|
43925
45404
|
if (isHiddenByHistory) return null;
|
|
43926
45405
|
const effectiveSettings = isDimmedByFocus || isDimmedByGhost ? { ...settings, opacity: Math.min(settings.opacity, FOCUS_MODE_DIM_OPACITY) } : settings;
|
|
45406
|
+
const voxelSettings = isDimmedByVoxel ? { ...effectiveSettings, opacity: Math.min(effectiveSettings.opacity, voxelIntentGhostOpacity) } : effectiveSettings;
|
|
43927
45407
|
const comparisonSettings = comparisonInspectActive && obj.shape ? {
|
|
43928
|
-
...
|
|
43929
|
-
opacity: Math.min(
|
|
45408
|
+
...voxelSettings,
|
|
45409
|
+
opacity: Math.min(voxelSettings.opacity, comparisonCandidateContextOpacity(comparisonCandidateOpacity)),
|
|
43930
45410
|
color: "#b9c3cc"
|
|
43931
|
-
} :
|
|
45411
|
+
} : voxelSettings;
|
|
43932
45412
|
const isHovered = hoveredObjectId === obj.id;
|
|
43933
45413
|
const matrix = objectMatrices[obj.id] ?? new Matrix4();
|
|
43934
45414
|
const objectClippingPlanes = objectClippingPlanesById[obj.id] ?? EMPTY_CLIPPING_PLANES;
|
|
@@ -43959,7 +45439,7 @@ function Viewport() {
|
|
|
43959
45439
|
onPointerEnter: (event) => updateHoverLabel(obj, event),
|
|
43960
45440
|
onPointerMove: (event) => updateHoverLabel(obj, event),
|
|
43961
45441
|
onPointerLeave: (event) => clearHoverLabel(obj, event),
|
|
43962
|
-
onClick: (event) => handleObjectClick(obj, event),
|
|
45442
|
+
onClick: (event) => voxelIntentMode ? handleVoxelObjectSurfaceClick(event, matrix) : handleObjectClick(obj, event),
|
|
43963
45443
|
onDoubleClick: (event) => handleObjectDoubleClick(obj, event),
|
|
43964
45444
|
onContextMenu: (event) => handleObjectContextMenu(obj, event)
|
|
43965
45445
|
},
|
|
@@ -44032,6 +45512,7 @@ function Viewport() {
|
|
|
44032
45512
|
}
|
|
44033
45513
|
return null;
|
|
44034
45514
|
}),
|
|
45515
|
+
voxelIntentMode && /* @__PURE__ */ jsxRuntimeExports.jsx(VoxelIntentOverlay, {}),
|
|
44035
45516
|
inspectChannel === "collisions" && /* @__PURE__ */ jsxRuntimeExports.jsx(CollisionInspectionOverlay, { geometries: inspectAnalysis.collisionGeometries }),
|
|
44036
45517
|
rigInspectActive && /* @__PURE__ */ jsxRuntimeExports.jsx(RigInspectionOverlay, { state: rigInspectionOverlay, config: jointOverlayConfig }),
|
|
44037
45518
|
comparisonInspectActive && comparisonAnalysis.referenceResult && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -44086,7 +45567,7 @@ function Viewport() {
|
|
|
44086
45567
|
}
|
|
44087
45568
|
),
|
|
44088
45569
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ZoomSampler, { onZoomChange: setZoomMmPerPx }),
|
|
44089
|
-
gridEnabled && !isSketchOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45570
|
+
gridEnabled && !isSketchOnly && !voxelIntentMode && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44090
45571
|
Grid,
|
|
44091
45572
|
{
|
|
44092
45573
|
args: [500, 500],
|
|
@@ -44111,7 +45592,7 @@ function Viewport() {
|
|
|
44111
45592
|
onPointerEnter: (obj, event) => updateHoverLabel(obj, event),
|
|
44112
45593
|
onPointerMove: (obj, event) => updateHoverLabel(obj, event),
|
|
44113
45594
|
onPointerLeave: (obj, event) => clearHoverLabel(obj, event),
|
|
44114
|
-
onClick: (obj, event) => handleObjectClick(obj, event),
|
|
45595
|
+
onClick: (obj, event) => voxelIntentMode ? handleVoxelObjectSurfaceClick(event, objectMatrices[obj.id] ?? new Matrix4()) : handleObjectClick(obj, event),
|
|
44115
45596
|
onDoubleClick: (obj, event) => handleObjectDoubleClick(obj, event),
|
|
44116
45597
|
onContextMenu: (obj, event) => handleObjectContextMenu(obj, event)
|
|
44117
45598
|
}
|
|
@@ -44146,7 +45627,7 @@ function Viewport() {
|
|
|
44146
45627
|
minPolarAngle: 0,
|
|
44147
45628
|
maxPolarAngle: Math.PI,
|
|
44148
45629
|
enableRotate: !isSketchOnly,
|
|
44149
|
-
mouseButtons: drawModeActive ? MOUSE_BUTTONS_DRAW : isSketchOnly ? MOUSE_BUTTONS_SKETCH : MOUSE_BUTTONS_3D,
|
|
45630
|
+
mouseButtons: voxelIntentMode ? MOUSE_BUTTONS_VOXEL : drawModeActive ? MOUSE_BUTTONS_DRAW : isSketchOnly ? MOUSE_BUTTONS_SKETCH : MOUSE_BUTTONS_3D,
|
|
44150
45631
|
touches: isSketchOnly ? TOUCH_GESTURES_SKETCH : TOUCH_GESTURES_3D
|
|
44151
45632
|
}
|
|
44152
45633
|
),
|
|
@@ -44154,6 +45635,7 @@ function Viewport() {
|
|
|
44154
45635
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ViewManager, { isSketchOnly, controlsRef }),
|
|
44155
45636
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ViewPersistence, { controlsRef, isSketchOnly, onResolved: handleViewPersistenceResolved }),
|
|
44156
45637
|
/* @__PURE__ */ jsxRuntimeExports.jsx(OrbitExporterBridge, { controlsRef }),
|
|
45638
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ViewportImageCaptureBridge, {}),
|
|
44157
45639
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ViewportRecordingBridge, { controlsRef }),
|
|
44158
45640
|
historyMode && /* @__PURE__ */ jsxRuntimeExports.jsx(HistoryRecorderBridge, {}),
|
|
44159
45641
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TrajectoryRecorderBridge, { controlsRef })
|
|
@@ -44255,7 +45737,7 @@ function Viewport() {
|
|
|
44255
45737
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ViewportOverlayHost, { entries: overlayEntries }),
|
|
44256
45738
|
viewportDisabledMessage && /* @__PURE__ */ jsxRuntimeExports.jsx(ViewportDisabledOverlay, { title: viewportDisabledMessage.title, body: viewportDisabledMessage.body }),
|
|
44257
45739
|
drawFlagEnabled && /* @__PURE__ */ jsxRuntimeExports.jsx(DrawToolbar, {}),
|
|
44258
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(HoverTooltipLayer, { ref: hoverTooltipRef, enabled: objectPickSyncEnabled && !measureMode }),
|
|
45740
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(HoverTooltipLayer, { ref: hoverTooltipRef, enabled: objectPickSyncEnabled && !measureMode && !voxelIntentMode }),
|
|
44259
45741
|
viewportPortalHost && objectContextMenu && reactDomExports.createPortal(
|
|
44260
45742
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
44261
45743
|
"div",
|
|
@@ -44729,7 +46211,7 @@ function Viewport() {
|
|
|
44729
46211
|
}
|
|
44730
46212
|
);
|
|
44731
46213
|
}
|
|
44732
|
-
const EditorApp$1 = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-
|
|
46214
|
+
const EditorApp$1 = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-QXsAISLR.js"), true ? __vite__mapDeps([0]) : void 0).then((m2) => ({ default: m2.EditorApp })));
|
|
44733
46215
|
const PENDING_SHARE_COPY_KEY = "fc-pending-share-copy";
|
|
44734
46216
|
function storePendingShareCopy(shareId) {
|
|
44735
46217
|
sessionStorage.setItem(PENDING_SHARE_COPY_KEY, shareId);
|
|
@@ -44995,17 +46477,17 @@ function SeoMetadata() {
|
|
|
44995
46477
|
}, [location.pathname]);
|
|
44996
46478
|
return null;
|
|
44997
46479
|
}
|
|
44998
|
-
reactExports.lazy(() => __vitePreload(() => import("./LandingPageProofDriven-
|
|
44999
|
-
const DocsPage = reactExports.lazy(() => __vitePreload(() => import("./DocsPage-
|
|
45000
|
-
reactExports.lazy(() => __vitePreload(() => import("./BlogPage-
|
|
45001
|
-
reactExports.lazy(() => __vitePreload(() => import("./BenchmarkPage-
|
|
45002
|
-
reactExports.lazy(() => __vitePreload(() => import("./AdminPage-
|
|
46480
|
+
reactExports.lazy(() => __vitePreload(() => import("./LandingPageProofDriven-yhhOodbf.js"), true ? __vite__mapDeps([1]) : void 0).then((m2) => ({ default: m2.LandingPageProofDriven })));
|
|
46481
|
+
const DocsPage = reactExports.lazy(() => __vitePreload(() => import("./DocsPage-B5LePEuj.js"), true ? [] : void 0).then((m2) => ({ default: m2.DocsPage })));
|
|
46482
|
+
reactExports.lazy(() => __vitePreload(() => import("./BlogPage-DodHpvmf.js"), true ? [] : void 0).then((m2) => ({ default: m2.BlogPage })));
|
|
46483
|
+
reactExports.lazy(() => __vitePreload(() => import("./BenchmarkPage-a9_f-1US.js"), true ? __vite__mapDeps([1,2]) : void 0).then((m2) => ({ default: m2.BenchmarkPage })));
|
|
46484
|
+
reactExports.lazy(() => __vitePreload(() => import("./AdminPage-DwYHz72L.js"), true ? [] : void 0).then((m2) => ({ default: m2.AdminPage })));
|
|
45003
46485
|
reactExports.lazy(() => __vitePreload(() => Promise.resolve().then(() => PublishedModelPage$1), true ? void 0 : void 0).then((m2) => ({ default: m2.PublishedModelPage })));
|
|
45004
|
-
reactExports.lazy(() => __vitePreload(() => import("./SettingsPage-
|
|
45005
|
-
reactExports.lazy(() => __vitePreload(() => import("./PricingPage-
|
|
45006
|
-
reactExports.lazy(() => __vitePreload(() => import("./LegalPage-
|
|
45007
|
-
const EditorApp = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-
|
|
45008
|
-
const EmbedViewer = reactExports.lazy(() => __vitePreload(() => import("./EmbedViewer-
|
|
46486
|
+
reactExports.lazy(() => __vitePreload(() => import("./SettingsPage-BJZcM97j.js"), true ? [] : void 0).then((m2) => ({ default: m2.SettingsPage })));
|
|
46487
|
+
reactExports.lazy(() => __vitePreload(() => import("./PricingPage-E3Rma7aV.js"), true ? __vite__mapDeps([1,3]) : void 0).then((m2) => ({ default: m2.PricingPage })));
|
|
46488
|
+
reactExports.lazy(() => __vitePreload(() => import("./LegalPage-5RbKRGYK.js"), true ? __vite__mapDeps([1,4]) : void 0).then((m2) => ({ default: m2.LegalPage })));
|
|
46489
|
+
const EditorApp = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-QXsAISLR.js"), true ? __vite__mapDeps([0]) : void 0).then((m2) => ({ default: m2.EditorApp })));
|
|
46490
|
+
const EmbedViewer = reactExports.lazy(() => __vitePreload(() => import("./EmbedViewer-DdEHGUMU.js"), true ? [] : void 0).then((m2) => ({ default: m2.EmbedViewer })));
|
|
45009
46491
|
const embedMode = isEmbedMode() && !window.location.pathname.startsWith("/m/");
|
|
45010
46492
|
const EDITABLE_CRASH_FILE = /\.(?:forge\.js|[cm]?[jt]sx?|json|md|txt|svg|dxf)$/i;
|
|
45011
46493
|
function firstMeaningfulLine(text) {
|
|
@@ -45225,61 +46707,73 @@ function App() {
|
|
|
45225
46707
|
applyTheme(localStorage.getItem("fc-theme") || "dark");
|
|
45226
46708
|
clientExports.createRoot(document.getElementById("root")).render(/* @__PURE__ */ jsxRuntimeExports.jsx(App, {}));
|
|
45227
46709
|
export {
|
|
45228
|
-
|
|
46710
|
+
INSPECT_POINT_SAMPLE_COUNT_MIN as $,
|
|
45229
46711
|
AuthApiError as A,
|
|
45230
46712
|
BrandMark as B,
|
|
45231
|
-
|
|
45232
|
-
|
|
45233
|
-
|
|
46713
|
+
captureViewportImageBlobFromStore as C,
|
|
46714
|
+
exportExactFromStore as D,
|
|
46715
|
+
storageQuotaUpgradeMessage as E,
|
|
45234
46716
|
FLAG_DEFINITIONS as F,
|
|
45235
|
-
|
|
45236
|
-
|
|
45237
|
-
|
|
45238
|
-
|
|
45239
|
-
|
|
45240
|
-
|
|
45241
|
-
|
|
45242
|
-
|
|
45243
|
-
|
|
45244
|
-
|
|
45245
|
-
|
|
45246
|
-
|
|
45247
|
-
|
|
45248
|
-
|
|
45249
|
-
|
|
45250
|
-
|
|
45251
|
-
|
|
45252
|
-
|
|
45253
|
-
|
|
45254
|
-
|
|
45255
|
-
|
|
46717
|
+
isImportableProjectMeshFile as G,
|
|
46718
|
+
isImportableProjectBinaryFile as H,
|
|
46719
|
+
hasExternalFiles as I,
|
|
46720
|
+
isImportableProjectExactFile as J,
|
|
46721
|
+
resolvePreviewFile as K,
|
|
46722
|
+
countParamSnapshotDiff as L,
|
|
46723
|
+
buildProjectShareUrl as M,
|
|
46724
|
+
buildEmbedSnippet as N,
|
|
46725
|
+
publishProjectShare as O,
|
|
46726
|
+
unpublishProjectShare as P,
|
|
46727
|
+
formatComputeBackendLabel as Q,
|
|
46728
|
+
themes as R,
|
|
46729
|
+
computeBackendFromParts as S,
|
|
46730
|
+
formatArea as T,
|
|
46731
|
+
sliderToAnimationSpeed as U,
|
|
46732
|
+
animationSpeedToSlider as V,
|
|
46733
|
+
formatAnimationSpeed as W,
|
|
46734
|
+
resolveJointRange as X,
|
|
46735
|
+
useJointsConfig as Y,
|
|
46736
|
+
useJointAnimationValues as Z,
|
|
46737
|
+
INSPECT_POINT_SAMPLE_COUNT_MAX as _,
|
|
45256
46738
|
applyTheme as a,
|
|
45257
|
-
|
|
45258
|
-
|
|
45259
|
-
|
|
45260
|
-
|
|
45261
|
-
|
|
45262
|
-
|
|
45263
|
-
|
|
45264
|
-
|
|
45265
|
-
|
|
45266
|
-
|
|
45267
|
-
|
|
45268
|
-
|
|
45269
|
-
|
|
45270
|
-
|
|
45271
|
-
|
|
45272
|
-
|
|
45273
|
-
|
|
45274
|
-
|
|
45275
|
-
|
|
45276
|
-
|
|
45277
|
-
|
|
45278
|
-
|
|
45279
|
-
|
|
45280
|
-
|
|
45281
|
-
|
|
45282
|
-
|
|
46739
|
+
VOXEL_INTENT_TOOL_ORDER as a0,
|
|
46740
|
+
VOXEL_INTENT_TOOL_LABELS as a1,
|
|
46741
|
+
VOXEL_INTENT_TOOL_COLORS as a2,
|
|
46742
|
+
VOXEL_INTENT_PLACEMENT_ORDER as a3,
|
|
46743
|
+
VOXEL_INTENT_PLACEMENT_LABELS as a4,
|
|
46744
|
+
highlightLanguageForProjectFile as a5,
|
|
46745
|
+
hasProjectTextFileExtension as a6,
|
|
46746
|
+
expandBoundsByTransformedAabb as a7,
|
|
46747
|
+
Canvas as a8,
|
|
46748
|
+
PerspectiveCamera as a9,
|
|
46749
|
+
buildShareUrl as aA,
|
|
46750
|
+
share as aB,
|
|
46751
|
+
ControlsInteractionBridge as aa,
|
|
46752
|
+
ViewController as ab,
|
|
46753
|
+
SceneConfigurator as ac,
|
|
46754
|
+
LocalEnvironment as ad,
|
|
46755
|
+
ForgeObject as ae,
|
|
46756
|
+
RenderLabelsOverlay as af,
|
|
46757
|
+
Grid as ag,
|
|
46758
|
+
OrbitControls2 as ah,
|
|
46759
|
+
TOUCH_GESTURES_3D as ai,
|
|
46760
|
+
MOUSE_BUTTONS_3D as aj,
|
|
46761
|
+
ModelJourneyBar as ak,
|
|
46762
|
+
useJointAnimationLoop as al,
|
|
46763
|
+
computeJointNodeMatrices as am,
|
|
46764
|
+
computeObjectJointMatrices as an,
|
|
46765
|
+
readLastActiveFileForUser as ao,
|
|
46766
|
+
ToastContainer as ap,
|
|
46767
|
+
isMobile as aq,
|
|
46768
|
+
useFeatureFlag as ar,
|
|
46769
|
+
decodeSharedHash as as,
|
|
46770
|
+
decodeSharedBundle as at,
|
|
46771
|
+
getExternalUrl as au,
|
|
46772
|
+
getGistId as av,
|
|
46773
|
+
Viewport as aw,
|
|
46774
|
+
shouldBlockBrowserShortcut as ax,
|
|
46775
|
+
useDrawStore as ay,
|
|
46776
|
+
storePendingShareCopy as az,
|
|
45283
46777
|
authFetch as b,
|
|
45284
46778
|
authApi as c,
|
|
45285
46779
|
showToast as d,
|
|
@@ -45287,22 +46781,22 @@ export {
|
|
|
45287
46781
|
formatStorageBytes as f,
|
|
45288
46782
|
useForgeStore as g,
|
|
45289
46783
|
fileSystem as h,
|
|
45290
|
-
|
|
45291
|
-
|
|
45292
|
-
|
|
45293
|
-
|
|
45294
|
-
|
|
45295
|
-
|
|
45296
|
-
|
|
45297
|
-
|
|
45298
|
-
|
|
46784
|
+
isRunnableFile as i,
|
|
46785
|
+
copyTextToClipboard as j,
|
|
46786
|
+
useFeatureFlagStore as k,
|
|
46787
|
+
fetchGistModel as l,
|
|
46788
|
+
monacoLanguageForProjectFile as m,
|
|
46789
|
+
fetchUrlModel as n,
|
|
46790
|
+
exportMeshFromStore as o,
|
|
46791
|
+
exportReportFromStore as p,
|
|
46792
|
+
exportViewportImageFromStore as q,
|
|
45299
46793
|
readProjectFilesFromDataTransfer as r,
|
|
45300
46794
|
storageUsagePercent as s,
|
|
45301
46795
|
triggerDownload as t,
|
|
45302
46796
|
useAuthStore as u,
|
|
45303
|
-
|
|
45304
|
-
|
|
45305
|
-
|
|
45306
|
-
|
|
45307
|
-
|
|
46797
|
+
exportOrbitVideoFromStore as v,
|
|
46798
|
+
exportSketchFromStore as w,
|
|
46799
|
+
buildGistShareUrl as x,
|
|
46800
|
+
deriveExportStem as y,
|
|
46801
|
+
sanitizeExportStem as z
|
|
45308
46802
|
};
|