Add Format Specification section 21 (Scenario Model 0.1) and the runtime that realizes it: the scenario director, seven trigger classes, timelines with repeats and branches, admission and concurrency control, nested ownership with bounded cleanup, and the production GC5 resource counters. Exhibit E provides a reproducible forty-minute long-scenario reference and scenario-challenge.xzbt covers the PRD 131 cases. A standalone acceptance/soak page is built by tools/build-scenario-acceptance.mjs. 252 automated checks pass. The two-hour real-duration development soak required by the GC6 schedule remains pending. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01M7dgfQ12mpM4JjSMv3inLA
93 lines
9.8 KiB
JavaScript
93 lines
9.8 KiB
JavaScript
// Authored exhibit data only. Subject names occur here, never in renderer dispatch.
|
|
import { withTemporaryScenario } from './scenario-fixtures.mjs';
|
|
const random = (min, max) => ({ random: { min, max } });
|
|
const dot = (color = '#cfe8ff', size = 3) => ({ type: 'point', style: { fill: color, pointSize: size } });
|
|
const lineStyle = { stroke: '#82b7c7', strokeWidth: 2 };
|
|
const graphic = content => ({ type: 'graphic', content });
|
|
const ring = (radius, color = '#72c4bd') => ({ type: 'ring', position: { x: 480, y: 270 }, radius, innerRadius: radius - 2, style: { fill: color } });
|
|
const auto = (target, from, to, duration = '12s') => ({ target, loop: { mode: 'ping-pong' }, interpolation: 'smooth', points: [{ at: '0ms', value: from }, { at: duration, value: to }] });
|
|
const uniform = { type: 'uniform', min: { x: 0, y: 0 }, max: { x: 960, y: 540 } };
|
|
const particles = (extra = {}) => ({ type: 'particles', count: 180, capacity: 256, distribution: uniform, render: dot(), ...extra });
|
|
const splinePoints = Array.from({ length: 9 }, (_, i) => ({ x: 100 + i * 95, y: 250 + Math.sin(i * 1.7) * 95 }));
|
|
const curve = { type: 'spline', points: splinePoints, style: lineStyle };
|
|
const wrap = { type: 'wrap', bounds: 'scene' };
|
|
const rain = particles({ count: 240, velocity: { x: -35, y: 220 }, render: { type: 'line', to: { x: -3, y: 15 }, style: { stroke: '#90acba', strokeWidth: 1, opacity: 0.6 } }, behaviors: [wrap] });
|
|
const landscape = graphic({
|
|
sky: { type: 'rectangle', size: { width: 960, height: 540 }, style: { fill: { type: 'linear-gradient', from: { x: 0, y: 0 }, to: { x: 0, y: 540 }, stops: [{ offset: 0, color: '#aabcb8' }, { offset: 1, color: '#eddcb0' }] } } },
|
|
sun: { type: 'ellipse', position: { x: 735, y: 120 }, radius: 48, style: { fill: '#fff1c6' } },
|
|
far: { type: 'polygon', points: [{ x: 0, y: 390 }, { x: 220, y: 150 }, { x: 470, y: 340 }, { x: 660, y: 200 }, { x: 960, y: 410 }, { x: 960, y: 540 }, { x: 0, y: 540 }], style: { fill: '#667e72' } },
|
|
near: { type: 'spline', closed: true, points: [{ x: 0, y: 450 }, { x: 250, y: 320 }, { x: 580, y: 480 }, { x: 960, y: 380 }, { x: 960, y: 600 }, { x: 0, y: 600 }], style: { fill: '#314c41' } }
|
|
});
|
|
const machine = {
|
|
wheel: graphic({ outer: ring(160), inner: ring(125, '#e1bd7c'), hub: ring(38, '#e1bd7c') }),
|
|
spokes: { type: 'repeater', count: 12, distribution: { type: 'point', position: { x: 480, y: 270 } }, repeat: {
|
|
type: 'group', transform: { rotation: { op: 'multiply', args: [{ ref: 'repeat.fraction' }, 330] } }, behaviors: [{ type: 'rotate', speed: 12 }],
|
|
children: { bar: { type: 'rectangle', position: { x: 40, y: -4 }, size: { width: 85, height: 8 }, style: { fill: '#e1bd7c' } } }
|
|
} }
|
|
};
|
|
const radar = {
|
|
rings: graphic({ a: ring(70), b: ring(130), c: ring(190),
|
|
label: { type: 'text', text: 'FIELD / 04', position: { x: 35, y: 45 }, size: 18, style: { fill: '#a7d3c4' } },
|
|
sweep: { type: 'group', position: { x: 480, y: 270 }, behaviors: [{ type: 'rotate', speed: 40 }], children: { ray: { type: 'line', to: { x: 188, y: 0 }, style: { stroke: '#ddf6a0', strokeWidth: 3 } } } }
|
|
}),
|
|
ticks: { type: 'repeater', count: 48, distribution: { type: 'ring', center: { x: 480, y: 270 }, radius: 210, innerRadius: 209, mode: 'even' }, repeat: dot('#789f95', 3) },
|
|
contacts: particles({ count: 18, capacity: 32, distribution: { type: 'ellipse', center: { x: 480, y: 270 }, radius: 170 }, render: dot('#d8e6a7', 5), behaviors: [{ type: 'twinkle', min: 0.2, max: 1, rate: 0.6 }] })
|
|
};
|
|
const organic = graphic({ body: { type: 'spline', closed: true,
|
|
points: Array.from({ length: 12 }, (_, i) => ({ x: 480 + Math.cos(i * Math.PI / 6) * (110 + i % 3 * 23), y: 270 + Math.sin(i * Math.PI / 6) * 125 })),
|
|
style: { fill: '#537a63', stroke: '#acd2a0', strokeWidth: 3 },
|
|
behaviors: [{ type: 'point-wander', amplitude: { x: 28, y: 28 }, rate: 0.15 }, { type: 'pulse', property: 'style.opacity', amplitude: -0.35, frequency: 0.2 }]
|
|
} });
|
|
const network = particles({ count: 65, capacity: 96, distribution: { type: 'rectangle', center: { x: 480, y: 270 }, size: { width: 800, height: 410 } },
|
|
links: { rule: 'nearest', count: 3, maxDistance: 180, maxLinks: 160, fadeWithDistance: true, style: { stroke: '#6899b7', strokeWidth: 1 } },
|
|
behaviors: [{ type: 'noise-displace', amplitude: { x: 25, y: 25 }, scale: 120, speed: 0.1 }], render: dot('#d8e9f5', 4) });
|
|
const morph = graphic({
|
|
shape: { ...curve, style: { stroke: '#ecbf85', strokeWidth: 4 }, behaviors: [{ type: 'morph', to: 'target', duration: '6s', loop: 'ping-pong' }] },
|
|
target: { ...curve, visible: false, points: splinePoints.map((p, i) => ({ x: p.x, y: 270 + Math.cos(i * 0.9) * 150 })) }
|
|
});
|
|
const burst = { type: 'particles', lifecycle: 'spawned', spawn: { lifetime: '2s', release: '1s' }, count: 120, capacity: 128,
|
|
distribution: { type: 'point', position: { x: 480, y: 270 } }, velocity: { x: random(-160, 160), y: random(-160, 160) },
|
|
lifetime: '3s', size: { from: 1.5, to: 0.1 }, opacity: { from: 1, to: 0 }, render: dot('#f5c276', 4),
|
|
trail: { length: 20, mode: 'line', width: 2, style: { stroke: '#d98963', strokeWidth: 2 } }
|
|
};
|
|
const definitions = [
|
|
['deep-parallax-starfield', 'Deep parallax starfield', { layers: { far: { parallax: 0.2 }, near: { parallax: 1 } }, camera: { projection: 'perspective', focalLength: 1000 }, automation: [auto('camera.x', 400, 570)], systems: {
|
|
far: particles({ layer: 'far', count: 250, z: random(300, 1200), render: dot('#8c9ebf', 2) }), near: particles({ layer: 'near', count: 90, z: random(0, 200), render: dot('#ecdfc1', 3) }) } }],
|
|
['flow-field', 'Fog or smoke-like flow field', { fields: { flow: { type: 'noise', mode: 'curl', scale: 160, amplitude: 25, speed: 0.12 } }, systems: { flow: particles({ count: 150, fields: ['flow'], drag: 0.2, render: { type: 'ellipse', radius: 20, style: { fill: '#b5c1c0', opacity: 0.025 } }, behaviors: [wrap] }) }, effects: [{ type: 'blur', radius: 5 }] }],
|
|
['spline-network', 'Evolving spline network', { systems: { curves: graphic(Object.fromEntries(Array.from({ length: 6 }, (_, i) => [`c${i}`, { ...curve, position: { x: 0, y: (i - 3) * 35 }, behaviors: [{ type: 'point-wander', amplitude: { x: 30, y: 45 }, rate: 0.13 }] }]))) } }],
|
|
['weather-particles', 'Rain particles', { systems: { rain } }],
|
|
['mechanical-abstraction', 'Rotating mechanical abstraction', { systems: machine }],
|
|
['organic-pulse', 'Organic pulsing structure', { systems: { organic } }],
|
|
['instrument-display', 'Instrument and radar display', { systems: radar }],
|
|
['constellation-network', 'Constellation network', { systems: { network } }],
|
|
['persistent-landscape', 'Persistent landscape', { systems: { landscape } }],
|
|
['transient-impact', 'Transient impact burst', { systems: { burst } }],
|
|
['morphing-art', 'Morphing geometric art', { systems: { morph } }],
|
|
['audio-reactive', 'Audio-reactive visualization', { layers: { field: {} }, systems: { network: { ...network, layer: 'field' } }, effects: [{ type: 'bloom', intensity: 0.4 }] }, { bindings: [{ source: 'signals.audio.energy', target: 'visuals.layers.field.opacity', scale: 0.8, offset: 0.2 }, { source: 'signals.audio.low', target: 'visuals.camera.zoom', scale: 0.25, offset: 1 }] }],
|
|
['window-traffic', 'Window containing procedural traffic', { systems: {
|
|
background: graphic({ wall: { type: 'rectangle', size: { width: 960, height: 540 }, style: { fill: '#c8b69c' } }, window: { type: 'rectangle', position: { x: 150, y: 90 }, size: { width: 660, height: 360 }, style: { fill: '#273f49' } } }),
|
|
traffic: { type: 'repeater', count: 14, distribution: { type: 'grid', columns: 7, rows: 2, origin: { x: 185, y: 235 }, spacing: { x: 88, y: 75 } }, repeat: { type: 'rectangle', size: { width: 24, height: 8 }, style: { fill: '#eed092' }, behaviors: [{ type: 'drift', velocity: { x: 20, y: 0 } }, { type: 'wrap', bounds: { x: -30, y: -30, width: 60, height: 60 } }] } },
|
|
frame: graphic({ mullion: { type: 'rectangle', position: { x: 470, y: 85 }, size: { width: 12, height: 370 }, style: { fill: '#d8c8af' } }, border: { type: 'rectangle', position: { x: 145, y: 85 }, size: { width: 670, height: 370 }, style: { stroke: '#e8d8bf', strokeWidth: 12 } } })
|
|
} }],
|
|
['natural-abstraction', 'Natural meadow abstraction', { systems: { landscape, seeds: particles({ count: 70, velocity: { x: 12, y: -4 }, render: dot('#e8daa5', 3), behaviors: [wrap, { type: 'noise-displace', amplitude: { x: 12, y: 24 }, scale: 100, speed: 0.1 }] }) } }]
|
|
];
|
|
|
|
export const visualChallenges = definitions.map(([id, name, visuals, extra], index) => ({
|
|
id, challenge: index + 1, document: { xzbt: '0.1', meta: { id, name, version: '1.0.0', description: `PRD 130 challenge ${index + 1}; generic visual primitives only.` }, runtime: { seed: 4200 + index },
|
|
visuals: { scene: { coordinateSpace: 'virtual', width: 960, height: 540, background: '#101d25' }, ...visuals }, ...extra }
|
|
}));
|
|
export const visualExhibits = [
|
|
['exhibit-a', 'Exhibit A — Procedural Machine', { systems: machine }],
|
|
['exhibit-b', 'Exhibit B — Deep Abstract Field', { systems: { network, morph }, effects: [{ type: 'bloom', radius: 5, intensity: 0.5 }] }],
|
|
['exhibit-c', 'Exhibit C — Natural Environment', { systems: { landscape, rain } }],
|
|
['exhibit-d', 'Exhibit D — Instrument Display', { systems: radar, layers: { display: {} } }]
|
|
].map(([id, name, visuals]) => {
|
|
const document = { xzbt: '0.1', meta: { id, name, version: '0.1.0', description: 'First visual composition; full reference exhibit acceptance remains Phase 9.' }, runtime: { seed: 42 }, visuals: { scene: { coordinateSpace: 'virtual', width: 960, height: 540, background: '#101d25' }, ...structuredClone(visuals) } };
|
|
if (id === 'exhibit-d') {
|
|
for (const system of Object.values(document.visuals.systems)) system.layer = 'display';
|
|
document.state = { level: { type: 'number', initial: 0.7, min: 0, max: 1 } };
|
|
document.bindings = [{ source: 'state.level', target: 'visuals.layers.display.opacity', scale: 0.7, offset: 0.3 }];
|
|
}
|
|
return { id, document: withTemporaryScenario(document) };
|
|
});
|