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.
- package/package.json +1 -1
- package/template/package-lock.json +313 -59
- package/template/package.json +1 -0
- package/template/src/entry.ts +3 -4
- package/template/src/example_scenes/alternativesScene.ts +1 -1
- package/template/src/example_scenes/fourierSeriesScene.ts +3 -3
- package/template/src/example_scenes/keyboardScene.ts +11 -11
- package/template/src/example_scenes/tests/animations/test_updater.ts +24 -0
- package/template/src/example_scenes/tests/deferred_anims/testing_deferredAnims.ts +71 -0
- package/template/src/example_scenes/tests/deferred_anims/testing_deferredAnims2.ts +65 -0
- package/template/src/example_scenes/tutorials/easy1.ts +4 -4
- package/template/src/example_scenes/tutorials/easy3.ts +1 -1
- package/template/src/example_scenes/visulizingFunctions.ts +2 -2
- package/template/src/main/index.ts +59 -3
- package/template/src/preload/index.ts +15 -1
- package/template/src/renderer/index.html +1 -1
- package/template/src/renderer/src/App.svelte +178 -23
- package/template/src/renderer/src/application_assets/360.svg +39 -0
- package/template/src/renderer/src/application_assets/move.svg +37 -0
- package/template/src/renderer/src/lib/animation/captureCanvas.ts +2 -2
- package/template/src/renderer/src/lib/animation/interpolations.ts +2 -1
- package/template/src/renderer/src/lib/general/helpers.ts +16 -47
- package/template/src/renderer/src/lib/scene/sceneClass.ts +100 -33
- package/template/src/renderer/src/lib/animation/helpers.ts +0 -7
- 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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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-
|
|
74
|
-
<div id={animationWindowID} class="w-full"></div>
|
|
75
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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:
|
|
269
|
+
width: 10px;
|
|
125
270
|
height: 16px;
|
|
126
|
-
border-radius:
|
|
127
|
-
background: #
|
|
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
|
-
|
|
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,4 @@
|
|
|
1
|
-
import type
|
|
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
|
-
}
|