feat: editable camera params panel on Court tab with live 3D update
Position Y, Height, Stereo gap, Rotation angle, HFOV — all adjustable in real time, 3D scene updates on change. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -213,6 +213,34 @@
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
/* Camera params panel */
|
||||
.cam-params {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
.param-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.param-row label {
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.param-row input {
|
||||
width: 60px;
|
||||
padding: 3px 5px;
|
||||
background: #1a1a2e;
|
||||
border: 1px solid #333;
|
||||
border-radius: 4px;
|
||||
color: #4ecca3;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Event banner */
|
||||
.event-banner {
|
||||
display: none;
|
||||
@@ -286,8 +314,30 @@
|
||||
<div class="bottom-bar">
|
||||
<div class="cam-thumb"><img id="court-cam1" alt="Camera 1"></div>
|
||||
<div class="cam-thumb"><img id="court-cam0" alt="Camera 0"></div>
|
||||
<div class="cam-params">
|
||||
<div class="param-row">
|
||||
<label>Pos Y (м от корта)</label>
|
||||
<input type="number" id="paramPosY" value="1" step="0.1" min="0.1" max="5" onchange="updateCamParams()">
|
||||
</div>
|
||||
<div class="param-row">
|
||||
<label>Pos Z (высота м)</label>
|
||||
<input type="number" id="paramPosZ" value="1" step="0.1" min="0.1" max="5" onchange="updateCamParams()">
|
||||
</div>
|
||||
<div class="param-row">
|
||||
<label>Stereo (см)</label>
|
||||
<input type="number" id="paramStereo" value="6" step="1" min="1" max="30" onchange="updateCamParams()">
|
||||
</div>
|
||||
<div class="param-row">
|
||||
<label>Разворот (°)</label>
|
||||
<input type="number" id="paramAngle" value="15" step="1" min="0" max="45" onchange="updateCamParams()">
|
||||
</div>
|
||||
<div class="param-row">
|
||||
<label>HFOV (°)</label>
|
||||
<input type="number" id="paramHfov" value="128" step="1" min="30" max="180" onchange="updateCamParams()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-panel">
|
||||
<button class="btn-calibrate" id="btnCalibrate" onclick="doCalibrate()">Calibrate Court</button>
|
||||
<button class="btn-calibrate" id="btnCalibrate" onclick="doCalibrate()">Calibrate</button>
|
||||
<div class="calibrate-status" id="calStatus">
|
||||
<span id="calStatusText">Not calibrated</span>
|
||||
</div>
|
||||
@@ -507,7 +557,7 @@ function initCourtScene() {
|
||||
courtScene.add(new THREE.AmbientLight(0xffffff, 0.8));
|
||||
|
||||
// Physical camera marker: 1m from net, center, 1m height
|
||||
addStereocameras(courtScene);
|
||||
addStereocameras(courtScene, getCamParams());
|
||||
|
||||
// Load existing calibration cameras
|
||||
fetch('/api/calibration/data')
|
||||
@@ -608,7 +658,7 @@ function initTrajectoryScene() {
|
||||
trajScene.add(new THREE.AmbientLight(0xffffff, 0.8));
|
||||
|
||||
// Physical camera marker: 1m from net, center, 1m height
|
||||
addStereocameras(trajScene);
|
||||
addStereocameras(trajScene, getCamParams());
|
||||
|
||||
function animateTraj() {
|
||||
requestAnimationFrame(animateTraj);
|
||||
@@ -627,12 +677,26 @@ function initTrajectoryScene() {
|
||||
}
|
||||
|
||||
// ===================== Stereo camera rig with court coverage =====================
|
||||
function addStereocameras(scene) {
|
||||
// Position: net line (X=6.7), 1m outside court edge (Y=-1), 1m height (Z=1)
|
||||
var baseX = 6.7, baseY = -1, baseZ = 1;
|
||||
var stereoGap = 0.06; // 6cm between cameras
|
||||
var camAngle = 15; // degrees each camera is rotated outward from straight +Y
|
||||
var hfov = 128; // horizontal FOV degrees (IMX219 160° diagonal, 4:3 sensor)
|
||||
// Track camera overlay objects for live update
|
||||
var camOverlayObjects = [];
|
||||
|
||||
function addStereocameras(scene, params) {
|
||||
params = params || {};
|
||||
var baseX = 6.7;
|
||||
var baseY = -(params.posY || 1);
|
||||
var baseZ = params.posZ || 1;
|
||||
var stereoGap = (params.stereo || 6) / 100;
|
||||
var camAngle = params.angle || 15;
|
||||
var hfov = params.hfov || 128;
|
||||
|
||||
// Remove old overlay objects
|
||||
camOverlayObjects.forEach(function(obj) { scene.remove(obj); });
|
||||
camOverlayObjects = [];
|
||||
|
||||
function addObj(obj) {
|
||||
scene.add(obj);
|
||||
camOverlayObjects.push(obj);
|
||||
}
|
||||
|
||||
var cam0x = baseX - stereoGap / 2;
|
||||
var cam1x = baseX + stereoGap / 2;
|
||||
@@ -644,7 +708,7 @@ function addStereocameras(scene) {
|
||||
new THREE.MeshBasicMaterial({ color: color })
|
||||
);
|
||||
body.position.set(cx, baseY, baseZ);
|
||||
scene.add(body);
|
||||
addObj(body);
|
||||
}
|
||||
drawCamBody(cam0x, 0x44aaff);
|
||||
drawCamBody(cam1x, 0xff44aa);
|
||||
@@ -654,7 +718,7 @@ function addStereocameras(scene) {
|
||||
new THREE.Vector3(baseX, baseY, 0),
|
||||
new THREE.Vector3(baseX, baseY, baseZ)
|
||||
]);
|
||||
scene.add(new THREE.Line(poleGeo, new THREE.LineBasicMaterial({ color: 0x666666 })));
|
||||
addObj(new THREE.Line(poleGeo, new THREE.LineBasicMaterial({ color: 0x666666 })));
|
||||
|
||||
// Court boundaries for clipping
|
||||
var courtMinX = 0, courtMaxX = 13.4, courtMinY = 0, courtMaxY = 6.1;
|
||||
@@ -713,7 +777,7 @@ function addStereocameras(scene) {
|
||||
var triGeo = new THREE.BufferGeometry().setFromPoints([
|
||||
camOnCourt, farPoints[i], farPoints[i + 1]
|
||||
]);
|
||||
scene.add(new THREE.Mesh(triGeo, mat));
|
||||
addObj(new THREE.Mesh(triGeo, mat));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -886,6 +950,23 @@ setInterval(function() {
|
||||
}
|
||||
}).catch(function() {});
|
||||
}, 1000);
|
||||
|
||||
// ===================== Camera params live update =====================
|
||||
function getCamParams() {
|
||||
return {
|
||||
posY: parseFloat(document.getElementById('paramPosY').value) || 1,
|
||||
posZ: parseFloat(document.getElementById('paramPosZ').value) || 1,
|
||||
stereo: parseFloat(document.getElementById('paramStereo').value) || 6,
|
||||
angle: parseFloat(document.getElementById('paramAngle').value) || 15,
|
||||
hfov: parseFloat(document.getElementById('paramHfov').value) || 128
|
||||
};
|
||||
}
|
||||
|
||||
function updateCamParams() {
|
||||
var p = getCamParams();
|
||||
if (courtSceneInitialized) addStereocameras(courtScene, p);
|
||||
if (trajSceneInitialized) addStereocameras(trajScene, p);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user