@dailies-studio/shot-editor 0.1.0 → 0.2.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/.vite/manifest.json +382 -0
- package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
- package/dist/assets/AgentPanel-DJEphah0.css +1 -0
- package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
- package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
- package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
- package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
- package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
- package/dist/assets/CurvePane-BjFVabKX.css +1 -0
- package/dist/assets/CurvePane-R22WzllJ.js +1 -0
- package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
- package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
- package/dist/assets/FontPicker-v0IOIArM.css +1 -0
- package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
- package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
- package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
- package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
- package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
- package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
- package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
- package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
- package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
- package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
- package/dist/assets/SoundMode-BmcZru3i.js +3 -0
- package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
- package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
- package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
- package/dist/assets/board-B88EPuLO.css +1 -0
- package/dist/assets/board-CL-he7SL.js +3 -0
- package/dist/assets/components-0oOSPN0l.js +1 -0
- package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
- package/dist/assets/googleFonts-BNon6M7X.js +1 -0
- package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
- package/dist/assets/index-4F5NQmI8.css +1 -0
- package/dist/assets/index-BGHQGV5z.js +117 -0
- package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/assets/libraryModel-CpIwfFha.js +1 -0
- package/dist/assets/lucide-6iK64tTB.js +9319 -0
- package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
- package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
- package/dist/assets/webmcp-BsB9gxWC.js +2 -0
- package/dist/index.html +34 -3
- package/package.json +12 -9
- package/src/addFamilies.ts +66 -0
- package/src/api.ts +1631 -55
- package/src/app/App.tsx +977 -198
- package/src/app/lazyPanel.tsx +137 -0
- package/src/app/lazySurfaces.tsx +203 -0
- package/src/audio/index.tsx +41 -10
- package/src/audio/meter.ts +485 -0
- package/src/audio/meterMath.ts +273 -0
- package/src/audio/meterStore.ts +155 -0
- package/src/audio/offlineMix.ts +289 -0
- package/src/audio/preview.ts +928 -118
- package/src/bridge.ts +15 -5
- package/src/bridgeTypes.ts +1611 -36
- package/src/channels.ts +17 -32
- package/src/fonts/googleFonts.ts +4 -0
- package/src/hostApi.ts +174 -0
- package/src/icons.tsx +770 -0
- package/src/layerWords.ts +61 -0
- package/src/main.tsx +56 -4
- package/src/panels/ResizeHandle.tsx +33 -43
- package/src/panels/agent/AgentDots.tsx +36 -0
- package/src/panels/agent/AgentPanel.tsx +119 -43
- package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
- package/src/panels/agent/agent.css +113 -97
- package/src/panels/agent/agentGate.ts +35 -0
- package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
- package/src/panels/code/CodeStudio.tsx +373 -0
- package/src/panels/code/FileTree.tsx +265 -0
- package/src/panels/code/codeBufferGuard.ts +52 -0
- package/src/panels/code/codeParts.tsx +321 -0
- package/src/panels/comments/CommentComposer.tsx +239 -0
- package/src/panels/comments/CommentThread.tsx +346 -0
- package/src/panels/comments/CommentsPanel.tsx +336 -0
- package/src/panels/comments/InboxButton.tsx +131 -0
- package/src/panels/comments/MentionMenu.tsx +98 -0
- package/src/panels/comments/parts.tsx +171 -0
- package/src/panels/export/ExportDialog.tsx +919 -96
- package/src/panels/export/ExportScrim.tsx +57 -0
- package/src/panels/files/AssetsTab.tsx +656 -0
- package/src/panels/files/FilePanel.tsx +1466 -216
- package/src/panels/files/LayerMarks.tsx +40 -0
- package/src/panels/files/LeftColumn.tsx +37 -0
- package/src/panels/files/McpSection.tsx +690 -47
- package/src/panels/files/MissingAssetsNotice.tsx +60 -0
- package/src/panels/files/assetDrag.ts +79 -0
- package/src/panels/files/resourceParts.tsx +255 -0
- package/src/panels/fonts/FontPicker.tsx +762 -0
- package/src/panels/fonts/faceCache.ts +207 -0
- package/src/panels/fonts/fontPicker.css +271 -0
- package/src/panels/fonts/pickerModel.ts +265 -0
- package/src/panels/fonts/pickerRequest.ts +46 -0
- package/src/panels/header/AccountAvatar.tsx +76 -0
- package/src/panels/header/AgentChip.tsx +77 -0
- package/src/panels/header/AppMenu.tsx +213 -0
- package/src/panels/header/Header.tsx +564 -84
- package/src/panels/header/HistoryControls.tsx +31 -5
- package/src/panels/header/PresenceStack.tsx +329 -0
- package/src/panels/header/ProjectTitle.tsx +232 -0
- package/src/panels/header/ShareButton.tsx +59 -0
- package/src/panels/header/StatusBadges.tsx +358 -27
- package/src/panels/header/SyncDeletedDialog.tsx +88 -0
- package/src/panels/header/ZoomControls.tsx +46 -15
- package/src/panels/header/agentHandoff.ts +52 -0
- package/src/panels/header/leave.ts +26 -0
- package/src/panels/help/HelpSection.tsx +189 -163
- package/src/panels/insert/InsertDrawer.tsx +723 -0
- package/src/panels/insert/iconSet.ts +109 -0
- package/src/panels/inspector/ExposeDot.tsx +117 -0
- package/src/panels/inspector/FramingRows.tsx +314 -44
- package/src/panels/inspector/InspectorFooter.tsx +91 -0
- package/src/panels/inspector/InspectorHead.tsx +425 -0
- package/src/panels/inspector/InspectorPanel.tsx +408 -124
- package/src/panels/inspector/KeyframeDot.tsx +373 -0
- package/src/panels/inspector/KeyframeSection.tsx +257 -0
- package/src/panels/inspector/MarkerSection.tsx +55 -11
- package/src/panels/inspector/ParamRow.tsx +84 -53
- package/src/panels/inspector/RightColumn.tsx +152 -0
- package/src/panels/inspector/TimeSection.tsx +866 -0
- package/src/panels/inspector/animate/EaseControl.tsx +244 -0
- package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
- package/src/panels/inspector/animate/ShotSection.tsx +562 -48
- package/src/panels/inspector/design/AlignRow.tsx +95 -31
- package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
- package/src/panels/inspector/design/ClonerSection.tsx +323 -62
- package/src/panels/inspector/design/ComponentSection.tsx +50 -43
- package/src/panels/inspector/design/CornerSection.tsx +45 -14
- package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
- package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
- package/src/panels/inspector/design/DeviceSection.tsx +210 -0
- package/src/panels/inspector/design/FillSection.tsx +83 -51
- package/src/panels/inspector/design/FontField.tsx +196 -0
- package/src/panels/inspector/design/HtmlSection.tsx +23 -12
- package/src/panels/inspector/design/InstanceSection.tsx +373 -154
- package/src/panels/inspector/design/MaskSection.tsx +84 -0
- package/src/panels/inspector/design/MaterialSection.tsx +177 -0
- package/src/panels/inspector/design/MediaSection.tsx +273 -120
- package/src/panels/inspector/design/MeshSection.tsx +910 -82
- package/src/panels/inspector/design/PaintRows.tsx +550 -0
- package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
- package/src/panels/inspector/design/ShadowSection.tsx +221 -48
- package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
- package/src/panels/inspector/design/StrokeSection.tsx +228 -43
- package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
- package/src/panels/inspector/design/TransformSection.tsx +244 -50
- package/src/panels/inspector/design/TypographySection.tsx +679 -110
- package/src/panels/inspector/layout.ts +295 -0
- package/src/panels/inspector/propValue.ts +199 -0
- package/src/panels/inspector/scene/CameraSection.tsx +232 -22
- package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
- package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
- package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
- package/src/panels/inspector/scene/SceneSection.tsx +219 -44
- package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
- package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
- package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
- package/src/panels/inspector/timeModel.ts +190 -0
- package/src/panels/inspector/useDocWrite.ts +438 -146
- package/src/panels/library/LibraryBrowser.tsx +435 -0
- package/src/panels/library/LibraryGrid.tsx +190 -0
- package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
- package/src/panels/library/LibraryRail.tsx +84 -0
- package/src/panels/library/LibrarySoundPanel.tsx +293 -0
- package/src/panels/library/SoundHistory.tsx +245 -0
- package/src/panels/library/StockSounds.tsx +390 -0
- package/src/panels/library/libraryModel.ts +147 -0
- package/src/panels/library/stockSearch.ts +77 -0
- package/src/panels/maskMarks.tsx +117 -0
- package/src/panels/material/MaterialCommentNode.tsx +54 -0
- package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
- package/src/panels/material/MaterialGraphMenu.tsx +45 -0
- package/src/panels/material/MaterialGroupNode.tsx +56 -0
- package/src/panels/material/MaterialLibrary.tsx +253 -0
- package/src/panels/material/MaterialNode.tsx +453 -0
- package/src/panels/material/MaterialNodePalette.tsx +349 -0
- package/src/panels/material/MaterialParameterControls.tsx +525 -0
- package/src/panels/material/MaterialRerouteNode.tsx +42 -0
- package/src/panels/material/MaterialWorkspace.tsx +501 -0
- package/src/panels/material/NodePreview.tsx +70 -0
- package/src/panels/material/flowModel.ts +441 -0
- package/src/panels/material/parameterValues.ts +231 -0
- package/src/panels/material/previewScheduler.ts +235 -0
- package/src/panels/material/reactFlow.css +6 -0
- package/src/panels/material/socketGlyph.ts +85 -0
- package/src/panels/material/viewportMemory.ts +58 -0
- package/src/panels/preferences/PreferencesDialog.tsx +215 -0
- package/src/panels/share/ShareComponentDialog.tsx +247 -0
- package/src/panels/stage/StageMenu.tsx +35 -5
- package/src/panels/stage/StagePanel.tsx +338 -23
- package/src/panels/stage/cellSlots.tsx +70 -0
- package/src/panels/storyboard/BoardCanvas.tsx +739 -0
- package/src/panels/storyboard/BoardCursors.tsx +158 -0
- package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
- package/src/panels/storyboard/BoardPlan.tsx +42 -0
- package/src/panels/storyboard/ComponentSheet.tsx +187 -0
- package/src/panels/storyboard/ComponentStage.tsx +235 -0
- package/src/panels/storyboard/MiniSketch.tsx +173 -0
- package/src/panels/storyboard/PartPicker.tsx +64 -0
- package/src/panels/storyboard/PlanPanel.tsx +617 -0
- package/src/panels/storyboard/ReferencePreview.tsx +217 -0
- package/src/panels/storyboard/ShotBrief.tsx +360 -0
- package/src/panels/storyboard/ShotCard.tsx +251 -0
- package/src/panels/storyboard/StagePreview.tsx +348 -0
- package/src/panels/storyboard/StillThumbs.tsx +162 -0
- package/src/panels/storyboard/StoryboardView.tsx +79 -0
- package/src/panels/storyboard/board.css +1065 -0
- package/src/panels/storyboard/board.ts +12 -0
- package/src/panels/storyboard/boardClipboard.ts +111 -0
- package/src/panels/storyboard/boardOrder.ts +68 -0
- package/src/panels/storyboard/boardRows.tsx +425 -0
- package/src/panels/storyboard/liveCap.ts +90 -0
- package/src/panels/storyboard/plan.css +295 -0
- package/src/panels/storyboard/posterCache.ts +133 -0
- package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
- package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
- package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
- package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
- package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
- package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
- package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
- package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
- package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
- package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
- package/src/panels/storyboard/shotboard/sketch.css +1392 -0
- package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
- package/src/panels/storyboard/storyboardDrop.ts +164 -0
- package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
- package/src/panels/studio/ComponentSidePanel.tsx +355 -0
- package/src/panels/studio/ComponentStudio.tsx +243 -0
- package/src/panels/studio/ComponentsHome.tsx +318 -0
- package/src/panels/studio/ComponentsPreview.tsx +179 -0
- package/src/panels/studio/EveryUse.tsx +244 -0
- package/src/panels/studio/ThisUsePanel.tsx +679 -0
- package/src/panels/studio/componentParts.tsx +341 -0
- package/src/panels/studio/components.ts +6 -0
- package/src/panels/studio/newCodeComponent.ts +66 -0
- package/src/panels/timeline/AudioFxButton.tsx +290 -0
- package/src/panels/timeline/AudioLane.tsx +674 -85
- package/src/panels/timeline/BlockRow.tsx +154 -85
- package/src/panels/timeline/ChannelRow.tsx +378 -129
- package/src/panels/timeline/CurvePane.tsx +508 -0
- package/src/panels/timeline/Filmstrip.tsx +361 -0
- package/src/panels/timeline/GenerateDrawer.tsx +407 -0
- package/src/panels/timeline/KeyStretch.tsx +115 -0
- package/src/panels/timeline/LifespanRow.tsx +730 -47
- package/src/panels/timeline/MarkerLane.tsx +46 -8
- package/src/panels/timeline/PlayButton.tsx +91 -0
- package/src/panels/timeline/PropertyPicker.tsx +254 -0
- package/src/panels/timeline/ShotRow.tsx +400 -0
- package/src/panels/timeline/SoundMode.tsx +1406 -0
- package/src/panels/timeline/TimelineDock.tsx +2338 -272
- package/src/panels/timeline/TransportVolume.tsx +163 -0
- package/src/panels/timeline/beatRuler.ts +74 -0
- package/src/panels/timeline/curveModel.ts +500 -0
- package/src/panels/timeline/duckUnderVoice.ts +68 -0
- package/src/panels/timeline/keyframeMenu.ts +138 -0
- package/src/panels/timeline/labelWidth.ts +49 -0
- package/src/panels/timeline/marquee.ts +155 -0
- package/src/panels/timeline/model.ts +1860 -199
- package/src/panels/timeline/snap.ts +125 -43
- package/src/panels/timeline/staggerPreview.tsx +92 -0
- package/src/panels/timeline/takeMeasure.ts +72 -0
- package/src/panels/timeline/useLive.ts +48 -0
- package/src/panels/timeline/viewport.ts +234 -0
- package/src/panels/toolbar/BackChip.tsx +116 -0
- package/src/panels/toolbar/CameraViewBar.tsx +118 -49
- package/src/panels/toolbar/CanvasBar.tsx +117 -0
- package/src/panels/toolbar/InsertButton.tsx +38 -0
- package/src/panels/toolbar/IsolationTrail.tsx +156 -0
- package/src/panels/toolbar/PanelToggles.tsx +86 -0
- package/src/panels/toolbar/ShadingBar.tsx +119 -26
- package/src/panels/toolbar/ToolDockBar.tsx +329 -80
- package/src/panels/toolbar/ToolFooter.tsx +102 -0
- package/src/panels/toolbar/ViewModeBar.tsx +247 -38
- package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
- package/src/panels/toolbar/useAddRows.tsx +326 -0
- package/src/panels/toolbar/useDismiss.ts +122 -0
- package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
- package/src/panels/versions/versionModel.ts +103 -0
- package/src/panels/virtualWindow.ts +271 -0
- package/src/shotTokens.css +151 -60
- package/src/stage/FrustumGizmo.tsx +504 -84
- package/src/stage/GridPlane.tsx +225 -16
- package/src/stage/NodePreviews.tsx +628 -0
- package/src/stage/PerfHud.tsx +437 -47
- package/src/stage/SplitRenderer.tsx +816 -169
- package/src/stage/StageApp.tsx +4274 -486
- package/src/stage/StageRootBoundary.tsx +114 -0
- package/src/stage/StageTools.tsx +651 -151
- package/src/stage/TransformGizmo.tsx +696 -0
- package/src/stage/adaptivePresentation.ts +145 -0
- package/src/stage/bridgeClient.ts +2522 -41
- package/src/stage/exportWorker.ts +9 -0
- package/src/stage/feed.ts +305 -14
- package/src/stage/fontPreview.ts +176 -0
- package/src/stage/frustumGeometry.ts +319 -1
- package/src/stage/furnitureTokens.ts +33 -0
- package/src/stage/gizmoMath.ts +508 -16
- package/src/stage/gpuTimer.ts +276 -0
- package/src/stage/instanceModules.ts +194 -33
- package/src/stage/interactions/move.ts +24 -5
- package/src/stage/isolation.ts +291 -33
- package/src/stage/jsSelfProfiler.ts +325 -0
- package/src/stage/main.tsx +116 -16
- package/src/stage/modalState.ts +222 -23
- package/src/stage/moduleEpoch.ts +60 -0
- package/src/stage/motionPathStage.ts +279 -0
- package/src/stage/motionPreview.ts +74 -0
- package/src/stage/overlays/Overlays.tsx +1112 -159
- package/src/stage/overlays/Rulers.tsx +495 -0
- package/src/stage/panes.ts +159 -10
- package/src/stage/project.ts +68 -3
- package/src/stage/renderScaleController.ts +730 -0
- package/src/stage/rulers.ts +129 -0
- package/src/stage/sceneExtent.ts +75 -0
- package/src/stage/shading.ts +20 -0
- package/src/stage/splineStage.ts +632 -0
- package/src/stage/stagePerformanceTelemetry.ts +717 -0
- package/src/stage/stageTheme.ts +36 -0
- package/src/stage/stageWarmup.ts +11 -0
- package/src/stage/textAutoSize.ts +124 -0
- package/src/stage/tier1Export.ts +641 -0
- package/src/stage/tokenColor.ts +14 -9
- package/src/stage/view.ts +56 -9
- package/src/stage/workingView.ts +277 -33
- package/src/state/accountPrefs.ts +261 -0
- package/src/state/agentActions.ts +173 -15
- package/src/state/agentActivity.ts +198 -0
- package/src/state/agentQuestion.ts +239 -0
- package/src/state/bootOpen.ts +42 -0
- package/src/state/cameraVerbs.ts +139 -0
- package/src/state/clientId.ts +6 -1
- package/src/state/comments.ts +1099 -0
- package/src/state/context.tsx +19 -1
- package/src/state/editorPrefs.ts +220 -0
- package/src/state/errorReports.ts +121 -0
- package/src/state/fieldWrite.ts +161 -0
- package/src/state/fontRecents.ts +70 -0
- package/src/state/fonts.ts +656 -0
- package/src/state/graphClipboard.ts +414 -0
- package/src/state/imageThumbs.ts +216 -0
- package/src/state/insert.ts +444 -67
- package/src/state/isolationTrail.ts +92 -0
- package/src/state/keyMoves.ts +134 -0
- package/src/state/keyframeClipboard.ts +177 -0
- package/src/state/keymap.ts +1153 -117
- package/src/state/layoutPrefs.ts +272 -0
- package/src/state/layouts.ts +205 -0
- package/src/state/measureMedia.ts +167 -0
- package/src/state/menu.ts +2341 -28
- package/src/state/motionStamp.ts +7 -0
- package/src/state/panelSize.ts +375 -5
- package/src/state/pendingChanges.ts +119 -0
- package/src/state/playHold.ts +78 -0
- package/src/state/playbackPreparation.ts +562 -0
- package/src/state/posterCapture.ts +242 -0
- package/src/state/presence.ts +254 -0
- package/src/state/projectComponents.ts +160 -0
- package/src/state/renderEvents.ts +298 -18
- package/src/state/runtimeErrors.ts +46 -0
- package/src/state/selection.ts +12 -0
- package/src/state/shortcuts.ts +1606 -0
- package/src/state/sketchGuide.ts +51 -0
- package/src/state/splineRefs.ts +87 -0
- package/src/state/stageHealth.ts +259 -0
- package/src/state/stageWatchdog.ts +47 -0
- package/src/state/stillStage.ts +330 -0
- package/src/state/stillThumbs.ts +474 -0
- package/src/state/store.ts +23591 -2982
- package/src/state/storyboardModel.ts +528 -0
- package/src/state/sync.ts +1257 -0
- package/src/state/syncCache.ts +159 -0
- package/src/state/triangles.ts +13 -0
- package/src/state/types.ts +1698 -55
- package/src/state/unsavedGuard.ts +49 -0
- package/src/state/viewMode.ts +200 -0
- package/src/state/webmcp.ts +1128 -0
- package/src/state/workspace.ts +1147 -0
- package/src/styles/assets-tab.css +304 -0
- package/src/styles/canvas.css +545 -0
- package/src/styles/comments.css +1435 -0
- package/src/styles/controls.css +162 -0
- package/src/styles/dialogs.css +390 -0
- package/src/styles/insert-drawer.css +586 -0
- package/src/styles/inspector-board.css +207 -0
- package/src/styles/inspector-object.css +486 -0
- package/src/styles/inspector-shot.css +252 -0
- package/src/styles/inspector.css +360 -0
- package/src/styles/left-column.css +1628 -0
- package/src/styles/library-preview.css +575 -0
- package/src/styles/library.css +512 -0
- package/src/styles/modes/code.css +569 -0
- package/src/styles/modes/component.css +1561 -0
- package/src/styles/modes/material.css +1201 -0
- package/src/styles/modes/storyboard.css +518 -0
- package/src/styles/share-component.css +49 -0
- package/src/styles/shell.css +653 -0
- package/src/styles/shot-brief.css +360 -0
- package/src/styles/states.css +76 -0
- package/src/styles/sync.css +380 -0
- package/src/styles/timeline-graph.css +384 -0
- package/src/styles/timeline-meters.css +89 -0
- package/src/styles/timeline-rows.css +1959 -0
- package/src/styles/timeline-sound.css +1003 -0
- package/src/styles/timeline.css +754 -0
- package/src/styles/tool-footer.css +525 -0
- package/src/styles/topbar.css +886 -0
- package/src/styles/versions.css +262 -0
- package/src/styles.css +72 -2921
- package/src/tools.ts +108 -23
- package/dist/assets/index-B7notge7.css +0 -1
- package/dist/assets/index-BBE6Ta9L.js +0 -52
- package/src/panels/files/icons.tsx +0 -118
- package/src/panels/header/DocSwitcher.tsx +0 -85
- package/src/panels/header/TransportBar.tsx +0 -86
- package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
- package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
- package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
- package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
- package/src/panels/inspector/animate/useBlocks.ts +0 -43
- package/src/panels/material/MaterialPicker.tsx +0 -172
- package/src/panels/timeline/SceneStrip.tsx +0 -145
- package/src/panels/toolbar/AddMenu.tsx +0 -230
- package/src/stage/MoveGizmo.tsx +0 -411
package/src/stage/gizmoMath.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// `world = (x, -y, -z)`. Labelling the arms with world axes instead would put
|
|
15
15
|
// the letter Y on the arm that moves the layer the opposite way from the field
|
|
16
16
|
// the author is watching.
|
|
17
|
+
import { borderDistance } from "./frustumGeometry.js";
|
|
17
18
|
import { presetWorkingView, viewBasis } from "./workingView.js";
|
|
18
19
|
import type { WorkingView, WorkingViewPreset } from "./workingView.js";
|
|
19
20
|
import type { ModalState } from "./modalState.js";
|
|
@@ -46,20 +47,53 @@ export const DESIGN_AXES: readonly {
|
|
|
46
47
|
{ axis: "z", world: [0, 0, -1], preset: "back", presetNegative: "front" },
|
|
47
48
|
];
|
|
48
49
|
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
50
|
+
/**
|
|
51
|
+
* Every handle the transform gizmo can be grabbed by, as ONE name.
|
|
52
|
+
*
|
|
53
|
+
* A single string rather than a `{kind, axis}` pair because every consumer
|
|
54
|
+
* wants it whole: the hover field compares it, the draw pass keys the active
|
|
55
|
+
* one off it, and the hit table is an ordered list of them. `screen` carries no
|
|
56
|
+
* axis at all — it is the view-plane disc — which is exactly the shape a pair
|
|
57
|
+
* would have had to fake with a null.
|
|
58
|
+
*/
|
|
59
|
+
export type GizmoHandleId =
|
|
60
|
+
| `axis:${DesignAxis}`
|
|
61
|
+
| `plane:${DesignAxis}`
|
|
62
|
+
| `arc:${DesignAxis}`
|
|
63
|
+
| "screen";
|
|
64
|
+
|
|
65
|
+
/** The design axis a handle names, or null for the view-plane disc. A `plane:`
|
|
66
|
+
* or `arc:` handle names its own NORMAL, so `plane:z` is the pad the author
|
|
67
|
+
* drags in x and y and `arc:z` is the ring they turn IN that plane. */
|
|
68
|
+
export function gizmoAxisOf(handle: GizmoHandleId): DesignAxis | null {
|
|
69
|
+
if (handle === "screen") return null;
|
|
70
|
+
return handle.slice(handle.indexOf(":") + 1) as DesignAxis;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** What a press on one of the gizmo's handles turned out to be. Plain arrays
|
|
74
|
+
* and a name, so the pointer lane can carry it without touching three — and so
|
|
51
75
|
* the origin is a SNAPSHOT: the gizmo re-anchors every frame, and an axis
|
|
52
76
|
* line that moved with the thing being dragged along it would chase its own
|
|
53
77
|
* translation.
|
|
54
78
|
*
|
|
55
79
|
* `subject` is `modalTarget`'s own answer, carried rather than re-derived:
|
|
56
|
-
* the
|
|
80
|
+
* the handles are drawn at ONE anchor, and a drag that decided for itself
|
|
57
81
|
* whether that anchor was a layer or the camera could pick the other one
|
|
58
82
|
* mid-gesture if the selection changed under it. */
|
|
59
83
|
export interface GizmoGrab {
|
|
60
84
|
subject: ModalState["target"];
|
|
61
|
-
|
|
85
|
+
handle: GizmoHandleId;
|
|
62
86
|
origin: Vec3;
|
|
87
|
+
/**
|
|
88
|
+
* ONE world vector, meaning what the handle needs it to mean: an `axis:`
|
|
89
|
+
* arm's own direction, or the NORMAL of the plane a `plane:` pad and the
|
|
90
|
+
* `screen` disc unproject onto.
|
|
91
|
+
*
|
|
92
|
+
* Two fields would be one field always null — an arm solves a LINE
|
|
93
|
+
* (`axisParam`, so the grabbed arm stays under the cursor) and a pad solves a
|
|
94
|
+
* PLANE (`designDelta`), and neither solve has any use for the other's
|
|
95
|
+
* vector.
|
|
96
|
+
*/
|
|
63
97
|
direction: Vec3;
|
|
64
98
|
}
|
|
65
99
|
|
|
@@ -105,6 +139,21 @@ export function framingArmDelta(
|
|
|
105
139
|
return { dx: 0, dy: 0, dzoom: t0 / t };
|
|
106
140
|
}
|
|
107
141
|
|
|
142
|
+
/**
|
|
143
|
+
* The camera's roll arc, as the framing delta a swept angle means.
|
|
144
|
+
*
|
|
145
|
+
* An arc measures an ANGLE where an arm measures travel, so it is its own
|
|
146
|
+
* function rather than a fourth axis of `framingArmDelta`. The sign is the
|
|
147
|
+
* layer arc's own: the drawn camera turns WITH the pointer around its ring, the
|
|
148
|
+
* way a layer does around its `arc:z` — the modal negates for the camera because
|
|
149
|
+
* it measures the picture in the camera pane, where a clockwise roll of the
|
|
150
|
+
* camera turns the picture the other way; this arc is drawn around the camera
|
|
151
|
+
* object, so no negation.
|
|
152
|
+
*/
|
|
153
|
+
export function framingArcDelta(angleDeg: number): FramingDelta {
|
|
154
|
+
return { dx: 0, dy: 0, dzoom: 1, droll: Number.isFinite(angleDeg) ? angleDeg : 0 };
|
|
155
|
+
}
|
|
156
|
+
|
|
108
157
|
function dot(a: Vec3, b: Vec3): number {
|
|
109
158
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
110
159
|
}
|
|
@@ -159,6 +208,61 @@ export function viewForBall(view: WorkingView, ball: NavBall): WorkingView {
|
|
|
159
208
|
return presetWorkingView(view, ball.preset);
|
|
160
209
|
}
|
|
161
210
|
|
|
211
|
+
/** Below this a middle-press has no direction to read: the author has not
|
|
212
|
+
* committed to a way yet, and picking an axis for them would be a guess. In
|
|
213
|
+
* screen px of accumulated travel. */
|
|
214
|
+
export const MIN_AUTO_CONSTRAIN_PX = 8;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Below this an axis is too FORESHORTENED to be what a screen drag meant.
|
|
218
|
+
*
|
|
219
|
+
* An axis pointing near the camera projects to almost nothing, so its screen
|
|
220
|
+
* direction is noise — every drag is "close to" it because it barely has a
|
|
221
|
+
* direction at all. `navBalls` keeps the foreshortening in the vector's length
|
|
222
|
+
* for exactly this reason (it is the nav gizmo's own depth cue), so the test is
|
|
223
|
+
* a length test.
|
|
224
|
+
*/
|
|
225
|
+
const MIN_AXIS_PROJECTION = 0.2;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The design axis a screen drag was heading along, or null.
|
|
229
|
+
*
|
|
230
|
+
* Blender's middle-mouse AUTO-CONSTRAIN: the author drags a little way, taps
|
|
231
|
+
* the middle button, and the transform locks to whichever axis they were
|
|
232
|
+
* already going along. This is that choice, and it is `navBalls`' projection
|
|
233
|
+
* re-read — the same three dot products the corner gizmo draws itself from, so
|
|
234
|
+
* the axis the widget shows pointing that way IS the axis this picks.
|
|
235
|
+
*
|
|
236
|
+
* Null when the travel is too short to have a direction, or when the best axis
|
|
237
|
+
* is too foreshortened to have one either. Refusing beats locking to an axis
|
|
238
|
+
* the author did not mean, which costs an undo to get out of.
|
|
239
|
+
*/
|
|
240
|
+
export function autoConstrainAxis(
|
|
241
|
+
view: WorkingView,
|
|
242
|
+
dx: number,
|
|
243
|
+
dy: number,
|
|
244
|
+
): DesignAxis | null {
|
|
245
|
+
const length = Math.hypot(dx, dy);
|
|
246
|
+
if (!(length >= MIN_AUTO_CONSTRAIN_PX)) return null;
|
|
247
|
+
let best: DesignAxis | null = null;
|
|
248
|
+
let bestScore = 0;
|
|
249
|
+
for (const ball of navBalls(view)) {
|
|
250
|
+
// The POSITIVE end alone: an axis and its opposite are one line, and a drag
|
|
251
|
+
// "along x" is along x whichever way it points.
|
|
252
|
+
if (!ball.positive) continue;
|
|
253
|
+
const axisLength = Math.hypot(ball.x, ball.y);
|
|
254
|
+
if (axisLength < MIN_AXIS_PROJECTION) continue;
|
|
255
|
+
// |cos| between the drag and the axis, so either direction along the axis
|
|
256
|
+
// scores the same.
|
|
257
|
+
const score = Math.abs((ball.x * dx + ball.y * dy) / (axisLength * length));
|
|
258
|
+
if (score > bestScore) {
|
|
259
|
+
bestScore = score;
|
|
260
|
+
best = ball.axis;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return best;
|
|
264
|
+
}
|
|
265
|
+
|
|
162
266
|
/**
|
|
163
267
|
* How far along an axis line the closest point to a ray sits.
|
|
164
268
|
*
|
|
@@ -199,12 +303,11 @@ const MIN_AXIS_DENOM = 1e-6;
|
|
|
199
303
|
* SHIPPED DEFAULT view all three arms measured 20 to 27 — so every arm failed
|
|
200
304
|
* and the gizmo answered nothing anywhere while still being painted.
|
|
201
305
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* so it does not pay for it.
|
|
306
|
+
* There used to be a second question here, an apex clearance for the camera's
|
|
307
|
+
* arms: the frustum's apex had its own 14 px grab tested AFTER the arms, and
|
|
308
|
+
* an arm rooted inside that radius took every press aimed at the dolly. The
|
|
309
|
+
* apex grab is gone (the Z arm IS the dolly, and the camera body's grab is
|
|
310
|
+
* tested after the arms too), so the length rule is the whole gate again.
|
|
208
311
|
*
|
|
209
312
|
* PURE, and here rather than inside `MoveGizmo`'s frame loop, because a gate
|
|
210
313
|
* that decides whether a whole widget works has to be checkable without a GL
|
|
@@ -213,14 +316,403 @@ const MIN_AXIS_DENOM = 1e-6;
|
|
|
213
316
|
export function armGrabbable(
|
|
214
317
|
root: readonly [number, number],
|
|
215
318
|
tip: readonly [number, number],
|
|
216
|
-
anchor: readonly [number, number],
|
|
217
|
-
subject: "layers" | "camera",
|
|
218
319
|
minLengthPx: number,
|
|
219
|
-
apexClearPx: number,
|
|
220
320
|
): boolean {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
321
|
+
return Math.hypot(tip[0] - root[0], tip[1] - root[1]) >= minLengthPx;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* The gizmo's shape, in CSS px, HELD CONSTANT as the author dollies.
|
|
326
|
+
*
|
|
327
|
+
* World-space sizes would be a gizmo that fills the pane up close and vanishes
|
|
328
|
+
* far out — and, because every hit test below is in screen px, one whose grab
|
|
329
|
+
* target changed size with the zoom.
|
|
330
|
+
*
|
|
331
|
+
* ── THE PROPORTIONS ARE DREI'S ──────────────────────────────────────────
|
|
332
|
+
*
|
|
333
|
+
* `arrowHead`, `padAt` and `pad` are `@react-three/drei` 10.7.7's PivotControls
|
|
334
|
+
* numbers, restated here as fractions of `arm` and rounded to whole pixels:
|
|
335
|
+
* `AxisArrow.js`'s `coneLength = 0.2`, `PlaneSlider.js`'s `pos1 * 1.7 = 1.7/7`
|
|
336
|
+
* and `length = 0.225`. MIT, same vendoring the player's `view.tsx` did with
|
|
337
|
+
* that release, and for the same reason — the shapes are tuned and the tuning
|
|
338
|
+
* is the part worth having. NONE OF DREI'S CODE IS USED: its shafts and arcs
|
|
339
|
+
* are `Line2`/`LineMaterial` (a GLSL `ShaderMaterial`), which is dead on the
|
|
340
|
+
* WebGPU backend and silently so; its hit-testing is r3f events, which this
|
|
341
|
+
* stage does not run; and its hover is React state under a `frameloop="never"`
|
|
342
|
+
* canvas, which cannot repaint. See docs and the plan for the full account.
|
|
343
|
+
*/
|
|
344
|
+
export const GIZMO_PX = {
|
|
345
|
+
/** An arm's tip, measured from the anchor. */
|
|
346
|
+
arm: 90,
|
|
347
|
+
/**
|
|
348
|
+
* Where an arm STARTS.
|
|
349
|
+
*
|
|
350
|
+
* It has to clear the anchor by more than BOTH slops added together. On the
|
|
351
|
+
* camera the anchor is the eye, the centre of the camera body's own grab box
|
|
352
|
+
* (`BODY_GRAB_PX`, frustumGeometry.ts), which the pointer lane tests AFTER
|
|
353
|
+
* the arms: the arms win where they overlap it, and a start inside the box's
|
|
354
|
+
* slop would leave no press near the eye that reached the body at all.
|
|
355
|
+
*
|
|
356
|
+
* 30 > 14 + 14. It also keeps the three roots from piling into one ambiguous
|
|
357
|
+
* blob, which is why the layer gizmo wants it too.
|
|
358
|
+
*/
|
|
359
|
+
armStart: 30,
|
|
360
|
+
/** Shaft radius. Thin enough to read as a line, thick enough to be a solid. */
|
|
361
|
+
shaft: 1.5,
|
|
362
|
+
/** Arrowhead length — drei's 0.2 of the arm. */
|
|
363
|
+
arrowHead: 18,
|
|
364
|
+
/** Arrowhead radius. */
|
|
365
|
+
arrowWidth: 5,
|
|
366
|
+
/** A plane pad's centre, on both of its two axes — drei's 1.7/7 of the arm. */
|
|
367
|
+
padAt: 22,
|
|
368
|
+
/** A plane pad's side — drei's 0.225 of the arm. */
|
|
369
|
+
pad: 20,
|
|
370
|
+
/**
|
|
371
|
+
* A rotate arc's radius — drei's 0.65 of the arm.
|
|
372
|
+
*
|
|
373
|
+
* INSIDE the arrowheads on purpose. A quarter arc between two axes crosses
|
|
374
|
+
* both of the arms that bound it, and the hit table ranks arms above arcs, so
|
|
375
|
+
* the two crossings belong to the arms and the 80% of the arc between them is
|
|
376
|
+
* the author's. That is drei's own layout and Blender's; a ring at the arm's
|
|
377
|
+
* full length would cross the cones instead, where the arm is widest.
|
|
378
|
+
*/
|
|
379
|
+
arc: 58,
|
|
380
|
+
/**
|
|
381
|
+
* How close a press has to land to an ARC.
|
|
382
|
+
*
|
|
383
|
+
* Tighter than an arm's 14. An arc is a curve crossing three other handles'
|
|
384
|
+
* neighbourhoods, and every px of slop is a px it takes from one of them —
|
|
385
|
+
* whereas an arm is a straight run through empty space.
|
|
386
|
+
*/
|
|
387
|
+
arcGrab: 9,
|
|
388
|
+
/** The view-plane disc's radius. Inside `padAt` less half a pad, so the disc
|
|
389
|
+
* and the three pads do not overlap and the hit order never has to arbitrate
|
|
390
|
+
* between them. */
|
|
391
|
+
screen: 11,
|
|
392
|
+
/** How close a press has to land to an ARM, in CSS px. The frustum's own 14 —
|
|
393
|
+
* a line is a line, and the author's finger is the same size in both. */
|
|
394
|
+
grab: 14,
|
|
395
|
+
} as const;
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* How long an arm's DRAWN segment must be, on screen, to answer a press.
|
|
399
|
+
*
|
|
400
|
+
* `armStart` and `arm` are world lengths taken at the anchor's depth and then
|
|
401
|
+
* projected, so what the author can actually hit is those lengths times the
|
|
402
|
+
* sine of the axis's angle to the view direction. An axis pointing at the
|
|
403
|
+
* camera collapses: at Numpad7 the design y arm draws about 2.6 px, and under
|
|
404
|
+
* an orthographic rig the design z arm draws a mathematical POINT on the
|
|
405
|
+
* anchor. A press within `grab` of that point would grab it, `axisParam` would
|
|
406
|
+
* divide by a zero denominator and answer null, and the press would open no
|
|
407
|
+
* scrub and do nothing at all — under a line that looked grabbable.
|
|
408
|
+
*/
|
|
409
|
+
export const MIN_ARM_SCREEN_PX = GIZMO_PX.grab * 2;
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* The two design axes a plane pad SPANS, given the one it is normal to.
|
|
413
|
+
*
|
|
414
|
+
* By filter rather than by cross product, so the pair is stable and stated in
|
|
415
|
+
* the table's own order — which is what lets the drawn pad and its hit quad be
|
|
416
|
+
* built from the same two vectors, in the same order, and therefore be the same
|
|
417
|
+
* square. A cross product would answer a correct plane with an arbitrary
|
|
418
|
+
* in-plane rotation, and the mismatch would be invisible until an author
|
|
419
|
+
* pressed the corner of a pad and got the arm underneath it.
|
|
420
|
+
*/
|
|
421
|
+
export function padAxes(axis: DesignAxis): readonly [Vec3, Vec3] {
|
|
422
|
+
const rest = DESIGN_AXES.filter((a) => a.axis !== axis);
|
|
423
|
+
return [rest[0]!.world, rest[1]!.world];
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** A point `distance` along a unit direction from an anchor. */
|
|
427
|
+
export function alongAxis(from: Vec3, direction: Vec3, distance: number): Vec3 {
|
|
428
|
+
return [
|
|
429
|
+
from[0] + direction[0] * distance,
|
|
430
|
+
from[1] + direction[1] * distance,
|
|
431
|
+
from[2] + direction[2] * distance,
|
|
432
|
+
];
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* A plane pad's four world corners, wound consistently.
|
|
437
|
+
*
|
|
438
|
+
* `at` is the centre's offset along BOTH of its axes and `half` is half its
|
|
439
|
+
* side, both already in world units. Wound so `pointInQuad`'s sign test sees a
|
|
440
|
+
* convex quad however the projection flips it.
|
|
441
|
+
*
|
|
442
|
+
* ONE function for the drawing and the hit test. They had drifted apart at the
|
|
443
|
+
* first attempt: the mesh took its rotation from `setFromUnitVectors`, which
|
|
444
|
+
* lands the square at whatever in-plane angle the shortest rotation happens to
|
|
445
|
+
* give, while the hit quad was built from `padAxes`. Same plane, different
|
|
446
|
+
* square — a handle whose corners answer no press.
|
|
447
|
+
*/
|
|
448
|
+
export function padCorners(centre: Vec3, axis: DesignAxis, at: number, half: number): Vec3[] {
|
|
449
|
+
const [u, v] = padAxes(axis);
|
|
450
|
+
return PAD_SIGNS.map(([su, sv]) => [
|
|
451
|
+
centre[0] + u[0] * (at + su * half) + v[0] * (at + sv * half),
|
|
452
|
+
centre[1] + u[1] * (at + su * half) + v[1] * (at + sv * half),
|
|
453
|
+
centre[2] + u[2] * (at + su * half) + v[2] * (at + sv * half),
|
|
454
|
+
]);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** A plane pad's world centre. */
|
|
458
|
+
export function padCentre(centre: Vec3, axis: DesignAxis, at: number): Vec3 {
|
|
459
|
+
const [u, v] = padAxes(axis);
|
|
460
|
+
return [
|
|
461
|
+
centre[0] + (u[0] + v[0]) * at,
|
|
462
|
+
centre[1] + (u[1] + v[1]) * at,
|
|
463
|
+
centre[2] + (u[2] + v[2]) * at,
|
|
464
|
+
];
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** The four corners as signed half-widths on the pad's two axes, wound. */
|
|
468
|
+
const PAD_SIGNS: readonly (readonly [number, number])[] = [
|
|
469
|
+
[-1, -1],
|
|
470
|
+
[1, -1],
|
|
471
|
+
[1, 1],
|
|
472
|
+
[-1, 1],
|
|
473
|
+
];
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* The shoelace area of a projected polygon, unsigned.
|
|
477
|
+
*
|
|
478
|
+
* `armGrabbable`'s claim in the units a quad has: a pad seen EDGE-ON projects
|
|
479
|
+
* to a sliver no press can land inside, and its plane is grazing enough that
|
|
480
|
+
* `designDelta` would answer a wild number if one did.
|
|
481
|
+
*/
|
|
482
|
+
export function screenArea(corners: readonly (readonly [number, number])[]): number {
|
|
483
|
+
let sum = 0;
|
|
484
|
+
for (let i = 0; i < corners.length; i++) {
|
|
485
|
+
const [ax, ay] = corners[i]!;
|
|
486
|
+
const [bx, by] = corners[(i + 1) % corners.length]!;
|
|
487
|
+
sum += ax * by - bx * ay;
|
|
488
|
+
}
|
|
489
|
+
return Math.abs(sum) / 2;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* The smallest projected pad, in square screen px, that can answer a press.
|
|
494
|
+
*
|
|
495
|
+
* A tenth of the pad drawn face-on (20 x 20 = 400) — well past the sliver and
|
|
496
|
+
* well short of anything a working pad reaches.
|
|
497
|
+
*/
|
|
498
|
+
export const MIN_PAD_SCREEN_AREA = 40;
|
|
499
|
+
|
|
500
|
+
type Pt = readonly [number, number];
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* One thing on screen a press can land on, already projected.
|
|
504
|
+
*
|
|
505
|
+
* Screen geometry and not world, because that is where the question is asked:
|
|
506
|
+
* "is the cursor on this handle" is a question about pixels, and asking it in
|
|
507
|
+
* world units makes a grab target that shrinks as the author dollies out.
|
|
508
|
+
*/
|
|
509
|
+
export interface GizmoTarget {
|
|
510
|
+
handle: GizmoHandleId;
|
|
511
|
+
origin: Vec3;
|
|
512
|
+
direction: Vec3;
|
|
513
|
+
shape:
|
|
514
|
+
/** An arm (two points) or an arc (many). `borderDistance` walks a CLOSED
|
|
515
|
+
* polygon, so a two-point path is that one segment measured twice — the
|
|
516
|
+
* point-to-segment distance — and an open arc is measured with one extra
|
|
517
|
+
* chord from its last point back to its first, which lies well outside
|
|
518
|
+
* the slop and therefore never wins. */
|
|
519
|
+
| { type: "path"; points: readonly Pt[]; slop: number }
|
|
520
|
+
| { type: "quad"; corners: readonly Pt[] }
|
|
521
|
+
| { type: "disc"; at: Pt; radius: number };
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* The FIRST target that contains a point, in the order it was handed.
|
|
526
|
+
*
|
|
527
|
+
* ── AN ORDER, NOT A NEAREST-WINS ────────────────────────────────────────
|
|
528
|
+
*
|
|
529
|
+
* With three arms and nothing else, "closest segment within 14px" was a
|
|
530
|
+
* complete answer. It stops being one the moment handles overlap: a plane pad
|
|
531
|
+
* SITS ON the two arms that bound it, and a nearest-wins test would give the
|
|
532
|
+
* pad's interior to whichever arm's centreline happened to be nearer, which is
|
|
533
|
+
* a handle that is drawn and never grabbed.
|
|
534
|
+
*
|
|
535
|
+
* So priority is declared — smallest and innermost first, which is the only
|
|
536
|
+
* order in which every handle stays reachable (the disc is inside the pads, the
|
|
537
|
+
* pads are inside the arms' span) — and each target carries its own exact
|
|
538
|
+
* shape rather than a distance. Ordering is the ARRAY's, so the one place it
|
|
539
|
+
* lives is where the targets are built, and it is checkable here without a GL
|
|
540
|
+
* context.
|
|
541
|
+
*
|
|
542
|
+
* The apex and the frustum's border are deliberately NOT in this list: they
|
|
543
|
+
* belong to `feed.frustumHit`, which `intentAt` ranks below `feed.grabAt` with
|
|
544
|
+
* a layer veto on the body. Folding them in here would rank them above both.
|
|
545
|
+
*/
|
|
546
|
+
export function gizmoHitAt(
|
|
547
|
+
targets: readonly GizmoTarget[],
|
|
548
|
+
x: number,
|
|
549
|
+
y: number,
|
|
550
|
+
): GizmoTarget | null {
|
|
551
|
+
for (const target of targets) {
|
|
552
|
+
const shape = target.shape;
|
|
553
|
+
if (shape.type === "path") {
|
|
554
|
+
if (borderDistance(x, y, shape.points) <= shape.slop) return target;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
if (shape.type === "quad") {
|
|
558
|
+
if (pointInQuad(x, y, shape.corners)) return target;
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
if (Math.hypot(shape.at[0] - x, shape.at[1] - y) <= shape.radius) return target;
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Is a point inside a projected convex polygon?
|
|
568
|
+
*
|
|
569
|
+
* The cross product of each edge with the point-to-vertex vector keeps one sign
|
|
570
|
+
* all the way round a convex polygon, and flips somewhere on a point outside
|
|
571
|
+
* it. Winding-agnostic (either sign is accepted, as long as it is consistent),
|
|
572
|
+
* because a plane pad seen from behind projects the other way round and it is
|
|
573
|
+
* still the same pad.
|
|
574
|
+
*
|
|
575
|
+
* A zero cross means the point is ON that edge, which counts as inside — the
|
|
576
|
+
* alternative is a one-pixel dead line down the middle of a handle.
|
|
577
|
+
*/
|
|
578
|
+
export function pointInQuad(x: number, y: number, corners: readonly Pt[]): boolean {
|
|
579
|
+
if (corners.length < 3) return false;
|
|
580
|
+
let sign = 0;
|
|
581
|
+
for (let i = 0; i < corners.length; i++) {
|
|
582
|
+
const [ax, ay] = corners[i]!;
|
|
583
|
+
const [bx, by] = corners[(i + 1) % corners.length]!;
|
|
584
|
+
const cross = (bx - ax) * (y - ay) - (by - ay) * (x - ax);
|
|
585
|
+
if (cross === 0) continue;
|
|
586
|
+
const next = cross > 0 ? 1 : -1;
|
|
587
|
+
if (sign === 0) sign = next;
|
|
588
|
+
else if (sign !== next) return false;
|
|
589
|
+
}
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* A rotate arc's world points: a QUARTER turn in the plane normal to `axis`,
|
|
595
|
+
* running from that plane's first design axis round to its second.
|
|
596
|
+
*
|
|
597
|
+
* A quarter and not a full ring, which is drei's PivotControls layout and
|
|
598
|
+
* Blender's: three full rings at one radius make a sphere of lines over exactly
|
|
599
|
+
* the geometry the author is judging, and each one crosses the other two twice.
|
|
600
|
+
* A quarter arc per axis says the same thing — "turn about this" — inside one
|
|
601
|
+
* octant, and leaves the rest of the view clear.
|
|
602
|
+
*
|
|
603
|
+
* `segments` points, so an arc of 24 is 24 chords; the projected polyline IS
|
|
604
|
+
* the hit shape (`gizmoHitAt`), so the drawing and the grab are the same curve
|
|
605
|
+
* by construction rather than by two matching formulas.
|
|
606
|
+
*/
|
|
607
|
+
/** Chords per quarter arc. Enough that the polyline reads as a curve at the
|
|
608
|
+
* radius it is drawn at, and that the hit test's own chords never sit further
|
|
609
|
+
* than a fraction of `arcGrab` inside the true circle. */
|
|
610
|
+
export const ARC_SEGMENTS = 24;
|
|
611
|
+
|
|
612
|
+
export function arcPoints(
|
|
613
|
+
centre: Vec3,
|
|
614
|
+
axis: DesignAxis,
|
|
615
|
+
radius: number,
|
|
616
|
+
segments: number,
|
|
617
|
+
): Vec3[] {
|
|
618
|
+
const [u, v] = padAxes(axis);
|
|
619
|
+
const out: Vec3[] = [];
|
|
620
|
+
for (let i = 0; i <= segments; i++) {
|
|
621
|
+
const t = (i / segments) * (Math.PI / 2);
|
|
622
|
+
const cos = Math.cos(t) * radius;
|
|
623
|
+
const sin = Math.sin(t) * radius;
|
|
624
|
+
out.push([
|
|
625
|
+
centre[0] + u[0] * cos + v[0] * sin,
|
|
626
|
+
centre[1] + u[1] * cos + v[1] * sin,
|
|
627
|
+
centre[2] + u[2] * cos + v[2] * sin,
|
|
628
|
+
]);
|
|
629
|
+
}
|
|
630
|
+
return out;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Where a point sits round a rotate arc, in radians, measured IN THE ARC'S OWN
|
|
635
|
+
* PLANE.
|
|
636
|
+
*
|
|
637
|
+
* ── WHY NOT A SCREEN-SPACE `atan2` ──────────────────────────────────────
|
|
638
|
+
*
|
|
639
|
+
* The camera-pane roll ring can use one, because that ring is drawn in screen
|
|
640
|
+
* space and the rotation it drives is about the view axis. A working-pane arc
|
|
641
|
+
* is a circle in the WORLD seen at an angle, so it projects to an ellipse: a
|
|
642
|
+
* screen angle runs fast across the ellipse's narrow axis and slow across its
|
|
643
|
+
* wide one, and the grabbed point slides out from under the cursor exactly
|
|
644
|
+
* where the author can see it doing so.
|
|
645
|
+
*
|
|
646
|
+
* Two dot products against the plane's own basis instead — the same solve
|
|
647
|
+
* `@react-three/drei` 10.7.7's `AxisRotator.calculateAngle` performs (MIT), and
|
|
648
|
+
* for the same reason.
|
|
649
|
+
*/
|
|
650
|
+
export function ringAngle(centre: Vec3, axis: DesignAxis, point: Vec3): number {
|
|
651
|
+
const [u, v] = padAxes(axis);
|
|
652
|
+
const dx = point[0] - centre[0];
|
|
653
|
+
const dy = point[1] - centre[1];
|
|
654
|
+
const dz = point[2] - centre[2];
|
|
655
|
+
return Math.atan2(
|
|
656
|
+
dx * v[0] + dy * v[1] + dz * v[2],
|
|
657
|
+
dx * u[0] + dy * u[1] + dz * u[2],
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* The representative of `delta` (an angle that has been wrapped into a single
|
|
663
|
+
* turn) nearest `total`.
|
|
664
|
+
*
|
|
665
|
+
* ── WHAT THIS IS FOR, AND WHY IT IS NOT A DRIFT ─────────────────────────
|
|
666
|
+
*
|
|
667
|
+
* `ringAngle`'s difference lives in (-PI, PI], so a sweep past half a turn
|
|
668
|
+
* comes back as a rotation the other way and a sweep past a full turn comes
|
|
669
|
+
* back as zero — the author drags round and the layer springs back. Blender
|
|
670
|
+
* counts turns; so does this.
|
|
671
|
+
*
|
|
672
|
+
* The count is path-dependent, which is the ONLY path-dependent quantity a
|
|
673
|
+
* gesture in this editor carries, and it has to be: "how many times round" is
|
|
674
|
+
* not recoverable from the pointer's current position. What is emitted is still
|
|
675
|
+
* a TOTAL from the press, so the store's re-derive-from-base, one-undo-entry
|
|
676
|
+
* and exact-Escape properties all hold unchanged — see `advanceVirtual`, whose
|
|
677
|
+
* accumulation is path-dependent for the same reason and with the same result.
|
|
678
|
+
*/
|
|
679
|
+
export function unwrapTurns(total: number, delta: number): number {
|
|
680
|
+
const turn = Math.PI * 2;
|
|
681
|
+
if (!Number.isFinite(total) || !Number.isFinite(delta)) return delta;
|
|
682
|
+
return delta + Math.round((total - delta) / turn) * turn;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* The constraint axis, as the long screen segment Blender draws through the
|
|
687
|
+
* pivot while a transform is locked to it.
|
|
688
|
+
*
|
|
689
|
+
* ── WHY IT IS DONE IN SCREEN SPACE ──────────────────────────────────────
|
|
690
|
+
*
|
|
691
|
+
* A perspective projection maps a 3D line to a 2D line, so the image of the
|
|
692
|
+
* whole axis IS the screen line through the projected anchor with the projected
|
|
693
|
+
* direction. Extending two world points to ±infinity and projecting those
|
|
694
|
+
* instead means clipping them against the camera's near plane first, since a
|
|
695
|
+
* point behind the eye projects to a mirrored ghost — real work, for the same
|
|
696
|
+
* segment.
|
|
697
|
+
*
|
|
698
|
+
* `towards` is any second point on the axis, ahead of it on screen; only the
|
|
699
|
+
* direction is used. Null when the two project to the same pixel, which is an
|
|
700
|
+
* axis pointing at the camera — the arm is already refusing presses in that
|
|
701
|
+
* pose (`armGrabbable`), and a guide line with no direction would be a random
|
|
702
|
+
* one.
|
|
703
|
+
*/
|
|
704
|
+
export function axisGuide(
|
|
705
|
+
at: Pt,
|
|
706
|
+
towards: Pt,
|
|
707
|
+
reach: number,
|
|
708
|
+
): { a: [number, number]; b: [number, number] } | null {
|
|
709
|
+
const dx = towards[0] - at[0];
|
|
710
|
+
const dy = towards[1] - at[1];
|
|
711
|
+
const length = Math.hypot(dx, dy);
|
|
712
|
+
if (!(length > 1e-6) || !Number.isFinite(reach)) return null;
|
|
713
|
+
const ux = (dx / length) * reach;
|
|
714
|
+
const uy = (dy / length) * reach;
|
|
715
|
+
return { a: [at[0] - ux, at[1] - uy], b: [at[0] + ux, at[1] + uy] };
|
|
224
716
|
}
|
|
225
717
|
|
|
226
718
|
export function axisParam(
|