forgecad 0.10.5 → 0.11.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-raksfnNA.js → AdminPage-B1nIvqLS.js} +1 -1
- package/dist/assets/{BenchmarkPage-DP3RxhPs.js → BenchmarkPage-YZJbw5nd.js} +1 -1
- package/dist/assets/{BlogPage-D7Dos-vl.js → BlogPage-DIWRApKS.js} +1 -1
- package/dist/assets/{DocsPage-DO1kvBns.js → DocsPage-ClL6X1hR.js} +2 -22
- package/dist/assets/{EditorApp-DQJmcmRT.js → EditorApp-CYBDvSyT.js} +575 -119
- package/dist/assets/{EmbedViewer-DFDUhOma.js → EmbedViewer-Dmfu_LIw.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-DbE_tp8-.js → LandingPageProofDriven-XYTiYxfM.js} +1 -1
- package/dist/assets/{LegalPage-CominSso.js → LegalPage-D5Z3CscF.js} +1 -1
- package/dist/assets/{PricingPage-CcVIN9yj.js → PricingPage-BP4lIGio.js} +1 -1
- package/dist/assets/{SettingsPage-DLWcP289.js → SettingsPage-D3bcPBsC.js} +1 -1
- package/dist/assets/{app-xW3hOdq9.js → app-BKjogwIZ.js} +2192 -231
- package/dist/assets/{backendInit-mDHk97u7.js → backendInit-6a9-ilom.js} +76448 -75066
- package/dist/assets/cli/{render--SIU27W_.js → render-CMNudGb0.js} +3 -3
- package/dist/assets/{constructionHistoryWorker-uEe_Q7Kg.js → constructionHistoryWorker-BuZgc606.js} +6985 -6706
- package/dist/assets/{evalWorker-BqyDHDcI.js → evalWorker-DQ82ueGu.js} +40862 -39497
- package/dist/assets/{inspectWorker-UXMxlcR8.js → inspectWorker-Cuby2qfT.js} +2078 -478
- package/dist/assets/{jointPose-bYMlwU3v.js → jointPose-CFql5I-u.js} +1 -1
- package/dist/assets/{manifold-CyOV5B9S.js → manifold-02pmr7O7.js} +2 -2
- package/dist/assets/{manifold-BR7UYI4P.js → manifold-C6KU0oII.js} +1 -1
- package/dist/assets/{manifold-D4d5NQst.js → manifold-P1yF3GKn.js} +1 -1
- package/dist/assets/{reportWorker-DsaICZsn.js → reportWorker-kg065BVL.js} +85183 -78309
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +6 -8
- package/dist/docs-raw/CLI.md +10 -10
- package/dist/docs-raw/component-model.md +28 -9
- package/dist/docs-raw/generated/concepts.md +13 -4
- package/dist/docs-raw/generated/core.md +244 -56
- package/dist/docs-raw/generated/curves.md +13 -0
- package/dist/docs-raw/generated/runtime-names.md +2 -2
- package/dist/docs-raw/guides/inspection-bundles.md +1 -1
- package/dist/docs-raw/guides/structural-fea.md +11 -0
- package/dist/docs-raw/skills/forgecad-build-model.md +70 -147
- package/dist/docs-raw/skills/forgecad-image-prompt.md +1 -1
- package/dist/docs-raw/skills/forgecad-project-sync.md +3 -3
- package/dist/docs-raw/skills/forgecad-reconstruct-cad-file.md +2 -2
- package/dist/docs-raw/skills/forgecad-reconstruct-from-images.md +4 -5
- package/dist/docs-raw/skills/forgecad.md +3 -1
- package/dist/docs-raw/skills/index.md +1 -5
- package/dist/docs-raw/welcome.md +3 -4
- package/dist/index.html +1 -1
- package/dist/llms.txt +1 -2
- package/dist/sitemap.xml +15 -15
- package/dist-cli/{check-compiler-7YAHVXYM.js → check-compiler-UJWUEIDC.js} +1 -1
- package/dist-cli/{check-query-propagation-ZRR6IOJW.js → check-query-propagation-O2EPDJSY.js} +1 -1
- package/dist-cli/{chunk-VNM67DIV.js → chunk-MNDROM7T.js} +77145 -75767
- package/dist-cli/forgecad.js +1145 -441
- package/dist-skill/CONTEXT.md +429 -64
- package/dist-skill/SKILL.md +3 -1
- package/dist-skill/docs/API/core/concepts.md +31 -4
- package/dist-skill/docs/CLI.md +10 -10
- package/dist-skill/docs/generated/core.md +240 -57
- package/dist-skill/docs/generated/curves.md +13 -0
- package/dist-skill/docs/generated/runtime-names.md +2 -2
- package/dist-skill/docs/guides/inspection-bundles.md +1 -1
- package/dist-skill/docs/guides/manual-parameters.md +130 -0
- package/dist-skill/docs/guides/structural-fea.md +11 -0
- package/dist-skill/library/README.md +0 -4
- package/dist-skill/library/forgecad-build-model/SKILL.md +57 -150
- package/dist-skill/library/forgecad-build-model/references/inspection-feedback.md +58 -0
- package/dist-skill/library/forgecad-build-model/references/module-contracts.md +53 -0
- package/dist-skill/library/forgecad-build-model/references/parameter-controls.md +22 -0
- package/dist-skill/library/forgecad-build-model/references/readiness-review.md +43 -0
- package/dist-skill/library/forgecad-build-model/references/simulation-feedback.md +49 -0
- package/dist-skill/library/forgecad-build-model/references/stage-1-design-intent.md +21 -0
- package/dist-skill/library/forgecad-build-model/references/stage-2-architecture-plan.md +23 -0
- package/dist-skill/library/forgecad-build-model/references/stage-3-build-slices.md +39 -0
- package/dist-skill/library/forgecad-build-model/references/stage-4-feedback-iteration.md +24 -0
- package/dist-skill/library/forgecad-build-model/references/stage-5-readiness-package.md +34 -0
- package/dist-skill/library/forgecad-image-prompt/SKILL.md +1 -1
- package/dist-skill/library/forgecad-project-sync/SKILL.md +3 -3
- package/dist-skill/library/forgecad-reconstruct-cad-file/SKILL.md +2 -2
- package/dist-skill/library/forgecad-reconstruct-from-images/SKILL.md +4 -5
- package/dist-skill/website/skills/forgecad-build-model.md +70 -147
- package/dist-skill/website/skills/forgecad-image-prompt.md +1 -1
- package/dist-skill/website/skills/forgecad-project-sync.md +3 -3
- package/dist-skill/website/skills/forgecad-reconstruct-cad-file.md +2 -2
- package/dist-skill/website/skills/forgecad-reconstruct-from-images.md +4 -5
- package/dist-skill/website/skills/forgecad.md +3 -1
- package/dist-skill/website/skills/index.md +1 -5
- package/examples/api/param-path2d.forge.js +65 -0
- package/examples/api/param-placement2d.forge.js +80 -0
- package/examples/api/param-spline2d-g-continuity.forge.js +57 -0
- package/examples/api/spoon-full-tang-handle.forge.js +57 -17
- package/examples/api/surface-variable-thickness-panel.forge.js +62 -0
- package/examples/mechanical/airplane-propeller.forge.js +81 -28
- package/package.json +2 -2
- package/dist/docs-raw/skills/forgecad-design-spec.md +0 -145
- package/dist/docs-raw/skills/forgecad-grade-model.md +0 -84
- package/dist/docs-raw/skills/forgecad-inspect-model.md +0 -80
- package/dist/docs-raw/skills/forgecad-verify-mujoco.md +0 -78
- package/dist-skill/library/forgecad-design-spec/SKILL.md +0 -132
- package/dist-skill/library/forgecad-design-spec/references/default-profiles.md +0 -99
- package/dist-skill/library/forgecad-design-spec/references/master-prompt.md +0 -73
- package/dist-skill/library/forgecad-grade-model/SKILL.md +0 -72
- package/dist-skill/library/forgecad-grade-model/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-inspect-model/SKILL.md +0 -68
- package/dist-skill/library/forgecad-verify-mujoco/SKILL.md +0 -66
- package/dist-skill/website/skills/forgecad-design-spec.md +0 -145
- package/dist-skill/website/skills/forgecad-grade-model.md +0 -84
- package/dist-skill/website/skills/forgecad-inspect-model.md +0 -80
- package/dist-skill/website/skills/forgecad-verify-mujoco.md +0 -78
- /package/dist-skill/library/{forgecad-verify-mujoco → forgecad-build-model}/scripts/mujoco_verify.py +0 -0
- /package/dist-skill/library/{forgecad-inspect-model → forgecad-build-model/scripts}/summarize_manifest.py +0 -0
|
@@ -4,7 +4,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
var _a2;
|
|
6
6
|
import { c as create, j as jsxRuntimeExports, r as reactExports, a as createWithEqualityFn, R as React, T as Tb, s as schedulerExports, b as clientExports, d as reactDomExports, u as useParams, e as useSearchParams, f as useNavigate, L as Link, g as useLocation, B as BrowserRouter, h as Routes, i as Route, N as Navigate } from "./vendor-react-6j1Kke-Y.js";
|
|
7
|
-
import { _ as __vitePreload, S as SDF_PROGRAM_OP, z as zipSync, s as strToU8, W as WebGLRenderer, R as Raycaster, O as OrthographicCamera$1, P as PerspectiveCamera$1, a as Scene, b as PCFSoftShadowMap, V as VSMShadowMap, c as PCFShadowMap, B as BasicShadowMap, C as ColorManagement, L as LinearSRGBColorSpace, d as SRGBColorSpace, N as NoToneMapping, A as ACESFilmicToneMapping, e as Layers, f as Color, g as RGBAFormat, U as UnsignedByteType, h as Vector3, i as Vector2, j as Clock, T as THREE, D as DoubleSide, k as REVISION, M as Mesh, I as IcosahedronGeometry, l as ShaderMaterial, m as Spherical, Q as Quaternion, n as MOUSE, o as TOUCH, p as Ray, q as Plane, r as DataTextureLoader, H as HalfFloatType, F as FloatType, t as DataUtils, u as LinearFilter, v as RedFormat, w as InstancedBufferGeometry, x as Float32BufferAttribute, y as InstancedInterleavedBuffer, E as InterleavedBufferAttribute, G as WireframeGeometry, J as Box3, K as Sphere, X as UniformsUtils, Y as UniformsLib, Z as Vector4, $ as Line3, a0 as Matrix4, a1 as MathUtils, a2 as Uniform, a3 as WebGLRenderTarget, a4 as DepthTexture, a5 as BackSide, a6 as ClampToEdgeWrapping, a7 as PlaneGeometry, a8 as UVMapping, a9 as DataTexture, aa as Texture, ab as MeshBasicMaterial, ac as IntType, ad as ShortType, ae as ByteType, af as UnsignedIntType, ag as Loader, ah as LoadingManager, ai as LinearMipMapLinearFilter, aj as FileLoader, ak as NoBlending, al as CubeReflectionMapping, am as EquirectangularReflectionMapping, an as CubeTextureLoader, ao as WebGLCubeRenderTarget, ap as ConstraintSketch, aq as setSketchPlacement3D, ar as Sketch, as as PROFILE_BACKEND_MARKER, at as FrozenShape, au as setShapeCompilePlan, av as hasAnyPorts, aw as setShapePortsInternal, ax as markShapePortsUsed, ay as writeViewPreferences, az as setParamOverrides, aA as readViewPreferences, aB as resolveForgeRenderStyle, aC as isConstraintSketch, aD as updateConstraintValue, aE as linearizeMultiObjectSteps, aF as getShapeCompilePlan, aG as resolveCameraControlMode, aH as resolveComparisonOpacity, aI as resolveComparisonInspectMode, aJ as resolveBooleanPref, aK as resolveInspectQuantizeBands, aL as resolveInspectIsolineSpacing, aM as resolveInspectColormap, aN as resolveThicknessColorRange, aO as resolveInspectPointSampleCount, aP as SCAN_PROXY_GRANULARITY_DEFAULT, aQ as DEFAULT_MANUAL_SCENE_SETTINGS, aR as resolveManualSceneSettings, aS as publishSolverWasmRunDebug, aT as resolveForgeQualityPreset, aU as DEFAULT_INSPECT_ISOLINES_ENABLED, aV as SCAN_PROXY_MATRIX_GRANULARITY_MAX, aW as findJointAnimationClip, aX as resolveJointAnimation, aY as resolveJointViewValues, aZ as resolveImportPath, a_ as DEFAULT_INSPECT_CRITICAL_LINE_ENABLED, a$ as resolveScanProxyGranularity, b0 as DEFAULT_COMPARISON_REFERENCE_OPACITY, b1 as DEFAULT_COMPARISON_CANDIDATE_OPACITY, b2 as BufferGeometry, b3 as LineBasicMaterial, b4 as Line$1, b5 as LineDashedMaterial, b6 as CanvasTexture, b7 as Object3D, b8 as FogExp2, b9 as Fog, ba as AmbientLight, bb as HemisphereLight, bc as SpotLight, bd as PointLight, be as DirectionalLight, bf as BufferAttribute, bg as heatPointsForSide, bh as COMPARISON_COLORS, bi as comparisonHeatDepthTest, bj as comparisonHeatEdgeOpacity, bk as comparisonHeatPatchOpacity, bl as shapeToGeometry, bm as buildComparisonHeatPatchGeometry, bn as EdgesGeometry, bo as buildShapeFromCompilePlan, bp as VIEWPORT_CAMERA_STORAGE_KEY, bq as parseViewportCameraState, br as getKernelFaceNameForTriangle, bs as OBJECT_CONTEXT_MENU_MARGIN, bt as buildVisibleHistoryStacks, bu as sketchToSvg, bv as sketchToDxf, bw as runScript, bx as MeshPhysicalMaterial, by as LineSegments, bz as findDesignTraceNodeForConstructionStep, bA as formatDesignTraceAnchor, bB as waitForAnimationFrame, bC as selectBuildLedgerNodes, bD as UNIDENTIFIED_FACE_NAME, bE as getBakedEdges, bF as worldAuthorPlaneToLocal, bG as compileSdfProgramEvaluator3, bH as SDF_LINEAR_OUTPUT_COLOR_GLSL, bI as GLSL3, bJ as BoxGeometry, bK as Data3DTexture, bL as buildSdfRaymarchFragmentShader, bM as SDF_RAYMARCH_PROXY_VERTEX_SHADER, bN as scanProxySourceBytes, bO as disposeScanProxyGeometry, bP as scanProxyGeometryFromPayload, bQ as AdditiveBlending, bR as geometryWithVisibleVertexColors, bS as MeshBVH, bT as makeColorScaleTexture, bU as colorScaleLUT, bV as makeScalarValueTexture, bW as makeInspectScalarUniforms, bX as updateInspectScalarUniforms, bY as descriptorToThreeTexture, bZ as applyProjectedTexture, b_ as getRenderStylePreset, b$ as SCAN_RENDER_COLORS, c0 as NormalBlending, c1 as acceleratedRaycast, c2 as INSPECT_SCALAR_FRAGMENT_SHADER, c3 as INSPECT_SCALAR_VERTEX_SHADER, c4 as scanMaterialShellColor, c5 as ZEBRA_STRIPE_SOFTNESS, c6 as ZEBRA_STRIPE_SCALE, c7 as ZEBRA_LIGHT_COLOR, c8 as ZEBRA_DARK_COLOR, c9 as ZEBRA_ACCENT_COLOR, ca as ZEBRA_STRIPE_FRAGMENT_SHADER, cb as ZEBRA_STRIPE_VERTEX_SHADER, cc as SCAN_PROXY_LAYER_STYLES, cd as scanMaterialLayerStyles, ce as SURFACE_FIELD_FRAGMENT_SHADER, cf as SURFACE_FIELD_VERTEX_SHADER, cg as WORLD_UP$1, ch as CatmullRomCurve3, ci as TubeGeometry, cj as DEFAULT_THICKNESS_COLOR_RANGE, ck as DEFAULT_COLORMAP, cl as colorScaleHexStops, cm as PERFORMANCE_SAMPLE_INTERVAL_SEC, cn as formatPerformanceCount, co as NON_TEXT_INPUT_TYPES, cp as MeshStandardMaterial, cq as Shape, cr as ShapeGeometry, cs as ShaderLib, ct as CylinderGeometry, cu as createResolvedExplodeConfig, cv as explodeBoundsCenter, cw as explodeMergeBounds, cx as resolveExplodeDirective, cy as computeExplodeMotion, cz 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_WIDTH, cQ as OBJECT_CONTEXT_MENU_HEIGHT, cR as
|
|
7
|
+
import { _ as __vitePreload, S as SDF_PROGRAM_OP, z as zipSync, s as strToU8, W as WebGLRenderer, R as Raycaster, O as OrthographicCamera$1, P as PerspectiveCamera$1, a as Scene, b as PCFSoftShadowMap, V as VSMShadowMap, c as PCFShadowMap, B as BasicShadowMap, C as ColorManagement, L as LinearSRGBColorSpace, d as SRGBColorSpace, N as NoToneMapping, A as ACESFilmicToneMapping, e as Layers, f as Color, g as RGBAFormat, U as UnsignedByteType, h as Vector3, i as Vector2, j as Clock, T as THREE, D as DoubleSide, k as REVISION, M as Mesh, I as IcosahedronGeometry, l as ShaderMaterial, m as Spherical, Q as Quaternion, n as MOUSE, o as TOUCH, p as Ray, q as Plane, r as DataTextureLoader, H as HalfFloatType, F as FloatType, t as DataUtils, u as LinearFilter, v as RedFormat, w as InstancedBufferGeometry, x as Float32BufferAttribute, y as InstancedInterleavedBuffer, E as InterleavedBufferAttribute, G as WireframeGeometry, J as Box3, K as Sphere, X as UniformsUtils, Y as UniformsLib, Z as Vector4, $ as Line3, a0 as Matrix4, a1 as MathUtils, a2 as Uniform, a3 as WebGLRenderTarget, a4 as DepthTexture, a5 as BackSide, a6 as ClampToEdgeWrapping, a7 as PlaneGeometry, a8 as UVMapping, a9 as DataTexture, aa as Texture, ab as MeshBasicMaterial, ac as IntType, ad as ShortType, ae as ByteType, af as UnsignedIntType, ag as Loader, ah as LoadingManager, ai as LinearMipMapLinearFilter, aj as FileLoader, ak as NoBlending, al as CubeReflectionMapping, am as EquirectangularReflectionMapping, an as CubeTextureLoader, ao as WebGLCubeRenderTarget, ap as ConstraintSketch, aq as setSketchPlacement3D, ar as Sketch, as as PROFILE_BACKEND_MARKER, at as FrozenShape, au as setShapeCompilePlan, av as hasAnyPorts, aw as setShapePortsInternal, ax as markShapePortsUsed, ay as writeViewPreferences, az as setParamOverrides, aA as readViewPreferences, aB as resolveForgeRenderStyle, aC as isConstraintSketch, aD as updateConstraintValue, aE as linearizeMultiObjectSteps, aF as getShapeCompilePlan, aG as resolveCameraControlMode, aH as resolveComparisonOpacity, aI as resolveComparisonInspectMode, aJ as resolveBooleanPref, aK as resolveInspectQuantizeBands, aL as resolveInspectIsolineSpacing, aM as resolveInspectColormap, aN as resolveThicknessColorRange, aO as resolveInspectPointSampleCount, aP as SCAN_PROXY_GRANULARITY_DEFAULT, aQ as DEFAULT_MANUAL_SCENE_SETTINGS, aR as resolveManualSceneSettings, aS as publishSolverWasmRunDebug, aT as resolveForgeQualityPreset, aU as DEFAULT_INSPECT_ISOLINES_ENABLED, aV as SCAN_PROXY_MATRIX_GRANULARITY_MAX, aW as findJointAnimationClip, aX as resolveJointAnimation, aY as resolveJointViewValues, aZ as resolveImportPath, a_ as DEFAULT_INSPECT_CRITICAL_LINE_ENABLED, a$ as resolveScanProxyGranularity, b0 as DEFAULT_COMPARISON_REFERENCE_OPACITY, b1 as DEFAULT_COMPARISON_CANDIDATE_OPACITY, b2 as BufferGeometry, b3 as LineBasicMaterial, b4 as Line$1, b5 as LineDashedMaterial, b6 as CanvasTexture, b7 as Object3D, b8 as FogExp2, b9 as Fog, ba as AmbientLight, bb as HemisphereLight, bc as SpotLight, bd as PointLight, be as DirectionalLight, bf as BufferAttribute, bg as heatPointsForSide, bh as COMPARISON_COLORS, bi as comparisonHeatDepthTest, bj as comparisonHeatEdgeOpacity, bk as comparisonHeatPatchOpacity, bl as shapeToGeometry, bm as buildComparisonHeatPatchGeometry, bn as EdgesGeometry, bo as buildShapeFromCompilePlan, bp as VIEWPORT_CAMERA_STORAGE_KEY, bq as parseViewportCameraState, br as getKernelFaceNameForTriangle, bs as OBJECT_CONTEXT_MENU_MARGIN, bt as buildVisibleHistoryStacks, bu as sketchToSvg, bv as sketchToDxf, bw as runScript, bx as MeshPhysicalMaterial, by as LineSegments, bz as findDesignTraceNodeForConstructionStep, bA as formatDesignTraceAnchor, bB as waitForAnimationFrame, bC as selectBuildLedgerNodes, bD as UNIDENTIFIED_FACE_NAME, bE as getBakedEdges, bF as worldAuthorPlaneToLocal, bG as compileSdfProgramEvaluator3, bH as SDF_LINEAR_OUTPUT_COLOR_GLSL, bI as GLSL3, bJ as BoxGeometry, bK as Data3DTexture, bL as buildSdfRaymarchFragmentShader, bM as SDF_RAYMARCH_PROXY_VERTEX_SHADER, bN as scanProxySourceBytes, bO as disposeScanProxyGeometry, bP as scanProxyGeometryFromPayload, bQ as AdditiveBlending, bR as geometryWithVisibleVertexColors, bS as MeshBVH, bT as makeColorScaleTexture, bU as colorScaleLUT, bV as makeScalarValueTexture, bW as makeInspectScalarUniforms, bX as updateInspectScalarUniforms, bY as descriptorToThreeTexture, bZ as applyProjectedTexture, b_ as getRenderStylePreset, b$ as SCAN_RENDER_COLORS, c0 as NormalBlending, c1 as acceleratedRaycast, c2 as INSPECT_SCALAR_FRAGMENT_SHADER, c3 as INSPECT_SCALAR_VERTEX_SHADER, c4 as scanMaterialShellColor, c5 as ZEBRA_STRIPE_SOFTNESS, c6 as ZEBRA_STRIPE_SCALE, c7 as ZEBRA_LIGHT_COLOR, c8 as ZEBRA_DARK_COLOR, c9 as ZEBRA_ACCENT_COLOR, ca as ZEBRA_STRIPE_FRAGMENT_SHADER, cb as ZEBRA_STRIPE_VERTEX_SHADER, cc as SCAN_PROXY_LAYER_STYLES, cd as scanMaterialLayerStyles, ce as SURFACE_FIELD_FRAGMENT_SHADER, cf as SURFACE_FIELD_VERTEX_SHADER, cg as WORLD_UP$1, ch as CatmullRomCurve3, ci as TubeGeometry, cj as DEFAULT_THICKNESS_COLOR_RANGE, ck as DEFAULT_COLORMAP, cl as colorScaleHexStops, cm as PERFORMANCE_SAMPLE_INTERVAL_SEC, cn as formatPerformanceCount, co as NON_TEXT_INPUT_TYPES, cp as MeshStandardMaterial, cq as Shape, cr as ShapeGeometry, cs as ShaderLib, ct as CylinderGeometry, cu as createResolvedExplodeConfig, cv as explodeBoundsCenter, cw as explodeMergeBounds, cx as resolveExplodeDirective, cy as computeExplodeMotion, cz 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_WIDTH, cQ as OBJECT_CONTEXT_MENU_HEIGHT, cR as placement2DItemsOverlap, cS as placement2DFootprintInsideFrame, cT as placement2DPointInsideFrame, cU as placement2DFrameBounds, cV as placement2DClampCenterToFrame, cW as triangleSoupFromMeshes, cX as compareTriangleSoups, cY as buildGeometryComparisonPointCloud, cZ as aabbOverlaps, c_ as aabbOverlapVolume, c$ as DEFAULT_COLLISION_INSPECTION_OPTIONS, d0 as resolveScalarSceneSampleBudget, d1 as INSPECT_POINT_SAMPLE_COUNT_MIN, d2 as FOCUS_MODE_DIM_OPACITY, d3 as DEFAULT_THICKNESS_INSPECTION_OPTIONS, d4 as comparisonCandidateContextOpacity, d5 as Matrix3, d6 as initBackendForEvaluation } from "./backendInit-6a9-ilom.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];
|
|
@@ -213,7 +213,7 @@ function trimRunResultCache() {
|
|
|
213
213
|
}
|
|
214
214
|
function persistCache() {
|
|
215
215
|
try {
|
|
216
|
-
const
|
|
216
|
+
const entries2 = {};
|
|
217
217
|
let estimatedPersistBytes = 0;
|
|
218
218
|
for (const [key, entry] of runResultCache) {
|
|
219
219
|
estimatedPersistBytes += entry.estimatedBytes;
|
|
@@ -224,7 +224,7 @@ function persistCache() {
|
|
|
224
224
|
}
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
|
-
|
|
227
|
+
entries2[key] = {
|
|
228
228
|
code: entry.code,
|
|
229
229
|
files: entry.files,
|
|
230
230
|
paramOverrides: entry.paramOverrides,
|
|
@@ -234,7 +234,7 @@ function persistCache() {
|
|
|
234
234
|
serialized: serializedResultToJson(entry.serialized)
|
|
235
235
|
};
|
|
236
236
|
}
|
|
237
|
-
const json = JSON.stringify({ v: CACHE_VERSION, entries });
|
|
237
|
+
const json = JSON.stringify({ v: CACHE_VERSION, entries: entries2 });
|
|
238
238
|
if (json.length > MAX_PERSIST_BYTES) {
|
|
239
239
|
try {
|
|
240
240
|
sessionStorage.removeItem(sessionStorageKey());
|
|
@@ -666,14 +666,14 @@ function BrandMark({
|
|
|
666
666
|
}
|
|
667
667
|
);
|
|
668
668
|
}
|
|
669
|
-
let nextId = 0;
|
|
669
|
+
let nextId$1 = 0;
|
|
670
670
|
let toasts = [];
|
|
671
671
|
const listeners$2 = /* @__PURE__ */ new Set();
|
|
672
672
|
function notify$1() {
|
|
673
673
|
listeners$2.forEach((l2) => l2());
|
|
674
674
|
}
|
|
675
675
|
function showToast(message, variant = "info", durationMs = 3e3) {
|
|
676
|
-
const toast = { id: nextId++, message, variant, durationMs };
|
|
676
|
+
const toast = { id: nextId$1++, message, variant, durationMs };
|
|
677
677
|
toasts = [...toasts, toast];
|
|
678
678
|
notify$1();
|
|
679
679
|
setTimeout(() => {
|
|
@@ -14866,6 +14866,7 @@ const dark = {
|
|
|
14866
14866
|
bg: "#0d1117",
|
|
14867
14867
|
bgPanel: "#161b22",
|
|
14868
14868
|
bgSurface: "#1c2128",
|
|
14869
|
+
bgSubtle: "#131920",
|
|
14869
14870
|
bgHover: "#242b35",
|
|
14870
14871
|
bgActive: "#2d333b",
|
|
14871
14872
|
bgInput: "#0a0e14",
|
|
@@ -14902,6 +14903,7 @@ const light = {
|
|
|
14902
14903
|
bg: "#f0f4f8",
|
|
14903
14904
|
bgPanel: "#f8fafc",
|
|
14904
14905
|
bgSurface: "#e8edf2",
|
|
14906
|
+
bgSubtle: "#eef2f6",
|
|
14905
14907
|
bgHover: "#dfe5ec",
|
|
14906
14908
|
bgActive: "#d2dae3",
|
|
14907
14909
|
bgInput: "#f8fafc",
|
|
@@ -14938,6 +14940,7 @@ const gruvbox = {
|
|
|
14938
14940
|
bg: "#282828",
|
|
14939
14941
|
bgPanel: "#1d2021",
|
|
14940
14942
|
bgSurface: "#3c3836",
|
|
14943
|
+
bgSubtle: "#32302f",
|
|
14941
14944
|
bgHover: "#504945",
|
|
14942
14945
|
bgActive: "#665c54",
|
|
14943
14946
|
bgInput: "#1d2021",
|
|
@@ -14974,6 +14977,7 @@ const tokyoNight = {
|
|
|
14974
14977
|
bg: "#1a1b26",
|
|
14975
14978
|
bgPanel: "#16161e",
|
|
14976
14979
|
bgSurface: "#24283b",
|
|
14980
|
+
bgSubtle: "#1f2335",
|
|
14977
14981
|
bgHover: "#292e42",
|
|
14978
14982
|
bgActive: "#33467c",
|
|
14979
14983
|
bgInput: "#16161e",
|
|
@@ -15010,6 +15014,7 @@ const kanagawaLotus = {
|
|
|
15010
15014
|
bg: "#f2ecbc",
|
|
15011
15015
|
bgPanel: "#f7f3d7",
|
|
15012
15016
|
bgSurface: "#e7dba0",
|
|
15017
|
+
bgSubtle: "#eee6b2",
|
|
15013
15018
|
bgHover: "#d9d08e",
|
|
15014
15019
|
bgActive: "#c9b97a",
|
|
15015
15020
|
bgInput: "#f7f3d7",
|
|
@@ -15046,6 +15051,7 @@ const shield = {
|
|
|
15046
15051
|
bg: "#02070a",
|
|
15047
15052
|
bgPanel: "#06141a",
|
|
15048
15053
|
bgSurface: "#0a2028",
|
|
15054
|
+
bgSubtle: "#031016",
|
|
15049
15055
|
bgHover: "#0d2b35",
|
|
15050
15056
|
bgActive: "#113d4a",
|
|
15051
15057
|
bgInput: "#01080c",
|
|
@@ -15082,6 +15088,7 @@ const shieldLight = {
|
|
|
15082
15088
|
bg: "#cdd5d3",
|
|
15083
15089
|
bgPanel: "#dbe3e0",
|
|
15084
15090
|
bgSurface: "#b9c8c5",
|
|
15091
|
+
bgSubtle: "#c4cfcc",
|
|
15085
15092
|
bgHover: "#a9b8b5",
|
|
15086
15093
|
bgActive: "#8ea3a0",
|
|
15087
15094
|
bgInput: "#eef3f1",
|
|
@@ -15287,15 +15294,14 @@ const runResultDeserializer = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Obje
|
|
|
15287
15294
|
__proto__: null,
|
|
15288
15295
|
deserializeRunResult
|
|
15289
15296
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
15290
|
-
function
|
|
15291
|
-
|
|
15292
|
-
return plan.kind !== "sdf";
|
|
15297
|
+
function compilePlanNeedsServerShapeLowering(_plan) {
|
|
15298
|
+
return true;
|
|
15293
15299
|
}
|
|
15294
|
-
function
|
|
15295
|
-
return object.compilePlan !== null && object.compilePlan !== void 0 &&
|
|
15300
|
+
function serializedSceneObjectNeedsServerShapeLowering(object) {
|
|
15301
|
+
return object.compilePlan !== null && object.compilePlan !== void 0 && compilePlanNeedsServerShapeLowering(object.compilePlan);
|
|
15296
15302
|
}
|
|
15297
|
-
function
|
|
15298
|
-
return result.objects.some(
|
|
15303
|
+
function serializedRunResultNeedsServerIrLowering(result) {
|
|
15304
|
+
return result.objects.some(serializedSceneObjectNeedsServerShapeLowering);
|
|
15299
15305
|
}
|
|
15300
15306
|
function decodeFromWire(data) {
|
|
15301
15307
|
const view2 = new DataView(data);
|
|
@@ -15976,7 +15982,7 @@ const CRASH_COOLDOWN_MS = 2e3;
|
|
|
15976
15982
|
class EvalWorkerClient {
|
|
15977
15983
|
constructor(workerFactory = () => new Worker(new URL(
|
|
15978
15984
|
/* @vite-ignore */
|
|
15979
|
-
"/assets/evalWorker-
|
|
15985
|
+
"/assets/evalWorker-DQ82ueGu.js",
|
|
15980
15986
|
import.meta.url
|
|
15981
15987
|
), { type: "module" })) {
|
|
15982
15988
|
__publicField(this, "worker", null);
|
|
@@ -17168,8 +17174,8 @@ async function fetchGistModel(gistId) {
|
|
|
17168
17174
|
if (!res.ok) throw new Error(`Failed to fetch gist: ${res.status} ${res.statusText}`);
|
|
17169
17175
|
const data = await res.json();
|
|
17170
17176
|
const files = data.files;
|
|
17171
|
-
const
|
|
17172
|
-
const forgeFile =
|
|
17177
|
+
const entries2 = Object.values(files);
|
|
17178
|
+
const forgeFile = entries2.find((f2) => f2.filename.endsWith(".forge.js")) || entries2.find((f2) => f2.filename.endsWith(".sketch.js")) || entries2[0];
|
|
17173
17179
|
if (!forgeFile) throw new Error("Gist contains no files");
|
|
17174
17180
|
return { filename: forgeFile.filename, code: forgeFile.content };
|
|
17175
17181
|
}
|
|
@@ -17360,6 +17366,9 @@ function createErrorRunResult(message, quality) {
|
|
|
17360
17366
|
params: [],
|
|
17361
17367
|
stringParams: [],
|
|
17362
17368
|
listParams: [],
|
|
17369
|
+
path2dParams: [],
|
|
17370
|
+
spline2dParams: [],
|
|
17371
|
+
placement2dParams: [],
|
|
17363
17372
|
dimensions: [],
|
|
17364
17373
|
highlights: [],
|
|
17365
17374
|
debugHighlights3D: [],
|
|
@@ -17431,6 +17440,9 @@ function buildRunState(previewFile, runResult, state2) {
|
|
|
17431
17440
|
params: runResult.params,
|
|
17432
17441
|
stringParams: runResult.stringParams,
|
|
17433
17442
|
listParams: runResult.listParams,
|
|
17443
|
+
path2dParams: runResult.path2dParams ?? [],
|
|
17444
|
+
spline2dParams: runResult.spline2dParams ?? [],
|
|
17445
|
+
placement2dParams: runResult.placement2dParams ?? [],
|
|
17434
17446
|
jointValues: nextJointValues,
|
|
17435
17447
|
jointAnimationClip: nextAnimationState.clip,
|
|
17436
17448
|
jointAnimationProgress: nextAnimationState.progress,
|
|
@@ -17452,13 +17464,15 @@ const KERNELS = [
|
|
|
17452
17464
|
{ id: "occt", name: "occt", location: "local", label: "OCCT" },
|
|
17453
17465
|
{ id: "truck", name: "truck", location: "local", label: "Truck" },
|
|
17454
17466
|
{ id: "sdf", name: "sdf", location: "local", label: "SDF" },
|
|
17455
|
-
{ id: "server-occt", name: "occt", location: "server", label: "Server OCCT" }
|
|
17467
|
+
{ id: "server-occt", name: "occt", location: "server", label: "Server OCCT" },
|
|
17468
|
+
{ id: "server-sdf", name: "sdf", location: "server", label: "Server SDF" }
|
|
17456
17469
|
];
|
|
17457
17470
|
function availableKernels(canUseServer) {
|
|
17458
17471
|
return KERNELS.filter((kernel) => kernel.location === "local" || canUseServer);
|
|
17459
17472
|
}
|
|
17460
17473
|
function kernelId(kernel) {
|
|
17461
|
-
|
|
17474
|
+
if (kernel.location === "local") return kernel.name;
|
|
17475
|
+
return kernel.name === "sdf" ? "server-sdf" : "server-occt";
|
|
17462
17476
|
}
|
|
17463
17477
|
function kernelFromId(id) {
|
|
17464
17478
|
const kernel = KERNELS.find((entry) => entry.id === id);
|
|
@@ -17533,6 +17547,9 @@ function errorRunResult(error, timeMs = 0) {
|
|
|
17533
17547
|
params: [],
|
|
17534
17548
|
stringParams: [],
|
|
17535
17549
|
listParams: [],
|
|
17550
|
+
path2dParams: [],
|
|
17551
|
+
spline2dParams: [],
|
|
17552
|
+
placement2dParams: [],
|
|
17536
17553
|
dimensions: [],
|
|
17537
17554
|
highlights: [],
|
|
17538
17555
|
debugHighlights3D: [],
|
|
@@ -17715,7 +17732,8 @@ function codeEditorPatchForActiveFile(activeFile, files, meshPreviewFile) {
|
|
|
17715
17732
|
}
|
|
17716
17733
|
function runResultHasViewportContent(result) {
|
|
17717
17734
|
var _a3, _b2, _c, _d;
|
|
17718
|
-
|
|
17735
|
+
const hasAnchoredParams = result.params.some((param) => !!param.anchor) || result.stringParams.some((param) => !!param.anchor) || result.listParams.some((param) => !!param.anchor) || (result.path2dParams ?? []).some((param) => !!param.anchor) || (result.spline2dParams ?? []).some((param) => !!param.anchor) || (result.placement2dParams ?? []).some((param) => !!param.anchor);
|
|
17736
|
+
return result.objects.length > 0 || !!result.assemblyKinematics || (((_a3 = result.jointsView) == null ? void 0 : _a3.joints.length) ?? 0) > 0 && ((_b2 = result.jointsView) == null ? void 0 : _b2.enabled) !== false || (((_c = result.renderLabels) == null ? void 0 : _c.length) ?? 0) > 0 || (((_d = result.debugHighlights3D) == null ? void 0 : _d.length) ?? 0) > 0 || hasAnchoredParams;
|
|
17719
17737
|
}
|
|
17720
17738
|
function codeEditorPatchForRunResult(result, meshPreviewFile) {
|
|
17721
17739
|
if (meshPreviewFile || runResultHasViewportContent(result)) return {};
|
|
@@ -17749,7 +17767,7 @@ const makeParamSnapshotId = () => {
|
|
|
17749
17767
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
17750
17768
|
return `param-snapshot-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
17751
17769
|
};
|
|
17752
|
-
function filterOverridesForCurrentParams(overrides, params, stringParams, listParams) {
|
|
17770
|
+
function filterOverridesForCurrentParams(overrides, params, stringParams, listParams, path2dParams, spline2dParams, placement2dParams) {
|
|
17753
17771
|
const scalarNames = /* @__PURE__ */ new Set([...params.map((param) => param.name), ...stringParams.map((param) => param.name)]);
|
|
17754
17772
|
const filtered = {};
|
|
17755
17773
|
let ignoredCount = 0;
|
|
@@ -17759,17 +17777,51 @@ function filterOverridesForCurrentParams(overrides, params, stringParams, listPa
|
|
|
17759
17777
|
continue;
|
|
17760
17778
|
}
|
|
17761
17779
|
const listDef = listParams.find((candidate) => key === `${candidate.name}.__count__` || key.startsWith(`${candidate.name}[`));
|
|
17762
|
-
if (
|
|
17763
|
-
|
|
17780
|
+
if (listDef) {
|
|
17781
|
+
if (key === `${listDef.name}.__count__`) {
|
|
17782
|
+
filtered[key] = value;
|
|
17783
|
+
continue;
|
|
17784
|
+
}
|
|
17785
|
+
const fieldName = key.slice(key.lastIndexOf(".") + 1);
|
|
17786
|
+
if (listDef.fieldOrder.includes(fieldName)) filtered[key] = value;
|
|
17787
|
+
else ignoredCount += 1;
|
|
17764
17788
|
continue;
|
|
17765
17789
|
}
|
|
17766
|
-
|
|
17767
|
-
|
|
17790
|
+
const pathDef = path2dParams.find((candidate) => key === `${candidate.name}.__count__` || key.startsWith(`${candidate.name}[`));
|
|
17791
|
+
if (pathDef) {
|
|
17792
|
+
if (key === `${pathDef.name}.__count__`) {
|
|
17793
|
+
filtered[key] = value;
|
|
17794
|
+
continue;
|
|
17795
|
+
}
|
|
17796
|
+
const fieldName = key.slice(key.lastIndexOf(".") + 1);
|
|
17797
|
+
if (fieldName === "x" || fieldName === "y") filtered[key] = value;
|
|
17798
|
+
else ignoredCount += 1;
|
|
17799
|
+
continue;
|
|
17800
|
+
}
|
|
17801
|
+
const splineDef = spline2dParams.find((candidate) => key === `${candidate.name}.__count__` || key.startsWith(`${candidate.name}[`));
|
|
17802
|
+
if (splineDef) {
|
|
17803
|
+
if (key === `${splineDef.name}.__count__`) {
|
|
17804
|
+
filtered[key] = value;
|
|
17805
|
+
continue;
|
|
17806
|
+
}
|
|
17807
|
+
const fieldName = key.slice(key.lastIndexOf(".") + 1);
|
|
17808
|
+
if (fieldName === "x" || fieldName === "y" || fieldName === "g") filtered[key] = value;
|
|
17809
|
+
else ignoredCount += 1;
|
|
17810
|
+
continue;
|
|
17811
|
+
}
|
|
17812
|
+
const placementDef = placement2dParams.find((candidate) => key.startsWith(`${candidate.name}.`));
|
|
17813
|
+
if (placementDef) {
|
|
17814
|
+
const suffix = key.slice(placementDef.name.length + 1);
|
|
17815
|
+
const [itemId, fieldName, ...extra] = suffix.split(".");
|
|
17816
|
+
const item = placementDef.items.find((candidate) => candidate.id === itemId);
|
|
17817
|
+
if (item && extra.length === 0 && (fieldName === "x" || fieldName === "y" || fieldName === "angle" || fieldName === "zone")) {
|
|
17818
|
+
filtered[key] = value;
|
|
17819
|
+
} else {
|
|
17820
|
+
ignoredCount += 1;
|
|
17821
|
+
}
|
|
17768
17822
|
continue;
|
|
17769
17823
|
}
|
|
17770
|
-
|
|
17771
|
-
if (listDef.fieldOrder.includes(fieldName)) filtered[key] = value;
|
|
17772
|
-
else ignoredCount += 1;
|
|
17824
|
+
ignoredCount += 1;
|
|
17773
17825
|
}
|
|
17774
17826
|
return { overrides: filtered, ignoredCount };
|
|
17775
17827
|
}
|
|
@@ -18186,6 +18238,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18186
18238
|
params: [],
|
|
18187
18239
|
stringParams: [],
|
|
18188
18240
|
listParams: [],
|
|
18241
|
+
path2dParams: [],
|
|
18242
|
+
spline2dParams: [],
|
|
18243
|
+
placement2dParams: [],
|
|
18189
18244
|
runQuality: resolveForgeQualityPreset(initialViewPreferences.runQuality ?? "live"),
|
|
18190
18245
|
setRunQuality: (quality) => {
|
|
18191
18246
|
const next = resolveForgeQualityPreset(quality);
|
|
@@ -18196,6 +18251,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18196
18251
|
paramOverrides: {},
|
|
18197
18252
|
paramOverridesByFile: {},
|
|
18198
18253
|
paramSnapshotsByFile: initialParamSnapshotsByFile,
|
|
18254
|
+
focusedParamName: null,
|
|
18255
|
+
spatialParamSheetName: null,
|
|
18256
|
+
expandedSpatialParamSheetName: null,
|
|
18199
18257
|
jointValues: {},
|
|
18200
18258
|
jointAnimationClip: null,
|
|
18201
18259
|
jointAnimationProgress: 0,
|
|
@@ -18234,8 +18292,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18234
18292
|
},
|
|
18235
18293
|
computeTarget: initialKernel.location,
|
|
18236
18294
|
setComputeTarget: (target) => {
|
|
18295
|
+
const preferredServerBackend = get().activeBackend === "sdf" ? "sdf" : "occt";
|
|
18237
18296
|
const kernel = resolveAvailableKernel(
|
|
18238
|
-
{ name: target === "server" ?
|
|
18297
|
+
{ name: target === "server" ? preferredServerBackend : get().activeBackend, location: target },
|
|
18239
18298
|
availableKernels(canUseServerCompute())
|
|
18240
18299
|
);
|
|
18241
18300
|
writeViewPreferences({ activeBackend: kernel.name, computeTarget: kernel.location });
|
|
@@ -18293,6 +18352,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18293
18352
|
params: [],
|
|
18294
18353
|
stringParams: [],
|
|
18295
18354
|
listParams: [],
|
|
18355
|
+
path2dParams: [],
|
|
18356
|
+
spline2dParams: [],
|
|
18357
|
+
placement2dParams: [],
|
|
18296
18358
|
previewFile: null,
|
|
18297
18359
|
objectSettings: {},
|
|
18298
18360
|
buildLedgerEvents: []
|
|
@@ -18346,14 +18408,15 @@ const useForgeStore = create((set, get) => ({
|
|
|
18346
18408
|
serverComputeAbortController = abortController;
|
|
18347
18409
|
try {
|
|
18348
18410
|
const irRequest = await evalWorkerClient.runIrPlan(runPayload);
|
|
18349
|
-
if (irRequest.planResult.error || !
|
|
18411
|
+
if (irRequest.planResult.error || !serializedRunResultNeedsServerIrLowering(irRequest.planResult)) {
|
|
18350
18412
|
serialized = irRequest.planResult;
|
|
18351
18413
|
} else {
|
|
18352
18414
|
const refreshTimer2 = window.setTimeout(() => {
|
|
18353
18415
|
void get().refreshServerJobs();
|
|
18354
18416
|
}, 250);
|
|
18355
18417
|
try {
|
|
18356
|
-
|
|
18418
|
+
const serverBackend = get().activeBackend === "sdf" ? "sdf" : "occt";
|
|
18419
|
+
serialized = await computeIr({ ...irRequest, serverBackend }, abortController.signal);
|
|
18357
18420
|
} finally {
|
|
18358
18421
|
window.clearTimeout(refreshTimer2);
|
|
18359
18422
|
void get().refreshServerJobs();
|
|
@@ -18494,6 +18557,19 @@ const useForgeStore = create((set, get) => ({
|
|
|
18494
18557
|
get().execute();
|
|
18495
18558
|
}, PARAM_DEBOUNCE_MS);
|
|
18496
18559
|
},
|
|
18560
|
+
setParams: (values, deleteKeys = []) => {
|
|
18561
|
+
const overrides = { ...get().paramOverrides, ...values };
|
|
18562
|
+
for (const key of deleteKeys) delete overrides[key];
|
|
18563
|
+
const { activeFile: curFile, paramOverridesByFile } = get();
|
|
18564
|
+
const previewKey = curFile ? resolvePreviewFile(curFile, get().files) : null;
|
|
18565
|
+
const nextByFile = previewKey ? { ...paramOverridesByFile, [previewKey]: overrides } : paramOverridesByFile;
|
|
18566
|
+
set({ paramOverrides: overrides, paramOverridesByFile: nextByFile });
|
|
18567
|
+
if (paramExecuteTimer) clearTimeout(paramExecuteTimer);
|
|
18568
|
+
paramExecuteTimer = setTimeout(() => {
|
|
18569
|
+
paramExecuteTimer = null;
|
|
18570
|
+
get().execute();
|
|
18571
|
+
}, PARAM_DEBOUNCE_MS);
|
|
18572
|
+
},
|
|
18497
18573
|
resetParamOverrides: () => {
|
|
18498
18574
|
const { activeFile, files, paramOverridesByFile } = get();
|
|
18499
18575
|
const previewKey = activeFile ? resolvePreviewFile(activeFile, files) : null;
|
|
@@ -18503,6 +18579,11 @@ const useForgeStore = create((set, get) => ({
|
|
|
18503
18579
|
setParamOverrides({});
|
|
18504
18580
|
get().execute();
|
|
18505
18581
|
},
|
|
18582
|
+
focusParam: (name) => set({ focusedParamName: name }),
|
|
18583
|
+
openSpatialParamSheet: (name) => set({ focusedParamName: name, spatialParamSheetName: name, expandedSpatialParamSheetName: null }),
|
|
18584
|
+
expandSpatialParamSheet: (name) => set({ focusedParamName: name, spatialParamSheetName: name, expandedSpatialParamSheetName: name }),
|
|
18585
|
+
closeExpandedSpatialParamSheet: () => set({ expandedSpatialParamSheetName: null }),
|
|
18586
|
+
closeSpatialParamSheet: () => set({ spatialParamSheetName: null, expandedSpatialParamSheetName: null }),
|
|
18506
18587
|
captureParamSnapshot: () => {
|
|
18507
18588
|
var _a3;
|
|
18508
18589
|
const { activeFile, files, paramOverrides, paramSnapshotsByFile } = get();
|
|
@@ -18522,11 +18603,30 @@ const useForgeStore = create((set, get) => ({
|
|
|
18522
18603
|
},
|
|
18523
18604
|
applyParamSnapshot: (id) => {
|
|
18524
18605
|
var _a3;
|
|
18525
|
-
const {
|
|
18606
|
+
const {
|
|
18607
|
+
activeFile,
|
|
18608
|
+
files,
|
|
18609
|
+
paramOverridesByFile,
|
|
18610
|
+
paramSnapshotsByFile,
|
|
18611
|
+
params,
|
|
18612
|
+
stringParams,
|
|
18613
|
+
listParams,
|
|
18614
|
+
path2dParams,
|
|
18615
|
+
spline2dParams,
|
|
18616
|
+
placement2dParams
|
|
18617
|
+
} = get();
|
|
18526
18618
|
const previewKey = activeFile ? resolvePreviewFile(activeFile, files) : null;
|
|
18527
18619
|
const snapshot = previewKey ? (_a3 = paramSnapshotsByFile[previewKey]) == null ? void 0 : _a3.find((candidate) => candidate.id === id) : null;
|
|
18528
18620
|
if (!previewKey || !snapshot) return;
|
|
18529
|
-
const { overrides, ignoredCount } = filterOverridesForCurrentParams(
|
|
18621
|
+
const { overrides, ignoredCount } = filterOverridesForCurrentParams(
|
|
18622
|
+
snapshot.overrides,
|
|
18623
|
+
params,
|
|
18624
|
+
stringParams,
|
|
18625
|
+
listParams,
|
|
18626
|
+
path2dParams,
|
|
18627
|
+
spline2dParams,
|
|
18628
|
+
placement2dParams
|
|
18629
|
+
);
|
|
18530
18630
|
const nextByFile = { ...paramOverridesByFile };
|
|
18531
18631
|
if (Object.keys(overrides).length > 0) nextByFile[previewKey] = overrides;
|
|
18532
18632
|
else delete nextByFile[previewKey];
|
|
@@ -19351,6 +19451,12 @@ const useForgeStore = create((set, get) => ({
|
|
|
19351
19451
|
writeViewPreferences({ dimensionsVisible: nextDimensionsVisible });
|
|
19352
19452
|
return { dimensionsVisible: nextDimensionsVisible };
|
|
19353
19453
|
}),
|
|
19454
|
+
paramAnchorsVisible: initialViewPreferences.paramAnchorsVisible ?? true,
|
|
19455
|
+
toggleParamAnchors: () => set((s) => {
|
|
19456
|
+
const nextParamAnchorsVisible = !s.paramAnchorsVisible;
|
|
19457
|
+
writeViewPreferences({ paramAnchorsVisible: nextParamAnchorsVisible });
|
|
19458
|
+
return { paramAnchorsVisible: nextParamAnchorsVisible };
|
|
19459
|
+
}),
|
|
19354
19460
|
attachmentsVisible: initialViewPreferences.attachmentsVisible ?? "none",
|
|
19355
19461
|
setAttachmentsVisible: (mode) => {
|
|
19356
19462
|
writeViewPreferences({ attachmentsVisible: mode });
|
|
@@ -19528,11 +19634,11 @@ const useForgeStore = create((set, get) => ({
|
|
|
19528
19634
|
fileSystem.save(normalized, text).catch((e2) => console.error("Save failed:", e2));
|
|
19529
19635
|
setTimeout(() => get().execute(), 0);
|
|
19530
19636
|
},
|
|
19531
|
-
importTextFiles: async (
|
|
19637
|
+
importTextFiles: async (entries2, options = {}) => {
|
|
19532
19638
|
const { files, folders, activeFile } = get();
|
|
19533
19639
|
const occupied = /* @__PURE__ */ new Set([...Object.keys(files), ...folders]);
|
|
19534
19640
|
const imported = [];
|
|
19535
|
-
for (const entry of
|
|
19641
|
+
for (const entry of entries2) {
|
|
19536
19642
|
const targetPath = resolveImportedProjectPath(entry.path, options.targetFolder);
|
|
19537
19643
|
if (!targetPath) continue;
|
|
19538
19644
|
const path = uniquifyProjectPath(targetPath, occupied);
|
|
@@ -19771,8 +19877,9 @@ onServerAvailabilityChange((available) => {
|
|
|
19771
19877
|
useForgeStore.setState({ serverAvailable: available });
|
|
19772
19878
|
});
|
|
19773
19879
|
const savedComputeTarget = initialViewPreferences.computeTarget;
|
|
19880
|
+
const savedServerBackend = initialViewPreferences.activeBackend === "sdf" ? "sdf" : "occt";
|
|
19774
19881
|
if (savedComputeTarget === "server" && canUseServerCompute()) {
|
|
19775
|
-
useForgeStore.setState({ activeBackend:
|
|
19882
|
+
useForgeStore.setState({ activeBackend: savedServerBackend, computeTarget: "server" });
|
|
19776
19883
|
startServerPolling();
|
|
19777
19884
|
}
|
|
19778
19885
|
{
|
|
@@ -22972,6 +23079,51 @@ function DroneCrosshairPicker({
|
|
|
22972
23079
|
}, [active, focusHitOrClear, gl.domElement, hideLabel, showHitInfo]);
|
|
22973
23080
|
return null;
|
|
22974
23081
|
}
|
|
23082
|
+
const ESCAPE_PRIORITY = {
|
|
23083
|
+
viewport: 20,
|
|
23084
|
+
popover: 50,
|
|
23085
|
+
modal: 100
|
|
23086
|
+
};
|
|
23087
|
+
let nextId = 1;
|
|
23088
|
+
let entries = [];
|
|
23089
|
+
let listening = false;
|
|
23090
|
+
function dispatchEscape(event) {
|
|
23091
|
+
if (event.key !== "Escape" || event.isComposing) return;
|
|
23092
|
+
const ordered = [...entries].sort((a2, b2) => b2.priority - a2.priority || b2.id - a2.id);
|
|
23093
|
+
for (const entry of ordered) {
|
|
23094
|
+
if (!entry.actionRef.current(event)) continue;
|
|
23095
|
+
event.preventDefault();
|
|
23096
|
+
event.stopPropagation();
|
|
23097
|
+
event.stopImmediatePropagation();
|
|
23098
|
+
return;
|
|
23099
|
+
}
|
|
23100
|
+
}
|
|
23101
|
+
function ensureEscapeListener() {
|
|
23102
|
+
if (listening || typeof window === "undefined") return;
|
|
23103
|
+
window.addEventListener("keydown", dispatchEscape, { capture: true });
|
|
23104
|
+
listening = true;
|
|
23105
|
+
}
|
|
23106
|
+
function removeEscapeListenerIfIdle() {
|
|
23107
|
+
if (!listening || entries.length > 0 || typeof window === "undefined") return;
|
|
23108
|
+
window.removeEventListener("keydown", dispatchEscape, { capture: true });
|
|
23109
|
+
listening = false;
|
|
23110
|
+
}
|
|
23111
|
+
function useEscapeAction(action, { active, label, priority = ESCAPE_PRIORITY.viewport }) {
|
|
23112
|
+
const actionRef = reactExports.useRef(action);
|
|
23113
|
+
reactExports.useEffect(() => {
|
|
23114
|
+
actionRef.current = action;
|
|
23115
|
+
}, [action]);
|
|
23116
|
+
reactExports.useEffect(() => {
|
|
23117
|
+
if (!active) return;
|
|
23118
|
+
ensureEscapeListener();
|
|
23119
|
+
const entry = { id: nextId++, label, priority, actionRef };
|
|
23120
|
+
entries = [...entries, entry];
|
|
23121
|
+
return () => {
|
|
23122
|
+
entries = entries.filter((candidate) => candidate !== entry);
|
|
23123
|
+
removeEscapeListenerIfIdle();
|
|
23124
|
+
};
|
|
23125
|
+
}, [active, label, priority]);
|
|
23126
|
+
}
|
|
22975
23127
|
const DEFAULT_DRONE_CAMERA_STATUS = {
|
|
22976
23128
|
speed: 0,
|
|
22977
23129
|
pointerLocked: false,
|
|
@@ -23070,6 +23222,13 @@ function DroneCameraController({
|
|
|
23070
23222
|
const pointerLockedRef = reactExports.useRef(false);
|
|
23071
23223
|
const lastStatusRef = reactExports.useRef(DEFAULT_DRONE_CAMERA_STATUS);
|
|
23072
23224
|
const handledKeyboardEventsRef = reactExports.useRef(/* @__PURE__ */ new WeakSet());
|
|
23225
|
+
const handleEscape = reactExports.useCallback(() => {
|
|
23226
|
+
if (!active) return false;
|
|
23227
|
+
if (onEscape == null ? void 0 : onEscape()) return true;
|
|
23228
|
+
onExit();
|
|
23229
|
+
return true;
|
|
23230
|
+
}, [active, onEscape, onExit]);
|
|
23231
|
+
useEscapeAction(handleEscape, { active, label: "Fly camera", priority: ESCAPE_PRIORITY.viewport + 5 });
|
|
23073
23232
|
const publishStatus = reactExports.useCallback(
|
|
23074
23233
|
(patch) => {
|
|
23075
23234
|
const next = { ...lastStatusRef.current, ...patch };
|
|
@@ -23185,11 +23344,6 @@ function DroneCameraController({
|
|
|
23185
23344
|
if (handledKeyboardEventsRef.current.has(event)) return;
|
|
23186
23345
|
handledKeyboardEventsRef.current.add(event);
|
|
23187
23346
|
if (event.key === "Escape") {
|
|
23188
|
-
consumeFlightKey(event);
|
|
23189
|
-
if (onEscape == null ? void 0 : onEscape()) {
|
|
23190
|
-
return;
|
|
23191
|
-
}
|
|
23192
|
-
onExit();
|
|
23193
23347
|
return;
|
|
23194
23348
|
}
|
|
23195
23349
|
const code = flightCodeFromEvent(event);
|
|
@@ -23293,19 +23447,7 @@ function DroneCameraController({
|
|
|
23293
23447
|
publishStatus(DEFAULT_DRONE_CAMERA_STATUS);
|
|
23294
23448
|
recordDroneDebug({ active: false, keys: [], lastSpeed: 0, pointerLocked: false });
|
|
23295
23449
|
};
|
|
23296
|
-
}, [
|
|
23297
|
-
active,
|
|
23298
|
-
applyLook,
|
|
23299
|
-
camera,
|
|
23300
|
-
controlsRef,
|
|
23301
|
-
gl.domElement,
|
|
23302
|
-
onEscape,
|
|
23303
|
-
onExit,
|
|
23304
|
-
onInteractionChange,
|
|
23305
|
-
publishStatus,
|
|
23306
|
-
rotateByMouse,
|
|
23307
|
-
stepFlight
|
|
23308
|
-
]);
|
|
23450
|
+
}, [active, applyLook, camera, controlsRef, gl.domElement, onInteractionChange, publishStatus, rotateByMouse, stepFlight]);
|
|
23309
23451
|
return null;
|
|
23310
23452
|
}
|
|
23311
23453
|
const panelStyle$1 = {
|
|
@@ -23742,7 +23884,7 @@ function OrbitTargetPulseLayer() {
|
|
|
23742
23884
|
class ConstructionHistoryWorkerClient {
|
|
23743
23885
|
constructor(workerFactory = () => new Worker(new URL(
|
|
23744
23886
|
/* @vite-ignore */
|
|
23745
|
-
"/assets/constructionHistoryWorker-
|
|
23887
|
+
"/assets/constructionHistoryWorker-BuZgc606.js",
|
|
23746
23888
|
import.meta.url
|
|
23747
23889
|
), { type: "module" })) {
|
|
23748
23890
|
__publicField(this, "worker", null);
|
|
@@ -26254,7 +26396,7 @@ function generateReportInWorker(options) {
|
|
|
26254
26396
|
return new Promise((resolve2, reject) => {
|
|
26255
26397
|
const worker = new Worker(new URL(
|
|
26256
26398
|
/* @vite-ignore */
|
|
26257
|
-
"/assets/reportWorker-
|
|
26399
|
+
"/assets/reportWorker-kg065BVL.js",
|
|
26258
26400
|
import.meta.url
|
|
26259
26401
|
), { type: "module" });
|
|
26260
26402
|
const cleanup = () => {
|
|
@@ -27607,6 +27749,14 @@ function AnimationBar() {
|
|
|
27607
27749
|
const [historySpeedInput, setHistorySpeedInput] = reactExports.useState(() => formatHistorySpeedInput(historySpeed));
|
|
27608
27750
|
const [editingHistorySpeed, setEditingHistorySpeed] = reactExports.useState(false);
|
|
27609
27751
|
const [copiedTraceId, setCopiedTraceId] = reactExports.useState(null);
|
|
27752
|
+
useEscapeAction(
|
|
27753
|
+
() => {
|
|
27754
|
+
if (!animationMode) return false;
|
|
27755
|
+
if (!recording) exit();
|
|
27756
|
+
return true;
|
|
27757
|
+
},
|
|
27758
|
+
{ active: Boolean(animationMode), label: "Animation mode", priority: ESCAPE_PRIORITY.viewport + 4 }
|
|
27759
|
+
);
|
|
27610
27760
|
const elapsed = useElapsedTime(recording);
|
|
27611
27761
|
reactExports.useEffect(() => {
|
|
27612
27762
|
if (!editingHistorySpeed) {
|
|
@@ -27616,12 +27766,6 @@ function AnimationBar() {
|
|
|
27616
27766
|
const handleKeyDown = reactExports.useCallback(
|
|
27617
27767
|
(e2) => {
|
|
27618
27768
|
if (!animationMode) return;
|
|
27619
|
-
if (e2.key === "Escape") {
|
|
27620
|
-
e2.preventDefault();
|
|
27621
|
-
if (recording) return;
|
|
27622
|
-
exit();
|
|
27623
|
-
return;
|
|
27624
|
-
}
|
|
27625
27769
|
if (animationMode === "construction") {
|
|
27626
27770
|
switch (e2.key) {
|
|
27627
27771
|
case " ":
|
|
@@ -27639,7 +27783,7 @@ function AnimationBar() {
|
|
|
27639
27783
|
}
|
|
27640
27784
|
}
|
|
27641
27785
|
},
|
|
27642
|
-
[animationMode, recording, historyCurrentStep, toggleHistoryPlayback, setHistoryStep
|
|
27786
|
+
[animationMode, recording, historyCurrentStep, toggleHistoryPlayback, setHistoryStep]
|
|
27643
27787
|
);
|
|
27644
27788
|
reactExports.useEffect(() => {
|
|
27645
27789
|
window.addEventListener("keydown", handleKeyDown);
|
|
@@ -28539,20 +28683,13 @@ function TrajectoryTimeline() {
|
|
|
28539
28683
|
const trajectoryDuration = useForgeStore((s) => s.trajectoryDuration);
|
|
28540
28684
|
const exitTrajectoryMode = useForgeStore((s) => s.exitTrajectoryMode);
|
|
28541
28685
|
const setTrajectoryPresetParams = useForgeStore((s) => s.setTrajectoryPresetParams);
|
|
28542
|
-
|
|
28543
|
-
(
|
|
28544
|
-
|
|
28545
|
-
|
|
28546
|
-
e2.preventDefault();
|
|
28547
|
-
exitTrajectoryMode();
|
|
28548
|
-
}
|
|
28686
|
+
useEscapeAction(
|
|
28687
|
+
() => {
|
|
28688
|
+
exitTrajectoryMode();
|
|
28689
|
+
return true;
|
|
28549
28690
|
},
|
|
28550
|
-
|
|
28691
|
+
{ active: Boolean(trajectoryMode), label: "Trajectory mode", priority: ESCAPE_PRIORITY.viewport + 4 }
|
|
28551
28692
|
);
|
|
28552
|
-
reactExports.useEffect(() => {
|
|
28553
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
28554
|
-
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
28555
|
-
}, [handleKeyDown]);
|
|
28556
28693
|
if (!trajectoryMode) return null;
|
|
28557
28694
|
const isActive = trajectoryRecording || trajectoryPreviewing;
|
|
28558
28695
|
const presetConfig = TRAJECTORY_PRESETS.find((p2) => p2.id === trajectoryPreset);
|
|
@@ -29713,7 +29850,7 @@ const PHASE_CONFIG = {
|
|
|
29713
29850
|
const PHASE_ORDER = ["kernel-init", "evaluating", "serializing"];
|
|
29714
29851
|
function formatEvaluationBackendLabel(activeBackend, computeTarget) {
|
|
29715
29852
|
const backend = activeBackend === "occt" ? "OCCT" : activeBackend === "manifold" ? "Manifold" : activeBackend === "truck" ? "Truck" : activeBackend === "sdf" ? "SDF" : "kernel";
|
|
29716
|
-
return computeTarget === "server" ?
|
|
29853
|
+
return computeTarget === "server" ? `Server ${backend}` : `Local ${backend}`;
|
|
29717
29854
|
}
|
|
29718
29855
|
function EvaluationIndicator({
|
|
29719
29856
|
phase,
|
|
@@ -32997,7 +33134,7 @@ function rangeDraftValue(value) {
|
|
|
32997
33134
|
function rangeKey(range) {
|
|
32998
33135
|
return `${range.min}:${range.max}`;
|
|
32999
33136
|
}
|
|
33000
|
-
function clamp(value, min, max2) {
|
|
33137
|
+
function clamp$1(value, min, max2) {
|
|
33001
33138
|
return Math.max(min, Math.min(max2, value));
|
|
33002
33139
|
}
|
|
33003
33140
|
function snapSliderValue(value) {
|
|
@@ -33022,20 +33159,20 @@ function DualRangeSlider({
|
|
|
33022
33159
|
onCommit
|
|
33023
33160
|
}) {
|
|
33024
33161
|
const trackRef = reactExports.useRef(null);
|
|
33025
|
-
const minPercent = clamp(range.min, 0, sliderMax) / sliderMax * 100;
|
|
33026
|
-
const maxPercent = clamp(range.max, 0, sliderMax) / sliderMax * 100;
|
|
33162
|
+
const minPercent = clamp$1(range.min, 0, sliderMax) / sliderMax * 100;
|
|
33163
|
+
const maxPercent = clamp$1(range.max, 0, sliderMax) / sliderMax * 100;
|
|
33027
33164
|
const valueFromPointer = (event) => {
|
|
33028
33165
|
var _a3;
|
|
33029
33166
|
const rect = (_a3 = trackRef.current) == null ? void 0 : _a3.getBoundingClientRect();
|
|
33030
33167
|
if (!rect || rect.width <= 0) return null;
|
|
33031
|
-
const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1);
|
|
33168
|
+
const ratio = clamp$1((event.clientX - rect.left) / rect.width, 0, 1);
|
|
33032
33169
|
return snapSliderValue(ratio * sliderMax);
|
|
33033
33170
|
};
|
|
33034
33171
|
const updateThumb = (thumb, value) => {
|
|
33035
33172
|
if (thumb === "min") {
|
|
33036
|
-
onDraftChange({ min: clamp(value, 0, range.max - MIN_RANGE_SPAN), max: range.max });
|
|
33173
|
+
onDraftChange({ min: clamp$1(value, 0, range.max - MIN_RANGE_SPAN), max: range.max });
|
|
33037
33174
|
} else {
|
|
33038
|
-
onDraftChange({ min: range.min, max: Math.max(range.min + MIN_RANGE_SPAN, clamp(value, MIN_RANGE_SPAN, sliderMax)) });
|
|
33175
|
+
onDraftChange({ min: range.min, max: Math.max(range.min + MIN_RANGE_SPAN, clamp$1(value, MIN_RANGE_SPAN, sliderMax)) });
|
|
33039
33176
|
}
|
|
33040
33177
|
};
|
|
33041
33178
|
const handlePointerMove = (thumb, event) => {
|
|
@@ -33275,7 +33412,7 @@ function InspectionLegend({
|
|
|
33275
33412
|
criticalValue
|
|
33276
33413
|
});
|
|
33277
33414
|
const swatches = liveSwatches && liveSwatches.length > 0 ? liveSwatches : definition == null ? void 0 : definition.swatches;
|
|
33278
|
-
const sliderGradient = colorScaleHexStops((colorScale == null ? void 0 : colorScale.colormap) ??
|
|
33415
|
+
const sliderGradient = colorScaleHexStops((colorScale == null ? void 0 : colorScale.colormap) ?? DEFAULT_COLORMAP, 8, (colorScale == null ? void 0 : colorScale.reversed) === true);
|
|
33279
33416
|
reactExports.useEffect(() => {
|
|
33280
33417
|
var _a3;
|
|
33281
33418
|
const parent = (_a3 = panelRef.current) == null ? void 0 : _a3.parentElement;
|
|
@@ -37893,10 +38030,10 @@ function slotMaxWidth(slot, occupied, inset) {
|
|
|
37893
38030
|
if (occupied.has(opposite)) return `min(390px, calc(50% - ${inset + 4}px))`;
|
|
37894
38031
|
return `min(390px, calc(100% - ${inset * 2}px))`;
|
|
37895
38032
|
}
|
|
37896
|
-
function ViewportOverlayHost({ entries, inset = 12, gap = 8 }) {
|
|
38033
|
+
function ViewportOverlayHost({ entries: entries2, inset = 12, gap = 8 }) {
|
|
37897
38034
|
const grouped = /* @__PURE__ */ new Map();
|
|
37898
38035
|
const occupied = /* @__PURE__ */ new Set();
|
|
37899
|
-
for (const entry of
|
|
38036
|
+
for (const entry of entries2) {
|
|
37900
38037
|
if (entry.content === null || entry.content === void 0 || typeof entry.content === "boolean") continue;
|
|
37901
38038
|
occupied.add(entry.slot);
|
|
37902
38039
|
const slotEntries = grouped.get(entry.slot) ?? [];
|
|
@@ -39373,6 +39510,7 @@ function useViewportState() {
|
|
|
39373
39510
|
const lengthUnit = useForgeStore((s) => s.lengthUnit);
|
|
39374
39511
|
const constructionGhost = useForgeStore((s) => s.constructionGhost);
|
|
39375
39512
|
const dimensionsVisible = useForgeStore((s) => s.dimensionsVisible);
|
|
39513
|
+
const paramAnchorsVisible = useForgeStore((s) => s.paramAnchorsVisible);
|
|
39376
39514
|
const attachmentsVisible = useForgeStore((s) => s.attachmentsVisible);
|
|
39377
39515
|
const _surfacesVisible = useForgeStore((s) => s.surfacesVisible);
|
|
39378
39516
|
const cutPlaneEnabled = useForgeStore((s) => s.cutPlaneEnabled);
|
|
@@ -39793,6 +39931,7 @@ function useViewportState() {
|
|
|
39793
39931
|
renderLabels,
|
|
39794
39932
|
debugHighlights3D,
|
|
39795
39933
|
dimensionsVisible,
|
|
39934
|
+
paramAnchorsVisible,
|
|
39796
39935
|
attachmentsVisible,
|
|
39797
39936
|
attachmentPoints,
|
|
39798
39937
|
cutPlaneEnabled,
|
|
@@ -40045,58 +40184,56 @@ function useViewportHandlers({
|
|
|
40045
40184
|
hideHoverTooltip();
|
|
40046
40185
|
setHoveredObjectId(null);
|
|
40047
40186
|
}, [hideHoverTooltip, objectPickSyncEnabled, setHoveredObjectId]);
|
|
40048
|
-
reactExports.
|
|
40049
|
-
|
|
40050
|
-
|
|
40051
|
-
|
|
40052
|
-
|
|
40053
|
-
|
|
40054
|
-
|
|
40055
|
-
|
|
40056
|
-
|
|
40057
|
-
|
|
40058
|
-
|
|
40059
|
-
|
|
40060
|
-
|
|
40061
|
-
|
|
40062
|
-
|
|
40063
|
-
|
|
40064
|
-
|
|
40065
|
-
|
|
40066
|
-
|
|
40067
|
-
|
|
40068
|
-
|
|
40069
|
-
|
|
40070
|
-
|
|
40071
|
-
|
|
40072
|
-
|
|
40073
|
-
|
|
40074
|
-
|
|
40075
|
-
|
|
40076
|
-
|
|
40077
|
-
|
|
40078
|
-
|
|
40079
|
-
|
|
40080
|
-
} else {
|
|
40081
|
-
store.toggleMeasure();
|
|
40082
|
-
}
|
|
40083
|
-
return;
|
|
40084
|
-
}
|
|
40085
|
-
if (store.constructionGhost !== null) {
|
|
40086
|
-
store.setConstructionGhost(null);
|
|
40087
|
-
return;
|
|
40088
|
-
}
|
|
40089
|
-
if (store.focusedObjectIds.length > 0) {
|
|
40090
|
-
clearFocusedObject();
|
|
40091
|
-
return;
|
|
40092
|
-
}
|
|
40093
|
-
if (store.selectedObjectId) {
|
|
40094
|
-
store.selectObject(null);
|
|
40187
|
+
const handleViewportEscape = reactExports.useCallback(() => {
|
|
40188
|
+
if (objectContextMenu) {
|
|
40189
|
+
closeObjectContextMenu();
|
|
40190
|
+
return true;
|
|
40191
|
+
}
|
|
40192
|
+
const store = useForgeStore.getState();
|
|
40193
|
+
if (store.selectedVertex) {
|
|
40194
|
+
store.setSelectedVertex(null);
|
|
40195
|
+
return true;
|
|
40196
|
+
}
|
|
40197
|
+
if (store.selectedEdge) {
|
|
40198
|
+
store.setSelectedEdge(null);
|
|
40199
|
+
return true;
|
|
40200
|
+
}
|
|
40201
|
+
if (store.selectedFace) {
|
|
40202
|
+
store.setSelectedFace(null);
|
|
40203
|
+
setFaceInfoPanel(null);
|
|
40204
|
+
return true;
|
|
40205
|
+
}
|
|
40206
|
+
if (faceInfoPanel) {
|
|
40207
|
+
setFaceInfoPanel(null);
|
|
40208
|
+
return true;
|
|
40209
|
+
}
|
|
40210
|
+
if (sketchEntityInfo) {
|
|
40211
|
+
setSketchEntityInfo(null);
|
|
40212
|
+
return true;
|
|
40213
|
+
}
|
|
40214
|
+
if (store.measureMode) {
|
|
40215
|
+
if (store.measureSelections.length > 0) {
|
|
40216
|
+
store.clearMeasureSelections();
|
|
40217
|
+
} else {
|
|
40218
|
+
store.toggleMeasure();
|
|
40095
40219
|
}
|
|
40096
|
-
|
|
40097
|
-
|
|
40098
|
-
|
|
40220
|
+
return true;
|
|
40221
|
+
}
|
|
40222
|
+
if (store.constructionGhost !== null) {
|
|
40223
|
+
store.setConstructionGhost(null);
|
|
40224
|
+
return true;
|
|
40225
|
+
}
|
|
40226
|
+
if (store.focusedObjectIds.length > 0) {
|
|
40227
|
+
clearFocusedObject();
|
|
40228
|
+
return true;
|
|
40229
|
+
}
|
|
40230
|
+
if (store.selectedObjectId) {
|
|
40231
|
+
store.selectObject(null);
|
|
40232
|
+
return true;
|
|
40233
|
+
}
|
|
40234
|
+
return false;
|
|
40099
40235
|
}, [clearFocusedObject, closeObjectContextMenu, faceInfoPanel, objectContextMenu, sketchEntityInfo]);
|
|
40236
|
+
useEscapeAction(handleViewportEscape, { active: true, label: "Viewport selection", priority: ESCAPE_PRIORITY.viewport });
|
|
40100
40237
|
reactExports.useEffect(() => {
|
|
40101
40238
|
const handleViewShortcut = (event) => {
|
|
40102
40239
|
if (event.isComposing || event.repeat) return;
|
|
@@ -40941,7 +41078,7 @@ const HoverTooltipLayer = reactExports.forwardRef(function HoverTooltipLayer2({
|
|
|
40941
41078
|
}
|
|
40942
41079
|
);
|
|
40943
41080
|
});
|
|
40944
|
-
const buttonStyle = {
|
|
41081
|
+
const buttonStyle$3 = {
|
|
40945
41082
|
border: "1px solid var(--fc-border)",
|
|
40946
41083
|
background: "var(--fc-btn)",
|
|
40947
41084
|
color: "var(--fc-text)",
|
|
@@ -40952,7 +41089,7 @@ const buttonStyle = {
|
|
|
40952
41089
|
lineHeight: 1
|
|
40953
41090
|
};
|
|
40954
41091
|
const iconButtonStyle = {
|
|
40955
|
-
...buttonStyle,
|
|
41092
|
+
...buttonStyle$3,
|
|
40956
41093
|
width: 30,
|
|
40957
41094
|
height: 30,
|
|
40958
41095
|
padding: 0,
|
|
@@ -41104,7 +41241,7 @@ function ModelJourneyBar({
|
|
|
41104
41241
|
padding: 4
|
|
41105
41242
|
},
|
|
41106
41243
|
children: [
|
|
41107
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", style: buttonStyle, onClick: () => activateStep(firstId, getStepStartIndex(firstJourney)), children: [
|
|
41244
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", style: buttonStyle$3, onClick: () => activateStep(firstId, getStepStartIndex(firstJourney)), children: [
|
|
41108
41245
|
"Explore: ",
|
|
41109
41246
|
label
|
|
41110
41247
|
] }),
|
|
@@ -41198,7 +41335,7 @@ function ModelJourneyBar({
|
|
|
41198
41335
|
"button",
|
|
41199
41336
|
{
|
|
41200
41337
|
type: "button",
|
|
41201
|
-
style: buttonStyle,
|
|
41338
|
+
style: buttonStyle$3,
|
|
41202
41339
|
onClick: () => {
|
|
41203
41340
|
setActive({ ...active, interrupted: false });
|
|
41204
41341
|
applyStep(activeStep);
|
|
@@ -41206,7 +41343,7 @@ function ModelJourneyBar({
|
|
|
41206
41343
|
children: "Resume"
|
|
41207
41344
|
}
|
|
41208
41345
|
),
|
|
41209
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", style: buttonStyle, disabled: atEnd, onClick: () => activateStep(active.journeyId, active.stepIndex + 1), children: "Next" })
|
|
41346
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", style: buttonStyle$3, disabled: atEnd, onClick: () => activateStep(active.journeyId, active.stepIndex + 1), children: "Next" })
|
|
41210
41347
|
] }),
|
|
41211
41348
|
caption && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginTop: 6, paddingLeft: 76, fontSize: 12, color: "var(--fc-textDim)" }, children: caption })
|
|
41212
41349
|
]
|
|
@@ -41337,6 +41474,1819 @@ function RigInspectionOverlay({ state: state2, config }) {
|
|
|
41337
41474
|
state2.joints.map((joint) => /* @__PURE__ */ jsxRuntimeExports.jsx(HoveredJointOverlay, { state: joint, config: joint.hidden ? hiddenConfig : config }, joint.joint.name))
|
|
41338
41475
|
] });
|
|
41339
41476
|
}
|
|
41477
|
+
function roundToStep(value, step) {
|
|
41478
|
+
return Number((Math.round(value / step) * step).toFixed(8));
|
|
41479
|
+
}
|
|
41480
|
+
function clamp(value, min, max2) {
|
|
41481
|
+
return Math.max(min, Math.min(max2, value));
|
|
41482
|
+
}
|
|
41483
|
+
function currentPath2DPoints(pathDef, overrides) {
|
|
41484
|
+
const countOverride = overrides[`${pathDef.name}.__count__`];
|
|
41485
|
+
const count = typeof countOverride === "number" ? clamp(Math.round(countOverride), pathDef.minPoints, pathDef.maxPoints) : pathDef.points.length;
|
|
41486
|
+
const points = [];
|
|
41487
|
+
for (let i = 0; i < count; i += 1) {
|
|
41488
|
+
const base = pathDef.points[i] ?? pathDef.defaultPoints[i] ?? { x: 0, y: 0 };
|
|
41489
|
+
const rawX = overrides[`${pathDef.name}[${i}].x`];
|
|
41490
|
+
const rawY = overrides[`${pathDef.name}[${i}].y`];
|
|
41491
|
+
points.push({
|
|
41492
|
+
x: typeof rawX === "number" ? rawX : base.x,
|
|
41493
|
+
y: typeof rawY === "number" ? rawY : base.y
|
|
41494
|
+
});
|
|
41495
|
+
}
|
|
41496
|
+
return points;
|
|
41497
|
+
}
|
|
41498
|
+
function frameForPath2D(width, height, pathDef) {
|
|
41499
|
+
const spanX = Math.max(1, pathDef.x.max - pathDef.x.min);
|
|
41500
|
+
const spanY = Math.max(1, pathDef.y.max - pathDef.y.min);
|
|
41501
|
+
const availableWidth = Math.max(1, width - 28);
|
|
41502
|
+
const availableHeight = Math.max(1, height - 28);
|
|
41503
|
+
const scale = Math.min(availableWidth / spanX, availableHeight / spanY);
|
|
41504
|
+
return {
|
|
41505
|
+
originX: (width - spanX * scale) / 2 - pathDef.x.min * scale,
|
|
41506
|
+
originY: (height + spanY * scale) / 2 + pathDef.y.min * scale,
|
|
41507
|
+
scale
|
|
41508
|
+
};
|
|
41509
|
+
}
|
|
41510
|
+
function path2DToScreen(point, frame2) {
|
|
41511
|
+
return { x: frame2.originX + point.x * frame2.scale, y: frame2.originY - point.y * frame2.scale };
|
|
41512
|
+
}
|
|
41513
|
+
function screenToPath2D(x, y, frame2) {
|
|
41514
|
+
return { x: (x - frame2.originX) / frame2.scale, y: (frame2.originY - y) / frame2.scale };
|
|
41515
|
+
}
|
|
41516
|
+
function panPath2DFrame(frame2, dx, dy) {
|
|
41517
|
+
return { ...frame2, originX: frame2.originX + dx, originY: frame2.originY + dy };
|
|
41518
|
+
}
|
|
41519
|
+
function zoomPath2DFrame(frame2, centerX, centerY, factor) {
|
|
41520
|
+
const nextScale = clamp(frame2.scale * factor, 0.05, 60);
|
|
41521
|
+
const model = screenToPath2D(centerX, centerY, frame2);
|
|
41522
|
+
return {
|
|
41523
|
+
originX: centerX - model.x * nextScale,
|
|
41524
|
+
originY: centerY + model.y * nextScale,
|
|
41525
|
+
scale: nextScale
|
|
41526
|
+
};
|
|
41527
|
+
}
|
|
41528
|
+
function drawPath2DGrid(ctx, width, height, frame2) {
|
|
41529
|
+
ctx.strokeStyle = "rgba(255,255,255,0.08)";
|
|
41530
|
+
ctx.lineWidth = 1;
|
|
41531
|
+
const modelGridStep = frame2.scale < 0.35 ? 100 : frame2.scale < 1 ? 50 : frame2.scale < 2.5 ? 20 : 10;
|
|
41532
|
+
const gridStep = modelGridStep * frame2.scale;
|
|
41533
|
+
for (let x = frame2.originX % gridStep; x < width; x += gridStep) {
|
|
41534
|
+
ctx.beginPath();
|
|
41535
|
+
ctx.moveTo(x, 0);
|
|
41536
|
+
ctx.lineTo(x, height);
|
|
41537
|
+
ctx.stroke();
|
|
41538
|
+
}
|
|
41539
|
+
for (let y = frame2.originY % gridStep; y < height; y += gridStep) {
|
|
41540
|
+
ctx.beginPath();
|
|
41541
|
+
ctx.moveTo(0, y);
|
|
41542
|
+
ctx.lineTo(width, y);
|
|
41543
|
+
ctx.stroke();
|
|
41544
|
+
}
|
|
41545
|
+
}
|
|
41546
|
+
function path2DPatch(pathName, points) {
|
|
41547
|
+
const patch = { [`${pathName}.__count__`]: points.length };
|
|
41548
|
+
points.forEach((point, index) => {
|
|
41549
|
+
patch[`${pathName}[${index}].x`] = point.x;
|
|
41550
|
+
patch[`${pathName}[${index}].y`] = point.y;
|
|
41551
|
+
});
|
|
41552
|
+
return patch;
|
|
41553
|
+
}
|
|
41554
|
+
function trailingPath2DKeys(pathName, oldCount, newCount) {
|
|
41555
|
+
const keys = [];
|
|
41556
|
+
for (let i = newCount; i < oldCount; i += 1) {
|
|
41557
|
+
keys.push(`${pathName}[${i}].x`, `${pathName}[${i}].y`);
|
|
41558
|
+
}
|
|
41559
|
+
return keys;
|
|
41560
|
+
}
|
|
41561
|
+
function hasPointSelectionModifier(event) {
|
|
41562
|
+
return event.shiftKey || event.metaKey || event.ctrlKey;
|
|
41563
|
+
}
|
|
41564
|
+
function normalizePointSelection(selectedIndices, pointCount, activeIndex) {
|
|
41565
|
+
if (pointCount <= 0) return [];
|
|
41566
|
+
const boundedActive = Math.max(0, Math.min(activeIndex, pointCount - 1));
|
|
41567
|
+
const normalized = [];
|
|
41568
|
+
const seen = /* @__PURE__ */ new Set();
|
|
41569
|
+
for (const index of selectedIndices) {
|
|
41570
|
+
if (!Number.isInteger(index) || index < 0 || index >= pointCount || seen.has(index)) continue;
|
|
41571
|
+
seen.add(index);
|
|
41572
|
+
normalized.push(index);
|
|
41573
|
+
}
|
|
41574
|
+
if (!seen.has(boundedActive)) normalized.push(boundedActive);
|
|
41575
|
+
return normalized.sort((a2, b2) => a2 - b2);
|
|
41576
|
+
}
|
|
41577
|
+
function pointSelectionForPointerDown(currentSelectedIndices, hitIndex, activeIndex, additive, pointCount) {
|
|
41578
|
+
const current = normalizePointSelection(currentSelectedIndices, pointCount, activeIndex);
|
|
41579
|
+
const alreadySelected = current.includes(hitIndex);
|
|
41580
|
+
if (!additive) {
|
|
41581
|
+
return {
|
|
41582
|
+
activeIndex: hitIndex,
|
|
41583
|
+
selectedIndices: alreadySelected ? current : [hitIndex],
|
|
41584
|
+
shouldDrag: true
|
|
41585
|
+
};
|
|
41586
|
+
}
|
|
41587
|
+
if (alreadySelected && current.length > 1) {
|
|
41588
|
+
const selectedIndices = current.filter((index) => index !== hitIndex);
|
|
41589
|
+
return {
|
|
41590
|
+
activeIndex: selectedIndices.includes(activeIndex) ? activeIndex : selectedIndices[0],
|
|
41591
|
+
selectedIndices,
|
|
41592
|
+
shouldDrag: false
|
|
41593
|
+
};
|
|
41594
|
+
}
|
|
41595
|
+
if (alreadySelected) {
|
|
41596
|
+
return { activeIndex: hitIndex, selectedIndices: current, shouldDrag: true };
|
|
41597
|
+
}
|
|
41598
|
+
return {
|
|
41599
|
+
activeIndex: hitIndex,
|
|
41600
|
+
selectedIndices: normalizePointSelection([...current, hitIndex], pointCount, hitIndex),
|
|
41601
|
+
shouldDrag: true
|
|
41602
|
+
};
|
|
41603
|
+
}
|
|
41604
|
+
const buttonStyle$2 = {
|
|
41605
|
+
background: "var(--fc-small-button-bg, none)",
|
|
41606
|
+
border: "1px solid var(--fc-small-button-border, var(--fc-border))",
|
|
41607
|
+
borderRadius: 3,
|
|
41608
|
+
color: "var(--fc-textDim)",
|
|
41609
|
+
fontSize: 10,
|
|
41610
|
+
padding: "1px 5px",
|
|
41611
|
+
cursor: "pointer",
|
|
41612
|
+
lineHeight: "14px",
|
|
41613
|
+
userSelect: "none"
|
|
41614
|
+
};
|
|
41615
|
+
function Path2DParamEditor({
|
|
41616
|
+
pathDef,
|
|
41617
|
+
allowFullScreen = true,
|
|
41618
|
+
large = false
|
|
41619
|
+
}) {
|
|
41620
|
+
const canvasRef = reactExports.useRef(null);
|
|
41621
|
+
const dragState = reactExports.useRef(null);
|
|
41622
|
+
const draftPointsRef = reactExports.useRef(null);
|
|
41623
|
+
const panStart = reactExports.useRef(null);
|
|
41624
|
+
const paramOverrides = useForgeStore((state2) => state2.paramOverrides);
|
|
41625
|
+
const setParams = useForgeStore((state2) => state2.setParams);
|
|
41626
|
+
const [expanded, setExpanded] = reactExports.useState(true);
|
|
41627
|
+
const [fullScreen, setFullScreen] = reactExports.useState(false);
|
|
41628
|
+
const [viewFrame, setViewFrame] = reactExports.useState(null);
|
|
41629
|
+
const [draftPoints, setDraftPoints] = reactExports.useState(null);
|
|
41630
|
+
const [selected, setSelected] = reactExports.useState(0);
|
|
41631
|
+
const [selectedIndices, setSelectedIndices] = reactExports.useState([0]);
|
|
41632
|
+
useEscapeAction(
|
|
41633
|
+
() => {
|
|
41634
|
+
setFullScreen(false);
|
|
41635
|
+
return true;
|
|
41636
|
+
},
|
|
41637
|
+
{ active: fullScreen, label: `${pathDef.name} path editor`, priority: ESCAPE_PRIORITY.modal }
|
|
41638
|
+
);
|
|
41639
|
+
const committedPoints = reactExports.useMemo(() => currentPath2DPoints(pathDef, paramOverrides), [paramOverrides, pathDef]);
|
|
41640
|
+
const points = draftPoints ?? committedPoints;
|
|
41641
|
+
const activeIndex = points.length > 0 ? Math.min(selected, points.length - 1) : 0;
|
|
41642
|
+
const normalizedSelectedIndices = reactExports.useMemo(
|
|
41643
|
+
() => normalizePointSelection(selectedIndices, points.length, activeIndex),
|
|
41644
|
+
[activeIndex, points.length, selectedIndices]
|
|
41645
|
+
);
|
|
41646
|
+
const selectedIndexSet = reactExports.useMemo(() => new Set(normalizedSelectedIndices), [normalizedSelectedIndices]);
|
|
41647
|
+
const selectedPoint = points[activeIndex] ?? points[0];
|
|
41648
|
+
const unitLabel = pathDef.unit ? ` ${pathDef.unit}` : "";
|
|
41649
|
+
const canAdd = points.length < pathDef.maxPoints;
|
|
41650
|
+
const canRemove = points.length - normalizedSelectedIndices.length >= pathDef.minPoints;
|
|
41651
|
+
reactExports.useEffect(() => {
|
|
41652
|
+
if (selected >= points.length) setSelected(Math.max(0, points.length - 1));
|
|
41653
|
+
}, [points.length, selected]);
|
|
41654
|
+
reactExports.useEffect(() => {
|
|
41655
|
+
const canvas = canvasRef.current;
|
|
41656
|
+
if (!canvas) return;
|
|
41657
|
+
const ctx = canvas.getContext("2d");
|
|
41658
|
+
if (!ctx) return;
|
|
41659
|
+
const draw = () => {
|
|
41660
|
+
const rect = canvas.getBoundingClientRect();
|
|
41661
|
+
const dpr = window.devicePixelRatio || 1;
|
|
41662
|
+
const nextWidth = Math.max(1, Math.round(rect.width * dpr));
|
|
41663
|
+
const nextHeight = Math.max(1, Math.round(rect.height * dpr));
|
|
41664
|
+
if (canvas.width !== nextWidth) canvas.width = nextWidth;
|
|
41665
|
+
if (canvas.height !== nextHeight) canvas.height = nextHeight;
|
|
41666
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
41667
|
+
ctx.clearRect(0, 0, rect.width, rect.height);
|
|
41668
|
+
const frame2 = viewFrame ?? frameForPath2D(canvas.clientWidth, canvas.clientHeight, pathDef);
|
|
41669
|
+
drawPath2DGrid(ctx, rect.width, rect.height, frame2);
|
|
41670
|
+
if (points.length > 0) {
|
|
41671
|
+
const projected = points.map((point) => path2DToScreen(point, frame2));
|
|
41672
|
+
ctx.beginPath();
|
|
41673
|
+
ctx.moveTo(projected[0].x, projected[0].y);
|
|
41674
|
+
for (let i = 1; i < projected.length; i += 1) ctx.lineTo(projected[i].x, projected[i].y);
|
|
41675
|
+
if (pathDef.closed) ctx.closePath();
|
|
41676
|
+
ctx.fillStyle = pathDef.closed ? "rgba(77, 147, 191, 0.22)" : "transparent";
|
|
41677
|
+
ctx.strokeStyle = pathDef.closed ? "#4d93bf" : "#3c8f83";
|
|
41678
|
+
ctx.lineWidth = pathDef.closed ? 2 : 7;
|
|
41679
|
+
if (pathDef.closed) ctx.fill();
|
|
41680
|
+
ctx.stroke();
|
|
41681
|
+
projected.forEach((point, index) => {
|
|
41682
|
+
const isSelected = selectedIndexSet.has(index);
|
|
41683
|
+
const isActive = index === activeIndex;
|
|
41684
|
+
ctx.beginPath();
|
|
41685
|
+
ctx.arc(point.x, point.y, isActive ? 8 : isSelected ? 7 : 6, 0, Math.PI * 2);
|
|
41686
|
+
ctx.fillStyle = index === 0 ? "#d84f45" : "#e2b647";
|
|
41687
|
+
ctx.fill();
|
|
41688
|
+
ctx.lineWidth = isActive ? 3 : isSelected ? 2.25 : 1.5;
|
|
41689
|
+
ctx.strokeStyle = isSelected ? "#fff6d4" : "#171914";
|
|
41690
|
+
ctx.stroke();
|
|
41691
|
+
});
|
|
41692
|
+
}
|
|
41693
|
+
};
|
|
41694
|
+
draw();
|
|
41695
|
+
const observer = new ResizeObserver(draw);
|
|
41696
|
+
observer.observe(canvas);
|
|
41697
|
+
return () => observer.disconnect();
|
|
41698
|
+
}, [activeIndex, fullScreen, large, pathDef, points, selectedIndexSet, viewFrame]);
|
|
41699
|
+
reactExports.useEffect(() => {
|
|
41700
|
+
const canvas = canvasRef.current;
|
|
41701
|
+
if (!canvas) return;
|
|
41702
|
+
const handleWheel = (event) => {
|
|
41703
|
+
event.preventDefault();
|
|
41704
|
+
event.stopPropagation();
|
|
41705
|
+
const rect = canvas.getBoundingClientRect();
|
|
41706
|
+
const x = event.clientX - rect.left;
|
|
41707
|
+
const y = event.clientY - rect.top;
|
|
41708
|
+
setViewFrame(
|
|
41709
|
+
(current) => zoomPath2DFrame(current ?? frameForPath2D(canvas.clientWidth, canvas.clientHeight, pathDef), x, y, event.deltaY > 0 ? 0.88 : 1.14)
|
|
41710
|
+
);
|
|
41711
|
+
};
|
|
41712
|
+
canvas.addEventListener("wheel", handleWheel, { passive: false });
|
|
41713
|
+
return () => canvas.removeEventListener("wheel", handleWheel);
|
|
41714
|
+
}, [pathDef]);
|
|
41715
|
+
const pointerPoint = (event) => {
|
|
41716
|
+
const canvas = event.currentTarget;
|
|
41717
|
+
const rect = canvas.getBoundingClientRect();
|
|
41718
|
+
return { x: event.clientX - rect.left, y: event.clientY - rect.top };
|
|
41719
|
+
};
|
|
41720
|
+
const nearestHandle = (canvas, x, y) => {
|
|
41721
|
+
const frame2 = viewFrame ?? frameForPath2D(canvas.clientWidth, canvas.clientHeight, pathDef);
|
|
41722
|
+
let bestIndex = null;
|
|
41723
|
+
let bestDistance = Infinity;
|
|
41724
|
+
points.forEach((point, index) => {
|
|
41725
|
+
const screen2 = path2DToScreen(point, frame2);
|
|
41726
|
+
const distance = Math.hypot(screen2.x - x, screen2.y - y);
|
|
41727
|
+
if (distance < bestDistance) {
|
|
41728
|
+
bestDistance = distance;
|
|
41729
|
+
bestIndex = index;
|
|
41730
|
+
}
|
|
41731
|
+
});
|
|
41732
|
+
return bestDistance <= 18 ? bestIndex : null;
|
|
41733
|
+
};
|
|
41734
|
+
const moveDraggedPoints = (drag, pointer) => {
|
|
41735
|
+
const dx = pointer.x - drag.startPointer.x;
|
|
41736
|
+
const dy = pointer.y - drag.startPointer.y;
|
|
41737
|
+
const dragIndices = new Set(drag.indices);
|
|
41738
|
+
const nextPoints = drag.startPoints.map(
|
|
41739
|
+
(point, index) => dragIndices.has(index) ? {
|
|
41740
|
+
x: roundToStep(point.x + dx, pathDef.x.step),
|
|
41741
|
+
y: roundToStep(point.y + dy, pathDef.y.step)
|
|
41742
|
+
} : point
|
|
41743
|
+
);
|
|
41744
|
+
draftPointsRef.current = nextPoints;
|
|
41745
|
+
setDraftPoints(nextPoints);
|
|
41746
|
+
};
|
|
41747
|
+
const commitDraftDrag = () => {
|
|
41748
|
+
const nextPoints = draftPointsRef.current;
|
|
41749
|
+
draftPointsRef.current = null;
|
|
41750
|
+
setDraftPoints(null);
|
|
41751
|
+
if (nextPoints) setParams(path2DPatch(pathDef.name, nextPoints));
|
|
41752
|
+
};
|
|
41753
|
+
const cancelDraftDrag = () => {
|
|
41754
|
+
draftPointsRef.current = null;
|
|
41755
|
+
setDraftPoints(null);
|
|
41756
|
+
};
|
|
41757
|
+
const insertPointAfterSelection = () => {
|
|
41758
|
+
if (!canAdd) return;
|
|
41759
|
+
const insertAt = Math.min(activeIndex + 1, points.length);
|
|
41760
|
+
const a2 = points[activeIndex] ?? points[points.length - 1] ?? { x: 0, y: 0 };
|
|
41761
|
+
const b2 = points[insertAt] ?? (pathDef.closed ? points[0] : a2);
|
|
41762
|
+
const nextPoint = {
|
|
41763
|
+
x: roundToStep((a2.x + b2.x) / 2, pathDef.x.step),
|
|
41764
|
+
y: roundToStep((a2.y + b2.y) / 2, pathDef.y.step)
|
|
41765
|
+
};
|
|
41766
|
+
const nextPoints = [...points];
|
|
41767
|
+
nextPoints.splice(insertAt, 0, nextPoint);
|
|
41768
|
+
setSelected(insertAt);
|
|
41769
|
+
setSelectedIndices([insertAt]);
|
|
41770
|
+
setParams(path2DPatch(pathDef.name, nextPoints));
|
|
41771
|
+
};
|
|
41772
|
+
const removeSelectedPoint = () => {
|
|
41773
|
+
if (!canRemove) return;
|
|
41774
|
+
const removeIndices = new Set(normalizedSelectedIndices);
|
|
41775
|
+
const nextPoints = points.filter((_, index) => !removeIndices.has(index));
|
|
41776
|
+
const firstRemoved = normalizedSelectedIndices[0] ?? activeIndex;
|
|
41777
|
+
const nextSelected = Math.max(0, Math.min(firstRemoved, nextPoints.length - 1));
|
|
41778
|
+
setSelected(nextSelected);
|
|
41779
|
+
setSelectedIndices([nextSelected]);
|
|
41780
|
+
setParams(path2DPatch(pathDef.name, nextPoints), trailingPath2DKeys(pathDef.name, points.length, nextPoints.length));
|
|
41781
|
+
};
|
|
41782
|
+
const canvasFrame = (canvas) => viewFrame ?? frameForPath2D(canvas.clientWidth, canvas.clientHeight, pathDef);
|
|
41783
|
+
const canvasHeight = large ? "min(62vh, 640px)" : fullScreen ? "calc(100vh - 150px)" : 220;
|
|
41784
|
+
const expandedLayout = large || fullScreen;
|
|
41785
|
+
const editorPanel = /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
41786
|
+
"div",
|
|
41787
|
+
{
|
|
41788
|
+
style: {
|
|
41789
|
+
border: "1px solid var(--fc-border)",
|
|
41790
|
+
borderRadius: 4,
|
|
41791
|
+
padding: 8,
|
|
41792
|
+
background: "var(--fc-bgSubtle, var(--fc-bgSurface, #1c2128))",
|
|
41793
|
+
userSelect: "none",
|
|
41794
|
+
WebkitUserSelect: "none",
|
|
41795
|
+
...expandedLayout ? {
|
|
41796
|
+
display: "flex",
|
|
41797
|
+
flexDirection: "column"
|
|
41798
|
+
} : {},
|
|
41799
|
+
...fullScreen ? {
|
|
41800
|
+
position: "fixed",
|
|
41801
|
+
inset: 16,
|
|
41802
|
+
zIndex: 2200,
|
|
41803
|
+
boxShadow: "0 18px 52px rgba(0, 0, 0, 0.45)",
|
|
41804
|
+
boxSizing: "border-box",
|
|
41805
|
+
color: "var(--fc-text)"
|
|
41806
|
+
} : {}
|
|
41807
|
+
},
|
|
41808
|
+
children: [
|
|
41809
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: 4, marginBottom: 6 }, children: [
|
|
41810
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => setViewFrame(null), title: "Fit path in editor", style: buttonStyle$2, children: "Fit" }),
|
|
41811
|
+
allowFullScreen && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
41812
|
+
"button",
|
|
41813
|
+
{
|
|
41814
|
+
type: "button",
|
|
41815
|
+
onClick: () => setFullScreen(!fullScreen),
|
|
41816
|
+
title: fullScreen ? "Close expanded editor" : "Expand editor",
|
|
41817
|
+
style: buttonStyle$2,
|
|
41818
|
+
children: fullScreen ? "Close" : "Full"
|
|
41819
|
+
}
|
|
41820
|
+
)
|
|
41821
|
+
] }),
|
|
41822
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
41823
|
+
"canvas",
|
|
41824
|
+
{
|
|
41825
|
+
ref: canvasRef,
|
|
41826
|
+
height: 220,
|
|
41827
|
+
"aria-label": `${pathDef.name} editor`,
|
|
41828
|
+
onPointerDown: (event) => {
|
|
41829
|
+
event.preventDefault();
|
|
41830
|
+
const pos = pointerPoint(event);
|
|
41831
|
+
const hit = nearestHandle(event.currentTarget, pos.x, pos.y);
|
|
41832
|
+
if (hit === null) panStart.current = { x: pos.x, y: pos.y, frame: canvasFrame(event.currentTarget) };
|
|
41833
|
+
else {
|
|
41834
|
+
const nextSelection = pointSelectionForPointerDown(
|
|
41835
|
+
normalizedSelectedIndices,
|
|
41836
|
+
hit,
|
|
41837
|
+
activeIndex,
|
|
41838
|
+
hasPointSelectionModifier(event),
|
|
41839
|
+
points.length
|
|
41840
|
+
);
|
|
41841
|
+
setSelected(nextSelection.activeIndex);
|
|
41842
|
+
setSelectedIndices(nextSelection.selectedIndices);
|
|
41843
|
+
if (nextSelection.shouldDrag) {
|
|
41844
|
+
const frame2 = canvasFrame(event.currentTarget);
|
|
41845
|
+
draftPointsRef.current = null;
|
|
41846
|
+
setDraftPoints(null);
|
|
41847
|
+
dragState.current = {
|
|
41848
|
+
indices: nextSelection.selectedIndices,
|
|
41849
|
+
startPointer: screenToPath2D(pos.x, pos.y, frame2),
|
|
41850
|
+
startPoints: points.map((point) => ({ ...point })),
|
|
41851
|
+
frame: frame2
|
|
41852
|
+
};
|
|
41853
|
+
}
|
|
41854
|
+
}
|
|
41855
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
41856
|
+
},
|
|
41857
|
+
onPointerMove: (event) => {
|
|
41858
|
+
const pos = pointerPoint(event);
|
|
41859
|
+
if (dragState.current) {
|
|
41860
|
+
moveDraggedPoints(dragState.current, screenToPath2D(pos.x, pos.y, dragState.current.frame));
|
|
41861
|
+
return;
|
|
41862
|
+
}
|
|
41863
|
+
if (panStart.current) {
|
|
41864
|
+
setViewFrame(panPath2DFrame(panStart.current.frame, pos.x - panStart.current.x, pos.y - panStart.current.y));
|
|
41865
|
+
}
|
|
41866
|
+
},
|
|
41867
|
+
onPointerUp: (event) => {
|
|
41868
|
+
if (dragState.current) commitDraftDrag();
|
|
41869
|
+
dragState.current = null;
|
|
41870
|
+
panStart.current = null;
|
|
41871
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
41872
|
+
},
|
|
41873
|
+
onPointerCancel: (event) => {
|
|
41874
|
+
if (dragState.current) cancelDraftDrag();
|
|
41875
|
+
dragState.current = null;
|
|
41876
|
+
panStart.current = null;
|
|
41877
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
41878
|
+
},
|
|
41879
|
+
style: { display: "block", width: "100%", height: canvasHeight, borderRadius: 3, background: "var(--fc-bg)", touchAction: "none" }
|
|
41880
|
+
}
|
|
41881
|
+
),
|
|
41882
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8, marginTop: 6 }, children: [
|
|
41883
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { color: "var(--fc-textDim)", fontSize: 10, fontVariantNumeric: "tabular-nums" }, children: [
|
|
41884
|
+
normalizedSelectedIndices.length > 1 ? `${normalizedSelectedIndices.length} selected · ` : "",
|
|
41885
|
+
"P",
|
|
41886
|
+
Math.min(activeIndex + 1, points.length),
|
|
41887
|
+
" x ",
|
|
41888
|
+
selectedPoint ? selectedPoint.x : 0,
|
|
41889
|
+
unitLabel,
|
|
41890
|
+
" · y ",
|
|
41891
|
+
selectedPoint ? selectedPoint.y : 0,
|
|
41892
|
+
unitLabel
|
|
41893
|
+
] }),
|
|
41894
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: 4 }, children: [
|
|
41895
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: insertPointAfterSelection, disabled: !canAdd, title: "Add point after selected", style: buttonStyle$2, children: "+ Point" }),
|
|
41896
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: removeSelectedPoint, disabled: !canRemove, title: "Remove selected point(s)", style: buttonStyle$2, children: "Remove" })
|
|
41897
|
+
] })
|
|
41898
|
+
] })
|
|
41899
|
+
]
|
|
41900
|
+
}
|
|
41901
|
+
);
|
|
41902
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginBottom: 8 }, children: [
|
|
41903
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
41904
|
+
"div",
|
|
41905
|
+
{
|
|
41906
|
+
onClick: () => setExpanded(!expanded),
|
|
41907
|
+
style: { display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: 12, cursor: "pointer", marginBottom: 4 },
|
|
41908
|
+
children: [
|
|
41909
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { color: "var(--fc-text)", fontWeight: 600 }, children: [
|
|
41910
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: 9, marginRight: 4 }, children: expanded ? "▼" : "▶" }),
|
|
41911
|
+
pathDef.name,
|
|
41912
|
+
" (",
|
|
41913
|
+
points.length,
|
|
41914
|
+
")"
|
|
41915
|
+
] }),
|
|
41916
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--fc-textDim)", fontSize: 10 }, children: pathDef.closed ? "outline" : "centerline" })
|
|
41917
|
+
]
|
|
41918
|
+
}
|
|
41919
|
+
),
|
|
41920
|
+
expanded && editorPanel
|
|
41921
|
+
] });
|
|
41922
|
+
}
|
|
41923
|
+
function currentPlacement2DItems(layoutDef, overrides) {
|
|
41924
|
+
const zones = new Set(layoutDef.zones.map((zone) => zone.id));
|
|
41925
|
+
return layoutDef.items.map((item) => {
|
|
41926
|
+
const rawX = overrides[`${layoutDef.name}.${item.id}.x`];
|
|
41927
|
+
const rawY = overrides[`${layoutDef.name}.${item.id}.y`];
|
|
41928
|
+
const rawAngle = overrides[`${layoutDef.name}.${item.id}.angle`];
|
|
41929
|
+
const rawZone = overrides[`${layoutDef.name}.${item.id}.zone`];
|
|
41930
|
+
const zone = typeof rawZone === "string" && zones.has(rawZone) ? rawZone : item.zone;
|
|
41931
|
+
return {
|
|
41932
|
+
...item,
|
|
41933
|
+
footprint: item.footprint.type === "rect" ? { ...item.footprint } : { ...item.footprint },
|
|
41934
|
+
x: typeof rawX === "number" ? rawX : item.x,
|
|
41935
|
+
y: typeof rawY === "number" ? rawY : item.y,
|
|
41936
|
+
angle: typeof rawAngle === "number" ? rawAngle : item.angle,
|
|
41937
|
+
zone
|
|
41938
|
+
};
|
|
41939
|
+
});
|
|
41940
|
+
}
|
|
41941
|
+
function placement2DItemPatch(layoutName, item) {
|
|
41942
|
+
const patch = {
|
|
41943
|
+
[`${layoutName}.${item.id}.x`]: item.x,
|
|
41944
|
+
[`${layoutName}.${item.id}.y`]: item.y,
|
|
41945
|
+
[`${layoutName}.${item.id}.angle`]: item.angle
|
|
41946
|
+
};
|
|
41947
|
+
if (item.zone) patch[`${layoutName}.${item.id}.zone`] = item.zone;
|
|
41948
|
+
return patch;
|
|
41949
|
+
}
|
|
41950
|
+
function placement2DFrameForItem(item, frame2, zones) {
|
|
41951
|
+
return (item.zone ? zones.find((zone) => zone.id === item.zone) : void 0) ?? frame2;
|
|
41952
|
+
}
|
|
41953
|
+
function placement2DZoneAtPoint(zones, point) {
|
|
41954
|
+
return zones.find((zone) => placement2DPointInsideFrame(point, zone));
|
|
41955
|
+
}
|
|
41956
|
+
function placement2DClampItem(item, frame2, zones) {
|
|
41957
|
+
const target = placement2DFrameForItem(item, frame2, zones);
|
|
41958
|
+
const center = placement2DClampCenterToFrame(item, target);
|
|
41959
|
+
return { ...item, x: center.x, y: center.y };
|
|
41960
|
+
}
|
|
41961
|
+
function placement2DHasBoundsViolation(layoutDef, item) {
|
|
41962
|
+
return !placement2DFootprintInsideFrame(item, placement2DFrameForItem(item, layoutDef.frame, layoutDef.zones));
|
|
41963
|
+
}
|
|
41964
|
+
function placement2DCollisionPairs(items) {
|
|
41965
|
+
const pairs = [];
|
|
41966
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
41967
|
+
for (let j2 = i + 1; j2 < items.length; j2 += 1) {
|
|
41968
|
+
if ((items[i].zone ?? "") !== (items[j2].zone ?? "")) continue;
|
|
41969
|
+
if (placement2DItemsOverlap(items[i], items[j2])) pairs.push([items[i].id, items[j2].id]);
|
|
41970
|
+
}
|
|
41971
|
+
}
|
|
41972
|
+
return pairs;
|
|
41973
|
+
}
|
|
41974
|
+
function placement2DItemCollides(candidate, items) {
|
|
41975
|
+
return items.some(
|
|
41976
|
+
(item) => item.id !== candidate.id && (item.zone ?? "") === (candidate.zone ?? "") && placement2DItemsOverlap(candidate, item)
|
|
41977
|
+
);
|
|
41978
|
+
}
|
|
41979
|
+
function placement2DSnapItem(item, snap) {
|
|
41980
|
+
return { ...item, x: roundToStep(item.x, snap), y: roundToStep(item.y, snap), angle: roundToStep(item.angle, 1) };
|
|
41981
|
+
}
|
|
41982
|
+
function placement2DFrameForCanvas(width, height, layoutDef) {
|
|
41983
|
+
const bounds = placement2DFrameBounds(layoutDef.frame);
|
|
41984
|
+
const spanX = Math.max(1, bounds.maxX - bounds.minX);
|
|
41985
|
+
const spanY = Math.max(1, bounds.maxY - bounds.minY);
|
|
41986
|
+
const availableWidth = Math.max(1, width - 28);
|
|
41987
|
+
const availableHeight = Math.max(1, height - 28);
|
|
41988
|
+
const scale = Math.min(availableWidth / spanX, availableHeight / spanY);
|
|
41989
|
+
return {
|
|
41990
|
+
originX: (width - spanX * scale) / 2 - bounds.minX * scale,
|
|
41991
|
+
originY: (height + spanY * scale) / 2 + bounds.minY * scale,
|
|
41992
|
+
scale
|
|
41993
|
+
};
|
|
41994
|
+
}
|
|
41995
|
+
function placement2DPointHitsItem(point, item) {
|
|
41996
|
+
if (item.footprint.type === "circle") return Math.hypot(point.x - item.x, point.y - item.y) <= item.footprint.radius;
|
|
41997
|
+
const angle = -item.angle * Math.PI / 180;
|
|
41998
|
+
const dx = point.x - item.x;
|
|
41999
|
+
const dy = point.y - item.y;
|
|
42000
|
+
const localX = dx * Math.cos(angle) - dy * Math.sin(angle);
|
|
42001
|
+
const localY = dx * Math.sin(angle) + dy * Math.cos(angle);
|
|
42002
|
+
return Math.abs(localX) <= item.footprint.width / 2 && Math.abs(localY) <= item.footprint.height / 2;
|
|
42003
|
+
}
|
|
42004
|
+
const buttonStyle$1 = {
|
|
42005
|
+
background: "var(--fc-small-button-bg, none)",
|
|
42006
|
+
border: "1px solid var(--fc-small-button-border, var(--fc-border))",
|
|
42007
|
+
borderRadius: 3,
|
|
42008
|
+
color: "var(--fc-textDim)",
|
|
42009
|
+
fontSize: 10,
|
|
42010
|
+
padding: "1px 5px",
|
|
42011
|
+
cursor: "pointer",
|
|
42012
|
+
lineHeight: "14px",
|
|
42013
|
+
userSelect: "none"
|
|
42014
|
+
};
|
|
42015
|
+
const selectStyle$1 = {
|
|
42016
|
+
background: "var(--fc-bg)",
|
|
42017
|
+
color: "var(--fc-text)",
|
|
42018
|
+
border: "1px solid var(--fc-border)",
|
|
42019
|
+
borderRadius: 3,
|
|
42020
|
+
padding: "1px 4px",
|
|
42021
|
+
fontSize: 11,
|
|
42022
|
+
cursor: "pointer",
|
|
42023
|
+
lineHeight: "16px"
|
|
42024
|
+
};
|
|
42025
|
+
function drawFrame(ctx, frame2, view2, label, active) {
|
|
42026
|
+
const bounds = placement2DFrameBounds(frame2);
|
|
42027
|
+
const min = path2DToScreen({ x: bounds.minX, y: bounds.minY }, view2);
|
|
42028
|
+
const max2 = path2DToScreen({ x: bounds.maxX, y: bounds.maxY }, view2);
|
|
42029
|
+
const x = Math.min(min.x, max2.x);
|
|
42030
|
+
const y = Math.min(min.y, max2.y);
|
|
42031
|
+
const width = Math.abs(max2.x - min.x);
|
|
42032
|
+
const height = Math.abs(max2.y - min.y);
|
|
42033
|
+
ctx.save();
|
|
42034
|
+
ctx.fillStyle = active ? "rgba(77, 147, 191, 0.13)" : "rgba(255,255,255,0.035)";
|
|
42035
|
+
ctx.strokeStyle = active ? "#4d93bf" : "rgba(255,255,255,0.22)";
|
|
42036
|
+
ctx.lineWidth = active ? 2 : 1;
|
|
42037
|
+
ctx.setLineDash(active ? [] : [5, 5]);
|
|
42038
|
+
ctx.fillRect(x, y, width, height);
|
|
42039
|
+
ctx.strokeRect(x, y, width, height);
|
|
42040
|
+
ctx.setLineDash([]);
|
|
42041
|
+
if (label) {
|
|
42042
|
+
ctx.fillStyle = active ? "#9ed6ff" : "rgba(255,255,255,0.55)";
|
|
42043
|
+
ctx.font = "11px system-ui, sans-serif";
|
|
42044
|
+
ctx.fillText(label, x + 6, y + 15, Math.max(20, width - 12));
|
|
42045
|
+
}
|
|
42046
|
+
ctx.restore();
|
|
42047
|
+
}
|
|
42048
|
+
function drawItem(ctx, item, view2, selected, conflicted) {
|
|
42049
|
+
const center = path2DToScreen({ x: item.x, y: item.y }, view2);
|
|
42050
|
+
const color = conflicted ? "#d65f45" : selected ? "#e2b647" : "#4d93bf";
|
|
42051
|
+
ctx.save();
|
|
42052
|
+
ctx.translate(center.x, center.y);
|
|
42053
|
+
ctx.rotate(-item.angle * Math.PI / 180);
|
|
42054
|
+
ctx.fillStyle = conflicted ? "rgba(214, 95, 69, 0.24)" : selected ? "rgba(226, 182, 71, 0.25)" : "rgba(77, 147, 191, 0.22)";
|
|
42055
|
+
ctx.strokeStyle = color;
|
|
42056
|
+
ctx.lineWidth = selected ? 2.5 : 1.5;
|
|
42057
|
+
if (item.locked) ctx.setLineDash([4, 4]);
|
|
42058
|
+
if (item.footprint.type === "circle") {
|
|
42059
|
+
ctx.beginPath();
|
|
42060
|
+
ctx.arc(0, 0, item.footprint.radius * view2.scale, 0, Math.PI * 2);
|
|
42061
|
+
ctx.fill();
|
|
42062
|
+
ctx.stroke();
|
|
42063
|
+
} else {
|
|
42064
|
+
const width = item.footprint.width * view2.scale;
|
|
42065
|
+
const height = item.footprint.height * view2.scale;
|
|
42066
|
+
ctx.beginPath();
|
|
42067
|
+
ctx.rect(-width / 2, -height / 2, width, height);
|
|
42068
|
+
ctx.fill();
|
|
42069
|
+
ctx.stroke();
|
|
42070
|
+
}
|
|
42071
|
+
ctx.setLineDash([]);
|
|
42072
|
+
ctx.rotate(item.angle * Math.PI / 180);
|
|
42073
|
+
ctx.fillStyle = "#f4f7f0";
|
|
42074
|
+
ctx.font = selected ? "600 11px system-ui, sans-serif" : "11px system-ui, sans-serif";
|
|
42075
|
+
ctx.textAlign = "center";
|
|
42076
|
+
ctx.textBaseline = "middle";
|
|
42077
|
+
ctx.fillText(item.label ?? item.id, 0, 0, 90);
|
|
42078
|
+
ctx.restore();
|
|
42079
|
+
}
|
|
42080
|
+
function Placement2DParamEditor({
|
|
42081
|
+
layoutDef,
|
|
42082
|
+
allowFullScreen = true,
|
|
42083
|
+
large = false
|
|
42084
|
+
}) {
|
|
42085
|
+
var _a3, _b2;
|
|
42086
|
+
const canvasRef = reactExports.useRef(null);
|
|
42087
|
+
const dragRef = reactExports.useRef(null);
|
|
42088
|
+
const draftItemsRef = reactExports.useRef(null);
|
|
42089
|
+
const panStart = reactExports.useRef(null);
|
|
42090
|
+
const paramOverrides = useForgeStore((state2) => state2.paramOverrides);
|
|
42091
|
+
const setParams = useForgeStore((state2) => state2.setParams);
|
|
42092
|
+
const [expanded, setExpanded] = reactExports.useState(true);
|
|
42093
|
+
const [fullScreen, setFullScreen] = reactExports.useState(false);
|
|
42094
|
+
const [viewFrame, setViewFrame] = reactExports.useState(null);
|
|
42095
|
+
const [draftItems, setDraftItems] = reactExports.useState(null);
|
|
42096
|
+
const [selectedId, setSelectedId] = reactExports.useState(((_a3 = layoutDef.items[0]) == null ? void 0 : _a3.id) ?? "");
|
|
42097
|
+
const [blockedId, setBlockedId] = reactExports.useState(null);
|
|
42098
|
+
useEscapeAction(
|
|
42099
|
+
() => {
|
|
42100
|
+
setFullScreen(false);
|
|
42101
|
+
return true;
|
|
42102
|
+
},
|
|
42103
|
+
{ active: fullScreen, label: `${layoutDef.name} placement editor`, priority: ESCAPE_PRIORITY.modal }
|
|
42104
|
+
);
|
|
42105
|
+
const committedItems = reactExports.useMemo(() => currentPlacement2DItems(layoutDef, paramOverrides), [layoutDef, paramOverrides]);
|
|
42106
|
+
const items = draftItems ?? committedItems;
|
|
42107
|
+
const collisionPairs = reactExports.useMemo(() => placement2DCollisionPairs(items), [items]);
|
|
42108
|
+
const conflictedIds = reactExports.useMemo(() => new Set(collisionPairs.flat()), [collisionPairs]);
|
|
42109
|
+
const selectedItem = items.find((item) => item.id === selectedId) ?? items[0];
|
|
42110
|
+
const unitLabel = layoutDef.unit ? ` ${layoutDef.unit}` : "";
|
|
42111
|
+
const collisionCount = collisionPairs.length;
|
|
42112
|
+
const boundsCount = items.filter((item) => placement2DHasBoundsViolation(layoutDef, item)).length;
|
|
42113
|
+
reactExports.useEffect(() => {
|
|
42114
|
+
var _a4;
|
|
42115
|
+
if (!items.some((item) => item.id === selectedId)) setSelectedId(((_a4 = items[0]) == null ? void 0 : _a4.id) ?? "");
|
|
42116
|
+
}, [items, selectedId]);
|
|
42117
|
+
reactExports.useEffect(() => {
|
|
42118
|
+
const canvas = canvasRef.current;
|
|
42119
|
+
if (!canvas) return;
|
|
42120
|
+
const ctx = canvas.getContext("2d");
|
|
42121
|
+
if (!ctx) return;
|
|
42122
|
+
const draw = () => {
|
|
42123
|
+
const rect = canvas.getBoundingClientRect();
|
|
42124
|
+
const dpr = window.devicePixelRatio || 1;
|
|
42125
|
+
const nextWidth = Math.max(1, Math.round(rect.width * dpr));
|
|
42126
|
+
const nextHeight = Math.max(1, Math.round(rect.height * dpr));
|
|
42127
|
+
if (canvas.width !== nextWidth) canvas.width = nextWidth;
|
|
42128
|
+
if (canvas.height !== nextHeight) canvas.height = nextHeight;
|
|
42129
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
42130
|
+
ctx.clearRect(0, 0, rect.width, rect.height);
|
|
42131
|
+
const frame2 = viewFrame ?? placement2DFrameForCanvas(canvas.clientWidth, canvas.clientHeight, layoutDef);
|
|
42132
|
+
drawPath2DGrid(ctx, rect.width, rect.height, frame2);
|
|
42133
|
+
if (layoutDef.zones.length === 0) {
|
|
42134
|
+
drawFrame(ctx, layoutDef.frame, frame2, "Frame", true);
|
|
42135
|
+
} else {
|
|
42136
|
+
for (const zone of layoutDef.zones) {
|
|
42137
|
+
drawFrame(
|
|
42138
|
+
ctx,
|
|
42139
|
+
zone,
|
|
42140
|
+
frame2,
|
|
42141
|
+
zone.label ?? zone.id,
|
|
42142
|
+
items.some((item) => item.zone === zone.id)
|
|
42143
|
+
);
|
|
42144
|
+
}
|
|
42145
|
+
}
|
|
42146
|
+
for (const item of items) drawItem(ctx, item, frame2, item.id === selectedId, conflictedIds.has(item.id) || blockedId === item.id);
|
|
42147
|
+
};
|
|
42148
|
+
draw();
|
|
42149
|
+
const observer = new ResizeObserver(draw);
|
|
42150
|
+
observer.observe(canvas);
|
|
42151
|
+
return () => observer.disconnect();
|
|
42152
|
+
}, [blockedId, conflictedIds, items, layoutDef, selectedId, viewFrame]);
|
|
42153
|
+
reactExports.useEffect(() => {
|
|
42154
|
+
const canvas = canvasRef.current;
|
|
42155
|
+
if (!canvas) return;
|
|
42156
|
+
const handleWheel = (event) => {
|
|
42157
|
+
event.preventDefault();
|
|
42158
|
+
event.stopPropagation();
|
|
42159
|
+
const rect = canvas.getBoundingClientRect();
|
|
42160
|
+
const x = event.clientX - rect.left;
|
|
42161
|
+
const y = event.clientY - rect.top;
|
|
42162
|
+
setViewFrame(
|
|
42163
|
+
(current) => zoomPath2DFrame(
|
|
42164
|
+
current ?? placement2DFrameForCanvas(canvas.clientWidth, canvas.clientHeight, layoutDef),
|
|
42165
|
+
x,
|
|
42166
|
+
y,
|
|
42167
|
+
event.deltaY > 0 ? 0.88 : 1.14
|
|
42168
|
+
)
|
|
42169
|
+
);
|
|
42170
|
+
};
|
|
42171
|
+
canvas.addEventListener("wheel", handleWheel, { passive: false });
|
|
42172
|
+
return () => canvas.removeEventListener("wheel", handleWheel);
|
|
42173
|
+
}, [layoutDef]);
|
|
42174
|
+
const canvasFrame = (canvas) => viewFrame ?? placement2DFrameForCanvas(canvas.clientWidth, canvas.clientHeight, layoutDef);
|
|
42175
|
+
const canvasHeight = large ? "min(62vh, 640px)" : fullScreen ? "calc(100vh - 166px)" : 240;
|
|
42176
|
+
const expandedLayout = large || fullScreen;
|
|
42177
|
+
const pointerPoint = (event) => {
|
|
42178
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
42179
|
+
return { x: event.clientX - rect.left, y: event.clientY - rect.top };
|
|
42180
|
+
};
|
|
42181
|
+
const itemAtPointer = (canvas, x, y) => {
|
|
42182
|
+
const model = screenToPath2D(x, y, canvasFrame(canvas));
|
|
42183
|
+
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
42184
|
+
if (placement2DPointHitsItem(model, items[index])) return items[index];
|
|
42185
|
+
}
|
|
42186
|
+
return null;
|
|
42187
|
+
};
|
|
42188
|
+
const resolveCandidateItem = (rawCandidate, sourceItems) => {
|
|
42189
|
+
let candidate = placement2DSnapItem(rawCandidate, layoutDef.rules.snap);
|
|
42190
|
+
if (layoutDef.rules.bounds === "prevent") {
|
|
42191
|
+
candidate = placement2DClampItem(candidate, layoutDef.frame, layoutDef.zones);
|
|
42192
|
+
if (placement2DHasBoundsViolation(layoutDef, candidate)) {
|
|
42193
|
+
setBlockedId(candidate.id);
|
|
42194
|
+
return null;
|
|
42195
|
+
}
|
|
42196
|
+
}
|
|
42197
|
+
const collides = placement2DItemCollides(candidate, sourceItems);
|
|
42198
|
+
setBlockedId(collides ? candidate.id : null);
|
|
42199
|
+
if (layoutDef.rules.collisions === "prevent" && collides) return null;
|
|
42200
|
+
return candidate;
|
|
42201
|
+
};
|
|
42202
|
+
const commitItem = (rawCandidate) => {
|
|
42203
|
+
const candidate = resolveCandidateItem(rawCandidate, items);
|
|
42204
|
+
if (!candidate) return;
|
|
42205
|
+
setParams(placement2DItemPatch(layoutDef.name, candidate));
|
|
42206
|
+
};
|
|
42207
|
+
const previewDraggedItem = (rawCandidate) => {
|
|
42208
|
+
const sourceItems = draftItemsRef.current ?? items;
|
|
42209
|
+
const candidate = resolveCandidateItem(rawCandidate, sourceItems);
|
|
42210
|
+
if (!candidate) return;
|
|
42211
|
+
const nextItems = sourceItems.map((item) => item.id === candidate.id ? candidate : item);
|
|
42212
|
+
draftItemsRef.current = nextItems;
|
|
42213
|
+
setDraftItems(nextItems);
|
|
42214
|
+
};
|
|
42215
|
+
const commitDraftDrag = (id) => {
|
|
42216
|
+
const nextItems = draftItemsRef.current;
|
|
42217
|
+
draftItemsRef.current = null;
|
|
42218
|
+
setDraftItems(null);
|
|
42219
|
+
const item = nextItems == null ? void 0 : nextItems.find((candidate) => candidate.id === id);
|
|
42220
|
+
if (item) setParams(placement2DItemPatch(layoutDef.name, item));
|
|
42221
|
+
};
|
|
42222
|
+
const cancelDraftDrag = () => {
|
|
42223
|
+
draftItemsRef.current = null;
|
|
42224
|
+
setDraftItems(null);
|
|
42225
|
+
};
|
|
42226
|
+
const setSelectedZone = (zone) => {
|
|
42227
|
+
if (!selectedItem) return;
|
|
42228
|
+
commitItem({ ...selectedItem, zone });
|
|
42229
|
+
};
|
|
42230
|
+
const setSelectedAngle = (angle) => {
|
|
42231
|
+
if (!selectedItem) return;
|
|
42232
|
+
commitItem({ ...selectedItem, angle });
|
|
42233
|
+
};
|
|
42234
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginBottom: 8 }, children: [
|
|
42235
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42236
|
+
"div",
|
|
42237
|
+
{
|
|
42238
|
+
onClick: () => setExpanded(!expanded),
|
|
42239
|
+
style: { display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: 12, cursor: "pointer", marginBottom: 4 },
|
|
42240
|
+
children: [
|
|
42241
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { color: "var(--fc-text)", fontWeight: 600 }, children: [
|
|
42242
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: 9, marginRight: 4 }, children: expanded ? "v" : ">" }),
|
|
42243
|
+
layoutDef.name,
|
|
42244
|
+
" (",
|
|
42245
|
+
items.length,
|
|
42246
|
+
")"
|
|
42247
|
+
] }),
|
|
42248
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: collisionCount > 0 || boundsCount > 0 ? "#d65f45" : "var(--fc-textDim)", fontSize: 10 }, children: "Placement Sheet" })
|
|
42249
|
+
]
|
|
42250
|
+
}
|
|
42251
|
+
),
|
|
42252
|
+
expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42253
|
+
"div",
|
|
42254
|
+
{
|
|
42255
|
+
style: {
|
|
42256
|
+
border: "1px solid var(--fc-border)",
|
|
42257
|
+
borderRadius: 4,
|
|
42258
|
+
padding: 8,
|
|
42259
|
+
background: "var(--fc-bgSubtle, var(--fc-bgSurface, #1c2128))",
|
|
42260
|
+
userSelect: "none",
|
|
42261
|
+
WebkitUserSelect: "none",
|
|
42262
|
+
...expandedLayout ? {
|
|
42263
|
+
display: "flex",
|
|
42264
|
+
flexDirection: "column"
|
|
42265
|
+
} : {},
|
|
42266
|
+
...fullScreen ? {
|
|
42267
|
+
position: "fixed",
|
|
42268
|
+
inset: 16,
|
|
42269
|
+
zIndex: 2200,
|
|
42270
|
+
boxShadow: "0 18px 52px rgba(0, 0, 0, 0.45)",
|
|
42271
|
+
boxSizing: "border-box",
|
|
42272
|
+
color: "var(--fc-text)"
|
|
42273
|
+
} : {}
|
|
42274
|
+
},
|
|
42275
|
+
children: [
|
|
42276
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: 6, marginBottom: 6 }, children: [
|
|
42277
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42278
|
+
"select",
|
|
42279
|
+
{
|
|
42280
|
+
"aria-label": `${layoutDef.name} selected item`,
|
|
42281
|
+
value: (selectedItem == null ? void 0 : selectedItem.id) ?? "",
|
|
42282
|
+
onChange: (event) => setSelectedId(event.target.value),
|
|
42283
|
+
style: { ...selectStyle$1, minWidth: 0, flex: "1 1 auto" },
|
|
42284
|
+
children: items.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: item.id, children: item.label ?? item.id }, item.id))
|
|
42285
|
+
}
|
|
42286
|
+
),
|
|
42287
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: 4 }, children: [
|
|
42288
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => setViewFrame(null), title: "Fit placement sheet in editor", style: buttonStyle$1, children: "Fit" }),
|
|
42289
|
+
allowFullScreen && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42290
|
+
"button",
|
|
42291
|
+
{
|
|
42292
|
+
type: "button",
|
|
42293
|
+
onClick: () => setFullScreen(!fullScreen),
|
|
42294
|
+
title: fullScreen ? "Close expanded editor" : "Expand editor",
|
|
42295
|
+
style: buttonStyle$1,
|
|
42296
|
+
children: fullScreen ? "Close" : "Full"
|
|
42297
|
+
}
|
|
42298
|
+
)
|
|
42299
|
+
] })
|
|
42300
|
+
] }),
|
|
42301
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42302
|
+
"canvas",
|
|
42303
|
+
{
|
|
42304
|
+
ref: canvasRef,
|
|
42305
|
+
height: 240,
|
|
42306
|
+
"aria-label": `${layoutDef.name} placement editor`,
|
|
42307
|
+
onPointerDown: (event) => {
|
|
42308
|
+
event.preventDefault();
|
|
42309
|
+
const pos = pointerPoint(event);
|
|
42310
|
+
const hit = itemAtPointer(event.currentTarget, pos.x, pos.y);
|
|
42311
|
+
if (!hit) {
|
|
42312
|
+
panStart.current = { x: pos.x, y: pos.y, frame: canvasFrame(event.currentTarget) };
|
|
42313
|
+
} else {
|
|
42314
|
+
setSelectedId(hit.id);
|
|
42315
|
+
const model = screenToPath2D(pos.x, pos.y, canvasFrame(event.currentTarget));
|
|
42316
|
+
if (!hit.locked) {
|
|
42317
|
+
draftItemsRef.current = null;
|
|
42318
|
+
setDraftItems(null);
|
|
42319
|
+
dragRef.current = { id: hit.id, offsetX: model.x - hit.x, offsetY: model.y - hit.y };
|
|
42320
|
+
}
|
|
42321
|
+
}
|
|
42322
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
42323
|
+
},
|
|
42324
|
+
onPointerMove: (event) => {
|
|
42325
|
+
const pos = pointerPoint(event);
|
|
42326
|
+
const drag = dragRef.current;
|
|
42327
|
+
if (drag) {
|
|
42328
|
+
const item = items.find((candidate) => candidate.id === drag.id);
|
|
42329
|
+
if (!item) return;
|
|
42330
|
+
const model = screenToPath2D(pos.x, pos.y, canvasFrame(event.currentTarget));
|
|
42331
|
+
const center = { x: model.x - drag.offsetX, y: model.y - drag.offsetY };
|
|
42332
|
+
const zone = placement2DZoneAtPoint(layoutDef.zones, center);
|
|
42333
|
+
previewDraggedItem({ ...item, x: center.x, y: center.y, zone: (zone == null ? void 0 : zone.id) ?? item.zone });
|
|
42334
|
+
return;
|
|
42335
|
+
}
|
|
42336
|
+
if (panStart.current) {
|
|
42337
|
+
setViewFrame(panPath2DFrame(panStart.current.frame, pos.x - panStart.current.x, pos.y - panStart.current.y));
|
|
42338
|
+
}
|
|
42339
|
+
},
|
|
42340
|
+
onPointerUp: (event) => {
|
|
42341
|
+
const drag = dragRef.current;
|
|
42342
|
+
if (drag) commitDraftDrag(drag.id);
|
|
42343
|
+
dragRef.current = null;
|
|
42344
|
+
panStart.current = null;
|
|
42345
|
+
setBlockedId(null);
|
|
42346
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
42347
|
+
},
|
|
42348
|
+
onPointerCancel: (event) => {
|
|
42349
|
+
if (dragRef.current) cancelDraftDrag();
|
|
42350
|
+
dragRef.current = null;
|
|
42351
|
+
panStart.current = null;
|
|
42352
|
+
setBlockedId(null);
|
|
42353
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
42354
|
+
},
|
|
42355
|
+
style: {
|
|
42356
|
+
display: "block",
|
|
42357
|
+
width: "100%",
|
|
42358
|
+
height: canvasHeight,
|
|
42359
|
+
borderRadius: 3,
|
|
42360
|
+
background: "var(--fc-bg)",
|
|
42361
|
+
touchAction: "none"
|
|
42362
|
+
}
|
|
42363
|
+
}
|
|
42364
|
+
),
|
|
42365
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "grid", gridTemplateColumns: "minmax(0, 1fr) auto", gap: 8, alignItems: "center", marginTop: 6 }, children: [
|
|
42366
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { color: "var(--fc-textDim)", fontSize: 10, fontVariantNumeric: "tabular-nums", minWidth: 0 }, children: [
|
|
42367
|
+
selectedItem ? `${selectedItem.id} x ${selectedItem.x}${unitLabel} | y ${selectedItem.y}${unitLabel}` : "No item",
|
|
42368
|
+
collisionCount > 0 ? ` | ${collisionCount} overlap${collisionCount === 1 ? "" : "s"}` : "",
|
|
42369
|
+
boundsCount > 0 ? ` | ${boundsCount} out of bounds` : ""
|
|
42370
|
+
] }),
|
|
42371
|
+
selectedItem && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
42372
|
+
layoutDef.zones.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42373
|
+
"select",
|
|
42374
|
+
{
|
|
42375
|
+
"aria-label": `${layoutDef.name} selected item zone`,
|
|
42376
|
+
value: selectedItem.zone ?? ((_b2 = layoutDef.zones[0]) == null ? void 0 : _b2.id) ?? "",
|
|
42377
|
+
onChange: (event) => setSelectedZone(event.target.value),
|
|
42378
|
+
style: selectStyle$1,
|
|
42379
|
+
children: layoutDef.zones.map((zone) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: zone.id, children: zone.label ?? zone.id }, zone.id))
|
|
42380
|
+
}
|
|
42381
|
+
),
|
|
42382
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42383
|
+
"button",
|
|
42384
|
+
{
|
|
42385
|
+
type: "button",
|
|
42386
|
+
onClick: () => setSelectedAngle(selectedItem.angle - 15),
|
|
42387
|
+
title: "Rotate selected item left",
|
|
42388
|
+
style: buttonStyle$1,
|
|
42389
|
+
children: "-15"
|
|
42390
|
+
}
|
|
42391
|
+
),
|
|
42392
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { color: "var(--fc-textDim)", fontSize: 10, minWidth: 42, textAlign: "center" }, children: [
|
|
42393
|
+
selectedItem.angle,
|
|
42394
|
+
" deg"
|
|
42395
|
+
] }),
|
|
42396
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42397
|
+
"button",
|
|
42398
|
+
{
|
|
42399
|
+
type: "button",
|
|
42400
|
+
onClick: () => setSelectedAngle(selectedItem.angle + 15),
|
|
42401
|
+
title: "Rotate selected item right",
|
|
42402
|
+
style: buttonStyle$1,
|
|
42403
|
+
children: "+15"
|
|
42404
|
+
}
|
|
42405
|
+
)
|
|
42406
|
+
] })
|
|
42407
|
+
] })
|
|
42408
|
+
]
|
|
42409
|
+
}
|
|
42410
|
+
)
|
|
42411
|
+
] });
|
|
42412
|
+
}
|
|
42413
|
+
const SPLINE_2D_CONTINUITIES = ["G0", "G1", "G2"];
|
|
42414
|
+
function continuityFromOverride(value, fallback) {
|
|
42415
|
+
if (value === "G0" || value === "G1" || value === "G2") return value;
|
|
42416
|
+
if (typeof value === "number") {
|
|
42417
|
+
const index = Math.round(value);
|
|
42418
|
+
return SPLINE_2D_CONTINUITIES[index] ?? fallback;
|
|
42419
|
+
}
|
|
42420
|
+
return fallback;
|
|
42421
|
+
}
|
|
42422
|
+
function currentSpline2DPoints(curveDef, overrides) {
|
|
42423
|
+
const countOverride = overrides[`${curveDef.name}.__count__`];
|
|
42424
|
+
const count = typeof countOverride === "number" ? clamp(Math.round(countOverride), curveDef.minPoints, curveDef.maxPoints) : curveDef.points.length;
|
|
42425
|
+
const points = [];
|
|
42426
|
+
for (let i = 0; i < count; i += 1) {
|
|
42427
|
+
const base = curveDef.points[i] ?? curveDef.defaultPoints[i] ?? { x: 0, y: 0, g: "G2" };
|
|
42428
|
+
const rawX = overrides[`${curveDef.name}[${i}].x`];
|
|
42429
|
+
const rawY = overrides[`${curveDef.name}[${i}].y`];
|
|
42430
|
+
const rawG = overrides[`${curveDef.name}[${i}].g`];
|
|
42431
|
+
points.push({
|
|
42432
|
+
x: typeof rawX === "number" ? rawX : base.x,
|
|
42433
|
+
y: typeof rawY === "number" ? rawY : base.y,
|
|
42434
|
+
g: continuityFromOverride(rawG, base.g)
|
|
42435
|
+
});
|
|
42436
|
+
}
|
|
42437
|
+
return points;
|
|
42438
|
+
}
|
|
42439
|
+
function spline2DPatch(curveName, points) {
|
|
42440
|
+
const patch = { [`${curveName}.__count__`]: points.length };
|
|
42441
|
+
points.forEach((point, index) => {
|
|
42442
|
+
patch[`${curveName}[${index}].x`] = point.x;
|
|
42443
|
+
patch[`${curveName}[${index}].y`] = point.y;
|
|
42444
|
+
patch[`${curveName}[${index}].g`] = point.g;
|
|
42445
|
+
});
|
|
42446
|
+
return patch;
|
|
42447
|
+
}
|
|
42448
|
+
function trailingSpline2DKeys(curveName, oldCount, newCount) {
|
|
42449
|
+
const keys = [];
|
|
42450
|
+
for (let i = newCount; i < oldCount; i += 1) {
|
|
42451
|
+
keys.push(`${curveName}[${i}].x`, `${curveName}[${i}].y`, `${curveName}[${i}].g`);
|
|
42452
|
+
}
|
|
42453
|
+
return keys;
|
|
42454
|
+
}
|
|
42455
|
+
function splineContinuityColor(continuity) {
|
|
42456
|
+
if (continuity === "G0") return "#d65f45";
|
|
42457
|
+
if (continuity === "G1") return "#e2b647";
|
|
42458
|
+
return "#4d93bf";
|
|
42459
|
+
}
|
|
42460
|
+
const buttonStyle = {
|
|
42461
|
+
background: "var(--fc-small-button-bg, none)",
|
|
42462
|
+
border: "1px solid var(--fc-small-button-border, var(--fc-border))",
|
|
42463
|
+
borderRadius: 3,
|
|
42464
|
+
color: "var(--fc-textDim)",
|
|
42465
|
+
fontSize: 10,
|
|
42466
|
+
padding: "1px 5px",
|
|
42467
|
+
cursor: "pointer",
|
|
42468
|
+
lineHeight: "14px",
|
|
42469
|
+
userSelect: "none"
|
|
42470
|
+
};
|
|
42471
|
+
const selectStyle = {
|
|
42472
|
+
background: "var(--fc-bg)",
|
|
42473
|
+
color: "var(--fc-text)",
|
|
42474
|
+
border: "1px solid var(--fc-border)",
|
|
42475
|
+
borderRadius: 3,
|
|
42476
|
+
padding: "1px 4px",
|
|
42477
|
+
fontSize: 11,
|
|
42478
|
+
cursor: "pointer",
|
|
42479
|
+
lineHeight: "16px"
|
|
42480
|
+
};
|
|
42481
|
+
function drawSmoothPreview(ctx, projected, points, closed) {
|
|
42482
|
+
if (projected.length < 2) return;
|
|
42483
|
+
ctx.beginPath();
|
|
42484
|
+
ctx.moveTo(projected[0].x, projected[0].y);
|
|
42485
|
+
const segmentCount = closed ? projected.length : projected.length - 1;
|
|
42486
|
+
for (let index = 0; index < segmentCount; index += 1) {
|
|
42487
|
+
const nextIndex = (index + 1) % projected.length;
|
|
42488
|
+
const current = projected[index];
|
|
42489
|
+
const next = projected[nextIndex];
|
|
42490
|
+
const currentG = points[index].g;
|
|
42491
|
+
const nextG = points[nextIndex].g;
|
|
42492
|
+
if (currentG === "G0" || nextG === "G0") {
|
|
42493
|
+
ctx.lineTo(next.x, next.y);
|
|
42494
|
+
continue;
|
|
42495
|
+
}
|
|
42496
|
+
const prev = projected[index === 0 ? closed ? projected.length - 1 : 0 : index - 1];
|
|
42497
|
+
const after = projected[nextIndex === projected.length - 1 ? closed ? 0 : nextIndex : nextIndex + 1];
|
|
42498
|
+
const reach = currentG === "G2" || nextG === "G2" ? 1 / 6 : 1 / 8;
|
|
42499
|
+
const c1 = { x: current.x + (next.x - prev.x) * reach, y: current.y + (next.y - prev.y) * reach };
|
|
42500
|
+
const c2 = { x: next.x - (after.x - current.x) * reach, y: next.y - (after.y - current.y) * reach };
|
|
42501
|
+
ctx.bezierCurveTo(c1.x, c1.y, c2.x, c2.y, next.x, next.y);
|
|
42502
|
+
}
|
|
42503
|
+
if (closed) ctx.closePath();
|
|
42504
|
+
ctx.stroke();
|
|
42505
|
+
}
|
|
42506
|
+
function Spline2DParamEditor({
|
|
42507
|
+
curveDef,
|
|
42508
|
+
allowFullScreen = true,
|
|
42509
|
+
large = false
|
|
42510
|
+
}) {
|
|
42511
|
+
const canvasRef = reactExports.useRef(null);
|
|
42512
|
+
const dragState = reactExports.useRef(null);
|
|
42513
|
+
const draftPointsRef = reactExports.useRef(null);
|
|
42514
|
+
const panStart = reactExports.useRef(null);
|
|
42515
|
+
const paramOverrides = useForgeStore((state2) => state2.paramOverrides);
|
|
42516
|
+
const setParams = useForgeStore((state2) => state2.setParams);
|
|
42517
|
+
const [expanded, setExpanded] = reactExports.useState(true);
|
|
42518
|
+
const [fullScreen, setFullScreen] = reactExports.useState(false);
|
|
42519
|
+
const [viewFrame, setViewFrame] = reactExports.useState(null);
|
|
42520
|
+
const [draftPoints, setDraftPoints] = reactExports.useState(null);
|
|
42521
|
+
const [selected, setSelected] = reactExports.useState(0);
|
|
42522
|
+
const [selectedIndices, setSelectedIndices] = reactExports.useState([0]);
|
|
42523
|
+
useEscapeAction(
|
|
42524
|
+
() => {
|
|
42525
|
+
setFullScreen(false);
|
|
42526
|
+
return true;
|
|
42527
|
+
},
|
|
42528
|
+
{ active: fullScreen, label: `${curveDef.name} spline editor`, priority: ESCAPE_PRIORITY.modal }
|
|
42529
|
+
);
|
|
42530
|
+
const committedPoints = reactExports.useMemo(() => currentSpline2DPoints(curveDef, paramOverrides), [curveDef, paramOverrides]);
|
|
42531
|
+
const points = draftPoints ?? committedPoints;
|
|
42532
|
+
const activeIndex = points.length > 0 ? Math.min(selected, points.length - 1) : 0;
|
|
42533
|
+
const normalizedSelectedIndices = reactExports.useMemo(
|
|
42534
|
+
() => normalizePointSelection(selectedIndices, points.length, activeIndex),
|
|
42535
|
+
[activeIndex, points.length, selectedIndices]
|
|
42536
|
+
);
|
|
42537
|
+
const selectedIndexSet = reactExports.useMemo(() => new Set(normalizedSelectedIndices), [normalizedSelectedIndices]);
|
|
42538
|
+
const selectedPoint = points[activeIndex] ?? points[0];
|
|
42539
|
+
const unitLabel = curveDef.unit ? ` ${curveDef.unit}` : "";
|
|
42540
|
+
const canAdd = points.length < curveDef.maxPoints;
|
|
42541
|
+
const canRemove = points.length - normalizedSelectedIndices.length >= curveDef.minPoints;
|
|
42542
|
+
reactExports.useEffect(() => {
|
|
42543
|
+
if (selected >= points.length) setSelected(Math.max(0, points.length - 1));
|
|
42544
|
+
}, [points.length, selected]);
|
|
42545
|
+
reactExports.useEffect(() => {
|
|
42546
|
+
const canvas = canvasRef.current;
|
|
42547
|
+
if (!canvas) return;
|
|
42548
|
+
const ctx = canvas.getContext("2d");
|
|
42549
|
+
if (!ctx) return;
|
|
42550
|
+
const draw = () => {
|
|
42551
|
+
const rect = canvas.getBoundingClientRect();
|
|
42552
|
+
const dpr = window.devicePixelRatio || 1;
|
|
42553
|
+
const nextWidth = Math.max(1, Math.round(rect.width * dpr));
|
|
42554
|
+
const nextHeight = Math.max(1, Math.round(rect.height * dpr));
|
|
42555
|
+
if (canvas.width !== nextWidth) canvas.width = nextWidth;
|
|
42556
|
+
if (canvas.height !== nextHeight) canvas.height = nextHeight;
|
|
42557
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
42558
|
+
ctx.clearRect(0, 0, rect.width, rect.height);
|
|
42559
|
+
const frame2 = viewFrame ?? frameForPath2D(canvas.clientWidth, canvas.clientHeight, curveDef);
|
|
42560
|
+
drawPath2DGrid(ctx, rect.width, rect.height, frame2);
|
|
42561
|
+
if (points.length === 0) return;
|
|
42562
|
+
const projected = points.map((point) => path2DToScreen(point, frame2));
|
|
42563
|
+
ctx.save();
|
|
42564
|
+
ctx.setLineDash([5, 5]);
|
|
42565
|
+
ctx.strokeStyle = "rgba(255,255,255,0.18)";
|
|
42566
|
+
ctx.lineWidth = 1;
|
|
42567
|
+
ctx.beginPath();
|
|
42568
|
+
ctx.moveTo(projected[0].x, projected[0].y);
|
|
42569
|
+
for (let index = 1; index < projected.length; index += 1) ctx.lineTo(projected[index].x, projected[index].y);
|
|
42570
|
+
if (curveDef.closed) ctx.closePath();
|
|
42571
|
+
ctx.stroke();
|
|
42572
|
+
ctx.restore();
|
|
42573
|
+
ctx.strokeStyle = "#4d93bf";
|
|
42574
|
+
ctx.lineWidth = 4;
|
|
42575
|
+
drawSmoothPreview(ctx, projected, points, curveDef.closed);
|
|
42576
|
+
projected.forEach((point, index) => {
|
|
42577
|
+
const isSelected = selectedIndexSet.has(index);
|
|
42578
|
+
const isActive = index === activeIndex;
|
|
42579
|
+
ctx.beginPath();
|
|
42580
|
+
ctx.arc(point.x, point.y, isActive ? 8 : isSelected ? 7 : 6, 0, Math.PI * 2);
|
|
42581
|
+
ctx.fillStyle = splineContinuityColor(points[index].g);
|
|
42582
|
+
ctx.fill();
|
|
42583
|
+
ctx.lineWidth = isActive ? 3 : isSelected ? 2.25 : 1.5;
|
|
42584
|
+
ctx.strokeStyle = isSelected ? "#fff6d4" : "#171914";
|
|
42585
|
+
ctx.stroke();
|
|
42586
|
+
});
|
|
42587
|
+
};
|
|
42588
|
+
draw();
|
|
42589
|
+
const observer = new ResizeObserver(draw);
|
|
42590
|
+
observer.observe(canvas);
|
|
42591
|
+
return () => observer.disconnect();
|
|
42592
|
+
}, [activeIndex, curveDef, fullScreen, large, points, selectedIndexSet, viewFrame]);
|
|
42593
|
+
reactExports.useEffect(() => {
|
|
42594
|
+
const canvas = canvasRef.current;
|
|
42595
|
+
if (!canvas) return;
|
|
42596
|
+
const handleWheel = (event) => {
|
|
42597
|
+
event.preventDefault();
|
|
42598
|
+
event.stopPropagation();
|
|
42599
|
+
const rect = canvas.getBoundingClientRect();
|
|
42600
|
+
const x = event.clientX - rect.left;
|
|
42601
|
+
const y = event.clientY - rect.top;
|
|
42602
|
+
setViewFrame(
|
|
42603
|
+
(current) => zoomPath2DFrame(current ?? frameForPath2D(canvas.clientWidth, canvas.clientHeight, curveDef), x, y, event.deltaY > 0 ? 0.88 : 1.14)
|
|
42604
|
+
);
|
|
42605
|
+
};
|
|
42606
|
+
canvas.addEventListener("wheel", handleWheel, { passive: false });
|
|
42607
|
+
return () => canvas.removeEventListener("wheel", handleWheel);
|
|
42608
|
+
}, [curveDef]);
|
|
42609
|
+
const setContinuityForSelection = (continuity) => {
|
|
42610
|
+
const nextPoints = points.map((point, index) => selectedIndexSet.has(index) ? { ...point, g: continuity } : point);
|
|
42611
|
+
setParams(spline2DPatch(curveDef.name, nextPoints));
|
|
42612
|
+
};
|
|
42613
|
+
const pointerPoint = (event) => {
|
|
42614
|
+
const canvas = event.currentTarget;
|
|
42615
|
+
const rect = canvas.getBoundingClientRect();
|
|
42616
|
+
return { x: event.clientX - rect.left, y: event.clientY - rect.top };
|
|
42617
|
+
};
|
|
42618
|
+
const nearestHandle = (canvas, x, y) => {
|
|
42619
|
+
const frame2 = viewFrame ?? frameForPath2D(canvas.clientWidth, canvas.clientHeight, curveDef);
|
|
42620
|
+
let bestIndex = null;
|
|
42621
|
+
let bestDistance = Infinity;
|
|
42622
|
+
points.forEach((point, index) => {
|
|
42623
|
+
const screen2 = path2DToScreen(point, frame2);
|
|
42624
|
+
const distance = Math.hypot(screen2.x - x, screen2.y - y);
|
|
42625
|
+
if (distance < bestDistance) {
|
|
42626
|
+
bestDistance = distance;
|
|
42627
|
+
bestIndex = index;
|
|
42628
|
+
}
|
|
42629
|
+
});
|
|
42630
|
+
return bestDistance <= 18 ? bestIndex : null;
|
|
42631
|
+
};
|
|
42632
|
+
const moveDraggedPoints = (drag, pointer) => {
|
|
42633
|
+
const dx = pointer.x - drag.startPointer.x;
|
|
42634
|
+
const dy = pointer.y - drag.startPointer.y;
|
|
42635
|
+
const dragIndices = new Set(drag.indices);
|
|
42636
|
+
const nextPoints = drag.startPoints.map(
|
|
42637
|
+
(point, index) => dragIndices.has(index) ? {
|
|
42638
|
+
...point,
|
|
42639
|
+
x: roundToStep(point.x + dx, curveDef.x.step),
|
|
42640
|
+
y: roundToStep(point.y + dy, curveDef.y.step)
|
|
42641
|
+
} : point
|
|
42642
|
+
);
|
|
42643
|
+
draftPointsRef.current = nextPoints;
|
|
42644
|
+
setDraftPoints(nextPoints);
|
|
42645
|
+
};
|
|
42646
|
+
const commitDraftDrag = () => {
|
|
42647
|
+
const nextPoints = draftPointsRef.current;
|
|
42648
|
+
draftPointsRef.current = null;
|
|
42649
|
+
setDraftPoints(null);
|
|
42650
|
+
if (nextPoints) setParams(spline2DPatch(curveDef.name, nextPoints));
|
|
42651
|
+
};
|
|
42652
|
+
const cancelDraftDrag = () => {
|
|
42653
|
+
draftPointsRef.current = null;
|
|
42654
|
+
setDraftPoints(null);
|
|
42655
|
+
};
|
|
42656
|
+
const insertPointAfterSelection = () => {
|
|
42657
|
+
if (!canAdd) return;
|
|
42658
|
+
const insertAt = Math.min(activeIndex + 1, points.length);
|
|
42659
|
+
const a2 = points[activeIndex] ?? points[points.length - 1] ?? { x: 0, y: 0, g: "G2" };
|
|
42660
|
+
const b2 = points[insertAt] ?? (curveDef.closed ? points[0] : a2);
|
|
42661
|
+
const nextPoint = {
|
|
42662
|
+
x: roundToStep((a2.x + b2.x) / 2, curveDef.x.step),
|
|
42663
|
+
y: roundToStep((a2.y + b2.y) / 2, curveDef.y.step),
|
|
42664
|
+
g: a2.g === "G0" ? "G1" : a2.g
|
|
42665
|
+
};
|
|
42666
|
+
const nextPoints = [...points];
|
|
42667
|
+
nextPoints.splice(insertAt, 0, nextPoint);
|
|
42668
|
+
setSelected(insertAt);
|
|
42669
|
+
setSelectedIndices([insertAt]);
|
|
42670
|
+
setParams(spline2DPatch(curveDef.name, nextPoints));
|
|
42671
|
+
};
|
|
42672
|
+
const removeSelectedPoint = () => {
|
|
42673
|
+
if (!canRemove) return;
|
|
42674
|
+
const removeIndices = new Set(normalizedSelectedIndices);
|
|
42675
|
+
const nextPoints = points.filter((_, index) => !removeIndices.has(index));
|
|
42676
|
+
const firstRemoved = normalizedSelectedIndices[0] ?? activeIndex;
|
|
42677
|
+
const nextSelected = Math.max(0, Math.min(firstRemoved, nextPoints.length - 1));
|
|
42678
|
+
setSelected(nextSelected);
|
|
42679
|
+
setSelectedIndices([nextSelected]);
|
|
42680
|
+
setParams(spline2DPatch(curveDef.name, nextPoints), trailingSpline2DKeys(curveDef.name, points.length, nextPoints.length));
|
|
42681
|
+
};
|
|
42682
|
+
const handleButtonPointerDown = (event, action) => {
|
|
42683
|
+
event.preventDefault();
|
|
42684
|
+
event.stopPropagation();
|
|
42685
|
+
action();
|
|
42686
|
+
};
|
|
42687
|
+
const handleButtonKeyDown = (event, action) => {
|
|
42688
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
42689
|
+
event.preventDefault();
|
|
42690
|
+
event.stopPropagation();
|
|
42691
|
+
action();
|
|
42692
|
+
};
|
|
42693
|
+
const canvasFrame = (canvas) => viewFrame ?? frameForPath2D(canvas.clientWidth, canvas.clientHeight, curveDef);
|
|
42694
|
+
const canvasHeight = large ? "min(62vh, 640px)" : fullScreen ? "calc(100vh - 150px)" : 220;
|
|
42695
|
+
const expandedLayout = large || fullScreen;
|
|
42696
|
+
const editorPanel = /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42697
|
+
"div",
|
|
42698
|
+
{
|
|
42699
|
+
style: {
|
|
42700
|
+
border: "1px solid var(--fc-border)",
|
|
42701
|
+
borderRadius: 4,
|
|
42702
|
+
padding: 8,
|
|
42703
|
+
background: "var(--fc-bgSubtle, var(--fc-bgSurface, #1c2128))",
|
|
42704
|
+
userSelect: "none",
|
|
42705
|
+
WebkitUserSelect: "none",
|
|
42706
|
+
...expandedLayout ? {
|
|
42707
|
+
display: "flex",
|
|
42708
|
+
flexDirection: "column"
|
|
42709
|
+
} : {},
|
|
42710
|
+
...fullScreen ? {
|
|
42711
|
+
position: "fixed",
|
|
42712
|
+
inset: 16,
|
|
42713
|
+
zIndex: 2200,
|
|
42714
|
+
boxShadow: "0 18px 52px rgba(0, 0, 0, 0.45)",
|
|
42715
|
+
boxSizing: "border-box",
|
|
42716
|
+
color: "var(--fc-text)"
|
|
42717
|
+
} : {}
|
|
42718
|
+
},
|
|
42719
|
+
children: [
|
|
42720
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: 4, marginBottom: 6 }, children: [
|
|
42721
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => setViewFrame(null), title: "Fit spline in editor", style: buttonStyle, children: "Fit" }),
|
|
42722
|
+
allowFullScreen && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42723
|
+
"button",
|
|
42724
|
+
{
|
|
42725
|
+
type: "button",
|
|
42726
|
+
onClick: () => setFullScreen(!fullScreen),
|
|
42727
|
+
title: fullScreen ? "Close expanded editor" : "Expand editor",
|
|
42728
|
+
style: buttonStyle,
|
|
42729
|
+
children: fullScreen ? "Close" : "Full"
|
|
42730
|
+
}
|
|
42731
|
+
)
|
|
42732
|
+
] }),
|
|
42733
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42734
|
+
"canvas",
|
|
42735
|
+
{
|
|
42736
|
+
ref: canvasRef,
|
|
42737
|
+
height: 220,
|
|
42738
|
+
"aria-label": `${curveDef.name} spline editor`,
|
|
42739
|
+
onPointerDown: (event) => {
|
|
42740
|
+
event.preventDefault();
|
|
42741
|
+
const pos = pointerPoint(event);
|
|
42742
|
+
const hit = nearestHandle(event.currentTarget, pos.x, pos.y);
|
|
42743
|
+
if (hit === null) panStart.current = { x: pos.x, y: pos.y, frame: canvasFrame(event.currentTarget) };
|
|
42744
|
+
else {
|
|
42745
|
+
const nextSelection = pointSelectionForPointerDown(
|
|
42746
|
+
normalizedSelectedIndices,
|
|
42747
|
+
hit,
|
|
42748
|
+
activeIndex,
|
|
42749
|
+
hasPointSelectionModifier(event),
|
|
42750
|
+
points.length
|
|
42751
|
+
);
|
|
42752
|
+
setSelected(nextSelection.activeIndex);
|
|
42753
|
+
setSelectedIndices(nextSelection.selectedIndices);
|
|
42754
|
+
if (nextSelection.shouldDrag) {
|
|
42755
|
+
const frame2 = canvasFrame(event.currentTarget);
|
|
42756
|
+
draftPointsRef.current = null;
|
|
42757
|
+
setDraftPoints(null);
|
|
42758
|
+
dragState.current = {
|
|
42759
|
+
indices: nextSelection.selectedIndices,
|
|
42760
|
+
startPointer: screenToPath2D(pos.x, pos.y, frame2),
|
|
42761
|
+
startPoints: points.map((point) => ({ ...point })),
|
|
42762
|
+
frame: frame2
|
|
42763
|
+
};
|
|
42764
|
+
}
|
|
42765
|
+
}
|
|
42766
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
42767
|
+
},
|
|
42768
|
+
onPointerMove: (event) => {
|
|
42769
|
+
const pos = pointerPoint(event);
|
|
42770
|
+
if (dragState.current) {
|
|
42771
|
+
moveDraggedPoints(dragState.current, screenToPath2D(pos.x, pos.y, dragState.current.frame));
|
|
42772
|
+
return;
|
|
42773
|
+
}
|
|
42774
|
+
if (panStart.current) {
|
|
42775
|
+
setViewFrame(panPath2DFrame(panStart.current.frame, pos.x - panStart.current.x, pos.y - panStart.current.y));
|
|
42776
|
+
}
|
|
42777
|
+
},
|
|
42778
|
+
onPointerUp: (event) => {
|
|
42779
|
+
if (dragState.current) commitDraftDrag();
|
|
42780
|
+
dragState.current = null;
|
|
42781
|
+
panStart.current = null;
|
|
42782
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
42783
|
+
},
|
|
42784
|
+
onPointerCancel: (event) => {
|
|
42785
|
+
if (dragState.current) cancelDraftDrag();
|
|
42786
|
+
dragState.current = null;
|
|
42787
|
+
panStart.current = null;
|
|
42788
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
42789
|
+
},
|
|
42790
|
+
style: { display: "block", width: "100%", height: canvasHeight, borderRadius: 3, background: "var(--fc-bg)", touchAction: "none" }
|
|
42791
|
+
}
|
|
42792
|
+
),
|
|
42793
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8, marginTop: 6 }, children: [
|
|
42794
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { color: "var(--fc-textDim)", fontSize: 10, fontVariantNumeric: "tabular-nums", minWidth: 0 }, children: [
|
|
42795
|
+
normalizedSelectedIndices.length > 1 ? `${normalizedSelectedIndices.length} selected · ` : "",
|
|
42796
|
+
"P",
|
|
42797
|
+
Math.min(activeIndex + 1, points.length),
|
|
42798
|
+
" x ",
|
|
42799
|
+
selectedPoint ? selectedPoint.x : 0,
|
|
42800
|
+
unitLabel,
|
|
42801
|
+
" · y ",
|
|
42802
|
+
selectedPoint ? selectedPoint.y : 0,
|
|
42803
|
+
unitLabel
|
|
42804
|
+
] }),
|
|
42805
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: 4, alignItems: "center" }, children: [
|
|
42806
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42807
|
+
"select",
|
|
42808
|
+
{
|
|
42809
|
+
"aria-label": `${curveDef.name} selected point continuity`,
|
|
42810
|
+
value: (selectedPoint == null ? void 0 : selectedPoint.g) ?? "G2",
|
|
42811
|
+
onChange: (event) => setContinuityForSelection(event.target.value),
|
|
42812
|
+
style: selectStyle,
|
|
42813
|
+
children: SPLINE_2D_CONTINUITIES.map((continuity) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: continuity, children: continuity }, continuity))
|
|
42814
|
+
}
|
|
42815
|
+
),
|
|
42816
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42817
|
+
"button",
|
|
42818
|
+
{
|
|
42819
|
+
type: "button",
|
|
42820
|
+
onPointerDown: (event) => handleButtonPointerDown(event, insertPointAfterSelection),
|
|
42821
|
+
onKeyDown: (event) => handleButtonKeyDown(event, insertPointAfterSelection),
|
|
42822
|
+
disabled: !canAdd,
|
|
42823
|
+
title: "Add point after selected",
|
|
42824
|
+
style: buttonStyle,
|
|
42825
|
+
children: "+ Point"
|
|
42826
|
+
}
|
|
42827
|
+
),
|
|
42828
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42829
|
+
"button",
|
|
42830
|
+
{
|
|
42831
|
+
type: "button",
|
|
42832
|
+
onPointerDown: (event) => handleButtonPointerDown(event, removeSelectedPoint),
|
|
42833
|
+
onKeyDown: (event) => handleButtonKeyDown(event, removeSelectedPoint),
|
|
42834
|
+
disabled: !canRemove,
|
|
42835
|
+
title: "Remove selected point(s)",
|
|
42836
|
+
style: buttonStyle,
|
|
42837
|
+
children: "Remove"
|
|
42838
|
+
}
|
|
42839
|
+
)
|
|
42840
|
+
] })
|
|
42841
|
+
] })
|
|
42842
|
+
]
|
|
42843
|
+
}
|
|
42844
|
+
);
|
|
42845
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginBottom: 8 }, children: [
|
|
42846
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42847
|
+
"div",
|
|
42848
|
+
{
|
|
42849
|
+
onClick: () => setExpanded(!expanded),
|
|
42850
|
+
style: { display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: 12, cursor: "pointer", marginBottom: 4 },
|
|
42851
|
+
children: [
|
|
42852
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { color: "var(--fc-text)", fontWeight: 600 }, children: [
|
|
42853
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: 9, marginRight: 4 }, children: expanded ? "▼" : "▶" }),
|
|
42854
|
+
curveDef.name,
|
|
42855
|
+
" (",
|
|
42856
|
+
points.length,
|
|
42857
|
+
")"
|
|
42858
|
+
] }),
|
|
42859
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { color: "var(--fc-textDim)", fontSize: 10 }, children: [
|
|
42860
|
+
"Spline d",
|
|
42861
|
+
curveDef.degree
|
|
42862
|
+
] })
|
|
42863
|
+
]
|
|
42864
|
+
}
|
|
42865
|
+
),
|
|
42866
|
+
expanded && editorPanel
|
|
42867
|
+
] });
|
|
42868
|
+
}
|
|
42869
|
+
const SPATIAL_PARAM_KIND_COLORS = {
|
|
42870
|
+
scalar: "#46b6a9",
|
|
42871
|
+
text: "#9ca3af",
|
|
42872
|
+
list: "#e5a83c",
|
|
42873
|
+
path2d: "#e5a83c",
|
|
42874
|
+
spline2d: "#5da9e9",
|
|
42875
|
+
placement2d: "#c77dff"
|
|
42876
|
+
};
|
|
42877
|
+
function spatialParamAnchorPosition(anchor) {
|
|
42878
|
+
return anchor.kind === "point" ? anchor.at : anchor.origin;
|
|
42879
|
+
}
|
|
42880
|
+
function spatialParamAnchorLabel(name, anchor) {
|
|
42881
|
+
return anchor.label ?? name;
|
|
42882
|
+
}
|
|
42883
|
+
function scalarSummary(param) {
|
|
42884
|
+
if (param.boolean) return param.value >= 0.5 ? "on" : "off";
|
|
42885
|
+
if (param.choices) return param.choices[Math.max(0, Math.min(Math.round(param.value), param.choices.length - 1))] ?? String(param.value);
|
|
42886
|
+
return `${param.value}${param.unit ? ` ${param.unit}` : ""}`;
|
|
42887
|
+
}
|
|
42888
|
+
function collectSpatialParamAnchors(params, stringParams, listParams, path2dParams, spline2dParams, placement2dParams) {
|
|
42889
|
+
const anchors = [];
|
|
42890
|
+
params.forEach((param) => {
|
|
42891
|
+
if (param.anchor) anchors.push({ name: param.name, kind: "scalar", anchor: param.anchor, summary: scalarSummary(param) });
|
|
42892
|
+
});
|
|
42893
|
+
stringParams.forEach((param) => {
|
|
42894
|
+
if (param.anchor) anchors.push({ name: param.name, kind: "text", anchor: param.anchor, summary: param.value });
|
|
42895
|
+
});
|
|
42896
|
+
listParams.forEach((param) => {
|
|
42897
|
+
if (param.anchor) anchors.push({ name: param.name, kind: "list", anchor: param.anchor, summary: `${param.items.length} items` });
|
|
42898
|
+
});
|
|
42899
|
+
path2dParams.forEach((param) => {
|
|
42900
|
+
if (param.anchor)
|
|
42901
|
+
anchors.push({ name: param.name, kind: "path2d", anchor: param.anchor, summary: `${param.points.length} pts`, pathDef: param });
|
|
42902
|
+
});
|
|
42903
|
+
spline2dParams.forEach((param) => {
|
|
42904
|
+
if (param.anchor)
|
|
42905
|
+
anchors.push({ name: param.name, kind: "spline2d", anchor: param.anchor, summary: `${param.points.length} pts`, splineDef: param });
|
|
42906
|
+
});
|
|
42907
|
+
placement2dParams.forEach((param) => {
|
|
42908
|
+
if (param.anchor)
|
|
42909
|
+
anchors.push({
|
|
42910
|
+
name: param.name,
|
|
42911
|
+
kind: "placement2d",
|
|
42912
|
+
anchor: param.anchor,
|
|
42913
|
+
summary: `${param.items.length} items`,
|
|
42914
|
+
placementDef: param
|
|
42915
|
+
});
|
|
42916
|
+
});
|
|
42917
|
+
return anchors;
|
|
42918
|
+
}
|
|
42919
|
+
function stopSpatialPointerDown$1(event) {
|
|
42920
|
+
event.stopPropagation();
|
|
42921
|
+
}
|
|
42922
|
+
function preventControlTextSelection$1(event) {
|
|
42923
|
+
const target = event.target;
|
|
42924
|
+
if (target instanceof HTMLElement && target.closest("input, textarea, select, button, canvas")) return;
|
|
42925
|
+
event.preventDefault();
|
|
42926
|
+
}
|
|
42927
|
+
function preventMarkerDrag(event) {
|
|
42928
|
+
event.preventDefault();
|
|
42929
|
+
}
|
|
42930
|
+
function stopSpatialWheel$1(event) {
|
|
42931
|
+
event.stopPropagation();
|
|
42932
|
+
}
|
|
42933
|
+
function SpatialParamMarker({ item }) {
|
|
42934
|
+
const focusedParamName = useForgeStore((s) => s.focusedParamName);
|
|
42935
|
+
const focusParam = useForgeStore((s) => s.focusParam);
|
|
42936
|
+
const openSpatialParamSheet = useForgeStore((s) => s.openSpatialParamSheet);
|
|
42937
|
+
const closeSpatialParamSheet = useForgeStore((s) => s.closeSpatialParamSheet);
|
|
42938
|
+
const selected = focusedParamName === item.name;
|
|
42939
|
+
const color = item.anchor.color ?? SPATIAL_PARAM_KIND_COLORS[item.kind];
|
|
42940
|
+
const isManual = item.kind === "path2d" || item.kind === "spline2d" || item.kind === "placement2d";
|
|
42941
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42942
|
+
Html,
|
|
42943
|
+
{
|
|
42944
|
+
position: spatialParamAnchorPosition(item.anchor),
|
|
42945
|
+
zIndexRange: [90, 0],
|
|
42946
|
+
style: { pointerEvents: "auto", userSelect: "none", WebkitUserSelect: "none" },
|
|
42947
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
42948
|
+
"button",
|
|
42949
|
+
{
|
|
42950
|
+
type: "button",
|
|
42951
|
+
"aria-label": `Focus parameter ${item.name}`,
|
|
42952
|
+
draggable: false,
|
|
42953
|
+
onDragStart: preventMarkerDrag,
|
|
42954
|
+
onPointerDown: (event) => {
|
|
42955
|
+
event.stopPropagation();
|
|
42956
|
+
event.preventDefault();
|
|
42957
|
+
},
|
|
42958
|
+
onClick: (event) => {
|
|
42959
|
+
event.stopPropagation();
|
|
42960
|
+
focusParam(item.name);
|
|
42961
|
+
if (isManual) openSpatialParamSheet(item.name);
|
|
42962
|
+
else closeSpatialParamSheet();
|
|
42963
|
+
},
|
|
42964
|
+
style: {
|
|
42965
|
+
display: "inline-flex",
|
|
42966
|
+
gap: 7,
|
|
42967
|
+
alignItems: "center",
|
|
42968
|
+
width: "max-content",
|
|
42969
|
+
maxWidth: 220,
|
|
42970
|
+
minWidth: 0,
|
|
42971
|
+
minHeight: 0,
|
|
42972
|
+
padding: 0,
|
|
42973
|
+
border: 0,
|
|
42974
|
+
background: "transparent",
|
|
42975
|
+
color: "var(--fc-text, #f8fafc)",
|
|
42976
|
+
cursor: "pointer",
|
|
42977
|
+
font: "inherit",
|
|
42978
|
+
lineHeight: 1,
|
|
42979
|
+
transform: "translate(8px, -50%)",
|
|
42980
|
+
userSelect: "none",
|
|
42981
|
+
WebkitUserSelect: "none",
|
|
42982
|
+
touchAction: "none"
|
|
42983
|
+
},
|
|
42984
|
+
children: [
|
|
42985
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
42986
|
+
"span",
|
|
42987
|
+
{
|
|
42988
|
+
style: {
|
|
42989
|
+
width: 18,
|
|
42990
|
+
height: 18,
|
|
42991
|
+
flex: "0 0 18px",
|
|
42992
|
+
borderRadius: 999,
|
|
42993
|
+
background: color,
|
|
42994
|
+
border: selected ? "2px solid #fff6d4" : "2px solid rgba(255,255,255,0.78)",
|
|
42995
|
+
boxShadow: selected ? "0 0 0 4px rgba(255, 246, 212, 0.18), 0 8px 20px rgba(0,0,0,0.32)" : "0 8px 20px rgba(0,0,0,0.32)"
|
|
42996
|
+
}
|
|
42997
|
+
}
|
|
42998
|
+
),
|
|
42999
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
43000
|
+
"span",
|
|
43001
|
+
{
|
|
43002
|
+
style: {
|
|
43003
|
+
padding: "4px 7px",
|
|
43004
|
+
borderRadius: 4,
|
|
43005
|
+
border: selected ? `1px solid ${color}` : "1px solid rgba(255,255,255,0.14)",
|
|
43006
|
+
background: "rgba(18, 24, 27, 0.9)",
|
|
43007
|
+
boxShadow: "0 10px 22px rgba(0,0,0,0.24)",
|
|
43008
|
+
boxSizing: "border-box",
|
|
43009
|
+
display: "block",
|
|
43010
|
+
maxWidth: 176,
|
|
43011
|
+
minWidth: 0,
|
|
43012
|
+
overflow: "hidden",
|
|
43013
|
+
fontSize: 11,
|
|
43014
|
+
fontWeight: 700,
|
|
43015
|
+
lineHeight: 1.15,
|
|
43016
|
+
textAlign: "left",
|
|
43017
|
+
whiteSpace: "nowrap",
|
|
43018
|
+
textOverflow: "ellipsis",
|
|
43019
|
+
userSelect: "none",
|
|
43020
|
+
WebkitUserSelect: "none"
|
|
43021
|
+
},
|
|
43022
|
+
children: [
|
|
43023
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { display: "block", overflow: "hidden", textOverflow: "ellipsis" }, children: spatialParamAnchorLabel(item.name, item.anchor) }),
|
|
43024
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43025
|
+
"span",
|
|
43026
|
+
{
|
|
43027
|
+
style: {
|
|
43028
|
+
display: "block",
|
|
43029
|
+
overflow: "hidden",
|
|
43030
|
+
textOverflow: "ellipsis",
|
|
43031
|
+
color: "var(--fc-textDim, #9ca3af)",
|
|
43032
|
+
fontSize: 10,
|
|
43033
|
+
fontWeight: 650
|
|
43034
|
+
},
|
|
43035
|
+
children: item.summary
|
|
43036
|
+
}
|
|
43037
|
+
)
|
|
43038
|
+
]
|
|
43039
|
+
}
|
|
43040
|
+
)
|
|
43041
|
+
]
|
|
43042
|
+
}
|
|
43043
|
+
)
|
|
43044
|
+
}
|
|
43045
|
+
);
|
|
43046
|
+
}
|
|
43047
|
+
function SpatialParamSheet({ item }) {
|
|
43048
|
+
const expandSpatialParamSheet = useForgeStore((s) => s.expandSpatialParamSheet);
|
|
43049
|
+
const closeSpatialParamSheet = useForgeStore((s) => s.closeSpatialParamSheet);
|
|
43050
|
+
useEscapeAction(
|
|
43051
|
+
() => {
|
|
43052
|
+
closeSpatialParamSheet();
|
|
43053
|
+
return true;
|
|
43054
|
+
},
|
|
43055
|
+
{ active: true, label: "Spatial parameter sheet", priority: ESCAPE_PRIORITY.popover }
|
|
43056
|
+
);
|
|
43057
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43058
|
+
Html,
|
|
43059
|
+
{
|
|
43060
|
+
position: spatialParamAnchorPosition(item.anchor),
|
|
43061
|
+
zIndexRange: [95, 0],
|
|
43062
|
+
style: { pointerEvents: "auto", userSelect: "none", WebkitUserSelect: "none" },
|
|
43063
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
43064
|
+
"div",
|
|
43065
|
+
{
|
|
43066
|
+
className: "fc-viewport-floating-panel fc-spatial-param-sheet",
|
|
43067
|
+
onPointerDown: stopSpatialPointerDown$1,
|
|
43068
|
+
onMouseDown: preventControlTextSelection$1,
|
|
43069
|
+
onWheel: stopSpatialWheel$1,
|
|
43070
|
+
style: {
|
|
43071
|
+
width: "clamp(320px, 34vw, 430px)",
|
|
43072
|
+
maxWidth: "calc(100vw - 32px)",
|
|
43073
|
+
maxHeight: "min(72vh, 560px)",
|
|
43074
|
+
transform: "translate(24px, -50%)",
|
|
43075
|
+
border: "1px solid var(--fc-floating-panel-border, var(--fc-border, #333))",
|
|
43076
|
+
borderRadius: 6,
|
|
43077
|
+
background: "var(--fc-floating-panel-bg, rgba(18, 24, 27, 0.96))",
|
|
43078
|
+
boxShadow: "0 18px 52px rgba(0,0,0,0.38)",
|
|
43079
|
+
boxSizing: "border-box",
|
|
43080
|
+
padding: 10,
|
|
43081
|
+
overflow: "hidden",
|
|
43082
|
+
color: "var(--fc-text)",
|
|
43083
|
+
userSelect: "none",
|
|
43084
|
+
WebkitUserSelect: "none"
|
|
43085
|
+
},
|
|
43086
|
+
children: [
|
|
43087
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: 10, alignItems: "center", marginBottom: 8 }, children: [
|
|
43088
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { minWidth: 0 }, children: [
|
|
43089
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { style: { display: "block", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontSize: 12 }, children: item.name }),
|
|
43090
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43091
|
+
"span",
|
|
43092
|
+
{
|
|
43093
|
+
style: {
|
|
43094
|
+
display: "block",
|
|
43095
|
+
overflow: "hidden",
|
|
43096
|
+
textOverflow: "ellipsis",
|
|
43097
|
+
whiteSpace: "nowrap",
|
|
43098
|
+
fontSize: 10,
|
|
43099
|
+
color: "var(--fc-textDim)",
|
|
43100
|
+
marginTop: 1
|
|
43101
|
+
},
|
|
43102
|
+
children: spatialParamAnchorLabel(item.name, item.anchor)
|
|
43103
|
+
}
|
|
43104
|
+
)
|
|
43105
|
+
] }),
|
|
43106
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: 4 }, children: [
|
|
43107
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43108
|
+
"button",
|
|
43109
|
+
{
|
|
43110
|
+
type: "button",
|
|
43111
|
+
onClick: () => expandSpatialParamSheet(item.name),
|
|
43112
|
+
style: {
|
|
43113
|
+
border: "1px solid var(--fc-border)",
|
|
43114
|
+
borderRadius: 4,
|
|
43115
|
+
background: "none",
|
|
43116
|
+
color: "var(--fc-textDim)",
|
|
43117
|
+
cursor: "pointer",
|
|
43118
|
+
fontSize: 11,
|
|
43119
|
+
lineHeight: "16px",
|
|
43120
|
+
padding: "2px 7px"
|
|
43121
|
+
},
|
|
43122
|
+
children: "Full"
|
|
43123
|
+
}
|
|
43124
|
+
),
|
|
43125
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43126
|
+
"button",
|
|
43127
|
+
{
|
|
43128
|
+
type: "button",
|
|
43129
|
+
onClick: closeSpatialParamSheet,
|
|
43130
|
+
style: {
|
|
43131
|
+
border: "1px solid var(--fc-border)",
|
|
43132
|
+
borderRadius: 4,
|
|
43133
|
+
background: "none",
|
|
43134
|
+
color: "var(--fc-textDim)",
|
|
43135
|
+
cursor: "pointer",
|
|
43136
|
+
fontSize: 11,
|
|
43137
|
+
lineHeight: "16px",
|
|
43138
|
+
padding: "2px 7px"
|
|
43139
|
+
},
|
|
43140
|
+
children: "Close"
|
|
43141
|
+
}
|
|
43142
|
+
)
|
|
43143
|
+
] })
|
|
43144
|
+
] }),
|
|
43145
|
+
item.pathDef && /* @__PURE__ */ jsxRuntimeExports.jsx(Path2DParamEditor, { pathDef: item.pathDef, allowFullScreen: false }),
|
|
43146
|
+
item.splineDef && /* @__PURE__ */ jsxRuntimeExports.jsx(Spline2DParamEditor, { curveDef: item.splineDef, allowFullScreen: false }),
|
|
43147
|
+
item.placementDef && /* @__PURE__ */ jsxRuntimeExports.jsx(Placement2DParamEditor, { layoutDef: item.placementDef, allowFullScreen: false })
|
|
43148
|
+
]
|
|
43149
|
+
}
|
|
43150
|
+
)
|
|
43151
|
+
}
|
|
43152
|
+
);
|
|
43153
|
+
}
|
|
43154
|
+
function SpatialParamAnchorsOverlay() {
|
|
43155
|
+
const params = useForgeStore((s) => s.params);
|
|
43156
|
+
const stringParams = useForgeStore((s) => s.stringParams);
|
|
43157
|
+
const listParams = useForgeStore((s) => s.listParams);
|
|
43158
|
+
const path2dParams = useForgeStore((s) => s.path2dParams);
|
|
43159
|
+
const spline2dParams = useForgeStore((s) => s.spline2dParams);
|
|
43160
|
+
const placement2dParams = useForgeStore((s) => s.placement2dParams);
|
|
43161
|
+
const sheetName = useForgeStore((s) => s.spatialParamSheetName);
|
|
43162
|
+
const anchors = reactExports.useMemo(
|
|
43163
|
+
() => collectSpatialParamAnchors(params, stringParams, listParams, path2dParams, spline2dParams, placement2dParams),
|
|
43164
|
+
[params, stringParams, listParams, path2dParams, placement2dParams, spline2dParams]
|
|
43165
|
+
);
|
|
43166
|
+
const sheetItem = reactExports.useMemo(
|
|
43167
|
+
() => anchors.find((item) => item.name === sheetName && (item.pathDef || item.splineDef || item.placementDef)) ?? null,
|
|
43168
|
+
[anchors, sheetName]
|
|
43169
|
+
);
|
|
43170
|
+
if (anchors.length === 0) return null;
|
|
43171
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("group", { renderOrder: 13, children: [
|
|
43172
|
+
anchors.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsx(SpatialParamMarker, { item }, item.name)),
|
|
43173
|
+
sheetItem && /* @__PURE__ */ jsxRuntimeExports.jsx(SpatialParamSheet, { item: sheetItem })
|
|
43174
|
+
] });
|
|
43175
|
+
}
|
|
43176
|
+
function stopSpatialPointerDown(event) {
|
|
43177
|
+
event.stopPropagation();
|
|
43178
|
+
}
|
|
43179
|
+
function preventControlTextSelection(event) {
|
|
43180
|
+
const target = event.target;
|
|
43181
|
+
if (target instanceof HTMLElement && target.closest("input, textarea, select, button, canvas")) return;
|
|
43182
|
+
event.preventDefault();
|
|
43183
|
+
}
|
|
43184
|
+
function stopSpatialWheel(event) {
|
|
43185
|
+
event.stopPropagation();
|
|
43186
|
+
}
|
|
43187
|
+
function SpatialParamExpandedSheetOverlay() {
|
|
43188
|
+
const params = useForgeStore((s) => s.params);
|
|
43189
|
+
const stringParams = useForgeStore((s) => s.stringParams);
|
|
43190
|
+
const listParams = useForgeStore((s) => s.listParams);
|
|
43191
|
+
const path2dParams = useForgeStore((s) => s.path2dParams);
|
|
43192
|
+
const spline2dParams = useForgeStore((s) => s.spline2dParams);
|
|
43193
|
+
const placement2dParams = useForgeStore((s) => s.placement2dParams);
|
|
43194
|
+
const sheetName = useForgeStore((s) => s.expandedSpatialParamSheetName);
|
|
43195
|
+
const closeExpandedSpatialParamSheet = useForgeStore((s) => s.closeExpandedSpatialParamSheet);
|
|
43196
|
+
const anchors = reactExports.useMemo(
|
|
43197
|
+
() => collectSpatialParamAnchors(params, stringParams, listParams, path2dParams, spline2dParams, placement2dParams),
|
|
43198
|
+
[params, stringParams, listParams, path2dParams, placement2dParams, spline2dParams]
|
|
43199
|
+
);
|
|
43200
|
+
const item = reactExports.useMemo(
|
|
43201
|
+
() => anchors.find((anchor) => anchor.name === sheetName && (anchor.pathDef || anchor.splineDef || anchor.placementDef)) ?? null,
|
|
43202
|
+
[anchors, sheetName]
|
|
43203
|
+
);
|
|
43204
|
+
useEscapeAction(
|
|
43205
|
+
() => {
|
|
43206
|
+
closeExpandedSpatialParamSheet();
|
|
43207
|
+
return true;
|
|
43208
|
+
},
|
|
43209
|
+
{ active: item !== null, label: "Expanded spatial parameter sheet", priority: ESCAPE_PRIORITY.modal }
|
|
43210
|
+
);
|
|
43211
|
+
if (!item) return null;
|
|
43212
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43213
|
+
"div",
|
|
43214
|
+
{
|
|
43215
|
+
className: "fc-spatial-param-expanded-overlay",
|
|
43216
|
+
role: "dialog",
|
|
43217
|
+
"aria-modal": "true",
|
|
43218
|
+
"aria-label": `${item.name} spatial parameter editor`,
|
|
43219
|
+
style: {
|
|
43220
|
+
position: "fixed",
|
|
43221
|
+
inset: 0,
|
|
43222
|
+
zIndex: 2200,
|
|
43223
|
+
padding: 24,
|
|
43224
|
+
boxSizing: "border-box",
|
|
43225
|
+
display: "flex",
|
|
43226
|
+
background: "rgba(2, 7, 10, 0.58)",
|
|
43227
|
+
backdropFilter: "blur(2px)",
|
|
43228
|
+
WebkitBackdropFilter: "blur(2px)",
|
|
43229
|
+
pointerEvents: "auto",
|
|
43230
|
+
userSelect: "none",
|
|
43231
|
+
WebkitUserSelect: "none"
|
|
43232
|
+
},
|
|
43233
|
+
onPointerDown: stopSpatialPointerDown,
|
|
43234
|
+
onMouseDown: preventControlTextSelection,
|
|
43235
|
+
onWheel: stopSpatialWheel,
|
|
43236
|
+
onContextMenu: (event) => event.preventDefault(),
|
|
43237
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
43238
|
+
"div",
|
|
43239
|
+
{
|
|
43240
|
+
className: "fc-viewport-floating-panel fc-spatial-param-expanded-surface",
|
|
43241
|
+
style: {
|
|
43242
|
+
display: "flex",
|
|
43243
|
+
flexDirection: "column",
|
|
43244
|
+
width: "100%",
|
|
43245
|
+
minWidth: 0,
|
|
43246
|
+
minHeight: 0,
|
|
43247
|
+
border: "1px solid var(--fc-floating-panel-border, var(--fc-border))",
|
|
43248
|
+
borderRadius: 6,
|
|
43249
|
+
background: "var(--fc-bgPanel, #161b22)",
|
|
43250
|
+
boxShadow: "0 18px 52px rgba(0,0,0,0.42)",
|
|
43251
|
+
padding: 12,
|
|
43252
|
+
color: "var(--fc-text)",
|
|
43253
|
+
overflow: "hidden"
|
|
43254
|
+
},
|
|
43255
|
+
children: [
|
|
43256
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, marginBottom: 10 }, children: [
|
|
43257
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { minWidth: 0 }, children: [
|
|
43258
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: 13, fontWeight: 700, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: item.name }),
|
|
43259
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: 11, color: "var(--fc-textDim)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: spatialParamAnchorLabel(item.name, item.anchor) })
|
|
43260
|
+
] }),
|
|
43261
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43262
|
+
"button",
|
|
43263
|
+
{
|
|
43264
|
+
type: "button",
|
|
43265
|
+
onClick: closeExpandedSpatialParamSheet,
|
|
43266
|
+
style: {
|
|
43267
|
+
border: "1px solid var(--fc-border)",
|
|
43268
|
+
borderRadius: 4,
|
|
43269
|
+
background: "transparent",
|
|
43270
|
+
color: "var(--fc-textDim)",
|
|
43271
|
+
cursor: "pointer",
|
|
43272
|
+
fontSize: 12,
|
|
43273
|
+
padding: "3px 8px"
|
|
43274
|
+
},
|
|
43275
|
+
children: "Close"
|
|
43276
|
+
}
|
|
43277
|
+
)
|
|
43278
|
+
] }),
|
|
43279
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { minHeight: 0, overflow: "auto" }, children: [
|
|
43280
|
+
item.pathDef && /* @__PURE__ */ jsxRuntimeExports.jsx(Path2DParamEditor, { pathDef: item.pathDef, allowFullScreen: false, large: true }),
|
|
43281
|
+
item.splineDef && /* @__PURE__ */ jsxRuntimeExports.jsx(Spline2DParamEditor, { curveDef: item.splineDef, allowFullScreen: false, large: true }),
|
|
43282
|
+
item.placementDef && /* @__PURE__ */ jsxRuntimeExports.jsx(Placement2DParamEditor, { layoutDef: item.placementDef, allowFullScreen: false, large: true })
|
|
43283
|
+
] })
|
|
43284
|
+
]
|
|
43285
|
+
}
|
|
43286
|
+
)
|
|
43287
|
+
}
|
|
43288
|
+
);
|
|
43289
|
+
}
|
|
41340
43290
|
const IDLE_STATE = {
|
|
41341
43291
|
status: "idle",
|
|
41342
43292
|
error: null,
|
|
@@ -41562,7 +43512,7 @@ function mergeResults(results, channel, analysisId) {
|
|
|
41562
43512
|
class InspectWorkerClient {
|
|
41563
43513
|
constructor(workerFactory = () => new Worker(new URL(
|
|
41564
43514
|
/* @vite-ignore */
|
|
41565
|
-
"/assets/inspectWorker-
|
|
43515
|
+
"/assets/inspectWorker-Cuby2qfT.js",
|
|
41566
43516
|
import.meta.url
|
|
41567
43517
|
), { type: "module" })) {
|
|
41568
43518
|
__publicField(this, "reqId", 0);
|
|
@@ -41760,14 +43710,14 @@ function addCandidate(candidates, candidate) {
|
|
|
41760
43710
|
}
|
|
41761
43711
|
if (candidate.bboxOverlapVolume > candidates[smallestIndex].bboxOverlapVolume) candidates[smallestIndex] = candidate;
|
|
41762
43712
|
}
|
|
41763
|
-
async function collectCollisionCandidates(
|
|
43713
|
+
async function collectCollisionCandidates(entries2, maybeYield) {
|
|
41764
43714
|
const candidates = [];
|
|
41765
43715
|
let candidateCount = 0;
|
|
41766
43716
|
let prunedPairCount = 0;
|
|
41767
|
-
for (let sourceIndex = 0; sourceIndex <
|
|
41768
|
-
const source =
|
|
41769
|
-
for (let targetIndex = sourceIndex + 1; targetIndex <
|
|
41770
|
-
const target =
|
|
43717
|
+
for (let sourceIndex = 0; sourceIndex < entries2.length; sourceIndex += 1) {
|
|
43718
|
+
const source = entries2[sourceIndex];
|
|
43719
|
+
for (let targetIndex = sourceIndex + 1; targetIndex < entries2.length; targetIndex += 1) {
|
|
43720
|
+
const target = entries2[targetIndex];
|
|
41771
43721
|
if (!aabbOverlaps(source.worldBounds, target.worldBounds)) continue;
|
|
41772
43722
|
const bboxOverlapVolume = aabbOverlapVolume(source.worldBounds, target.worldBounds);
|
|
41773
43723
|
if (bboxOverlapVolume <= DEFAULT_COLLISION_INSPECTION_OPTIONS.minOverlapVolume) {
|
|
@@ -41814,7 +43764,7 @@ async function cloneCollisionShapePayloadForWorker(shape, maybeYield) {
|
|
|
41814
43764
|
async function buildCollisionWorkerObjects(args) {
|
|
41815
43765
|
var _a3;
|
|
41816
43766
|
const warnings = [];
|
|
41817
|
-
const
|
|
43767
|
+
const entries2 = [];
|
|
41818
43768
|
const maybeYield = createBuildYield(args.isCancelled);
|
|
41819
43769
|
for (const obj of args.objects) {
|
|
41820
43770
|
if (args.isCancelled()) throw new InspectBuildCancelledError();
|
|
@@ -41824,10 +43774,10 @@ async function buildCollisionWorkerObjects(args) {
|
|
|
41824
43774
|
const local = localBounds(obj);
|
|
41825
43775
|
const world = transformedBounds(obj, matrix);
|
|
41826
43776
|
if (!local || !world) continue;
|
|
41827
|
-
|
|
43777
|
+
entries2.push({ obj, matrix, localBounds: local, worldBounds: world });
|
|
41828
43778
|
await maybeYield();
|
|
41829
43779
|
}
|
|
41830
|
-
const { candidates, candidateCount, prunedPairCount } = await collectCollisionCandidates(
|
|
43780
|
+
const { candidates, candidateCount, prunedPairCount } = await collectCollisionCandidates(entries2, maybeYield);
|
|
41831
43781
|
if (candidateCount === 0) {
|
|
41832
43782
|
return {
|
|
41833
43783
|
objects: [],
|
|
@@ -41859,7 +43809,7 @@ async function buildCollisionWorkerObjects(args) {
|
|
|
41859
43809
|
const out = [];
|
|
41860
43810
|
let payloadBytes = 0;
|
|
41861
43811
|
for (const entryIndex of selectedIndexes) {
|
|
41862
|
-
const entry =
|
|
43812
|
+
const entry = entries2[entryIndex];
|
|
41863
43813
|
const collisionShape = await cloneCollisionShapePayloadForWorker(entry.obj.shape, maybeYield);
|
|
41864
43814
|
const nextBytes = collisionPayloadBytes(collisionShape);
|
|
41865
43815
|
if (payloadBytes + nextBytes > VIEWPORT_COLLISION_MAX_PAYLOAD_BYTES) {
|
|
@@ -42640,6 +44590,7 @@ function Viewport() {
|
|
|
42640
44590
|
renderLabels,
|
|
42641
44591
|
debugHighlights3D,
|
|
42642
44592
|
dimensionsVisible,
|
|
44593
|
+
paramAnchorsVisible,
|
|
42643
44594
|
attachmentsVisible,
|
|
42644
44595
|
attachmentPoints,
|
|
42645
44596
|
sectionPlaneGuidesEnabled,
|
|
@@ -43419,6 +45370,7 @@ function Viewport() {
|
|
|
43419
45370
|
!rigInspectActive && hoveredJointOverlay && /* @__PURE__ */ jsxRuntimeExports.jsx(HoveredJointOverlay, { state: hoveredJointOverlay, config: jointOverlayConfig }),
|
|
43420
45371
|
!rigInspectActive && dimensionsVisible && dimensions.map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(DimensionAnnotation, { def: d, lengthUnit }, d.id)),
|
|
43421
45372
|
!rigInspectActive && /* @__PURE__ */ jsxRuntimeExports.jsx(RenderLabelsOverlay, { labels: renderLabels }),
|
|
45373
|
+
!rigInspectActive && paramAnchorsVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(SpatialParamAnchorsOverlay, {}),
|
|
43422
45374
|
!rigInspectActive && attachmentsVisible !== "none" && attachmentPoints.map((ap) => {
|
|
43423
45375
|
const matrix = objectMatrices[ap.objectId];
|
|
43424
45376
|
return matrix ? /* @__PURE__ */ jsxRuntimeExports.jsx("group", { matrixAutoUpdate: false, matrix, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ConnectorAttachmentAnnotation, { def: ap }) }, `${ap.objectId}:${ap.name}`) : /* @__PURE__ */ jsxRuntimeExports.jsx(ConnectorAttachmentAnnotation, { def: ap }, `${ap.objectId}:${ap.name}`);
|
|
@@ -43629,6 +45581,7 @@ function Viewport() {
|
|
|
43629
45581
|
}
|
|
43630
45582
|
),
|
|
43631
45583
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ViewportOverlayHost, { entries: overlayEntries }),
|
|
45584
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SpatialParamExpandedSheetOverlay, {}),
|
|
43632
45585
|
viewportDisabledMessage && /* @__PURE__ */ jsxRuntimeExports.jsx(ViewportDisabledOverlay, { title: viewportDisabledMessage.title, body: viewportDisabledMessage.body }),
|
|
43633
45586
|
drawFlagEnabled && /* @__PURE__ */ jsxRuntimeExports.jsx(DrawToolbar, {}),
|
|
43634
45587
|
/* @__PURE__ */ jsxRuntimeExports.jsx(HoverTooltipLayer, { ref: hoverTooltipRef, enabled: objectPickSyncEnabled && !measureMode && !voxelIntentMode }),
|
|
@@ -44309,7 +46262,7 @@ function Viewport() {
|
|
|
44309
46262
|
}
|
|
44310
46263
|
);
|
|
44311
46264
|
}
|
|
44312
|
-
const EditorApp$1 = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-
|
|
46265
|
+
const EditorApp$1 = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-CYBDvSyT.js"), true ? __vite__mapDeps([0]) : void 0).then((m2) => ({ default: m2.EditorApp })));
|
|
44313
46266
|
const PENDING_SHARE_COPY_KEY = "fc-pending-share-copy";
|
|
44314
46267
|
function storePendingShareCopy(shareId) {
|
|
44315
46268
|
sessionStorage.setItem(PENDING_SHARE_COPY_KEY, shareId);
|
|
@@ -44575,17 +46528,17 @@ function SeoMetadata() {
|
|
|
44575
46528
|
}, [location.pathname]);
|
|
44576
46529
|
return null;
|
|
44577
46530
|
}
|
|
44578
|
-
reactExports.lazy(() => __vitePreload(() => import("./LandingPageProofDriven-
|
|
44579
|
-
const DocsPage = reactExports.lazy(() => __vitePreload(() => import("./DocsPage-
|
|
44580
|
-
reactExports.lazy(() => __vitePreload(() => import("./BlogPage-
|
|
44581
|
-
reactExports.lazy(() => __vitePreload(() => import("./BenchmarkPage-
|
|
44582
|
-
reactExports.lazy(() => __vitePreload(() => import("./AdminPage-
|
|
46531
|
+
reactExports.lazy(() => __vitePreload(() => import("./LandingPageProofDriven-XYTiYxfM.js"), true ? __vite__mapDeps([1]) : void 0).then((m2) => ({ default: m2.LandingPageProofDriven })));
|
|
46532
|
+
const DocsPage = reactExports.lazy(() => __vitePreload(() => import("./DocsPage-ClL6X1hR.js"), true ? [] : void 0).then((m2) => ({ default: m2.DocsPage })));
|
|
46533
|
+
reactExports.lazy(() => __vitePreload(() => import("./BlogPage-DIWRApKS.js"), true ? [] : void 0).then((m2) => ({ default: m2.BlogPage })));
|
|
46534
|
+
reactExports.lazy(() => __vitePreload(() => import("./BenchmarkPage-YZJbw5nd.js"), true ? __vite__mapDeps([1,2]) : void 0).then((m2) => ({ default: m2.BenchmarkPage })));
|
|
46535
|
+
reactExports.lazy(() => __vitePreload(() => import("./AdminPage-B1nIvqLS.js"), true ? [] : void 0).then((m2) => ({ default: m2.AdminPage })));
|
|
44583
46536
|
reactExports.lazy(() => __vitePreload(() => Promise.resolve().then(() => PublishedModelPage$1), true ? void 0 : void 0).then((m2) => ({ default: m2.PublishedModelPage })));
|
|
44584
|
-
reactExports.lazy(() => __vitePreload(() => import("./SettingsPage-
|
|
44585
|
-
reactExports.lazy(() => __vitePreload(() => import("./PricingPage-
|
|
44586
|
-
reactExports.lazy(() => __vitePreload(() => import("./LegalPage-
|
|
44587
|
-
const EditorApp = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-
|
|
44588
|
-
const EmbedViewer = reactExports.lazy(() => __vitePreload(() => import("./EmbedViewer-
|
|
46537
|
+
reactExports.lazy(() => __vitePreload(() => import("./SettingsPage-D3bcPBsC.js"), true ? [] : void 0).then((m2) => ({ default: m2.SettingsPage })));
|
|
46538
|
+
reactExports.lazy(() => __vitePreload(() => import("./PricingPage-BP4lIGio.js"), true ? __vite__mapDeps([1,3]) : void 0).then((m2) => ({ default: m2.PricingPage })));
|
|
46539
|
+
reactExports.lazy(() => __vitePreload(() => import("./LegalPage-D5Z3CscF.js"), true ? __vite__mapDeps([1,4]) : void 0).then((m2) => ({ default: m2.LegalPage })));
|
|
46540
|
+
const EditorApp = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-CYBDvSyT.js"), true ? __vite__mapDeps([0]) : void 0).then((m2) => ({ default: m2.EditorApp })));
|
|
46541
|
+
const EmbedViewer = reactExports.lazy(() => __vitePreload(() => import("./EmbedViewer-Dmfu_LIw.js"), true ? [] : void 0).then((m2) => ({ default: m2.EmbedViewer })));
|
|
44589
46542
|
const embedMode = isEmbedMode() && !window.location.pathname.startsWith("/m/");
|
|
44590
46543
|
const EDITABLE_CRASH_FILE = /\.(?:forge\.js|[cm]?[jt]sx?|json|md|txt|svg|dxf)$/i;
|
|
44591
46544
|
function firstMeaningfulLine(text) {
|
|
@@ -44805,73 +46758,81 @@ function App() {
|
|
|
44805
46758
|
applyTheme(localStorage.getItem("fc-theme") || "dark");
|
|
44806
46759
|
clientExports.createRoot(document.getElementById("root")).render(/* @__PURE__ */ jsxRuntimeExports.jsx(App, {}));
|
|
44807
46760
|
export {
|
|
44808
|
-
|
|
46761
|
+
formatArea as $,
|
|
44809
46762
|
AuthApiError as A,
|
|
44810
46763
|
BrandMark as B,
|
|
44811
|
-
|
|
44812
|
-
|
|
44813
|
-
|
|
46764
|
+
sanitizeExportStem as C,
|
|
46765
|
+
captureViewportImageBlobFromStore as D,
|
|
46766
|
+
ESCAPE_PRIORITY as E,
|
|
44814
46767
|
FLAG_DEFINITIONS as F,
|
|
44815
|
-
|
|
44816
|
-
|
|
44817
|
-
|
|
44818
|
-
|
|
44819
|
-
|
|
44820
|
-
|
|
44821
|
-
|
|
44822
|
-
|
|
44823
|
-
|
|
44824
|
-
|
|
44825
|
-
|
|
44826
|
-
|
|
44827
|
-
|
|
44828
|
-
|
|
44829
|
-
|
|
44830
|
-
|
|
44831
|
-
|
|
44832
|
-
|
|
44833
|
-
|
|
44834
|
-
|
|
44835
|
-
|
|
46768
|
+
exportExactFromStore as G,
|
|
46769
|
+
storageQuotaUpgradeMessage as H,
|
|
46770
|
+
isImportableProjectMeshFile as I,
|
|
46771
|
+
isImportableProjectBinaryFile as J,
|
|
46772
|
+
hasExternalFiles as K,
|
|
46773
|
+
isImportableProjectExactFile as L,
|
|
46774
|
+
currentPath2DPoints as M,
|
|
46775
|
+
currentSpline2DPoints as N,
|
|
46776
|
+
currentPlacement2DItems as O,
|
|
46777
|
+
resolvePreviewFile as P,
|
|
46778
|
+
Path2DParamEditor as Q,
|
|
46779
|
+
Placement2DParamEditor as R,
|
|
46780
|
+
Spline2DParamEditor as S,
|
|
46781
|
+
countParamSnapshotDiff as T,
|
|
46782
|
+
buildProjectShareUrl as U,
|
|
46783
|
+
buildEmbedSnippet as V,
|
|
46784
|
+
publishProjectShare as W,
|
|
46785
|
+
unpublishProjectShare as X,
|
|
46786
|
+
formatComputeBackendLabel as Y,
|
|
46787
|
+
themes as Z,
|
|
46788
|
+
computeBackendFromParts as _,
|
|
44836
46789
|
applyTheme as a,
|
|
44837
|
-
|
|
44838
|
-
|
|
44839
|
-
|
|
44840
|
-
|
|
44841
|
-
|
|
44842
|
-
|
|
44843
|
-
|
|
44844
|
-
|
|
44845
|
-
|
|
44846
|
-
|
|
44847
|
-
|
|
44848
|
-
|
|
44849
|
-
|
|
44850
|
-
|
|
44851
|
-
|
|
44852
|
-
|
|
44853
|
-
|
|
44854
|
-
|
|
44855
|
-
|
|
44856
|
-
|
|
44857
|
-
|
|
44858
|
-
|
|
44859
|
-
|
|
44860
|
-
|
|
44861
|
-
|
|
44862
|
-
|
|
44863
|
-
|
|
44864
|
-
|
|
44865
|
-
|
|
44866
|
-
|
|
44867
|
-
|
|
44868
|
-
|
|
44869
|
-
|
|
44870
|
-
|
|
44871
|
-
|
|
44872
|
-
|
|
44873
|
-
|
|
44874
|
-
|
|
46790
|
+
sliderToAnimationSpeed as a0,
|
|
46791
|
+
animationSpeedToSlider as a1,
|
|
46792
|
+
formatAnimationSpeed as a2,
|
|
46793
|
+
resolveJointRange as a3,
|
|
46794
|
+
availableKernels as a4,
|
|
46795
|
+
useJointsConfig as a5,
|
|
46796
|
+
useJointAnimationValues as a6,
|
|
46797
|
+
VOXEL_INTENT_TOOL_ORDER as a7,
|
|
46798
|
+
VOXEL_INTENT_TOOL_LABELS as a8,
|
|
46799
|
+
VOXEL_INTENT_TOOL_COLORS as a9,
|
|
46800
|
+
decodeSharedHash as aA,
|
|
46801
|
+
decodeSharedBundle as aB,
|
|
46802
|
+
getExternalUrl as aC,
|
|
46803
|
+
getGistId as aD,
|
|
46804
|
+
Viewport as aE,
|
|
46805
|
+
shouldBlockBrowserShortcut as aF,
|
|
46806
|
+
useDrawStore as aG,
|
|
46807
|
+
storePendingShareCopy as aH,
|
|
46808
|
+
buildShareUrl as aI,
|
|
46809
|
+
share as aJ,
|
|
46810
|
+
VOXEL_INTENT_PLACEMENT_ORDER as aa,
|
|
46811
|
+
VOXEL_INTENT_PLACEMENT_LABELS as ab,
|
|
46812
|
+
highlightLanguageForProjectFile as ac,
|
|
46813
|
+
hasProjectTextFileExtension as ad,
|
|
46814
|
+
expandBoundsByTransformedAabb as ae,
|
|
46815
|
+
Canvas as af,
|
|
46816
|
+
ActiveCameraBridge as ag,
|
|
46817
|
+
PerspectiveCamera as ah,
|
|
46818
|
+
ControlsInteractionBridge as ai,
|
|
46819
|
+
SceneConfigurator as aj,
|
|
46820
|
+
LocalEnvironment as ak,
|
|
46821
|
+
ForgeObject as al,
|
|
46822
|
+
RenderLabelsOverlay as am,
|
|
46823
|
+
Grid as an,
|
|
46824
|
+
OrbitControls2 as ao,
|
|
46825
|
+
TOUCH_GESTURES_3D as ap,
|
|
46826
|
+
MOUSE_BUTTONS_3D as aq,
|
|
46827
|
+
ViewController as ar,
|
|
46828
|
+
ModelJourneyBar as as,
|
|
46829
|
+
useJointAnimationLoop as at,
|
|
46830
|
+
computeJointNodeMatrices as au,
|
|
46831
|
+
computeObjectJointMatrices as av,
|
|
46832
|
+
readLastActiveFileForUser as aw,
|
|
46833
|
+
ToastContainer as ax,
|
|
46834
|
+
isMobile as ay,
|
|
46835
|
+
useFeatureFlag as az,
|
|
44875
46836
|
authFetch as b,
|
|
44876
46837
|
authApi as c,
|
|
44877
46838
|
showToast as d,
|
|
@@ -44885,16 +46846,16 @@ export {
|
|
|
44885
46846
|
fetchGistModel as l,
|
|
44886
46847
|
monacoLanguageForProjectFile as m,
|
|
44887
46848
|
fetchUrlModel as n,
|
|
44888
|
-
|
|
44889
|
-
|
|
44890
|
-
|
|
46849
|
+
useEscapeAction as o,
|
|
46850
|
+
exportMeshFromStore as p,
|
|
46851
|
+
exportReportFromStore as q,
|
|
44891
46852
|
readProjectFilesFromDataTransfer as r,
|
|
44892
46853
|
storageUsagePercent as s,
|
|
44893
46854
|
triggerDownload as t,
|
|
44894
46855
|
useAuthStore as u,
|
|
44895
|
-
|
|
44896
|
-
|
|
44897
|
-
|
|
44898
|
-
|
|
44899
|
-
|
|
46856
|
+
exportViewportImageFromStore as v,
|
|
46857
|
+
exportOrbitVideoFromStore as w,
|
|
46858
|
+
exportSketchFromStore as x,
|
|
46859
|
+
buildGistShareUrl as y,
|
|
46860
|
+
deriveExportStem as z
|
|
44900
46861
|
};
|