create-definedmotion 0.1.3 → 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.
Files changed (25) hide show
  1. package/package.json +1 -1
  2. package/template/package-lock.json +313 -59
  3. package/template/package.json +1 -0
  4. package/template/src/entry.ts +3 -4
  5. package/template/src/example_scenes/alternativesScene.ts +1 -1
  6. package/template/src/example_scenes/fourierSeriesScene.ts +3 -3
  7. package/template/src/example_scenes/keyboardScene.ts +11 -11
  8. package/template/src/example_scenes/tests/animations/test_updater.ts +24 -0
  9. package/template/src/example_scenes/tests/deferred_anims/testing_deferredAnims.ts +71 -0
  10. package/template/src/example_scenes/tests/deferred_anims/testing_deferredAnims2.ts +65 -0
  11. package/template/src/example_scenes/tutorials/easy1.ts +4 -4
  12. package/template/src/example_scenes/tutorials/easy3.ts +1 -1
  13. package/template/src/example_scenes/visulizingFunctions.ts +2 -2
  14. package/template/src/main/index.ts +59 -3
  15. package/template/src/preload/index.ts +15 -1
  16. package/template/src/renderer/index.html +1 -1
  17. package/template/src/renderer/src/App.svelte +178 -23
  18. package/template/src/renderer/src/application_assets/360.svg +39 -0
  19. package/template/src/renderer/src/application_assets/move.svg +37 -0
  20. package/template/src/renderer/src/lib/animation/captureCanvas.ts +2 -2
  21. package/template/src/renderer/src/lib/animation/interpolations.ts +2 -1
  22. package/template/src/renderer/src/lib/general/helpers.ts +16 -47
  23. package/template/src/renderer/src/lib/scene/sceneClass.ts +100 -33
  24. package/template/src/renderer/src/lib/animation/helpers.ts +0 -7
  25. package/template/src/scenes/.gitignore +0 -5
@@ -2,16 +2,42 @@
2
2
  import './app.css'
3
3
  import { generateID, setStateInScene, updateStateInUrl } from './lib/general/helpers'
4
4
  import { onDestroy, onMount } from 'svelte'
5
- import { setGlobalContainerRef, type AnimatedScene } from './lib/scene/sceneClass'
5
+ import { hotreloadNameLookup, screenFPS, setGlobalContainerRef, type AnimatedScene } from './lib/scene/sceneClass'
6
6
  import { loadFonts } from './lib/rendering/objects2d'
7
- import { entryScene } from '../../entry'
7
+ import { animationFPSDivider, entryScene, renderSkip } from '../../entry'
8
8
  import { callAllDestroyFunctions } from './lib/general/onDestory'
9
+ import rotateIcon from "./application_assets/360.svg"
10
+ import moveIcon from "./application_assets/move.svg"
11
+
12
+
13
+ let frameValueElement: HTMLParagraphElement
14
+ let timeValueElement: HTMLParagraphElement
15
+ let sliderElement: HTMLInputElement
16
+
17
+ const TEXT_FRAME_MS_LIMIT = 0.90*1000 / 30; // A little lower to avoid skipping frames when timing is unfourtunate
18
+ const SLIDER_FRAME_MS_LIMIT = 0.90*1000 / 60; // A little lower to avoid skipping frames when timing is unfourtunate
19
+
20
+ let lastTextUpdate = 0;
21
+ let lastSliderUpdate = 0;
22
+
23
+ let screenRefreshRate = $state(0)
24
+ let isRendering = $state(false)
25
+
26
+
27
+ function formatMs(ms: number) {
28
+ const sign = ms < 0 ? '-' : ''
29
+ ms = Math.abs(ms)
30
+ const minutes = Math.floor(ms / 60000)
31
+ const seconds = Math.floor((ms % 60000) / 1000)
32
+ const millis = Math.floor(ms % 1000)
33
+ return `${sign}${String(minutes).padStart(2,'0')}:${String(seconds).padStart(2,'0')}.${String(millis).padStart(3,'0')}`
34
+ }
9
35
 
10
- //const ipcHandle = (): void => window.electron.ipcRenderer.send('ping')
11
36
 
12
37
  const animationWindowID = generateID()
13
38
 
14
39
  let scene: AnimatedScene
40
+ let hasInitScene = $state(false)
15
41
 
16
42
  let isPlayingStateVar = $state(false)
17
43
 
