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

Optimize styles and layout

Browse files
Files changed (1) hide show
  1. index.html +426 -439
index.html CHANGED
@@ -2,449 +2,436 @@
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>
 
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
+
219
+ function loadEpisodesCsv() {
220
+ fetch('https://huggingface.co/datasets/cyberorigin/test/resolve/main/episodes.csv')
221
+ .then(response => response.text())
222
+ .then(data => {
223
+ const lines = data.split('\n')
224
+ processEpisodes(lines)
225
+ })
226
+ .catch(error => console.error('Error loading CSV file:', error))
227
+ }
228
+
229
+ function processEpisodes(lines) {
230
+ episodesGrid.innerHTML = ''
231
+ lines.forEach((line, index) => {
232
+ if (line.trim() !== '' && index != 0) {
233
+ const id = line.split(',')[0]
234
+ const episodeNumber = index
235
+
236
+ const radioDiv = document.createElement('div')
237
+ radioDiv.className = 'episode-radio'
238
+
239
+ const radio = document.createElement('input')
240
+ radio.type = 'radio'
241
+ radio.id = `episode${episodeNumber}`
242
+ radio.name = 'episodeGroup'
243
+ radio.addEventListener('change', () => updateVideoAndCSVSource(id))
244
+
245
+ const label = document.createElement('label')
246
+ label.htmlFor = `episode${episodeNumber}`
247
+ label.textContent = `Episode ${episodeNumber}`
248
+
249
+ radioDiv.appendChild(radio)
250
+ radioDiv.appendChild(label)
251
+ episodesGrid.appendChild(radioDiv)
252
+ }
253
+ })
254
+ }
255
+
256
+
257
+ let totalEpisodes = 100 //获取episode的数量
258
+ // const container = document.getElementById('episodes-container')
259
+ // 循环创建复选框
260
+
261
+ let animationFrameId
262
+ let isPlaying = false
263
+
264
+ function togglePlayPause() {
265
+ if (!isPlaying) {
266
+ laptopVideo.play()
267
+ playPauseBtn.textContent = '⏸️'
268
+ isPlaying = true
269
+ animate3DVisualization()
270
+ } else {
271
+ laptopVideo.pause()
272
+ playPauseBtn.textContent = '▶️'
273
+ isPlaying = false
274
+ cancelAnimationFrame(animationFrameId)
275
+ }
276
+ }
277
+
278
+ function rewind() {
279
+ laptopVideo.currentTime -= 5
280
+ update3DVisualization()
281
+ }
282
+
283
+ function forward() {
284
+ laptopVideo.currentTime += 5
285
+ update3DVisualization()
286
+ }
287
+
288
+ function restart() {
289
+ laptopVideo.currentTime = 0
290
+ update3DVisualization()
291
+ }
292
+
293
+ playPauseBtn.addEventListener('click', togglePlayPause)
294
+ rewindBtn.addEventListener('click', rewind)
295
+ forwardBtn.addEventListener('click', forward)
296
+ restartBtn.addEventListener('click', restart)
297
+
298
+ function getCoordinates(data, coordinate) {
299
+ return body_part_names.map(part => parseFloat(data[`${part}_${coordinate}`]))
300
+ }
301
+
302
+ let frames
303
+
304
+ function processData(results) {
305
+ console.log('Processing data:', results)
306
+ const motion_capture_data = results.data.filter((_, index) => index % 3 === 0)
307
+ frames = motion_capture_data.map((row, index) => ({
308
+ name: index.toString(),
309
+ data: [{
310
+ x: getCoordinates(row, 'x'),
311
+ y: getCoordinates(row, 'y'),
312
+ z: getCoordinates(row, 'z'),
313
+ mode: 'markers',
314
+ type: 'scatter3d',
315
+ marker: { size: 4.8, color: 'blue' },
316
+ }],
317
+ }))
318
+ if (frames.length === 0) {
319
+ console.error('No frames were created from the data')
320
+ return
321
+ }
322
+ const initialFrame = frames[0].data[0]
323
+ const layout = {
324
+ title: {
325
+ text: '3D Motion Capture',
326
+ font: {
327
+ color: 'white',
328
+ size: 20, // 设置字体大小
329
+ },
330
+ x: 0.5, // 设置标题在x轴的位置(0.5表示居中)
331
+ y: 1.2,
332
+ },
333
+ paper_bgcolor: 'black',
334
+ plot_bgcolor: 'black',
335
+ scene: {
336
+ xaxis: {
337
+ title: 'X',
338
+ color: 'white',
339
+ gridcolor: 'gray',
340
+ },
341
+ yaxis: {
342
+ title: 'Y',
343
+ color: 'white',
344
+ gridcolor: 'gray',
345
+ },
346
+ zaxis: {
347
+ title: 'Z',
348
+ color: 'white',
349
+ gridcolor: 'gray',
350
+ },
351
+ bgcolor: 'black',
352
+ },
353
+ font: { color: 'white' },
354
+ margin: {
355
+ l: 2, // 左边距
356
+ r: 2, // 右边距
357
+ b: 2, // 底部边距
358
+ t: 50, // 顶部边距
359
+ pad: 4, // 图表内边距
360
+ },
361
+ }
362
+ Plotly.newPlot('plotDiv', [initialFrame], layout)
363
+ }
364
+
365
+ function update3DVisualization() {
366
+ if (!frames) return
367
+ const currentTime = laptopVideo.currentTime
368
+ const totalDuration = laptopVideo.duration
369
+ const frameIndex = Math.floor((currentTime / totalDuration) * frames.length)
370
+ const frame = frames[Math.min(frameIndex, frames.length - 1)]
371
+ Plotly.animate('plotDiv', frame, {
372
+ transition: { duration: 0 },
373
+ frame: { duration: 0, redraw: true },
374
+ })
375
+ }
376
+
377
+ function animate3DVisualization() {
378
+ update3DVisualization()
379
+ if (isPlaying) {
380
+ animationFrameId = requestAnimationFrame(animate3DVisualization)
381
+ }
382
+ }
383
+
384
+ //function updateVideoAndCSVSource() {
385
+ //const selectedOption = document.querySelector('input[name="videoOption"]:checked').value;
386
+ //const videoUrl = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/Video/video.mp4`;
387
+ //if (selectedOption != "twist_the_tube") {
388
+ // csvFilePath = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/MoCap/mocap.csv`;
389
+ //}
390
+ //else {
391
+ // csvFilePath = `https://huggingface.co/datasets/cyberorigin/${selectedOption}/resolve/main/MoCap/MoCap.csv`;
392
+ //}
393
+ function updateVideoAndCSVSource(id) {
394
+ const selectedOption = document.querySelector('input[name="episodeGroup"]:checked').value
395
+ const videoUrl = `https://huggingface.co/datasets/cyberorigin/pick_and_place/resolve/main/color/${id}.mp4`
396
+ csvFilePath = `https://huggingface.co/datasets/cyberorigin/pick_and_place/resolve/main/motion_capture/${id}.csv`
397
+
398
+ laptopVideo.pause()
399
+
400
+ laptopVideo.querySelector('source').src = videoUrl
401
+
402
+ laptopVideo.load()
403
+
404
+ isPlaying = false
405
+ playPauseBtn.textContent = '▶️'
406
+ // Fetch and process the new CSV data
407
+ fetchAndProcessActionCSV()
408
+ }
409
+
410
+ function fetchAndProcessActionCSV() {
411
+ fetch(csvFilePath)
412
+ .then(response => {
413
+ if (!response.ok) {
414
+ throw new Error(`HTTP error! status: ${response.status}`)
415
+ }
416
+ return response.text()
417
+ })
418
+ .then(csvString => {
419
+ console.log('CSV data loaded successfully')
420
+ Papa.parse(csvString, {
421
+ header: true,
422
+ dynamicTyping: true,
423
+ complete: processData,
424
+ })
425
+ })
426
+ .catch(error => console.error('Error loading the CSV file:', error))
427
+ }
428
+
429
+ //radioButtons.forEach(radio => {
430
+ // radio.addEventListener('change', updateVideoAndCSVSource);
431
+ //});
432
+ // Initial CSV fetch and processing
433
+ fetchAndProcessActionCSV()
434
+ </script>
435
  </body>
436
 
437
  </html>