Files
TeslaRel410/emulator/firmware-decomp/render-readout.html
T
CydandClaude Opus 4.8 6dc017cb3d THE TEST PATTERN: cap7's bench scene recovered exactly (137 triangles)
tri_recover.py parses each payload's stride-0x10 edge groups {scale,A,B,C}
(GOODEQNS edgeize output), solves the three edge equations pairwise into true
vertices, and fills the triangles. cap7's bench = a triangulated calibration
grid + a multi-part test model + triangle strips, in global screen coords --
the image Division's engineers used to validate VelociRender boards, exact to
the compiled coefficients. The earlier 9x5 patch was one corner of this scene.
Readout gains §06 (test pattern) with trek/klng scenes moving to §07. The demo
captures' streak/point payloads use a different layout (parser refinement
pending) -- the bench is pure triangles and recovers perfectly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:35:17 -05:00

849 lines
77 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<title>VelociRender — firmware render output</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root{
--ground:#070b10; --panel:#0c141c; --panel2:#101c26;
--line:#1c2e3a; --line2:#2a4150;
--phos:#41ff8e; --phos-dim:#1f6e46;
--amber:#ffb020; --cyan:#5fd0ff; --red:#ff5a6a;
--ink:#cfe0ea; --mute:#8093a2; --faint:#54697a;
--mono:ui-monospace,"Cascadia Code","SF Mono",Menlo,Consolas,monospace;
--sans:"Bahnschrift","DIN Alternate","Segoe UI",system-ui,sans-serif;
--body:"Segoe UI",system-ui,-apple-system,sans-serif;
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
margin:0;background:var(--ground);color:var(--ink);
font-family:var(--body);line-height:1.6;
background-image:
radial-gradient(1200px 600px at 70% -10%, rgba(65,255,142,.05), transparent 60%),
repeating-linear-gradient(0deg, rgba(255,255,255,.014) 0 1px, transparent 1px 3px);
}
.wrap{max-width:1080px;margin:0 auto;padding:clamp(20px,5vw,56px) clamp(16px,4vw,40px) 80px}
.eyebrow{font-family:var(--mono);font-size:12px;letter-spacing:.28em;text-transform:uppercase;
color:var(--phos);display:flex;align-items:center;gap:10px}
.eyebrow::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--phos);
box-shadow:0 0 10px var(--phos);animation:blink 2.4s infinite}
@keyframes blink{0%,72%,100%{opacity:1}80%,92%{opacity:.25}}
@media (prefers-reduced-motion:reduce){.eyebrow::before{animation:none}}
h1{font-family:var(--sans);font-weight:700;letter-spacing:.01em;line-height:1.02;
font-size:clamp(34px,7vw,68px);margin:.35em 0 .15em;text-wrap:balance;
text-transform:uppercase}
h1 .b{color:var(--phos)}
.lede{font-size:clamp(16px,2.2vw,20px);color:var(--ink);max-width:60ch;margin:0}
.lede b{color:var(--cyan);font-weight:600}
.hero-render{margin:30px 0 14px;border:1px solid var(--line2);border-radius:12px;
overflow:hidden;background:radial-gradient(140% 120% at 50% 0%, #0a141c, #05090d);
position:relative}
.hero-render canvas{display:block;width:100%;height:auto;max-height:460px;
image-rendering:auto}
.hero-render .cap{position:absolute;left:16px;bottom:12px;right:16px;
font-family:var(--mono);font-size:11.5px;letter-spacing:.1em;color:var(--mute);
text-transform:uppercase;line-height:1.5}
.hero-render .cap b{color:var(--phos)}
.duo{display:grid;grid-template-columns:1.5fr 1fr;gap:14px;margin-top:18px}
@media(max-width:640px){.duo{grid-template-columns:1fr}}
.duo figure{margin:0;border:1px solid var(--line2);border-radius:10px;overflow:hidden;
background:radial-gradient(130% 110% at 50% 12%, #0a141c, #05090d)}
.duo canvas{display:block;width:100%;height:auto}
.duo figcaption{padding:10px 13px;font-family:var(--mono);font-size:11px;
letter-spacing:.05em;color:var(--mute);line-height:1.5;border-top:1px solid var(--line)}
.duo figcaption b{color:var(--phos)}
.arr{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:18px;align-items:start}
@media(max-width:640px){.arr{grid-template-columns:1fr}}
.arr figure{margin:0;border:1px solid var(--line2);border-radius:10px;overflow:hidden;
background:linear-gradient(180deg,#05090d,#070d13)}
.arr canvas{display:block;width:100%;height:auto}
.arr figcaption{padding:10px 13px;font-family:var(--mono);font-size:11px;color:var(--mute);
line-height:1.5;border-top:1px solid var(--line)}
.arr figcaption b{color:var(--phos)}
.arrinfo{display:flex;flex-direction:column;gap:14px;padding-top:4px}
.pmlab{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
color:var(--faint)}
.pmbar{display:flex;gap:2px;height:34px;font-family:var(--mono);font-size:10px;font-weight:600}
.pmbar span{display:flex;align-items:center;justify-content:center;border-radius:3px;
color:#04070a;letter-spacing:.02em;white-space:nowrap;overflow:hidden}
.pmbar .z{flex:24;background:var(--cyan)}
.pmbar .r{flex:8;background:#ff6b6b}
.pmbar .g{flex:8;background:var(--phos)}
.pmbar .b{flex:8;background:#6ba8ff}
.pmbar .free{flex:160;background:#12202a;color:var(--faint)}
.mini{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:8px}
.mini li{position:relative;padding-left:18px;font-size:13.5px;color:var(--mute);line-height:1.45}
.mini li::before{content:"";position:absolute;left:2px;color:var(--cyan);font-weight:700}
.mini li b{color:var(--ink)}
.hero-render .tag{position:absolute;top:12px;left:16px;font-family:var(--mono);
font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--faint);
border:1px solid var(--line2);border-radius:5px;padding:4px 9px;z-index:2}
.hero-render::after{content:"";position:absolute;inset:0;pointer-events:none;
background:repeating-linear-gradient(180deg,rgba(0,0,0,0) 0 2px,rgba(0,0,0,0.16) 2px 3px),
radial-gradient(120% 100% at 50% 42%, rgba(0,0,0,0) 58%, rgba(3,5,8,0.66) 100%);
mix-blend-mode:multiply}
.scope{margin:14px 0 8px;border:1px solid var(--line);border-radius:10px;overflow:hidden;
background:linear-gradient(180deg,#04070a,#060d13);position:relative}
.scope canvas{display:block;width:100%;height:120px}
.scope .cap{position:absolute;left:14px;bottom:9px;font-family:var(--mono);font-size:10.5px;
letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.scope .cap b{color:var(--phos-dim)}
.stat-row{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:26px 0 8px}
@media (max-width:640px){.stat-row{grid-template-columns:repeat(2,1fr)}}
.stat{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:14px 16px}
.stat .n{font-family:var(--mono);font-size:clamp(22px,3.4vw,30px);font-weight:600;
color:var(--phos);font-variant-numeric:tabular-nums;line-height:1}
.stat .l{font-size:12px;color:var(--mute);margin-top:7px;letter-spacing:.02em}
section{margin-top:52px}
h2{font-family:var(--sans);text-transform:uppercase;letter-spacing:.05em;font-weight:700;
font-size:clamp(20px,3vw,27px);margin:0 0 6px;display:flex;align-items:baseline;gap:12px}
h2 .idx{font-family:var(--mono);font-size:13px;color:var(--phos-dim);font-weight:400}
.sub{color:var(--mute);max-width:64ch;margin:0 0 22px}
/* pipeline signal chain */
.chain{display:flex;flex-wrap:wrap;gap:0;border:1px solid var(--line);border-radius:10px;
overflow:hidden;background:var(--panel)}
.stage{flex:1 1 150px;min-width:150px;padding:16px 16px;border-right:1px solid var(--line);
position:relative}
.stage:last-child{border-right:0}
.stage .k{font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;
color:var(--faint)}
.stage .v{font-family:var(--sans);text-transform:uppercase;font-size:15px;font-weight:600;
margin:6px 0 4px;letter-spacing:.02em}
.stage .d{font-size:12.5px;color:var(--mute)}
.stage.on .v{color:var(--phos)}
.stage.wait .v{color:var(--amber)}
.stage.sim .v{color:var(--cyan)}
.stage .dot{position:absolute;top:14px;right:14px;width:7px;height:7px;border-radius:50%}
.stage.on .dot{background:var(--phos);box-shadow:0 0 8px var(--phos)}
.stage.wait .dot{background:var(--amber);box-shadow:0 0 8px var(--amber)}
.stage.sim .dot{background:var(--cyan);box-shadow:0 0 8px var(--cyan)}
/* two-column data */
.cols{display:grid;grid-template-columns:1.05fr .95fr;gap:20px}
@media (max-width:760px){.cols{grid-template-columns:1fr}}
.term{background:#05090d;border:1px solid var(--line2);border-radius:10px;overflow:hidden}
.term .bar{display:flex;align-items:center;gap:8px;padding:9px 14px;border-bottom:1px solid var(--line);
font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.term .bar .d{width:9px;height:9px;border-radius:50%;background:var(--line2)}
.term .bar .d.g{background:var(--phos)}
.term pre{margin:0;padding:14px 16px;font-family:var(--mono);font-size:12.5px;line-height:1.75;
overflow-x:auto;color:var(--ink)}
.term .op{color:var(--phos)}
.term .ad{color:var(--cyan)}
.term .cm{color:var(--faint)}
.term .am{color:var(--amber)}
.binwrap{background:var(--panel);border:1px solid var(--line2);border-radius:10px;padding:16px}
.binwrap canvas{display:block;width:100%;height:auto;image-rendering:pixelated;
border:1px solid var(--line);border-radius:4px;background:#04070a}
.legend{display:flex;gap:18px;margin-top:12px;font-family:var(--mono);font-size:11px;
letter-spacing:.06em;color:var(--mute);flex-wrap:wrap}
.legend i{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:6px;
vertical-align:-1px}
.fixlist{border-top:1px solid var(--line);margin-top:8px}
.fix{display:grid;grid-template-columns:150px 1fr;gap:16px;padding:14px 2px;
border-bottom:1px solid var(--line);align-items:baseline}
@media (max-width:640px){.fix{grid-template-columns:1fr;gap:4px}}
.fix .t{font-family:var(--mono);font-size:12.5px;color:var(--cyan);letter-spacing:.02em}
.fix .x{font-size:14px;color:var(--ink)}
.fix .x b{color:var(--phos);font-weight:600}
.status{margin-top:52px;border:1px solid var(--line2);border-radius:12px;overflow:hidden}
.status .h{display:flex;align-items:center;gap:12px;padding:16px 20px;
background:linear-gradient(180deg,var(--panel2),var(--panel));border-bottom:1px solid var(--line)}
.status .h .tag{font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;
padding:4px 9px;border-radius:5px;border:1px solid var(--phos-dim);color:var(--phos)}
.status .h .tag.next{border-color:var(--line2);color:var(--amber)}
.status .h .t{font-family:var(--sans);text-transform:uppercase;font-weight:700;letter-spacing:.03em;font-size:16px}
.status .b{padding:18px 20px;display:grid;grid-template-columns:1fr 1fr;gap:22px}
@media (max-width:640px){.status .b{grid-template-columns:1fr}}
.status .b h3{font-size:12px;text-transform:uppercase;letter-spacing:.14em;color:var(--faint);
font-family:var(--mono);margin:0 0 10px;font-weight:400}
.status ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:9px}
.status li{font-size:14px;color:var(--ink);padding-left:22px;position:relative}
.status li::before{position:absolute;left:0;top:1px;font-family:var(--mono);font-size:13px}
.status .done li::before{content:"✓";color:var(--phos)}
.status .todo li::before{content:"→";color:var(--amber)}
.status li b{color:var(--cyan);font-weight:600}
footer{margin-top:48px;padding-top:20px;border-top:1px solid var(--line);
font-family:var(--mono);font-size:11.5px;letter-spacing:.08em;color:var(--faint);
display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px}
.mono{font-family:var(--mono)}
a{color:var(--cyan)}
</style>
<div class="wrap">
<div class="eyebrow">VWE&nbsp;TESLA · Division VelociRender · i860 firmware emulator</div>
<h1>The board's own firmware<br>is <span class="b">rendering</span> again</h1>
<p class="lede">A cycle-faithful Intel&nbsp;i860 interpreter now runs the game's
<b>actual shipped firmware</b> — booted, initialised, and fed a complete recorded
mission over the wire. It replays every command, emits the real
<b>PXPL5 IGC render stream</b>, and projects the scene geometry. Below is an object
it drew, its vertices and normals pulled straight off the emulated chip and
<b>reconstructed in 3D</b> — geometry this board's firmware has not computed in thirty years.</p>
<div class="hero-render">
<span class="tag">reconstructed · firmware geometry + normals</span>
<canvas id="shaded" width="880" height="560" aria-label="the object rebuilt as a 3D height-field surface"></canvas>
<div class="cap"><b>the object cap7 was drawing</b> — its 45 projected vertices resolve into a
complete 9×5 height-field surface, rebuilt here in true 3D and lit by the board's own
per-vertex normals. Pulled live off the emulated i860.</div>
</div>
<div class="scope">
<canvas id="scope" width="1040" height="120"></canvas>
<div class="cap"><b>trace ·</b> i860 execution → IGC command DMA</div>
</div>
<div class="stat-row">
<div class="stat"><div class="n">26,422</div><div class="l">wire commands replayed (whole mission)</div></div>
<div class="stat"><div class="n">8,562</div><div class="l">draw_scene frames processed</div></div>
<div class="stat"><div class="n">191<span style="color:var(--faint)">/192</span></div><div class="l">screen regions binned with geometry</div></div>
<div class="stat"><div class="n">0</div><div class="l">unimplemented ops remaining on the path</div></div>
</div>
<section>
<h2><span class="idx">01</span> The render signal chain</h2>
<p class="sub">Nothing here is re-interpreted by us. Each stage is the firmware's own
code executing on the emulated i860 — from parsing the wire packet to writing
the bit-serial micro-program the Pixel-Planes array runs.</p>
<div class="chain">
<div class="stage on"><span class="dot"></span><div class="k">in</div><div class="v">Wire</div><div class="d">dN_receive → 42-action dispatch</div></div>
<div class="stage on"><span class="dot"></span><div class="k">cpu</div><div class="v">i860</div><div class="d">transform · clip · classify</div></div>
<div class="stage on"><span class="dot"></span><div class="k">geom</div><div class="v">Binitize</div><div class="d">per-region 64×128 bins</div></div>
<div class="stage on"><span class="dot"></span><div class="k">out</div><div class="v">IGC DMA</div><div class="d">SEND / TILE / FLUSH stream</div></div>
<div class="stage sim"><span class="dot"></span><div class="k">px</div><div class="v">Pixel-Planes</div><div class="d">array model simulated · §05</div></div>
</div>
</section>
<section>
<h2><span class="idx">02</span> The output, decoded</h2>
<p class="sub">The firmware DMAs the render to the card as an opcode stream
(<span class="mono">DMAENGN.H</span>). Both panels below are captured verbatim from the
emulator. Left: a real per-region command list — every region references the same
tile-relative payloads and differs only in its <span class="mono">TILE</span> id and the
<span class="mono">GOTO</span> link. Right: inside a SEND — the payload isn't opaque, it
interleaves control words with the actual <b>float coefficients</b>, swept bit-plane by
bit-plane into pixel memory.</p>
<div class="cols">
<div class="term">
<div class="bar"><span class="d g"></span><span class="d"></span><span class="d"></span>
&nbsp;region 0x0801fa40 · DMA list</div>
<pre><span class="cm"># addr opcode</span>
<span class="ad">0x08015000</span> <span class="op">SEND</span>(4) <span class="cm">; edge coeffs</span>
<span class="ad">0x00000000</span> <span class="am">FLUSH</span>
<span class="ad">0x08015020</span> <span class="op">SENDE</span>(0x45) <span class="cm">; z / colour</span>
<span class="ad">0x00000000</span> <span class="am">FLUSH</span>
<span class="ad">0x08014100</span> <span class="op">TXDN</span>
<span class="ad">0x08015260</span> <span class="op">SEND</span>(0x21)
<span class="ad">0x08015380</span> <span class="op">SEND</span>(0x29)
<span class="ad">0x00000020</span> <span class="am">TILE</span> <span class="cm">; tile id</span>
<span class="ad">0x0801f008</span> <span class="am">GOTO</span> <span class="cm">; → next region</span></pre>
</div>
<div class="term">
<div class="bar"><span class="d g"></span><span class="d"></span><span class="d"></span>
&nbsp;SENDE target 0x08015020 · payload</div>
<pre><span class="cm"># bit-serial sweep (MEMpluseqMEM)</span>
+000 <span class="ad">00000100</span> <span class="cm">; header</span>
+004 <span class="op">3a804834</span> <span class="cm">; = 9.79e-4 float</span>
+008 <span class="ad">8401213a</span>
+00c <span class="ad">00000021</span> <span class="cm">; bit-plane 0x21</span>
+010 <span class="op">ba01253a</span> <span class="cm">; = 4.93e-4 += A</span>
+018 <span class="ad">8381213a</span>
+01c <span class="ad">00000022</span> <span class="cm">; bit-plane 0x22 …</span></pre>
</div>
</div>
<p class="sub" style="margin-top:14px">Pull the floats out of those payloads and they
line up into clean <b>×2 chains</b>
<span class="mono">0.0079 · 0.016 · 0.032 · 0.063 · 0.126 · 0.252 · 0.504 · 1.009</span>
a coefficient stored as its binary place values <span class="mono">C·2ᵏ</span>, one per
bit-plane, exactly how a bit-serial adder holds a number. And the recovered values are the
object's own geometry: a payload edge coefficient (<span class="mono">0.12527</span>) lands on
the edge normal computed from the captured vertices (<span class="mono">0.12555</span>) to
<b>0.2%</b>. The plane <b>constants</b> sit alongside as fixed-point screen coordinates
(<span class="mono">0x0000ec00 = 236.0</span>, a vertex x). The coefficients feeding the array
simulator (§05) are <b>the ones the hardware actually shipped</b>.</p>
</section>
<section>
<h2><span class="idx">03</span> Where the geometry landed</h2>
<p class="sub">At the frame flush the firmware walks the screen-bin array and hands the
IGC one descriptor per 64×128 region. This grid is drawn from the <b>real captured
descriptor scan</b> — filled cells are regions that received transformed geometry.
A full mission view fills nearly the whole frame: ground plane and sky, exactly as expected.</p>
<div class="binwrap">
<canvas id="bins" width="192" height="96" aria-label="region binning coverage"></canvas>
<div class="legend">
<span><i style="background:var(--phos)"></i>region has geometry</span>
<span><i style="background:#0d1a12"></i>empty region (shared page)</span>
<span style="color:var(--faint)">96 × 2 descriptor grid · 191 of 192 filled</span>
</div>
</div>
</section>
<section>
<h2><span class="idx">04</span> How the death-camera saw it</h2>
<p class="sub">The surface above is the same 45 vertices, sorted back into model space —
they form an exact <b>9 × 5 height-field grid</b> (x and z in even 2-unit steps, y the
height at every node; all 45 cells filled). What the board actually wrote to screen is
below: cap7's death-camera views that surface nearly <b>edge-on</b>, so the frame the i860
projected is a thin, folded sliver. It is authentic output — just an awkward angle. Left,
that projection Gouraud-shaded; right, its raw <span class="mono">VSTRIP</span> wireframe.</p>
<div class="duo">
<figure>
<canvas id="surf" width="500" height="560" aria-label="the death-camera's edge-on shaded projection"></canvas>
<figcaption><b>the board's screen-space frame</b> — Gouraud-shaded,
grazing near-edge-on angle</figcaption>
</figure>
<figure>
<canvas id="verts" width="420" height="560" aria-label="raw VSTRIP wireframe"></canvas>
<figcaption><b>raw VSTRIP wireframe</b> — 4 strips, 45 vertices,
exactly as the i860 emitted them</figcaption>
</figure>
</div>
</section>
<section>
<h2><span class="idx">05</span> The Pixel-Planes array, simulated</h2>
<p class="sub">This is the final stage in silicon — and it now runs in software. The
<span class="mono">PXPL5 IGC</span> is a region rasteriser: the screen splits into
<b>64 × 128 tiles</b>; every pixel carries a 26-byte bit-memory and an enable bit; all
pixels evaluate one linear tree in lockstep —
<span class="mono">eval_ltree(x,y,A,B,C) = (int)(x·A + y·B + C)</span>. A triangle becomes
three <b>edge trees</b> into the enable register, then <b>Z and R/G/B planes</b>
interpolated per pixel and z-buffered in pixel memory (<span class="mono">IGCOPS.C</span>).
Fed the captured surface, the model lights the tiles below and renders pixels that match our
reference rasteriser to within edge anti-aliasing — the array reproduced faithfully.</p>
<div class="arr">
<figure>
<canvas id="depth" width="560" height="500" aria-label="the depth buffer the array computes"></canvas>
<figcaption><b>the depth buffer</b>, read straight out of pixel memory — the 24-bit
Z plane the <span class="mono">SENDE</span> z-sweep interpolates (near = bright)</figcaption>
</figure>
<figure>
<canvas id="tiles" width="500" height="360" aria-label="the object's footprint across the 64x128 tile array"></canvas>
<figcaption><b>the object's footprint</b> — 18 of 50 <b>64×128</b> tiles lit,
each a region of pixel processors</figcaption>
</figure>
</div>
<div class="arrinfo" style="margin-top:16px">
<div class="pmlab">pixel memory · 26 bytes = 208 bits</div>
<div class="pmbar">
<span class="z">Z · 24b</span><span class="r">R·8</span><span class="g">G·8</span>
<span class="b">B·8</span><span class="free">enable + working bits</span>
</div>
<ul class="mini">
<li>three edge trees → the <b>enable register</b> (the inside test)</li>
<li>Z / R / G / B are <b>planes</b>, evaluated per pixel by the linear tree</li>
<li>z-buffer via <span class="mono">MEM2geMEM2</span>; every write gated by enable</li>
<li>the depth image (left) is that Z plane, read back out of pixel memory</li>
<li><b>validated</b> pixel-faithful to the reference — ~1%, at edges only</li>
</ul>
</div>
<p class="sub" style="margin-top:16px">What this is <b>not</b>, yet: a full from-scratch run of
the compiled <b>micro-code</b> the DMA ships (§02) — the form the geometry takes on the wire to the
card. But that stream is now largely decoded: the command lists read cleanly,
the <span class="mono">SENDE</span> z-sweep resolves into a regular 4-word instruction, and its
coefficients are the same ones driving this depth buffer — the array's inputs are
<b>cross-validated against the hardware's own compiled stream</b>. And scanning
<b>all 14,223 payload writes</b> of this draw, every screen coordinate lands in the object's own
range (<span class="mono">x 66236</span>) — this death-cam frame carries <b>no geometry beyond
the object</b>; the rest is a background clear. So the array's render above isn't one primitive of
many: for this frame, it <b>is</b> the geometry.</p>
</section>
<section>
<h2><span class="idx">06</span> The test pattern — the bench revealed</h2>
<p class="sub">And the scene this page began with — the test bench that every
<span class="mono">cap*</span> capture records — turns out to have been hiding in plain
sight. Walking its DMA chains and solving each primitive's <b>three edge equations</b>
(<span class="mono">Ax+By+C</span>, pairwise-intersected into vertices) recovers the whole
thing exactly: <b>137 triangles</b> — a triangulated <b>calibration grid</b>, a multi-part
<b>test model</b>, and <b>triangle strips</b> along the base. This is the image Division's
engineers used to validate VelociRender boards — reconstructed to the pixel from the
compiled coefficients, thirty years on. The 9×5 patch rendered earlier (§0305) is one
corner of this scene.</p>
<div class="hero-render" style="margin-top:16px">
<span class="tag">exact recovery · edge equations solved to vertices</span>
<canvas id="benchframe" width="832" height="512" aria-label="the hardware test pattern recovered exactly from compiled edge equations"></canvas>
<div class="cap"><b>the VelociRender test bench</b> — every triangle solved from its
compiled edge equations; colors distinguish primitives (true per-primitive colors live
in the payload's colour planes, still being decoded).</div>
</div>
</section>
<section>
<h2><span class="idx">07</span> The warp field — a Star Trek scene, recovered</h2>
<p class="sub">The archive's <span class="mono">trek</span> capture — unreleased Star Trek
material — <b>replays and draws</b> after the dual-instruction-mode fix. Walking the
per-region DMA chains in the bin pages enumerated <b>898 compiled payload programs</b> for
the frame: four are the standard end-of-frame pipeline (fog, texture, RGB — byte-identical
in every capture), and <b>~860 are the scene itself</b>, per-primitive coefficient blocks
with their geometry sitting in plain screen-space floats. Plotted below:
<b>433 star positions and 389 streaks</b>, radiating from a convergence point — the
<b>warp-speed starfield</b>, reconstructed from micro-code compiled by the board's own
firmware running on the emulated i860.</p>
<div class="hero-render" style="margin-top:16px">
<span class="tag">recovered from the frame's compiled payload programs</span>
<canvas id="trekframe" width="832" height="512" aria-label="the warp starfield recovered from trek's compiled frame"></canvas>
<div class="cap"><b>trek at warp</b> — every point and streak is one primitive's
position read from its compiled coefficient block (fields +0x14/+0x18 and +0x24/+0x28,
plain IEEE floats in screen space).</div>
</div>
<div class="hero-render" style="margin-top:14px">
<span class="tag">same recovery · the Klingon capture</span>
<canvas id="klngframe" width="832" height="512" aria-label="the Klingon scene recovered from klngvid's compiled frame"></canvas>
<div class="cap"><b>the Klingon scene</b> — 96 positions and 62 edge segments: a sparse
starfield with a dense tangle of intersecting edges right-of-center — <b>a vessel</b>,
recovered by the identical chain-walk from the <span class="mono">klngvid</span> capture.</div>
</div>
</section>
<section>
<h2><span class="idx">08</span> What it took to get here</h2>
<p class="sub">The i860 core was corrected against the toolchain's own assembler output
and MAME's validated i860 model. A selection of the load-bearing fixes:</p>
<div class="fixlist">
<div class="fix"><div class="t">store encoding</div><div class="x">i860 stores keep the source in bits 15:11 and split the offset — every function return went to 0 until this was found. <b>Prologues now save r1.</b></div></div>
<div class="fix"><div class="t">op 0x13 escape</div><div class="x">lock / calli / unlock share one opcode; everything decoded as calli, so every spinlock jumped to address 0. <b>The phantom "exit stub."</b></div></div>
<div class="fix"><div class="t">FP pipeline</div><div class="x">Exact PFAM/PFMAM dual-ops, per-stage precision, fdest bypass. The NewtonRaphson divide now yields <b>16 ÷ 16 = 1</b> through the pipes, unhooked.</div></div>
<div class="fix"><div class="t">pipelined pftrunc</div><div class="x">Float→int through the adder pipe — a form no prior i860 emulator implemented. <b>Killed an 8.3-million phantom-row loop.</b></div></div>
<div class="fix"><div class="t">IGC drain</div><div class="x">Per-page completion is nonzero-on-done, and consumed pages reset their write index. <b>Frames stopped saturating after ~1,400 draws.</b></div></div>
</div>
</section>
<div class="status">
<div class="h"><span class="tag">Landed</span>
<span class="t">Firmware → render command stream, end to end</span></div>
<div class="b">
<div>
<h3>Verified working</h3>
<ul class="done">
<li>Cold boot of the capture's own firmware build on the emulated i860</li>
<li>Full recorded mission replays to completion — <b>26,422 commands</b></li>
<li>Scene graph, transform, frustum classify on real geometry</li>
<li>Real PXPL5 IGC opcode stream emitted and decoded (<b>SEND / TILE / FLUSH</b>)</li>
<li>Geometry binned into <b>191 screen regions</b></li>
<li>Object <b>recovered and shaded</b> — a 9×5 height-field surface, lit by the firmware's own normals (above)</li>
<li><b>Pixel-Planes array simulated</b> (§05) — 64×128 tiles, per-pixel memory, edge trees + z-buffer, validated pixel-faithful</li>
</ul>
</div>
<div>
<h3>The last mile to pixels</h3>
<ul class="todo">
<li>The array's <b>computational model</b> runs and matches the reference — the piece left is executing the board's own <b>compiled micro-code</b></li>
<li>The per-region <b>DMA command lists decode cleanly</b> (§02) — SEND / SENDE / TXDN / TILE / GOTO, tile-relative</li>
<li>The SEND payloads carry <b>real float coefficients</b> in a regular bit-serial sweep — recoverable, not opaque</li>
<li>Coefficients <b>verified to 0.2%</b> against the geometry; for this frame, the object <b>is</b> the geometry (§05)</li>
<li>cap7 redraws <b>this one object every frame</b> — confirmed identical at cmd 735 and cmd 19,889, same 9 coordinates. A battle scene with terrain is a different capture entirely</li>
</ul>
</div>
</div>
</div>
<footer>
<span>VWE&nbsp;TESLA REL410 · firmware-decomp · emu860</span>
<span>Tier 0 complete · Tier 1 array simulated · micro-code decode next</span>
</footer>
</div>
<script>
(function(){
var reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
/* ---- scope: an i860 execution trace as an oscilloscope sweep ---- */
var c = document.getElementById('scope'), x = c.getContext('2d');
var W = c.width, H = c.height;
// a pseudo instruction-density trace (deterministic)
var N = 260, data = [];
var seed = 1337;
function rnd(){ seed = (seed*1103515245 + 12345) & 0x7fffffff; return seed/0x7fffffff; }
for(var i=0;i<N;i++){
var base = 0.5 + 0.32*Math.sin(i*0.11) + 0.12*Math.sin(i*0.37+1);
var spike = rnd()<0.12 ? (rnd()*0.5) : 0;
data.push(Math.max(0.04, Math.min(0.97, base*0.7 + spike)));
}
var t=0;
function grid(){
x.clearRect(0,0,W,H);
x.strokeStyle='rgba(65,255,142,0.06)'; x.lineWidth=1;
for(var gy=0; gy<H; gy+=H/6){ x.beginPath(); x.moveTo(0,gy); x.lineTo(W,gy); x.stroke(); }
for(var gx=0; gx<W; gx+=W/16){ x.beginPath(); x.moveTo(gx,0); x.lineTo(gx,H); x.stroke(); }
}
function frame(){
grid();
// glow trace
for(var pass=0; pass<2; pass++){
x.beginPath();
for(var i=0;i<N;i++){
var px = i/(N-1)*W;
var idx = (i + Math.floor(t)) % N;
var py = H - data[idx]*(H-24) - 12;
if(i===0) x.moveTo(px,py); else x.lineTo(px,py);
}
x.strokeStyle = pass===0 ? 'rgba(65,255,142,0.18)' : 'rgba(65,255,142,0.95)';
x.lineWidth = pass===0 ? 6 : 1.6;
x.stroke();
}
// sweep head
var hx = ((t*4)%W);
var g = x.createLinearGradient(hx-40,0,hx,0);
g.addColorStop(0,'rgba(65,255,142,0)'); g.addColorStop(1,'rgba(65,255,142,0.25)');
x.fillStyle=g; x.fillRect(hx-40,0,40,H);
if(!reduce){ t += 0.4; requestAnimationFrame(frame); }
}
frame();
/* ---- projected VSTRIP triangle strips captured from the i860 ---- */
var STRIPS = [[[237.52,62.0],[235.22,46.5],[238.02,31.0],[236.58,18.6]],
[[240.88,43.4],[238.91,6.2],[237.46,31.0],[238.02,9.3],[237.11,62.0],[242.21,21.7],
[238.4,37.2],[242.27,15.5],[237.55,31.0],[246.45,0.0],[242.77,31.0],[246.92,12.4],
[241.71,40.3],[236.31,0.0],[253.7,62.0],[252.07,43.4]],
[[245.13,15.5],[239.94,40.3],[238.5,93.0],[234.37,62.0],[239.09,0.0],[231.87,0.0],
[240.72,0.0],[233.5,40.3],[254.57,24.8],[243.22,0.0],[233.78,12.4],[243.22,15.5],
[254.29,0.0],[250.44,9.3],[249.57,12.4],[245.74,0.0]],
[[126.07,62.0],[238.81,49.6],[239.09,62.0],[250.44,40.3],[234.09,31.0],[250.44,43.4],
[231.29,46.5],[254.87,37.2],[252.65,34.1]]];
(function(){
var vc=document.getElementById('verts'), vx=vc.getContext('2d');
var W=vc.width, H=vc.height;
var all=[]; STRIPS.forEach(function(s){s.forEach(function(p){all.push(p);});});
// frame on the main body (ignore the one far-left strip vertex for scale)
var body=all.filter(function(p){return p[0]>200;});
var mnx=Math.min.apply(0,body.map(function(p){return p[0]}));
var mxx=Math.max.apply(0,body.map(function(p){return p[0]}));
var mny=Math.min.apply(0,body.map(function(p){return p[1]}));
var mxy=Math.max.apply(0,body.map(function(p){return p[1]}));
var pad=46, s=Math.min((W-2*pad)/(mxx-mnx),(H-2*pad)/(mxy-mny));
var ox=(W-(mxx-mnx)*s)/2, oy=(H-(mxy-mny)*s)/2;
function T(p){return [ox+(p[0]-mnx)*s, oy+(p[1]-mny)*s];}
function seg(a,b,col,wd){var A=T(a),B=T(b);vx.beginPath();vx.moveTo(A[0],A[1]);
vx.lineTo(B[0],B[1]);vx.strokeStyle=col;vx.lineWidth=wd;vx.stroke();}
// grid
vx.strokeStyle='rgba(65,255,142,0.045)';
for(var gx=0;gx<W;gx+=44){vx.beginPath();vx.moveTo(gx,0);vx.lineTo(gx,H);vx.stroke();}
for(var gy=0;gy<H;gy+=44){vx.beginPath();vx.moveTo(0,gy);vx.lineTo(W,gy);vx.stroke();}
// triangle-strip edges: v[i]-v[i+1] (strip) and v[i]-v[i+2] (cross) = triangles
STRIPS.forEach(function(st){
for(var i=0;i<st.length-2;i++) seg(st[i],st[i+2],'rgba(95,208,255,0.4)',1);
for(var i=0;i<st.length-1;i++) seg(st[i],st[i+1],'rgba(65,255,142,0.7)',1.3);
});
// glow pass over strip edges
vx.globalCompositeOperation='lighter';
STRIPS.forEach(function(st){
for(var i=0;i<st.length-1;i++) seg(st[i],st[i+1],'rgba(65,255,142,0.15)',3.5);
});
vx.globalCompositeOperation='source-over';
// vertices
all.forEach(function(p){var t=T(p);
vx.beginPath();vx.arc(t[0],t[1],2.6,0,7);vx.fillStyle='#eaffef';vx.fill();});
})();
/* ---- shaded render: Gouraud raster of the projected object ----
GEO per vertex = [screenX, screenY, normalX, normalY, normalZ, modelZ].
Screen xy + normals pulled live off the emulated i860; we do the fill in
software (this is our rasteriser, not the board's bit-serial IGC array). */
var GEO=[[[237.5,62,0.931,0.262,0.131,-8],[235.2,46.5,0.922,0.187,0.094,-6],[238,31,0.933,0.278,0.139,-8],[236.6,18.6,0.928,0.231,0.116,-6]],
[[240.9,43.4,0.945,0.372,0.186,-8],[238.9,6.2,0.937,0.307,0.154,-6],[237.5,31,0.931,0.26,0.13,-8],[238,9.3,0.933,0.278,0.139,-6],[237.1,62,0.93,0.249,0.124,-8],[242.2,21.7,0.95,0.415,0.208,-6],[238.4,37.2,0.935,0.291,0.146,-8],[242.3,15.5,0.95,0.417,0.209,-6],[237.6,31,0.932,0.263,0.431,-8],[246.5,0,0.966,0.554,0.377,-6],[242.8,31,0.952,0.943,0.517,-8],[246.9,12.4,0.968,0.957,0.685,-6],[241.7,40.3,0.948,0.98,0.65,-8],[236.3,0,0.927,0.936,0.678,-6],[253.7,62,0.995,0.246,0.123,-4],[252.1,43.4,0.988,0.324,0.162,-4]],
[[245.1,15.5,0.961,0.384,0.192,-4],[239.9,40.3,0.941,0.341,0.171,-4],[238.5,93,0.935,0.446,0.223,-4],[234.4,62,0.919,0.643,0.216,-4],[239.1,0,0.938,0.645,0.424,-4],[231.9,0,0.909,0.951,0.539,-4],[240.7,0,0.944,0.984,0.685,-4],[233.5,40.3,0.916,0.263,0.132,-2],[254.6,24.8,0.998,0.415,0.208,-2],[243.2,0,0.954,0.461,0.231,-2],[233.8,12.4,0.917,0.431,0.215,-2],[243.2,15.5,0.954,0.461,0.231,-2],[254.3,0,0.997,0.614,0.207,-2],[250.4,9.3,0.982,0.64,0.501,-2],[249.6,12.4,0.979,0.94,0.58,-2],[245.7,0,0.964,0.939,0.723,-2]],
[[126.1,62,0.494,0.914,0.206,0],[238.8,49.6,0.937,0.447,0.223,0],[239.1,62,0.938,0.448,0.224,0],[250.4,40.3,0.982,0.402,0.201,0],[234.1,31,0.918,0.432,0.216,0],[250.4,43.4,0.982,0.402,0.201,0],[231.3,46.5,0.907,0.639,0.37,0],[254.9,37.2,0.999,0.916,0.508,0],[252.7,34.1,0.991,0.926,0.663,0]]];
(function(){
var sc=document.getElementById('surf'), sctx=sc.getContext('2d');
var CW=sc.width, CH=sc.height, SS=2, W=CW*SS, H=CH*SS;
var inten=new Float32Array(W*H), zb=new Float32Array(W*H);
for(var i=0;i<W*H;i++) zb[i]=-1e9;
var body=[]; GEO.forEach(function(o){o.forEach(function(v){if(v[0]>200)body.push(v);});});
var mnx=Math.min.apply(0,body.map(function(v){return v[0];}));
var mxx=Math.max.apply(0,body.map(function(v){return v[0];}));
var mny=Math.min.apply(0,body.map(function(v){return v[1];}));
var mxy=Math.max.apply(0,body.map(function(v){return v[1];}));
var pad=70*SS, s=Math.min((W-2*pad)/(mxx-mnx),(H-2*pad)/(mxy-mny));
var ox=(W-(mxx-mnx)*s)/2-mnx*s, oy=(H-(mxy-mny)*s)/2-mny*s;
function nrm(a,b,c){var m=Math.sqrt(a*a+b*b+c*c)||1;return [a/m,b/m,c/m];}
var L=nrm(0.35,-0.55,0.75);
function I(v){var n=nrm(v[2],v[3],v[4]);var d=n[0]*L[0]+n[1]*L[1]+n[2]*L[2];
return Math.max(0.2,Math.min(1,0.24+0.82*Math.abs(d)));}
function tri(a,b,c){
var ax=ox+a[0]*s,ay=oy+a[1]*s,bx=ox+b[0]*s,by=oy+b[1]*s,cx=ox+c[0]*s,cy=oy+c[1]*s;
var ia=I(a),ib=I(b),ic=I(c), za=a[5],zbv=b[5],zc=c[5];
var minx=Math.max(0,Math.floor(Math.min(ax,bx,cx))),maxx=Math.min(W-1,Math.ceil(Math.max(ax,bx,cx)));
var miny=Math.max(0,Math.floor(Math.min(ay,by,cy))),maxy=Math.min(H-1,Math.ceil(Math.max(ay,by,cy)));
var area=(bx-ax)*(cy-ay)-(cx-ax)*(by-ay); if(Math.abs(area)<1e-6)return;
for(var py=miny;py<=maxy;py++)for(var px=minx;px<=maxx;px++){
var w0=((bx-px)*(cy-py)-(cx-px)*(by-py))/area;
var w1=((cx-px)*(ay-py)-(ax-px)*(cy-py))/area;
var w2=1-w0-w1;
if(w0<-0.003||w1<-0.003||w2<-0.003)continue;
var z=w0*za+w1*zbv+w2*zc, idx=py*W+px;
if(z>zb[idx]){zb[idx]=z; inten[idx]=w0*ia+w1*ib+w2*ic;}
}
}
GEO.forEach(function(st){for(var i=0;i<st.length-2;i++)tri(st[i],st[i+1],st[i+2]);});
var img=sctx.createImageData(CW,CH), d=img.data;
for(var y=0;y<CH;y++)for(var xx=0;xx<CW;xx++){
var acc=0,cnt=0;
for(var yy=0;yy<SS;yy++)for(var xs=0;xs<SS;xs++){
var v=inten[(y*SS+yy)*W+(xx*SS+xs)]; if(v>0){acc+=v;cnt++;}
}
var o=(y*CW+xx)*4;
if(cnt>0){
var vv=acc/cnt, cov=cnt/(SS*SS);
d[o]=Math.min(255,28+vv*168); d[o+1]=Math.min(255,50+vv*205);
d[o+2]=Math.min(255,54+vv*122); d[o+3]=Math.round(cov*255);
} else d[o+3]=0;
}
sctx.putImageData(img,0,0);
})();
/* ---- true-3D surface: the object rebuilt as its 9x5 height-field grid ----
xs/zs = model grid axes; rows[z][x] = [heightY, nx,ny,nz]. All 45 cells real,
captured off the i860. We reconstruct the grid connectivity (2 tris/quad) and
light it with the firmware's own normals from a clean 3/4 camera. */
var GRID={"xs":[0,2,4,6,8,10,12,14,16],"zs":[-8,-6,-4,-2,0],"rows":[[[-2.167,0.948,0.98,0.65],[-1.295,0.952,0.943,0.517],[-1.439,0.932,0.263,0.431],[-1.591,0.935,0.291,0.146],[-1.87,0.93,0.249,0.124],[-2.311,0.931,0.26,0.13],[-2.014,0.945,0.372,0.186],[-2.446,0.933,0.278,0.139],[-3.021,0.931,0.262,0.131]],[[-2.158,0.927,0.936,0.678],[-1.016,0.968,0.957,0.685],[-0.584,0.966,0.554,0.377],[-1.007,0.95,0.417,0.209],[-1.016,0.95,0.415,0.208],[-1.726,0.933,0.278,0.139],[-1.735,0.937,0.307,0.154],[-2.023,0.928,0.231,0.116],[-2.589,0.922,0.187,0.094]],[[-2.158,0.944,0.984,0.685],[-1.735,0.909,0.951,0.539],[-1.87,0.938,0.645,0.424],[-1.87,0.919,0.643,0.216],[-1.448,0.935,0.446,0.223],[-1.582,0.941,0.341,0.171],[-1.16,0.961,0.384,0.192],[-1.016,0.988,0.324,0.162],[-1.448,0.995,0.246,0.123]],[[-2.446,0.964,0.939,0.723],[-2.598,0.979,0.94,0.58],[-2.733,0.982,0.64,0.501],[-2.455,0.997,0.614,0.207],[-1.735,0.954,0.461,0.231],[-1.448,0.917,0.431,0.215],[-0.872,0.954,0.461,0.231],[-0.441,0.998,0.415,0.208],[-0.719,0.916,0.263,0.132]],[[-3.021,0.991,0.926,0.663],[-3.021,0.999,0.916,0.508],[-3.165,0.907,0.639,0.37],[-2.589,0.982,0.402,0.201],[-1.87,0.918,0.432,0.216],[-1.726,0.982,0.402,0.201],[-1.151,0.938,0.448,0.224],[-0.432,0.937,0.447,0.223],[0,0.494,0.914,0.206]]]};
(function(){
var sc=document.getElementById('shaded'), sx=sc.getContext('2d');
var CW=sc.width, CH=sc.height, SS=2, W=CW*SS, H=CH*SS;
var XS=GRID.xs, ZS=GRID.zs, R=GRID.rows, NX=XS.length, NZ=ZS.length;
var cx=8, cz=-4, cy=0; // grid centre
(function(){var s=0,n=0;for(var j=0;j<NZ;j++)for(var i=0;i<NX;i++){s+=R[j][i][0];n++;}cy=s/n;})();
function nrm(a,b,c){var m=Math.sqrt(a*a+b*b+c*c)||1;return [a/m,b/m,c/m];}
var L=nrm(0.35,0.55,0.72);
var YAW=40*Math.PI/180, PIT=27*Math.PI/180;
var cyw=Math.cos(YAW),syw=Math.sin(YAW),cp=Math.cos(PIT),sp=Math.sin(PIT);
function rot(x,y,z){var x2=x*cyw+z*syw, z2=-x*syw+z*cyw; var y2=y*cp-z2*sp, z3=y*sp+z2*cp; return [x2,y2,z3];}
// project every grid node
var P=[];
for(var j=0;j<NZ;j++){P[j]=[];for(var i=0;i<NX;i++){
var c=R[j][i];
var p=rot(XS[i]-cx,(c[0]-cy)*1.8,ZS[j]-cz);
var nn=rot(c[1],c[2],c[3]); nn=nrm(nn[0],nn[1],nn[2]);
var d=nn[0]*L[0]+nn[1]*L[1]+nn[2]*L[2];
var inten=Math.max(0.16,Math.min(1,0.22+0.85*Math.abs(d)));
P[j][i]={X:p[0],Y:p[1],Z:p[2],I:inten};
}}
var mnx=1e9,mxx=-1e9,mny=1e9,mxy=-1e9;
for(var j=0;j<NZ;j++)for(var i=0;i<NX;i++){var p=P[j][i];
if(p.X<mnx)mnx=p.X;if(p.X>mxx)mxx=p.X;if(p.Y<mny)mny=p.Y;if(p.Y>mxy)mxy=p.Y;}
var pad=0.1*W, s=Math.min((W-2*pad)/(mxx-mnx),(H-2*pad)/(mxy-mny));
var ox=(W-(mxx-mnx)*s)/2, oy=(H-(mxy-mny)*s)/2;
function SXp(p){return (p.X-mnx)*s+ox;}
function SYp(p){return (mxy-p.Y)*s+oy;}
var inten=new Float32Array(W*H), zb=new Float32Array(W*H);
for(var k=0;k<W*H;k++)zb[k]=-1e9;
function tri(a,b,c){
var ax=SXp(a),ay=SYp(a),bx=SXp(b),by=SYp(b),cx2=SXp(c),cy2=SYp(c);
var area=(bx-ax)*(cy2-ay)-(cx2-ax)*(by-ay); if(Math.abs(area)<1e-6)return;
var x0=Math.max(0,Math.floor(Math.min(ax,bx,cx2))),x1=Math.min(W-1,Math.ceil(Math.max(ax,bx,cx2)));
var y0=Math.max(0,Math.floor(Math.min(ay,by,cy2))),y1=Math.min(H-1,Math.ceil(Math.max(ay,by,cy2)));
for(var py=y0;py<=y1;py++)for(var px=x0;px<=x1;px++){
var w0=((bx-px)*(cy2-py)-(cx2-px)*(by-py))/area;
var w1=((cx2-px)*(ay-py)-(ax-px)*(cy2-py))/area;
var w2=1-w0-w1; if(w0<-0.004||w1<-0.004||w2<-0.004)continue;
var z=w0*a.Z+w1*b.Z+w2*c.Z, idx=py*W+px;
if(z>zb[idx]){zb[idx]=z; inten[idx]=w0*a.I+w1*b.I+w2*c.I;}
}
}
for(var j=0;j<NZ-1;j++)for(var i=0;i<NX-1;i++){
var a=P[j][i],b=P[j][i+1],c=P[j+1][i],d=P[j+1][i+1];
tri(a,b,c); tri(b,d,c);
}
var img=sx.createImageData(CW,CH), dd=img.data;
for(var y=0;y<CH;y++)for(var xx=0;xx<CW;xx++){
var acc=0,cnt=0;
for(var yy=0;yy<SS;yy++)for(var xs2=0;xs2<SS;xs2++){var v=inten[(y*SS+yy)*W+(xx*SS+xs2)];if(v>0){acc+=v;cnt++;}}
var o=(y*CW+xx)*4;
if(cnt>0){var vv=acc/cnt,cov=cnt/(SS*SS);
dd[o]=Math.min(255,28+vv*168);dd[o+1]=Math.min(255,50+vv*205);
dd[o+2]=Math.min(255,54+vv*122);dd[o+3]=Math.round(cov*255);}
else dd[o+3]=0;
}
sx.putImageData(img,0,0);
})();
/* ---- region binning grid from the real descriptor scan ---- */
// 96x2 pass, 191/192 filled: the single empty cell recorded in the capture.
var bc = document.getElementById('bins'), bx = bc.getContext('2d');
var COLS=96, ROWS=2, CELL=8, GAP=1;
bc.width = COLS*(CELL+GAP)+GAP; bc.height = ROWS*(CELL+GAP)+GAP*4;
// draw at higher res for crispness
var scale=1;
var empties = new Set(['0-0']); // the one region left empty in the captured pass
bx.fillStyle = '#04070a'; bx.fillRect(0,0,bc.width,bc.height);
for(var r=0;r<ROWS;r++){
for(var cc=0;cc<COLS;cc++){
var key = r+'-'+cc;
var filled = !empties.has(key);
var px = GAP + cc*(CELL+GAP);
var py = GAP + r*(CELL+GAP);
if(filled){
var v = 0.55 + 0.45*Math.abs(Math.sin(cc*0.5 + r));
bx.fillStyle = 'rgba(65,255,142,'+ (0.35+0.5*v) +')';
} else {
bx.fillStyle = '#0d1a12';
}
bx.fillRect(px,py,CELL,CELL);
}
}
/* ---- depth buffer: the same surface, coloured by camera Z (the array's z-plane) ---- */
(function(){
var sc=document.getElementById('depth'); if(!sc) return;
var dx=sc.getContext('2d');
var CW=sc.width, CH=sc.height, SS=2, W=CW*SS, H=CH*SS;
var XS=GRID.xs, ZS=GRID.zs, R=GRID.rows, NX=XS.length, NZ=ZS.length;
var cx=8, cz=-4, cy=0;
(function(){var s=0,n=0;for(var j=0;j<NZ;j++)for(var i=0;i<NX;i++){s+=R[j][i][0];n++;}cy=s/n;})();
var YAW=40*Math.PI/180, PIT=27*Math.PI/180;
var cyw=Math.cos(YAW),syw=Math.sin(YAW),cp=Math.cos(PIT),sp=Math.sin(PIT);
function rot(x,y,z){var x2=x*cyw+z*syw,z2=-x*syw+z*cyw;var y2=y*cp-z2*sp,z3=y*sp+z2*cp;return [x2,y2,z3];}
var P=[];
for(var j=0;j<NZ;j++){P[j]=[];for(var i=0;i<NX;i++){
var c=R[j][i]; var p=rot(XS[i]-cx,(c[0]-cy)*1.8,ZS[j]-cz);
P[j][i]={X:p[0],Y:p[1],Z:p[2]};
}}
var mnx=1e9,mxx=-1e9,mny=1e9,mxy=-1e9,mnz=1e9,mxz=-1e9;
for(var j=0;j<NZ;j++)for(var i=0;i<NX;i++){var p=P[j][i];
if(p.X<mnx)mnx=p.X;if(p.X>mxx)mxx=p.X;if(p.Y<mny)mny=p.Y;if(p.Y>mxy)mxy=p.Y;
if(p.Z<mnz)mnz=p.Z;if(p.Z>mxz)mxz=p.Z;}
var pad=0.1*W, s=Math.min((W-2*pad)/(mxx-mnx),(H-2*pad)/(mxy-mny));
var ox=(W-(mxx-mnx)*s)/2, oy=(H-(mxy-mny)*s)/2, zsp=(mxz-mnz)||1;
function SXp(p){return (p.X-mnx)*s+ox;} function SYp(p){return (mxy-p.Y)*s+oy;}
var dep=new Float32Array(W*H); dep.fill(-2); var zb=new Float32Array(W*H); for(var k=0;k<W*H;k++)zb[k]=-1e9;
function tri(a,b,c){
var ax=SXp(a),ay=SYp(a),bx=SXp(b),by=SYp(b),cx2=SXp(c),cy2=SYp(c);
var area=(bx-ax)*(cy2-ay)-(cx2-ax)*(by-ay); if(Math.abs(area)<1e-6)return;
var x0=Math.max(0,Math.floor(Math.min(ax,bx,cx2))),x1=Math.min(W-1,Math.ceil(Math.max(ax,bx,cx2)));
var y0=Math.max(0,Math.floor(Math.min(ay,by,cy2))),y1=Math.min(H-1,Math.ceil(Math.max(ay,by,cy2)));
for(var py=y0;py<=y1;py++)for(var px=x0;px<=x1;px++){
var w0=((bx-px)*(cy2-py)-(cx2-px)*(by-py))/area;
var w1=((cx2-px)*(ay-py)-(ax-px)*(cy2-py))/area; var w2=1-w0-w1;
if(w0<-0.004||w1<-0.004||w2<-0.004)continue;
var z=w0*a.Z+w1*b.Z+w2*c.Z, idx=py*W+px;
if(z>zb[idx]){zb[idx]=z; dep[idx]=(z-mnz)/zsp;}
}
}
for(var j=0;j<NZ-1;j++)for(var i=0;i<NX-1;i++){
var a=P[j][i],b=P[j][i+1],c=P[j+1][i],d=P[j+1][i+1]; tri(a,b,c); tri(b,d,c);
}
var img=dx.createImageData(CW,CH), dd=img.data;
for(var y=0;y<CH;y++)for(var xx=0;xx<CW;xx++){
var acc=0,cnt=0;
for(var yy=0;yy<SS;yy++)for(var xs2=0;xs2<SS;xs2++){var v=dep[(y*SS+yy)*W+(xx*SS+xs2)];if(v>=0){acc+=v;cnt++;}}
var o=(y*CW+xx)*4;
if(cnt>0){var vv=1-acc/cnt,cov=cnt/(SS*SS); // near = bright
dd[o]=30+vv*120; dd[o+1]=90+vv*150; dd[o+2]=120+vv*130; dd[o+3]=Math.round(cov*255);}
else dd[o+3]=0;
}
dx.putImageData(img,0,0);
})();
/* ---- the test pattern: exact triangles from solved edge equations ---- */
var BENCH_TRIS=[[[192.3,86.2],[193.1,84.6],[192.4,84.1]],[[191.5,85.8],[192.3,86.2],[192.4,84.1]],[[192.4,84.1],[193.1,84.6],[195.6,84.1]],[[188.0,87.6],[191.2,86.9],[190.4,86.4]],[[187.2,87.2],[188.0,87.6],[190.4,86.4]],[[191.8,80.7],[192.5,81.2],[192.4,80.1]],[[191.2,86.9],[192.3,86.2],[191.5,85.8]],[[190.4,86.4],[191.2,86.9],[191.5,85.8]],[[190.0,78.9],[187.2,79.1],[188.8,82.1]],[[191.6,82.1],[188.8,82.1],[192.4,84.1]],[[195.6,84.1],[191.8,80.7],[191.6,82.1]],[[190.4,86.4],[188.8,82.1],[185.4,82.6]],[[188.8,82.1],[187.2,79.1],[185.4,82.6]],[[187.2,87.2],[185.4,82.6],[183.4,83.2]],[[187.2,79.1],[184.9,78.9],[185.4,82.6]],[[185.4,82.6],[184.9,78.9],[183.4,83.2]],[[184.9,78.9],[183.5,79.3],[183.4,83.2]],[[183.5,79.3],[181.5,80.9],[180.1,85.2]],[[181.5,80.9],[179.8,83.1],[180.1,85.2]],[[181.9,88.4],[180.1,85.2],[179.3,88.1]],[[179.8,83.1],[178.8,86.9],[180.1,85.2]],[[180.1,85.2],[178.8,87.6],[179.3,88.1]],[[183.4,88.6],[180.1,85.2],[181.9,88.4]],[[183.5,79.3],[180.1,85.2],[183.4,83.2]],[[190.4,86.4],[185.4,82.6],[187.2,87.2]],[[191.5,85.8],[188.8,82.1],[190.4,86.4]],[[192.4,84.1],[188.8,82.1],[191.5,85.8]],[[191.8,80.7],[188.8,82.1],[191.6,82.1]],[[190.0,78.9],[188.8,82.1],[191.8,80.7]],[[191.4,79.2],[190.0,78.9],[191.8,80.7]],[[192.4,80.1],[191.4,79.2],[191.8,80.7]],[[195.6,84.1],[191.6,82.1],[192.4,84.1]],[[196.8,82.6],[191.8,80.7],[195.6,84.1]],[[196.9,81.1],[191.8,80.7],[196.8,82.6]],[[195.8,79.1],[191.8,80.7],[196.9,81.1]],[[187.9,87.9],[187.2,87.2],[183.4,88.6]],[[80.8,80.9],[123.5,88.3],[102.2,84.6]],[[62.2,90.4],[123.5,88.3],[80.8,80.9]],[[31.7,113.2],[-8.1,109.6],[-9.9,72.1]],[[30.8,67.2],[31.7,113.2],[-9.9,72.1]],[[-50.0,-4.2],[30.8,67.2],[-9.9,72.1]],[[-26.2,204.9],[-8.1,109.6],[31.7,113.2]],[[345.1,136.1],[336.3,140.1],[397.3,146.3]],[[387.2,137.6],[345.1,136.1],[397.3,146.3]],[[338.6,133.0],[345.1,136.1],[387.2,137.6]],[[326.3,144.6],[397.3,146.3],[336.3,140.1]],[[331.7,162.6],[361.9,180.8],[310.7,186.4]],[[331.7,162.6],[368.9,158.7],[361.9,180.8]],[[368.9,158.8],[406.8,163.1],[361.9,180.8]],[[329.4,154.1],[368.9,158.8],[331.7,162.6]],[[335.3,29.4],[284.2,29.7],[230.9,29.9]],[[293.4,-46.6],[335.3,29.4],[275.0,-38.6]],[[335.3,29.4],[230.9,29.9],[275.0,-38.6]],[[108.1,104.4],[127.6,108.5],[146.8,112.5]],[[107.8,124.4],[108.1,104.4],[146.8,112.5]],[[482.2,153.8],[383.6,170.3],[486.8,176.7]],[[384.7,146.3],[193.2,130.0],[383.6,170.3]],[[208.3,114.2],[31.8,103.5],[193.2,130.0]],[[65.2,88.1],[9.4,91.8],[31.8,103.5]],[[9.4,91.8],[26.1,76.3],[-13.4,88.2]],[[26.1,76.3],[70.9,60.1],[-13.4,88.2]],[[357.7,63.5],[437.2,66.5],[443.8,36.8]],[[357.7,63.5],[384.7,146.3],[437.2,66.5]],[[273.2,64.7],[357.7,63.5],[311.4,13.1]],[[178.6,69.2],[273.2,64.7],[311.4,13.1]],[[208.3,114.2],[384.7,146.3],[273.2,64.7]],[[108.4,63.9],[178.6,69.2],[202.3,22.2]],[[178.6,69.2],[208.3,114.2],[273.2,64.7]],[[108.4,63.9],[208.3,114.2],[178.6,69.2]],[[46.2,74.4],[108.4,63.9],[83.8,49.8]],[[65.2,88.1],[208.3,114.2],[46.2,74.4]],[[26.1,76.3],[46.2,74.4],[83.8,49.8]],[[482.2,153.8],[384.7,146.3],[383.6,170.3]],[[384.7,146.3],[208.3,114.2],[193.2,130.0]],[[208.3,114.2],[65.2,88.1],[31.8,103.5]],[[65.2,88.1],[26.1,76.3],[9.4,91.8]],[[311.4,13.1],[357.7,63.5],[443.8,36.8]],[[273.2,64.7],[384.7,146.3],[357.7,63.5]],[[202.3,22.2],[178.6,69.2],[311.4,13.1]],[[83.8,49.8],[108.4,63.9],[202.3,22.2]],[[46.2,74.4],[208.3,114.2],[108.4,63.9]],[[26.1,76.3],[65.2,88.1],[46.2,74.4]],[[161.5,38.2],[200.8,95.8],[208.1,50.4]],[[154.5,86.4],[200.8,95.8],[161.5,38.2]],[[154.5,86.4],[195.4,139.6],[200.8,95.8]],[[151.0,132.5],[195.4,139.6],[154.5,86.4]],[[151.0,132.5],[199.1,181.4],[195.4,139.6]],[[150.9,177.4],[199.1,181.4],[151.0,132.5]],[[150.9,177.4],[193.6,224.4],[199.1,181.4]],[[142.4,222.9],[193.6,224.4],[150.9,177.4]],[[142.4,222.9],[190.2,266.5],[193.6,224.4]],[[142.3,267.1],[190.2,266.5],[142.4,222.9]],[[142.3,267.1],[188.4,308.2],[190.2,266.5]],[[137.4,310.6],[188.4,308.2],[142.3,267.1]],[[137.4,310.6],[186.6,349.7],[188.4,308.2]],[[142.4,355.3],[186.6,349.7],[137.4,310.6]],[[142.4,355.3],[197.3,394.0],[186.6,349.7]],[[156.1,403.4],[197.3,394.0],[142.4,355.3]],[[100.6,28.8],[154.5,86.4],[161.5,38.2]],[[95.7,78.6],[154.5,86.4],[100.6,28.8]],[[95.7,78.6],[151.0,132.5],[154.5,86.4]],[[97.3,125.4],[151.0,132.5],[95.7,78.6]],[[97.3,125.4],[150.9,177.4],[151.0,132.5]],[[102.2,172.0],[150.9,177.4],[97.3,125.4]],[[102.2,172.0],[142.4,222.9],[150.9,177.4]],[[100.6,220.1],[142.4,222.9],[102.2,172.0]],[[100.6,220.1],[142.3,267.1],[142.4,222.9]],[[105.6,268.1],[142.3,267.1],[100.6,220.1]],[[105.6,268.1],[137.4,310.6],[142.3,267.1]],[[105.6,316.4],[142.4,355.3],[137.4,310.6]],[[104.0,364.4],[142.4,355.3],[105.6,316.4]],[[104.0,364.4],[156.1,403.4],[142.4,355.3]],[[109.0,414.3],[156.1,403.4],[104.0,364.4]],[[38.6,15.8],[95.7,78.6],[100.6,28.8]],[[35.6,67.8],[95.7,78.6],[38.6,15.8]],[[35.6,67.8],[97.3,125.4],[95.7,78.6]],[[40.2,116.4],[97.3,125.4],[35.6,67.8]],[[40.2,116.4],[102.2,172.0],[97.3,125.4]],[[46.4,165.8],[102.2,172.0],[40.2,116.4]],[[46.4,165.8],[100.6,220.1],[102.2,172.0]],[[49.6,217.0],[100.6,220.1],[46.4,165.8]],[[49.6,217.0],[105.6,268.1],[100.6,220.1]],[[57.8,269.2],[105.6,268.1],[49.6,217.0]],[[57.8,269.2],[346.7,554.7],[57.8,269.3]],[[61.1,322.8],[105.6,316.4],[57.8,269.2]],[[61.1,322.8],[104.0,364.4],[105.6,316.4]],[[59.5,375.8],[104.0,364.4],[61.1,322.8]],[[59.5,375.8],[109.0,414.3],[104.0,364.4]],[[57.8,428.3],[109.0,414.3],[59.5,375.8]],[[-30.9,1.2],[35.6,67.8],[38.6,15.8]],[[-26.9,52.4],[35.6,67.8],[-30.9,1.2]],[[-26.9,52.4],[40.2,116.4],[35.6,67.8]],[[-19.9,103.5],[40.2,116.4],[-26.9,52.4]],[[-19.9,103.5],[46.4,165.8],[40.2,116.4]],[[-14.1,157.3],[46.4,165.8],[-19.9,103.5]],[[-14.1,157.3],[49.6,217.0],[46.4,165.8]],[[-12.7,213.5],[49.6,217.0],[-14.1,157.3]],[[-12.7,213.5],[57.8,269.2],[49.6,217.0]],[[-5.1,270.5],[57.8,269.2],[-12.7,213.5]],[[-5.1,270.5],[61.1,322.8],[57.8,269.2]],[[1.2,329.7],[61.1,322.8],[-5.1,270.5]],[[1.2,329.7],[59.5,375.8],[61.1,322.8]],[[-0.4,388.0],[59.5,375.8],[1.2,329.7]],[[-0.4,388.0],[57.8,428.3],[59.5,375.8]],[[57.8,428.3],[57.7,428.3],[57.7,428.3]],[[289.2,-8.3],[242.4,50.6],[251.8,5.1]],[[280.4,39.3],[242.4,50.6],[289.2,-8.3]],[[280.4,39.3],[235.1,99.5],[242.4,50.6]],[[275.8,91.9],[235.1,99.5],[280.4,39.3]],[[275.8,91.9],[240.1,153.9],[235.1,99.5]],[[275.7,147.4],[240.1,153.9],[275.8,91.9]],[[275.7,147.4],[232.6,205.1],[240.1,153.9]],[[263.7,201.1],[232.6,205.1],[275.7,147.4]],[[263.7,201.1],[227.6,258.6],[232.6,205.1]],[[263.6,258.7],[227.6,258.6],[263.7,201.1]],[[263.6,258.7],[224.8,313.0],[227.6,258.6]],[[256.0,317.6],[224.8,313.0],[263.6,258.7]],[[256.0,317.6],[222.1,368.2],[224.8,313.0]],[[263.7,373.8],[222.1,368.2],[256.0,317.6]],[[263.7,373.8],[237.7,416.3],[222.1,368.2]],[[282.6,421.9],[237.7,416.3],[263.7,373.8]],[[320.6,-33.4],[280.4,39.3],[289.2,-8.3]],[[314.1,19.8],[280.4,39.3],[320.6,-33.4]],[[314.1,19.8],[275.8,91.9],[280.4,39.3]],[[316.3,80.9],[275.8,91.9],[314.1,19.8]],[[316.3,80.9],[275.7,147.4],[275.8,91.9]],[[322.6,142.6],[275.7,147.4],[316.3,80.9]],[[322.6,142.6],[263.7,201.1],[275.7,147.4]],[[320.6,200.3],[263.7,201.1],[322.6,142.6]],[[320.6,200.3],[263.6,258.7],[263.7,201.1]],[[320.6,200.3],[252.1,270.4],[339.0,375.4]],[[256.0,317.6],[256.0,317.6],[256.0,317.6]],[[326.8,315.9],[263.7,373.8],[256.0,317.6]],[[324.9,373.9],[263.7,373.8],[326.8,315.9]],[[324.9,373.9],[282.6,421.9],[263.7,373.8]],[[330.8,427.9],[282.6,421.9],[324.9,373.9]],[[364.4,-56.6],[314.1,19.8],[320.6,-33.4]],[[360.6,2.7],[314.1,19.8],[364.4,-56.6]],[[360.6,2.7],[316.3,80.9],[314.1,19.8]],[[366.5,71.2],[316.3,80.9],[360.6,2.7]],[[366.5,71.2],[322.6,142.6],[316.3,80.9]],[[374.0,137.5],[322.6,142.6],[366.5,71.2]],[[374.0,137.5],[320.6,200.3],[322.6,142.6]],[[377.6,199.1],[320.6,200.3],[374.0,137.5]],[[377.6,199.1],[326.8,258.7],[320.6,200.3]],[[377.6,199.1],[394.5,179.2],[386.1,258.7]],[[389.2,315.5],[326.8,315.9],[386.1,258.7]],[[389.2,315.5],[324.9,373.9],[326.8,315.9]],[[387.7,373.1],[324.9,373.9],[389.2,315.5]],[[387.7,373.1],[330.8,427.9],[324.9,373.9]],[[385.9,431.6],[330.8,427.9],[387.7,373.1]],[[416.0,-83.5],[360.6,2.7],[364.4,-56.6]],[[420.9,-8.1],[360.6,2.7],[416.0,-83.5]],[[420.9,-8.1],[366.5,71.2],[360.6,2.7]],[[428.6,66.6],[366.5,71.2],[420.9,-8.1]],[[428.6,66.6],[374.0,137.5],[366.5,71.2]],[[434.4,134.6],[374.0,137.5],[428.6,66.6]],[[434.4,134.6],[377.6,199.1],[374.0,137.5]],[[435.8,197.2],[377.6,199.1],[434.4,134.6]],[[435.8,197.2],[386.1,258.7],[377.6,199.1]]];
(function(){
var bc=document.getElementById('benchframe'); if(!bc) return;
var bx=bc.getContext('2d');
bx.fillStyle='#06090e'; bx.fillRect(0,0,bc.width,bc.height);
var COLS=['#41ff8e','#5fd0ff','#ffb020','#ff5a6a','#eaffef','#a078ff','#c8c85a','#5ac8a0','#ff8c6e','#78b4e6'];
BENCH_TRIS.forEach(function(t,i){
bx.fillStyle=COLS[i%COLS.length];
bx.globalAlpha=0.55;
bx.beginPath();
bx.moveTo(t[0][0],t[0][1]); bx.lineTo(t[1][0],t[1][1]); bx.lineTo(t[2][0],t[2][1]);
bx.closePath(); bx.fill();
bx.globalAlpha=0.9; bx.strokeStyle='rgba(234,255,239,0.25)'; bx.lineWidth=0.7;
bx.stroke();
});
bx.globalAlpha=1;
})();
/* ---- the warp field: star positions + streaks recovered from trek's
compiled per-primitive payload blocks (screen-space floats) ---- */
var WARP_PTS=[[196.5,174.7],[73.5,277.0],[113.9,474.8],[120.3,347.2],[350.8,358.9],[100.0,438.9],[102.7,474.7],[143.5,56.3],[521.1,111.5],[335.1,400.5],[39.3,353.8],[287.8,323.5],[251.4,554.9],[93.4,246.8],[479.9,300.7],[274.7,347.7],[97.6,465.2],[382.6,241.0],[476.5,80.5],[160.7,418.5],[334.4,218.4],[326.4,553.0],[473.7,587.9],[89.8,371.7],[100.0,127.6],[593.2,335.7],[530.1,583.7],[28.3,420.3],[160.3,499.2],[106.8,237.8],[268.7,370.0],[224.2,219.2],[217.4,448.6],[139.3,256.6],[408.1,493.9],[252.3,282.6],[8.2,586.6],[309.7,583.4],[416.5,337.7],[270.2,215.6],[730.9,473.8],[6.6,364.1],[641.3,69.2],[423.6,565.8],[84.0,313.2],[289.9,488.0],[407.1,350.0],[187.8,61.4],[53.3,340.4],[180.7,406.5],[551.6,104.8],[280.6,464.0],[615.6,412.7],[215.4,192.7],[173.6,99.2],[5.2,313.3],[44.7,238.4],[211.9,472.9],[334.4,52.8],[329.5,248.3],[564.8,232.5],[91.9,450.9],[105.0,2.9],[366.1,113.9],[291.5,368.5],[266.1,254.2],[357.0,201.0],[483.0,174.3],[529.5,34.7],[29.2,240.2],[258.7,12.5],[664.8,238.8],[206.6,337.8],[591.0,419.0],[243.1,220.4],[351.8,300.5],[262.0,390.1],[55.0,544.2],[256.1,200.8],[446.6,434.5],[471.1,416.2],[437.2,124.0],[700.2,381.8],[196.6,277.9],[61.1,316.4],[141.9,576.4],[171.4,516.7],[264.4,444.2],[312.7,574.3],[454.3,345.4],[507.3,547.9],[140.8,301.1],[283.3,140.1],[186.6,218.3],[224.1,2.1],[36.1,331.8],[363.8,257.2],[546.4,375.3],[466.2,493.0],[78.2,307.8],[79.7,550.5],[299.3,277.5],[442.7,267.7],[442.7,461.9],[474.9,543.9],[353.3,177.4],[221.3,526.3],[359.7,381.9],[105.0,343.5],[281.6,194.2],[503.9,422.3],[475.1,407.9],[473.7,203.8],[293.6,222.1],[578.8,352.3],[120.9,577.2],[56.9,442.2],[369.5,368.0],[60.2,123.9],[370.9,394.4],[86.3,262.0],[236.9,166.5],[137.0,257.6],[209.9,572.5],[393.1,74.3],[271.4,289.7],[414.7,560.1],[549.8,156.7],[304.6,466.1],[133.0,385.4],[281.6,586.3],[574.1,124.0],[169.6,193.1],[88.4,388.4],[92.3,136.0],[620.2,559.1],[733.7,173.2],[481.8,247.5],[501.2,277.0],[223.4,494.1],[403.7,264.9],[248.9,259.6],[223.6,480.0],[594.2,446.4],[266.3,327.8],[586.8,101.1],[673.0,67.1],[554.5,550.2],[42.7,265.6],[375.2,209.5],[491.0,344.8],[7.8,453.0],[389.1,316.0],[465.7,535.9],[537.6,388.0],[530.8,525.5],[744.7,353.7],[676.1,184.2],[153.8,173.2],[451.5,351.5],[416.8,254.6],[560.5,219.5],[52.6,530.0],[621.6,126.7],[199.3,329.9],[152.3,351.4],[592.3,315.7],[598.4,280.3],[144.0,322.3],[153.5,243.5],[125.4,55.6],[619.1,78.7],[100.4,531.9],[9.1,480.6],[292.8,556.8],[29.5,360.0],[307.4,512.8],[324.5,487.0],[167.5,137.6],[482.1,263.5],[520.6,496.8],[485.1,154.1],[135.6,371.2],[369.8,262.5],[563.6,425.9],[553.7,379.0],[106.3,54.7],[208.8,176.5],[437.1,272.8],[109.5,345.7],[520.9,318.4],[156.9,539.9],[433.7,196.8],[44.5,384.6],[25.4,484.0],[511.8,511.6],[453.1,564.0],[448.3,534.4],[236.8,369.7],[263.6,435.0],[212.2,514.6],[359.4,390.3],[192.0,508.4],[233.2,595.6],[381.4,294.1],[289.1,136.5],[455.7,97.3],[464.1,116.0],[690.2,174.3],[215.9,547.4],[184.3,568.8],[317.7,567.7],[505.4,469.0],[422.1,347.9],[472.6,235.2],[579.0,277.0],[521.7,5.8],[223.3,204.7],[224.5,474.7],[163.8,563.9],[366.0,373.8],[89.1,234.0],[458.4,488.6],[331.7,375.1],[452.8,7.6],[436.2,67.3],[121.1,284.0],[481.7,496.3],[199.2,393.1],[37.8,220.0],[163.6,158.9],[187.1,566.6],[558.4,441.0],[662.0,418.0],[334.4,472.2],[183.4,226.5],[235.5,181.7],[291.3,471.5],[19.2,330.1],[374.6,15.7],[303.1,571.5],[491.8,420.7],[489.5,238.9],[547.0,253.0],[233.0,326.7],[374.7,258.2],[393.3,364.4],[232.5,311.9],[330.4,318.4],[516.5,335.8],[337.1,120.5],[36.7,362.1],[533.1,404.5],[555.4,109.3],[268.1,158.9],[196.5,482.6],[186.1,390.8],[462.0,245.4],[356.6,92.5],[310.3,495.0],[259.6,551.2],[543.3,503.9],[26.7,164.1],[22.3,470.7],[127.2,351.5],[517.7,240.7],[270.8,130.4],[639.1,187.3],[402.4,535.3],[696.5,169.1],[322.6,117.5],[70.3,238.6],[614.4,332.2],[334.4,29.7],[757.0,62.5],[739.8,538.3],[476.6,198.0],[424.5,142.8],[309.5,247.3],[428.5,237.9],[533.4,482.8],[391.7,585.8],[632.0,315.4],[514.7,334.3],[19.6,367.4],[414.1,537.2],[585.7,354.3],[165.6,485.9],[333.1,286.6],[323.6,590.1],[343.1,33.8],[44.8,484.3],[421.3,313.3],[231.5,68.5],[469.3,517.1],[587.8,421.2],[497.0,338.7],[117.1,462.3],[205.4,228.7],[150.4,372.2],[280.2,372.3],[189.1,20.7],[346.9,274.7],[351.4,254.9],[28.5,361.5],[36.0,137.3],[132.3,353.8],[365.2,304.9],[475.0,404.6],[430.1,308.5],[445.6,59.5],[559.0,428.6],[298.9,154.5],[697.9,179.8],[486.3,300.0],[69.1,103.0],[16.2,178.0],[292.3,34.3],[240.7,179.4],[456.2,421.9],[111.8,58.7],[457.9,556.8],[109.3,447.9],[14.0,589.3],[102.7,52.9],[185.0,431.4],[63.4,273.4],[11.7,523.4],[269.2,572.9],[504.6,393.5],[518.1,512.8],[692.4,139.5],[81.2,467.9],[15.6,511.0],[378.5,33.7],[295.6,52.6],[296.2,167.0],[371.0,586.7],[127.0,531.6],[388.1,564.7],[527.6,595.7],[687.9,404.1],[542.7,367.5],[407.9,458.3],[339.1,550.6],[164.6,509.1],[359.8,569.6],[252.2,420.7],[237.8,198.6],[455.9,65.6],[115.0,475.2],[349.6,36.9],[493.5,242.2],[350.0,141.8],[260.0,470.2],[76.3,453.5],[374.1,195.7],[22.4,205.3],[151.0,227.5],[607.0,135.5],[762.1,310.5],[644.8,242.7],[149.8,287.5],[515.3,71.1],[55.6,441.5],[385.7,484.8],[48.8,292.0],[325.5,86.8],[552.2,12.2],[704.6,509.8],[410.5,309.1],[733.1,162.6],[111.1,13.9],[563.9,335.9],[230.1,100.9],[638.4,308.2],[429.5,533.8],[201.9,237.9],[507.9,523.0],[79.3,333.2],[595.8,557.4],[404.5,528.0],[376.4,368.6],[620.3,70.4],[392.4,595.1],[155.0,380.9],[603.5,350.7],[313.1,510.5],[350.9,149.9],[333.4,543.3],[95.4,425.8],[368.0,544.3],[182.6,323.6],[236.0,472.3],[491.6,533.1],[194.7,521.2],[663.2,445.1],[218.3,312.8],[295.6,467.2],[90.8,561.6],[28.9,416.9],[293.2,27.5],[437.1,386.7],[442.3,360.3],[629.0,202.8],[557.0,129.9],[749.3,177.2],[229.6,277.3],[87.9,439.4],[91.2,275.6],[515.0,390.7],[241.4,465.3],[76.6,582.5],[414.4,346.0],[427.8,427.1],[418.7,207.4],[576.2,356.8],[407.4,95.1],[693.1,84.1],[355.8,53.4],[73.7,344.1],[148.1,447.2],[220.6,22.3],[371.4,413.4],[163.2,400.9],[389.6,210.0],[542.1,334.1],[499.3,116.5],[126.7,567.5],[182.1,321.8],[225.3,271.6],[264.2,231.3],[53.7,376.7]];
var WARP_SEGS=[[196.5,174.7,485.3,268.5],[73.5,277.0,469.5,268.5],[113.9,474.8,508.4,268.5],[120.3,347.2,452.0,268.5],[350.8,358.9,541.3,268.5],[100.0,438.9,567.0,268.5],[102.7,474.7,642.4,268.5],[335.1,400.5,487.6,268.5],[39.3,353.8,581.0,268.5],[287.8,323.5,619.1,268.5],[251.4,554.9,397.1,268.5],[93.4,246.8,535.7,268.5],[479.9,300.7,668.6,268.5],[274.7,347.7,650.0,268.5],[97.6,465.2,609.1,268.5],[382.6,241.0,575.7,268.5],[476.5,80.5,415.7,268.5],[334.4,218.4,993.2,268.5],[326.4,553.0,468.2,268.5],[473.7,587.9,759.0,268.5],[89.8,371.7,577.2,268.5],[100.0,127.6,962.8,268.5],[593.2,335.7,993.1,268.5],[530.1,583.7,310.6,268.5],[28.3,420.3,541.2,268.5],[160.3,499.2,486.4,268.5],[106.8,237.8,787.0,268.5],[268.7,370.0,512.7,268.5],[224.2,219.2,378.0,268.5],[217.4,448.6,619.4,268.5],[408.1,493.9,736.4,268.5],[252.3,282.6,457.5,268.5],[8.2,586.6,656.4,268.5],[309.7,583.4,425.4,268.5],[416.5,337.7,514.7,268.5],[270.2,215.6,533.3,268.5],[730.9,473.8,805.2,268.5],[423.6,565.8,427.4,268.5],[84.0,313.2,460.8,268.5],[289.9,488.0,514.0,268.5],[407.1,350.0,482.7,268.5],[187.8,61.4,657.0,268.5],[53.3,340.4,812.8,268.5],[180.7,406.5,484.0,268.5],[551.6,104.8,490.4,268.5],[280.6,464.0,341.2,268.5],[615.6,412.7,376.7,268.5],[215.4,192.7,398.9,268.5],[173.6,99.2,435.5,268.5],[5.2,313.3,612.8,268.5],[44.7,238.4,578.9,268.5],[211.9,472.9,590.8,268.5],[334.4,52.8,405.8,268.5],[329.5,248.3,423.2,268.5],[564.8,232.5,481.7,268.5],[91.9,450.9,665.9,268.5],[105.0,2.9,607.9,268.5],[366.1,113.9,766.6,268.5],[291.5,368.5,401.1,268.5],[266.1,254.2,661.7,268.5],[357.0,201.0,586.7,268.5],[483.0,174.3,658.7,268.5],[529.5,34.7,593.6,268.5],[29.2,240.2,741.8,268.5],[258.7,12.5,856.2,268.5],[206.6,337.8,235.6,268.5],[591.0,419.0,444.6,268.5],[243.1,220.4,430.7,268.5],[351.8,300.5,677.4,268.5],[262.0,390.1,401.6,268.5],[55.0,544.2,760.5,268.5],[256.1,200.8,497.2,268.5],[446.6,434.5,994.6,268.5],[471.1,416.2,779.4,268.5],[437.2,124.0,791.7,268.5],[700.2,381.8,328.2,268.5],[196.6,277.9,437.7,268.5],[61.1,316.4,932.5,268.5],[141.9,576.4,687.4,268.5],[171.4,516.7,415.2,268.5],[264.4,444.2,357.8,268.5],[312.7,574.3,517.0,268.5],[454.3,345.4,739.8,268.5],[507.3,547.9,434.7,268.5],[140.8,301.1,591.7,268.5],[283.3,140.1,503.3,268.5],[186.6,218.3,801.3,268.5],[224.1,2.1,783.5,268.5],[36.1,331.8,860.8,268.5],[363.8,257.2,568.6,268.5],[466.2,493.0,412.0,268.5],[78.2,307.8,490.8,268.5],[79.7,550.5,627.1,268.5],[299.3,277.5,644.4,268.5],[442.7,267.7,486.0,268.5],[442.7,461.9,680.7,268.5],[474.9,543.9,447.7,268.5],[353.3,177.4,587.6,268.5],[221.3,526.3,582.4,268.5],[359.7,381.9,531.4,268.5],[105.0,343.5,783.4,268.5],[281.6,194.2,968.2,268.5],[503.9,422.3,438.9,268.5],[475.1,407.9,617.0,268.5],[473.7,203.8,647.4,268.5],[293.6,222.1,409.1,268.5],[578.8,352.3,464.4,268.5],[120.9,577.2,401.1,268.5],[56.9,442.2,555.3,268.5],[369.5,368.0,456.4,268.5],[60.2,123.9,629.9,268.5],[370.9,394.4,423.9,268.5],[86.3,262.0,917.1,268.5],[137.0,257.6,285.9,268.5],[209.9,572.5,435.1,268.5],[393.1,74.3,709.3,268.5],[414.7,560.1,454.8,268.5],[549.8,156.7,440.8,268.5],[304.6,466.1,432.9,268.5],[133.0,385.4,494.2,268.5],[281.6,586.3,387.7,268.5],[574.1,124.0,408.1,268.5],[169.6,193.1,627.6,268.5],[88.4,388.4,674.2,268.5],[92.3,136.0,449.0,268.5],[620.2,559.1,700.9,268.5],[481.8,247.5,617.8,268.5],[501.2,277.0,465.0,268.5],[223.4,494.1,487.7,268.5],[403.7,264.9,702.9,268.5],[248.9,259.6,681.5,268.5],[223.6,480.0,674.1,268.5],[594.2,446.4,686.5,268.5],[266.3,327.8,568.4,268.5],[673.0,67.1,591.7,268.5],[554.5,550.2,402.7,268.5],[42.7,265.6,617.9,268.5],[375.2,209.5,635.1,268.5],[491.0,344.8,433.3,268.5],[7.8,453.0,893.4,268.5],[389.1,316.0,580.4,268.5],[465.7,535.9,556.7,268.5],[537.6,388.0,635.1,268.5],[153.8,173.2,527.5,268.5],[451.5,351.5,456.4,268.5],[560.5,219.5,704.6,268.5],[52.6,530.0,395.9,268.5],[621.6,126.7,695.4,268.5],[199.3,329.9,248.2,268.5],[152.3,351.4,624.1,268.5],[592.3,315.7,398.2,268.5],[598.4,280.3,402.9,268.5],[144.0,322.3,712.1,268.5],[153.5,243.5,893.7,268.5],[125.4,55.6,559.8,268.5],[619.1,78.7,574.2,268.5],[100.4,531.9,549.8,268.5],[9.1,480.6,585.8,268.5],[292.8,556.8,847.0,268.5],[307.4,512.8,437.9,268.5],[324.5,487.0,410.0,268.5],[167.5,137.6,889.3,268.5],[482.1,263.5,650.7,268.5],[520.6,496.8,641.3,268.5],[485.1,154.1,483.0,268.5],[135.6,371.2,486.4,268.5],[369.8,262.5,555.6,268.5],[563.6,425.9,684.8,268.5],[553.7,379.0,448.3,268.5],[106.3,54.7,637.3,268.5],[208.8,176.5,654.0,268.5],[437.1,272.8,991.3,268.5],[109.5,345.7,643.2,268.5],[520.9,318.4,403.6,268.5],[156.9,539.9,401.2,268.5],[433.7,196.8,684.3,268.5],[25.4,484.0,627.7,268.5],[511.8,511.6,708.4,268.5],[453.1,564.0,345.1,268.5],[448.3,534.4,385.1,268.5],[236.8,369.7,281.3,268.5],[263.6,435.0,460.5,268.5],[212.2,514.6,422.0,268.5],[359.4,390.3,430.8,268.5],[192.0,508.4,559.0,268.5],[233.2,595.6,610.3,268.5],[381.4,294.1,596.9,268.5],[289.1,136.5,853.0,268.5],[455.7,97.3,406.3,268.5],[690.2,174.3,597.2,268.5],[215.9,547.4,643.8,268.5],[184.3,568.8,724.3,268.5],[317.7,567.7,629.7,268.5],[505.4,469.0,712.6,268.5],[422.1,347.9,472.6,268.5],[472.6,235.2,544.9,268.5],[579.0,277.0,281.7,268.5],[521.7,5.8,714.6,268.5],[223.3,204.7,429.2,268.5],[224.5,474.7,629.9,268.5],[163.8,563.9,532.9,268.5],[366.0,373.8,401.0,268.5],[89.1,234.0,654.5,268.5],[458.4,488.6,539.6,268.5],[452.8,7.6,500.6,268.5],[436.2,67.3,701.0,268.5],[121.1,284.0,469.1,268.5],[481.7,496.3,507.4,268.5],[199.2,393.1,576.5,268.5],[163.6,158.9,813.7,268.5],[187.1,566.6,506.1,268.5],[558.4,441.0,442.3,268.5],[662.0,418.0,704.1,268.5],[334.4,472.2,562.0,268.5],[183.4,226.5,541.8,268.5],[235.5,181.7,584.7,268.5],[291.3,471.5,964.9,268.5],[19.2,330.1,771.4,268.5],[374.6,15.7,576.5,268.5],[491.8,420.7,497.0,268.5],[489.5,238.9,513.2,268.5],[547.0,253.0,522.3,268.5],[233.0,326.7,595.2,268.5],[374.7,258.2,384.8,268.5],[393.3,364.4,500.5,268.5],[232.5,311.9,928.5,268.5],[330.4,318.4,486.8,268.5],[516.5,335.8,354.5,268.5],[337.1,120.5,402.5,268.5],[36.7,362.1,349.5,268.5],[533.1,404.5,882.9,268.5],[555.4,109.3,762.0,268.5],[268.1,158.9,650.6,268.5],[196.5,482.6,407.5,268.5],[186.1,390.8,517.0,268.5],[462.0,245.4,426.6,268.5],[356.6,92.5,497.5,268.5],[310.3,495.0,908.2,268.5],[259.6,551.2,880.7,268.5],[543.3,503.9,642.4,268.5],[26.7,164.1,504.0,268.5],[22.3,470.7,667.8,268.5],[127.2,351.5,437.9,268.5],[517.7,240.7,710.7,268.5],[402.4,535.3,464.1,268.5],[696.5,169.1,808.2,268.5],[322.6,117.5,466.1,268.5],[70.3,238.6,558.9,268.5],[614.4,332.2,953.5,268.5],[757.0,62.5,725.2,268.5],[739.8,538.3,784.4,268.5],[476.6,198.0,644.8,268.5],[424.5,142.8,581.9,268.5],[309.5,247.3,755.7,268.5],[428.5,237.9,544.3,268.5],[533.4,482.8,965.3,268.5],[391.7,585.8,282.7,268.5],[632.0,315.4,563.7,268.5],[514.7,334.3,473.4,268.5],[19.6,367.4,547.2,268.5],[414.1,537.2,728.4,268.5],[585.7,354.3,434.2,268.5],[165.6,485.9,731.1,268.5],[333.1,286.6,546.6,268.5],[323.6,590.1,584.0,268.5],[343.1,33.8,597.1,268.5],[44.8,484.3,689.5,268.5],[421.3,313.3,511.8,268.5],[231.5,68.5,487.5,268.5],[469.3,517.1,411.2,268.5],[587.8,421.2,282.5,268.5],[497.0,338.7,365.9,268.5],[117.1,462.3,537.4,268.5],[205.4,228.7,715.4,268.5],[150.4,372.2,638.5,268.5],[280.2,372.3,662.8,268.5],[189.1,20.7,690.6,268.5],[351.4,254.9,558.3,268.5],[28.5,361.5,531.3,268.5],[36.0,137.3,586.9,268.5],[132.3,353.8,567.8,268.5],[365.2,304.9,662.9,268.5],[475.0,404.6,454.5,268.5],[430.1,308.5,409.2,268.5],[445.6,59.5,718.4,268.5],[298.9,154.5,615.7,268.5],[697.9,179.8,945.6,268.5],[486.3,300.0,420.3,268.5],[69.1,103.0,842.2,268.5],[16.2,178.0,807.8,268.5],[456.2,421.9,677.9,268.5],[111.8,58.7,419.3,268.5],[457.9,556.8,498.5,268.5],[109.3,447.9,730.4,268.5],[102.7,52.9,529.9,268.5],[63.4,273.4,708.1,268.5],[269.2,572.9,551.9,268.5],[504.6,393.5,703.2,268.5],[518.1,512.8,776.0,268.5],[692.4,139.5,363.4,268.5],[81.2,467.9,845.8,268.5],[15.6,511.0,534.8,268.5],[295.6,52.6,667.1,268.5],[296.2,167.0,516.6,268.5],[371.0,586.7,463.2,268.5],[127.0,531.6,492.6,268.5],[388.1,564.7,582.3,268.5],[527.6,595.7,846.1,268.5],[542.7,367.5,568.2,268.5],[407.9,458.3,405.0,268.5],[339.1,550.6,491.6,268.5],[164.6,509.1,564.6,268.5],[359.8,569.6,676.7,268.5],[252.2,420.7,475.5,268.5],[237.8,198.6,913.0,268.5],[455.9,65.6,292.5,268.5],[115.0,475.2,871.2,268.5],[493.5,242.2,597.9,268.5],[350.0,141.8,443.3,268.5],[260.0,470.2,448.0,268.5],[76.3,453.5,586.2,268.5],[374.1,195.7,517.0,268.5],[151.0,227.5,781.8,268.5],[762.1,310.5,457.1,268.5],[644.8,242.7,649.5,268.5],[149.8,287.5,538.1,268.5],[515.3,71.1,540.3,268.5],[55.6,441.5,643.8,268.5],[385.7,484.8,415.4,268.5],[48.8,292.0,460.0,268.5],[325.5,86.8,573.7,268.5],[704.6,509.8,572.6,268.5],[733.1,162.6,364.7,268.5],[111.1,13.9,567.2,268.5],[563.9,335.9,434.5,268.5],[230.1,100.9,641.6,268.5],[429.5,533.8,244.5,268.5],[201.9,237.9,830.3,268.5],[507.9,523.0,397.8,268.5],[79.3,333.2,393.9,268.5],[595.8,557.4,434.7,268.5],[404.5,528.0,253.3,268.5],[376.4,368.6,551.3,268.5],[392.4,595.1,477.6,268.5],[155.0,380.9,694.3,268.5],[603.5,350.7,545.9,268.5],[313.1,510.5,931.7,268.5],[350.9,149.9,547.4,268.5],[333.4,543.3,400.8,268.5],[95.4,425.8,393.2,268.5],[368.0,544.3,404.7,268.5],[182.6,323.6,613.6,268.5],[236.0,472.3,543.7,268.5],[491.6,533.1,667.2,268.5],[194.7,521.2,460.4,268.5],[663.2,445.1,568.7,268.5],[218.3,312.8,477.2,268.5],[295.6,467.2,495.0,268.5],[90.8,561.6,672.4,268.5],[293.2,27.5,635.2,268.5],[442.3,360.3,562.2,268.5],[629.0,202.8,625.5,268.5],[557.0,129.9,391.6,268.5],[749.3,177.2,744.3,268.5],[229.6,277.3,466.5,268.5],[87.9,439.4,625.0,268.5],[91.2,275.6,572.8,268.5],[515.0,390.7,592.5,268.5],[241.4,465.3,457.6,268.5],[76.6,582.5,531.6,268.5],[414.4,346.0,635.1,268.5],[427.8,427.1,526.3,268.5],[418.7,207.4,422.4,268.5],[576.2,356.8,414.0,268.5],[407.4,95.1,910.3,268.5],[355.8,53.4,596.9,268.5],[73.7,344.1,556.1,268.5],[148.1,447.2,464.9,268.5],[220.6,22.3,595.4,268.5],[371.4,413.4,688.9,268.5],[163.2,400.9,406.3,268.5],[389.6,210.0,261.7,268.5],[542.1,334.1,429.7,268.5],[499.3,116.5,474.2,268.5],[126.7,567.5,656.4,268.5],[182.1,321.8,523.1,268.5],[225.3,271.6,493.2,268.5],[264.2,231.3,552.2,268.5],[53.7,376.7,989.7,268.5]];
(function(){
var tc=document.getElementById('trekframe'); if(!tc) return;
var tx=tc.getContext('2d'), W=tc.width, H=tc.height;
tx.fillStyle='#04070c'; tx.fillRect(0,0,W,H);
// streaks
tx.lineWidth=0.8;
WARP_SEGS.forEach(function(s){
var g=tx.createLinearGradient(s[0],s[1],s[2],s[3]);
g.addColorStop(0,'rgba(180,230,255,0.75)');
g.addColorStop(1,'rgba(40,110,180,0.08)');
tx.strokeStyle=g;
tx.beginPath(); tx.moveTo(s[0],s[1]); tx.lineTo(s[2],s[3]); tx.stroke();
});
// stars
WARP_PTS.forEach(function(p){
tx.fillStyle='rgba(235,250,255,0.95)';
tx.fillRect(p[0]-0.5,p[1]-0.5,1.6,1.6);
tx.fillStyle='rgba(120,180,230,0.35)';
tx.fillRect(p[0]-1.5,p[1]-1.5,3.5,3.5);
});
})();
/* ---- the Klingon scene: same recovery, klngvid capture ---- */
var KLNG_PTS=[[634.1,336.3],[655.5,143.7],[633.2,120.7],[742.5,406.4],[731.1,409.1],[685.4,430.7],[763.6,87.0],[713.4,428.6],[754.3,516.8],[559.1,507.4],[632.1,378.5],[729.6,493.3],[744.2,139.9],[708.8,461.7],[716.8,225.7],[625.4,209.7],[690.5,514.9],[665.5,560.0],[755.0,303.2],[696.3,417.4],[686.0,505.1],[694.7,384.9],[740.5,47.5],[733.5,295.4],[750.3,15.0],[709.3,521.6],[306.5,131.8],[523.3,30.9],[608.5,400.1],[710.5,212.0],[737.3,70.4],[680.3,381.3],[759.0,494.0],[735.2,429.2],[539.6,391.9],[159.3,478.9],[551.3,181.5],[221.2,246.9],[728.5,516.6],[674.6,420.1],[458.0,383.2],[630.6,395.1],[447.9,388.9],[339.3,427.6],[601.5,64.8],[547.8,404.2],[421.2,424.0],[602.9,69.6],[682.6,303.3],[501.6,579.4],[658.2,475.4],[20.5,582.0],[643.9,513.8],[620.1,297.1],[546.2,301.7],[495.6,300.0],[207.3,326.4],[465.0,536.9],[625.6,39.4],[635.8,389.8],[382.2,478.9],[378.2,14.5],[578.0,231.3],[635.6,379.8],[578.0,565.2],[340.2,582.1],[685.5,536.7],[481.1,484.7],[560.5,61.5],[293.6,119.6],[211.3,448.9],[576.1,220.1],[25.2,309.4],[658.6,482.1],[664.0,399.7],[458.2,541.4],[673.8,154.1],[590.9,348.1],[681.4,90.3],[610.4,268.0],[317.0,549.9],[407.3,486.8],[442.2,80.8],[500.7,597.4],[338.5,142.9],[402.8,46.7],[544.0,82.7],[482.8,5.5],[539.8,103.7],[767.5,322.4],[534.7,547.2],[300.9,441.2],[586.1,238.1],[575.0,548.3],[545.6,239.1],[525.0,9.8]];
var KLNG_SEGS=[[634.1,336.3,452.4,268.5],[655.5,143.7,459.2,268.5],[633.2,120.7,487.3,268.5],[742.5,406.4,429.0,268.5],[731.1,409.1,420.4,268.5],[685.4,430.7,439.1,268.5],[763.6,87.0,472.1,268.5],[713.4,428.6,416.3,268.5],[754.3,516.8,409.6,268.5],[559.1,507.4,479.0,268.5],[632.1,378.5,458.0,268.5],[729.6,493.3,403.5,268.5],[744.2,139.9,566.1,268.5],[708.8,461.7,407.5,268.5],[716.8,225.7,420.0,268.5],[625.4,209.7,439.4,268.5],[690.5,514.9,411.7,268.5],[665.5,560.0,536.9,268.5],[755.0,303.2,526.9,268.5],[696.3,417.4,588.4,268.5],[686.0,505.1,550.5,268.5],[694.7,384.9,567.3,268.5],[740.5,47.5,512.5,268.5],[733.5,295.4,740.1,268.5],[750.3,15.0,537.7,268.5],[709.3,521.6,682.6,268.5],[306.5,131.8,725.5,268.5],[523.3,30.9,530.3,268.5],[608.5,400.1,810.0,268.5],[710.5,212.0,629.1,268.5],[737.3,70.4,731.9,268.5],[680.3,381.3,693.8,268.5],[759.0,494.0,429.4,268.5],[735.2,429.2,579.1,268.5],[539.6,391.9,844.9,268.5],[159.3,478.9,843.5,268.5],[551.3,181.5,928.0,268.5],[221.2,246.9,931.0,268.5],[728.5,516.6,984.5,268.5],[674.6,420.1,801.6,268.5],[458.0,383.2,843.1,268.5],[630.6,395.1,727.7,268.5],[447.9,388.9,812.6,268.5],[339.3,427.6,880.1,268.5],[601.5,64.8,700.0,268.5],[547.8,404.2,860.3,268.5],[421.2,424.0,798.5,268.5],[602.9,69.6,994.4,268.5],[682.6,303.3,633.9,268.5],[501.6,579.4,775.9,268.5],[643.9,513.8,787.7,268.5],[620.1,297.1,761.4,268.5],[495.6,300.0,929.0,268.5],[207.3,326.4,953.2,268.5],[465.0,536.9,755.8,268.5],[625.6,39.4,757.9,268.5],[635.8,389.8,926.6,268.5],[382.2,478.9,745.5,268.5],[378.2,14.5,790.5,268.5],[578.0,565.2,889.0,268.5],[340.2,582.1,760.3,268.5],[458.2,541.4,972.6,268.5]];
(function(){
var kc=document.getElementById('klngframe'); if(!kc) return;
var kx=kc.getContext('2d'), W=kc.width, H=kc.height;
kx.fillStyle='#0a0608'; kx.fillRect(0,0,W,H);
kx.lineWidth=0.8;
KLNG_SEGS.forEach(function(s){
var g=kx.createLinearGradient(s[0],s[1],s[2],s[3]);
g.addColorStop(0,'rgba(255,140,110,0.8)');
g.addColorStop(1,'rgba(160,50,50,0.1)');
kx.strokeStyle=g;
kx.beginPath(); kx.moveTo(s[0],s[1]); kx.lineTo(s[2],s[3]); kx.stroke();
});
KLNG_PTS.forEach(function(p){
kx.fillStyle='rgba(255,245,235,0.95)';
kx.fillRect(p[0]-0.5,p[1]-0.5,1.6,1.6);
kx.fillStyle='rgba(220,140,110,0.35)';
kx.fillRect(p[0]-1.5,p[1]-1.5,3.5,3.5);
});
})();
/* ---- IGC tile footprint: which 64x128 tiles the array lit for the object ---- */
var TILES={ntx:10,nty:5,touched:[[0,2],[1,1],[1,2],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3],[4,1],[4,2],[4,3],[5,1],[5,2],[6,1],[6,2],[7,1],[8,1]]};
(function(){
var tc=document.getElementById('tiles'), tx=tc.getContext('2d');
var W=tc.width, H=tc.height, NX=TILES.ntx, NY=TILES.nty;
var lit={}; TILES.touched.forEach(function(t){lit[t[0]+'_'+t[1]]=1;});
var gap=3;
var cw=(H-2*gap)/(NY*2); // enforce 64:128 -> cellH = 2*cellW
var ch=cw*2;
var gw=NX*cw, gh=NY*ch;
var ox=(W-gw)/2, oy=(H-gh)/2;
tx.clearRect(0,0,W,H);
for(var ty2=0;ty2<NY;ty2++)for(var tx2=0;tx2<NX;tx2++){
var px=ox+tx2*cw, py=oy+ty2*ch;
var on=lit[tx2+'_'+ty2];
if(on){
tx.fillStyle='rgba(65,255,142,0.16)'; tx.fillRect(px,py,cw-gap,ch-gap);
tx.strokeStyle='rgba(65,255,142,0.85)'; tx.lineWidth=1.2;
tx.strokeRect(px+0.5,py+0.5,cw-gap-1,ch-gap-1);
tx.fillStyle='rgba(65,255,142,0.9)';
tx.fillRect(px+cw/2-1.5,py+ch/2-1.5,3,3);
} else {
tx.strokeStyle='rgba(84,105,122,0.28)'; tx.lineWidth=1;
tx.strokeRect(px+0.5,py+0.5,cw-gap-1,ch-gap-1);
}
}
// caption-ish label of tile dimensions along the top-left cell
tx.fillStyle='rgba(95,208,255,0.7)'; tx.font='9px ui-monospace,monospace';
tx.fillText('64×128',ox+2,oy-4>8?oy-4:10);
})();
})();
</script>