add gizmo (#738)

* add gizmo

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix force ast execution

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2023-09-29 12:41:58 -07:00
committed by GitHub
parent 056fa00adc
commit a367be4e2b
4 changed files with 50 additions and 16 deletions

View File

@ -3,6 +3,7 @@ import { _executor } from '../lang/wasm'
import { useStore } from '../useStore' import { useStore } from '../useStore'
import { engineCommandManager } from '../lang/std/engineConnection' import { engineCommandManager } from '../lang/std/engineConnection'
import { deferExecution } from 'lib/utils' import { deferExecution } from 'lib/utils'
import { v4 as uuidv4 } from 'uuid'
export function useSetupEngineManager( export function useSetupEngineManager(
streamRef: React.RefObject<HTMLDivElement>, streamRef: React.RefObject<HTMLDivElement>,
@ -12,14 +13,14 @@ export function useSetupEngineManager(
setMediaStream, setMediaStream,
setIsStreamReady, setIsStreamReady,
setStreamDimensions, setStreamDimensions,
executeCode,
streamDimensions, streamDimensions,
executeCode,
} = useStore((s) => ({ } = useStore((s) => ({
setMediaStream: s.setMediaStream, setMediaStream: s.setMediaStream,
setIsStreamReady: s.setIsStreamReady, setIsStreamReady: s.setIsStreamReady,
setStreamDimensions: s.setStreamDimensions, setStreamDimensions: s.setStreamDimensions,
executeCode: s.executeCode,
streamDimensions: s.streamDimensions, streamDimensions: s.streamDimensions,
executeCode: s.executeCode,
})) }))
const streamWidth = streamRef?.current?.offsetWidth const streamWidth = streamRef?.current?.offsetWidth
@ -40,11 +41,9 @@ export function useSetupEngineManager(
setIsStreamReady, setIsStreamReady,
width: quadWidth, width: quadWidth,
height: quadHeight, height: quadHeight,
executeCode,
token, token,
}) })
engineCommandManager.waitForReady.then(() => {
executeCode()
})
setStreamDimensions({ setStreamDimensions({
streamWidth: quadWidth, streamWidth: quadWidth,
streamHeight: quadHeight, streamHeight: quadHeight,

View File

@ -491,9 +491,11 @@ export class EngineConnection {
this.onDataChannelOpen(this) this.onDataChannelOpen(this)
this.onEngineConnectionOpen(this)
this.ready = true this.ready = true
this.connecting = false this.connecting = false
// Do this after we set the connection is ready to avoid errors when
// we try to send messages before the connection is ready.
this.onEngineConnectionOpen(this)
}) })
this.unreliableDataChannel.addEventListener('close', (event) => { this.unreliableDataChannel.addEventListener('close', (event) => {
@ -586,6 +588,9 @@ export class EngineCommandManager {
outSequence = 1 outSequence = 1
inSequence = 1 inSequence = 1
engineConnection?: EngineConnection engineConnection?: EngineConnection
// Folks should realize that wait for ready does not get called _everytime_
// the connection resets and restarts, it only gets called the first time.
// Be careful what you put here.
waitForReady: Promise<void> = new Promise(() => {}) waitForReady: Promise<void> = new Promise(() => {})
private resolveReady = () => {} private resolveReady = () => {}
@ -609,12 +614,14 @@ export class EngineCommandManager {
setIsStreamReady, setIsStreamReady,
width, width,
height, height,
executeCode,
token, token,
}: { }: {
setMediaStream: (stream: MediaStream) => void setMediaStream: (stream: MediaStream) => void
setIsStreamReady: (isStreamReady: boolean) => void setIsStreamReady: (isStreamReady: boolean) => void
width: number width: number
height: number height: number
executeCode: (code?: string, force?: boolean) => void
token?: string token?: string
}) { }) {
if (width === 0 || height === 0) { if (width === 0 || height === 0) {
@ -637,6 +644,32 @@ export class EngineCommandManager {
onEngineConnectionOpen: () => { onEngineConnectionOpen: () => {
this.resolveReady() this.resolveReady()
setIsStreamReady(true) setIsStreamReady(true)
// Make the axis gizmo.
// We do this after the connection opened to avoid a race condition.
// Connected opened is the last thing that happens when the stream
// is ready.
// We also do this here because we want to ensure we create the gizmo
// and execute the code everytime the stream is restarted.
const gizmoId = uuidv4()
this.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: gizmoId,
cmd: {
type: 'make_axes_gizmo',
clobber: false,
// If true, axes gizmo will be placed in the corner of the screen.
// If false, it will be placed at the origin of the scene.
gizmo_mode: true,
},
})
// We execute the code here to make sure if the stream was to
// restart in a session, we want to make sure to execute the code.
// We force it to re-execute the code because we want to make sure
// the code is executed everytime the stream is restarted.
// We pass undefined for the code so it reads from the current state.
executeCode(undefined, true)
}, },
onClose: () => { onClose: () => {
setIsStreamReady(false) setIsStreamReady(false)
@ -733,10 +766,6 @@ export class EngineCommandManager {
this.engineConnection?.send(resizeCmd) this.engineConnection?.send(resizeCmd)
} }
handleModelingCommand(message: WebSocketResponse, id: string) { handleModelingCommand(message: WebSocketResponse, id: string) {
if (this.engineConnection === undefined) {
return
}
if (message.type !== 'modeling') { if (message.type !== 'modeling') {
return return
} }
@ -904,6 +933,11 @@ export class EngineCommandManager {
if (this.engineConnection === undefined) { if (this.engineConnection === undefined) {
return Promise.resolve() return Promise.resolve()
} }
if (!this.engineConnection?.isReady()) {
return Promise.resolve()
}
if ( if (
command.type === 'modeling_cmd_req' && command.type === 'modeling_cmd_req' &&
command.cmd.type !== lastMessage command.cmd.type !== lastMessage
@ -911,9 +945,6 @@ export class EngineCommandManager {
console.log('sending command', command.cmd.type) console.log('sending command', command.cmd.type)
lastMessage = command.cmd.type lastMessage = command.cmd.type
} }
if (!this.engineConnection?.isReady()) {
return Promise.resolve()
}
if (command.type !== 'modeling_cmd_req') return Promise.resolve() if (command.type !== 'modeling_cmd_req') return Promise.resolve()
const cmd = command.cmd const cmd = command.cmd
if ( if (

View File

@ -79,6 +79,7 @@ export async function executor(
setMediaStream: () => {}, setMediaStream: () => {},
width: 0, width: 0,
height: 0, height: 0,
executeCode: () => {},
}) })
await engineCommandManager.waitForReady await engineCommandManager.waitForReady
engineCommandManager.startNewSession() engineCommandManager.startNewSession()

View File

@ -157,7 +157,7 @@ export interface StoreState {
code: string code: string
setCode: (code: string) => void setCode: (code: string) => void
deferredSetCode: (code: string) => void deferredSetCode: (code: string) => void
executeCode: (code?: string) => void executeCode: (code?: string, force?: boolean) => void
formatCode: () => void formatCode: () => void
programMemory: ProgramMemory programMemory: ProgramMemory
setProgramMemory: (programMemory: ProgramMemory) => void setProgramMemory: (programMemory: ProgramMemory) => void
@ -221,11 +221,12 @@ export const useStore = create<StoreState>()(
editorView.dispatch({ effects: addLineHighlight.of(selection) }) editorView.dispatch({ effects: addLineHighlight.of(selection) })
} }
}, },
executeCode: async (code) => { executeCode: async (code, force) => {
const result = await executeCode({ const result = await executeCode({
code: code || get().code, code: code || get().code,
lastAst: get().ast, lastAst: get().ast,
engineCommandManager: engineCommandManager, engineCommandManager: engineCommandManager,
force,
}) })
if (!result.isChange) { if (!result.isChange) {
return return
@ -511,10 +512,12 @@ async function executeCode({
engineCommandManager, engineCommandManager,
code, code,
lastAst, lastAst,
force,
}: { }: {
code: string code: string
lastAst: Program lastAst: Program
engineCommandManager: EngineCommandManager engineCommandManager: EngineCommandManager
force?: boolean
}): Promise< }): Promise<
| { | {
logs: string[] logs: string[]
@ -557,7 +560,7 @@ async function executeCode({
} }
// Check if the ast we have is equal to the ast in the storage. // Check if the ast we have is equal to the ast in the storage.
// If it is, we don't need to update the ast. // If it is, we don't need to update the ast.
if (JSON.stringify(ast) === JSON.stringify(lastAst)) if (JSON.stringify(ast) === JSON.stringify(lastAst) && !force)
return { isChange: false } return { isChange: false }
const { logs, errors, programMemory } = await executeAst({ const { logs, errors, programMemory } = await executeAst({