@@ -25,25 +51,63 @@
25
51
  const frame = Math.round((sliderValue / maxSliderValue) * (scene.totalSceneTicks - 1))
26
52
  if (frame !== lastSetFrame) {
27
53
  await scene.jumpToFrameAtIndex(frame)
54
+ updateUIImmediate();
28
55
  lastSetFrame = frame
29
56
  }
30
57
  }
31
58
  }
32
59
 
60
+ function updateSliderOnly() {
61
+ if (!scene || !sliderElement) return;
62
+ const denom = Math.max(1, (scene.totalSceneTicks - 1));
63
+ (sliderElement as any).value = (scene.sceneRenderTick / denom) * maxSliderValue;
64
+ }
65
+
66
+ function updateTextsOnly() {
67
+ if (!scene) return;
68
+ if (frameValueElement) frameValueElement.textContent = `Frame: ${scene.sceneRenderTick}`;
69
+ if (timeValueElement) timeValueElement.textContent = `Time: ${formatMs(scene.getCurrentTimeMs())}`;
70
+ }
71
+
72
+ function updateUIImmediate() {
73
+ updateSliderOnly();
74
+ updateTextsOnly();
75
+ }
76
+
77
+
78
+ function maybeUpdateUI() {
79
+ const now = performance.now();
80
+
81
+ // ~60 Hz slider
82
+ if (now - lastSliderUpdate >= SLIDER_FRAME_MS_LIMIT) {
83
+ lastSliderUpdate = now;
84
+ updateSliderOnly();
85
+ }
86
+
87
+ // ~30 Hz texts
88
+ if (now - lastTextUpdate >= TEXT_FRAME_MS_LIMIT) {
89
+ lastTextUpdate = now;
90
+ updateTextsOnly();
91
+ }
92
+ }
93
+
33
94
  onMount(async () => {
34
95
  if (!entryScene) return
35
96
  await loadFonts()
36
97
  const animationWindow = document.getElementById(animationWindowID)
37
- const sliderElement = document.getElementById('playerSliderID')
98
+
38
99
  if (!animationWindow || !sliderElement) return
39
100
 
40
101
  setGlobalContainerRef(animationWindow)
41
102
 
42
103
  scene = entryScene()
104
+ hasInitScene = true
43
105
 
44
106
  scene.playEffectFunction = () => {
45
- ;(sliderElement as any).value =
46
- (scene.sceneRenderTick / (scene.totalSceneTicks - 1)) * maxSliderValue
107
+ maybeUpdateUI();
108
+ }
109
+ scene.renderingEventFunction = (isStart) => {
110
+ isRendering = isStart
47
111
  }
48
112
  const currentWidth = animationWindow.clientWidth
49
113
  animationWindow.style.height = `${currentWidth / scene.getAspectRatio()}px`
@@ -61,6 +125,8 @@
61
125
  animationWindow.style.height = `${currentWidth / scene.getAspectRatio()}px`
62
126
  })
63
127
 
128
+ screenRefreshRate = screenFPS
129
+
64
130
  // ipcRenderer.send('resize-window', { width: 1000, height: 1000 })
65
131
  })
66
132
 
@@ -68,23 +134,67 @@
68
134
  clearInterval(urlUpdaterInterval)
69
135
  callAllDestroyFunctions()
70
136
  })
137
+
138
+ function fmt(n: number) {
139
+ return Number(n).toPrecision(7);
140
+ }
141
+
142
+ function cameraPositionCode() {
143
+ const p = scene.camera.position;
144
+ return `scene.camera.position.set(
145
+ ${fmt(p.x)},
146
+ ${fmt(p.y)},
147
+ ${fmt(p.z)}
148
+ );`;
149
+ }
150
+
151
+ function cameraRotationCode() {
152
+ const q = scene.camera.quaternion;
153
+ return `scene.camera.quaternion.set(
154
+ ${fmt(q.x)},
155
+ ${fmt(q.y)},
156
+ ${fmt(q.z)},
157
+ ${fmt(q.w)}
158
+ );`;
159
+ }
160
+
161
+ export async function copyToClipboard(text: string): Promise<void> {
162
+ await navigator.clipboard.writeText(text);
163
+ }
71
164
  </script>
72
165
 
