Skip to content

Commit

Permalink
feat(transducers): add interpolate() iterator, update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Dec 28, 2018
1 parent ea0dee0 commit 846ab5c
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 0 deletions.
18 changes: 18 additions & 0 deletions packages/transducers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This project is part of the
- [Bitstream](#bitstream)
- [Base64 & UTF-8 en/decoding](#base64--utf-8-endecoding)
- [Weighted random choices](#weighted-random-choices)
- [Keyframe interpolation](#keyframe-interpolation)
- [API](#api)
- [Types](#types)
- [IReducible](#ireducible)
Expand Down Expand Up @@ -507,6 +508,22 @@ tx.transduce(tx.take(1000), tx.frequencies(), tx.choices("abcd", [1, 0.5, 0.25,
// Map { 'c' => 132, 'a' => 545, 'b' => 251, 'd' => 72 }
```

### Keyframe interpolation

See [`interpolate()`](https://github.com/thi-ng/umbrella/tree/master/packages/transducers/src/iter/interpolate.ts) for details.

```ts
[...tx.interpolate(
10,
(a, b) => [a,b],
([a, b], t) => Math.floor(a + (b-a) * t),
[0.2, 100],
[0.5, 200],
[0.8, 0]
)]
// [ 100, 100, 100, 133, 166, 200, 133, 66, 0, 0, 0 ]
```

## API

_Documentation is slowly forthcoming in the form of doc comments (incl.
Expand Down Expand Up @@ -764,6 +781,7 @@ tx.transduce(tx.map((x) => x*10), tx.push(), tx.range(4))
- [choices](https://github.com/thi-ng/umbrella/tree/master/packages/transducers/src/iter/choices.ts)
- [concat](https://github.com/thi-ng/umbrella/tree/master/packages/transducers/src/iter/concat.ts)
- [cycle](https://github.com/thi-ng/umbrella/tree/master/packages/transducers/src/iter/cycle.ts)
- [interpolate](https://github.com/thi-ng/umbrella/tree/master/packages/transducers/src/iter/interpolate.ts)
- [iterate](https://github.com/thi-ng/umbrella/tree/master/packages/transducers/src/iter/iterate.ts)
- [keys](https://github.com/thi-ng/umbrella/tree/master/packages/transducers/src/iter/keys.ts)
- [normRange](https://github.com/thi-ng/umbrella/tree/master/packages/transducers/src/iter/normRange.ts)
Expand Down
1 change: 1 addition & 0 deletions packages/transducers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export * from "./iter/as-iterable";
export * from "./iter/choices";
export * from "./iter/concat";
export * from "./iter/cycle";
export * from "./iter/interpolate";
export * from "./iter/iterate";
export * from "./iter/keys";
export * from "./iter/norm-range";
Expand Down
68 changes: 68 additions & 0 deletions packages/transducers/src/iter/interpolate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { repeat } from "./repeat";
import { normRange } from "./norm-range";

/**
* Takes a number of keyframe tuples (`stops`) and yields a sequence of
* `n` equally spaced, interpolated values. Keyframes are defined as
* `[pos, value]`, where `pos` must be a normalized value in [0,1]
* interval.
*
* Interpolation happens in two stages: First the given `init` function
* is called for each new key frame pair to produce a single interval
* type. Then for each result value calls `mix` with the current
* interval and interpolation time value `t` (normalized). The iterator
* yields results of these `mix()` function calls.
*
* The given keyframe positions don't need to cover the full [0,1] range
* and interpolated values before the 1st or last keyframe will yield
* the value of the 1st/last keyframe.
*
* ```
* [...interpolate(
* 10,
* (a, b) => [a,b],
* ([a, b], t) => Math.floor(a + (b - a) * t),
* [0.2, 100],
* [0.5, 200],
* [0.8, 0]
* )]
* // [ 100, 100, 100, 133, 166, 200, 133, 66, 0, 0, 0 ]
* ```
*
* @param n
* @param init
* @param mix
* @param stops
*/
export function* interpolate<A, B, C>(
n: number,
init: (a: A, b: A) => B,
mix: (interval: B, t: number) => C,
...stops: [number, A][]
): IterableIterator<C> {
let l = stops.length;
if (l < 1) return;
if (l === 1) {
yield* repeat(mix(init(stops[0][1], stops[0][1]), 0), n);
}
if (stops[l - 1][0] < 1) {
stops.push([1, stops[l - 1][1]]);
}
if (stops[0][0] > 0) {
stops.unshift([0, stops[0][1]]);
}
let start = stops[0][0];
let end = stops[1][0];
let interval = init(stops[0][1], stops[1][1]);
let i = 1;
l = stops.length - 1;
for (let t of normRange(n)) {
if (t > end && t < 1) {
while (i < l && t > stops[i][0]) i++;
start = stops[i - 1][0];
end = stops[i][0];
interval = init(stops[i - 1][1], stops[i][1]);
}
yield mix(interval, end !== start ? (t - start) / (end - start) : 0);
}
}

0 comments on commit 846ab5c

Please sign in to comment.