diff --git a/Architecture.md b/Architecture.md index 697c5e0..29410ef 100644 --- a/Architecture.md +++ b/Architecture.md @@ -79,8 +79,8 @@ src/editor/texture/ TextureEditor (header + picker), TextureCanvas (picture + ImagePicker + imageGrid (the "+ → Texture" dialog), checker (the transparency board) src/editor/animation/ AnimationEditor (header + picker), AnimationCanvas (preview + frame strip), strip (its geometry) src/editor/statemachine/ StateMachineEditor (header + picker, on the graph canvas) -src/editor/particles/ ParticlesEditor (graph canvas above a preview strip, splitter between them), - ParticlePreview (the strip: the asset's own particles at the type's defaults) +src/editor/particles/ ParticlesEditor (graph canvas left of a preview panel, splitter between them), + ParticlePreview (the panel: the asset's own particles, wearing its own rows) src/editor/list/ ListEditor (a row per item), slot (which list is being edited, and how it is written back) src/editor/console/ log (ring buffer, argument formatting, the console.* capture), ConsolePanel (the tail under the tabs) api/ PHP backend: index.php (routing), lib/{Store,TextureStore,FontStore,OriginalName,Response}.php @@ -102,7 +102,10 @@ outline, a name plate or a checkerboard left outside it is drawn over the panel per frame** — `core/particles/emission.ts` says which particles are alive at second *t* and `core/particles/compile.ts` turns a graph into what one of them looks like at its age, so a field of 500 particles is reproducible, holds still for a check, and needs no state beyond where its -source stood when each one was born. Both are pure and tested without a browser. +source stood when each one was born. Both are pure and tested without a browser. The other side of +that choice: **freezing such a thing means moving its origin, not pausing a counter** — the clock +belongs to the library and keeps running, so a preview let go after a minute on hold has to be +rebased to the second it was stopped at, or it resumes a minute further on. Comments state contracts and reasons, not what the code does: a `/** */` on every export whose name does not say it all, `//` only for a decision or gotcha. diff --git a/Development.md b/Development.md index b942b25..163fb04 100644 --- a/Development.md +++ b/Development.md @@ -12,7 +12,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs # code is lost (tail's is reported), so a red run reads as green npm run build # writes dist/ and regenerates index.html ./build.sh # the same under `set -o pipefail`, for callers that pipe the output -node tools/seed-e2e.mjs && node tools/e2e.mjs # 809 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 816 editor checks, screenshots in /tmp/gm-e2e-*.png GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step ./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh status # live progress, or the last run's result @@ -68,6 +68,11 @@ scene state and share the CDP driver in `tools/lib/harness.mjs` — `press`, `un `object(id)`, `world()`, `mutate(js)` and friends, so a check reads like the interaction it exercises. +A step that needs a tab opens it with `openTab(t, 'container')` from `tools/e2e/lib/scaffold.mjs`, +not by clicking its name: since the tenth tab the row hides some of them at the test window's width, +and the helper goes through the `»` menu when it has to. Clicking the label directly took the +whole suite down the day the Particles tab was added. + `GM_STEPS=20`, `GM_STEPS=1-12` or `GM_STEPS=6,20` runs a subset — seconds instead of minutes while one step is being written. Because a step lives off what the earlier ones left behind, a subset that skips a prerequisite fails on purpose; take `1-N` when in doubt. @@ -151,4 +156,4 @@ widths to find click points. The **render device is not in this repo** — `/dev and a 1 GB `/dev/shm` come from `container.options` in the runner's own `config.yaml`, which applies to every job on that runner. Without them chromium falls back to llvmpipe, which gets WebGL 1.0 only. Measured in the job container: `ANGLE (Intel, Vulkan 1.3.230, HD Graphics 630)`, -WebGL 2.0, and the same 809 checks as here. +WebGL 2.0, and the same 816 checks as here. diff --git a/Particles.md b/Particles.md index bbff675..f8449a6 100644 --- a/Particles.md +++ b/Particles.md @@ -9,7 +9,7 @@ the model was chosen: - **The same second always looks the same.** Two runs at the same frame rate, or a different one, draw the same picture — the field is a function of the clock, not of how many frames have gone by. -- **Any second can be shown at once.** The preview strip in the tab has a slider: drag it to 2 s and +- **Any second can be shown at once.** The preview panel in the tab has a slider: drag it to 2 s and the particles stand where they are at 2 s, exactly, with nothing simulated on the way there. - **Collision is out, for good.** "Sparks bounce off the ground", wind, drag mid-flight — none of these can be written as a function of age, and no later addition will change that. @@ -17,7 +17,7 @@ the model was chosen: ## The Particles tab `+` in the **Assets** panel makes a particle asset and opens the **Particles** tab on it (a -double-click on an existing one does the same; a click only selects). The upper part is the same +double-click on an existing one does the same; a click only selects). The left part is the same canvas the Behavior and Shader tabs have: right-click places a node, drag moves, drag from a port links, `Snap` snaps to the 24 px grid, `Fit` frames the graph, undo takes every step back. The readout on the right says `N parameters` while the graph compiles, otherwise why it does not @@ -25,11 +25,18 @@ readout on the right says `N parameters` while the graph compiles, otherwise why compiling shows the reason on its body and a red frame; a source whose graph does not compile emits nothing. -Below it, the **preview** strip emits the asset's own particles from its middle, with the type's -default rows (20 a second, one second each, a plain 16 px square). `Hold` stops the clock where it -is and turns into `Run`; the slider next to it jumps to any second in the first three lifetimes; -`Restart` begins the stream again at particle 0. The strip deliberately draws plain squares: which -picture a source throws belongs to the placed object, not to the asset the tab is showing. +Right of it, past a divider that drags, the **preview** panel emits the asset's own particles from +its middle, wearing **the rows the asset itself carries** — edit `Rate` or `Lifetime` on the asset +and the panel changes with them, including how far the slider reaches (three lifetimes). Rows the +asset leaves alone fall back to the type's defaults: 20 a second, one second each, a plain 16 px +square. It stands beside the graph rather than under it because a graph is read across: width is +what it can spare, height is not. `Hold` stops the clock where it is and turns into `Run`; the +slider jumps to any second in the first three lifetimes — and holds the panel too, so the button +reads `Run` after a drag as well; `Restart` begins the stream again at particle 0, which a held +panel shows as 0.00 s rather than keeping the second it stood on. **A hold owes nothing when it +ends**: letting the panel run again carries on from the second it was stopped at, however long it +stood there — the field's origin is moved, rather than the library clock being allowed to run on +underneath the freeze. ## The nodes @@ -105,8 +112,8 @@ to a cold one over `Age`. - **A source is culled by nothing.** Its particles may be anywhere on screen, so the source itself is never skipped for being out of frame — with many sources in one scene that costs. - **Particle time is the editor's clock, not the game's.** Pausing the game does not freeze a spray. -- **The preview shows the asset, not an instance**: the type's default rows and a plain square, never - the texture or the rate a particular placed source was given. +- **The preview shows the asset, not an instance**: the rows the asset carries, never the ones a + particular placed source was given — every one of them may be overridden where it is placed. - **A source with `Emitting` off draws nothing at all** once the last particle has died, so it disappears from the scene view; it is still in the hierarchy panel, and selecting it there shows its handle.