13 Layers.
This commit is contained in:
@@ -133,8 +133,19 @@ const p = bcAssembleParams();
|
||||
const scene = generateScene(p);
|
||||
const bare = { ...scene, instrument: null, artifacts: null, media: null };
|
||||
|
||||
const tracksImg = renderSVG({ ...bare, tracks: scene.tracks, shock: null },
|
||||
{ ...p, emit: ['bubble'], originX: VERTEX.x, originY: VERTEX.y, sceneZoom: TRACE_ZOOM }, W);
|
||||
const eventBase = { ...p, emit: ['bubble'], originX: VERTEX.x, originY: VERTEX.y, sceneZoom: TRACE_ZOOM };
|
||||
// TIME-SPLIT into BIRTH / LIFE / DEATH by b.life (arc-length fraction from the
|
||||
// vertex). Because the β-law drives colour BY proper time (magenta birth →
|
||||
// violet death), this splits the event by hue too: the event ages in depth.
|
||||
// Later time = nearer. Scatter spreads the dense vertex knot across bands.
|
||||
const SCATTER = 0.32;
|
||||
const BANDS = { birth: [0.0, 0.45], life: [0.45, 0.78], death: [0.78, 1.001] };
|
||||
const evt = (band) => renderSVG({ ...bare, tracks: scene.tracks, shock: null },
|
||||
band ? { ...eventBase, bubbleBand: band, bubbleScatter: SCATTER } : eventBase, W);
|
||||
const tracksAll = evt(null); // glow = whole event, blurred deep
|
||||
const tracksBirth = evt(BANDS.birth);
|
||||
const tracksLife = evt(BANDS.life);
|
||||
const tracksDeath = evt(BANDS.death);
|
||||
const diskImg = renderSVG({ ...bare, tracks: [], shock: scene.shock },
|
||||
{ ...p, emit: ['disk'], originX: SUN.x, originY: SUN.y }, W);
|
||||
const sheets = deck();
|
||||
@@ -150,17 +161,26 @@ const IMG = (svg) => `<image x="0" y="0" width="${W}" height="${H}" href="${data
|
||||
const frontGlass = planeSVG(
|
||||
`${IMG(aging)}<g opacity="0.85">${IMG(grain)}</g>`);
|
||||
|
||||
// DOF blur is tuned per MARK TYPE: thin wave-lines dissolve under blur far
|
||||
// faster than dense bubbles, so line-art planes get very little (parallax
|
||||
// offset carries their depth). Bubbles/glow tolerate more.
|
||||
// SEA on THREE individual plates (own internal parallax), FULL opacity so the
|
||||
// rearmost (sea-far) stays visible — real air gaps carry depth, not opacity
|
||||
// dimming. Thin wave-lines get minimal blur (they dissolve under it).
|
||||
//
|
||||
// ORDER (deep → near): sun · event-GLOW (submerged afterimage) · SEA ×3 ·
|
||||
// event BIRTH/LIFE/DEATH · glass. The sea is a VEIL between the event's ghost
|
||||
// (its light, held under the water) and its body (the fresh bubbles above it).
|
||||
// sun +50%: a 0.7-opacity second pass of the disk lifts the faint hollow sun
|
||||
// (~+50% across its range) without a hard opacity clip. sea −30%: dim 1.0→0.7.
|
||||
const sunPlane = planeSVG(`${IMG(diskImg)}<g opacity="0.7">${IMG(diskImg)}</g>`);
|
||||
const PLANES = [
|
||||
{ id: 'sun', file: 'plane-1-sun.svg', z: 0, blur: 1.4, dim: 0.95, white: 'none', glow: 1.0, svg: planeSVG(IMG(diskImg)) },
|
||||
{ id: 'sea-far', file: 'plane-2-sea-far.svg', z: 9, blur: 0.9, dim: 1.0, white: 'none', glow: 0.7, svg: planeSVG(IMG(sheets[0])) },
|
||||
{ id: 'sea-mid', file: 'plane-3-sea-mid.svg', z: 15, blur: 0.5, dim: 1.0, white: 'trace', glow: 0.7, svg: planeSVG(IMG(sheets[1])) },
|
||||
{ id: 'sea-near', file: 'plane-4-sea-near.svg', z: 18, blur: 0.2, dim: 1.0, white: 'trace', glow: 0.7, svg: planeSVG(IMG(sheets[2])) },
|
||||
{ id: 'event-halo', file: 'plane-5-event-halo.svg', z: 27, blur: 2.4, dim: 0.85, white: 'none', glow: 0.9, svg: planeSVG(IMG(tracksImg)) },
|
||||
{ id: 'event-core', file: 'plane-6-event-core.svg', z: 30, blur: 0.0, dim: 1.0, white: 'under', glow: 0.5, svg: planeSVG(IMG(tracksImg)) },
|
||||
{ id: 'front-glass',file: 'plane-7-front-glass.svg',z: 30, blur: 0.0, dim: 1.0, white: 'none', glow: 0.0, blend: 'multiply', svg: frontGlass },
|
||||
{ id: 'sun', file: 'plane-1-sun.svg', z: 0, blur: 1.4, dim: 1.0, white: 'none', glow: 1.0, svg: sunPlane },
|
||||
{ id: 'event-glow', file: 'plane-2-event-glow.svg', z: 8, blur: 2.6, dim: 0.85, white: 'none', glow: 0.9, svg: planeSVG(IMG(tracksAll)) },
|
||||
{ id: 'sea-far', file: 'plane-3-sea-far.svg', z: 14, blur: 0.9, dim: 0.7, white: 'none', glow: 0.8, svg: planeSVG(IMG(sheets[0])) },
|
||||
{ id: 'sea-mid', file: 'plane-4-sea-mid.svg', z: 18, blur: 0.5, dim: 0.7, white: 'trace', glow: 0.7, svg: planeSVG(IMG(sheets[1])) },
|
||||
{ id: 'sea-near', file: 'plane-5-sea-near.svg', z: 22, blur: 0.2, dim: 0.7, white: 'trace', glow: 0.7, svg: planeSVG(IMG(sheets[2])) },
|
||||
{ id: 'event-birth', file: 'plane-6-event-birth.svg', z: 28, blur: 0.7, dim: 1.0, white: 'none', glow: 0.8, svg: planeSVG(IMG(tracksBirth)) },
|
||||
{ id: 'event-life', file: 'plane-7-event-life.svg', z: 32, blur: 0.3, dim: 1.0, white: 'trace', glow: 0.5, svg: planeSVG(IMG(tracksLife)) },
|
||||
{ id: 'event-death', file: 'plane-8-event-death.svg', z: 36, blur: 0.0, dim: 1.0, white: 'under', glow: 0.3, svg: planeSVG(IMG(tracksDeath)) },
|
||||
{ id: 'front-glass', file: 'plane-9-front-glass.svg', z: 36, blur: 0.0, dim: 1.0, white: 'none', glow: 0.0, blend: 'multiply', svg: frontGlass },
|
||||
];
|
||||
|
||||
for (const pl of PLANES) writeFileSync(`${OUT}/${pl.file}`, pl.svg);
|
||||
@@ -181,14 +201,13 @@ const planesJSON = JSON.stringify(PLANES.map(({ svg, ...r }) => r));
|
||||
writeFileSync(`${OUT}/simulator.html`, `<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||
<title>Proof1 · lit chamber — backlit parallax</title>
|
||||
<style>
|
||||
:root{--gap:6;--bright:1;}
|
||||
html,body{margin:0;height:100%;background:#0a0a0b;overflow:hidden;
|
||||
font:12px/1.5 ui-monospace,monospace;color:#9aa;}
|
||||
#wrap{position:fixed;inset:0;display:grid;place-items:center;}
|
||||
.scene{perspective:2200px;perspective-origin:50% 45%;}
|
||||
.scene{perspective:1400px;perspective-origin:50% 45%;}
|
||||
.stack{position:relative;transform-style:preserve-3d;transition:transform .06s linear;
|
||||
width:${W / 2}px;height:${H / 2}px;}
|
||||
.panel{position:absolute;inset:-14%;transform:translateZ(-120px);
|
||||
.panel{position:absolute;inset:-70%;
|
||||
background:radial-gradient(120% 90% at 50% 42%,#fff6ec, #ffe7c8 42%, #f0c48c 78%, #caa06a 100%);
|
||||
filter:blur(2px);}
|
||||
.plane{position:absolute;inset:0;width:100%;height:100%;background-size:cover;
|
||||
@@ -207,7 +226,9 @@ writeFileSync(`${OUT}/simulator.html`, `<!DOCTYPE html><html><head><meta charset
|
||||
<h1>Proof1 · lit chamber</h1>
|
||||
<div>move mouse = parallax · drag sliders</div>
|
||||
<label>air gap ×<span id="gv"></span></label>
|
||||
<input id="gap" type="range" min="1" max="18" step="0.5" value="6">
|
||||
<input id="gap" type="range" min="1" max="28" step="0.5" value="12">
|
||||
<label>parallax ×<span id="pv"></span></label>
|
||||
<input id="par" type="range" min="0" max="220" step="5" value="110">
|
||||
<label>backlight<span id="bv"></span></label>
|
||||
<input id="bright" type="range" min="0.3" max="1.8" step="0.05" value="1">
|
||||
<div id="pills"></div>
|
||||
@@ -232,32 +253,40 @@ PLANES.forEach(p=>{
|
||||
pill.onclick=()=>{d.dataset.off=d.dataset.off?'':'1'; pill.classList.toggle('off'); layout();};
|
||||
pills.appendChild(pill);
|
||||
});
|
||||
let gap=6, bright=1, mx=0, my=0;
|
||||
function layout(){
|
||||
let gap=12, par=110, bright=1, mx=0, my=0;
|
||||
const panel=document.getElementById('panel');
|
||||
// One transform per plane = translateZ (real depth) + a depth-scaled screen
|
||||
// shift (EXAGGERATED parallax) + the stack's 3D tilt. Deeper planes shift more,
|
||||
// so the event's birth→death timeline slides apart as you move.
|
||||
function update(){
|
||||
stack.style.transform='rotateX('+(my*11).toFixed(2)+'deg) rotateY('+(mx*15).toFixed(2)+'deg)';
|
||||
for(const p of PLANES){
|
||||
const e=els[p.id];
|
||||
const z=(p.z-maxZ)*gap; // front plane at 0, others recede
|
||||
const blur=(p.blur||0)*(gap/6);
|
||||
const off=e.dataset.off?' opacity:0;':'';
|
||||
e.style.transform='translateZ('+z+'px)';
|
||||
e.style.filter='blur('+blur.toFixed(2)+'px)';
|
||||
const z=(p.z-maxZ)*gap; // front plane at 0, others recede
|
||||
const depth=(maxZ-p.z)/(maxZ||1); // 0 = front, 1 = deepest
|
||||
const ox=(-mx*depth*par).toFixed(1), oy=(my*depth*par).toFixed(1);
|
||||
e.style.transform='translate3d('+ox+'px,'+oy+'px,'+z.toFixed(1)+'px)';
|
||||
e.style.filter='blur('+((p.blur||0)*(gap/8)).toFixed(2)+'px)';
|
||||
e.style.opacity=e.dataset.off?0:(p.dim??1);
|
||||
e.style.mixBlendMode=p.blend||'normal';
|
||||
}
|
||||
document.getElementById('panel').style.filter='blur(2px) brightness('+bright+')';
|
||||
}
|
||||
function parallax(){
|
||||
stack.style.transform='rotateX('+(my*7)+'deg) rotateY('+(mx*9)+'deg)';
|
||||
// keep the light panel ALWAYS behind the deepest plane, scaled up so it still
|
||||
// fills the backdrop as it recedes (else deep planes pop behind it at high gap).
|
||||
const pz=(0-maxZ)*gap-90;
|
||||
panel.style.transform='translateZ('+pz.toFixed(0)+'px) scale('+(1+(-pz)/1400).toFixed(2)+')';
|
||||
panel.style.filter='blur(2px) brightness('+bright+')';
|
||||
}
|
||||
addEventListener('mousemove',e=>{
|
||||
mx=(e.clientX/innerWidth-0.5)*2; my=-(e.clientY/innerHeight-0.5)*2; parallax();
|
||||
mx=(e.clientX/innerWidth-0.5)*2; my=-(e.clientY/innerHeight-0.5)*2; update();
|
||||
});
|
||||
const gapI=document.getElementById('gap'), brI=document.getElementById('bright');
|
||||
gapI.oninput=()=>{gap=+gapI.value; document.getElementById('gv').textContent=' '+gap; layout();};
|
||||
brI.oninput=()=>{bright=+brI.value; document.getElementById('bv').textContent=' '+bright.toFixed(2); layout();};
|
||||
const gapI=document.getElementById('gap'), parI=document.getElementById('par'), brI=document.getElementById('bright');
|
||||
gapI.oninput=()=>{gap=+gapI.value; document.getElementById('gv').textContent=' '+gap; update();};
|
||||
parI.oninput=()=>{par=+parI.value; document.getElementById('pv').textContent=' '+par; update();};
|
||||
brI.oninput=()=>{bright=+brI.value; document.getElementById('bv').textContent=' '+bright.toFixed(2); update();};
|
||||
document.getElementById('gv').textContent=' '+gap;
|
||||
document.getElementById('pv').textContent=' '+par;
|
||||
document.getElementById('bv').textContent=' '+bright.toFixed(2);
|
||||
layout(); parallax();
|
||||
update();
|
||||
</script></body></html>`);
|
||||
|
||||
console.log(`PROOF1 — ${W}×${H} (φ) — ${PLANES.length} planes → ${OUT}/`);
|
||||
|
||||
Reference in New Issue
Block a user