fix_md
This commit is contained in:
parent
25d8520e78
commit
b171863a48
@ -671,6 +671,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<h3>Keep History</h3>
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="enable-keep-history">
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
<label for="enable-keep-history">Enable Keep History</label>
|
||||||
|
</div>
|
||||||
|
<div class="slider-container">
|
||||||
|
<label for="history-stride">Stride</label>
|
||||||
|
<select id="history-stride">
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
<option value="5" selected>5</option>
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="20">20</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<h3>Background</h3>
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="white-background">
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
<label for="white-background">White Background</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button id="reset-view-btn" style="flex: 1; margin-right: 5px;">Reset View</button>
|
<button id="reset-view-btn" style="flex: 1; margin-right: 5px;">Reset View</button>
|
||||||
@ -739,7 +771,10 @@
|
|||||||
showCameraFrustum: document.getElementById('show-camera-frustum'),
|
showCameraFrustum: document.getElementById('show-camera-frustum'),
|
||||||
frustumSize: document.getElementById('frustum-size'),
|
frustumSize: document.getElementById('frustum-size'),
|
||||||
hideSettingsBtn: document.getElementById('hide-settings-btn'),
|
hideSettingsBtn: document.getElementById('hide-settings-btn'),
|
||||||
showSettingsBtn: document.getElementById('show-settings-btn')
|
showSettingsBtn: document.getElementById('show-settings-btn'),
|
||||||
|
enableKeepHistory: document.getElementById('enable-keep-history'),
|
||||||
|
historyStride: document.getElementById('history-stride'),
|
||||||
|
whiteBackground: document.getElementById('white-background')
|
||||||
};
|
};
|
||||||
|
|
||||||
this.scene = null;
|
this.scene = null;
|
||||||
@ -750,6 +785,12 @@
|
|||||||
this.trajectories = [];
|
this.trajectories = [];
|
||||||
this.cameraFrustum = null;
|
this.cameraFrustum = null;
|
||||||
|
|
||||||
|
// Keep History functionality
|
||||||
|
this.historyPointClouds = [];
|
||||||
|
this.historyTrajectories = [];
|
||||||
|
this.historyFrames = [];
|
||||||
|
this.maxHistoryFrames = 20;
|
||||||
|
|
||||||
this.initThreeJS();
|
this.initThreeJS();
|
||||||
this.loadDefaultSettings().then(() => {
|
this.loadDefaultSettings().then(() => {
|
||||||
this.initEventListeners();
|
this.initEventListeners();
|
||||||
@ -977,6 +1018,28 @@
|
|||||||
this.ui.showSettingsBtn.style.display = 'none';
|
this.ui.showSettingsBtn.style.display = 'none';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep History event listeners
|
||||||
|
if (this.ui.enableKeepHistory) {
|
||||||
|
this.ui.enableKeepHistory.addEventListener('change', () => {
|
||||||
|
if (!this.ui.enableKeepHistory.checked) {
|
||||||
|
this.clearHistory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.ui.historyStride) {
|
||||||
|
this.ui.historyStride.addEventListener('change', () => {
|
||||||
|
this.clearHistory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background toggle event listener
|
||||||
|
if (this.ui.whiteBackground) {
|
||||||
|
this.ui.whiteBackground.addEventListener('change', () => {
|
||||||
|
this.toggleBackground();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeElementDraggable(element) {
|
makeElementDraggable(element) {
|
||||||
@ -1296,6 +1359,9 @@
|
|||||||
|
|
||||||
this.updateTrajectories(frameIndex);
|
this.updateTrajectories(frameIndex);
|
||||||
|
|
||||||
|
// Keep History management
|
||||||
|
this.updateHistory(frameIndex);
|
||||||
|
|
||||||
const progress = (frameIndex + 1) / this.config.totalFrames;
|
const progress = (frameIndex + 1) / this.config.totalFrames;
|
||||||
this.ui.progress.style.width = `${progress * 100}%`;
|
this.ui.progress.style.width = `${progress * 100}%`;
|
||||||
|
|
||||||
@ -1752,15 +1818,286 @@
|
|||||||
this.updateCameraFrustum(this.currentFrame);
|
this.updateCameraFrustum(this.currentFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep History methods
|
||||||
|
updateHistory(frameIndex) {
|
||||||
|
if (!this.ui.enableKeepHistory.checked || !this.data) return;
|
||||||
|
|
||||||
|
const stride = parseInt(this.ui.historyStride.value);
|
||||||
|
const newHistoryFrames = this.calculateHistoryFrames(frameIndex, stride);
|
||||||
|
|
||||||
|
// Check if history frames changed
|
||||||
|
if (this.arraysEqual(this.historyFrames, newHistoryFrames)) return;
|
||||||
|
|
||||||
|
this.clearHistory();
|
||||||
|
this.historyFrames = newHistoryFrames;
|
||||||
|
|
||||||
|
// Create history point clouds and trajectories
|
||||||
|
this.historyFrames.forEach(historyFrame => {
|
||||||
|
if (historyFrame !== frameIndex) {
|
||||||
|
this.createHistoryPointCloud(historyFrame);
|
||||||
|
this.createHistoryTrajectories(historyFrame);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateHistoryFrames(currentFrame, stride) {
|
||||||
|
const frames = [];
|
||||||
|
let frame = 1; // Start from frame 1
|
||||||
|
|
||||||
|
while (frame <= currentFrame && frames.length < this.maxHistoryFrames) {
|
||||||
|
frames.push(frame);
|
||||||
|
frame += stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always include current frame
|
||||||
|
if (!frames.includes(currentFrame)) {
|
||||||
|
frames.push(currentFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return frames.sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
createHistoryPointCloud(frameIndex) {
|
||||||
|
const numPoints = this.config.resolution[0] * this.config.resolution[1];
|
||||||
|
const positions = new Float32Array(numPoints * 3);
|
||||||
|
const colors = new Float32Array(numPoints * 3);
|
||||||
|
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||||
|
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||||
|
|
||||||
|
const material = new THREE.PointsMaterial({
|
||||||
|
size: parseFloat(this.ui.pointSize.value),
|
||||||
|
vertexColors: true,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.5, // Transparent for history
|
||||||
|
sizeAttenuation: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const historyPointCloud = new THREE.Points(geometry, material);
|
||||||
|
this.scene.add(historyPointCloud);
|
||||||
|
this.historyPointClouds.push(historyPointCloud);
|
||||||
|
|
||||||
|
// Update the history point cloud with data
|
||||||
|
this.updateHistoryPointCloud(historyPointCloud, frameIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHistoryPointCloud(pointCloud, frameIndex) {
|
||||||
|
const positions = pointCloud.geometry.attributes.position.array;
|
||||||
|
const colors = pointCloud.geometry.attributes.color.array;
|
||||||
|
|
||||||
|
const rgbVideo = this.data.rgb_video;
|
||||||
|
const depthsRgb = this.data.depths_rgb;
|
||||||
|
const intrinsics = this.data.intrinsics;
|
||||||
|
const invExtrinsics = this.data.inv_extrinsics;
|
||||||
|
|
||||||
|
const width = this.config.resolution[0];
|
||||||
|
const height = this.config.resolution[1];
|
||||||
|
const numPoints = width * height;
|
||||||
|
|
||||||
|
const K = this.get3x3Matrix(intrinsics.data, intrinsics.shape, frameIndex);
|
||||||
|
const fx = K[0][0], fy = K[1][1], cx = K[0][2], cy = K[1][2];
|
||||||
|
|
||||||
|
const invExtrMat = this.get4x4Matrix(invExtrinsics.data, invExtrinsics.shape, frameIndex);
|
||||||
|
const transform = this.getTransformElements(invExtrMat);
|
||||||
|
|
||||||
|
const rgbFrame = this.getFrame(rgbVideo.data, rgbVideo.shape, frameIndex);
|
||||||
|
const depthFrame = this.getFrame(depthsRgb.data, depthsRgb.shape, frameIndex);
|
||||||
|
|
||||||
|
const maxDepth = parseFloat(this.ui.maxDepth.value) || 10.0;
|
||||||
|
|
||||||
|
let validPointCount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const xPix = i % width;
|
||||||
|
const yPix = Math.floor(i / width);
|
||||||
|
|
||||||
|
const d0 = depthFrame[i * 3];
|
||||||
|
const d1 = depthFrame[i * 3 + 1];
|
||||||
|
const depthEncoded = d0 | (d1 << 8);
|
||||||
|
const depthValue = (depthEncoded / ((1 << 16) - 1)) *
|
||||||
|
(this.config.depthRange[1] - this.config.depthRange[0]) +
|
||||||
|
this.config.depthRange[0];
|
||||||
|
|
||||||
|
if (depthValue === 0 || depthValue > maxDepth) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const X = ((xPix - cx) * depthValue) / fx;
|
||||||
|
const Y = ((yPix - cy) * depthValue) / fy;
|
||||||
|
const Z = depthValue;
|
||||||
|
|
||||||
|
const tx = transform.m11 * X + transform.m12 * Y + transform.m13 * Z + transform.m14;
|
||||||
|
const ty = transform.m21 * X + transform.m22 * Y + transform.m23 * Z + transform.m24;
|
||||||
|
const tz = transform.m31 * X + transform.m32 * Y + transform.m33 * Z + transform.m34;
|
||||||
|
|
||||||
|
const index = validPointCount * 3;
|
||||||
|
positions[index] = tx;
|
||||||
|
positions[index + 1] = -ty;
|
||||||
|
positions[index + 2] = -tz;
|
||||||
|
|
||||||
|
colors[index] = rgbFrame[i * 3] / 255;
|
||||||
|
colors[index + 1] = rgbFrame[i * 3 + 1] / 255;
|
||||||
|
colors[index + 2] = rgbFrame[i * 3 + 2] / 255;
|
||||||
|
|
||||||
|
validPointCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointCloud.geometry.setDrawRange(0, validPointCount);
|
||||||
|
pointCloud.geometry.attributes.position.needsUpdate = true;
|
||||||
|
pointCloud.geometry.attributes.color.needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
createHistoryTrajectories(frameIndex) {
|
||||||
|
if (!this.data.trajectories) return;
|
||||||
|
|
||||||
|
const trajectoryData = this.data.trajectories.data;
|
||||||
|
const [totalFrames, numTrajectories] = this.data.trajectories.shape;
|
||||||
|
const palette = this.createColorPalette(numTrajectories);
|
||||||
|
|
||||||
|
const historyTrajectoryGroup = new THREE.Group();
|
||||||
|
|
||||||
|
for (let i = 0; i < numTrajectories; i++) {
|
||||||
|
const ballSize = parseFloat(this.ui.trajectoryBallSize.value);
|
||||||
|
const sphereGeometry = new THREE.SphereGeometry(ballSize, 16, 16);
|
||||||
|
const sphereMaterial = new THREE.MeshBasicMaterial({
|
||||||
|
color: palette[i],
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.3 // Transparent for history
|
||||||
|
});
|
||||||
|
const positionMarker = new THREE.Mesh(sphereGeometry, sphereMaterial);
|
||||||
|
|
||||||
|
const currentOffset = (frameIndex * numTrajectories + i) * 3;
|
||||||
|
positionMarker.position.set(
|
||||||
|
trajectoryData[currentOffset],
|
||||||
|
-trajectoryData[currentOffset + 1],
|
||||||
|
-trajectoryData[currentOffset + 2]
|
||||||
|
);
|
||||||
|
|
||||||
|
historyTrajectoryGroup.add(positionMarker);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scene.add(historyTrajectoryGroup);
|
||||||
|
this.historyTrajectories.push(historyTrajectoryGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearHistory() {
|
||||||
|
// Clear history point clouds
|
||||||
|
this.historyPointClouds.forEach(pointCloud => {
|
||||||
|
if (pointCloud.geometry) pointCloud.geometry.dispose();
|
||||||
|
if (pointCloud.material) pointCloud.material.dispose();
|
||||||
|
this.scene.remove(pointCloud);
|
||||||
|
});
|
||||||
|
this.historyPointClouds = [];
|
||||||
|
|
||||||
|
// Clear history trajectories
|
||||||
|
this.historyTrajectories.forEach(trajectoryGroup => {
|
||||||
|
trajectoryGroup.children.forEach(child => {
|
||||||
|
if (child.geometry) child.geometry.dispose();
|
||||||
|
if (child.material) child.material.dispose();
|
||||||
|
});
|
||||||
|
this.scene.remove(trajectoryGroup);
|
||||||
|
});
|
||||||
|
this.historyTrajectories = [];
|
||||||
|
|
||||||
|
this.historyFrames = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
arraysEqual(a, b) {
|
||||||
|
if (a.length !== b.length) return false;
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
if (a[i] !== b[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleBackground() {
|
||||||
|
const isWhiteBackground = this.ui.whiteBackground.checked;
|
||||||
|
|
||||||
|
if (isWhiteBackground) {
|
||||||
|
// Switch to white background
|
||||||
|
document.body.style.backgroundColor = '#ffffff';
|
||||||
|
this.scene.background = new THREE.Color(0xffffff);
|
||||||
|
|
||||||
|
// Update UI elements for white background
|
||||||
|
document.documentElement.style.setProperty('--bg', '#ffffff');
|
||||||
|
document.documentElement.style.setProperty('--text', '#333333');
|
||||||
|
document.documentElement.style.setProperty('--text-secondary', '#666666');
|
||||||
|
document.documentElement.style.setProperty('--border', '#cccccc');
|
||||||
|
document.documentElement.style.setProperty('--surface', '#f5f5f5');
|
||||||
|
document.documentElement.style.setProperty('--shadow', 'rgba(0, 0, 0, 0.1)');
|
||||||
|
document.documentElement.style.setProperty('--shadow-hover', 'rgba(0, 0, 0, 0.2)');
|
||||||
|
|
||||||
|
// Update status bar and control panel backgrounds
|
||||||
|
this.ui.statusBar.style.background = 'rgba(245, 245, 245, 0.9)';
|
||||||
|
this.ui.statusBar.style.color = '#333333';
|
||||||
|
|
||||||
|
const controlPanel = document.getElementById('control-panel');
|
||||||
|
if (controlPanel) {
|
||||||
|
controlPanel.style.background = 'rgba(245, 245, 245, 0.95)';
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsPanel = document.getElementById('settings-panel');
|
||||||
|
if (settingsPanel) {
|
||||||
|
settingsPanel.style.background = 'rgba(245, 245, 245, 0.98)';
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Switch back to dark background
|
||||||
|
document.body.style.backgroundColor = '#1a1a1a';
|
||||||
|
this.scene.background = new THREE.Color(0x1a1a1a);
|
||||||
|
|
||||||
|
// Restore original dark theme variables
|
||||||
|
document.documentElement.style.setProperty('--bg', '#1a1a1a');
|
||||||
|
document.documentElement.style.setProperty('--text', '#e0e0e0');
|
||||||
|
document.documentElement.style.setProperty('--text-secondary', '#a0a0a0');
|
||||||
|
document.documentElement.style.setProperty('--border', '#444444');
|
||||||
|
document.documentElement.style.setProperty('--surface', '#2c2c2c');
|
||||||
|
document.documentElement.style.setProperty('--shadow', 'rgba(0, 0, 0, 0.2)');
|
||||||
|
document.documentElement.style.setProperty('--shadow-hover', 'rgba(0, 0, 0, 0.3)');
|
||||||
|
|
||||||
|
// Restore original UI backgrounds
|
||||||
|
this.ui.statusBar.style.background = 'rgba(30, 30, 30, 0.9)';
|
||||||
|
this.ui.statusBar.style.color = '#e0e0e0';
|
||||||
|
|
||||||
|
const controlPanel = document.getElementById('control-panel');
|
||||||
|
if (controlPanel) {
|
||||||
|
controlPanel.style.background = 'rgba(44, 44, 44, 0.95)';
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsPanel = document.getElementById('settings-panel');
|
||||||
|
if (settingsPanel) {
|
||||||
|
settingsPanel.style.background = 'rgba(44, 44, 44, 0.98)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show status message
|
||||||
|
this.ui.statusBar.textContent = isWhiteBackground ? "Switched to white background" : "Switched to dark background";
|
||||||
|
this.ui.statusBar.classList.remove('hidden');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.ui.statusBar.classList.add('hidden');
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
resetSettings() {
|
resetSettings() {
|
||||||
if (!this.defaultSettings) return;
|
if (!this.defaultSettings) return;
|
||||||
|
|
||||||
this.applyDefaultSettings();
|
this.applyDefaultSettings();
|
||||||
|
|
||||||
|
// Reset background to dark theme
|
||||||
|
if (this.ui.whiteBackground) {
|
||||||
|
this.ui.whiteBackground.checked = false;
|
||||||
|
this.toggleBackground();
|
||||||
|
}
|
||||||
|
|
||||||
this.updatePointCloudSettings();
|
this.updatePointCloudSettings();
|
||||||
this.updateTrajectorySettings();
|
this.updateTrajectorySettings();
|
||||||
this.updateFrustumDimensions();
|
this.updateFrustumDimensions();
|
||||||
|
|
||||||
|
// Clear history when resetting settings
|
||||||
|
this.clearHistory();
|
||||||
|
|
||||||
this.ui.statusBar.textContent = "Settings reset to defaults";
|
this.ui.statusBar.textContent = "Settings reset to defaults";
|
||||||
this.ui.statusBar.classList.remove('hidden');
|
this.ui.statusBar.classList.remove('hidden');
|
||||||
|
|
||||||
|
|||||||
339
viz.html
339
viz.html
@ -671,6 +671,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<h3>Keep History</h3>
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="enable-keep-history">
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
<label for="enable-keep-history">Enable Keep History</label>
|
||||||
|
</div>
|
||||||
|
<div class="slider-container">
|
||||||
|
<label for="history-stride">Stride</label>
|
||||||
|
<select id="history-stride">
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
<option value="5" selected>5</option>
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="20">20</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<h3>Background</h3>
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="white-background">
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
<label for="white-background">White Background</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="settings-group">
|
<div class="settings-group">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button id="reset-view-btn" style="flex: 1; margin-right: 5px;">Reset View</button>
|
<button id="reset-view-btn" style="flex: 1; margin-right: 5px;">Reset View</button>
|
||||||
@ -739,7 +771,10 @@
|
|||||||
showCameraFrustum: document.getElementById('show-camera-frustum'),
|
showCameraFrustum: document.getElementById('show-camera-frustum'),
|
||||||
frustumSize: document.getElementById('frustum-size'),
|
frustumSize: document.getElementById('frustum-size'),
|
||||||
hideSettingsBtn: document.getElementById('hide-settings-btn'),
|
hideSettingsBtn: document.getElementById('hide-settings-btn'),
|
||||||
showSettingsBtn: document.getElementById('show-settings-btn')
|
showSettingsBtn: document.getElementById('show-settings-btn'),
|
||||||
|
enableKeepHistory: document.getElementById('enable-keep-history'),
|
||||||
|
historyStride: document.getElementById('history-stride'),
|
||||||
|
whiteBackground: document.getElementById('white-background')
|
||||||
};
|
};
|
||||||
|
|
||||||
this.scene = null;
|
this.scene = null;
|
||||||
@ -750,6 +785,12 @@
|
|||||||
this.trajectories = [];
|
this.trajectories = [];
|
||||||
this.cameraFrustum = null;
|
this.cameraFrustum = null;
|
||||||
|
|
||||||
|
// Keep History functionality
|
||||||
|
this.historyPointClouds = [];
|
||||||
|
this.historyTrajectories = [];
|
||||||
|
this.historyFrames = [];
|
||||||
|
this.maxHistoryFrames = 20;
|
||||||
|
|
||||||
this.initThreeJS();
|
this.initThreeJS();
|
||||||
this.loadDefaultSettings().then(() => {
|
this.loadDefaultSettings().then(() => {
|
||||||
this.initEventListeners();
|
this.initEventListeners();
|
||||||
@ -977,6 +1018,28 @@
|
|||||||
this.ui.showSettingsBtn.style.display = 'none';
|
this.ui.showSettingsBtn.style.display = 'none';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep History event listeners
|
||||||
|
if (this.ui.enableKeepHistory) {
|
||||||
|
this.ui.enableKeepHistory.addEventListener('change', () => {
|
||||||
|
if (!this.ui.enableKeepHistory.checked) {
|
||||||
|
this.clearHistory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.ui.historyStride) {
|
||||||
|
this.ui.historyStride.addEventListener('change', () => {
|
||||||
|
this.clearHistory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background toggle event listener
|
||||||
|
if (this.ui.whiteBackground) {
|
||||||
|
this.ui.whiteBackground.addEventListener('change', () => {
|
||||||
|
this.toggleBackground();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeElementDraggable(element) {
|
makeElementDraggable(element) {
|
||||||
@ -1296,6 +1359,9 @@
|
|||||||
|
|
||||||
this.updateTrajectories(frameIndex);
|
this.updateTrajectories(frameIndex);
|
||||||
|
|
||||||
|
// Keep History management
|
||||||
|
this.updateHistory(frameIndex);
|
||||||
|
|
||||||
const progress = (frameIndex + 1) / this.config.totalFrames;
|
const progress = (frameIndex + 1) / this.config.totalFrames;
|
||||||
this.ui.progress.style.width = `${progress * 100}%`;
|
this.ui.progress.style.width = `${progress * 100}%`;
|
||||||
|
|
||||||
@ -1752,15 +1818,286 @@
|
|||||||
this.updateCameraFrustum(this.currentFrame);
|
this.updateCameraFrustum(this.currentFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep History methods
|
||||||
|
updateHistory(frameIndex) {
|
||||||
|
if (!this.ui.enableKeepHistory.checked || !this.data) return;
|
||||||
|
|
||||||
|
const stride = parseInt(this.ui.historyStride.value);
|
||||||
|
const newHistoryFrames = this.calculateHistoryFrames(frameIndex, stride);
|
||||||
|
|
||||||
|
// Check if history frames changed
|
||||||
|
if (this.arraysEqual(this.historyFrames, newHistoryFrames)) return;
|
||||||
|
|
||||||
|
this.clearHistory();
|
||||||
|
this.historyFrames = newHistoryFrames;
|
||||||
|
|
||||||
|
// Create history point clouds and trajectories
|
||||||
|
this.historyFrames.forEach(historyFrame => {
|
||||||
|
if (historyFrame !== frameIndex) {
|
||||||
|
this.createHistoryPointCloud(historyFrame);
|
||||||
|
this.createHistoryTrajectories(historyFrame);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateHistoryFrames(currentFrame, stride) {
|
||||||
|
const frames = [];
|
||||||
|
let frame = 1; // Start from frame 1
|
||||||
|
|
||||||
|
while (frame <= currentFrame && frames.length < this.maxHistoryFrames) {
|
||||||
|
frames.push(frame);
|
||||||
|
frame += stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always include current frame
|
||||||
|
if (!frames.includes(currentFrame)) {
|
||||||
|
frames.push(currentFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return frames.sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
createHistoryPointCloud(frameIndex) {
|
||||||
|
const numPoints = this.config.resolution[0] * this.config.resolution[1];
|
||||||
|
const positions = new Float32Array(numPoints * 3);
|
||||||
|
const colors = new Float32Array(numPoints * 3);
|
||||||
|
|
||||||
|
const geometry = new THREE.BufferGeometry();
|
||||||
|
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||||
|
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||||
|
|
||||||
|
const material = new THREE.PointsMaterial({
|
||||||
|
size: parseFloat(this.ui.pointSize.value),
|
||||||
|
vertexColors: true,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.5, // Transparent for history
|
||||||
|
sizeAttenuation: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const historyPointCloud = new THREE.Points(geometry, material);
|
||||||
|
this.scene.add(historyPointCloud);
|
||||||
|
this.historyPointClouds.push(historyPointCloud);
|
||||||
|
|
||||||
|
// Update the history point cloud with data
|
||||||
|
this.updateHistoryPointCloud(historyPointCloud, frameIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHistoryPointCloud(pointCloud, frameIndex) {
|
||||||
|
const positions = pointCloud.geometry.attributes.position.array;
|
||||||
|
const colors = pointCloud.geometry.attributes.color.array;
|
||||||
|
|
||||||
|
const rgbVideo = this.data.rgb_video;
|
||||||
|
const depthsRgb = this.data.depths_rgb;
|
||||||
|
const intrinsics = this.data.intrinsics;
|
||||||
|
const invExtrinsics = this.data.inv_extrinsics;
|
||||||
|
|
||||||
|
const width = this.config.resolution[0];
|
||||||
|
const height = this.config.resolution[1];
|
||||||
|
const numPoints = width * height;
|
||||||
|
|
||||||
|
const K = this.get3x3Matrix(intrinsics.data, intrinsics.shape, frameIndex);
|
||||||
|
const fx = K[0][0], fy = K[1][1], cx = K[0][2], cy = K[1][2];
|
||||||
|
|
||||||
|
const invExtrMat = this.get4x4Matrix(invExtrinsics.data, invExtrinsics.shape, frameIndex);
|
||||||
|
const transform = this.getTransformElements(invExtrMat);
|
||||||
|
|
||||||
|
const rgbFrame = this.getFrame(rgbVideo.data, rgbVideo.shape, frameIndex);
|
||||||
|
const depthFrame = this.getFrame(depthsRgb.data, depthsRgb.shape, frameIndex);
|
||||||
|
|
||||||
|
const maxDepth = parseFloat(this.ui.maxDepth.value) || 10.0;
|
||||||
|
|
||||||
|
let validPointCount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const xPix = i % width;
|
||||||
|
const yPix = Math.floor(i / width);
|
||||||
|
|
||||||
|
const d0 = depthFrame[i * 3];
|
||||||
|
const d1 = depthFrame[i * 3 + 1];
|
||||||
|
const depthEncoded = d0 | (d1 << 8);
|
||||||
|
const depthValue = (depthEncoded / ((1 << 16) - 1)) *
|
||||||
|
(this.config.depthRange[1] - this.config.depthRange[0]) +
|
||||||
|
this.config.depthRange[0];
|
||||||
|
|
||||||
|
if (depthValue === 0 || depthValue > maxDepth) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const X = ((xPix - cx) * depthValue) / fx;
|
||||||
|
const Y = ((yPix - cy) * depthValue) / fy;
|
||||||
|
const Z = depthValue;
|
||||||
|
|
||||||
|
const tx = transform.m11 * X + transform.m12 * Y + transform.m13 * Z + transform.m14;
|
||||||
|
const ty = transform.m21 * X + transform.m22 * Y + transform.m23 * Z + transform.m24;
|
||||||
|
const tz = transform.m31 * X + transform.m32 * Y + transform.m33 * Z + transform.m34;
|
||||||
|
|
||||||
|
const index = validPointCount * 3;
|
||||||
|
positions[index] = tx;
|
||||||
|
positions[index + 1] = -ty;
|
||||||
|
positions[index + 2] = -tz;
|
||||||
|
|
||||||
|
colors[index] = rgbFrame[i * 3] / 255;
|
||||||
|
colors[index + 1] = rgbFrame[i * 3 + 1] / 255;
|
||||||
|
colors[index + 2] = rgbFrame[i * 3 + 2] / 255;
|
||||||
|
|
||||||
|
validPointCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointCloud.geometry.setDrawRange(0, validPointCount);
|
||||||
|
pointCloud.geometry.attributes.position.needsUpdate = true;
|
||||||
|
pointCloud.geometry.attributes.color.needsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
createHistoryTrajectories(frameIndex) {
|
||||||
|
if (!this.data.trajectories) return;
|
||||||
|
|
||||||
|
const trajectoryData = this.data.trajectories.data;
|
||||||
|
const [totalFrames, numTrajectories] = this.data.trajectories.shape;
|
||||||
|
const palette = this.createColorPalette(numTrajectories);
|
||||||
|
|
||||||
|
const historyTrajectoryGroup = new THREE.Group();
|
||||||
|
|
||||||
|
for (let i = 0; i < numTrajectories; i++) {
|
||||||
|
const ballSize = parseFloat(this.ui.trajectoryBallSize.value);
|
||||||
|
const sphereGeometry = new THREE.SphereGeometry(ballSize, 16, 16);
|
||||||
|
const sphereMaterial = new THREE.MeshBasicMaterial({
|
||||||
|
color: palette[i],
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.3 // Transparent for history
|
||||||
|
});
|
||||||
|
const positionMarker = new THREE.Mesh(sphereGeometry, sphereMaterial);
|
||||||
|
|
||||||
|
const currentOffset = (frameIndex * numTrajectories + i) * 3;
|
||||||
|
positionMarker.position.set(
|
||||||
|
trajectoryData[currentOffset],
|
||||||
|
-trajectoryData[currentOffset + 1],
|
||||||
|
-trajectoryData[currentOffset + 2]
|
||||||
|
);
|
||||||
|
|
||||||
|
historyTrajectoryGroup.add(positionMarker);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scene.add(historyTrajectoryGroup);
|
||||||
|
this.historyTrajectories.push(historyTrajectoryGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearHistory() {
|
||||||
|
// Clear history point clouds
|
||||||
|
this.historyPointClouds.forEach(pointCloud => {
|
||||||
|
if (pointCloud.geometry) pointCloud.geometry.dispose();
|
||||||
|
if (pointCloud.material) pointCloud.material.dispose();
|
||||||
|
this.scene.remove(pointCloud);
|
||||||
|
});
|
||||||
|
this.historyPointClouds = [];
|
||||||
|
|
||||||
|
// Clear history trajectories
|
||||||
|
this.historyTrajectories.forEach(trajectoryGroup => {
|
||||||
|
trajectoryGroup.children.forEach(child => {
|
||||||
|
if (child.geometry) child.geometry.dispose();
|
||||||
|
if (child.material) child.material.dispose();
|
||||||
|
});
|
||||||
|
this.scene.remove(trajectoryGroup);
|
||||||
|
});
|
||||||
|
this.historyTrajectories = [];
|
||||||
|
|
||||||
|
this.historyFrames = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
arraysEqual(a, b) {
|
||||||
|
if (a.length !== b.length) return false;
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
if (a[i] !== b[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleBackground() {
|
||||||
|
const isWhiteBackground = this.ui.whiteBackground.checked;
|
||||||
|
|
||||||
|
if (isWhiteBackground) {
|
||||||
|
// Switch to white background
|
||||||
|
document.body.style.backgroundColor = '#ffffff';
|
||||||
|
this.scene.background = new THREE.Color(0xffffff);
|
||||||
|
|
||||||
|
// Update UI elements for white background
|
||||||
|
document.documentElement.style.setProperty('--bg', '#ffffff');
|
||||||
|
document.documentElement.style.setProperty('--text', '#333333');
|
||||||
|
document.documentElement.style.setProperty('--text-secondary', '#666666');
|
||||||
|
document.documentElement.style.setProperty('--border', '#cccccc');
|
||||||
|
document.documentElement.style.setProperty('--surface', '#f5f5f5');
|
||||||
|
document.documentElement.style.setProperty('--shadow', 'rgba(0, 0, 0, 0.1)');
|
||||||
|
document.documentElement.style.setProperty('--shadow-hover', 'rgba(0, 0, 0, 0.2)');
|
||||||
|
|
||||||
|
// Update status bar and control panel backgrounds
|
||||||
|
this.ui.statusBar.style.background = 'rgba(245, 245, 245, 0.9)';
|
||||||
|
this.ui.statusBar.style.color = '#333333';
|
||||||
|
|
||||||
|
const controlPanel = document.getElementById('control-panel');
|
||||||
|
if (controlPanel) {
|
||||||
|
controlPanel.style.background = 'rgba(245, 245, 245, 0.95)';
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsPanel = document.getElementById('settings-panel');
|
||||||
|
if (settingsPanel) {
|
||||||
|
settingsPanel.style.background = 'rgba(245, 245, 245, 0.98)';
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Switch back to dark background
|
||||||
|
document.body.style.backgroundColor = '#1a1a1a';
|
||||||
|
this.scene.background = new THREE.Color(0x1a1a1a);
|
||||||
|
|
||||||
|
// Restore original dark theme variables
|
||||||
|
document.documentElement.style.setProperty('--bg', '#1a1a1a');
|
||||||
|
document.documentElement.style.setProperty('--text', '#e0e0e0');
|
||||||
|
document.documentElement.style.setProperty('--text-secondary', '#a0a0a0');
|
||||||
|
document.documentElement.style.setProperty('--border', '#444444');
|
||||||
|
document.documentElement.style.setProperty('--surface', '#2c2c2c');
|
||||||
|
document.documentElement.style.setProperty('--shadow', 'rgba(0, 0, 0, 0.2)');
|
||||||
|
document.documentElement.style.setProperty('--shadow-hover', 'rgba(0, 0, 0, 0.3)');
|
||||||
|
|
||||||
|
// Restore original UI backgrounds
|
||||||
|
this.ui.statusBar.style.background = 'rgba(30, 30, 30, 0.9)';
|
||||||
|
this.ui.statusBar.style.color = '#e0e0e0';
|
||||||
|
|
||||||
|
const controlPanel = document.getElementById('control-panel');
|
||||||
|
if (controlPanel) {
|
||||||
|
controlPanel.style.background = 'rgba(44, 44, 44, 0.95)';
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsPanel = document.getElementById('settings-panel');
|
||||||
|
if (settingsPanel) {
|
||||||
|
settingsPanel.style.background = 'rgba(44, 44, 44, 0.98)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show status message
|
||||||
|
this.ui.statusBar.textContent = isWhiteBackground ? "Switched to white background" : "Switched to dark background";
|
||||||
|
this.ui.statusBar.classList.remove('hidden');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.ui.statusBar.classList.add('hidden');
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
resetSettings() {
|
resetSettings() {
|
||||||
if (!this.defaultSettings) return;
|
if (!this.defaultSettings) return;
|
||||||
|
|
||||||
this.applyDefaultSettings();
|
this.applyDefaultSettings();
|
||||||
|
|
||||||
|
// Reset background to dark theme
|
||||||
|
if (this.ui.whiteBackground) {
|
||||||
|
this.ui.whiteBackground.checked = false;
|
||||||
|
this.toggleBackground();
|
||||||
|
}
|
||||||
|
|
||||||
this.updatePointCloudSettings();
|
this.updatePointCloudSettings();
|
||||||
this.updateTrajectorySettings();
|
this.updateTrajectorySettings();
|
||||||
this.updateFrustumDimensions();
|
this.updateFrustumDimensions();
|
||||||
|
|
||||||
|
// Clear history when resetting settings
|
||||||
|
this.clearHistory();
|
||||||
|
|
||||||
this.ui.statusBar.textContent = "Settings reset to defaults";
|
this.ui.statusBar.textContent = "Settings reset to defaults";
|
||||||
this.ui.statusBar.classList.remove('hidden');
|
this.ui.statusBar.classList.remove('hidden');
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user