<!-- HAWAIIAN-COMPASS · part 6 -->
d = document.getElementById("bearing-field");

function polar(bearing, radius) {
  const rad = (bearing * Math.PI) / 180;
  return [CX + radius * Math.sin(rad), CY - radius * Math.cos(rad)];
}

function el(name, attrs = {}, parent = svg) {
  const node = document.createElementNS("http://www.w3.org/2000/svg", name);
  for (const [key, value] of Object.entries(attrs)) node.setAttribute(key, value);
  parent.appendChild(node);
  return node;
}

function line(a, r0, r1, attrs) {
  const [x1, y1] = polar(a, r0);
  const [x2, y2] = polar(a, r1);
  el("line", { x1, y1, x2, y2, ...attrs });
}

function ring(radius, attrs) {
  el("circle", { cx: CX, cy: CY, r: radius, fill: "none", ...attrs });
}

function diamond(bearing, inner, outer, halfAngle) {
  const mid = (inner + outer) / 2;
  const pts = [
    polar(bearing, outer),
    polar(bearing + halfAngle, mid),
    polar(bearing, inner),
    polar(bearing - halfAngle, mid),
  ];
  el("polygon", {
    points: pts.map(([x, y]) => `${x.toFixed(3)},${y.toFixed(3)}`).join(" "),
    class: "diamond",
  });
}

function star(bearing, radius, size) {
  const [cx, cy] = polar(bearing, radius);
  const pts = [];
  for (let i = 0; i < 10; i += 1) {
    const ang = (-90 + i * 36) * (Math.PI / 180);
    const r = i % 2 === 0 ? size : size * 0.4;
    pts.push([cx + r * Math.cos(ang), cy + r * Math.sin(ang)]);
  }
  el("polygon", {
    points: pts.map(([x, y]) => `${x.toFixed(3)},${y.toFixed(3)}`).join(" "),
    class: "star",
  });
}

function drawCard() {
  ring(R, { class: "ring rim" });
  ring(R - 28, { class: "ring" });
  ring(R - 46, { class: "ring fine" });
  ring(318, { class: "ring" });
  ring(248, { class: "ring fine" });
  ring(168, { class: "ring" });
  ring(78, { class: "ring fine" });

  for (let deg = 0; deg < 360; deg += 1) {
    const tenth = deg % 10 === 0;
    const fifth = deg % 5 === 0;
    const inner = tenth ? R - 28 : fifth ? R - 18 : R - 12;
    line(deg, inner, R, { class: tenth ? "tick ten" : fifth ? "tick five" : "tick" });
    if (tenth) {
      const [x, y] = polar(deg, R - 42);
      const text = el("text", {
        x,
        y,
        class: "degree",
        "text-anchor": "middle",
        "dominant-baseline": "middle",
      });
      text.textContent = String(deg).padStart(3, "0");
    }
  }

  for (const point of POINTS) {
    const cardinal = point.rank === "cardinal";
    const inter = point.rank === "intercardinal";
    const half = point.rank === "half";
    line(point.bearing, 86, cardinal ? 330 : 300, {
      class: cardinal ? "spoke cardinal" : "spoke",
    });
    const outer = cardinal ? 300 : inter ? 268 : half ? 236 : 208;
    const halfAngle = cardinal ? 6.4 : inter ? 4.6 : half ? 3.2 : 2.15;
    diamond(point.bearing, 150, outer, halfAngle);

    const labelR = cardinal ? 368 : 338;
    const [x, y] = polar(point.bearing, labelR);
    const text = el("text", {
      x,
      y,
      class: cardinal ? "name cardinal" : "name",
      "text-anchor": "middle",
      "dominant-baseline": "middle",
    });
    text.textContent = point.mark;
  }

  star(0, 392, 16);
  ring(18, { class: "hub" });
  el("circle", { cx: CX, cy: CY, r: 3.5, class: "hub-pin" });
}

function drawNeedle(bearing) {
  needle.replaceChildren();
  const [x, y] = polar(bearing, R - 52);
  const [tx, ty] = polar(bearing + 180, 210);
  el("line", { x1: CX, y1: CY, x2: tx, y2: ty, class: "needle tail" }, needle);
  el("line", { x1: CX, y1: CY, x2: x, y2: y, class: "needle head" }, needle);
  const [ax, ay] = polar(bearing, R - 36);
  const [lx, ly] = polar(bearing + 2.2, R - 62);
  const [rx, ry] = polar(bearing - 2.2, R - 62);
  el(
    "polygon",
    { points: `${ax},${ay} ${lx},${ly} ${rx},${ry}`, class: "needle head" },
    needle,
  );
  el("circle", { cx: CX, cy: CY, r: 5, class: "hub-pin" }, needle);
}

function paint(deg) {
  const course = describe(deg);
  drawNeedle(course.bearing);
  readouts.bearing.textContent = `${formatDeg(course.bearing)}°`;
  readouts.spoken.textContent = course.point.spoken;
  readouts.mark.textContent = course.point.mark;
  readouts.pointBearing.textContent = `${formatDeg(course.point.bearing)}°`;
  readouts.offset.textContent = formatSigned(course.offset);
  readouts.quadrant.textContent = course.quadrant;
  readouts.reciprocal.textContent = `${formatDeg(course.reciprocal)}°`;
  readouts.reciprocalName.textContent = course.reciprocalPoint.spoken;
  readouts.northing.textContent = course.northing.toFixed(4);
  readouts.easting.textContent = course.easting.toFixed(4);
  readouts.radians.textContent = course.radians.toFixed(4);
  dial.value = String(course.bearing);
  dial.setAttribute("aria-valuenow", formatDeg(course.bearing));
  if (document.activeElement !== field) field.value = formatDeg(course.bearing);
  return course;
}

drawCard();
svg.appendChild(needle);
let bearing = 0;
paint(bearing);

function setBearing(next) {
  bearing = ((next % 360) + 360) % 360;
  paint(bearing);
}

dial.addE