diff options
| author | s-ol <s+removethis@s-ol.nu> | 2024-11-13 18:47:56 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-02 14:24:49 +0000 |
| commit | be796c2988677c22f4cb76ee848efdc7153545ef (patch) | |
| tree | 3f099f5e7834d5348be5ca92ca47e2400d18ee77 | |
| parent | lib: add // to math (diff) | |
| download | alive-be796c2988677c22f4cb76ee848efdc7153545ef.tar.gz alive-be796c2988677c22f4cb76ee848efdc7153545ef.zip | |
lib: add time/spring
| -rw-r--r-- | alv-lib/time.moon | 82 |
1 files changed, 81 insertions, 1 deletions
diff --git a/alv-lib/time.moon b/alv-lib/time.moon index beaeac0..1f8e324 100644 --- a/alv-lib/time.moon +++ b/alv-lib/time.moon @@ -28,7 +28,7 @@ frame rate. dt = time - @state ft = 1 / @inputs.fps! - if dt >= ft + if dt >= ft and not @inputs.io.result\dirty! @inputs.io.result\set true true @@ -353,6 +353,85 @@ Creates smooth transitions when `value` changes. return if 1e-15 > math.abs delta @out\set current + delta * rate +spring = Constant.meta + meta: + name: 'spring' + summary: "Integrate impulses using easing" + examples: { '(spring [clock] evt0 [evt1…])' } + description: " +Every arriving `evt` triggers an impulse that is integrated as `clock` ticks. +Returns a num~ stream that starts at 0. + +- `clock` should be a `time/clock!` stream. This argument can be omitted + and the stream be passed as a dynamic definition in `*clock*` instead. +- `evt!` are !-streams of structs. + The following struct keys can be specified: + - `amp`: amplitude of the impulse + - `dur`: duration of the impulse (seconds) + - `del`: start delay (seconds) + - `ease`: easing function to use + +Instead of `dur`, `in` and `out` can be specified to create both an impulse +and counterimpulse, which has an amplitude of `-amp` and is delayed so as to +start immediately after the original impulse. Similarily, `easein` and `easeout` +can be specified instead of the single `ease` value." + + value: class extends Op + pattern = -evt.clock + evt!*0 + setup: (inputs, scope) => + { clock, events } = pattern\match inputs + + super + clock: Input.hot clock or scope\get '*clock*' + events: [Input.hot e for e in *events] + + @state or= { offset: 0, events: {} } + @setup_out '~', T.num, @state.offset + + tick: => + for inp in *@inputs.events + if evt = inp! + amp = evt.amp or 1 + del = evt.del or 0 + dur = evt.dur or 1 + + on = { + i: -del + amp: amp + dur: evt.in or evt.dur + ease: evt.easein or evt.ease or "quad.out" + } + on.i = on.i / on.dur + @state.events[on] = true + + if evt.out + off = { + i: -(del + on.dur), + amp: -amp, + dur: evt.out + ease: evt.easeout or evt.ease or "quad.out" + } + off.i = off.i / off.dur + @state.events[off] = true + + if clk = @inputs.clock! + dt = clk.dt + accum = 0 + + for evt in pairs @state.events + evt.i += dt / evt.dur + + if evt.i >= 1 + @state.offset += evt.amp + @state.events[evt] = nil + elseif evt.i >= 0 + t = 1 - evt.i + t = 1 - t * t + accum += t * evt.amp + + @out\set @state.offset + accum + + delay = Constant.meta meta: name: 'delay!' @@ -416,6 +495,7 @@ RTNode 'bang-seq': bang_seq :smooth + :spring 'delay!': delay '*clock*': default_clock |
