/ proof-of-concept / toy-instruments
Sign in
proof-of-concept / toy-instruments
Widget: draw the initial pluck/press shape at the widget's exaggeration, so amplitudes compare
Jeremy Magland <jmagland@flatironinstitute.org> committed commit bc0383968b50 parent 7d6ced2 Browse files
2 changed files+8−5
README.mdmodified+1−1View file
@@ -79,7 +79,7 @@ Runs are cached on [artifact-cache](https://artifact-cache.tempory.net) (app `to
7979 ```
8080
8181 - `settings` is a link from "Copy link to these settings" (or its `#/<instrument>?…` part). `exaggeration` magnifies the drawn motion (default 5; the reader can change it in the motion bar, from ×1 to ×100); it is not part of the cache key. `fmax` sets the top of the spectrogram's frequency axis (default 24000 Hz, capped at half the sample rate; the reader can change it). `name` names the downloaded `.wav`.
82-- The reader sees the model at rest (the 3D view without its tabs, view buttons or exaggeration input; drag to rotate), the spectrogram of the sound, and two buttons. The light entry is downloaded when the widget comes within 400 px of the viewport, for the spectrogram. "Play sound" plays it in an audio player; "Play motion (N MB)" downloads the frames and plays the replay in slow motion, with pause, a time slider, an exaggeration choice and a speed choice. Each row expands in place, so the other button does not move. On load the widget only sends HEAD requests (for the link's backend first, then the other); the frames are downloaded only for the motion.
82+- The reader sees the model at rest (the 3D view without its tabs, view buttons or exaggeration input; drag to rotate; an initial pluck or press shape is drawn at the widget's exaggeration, not scaled to a fixed size, so different amplitudes look different), the spectrogram of the sound, and two buttons. The light entry is downloaded when the widget comes within 400 px of the viewport, for the spectrogram. "Play sound" plays it in an audio player; "Play motion (N MB)" downloads the frames and plays the replay in slow motion, with pause, a time slider, an exaggeration choice and a speed choice. Each row expands in place, so the other button does not move. On load the widget only sends HEAD requests (for the link's backend first, then the other); the frames are downloaded only for the motion.
8383 - If the cache has no result, or cannot be reached, the widget offers to run the simulation in the browser, with a rough time estimate, and then shows both players. With the cache passcode saved in that browser (under "Shared cache" in the widget), the run is stored under the same key.
8484 - "Open the full app" shows the whole instrument page (every parameter, the plots, Run and Load cached) over the host page, starting from the widget's settings; Close or Escape returns to the post, and reopening keeps its state. It is the bundle's own copy of the page (not the live app in a frame), so it computes the same cache keys as the widget. It has no back link or "Copy link", and leaves the address alone.
8585 - The key hashes what the bundle's own model code builds, so a page that keeps one build of the bundle asks for, and can always refill, the same entries, whatever later versions of the app do. jeremy.magland.org uploads each build under a content hash and never replaces it.
src/ui/scene3d.tsmodified+7−4View file
@@ -31,7 +31,9 @@ export interface SceneOptions {
3131 // just the model and its legend: no setup/replay tabs, view buttons, exaggeration
3232 // input, timeline bar or help note (the embedded widget has its own controls)
3333 simple?: boolean;
34- // displacement exaggeration for replays (default: automatic, so the largest motion is visible)
34+ // displacement exaggeration for replays (default: automatic, so the largest motion is
35+ // visible). When set, the setup view draws the initial shape (pluck, press) at the same
36+ // scale, so different amplitudes look different; otherwise it is scaled to a fixed size.
3537 exaggeration?: number;
3638 }
3739 type View = 'persp' | 'front' | 'top' | 'end';
@@ -156,7 +158,8 @@ export class BeadScene {
156158 setExaggeration(x: number) {
157159 this.opts.exaggeration = this.exaggeration = x;
158160 this.exagInput.value = fmtNum(x);
159- this.update();
161+ if (this.mode === 'setup') this.rebuild(); // the initial shape is drawn at this scale
162+ else this.update();
160163 }
161164
162165 // redraw with the current colors (after a theme change the view cannot see by itself)
@@ -389,7 +392,7 @@ export class BeadScene {
389392 if (this.mode === 'setup' && spec.excKind === 'pluck') {
390393 const m = dirs(exc.u0);
391394 if (m > 0) {
392- const E = (GLYPH * size0) / m;
395+ const E = this.opts.exaggeration ?? (GLYPH * size0) / m;
393396 const pts: THREE.Vector3[] = [];
394397 const left = sys.anchors.find((a) => a.pos[0] < sys.rest[0]);
395398 const right = sys.anchors.find((a) => a.pos[0] > sys.rest[3 * (n - 1)]);
@@ -419,7 +422,7 @@ export class BeadScene {
419422 if (this.mode === 'setup' && spec.excKind === 'press') {
420423 const m = dirs(exc.u0);
421424 if (m > 0) {
422- const E = (0.5 * GLYPH * size0) / m;
425+ const E = this.opts.exaggeration ?? (0.5 * GLYPH * size0) / m;
423426 const pts: THREE.Vector3[] = [];
424427 for (let i = 0; i < n; i++) {
425428 if (Math.hypot(exc.u0[3 * i], exc.u0[3 * i + 1], exc.u0[3 * i + 2]) < 0.02 * m) continue;
↑↓ move↵openescclose