import React from 'react';
import { num } from '../api/useXplane.js';
// Classic analog "six-pack" VFR panel: airspeed, attitude, altimeter, turn
// coordinator, heading indicator, vertical speed — round steam gauges driven by
// the same X-Plane datarefs. For steam/VFR aircraft (and just because it looks
// great). Each gauge is a self-contained SVG on a dark instrument panel.
const arr0 = (v, d = 0) => (Array.isArray(v) ? num(v[0], d) : num(v, d));
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
// point on a dial: ang in degrees, 0 = up (12 o'clock), clockwise positive.
const pt = (cx, cy, r, ang) => {
const a = (ang - 90) * Math.PI / 180;
return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
};
function Bezel({ title, children }) {
return (
{title}
);
}
const Needle = ({ ang, len = 70, w = 4, color = '#fff', tail = 14 }) => (
);
// generic tick ring
function ticks(min, max, a0, a1, step, big = 1, r = 84, lab) {
const out = [];
let i = 0;
for (let v = min; v <= max + 1e-6; v += step, i++) {
const ang = a0 + ((v - min) / (max - min)) * (a1 - a0);
const isBig = i % big === 0;
const [x1, y1] = pt(100, 100, r, ang);
const [x2, y2] = pt(100, 100, r - (isBig ? 12 : 7), ang);
out.push();
if (lab && isBig) {
const [lx, ly] = pt(100, 100, r - 24, ang);
out.push({lab(v)});
}
}
return out;
}
/* ---------- Airspeed ---------- */
function ASI({ V }) {
const kt = num(V.airspeed);
const A0 = -150, A1 = 150, MIN = 0, MAX = 200;
const ang = A0 + (clamp(kt, MIN, MAX) - MIN) / (MAX - MIN) * (A1 - A0);
const arc = (lo, hi, color, rr, wdt) => {
const [x1, y1] = pt(100, 100, rr, A0 + (lo / MAX) * (A1 - A0));
const [x2, y2] = pt(100, 100, rr, A0 + (hi / MAX) * (A1 - A0));
const large = ((hi - lo) / MAX) * 300 > 180 ? 1 : 0;
return ;
};
return (
{arc(33, 85, '#fff', 70, 4)} {/* white flap arc */}
{arc(48, 129, '#21d04a', 78, 5)} {/* green normal */}
{arc(129, 163, '#e6c200', 78, 5)}{/* yellow caution */}
{(() => { const [x, y] = pt(100, 100, 78, A0 + (163 / MAX) * (A1 - A0)); const [x2, y2] = pt(100, 100, 70, A0 + (163 / MAX) * (A1 - A0)); return ; })()}
{ticks(0, 200, A0, A1, 10, 2, 84, (v) => (v % 20 === 0 && v >= 40 ? v : ''))}
KT
);
}
/* ---------- Attitude ---------- */
function AI({ V }) {
const pitch = num(V.pitch), roll = num(V.roll);
const PPD = 2.0; // px per degree pitch
const off = clamp(pitch, -25, 25) * PPD;
return (
{[-20, -10, 10, 20].map((d) => (
{d % 20 === 0 && {Math.abs(d)}}
))}
{/* bank arc + pointer */}
{[-60, -30, -20, -10, 0, 10, 20, 30, 60].map((b) => { const [x1, y1] = pt(100, 100, 84, b); const [x2, y2] = pt(100, 100, 78, b); return ; })}
{/* fixed aircraft reference */}
);
}
/* ---------- Altimeter (3-pointer) ---------- */
function ALT({ V }) {
const alt = num(V.altitude), baro = num(V.baro, 29.92);
const a100 = (alt % 1000) / 1000 * 360;
const a1000 = (alt % 10000) / 10000 * 360;
const a10000 = (alt % 100000) / 100000 * 360;
return (
{ticks(0, 1000, 0, 360, 100, 1, 84, (v) => (v < 1000 ? v / 100 : ''))}
{ticks(0, 1000, 0, 360, 20, 5, 84)}
{baro.toFixed(2)}
{/* 10000 ft thin pointer */}
{/* 1000 ft short fat */}
{/* 100 ft long */}
);
}
/* ---------- Turn coordinator ---------- */
function TC({ V }) {
const roll = num(V.roll), slip = num(V.slip);
const bank = clamp(roll, -30, 30); // little-plane bank (approx turn rate)
const ballX = 100 + clamp(slip, -8, 8) * 3.0;
return (
{/* standard-rate marks */}
{[-25, 25].map((b) => { const [x1, y1] = pt(100, 100, 80, b); const [x2, y2] = pt(100, 100, 66, b); return ; })}
LR
{/* little airplane */}
2 MIN
{/* inclinometer (slip ball) */}
);
}
/* ---------- Heading indicator ---------- */
function HI({ V }) {
const hdg = ((num(V.heading) % 360) + 360) % 360;
const card = [];
for (let d = 0; d < 360; d += 5) {
const big = d % 30 === 0;
const [x1, y1] = pt(100, 100, 84, d);
const [x2, y2] = pt(100, 100, 84 - (big ? 12 : 7), d);
card.push();
if (big) {
const [lx, ly] = pt(100, 100, 62, d);
const lbl = d === 0 ? 'N' : d === 90 ? 'E' : d === 180 ? 'S' : d === 270 ? 'W' : d / 10;
card.push({lbl});
}
}
return (
{card}
{/* fixed aircraft */}
);
}
/* ---------- Vertical speed ---------- */
function VSI({ V }) {
const vs = clamp(num(V.vspeed), -2000, 2000);
// 0 at 9 o'clock (270°), climb sweeps up (toward 0/up), descent down.
const ang = 270 + (vs / 2000) * 160; // -2000→110°, 0→270°, +2000→430°(=70°)
return (
{ticks(-2000, 2000, 110, 430, 500, 1, 84, (v) => Math.abs(v) / 1000)}
{ticks(-2000, 2000, 110, 430, 100, 5, 84)}
FPM x10000
);
}
/* ---------- small gauges for the engine/fuel cluster ---------- */
const KG_GAL = 2.72;
function SmallBezel({ title, children }) {
return (