diff --git a/gui/app.css b/gui/app.css index dee960a..f62cad4 100644 --- a/gui/app.css +++ b/gui/app.css @@ -281,8 +281,8 @@ header { top: 50%; left: 50%; transform: translate(-50%, -50%); - width: calc(180px * var(--cam-scale)); - height: calc(180px * var(--cam-scale)); + width: calc(220px * var(--cam-scale)); + height: calc(220px * var(--cam-scale)); z-index: 10; pointer-events: none; } diff --git a/gui/index.html b/gui/index.html index 263d811..7552c7c 100644 --- a/gui/index.html +++ b/gui/index.html @@ -94,50 +94,76 @@ S: 0,00 m
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + - HDG 0° - R +0.0° - P +0.0° + + + + + + + + + + + + + + + + HDG 000° + + R +0.0° + P +0.0° - - - + + +
@@ -336,6 +362,7 @@ function normalizeHeadingDeg(value) { function updateImuOverlaySvg() { var group = document.getElementById("imu-horizon-group"); var frame = document.getElementById("imu-horizon-frame"); + var rollPointer = document.getElementById("imu-roll-pointer"); var headingText = document.getElementById("imu-heading-text"); var rollText = document.getElementById("imu-roll-text"); var pitchText = document.getElementById("imu-pitch-text"); @@ -350,11 +377,14 @@ function updateImuOverlaySvg() { var rollDeg = Number(imuOverlayState.rollDeg || 0); var pitchDeg = Number(imuOverlayState.pitchDeg || 0); var headingDeg = normalizeHeadingDeg(imuOverlayState.yawDeg); - var pitchOffset = Math.max(-26, Math.min(26, pitchDeg * 1.1)); + var pitchOffset = Math.max(-55, Math.min(55, pitchDeg * 2.2)); group.setAttribute("opacity", "1"); frame.setAttribute("transform", "rotate(" + (-rollDeg).toFixed(2) + ") translate(0 " + pitchOffset.toFixed(2) + ")"); - headingText.textContent = "HDG " + Math.round(headingDeg) + "°"; + if (rollPointer) { + rollPointer.setAttribute("transform", "rotate(" + (-rollDeg).toFixed(2) + ")"); + } + headingText.textContent = "HDG " + String(Math.round(headingDeg)).padStart(3, "0") + "°"; rollText.textContent = "R " + formatSignedNumber(rollDeg, 1) + "°"; pitchText.textContent = "P " + formatSignedNumber(pitchDeg, 1) + "°"; } @@ -1076,44 +1106,66 @@ function drawImuHorizonOverlay(context, canvasWidth, canvasHeight, size) { var cx = canvasWidth / 2; var cy = canvasHeight / 2; - var unit = size / 180; + var unit = size / 220; var rollRad = Number(imuOverlayState.rollDeg || 0) * Math.PI / 180; - var pitchOffsetPx = Math.max(-26, Math.min(26, Number(imuOverlayState.pitchDeg || 0) * 0.9 * unit)); + var pitchOffsetPx = Math.max(-55, Math.min(55, Number(imuOverlayState.pitchDeg || 0) * 2.2)) * unit; var headingDeg = normalizeHeadingDeg(imuOverlayState.yawDeg); + var rollDeg = Number(imuOverlayState.rollDeg || 0); + var pitchDeg = Number(imuOverlayState.pitchDeg || 0); + /* roll pointer triangle (only roll, no pitch) */ + context.save(); + context.translate(cx, cy); + context.rotate(-rollRad); + 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.fill(); + context.restore(); + + /* horizon + pitch ladder */ context.save(); context.translate(cx, cy); context.rotate(-rollRad); context.translate(0, pitchOffsetPx); - context.strokeStyle = "rgba(0,255,145,.82)"; - context.lineWidth = 1.25 * unit; - context.beginPath(); - context.moveTo(-28 * unit, 0); - context.lineTo(28 * unit, 0); - context.stroke(); - - context.strokeStyle = "rgba(0,255,145,.36)"; - context.lineWidth = 0.9 * unit; - [-18, -9, 9, 18].forEach(function(stepDeg) { - var y = stepDeg * 1.1 * unit; - var span = Math.abs(stepDeg) === 18 ? 13 : 9; + [ + { y: -33, span: 11, op: .28 }, { y: 33, span: 11, op: .28 }, + { y: -22, span: 17, op: .44 }, { y: 22, span: 17, op: .44 }, + { y: -11, span: 10, op: .3 }, { y: 11, span: 10, op: .3 } + ].forEach(function(pl) { context.beginPath(); - context.moveTo(-span * unit, y); - context.lineTo(span * unit, y); + 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.lineWidth = 0.9 * unit; context.stroke(); }); + context.strokeStyle = "rgba(0,255,145,.92)"; + context.lineWidth = 1.5 * unit; + context.beginPath(); context.moveTo(-40 * unit, 0); context.lineTo(40 * unit, 0); context.stroke(); + context.beginPath(); context.moveTo(-40 * unit, 0); context.lineTo(-32 * unit, -5 * unit); context.stroke(); + context.beginPath(); context.moveTo(40 * unit, 0); context.lineTo(32 * unit, -5 * unit); context.stroke(); context.restore(); + /* HDG box + text labels */ context.save(); - context.font = (8.5 * unit) + 'px "IBM Plex Mono", "Roboto Mono", monospace'; - context.fillStyle = "rgba(0,255,145,.78)"; + var bw = 54 * unit, bh = 13 * unit; + context.fillStyle = "rgba(0,15,8,.58)"; + context.fillRect(cx - bw / 2, cy - 70 * unit, bw, bh); + context.font = (9 * unit) + 'px "Consolas","Cascadia Mono",monospace'; + context.fillStyle = "rgba(0,255,145,.9)"; context.textAlign = "center"; context.textBaseline = "middle"; - context.fillText("HDG " + Math.round(headingDeg) + "°", cx, cy - (48 * unit)); - context.fillText("R " + formatSignedNumber(imuOverlayState.rollDeg, 1) + "°", cx - (56 * unit), cy + (52 * unit)); - context.fillText("P " + formatSignedNumber(imuOverlayState.pitchDeg, 1) + "°", cx + (56 * unit), cy + (52 * unit)); + context.fillText("HDG " + String(Math.round(headingDeg)).padStart(3, "0") + "°", cx, cy - 63.5 * unit); + context.font = (7.5 * unit) + 'px "Consolas","Cascadia Mono",monospace'; + context.fillStyle = "rgba(0,255,145,.72)"; + 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(); } @@ -1263,7 +1315,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, 180 * camScale * scaleToCanvas); + drawCrosshairOverlayToCanvas(context, canvasWidth, canvasHeight, 220 * camScale * scaleToCanvas); drawVignetteAndScanlines(context, canvasWidth, canvasHeight); }