Move the model when panes are open
This commit is contained in:
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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> {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user