ghosthcp516 commited on
Commit
cd8601d
·
verified ·
1 Parent(s): afae829

Upload index.html

Browse files
Files changed (1) hide show
  1. index.html +429 -449
index.html CHANGED
@@ -1,450 +1,430 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Motion Capture Visualization</title>
8
- <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
9
- <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script>
10
- <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
11
- <!--'Orbitron', sans-serif;Arial, Helvetica, sans-serif-->
12
- <style>
13
- body {
14
- font-family: 'Orbitron', sans-serif;
15
- margin: 0;
16
- padding: 20px;
17
- display: flex;
18
- flex-direction: column;
19
- align-items: center;
20
- background-color: #000;
21
- color: #fff;
22
- }
23
-
24
- h1 {
25
- color: gold;
26
- text-shadow: 2px 2px 4px rgba(255, 215, 0, 0.5);
27
- }
28
-
29
- .main-container {
30
- display: flex;
31
- width: 100%;
32
- max-width: 1500px;
33
- margin-top: 100px;
34
- }
35
-
36
- .episode-list {
37
- width: 150px;
38
- margin-right: 0px;
39
- }
40
-
41
- .episode-list label {
42
- display: block;
43
- margin-bottom: 10px;
44
- color: #ddd;
45
- }
46
-
47
- .content-container {
48
- display: flex;
49
- justify-content: space-between;
50
- flex-grow: 1;
51
- margin-left: 120px;
52
- }
53
-
54
- .video-container {
55
- width: 58%;
56
- }
57
-
58
- #plotDiv {
59
- width: 460px;
60
- height: 485px;
61
- }
62
-
63
- #episodes-container {
64
- max-height: 500px;
65
- width: 210px;
66
- overflow-y: auto;
67
- overflow-x: hidden;
68
- border: 1px solid #ccc;
69
- border-radius: 5px;
70
- background-color: #222;
71
- margin-left: 10px;
72
- }
73
-
74
- #episodes-title {
75
- background-color: #222;
76
- color: #fff;
77
- padding: 10px;
78
- margin: 0;
79
- position: sticky;
80
- top: 0;
81
- z-index: 1;
82
- }
83
-
84
- #episodes-grid {
85
- display: grid;
86
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
87
- gap: 2px;
88
- padding: 10px;
89
- }
90
-
91
- .episode-radio {
92
- display: flex;
93
- align-items: center;
94
- }
95
-
96
- video {
97
- width: 100%;
98
- height: auto;
99
- }
100
-
101
- .controls {
102
- display: flex;
103
- justify-content: center;
104
- margin-top: 2px;
105
- }
106
-
107
- · button {
108
- margin: 0 5px;
109
- font-size: 20px;
110
- background-color: #333;
111
- color: #fff;
112
- border: none;
113
- padding: 5px 10px;
114
- cursor: pointer;
115
- }
116
-
117
- .checkbox-container label {
118
- display: block;
119
- margin-bottom: 20px;
120
- }
121
-
122
- #loadingIndicator {
123
- display: none;
124
- position: fixed;
125
- top: 50%;
126
- left: 50%;
127
- transform: translate(-50%, -50%);
128
- background-color: rgba(0, 0, 0, 0.7);
129
- color: rgb(255, 255, 255);
130
- padding: 20px;
131
- border-radius: 5px;
132
- z-index: 1000;
133
- }
134
-
135
- .checkbox-list {
136
- max-height: 400px;
137
- /* 设置最大高度,超过此高度会出现滚动条 */
138
- overflow-y: auto;
139
- /* 添加垂直滚动条 */
140
- padding-right: 10px;
141
- /* 为滚动条留出空间 */
142
- }
143
-
144
- .checkbox-list label {
145
- display: block;
146
- margin-bottom: 0px;
147
- color: #ddd;
148
- }
149
-
150
- .checkbox-list input[type="checkbox"] {
151
- margin-right: 10px;
152
- }
153
-
154
- /* 自定义滚动条样式(针对WebKit浏览器) */
155
- .checkbox-list::-webkit-scrollbar {
156
- width: 0px;
157
- }
158
-
159
- .checkbox-list::-webkit-scrollbar-track {
160
- background: #333;
161
- border-radius: 4px;
162
- }
163
-
164
- .checkbox-list::-webkit-scrollbar-thumb {
165
- background: #666;
166
- border-radius: 4px;
167
- }
168
-
169
- .checkbox-list::-webkit-scrollbar-thumb:hover {
170
- background: #888;
171
- }
172
- </style>
173
- </head>
174
-
175
- <body>
176
- <h1>Motion Capture Visualization</h1>
177
-
178
- <div class="main-container">
179
- <!--
180
- <div class="episode-list">
181
- <h3>Episodes</h3>
182
- <label><input type="checkbox"> Episode 1</label>
183
- <label><input type="checkbox"> Episode 2</label>
184
- <label><input type="checkbox"> Episode 29</label>
185
- <label><input type="checkbox"> Episode 30</label>
186
- </div>
187
- -->
188
- <div id="episodes-container">
189
- <h3 id="episodes-title">Episodes</h3>
190
- <div id="episodes-grid"></div>
191
- </div>
192
- <div class="content-container">
193
- <!--
194
- <div class="checkbox-container">
195
- <h3>Tasks</h3>
196
- <label>
197
- <input type="radio" name="videoOption" value="fold_towels" checked> Fold towels
198
- </label>
199
- <label>
200
- <input type="radio" name="videoOption" value="pipette"> Pipette
201
- </label>
202
- <label>
203
- <input type="radio" name="videoOption" value="take_the_item"> Take the item
204
- </label>
205
- <label>
206
- <input type="radio" name="videoOption" value="twist_the_tube"> Twist the tube
207
- </label>
208
- </div>-->
209
-
210
- <div class="video-container">
211
- <video id="laptopVideo">
212
- <source src="https://huggingface.co/datasets/cyberorigin/fold_towels/resolve/main/Video/video.mp4"
213
- type="video/mp4">
214
- Your browser does not support the video tag.
215
- </video>
216
- <div class="controls">
217
- <button id="playPauseBtn">▶️</button>
218
- <button id="rewindBtn">⏪</button>
219
- <button id="forwardBtn">⏩</button>
220
- <button id="restartBtn">↩️</button>
221
- </div>
222
- </div>
223
- <div id="plotDiv"></div>
224
- </div>
225
- </div>
226
-
227
- <div id="loadingIndicator">Loading...</div>
228
-
229
- <script>
230
- let csvFilePath = 'https://huggingface.co/datasets/cyberorigin/fold_towels/resolve/main/MoCap/mocap.csv';
231
- const body_part_names = [
232
- 'Left Shoulder', 'Right Upper Arm', 'Left Lower Leg', 'Spine1', 'Right Upper Leg',
233
- 'Spine3', 'Right Lower Arm', 'Left Foot', 'Right Lower Leg', 'Right Shoulder',
234
- 'Left Hand', 'Left Upper Leg', 'Right Foot', 'Spine', 'Spine2', 'Left Lower Arm',
235
- 'Left Toe', 'Neck', 'Right Hand', 'Right Toe', 'Head', 'Left Upper Arm', 'Hips'
236
- ];
237
- const laptopVideo = document.getElementById('laptopVideo');
238
- const playPauseBtn = document.getElementById('playPauseBtn');
239
- const rewindBtn = document.getElementById('rewindBtn');
240
- const forwardBtn = document.getElementById('forwardBtn');
241
- const restartBtn = document.getElementById('restartBtn');
242
- const radioButtons = document.querySelectorAll('input[name="videoOption"]');
243
- const episodeContainer = document.getElementById('episodes-container');
244
- const episodesGrid = document.getElementById('episodes-grid');
245
- document.addEventListener('DOMContentLoaded', loadEpisodesCsv);
246
- function loadEpisodesCsv() {
247
- fetch("https://huggingface.co/datasets/cyberorigin/test/resolve/main/episodes.csv")
248
- .then(response => response.text())
249
- .then(data => {
250
- const lines = data.split('\n');
251
- processEpisodes(lines);
252
- })
253
- .catch(error => console.error('Error loading CSV file:', error));
254
- }
255
- function processEpisodes(lines) {
256
- episodesGrid.innerHTML = '';
257
- lines.forEach((line, index) => {
258
- if (line.trim() !== '' && index != 0) {
259
- const id = line.split(',')[0];
260
- const episodeNumber = index;
261
-
262
- const radioDiv = document.createElement('div');
263
- radioDiv.className = 'episode-radio';
264
-
265
- const radio = document.createElement('input');
266
- radio.type = 'radio';
267
- radio.id = `episode${episodeNumber}`;
268
- radio.name = 'episodeGroup';
269
- radio.addEventListener('change', () => updateVideoAndCSVSource(id));
270
-
271
- const label = document.createElement('label');
272
- label.htmlFor = `episode${episodeNumber}`;
273
- label.textContent = `Episode ${episodeNumber}`;
274
-
275
- radioDiv.appendChild(radio);
276
- radioDiv.appendChild(label);
277
- episodesGrid.appendChild(radioDiv);
278
- }
279
- });
280
- }
281
-
282
-
283
- let totalEpisodes = 100; //获取episode的数量
284
- // const container = document.getElementById('episodes-container')
285
- // 循环创建复选框
286
-
287
- let animationFrameId;
288
- let isPlaying = false;
289
- function togglePlayPause() {
290
- if (!isPlaying) {
291
- laptopVideo.play();
292
- playPauseBtn.textContent = '⏸️';
293
- isPlaying = true;
294
- animate3DVisualization();
295
- } else {
296
- laptopVideo.pause();
297
- playPauseBtn.textContent = '▶️';
298
- isPlaying = false;
299
- cancelAnimationFrame(animationFrameId);
300
- }
301
- }
302
- function rewind() {
303
- laptopVideo.currentTime -= 5;
304
- update3DVisualization();
305
- }
306
- function forward() {
307
- laptopVideo.currentTime += 5;
308
- update3DVisualization();
309
- }
310
- function restart() {
311
- laptopVideo.currentTime = 0;
312
- update3DVisualization();
313
- }
314
- playPauseBtn.addEventListener('click', togglePlayPause);
315
- rewindBtn.addEventListener('click', rewind);
316
- forwardBtn.addEventListener('click', forward);
317
- restartBtn.addEventListener('click', restart);
318
- function getCoordinates(data, coordinate) {
319
- return body_part_names.map(part => parseFloat(data[`${part}_${coordinate}`]));
320
- }
321
- let frames;
322
- function processData(results) {
323
- console.log("Processing data:", results);
324
- const motion_capture_data = results.data.filter((_, index) => index % 3 === 0);
325
- frames = motion_capture_data.map((row, index) => ({
326
- name: index.toString(),
327
- data: [{
328
- x: getCoordinates(row, 'x'),
329
- y: getCoordinates(row, 'y'),
330
- z: getCoordinates(row, 'z'),
331
- mode: 'markers',
332
- type: 'scatter3d',
333
- marker: { size: 4.8, color: "blue" }
334
- }]
335
- }));
336
- if (frames.length === 0) {
337
- console.error("No frames were created from the data");
338
- return;
339
- }
340
- const initialFrame = frames[0].data[0];
341
- const layout = {
342
- title: {
343
- text: '3D Motion Capture',
344
- font: {
345
- color: 'white',
346
- size: 20 // 设置字体大小
347
- },
348
- x: 0.5, // 设置标题在x轴的位置(0.5表示居中)
349
- y: 1.2
350
- },
351
- paper_bgcolor: 'black',
352
- plot_bgcolor: 'black',
353
- scene: {
354
- xaxis: {
355
- title: 'X',
356
- color: 'white',
357
- gridcolor: 'gray'
358
- },
359
- yaxis: {
360
- title: 'Y',
361
- color: 'white',
362
- gridcolor: 'gray'
363
- },
364
- zaxis: {
365
- title: 'Z',
366
- color: 'white',
367
- gridcolor: 'gray'
368
- },
369
- bgcolor: 'black'
370
- },
371
- font: { color: 'white' },
372
- margin: {
373
- l: 2, // 左边距
374
- r: 2, // 右边距
375
- b: 2, // 底部边距
376
- t: 50, // 顶部边距
377
- pad: 4 // 图表内边距
378
- }
379
- };
380
- Plotly.newPlot('plotDiv', [initialFrame], layout);
381
- }
382
- function update3DVisualization() {
383
- if (!frames) return;
384
- const currentTime = laptopVideo.currentTime;
385
- const totalDuration = laptopVideo.duration;
386
- const frameIndex = Math.floor((currentTime / totalDuration) * frames.length);
387
- const frame = frames[Math.min(frameIndex, frames.length - 1)];
388
- Plotly.animate('plotDiv', frame, {
389
- transition: { duration: 0 },
390
- frame: { duration: 0, redraw: true }
391
- });
392
- }
393
- function animate3DVisualization() {
394
- update3DVisualization();
395
- if (isPlaying) {
396
- animationFrameId = requestAnimationFrame(animate3DVisualization);
397
- }
398
- }
399
- //function updateVideoAndCSVSource() {
400
- //const selectedOption = document.querySelector('input[name="videoOption"]:checked').value;
401
- //const videoUrl = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/Video/video.mp4`;
402
- //if (selectedOption != "twist_the_tube") {
403
- // csvFilePath = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/MoCap/mocap.csv`;
404
- //}
405
- //else {
406
- // csvFilePath = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/MoCap/MoCap.csv`;
407
- //}
408
- function updateVideoAndCSVSource(id) {
409
- const selectedOption = document.querySelector('input[name="episodeGroup"]:checked').value;
410
- const videoUrl = `https://huggingface.co/datasets/cyberorigin/pick_and_place/resolve/main/color/${id}.mp4`;
411
- csvFilePath = `https://huggingface.co/datasets/cyberorigin/pick_and_place/resolve/main/motion_capture/${id}.csv`;
412
-
413
- laptopVideo.pause();
414
-
415
- laptopVideo.querySelector('source').src = videoUrl;
416
-
417
- laptopVideo.load();
418
-
419
- isPlaying = false;
420
- playPauseBtn.textContent = '▶️';
421
- // Fetch and process the new CSV data
422
- fetchAndProcessActionCSV();
423
- }
424
- function fetchAndProcessActionCSV() {
425
- fetch(csvFilePath)
426
- .then(response => {
427
- if (!response.ok) {
428
- throw new Error(`HTTP error! status: ${response.status}`);
429
- }
430
- return response.text();
431
- })
432
- .then(csvString => {
433
- console.log("CSV data loaded successfully");
434
- Papa.parse(csvString, {
435
- header: true,
436
- dynamicTyping: true,
437
- complete: processData
438
- });
439
- })
440
- .catch(error => console.error('Error loading the CSV file:', error));
441
- }
442
- //radioButtons.forEach(radio => {
443
- // radio.addEventListener('change', updateVideoAndCSVSource);
444
- //});
445
- // Initial CSV fetch and processing
446
- fetchAndProcessActionCSV();
447
- </script>
448
- </body>
449
-
450
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Motion Capture Visualization</title>
8
+ <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script>
10
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
11
+ <!--'Orbitron', sans-serif;Arial, Helvetica, sans-serif-->
12
+ <style>
13
+ body {
14
+ margin: 0;
15
+ padding: 20px;
16
+ font-family: 'Orbitron', 'Arial', sans-serif;
17
+ background-color: #1a1a1a;
18
+ color: #ffffff;
19
+ min-height: 90vh;
20
+ }
21
+
22
+ h1 {
23
+ width: 1280px;
24
+ color: #ffffff;
25
+ margin: 0 auto 30px;
26
+ font-size: 30px;
27
+ text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.5)
28
+ }
29
+
30
+ .main-container {
31
+ display: grid;
32
+ grid-template-columns: 250px 1fr;
33
+ gap: 20px;
34
+ max-width: 1280px;
35
+ margin: 0 auto;
36
+ height: calc(100vh - 80px);
37
+ }
38
+
39
+ /* Panel A: Episodes List */
40
+ #episodes-container {
41
+ background-color: #2a2a2a;
42
+ border-radius: 10px;
43
+ padding: 15px;
44
+ height: 90%;
45
+ overflow-y: auto;
46
+ }
47
+
48
+ #episodes-title {
49
+ color: #ffffff;
50
+ margin-top: 0;
51
+ padding-bottom: 10px;
52
+ border-bottom: 1px solid #3a3a3a;
53
+ }
54
+
55
+ #episodes-grid {
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: 4px;
59
+ margin-top: 15px;
60
+ }
61
+
62
+ .episode-radio {
63
+ padding: 8px;
64
+ border-radius: 6px;
65
+ transition: background-color 0.2s;
66
+ }
67
+
68
+ .episode-radio:hover {
69
+ background-color: #3a3a3a;
70
+ }
71
+
72
+ .episode-radio input[type="radio"] {
73
+ margin-right: 10px;
74
+ }
75
+
76
+ .episode-radio label {
77
+ cursor: pointer;
78
+ }
79
+
80
+ /* Right Content Container */
81
+ .content-container {
82
+ display: grid;
83
+ grid-template-columns: 1fr;
84
+ grid-template-rows: auto 1fr;
85
+ gap: 20px;
86
+ height: 100%;
87
+ }
88
+
89
+ /* Video Container */
90
+ .video-container {
91
+ display: flex;
92
+ flex-direction: column;
93
+ align-items: center;
94
+ background-color: #2a2a2a;
95
+ border-radius: 10px;
96
+ padding: 22px;
97
+ height: fit-content;
98
+ }
99
+
100
+ video {
101
+ width: 720px;
102
+ border-radius: 6px;
103
+ margin-bottom: 15px;
104
+ background-color: #000;
105
+ }
106
+
107
+ .controls {
108
+ display: flex;
109
+ gap: 10px;
110
+ justify-content: center;
111
+ padding: 10px 0;
112
+ }
113
+
114
+ .controls button {
115
+ background-color: #3a3a3a;
116
+ border: none;
117
+ border-radius: 6px;
118
+ padding: 4px 8px;
119
+ cursor: pointer;
120
+ transition: background-color 0.2s;
121
+ font-size: 18px;
122
+ }
123
+
124
+ .controls button:hover {
125
+ background-color: #4a4a4a;
126
+ }
127
+
128
+ /* Plot Container */
129
+ #plotDiv {
130
+ background-color: #2a2a2a;
131
+ border-radius: 10px;
132
+ padding: 15px;
133
+ width: 720px;
134
+ height: 550px;
135
+ }
136
+
137
+ #loadingIndicator {
138
+ position: fixed;
139
+ top: 50%;
140
+ left: 50%;
141
+ transform: translate(-50%, -50%);
142
+ background-color: rgba(0, 0, 0, 0.8);
143
+ padding: 20px 40px;
144
+ border-radius: 10px;
145
+ display: none;
146
+ }
147
+
148
+ /* Scrollbar Styling */
149
+ ::-webkit-scrollbar {
150
+ width: 8px;
151
+ }
152
+
153
+ ::-webkit-scrollbar-track {
154
+ background: #2a2a2a;
155
+ }
156
+
157
+ ::-webkit-scrollbar-thumb {
158
+ background: #4a4a4a;
159
+ border-radius: 4px;
160
+ }
161
+
162
+ ::-webkit-scrollbar-thumb:hover {
163
+ background: #5a5a5a;
164
+ }
165
+
166
+ </style>
167
+ </head>
168
+
169
+ <body>
170
+ <h1>Motion Capture Visualization</h1>
171
+
172
+ <div class="main-container">
173
+ <!-- Panel A: Episodes List -->
174
+ <div id="episodes-container">
175
+ <h3 id="episodes-title">Episodes</h3>
176
+ <div id="episodes-grid"></div>
177
+ </div>
178
+
179
+ <div class="content-container">
180
+ <!-- Panel B: Video Player -->
181
+ <div class="video-container">
182
+ <video id="laptopVideo">
183
+ <source src="https://huggingface.co/datasets/cyberorigin/fold_towels/resolve/main/Video/video.mp4"
184
+ type="video/mp4">
185
+ Your browser does not support the video tag.
186
+ </video>
187
+ <div class="controls">
188
+ <button id="playPauseBtn">▶️</button>
189
+ <button id="rewindBtn">⏪</button>
190
+ <button id="forwardBtn">⏩</button>
191
+ <button id="restartBtn">↩️</button>
192
+ </div>
193
+ <!-- Panel C: Plot Visualization -->
194
+ <div id="plotDiv"></div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+
199
+ <div id="loadingIndicator">Loading...</div>
200
+
201
+ <script>
202
+ let csvFilePath = 'https://huggingface.co/datasets/cyberorigin/fold_towels/resolve/main/MoCap/mocap.csv';
203
+ const body_part_names = [
204
+ 'Left Shoulder', 'Right Upper Arm', 'Left Lower Leg', 'Spine1', 'Right Upper Leg',
205
+ 'Spine3', 'Right Lower Arm', 'Left Foot', 'Right Lower Leg', 'Right Shoulder',
206
+ 'Left Hand', 'Left Upper Leg', 'Right Foot', 'Spine', 'Spine2', 'Left Lower Arm',
207
+ 'Left Toe', 'Neck', 'Right Hand', 'Right Toe', 'Head', 'Left Upper Arm', 'Hips'
208
+ ];
209
+ const laptopVideo = document.getElementById('laptopVideo');
210
+ const playPauseBtn = document.getElementById('playPauseBtn');
211
+ const rewindBtn = document.getElementById('rewindBtn');
212
+ const forwardBtn = document.getElementById('forwardBtn');
213
+ const restartBtn = document.getElementById('restartBtn');
214
+ const radioButtons = document.querySelectorAll('input[name="videoOption"]');
215
+ const episodeContainer = document.getElementById('episodes-container');
216
+ const episodesGrid = document.getElementById('episodes-grid');
217
+ document.addEventListener('DOMContentLoaded', loadEpisodesCsv);
218
+ function loadEpisodesCsv() {
219
+ fetch("https://huggingface.co/datasets/cyberorigin/test/resolve/main/episodes.csv")
220
+ .then(response => response.text())
221
+ .then(data => {
222
+ const lines = data.split('\n');
223
+ processEpisodes(lines);
224
+ })
225
+ .catch(error => console.error('Error loading CSV file:', error));
226
+ }
227
+ function processEpisodes(lines) {
228
+ episodesGrid.innerHTML = '';
229
+ lines.forEach((line, index) => {
230
+ if (line.trim() !== '' && index != 0) {
231
+ const id = line.split(',')[0];
232
+ const episodeNumber = index;
233
+
234
+ const radioDiv = document.createElement('div');
235
+ radioDiv.className = 'episode-radio';
236
+
237
+ const radio = document.createElement('input');
238
+ radio.type = 'radio';
239
+ radio.id = `episode${episodeNumber}`;
240
+ radio.name = 'episodeGroup';
241
+ radio.addEventListener('change', () => updateVideoAndCSVSource(id));
242
+
243
+ const label = document.createElement('label');
244
+ label.htmlFor = `episode${episodeNumber}`;
245
+ label.textContent = `Episode ${episodeNumber}`;
246
+
247
+ radioDiv.appendChild(radio);
248
+ radioDiv.appendChild(label);
249
+ episodesGrid.appendChild(radioDiv);
250
+ }
251
+ });
252
+ }
253
+
254
+
255
+ let totalEpisodes = 100;
256
+ // const container = document.getElementById('episodes-container')
257
+
258
+ let animationFrameId;
259
+ let isPlaying = false;
260
+ function togglePlayPause() {
261
+ if (!isPlaying) {
262
+ laptopVideo.play();
263
+ playPauseBtn.textContent = '⏸️';
264
+ isPlaying = true;
265
+ animate3DVisualization();
266
+ } else {
267
+ laptopVideo.pause();
268
+ playPauseBtn.textContent = '▶️';
269
+ isPlaying = false;
270
+ cancelAnimationFrame(animationFrameId);
271
+ }
272
+ }
273
+
274
+ function rewind() {
275
+ laptopVideo.currentTime -= 5;
276
+ update3DVisualization();
277
+ }
278
+
279
+ function forward() {
280
+ laptopVideo.currentTime += 5;
281
+ update3DVisualization();
282
+ }
283
+
284
+ function restart() {
285
+ laptopVideo.currentTime = 0;
286
+ update3DVisualization();
287
+ }
288
+ playPauseBtn.addEventListener('click', togglePlayPause);
289
+ rewindBtn.addEventListener('click', rewind);
290
+ forwardBtn.addEventListener('click', forward);
291
+ restartBtn.addEventListener('click', restart);
292
+ function getCoordinates(data, coordinate) {
293
+ return body_part_names.map(part => parseFloat(data[`${part}_${coordinate}`]));
294
+ }
295
+ let frames;
296
+ function processData(results) {
297
+ console.log("Processing data:", results);
298
+ const motion_capture_data = results.data.filter((_, index) => index % 3 === 0);
299
+ frames = motion_capture_data.map((row, index) => ({
300
+ name: index.toString(),
301
+ data: [{
302
+ x: getCoordinates(row, 'x'),
303
+ y: getCoordinates(row, 'y'),
304
+ z: getCoordinates(row, 'z'),
305
+ mode: 'markers',
306
+ type: 'scatter3d',
307
+ marker: { size: 4.8, color: "blue" }
308
+ }]
309
+ }));
310
+ if (frames.length === 0) {
311
+ console.error("No frames were created from the data");
312
+ return;
313
+ }
314
+ const initialFrame = frames[0].data[0]
315
+ // Config canvas layout styles
316
+ const layout = {
317
+ title: {
318
+ text: '3D Motion Capture',
319
+ font: {
320
+ color: 'white',
321
+ size: 20
322
+ },
323
+ x: 0.5,
324
+ y: 1.2
325
+ },
326
+ paper_bgcolor: 'black',
327
+ plot_bgcolor: 'black',
328
+ scene: {
329
+ xaxis: {
330
+ title: 'X',
331
+ color: 'white',
332
+ gridcolor: 'gray'
333
+ },
334
+ yaxis: {
335
+ title: 'Y',
336
+ color: 'white',
337
+ gridcolor: 'gray'
338
+ },
339
+ zaxis: {
340
+ title: 'Z',
341
+ color: 'white',
342
+ gridcolor: 'gray'
343
+ },
344
+ bgcolor: 'black'
345
+ },
346
+ font: {color: 'white'},
347
+ margin: {
348
+ l: 2,
349
+ r: 2,
350
+ b: 2,
351
+ t: 50,
352
+ pad: 4,
353
+ },
354
+ }
355
+ Plotly.newPlot('plotDiv', [initialFrame], layout)
356
+ }
357
+
358
+ function update3DVisualization() {
359
+ if (!frames) return
360
+ const currentTime = laptopVideo.currentTime
361
+ const totalDuration = laptopVideo.duration
362
+ const frameIndex = Math.floor((currentTime / totalDuration) * frames.length)
363
+ const frame = frames[Math.min(frameIndex, frames.length - 1)]
364
+ Plotly.animate('plotDiv', frame, {
365
+ transition: {duration: 0},
366
+ frame: {duration: 0, redraw: true},
367
+ })
368
+ }
369
+
370
+ function animate3DVisualization() {
371
+ update3DVisualization()
372
+ if (isPlaying) {
373
+ animationFrameId = requestAnimationFrame(animate3DVisualization)
374
+ }
375
+ }
376
+
377
+ //function updateVideoAndCSVSource() {
378
+ //const selectedOption = document.querySelector('input[name="videoOption"]:checked').value;
379
+ //const videoUrl = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/Video/video.mp4`;
380
+ //if (selectedOption != "twist_the_tube") {
381
+ // csvFilePath = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/MoCap/mocap.csv`;
382
+ //}
383
+ //else {
384
+ // csvFilePath = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/MoCap/MoCap.csv`;
385
+ //}
386
+ function updateVideoAndCSVSource(id) {
387
+ const selectedOption = document.querySelector('input[name="episodeGroup"]:checked').value;
388
+ const videoUrl = `https://huggingface.co/datasets/cyberorigin/pick_and_place/resolve/main/color/${id}.mp4`;
389
+ csvFilePath = `https://huggingface.co/datasets/cyberorigin/pick_and_place/resolve/main/motion_capture/${id}.csv`;
390
+
391
+ laptopVideo.pause();
392
+
393
+ laptopVideo.querySelector('source').src = videoUrl;
394
+
395
+ laptopVideo.load();
396
+
397
+ isPlaying = false;
398
+ playPauseBtn.textContent = '▶️';
399
+ // Fetch and process the new CSV data
400
+ fetchAndProcessActionCSV();
401
+ }
402
+
403
+ function fetchAndProcessActionCSV() {
404
+ fetch(csvFilePath)
405
+ .then(response => {
406
+ if (!response.ok) {
407
+ throw new Error(`HTTP error! status: ${response.status}`);
408
+ }
409
+ return response.text();
410
+ })
411
+ .then(csvString => {
412
+ console.log("CSV data loaded successfully");
413
+ Papa.parse(csvString, {
414
+ header: true,
415
+ dynamicTyping: true,
416
+ complete: processData
417
+ });
418
+ })
419
+ .catch(error => console.error('Error loading the CSV file:', error));
420
+ }
421
+
422
+ //radioButtons.forEach(radio => {
423
+ // radio.addEventListener('change', updateVideoAndCSVSource);
424
+ //});
425
+ // Initial CSV fetch and processing
426
+ fetchAndProcessActionCSV();
427
+ </script>
428
+ </body>
429
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  </html>