File size: 3,027 Bytes
b7ed1ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { ref, watch } from 'vue'
import { CameraWidget } from '../CameraWidget'
import type { CameraState } from '../types'

export function useCameraWidget(

  initialState: Partial<CameraState> = {},

  onExternalStateChange?: (state: CameraState) => void

) {
  const azimuth = ref(initialState.azimuth ?? 0)
  const elevation = ref(initialState.elevation ?? 0)
  const distance = ref(initialState.distance ?? 5)
  const imageUrl = ref<string | null>(null)
  const prompt = ref('<sks> front view eye-level shot medium shot')

  let widget: CameraWidget | null = null
  let updatingFromWidget = false
  let updatingFromExternal = false

  function notifyParent() {
    onExternalStateChange?.({
      azimuth: azimuth.value,
      elevation: elevation.value,
      distance: distance.value,
      imageUrl: imageUrl.value
    })
  }

  function initScene(container: HTMLElement) {
    widget = new CameraWidget({
      container,
      initialState: {
        azimuth: azimuth.value,
        elevation: elevation.value,
        distance: distance.value
      },
      onStateChange: (state: CameraState) => {
        updatingFromWidget = true
        azimuth.value = state.azimuth
        elevation.value = state.elevation
        distance.value = state.distance
        prompt.value = widget!.generatePrompt()
        updatingFromWidget = false
        notifyParent()
      }
    })
    prompt.value = widget.generatePrompt()
  }

  watch([azimuth, elevation, distance], () => {
    if (!updatingFromWidget && !updatingFromExternal && widget) {
      widget.setState({
        azimuth: azimuth.value,
        elevation: elevation.value,
        distance: distance.value
      })
      prompt.value = widget.generatePrompt()
      notifyParent()
    }
  }, { flush: 'sync' })

  function setState(state: Partial<CameraState>) {
    updatingFromExternal = true
    if (state.azimuth !== undefined) azimuth.value = state.azimuth
    if (state.elevation !== undefined) elevation.value = state.elevation
    if (state.distance !== undefined) distance.value = state.distance
    widget?.setState(state)
    if (widget) prompt.value = widget.generatePrompt()
    updatingFromExternal = false
  }

  function updateImage(url: string | null) {
    imageUrl.value = url
    widget?.updateImage(url)
  }

  function setCameraView(enabled: boolean) {
    widget?.setCameraView(enabled)
  }

  function reset() {
    updatingFromExternal = true
    azimuth.value = 0
    elevation.value = 0
    distance.value = 5
    updatingFromExternal = false
    widget?.setState({ azimuth: 0, elevation: 0, distance: 5 })
    if (widget) prompt.value = widget.generatePrompt()
    notifyParent()
  }

  function cleanup() {
    widget?.dispose()
    widget = null
  }

  return {
    azimuth,
    elevation,
    distance,
    imageUrl,
    prompt,
    initScene,
    setState,
    updateImage,
    setCameraView,
    reset,
    cleanup
  }
}