diff --git a/gui/admin.html b/gui/admin.html index 9f21097..cb0fc05 100644 --- a/gui/admin.html +++ b/gui/admin.html @@ -172,6 +172,14 @@ +
+ + HUD-Farbe + + + + +
Hinweis @@ -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...'); diff --git a/gui/app.css b/gui/app.css index 1a8b608..898f678 100644 --- a/gui/app.css +++ b/gui/app.css @@ -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 { diff --git a/gui/index.html b/gui/index.html index ecd2628..4c6ee82 100644 --- a/gui/index.html +++ b/gui/index.html @@ -94,7 +94,7 @@ S: 0,00 m
- + @@ -158,8 +158,8 @@ HDG 000° N - R +0.0° - P +0.0° + R +0.0° + P +0.0° @@ -167,7 +167,6 @@
-
@@ -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();