66 lines
3.9 KiB
TypeScript
66 lines
3.9 KiB
TypeScript
// Copied from 13: a small redraw of 12's general chart, with "You are here". In 14 it lives in the Paths tab.
|
||
// Coordinates are 12's general chart scaled into a 320×210 card.
|
||
|
||
const P = (x: number, y: number): [number, number] => [10 + ((x - 50) * 300) / 1050, 10 + ((y - 85) * 190) / 660];
|
||
const PORTS: [number, number][] = [
|
||
P(344, 277), P(404, 331), P(432, 407), P(420, 488), P(373, 552),
|
||
P(456, 498), P(543, 576), P(679, 576), P(766, 499), P(960, 400),
|
||
];
|
||
const LOOP = P(410, 592);
|
||
const AT_SEA = P(392, 584);
|
||
|
||
const pt = (p: [number, number]) => `${p[0].toFixed(1)},${p[1].toFixed(1)}`;
|
||
const port = (i: number) => PORTS[i]!;
|
||
|
||
/** The ship's course on the card, as one path (12's red dashed line). */
|
||
const COURSE = `M${pt(port(0))} Q${pt(P(380, 300))} ${pt(port(1))} Q${pt(P(440, 360))} ${pt(port(2))} L${pt(port(3))} L${pt(port(4))}
|
||
Q${pt(P(380, 600))} ${pt(LOOP)} Q${pt(P(440, 570))} ${pt(port(5))} Q${pt(P(480, 570))} ${pt(port(6))} L${pt(port(7))}
|
||
Q${pt(P(730, 560))} ${pt(port(8))} Q${pt(P(860, 530))} ${pt(port(9))}`;
|
||
|
||
/** Where "You are here" sits: k=0 out at sea after Troy, k=1 at the first Cyclops port. */
|
||
export function hereAt(k: number): [number, number] {
|
||
const a = AT_SEA, b = LOOP, c = port(5);
|
||
// two straight legs are close enough at this size
|
||
if (k < 0.4) { const s = k / 0.4; return [a[0] + (b[0] - a[0]) * s, a[1] + (b[1] - a[1]) * s]; }
|
||
const s = (k - 0.4) / 0.6;
|
||
return [b[0] + (c[0] - b[0]) * s, b[1] + (c[1] - b[1]) * s];
|
||
}
|
||
|
||
export function chartSvg(): string {
|
||
const [ix, iy] = P(605, 470);
|
||
const coast = `M0,0 L${pt(P(275, 85))} Q${pt(P(300, 200))} ${pt(P(290, 300))} Q${pt(P(390, 400))} ${pt(P(375, 430))} Q${pt(P(330, 520))} ${pt(P(285, 550))} Q${pt(P(240, 680))} ${pt(P(200, 745))} L0,210 Z`;
|
||
const ports = PORTS.map((p, i) => `<circle class="c-port" cx="${p[0].toFixed(1)}" cy="${p[1].toFixed(1)}" r="2.6"/><text class="c-num" x="${(p[0] + 4).toFixed(1)}" y="${(p[1] - 3).toFixed(1)}">${i + 1}</text>`).join("");
|
||
let rhumbs = "";
|
||
for (let i = 0; i < 16; i++) {
|
||
const a = (i / 16) * Math.PI * 2;
|
||
rhumbs += `M206,48 L${(206 + Math.cos(a) * 260).toFixed(0)},${(48 + Math.sin(a) * 260).toFixed(0)} `;
|
||
}
|
||
let spiral = "";
|
||
for (let r = 3; r < 15; r += 3) spiral += `<ellipse class="c-ink" cx="${port(9)[0].toFixed(1)}" cy="${(port(9)[1] - 4).toFixed(1)}" rx="${r * 1.5}" ry="${r * 0.8}"/>`;
|
||
return `<svg class="chart__svg" viewBox="0 0 320 210" role="img" aria-label="General chart of the voyage from Troy, with the ship's position marked">
|
||
<defs><clipPath id="chart-clip"><rect x="6" y="6" width="308" height="198"/></clipPath></defs>
|
||
<rect class="c-vellum" x="0" y="0" width="320" height="210"/>
|
||
<g clip-path="url(#chart-clip)">
|
||
<path class="c-rhumb" d="${rhumbs}"/>
|
||
<path class="c-land" d="${coast}"/>
|
||
<ellipse class="c-land" cx="${ix.toFixed(1)}" cy="${iy.toFixed(1)}" rx="38" ry="22"/>
|
||
<path class="c-ink" d="M${(ix - 6).toFixed(1)},${(iy + 6).toFixed(1)} a8,8 0 0 1 16,0" />
|
||
<ellipse class="c-land" cx="${P(450, 195)[0]}" cy="${P(450, 195)[1]}" rx="9" ry="6"/>
|
||
<ellipse class="c-land" cx="${P(722, 335)[0]}" cy="${P(722, 335)[1]}" rx="5" ry="3.5"/>
|
||
${spiral}
|
||
<path class="c-course" d="${COURSE}"/>
|
||
${ports}
|
||
<text class="c-place" x="16" y="34">Troy</text>
|
||
<text class="c-place" x="${(ix - 30).toFixed(1)}" y="${(iy - 28).toFixed(1)}">Cyclops' Island</text>
|
||
<text class="c-sea" x="${P(870, 440)[0].toFixed(1)}" y="${P(870, 440)[1].toFixed(1)}">two years at sea</text>
|
||
<g class="c-rose" transform="translate(292,178)"><circle r="11"/><path d="M0,-15 L3,0 L0,15 L-3,0 Z M-15,0 L0,3 L15,0 L0,-3 Z"/></g>
|
||
</g>
|
||
<g class="c-here" id="chart-here">
|
||
<circle class="c-here__ring" r="7"/>
|
||
<circle class="c-here__dot" r="3"/>
|
||
<text class="c-here__label" x="10" y="-8">You are here</text>
|
||
</g>
|
||
<rect class="c-frame" x="4" y="4" width="312" height="202"/>
|
||
</svg>`;
|
||
}
|