import React, { useState, useEffect, useRef } from 'react';
import { num } from '../../api/useXplane.js';
import { useEased, useEasedAngle } from '../../api/ease.js';
// ============================================================================
// Citation X — Multi-Function Display (Honeywell Primus 2000 arc map).
// Built against the manual (pages 32-33):
// 1 Heading bug · 2 Heading · 3 Compass arc · 4 FMS source · 5 future leg (white)
// 6 active leg (magenta) · 7 range arc · 8 ETE/SAT/TAS/GSPD group · 9 RNG
// 10 V-SPEEDS · 11 EICAS SYS · 12 ET/FT timer · 13 MFD setup (TRAFFIC/TERRAIN/
// APTS/VOR) · 14 PFD setup · 15 RTN · 16 WX status · 17 ownship · 18 airport
// 19 navaid · 20 digital heading bug
// ============================================================================
const RNGS = [10, 20, 40, 80, 160];
const mod360 = (d) => ((d % 360) + 360) % 360;
const toRad = (d) => (d * Math.PI) / 180;
// great-circle distance (NM) + initial bearing (deg) from a→b
function geo(aLat, aLon, bLat, bLon) {
const φ1 = toRad(aLat), φ2 = toRad(bLat), dφ = toRad(bLat - aLat), dλ = toRad(bLon - aLon);
const h = Math.sin(dφ / 2) ** 2 + Math.cos(φ1) * Math.cos(φ2) * Math.sin(dλ / 2) ** 2;
const dist = 3440.065 * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h));
const y = Math.sin(dλ) * Math.cos(φ2);
const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(dλ);
return { dist, brg: mod360((Math.atan2(y, x) * 180) / Math.PI) };
}
export default function CitMFD({ xp }) {
const V = xp.values || {};
const fp = xp.flightPlan || { waypoints: [] };
const [rng, setRng] = useState(40);
const [ov, setOv] = useState({ traffic: true, terrain: false, apts: true, vor: true });
const [setup, setSetup] = useState(null); // null | 'mfd' | 'eicas' | 'pfd'
const [vspd, setVspd] = useState(false);
const [et, setEt] = useState(0);
const etRun = useRef(false);
useEffect(() => { const id = setInterval(() => etRun.current && setEt((t) => t + 1), 1000); return () => clearInterval(id); }, []);
// smooth ownship + compass (same rAF glide as the G1000 map)
const lat = useEased(num(V.lat), 0.14);
const lon = useEased(num(V.lon), 0.14);
const hdg = useEasedAngle(num(V.heading), 0.10);
const trk = num(V.track);
// arc map geometry: ownship near bottom, ~120° forward arc
const W = 760, H = 760, cx = W / 2, cy = 600, R = 470; // compass radius
const pxPerNm = R / rng;
const project = (d, brg) => { // heading-up
const rel = toRad(brg - hdg);
return [cx + Math.sin(rel) * d * pxPerNm, cy - Math.cos(rel) * d * pxPerNm];
};
// build route polyline from waypoints relative to ownship
const wps = (fp.waypoints || []).map((w) => {
if (!isFinite(w.lat) || !isFinite(w.lon)) return null;
const g = geo(lat, lon, w.lat, w.lon);
const [x, y] = project(g.dist, g.brg);
return { ...w, x, y, dist: g.dist };
}).filter(Boolean);
const active = num(fp.activeLeg ?? 1);
// compass arc ticks
const ticks = [];
for (let i = -60; i <= 60; i += 5) {
const a = toRad(i), x1 = cx + Math.sin(a) * R, y1 = cy - Math.cos(a) * R;
const len = i % 30 === 0 ? 20 : i % 10 === 0 ? 14 : 8;
const x2 = cx + Math.sin(a) * (R - len), y2 = cy - Math.cos(a) * (R - len);
ticks.push(