@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +87 -0
- package/LICENSE.md +664 -0
- package/README.md +84 -2
- package/dist/dist-manifest.json +218 -0
- package/dist/eyes/index.js +109 -0
- package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
- package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
- package/dist/fat/pkg-node/package.json +12 -0
- package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
- package/dist/frqtl/compat/boot-node.js +34 -0
- package/dist/frqtl/compat/engine.js +127 -0
- package/dist/frqtl/compat/package-root.js +24 -0
- package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
- package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
- package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
- package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
- package/dist/frqtl/compat/renderer/Filters.js +163 -0
- package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
- package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
- package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
- package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
- package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
- package/dist/frqtl/compat/renderer/display-slot.js +96 -0
- package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
- package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
- package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
- package/dist/frqtl/compat/renderer/providers/index.js +22 -0
- package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
- package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
- package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
- package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
- package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
- package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
- package/dist/frqtl/compat/vendor.js +196 -0
- package/dist/frqtl/node-entry.js +17 -0
- package/dist/frqtl/objects/FrqtlApp.js +2920 -0
- package/dist/frqtl/objects/ObjectFactory.js +296 -0
- package/dist/frqtl/objects/RecipeContext.js +322 -0
- package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
- package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
- package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
- package/dist/frqtl/objects/instruments/capture-host.js +453 -0
- package/dist/frqtl/objects/instruments/capture.js +710 -0
- package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
- package/dist/frqtl/objects/kinds-entry.js +14 -0
- package/dist/frqtl/objects/kinds.js +190 -0
- package/dist/frqtl/objects/objects/Box.js +273 -0
- package/dist/frqtl/objects/objects/Bullet.js +155 -0
- package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
- package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
- package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
- package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
- package/dist/frqtl/objects/objects/LightSource.js +351 -0
- package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
- package/dist/frqtl/objects/objects/Tube.js +471 -0
- package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
- package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
- package/dist/frqtl/objects/objects/controlAction.js +89 -0
- package/dist/frqtl/objects/objects/controlBind.js +278 -0
- package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
- package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
- package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
- package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
- package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
- package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
- package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
- package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
- package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
- package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
- package/dist/frqtl/objects/objects/tagger.js +228 -0
- package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
- package/dist/frqtl/objects/schema/index.js +256 -0
- package/dist/frqtl/objects/schema/scopes.js +457 -0
- package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
- package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
- package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
- package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
- package/dist/frqtl/objects/setup/_worldInit.js +162 -0
- package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
- package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
- package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
- package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
- package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
- package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
- package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
- package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
- package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
- package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
- package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
- package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
- package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
- package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
- package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
- package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
- package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
- package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
- package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
- package/dist/frqtl/objects/v1-replacements.js +29 -0
- package/dist/frqtl/package-core.js +23 -0
- package/dist/frqtl/parts/CameraControls.js +643 -0
- package/dist/frqtl/parts/CameraPath.js +575 -0
- package/dist/frqtl/parts/CollisionActions.js +34 -0
- package/dist/frqtl/parts/CollisionTypes.js +30 -0
- package/dist/frqtl/parts/DetectorWall.js +496 -0
- package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
- package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
- package/dist/frqtl/parts/FrqtlVision.js +818 -0
- package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
- package/dist/frqtl/parts/LargeObject.js +405 -0
- package/dist/frqtl/parts/LightColor.js +255 -0
- package/dist/frqtl/parts/LiveChart.js +285 -0
- package/dist/frqtl/parts/LogicGate.js +717 -0
- package/dist/frqtl/parts/OvenBox.js +611 -0
- package/dist/frqtl/parts/PointSource.js +762 -0
- package/dist/frqtl/parts/Rectangle.js +200 -0
- package/dist/frqtl/parts/SlitLightSource.js +234 -0
- package/dist/frqtl/parts/SlitScreen.js +628 -0
- package/dist/frqtl/parts/SphereEmitter.js +946 -0
- package/dist/frqtl/parts/Trace.js +508 -0
- package/dist/frqtl/parts/Tunnel.js +1057 -0
- package/dist/frqtl/parts/Wall.js +1211 -0
- package/dist/frqtl/parts/index.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
- package/dist/frqtl/scenes/absorbing-wall.js +32 -0
- package/dist/frqtl/scenes/atomic-model.js +88 -0
- package/dist/frqtl/scenes/big-bang.js +46 -0
- package/dist/frqtl/scenes/blackbody.js +81 -0
- package/dist/frqtl/scenes/circuit-sat.js +127 -0
- package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
- package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
- package/dist/frqtl/scenes/counting-wall.js +38 -0
- package/dist/frqtl/scenes/double-slit.js +31 -0
- package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
- package/dist/frqtl/scenes/dse-one-slit.js +114 -0
- package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
- package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
- package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
- package/dist/frqtl/scenes/dse-two-slits.js +132 -0
- package/dist/frqtl/scenes/empty.js +14 -0
- package/dist/frqtl/scenes/laser-buildup.js +49 -0
- package/dist/frqtl/scenes/mirror-3d.js +56 -0
- package/dist/frqtl/scenes/mirror-route.js +140 -0
- package/dist/frqtl/scenes/mirror.js +38 -0
- package/dist/frqtl/scenes/omni-3d.js +27 -0
- package/dist/frqtl/scenes/omni-source.js +22 -0
- package/dist/frqtl/scenes/self-interference.js +39 -0
- package/dist/frqtl/scenes/smoking-gun.js +122 -0
- package/dist/frqtl/scenes/straight-walk.js +22 -0
- package/dist/frqtl/scenes/sun.js +39 -0
- package/dist/frqtl/scenes/two-source.js +37 -0
- package/dist/frqtl/scenes/wave.js +26 -0
- package/dist/shim/bundle/frqnb.js +112 -0
- package/dist/shim/bundle/index.js +121 -0
- package/dist/shim/bundle/indexHtml.js +107 -0
- package/dist/shim/bundle/manifest.js +95 -0
- package/dist/shim/bundle/zip.js +243 -0
- package/dist/shim/bus.js +67 -0
- package/dist/shim/controls/index.js +627 -0
- package/dist/shim/controls/registry.js +274 -0
- package/dist/shim/engine/artifact-shell.js +211 -0
- package/dist/shim/engine/boot-phases.js +44 -0
- package/dist/shim/engine/engine-stack-boot.js +59 -0
- package/dist/shim/engine/router.js +690 -0
- package/dist/shim/experiment-frame/index.js +296 -0
- package/dist/shim/expr/ast-schema.js +369 -0
- package/dist/shim/field-descriptor/index.js +325 -0
- package/dist/shim/frame-data/index.js +224 -0
- package/dist/shim/index.js +67 -0
- package/dist/shim/notebook/canvas-guard.js +82 -0
- package/dist/shim/notebook/catalog.js +35 -0
- package/dist/shim/notebook/format.js +736 -0
- package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
- package/dist/shim/notebook/kernel.js +671 -0
- package/dist/shim/notebook/knob.js +150 -0
- package/dist/shim/notebook/module-resolver.js +260 -0
- package/dist/shim/notebook/notebook-knobs.js +65 -0
- package/dist/shim/notebook/produced-bindings.js +67 -0
- package/dist/shim/notebook/realm-registry.js +121 -0
- package/dist/shim/notebook/realm-transport.js +73 -0
- package/dist/shim/notebook/setup-builder.js +507 -0
- package/dist/shim/notebook/store.js +159 -0
- package/dist/shim/notebook/vendor-importmap.js +134 -0
- package/dist/shim/notebook/view-builder.js +189 -0
- package/dist/shim/notebook/view-ctx.js +100 -0
- package/dist/shim/notebook/view-handle.js +202 -0
- package/dist/shim/notebook/warmup.js +102 -0
- package/dist/shim/package.json +4 -0
- package/dist/shim/page-params/index.js +213 -0
- package/dist/shim/project/identity.js +112 -0
- package/dist/shim/project/index.js +19 -0
- package/dist/shim/project/link.js +402 -0
- package/dist/shim/project/node-specifier-hook.mjs +35 -0
- package/dist/shim/project/register-node.mjs +17 -0
- package/dist/shim/project/specifiers.js +106 -0
- package/dist/shim/registry/copy-gate.js +110 -0
- package/dist/shim/registry/exhibits.js +455 -0
- package/dist/shim/registry/experiments.js +215 -0
- package/dist/shim/registry/index.js +19 -0
- package/dist/shim/registry/scene-source.js +157 -0
- package/dist/shim/registry/walkthrough.js +804 -0
- package/dist/shim/render/capture-envelope.js +279 -0
- package/dist/shim/render/heat-colormap.js +92 -0
- package/dist/shim/render/render-stages.js +855 -0
- package/dist/shim/render/sun-look.js +33 -0
- package/dist/shim/render-state/apply.js +262 -0
- package/dist/shim/render-state/index.js +74 -0
- package/dist/shim/rpc/index.js +207 -0
- package/dist/shim/runtime/index.js +1023 -0
- package/dist/shim/storage/boot.js +69 -0
- package/dist/shim/storage/index.js +169 -0
- package/dist/shim/storage/local-storage-provider.js +230 -0
- package/dist/shim/storage/make-namespace-store.js +132 -0
- package/dist/shim/storage/modules.js +249 -0
- package/dist/shim/storage/projects.js +150 -0
- package/llms.txt +12 -0
- package/package.json +63 -4
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* capture-envelope.js — the TRANSPORT contract for a capture leaving this process.
|
|
5
|
+
*
|
|
6
|
+
* ── What it packages ─────────────────────────────────────────────────────────
|
|
7
|
+
*
|
|
8
|
+
* A still that stays in-process is `normalizeStill`'s bag and nothing more. A capture that
|
|
9
|
+
* CROSSES a boundary — to a model, an app microservice, a corpus row — needs two things the
|
|
10
|
+
* bag deliberately does not carry: the ENCODED bytes (rgba8 is not transportable), and, for
|
|
11
|
+
* a sequence, the map from frame index back to simulation state.
|
|
12
|
+
*
|
|
13
|
+
* This module adds exactly that envelope and NOTHING else.
|
|
14
|
+
*
|
|
15
|
+
* ── Why it composes rather than re-declares ──────────────────────────────────
|
|
16
|
+
*
|
|
17
|
+
* `render-stages.js` in this same directory OWNS the still schema: it holds `SCHEMA_VERSION`,
|
|
18
|
+
* refuses an unknown stage, refuses a `format` disagreeing with its stage, refuses an empty
|
|
19
|
+
* payload, refuses a still with no `scene` hash, and snapshots `data` so a live reference can
|
|
20
|
+
* never leak into a corpus. Re-listing `{stage, format, width, height, data, tick, scene}`
|
|
21
|
+
* here would be a hand mirror of that schema and would drift the first time a stage is added
|
|
22
|
+
* — the review-blocking bug class this codebase names by hand. So the still rides WHOLE,
|
|
23
|
+
* under one key, normalized by its owner, and this file never reads inside it except to
|
|
24
|
+
* cross-check what the encoder claims.
|
|
25
|
+
*
|
|
26
|
+
* ── The two forms ────────────────────────────────────────────────────────────
|
|
27
|
+
*
|
|
28
|
+
* stillEnvelope({ still, encoded }) one capture
|
|
29
|
+
* videoEnvelope({ track, encoded, truncated }) a sequence + its sidecar manifest
|
|
30
|
+
*
|
|
31
|
+
* The still envelope carries NO pose of its own: `normalizeStill` already holds `pose`, and a
|
|
32
|
+
* second one here would be a duplicated owner field. The VIDEO track carries pose per frame,
|
|
33
|
+
* because the owner has no notion of a sequence and nothing there could hold it.
|
|
34
|
+
*
|
|
35
|
+
* ── The encoded half: ONE key table per form, bytes always base64 ─────────────
|
|
36
|
+
*
|
|
37
|
+
* `ENCODED_KEYS[kind]` is the whole encoded shape of each form and the normalizer BUILDS its
|
|
38
|
+
* output by iterating it, so a declared field cannot be silently dropped on the way through.
|
|
39
|
+
* `bytes` is a base64 STRING in every host — the envelope is the transport form, and a string
|
|
40
|
+
* crosses a JSON boundary (a browser-automation `evaluate`, an HTTP body) unchanged. A video
|
|
41
|
+
* additionally REQUIRES `codec` (the codec string the encoder actually PRODUCED, never the
|
|
42
|
+
* request) and `muxer` (the pinned muxer that wrote the container). `ENCODERS` is the table of
|
|
43
|
+
* producers this contract recognises, each with the form and mime it produces.
|
|
44
|
+
*
|
|
45
|
+
* ── Why `truncated` has NO default ───────────────────────────────────────────
|
|
46
|
+
*
|
|
47
|
+
* `FrqtlVision.record` stops at its `maxFrames` bound and reports it as `truncated: true`,
|
|
48
|
+
* because "a truncated sequence that LOOKS complete is worse than a refusal". Defaulting
|
|
49
|
+
* `truncated` to false here would re-create that lie one layer up: a caller who forgot to
|
|
50
|
+
* ferry the flag would ship a short recording carrying an explicit claim of completeness. So it
|
|
51
|
+
* is REQUIRED, and a missing one throws.
|
|
52
|
+
*
|
|
53
|
+
* ── Why frame cadence is checked against the declared cadence ────────────────
|
|
54
|
+
*
|
|
55
|
+
* `ticksPerFrame` is the CAPTURE cadence (a tick IS the frame); `fps` is a PLAYBACK choice
|
|
56
|
+
* with no physical meaning — engine speed is 1 px/tick, so a presentation rate says nothing
|
|
57
|
+
* about the simulation. Both are required because collapsing them destroys the tick index,
|
|
58
|
+
* and the tick index is the only thing that makes a frame comparable to engine state. The
|
|
59
|
+
* frames' own tick deltas must equal the declared cadence: a manifest whose label disagrees
|
|
60
|
+
* with its data is worse than an unlabelled one, because it will be believed.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
import * as RenderStages from './render-stages.js';
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
/** Envelope schema version. Independent of the still's own `v`, which its owner sets. */
|
|
68
|
+
var ENVELOPE_VERSION = 1;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The encoded shape of each envelope form. `encoder` is optional (null when absent) and
|
|
72
|
+
* validated against `ENCODERS` when present; every other key is a REQUIRED non-empty string.
|
|
73
|
+
*/
|
|
74
|
+
var ENCODED_KEYS = {
|
|
75
|
+
still: ['mime', 'bytes', 'encoder'],
|
|
76
|
+
video: ['mime', 'bytes', 'encoder', 'codec', 'muxer']
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** The mime family each form accepts. */
|
|
80
|
+
var MIME_PREFIX = { still: 'image/', video: 'video/' };
|
|
81
|
+
|
|
82
|
+
/** The producers this contract recognises: the form each produces and its mime. */
|
|
83
|
+
var ENCODERS = {
|
|
84
|
+
'FrqtlVision.toPNG': { kind: 'still', mime: 'image/png' },
|
|
85
|
+
'FrqtlVision.toDataURL': { kind: 'still', mime: 'image/png' },
|
|
86
|
+
'FrqtlVision.record': { kind: 'video', mime: 'video/mp4' }
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function _fail(msg) { throw new Error('[capture-envelope] ' + msg + ' (FAIL-LOUD.)'); }
|
|
90
|
+
|
|
91
|
+
function _requireBool(value, name) {
|
|
92
|
+
if (typeof value !== 'boolean') {
|
|
93
|
+
_fail(name + ' must be an explicit boolean, got ' + String(value) +
|
|
94
|
+
' — it is never defaulted, because a fabricated completeness claim is the ' +
|
|
95
|
+
'exact failure the recorder\'s own bound reporting exists to prevent');
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function _requirePosInt(value, name) {
|
|
101
|
+
if (typeof value !== 'number' || !isFinite(value) || value <= 0 || value % 1 !== 0) {
|
|
102
|
+
_fail(name + ' must be a positive whole number, got ' + String(value));
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function _requireNonEmptyString(value, name, why) {
|
|
108
|
+
if (typeof value !== 'string' || !value) { _fail(name + ' must be a non-empty string' + (why ? ' — ' + why : '') + ', got ' + String(value)); }
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Validate the encoded half of a `kind` envelope, building the output off `ENCODED_KEYS`. */
|
|
113
|
+
function _normalizeEncoded(encoded, kind) {
|
|
114
|
+
if (!encoded || typeof encoded !== 'object') { _fail('`encoded` must be an object carrying the transportable bytes'); }
|
|
115
|
+
var out = {};
|
|
116
|
+
var keys = ENCODED_KEYS[kind];
|
|
117
|
+
for (var i = 0; i < keys.length; i++) {
|
|
118
|
+
var k = keys[i];
|
|
119
|
+
if (k === 'encoder') {
|
|
120
|
+
out.encoder = (encoded.encoder === undefined) ? null : encoded.encoder;
|
|
121
|
+
} else if (k === 'bytes') {
|
|
122
|
+
if (typeof encoded.bytes !== 'string' || !encoded.bytes) {
|
|
123
|
+
_fail('encoded.bytes must be a non-empty base64 string — the envelope carries ' +
|
|
124
|
+
'TRANSPORTABLE bytes; an rgba8 payload does not cross a boundary and the ' +
|
|
125
|
+
'still already holds it');
|
|
126
|
+
}
|
|
127
|
+
out.bytes = encoded.bytes;
|
|
128
|
+
} else {
|
|
129
|
+
out[k] = _requireNonEmptyString(encoded[k], 'encoded.' + k);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (out.mime.indexOf(MIME_PREFIX[kind]) !== 0) {
|
|
133
|
+
_fail('encoded.mime ' + JSON.stringify(out.mime) + ' is not a ' + MIME_PREFIX[kind] + '* type');
|
|
134
|
+
}
|
|
135
|
+
if (out.encoder !== null) {
|
|
136
|
+
if (!Object.prototype.hasOwnProperty.call(ENCODERS, out.encoder)) {
|
|
137
|
+
_fail('unknown encoder ' + JSON.stringify(out.encoder) + ' — known: ' +
|
|
138
|
+
Object.keys(ENCODERS).join(', '));
|
|
139
|
+
}
|
|
140
|
+
var known = ENCODERS[out.encoder];
|
|
141
|
+
if (known.kind !== kind) {
|
|
142
|
+
_fail('encoder ' + JSON.stringify(out.encoder) + ' produces a ' + known.kind +
|
|
143
|
+
', not a ' + kind);
|
|
144
|
+
}
|
|
145
|
+
if (known.mime !== out.mime) {
|
|
146
|
+
_fail('encoder ' + JSON.stringify(out.encoder) + ' produces ' +
|
|
147
|
+
JSON.stringify(known.mime) + ', but encoded.mime says ' + JSON.stringify(out.mime));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* bytesToBase64(bytes) — THE transportable-bytes encoding, in every host.
|
|
155
|
+
*
|
|
156
|
+
* A typed array → a base64 string, chunked (a per-byte concat of megabytes is slow, and
|
|
157
|
+
* `String.fromCharCode.apply` has an argument limit). The one encoder of `encoded.bytes`, and
|
|
158
|
+
* of the capture host's in-page payload: both read it from here, never from a copy.
|
|
159
|
+
*
|
|
160
|
+
* @param {ArrayBufferView} bytes
|
|
161
|
+
* @returns {string}
|
|
162
|
+
*/
|
|
163
|
+
function bytesToBase64(bytes) {
|
|
164
|
+
if (!bytes || typeof bytes.BYTES_PER_ELEMENT !== 'number') {
|
|
165
|
+
_fail('bytesToBase64 expects a typed array, got ' + String(bytes));
|
|
166
|
+
}
|
|
167
|
+
if (typeof btoa !== 'function') {
|
|
168
|
+
_fail('bytesToBase64 needs a global btoa (every browser, and node >= 16)');
|
|
169
|
+
}
|
|
170
|
+
var u8 = new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
171
|
+
var bin = '';
|
|
172
|
+
for (var i = 0; i < u8.length; i += 0x8000) {
|
|
173
|
+
bin += String.fromCharCode.apply(null, u8.subarray(i, i + 0x8000));
|
|
174
|
+
}
|
|
175
|
+
return btoa(bin);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Compose a single-capture envelope.
|
|
180
|
+
*
|
|
181
|
+
* @param {{still:object, encoded:{mime:string,bytes:string,encoder?:string}}} partial
|
|
182
|
+
* `still` is a RAW partial bag; it is normalized HERE by its owner, never pre-normalized
|
|
183
|
+
* by the caller (that would let two callers disagree about who validated).
|
|
184
|
+
* @returns {object} the envelope
|
|
185
|
+
*/
|
|
186
|
+
function stillEnvelope(partial) {
|
|
187
|
+
if (!partial || typeof partial !== 'object') { _fail('expected an object, got ' + String(partial)); }
|
|
188
|
+
|
|
189
|
+
var still = RenderStages.normalizeStill(partial.still);
|
|
190
|
+
var encoded = _normalizeEncoded(partial.encoded, 'still');
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
kind: 'still',
|
|
194
|
+
v: ENVELOPE_VERSION,
|
|
195
|
+
still: still,
|
|
196
|
+
encoded: encoded
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Compose a sequence envelope. `track` is the SIDECAR MANIFEST — without it the bytes are an
|
|
202
|
+
* unlabelled movie and no frame can be aligned to the state that produced it.
|
|
203
|
+
*
|
|
204
|
+
* @param {{track:object, encoded:{mime:string,bytes:string,codec:string,muxer:string,encoder?:string},
|
|
205
|
+
* truncated:boolean}} partial
|
|
206
|
+
* @returns {object} the envelope
|
|
207
|
+
*/
|
|
208
|
+
function videoEnvelope(partial) {
|
|
209
|
+
if (!partial || typeof partial !== 'object') { _fail('expected an object, got ' + String(partial)); }
|
|
210
|
+
|
|
211
|
+
var t = partial.track;
|
|
212
|
+
if (!t || typeof t !== 'object') { _fail('`track` (the sidecar manifest) is REQUIRED — without it the bytes are an unlabelled movie'); }
|
|
213
|
+
|
|
214
|
+
_requireNonEmptyString(t.scene, 'track.scene',
|
|
215
|
+
'a sequence without a scene id is unmoored exactly as a still without one is');
|
|
216
|
+
var stage = RenderStages.assertStage(t.stage);
|
|
217
|
+
|
|
218
|
+
var width = _requirePosInt(t.width, 'track.width');
|
|
219
|
+
var height = _requirePosInt(t.height, 'track.height');
|
|
220
|
+
var ticksPerFrame = _requirePosInt(t.ticksPerFrame, 'track.ticksPerFrame');
|
|
221
|
+
var fps = _requirePosInt(t.fps, 'track.fps');
|
|
222
|
+
|
|
223
|
+
var frames = t.frames;
|
|
224
|
+
if (!Array.isArray(frames) || frames.length === 0) {
|
|
225
|
+
_fail('track.frames must be a non-empty array of {i, tick, pose} — an empty manifest ' +
|
|
226
|
+
'over real bytes is a measurement built on nothing');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
for (var n = 0; n < frames.length; n++) {
|
|
230
|
+
var f = frames[n];
|
|
231
|
+
if (!f || typeof f !== 'object') { _fail('track.frames[' + n + '] must be an object'); }
|
|
232
|
+
if (f.i !== n) {
|
|
233
|
+
_fail('track.frames[' + n + '].i is ' + String(f.i) + ' — frame indices must be ' +
|
|
234
|
+
'contiguous from 0, or the map from bytes to state is guesswork');
|
|
235
|
+
}
|
|
236
|
+
if (typeof f.tick !== 'number' || !isFinite(f.tick) || f.tick % 1 !== 0) {
|
|
237
|
+
_fail('track.frames[' + n + '].tick must be a whole number, got ' + String(f.tick));
|
|
238
|
+
}
|
|
239
|
+
if (n > 0) {
|
|
240
|
+
var delta = f.tick - frames[n - 1].tick;
|
|
241
|
+
if (delta !== ticksPerFrame) {
|
|
242
|
+
_fail('track.frames[' + n + '] is ' + delta + ' ticks after its predecessor, but ' +
|
|
243
|
+
'track.ticksPerFrame declares ' + ticksPerFrame + ' — a manifest whose ' +
|
|
244
|
+
'label disagrees with its data is worse than none, because it is believed');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
var encoded = _normalizeEncoded(partial.encoded, 'video');
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
kind: 'video',
|
|
253
|
+
v: ENVELOPE_VERSION,
|
|
254
|
+
track: {
|
|
255
|
+
scene: t.scene,
|
|
256
|
+
stage: stage,
|
|
257
|
+
width: width,
|
|
258
|
+
height: height,
|
|
259
|
+
ticksPerFrame: ticksPerFrame,
|
|
260
|
+
fps: fps,
|
|
261
|
+
firstTick: frames[0].tick,
|
|
262
|
+
lastTick: frames[frames.length - 1].tick,
|
|
263
|
+
frames: frames.map(function (f) {
|
|
264
|
+
return { i: f.i, tick: f.tick, pose: f.pose === undefined ? null : f.pose };
|
|
265
|
+
})
|
|
266
|
+
},
|
|
267
|
+
encoded: encoded,
|
|
268
|
+
truncated: _requireBool(partial.truncated, 'truncated')
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export {
|
|
273
|
+
ENVELOPE_VERSION,
|
|
274
|
+
ENCODED_KEYS,
|
|
275
|
+
ENCODERS,
|
|
276
|
+
bytesToBase64,
|
|
277
|
+
stillEnvelope,
|
|
278
|
+
videoEnvelope
|
|
279
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* Copyright 2023-2026 by Essam Abadir */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* lib/shim/render/heat-colormap — the ONE count→intensity colour ramp.
|
|
5
|
+
*
|
|
6
|
+
* Peer of `lib/shim/frame-data` (the data-bubble/render-intent contract) and
|
|
7
|
+
* `lib/shim/rpc` (the call() primitive): named ES-module exports, imported BOTH
|
|
8
|
+
* - by the game layer (`lib/frqtl/parts/GraphingDetectorWall.js`'s heat
|
|
9
|
+
* painter), and
|
|
10
|
+
* - by a component above the shim (`lib/components/notebook-pane/realm-chart.js`).
|
|
11
|
+
* Both arrows are legal; neither consumer owns the ramp.
|
|
12
|
+
*
|
|
13
|
+
* ── Why it lives here, and NOT in LightColor.js ──────────────────────────────
|
|
14
|
+
*
|
|
15
|
+
* `lib/frqtl/parts/LightColor.js` owns FREQUENCY→light-colour
|
|
16
|
+
* semantics (ROYGBIV, derived from a leaf's fullness). A count→intensity heat
|
|
17
|
+
* ramp is a different vocabulary entirely — it colours a STATISTIC (how many
|
|
18
|
+
* hits landed in a bin), not a particle's frequency band. Housing it in
|
|
19
|
+
* LightColor would be dishonest labelling; housing a private copy in each
|
|
20
|
+
* consumer is the schema-mirror-drift anti-pattern (the tell: the in-scene
|
|
21
|
+
* detector heatmap and the DOM topology chart, reading the SAME grid, drifting
|
|
22
|
+
* to different colours).
|
|
23
|
+
*
|
|
24
|
+
* ── Layer 1: API contract ───────────────────────────────────────────────────
|
|
25
|
+
*
|
|
26
|
+
* HEAT_STOPS — the frozen [r,g,b] control points (deep blue → cyan →
|
|
27
|
+
* green → yellow → red, a standard topographic ramp).
|
|
28
|
+
* heatColor(t) — normalized intensity t (0..1, clamped) → an `rgb(r,g,b)`
|
|
29
|
+
* CSS string (a canvas `fillStyle` / Chart.js colour).
|
|
30
|
+
* heatRgb(t) — the same sample as an [r,g,b] triple (0-255 integers),
|
|
31
|
+
* for a consumer that needs the channels (e.g. compositing
|
|
32
|
+
* with its own alpha).
|
|
33
|
+
*
|
|
34
|
+
* ── Layer 2: physics behaviour ──────────────────────────────────────────────
|
|
35
|
+
*
|
|
36
|
+
* ZERO physics — this is an output-boundary presentation map over an
|
|
37
|
+
* already-collected statistic. It never reads a frame, never draws from the
|
|
38
|
+
* engine RNG, and cannot move a canonical-bench golden (SU1).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The topographic control points: deep blue → cyan → green → yellow → red.
|
|
45
|
+
* Exported so a conformance/drift test can assert both consumers resolve the
|
|
46
|
+
* SAME ramp instead of hand-copying five triples into a test file.
|
|
47
|
+
*/
|
|
48
|
+
var HEAT_STOPS = Object.freeze([
|
|
49
|
+
Object.freeze([12, 24, 70]),
|
|
50
|
+
Object.freeze([20, 130, 190]),
|
|
51
|
+
Object.freeze([40, 190, 120]),
|
|
52
|
+
Object.freeze([235, 210, 60]),
|
|
53
|
+
Object.freeze([235, 70, 45])
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Sample the ramp at normalized intensity `t`.
|
|
58
|
+
* @param {number} t — 0..1 (clamped; a non-finite input samples the low end).
|
|
59
|
+
* @returns {Array<number>} [r, g, b], each a 0-255 integer.
|
|
60
|
+
*/
|
|
61
|
+
function heatRgb(t) {
|
|
62
|
+
var n = Number(t);
|
|
63
|
+
if (!isFinite(n)) { n = 0; }
|
|
64
|
+
var u = n < 0 ? 0 : (n > 1 ? 1 : n);
|
|
65
|
+
var f = u * (HEAT_STOPS.length - 1);
|
|
66
|
+
var i = Math.min(HEAT_STOPS.length - 2, Math.floor(f));
|
|
67
|
+
var k = f - i;
|
|
68
|
+
var a = HEAT_STOPS[i], b = HEAT_STOPS[i + 1];
|
|
69
|
+
return [
|
|
70
|
+
Math.round(a[0] + (b[0] - a[0]) * k),
|
|
71
|
+
Math.round(a[1] + (b[1] - a[1]) * k),
|
|
72
|
+
Math.round(a[2] + (b[2] - a[2]) * k)
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Sample the ramp at normalized intensity `t` as a CSS colour string.
|
|
78
|
+
* @param {number} t — 0..1 (clamped).
|
|
79
|
+
* @returns {string} `rgb(r,g,b)`
|
|
80
|
+
*/
|
|
81
|
+
function heatColor(t) {
|
|
82
|
+
var c = heatRgb(t);
|
|
83
|
+
return 'rgb(' + c[0] + ',' + c[1] + ',' + c[2] + ')';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── Export ────────────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
HEAT_STOPS,
|
|
90
|
+
heatRgb,
|
|
91
|
+
heatColor
|
|
92
|
+
};
|