// @wuys/map · math/projections/equal-earth.js
//
// Equal Earth projection (Šavrič, Patterson, Jenny — 2018).
// Equal-area pseudocylindrical. The world fits a 2000×1000 viewBox.
//
// Natural ranges in math space: x ∈ ±2.7063831,  y ∈ ±1.3173627.
// We scale uniformly into the 2000×1000 viewBox, centered (small letterbox at top/bottom).

const A1 = 1.340264;
const A2 = -0.081106;
const A3 = 0.000893;
const A4 = 0.003796;
const M  = Math.sqrt(3) / 2;

const X_HALF = 2.7063831;
const Y_HALF = 1.3173627;

const VB_W = 2000;
const VB_H = 1000;
const SCALE = Math.min((VB_W / 2) / X_HALF, (VB_H / 2) / Y_HALF);
const CX = VB_W / 2;
const CY = VB_H / 2;

/** Forward: (lon°, lat°) → (x, y) in 2000×1000 viewBox. */
export function project(lon, lat) {
  const lonR = lon * Math.PI / 180;
  const latR = lat * Math.PI / 180;
  const th = Math.asin(M * Math.sin(latR));
  const t2 = th * th;
  const t6 = t2 * t2 * t2;
  const Mp = A1 + 3 * A2 * t2 + t6 * (7 * A3 + 9 * A4 * t2);
  const Mt = A1 + A2 * t2 + t6 * (A3 + A4 * t2);
  const xn = 2 * Math.sqrt(3) * lonR * Math.cos(th) / (3 * Mp);
  const yn = Mt * th;
  return [CX + xn * SCALE, CY - yn * SCALE];
}

/** Inverse: (x, y) in viewBox → (lon°, lat°). Newton iteration on y. */
export function unproject(vbx, vby) {
  const xn = (vbx - CX) / SCALE;
  const yn = (CY - vby) / SCALE;
  let theta = yn;
  for (let i = 0; i < 20; i++) {
    const t2 = theta * theta;
    const t6 = t2 * t2 * t2;
    const Mt = A1 + A2 * t2 + t6 * (A3 + A4 * t2);
    const Mp = A1 + 3 * A2 * t2 + t6 * (7 * A3 + 9 * A4 * t2);
    const f = Mt * theta - yn;
    const d = f / Mp;
    theta -= d;
    if (Math.abs(d) < 1e-9) break;
  }
  const latR = Math.asin(Math.max(-1, Math.min(1, Math.sin(theta) / M)));
  const t2 = theta * theta;
  const t6 = t2 * t2 * t2;
  const Mp = A1 + 3 * A2 * t2 + t6 * (7 * A3 + 9 * A4 * t2);
  const cosTh = Math.cos(theta);
  const lonR = cosTh < 1e-9 ? 0 : (3 * Mp * xn) / (2 * Math.sqrt(3) * cosTh);
  return { lon: lonR * 180 / Math.PI, lat: latR * 180 / Math.PI };
}

export const meta = {
  id: "equalEarth",
  name: "Equal Earth",
  type: "pseudocylindrical",
  equalArea: true,
  viewBox: { w: VB_W, h: VB_H, cx: CX, cy: CY },
  scale: SCALE,
  naturalRange: { xHalf: X_HALF, yHalf: Y_HALF },
};
