fix: rasterized court coverage with 3 distinct colors
- Blue = cam0 only, Pink = cam1 only, Purple = overlap - Grid-based fill, no dashed lines or edge artifacts - White court lines on top Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -661,98 +661,54 @@ function addStereocameras(scene) {
|
|||||||
var deg2rad = Math.PI / 180;
|
var deg2rad = Math.PI / 180;
|
||||||
var halfFov = hfov / 2;
|
var halfFov = hfov / 2;
|
||||||
|
|
||||||
function drawCourtCoverage(cx, angleDeg, color) {
|
// Rasterize coverage onto a grid, then draw colored quads
|
||||||
// Center direction: +Y (90°) rotated by angleDeg
|
var gridRes = 0.2; // 20cm per cell
|
||||||
var centerAngle = 90 + angleDeg; // +angle = toward +X, -angle = toward -X
|
var cols = Math.ceil((courtMaxX - courtMinX) / gridRes);
|
||||||
var leftAngle = centerAngle + halfFov;
|
var rows = Math.ceil((courtMaxY - courtMinY) / gridRes);
|
||||||
var rightAngle = centerAngle - halfFov;
|
// 0 = no coverage, 1 = cam0 only, 2 = cam1 only, 3 = both
|
||||||
|
var grid = new Array(cols * rows).fill(0);
|
||||||
|
|
||||||
// Cast ray from camera ground pos, clip to court rect
|
function markCoverage(cx, angleDeg, bit) {
|
||||||
var ox = cx, oy = baseY;
|
var centerAngle = (90 + angleDeg) * deg2rad;
|
||||||
|
var halfFovRad = halfFov * deg2rad;
|
||||||
|
var leftAngle = centerAngle + halfFovRad;
|
||||||
|
var rightAngle = centerAngle - halfFovRad;
|
||||||
|
|
||||||
function rayCourtIntersect(angleDeg2) {
|
for (var r = 0; r < rows; r++) {
|
||||||
var rad = angleDeg2 * deg2rad;
|
for (var c = 0; c < cols; c++) {
|
||||||
var dx = Math.cos(rad);
|
var cellX = courtMinX + (c + 0.5) * gridRes;
|
||||||
var dy = Math.sin(rad);
|
var cellY = courtMinY + (r + 0.5) * gridRes;
|
||||||
if (Math.abs(dx) < 1e-9 && Math.abs(dy) < 1e-9) return null;
|
var dx = cellX - cx;
|
||||||
|
var dy = cellY - baseY;
|
||||||
var tMin = 0.01, tMax = 50;
|
var angle = Math.atan2(dy, dx);
|
||||||
// Clip to court Y bounds
|
if (angle >= rightAngle && angle <= leftAngle) {
|
||||||
if (dy > 1e-9) {
|
grid[r * cols + c] |= bit;
|
||||||
tMax = Math.min(tMax, (courtMaxY - oy) / dy);
|
}
|
||||||
} else if (dy < -1e-9) {
|
|
||||||
tMax = Math.min(tMax, (courtMinY - oy) / dy);
|
|
||||||
}
|
}
|
||||||
// Clip to court X bounds
|
|
||||||
if (dx > 1e-9) {
|
|
||||||
var tx = (courtMaxX - ox) / dx;
|
|
||||||
tMax = Math.min(tMax, tx);
|
|
||||||
} else if (dx < -1e-9) {
|
|
||||||
var tx = (courtMinX - ox) / dx;
|
|
||||||
tMax = Math.min(tMax, tx);
|
|
||||||
}
|
|
||||||
// Must enter court (Y >= 0)
|
|
||||||
if (dy > 1e-9) {
|
|
||||||
tMin = Math.max(tMin, (courtMinY - oy) / dy);
|
|
||||||
} else if (dy <= 0) {
|
|
||||||
return null; // ray goes away from court
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tMax <= tMin) return null;
|
|
||||||
return new THREE.Vector3(ox + dx * tMax, oy + dy * tMax, 0.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build coverage polygon on court surface
|
|
||||||
var points = [];
|
|
||||||
var origin = new THREE.Vector3(ox, Math.max(oy, courtMinY), 0.02);
|
|
||||||
var steps = 48;
|
|
||||||
for (var i = 0; i <= steps; i++) {
|
|
||||||
var a = rightAngle + (leftAngle - rightAngle) * (i / steps);
|
|
||||||
var pt = rayCourtIntersect(a);
|
|
||||||
if (pt) {
|
|
||||||
// Clamp to court bounds
|
|
||||||
pt.x = Math.max(courtMinX, Math.min(courtMaxX, pt.x));
|
|
||||||
pt.y = Math.max(courtMinY, Math.min(courtMaxY, pt.y));
|
|
||||||
points.push(pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (points.length < 2) return;
|
|
||||||
|
|
||||||
// Draw coverage edge line on court
|
|
||||||
var edgeGeo = new THREE.BufferGeometry().setFromPoints(points);
|
|
||||||
scene.add(new THREE.Line(edgeGeo, new THREE.LineBasicMaterial({ color: color, transparent: true, opacity: 0.7 })));
|
|
||||||
|
|
||||||
// Fill coverage area as triangle fan from camera ground position
|
|
||||||
var fanOrigin = new THREE.Vector3(ox, courtMinY, 0.02);
|
|
||||||
for (var i = 0; i < points.length - 1; i++) {
|
|
||||||
var triGeo = new THREE.BufferGeometry().setFromPoints([
|
|
||||||
fanOrigin, points[i], points[i + 1]
|
|
||||||
]);
|
|
||||||
scene.add(new THREE.Mesh(triGeo, new THREE.MeshBasicMaterial({
|
|
||||||
color: color, transparent: true, opacity: 0.4, side: THREE.DoubleSide
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Center line on court
|
|
||||||
var centerPt = rayCourtIntersect(centerAngle);
|
|
||||||
if (centerPt) {
|
|
||||||
centerPt.x = Math.max(courtMinX, Math.min(courtMaxX, centerPt.x));
|
|
||||||
centerPt.y = Math.max(courtMinY, Math.min(courtMaxY, centerPt.y));
|
|
||||||
var clGeo = new THREE.BufferGeometry().setFromPoints([
|
|
||||||
new THREE.Vector3(ox, courtMinY, 0.02), centerPt
|
|
||||||
]);
|
|
||||||
scene.add(new THREE.Line(clGeo, new THREE.LineDashedMaterial({
|
|
||||||
color: color, dashSize: 0.2, gapSize: 0.1
|
|
||||||
})));
|
|
||||||
scene.children[scene.children.length - 1].computeLineDistances();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cam0: rotated -28° (looks slightly toward -X)
|
markCoverage(cam0x, -camAngle, 1);
|
||||||
drawCourtCoverage(cam0x, -camAngle, 0x44aaff);
|
markCoverage(cam1x, camAngle, 2);
|
||||||
// cam1: rotated +28° (looks slightly toward +X)
|
|
||||||
drawCourtCoverage(cam1x, camAngle, 0xff44aa);
|
// Colors: cam0 only = blue, cam1 only = pink, overlap = purple
|
||||||
|
var colors = { 1: 0x4488ff, 2: 0xff44aa, 3: 0xaa44ff };
|
||||||
|
var opacities = { 1: 0.35, 2: 0.35, 3: 0.5 };
|
||||||
|
|
||||||
|
for (var r = 0; r < rows; r++) {
|
||||||
|
for (var c = 0; c < cols; c++) {
|
||||||
|
var val = grid[r * cols + c];
|
||||||
|
if (val === 0) continue;
|
||||||
|
var x1 = courtMinX + c * gridRes;
|
||||||
|
var y1 = courtMinY + r * gridRes;
|
||||||
|
var quad = new THREE.PlaneGeometry(gridRes, gridRes);
|
||||||
|
var mesh = new THREE.Mesh(quad, new THREE.MeshBasicMaterial({
|
||||||
|
color: colors[val], transparent: true, opacity: opacities[val], side: THREE.DoubleSide
|
||||||
|
}));
|
||||||
|
mesh.position.set(x1 + gridRes / 2, y1 + gridRes / 2, 0.015);
|
||||||
|
scene.add(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===================== Draw court lines =====================
|
// ===================== Draw court lines =====================
|
||||||
|
|||||||
Reference in New Issue
Block a user