@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/view.ts
CHANGED
|
@@ -144,16 +144,63 @@ export function designToScreen(
|
|
|
144
144
|
/** Space left around the frame when fitting, as a fraction of the viewport. */
|
|
145
145
|
export const FIT_MARGIN = 0.08;
|
|
146
146
|
|
|
147
|
-
/**
|
|
148
|
-
*
|
|
149
|
-
|
|
147
|
+
/**
|
|
148
|
+
* The view that shows the whole frame with a margin. Pure in the viewport and
|
|
149
|
+
* the frame, so the chrome's "fit" button and a window resize agree.
|
|
150
|
+
*
|
|
151
|
+
* ── WHY IT CAN FIT A SUB-RECT ────────────────────────────────────────────
|
|
152
|
+
*
|
|
153
|
+
* The stage renders full-bleed and every chrome panel is docked over it, so the
|
|
154
|
+
* viewport is the whole window and fitting to it framed the shot behind the
|
|
155
|
+
* header and the timeline dock — the author pressed Fit and got a frame with
|
|
156
|
+
* its top and bottom covered. `within` is the part of that viewport the chrome
|
|
157
|
+
* is NOT covering, in the SAME coordinates as `viewport` (pane-local: the
|
|
158
|
+
* caller hands this a pane's size, so it hands `within` a pane-local rect).
|
|
159
|
+
* The render still fills the viewport; only what the frame is sized and
|
|
160
|
+
* centred against moves.
|
|
161
|
+
*
|
|
162
|
+
* Defaulting `within` to the whole viewport keeps `panX/panY` at 0 for every
|
|
163
|
+
* caller that does not pass one, so nothing that fits an unobstructed viewport
|
|
164
|
+
* changed.
|
|
165
|
+
*/
|
|
166
|
+
export function fitView(
|
|
167
|
+
viewport: Viewport,
|
|
168
|
+
frame: FrameSize,
|
|
169
|
+
within: { x: number; y: number; width: number; height: number } = {
|
|
170
|
+
x: 0,
|
|
171
|
+
y: 0,
|
|
172
|
+
width: viewport.width,
|
|
173
|
+
height: viewport.height,
|
|
174
|
+
},
|
|
175
|
+
/** Per side, as a fraction. A preview hole IS the frame, so it passes 0. */
|
|
176
|
+
margin: number = FIT_MARGIN,
|
|
177
|
+
): ShotView {
|
|
150
178
|
if (viewport.width <= 0 || viewport.height <= 0) return { zoom: 1, panX: 0, panY: 0 };
|
|
151
|
-
const usable = 1 -
|
|
152
|
-
const zoom = Math.min(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
179
|
+
const usable = 1 - margin * 2;
|
|
180
|
+
const zoom = Math.min((within.width * usable) / frame.width, (within.height * usable) / frame.height);
|
|
181
|
+
// `panX/panY` are the frame CENTRE's displacement from the VIEWPORT centre
|
|
182
|
+
// (see `ShotView`, and `frameRect` which derives the screen rect from it), so
|
|
183
|
+
// centring in `within` is the difference between the two centres — not
|
|
184
|
+
// `within`'s origin, which is the sign error this shape invites.
|
|
185
|
+
return {
|
|
186
|
+
zoom: clampZoom(zoom),
|
|
187
|
+
panX: finitePan(within.x + within.width / 2 - viewport.width / 2),
|
|
188
|
+
panY: finitePan(within.y + within.height / 2 - viewport.height / 2),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 0 for a pan that is not a number, for the same reason `clampZoom` answers 1.
|
|
194
|
+
*
|
|
195
|
+
* The pan used to be the constant 0, so `fitView` was TOTAL: no input could
|
|
196
|
+
* make it return a view the camera could not use. Two centres subtracted is
|
|
197
|
+
* not — a non-finite viewport or sub-rect (a first-frame measure, a mid-resize
|
|
198
|
+
* `Infinity`) makes it NaN, and a NaN pan is a `setViewOffset` of NaN, which is
|
|
199
|
+
* a projection matrix of NaN: the pane draws nothing at all, with no error
|
|
200
|
+
* anywhere. `paneRects` guards the same class of input for the same reason.
|
|
201
|
+
*/
|
|
202
|
+
function finitePan(n: number): number {
|
|
203
|
+
return Number.isFinite(n) ? n : 0;
|
|
157
204
|
}
|
|
158
205
|
|
|
159
206
|
/**
|
package/src/stage/workingView.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The working pane's free-orbit camera, as pure spherical arithmetic.
|
|
1
|
+
// The working pane's free-orbit camera, as pure spherical arithmetic.
|
|
2
2
|
//
|
|
3
3
|
// ── COORDINATE CONVENTION, STATED ONCE ──────────────────────────────────
|
|
4
4
|
//
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
//
|
|
33
33
|
// THREE-FREE: plain arrays and numbers, so the chrome can render a readout of
|
|
34
34
|
// the view and these functions are testable in node.
|
|
35
|
-
import { DEFAULT_FOV_DEG, planeDistance } from "@dailies-studio/shot/doc";
|
|
35
|
+
import { DEFAULT_FOV_DEG, MAX_CAMERA_PITCH_DEG, planeDistance } from "@dailies-studio/shot/doc";
|
|
36
36
|
import { FIT_MARGIN } from "./view.js";
|
|
37
37
|
import type { FrameSize } from "./view.js";
|
|
38
38
|
|
|
@@ -87,6 +87,26 @@ export interface WorkingView {
|
|
|
87
87
|
ortho?: boolean;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* How far behind the orbit position the ORTHOGRAPHIC eye stands.
|
|
92
|
+
*
|
|
93
|
+
* An orthographic projection does not depend on where along the view axis the
|
|
94
|
+
* eye sits, so this is free to the picture — and `StageTools` needs it because
|
|
95
|
+
* `Raycaster.setFromCamera` starts an ortho ray AT the near plane, so anything
|
|
96
|
+
* behind that plane draws perfectly and answers no click. Standing the eye back
|
|
97
|
+
* puts the whole slab in front of a positive near plane.
|
|
98
|
+
*
|
|
99
|
+
* NAMED, rather than inlined at the one call site, because it is the reason
|
|
100
|
+
* `GridPlane` cannot measure its fade from `cameraPosition` in ortho: this is
|
|
101
|
+
* many times `gridFade`'s far end at every legal dolly, so measured from here
|
|
102
|
+
* the entire floor resolves to the backdrop colour and disappears. Two places
|
|
103
|
+
* reasoning about the same number by restating it is how that shipped;
|
|
104
|
+
* `gizmoMath.test.ts` now pins the relationship against this export.
|
|
105
|
+
*/
|
|
106
|
+
export function orthoEyePushBack(distance: number): number {
|
|
107
|
+
return Math.max(distance * 8, 10_000);
|
|
108
|
+
}
|
|
109
|
+
|
|
90
110
|
export const ORBIT_DEG_PER_PX = 0.3;
|
|
91
111
|
|
|
92
112
|
/**
|
|
@@ -100,12 +120,32 @@ export const ORBIT_DEG_PER_PX = 0.3;
|
|
|
100
120
|
export const ORBIT_STEP_DEG = 15;
|
|
101
121
|
|
|
102
122
|
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
|
|
108
|
-
|
|
123
|
+
/**
|
|
124
|
+
* The poles are REACHABLE, and Numpad7 really does look straight down.
|
|
125
|
+
*
|
|
126
|
+
* ── WHAT USED TO BREAK, AND WHERE ───────────────────────────────────────
|
|
127
|
+
*
|
|
128
|
+
* The clamp was [5, 175], so "top" and "bottom" landed five degrees short and
|
|
129
|
+
* an author could never get a true plan or elevation view — the one thing a
|
|
130
|
+
* numpad axis key is for. The reason given was that at phi 0 or 180 the
|
|
131
|
+
* camera's roll is undefined, and that is true of `lookAt` WITH A WORLD-UP
|
|
132
|
+
* VECTOR: up becomes parallel to the view direction, the cross product is zero,
|
|
133
|
+
* and the quaternion comes out NaN.
|
|
134
|
+
*
|
|
135
|
+
* But `viewBasis` never had that problem. Its closed form for up is
|
|
136
|
+
* `[-cosφ·sinθ, sinφ, -cosφ·cosθ]`, which at φ = 0 is `[-sinθ, 0, -cosθ]` — a
|
|
137
|
+
* unit vector, perpendicular to the view direction, and continuous through the
|
|
138
|
+
* pole. The turntable convention: at the pole the roll is decided by theta,
|
|
139
|
+
* which is exactly what an author spinning a top view expects.
|
|
140
|
+
*
|
|
141
|
+
* So the fix was never a bigger epsilon. `StageTools` sets `cam.up` from
|
|
142
|
+
* `viewBasis(view).up` instead of from world +y, and the clamp opens to the
|
|
143
|
+
* full range. For every interior phi the two are the same vector (`viewBasis`'s
|
|
144
|
+
* own header states that identity, and gizmoMath.test.ts pins it against a real
|
|
145
|
+
* `Matrix4.lookAt`), so nothing between the poles moved by a float.
|
|
146
|
+
*/
|
|
147
|
+
export const MIN_PHI_DEG = 0;
|
|
148
|
+
export const MAX_PHI_DEG = 180;
|
|
109
149
|
|
|
110
150
|
/** A degenerate distance is not a far-away camera, it is a camera AT the target:
|
|
111
151
|
* `lookAt` gets a zero-length direction and writes a NaN quaternion, and every
|
|
@@ -224,11 +264,50 @@ export function workingPose(view: WorkingView): { position: [number, number, num
|
|
|
224
264
|
* `panBy` states, for the same reason.
|
|
225
265
|
*/
|
|
226
266
|
export function orbitBy(view: WorkingView, dxPx: number, dyPx: number): WorkingView {
|
|
227
|
-
|
|
267
|
+
const next: WorkingView = {
|
|
228
268
|
...view,
|
|
229
269
|
theta: wrapDeg(view.theta - dxPx * ORBIT_DEG_PER_PX),
|
|
230
270
|
phi: clampPhi(clampPhi(view.phi) - dyPx * ORBIT_DEG_PER_PX),
|
|
231
271
|
};
|
|
272
|
+
// ── AUTO PERSPECTIVE, ON THE WAY OUT OF AN AXIS VIEW ──────────────────
|
|
273
|
+
//
|
|
274
|
+
// `presetWorkingView` puts the pane in ORTHOGRAPHIC, because that is what a
|
|
275
|
+
// front or a top view is FOR: a plan and an elevation with no vanishing point
|
|
276
|
+
// to read a size wrong from. Turning away from one and staying flat is the
|
|
277
|
+
// other half of that — an orthographic three-quarter view of a scene with
|
|
278
|
+
// depth in it reads as broken, which is why Blender enables Auto Perspective
|
|
279
|
+
// by default and why leaving it out would make the preset key a trap.
|
|
280
|
+
//
|
|
281
|
+
// STATELESS, on the pose rather than on a remembered flag: "was this view
|
|
282
|
+
// reached by a preset" is a question that needs one more field on a record
|
|
283
|
+
// three lanes copy, and "is this view ON an axis" answers the same question
|
|
284
|
+
// from what is already there. Manual orthographic (Numpad5) at a
|
|
285
|
+
// three-quarter angle is untouched, which is the case the flag would have
|
|
286
|
+
// been for.
|
|
287
|
+
if (view.ortho === true && next.ortho === true && isAxisView(view) && !isAxisView(next)) {
|
|
288
|
+
const { ortho: _drop, ...perspective } = next;
|
|
289
|
+
return perspective;
|
|
290
|
+
}
|
|
291
|
+
return next;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Within this many degrees of an axis, a pose IS that axis view. Half of one
|
|
295
|
+
* numpad step, so a single Numpad4 press always leaves the neighbourhood. */
|
|
296
|
+
const AXIS_VIEW_EPSILON_DEG = 1e-6;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Is the eye on one of the six axis views?
|
|
300
|
+
*
|
|
301
|
+
* The poles (phi 0 / 180) are axis views whatever theta is — straight down is
|
|
302
|
+
* straight down. Otherwise it is the equator with theta on a quarter turn.
|
|
303
|
+
*/
|
|
304
|
+
export function isAxisView(view: WorkingView): boolean {
|
|
305
|
+
const phi = clampPhi(view.phi);
|
|
306
|
+
const near = (a: number, b: number): boolean => Math.abs(a - b) <= AXIS_VIEW_EPSILON_DEG;
|
|
307
|
+
if (near(phi, MIN_PHI_DEG) || near(phi, MAX_PHI_DEG)) return true;
|
|
308
|
+
if (!near(phi, 90)) return false;
|
|
309
|
+
const theta = Math.abs(wrapDeg(view.theta));
|
|
310
|
+
return near(theta, 0) || near(theta, 90) || near(theta, 180);
|
|
232
311
|
}
|
|
233
312
|
|
|
234
313
|
/**
|
|
@@ -367,6 +446,101 @@ export function dollyBy(view: WorkingView, wheelDeltaY: number, bounds?: DollyBo
|
|
|
367
446
|
return { ...view, distance };
|
|
368
447
|
}
|
|
369
448
|
|
|
449
|
+
/**
|
|
450
|
+
* Point the orbit at a new place WITHOUT moving the eye.
|
|
451
|
+
*
|
|
452
|
+
* ── ORBIT AROUND THE SELECTION ──────────────────────────────────────────
|
|
453
|
+
*
|
|
454
|
+
* `orbitBy` always pivots on `view.target`, and nothing ever fed the selection
|
|
455
|
+
* into it — so after a few pans the pivot was wherever the target had drifted
|
|
456
|
+
* to, and orbiting swung the thing the author was working on straight off the
|
|
457
|
+
* pane. Blender's "Orbit Around Selection" fixes it by re-pointing the pivot,
|
|
458
|
+
* and the whole trick is that the EYE STAYS PUT: `workingPose` run backwards,
|
|
459
|
+
* so the picture at the moment of re-targeting is identical to the pixel and
|
|
460
|
+
* only the next turn behaves differently. A re-target that moved the eye would
|
|
461
|
+
* be a jump cut on the way into a gesture.
|
|
462
|
+
*
|
|
463
|
+
* `workingPose` puts the eye at `target + r(sinφ sinθ, cosφ, sinφ cosθ)`, so
|
|
464
|
+
* inverting is one length and two inverse trig calls — the same convention
|
|
465
|
+
* `workingViewForIsolation` inverts, stated once more rather than shared
|
|
466
|
+
* because that one also has an ortho flag and a fit to answer for.
|
|
467
|
+
*
|
|
468
|
+
* Refuses (returns the view unchanged) when the new target is where the eye
|
|
469
|
+
* already is: there is no direction to derive angles from, and `lookAt` on a
|
|
470
|
+
* zero-length direction writes a NaN quaternion.
|
|
471
|
+
*/
|
|
472
|
+
export function retargetKeepingEye(
|
|
473
|
+
view: WorkingView,
|
|
474
|
+
target: readonly [number, number, number],
|
|
475
|
+
): WorkingView {
|
|
476
|
+
if (target.some((n) => !Number.isFinite(n))) return view;
|
|
477
|
+
const eye = workingPose(view).position;
|
|
478
|
+
const dx = eye[0] - target[0];
|
|
479
|
+
const dy = eye[1] - target[1];
|
|
480
|
+
const dz = eye[2] - target[2];
|
|
481
|
+
const distance = Math.hypot(dx, dy, dz);
|
|
482
|
+
if (!(distance > MIN_DISTANCE)) return view;
|
|
483
|
+
return {
|
|
484
|
+
...view,
|
|
485
|
+
// acos of the y component over the radius, which is phi by construction.
|
|
486
|
+
// Clamped for the float that lands a hair outside [-1, 1].
|
|
487
|
+
phi: (Math.acos(Math.max(-1, Math.min(1, dy / distance))) * 180) / Math.PI,
|
|
488
|
+
theta: wrapDeg((Math.atan2(dx, dz) * 180) / Math.PI),
|
|
489
|
+
distance: clampDistance(distance),
|
|
490
|
+
target: [target[0], target[1], target[2]],
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Dolly TOWARDS A POINT: the world point under the cursor holds still while
|
|
496
|
+
* everything else moves in or out around it.
|
|
497
|
+
*
|
|
498
|
+
* ── THE GAP THIS CLOSES ─────────────────────────────────────────────────
|
|
499
|
+
*
|
|
500
|
+
* The camera pane has anchored its wheel at the cursor since it shipped
|
|
501
|
+
* (`wheelToView`), and the working pane did not: a plain `dollyBy` only
|
|
502
|
+
* shortens the distance to the TARGET, so wheeling toward a layer off to one
|
|
503
|
+
* side approached the middle of the pane instead and the author had to pan
|
|
504
|
+
* after every notch. Blender calls it "Zoom to Mouse Position" and everyone
|
|
505
|
+
* turns it on.
|
|
506
|
+
*
|
|
507
|
+
* ── WHY THE RATIO IS TAKEN AFTER THE CLAMP ──────────────────────────────
|
|
508
|
+
*
|
|
509
|
+
* The whole rig scales UNIFORMLY about `anchor`, which makes that point a fixed
|
|
510
|
+
* point of the map — and a fixed point is what lets the wheel coalescer's
|
|
511
|
+
* summed deltas compose. Re-raycasting a stationary cursor after each flush
|
|
512
|
+
* returns the same anchor, so clamping per-step and clamping the composed step
|
|
513
|
+
* land on the same view, exactly as `dollyBy`'s own note requires.
|
|
514
|
+
*
|
|
515
|
+
* That only holds if the scale factor is the one the DISTANCE actually took.
|
|
516
|
+
* Deriving it from the raw `exp(dy / 320)` instead would keep sliding the
|
|
517
|
+
* target after the distance had hit a wall — the picture would go on drifting
|
|
518
|
+
* while the dolly had stopped, which is the one thing a wall must not do.
|
|
519
|
+
*
|
|
520
|
+
* `dollyBy` itself is untouched: this is a second function, so every caller
|
|
521
|
+
* that wants the plain dolly (the zoom buttons, the numpad, the fallback when
|
|
522
|
+
* the cursor's ray meets nothing) keeps it byte-identical.
|
|
523
|
+
*/
|
|
524
|
+
export function dollyTowards(
|
|
525
|
+
view: WorkingView,
|
|
526
|
+
anchor: readonly [number, number, number],
|
|
527
|
+
wheelDeltaY: number,
|
|
528
|
+
bounds?: DollyBounds,
|
|
529
|
+
): WorkingView {
|
|
530
|
+
const next = dollyBy(view, wheelDeltaY, bounds);
|
|
531
|
+
const was = clampDistance(view.distance);
|
|
532
|
+
const k = was > 0 ? next.distance / was : 1;
|
|
533
|
+
if (!Number.isFinite(k) || anchor.some((n) => !Number.isFinite(n))) return next;
|
|
534
|
+
return {
|
|
535
|
+
...next,
|
|
536
|
+
target: [
|
|
537
|
+
anchor[0] + (view.target[0] - anchor[0]) * k,
|
|
538
|
+
anchor[1] + (view.target[1] - anchor[1]) * k,
|
|
539
|
+
anchor[2] + (view.target[2] - anchor[2]) * k,
|
|
540
|
+
],
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
370
544
|
/**
|
|
371
545
|
* The view an author gets before they touch anything: an off-axis three-quarter
|
|
372
546
|
* look with the WHOLE canvas in shot.
|
|
@@ -461,25 +635,38 @@ export type WorkingViewPreset = "front" | "back" | "right" | "left" | "top" | "b
|
|
|
461
635
|
* to lean on mid-layout. Front is theta 0 (the shot camera's own axis, per
|
|
462
636
|
* this file's convention) and back wraps to -180 via `wrapDeg`.
|
|
463
637
|
*
|
|
464
|
-
* TOP AND BOTTOM
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
* the
|
|
638
|
+
* TOP AND BOTTOM ARE THE POLES. They used to land five degrees short, because
|
|
639
|
+
* `lookAt` with world-up writes a NaN quaternion straight down the pole — so
|
|
640
|
+
* "top" was a nearly-plan view and a true plan view was unreachable, which is
|
|
641
|
+
* the one thing a numpad axis key exists for. `StageTools` now takes `cam.up`
|
|
642
|
+
* from `viewBasis`, whose closed form is well defined AT the pole (see
|
|
643
|
+
* MIN_PHI_DEG), and the clamp opens to the full range.
|
|
644
|
+
*
|
|
645
|
+
* The author's THETA is kept, which is what decides the roll up there — spin
|
|
646
|
+
* the view before pressing Numpad7 and the plan view arrives rotated the same
|
|
647
|
+
* way, which is the turntable behaviour Blender has.
|
|
468
648
|
*/
|
|
469
649
|
export function presetWorkingView(view: WorkingView, preset: WorkingViewPreset): WorkingView {
|
|
650
|
+
// ── AND IT GOES ORTHOGRAPHIC ────────────────────────────────────────────
|
|
651
|
+
//
|
|
652
|
+
// Which is what a front or a top view is FOR: a plan and an elevation, with
|
|
653
|
+
// no vanishing point to read a size wrong from. Blender's Auto Perspective,
|
|
654
|
+
// on by default there and now here; `orbitBy` is the other half, dropping
|
|
655
|
+
// back to perspective on the way out of an axis view.
|
|
656
|
+
const flat = { ...view, ortho: true } as const;
|
|
470
657
|
switch (preset) {
|
|
471
658
|
case "front":
|
|
472
|
-
return { ...
|
|
659
|
+
return { ...flat, theta: 0, phi: 90 };
|
|
473
660
|
case "back":
|
|
474
|
-
return { ...
|
|
661
|
+
return { ...flat, theta: wrapDeg(180), phi: 90 };
|
|
475
662
|
case "right":
|
|
476
|
-
return { ...
|
|
663
|
+
return { ...flat, theta: 90, phi: 90 };
|
|
477
664
|
case "left":
|
|
478
|
-
return { ...
|
|
665
|
+
return { ...flat, theta: -90, phi: 90 };
|
|
479
666
|
case "top":
|
|
480
|
-
return { ...
|
|
667
|
+
return { ...flat, phi: MIN_PHI_DEG };
|
|
481
668
|
case "bottom":
|
|
482
|
-
return { ...
|
|
669
|
+
return { ...flat, phi: MAX_PHI_DEG };
|
|
483
670
|
}
|
|
484
671
|
}
|
|
485
672
|
|
|
@@ -498,15 +685,39 @@ export const INITIAL_WORKING_VIEW: WorkingView = defaultWorkingView(
|
|
|
498
685
|
WORKING_FOV_DEG,
|
|
499
686
|
);
|
|
500
687
|
|
|
688
|
+
/**
|
|
689
|
+
* Frame a WORLD of a given extent around a point, keeping the caller's angles:
|
|
690
|
+
* `defaultWorkingView`'s own derivation with the extent substituted for the
|
|
691
|
+
* canvas diagonal. Home inside a component-driven camera fits the component's
|
|
692
|
+
* world with this rather than an empty canvas thousands of units away.
|
|
693
|
+
*/
|
|
694
|
+
export function workingViewForExtent(
|
|
695
|
+
view: WorkingView,
|
|
696
|
+
centre: readonly [number, number, number],
|
|
697
|
+
extent: number,
|
|
698
|
+
fovDeg: number = WORKING_FOV_DEG,
|
|
699
|
+
): WorkingView {
|
|
700
|
+
const span = Number.isFinite(extent) && extent > 0 ? extent : 1;
|
|
701
|
+
return {
|
|
702
|
+
theta: view.theta,
|
|
703
|
+
phi: view.phi,
|
|
704
|
+
distance: clampDistance(planeDistance(span, fovDeg, 1) / (1 - FIT_MARGIN * 2)),
|
|
705
|
+
target: [centre[0], centre[1], centre[2]],
|
|
706
|
+
...(view.ortho === undefined ? {} : { ortho: view.ortho }),
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
501
710
|
/**
|
|
502
711
|
* The working view, projected onto the FRAMING model — "align the camera to
|
|
503
712
|
* this view", and the half of the round trip that can fail.
|
|
504
713
|
*
|
|
505
714
|
* ── WHY THIS CAN REFUSE, AND WHY REFUSING IS THE FEATURE ────────────────
|
|
506
715
|
*
|
|
507
|
-
* A `Framing` is
|
|
508
|
-
*
|
|
509
|
-
* describe a
|
|
716
|
+
* A `Framing` is five numbers (x, y, zoom, rollDeg, focusZ), of which only four
|
|
717
|
+
* are POSE — `focusZ` picks the focus plane, not where the camera stands — and
|
|
718
|
+
* all four describe a camera that looks straight down -z from the +z side. A
|
|
719
|
+
* free orbit has five pose degrees of freedom, and most of them describe a pose
|
|
720
|
+
* no framing can name. Writing "the closest framing" for a pose
|
|
510
721
|
* the author is looking at from behind the canvas would hand them a camera
|
|
511
722
|
* pointing at something else and call it aligned; naming the reason costs one
|
|
512
723
|
* message and is the only answer they can act on.
|
|
@@ -530,7 +741,7 @@ export const INITIAL_WORKING_VIEW: WorkingView = defaultWorkingView(
|
|
|
530
741
|
* the framing already had.
|
|
531
742
|
*/
|
|
532
743
|
export type ViewFramingResult =
|
|
533
|
-
| { ok: true; x: number; y: number; zoom: number }
|
|
744
|
+
| { ok: true; x: number; y: number; zoom: number; yawDeg: number; pitchDeg: number }
|
|
534
745
|
| { ok: false; refusal: string };
|
|
535
746
|
|
|
536
747
|
/**
|
|
@@ -564,7 +775,7 @@ export function framingFromWorkingView(
|
|
|
564
775
|
return {
|
|
565
776
|
ok: false,
|
|
566
777
|
refusal:
|
|
567
|
-
"The working camera is not looking anywhere a framing could describe
|
|
778
|
+
"The working camera is not looking anywhere a framing could describe. Press Home to " +
|
|
568
779
|
"put it back, then align.",
|
|
569
780
|
};
|
|
570
781
|
}
|
|
@@ -574,7 +785,7 @@ export function framingFromWorkingView(
|
|
|
574
785
|
ok: false,
|
|
575
786
|
refusal:
|
|
576
787
|
"The working camera is behind the canvas, and the shot camera always looks at it from the " +
|
|
577
|
-
"front
|
|
788
|
+
"front. Orbit back round to the front of the layers, then align.",
|
|
578
789
|
};
|
|
579
790
|
}
|
|
580
791
|
// Towards the plane means -z: the eye is in front of it, so the axis has to
|
|
@@ -585,7 +796,7 @@ export function framingFromWorkingView(
|
|
|
585
796
|
ok: false,
|
|
586
797
|
refusal:
|
|
587
798
|
"The working camera is looking along the canvas rather than at it, so there is no " +
|
|
588
|
-
"rectangle for the shot camera to frame
|
|
799
|
+
"rectangle for the shot camera to frame. Orbit until the canvas faces you, then align.",
|
|
589
800
|
};
|
|
590
801
|
}
|
|
591
802
|
|
|
@@ -607,10 +818,21 @@ export function framingFromWorkingView(
|
|
|
607
818
|
const span = view.ortho === true ? clampDistance(view.distance) : t;
|
|
608
819
|
const zoom = frame.height / (2 * span * Math.tan((fovDeg * DEG) / 2));
|
|
609
820
|
if (!Number.isFinite(zoom) || zoom <= 0) {
|
|
610
|
-
return { ok: false, refusal: "The working camera is too close to the canvas to express as a framing
|
|
821
|
+
return { ok: false, refusal: "The working camera is too close to the canvas to express as a framing. Dolly back, then align." };
|
|
611
822
|
}
|
|
612
|
-
// The one mapping, undone: world y is design -y.
|
|
613
|
-
|
|
823
|
+
// The one mapping, undone: world y is design -y. And THE ANGLES (schema
|
|
824
|
+
// v26): the shot camera stands on the same sphere the working view orbits,
|
|
825
|
+
// so its yaw is the view's azimuth and its pitch the view's elevation. The
|
|
826
|
+
// orbit pivot moves from the view's target to the hit on the plane, but the
|
|
827
|
+
// eye and the direction are the same, so the picture is.
|
|
828
|
+
return {
|
|
829
|
+
ok: true,
|
|
830
|
+
x: hit[0],
|
|
831
|
+
y: -hit[1],
|
|
832
|
+
zoom,
|
|
833
|
+
yawDeg: wrapDeg(view.theta),
|
|
834
|
+
pitchDeg: Math.max(-MAX_CAMERA_PITCH_DEG, Math.min(MAX_CAMERA_PITCH_DEG, 90 - clampPhi(view.phi))),
|
|
835
|
+
};
|
|
614
836
|
}
|
|
615
837
|
|
|
616
838
|
/**
|
|
@@ -622,15 +844,37 @@ export function framingFromWorkingView(
|
|
|
622
844
|
* claim, and so the fitter has one place to derive a distance from a lens.
|
|
623
845
|
*/
|
|
624
846
|
export function workingViewForFraming(
|
|
625
|
-
framing: { x: number; y: number; zoom: number },
|
|
847
|
+
framing: { x: number; y: number; zoom: number; yawDeg?: number; pitchDeg?: number },
|
|
626
848
|
frame: FrameSize,
|
|
627
849
|
fovDeg: number = WORKING_FOV_DEG,
|
|
850
|
+
/**
|
|
851
|
+
* Which extent has to fit inside the pane.
|
|
852
|
+
*
|
|
853
|
+
* ── "height" IS THE ROUND TRIP; "diagonal" IS THE FIT ───────────────────
|
|
854
|
+
*
|
|
855
|
+
* `framingFromWorkingView` derives a zoom from the framed HEIGHT, so the
|
|
856
|
+
* inverse has to use the height too or "align, then look at what you aligned
|
|
857
|
+
* to" stops being an identity. That is what this function is for and it
|
|
858
|
+
* stays the default, byte-identical.
|
|
859
|
+
*
|
|
860
|
+
* But FRAME SELECTED keeps the author's angles, and a rectangle seen from an
|
|
861
|
+
* angle is turned: the extent that has to fit is its DIAGONAL. Framing the
|
|
862
|
+
* height instead under-fits at every oblique view, which is the ponytail
|
|
863
|
+
* ceiling `fitWorkingViewToSelection` used to carry — the box lands correct
|
|
864
|
+
* head-on and creeps off the pane the further round the author has orbited.
|
|
865
|
+
* The diagonal is rotation-invariant, so no angle can push a corner out; it
|
|
866
|
+
* is `defaultWorkingView`'s own derivation with the framed rectangle
|
|
867
|
+
* substituted for the canvas.
|
|
868
|
+
*/
|
|
869
|
+
fit: "height" | "diagonal" = "height",
|
|
628
870
|
): WorkingView {
|
|
629
871
|
const zoom = framing.zoom > 0 ? framing.zoom : 1;
|
|
872
|
+
const extent = fit === "diagonal" ? Math.hypot(frame.width, frame.height) : frame.height;
|
|
873
|
+
// The framing's own orbit angles (v26): the inverse of `framingFromWorkingView`.
|
|
630
874
|
return {
|
|
631
|
-
theta: 0,
|
|
632
|
-
phi: 90,
|
|
633
|
-
distance: clampDistance(
|
|
875
|
+
theta: wrapDeg(framing.yawDeg ?? 0),
|
|
876
|
+
phi: clampPhi(90 - (framing.pitchDeg ?? 0)),
|
|
877
|
+
distance: clampDistance(extent / (2 * zoom * Math.tan((fovDeg * DEG) / 2))),
|
|
634
878
|
target: [framing.x, -framing.y, 0],
|
|
635
879
|
};
|
|
636
880
|
}
|