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>
849 lines
77 KiB
HTML
849 lines
77 KiB
HTML
<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 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 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>
|
||
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>
|
||
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 66–236</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 (§03–05) 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 Newton–Raphson 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 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>
|