Move the model when panes are open

This commit is contained in:
49lf
2024-09-18 16:54:11 -04:00
parent 563096fba4
commit 6845f0c4bc
4 changed files with 94 additions and 26 deletions

View File

@ -8,7 +8,7 @@ import { PATHS } from 'lib/paths'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { onboardingPaths } from 'routes/Onboarding/paths'
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
import { codeManager, engineCommandManager } from 'lib/singletons'
import { codeManager, engineCommandManager, sceneInfra } from 'lib/singletons'
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
import { isDesktop } from 'lib/isDesktop'
import { useLspContext } from 'components/LspProvider'
@ -37,6 +37,7 @@ export function App() {
// Stream related refs and data
const videoRef = useRef<HTMLVideoElement>(null)
const canvasRef = useRef<HTMLCanvasElement>(null)
const modelingSidebarRef = useRef<HTMLDivElement>(null)
let [searchParams] = useSearchParams()
const pool = searchParams.get('pool')
@ -60,6 +61,10 @@ export function App() {
app: { onboardingStatus },
} = settings.context
useEffect(() => {
sceneInfra.camControls.modelingSidebarRef = modelingSidebarRef
}, [modelingSidebarRef.current])
useHotkeys('backspace', (e) => {
e.preventDefault()
})
@ -90,7 +95,7 @@ export function App() {
enableMenu={true}
/>
<ModalContainer />
<ModelingSidebar paneOpacity={paneOpacity} />
<ModelingSidebar paneOpacity={paneOpacity} ref={modelingSidebarRef} />
<EngineStreamContext.Provider options={{
input: {
videoRef,

View File

@ -1,3 +1,4 @@
import { MutableRefObject } from 'react'
import { cameraMouseDragGuards, MouseGuard } from 'lib/cameraControls'
import {
Euler,
@ -87,6 +88,7 @@ class CameraRateLimiter {
export class CameraControls {
engineCommandManager: EngineCommandManager
modelingSidebarRef: MutableRefObject<HTMLDivElement>
syncDirection: 'clientToEngine' | 'engineToClient' = 'engineToClient'
camera: PerspectiveCamera | OrthographicCamera
target: Vector3
@ -914,16 +916,9 @@ export class CameraControls {
up: { x: 0, y: 0, z: 1 },
},
})
await this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'zoom_to_fit',
object_ids: [], // leave empty to zoom to all objects
padding: 0.2, // padding around the objects
animated: false, // don't animate the zoom for now
},
})
await this.centerModelRelativeToPanes()
this.cameraDragStartXY = {
x: 0,
y: 0,
@ -952,6 +947,71 @@ export class CameraControls {
})
}
async centerModelRelativeToPanes(zoomObjectId?: string): Promise<void> {
const panes = this.modelingSidebarRef.current
if (!panes) return
const panesWidth = panes.offsetWidth + panes.offsetLeft
const goRightPx = panesWidth > 0 ? (window.innerWidth - panesWidth) / 2 : 0
// Originally I had tried to use the default_camera_look_at endpoint and
// some quaternion math to move the camera right, but it ended up being
// overly complicated, and I think the threejs scene also doesn't have the
// camera coordinates after a zoom-to-fit... So this is much easier, and
// maps better to screen coordinates.
await this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_batch_req',
batch_id: uuidv4(),
responses: true,
requests: [
{
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'zoom_to_fit',
object_ids: zoomObjectId ? [zoomObjectId] : [], // leave empty to zoom to all objects
padding: panesWidth > 0 ? (window.innerWidth / panesWidth) : 0.2, // padding around the objects
},
},
{
type: 'modeling_cmd_req',
cmd: {
type: 'camera_drag_start',
interaction: 'pan',
window: { x: 0, y: 0 },
},
cmd_id: uuidv4(),
},
{
type: 'modeling_cmd_req',
cmd: {
type: 'camera_drag_move',
interaction: 'pan',
window: {
x: goRightPx,
y: 0,
},
},
cmd_id: uuidv4(),
},
]
})
// engineCommandManager can't subscribe to batch responses so we'll send
// this one off by its lonesome after.
.then(() => this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd: {
type: 'camera_drag_end',
interaction: 'pan',
window: {
x: goRightPx,
y: 0,
},
},
cmd_id: uuidv4(),
}))
}
async tweenCameraToQuaternion(
targetQuaternion: Quaternion,

View File

@ -7,7 +7,9 @@ import {
useMemo,
ReactNode,
useContext,
} from 'react'
MutableRefObject,
forwardRef,
} from 'react'm
import { useHotkeys } from 'react-hotkeys-hook'
import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes'
import Tooltip from 'components/Tooltip'
@ -23,7 +25,8 @@ import { useKclContext } from 'lang/KclProvider'
import { MachineManagerContext } from 'components/MachineManagerProvider'
interface ModelingSidebarProps {
paneOpacity: '' | 'opacity-20' | 'opacity-40'
paneOpacity: '' | 'opacity-20' | 'opacity-40',
ref: MutableRefObject<HTMLDivElement>
}
interface BadgeInfoComputed {
@ -35,7 +38,7 @@ function getPlatformString(): 'web' | 'desktop' {
return isDesktop() ? 'desktop' : 'web'
}
export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
export const ModelingSidebar = forwardRef(function ModelingSidebar({ paneOpacity }: ModelingSidebarProps, ref) {
const machineManager = useContext(MachineManagerContext)
const { commandBarSend } = useCommandsContext()
const kclContext = useKclContext()
@ -159,6 +162,14 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
[context.store?.openPanes, send]
)
useEffect(() => {
// Don't send camera adjustment commands after 1 pane is open. It
// won't make any difference.
if (context.store?.openPanes > 1) return
void sceneInfra.camControls.centerModelRelativeToPanes()
}, [context.store?.openPanes])
return (
<Resizable
className={`group flex-1 flex flex-col z-10 my-2 pr-1 ${paneOpacity} ${pointerEventsCssClass}`}
@ -236,6 +247,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
</ul>
<ul
id="pane-section"
ref={ref}
className={
'ml-[-1px] col-start-2 col-span-1 flex flex-col gap-2 ' +
(context.store?.openPanes.length >= 1
@ -265,7 +277,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
</div>
</Resizable>
)
}
})
interface ModelingPaneButtonProps
extends React.HTMLAttributes<HTMLButtonElement> {

View File

@ -285,16 +285,7 @@ export class KclManager {
)
}
await this.engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'zoom_to_fit',
object_ids: zoomObjectId ? [zoomObjectId] : [], // leave empty to zoom to all objects
padding: 0.1, // padding around the objects
animated: false, // don't animate the zoom for now
},
})
await sceneInfra.camControls.centerModelRelativeToPanes(zoomObjectId)
}
}