This commit is contained in:
2026-05-29 09:20:28 +02:00
parent b4ea4799a9
commit 5e39a41f49
3 changed files with 111 additions and 50 deletions
+29
View File
@@ -172,6 +172,14 @@
<button class="act-btn subtle full" onclick="setImuNorth()">Aktuelle Richtung als Norden setzen</button>
<button class="act-btn subtle full" onclick="setImuLevel()">Aktuelle Neigung als Null setzen</button>
</div>
<div class="sr">
<span class="sr-led led-off"></span>
<span class="sr-key">HUD-Farbe</span>
<span class="sr-val" style="display:flex;align-items:center;gap:6px;">
<input type="color" id="hud_color_picker" value="#00ff91" style="width:32px;height:22px;border:none;background:none;cursor:pointer;padding:0;" oninput="applyHudColorFromAdmin(this.value)" onchange="applyHudColorFromAdmin(this.value)">
<button class="act-btn subtle" style="padding:2px 8px;font-size:11px;" onclick="resetHudColor()">Reset</button>
</span>
</div>
<div class="sr">
<span class="sr-led led-off"></span>
<span class="sr-key">Hinweis</span>
@@ -2820,6 +2828,27 @@
}
}
(function() {
try {
var h = localStorage.getItem('hudHexColor');
if (h) {
var el = document.getElementById('hud_color_picker');
if (el) el.value = '#' + h;
}
} catch(e) {}
})();
function applyHudColorFromAdmin(cssColor) {
var hex = cssColor.replace('#', '');
try { localStorage.setItem('hudHexColor', hex); } catch(e) {}
}
function resetHudColor() {
var el = document.getElementById('hud_color_picker');
if (el) el.value = '#00ff91';
applyHudColorFromAdmin('#00ff91');
}
async function setImuNorth() {
if (!window.confirm('Rover jetzt wirklich sauber nach Norden ausgerichtet?')) return;
setAdminStatus('Speichere Nordrichtung...');
+3 -2
View File
@@ -281,10 +281,11 @@ header {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(270px * var(--cam-scale));
height: calc(270px * var(--cam-scale));
width: calc(405px * var(--cam-scale));
height: calc(405px * var(--cam-scale));
z-index: 10;
pointer-events: none;
filter: drop-shadow(0 0 1.5px rgba(0,0,0,.9)) drop-shadow(0 0 3px rgba(0,0,0,.6));
}
@keyframes xh-spin { to { transform: rotate(360deg); } }
@keyframes xh-pulse {
+79 -48
View File
@@ -94,7 +94,7 @@
S: <span id="overlay-distance">0,00</span> m
</div>
<div class="c-xhair">
<svg viewBox="-110 -110 220 220" xmlns="http://www.w3.org/2000/svg" width="220" height="220">
<svg viewBox="-110 -110 220 220" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<!-- outer rotating dashed ring -->
<circle class="xh-rot" cx="0" cy="0" r="103" fill="none" stroke="rgba(0,255,145,.14)" stroke-width="1" stroke-dasharray="4 13"/>
<!-- roll arc (static) r=96, ±60° from top -->
@@ -158,8 +158,8 @@
<line x1="-42" y1="-72" x2="42" y2="-72" stroke="rgba(0,255,145,.35)" stroke-width=".5"/>
<text id="imu-heading-text" x="0" y="-62.5" fill="rgba(0,255,145,.92)" font-size="9" text-anchor="middle" letter-spacing="1">HDG 000° N</text>
<!-- roll / pitch readouts -->
<text id="imu-roll-text" x="-73" y="76" fill="rgba(0,255,145,.82)" font-size="7.5" text-anchor="middle">R +0.0°</text>
<text id="imu-pitch-text" x="73" y="76" fill="rgba(0,255,145,.82)" font-size="7.5" text-anchor="middle">P +0.0°</text>
<text id="imu-roll-text" x="-73" y="76" fill="rgba(0,255,145,.82)" font-size="10.5" text-anchor="middle">R +0.0°</text>
<text id="imu-pitch-text" x="73" y="76" fill="rgba(0,255,145,.82)" font-size="10.5" text-anchor="middle">P +0.0°</text>
</g>
<!-- center dot -->
<circle cx="0" cy="0" r="3" fill="rgba(0,255,145,.92)"/>
@@ -167,7 +167,6 @@
</svg>
</div>
<div class="c-scanlines"></div>
<div class="c-vignette"></div>
</div>
</div>
</div>
@@ -329,6 +328,26 @@ var imuOverlayState = {
lastUpdateAt: 0
};
var hudRGB = [0, 255, 145];
var xhairSvgOrigHTML = null;
function hudRgba(alpha) {
return 'rgba(' + hudRGB[0] + ',' + hudRGB[1] + ',' + hudRGB[2] + ',' + alpha + ')';
}
function applyHudColor(hex) {
var r = parseInt(hex.slice(0, 2), 16);
var g = parseInt(hex.slice(2, 4), 16);
var b = parseInt(hex.slice(4, 6), 16);
if (isNaN(r) || isNaN(g) || isNaN(b)) return;
hudRGB = [r, g, b];
try { localStorage.setItem('hudHexColor', hex); } catch (e) {}
var svgEl = document.querySelector('.c-xhair svg');
if (!svgEl) return;
if (!xhairSvgOrigHTML) xhairSvgOrigHTML = svgEl.innerHTML;
svgEl.innerHTML = xhairSvgOrigHTML.replace(/rgba\(0,255,145,/g, 'rgba(' + r + ',' + g + ',' + b + ',');
}
function setText(id, text) {
var el = document.getElementById(id);
if (el) {
@@ -357,7 +376,7 @@ function formatSignedNumber(value, digits) {
}
function headingToCompass(deg) {
var dirs = ["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"];
var dirs = ["N","NNO","NO","ONO","O","OSO","SO","SSO","S","SSW","SW","WSW","W","WNW","NW","NNW"];
return dirs[Math.round(((deg % 360) + 360) % 360 / 22.5) % 16];
}
@@ -1141,12 +1160,14 @@ function drawImuHorizonOverlay(context, canvasWidth, canvasHeight, size) {
context.save();
context.translate(cx, cy);
context.rotate(-rollRad);
context.shadowColor = "rgba(0,0,0,.85)";
context.shadowBlur = 3;
context.beginPath();
context.moveTo(0, -90 * unit);
context.lineTo(-5.5 * unit, -79 * unit);
context.lineTo(5.5 * unit, -79 * unit);
context.closePath();
context.fillStyle = "rgba(0,255,145,.88)";
context.fillStyle = hudRgba(.88);
context.fill();
context.restore();
@@ -1155,6 +1176,8 @@ function drawImuHorizonOverlay(context, canvasWidth, canvasHeight, size) {
context.translate(cx, cy);
context.rotate(-rollRad);
context.translate(0, pitchOffsetPx);
context.shadowColor = "rgba(0,0,0,.85)";
context.shadowBlur = 2.5;
[
{ y: -33, span: 11, op: .28 }, { y: 33, span: 11, op: .28 },
@@ -1164,12 +1187,12 @@ function drawImuHorizonOverlay(context, canvasWidth, canvasHeight, size) {
context.beginPath();
context.moveTo(-pl.span * unit, pl.y * unit);
context.lineTo(pl.span * unit, pl.y * unit);
context.strokeStyle = "rgba(0,255,145," + pl.op + ")";
context.strokeStyle = hudRgba(pl.op);
context.lineWidth = 0.9 * unit;
context.stroke();
});
context.strokeStyle = "rgba(0,255,145,.95)";
context.strokeStyle = hudRgba(.95);
context.lineWidth = 2.8 * unit;
context.beginPath(); context.moveTo(-60 * unit, 0); context.lineTo(60 * unit, 0); context.stroke();
context.beginPath(); context.moveTo(-60 * unit, 0); context.lineTo(-52 * unit, -5 * unit); context.stroke();
@@ -1181,13 +1204,15 @@ function drawImuHorizonOverlay(context, canvasWidth, canvasHeight, size) {
var bw = 84 * unit, bh = 15 * unit;
context.fillStyle = "rgba(0,15,8,.65)";
context.fillRect(cx - bw / 2, cy - 72 * unit, bw, bh);
context.shadowColor = "rgba(0,0,0,.9)";
context.shadowBlur = 3;
context.font = (9 * unit) + 'px "Consolas","Cascadia Mono",monospace';
context.fillStyle = "rgba(0,255,145,.92)";
context.fillStyle = hudRgba(.92);
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("HDG " + String(Math.round(headingDeg)).padStart(3, "0") + "° " + headingToCompass(headingDeg), cx, cy - 62.5 * unit);
context.font = (7.5 * unit) + 'px "Consolas","Cascadia Mono",monospace';
context.fillStyle = "rgba(0,255,145,.72)";
context.font = (10.5 * unit) + 'px "Consolas","Cascadia Mono",monospace';
context.fillStyle = hudRgba(.82);
context.fillText("R " + formatSignedNumber(rollDeg, 1) + "°", cx - 73 * unit, cy + 76 * unit);
context.fillText("P " + formatSignedNumber(pitchDeg, 1) + "°", cx + 73 * unit, cy + 76 * unit);
context.restore();
@@ -1198,67 +1223,76 @@ function drawCrosshairOverlayToCanvas(context, canvasWidth, canvasHeight, size)
var cy = canvasHeight / 2;
var unit = size / 180;
function circle(radius, stroke, lineWidth, dash) {
function circle(radius, alpha, lineWidth, dash) {
context.save();
context.beginPath();
context.arc(cx, cy, radius * unit, 0, Math.PI * 2);
if (dash) context.setLineDash(dash.map(function (value) { return value * unit; }));
context.strokeStyle = stroke;
context.shadowColor = "rgba(0,0,0,.85)";
context.shadowBlur = 2.5;
context.strokeStyle = hudRgba(alpha);
context.lineWidth = lineWidth * unit;
context.stroke();
context.restore();
}
function line(x1, y1, x2, y2, stroke, lineWidth) {
function line(x1, y1, x2, y2, alpha, lineWidth) {
context.save();
context.beginPath();
context.moveTo(cx + (x1 * unit), cy + (y1 * unit));
context.lineTo(cx + (x2 * unit), cy + (y2 * unit));
context.strokeStyle = stroke;
context.shadowColor = "rgba(0,0,0,.85)";
context.shadowBlur = 2.5;
context.strokeStyle = hudRgba(alpha);
context.lineWidth = lineWidth * unit;
context.stroke();
context.restore();
}
function path(points, stroke, lineWidth) {
function path(points, alpha, lineWidth) {
context.save();
context.beginPath();
context.moveTo(cx + (points[0][0] * unit), cy + (points[0][1] * unit));
for (var i = 1; i < points.length; i += 1) {
context.lineTo(cx + (points[i][0] * unit), cy + (points[i][1] * unit));
}
context.strokeStyle = stroke;
context.shadowColor = "rgba(0,0,0,.85)";
context.shadowBlur = 2.5;
context.strokeStyle = hudRgba(alpha);
context.lineWidth = lineWidth * unit;
context.stroke();
context.restore();
}
circle(82, "rgba(0,255,145,.2)", 1, [4, 10]);
circle(60, "rgba(0,255,145,.15)", 0.75, [2, 16]);
circle(16, "rgba(0,255,145,.3)", 0.75);
line(-86, 0, -19, 0, "rgba(0,255,145,.6)", 1);
line(19, 0, 86, 0, "rgba(0,255,145,.6)", 1);
line(0, -86, 0, -19, "rgba(0,255,145,.6)", 1);
line(0, 19, 0, 86, "rgba(0,255,145,.6)", 1);
line(-60, -6, -60, 6, "rgba(0,255,145,.45)", 0.75);
line(60, -6, 60, 6, "rgba(0,255,145,.45)", 0.75);
line(-6, -60, 6, -60, "rgba(0,255,145,.45)", 0.75);
line(-6, 60, 6, 60, "rgba(0,255,145,.45)", 0.75);
line(-38, -3.5, -38, 3.5, "rgba(0,255,145,.3)", 0.5);
line(38, -3.5, 38, 3.5, "rgba(0,255,145,.3)", 0.5);
line(-3.5, -38, 3.5, -38, "rgba(0,255,145,.3)", 0.5);
line(-3.5, 38, 3.5, 38, "rgba(0,255,145,.3)", 0.5);
path([[-74, -58], [-74, -74], [-58, -74]], "rgba(0,255,145,.75)", 1.5);
path([[58, -74], [74, -74], [74, -58]], "rgba(0,255,145,.75)", 1.5);
path([[-74, 58], [-74, 74], [-58, 74]], "rgba(0,255,145,.75)", 1.5);
path([[58, 74], [74, 74], [74, 58]], "rgba(0,255,145,.75)", 1.5);
circle(6, "rgba(0,255,145,.7)", 1);
circle(82, .2, 1, [4, 10]);
circle(60, .15, 0.75, [2, 16]);
circle(16, .3, 0.75);
line(-86, 0, -19, 0, .6, 1);
line(19, 0, 86, 0, .6, 1);
line(0, -86, 0, -19, .6, 1);
line(0, 19, 0, 86, .6, 1);
line(-60, -6, -60, 6, .45, 0.75);
line(60, -6, 60, 6, .45, 0.75);
line(-6, -60, 6, -60, .45, 0.75);
line(-6, 60, 6, 60, .45, 0.75);
line(-38, -3.5, -38, 3.5, .3, 0.5);
line(38, -3.5, 38, 3.5, .3, 0.5);
line(-3.5, -38, 3.5, -38, .3, 0.5);
line(-3.5, 38, 3.5, 38, .3, 0.5);
path([[-74, -58], [-74, -74], [-58, -74]], .75, 1.5);
path([[58, -74], [74, -74], [74, -58]], .75, 1.5);
path([[-74, 58], [-74, 74], [-58, 74]], .75, 1.5);
path([[58, 74], [74, 74], [74, 58]], .75, 1.5);
circle(6, .7, 1);
context.save();
context.shadowColor = "rgba(0,0,0,.85)";
context.shadowBlur = 3;
context.beginPath();
context.arc(cx, cy, 2.5 * unit, 0, Math.PI * 2);
context.fillStyle = "rgba(0,255,145,.9)";
context.fillStyle = hudRgba(.9);
context.fill();
context.shadowBlur = 0;
context.beginPath();
context.arc(cx, cy, 1 * unit, 0, Math.PI * 2);
context.fillStyle = "rgba(255,255,255,.7)";
@@ -1271,17 +1305,9 @@ function drawCrosshairOverlayToCanvas(context, canvasWidth, canvasHeight, size)
function drawVignetteAndScanlines(context, canvasWidth, canvasHeight) {
context.save();
for (var y = 0; y < canvasHeight; y += 4) {
context.fillStyle = "rgba(0,255,145,.012)";
context.fillStyle = hudRgba(.012);
context.fillRect(0, y, canvasWidth, 1);
}
var vignette = context.createRadialGradient(
canvasWidth * 0.5, canvasHeight * 0.5, canvasWidth * 0.25,
canvasWidth * 0.5, canvasHeight * 0.5, canvasWidth * 0.75
);
vignette.addColorStop(0, "rgba(0,0,0,0)");
vignette.addColorStop(1, "rgba(0,0,0,.58)");
context.fillStyle = vignette;
context.fillRect(0, 0, canvasWidth, canvasHeight);
context.restore();
}
@@ -1339,7 +1365,7 @@ function drawHudOverlayToCanvas(context, canvasWidth, canvasHeight) {
context.fillText(document.getElementById("cam-live-text").textContent, canvasWidth / 2 + (6 * camScale * scaleToCanvas), padding);
context.restore();
drawCrosshairOverlayToCanvas(context, canvasWidth, canvasHeight, 270 * camScale * scaleToCanvas);
drawCrosshairOverlayToCanvas(context, canvasWidth, canvasHeight, 405 * camScale * scaleToCanvas);
drawVignetteAndScanlines(context, canvasWidth, canvasHeight);
}
@@ -1544,7 +1570,12 @@ function toggleStatPanel() {
setStatPanelCollapsed(!panel.classList.contains("collapsed"));
}
window.addEventListener("storage", function (e) {
if (e.key === "hudHexColor" && e.newValue) applyHudColor(e.newValue);
});
window.addEventListener("load", function () {
try { var h = localStorage.getItem("hudHexColor"); if (h) applyHudColor(h); } catch (e) {}
setText("cam-host", hostUrl);
updateModeDisplay();
updateMotorDisplay();