73
- <div class=" flex flex-col p-4">
74
- <div id={animationWindowID} class="w-full"></div>
75
- <div class="flex justify-between mt-2 font-bold text-sm">
166
+ <div class=" flex flex-col p-2">
167
+ <div id={animationWindowID} class="w-full rounded-sm overflow-clip"></div>
168
+ {#if isRendering}
169
+ <p class="text-[17px] self-center p-6 pb-1">Do <strong>not save</strong> code during rendering</p>
170
+ <p class="text-xs self-center pt-0 p-2">The viewer might hot reload and affect the result</p>
171
+ {/if}
172
+
173
+ <div class="flex justify-between mt-2 font-bold text-sm items-center">
174
+
175
+
176
+
76
177
  <button
77
- onclick={() => {
78
- if (scene.isPlaying) {
79
- scene.pause()
80
- isPlayingStateVar = false
81
- } else {
82
- scene.playSequenceOfAnimation(scene.sceneRenderTick, scene.totalSceneTicks - 1)
83
- isPlayingStateVar = true
84
- }
85
- }}>{isPlayingStateVar ? 'Pause' : 'Play'}</button
86
- >
178
+ class="w-[70px] text-xs cursor-pointer bg-black/5 rounded-full p-1 hover:bg-black/10 transition"
179
+ onclick={() => {
180
+ if (scene.isPlaying) {
181
+ scene.pause()
182
+ updateUIImmediate();
183
+ isPlayingStateVar = false
184
+ } else {
185
+ scene.playSequenceOfAnimation(scene.sceneRenderTick, scene.totalSceneTicks - 1)
186
+ isPlayingStateVar = true
187
+ }
188
+ }}>{isPlayingStateVar ? 'Pause' : 'Play'}</button
189
+ >
190
+
191
+
192
+ <div class="flex ">
193
+ <p bind:this={frameValueElement} class="font-normal text-[0.7rem] leading-none mr-2 w-[83px]">Frame:</p>
194
+ <p bind:this={timeValueElement} class="font-normal text-[0.7rem] leading-none w-[93px] ">Time:</p>
195
+ </div>
87
196
  <button
197
+ class="w-[70px] text-xs cursor-pointer bg-black/5 rounded-full p-1 hover:bg-black/10 transition"
88
198
  onclick={() => {
89
199
  scene.render()
90
200
  }}>Render</button
@@ -92,16 +202,51 @@
92
202
  </div>
93
203
  <div class="w-full px-0 mx-0">
94
204
  <input
205
+ bind:this={sliderElement}
95
206
  type="range"
96
207
  min="0"
97
208
  max={maxSliderValue}
98
209
  oninput={(e: any) => handleSliderChange(Number(e.target.value))}
99
210
  class="w-full focus:outline-none"
100
- id="playerSliderID"
101
211
  />
102
212
  </div>
213
+
214
+ <div class="h-4"></div>
215
+ {#if hasInitScene && scene}
216
+
217
+ <p class="font-bold text-sm">Helpers</p>
218
+ <div class="h-2"></div>
219
+ <div class="flex flex-wrap gap-2">
220
+ <button onclick={() => copyToClipboard(cameraPositionCode())} class="text-[0.65rem] font-medium cursor-pointer bg-black/5 rounded-full p-1 pl-4 pr-4 hover:bg-black/10 transition active:bg-blue-200 active:border-blue-200" >
221
+ <div class="flex gap-1 items-center">
222
+
223
+ <p>Copy camera <strong>position</strong></p>
224
+ <img src={moveIcon} alt="Rotation icon" class="w-[15px]"/></div>
225
+ </button>
226
+ <button onclick={() => copyToClipboard(cameraRotationCode())} class="text-[0.65rem] font-medium cursor-pointer bg-black/5 rounded-full p-1 pl-4 pr-4 hover:bg-black/10 transition active:bg-blue-200 active:border-blue-200" >
227
+ <div class="flex gap-1 items-center">
228
+
229
+ <p>Copy camera <strong>rotation</strong></p>
230
+ <img src={rotateIcon} alt="Rotation icon" class="w-[15px]"/></div>
231
+ </button>
232
+ </div>
233
+
234
+ <div class="h-6"></div>
235
+ <p class="font-bold text-sm">Details</p>
236
+ <div class="h-2"></div>
237
+ <p class="text-xs">Animation playback FPS: <strong>{(screenRefreshRate/animationFPSDivider).toFixed(2)}</strong> Hz, rendered video FPS: <strong>{(screenRefreshRate/animationFPSDivider/renderSkip).toFixed(2)}</strong> Hz</p>
238
+ <div class="h-2"></div>
239
+ <p class="text-[0.7rem] opacity-50">Hot reload mode: <strong>{hotreloadNameLookup(scene.hotReloadSetting)}</strong></p>
240
+ <p class="text-[0.7rem] opacity-50">Screen refresh rate: <strong>{screenRefreshRate.toFixed(2)}</strong> Hz</p>
241
+ <p class="text-[0.7rem] opacity-50">Animation FPS divider <strong>{animationFPSDivider}</strong></p>
242
+ <p class="text-[0.7rem] opacity-50">Render skip constant <strong>{renderSkip}</strong></p>
243
+ {/if}
244
+
245
+
246
+ <!--
103
247
  <p id="cameraPositionTextID" class="mt-2 text-xs"></p>
104
248
  <p id="cameraRotationTextID" class="mt-2 text-xs"></p>
249
+ -->
105
250
  </div>
106
251
 
107
252
  <style>
@@ -121,10 +266,20 @@
121
266
  /* Thumb style for Chrome */
122
267
  input[type='range']::-webkit-slider-thumb {
123
268
  -webkit-appearance: none;
124
- width: 16px;
269
+ width: 10px;
125
270
  height: 16px;
126
- border-radius: 50%;
127
- background: #3b82f6;
271
+ border-radius: 5px;
272
+ background: #c2c2c2;
273
+ border: 2px solid #616161;
128
274
  margin-top: -6px; /* Center the thumb on the track */
275
+ cursor: pointer;
276
+ transition: all 0.1s ease;
277
+
278
+ /* Larger hitbox using box-shadow trick */
279
+ box-shadow: 0 0 0 8px transparent;
280
+ }
281
+
282
+ input[type='range']::-webkit-slider-thumb:hover {
283
+ background: #616161;
129
284
  }
130
285
  </style>
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5
+ width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
6
+ preserveAspectRatio="xMidYMid meet">
7
+
8
+ <g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
9
+ fill="#000000" stroke="none">
10
+ <path d="M2455 5107 c-370 -95 -662 -590 -820 -1392 -21 -104 -39 -192 -41
11
+ -193 -1 -2 -62 -15 -136 -28 -679 -125 -1176 -363 -1363 -654 -70 -109 -89
12
+ -169 -89 -290 0 -90 4 -115 27 -173 89 -229 331 -423 714 -576 302 -120 714
13
+ -212 1141 -257 94 -10 172 -21 172 -25 0 -4 -20 -23 -44 -42 -56 -46 -80 -96
14
+ -73 -153 11 -105 124 -166 214 -116 18 9 99 81 181 160 l150 142 149 0 c144 0
15
+ 407 13 527 26 l59 6 -7 -38 c-103 -575 -330 -1064 -545 -1173 -68 -35 -120
16
+ -37 -185 -8 -106 49 -198 155 -303 352 -53 100 -63 112 -106 133 -63 32 -118
17
+ 24 -168 -27 -53 -53 -59 -101 -23 -183 130 -292 329 -508 531 -574 86 -28 238
18
+ -26 318 6 261 101 470 384 631 854 51 150 123 432 150 589 16 93 21 108 39
19
+ 112 11 2 72 14 135 25 323 59 642 161 878 279 272 137 445 299 519 488 23 58
20
+ 27 83 27 173 -1 87 -5 116 -25 170 -99 262 -398 477 -889 640 -100 33 -134 40
21
+ -167 35 -84 -12 -145 -105 -123 -186 17 -60 63 -93 190 -134 282 -93 488 -202
22
+ 611 -325 74 -74 103 -129 103 -200 1 -178 -261 -375 -689 -518 -186 -62 -527
23
+ -143 -542 -129 -3 3 0 70 6 149 50 611 2 1310 -125 1838 -31 129 -66 257 -81
24
+ 296 -3 9 16 5 59 -12 80 -32 148 -35 186 -7 87 64 96 170 20 240 -12 11 -112
25
+ 55 -222 96 -109 42 -199 77 -201 79 -1 2 -21 37 -45 78 -124 217 -280 368
26
+ -445 430 -73 27 -207 35 -280 17z m216 -317 c81 -42 194 -172 257 -296 l21
27
+ -42 -75 -173 c-41 -96 -79 -190 -85 -209 -24 -80 35 -170 120 -185 33 -5 51
28
+ -2 89 17 40 19 52 33 78 87 17 35 34 60 36 55 12 -19 78 -296 102 -424 30
29
+ -157 60 -384 77 -590 24 -272 15 -846 -18 -1127 l-6 -51 -46 -6 c-119 -16
30
+ -295 -27 -501 -32 l-225 -5 -139 161 c-143 167 -172 190 -238 190 -34 0 -93
31
+ -30 -115 -59 -7 -9 -17 -33 -23 -53 -16 -55 2 -107 56 -167 53 -59 59 -56 -97
32
+ -41 -804 81 -1465 332 -1610 611 -31 60 -31 138 0 198 76 147 315 297 658 415
33
+ 132 45 360 104 496 129 l79 15 -6 -47 c-14 -107 -26 -360 -26 -552 0 -197 1
34
+ -207 23 -239 36 -54 71 -74 127 -74 54 0 87 18 124 68 20 26 21 46 27 294 7
35
+ 268 26 561 39 581 15 26 509 56 787 49 161 -5 187 -3 222 13 53 23 81 70 81
36
+ 132 0 85 -42 131 -137 149 -69 13 -606 5 -787 -12 -65 -6 -120 -9 -122 -7 -6
37
+ 6 53 282 87 399 131 466 310 766 504 845 40 17 114 9 166 -17z"/>
38
+ </g>
39
+ </svg>
@@ -0,0 +1,37 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
3
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
4
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg"
5
+ width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
6
+ preserveAspectRatio="xMidYMid meet">
7
+
8
+ <g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
9
+ fill="#000000" stroke="none">
10
+ <path d="M2485 4781 c-16 -10 -166 -155 -332 -322 -254 -257 -303 -310 -312
11
+ -344 -35 -134 112 -245 234 -177 11 6 88 79 170 161 l150 151 5 -554 c5 -540
12
+ 6 -555 26 -582 39 -53 71 -69 134 -69 63 0 95 16 134 69 20 27 21 42 26 582
13
+ l5 554 150 -151 c83 -82 159 -155 170 -161 64 -36 140 -25 191 26 30 30 57 94
14
+ 49 119 -2 7 -6 26 -10 42 -5 22 -89 113 -313 338 -169 170 -319 315 -334 323
15
+ -38 20 -107 18 -143 -5z"/>
16
+ <path d="M1035 3285 c-5 -2 -23 -6 -40 -10 -22 -5 -113 -89 -338 -313 -170
17
+ -169 -315 -319 -323 -334 -18 -35 -18 -101 0 -136 8 -15 153 -165 323 -334
18
+ 225 -224 316 -308 338 -313 17 -4 35 -8 42 -10 25 -8 89 19 119 49 51 51 62
19
+ 127 26 191 -6 11 -79 88 -161 170 l-151 150 554 5 c540 5 555 6 582 26 53 39
20
+ 69 71 69 134 0 63 -16 95 -69 134 -27 20 -42 21 -582 26 l-554 5 151 150 c82
21
+ 83 155 159 161 170 55 98 0 213 -112 238 -14 3 -29 4 -35 2z"/>
22
+ <path d="M4030 3278 c-96 -27 -142 -143 -92 -233 6 -11 79 -87 161 -170 l151
23
+ -150 -554 -5 c-540 -5 -555 -6 -582 -26 -53 -39 -69 -71 -69 -134 0 -63 16
24
+ -95 69 -134 27 -21 40 -21 584 -24 l556 -2 -161 -163 c-130 -131 -163 -170
25
+ -172 -202 -14 -54 2 -110 43 -151 30 -30 94 -57 119 -49 7 2 26 6 42 10 22 5
26
+ 113 89 338 313 170 169 315 319 323 334 8 15 14 45 14 68 0 23 -6 53 -14 68
27
+ -8 15 -153 165 -323 334 -224 223 -316 308 -338 313 -16 4 -37 8 -45 10 -8 2
28
+ -31 -1 -50 -7z"/>
29
+ <path d="M2495 2066 c-37 -17 -70 -52 -84 -89 -7 -19 -11 -211 -11 -570 l0
30
+ -541 -162 161 c-114 112 -172 163 -193 168 -16 4 -35 8 -41 10 -26 8 -94 -19
31
+ -122 -48 -29 -31 -55 -96 -47 -120 2 -7 6 -25 10 -42 5 -22 89 -113 313 -338
32
+ 169 -170 319 -315 334 -323 35 -18 101 -18 136 0 15 8 165 153 334 323 224
33
+ 225 308 316 313 338 4 17 8 35 10 42 8 25 -19 89 -49 119 -51 51 -127 62 -191
34
+ 26 -11 -6 -87 -79 -170 -161 l-150 -151 -5 554 c-5 540 -6 555 -26 582 -11 15
35
+ -32 37 -46 47 -34 25 -113 32 -153 13z"/>
36
+ </g>
37
+ </svg>
@@ -1,6 +1,6 @@
1
- import { renderOutputFps } from '../../../../entry'
1
+
2
2
  import * as THREE from 'three'
3
- import { AnimatedScene } from '../scene/sceneClass'
3
+ import { AnimatedScene, renderOutputFps } from '../scene/sceneClass'
4
4
  import { AudioInScene } from '../audio/loader'
5
5
 
6
6
  const fs = require('fs')
@@ -1,4 +1,5 @@
1
- import { millisToTicks } from './helpers'
1
+ import { millisToTicks } from "../scene/sceneClass"
2
+
2
3
 
3
4
  export const concatInterpols = (...interpolations: number[][]) => {
4
5
  return interpolations.reduce((acc, curr) => acc.concat(curr), [])
@@ -1,4 +1,4 @@
1
- import type { AnimatedScene } from '../scene/sceneClass'
1
+ import { HotReloadSetting, type AnimatedScene } from '../scene/sceneClass'
2
2
  import * as THREE from 'three'
3
3
 
4
4
  const frameValueString = 'frameValueIndex'
@@ -12,7 +12,22 @@ export const updateStateInUrl = (stateValue: number) => {
12
12
  window.history.replaceState(null, '', url.toString())
13
13
  }
14
14
 
15
+ export const clearStateInUrl = () => {
16
+ const url = new URL(window.location.href)
17
+ url.searchParams.delete(frameValueString)
18
+ window.history.replaceState(null, '', url.toString())
19
+ }
20
+
21
+
15
22
  export const setStateInScene = async (scene: AnimatedScene) => {
23
+
24
+ // If the user wants a completely fresh start on each rebuild, ignore URL state.
25
+ if (scene.hotReloadSetting === HotReloadSetting.BeginFreshOnSave) {
26
+ clearStateInUrl() // optional, but keeps URL tidy
27
+ await scene.jumpToFrameAtIndex(0) // fully fresh
28
+ return
29
+ }
30
+
16
31
  const url = new URL(window.location.href)
17
32
  const stateParam = url.searchParams.get(frameValueString)
18
33
 
@@ -29,49 +44,3 @@ export const setStateInScene = async (scene: AnimatedScene) => {
29
44
  }
30
45
  await scene.jumpToFrameAtIndex(0)
31
46
  }
32
-
33
- let lastStateText = ''
34
-
35
- export const logCameraState = (
36
- camera: THREE.PerspectiveCamera | THREE.OrthographicCamera
37
- ): void => {
38
- // Clone all values to prevent reference-related issues
39
- const position = camera.position.clone()
40
- const rotation = camera.rotation.clone()
41
- const quaternion = camera.quaternion.clone()
42
-
43
- // Format numbers to 7 significant digits and create code-ready string
44
- const stateCode = `
45
- scene.camera.position.set(
46
- ${position.x.toPrecision(7)},
47
- ${position.y.toPrecision(7)},
48
- ${position.z.toPrecision(7)}
49
- );
50
-
51
- scene.camera.rotation.set(
52
- ${rotation.x.toPrecision(7)},
53
- ${rotation.y.toPrecision(7)},
54
- ${rotation.z.toPrecision(7)}
55
- );
56
-
57
- scene.camera.quaternion.set(
58
- ${quaternion.x.toPrecision(7)},
59
- ${quaternion.y.toPrecision(7)},
60
- ${quaternion.z.toPrecision(7)},
61
- ${quaternion.w.toPrecision(7)}
62
- );
63
-
64
- scene.camera.rotation.order = '${rotation.order}';
65
- `
66
-
67
- // Only update DOM if state changed
68
- if (stateCode !== lastStateText) {
69
- lastStateText = stateCode
70
- const output = document.getElementById('cameraPositionTextID')
71
- if (output) {
72
- output.textContent = stateCode
73
- // Add CSS to preserve whitespace and line breaks
74
- output.style.whiteSpace = 'pre'
75
- }
76
- }
77
- }