forgecad 0.10.4 → 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-B3L3W1Uo.js → AdminPage-B1nIvqLS.js} +1 -1
- package/dist/assets/{BenchmarkPage-DXKVXMrJ.js → BenchmarkPage-YZJbw5nd.js} +2 -2
- package/dist/assets/{BlogPage-B7BWxOCg.js → BlogPage-DIWRApKS.js} +1 -1
- package/dist/assets/{DocsPage-BPGGwht1.js → DocsPage-ClL6X1hR.js} +8 -22
- package/dist/assets/EditorApp-CYBDvSyT.js +17067 -0
- package/dist/assets/{EmbedViewer-DygByZS2.js → EmbedViewer-Dmfu_LIw.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-BoVE7JGY.js → LandingPageProofDriven-XYTiYxfM.js} +2 -2
- package/dist/assets/{LegalPage-Din8wv8d.js → LegalPage-D5Z3CscF.js} +2 -2
- package/dist/assets/{PricingPage-C2PMzmDc.js → PricingPage-BP4lIGio.js} +2 -2
- package/dist/assets/{SettingsPage-BlJDCRe8.js → SettingsPage-D3bcPBsC.js} +1 -1
- package/dist/assets/{app-BsRYSfxY.js → app-BKjogwIZ.js} +3288 -512
- package/dist/assets/{backendInit-6C0DLgH0.js → backendInit-6a9-ilom.js} +80498 -74979
- package/dist/assets/cli/{render-XXol_ET7.js → render-CMNudGb0.js} +1264 -113
- package/dist/assets/{constructionHistoryWorker-cTHWRJEi.js → constructionHistoryWorker-BuZgc606.js} +8369 -6839
- package/dist/assets/{evalWorker-BssDYW9u.js → evalWorker-DQ82ueGu.js} +45438 -39996
- package/dist/assets/{forgecad_geometry-CZ_IfuvA.js → forgecad_geometry-D8rWX7nQ.js} +1 -1
- package/dist/assets/{forgecad_geometry_bg-C3rQHfwg.wasm → forgecad_geometry_bg-ObqfqjJT.wasm} +0 -0
- package/dist/assets/{inspectWorker-ymhBV4Ll.js → inspectWorker-Cuby2qfT.js} +4899 -1303
- package/dist/assets/{jointPose-B0blBj9A.js → jointPose-CFql5I-u.js} +1 -1
- package/dist/assets/{landing-proof-driven-Cpf-MIbI.css → landing-proof-driven-_u4v_xQb.css} +2 -2
- package/dist/assets/{manifold-CYlIm-M6.js → manifold-02pmr7O7.js} +2 -2
- package/dist/assets/{manifold-B_7QXpGB.js → manifold-C6KU0oII.js} +1 -1
- package/dist/assets/{manifold-CNShmpEJ.js → manifold-P1yF3GKn.js} +1 -1
- package/dist/assets/{reportWorker-Cb5eyM7D.js → reportWorker-kg065BVL.js} +76583 -65731
- 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 +14 -12
- package/dist/docs-raw/component-model.md +28 -9
- package/dist/docs-raw/generated/assembly.md +76 -3
- package/dist/docs-raw/generated/concepts.md +43 -7
- package/dist/docs-raw/generated/core.md +399 -73
- package/dist/docs-raw/generated/curves.md +357 -6
- package/dist/docs-raw/generated/runtime-names.md +12 -12
- package/dist/docs-raw/generated/sketch.md +16 -3
- package/dist/docs-raw/guides/inspection-bundles.md +5 -3
- package/dist/docs-raw/guides/structural-fea.md +235 -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 +4 -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-4RPB6SB5.js → check-compiler-UJWUEIDC.js} +1 -1
- package/dist-cli/{check-query-propagation-KN3DFQTX.js → check-query-propagation-O2EPDJSY.js} +1 -1
- package/dist-cli/{chunk-UHBRMYA6.js → chunk-MNDROM7T.js} +78926 -73392
- package/dist-cli/forgecad.js +6306 -1061
- package/dist-cli/forgecad_geometry_bg.wasm +0 -0
- package/dist-skill/CONTEXT.md +1257 -110
- package/dist-skill/SKILL.md +4 -1
- package/dist-skill/docs/API/core/concepts.md +31 -4
- package/dist-skill/docs/CLI.md +14 -12
- package/dist-skill/docs/generated/assembly.md +73 -3
- package/dist-skill/docs/generated/core.md +395 -74
- package/dist-skill/docs/generated/curves.md +356 -6
- package/dist-skill/docs/generated/runtime-names.md +12 -12
- package/dist-skill/docs/generated/sketch.md +16 -3
- package/dist-skill/docs/guides/inspection-bundles.md +5 -3
- package/dist-skill/docs/guides/manual-parameters.md +130 -0
- package/dist-skill/docs/guides/structural-fea.md +235 -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 +4 -1
- package/dist-skill/website/skills/index.md +1 -5
- package/examples/analysis/structural-stress-fea.forge.js +19 -0
- package/examples/api/blend-full-round.forge.js +37 -0
- package/examples/api/blend-variable-radius.forge.js +51 -0
- package/examples/api/curve-project-and-intersect.forge.js +59 -0
- package/examples/api/extrude-up-to-face.forge.js +47 -0
- 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 +188 -0
- package/examples/api/surface-boundarynet-dished-bowl.forge.js +63 -0
- package/examples/api/surface-fill-interior-constraints.forge.js +59 -0
- package/examples/api/surface-variable-thickness-panel.forge.js +62 -0
- package/examples/mechanical/airplane-propeller.forge.js +81 -28
- package/package.json +5 -2
- package/dist/assets/EditorApp-BWUGCdD5.js +0 -16610
- 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/assets/{landing-proof-driven-BxZZh5r5.js → landing-proof-driven-DNPRKL_p.js} +0 -0
- /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
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/EditorApp-C5f24ZN9.css","assets/landing-proof-driven-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/EditorApp-C5f24ZN9.css","assets/landing-proof-driven-_u4v_xQb.css","assets/BenchmarkPage-BAbsyMaF.css","assets/PricingPage-BPF6HKyO.css","assets/LegalPage-BRlScr9A.css"])))=>i.map(i=>d[i]);
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
var _a2;
|
|
6
6
|
import { c as create, j as jsxRuntimeExports, r as reactExports, a as createWithEqualityFn, R as React, T as Tb, s as schedulerExports, b as clientExports, d as reactDomExports, u as useParams, e as useSearchParams, f as useNavigate, L as Link, g as useLocation, B as BrowserRouter, h as Routes, i as Route, N as Navigate } from "./vendor-react-6j1Kke-Y.js";
|
|
7
|
-
import { _ as __vitePreload, 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
|
|
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(() => {
|
|
@@ -1551,7 +1551,7 @@ const is = {
|
|
|
1551
1551
|
return true;
|
|
1552
1552
|
}
|
|
1553
1553
|
};
|
|
1554
|
-
function buildGraph(object) {
|
|
1554
|
+
function buildGraph$1(object) {
|
|
1555
1555
|
const data = {
|
|
1556
1556
|
nodes: {},
|
|
1557
1557
|
materials: {},
|
|
@@ -2359,7 +2359,7 @@ function loadingFn(extensions, onProgress) {
|
|
|
2359
2359
|
}
|
|
2360
2360
|
if (extensions) extensions(loader);
|
|
2361
2361
|
return Promise.all(input.map((input2) => new Promise((res, reject) => loader.load(input2, (data) => {
|
|
2362
|
-
if (isObject3D(data == null ? void 0 : data.scene)) Object.assign(data, buildGraph(data.scene));
|
|
2362
|
+
if (isObject3D(data == null ? void 0 : data.scene)) Object.assign(data, buildGraph$1(data.scene));
|
|
2363
2363
|
res(data);
|
|
2364
2364
|
}, onProgress, (error) => reject(new Error(`Could not load ${input2}: ${error == null ? void 0 : error.message}`))))));
|
|
2365
2365
|
};
|
|
@@ -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: [],
|
|
@@ -17636,6 +17653,7 @@ const VIEW_INSPECT_CHANNELS = /* @__PURE__ */ new Set([
|
|
|
17636
17653
|
"distance",
|
|
17637
17654
|
"collisions",
|
|
17638
17655
|
"thickness",
|
|
17656
|
+
"throughThickness",
|
|
17639
17657
|
"roughness"
|
|
17640
17658
|
]);
|
|
17641
17659
|
const INSPECT_DISPLAY_MODES = /* @__PURE__ */ new Set(["heatmap", "points", "both", "scan"]);
|
|
@@ -17658,7 +17676,7 @@ function resolveInspectDisplayMode(value) {
|
|
|
17658
17676
|
return typeof value === "string" && INSPECT_DISPLAY_MODES.has(value) ? value : "heatmap";
|
|
17659
17677
|
}
|
|
17660
17678
|
function isScalarInspectChannel(channel) {
|
|
17661
|
-
return channel === "thickness" || channel === "roughness";
|
|
17679
|
+
return channel === "thickness" || channel === "throughThickness" || channel === "roughness";
|
|
17662
17680
|
}
|
|
17663
17681
|
function shouldUseScanRenderStyle(channel, displayMode) {
|
|
17664
17682
|
return isScalarInspectChannel(channel) && displayMode === "scan";
|
|
@@ -17714,7 +17732,8 @@ function codeEditorPatchForActiveFile(activeFile, files, meshPreviewFile) {
|
|
|
17714
17732
|
}
|
|
17715
17733
|
function runResultHasViewportContent(result) {
|
|
17716
17734
|
var _a3, _b2, _c, _d;
|
|
17717
|
-
|
|
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;
|
|
17718
17737
|
}
|
|
17719
17738
|
function codeEditorPatchForRunResult(result, meshPreviewFile) {
|
|
17720
17739
|
if (meshPreviewFile || runResultHasViewportContent(result)) return {};
|
|
@@ -17748,7 +17767,7 @@ const makeParamSnapshotId = () => {
|
|
|
17748
17767
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
17749
17768
|
return `param-snapshot-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
17750
17769
|
};
|
|
17751
|
-
function filterOverridesForCurrentParams(overrides, params, stringParams, listParams) {
|
|
17770
|
+
function filterOverridesForCurrentParams(overrides, params, stringParams, listParams, path2dParams, spline2dParams, placement2dParams) {
|
|
17752
17771
|
const scalarNames = /* @__PURE__ */ new Set([...params.map((param) => param.name), ...stringParams.map((param) => param.name)]);
|
|
17753
17772
|
const filtered = {};
|
|
17754
17773
|
let ignoredCount = 0;
|
|
@@ -17758,17 +17777,51 @@ function filterOverridesForCurrentParams(overrides, params, stringParams, listPa
|
|
|
17758
17777
|
continue;
|
|
17759
17778
|
}
|
|
17760
17779
|
const listDef = listParams.find((candidate) => key === `${candidate.name}.__count__` || key.startsWith(`${candidate.name}[`));
|
|
17761
|
-
if (
|
|
17762
|
-
|
|
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;
|
|
17763
17788
|
continue;
|
|
17764
17789
|
}
|
|
17765
|
-
|
|
17766
|
-
|
|
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
|
+
}
|
|
17767
17822
|
continue;
|
|
17768
17823
|
}
|
|
17769
|
-
|
|
17770
|
-
if (listDef.fieldOrder.includes(fieldName)) filtered[key] = value;
|
|
17771
|
-
else ignoredCount += 1;
|
|
17824
|
+
ignoredCount += 1;
|
|
17772
17825
|
}
|
|
17773
17826
|
return { overrides: filtered, ignoredCount };
|
|
17774
17827
|
}
|
|
@@ -18185,6 +18238,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18185
18238
|
params: [],
|
|
18186
18239
|
stringParams: [],
|
|
18187
18240
|
listParams: [],
|
|
18241
|
+
path2dParams: [],
|
|
18242
|
+
spline2dParams: [],
|
|
18243
|
+
placement2dParams: [],
|
|
18188
18244
|
runQuality: resolveForgeQualityPreset(initialViewPreferences.runQuality ?? "live"),
|
|
18189
18245
|
setRunQuality: (quality) => {
|
|
18190
18246
|
const next = resolveForgeQualityPreset(quality);
|
|
@@ -18195,6 +18251,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18195
18251
|
paramOverrides: {},
|
|
18196
18252
|
paramOverridesByFile: {},
|
|
18197
18253
|
paramSnapshotsByFile: initialParamSnapshotsByFile,
|
|
18254
|
+
focusedParamName: null,
|
|
18255
|
+
spatialParamSheetName: null,
|
|
18256
|
+
expandedSpatialParamSheetName: null,
|
|
18198
18257
|
jointValues: {},
|
|
18199
18258
|
jointAnimationClip: null,
|
|
18200
18259
|
jointAnimationProgress: 0,
|
|
@@ -18233,8 +18292,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18233
18292
|
},
|
|
18234
18293
|
computeTarget: initialKernel.location,
|
|
18235
18294
|
setComputeTarget: (target) => {
|
|
18295
|
+
const preferredServerBackend = get().activeBackend === "sdf" ? "sdf" : "occt";
|
|
18236
18296
|
const kernel = resolveAvailableKernel(
|
|
18237
|
-
{ name: target === "server" ?
|
|
18297
|
+
{ name: target === "server" ? preferredServerBackend : get().activeBackend, location: target },
|
|
18238
18298
|
availableKernels(canUseServerCompute())
|
|
18239
18299
|
);
|
|
18240
18300
|
writeViewPreferences({ activeBackend: kernel.name, computeTarget: kernel.location });
|
|
@@ -18292,6 +18352,9 @@ const useForgeStore = create((set, get) => ({
|
|
|
18292
18352
|
params: [],
|
|
18293
18353
|
stringParams: [],
|
|
18294
18354
|
listParams: [],
|
|
18355
|
+
path2dParams: [],
|
|
18356
|
+
spline2dParams: [],
|
|
18357
|
+
placement2dParams: [],
|
|
18295
18358
|
previewFile: null,
|
|
18296
18359
|
objectSettings: {},
|
|
18297
18360
|
buildLedgerEvents: []
|
|
@@ -18345,14 +18408,15 @@ const useForgeStore = create((set, get) => ({
|
|
|
18345
18408
|
serverComputeAbortController = abortController;
|
|
18346
18409
|
try {
|
|
18347
18410
|
const irRequest = await evalWorkerClient.runIrPlan(runPayload);
|
|
18348
|
-
if (irRequest.planResult.error || !
|
|
18411
|
+
if (irRequest.planResult.error || !serializedRunResultNeedsServerIrLowering(irRequest.planResult)) {
|
|
18349
18412
|
serialized = irRequest.planResult;
|
|
18350
18413
|
} else {
|
|
18351
18414
|
const refreshTimer2 = window.setTimeout(() => {
|
|
18352
18415
|
void get().refreshServerJobs();
|
|
18353
18416
|
}, 250);
|
|
18354
18417
|
try {
|
|
18355
|
-
|
|
18418
|
+
const serverBackend = get().activeBackend === "sdf" ? "sdf" : "occt";
|
|
18419
|
+
serialized = await computeIr({ ...irRequest, serverBackend }, abortController.signal);
|
|
18356
18420
|
} finally {
|
|
18357
18421
|
window.clearTimeout(refreshTimer2);
|
|
18358
18422
|
void get().refreshServerJobs();
|
|
@@ -18493,6 +18557,19 @@ const useForgeStore = create((set, get) => ({
|
|
|
18493
18557
|
get().execute();
|
|
18494
18558
|
}, PARAM_DEBOUNCE_MS);
|
|
18495
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
|
+
},
|
|
18496
18573
|
resetParamOverrides: () => {
|
|
18497
18574
|
const { activeFile, files, paramOverridesByFile } = get();
|
|
18498
18575
|
const previewKey = activeFile ? resolvePreviewFile(activeFile, files) : null;
|
|
@@ -18502,6 +18579,11 @@ const useForgeStore = create((set, get) => ({
|
|
|
18502
18579
|
setParamOverrides({});
|
|
18503
18580
|
get().execute();
|
|
18504
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 }),
|
|
18505
18587
|
captureParamSnapshot: () => {
|
|
18506
18588
|
var _a3;
|
|
18507
18589
|
const { activeFile, files, paramOverrides, paramSnapshotsByFile } = get();
|
|
@@ -18521,11 +18603,30 @@ const useForgeStore = create((set, get) => ({
|
|
|
18521
18603
|
},
|
|
18522
18604
|
applyParamSnapshot: (id) => {
|
|
18523
18605
|
var _a3;
|
|
18524
|
-
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();
|
|
18525
18618
|
const previewKey = activeFile ? resolvePreviewFile(activeFile, files) : null;
|
|
18526
18619
|
const snapshot = previewKey ? (_a3 = paramSnapshotsByFile[previewKey]) == null ? void 0 : _a3.find((candidate) => candidate.id === id) : null;
|
|
18527
18620
|
if (!previewKey || !snapshot) return;
|
|
18528
|
-
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
|
+
);
|
|
18529
18630
|
const nextByFile = { ...paramOverridesByFile };
|
|
18530
18631
|
if (Object.keys(overrides).length > 0) nextByFile[previewKey] = overrides;
|
|
18531
18632
|
else delete nextByFile[previewKey];
|
|
@@ -18978,7 +19079,17 @@ const useForgeStore = create((set, get) => ({
|
|
|
18978
19079
|
};
|
|
18979
19080
|
}),
|
|
18980
19081
|
selectedObjectId: null,
|
|
18981
|
-
selectObject: (id) => set(
|
|
19082
|
+
selectObject: (id) => set((state2) => ({
|
|
19083
|
+
selectedObjectId: id,
|
|
19084
|
+
constructionGhost: null,
|
|
19085
|
+
selectedConstraintId: null,
|
|
19086
|
+
// Face selection is a sub-selection of an object; drop it when the active
|
|
19087
|
+
// object changes (the click handler re-populates it for the new object).
|
|
19088
|
+
selectedFace: state2.selectedFace && state2.selectedFace.objectId === id ? state2.selectedFace : null,
|
|
19089
|
+
// Edge/vertex sub-selections are likewise scoped to the active object.
|
|
19090
|
+
selectedEdge: state2.selectedEdge && state2.selectedEdge.objectId === id ? state2.selectedEdge : null,
|
|
19091
|
+
selectedVertex: state2.selectedVertex && state2.selectedVertex.objectId === id ? state2.selectedVertex : null
|
|
19092
|
+
})),
|
|
18982
19093
|
constructionGhost: null,
|
|
18983
19094
|
setConstructionGhost: (ghost) => set({ constructionGhost: ghost }),
|
|
18984
19095
|
// ── Construction history replay ──
|
|
@@ -19177,6 +19288,30 @@ const useForgeStore = create((set, get) => ({
|
|
|
19177
19288
|
setHoveredSurfaceIndex: (index) => set((state2) => state2.hoveredSurfaceIndex === index ? state2 : { hoveredSurfaceIndex: index }),
|
|
19178
19289
|
selectedSurfaceIndex: null,
|
|
19179
19290
|
setSelectedSurfaceIndex: (index) => set((state2) => state2.selectedSurfaceIndex === index ? { selectedSurfaceIndex: null } : { selectedSurfaceIndex: index }),
|
|
19291
|
+
selectedFace: null,
|
|
19292
|
+
setSelectedFace: (face) => set((state2) => {
|
|
19293
|
+
const prev = state2.selectedFace;
|
|
19294
|
+
if (face && prev && prev.objectId === face.objectId && prev.carrierName !== null && prev.carrierName === face.carrierName) {
|
|
19295
|
+
return { selectedFace: null };
|
|
19296
|
+
}
|
|
19297
|
+
return { selectedFace: face };
|
|
19298
|
+
}),
|
|
19299
|
+
selectedEdge: null,
|
|
19300
|
+
setSelectedEdge: (edge) => set((state2) => {
|
|
19301
|
+
const prev = state2.selectedEdge;
|
|
19302
|
+
if (edge && prev && prev.objectId === edge.objectId && prev.faceA === edge.faceA && prev.faceB === edge.faceB) {
|
|
19303
|
+
return { selectedEdge: null };
|
|
19304
|
+
}
|
|
19305
|
+
return { selectedEdge: edge };
|
|
19306
|
+
}),
|
|
19307
|
+
selectedVertex: null,
|
|
19308
|
+
setSelectedVertex: (vertex) => set((state2) => {
|
|
19309
|
+
const prev = state2.selectedVertex;
|
|
19310
|
+
if (vertex && prev && prev.objectId === vertex.objectId && prev.point[0] === vertex.point[0] && prev.point[1] === vertex.point[1] && prev.point[2] === vertex.point[2]) {
|
|
19311
|
+
return { selectedVertex: null };
|
|
19312
|
+
}
|
|
19313
|
+
return { selectedVertex: vertex };
|
|
19314
|
+
}),
|
|
19180
19315
|
selectedSketchEntityId: null,
|
|
19181
19316
|
setSelectedSketchEntityId: (id) => set((state2) => state2.selectedSketchEntityId === id ? { selectedSketchEntityId: null } : { selectedSketchEntityId: id }),
|
|
19182
19317
|
hoveredJointName: null,
|
|
@@ -19214,6 +19349,13 @@ const useForgeStore = create((set, get) => ({
|
|
|
19214
19349
|
});
|
|
19215
19350
|
},
|
|
19216
19351
|
clearMeasureSelections: () => set({ measureSelections: [] }),
|
|
19352
|
+
replaceLastMeasureSelection: (entity) => {
|
|
19353
|
+
set((s) => {
|
|
19354
|
+
const sels = s.measureSelections;
|
|
19355
|
+
if (sels.length === 0) return { measureSelections: [entity] };
|
|
19356
|
+
return { measureSelections: [...sels.slice(0, -1), entity] };
|
|
19357
|
+
});
|
|
19358
|
+
},
|
|
19217
19359
|
measurements: [],
|
|
19218
19360
|
addMeasurePoint: (pt2) => {
|
|
19219
19361
|
const measurements = get().measurements;
|
|
@@ -19309,6 +19451,12 @@ const useForgeStore = create((set, get) => ({
|
|
|
19309
19451
|
writeViewPreferences({ dimensionsVisible: nextDimensionsVisible });
|
|
19310
19452
|
return { dimensionsVisible: nextDimensionsVisible };
|
|
19311
19453
|
}),
|
|
19454
|
+
paramAnchorsVisible: initialViewPreferences.paramAnchorsVisible ?? true,
|
|
19455
|
+
toggleParamAnchors: () => set((s) => {
|
|
19456
|
+
const nextParamAnchorsVisible = !s.paramAnchorsVisible;
|
|
19457
|
+
writeViewPreferences({ paramAnchorsVisible: nextParamAnchorsVisible });
|
|
19458
|
+
return { paramAnchorsVisible: nextParamAnchorsVisible };
|
|
19459
|
+
}),
|
|
19312
19460
|
attachmentsVisible: initialViewPreferences.attachmentsVisible ?? "none",
|
|
19313
19461
|
setAttachmentsVisible: (mode) => {
|
|
19314
19462
|
writeViewPreferences({ attachmentsVisible: mode });
|
|
@@ -19486,11 +19634,11 @@ const useForgeStore = create((set, get) => ({
|
|
|
19486
19634
|
fileSystem.save(normalized, text).catch((e2) => console.error("Save failed:", e2));
|
|
19487
19635
|
setTimeout(() => get().execute(), 0);
|
|
19488
19636
|
},
|
|
19489
|
-
importTextFiles: async (
|
|
19637
|
+
importTextFiles: async (entries2, options = {}) => {
|
|
19490
19638
|
const { files, folders, activeFile } = get();
|
|
19491
19639
|
const occupied = /* @__PURE__ */ new Set([...Object.keys(files), ...folders]);
|
|
19492
19640
|
const imported = [];
|
|
19493
|
-
for (const entry of
|
|
19641
|
+
for (const entry of entries2) {
|
|
19494
19642
|
const targetPath = resolveImportedProjectPath(entry.path, options.targetFolder);
|
|
19495
19643
|
if (!targetPath) continue;
|
|
19496
19644
|
const path = uniquifyProjectPath(targetPath, occupied);
|
|
@@ -19729,8 +19877,9 @@ onServerAvailabilityChange((available) => {
|
|
|
19729
19877
|
useForgeStore.setState({ serverAvailable: available });
|
|
19730
19878
|
});
|
|
19731
19879
|
const savedComputeTarget = initialViewPreferences.computeTarget;
|
|
19880
|
+
const savedServerBackend = initialViewPreferences.activeBackend === "sdf" ? "sdf" : "occt";
|
|
19732
19881
|
if (savedComputeTarget === "server" && canUseServerCompute()) {
|
|
19733
|
-
useForgeStore.setState({ activeBackend:
|
|
19882
|
+
useForgeStore.setState({ activeBackend: savedServerBackend, computeTarget: "server" });
|
|
19734
19883
|
startServerPolling();
|
|
19735
19884
|
}
|
|
19736
19885
|
{
|
|
@@ -22930,6 +23079,51 @@ function DroneCrosshairPicker({
|
|
|
22930
23079
|
}, [active, focusHitOrClear, gl.domElement, hideLabel, showHitInfo]);
|
|
22931
23080
|
return null;
|
|
22932
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
|
+
}
|
|
22933
23127
|
const DEFAULT_DRONE_CAMERA_STATUS = {
|
|
22934
23128
|
speed: 0,
|
|
22935
23129
|
pointerLocked: false,
|
|
@@ -23028,6 +23222,13 @@ function DroneCameraController({
|
|
|
23028
23222
|
const pointerLockedRef = reactExports.useRef(false);
|
|
23029
23223
|
const lastStatusRef = reactExports.useRef(DEFAULT_DRONE_CAMERA_STATUS);
|
|
23030
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 });
|
|
23031
23232
|
const publishStatus = reactExports.useCallback(
|
|
23032
23233
|
(patch) => {
|
|
23033
23234
|
const next = { ...lastStatusRef.current, ...patch };
|
|
@@ -23143,11 +23344,6 @@ function DroneCameraController({
|
|
|
23143
23344
|
if (handledKeyboardEventsRef.current.has(event)) return;
|
|
23144
23345
|
handledKeyboardEventsRef.current.add(event);
|
|
23145
23346
|
if (event.key === "Escape") {
|
|
23146
|
-
consumeFlightKey(event);
|
|
23147
|
-
if (onEscape == null ? void 0 : onEscape()) {
|
|
23148
|
-
return;
|
|
23149
|
-
}
|
|
23150
|
-
onExit();
|
|
23151
23347
|
return;
|
|
23152
23348
|
}
|
|
23153
23349
|
const code = flightCodeFromEvent(event);
|
|
@@ -23251,19 +23447,7 @@ function DroneCameraController({
|
|
|
23251
23447
|
publishStatus(DEFAULT_DRONE_CAMERA_STATUS);
|
|
23252
23448
|
recordDroneDebug({ active: false, keys: [], lastSpeed: 0, pointerLocked: false });
|
|
23253
23449
|
};
|
|
23254
|
-
}, [
|
|
23255
|
-
active,
|
|
23256
|
-
applyLook,
|
|
23257
|
-
camera,
|
|
23258
|
-
controlsRef,
|
|
23259
|
-
gl.domElement,
|
|
23260
|
-
onEscape,
|
|
23261
|
-
onExit,
|
|
23262
|
-
onInteractionChange,
|
|
23263
|
-
publishStatus,
|
|
23264
|
-
rotateByMouse,
|
|
23265
|
-
stepFlight
|
|
23266
|
-
]);
|
|
23450
|
+
}, [active, applyLook, camera, controlsRef, gl.domElement, onInteractionChange, publishStatus, rotateByMouse, stepFlight]);
|
|
23267
23451
|
return null;
|
|
23268
23452
|
}
|
|
23269
23453
|
const panelStyle$1 = {
|
|
@@ -23499,7 +23683,7 @@ function moveOrbitTargetToPoint(camera, controls, target) {
|
|
|
23499
23683
|
}
|
|
23500
23684
|
const MIDDLE_BUTTON = 1;
|
|
23501
23685
|
const DOUBLE_CLICK_MAX_MS = 500;
|
|
23502
|
-
const CLICK_DRAG_TOLERANCE_PX = 5;
|
|
23686
|
+
const CLICK_DRAG_TOLERANCE_PX$1 = 5;
|
|
23503
23687
|
const DOUBLE_CLICK_DISTANCE_TOLERANCE_PX = 8;
|
|
23504
23688
|
function distanceSq(a2, b2) {
|
|
23505
23689
|
return (a2.clientX - b2.clientX) ** 2 + (a2.clientY - b2.clientY) ** 2;
|
|
@@ -23535,7 +23719,7 @@ function OrbitTargetPicker({ active, controlsRef }) {
|
|
|
23535
23719
|
previousClickRef.current = null;
|
|
23536
23720
|
if (!active) return;
|
|
23537
23721
|
const element = gl.domElement;
|
|
23538
|
-
const dragToleranceSq = CLICK_DRAG_TOLERANCE_PX ** 2;
|
|
23722
|
+
const dragToleranceSq = CLICK_DRAG_TOLERANCE_PX$1 ** 2;
|
|
23539
23723
|
const doubleDistanceToleranceSq = DOUBLE_CLICK_DISTANCE_TOLERANCE_PX ** 2;
|
|
23540
23724
|
const onPointerDown = (event) => {
|
|
23541
23725
|
if (event.button !== MIDDLE_BUTTON) return;
|
|
@@ -23700,7 +23884,7 @@ function OrbitTargetPulseLayer() {
|
|
|
23700
23884
|
class ConstructionHistoryWorkerClient {
|
|
23701
23885
|
constructor(workerFactory = () => new Worker(new URL(
|
|
23702
23886
|
/* @vite-ignore */
|
|
23703
|
-
"/assets/constructionHistoryWorker-
|
|
23887
|
+
"/assets/constructionHistoryWorker-BuZgc606.js",
|
|
23704
23888
|
import.meta.url
|
|
23705
23889
|
), { type: "module" })) {
|
|
23706
23890
|
__publicField(this, "worker", null);
|
|
@@ -26212,7 +26396,7 @@ function generateReportInWorker(options) {
|
|
|
26212
26396
|
return new Promise((resolve2, reject) => {
|
|
26213
26397
|
const worker = new Worker(new URL(
|
|
26214
26398
|
/* @vite-ignore */
|
|
26215
|
-
"/assets/reportWorker-
|
|
26399
|
+
"/assets/reportWorker-kg065BVL.js",
|
|
26216
26400
|
import.meta.url
|
|
26217
26401
|
), { type: "module" });
|
|
26218
26402
|
const cleanup = () => {
|
|
@@ -27565,6 +27749,14 @@ function AnimationBar() {
|
|
|
27565
27749
|
const [historySpeedInput, setHistorySpeedInput] = reactExports.useState(() => formatHistorySpeedInput(historySpeed));
|
|
27566
27750
|
const [editingHistorySpeed, setEditingHistorySpeed] = reactExports.useState(false);
|
|
27567
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
|
+
);
|
|
27568
27760
|
const elapsed = useElapsedTime(recording);
|
|
27569
27761
|
reactExports.useEffect(() => {
|
|
27570
27762
|
if (!editingHistorySpeed) {
|
|
@@ -27574,12 +27766,6 @@ function AnimationBar() {
|
|
|
27574
27766
|
const handleKeyDown = reactExports.useCallback(
|
|
27575
27767
|
(e2) => {
|
|
27576
27768
|
if (!animationMode) return;
|
|
27577
|
-
if (e2.key === "Escape") {
|
|
27578
|
-
e2.preventDefault();
|
|
27579
|
-
if (recording) return;
|
|
27580
|
-
exit();
|
|
27581
|
-
return;
|
|
27582
|
-
}
|
|
27583
27769
|
if (animationMode === "construction") {
|
|
27584
27770
|
switch (e2.key) {
|
|
27585
27771
|
case " ":
|
|
@@ -27597,7 +27783,7 @@ function AnimationBar() {
|
|
|
27597
27783
|
}
|
|
27598
27784
|
}
|
|
27599
27785
|
},
|
|
27600
|
-
[animationMode, recording, historyCurrentStep, toggleHistoryPlayback, setHistoryStep
|
|
27786
|
+
[animationMode, recording, historyCurrentStep, toggleHistoryPlayback, setHistoryStep]
|
|
27601
27787
|
);
|
|
27602
27788
|
reactExports.useEffect(() => {
|
|
27603
27789
|
window.addEventListener("keydown", handleKeyDown);
|
|
@@ -28497,20 +28683,13 @@ function TrajectoryTimeline() {
|
|
|
28497
28683
|
const trajectoryDuration = useForgeStore((s) => s.trajectoryDuration);
|
|
28498
28684
|
const exitTrajectoryMode = useForgeStore((s) => s.exitTrajectoryMode);
|
|
28499
28685
|
const setTrajectoryPresetParams = useForgeStore((s) => s.setTrajectoryPresetParams);
|
|
28500
|
-
|
|
28501
|
-
(
|
|
28502
|
-
|
|
28503
|
-
|
|
28504
|
-
e2.preventDefault();
|
|
28505
|
-
exitTrajectoryMode();
|
|
28506
|
-
}
|
|
28686
|
+
useEscapeAction(
|
|
28687
|
+
() => {
|
|
28688
|
+
exitTrajectoryMode();
|
|
28689
|
+
return true;
|
|
28507
28690
|
},
|
|
28508
|
-
|
|
28691
|
+
{ active: Boolean(trajectoryMode), label: "Trajectory mode", priority: ESCAPE_PRIORITY.viewport + 4 }
|
|
28509
28692
|
);
|
|
28510
|
-
reactExports.useEffect(() => {
|
|
28511
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
28512
|
-
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
28513
|
-
}, [handleKeyDown]);
|
|
28514
28693
|
if (!trajectoryMode) return null;
|
|
28515
28694
|
const isActive = trajectoryRecording || trajectoryPreviewing;
|
|
28516
28695
|
const presetConfig = TRAJECTORY_PRESETS.find((p2) => p2.id === trajectoryPreset);
|
|
@@ -29671,7 +29850,7 @@ const PHASE_CONFIG = {
|
|
|
29671
29850
|
const PHASE_ORDER = ["kernel-init", "evaluating", "serializing"];
|
|
29672
29851
|
function formatEvaluationBackendLabel(activeBackend, computeTarget) {
|
|
29673
29852
|
const backend = activeBackend === "occt" ? "OCCT" : activeBackend === "manifold" ? "Manifold" : activeBackend === "truck" ? "Truck" : activeBackend === "sdf" ? "SDF" : "kernel";
|
|
29674
|
-
return computeTarget === "server" ?
|
|
29853
|
+
return computeTarget === "server" ? `Server ${backend}` : `Local ${backend}`;
|
|
29675
29854
|
}
|
|
29676
29855
|
function EvaluationIndicator({
|
|
29677
29856
|
phase,
|
|
@@ -29980,6 +30159,185 @@ function LedgerMetric({ label, value }) {
|
|
|
29980
30159
|
}
|
|
29981
30160
|
);
|
|
29982
30161
|
}
|
|
30162
|
+
const QUANT = 1e4;
|
|
30163
|
+
const q = (v) => Math.round(v * QUANT);
|
|
30164
|
+
const vertKey = (pos, i) => `${q(pos.getX(i))},${q(pos.getY(i))},${q(pos.getZ(i))}`;
|
|
30165
|
+
const edgeKey = (a2, b2) => a2 < b2 ? `${a2}|${b2}` : `${b2}|${a2}`;
|
|
30166
|
+
function buildEdgeAdjacency(positions, triCount) {
|
|
30167
|
+
const edgeToTris = /* @__PURE__ */ new Map();
|
|
30168
|
+
for (let t2 = 0; t2 < triCount; t2++) {
|
|
30169
|
+
const base = t2 * 3;
|
|
30170
|
+
const v0 = vertKey(positions, base);
|
|
30171
|
+
const v12 = vertKey(positions, base + 1);
|
|
30172
|
+
const v22 = vertKey(positions, base + 2);
|
|
30173
|
+
for (const ek of [edgeKey(v0, v12), edgeKey(v12, v22), edgeKey(v22, v0)]) {
|
|
30174
|
+
let list = edgeToTris.get(ek);
|
|
30175
|
+
if (!list) {
|
|
30176
|
+
list = [];
|
|
30177
|
+
edgeToTris.set(ek, list);
|
|
30178
|
+
}
|
|
30179
|
+
list.push(t2);
|
|
30180
|
+
}
|
|
30181
|
+
}
|
|
30182
|
+
return edgeToTris;
|
|
30183
|
+
}
|
|
30184
|
+
function floodComponent(positions, edgeToTris, startTriIndex, accept) {
|
|
30185
|
+
const visited = /* @__PURE__ */ new Set();
|
|
30186
|
+
const queue = [startTriIndex];
|
|
30187
|
+
visited.add(startTriIndex);
|
|
30188
|
+
while (queue.length > 0) {
|
|
30189
|
+
const t2 = queue.pop();
|
|
30190
|
+
const base = t2 * 3;
|
|
30191
|
+
const v0 = vertKey(positions, base);
|
|
30192
|
+
const v12 = vertKey(positions, base + 1);
|
|
30193
|
+
const v22 = vertKey(positions, base + 2);
|
|
30194
|
+
for (const ek of [edgeKey(v0, v12), edgeKey(v12, v22), edgeKey(v22, v0)]) {
|
|
30195
|
+
const neighbors = edgeToTris.get(ek);
|
|
30196
|
+
if (!neighbors) continue;
|
|
30197
|
+
for (const n of neighbors) {
|
|
30198
|
+
if (visited.has(n)) continue;
|
|
30199
|
+
if (!accept(n)) continue;
|
|
30200
|
+
visited.add(n);
|
|
30201
|
+
queue.push(n);
|
|
30202
|
+
}
|
|
30203
|
+
}
|
|
30204
|
+
}
|
|
30205
|
+
return Array.from(visited);
|
|
30206
|
+
}
|
|
30207
|
+
function summarizeRegion(positions, normals, indices) {
|
|
30208
|
+
let totalArea = 0;
|
|
30209
|
+
const centroid = new Vector3();
|
|
30210
|
+
const weightedNormal = new Vector3();
|
|
30211
|
+
const tmpA = new Vector3();
|
|
30212
|
+
const tmpB = new Vector3();
|
|
30213
|
+
const tmpC = new Vector3();
|
|
30214
|
+
for (const t2 of indices) {
|
|
30215
|
+
const base = t2 * 3;
|
|
30216
|
+
tmpA.set(positions.getX(base), positions.getY(base), positions.getZ(base));
|
|
30217
|
+
tmpB.set(positions.getX(base + 1), positions.getY(base + 1), positions.getZ(base + 1));
|
|
30218
|
+
tmpC.set(positions.getX(base + 2), positions.getY(base + 2), positions.getZ(base + 2));
|
|
30219
|
+
const ab = tmpB.clone().sub(tmpA);
|
|
30220
|
+
const ac = tmpC.clone().sub(tmpA);
|
|
30221
|
+
const cross = ab.cross(ac);
|
|
30222
|
+
const triArea = cross.length() * 0.5;
|
|
30223
|
+
totalArea += triArea;
|
|
30224
|
+
const triCenter = tmpA.clone().add(tmpB).add(tmpC).multiplyScalar(1 / 3);
|
|
30225
|
+
centroid.add(triCenter.multiplyScalar(triArea));
|
|
30226
|
+
if (cross.lengthSq() > 0) weightedNormal.add(cross.multiplyScalar(0.5));
|
|
30227
|
+
}
|
|
30228
|
+
if (totalArea > 0) centroid.multiplyScalar(1 / totalArea);
|
|
30229
|
+
let normal;
|
|
30230
|
+
if (weightedNormal.lengthSq() > 1e-12) {
|
|
30231
|
+
normal = weightedNormal.normalize();
|
|
30232
|
+
} else if (normals) {
|
|
30233
|
+
const si = indices[0] * 3;
|
|
30234
|
+
normal = new Vector3(normals.getX(si), normals.getY(si), normals.getZ(si)).normalize();
|
|
30235
|
+
} else {
|
|
30236
|
+
normal = new Vector3(0, 0, 1);
|
|
30237
|
+
}
|
|
30238
|
+
return { normal, center: centroid, area: totalArea };
|
|
30239
|
+
}
|
|
30240
|
+
function getFaceRegion(geometry, startTriIndex, options = {}) {
|
|
30241
|
+
const positions = geometry.getAttribute("position");
|
|
30242
|
+
const normals = geometry.getAttribute("normal") ?? null;
|
|
30243
|
+
const triCount = positions.count / 3;
|
|
30244
|
+
const edgeToTris = buildEdgeAdjacency(positions, triCount);
|
|
30245
|
+
const userData = geometry.userData;
|
|
30246
|
+
const faceIds = userData.forgeTriangleFaceIds;
|
|
30247
|
+
const faceIdNames = userData.forgeFaceIdNames;
|
|
30248
|
+
if (faceIds && faceIdNames && startTriIndex < faceIds.length) {
|
|
30249
|
+
const hitFaceId = faceIds[startTriIndex];
|
|
30250
|
+
if (hitFaceId >= 0) {
|
|
30251
|
+
const indices2 = floodComponent(positions, edgeToTris, startTriIndex, (n) => faceIds[n] === hitFaceId);
|
|
30252
|
+
const summary2 = summarizeRegion(positions, normals, indices2);
|
|
30253
|
+
const rawName = faceIdNames[hitFaceId] ?? null;
|
|
30254
|
+
const carrierName = rawName && rawName !== UNIDENTIFIED_FACE_NAME ? rawName : null;
|
|
30255
|
+
return { triangleIndices: indices2, ...summary2, carrierName };
|
|
30256
|
+
}
|
|
30257
|
+
}
|
|
30258
|
+
const normalTolerance = options.normalTolerance ?? 0.9995;
|
|
30259
|
+
const si = startTriIndex * 3;
|
|
30260
|
+
const startNormal = normals ? new Vector3(normals.getX(si), normals.getY(si), normals.getZ(si)) : new Vector3(0, 0, 1);
|
|
30261
|
+
const indices = floodComponent(positions, edgeToTris, startTriIndex, (n) => {
|
|
30262
|
+
if (!normals) return false;
|
|
30263
|
+
const ni = n * 3;
|
|
30264
|
+
const nNormal = new Vector3(normals.getX(ni), normals.getY(ni), normals.getZ(ni));
|
|
30265
|
+
return startNormal.dot(nNormal) >= normalTolerance;
|
|
30266
|
+
});
|
|
30267
|
+
const summary = summarizeRegion(positions, normals, indices);
|
|
30268
|
+
return { triangleIndices: indices, normal: startNormal.clone().normalize(), center: summary.center, area: summary.area, carrierName: null };
|
|
30269
|
+
}
|
|
30270
|
+
function buildFaceHighlightGeometry(sourceGeometry, triangleIndices) {
|
|
30271
|
+
const srcPos = sourceGeometry.getAttribute("position");
|
|
30272
|
+
const count = triangleIndices.length * 9;
|
|
30273
|
+
const positions = new Float32Array(count);
|
|
30274
|
+
for (let i = 0; i < triangleIndices.length; i++) {
|
|
30275
|
+
const base = triangleIndices[i] * 3;
|
|
30276
|
+
const out = i * 9;
|
|
30277
|
+
for (let v = 0; v < 3; v++) {
|
|
30278
|
+
positions[out + v * 3] = srcPos.getX(base + v);
|
|
30279
|
+
positions[out + v * 3 + 1] = srcPos.getY(base + v);
|
|
30280
|
+
positions[out + v * 3 + 2] = srcPos.getZ(base + v);
|
|
30281
|
+
}
|
|
30282
|
+
}
|
|
30283
|
+
const geo = new BufferGeometry();
|
|
30284
|
+
geo.setAttribute("position", new BufferAttribute(positions, 3));
|
|
30285
|
+
return geo;
|
|
30286
|
+
}
|
|
30287
|
+
function closestOnSegment(p2, a2, b2, out) {
|
|
30288
|
+
const abx = b2.x - a2.x;
|
|
30289
|
+
const aby = b2.y - a2.y;
|
|
30290
|
+
const abz = b2.z - a2.z;
|
|
30291
|
+
const abLenSq = abx * abx + aby * aby + abz * abz;
|
|
30292
|
+
let t2 = abLenSq > 0 ? ((p2.x - a2.x) * abx + (p2.y - a2.y) * aby + (p2.z - a2.z) * abz) / abLenSq : 0;
|
|
30293
|
+
if (t2 < 0) t2 = 0;
|
|
30294
|
+
else if (t2 > 1) t2 = 1;
|
|
30295
|
+
out.set(a2.x + abx * t2, a2.y + aby * t2, a2.z + abz * t2);
|
|
30296
|
+
return out.distanceToSquared(p2);
|
|
30297
|
+
}
|
|
30298
|
+
function getNearestEdge(geometry, localPoint, maxDistance) {
|
|
30299
|
+
const edges = getBakedEdges(geometry);
|
|
30300
|
+
if (edges.length === 0) return null;
|
|
30301
|
+
const maxSq = maxDistance * maxDistance;
|
|
30302
|
+
let best = null;
|
|
30303
|
+
let bestSq = maxSq;
|
|
30304
|
+
const a2 = new Vector3();
|
|
30305
|
+
const b2 = new Vector3();
|
|
30306
|
+
const closest = new Vector3();
|
|
30307
|
+
for (const edge of edges) {
|
|
30308
|
+
const pts = edge.points;
|
|
30309
|
+
for (let i = 0; i + 5 < pts.length; i += 3) {
|
|
30310
|
+
a2.set(pts[i], pts[i + 1], pts[i + 2]);
|
|
30311
|
+
b2.set(pts[i + 3], pts[i + 4], pts[i + 5]);
|
|
30312
|
+
const dSq = closestOnSegment(localPoint, a2, b2, closest);
|
|
30313
|
+
if (dSq < bestSq) {
|
|
30314
|
+
bestSq = dSq;
|
|
30315
|
+
best = { edge, point: closest.clone(), distance: Math.sqrt(dSq) };
|
|
30316
|
+
}
|
|
30317
|
+
}
|
|
30318
|
+
}
|
|
30319
|
+
return best;
|
|
30320
|
+
}
|
|
30321
|
+
function getNearestVertex(geometry, localPoint, maxDistance) {
|
|
30322
|
+
const edges = getBakedEdges(geometry);
|
|
30323
|
+
if (edges.length === 0) return null;
|
|
30324
|
+
const maxSq = maxDistance * maxDistance;
|
|
30325
|
+
let best = null;
|
|
30326
|
+
let bestSq = maxSq;
|
|
30327
|
+
const v = new Vector3();
|
|
30328
|
+
for (const edge of edges) {
|
|
30329
|
+
const pts = edge.points;
|
|
30330
|
+
for (let i = 0; i + 2 < pts.length; i += 3) {
|
|
30331
|
+
v.set(pts[i], pts[i + 1], pts[i + 2]);
|
|
30332
|
+
const dSq = v.distanceToSquared(localPoint);
|
|
30333
|
+
if (dSq < bestSq) {
|
|
30334
|
+
bestSq = dSq;
|
|
30335
|
+
best = { point: v.clone(), distance: Math.sqrt(dSq) };
|
|
30336
|
+
}
|
|
30337
|
+
}
|
|
30338
|
+
}
|
|
30339
|
+
return best;
|
|
30340
|
+
}
|
|
29983
30341
|
const PREVIEW_RENDER_ORDER_STEP = 1;
|
|
29984
30342
|
const HATCH_DIRECTION_A = new Vector2(Math.cos(MathUtils.degToRad(35)), Math.sin(MathUtils.degToRad(35)));
|
|
29985
30343
|
const HATCH_DIRECTION_B = new Vector2(Math.cos(MathUtils.degToRad(125)), Math.sin(MathUtils.degToRad(125)));
|
|
@@ -31315,6 +31673,9 @@ function ZebraInspectionMaterial({ clippingPlanes }) {
|
|
|
31315
31673
|
}
|
|
31316
31674
|
const EMPTY_CLIPPING_PLANES$1 = [];
|
|
31317
31675
|
const EMPTY_SECTION_PLANES$1 = [];
|
|
31676
|
+
const SELECTED_FACE_HIGHLIGHT_COLOR = "#ffa040";
|
|
31677
|
+
const SELECTED_EDGE_HIGHLIGHT_COLOR = "#3fd8ff";
|
|
31678
|
+
const SELECTED_VERTEX_HIGHLIGHT_COLOR = "#ffffff";
|
|
31318
31679
|
function scanCellKey(ix, iy, iz) {
|
|
31319
31680
|
return `${ix}:${iy}:${iz}`;
|
|
31320
31681
|
}
|
|
@@ -31505,6 +31866,9 @@ function ForgeObject({
|
|
|
31505
31866
|
sectionPlanes,
|
|
31506
31867
|
sectionPreviewRenderOrderBase,
|
|
31507
31868
|
debugHighlightColor,
|
|
31869
|
+
selectedFaceTriangleIndices,
|
|
31870
|
+
selectedEdgePoints,
|
|
31871
|
+
selectedVertexPoint,
|
|
31508
31872
|
onPointerEnter,
|
|
31509
31873
|
onPointerMove,
|
|
31510
31874
|
onPointerLeave,
|
|
@@ -31546,7 +31910,7 @@ function ForgeObject({
|
|
|
31546
31910
|
};
|
|
31547
31911
|
}
|
|
31548
31912
|
}, [obj.shape, wantsDirectSdf]);
|
|
31549
|
-
const isScalarInspect = inspectChannel === "thickness" || inspectChannel === "roughness";
|
|
31913
|
+
const isScalarInspect = inspectChannel === "thickness" || inspectChannel === "throughThickness" || inspectChannel === "roughness";
|
|
31550
31914
|
const isScalarScan = isScalarInspect && inspectDisplayMode === "scan";
|
|
31551
31915
|
const inspectPointGeo = reactExports.useMemo(() => {
|
|
31552
31916
|
if (!inspectPointCloud) return null;
|
|
@@ -31577,6 +31941,7 @@ function ForgeObject({
|
|
|
31577
31941
|
geometry.setAttribute("position", new BufferAttribute(inspectScalarSurface.positions, 3));
|
|
31578
31942
|
geometry.setAttribute("normal", new BufferAttribute(inspectScalarSurface.normals, 3));
|
|
31579
31943
|
geometry.setAttribute("aValue", new BufferAttribute(inspectScalarSurface.aValue, 1));
|
|
31944
|
+
geometry.setAttribute("uv", new BufferAttribute(inspectScalarSurface.uvs ?? new Float32Array(vertexCount * 2), 2));
|
|
31580
31945
|
geometry.setIndex(new BufferAttribute(inspectScalarSurface.index, 1));
|
|
31581
31946
|
try {
|
|
31582
31947
|
geometry.boundsTree = MeshBVH.deserialize(
|
|
@@ -31597,14 +31962,24 @@ function ForgeObject({
|
|
|
31597
31962
|
if (!inspectScalarSurface || !effectiveColorScale) return null;
|
|
31598
31963
|
return makeColorScaleTexture(colorScaleLUT(effectiveColorScale.colormap));
|
|
31599
31964
|
}, [inspectScalarSurface, effectiveColorScale == null ? void 0 : effectiveColorScale.colormap]);
|
|
31965
|
+
const inspectScalarValueTexture = reactExports.useMemo(() => {
|
|
31966
|
+
if (!(inspectScalarSurface == null ? void 0 : inspectScalarSurface.textureValues) || !inspectScalarSurface.textureWidth || !inspectScalarSurface.textureHeight) return null;
|
|
31967
|
+
return makeScalarValueTexture(
|
|
31968
|
+
inspectScalarSurface.textureValues,
|
|
31969
|
+
inspectScalarSurface.textureWidth,
|
|
31970
|
+
inspectScalarSurface.textureHeight
|
|
31971
|
+
);
|
|
31972
|
+
}, [inspectScalarSurface]);
|
|
31600
31973
|
const inspectScalarMaterialRef = reactExports.useRef(null);
|
|
31601
31974
|
const invalidate2 = useThree((s) => s.invalidate);
|
|
31602
31975
|
const inspectScalarUniforms = reactExports.useMemo(() => {
|
|
31603
31976
|
if (!inspectColormapTexture || !effectiveColorScale) return null;
|
|
31604
31977
|
return makeInspectScalarUniforms({
|
|
31605
31978
|
colorScale: inspectColormapTexture,
|
|
31979
|
+
scalarTexture: inspectScalarValueTexture,
|
|
31606
31980
|
domainMin: effectiveColorScale.domainMin,
|
|
31607
31981
|
domainMax: effectiveColorScale.domainMax,
|
|
31982
|
+
colorScaleReversed: effectiveColorScale.reversed === true,
|
|
31608
31983
|
quantizeBands: (inspectScalarParams == null ? void 0 : inspectScalarParams.quantizeBands) ?? 0,
|
|
31609
31984
|
isoEnabled: (inspectScalarParams == null ? void 0 : inspectScalarParams.isolinesEnabled) ?? false,
|
|
31610
31985
|
isoSpacing: (inspectScalarParams == null ? void 0 : inspectScalarParams.isolineSpacing) ?? 0,
|
|
@@ -31612,7 +31987,7 @@ function ForgeObject({
|
|
|
31612
31987
|
criticalThreshold: (inspectScalarParams == null ? void 0 : inspectScalarParams.criticalThreshold) ?? 0,
|
|
31613
31988
|
shadingEnabled: (inspectScalarParams == null ? void 0 : inspectScalarParams.shadingEnabled) ?? true
|
|
31614
31989
|
});
|
|
31615
|
-
}, [inspectColormapTexture]);
|
|
31990
|
+
}, [inspectColormapTexture, inspectScalarValueTexture]);
|
|
31616
31991
|
const wantsScanProxy = Boolean(
|
|
31617
31992
|
settings.visible && (inspectChannel === "none" && (renderStyle === "scan" || renderStyle === "matrix") || isScalarScan)
|
|
31618
31993
|
);
|
|
@@ -31623,6 +31998,40 @@ function ForgeObject({
|
|
|
31623
31998
|
const shell = createScanAnalysisColorGeometry(scanProxy.geometries.shell, inspectPointCloud, scanProxy.grid, matrix);
|
|
31624
31999
|
return shell ? { shell } : null;
|
|
31625
32000
|
}, [inspectPointCloud, isScalarScan, matrix, scanProxy]);
|
|
32001
|
+
const selectedFaceHighlightGeo = reactExports.useMemo(() => {
|
|
32002
|
+
if (!solidGeo || !selectedFaceTriangleIndices || selectedFaceTriangleIndices.length === 0) return null;
|
|
32003
|
+
return buildFaceHighlightGeometry(solidGeo, selectedFaceTriangleIndices);
|
|
32004
|
+
}, [solidGeo, selectedFaceTriangleIndices]);
|
|
32005
|
+
reactExports.useEffect(() => {
|
|
32006
|
+
return () => {
|
|
32007
|
+
selectedFaceHighlightGeo == null ? void 0 : selectedFaceHighlightGeo.dispose();
|
|
32008
|
+
};
|
|
32009
|
+
}, [selectedFaceHighlightGeo]);
|
|
32010
|
+
const selectedEdgeGeo = reactExports.useMemo(() => {
|
|
32011
|
+
if (!selectedEdgePoints || selectedEdgePoints.length < 6) return null;
|
|
32012
|
+
const segCount = selectedEdgePoints.length / 3 - 1;
|
|
32013
|
+
const positions = new Float32Array(segCount * 6);
|
|
32014
|
+
for (let i = 0; i < segCount; i++) {
|
|
32015
|
+
const a2 = i * 3;
|
|
32016
|
+
positions.set(selectedEdgePoints.slice(a2, a2 + 3), i * 6);
|
|
32017
|
+
positions.set(selectedEdgePoints.slice(a2 + 3, a2 + 6), i * 6 + 3);
|
|
32018
|
+
}
|
|
32019
|
+
const geo = new BufferGeometry();
|
|
32020
|
+
geo.setAttribute("position", new BufferAttribute(positions, 3));
|
|
32021
|
+
return geo;
|
|
32022
|
+
}, [selectedEdgePoints]);
|
|
32023
|
+
reactExports.useEffect(() => {
|
|
32024
|
+
return () => {
|
|
32025
|
+
selectedEdgeGeo == null ? void 0 : selectedEdgeGeo.dispose();
|
|
32026
|
+
};
|
|
32027
|
+
}, [selectedEdgeGeo]);
|
|
32028
|
+
const selectedVertexMarkerRadius = reactExports.useMemo(() => {
|
|
32029
|
+
var _a4;
|
|
32030
|
+
if (!selectedVertexPoint || !solidGeo) return 0;
|
|
32031
|
+
if (!solidGeo.boundingSphere) solidGeo.computeBoundingSphere();
|
|
32032
|
+
const r2 = ((_a4 = solidGeo.boundingSphere) == null ? void 0 : _a4.radius) ?? 1;
|
|
32033
|
+
return r2 * 0.02;
|
|
32034
|
+
}, [selectedVertexPoint, solidGeo]);
|
|
31626
32035
|
reactExports.useEffect(() => {
|
|
31627
32036
|
return () => {
|
|
31628
32037
|
solidGeo == null ? void 0 : solidGeo.dispose();
|
|
@@ -31649,11 +32058,17 @@ function ForgeObject({
|
|
|
31649
32058
|
inspectColormapTexture == null ? void 0 : inspectColormapTexture.dispose();
|
|
31650
32059
|
};
|
|
31651
32060
|
}, [inspectColormapTexture]);
|
|
32061
|
+
reactExports.useEffect(() => {
|
|
32062
|
+
return () => {
|
|
32063
|
+
inspectScalarValueTexture == null ? void 0 : inspectScalarValueTexture.dispose();
|
|
32064
|
+
};
|
|
32065
|
+
}, [inspectScalarValueTexture]);
|
|
31652
32066
|
reactExports.useEffect(() => {
|
|
31653
32067
|
if (!inspectScalarUniforms || !effectiveColorScale || !inspectScalarParams) return;
|
|
31654
32068
|
updateInspectScalarUniforms(inspectScalarUniforms, {
|
|
31655
32069
|
domainMin: effectiveColorScale.domainMin,
|
|
31656
32070
|
domainMax: effectiveColorScale.domainMax,
|
|
32071
|
+
colorScaleReversed: effectiveColorScale.reversed === true,
|
|
31657
32072
|
quantizeBands: inspectScalarParams.quantizeBands,
|
|
31658
32073
|
isoEnabled: inspectScalarParams.isolinesEnabled,
|
|
31659
32074
|
isoSpacing: inspectScalarParams.isolineSpacing,
|
|
@@ -32057,7 +32472,25 @@ function ForgeObject({
|
|
|
32057
32472
|
polygonOffsetUnits: -1
|
|
32058
32473
|
}
|
|
32059
32474
|
) }),
|
|
32060
|
-
debugHighlightColor && edgesGeo && /* @__PURE__ */ jsxRuntimeExports.jsx("lineSegments", { geometry: edgesGeo, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx("lineBasicMaterial", { color: debugHighlightColor, linewidth: 2, depthTest: false }) })
|
|
32475
|
+
debugHighlightColor && edgesGeo && /* @__PURE__ */ jsxRuntimeExports.jsx("lineSegments", { geometry: edgesGeo, raycast: () => null, children: /* @__PURE__ */ jsxRuntimeExports.jsx("lineBasicMaterial", { color: debugHighlightColor, linewidth: 2, depthTest: false }) }),
|
|
32476
|
+
selectedFaceHighlightGeo && /* @__PURE__ */ jsxRuntimeExports.jsx("mesh", { geometry: selectedFaceHighlightGeo, raycast: () => null, renderOrder: 11, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
32477
|
+
"meshBasicMaterial",
|
|
32478
|
+
{
|
|
32479
|
+
color: SELECTED_FACE_HIGHLIGHT_COLOR,
|
|
32480
|
+
transparent: true,
|
|
32481
|
+
opacity: 0.45,
|
|
32482
|
+
side: DoubleSide,
|
|
32483
|
+
depthTest: false,
|
|
32484
|
+
polygonOffset: true,
|
|
32485
|
+
polygonOffsetFactor: -2,
|
|
32486
|
+
polygonOffsetUnits: -2
|
|
32487
|
+
}
|
|
32488
|
+
) }),
|
|
32489
|
+
selectedEdgeGeo && /* @__PURE__ */ jsxRuntimeExports.jsx("lineSegments", { geometry: selectedEdgeGeo, raycast: () => null, renderOrder: 12, children: /* @__PURE__ */ jsxRuntimeExports.jsx("lineBasicMaterial", { color: SELECTED_EDGE_HIGHLIGHT_COLOR, linewidth: 2, depthTest: false, toneMapped: false }) }),
|
|
32490
|
+
selectedVertexPoint && selectedVertexMarkerRadius > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("mesh", { position: selectedVertexPoint, raycast: () => null, renderOrder: 13, children: [
|
|
32491
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("sphereGeometry", { args: [selectedVertexMarkerRadius, 12, 12] }),
|
|
32492
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("meshBasicMaterial", { color: SELECTED_VERTEX_HIGHLIGHT_COLOR, depthTest: false, toneMapped: false })
|
|
32493
|
+
] })
|
|
32061
32494
|
]
|
|
32062
32495
|
}
|
|
32063
32496
|
);
|
|
@@ -32344,7 +32777,7 @@ function HoveredJointOverlay({ state: state2, config }) {
|
|
|
32344
32777
|
] });
|
|
32345
32778
|
}
|
|
32346
32779
|
function scalarRamp(options, domainMin, domainMax, unit) {
|
|
32347
|
-
var _a3;
|
|
32780
|
+
var _a3, _b2;
|
|
32348
32781
|
const colormap = ((_a3 = options.colorScale) == null ? void 0 : _a3.colormap) ?? DEFAULT_COLORMAP;
|
|
32349
32782
|
const midpoint = domainMin + (domainMax - domainMin) / 2;
|
|
32350
32783
|
const unitSuffix = unit ? ` ${unit}` : "";
|
|
@@ -32352,7 +32785,7 @@ function scalarRamp(options, domainMin, domainMax, unit) {
|
|
|
32352
32785
|
const critical = options.criticalValue;
|
|
32353
32786
|
const criticalTick = critical != null && Number.isFinite(critical) && span > 0 ? { position: Math.max(0, Math.min(1, (critical - domainMin) / span)), label: `${fmt(critical)}${unitSuffix}` } : void 0;
|
|
32354
32787
|
return {
|
|
32355
|
-
colors: colorScaleHexStops(colormap),
|
|
32788
|
+
colors: colorScaleHexStops(colormap, 8, ((_b2 = options.colorScale) == null ? void 0 : _b2.reversed) === true),
|
|
32356
32789
|
leftLabel: `${fmt(domainMin)}${unitSuffix}`,
|
|
32357
32790
|
centerLabel: `${fmt(midpoint)}${unitSuffix}`,
|
|
32358
32791
|
rightLabel: `${fmt(domainMax)}${unitSuffix}`,
|
|
@@ -32366,7 +32799,7 @@ function fmt(value) {
|
|
|
32366
32799
|
return fixed.replace(/\.?0+$/, "");
|
|
32367
32800
|
}
|
|
32368
32801
|
function inspectionLegendDefinitionFor(channel, displayMode, legendOptions = {}) {
|
|
32369
|
-
var _a3, _b2, _c, _d;
|
|
32802
|
+
var _a3, _b2, _c, _d, _e, _f;
|
|
32370
32803
|
switch (channel) {
|
|
32371
32804
|
case "mask":
|
|
32372
32805
|
return {
|
|
@@ -32468,13 +32901,32 @@ function inspectionLegendDefinitionFor(channel, displayMode, legendOptions = {})
|
|
|
32468
32901
|
}
|
|
32469
32902
|
return {
|
|
32470
32903
|
title: "Wall Thickness",
|
|
32471
|
-
summary: "
|
|
32904
|
+
summary: "Warm colors mark thinner material; cool colors mark thicker material.",
|
|
32905
|
+
ramp
|
|
32906
|
+
};
|
|
32907
|
+
}
|
|
32908
|
+
case "throughThickness": {
|
|
32909
|
+
const colorRange = legendOptions.thicknessColorRange ?? DEFAULT_THICKNESS_COLOR_RANGE;
|
|
32910
|
+
const domainMin = ((_c = legendOptions.colorScale) == null ? void 0 : _c.domainMin) ?? colorRange.min;
|
|
32911
|
+
const domainMax = ((_d = legendOptions.colorScale) == null ? void 0 : _d.domainMax) ?? colorRange.max;
|
|
32912
|
+
const unit = legendOptions.unitLabel ?? "mm";
|
|
32913
|
+
const ramp = scalarRamp(legendOptions, domainMin, domainMax, unit);
|
|
32914
|
+
if (displayMode === "scan") {
|
|
32915
|
+
return {
|
|
32916
|
+
title: "Minimum Solid Span Scan",
|
|
32917
|
+
summary: "Each scan box uses the same minimum solid span ramp as the heatmap.",
|
|
32918
|
+
ramp
|
|
32919
|
+
};
|
|
32920
|
+
}
|
|
32921
|
+
return {
|
|
32922
|
+
title: "Minimum Solid Span",
|
|
32923
|
+
summary: "Warm colors mark short through-material spans; cool colors mark more surrounding material.",
|
|
32472
32924
|
ramp
|
|
32473
32925
|
};
|
|
32474
32926
|
}
|
|
32475
32927
|
case "roughness": {
|
|
32476
|
-
const domainMin = ((
|
|
32477
|
-
const domainMax = ((
|
|
32928
|
+
const domainMin = ((_e = legendOptions.colorScale) == null ? void 0 : _e.domainMin) ?? 0;
|
|
32929
|
+
const domainMax = ((_f = legendOptions.colorScale) == null ? void 0 : _f.domainMax) ?? 1;
|
|
32478
32930
|
const unit = legendOptions.unitLabel ?? "deg";
|
|
32479
32931
|
const ramp = scalarRamp(legendOptions, domainMin, domainMax, unit);
|
|
32480
32932
|
if (displayMode === "scan") {
|
|
@@ -32682,7 +33134,7 @@ function rangeDraftValue(value) {
|
|
|
32682
33134
|
function rangeKey(range) {
|
|
32683
33135
|
return `${range.min}:${range.max}`;
|
|
32684
33136
|
}
|
|
32685
|
-
function clamp(value, min, max2) {
|
|
33137
|
+
function clamp$1(value, min, max2) {
|
|
32686
33138
|
return Math.max(min, Math.min(max2, value));
|
|
32687
33139
|
}
|
|
32688
33140
|
function snapSliderValue(value) {
|
|
@@ -32707,20 +33159,20 @@ function DualRangeSlider({
|
|
|
32707
33159
|
onCommit
|
|
32708
33160
|
}) {
|
|
32709
33161
|
const trackRef = reactExports.useRef(null);
|
|
32710
|
-
const minPercent = clamp(range.min, 0, sliderMax) / sliderMax * 100;
|
|
32711
|
-
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;
|
|
32712
33164
|
const valueFromPointer = (event) => {
|
|
32713
33165
|
var _a3;
|
|
32714
33166
|
const rect = (_a3 = trackRef.current) == null ? void 0 : _a3.getBoundingClientRect();
|
|
32715
33167
|
if (!rect || rect.width <= 0) return null;
|
|
32716
|
-
const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1);
|
|
33168
|
+
const ratio = clamp$1((event.clientX - rect.left) / rect.width, 0, 1);
|
|
32717
33169
|
return snapSliderValue(ratio * sliderMax);
|
|
32718
33170
|
};
|
|
32719
33171
|
const updateThumb = (thumb, value) => {
|
|
32720
33172
|
if (thumb === "min") {
|
|
32721
|
-
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 });
|
|
32722
33174
|
} else {
|
|
32723
|
-
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)) });
|
|
32724
33176
|
}
|
|
32725
33177
|
};
|
|
32726
33178
|
const handlePointerMove = (thumb, event) => {
|
|
@@ -32960,7 +33412,7 @@ function InspectionLegend({
|
|
|
32960
33412
|
criticalValue
|
|
32961
33413
|
});
|
|
32962
33414
|
const swatches = liveSwatches && liveSwatches.length > 0 ? liveSwatches : definition == null ? void 0 : definition.swatches;
|
|
32963
|
-
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);
|
|
32964
33416
|
reactExports.useEffect(() => {
|
|
32965
33417
|
var _a3;
|
|
32966
33418
|
const parent = (_a3 = panelRef.current) == null ? void 0 : _a3.parentElement;
|
|
@@ -32973,7 +33425,7 @@ function InspectionLegend({
|
|
|
32973
33425
|
}, [channel]);
|
|
32974
33426
|
if (!definition) return null;
|
|
32975
33427
|
const warning = warnings[0];
|
|
32976
|
-
const showThicknessControls = channel === "thickness" && thicknessColorRange !== void 0 && onThicknessColorRangeChange !== void 0;
|
|
33428
|
+
const showThicknessControls = (channel === "thickness" || channel === "throughThickness") && thicknessColorRange !== void 0 && onThicknessColorRangeChange !== void 0;
|
|
32977
33429
|
const swatchCount = (swatches == null ? void 0 : swatches.length) ?? 0;
|
|
32978
33430
|
const hasScrollableSwatches = swatchCount > 10;
|
|
32979
33431
|
const effectivePanelStyle = {
|
|
@@ -33066,212 +33518,263 @@ function LabeledAxes({ size = 50 }) {
|
|
|
33066
33518
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Html, { position: [0, 0, size + 3], center: true, style: labelStyle("#4488ff"), children: "Z" })
|
|
33067
33519
|
] });
|
|
33068
33520
|
}
|
|
33069
|
-
const
|
|
33070
|
-
|
|
33071
|
-
|
|
33072
|
-
|
|
33073
|
-
|
|
33074
|
-
|
|
33075
|
-
|
|
33076
|
-
selection: "#ffa040",
|
|
33077
|
-
// both selections — warm amber
|
|
33078
|
-
line: "#ffa040",
|
|
33079
|
-
panelLabel: "#888",
|
|
33080
|
-
panelValue: "#ffd060"
|
|
33081
|
-
};
|
|
33082
|
-
const QUANT = 1e4;
|
|
33083
|
-
const q = (v) => Math.round(v * QUANT);
|
|
33084
|
-
const vertKey = (pos, i) => `${q(pos.getX(i))},${q(pos.getY(i))},${q(pos.getZ(i))}`;
|
|
33085
|
-
const edgeKey = (a2, b2) => a2 < b2 ? `${a2}|${b2}` : `${b2}|${a2}`;
|
|
33086
|
-
function floodFillFace(geometry, startTriIndex, normalTolerance = 0.9995) {
|
|
33521
|
+
const SHARP_DOT = 0.9995;
|
|
33522
|
+
const CORNER_TURN_DEG = 35;
|
|
33523
|
+
const LEN_RATIO_SPLIT = 4;
|
|
33524
|
+
const CONTINUITY_LIMIT_DEG = 60;
|
|
33525
|
+
const CORNER_TURN_COS = Math.cos(CORNER_TURN_DEG * Math.PI / 180);
|
|
33526
|
+
const CONTINUITY_LIMIT_COS = Math.cos(CONTINUITY_LIMIT_DEG * Math.PI / 180);
|
|
33527
|
+
function extractFeatureEdges(geometry) {
|
|
33087
33528
|
const positions = geometry.getAttribute("position");
|
|
33088
33529
|
const normals = geometry.getAttribute("normal");
|
|
33530
|
+
if (!positions) return [];
|
|
33089
33531
|
const triCount = positions.count / 3;
|
|
33090
|
-
const
|
|
33091
|
-
const startNormal = new Vector3(normals.getX(si), normals.getY(si), normals.getZ(si));
|
|
33092
|
-
const edgeToTris = /* @__PURE__ */ new Map();
|
|
33532
|
+
const data = /* @__PURE__ */ new Map();
|
|
33093
33533
|
for (let t2 = 0; t2 < triCount; t2++) {
|
|
33094
33534
|
const base = t2 * 3;
|
|
33095
|
-
const
|
|
33096
|
-
|
|
33097
|
-
|
|
33098
|
-
|
|
33099
|
-
|
|
33100
|
-
|
|
33101
|
-
|
|
33102
|
-
|
|
33103
|
-
|
|
33104
|
-
|
|
33105
|
-
|
|
33106
|
-
|
|
33107
|
-
|
|
33108
|
-
const queue = [startTriIndex];
|
|
33109
|
-
visited.add(startTriIndex);
|
|
33110
|
-
while (queue.length > 0) {
|
|
33111
|
-
const t2 = queue.pop();
|
|
33112
|
-
const base = t2 * 3;
|
|
33113
|
-
const v0 = vertKey(positions, base);
|
|
33114
|
-
const v12 = vertKey(positions, base + 1);
|
|
33115
|
-
const v22 = vertKey(positions, base + 2);
|
|
33116
|
-
for (const ek of [edgeKey(v0, v12), edgeKey(v12, v22), edgeKey(v22, v0)]) {
|
|
33117
|
-
const neighbors = edgeToTris.get(ek);
|
|
33118
|
-
if (!neighbors) continue;
|
|
33119
|
-
for (const n of neighbors) {
|
|
33120
|
-
if (visited.has(n)) continue;
|
|
33121
|
-
const ni = n * 3;
|
|
33122
|
-
const nNormal = new Vector3(normals.getX(ni), normals.getY(ni), normals.getZ(ni));
|
|
33123
|
-
if (startNormal.dot(nNormal) >= normalTolerance) {
|
|
33124
|
-
visited.add(n);
|
|
33125
|
-
queue.push(n);
|
|
33126
|
-
}
|
|
33535
|
+
const tn = normals ? new Vector3(normals.getX(base), normals.getY(base), normals.getZ(base)) : new Vector3(0, 0, 1);
|
|
33536
|
+
for (let e2 = 0; e2 < 3; e2++) {
|
|
33537
|
+
const i0 = base + e2;
|
|
33538
|
+
const i1 = base + (e2 + 1) % 3;
|
|
33539
|
+
const ek = edgeKey(vertKey(positions, i0), vertKey(positions, i1));
|
|
33540
|
+
let d = data.get(ek);
|
|
33541
|
+
if (!d) {
|
|
33542
|
+
d = {
|
|
33543
|
+
a: new Vector3().fromBufferAttribute(positions, i0),
|
|
33544
|
+
b: new Vector3().fromBufferAttribute(positions, i1),
|
|
33545
|
+
normals: []
|
|
33546
|
+
};
|
|
33547
|
+
data.set(ek, d);
|
|
33127
33548
|
}
|
|
33549
|
+
d.normals.push(tn);
|
|
33128
33550
|
}
|
|
33129
33551
|
}
|
|
33130
|
-
const
|
|
33131
|
-
|
|
33132
|
-
|
|
33133
|
-
|
|
33134
|
-
|
|
33135
|
-
const tmpC = new Vector3();
|
|
33136
|
-
for (const t2 of indices) {
|
|
33137
|
-
const base = t2 * 3;
|
|
33138
|
-
tmpA.set(positions.getX(base), positions.getY(base), positions.getZ(base));
|
|
33139
|
-
tmpB.set(positions.getX(base + 1), positions.getY(base + 1), positions.getZ(base + 1));
|
|
33140
|
-
tmpC.set(positions.getX(base + 2), positions.getY(base + 2), positions.getZ(base + 2));
|
|
33141
|
-
const ab = tmpB.clone().sub(tmpA);
|
|
33142
|
-
const ac = tmpC.clone().sub(tmpA);
|
|
33143
|
-
const triArea = ab.cross(ac).length() * 0.5;
|
|
33144
|
-
totalArea += triArea;
|
|
33145
|
-
const triCenter = tmpA.clone().add(tmpB).add(tmpC).multiplyScalar(1 / 3);
|
|
33146
|
-
centroid.add(triCenter.multiplyScalar(triArea));
|
|
33552
|
+
const out = [];
|
|
33553
|
+
for (const d of data.values()) {
|
|
33554
|
+
const sharp = d.normals.length === 2 && d.normals[0].dot(d.normals[1]) < SHARP_DOT;
|
|
33555
|
+
const boundary = d.normals.length === 1;
|
|
33556
|
+
if (sharp || boundary) out.push({ a: d.a, b: d.b });
|
|
33147
33557
|
}
|
|
33148
|
-
|
|
33149
|
-
return { triangleIndices: indices, normal: startNormal.clone(), center: centroid, area: totalArea };
|
|
33558
|
+
return out;
|
|
33150
33559
|
}
|
|
33151
|
-
function
|
|
33152
|
-
const
|
|
33153
|
-
const
|
|
33154
|
-
const
|
|
33155
|
-
for (
|
|
33156
|
-
const
|
|
33157
|
-
const
|
|
33158
|
-
|
|
33159
|
-
|
|
33160
|
-
|
|
33161
|
-
|
|
33560
|
+
function buildGraph(edges) {
|
|
33561
|
+
const point = /* @__PURE__ */ new Map();
|
|
33562
|
+
const adj = /* @__PURE__ */ new Map();
|
|
33563
|
+
const key = (p2) => `${q(p2.x)},${q(p2.y)},${q(p2.z)}`;
|
|
33564
|
+
for (const { a: a2, b: b2 } of edges) {
|
|
33565
|
+
const ka = key(a2);
|
|
33566
|
+
const kb = key(b2);
|
|
33567
|
+
if (ka === kb) continue;
|
|
33568
|
+
point.set(ka, a2);
|
|
33569
|
+
point.set(kb, b2);
|
|
33570
|
+
(adj.get(ka) ?? adj.set(ka, /* @__PURE__ */ new Set()).get(ka)).add(kb);
|
|
33571
|
+
(adj.get(kb) ?? adj.set(kb, /* @__PURE__ */ new Set()).get(kb)).add(ka);
|
|
33572
|
+
}
|
|
33573
|
+
return { point, adj: new Map(Array.from(adj, ([k2, v]) => [k2, Array.from(v)])) };
|
|
33574
|
+
}
|
|
33575
|
+
function nearestSegment(graph, localPoint) {
|
|
33576
|
+
let best = null;
|
|
33577
|
+
let bestSq = Infinity;
|
|
33578
|
+
const closest = new Vector3();
|
|
33579
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33580
|
+
for (const [u, neighbors] of graph.adj) {
|
|
33581
|
+
const a2 = graph.point.get(u);
|
|
33582
|
+
for (const v of neighbors) {
|
|
33583
|
+
const ek = edgeKey(u, v);
|
|
33584
|
+
if (seen.has(ek)) continue;
|
|
33585
|
+
seen.add(ek);
|
|
33586
|
+
const b2 = graph.point.get(v);
|
|
33587
|
+
const dSq = closestOnSegment(localPoint, a2, b2, closest);
|
|
33588
|
+
if (dSq < bestSq) {
|
|
33589
|
+
bestSq = dSq;
|
|
33590
|
+
best = { u, v };
|
|
33591
|
+
}
|
|
33162
33592
|
}
|
|
33163
33593
|
}
|
|
33164
|
-
|
|
33165
|
-
geo.setAttribute("position", new BufferAttribute(positions, 3));
|
|
33166
|
-
return geo;
|
|
33594
|
+
return best;
|
|
33167
33595
|
}
|
|
33168
|
-
function
|
|
33169
|
-
const
|
|
33170
|
-
const
|
|
33171
|
-
|
|
33172
|
-
|
|
33173
|
-
for (let
|
|
33174
|
-
const
|
|
33175
|
-
const
|
|
33176
|
-
|
|
33177
|
-
|
|
33178
|
-
|
|
33179
|
-
|
|
33180
|
-
|
|
33181
|
-
|
|
33182
|
-
|
|
33183
|
-
|
|
33184
|
-
|
|
33185
|
-
|
|
33186
|
-
|
|
33187
|
-
|
|
33188
|
-
|
|
33189
|
-
|
|
33190
|
-
|
|
33191
|
-
|
|
33192
|
-
|
|
33193
|
-
|
|
33194
|
-
|
|
33195
|
-
|
|
33196
|
-
|
|
33197
|
-
|
|
33198
|
-
|
|
33199
|
-
|
|
33200
|
-
|
|
33201
|
-
|
|
33202
|
-
|
|
33203
|
-
|
|
33204
|
-
|
|
33205
|
-
|
|
33206
|
-
const denom = ab.lengthSq();
|
|
33207
|
-
if (denom === 0) continue;
|
|
33208
|
-
const t2 = MathUtils.clamp(localHit.clone().sub(edge.a).dot(ab) / denom, 0, 1);
|
|
33209
|
-
const closest = edge.a.clone().add(ab.multiplyScalar(t2));
|
|
33210
|
-
const dist = closest.distanceTo(localHit);
|
|
33211
|
-
if (dist < closestDist) {
|
|
33212
|
-
closestDist = dist;
|
|
33213
|
-
closestEdge = edge;
|
|
33214
|
-
}
|
|
33215
|
-
}
|
|
33216
|
-
if (!closestEdge) return null;
|
|
33217
|
-
const dir = closestEdge.b.clone().sub(closestEdge.a).normalize();
|
|
33218
|
-
const vertToEdges = /* @__PURE__ */ new Map();
|
|
33219
|
-
for (const edge of sharpEdges) {
|
|
33220
|
-
const vk0 = `${q(edge.a.x)},${q(edge.a.y)},${q(edge.a.z)}`;
|
|
33221
|
-
const vk1 = `${q(edge.b.x)},${q(edge.b.y)},${q(edge.b.z)}`;
|
|
33222
|
-
for (const vk of [vk0, vk1]) {
|
|
33223
|
-
let list = vertToEdges.get(vk);
|
|
33224
|
-
if (!list) {
|
|
33225
|
-
list = [];
|
|
33226
|
-
vertToEdges.set(vk, list);
|
|
33227
|
-
}
|
|
33228
|
-
list.push(edge);
|
|
33596
|
+
function walk(graph, fromKey, curKey, mode, stopAtKey) {
|
|
33597
|
+
const out = [];
|
|
33598
|
+
const visited = /* @__PURE__ */ new Set([fromKey, curKey]);
|
|
33599
|
+
let prev = fromKey;
|
|
33600
|
+
let cur = curKey;
|
|
33601
|
+
for (let guard = 0; guard < 1e5; guard++) {
|
|
33602
|
+
const prevP = graph.point.get(prev);
|
|
33603
|
+
const curP = graph.point.get(cur);
|
|
33604
|
+
const dIn = curP.clone().sub(prevP);
|
|
33605
|
+
const lenIn = dIn.length();
|
|
33606
|
+
if (lenIn === 0) break;
|
|
33607
|
+
dIn.multiplyScalar(1 / lenIn);
|
|
33608
|
+
let bestKey = null;
|
|
33609
|
+
let bestDot = -Infinity;
|
|
33610
|
+
let bestLen = 0;
|
|
33611
|
+
for (const w of graph.adj.get(cur) ?? []) {
|
|
33612
|
+
if (w === prev) continue;
|
|
33613
|
+
const wp = graph.point.get(w);
|
|
33614
|
+
const dOut = wp.clone().sub(curP);
|
|
33615
|
+
const lenOut = dOut.length();
|
|
33616
|
+
if (lenOut === 0) continue;
|
|
33617
|
+
const dot = dIn.dot(dOut) / lenOut;
|
|
33618
|
+
if (dot > bestDot) {
|
|
33619
|
+
bestDot = dot;
|
|
33620
|
+
bestKey = w;
|
|
33621
|
+
bestLen = lenOut;
|
|
33622
|
+
}
|
|
33623
|
+
}
|
|
33624
|
+
if (!bestKey) break;
|
|
33625
|
+
if (bestDot < CONTINUITY_LIMIT_COS) break;
|
|
33626
|
+
if (mode === "single") {
|
|
33627
|
+
if (bestDot < CORNER_TURN_COS) break;
|
|
33628
|
+
const ratio = bestLen >= lenIn ? bestLen / lenIn : lenIn / bestLen;
|
|
33629
|
+
if (ratio > LEN_RATIO_SPLIT) break;
|
|
33630
|
+
}
|
|
33631
|
+
if (bestKey === stopAtKey) {
|
|
33632
|
+
out.push(bestKey);
|
|
33633
|
+
break;
|
|
33229
33634
|
}
|
|
33635
|
+
if (visited.has(bestKey)) break;
|
|
33636
|
+
visited.add(bestKey);
|
|
33637
|
+
out.push(bestKey);
|
|
33638
|
+
prev = cur;
|
|
33639
|
+
cur = bestKey;
|
|
33230
33640
|
}
|
|
33231
|
-
|
|
33232
|
-
|
|
33233
|
-
|
|
33234
|
-
const
|
|
33235
|
-
|
|
33236
|
-
|
|
33237
|
-
|
|
33238
|
-
|
|
33239
|
-
|
|
33240
|
-
const
|
|
33241
|
-
|
|
33242
|
-
|
|
33243
|
-
|
|
33244
|
-
|
|
33245
|
-
|
|
33246
|
-
|
|
33247
|
-
|
|
33248
|
-
|
|
33249
|
-
|
|
33250
|
-
|
|
33251
|
-
|
|
33252
|
-
|
|
33641
|
+
return out;
|
|
33642
|
+
}
|
|
33643
|
+
function chainFromSeed(graph, seed, mode) {
|
|
33644
|
+
const forward = walk(graph, seed.u, seed.v, mode, seed.u);
|
|
33645
|
+
const closed = forward.length > 0 && forward[forward.length - 1] === seed.u;
|
|
33646
|
+
let keys;
|
|
33647
|
+
if (closed) {
|
|
33648
|
+
keys = [seed.u, seed.v, ...forward];
|
|
33649
|
+
} else {
|
|
33650
|
+
const backward = walk(graph, seed.v, seed.u, mode, seed.v);
|
|
33651
|
+
keys = [...backward.slice().reverse(), seed.u, seed.v, ...forward];
|
|
33652
|
+
}
|
|
33653
|
+
return keys.map((k2) => graph.point.get(k2).clone());
|
|
33654
|
+
}
|
|
33655
|
+
function getNearestEdgeChain(geometry, localPoint) {
|
|
33656
|
+
const graph = buildGraph(extractFeatureEdges(geometry));
|
|
33657
|
+
const seed = nearestSegment(graph, localPoint);
|
|
33658
|
+
if (!seed) return null;
|
|
33659
|
+
return chainFromSeed(graph, seed, "single");
|
|
33660
|
+
}
|
|
33661
|
+
function getWholeEdgePath(geometry, localPoint) {
|
|
33662
|
+
const graph = buildGraph(extractFeatureEdges(geometry));
|
|
33663
|
+
const seed = nearestSegment(graph, localPoint);
|
|
33664
|
+
if (!seed) return null;
|
|
33665
|
+
return chainFromSeed(graph, seed, "whole");
|
|
33666
|
+
}
|
|
33667
|
+
function circleMeasurement(curve) {
|
|
33668
|
+
if (!curve || curve.kind !== "circle") return null;
|
|
33669
|
+
const radius = curve.radius;
|
|
33670
|
+
return {
|
|
33671
|
+
radius,
|
|
33672
|
+
diameter: radius * 2,
|
|
33673
|
+
circumference: 2 * Math.PI * radius,
|
|
33674
|
+
center: [curve.center[0], curve.center[1], curve.center[2]],
|
|
33675
|
+
axis: [curve.axis[0], curve.axis[1], curve.axis[2]]
|
|
33676
|
+
};
|
|
33677
|
+
}
|
|
33678
|
+
function worldScale(m2) {
|
|
33679
|
+
const s = new Vector3();
|
|
33680
|
+
m2.decompose(new Vector3(), new Quaternion(), s);
|
|
33681
|
+
return (Math.abs(s.x) + Math.abs(s.y) + Math.abs(s.z)) / 3;
|
|
33682
|
+
}
|
|
33683
|
+
function applyPoint(p2, m2) {
|
|
33684
|
+
const v = new Vector3(p2[0], p2[1], p2[2]).applyMatrix4(m2);
|
|
33685
|
+
return [v.x, v.y, v.z];
|
|
33686
|
+
}
|
|
33687
|
+
function edgeEntityFromBakedEdge(edge, meshMatrixWorld, meshUuid) {
|
|
33688
|
+
const scale = worldScale(meshMatrixWorld);
|
|
33689
|
+
const pts = edge.points;
|
|
33690
|
+
const n = pts.length / 3;
|
|
33691
|
+
const firstLocal = [pts[0], pts[1], pts[2]];
|
|
33692
|
+
const lastLocal = n > 0 ? [pts[(n - 1) * 3], pts[(n - 1) * 3 + 1], pts[(n - 1) * 3 + 2]] : firstLocal;
|
|
33693
|
+
const start = applyPoint(firstLocal, meshMatrixWorld);
|
|
33694
|
+
const end = applyPoint(lastLocal, meshMatrixWorld);
|
|
33695
|
+
const polyline = edge.curve.kind === "line" ? void 0 : Array.from({ length: n }, (_, i) => applyPoint([pts[i * 3], pts[i * 3 + 1], pts[i * 3 + 2]], meshMatrixWorld));
|
|
33696
|
+
if (edge.curve.kind === "line") {
|
|
33697
|
+
const s = new Vector3(...edge.curve.start).applyMatrix4(meshMatrixWorld);
|
|
33698
|
+
const e2 = new Vector3(...edge.curve.end).applyMatrix4(meshMatrixWorld);
|
|
33699
|
+
const dir2 = e2.clone().sub(s);
|
|
33700
|
+
const length = dir2.length();
|
|
33701
|
+
const direction2 = length > 0 ? dir2.normalize() : new Vector3(1, 0, 0);
|
|
33702
|
+
return {
|
|
33703
|
+
kind: "edge",
|
|
33704
|
+
start: [s.x, s.y, s.z],
|
|
33705
|
+
end: [e2.x, e2.y, e2.z],
|
|
33706
|
+
length,
|
|
33707
|
+
direction: [direction2.x, direction2.y, direction2.z],
|
|
33708
|
+
meshUuid,
|
|
33709
|
+
curve: { kind: "line", start: [s.x, s.y, s.z], end: [e2.x, e2.y, e2.z], faceName: edge.curve.faceName }
|
|
33710
|
+
};
|
|
33253
33711
|
}
|
|
33254
|
-
|
|
33255
|
-
|
|
33256
|
-
|
|
33257
|
-
|
|
33258
|
-
|
|
33259
|
-
|
|
33260
|
-
|
|
33261
|
-
|
|
33262
|
-
|
|
33263
|
-
|
|
33264
|
-
|
|
33265
|
-
|
|
33266
|
-
|
|
33267
|
-
|
|
33268
|
-
}
|
|
33712
|
+
if (edge.curve.kind === "circle") {
|
|
33713
|
+
const center = new Vector3(...edge.curve.center).applyMatrix4(meshMatrixWorld);
|
|
33714
|
+
const axis = new Vector3(...edge.curve.axis).transformDirection(meshMatrixWorld).normalize();
|
|
33715
|
+
const radius = edge.curve.radius * scale;
|
|
33716
|
+
return {
|
|
33717
|
+
kind: "edge",
|
|
33718
|
+
start,
|
|
33719
|
+
end,
|
|
33720
|
+
length: 2 * Math.PI * radius,
|
|
33721
|
+
// circumference
|
|
33722
|
+
direction: [axis.x, axis.y, axis.z],
|
|
33723
|
+
meshUuid,
|
|
33724
|
+
curve: { kind: "circle", center: [center.x, center.y, center.z], axis: [axis.x, axis.y, axis.z], radius, faceName: edge.curve.faceName },
|
|
33725
|
+
polyline
|
|
33726
|
+
};
|
|
33269
33727
|
}
|
|
33728
|
+
const dir = new Vector3(end[0] - start[0], end[1] - start[1], end[2] - start[2]);
|
|
33729
|
+
const direction = dir.lengthSq() > 0 ? dir.normalize() : new Vector3(1, 0, 0);
|
|
33270
33730
|
return {
|
|
33271
|
-
|
|
33272
|
-
|
|
33273
|
-
|
|
33731
|
+
kind: "edge",
|
|
33732
|
+
start,
|
|
33733
|
+
end,
|
|
33734
|
+
length: edge.length * scale,
|
|
33735
|
+
direction: [direction.x, direction.y, direction.z],
|
|
33736
|
+
meshUuid,
|
|
33737
|
+
curve: { kind: "unidentified" },
|
|
33738
|
+
polyline
|
|
33739
|
+
};
|
|
33740
|
+
}
|
|
33741
|
+
const MEASURE_COLORS = {
|
|
33742
|
+
face: "#60b8ff",
|
|
33743
|
+
// hover face — gentle blue
|
|
33744
|
+
edge: "#ffffff",
|
|
33745
|
+
// hover edge — white
|
|
33746
|
+
vertex: "#ffffff",
|
|
33747
|
+
// hover vertex — white
|
|
33748
|
+
selection: "#ffa040",
|
|
33749
|
+
// both selections — warm amber
|
|
33750
|
+
line: "#ffa040",
|
|
33751
|
+
panelLabel: "#888",
|
|
33752
|
+
panelValue: "#ffd060"
|
|
33753
|
+
};
|
|
33754
|
+
function edgeEntityFromLocalPolyline(localPts, meshMatrixWorld, meshUuid) {
|
|
33755
|
+
const world = localPts.map((p2) => p2.clone().applyMatrix4(meshMatrixWorld));
|
|
33756
|
+
const start = world[0];
|
|
33757
|
+
const end = world[world.length - 1];
|
|
33758
|
+
let length = 0;
|
|
33759
|
+
const segments = [];
|
|
33760
|
+
for (let i = 1; i < world.length; i++) {
|
|
33761
|
+
length += world[i].distanceTo(world[i - 1]);
|
|
33762
|
+
segments.push([world[i - 1], world[i]]);
|
|
33763
|
+
}
|
|
33764
|
+
const dirVec = end.clone().sub(start);
|
|
33765
|
+
const direction = dirVec.lengthSq() > 0 ? dirVec.normalize() : new Vector3(1, 0, 0);
|
|
33766
|
+
const isLine = world.length <= 2;
|
|
33767
|
+
const entity = {
|
|
33768
|
+
kind: "edge",
|
|
33769
|
+
start: [start.x, start.y, start.z],
|
|
33770
|
+
end: [end.x, end.y, end.z],
|
|
33771
|
+
length,
|
|
33772
|
+
direction: [direction.x, direction.y, direction.z],
|
|
33773
|
+
meshUuid,
|
|
33774
|
+
curve: isLine ? { kind: "line", start: [start.x, start.y, start.z], end: [end.x, end.y, end.z] } : { kind: "unidentified" },
|
|
33775
|
+
polyline: isLine ? void 0 : world.map((p2) => [p2.x, p2.y, p2.z])
|
|
33274
33776
|
};
|
|
33777
|
+
return { entity, preview: { kind: "edge", edgeSegments: segments, meshUuid } };
|
|
33275
33778
|
}
|
|
33276
33779
|
function computeMeasureResult(a2, b2) {
|
|
33277
33780
|
const v32 = (xyz) => new Vector3(...xyz);
|
|
@@ -33413,9 +33916,11 @@ function MeasureTool() {
|
|
|
33413
33916
|
const measureMode = useForgeStore((s) => s.measureMode);
|
|
33414
33917
|
const measureSelections = useForgeStore((s) => s.measureSelections);
|
|
33415
33918
|
const addMeasureSelection = useForgeStore((s) => s.addMeasureSelection);
|
|
33919
|
+
const replaceLastMeasureSelection = useForgeStore((s) => s.replaceLastMeasureSelection);
|
|
33416
33920
|
const { camera, raycaster, scene, gl } = useThree();
|
|
33417
33921
|
const [hover, setHover] = reactExports.useState(null);
|
|
33418
33922
|
const pointerDownRef = reactExports.useRef(null);
|
|
33923
|
+
const lastSelectUpRef = reactExports.useRef(null);
|
|
33419
33924
|
const [selectionVisuals, setSelectionVisuals] = reactExports.useState({ geos: [], matrices: [], edgeSegments: [], vertexPositions: [] });
|
|
33420
33925
|
reactExports.useEffect(() => {
|
|
33421
33926
|
const geos = [];
|
|
@@ -33443,7 +33948,15 @@ function MeasureTool() {
|
|
|
33443
33948
|
} else if (sel.kind === "edge") {
|
|
33444
33949
|
geos.push(null);
|
|
33445
33950
|
matrices.push(null);
|
|
33446
|
-
|
|
33951
|
+
if (sel.polyline && sel.polyline.length >= 2) {
|
|
33952
|
+
const segs = [];
|
|
33953
|
+
for (let i = 1; i < sel.polyline.length; i++) {
|
|
33954
|
+
segs.push([new Vector3(...sel.polyline[i - 1]), new Vector3(...sel.polyline[i])]);
|
|
33955
|
+
}
|
|
33956
|
+
edgeSegs.push(segs);
|
|
33957
|
+
} else {
|
|
33958
|
+
edgeSegs.push([[new Vector3(...sel.start), new Vector3(...sel.end)]]);
|
|
33959
|
+
}
|
|
33447
33960
|
vertexPos.push(null);
|
|
33448
33961
|
} else {
|
|
33449
33962
|
geos.push(null);
|
|
@@ -33491,7 +34004,7 @@ function MeasureTool() {
|
|
|
33491
34004
|
[gl.domElement]
|
|
33492
34005
|
);
|
|
33493
34006
|
const detectEntity = reactExports.useCallback(
|
|
33494
|
-
(event) => {
|
|
34007
|
+
(event, wholePath = false) => {
|
|
33495
34008
|
if (!measureMode) return null;
|
|
33496
34009
|
const pointer = getPointerNDC(event);
|
|
33497
34010
|
raycaster.setFromCamera(new Vector2(pointer.x, pointer.y), camera);
|
|
@@ -33521,58 +34034,89 @@ function MeasureTool() {
|
|
|
33521
34034
|
const vA = new Vector3().fromBufferAttribute(positions, ia).applyMatrix4(mesh.matrixWorld);
|
|
33522
34035
|
const vB = new Vector3().fromBufferAttribute(positions, ib).applyMatrix4(mesh.matrixWorld);
|
|
33523
34036
|
const vC = new Vector3().fromBufferAttribute(positions, ic).applyMatrix4(mesh.matrixWorld);
|
|
33524
|
-
|
|
33525
|
-
|
|
33526
|
-
|
|
33527
|
-
|
|
33528
|
-
|
|
33529
|
-
|
|
33530
|
-
|
|
33531
|
-
|
|
33532
|
-
|
|
33533
|
-
|
|
33534
|
-
|
|
33535
|
-
|
|
33536
|
-
|
|
33537
|
-
|
|
33538
|
-
|
|
33539
|
-
|
|
33540
|
-
|
|
33541
|
-
|
|
33542
|
-
|
|
33543
|
-
|
|
33544
|
-
|
|
33545
|
-
|
|
33546
|
-
|
|
33547
|
-
|
|
33548
|
-
|
|
33549
|
-
|
|
33550
|
-
|
|
33551
|
-
|
|
33552
|
-
|
|
33553
|
-
|
|
33554
|
-
|
|
33555
|
-
|
|
33556
|
-
|
|
33557
|
-
|
|
34037
|
+
const invWorld = new Matrix4().copy(mesh.matrixWorld).invert();
|
|
34038
|
+
const localHit = hit.point.clone().applyMatrix4(invWorld);
|
|
34039
|
+
const bakedEdges = getBakedEdges(geometry);
|
|
34040
|
+
if (bakedEdges.length > 0) {
|
|
34041
|
+
if (!wholePath) {
|
|
34042
|
+
let cornerWorld = null;
|
|
34043
|
+
let cornerDist = SNAP_PX;
|
|
34044
|
+
for (const e2 of bakedEdges) {
|
|
34045
|
+
if (e2.curve.kind !== "line") continue;
|
|
34046
|
+
for (const pt2 of [e2.curve.start, e2.curve.end]) {
|
|
34047
|
+
const w = new Vector3(pt2[0], pt2[1], pt2[2]).applyMatrix4(mesh.matrixWorld);
|
|
34048
|
+
const s = worldToScreen2D(w);
|
|
34049
|
+
const d = Math.hypot(screenX - s.x, screenY - s.y);
|
|
34050
|
+
if (d < cornerDist) {
|
|
34051
|
+
cornerDist = d;
|
|
34052
|
+
cornerWorld = w;
|
|
34053
|
+
}
|
|
34054
|
+
}
|
|
34055
|
+
}
|
|
34056
|
+
if (cornerWorld) {
|
|
34057
|
+
const entity2 = {
|
|
34058
|
+
kind: "vertex",
|
|
34059
|
+
position: [cornerWorld.x, cornerWorld.y, cornerWorld.z],
|
|
34060
|
+
meshUuid: mesh.uuid
|
|
34061
|
+
};
|
|
34062
|
+
return { entity: entity2, preview: { kind: "vertex", vertexPosition: cornerWorld.clone() } };
|
|
34063
|
+
}
|
|
34064
|
+
}
|
|
34065
|
+
const carrierEdge = getNearestEdge(geometry, localHit, Number.POSITIVE_INFINITY);
|
|
34066
|
+
if (carrierEdge && (!wholePath || carrierEdge.edge.curve.kind === "circle")) {
|
|
34067
|
+
const worldPt = carrierEdge.point.clone().applyMatrix4(mesh.matrixWorld);
|
|
34068
|
+
const sp = worldToScreen2D(worldPt);
|
|
34069
|
+
if (Math.hypot(screenX - sp.x, screenY - sp.y) < SNAP_PX * 1.5) {
|
|
34070
|
+
const entity2 = edgeEntityFromBakedEdge(carrierEdge.edge, mesh.matrixWorld, mesh.uuid);
|
|
34071
|
+
const segs = [];
|
|
34072
|
+
const p2 = carrierEdge.edge.points;
|
|
34073
|
+
for (let i = 0; i + 5 < p2.length; i += 3) {
|
|
34074
|
+
segs.push([
|
|
34075
|
+
new Vector3(p2[i], p2[i + 1], p2[i + 2]).applyMatrix4(mesh.matrixWorld),
|
|
34076
|
+
new Vector3(p2[i + 3], p2[i + 4], p2[i + 5]).applyMatrix4(mesh.matrixWorld)
|
|
34077
|
+
]);
|
|
34078
|
+
}
|
|
34079
|
+
return { entity: entity2, preview: { kind: "edge", edgeSegments: segs, meshUuid: mesh.uuid } };
|
|
34080
|
+
}
|
|
34081
|
+
}
|
|
34082
|
+
} else if (!wholePath) {
|
|
34083
|
+
let closestVertexDist = Infinity;
|
|
34084
|
+
let closestVertex = null;
|
|
34085
|
+
for (const v of [vA, vB, vC]) {
|
|
34086
|
+
const s = worldToScreen2D(v);
|
|
34087
|
+
const d = Math.hypot(screenX - s.x, screenY - s.y);
|
|
34088
|
+
if (d < closestVertexDist && d < SNAP_PX) {
|
|
34089
|
+
closestVertexDist = d;
|
|
34090
|
+
closestVertex = v;
|
|
34091
|
+
}
|
|
34092
|
+
}
|
|
34093
|
+
if (closestVertex) {
|
|
33558
34094
|
const entity2 = {
|
|
33559
|
-
kind: "
|
|
33560
|
-
|
|
33561
|
-
end: [edgeResult.end.x, edgeResult.end.y, edgeResult.end.z],
|
|
33562
|
-
length: edgeResult.start.distanceTo(edgeResult.end),
|
|
33563
|
-
direction: [dir.x, dir.y, dir.z],
|
|
34095
|
+
kind: "vertex",
|
|
34096
|
+
position: [closestVertex.x, closestVertex.y, closestVertex.z],
|
|
33564
34097
|
meshUuid: mesh.uuid
|
|
33565
34098
|
};
|
|
33566
|
-
|
|
33567
|
-
|
|
33568
|
-
|
|
33569
|
-
|
|
33570
|
-
|
|
33571
|
-
|
|
33572
|
-
|
|
34099
|
+
return { entity: entity2, preview: { kind: "vertex", vertexPosition: closestVertex.clone() } };
|
|
34100
|
+
}
|
|
34101
|
+
}
|
|
34102
|
+
const chain = wholePath ? getWholeEdgePath(geometry, localHit) : getNearestEdgeChain(geometry, localHit);
|
|
34103
|
+
if (chain && chain.length >= 2) {
|
|
34104
|
+
const closest = new Vector3();
|
|
34105
|
+
const tmp = new Vector3();
|
|
34106
|
+
let nearestSq = Infinity;
|
|
34107
|
+
for (let i = 1; i < chain.length; i++) {
|
|
34108
|
+
const dSq = closestOnSegment(localHit, chain[i - 1], chain[i], tmp);
|
|
34109
|
+
if (dSq < nearestSq) {
|
|
34110
|
+
nearestSq = dSq;
|
|
34111
|
+
closest.copy(tmp);
|
|
34112
|
+
}
|
|
34113
|
+
}
|
|
34114
|
+
const screenPt = worldToScreen2D(closest.clone().applyMatrix4(mesh.matrixWorld));
|
|
34115
|
+
if (Math.hypot(screenX - screenPt.x, screenY - screenPt.y) < SNAP_PX * 1.5) {
|
|
34116
|
+
return edgeEntityFromLocalPolyline(chain, mesh.matrixWorld, mesh.uuid);
|
|
33573
34117
|
}
|
|
33574
34118
|
}
|
|
33575
|
-
const ffResult =
|
|
34119
|
+
const ffResult = getFaceRegion(geometry, faceIndex);
|
|
33576
34120
|
const worldNormal = ffResult.normal.clone().transformDirection(mesh.matrixWorld).normalize();
|
|
33577
34121
|
const worldCenter = ffResult.center.clone().applyMatrix4(mesh.matrixWorld);
|
|
33578
34122
|
const highlightGeo = buildFaceHighlightGeometry(geometry, ffResult.triangleIndices);
|
|
@@ -33636,17 +34180,31 @@ function MeasureTool() {
|
|
|
33636
34180
|
const down = pointerDownRef.current;
|
|
33637
34181
|
pointerDownRef.current = null;
|
|
33638
34182
|
if (!down || down.moved) return;
|
|
34183
|
+
const clearHover = () => setHover((prev) => {
|
|
34184
|
+
var _a4;
|
|
34185
|
+
(_a4 = prev == null ? void 0 : prev.faceHighlightGeo) == null ? void 0 : _a4.dispose();
|
|
34186
|
+
return null;
|
|
34187
|
+
});
|
|
34188
|
+
const now = performance.now();
|
|
34189
|
+
const last2 = lastSelectUpRef.current;
|
|
34190
|
+
const isDouble = !!last2 && last2.added && now - last2.t < 300 && Math.hypot(event.clientX - last2.x, event.clientY - last2.y) < 6;
|
|
34191
|
+
if (isDouble) {
|
|
34192
|
+
const whole = detectEntity(event, true);
|
|
34193
|
+
if (whole) {
|
|
34194
|
+
replaceLastMeasureSelection(whole.entity);
|
|
34195
|
+
clearHover();
|
|
34196
|
+
}
|
|
34197
|
+
lastSelectUpRef.current = null;
|
|
34198
|
+
return;
|
|
34199
|
+
}
|
|
33639
34200
|
const result = detectEntity(event);
|
|
33640
34201
|
if (result) {
|
|
33641
34202
|
addMeasureSelection(result.entity);
|
|
33642
|
-
|
|
33643
|
-
var _a4;
|
|
33644
|
-
(_a4 = prev == null ? void 0 : prev.faceHighlightGeo) == null ? void 0 : _a4.dispose();
|
|
33645
|
-
return null;
|
|
33646
|
-
});
|
|
34203
|
+
clearHover();
|
|
33647
34204
|
}
|
|
34205
|
+
lastSelectUpRef.current = { t: now, x: event.clientX, y: event.clientY, added: !!result };
|
|
33648
34206
|
},
|
|
33649
|
-
[addMeasureSelection, detectEntity, measureMode]
|
|
34207
|
+
[addMeasureSelection, detectEntity, measureMode, replaceLastMeasureSelection]
|
|
33650
34208
|
);
|
|
33651
34209
|
reactExports.useEffect(() => {
|
|
33652
34210
|
if (!measureMode) {
|
|
@@ -33859,15 +34417,31 @@ function MeasureInfoPanel() {
|
|
|
33859
34417
|
] });
|
|
33860
34418
|
}
|
|
33861
34419
|
if (sel.kind === "edge") {
|
|
34420
|
+
const circle = circleMeasurement(sel.curve);
|
|
33862
34421
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fc-viewport-floating-panel fc-measure-info-panel", style: PANEL_STYLE, children: [
|
|
33863
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontWeight: 600, marginBottom: 6, color: MEASURE_COLORS.selection }, children: "Edge" }),
|
|
33864
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
33865
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
33866
|
-
|
|
33867
|
-
|
|
33868
|
-
|
|
33869
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
33870
|
-
|
|
34422
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontWeight: 600, marginBottom: 6, color: MEASURE_COLORS.selection }, children: circle ? "Circular Edge" : "Edge" }),
|
|
34423
|
+
circle ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
34424
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: ROW_STYLE, children: [
|
|
34425
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelLabel }, children: "Radius" }),
|
|
34426
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelValue, fontWeight: 600, fontSize: 14 }, children: formatLength(circle.radius, lengthUnit) })
|
|
34427
|
+
] }),
|
|
34428
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: ROW_STYLE, children: [
|
|
34429
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelLabel }, children: "Diameter" }),
|
|
34430
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelValue, fontWeight: 600, fontSize: 14 }, children: formatLength(circle.diameter, lengthUnit) })
|
|
34431
|
+
] }),
|
|
34432
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: ROW_STYLE, children: [
|
|
34433
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelLabel }, children: "Circumference" }),
|
|
34434
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelValue }, children: formatLength(circle.circumference, lengthUnit) })
|
|
34435
|
+
] })
|
|
34436
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
34437
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: ROW_STYLE, children: [
|
|
34438
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelLabel }, children: "Length" }),
|
|
34439
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelValue }, children: formatLength(sel.length, lengthUnit) })
|
|
34440
|
+
] }),
|
|
34441
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: ROW_STYLE, children: [
|
|
34442
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: MEASURE_COLORS.panelLabel }, children: "Direction" }),
|
|
34443
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: fmtNormal(sel.direction) })
|
|
34444
|
+
] })
|
|
33871
34445
|
] }),
|
|
33872
34446
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginTop: 6, fontSize: 10, color: MEASURE_COLORS.panelLabel }, children: "Click another entity to measure" })
|
|
33873
34447
|
] });
|
|
@@ -37456,10 +38030,10 @@ function slotMaxWidth(slot, occupied, inset) {
|
|
|
37456
38030
|
if (occupied.has(opposite)) return `min(390px, calc(50% - ${inset + 4}px))`;
|
|
37457
38031
|
return `min(390px, calc(100% - ${inset * 2}px))`;
|
|
37458
38032
|
}
|
|
37459
|
-
function ViewportOverlayHost({ entries, inset = 12, gap = 8 }) {
|
|
38033
|
+
function ViewportOverlayHost({ entries: entries2, inset = 12, gap = 8 }) {
|
|
37460
38034
|
const grouped = /* @__PURE__ */ new Map();
|
|
37461
38035
|
const occupied = /* @__PURE__ */ new Set();
|
|
37462
|
-
for (const entry of
|
|
38036
|
+
for (const entry of entries2) {
|
|
37463
38037
|
if (entry.content === null || entry.content === void 0 || typeof entry.content === "boolean") continue;
|
|
37464
38038
|
occupied.add(entry.slot);
|
|
37465
38039
|
const slotEntries = grouped.get(entry.slot) ?? [];
|
|
@@ -38530,7 +39104,7 @@ const computeExplodeTreeOffsets = (root, explodeAmount, explodeConfig) => {
|
|
|
38530
39104
|
if (Math.abs(config.amount) <= 1e-8) return {};
|
|
38531
39105
|
const rootCenter = explodeBoundsCenter(root.bounds) ?? [0, 0, 0];
|
|
38532
39106
|
const offsets = {};
|
|
38533
|
-
const
|
|
39107
|
+
const walk2 = (node, depth, inherited, parentCenter, parentDirection) => {
|
|
38534
39108
|
const center = explodeBoundsCenter(node.bounds) ?? parentCenter;
|
|
38535
39109
|
const directive = resolveExplodeDirective([node.path.join("/")], node.label, void 0, config);
|
|
38536
39110
|
const motion = depth > 1 && node.children.length === 0 && !hasExplodeOverride(directive) ? (() => {
|
|
@@ -38554,9 +39128,9 @@ const computeExplodeTreeOffsets = (root, explodeAmount, explodeConfig) => {
|
|
|
38554
39128
|
node.objectIds.forEach((objectId) => {
|
|
38555
39129
|
offsets[objectId] = total;
|
|
38556
39130
|
});
|
|
38557
|
-
node.children.forEach((child) =>
|
|
39131
|
+
node.children.forEach((child) => walk2(child, depth + 1, total, center, motion.branchDirection));
|
|
38558
39132
|
};
|
|
38559
|
-
root.children.forEach((child) =>
|
|
39133
|
+
root.children.forEach((child) => walk2(child, 1, [0, 0, 0], rootCenter, void 0));
|
|
38560
39134
|
return offsets;
|
|
38561
39135
|
};
|
|
38562
39136
|
const EMPTY_RIG_INSPECTION_OVERLAY_STATE = {
|
|
@@ -38936,6 +39510,7 @@ function useViewportState() {
|
|
|
38936
39510
|
const lengthUnit = useForgeStore((s) => s.lengthUnit);
|
|
38937
39511
|
const constructionGhost = useForgeStore((s) => s.constructionGhost);
|
|
38938
39512
|
const dimensionsVisible = useForgeStore((s) => s.dimensionsVisible);
|
|
39513
|
+
const paramAnchorsVisible = useForgeStore((s) => s.paramAnchorsVisible);
|
|
38939
39514
|
const attachmentsVisible = useForgeStore((s) => s.attachmentsVisible);
|
|
38940
39515
|
const _surfacesVisible = useForgeStore((s) => s.surfacesVisible);
|
|
38941
39516
|
const cutPlaneEnabled = useForgeStore((s) => s.cutPlaneEnabled);
|
|
@@ -39356,6 +39931,7 @@ function useViewportState() {
|
|
|
39356
39931
|
renderLabels,
|
|
39357
39932
|
debugHighlights3D,
|
|
39358
39933
|
dimensionsVisible,
|
|
39934
|
+
paramAnchorsVisible,
|
|
39359
39935
|
attachmentsVisible,
|
|
39360
39936
|
attachmentPoints,
|
|
39361
39937
|
cutPlaneEnabled,
|
|
@@ -39474,6 +40050,48 @@ function shouldRequestInitialFit({
|
|
|
39474
40050
|
if (initialFitRequested && previousPreviewFile === previewFile) return false;
|
|
39475
40051
|
return true;
|
|
39476
40052
|
}
|
|
40053
|
+
const SUBFEATURE_SNAP_PX = 14;
|
|
40054
|
+
const CLICK_DRAG_TOLERANCE_PX = 4;
|
|
40055
|
+
function worldToScreenPx(pt2, camera, rect) {
|
|
40056
|
+
const p2 = pt2.clone().project(camera);
|
|
40057
|
+
return {
|
|
40058
|
+
x: (p2.x * 0.5 + 0.5) * rect.width + rect.left,
|
|
40059
|
+
y: (-p2.y * 0.5 + 0.5) * rect.height + rect.top
|
|
40060
|
+
};
|
|
40061
|
+
}
|
|
40062
|
+
function drillIntoSubFeature(mesh, worldHit, camera, rect, clientX, clientY, objectId, store) {
|
|
40063
|
+
const geometry = mesh.geometry;
|
|
40064
|
+
const inv = new Matrix4().copy(mesh.matrixWorld).invert();
|
|
40065
|
+
const localHit = worldHit.clone().applyMatrix4(inv);
|
|
40066
|
+
const vtx = getNearestVertex(geometry, localHit, Number.POSITIVE_INFINITY);
|
|
40067
|
+
if (vtx) {
|
|
40068
|
+
const worldPt = vtx.point.clone().applyMatrix4(mesh.matrixWorld);
|
|
40069
|
+
const s = worldToScreenPx(worldPt, camera, rect);
|
|
40070
|
+
if (Math.hypot(clientX - s.x, clientY - s.y) <= SUBFEATURE_SNAP_PX) {
|
|
40071
|
+
store.setSelectedEdge(null);
|
|
40072
|
+
store.setSelectedVertex({ objectId, point: [vtx.point.x, vtx.point.y, vtx.point.z] });
|
|
40073
|
+
return true;
|
|
40074
|
+
}
|
|
40075
|
+
}
|
|
40076
|
+
const edge = getNearestEdge(geometry, localHit, Number.POSITIVE_INFINITY);
|
|
40077
|
+
if (edge) {
|
|
40078
|
+
const worldPt = edge.point.clone().applyMatrix4(mesh.matrixWorld);
|
|
40079
|
+
const s = worldToScreenPx(worldPt, camera, rect);
|
|
40080
|
+
if (Math.hypot(clientX - s.x, clientY - s.y) <= SUBFEATURE_SNAP_PX) {
|
|
40081
|
+
store.setSelectedVertex(null);
|
|
40082
|
+
store.setSelectedEdge({
|
|
40083
|
+
objectId,
|
|
40084
|
+
faceA: edge.edge.faceA,
|
|
40085
|
+
faceB: edge.edge.faceB,
|
|
40086
|
+
curve: edge.edge.curve,
|
|
40087
|
+
length: edge.edge.length,
|
|
40088
|
+
points: edge.edge.points
|
|
40089
|
+
});
|
|
40090
|
+
return true;
|
|
40091
|
+
}
|
|
40092
|
+
}
|
|
40093
|
+
return false;
|
|
40094
|
+
}
|
|
39477
40095
|
function useViewportHandlers({
|
|
39478
40096
|
containerRef,
|
|
39479
40097
|
contextMenuRef,
|
|
@@ -39505,6 +40123,14 @@ function useViewportHandlers({
|
|
|
39505
40123
|
const [faceInfoData, setFaceInfoData] = reactExports.useState(null);
|
|
39506
40124
|
const [faceInfoLoading, setFaceInfoLoading] = reactExports.useState(false);
|
|
39507
40125
|
const [sketchEntityInfo, setSketchEntityInfo] = reactExports.useState(null);
|
|
40126
|
+
const pointerDownPosRef = reactExports.useRef(null);
|
|
40127
|
+
reactExports.useEffect(() => {
|
|
40128
|
+
const onDown = (e2) => {
|
|
40129
|
+
pointerDownPosRef.current = { x: e2.clientX, y: e2.clientY };
|
|
40130
|
+
};
|
|
40131
|
+
window.addEventListener("pointerdown", onDown, { capture: true });
|
|
40132
|
+
return () => window.removeEventListener("pointerdown", onDown, { capture: true });
|
|
40133
|
+
}, []);
|
|
39508
40134
|
const closeObjectContextMenu = reactExports.useCallback(() => {
|
|
39509
40135
|
setObjectContextMenu(null);
|
|
39510
40136
|
}, []);
|
|
@@ -39558,40 +40184,56 @@ function useViewportHandlers({
|
|
|
39558
40184
|
hideHoverTooltip();
|
|
39559
40185
|
setHoveredObjectId(null);
|
|
39560
40186
|
}, [hideHoverTooltip, objectPickSyncEnabled, setHoveredObjectId]);
|
|
39561
|
-
reactExports.
|
|
39562
|
-
|
|
39563
|
-
|
|
39564
|
-
|
|
39565
|
-
|
|
39566
|
-
|
|
39567
|
-
|
|
39568
|
-
|
|
39569
|
-
|
|
39570
|
-
|
|
39571
|
-
|
|
39572
|
-
|
|
39573
|
-
|
|
39574
|
-
|
|
39575
|
-
|
|
39576
|
-
|
|
39577
|
-
|
|
39578
|
-
|
|
39579
|
-
|
|
39580
|
-
|
|
39581
|
-
|
|
39582
|
-
|
|
39583
|
-
|
|
39584
|
-
|
|
39585
|
-
|
|
39586
|
-
|
|
39587
|
-
|
|
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();
|
|
39588
40219
|
}
|
|
39589
|
-
|
|
40220
|
+
return true;
|
|
40221
|
+
}
|
|
40222
|
+
if (store.constructionGhost !== null) {
|
|
40223
|
+
store.setConstructionGhost(null);
|
|
40224
|
+
return true;
|
|
40225
|
+
}
|
|
40226
|
+
if (store.focusedObjectIds.length > 0) {
|
|
39590
40227
|
clearFocusedObject();
|
|
39591
|
-
|
|
39592
|
-
|
|
39593
|
-
|
|
40228
|
+
return true;
|
|
40229
|
+
}
|
|
40230
|
+
if (store.selectedObjectId) {
|
|
40231
|
+
store.selectObject(null);
|
|
40232
|
+
return true;
|
|
40233
|
+
}
|
|
40234
|
+
return false;
|
|
39594
40235
|
}, [clearFocusedObject, closeObjectContextMenu, faceInfoPanel, objectContextMenu, sketchEntityInfo]);
|
|
40236
|
+
useEscapeAction(handleViewportEscape, { active: true, label: "Viewport selection", priority: ESCAPE_PRIORITY.viewport });
|
|
39595
40237
|
reactExports.useEffect(() => {
|
|
39596
40238
|
const handleViewShortcut = (event) => {
|
|
39597
40239
|
if (event.isComposing || event.repeat) return;
|
|
@@ -39681,11 +40323,42 @@ function useViewportHandlers({
|
|
|
39681
40323
|
);
|
|
39682
40324
|
const handleObjectClick = reactExports.useCallback(
|
|
39683
40325
|
(obj, event) => {
|
|
40326
|
+
var _a3;
|
|
39684
40327
|
if (!objectPickSyncEnabled || measureMode || isViewportInteracting) return;
|
|
40328
|
+
const down = pointerDownPosRef.current;
|
|
40329
|
+
if (down && Math.hypot(event.clientX - down.x, event.clientY - down.y) > CLICK_DRAG_TOLERANCE_PX) return;
|
|
39685
40330
|
event.stopPropagation();
|
|
39686
|
-
|
|
40331
|
+
const store = useForgeStore.getState();
|
|
40332
|
+
const alreadySelected = store.selectedObjectId === obj.id;
|
|
40333
|
+
if (!alreadySelected) {
|
|
40334
|
+
selectObject(obj.id);
|
|
40335
|
+
setFaceInfoPanel(null);
|
|
40336
|
+
return;
|
|
40337
|
+
}
|
|
40338
|
+
const rectForPick = (_a3 = containerRef.current) == null ? void 0 : _a3.getBoundingClientRect();
|
|
40339
|
+
if (event.object instanceof Mesh && rectForPick && event.point && drillIntoSubFeature(event.object, event.point, event.camera, rectForPick, event.clientX, event.clientY, obj.id, store)) {
|
|
40340
|
+
store.setSelectedFace(null);
|
|
40341
|
+
setFaceInfoPanel(null);
|
|
40342
|
+
return;
|
|
40343
|
+
}
|
|
40344
|
+
const triangleIndex = event.faceIndex ?? (event.face ? Math.floor(event.face.a / 3) : null);
|
|
40345
|
+
if (event.object instanceof Mesh && triangleIndex !== null && triangleIndex >= 0) {
|
|
40346
|
+
store.setSelectedEdge(null);
|
|
40347
|
+
store.setSelectedVertex(null);
|
|
40348
|
+
const region = getFaceRegion(event.object.geometry, triangleIndex);
|
|
40349
|
+
const worldNormal = region.normal.clone().transformDirection(event.object.matrixWorld).normalize();
|
|
40350
|
+
const worldCenter = region.center.clone().applyMatrix4(event.object.matrixWorld);
|
|
40351
|
+
store.setSelectedFace({
|
|
40352
|
+
objectId: obj.id,
|
|
40353
|
+
carrierName: region.carrierName,
|
|
40354
|
+
triangleIndices: region.triangleIndices,
|
|
40355
|
+
meshUuid: event.object.uuid,
|
|
40356
|
+
normal: [worldNormal.x, worldNormal.y, worldNormal.z],
|
|
40357
|
+
center: [worldCenter.x, worldCenter.y, worldCenter.z]
|
|
40358
|
+
});
|
|
40359
|
+
}
|
|
39687
40360
|
},
|
|
39688
|
-
[isViewportInteracting, measureMode, objectPickSyncEnabled, selectObject]
|
|
40361
|
+
[containerRef, isViewportInteracting, measureMode, objectPickSyncEnabled, selectObject, setFaceInfoPanel]
|
|
39689
40362
|
);
|
|
39690
40363
|
const handleObjectDoubleClick = reactExports.useCallback(
|
|
39691
40364
|
(obj, event) => {
|
|
@@ -39693,8 +40366,13 @@ function useViewportHandlers({
|
|
|
39693
40366
|
event.stopPropagation();
|
|
39694
40367
|
const additive = event.shiftKey || event.metaKey || event.ctrlKey;
|
|
39695
40368
|
focusObject(obj.id, { additive });
|
|
40369
|
+
const store = useForgeStore.getState();
|
|
40370
|
+
store.setSelectedFace(null);
|
|
40371
|
+
store.setSelectedEdge(null);
|
|
40372
|
+
store.setSelectedVertex(null);
|
|
40373
|
+
setFaceInfoPanel(null);
|
|
39696
40374
|
},
|
|
39697
|
-
[focusObject, isViewportInteracting, measureMode]
|
|
40375
|
+
[focusObject, isViewportInteracting, measureMode, setFaceInfoPanel]
|
|
39698
40376
|
);
|
|
39699
40377
|
const handleObjectContextMenu = reactExports.useCallback(
|
|
39700
40378
|
(obj, event) => {
|
|
@@ -40400,7 +41078,7 @@ const HoverTooltipLayer = reactExports.forwardRef(function HoverTooltipLayer2({
|
|
|
40400
41078
|
}
|
|
40401
41079
|
);
|
|
40402
41080
|
});
|
|
40403
|
-
const buttonStyle = {
|
|
41081
|
+
const buttonStyle$3 = {
|
|
40404
41082
|
border: "1px solid var(--fc-border)",
|
|
40405
41083
|
background: "var(--fc-btn)",
|
|
40406
41084
|
color: "var(--fc-text)",
|
|
@@ -40411,7 +41089,7 @@ const buttonStyle = {
|
|
|
40411
41089
|
lineHeight: 1
|
|
40412
41090
|
};
|
|
40413
41091
|
const iconButtonStyle = {
|
|
40414
|
-
...buttonStyle,
|
|
41092
|
+
...buttonStyle$3,
|
|
40415
41093
|
width: 30,
|
|
40416
41094
|
height: 30,
|
|
40417
41095
|
padding: 0,
|
|
@@ -40563,7 +41241,7 @@ function ModelJourneyBar({
|
|
|
40563
41241
|
padding: 4
|
|
40564
41242
|
},
|
|
40565
41243
|
children: [
|
|
40566
|
-
/* @__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: [
|
|
40567
41245
|
"Explore: ",
|
|
40568
41246
|
label
|
|
40569
41247
|
] }),
|
|
@@ -40657,7 +41335,7 @@ function ModelJourneyBar({
|
|
|
40657
41335
|
"button",
|
|
40658
41336
|
{
|
|
40659
41337
|
type: "button",
|
|
40660
|
-
style: buttonStyle,
|
|
41338
|
+
style: buttonStyle$3,
|
|
40661
41339
|
onClick: () => {
|
|
40662
41340
|
setActive({ ...active, interrupted: false });
|
|
40663
41341
|
applyStep(activeStep);
|
|
@@ -40665,7 +41343,7 @@ function ModelJourneyBar({
|
|
|
40665
41343
|
children: "Resume"
|
|
40666
41344
|
}
|
|
40667
41345
|
),
|
|
40668
|
-
/* @__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" })
|
|
40669
41347
|
] }),
|
|
40670
41348
|
caption && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginTop: 6, paddingLeft: 76, fontSize: 12, color: "var(--fc-textDim)" }, children: caption })
|
|
40671
41349
|
]
|
|
@@ -40796,6 +41474,1819 @@ function RigInspectionOverlay({ state: state2, config }) {
|
|
|
40796
41474
|
state2.joints.map((joint) => /* @__PURE__ */ jsxRuntimeExports.jsx(HoveredJointOverlay, { state: joint, config: joint.hidden ? hiddenConfig : config }, joint.joint.name))
|
|
40797
41475
|
] });
|
|
40798
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
|
+
}
|
|
40799
43290
|
const IDLE_STATE = {
|
|
40800
43291
|
status: "idle",
|
|
40801
43292
|
error: null,
|
|
@@ -40965,7 +43456,7 @@ function useGeometryComparison(args) {
|
|
|
40965
43456
|
}, [args.activeBackend, args.candidate, args.enabled, args.files, args.previewFile, args.quality]);
|
|
40966
43457
|
return state2;
|
|
40967
43458
|
}
|
|
40968
|
-
const PARALLEL_CHANNELS = /* @__PURE__ */ new Set(["thickness", "roughness"]);
|
|
43459
|
+
const PARALLEL_CHANNELS = /* @__PURE__ */ new Set(["thickness", "throughThickness", "roughness"]);
|
|
40969
43460
|
const MAX_INSPECT_WORKERS = Math.max(
|
|
40970
43461
|
1,
|
|
40971
43462
|
Math.min((typeof navigator !== "undefined" && navigator.hardwareConcurrency ? navigator.hardwareConcurrency : 4) - 1, 8)
|
|
@@ -41021,7 +43512,7 @@ function mergeResults(results, channel, analysisId) {
|
|
|
41021
43512
|
class InspectWorkerClient {
|
|
41022
43513
|
constructor(workerFactory = () => new Worker(new URL(
|
|
41023
43514
|
/* @vite-ignore */
|
|
41024
|
-
"/assets/inspectWorker-
|
|
43515
|
+
"/assets/inspectWorker-Cuby2qfT.js",
|
|
41025
43516
|
import.meta.url
|
|
41026
43517
|
), { type: "module" })) {
|
|
41027
43518
|
__publicField(this, "reqId", 0);
|
|
@@ -41075,8 +43566,16 @@ class InspectWorkerClient {
|
|
|
41075
43566
|
}
|
|
41076
43567
|
}
|
|
41077
43568
|
const inspectWorkerClient = new InspectWorkerClient();
|
|
41078
|
-
const WORKER_CHANNELS = /* @__PURE__ */ new Set([
|
|
41079
|
-
|
|
43569
|
+
const WORKER_CHANNELS = /* @__PURE__ */ new Set([
|
|
43570
|
+
"thickness",
|
|
43571
|
+
"throughThickness",
|
|
43572
|
+
"roughness",
|
|
43573
|
+
"connectivity",
|
|
43574
|
+
"floating",
|
|
43575
|
+
"distance",
|
|
43576
|
+
"collisions"
|
|
43577
|
+
]);
|
|
43578
|
+
const SCALAR_WORKER_CHANNELS = /* @__PURE__ */ new Set(["thickness", "throughThickness", "roughness"]);
|
|
41080
43579
|
const MESH_COMPONENT_WORKER_CHANNELS = /* @__PURE__ */ new Set(["connectivity", "floating", "distance"]);
|
|
41081
43580
|
const IDENTITY_MATRIX_ELEMENTS = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
41082
43581
|
const INSPECT_BUILD_YIELD_INTERVAL_MS = 8;
|
|
@@ -41211,14 +43710,14 @@ function addCandidate(candidates, candidate) {
|
|
|
41211
43710
|
}
|
|
41212
43711
|
if (candidate.bboxOverlapVolume > candidates[smallestIndex].bboxOverlapVolume) candidates[smallestIndex] = candidate;
|
|
41213
43712
|
}
|
|
41214
|
-
async function collectCollisionCandidates(
|
|
43713
|
+
async function collectCollisionCandidates(entries2, maybeYield) {
|
|
41215
43714
|
const candidates = [];
|
|
41216
43715
|
let candidateCount = 0;
|
|
41217
43716
|
let prunedPairCount = 0;
|
|
41218
|
-
for (let sourceIndex = 0; sourceIndex <
|
|
41219
|
-
const source =
|
|
41220
|
-
for (let targetIndex = sourceIndex + 1; targetIndex <
|
|
41221
|
-
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];
|
|
41222
43721
|
if (!aabbOverlaps(source.worldBounds, target.worldBounds)) continue;
|
|
41223
43722
|
const bboxOverlapVolume = aabbOverlapVolume(source.worldBounds, target.worldBounds);
|
|
41224
43723
|
if (bboxOverlapVolume <= DEFAULT_COLLISION_INSPECTION_OPTIONS.minOverlapVolume) {
|
|
@@ -41265,7 +43764,7 @@ async function cloneCollisionShapePayloadForWorker(shape, maybeYield) {
|
|
|
41265
43764
|
async function buildCollisionWorkerObjects(args) {
|
|
41266
43765
|
var _a3;
|
|
41267
43766
|
const warnings = [];
|
|
41268
|
-
const
|
|
43767
|
+
const entries2 = [];
|
|
41269
43768
|
const maybeYield = createBuildYield(args.isCancelled);
|
|
41270
43769
|
for (const obj of args.objects) {
|
|
41271
43770
|
if (args.isCancelled()) throw new InspectBuildCancelledError();
|
|
@@ -41275,10 +43774,10 @@ async function buildCollisionWorkerObjects(args) {
|
|
|
41275
43774
|
const local = localBounds(obj);
|
|
41276
43775
|
const world = transformedBounds(obj, matrix);
|
|
41277
43776
|
if (!local || !world) continue;
|
|
41278
|
-
|
|
43777
|
+
entries2.push({ obj, matrix, localBounds: local, worldBounds: world });
|
|
41279
43778
|
await maybeYield();
|
|
41280
43779
|
}
|
|
41281
|
-
const { candidates, candidateCount, prunedPairCount } = await collectCollisionCandidates(
|
|
43780
|
+
const { candidates, candidateCount, prunedPairCount } = await collectCollisionCandidates(entries2, maybeYield);
|
|
41282
43781
|
if (candidateCount === 0) {
|
|
41283
43782
|
return {
|
|
41284
43783
|
objects: [],
|
|
@@ -41310,7 +43809,7 @@ async function buildCollisionWorkerObjects(args) {
|
|
|
41310
43809
|
const out = [];
|
|
41311
43810
|
let payloadBytes = 0;
|
|
41312
43811
|
for (const entryIndex of selectedIndexes) {
|
|
41313
|
-
const entry =
|
|
43812
|
+
const entry = entries2[entryIndex];
|
|
41314
43813
|
const collisionShape = await cloneCollisionShapePayloadForWorker(entry.obj.shape, maybeYield);
|
|
41315
43814
|
const nextBytes = collisionPayloadBytes(collisionShape);
|
|
41316
43815
|
if (payloadBytes + nextBytes > VIEWPORT_COLLISION_MAX_PAYLOAD_BYTES) {
|
|
@@ -41386,6 +43885,15 @@ function analyzePayloadFor(channel, objects, inspectPointSampleCount, groundZ, w
|
|
|
41386
43885
|
}
|
|
41387
43886
|
};
|
|
41388
43887
|
}
|
|
43888
|
+
if (channel === "throughThickness") {
|
|
43889
|
+
return {
|
|
43890
|
+
channel,
|
|
43891
|
+
objects,
|
|
43892
|
+
throughThickness: {
|
|
43893
|
+
maxSamplesPerObject
|
|
43894
|
+
}
|
|
43895
|
+
};
|
|
43896
|
+
}
|
|
41389
43897
|
if (channel === "roughness") {
|
|
41390
43898
|
return {
|
|
41391
43899
|
channel,
|
|
@@ -41413,6 +43921,10 @@ function resultToState(channel, result) {
|
|
|
41413
43921
|
normals: object.normals,
|
|
41414
43922
|
index: object.index,
|
|
41415
43923
|
aValue: object.aValue,
|
|
43924
|
+
uvs: object.uvs,
|
|
43925
|
+
textureValues: object.textureValues,
|
|
43926
|
+
textureWidth: object.textureWidth,
|
|
43927
|
+
textureHeight: object.textureHeight,
|
|
41416
43928
|
valueMin: object.valueMin,
|
|
41417
43929
|
valueMax: object.valueMax,
|
|
41418
43930
|
capped: object.capped,
|
|
@@ -41743,6 +44255,24 @@ function panelNumber(value) {
|
|
|
41743
44255
|
function vec3Label(value) {
|
|
41744
44256
|
return `[${value.map(panelNumber).join(", ")}]`;
|
|
41745
44257
|
}
|
|
44258
|
+
function localPointToWorld(point, matrix) {
|
|
44259
|
+
const v = new Vector3(point[0], point[1], point[2]).applyMatrix4(matrix);
|
|
44260
|
+
return [v.x, v.y, v.z];
|
|
44261
|
+
}
|
|
44262
|
+
function localDirToWorld(dir, matrix) {
|
|
44263
|
+
const normal = new Matrix3().getNormalMatrix(matrix);
|
|
44264
|
+
const v = new Vector3(dir[0], dir[1], dir[2]).applyMatrix3(normal).normalize();
|
|
44265
|
+
return [v.x, v.y, v.z];
|
|
44266
|
+
}
|
|
44267
|
+
function worldScaleFactor(matrix) {
|
|
44268
|
+
const det = matrix.determinant();
|
|
44269
|
+
const scale = Math.cbrt(Math.abs(det));
|
|
44270
|
+
return Number.isFinite(scale) && scale > 0 ? scale : 1;
|
|
44271
|
+
}
|
|
44272
|
+
function copyNumberToClipboard(value) {
|
|
44273
|
+
var _a3;
|
|
44274
|
+
void ((_a3 = navigator.clipboard) == null ? void 0 : _a3.writeText(panelNumber(value)));
|
|
44275
|
+
}
|
|
41746
44276
|
function edgeCurveLabel(edge) {
|
|
41747
44277
|
var _a3;
|
|
41748
44278
|
switch ((_a3 = edge.curve) == null ? void 0 : _a3.kind) {
|
|
@@ -41778,6 +44308,8 @@ function inspectChannelLabel(channel) {
|
|
|
41778
44308
|
return "Comparison";
|
|
41779
44309
|
case "thickness":
|
|
41780
44310
|
return "Thickness";
|
|
44311
|
+
case "throughThickness":
|
|
44312
|
+
return "Minimum Solid Span";
|
|
41781
44313
|
case "roughness":
|
|
41782
44314
|
return "Roughness";
|
|
41783
44315
|
default:
|
|
@@ -41993,6 +44525,11 @@ function Viewport() {
|
|
|
41993
44525
|
const runQuality = useForgeStore((s) => s.runQuality);
|
|
41994
44526
|
const buildLedgerEvents = useForgeStore((s) => s.buildLedgerEvents);
|
|
41995
44527
|
const measureSelections = useForgeStore((s) => s.measureSelections);
|
|
44528
|
+
const selectedFace = useForgeStore((s) => s.selectedFace);
|
|
44529
|
+
const selectedEdge = useForgeStore((s) => s.selectedEdge);
|
|
44530
|
+
const selectedVertex = useForgeStore((s) => s.selectedVertex);
|
|
44531
|
+
const setSelectedEdge = useForgeStore((s) => s.setSelectedEdge);
|
|
44532
|
+
const setSelectedVertex = useForgeStore((s) => s.setSelectedVertex);
|
|
41996
44533
|
const meshPreviewFile = useForgeStore((s) => s.meshPreviewFile);
|
|
41997
44534
|
const voxelIntentMode = useForgeStore((s) => s.voxelIntentMode);
|
|
41998
44535
|
const voxelIntentTool = useForgeStore((s) => s.voxelIntentTool);
|
|
@@ -42053,6 +44590,7 @@ function Viewport() {
|
|
|
42053
44590
|
renderLabels,
|
|
42054
44591
|
debugHighlights3D,
|
|
42055
44592
|
dimensionsVisible,
|
|
44593
|
+
paramAnchorsVisible,
|
|
42056
44594
|
attachmentsVisible,
|
|
42057
44595
|
attachmentPoints,
|
|
42058
44596
|
sectionPlaneGuidesEnabled,
|
|
@@ -42176,15 +44714,35 @@ function Viewport() {
|
|
|
42176
44714
|
groundZ: inspectGroundZ
|
|
42177
44715
|
});
|
|
42178
44716
|
const scalarChannelInfo = reactExports.useMemo(() => {
|
|
42179
|
-
if (inspectChannel === "thickness") {
|
|
44717
|
+
if (inspectChannel === "thickness" || inspectChannel === "throughThickness") {
|
|
42180
44718
|
return { unitLabel: "mm", criticalThreshold: DEFAULT_THICKNESS_INSPECTION_OPTIONS.minThickness };
|
|
42181
44719
|
}
|
|
42182
44720
|
if (inspectChannel === "roughness") return { unitLabel: "deg", criticalThreshold: null };
|
|
42183
44721
|
return null;
|
|
42184
44722
|
}, [inspectChannel]);
|
|
44723
|
+
const measuredThicknessMax = reactExports.useMemo(() => {
|
|
44724
|
+
if (inspectChannel !== "thickness" && inspectChannel !== "throughThickness") return null;
|
|
44725
|
+
let max2 = -Infinity;
|
|
44726
|
+
for (const surface of Object.values(inspectAnalysis.scalarSurfaces)) {
|
|
44727
|
+
if (Number.isFinite(surface.valueMax)) max2 = Math.max(max2, surface.valueMax);
|
|
44728
|
+
}
|
|
44729
|
+
return Number.isFinite(max2) ? max2 : null;
|
|
44730
|
+
}, [inspectAnalysis.scalarSurfaces, inspectChannel]);
|
|
44731
|
+
const effectiveThicknessColorRange = reactExports.useMemo(() => {
|
|
44732
|
+
const isDefaultRange = thicknessColorRange.min === DEFAULT_THICKNESS_COLOR_RANGE.min && thicknessColorRange.max === DEFAULT_THICKNESS_COLOR_RANGE.max;
|
|
44733
|
+
if (isDefaultRange && measuredThicknessMax != null && measuredThicknessMax > thicknessColorRange.max) {
|
|
44734
|
+
return { min: thicknessColorRange.min, max: measuredThicknessMax };
|
|
44735
|
+
}
|
|
44736
|
+
return thicknessColorRange;
|
|
44737
|
+
}, [measuredThicknessMax, thicknessColorRange]);
|
|
42185
44738
|
const inspectColorScale = reactExports.useMemo(
|
|
42186
|
-
() => ({
|
|
42187
|
-
|
|
44739
|
+
() => ({
|
|
44740
|
+
colormap: inspectColormap,
|
|
44741
|
+
domainMin: effectiveThicknessColorRange.min,
|
|
44742
|
+
domainMax: effectiveThicknessColorRange.max,
|
|
44743
|
+
...(inspectChannel === "thickness" || inspectChannel === "throughThickness") && inspectColormap !== "thickness-classic" ? { reversed: true } : {}
|
|
44744
|
+
}),
|
|
44745
|
+
[effectiveThicknessColorRange.max, effectiveThicknessColorRange.min, inspectChannel, inspectColormap]
|
|
42188
44746
|
);
|
|
42189
44747
|
const inspectScalarParams = reactExports.useMemo(() => {
|
|
42190
44748
|
if (!scalarChannelInfo) return void 0;
|
|
@@ -42195,11 +44753,12 @@ function Viewport() {
|
|
|
42195
44753
|
isolineSpacing: inspectIsolineSpacing,
|
|
42196
44754
|
criticalEnabled: inspectCriticalLineEnabled,
|
|
42197
44755
|
criticalThreshold: scalarChannelInfo.criticalThreshold,
|
|
42198
|
-
shadingEnabled:
|
|
44756
|
+
shadingEnabled: inspectChannel !== "thickness" && inspectChannel !== "throughThickness"
|
|
42199
44757
|
};
|
|
42200
44758
|
}, [
|
|
42201
44759
|
scalarChannelInfo,
|
|
42202
44760
|
inspectColorScale,
|
|
44761
|
+
inspectChannel,
|
|
42203
44762
|
inspectQuantizeBands,
|
|
42204
44763
|
inspectIsolinesEnabled,
|
|
42205
44764
|
inspectIsolineSpacing,
|
|
@@ -42440,7 +44999,7 @@ function Viewport() {
|
|
|
42440
44999
|
displayMode: inspectDisplayMode,
|
|
42441
45000
|
warnings: inspectWarnings,
|
|
42442
45001
|
swatches: inspectLegendSwatches,
|
|
42443
|
-
thicknessColorRange,
|
|
45002
|
+
thicknessColorRange: effectiveThicknessColorRange,
|
|
42444
45003
|
onThicknessColorRangeChange: setThicknessColorRange,
|
|
42445
45004
|
colorScale: scalarChannelInfo ? inspectColorScale : void 0,
|
|
42446
45005
|
unitLabel: scalarChannelInfo == null ? void 0 : scalarChannelInfo.unitLabel,
|
|
@@ -42674,6 +45233,9 @@ function Viewport() {
|
|
|
42674
45233
|
sectionPlanes: objectSectionPlanesById[obj.id] ?? EMPTY_SECTION_PLANES,
|
|
42675
45234
|
sectionPreviewRenderOrderBase: 2e3 + objIndex * 64,
|
|
42676
45235
|
debugHighlightColor: shapeHl == null ? void 0 : shapeHl.color,
|
|
45236
|
+
selectedFaceTriangleIndices: (selectedFace == null ? void 0 : selectedFace.objectId) === obj.id ? selectedFace.triangleIndices : null,
|
|
45237
|
+
selectedEdgePoints: (selectedEdge == null ? void 0 : selectedEdge.objectId) === obj.id ? selectedEdge.points : null,
|
|
45238
|
+
selectedVertexPoint: (selectedVertex == null ? void 0 : selectedVertex.objectId) === obj.id ? selectedVertex.point : null,
|
|
42677
45239
|
onPointerEnter: (event) => updateHoverLabel(obj, event),
|
|
42678
45240
|
onPointerMove: (event) => updateHoverLabel(obj, event),
|
|
42679
45241
|
onPointerLeave: (event) => clearHoverLabel(obj, event),
|
|
@@ -42808,6 +45370,7 @@ function Viewport() {
|
|
|
42808
45370
|
!rigInspectActive && hoveredJointOverlay && /* @__PURE__ */ jsxRuntimeExports.jsx(HoveredJointOverlay, { state: hoveredJointOverlay, config: jointOverlayConfig }),
|
|
42809
45371
|
!rigInspectActive && dimensionsVisible && dimensions.map((d) => /* @__PURE__ */ jsxRuntimeExports.jsx(DimensionAnnotation, { def: d, lengthUnit }, d.id)),
|
|
42810
45372
|
!rigInspectActive && /* @__PURE__ */ jsxRuntimeExports.jsx(RenderLabelsOverlay, { labels: renderLabels }),
|
|
45373
|
+
!rigInspectActive && paramAnchorsVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(SpatialParamAnchorsOverlay, {}),
|
|
42811
45374
|
!rigInspectActive && attachmentsVisible !== "none" && attachmentPoints.map((ap) => {
|
|
42812
45375
|
const matrix = objectMatrices[ap.objectId];
|
|
42813
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}`);
|
|
@@ -43018,6 +45581,7 @@ function Viewport() {
|
|
|
43018
45581
|
}
|
|
43019
45582
|
),
|
|
43020
45583
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ViewportOverlayHost, { entries: overlayEntries }),
|
|
45584
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(SpatialParamExpandedSheetOverlay, {}),
|
|
43021
45585
|
viewportDisabledMessage && /* @__PURE__ */ jsxRuntimeExports.jsx(ViewportDisabledOverlay, { title: viewportDisabledMessage.title, body: viewportDisabledMessage.body }),
|
|
43022
45586
|
drawFlagEnabled && /* @__PURE__ */ jsxRuntimeExports.jsx(DrawToolbar, {}),
|
|
43023
45587
|
/* @__PURE__ */ jsxRuntimeExports.jsx(HoverTooltipLayer, { ref: hoverTooltipRef, enabled: objectPickSyncEnabled && !measureMode && !voxelIntentMode }),
|
|
@@ -43129,6 +45693,7 @@ function Viewport() {
|
|
|
43129
45693
|
),
|
|
43130
45694
|
viewportPortalHost && faceInfoPanel && reactDomExports.createPortal(
|
|
43131
45695
|
(() => {
|
|
45696
|
+
var _a4;
|
|
43132
45697
|
const obj = objects.find((o2) => o2.id === faceInfoPanel.objectId);
|
|
43133
45698
|
if (!obj) return null;
|
|
43134
45699
|
const activeFaceName = faceInfoPanel.faceName;
|
|
@@ -43139,8 +45704,8 @@ function Viewport() {
|
|
|
43139
45704
|
const history = activeFaceName ? (faceInfoData == null ? void 0 : faceInfoData.faceHistories[activeFaceName]) ?? null : null;
|
|
43140
45705
|
const faceNames = (faceInfoData == null ? void 0 : faceInfoData.faceNames) ?? [];
|
|
43141
45706
|
const activeEdges = activeFaceName && faceInfoData ? (faceInfoData.edgeNames ?? []).map((name) => {
|
|
43142
|
-
var
|
|
43143
|
-
return (
|
|
45707
|
+
var _a5;
|
|
45708
|
+
return (_a5 = faceInfoData.edges) == null ? void 0 : _a5[name];
|
|
43144
45709
|
}).filter((edge) => Boolean(edge) && edgeBelongsToFace(edge, activeFaceName)) : [];
|
|
43145
45710
|
const visibleEdges = activeEdges.slice(0, 12);
|
|
43146
45711
|
const viewportMaxRight = Math.min(viewportScreenRight, viewportWindowWidth);
|
|
@@ -43171,23 +45736,47 @@ function Viewport() {
|
|
|
43171
45736
|
children: [
|
|
43172
45737
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 4 }, children: [
|
|
43173
45738
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600, fontSize: 13 }, children: "Surface History" }),
|
|
43174
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
43175
|
-
|
|
43176
|
-
|
|
43177
|
-
|
|
43178
|
-
|
|
43179
|
-
|
|
43180
|
-
|
|
43181
|
-
|
|
43182
|
-
|
|
43183
|
-
|
|
43184
|
-
|
|
43185
|
-
|
|
43186
|
-
|
|
43187
|
-
|
|
43188
|
-
|
|
43189
|
-
|
|
43190
|
-
|
|
45739
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
45740
|
+
activeFaceName && activeFace && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45741
|
+
"button",
|
|
45742
|
+
{
|
|
45743
|
+
type: "button",
|
|
45744
|
+
title: `Copy face("${activeFaceName}") selector to clipboard`,
|
|
45745
|
+
onClick: () => {
|
|
45746
|
+
var _a5;
|
|
45747
|
+
void ((_a5 = navigator.clipboard) == null ? void 0 : _a5.writeText(`face(${JSON.stringify(activeFaceName)})`));
|
|
45748
|
+
},
|
|
45749
|
+
style: {
|
|
45750
|
+
border: "1px solid var(--fc-border)",
|
|
45751
|
+
background: "transparent",
|
|
45752
|
+
color: "var(--fc-textMuted)",
|
|
45753
|
+
cursor: "pointer",
|
|
45754
|
+
fontSize: 11,
|
|
45755
|
+
lineHeight: 1,
|
|
45756
|
+
borderRadius: 4,
|
|
45757
|
+
padding: "3px 6px"
|
|
45758
|
+
},
|
|
45759
|
+
children: "Copy selector"
|
|
45760
|
+
}
|
|
45761
|
+
),
|
|
45762
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45763
|
+
"button",
|
|
45764
|
+
{
|
|
45765
|
+
type: "button",
|
|
45766
|
+
onClick: () => setFaceInfoPanel(null),
|
|
45767
|
+
style: {
|
|
45768
|
+
border: "none",
|
|
45769
|
+
background: "transparent",
|
|
45770
|
+
color: "var(--fc-textMuted)",
|
|
45771
|
+
cursor: "pointer",
|
|
45772
|
+
fontSize: 16,
|
|
45773
|
+
lineHeight: 1,
|
|
45774
|
+
padding: 0
|
|
45775
|
+
},
|
|
45776
|
+
children: "×"
|
|
45777
|
+
}
|
|
45778
|
+
)
|
|
45779
|
+
] })
|
|
43191
45780
|
] }),
|
|
43192
45781
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43193
45782
|
"div",
|
|
@@ -43274,6 +45863,33 @@ function Viewport() {
|
|
|
43274
45863
|
]
|
|
43275
45864
|
}
|
|
43276
45865
|
),
|
|
45866
|
+
((_a4 = history == null ? void 0 : history.origin) == null ? void 0 : _a4.sourceSpan) && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
45867
|
+
"div",
|
|
45868
|
+
{
|
|
45869
|
+
style: {
|
|
45870
|
+
marginBottom: 10,
|
|
45871
|
+
padding: 8,
|
|
45872
|
+
border: "1px solid var(--fc-border)",
|
|
45873
|
+
borderRadius: 6,
|
|
45874
|
+
background: "color-mix(in srgb, var(--fc-bgInput) 70%, transparent)"
|
|
45875
|
+
},
|
|
45876
|
+
children: [
|
|
45877
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: 11, fontWeight: 600, marginBottom: 3 }, children: "Source" }),
|
|
45878
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45879
|
+
"div",
|
|
45880
|
+
{
|
|
45881
|
+
style: {
|
|
45882
|
+
fontSize: 10,
|
|
45883
|
+
color: "var(--fc-textMuted)",
|
|
45884
|
+
fontFamily: "monospace",
|
|
45885
|
+
wordBreak: "break-all"
|
|
45886
|
+
},
|
|
45887
|
+
children: `${history.origin.sourceSpan.fileName}:${history.origin.sourceSpan.line}:${history.origin.sourceSpan.column}`
|
|
45888
|
+
}
|
|
45889
|
+
)
|
|
45890
|
+
]
|
|
45891
|
+
}
|
|
45892
|
+
),
|
|
43277
45893
|
history && history.timeline.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: history.timeline.map((entry, i) => {
|
|
43278
45894
|
const isFirst = i === 0;
|
|
43279
45895
|
const isLast = i === history.timeline.length - 1;
|
|
@@ -43489,12 +46105,164 @@ function Viewport() {
|
|
|
43489
46105
|
]
|
|
43490
46106
|
}
|
|
43491
46107
|
);
|
|
46108
|
+
})(),
|
|
46109
|
+
(selectedEdge || selectedVertex) && (() => {
|
|
46110
|
+
const objectId = (selectedEdge == null ? void 0 : selectedEdge.objectId) ?? (selectedVertex == null ? void 0 : selectedVertex.objectId) ?? "";
|
|
46111
|
+
const obj = objects.find((o2) => o2.id === objectId);
|
|
46112
|
+
const matrix = objectMatrices[objectId] ?? new Matrix4();
|
|
46113
|
+
const scale = worldScaleFactor(matrix);
|
|
46114
|
+
let title = "";
|
|
46115
|
+
const rows = [];
|
|
46116
|
+
let note = null;
|
|
46117
|
+
if (selectedVertex) {
|
|
46118
|
+
title = "Vertex";
|
|
46119
|
+
const world = localPointToWorld(selectedVertex.point, matrix);
|
|
46120
|
+
rows.push(["Coordinate", formatCoord(world, lengthUnit)]);
|
|
46121
|
+
rows.push(["X", formatLength(world[0], lengthUnit, 3), world[0]]);
|
|
46122
|
+
rows.push(["Y", formatLength(world[1], lengthUnit, 3), world[1]]);
|
|
46123
|
+
rows.push(["Z", formatLength(world[2], lengthUnit, 3), world[2]]);
|
|
46124
|
+
} else if (selectedEdge) {
|
|
46125
|
+
const { curve } = selectedEdge;
|
|
46126
|
+
if (curve.kind === "circle") {
|
|
46127
|
+
title = "Edge — Circle";
|
|
46128
|
+
const radius = curve.radius * scale;
|
|
46129
|
+
const center = localPointToWorld(curve.center, matrix);
|
|
46130
|
+
const axis = localDirToWorld(curve.axis, matrix);
|
|
46131
|
+
rows.push(["Radius", formatLength(radius, lengthUnit, 3), radius]);
|
|
46132
|
+
rows.push(["Diameter", formatLength(radius * 2, lengthUnit, 3), radius * 2]);
|
|
46133
|
+
rows.push(["Center", formatCoord(center, lengthUnit)]);
|
|
46134
|
+
rows.push(["Axis", vec3Label(axis)]);
|
|
46135
|
+
} else if (curve.kind === "line") {
|
|
46136
|
+
title = "Edge — Line";
|
|
46137
|
+
const start = localPointToWorld(curve.start, matrix);
|
|
46138
|
+
const end = localPointToWorld(curve.end, matrix);
|
|
46139
|
+
const dir = new Vector3(end[0] - start[0], end[1] - start[1], end[2] - start[2]).normalize();
|
|
46140
|
+
const length = selectedEdge.length * scale;
|
|
46141
|
+
rows.push(["Length", formatLength(length, lengthUnit, 3), length]);
|
|
46142
|
+
rows.push(["Direction", vec3Label([dir.x, dir.y, dir.z])]);
|
|
46143
|
+
rows.push(["Start", formatCoord(start, lengthUnit)]);
|
|
46144
|
+
rows.push(["End", formatCoord(end, lengthUnit)]);
|
|
46145
|
+
} else {
|
|
46146
|
+
title = "Edge — Non-analytic";
|
|
46147
|
+
const length = selectedEdge.length * scale;
|
|
46148
|
+
rows.push(["Length", formatLength(length, lengthUnit, 3), length]);
|
|
46149
|
+
note = "Non-analytic edge: length only (polyline). No analytic radius/axis.";
|
|
46150
|
+
}
|
|
46151
|
+
}
|
|
46152
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
46153
|
+
"div",
|
|
46154
|
+
{
|
|
46155
|
+
className: "fc-viewport-floating-panel fc-edge-info-panel",
|
|
46156
|
+
style: {
|
|
46157
|
+
position: "absolute",
|
|
46158
|
+
left: 16,
|
|
46159
|
+
bottom: 16,
|
|
46160
|
+
width: 248,
|
|
46161
|
+
background: "var(--fc-floating-panel-bg, var(--fc-bgPanel))",
|
|
46162
|
+
border: "1px solid var(--fc-floating-panel-border, var(--fc-border))",
|
|
46163
|
+
borderRadius: "var(--fc-floating-panel-radius, 8px)",
|
|
46164
|
+
boxShadow: "var(--fc-floating-panel-shadow, 0 12px 28px rgba(0, 0, 0, 0.28))",
|
|
46165
|
+
padding: 12,
|
|
46166
|
+
zIndex: 20,
|
|
46167
|
+
fontSize: 12,
|
|
46168
|
+
color: "var(--fc-text)"
|
|
46169
|
+
},
|
|
46170
|
+
onPointerDown: (e2) => e2.stopPropagation(),
|
|
46171
|
+
onContextMenu: (e2) => e2.preventDefault(),
|
|
46172
|
+
children: [
|
|
46173
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }, children: [
|
|
46174
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600, fontSize: 13 }, children: title }),
|
|
46175
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
46176
|
+
"button",
|
|
46177
|
+
{
|
|
46178
|
+
type: "button",
|
|
46179
|
+
onClick: () => {
|
|
46180
|
+
setSelectedEdge(null);
|
|
46181
|
+
setSelectedVertex(null);
|
|
46182
|
+
},
|
|
46183
|
+
style: {
|
|
46184
|
+
border: "none",
|
|
46185
|
+
background: "transparent",
|
|
46186
|
+
color: "var(--fc-textMuted)",
|
|
46187
|
+
cursor: "pointer",
|
|
46188
|
+
fontSize: 16,
|
|
46189
|
+
lineHeight: 1,
|
|
46190
|
+
padding: 0
|
|
46191
|
+
},
|
|
46192
|
+
children: "×"
|
|
46193
|
+
}
|
|
46194
|
+
)
|
|
46195
|
+
] }),
|
|
46196
|
+
obj && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
46197
|
+
"div",
|
|
46198
|
+
{
|
|
46199
|
+
style: {
|
|
46200
|
+
fontSize: 11,
|
|
46201
|
+
color: "var(--fc-textMuted)",
|
|
46202
|
+
marginBottom: 8,
|
|
46203
|
+
overflow: "hidden",
|
|
46204
|
+
textOverflow: "ellipsis",
|
|
46205
|
+
whiteSpace: "nowrap"
|
|
46206
|
+
},
|
|
46207
|
+
children: obj.treePath && obj.treePath.length > 0 ? obj.treePath.join(" / ") : obj.name
|
|
46208
|
+
}
|
|
46209
|
+
),
|
|
46210
|
+
rows.map(([label, value, raw]) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
46211
|
+
"div",
|
|
46212
|
+
{
|
|
46213
|
+
style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 8, marginBottom: 5 },
|
|
46214
|
+
children: [
|
|
46215
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--fc-textMuted)", fontSize: 11 }, children: label }),
|
|
46216
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { style: { display: "flex", alignItems: "baseline", gap: 6, minWidth: 0 }, children: [
|
|
46217
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontFamily: "monospace", fontSize: 11, textAlign: "right", wordBreak: "break-word" }, children: value }),
|
|
46218
|
+
raw !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
46219
|
+
"button",
|
|
46220
|
+
{
|
|
46221
|
+
type: "button",
|
|
46222
|
+
title: `Copy ${label.toLowerCase()} value (${panelNumber(raw)} mm) to clipboard`,
|
|
46223
|
+
onClick: () => copyNumberToClipboard(raw),
|
|
46224
|
+
style: {
|
|
46225
|
+
flexShrink: 0,
|
|
46226
|
+
border: "1px solid var(--fc-border)",
|
|
46227
|
+
background: "transparent",
|
|
46228
|
+
color: "var(--fc-textMuted)",
|
|
46229
|
+
cursor: "pointer",
|
|
46230
|
+
fontSize: 9,
|
|
46231
|
+
lineHeight: 1,
|
|
46232
|
+
borderRadius: 3,
|
|
46233
|
+
padding: "2px 4px"
|
|
46234
|
+
},
|
|
46235
|
+
children: "Copy"
|
|
46236
|
+
}
|
|
46237
|
+
)
|
|
46238
|
+
] })
|
|
46239
|
+
]
|
|
46240
|
+
},
|
|
46241
|
+
label
|
|
46242
|
+
)),
|
|
46243
|
+
note && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
46244
|
+
"div",
|
|
46245
|
+
{
|
|
46246
|
+
style: {
|
|
46247
|
+
marginTop: 6,
|
|
46248
|
+
paddingTop: 6,
|
|
46249
|
+
borderTop: "1px solid var(--fc-border)",
|
|
46250
|
+
fontSize: 10,
|
|
46251
|
+
color: "var(--fc-textMuted)",
|
|
46252
|
+
lineHeight: 1.35
|
|
46253
|
+
},
|
|
46254
|
+
children: note
|
|
46255
|
+
}
|
|
46256
|
+
)
|
|
46257
|
+
]
|
|
46258
|
+
}
|
|
46259
|
+
);
|
|
43492
46260
|
})()
|
|
43493
46261
|
]
|
|
43494
46262
|
}
|
|
43495
46263
|
);
|
|
43496
46264
|
}
|
|
43497
|
-
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 })));
|
|
43498
46266
|
const PENDING_SHARE_COPY_KEY = "fc-pending-share-copy";
|
|
43499
46267
|
function storePendingShareCopy(shareId) {
|
|
43500
46268
|
sessionStorage.setItem(PENDING_SHARE_COPY_KEY, shareId);
|
|
@@ -43760,17 +46528,17 @@ function SeoMetadata() {
|
|
|
43760
46528
|
}, [location.pathname]);
|
|
43761
46529
|
return null;
|
|
43762
46530
|
}
|
|
43763
|
-
reactExports.lazy(() => __vitePreload(() => import("./LandingPageProofDriven-
|
|
43764
|
-
const DocsPage = reactExports.lazy(() => __vitePreload(() => import("./DocsPage-
|
|
43765
|
-
reactExports.lazy(() => __vitePreload(() => import("./BlogPage-
|
|
43766
|
-
reactExports.lazy(() => __vitePreload(() => import("./BenchmarkPage-
|
|
43767
|
-
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 })));
|
|
43768
46536
|
reactExports.lazy(() => __vitePreload(() => Promise.resolve().then(() => PublishedModelPage$1), true ? void 0 : void 0).then((m2) => ({ default: m2.PublishedModelPage })));
|
|
43769
|
-
reactExports.lazy(() => __vitePreload(() => import("./SettingsPage-
|
|
43770
|
-
reactExports.lazy(() => __vitePreload(() => import("./PricingPage-
|
|
43771
|
-
reactExports.lazy(() => __vitePreload(() => import("./LegalPage-
|
|
43772
|
-
const EditorApp = reactExports.lazy(() => __vitePreload(() => import("./EditorApp-
|
|
43773
|
-
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 })));
|
|
43774
46542
|
const embedMode = isEmbedMode() && !window.location.pathname.startsWith("/m/");
|
|
43775
46543
|
const EDITABLE_CRASH_FILE = /\.(?:forge\.js|[cm]?[jt]sx?|json|md|txt|svg|dxf)$/i;
|
|
43776
46544
|
function firstMeaningfulLine(text) {
|
|
@@ -43990,73 +46758,81 @@ function App() {
|
|
|
43990
46758
|
applyTheme(localStorage.getItem("fc-theme") || "dark");
|
|
43991
46759
|
clientExports.createRoot(document.getElementById("root")).render(/* @__PURE__ */ jsxRuntimeExports.jsx(App, {}));
|
|
43992
46760
|
export {
|
|
43993
|
-
|
|
46761
|
+
formatArea as $,
|
|
43994
46762
|
AuthApiError as A,
|
|
43995
46763
|
BrandMark as B,
|
|
43996
|
-
|
|
43997
|
-
|
|
43998
|
-
|
|
46764
|
+
sanitizeExportStem as C,
|
|
46765
|
+
captureViewportImageBlobFromStore as D,
|
|
46766
|
+
ESCAPE_PRIORITY as E,
|
|
43999
46767
|
FLAG_DEFINITIONS as F,
|
|
44000
|
-
|
|
44001
|
-
|
|
44002
|
-
|
|
44003
|
-
|
|
44004
|
-
|
|
44005
|
-
|
|
44006
|
-
|
|
44007
|
-
|
|
44008
|
-
|
|
44009
|
-
|
|
44010
|
-
|
|
44011
|
-
|
|
44012
|
-
|
|
44013
|
-
|
|
44014
|
-
|
|
44015
|
-
|
|
44016
|
-
|
|
44017
|
-
|
|
44018
|
-
|
|
44019
|
-
|
|
44020
|
-
|
|
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 _,
|
|
44021
46789
|
applyTheme as a,
|
|
44022
|
-
|
|
44023
|
-
|
|
44024
|
-
|
|
44025
|
-
|
|
44026
|
-
|
|
44027
|
-
|
|
44028
|
-
|
|
44029
|
-
|
|
44030
|
-
|
|
44031
|
-
|
|
44032
|
-
|
|
44033
|
-
|
|
44034
|
-
|
|
44035
|
-
|
|
44036
|
-
|
|
44037
|
-
|
|
44038
|
-
|
|
44039
|
-
|
|
44040
|
-
|
|
44041
|
-
|
|
44042
|
-
|
|
44043
|
-
|
|
44044
|
-
|
|
44045
|
-
|
|
44046
|
-
|
|
44047
|
-
|
|
44048
|
-
|
|
44049
|
-
|
|
44050
|
-
|
|
44051
|
-
|
|
44052
|
-
|
|
44053
|
-
|
|
44054
|
-
|
|
44055
|
-
|
|
44056
|
-
|
|
44057
|
-
|
|
44058
|
-
|
|
44059
|
-
|
|
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,
|
|
44060
46836
|
authFetch as b,
|
|
44061
46837
|
authApi as c,
|
|
44062
46838
|
showToast as d,
|
|
@@ -44070,16 +46846,16 @@ export {
|
|
|
44070
46846
|
fetchGistModel as l,
|
|
44071
46847
|
monacoLanguageForProjectFile as m,
|
|
44072
46848
|
fetchUrlModel as n,
|
|
44073
|
-
|
|
44074
|
-
|
|
44075
|
-
|
|
46849
|
+
useEscapeAction as o,
|
|
46850
|
+
exportMeshFromStore as p,
|
|
46851
|
+
exportReportFromStore as q,
|
|
44076
46852
|
readProjectFilesFromDataTransfer as r,
|
|
44077
46853
|
storageUsagePercent as s,
|
|
44078
46854
|
triggerDownload as t,
|
|
44079
46855
|
useAuthStore as u,
|
|
44080
|
-
|
|
44081
|
-
|
|
44082
|
-
|
|
44083
|
-
|
|
44084
|
-
|
|
46856
|
+
exportViewportImageFromStore as v,
|
|
46857
|
+
exportOrbitVideoFromStore as w,
|
|
46858
|
+
exportSketchFromStore as x,
|
|
46859
|
+
buildGistShareUrl as y,
|
|
46860
|
+
deriveExportStem as z
|
|
44085
46861
|
};
|