Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

curve-store

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

curve-store - npm Package Compare versions

Comparing version
0.0.1
to
1.0.0
+1
-3
examples/mouse-move.js
// Import libraries
import { createStore } from '../src';
import { getPointAfter, getPointBefore } from '../src/utils';
import { linear, derivative } from '../src/samplers';

@@ -9,3 +8,2 @@ import raf from 'raf';

const size = 100;
let time = 0;
document.body.style.padding = 0;

@@ -22,4 +20,4 @@ document.body.style.margin = 0;

// The start of the interesting part
let time = 0;
const store = createStore({

@@ -26,0 +24,0 @@ x: linear('x'),

{
"name": "curve-store",
"version": "0.0.1",
"version": "1.0.0",
"description": "Redux-inspired store for dealing with continuous values",

@@ -8,3 +8,4 @@ "main": "src/index.js",

"example": "budo examples/graphs.js --live -o -- -t [ babelify --presets [ es2015 ] ]",
"test": "semistandard && mocha --compilers js:babel-core/register"
"test": "semistandard && mocha --compilers js:babel-core/register",
"syntax-fix": "semistandard --fix"
},

@@ -22,6 +23,7 @@ "repository": {

"dependencies": {
"lodash.sortedindexby": "^4.5.1"
"lodash": "^4.15.0"
},
"devDependencies": {
"babel-cli": "^6.11.4",
"babel-preset-es2015": "^6.13.2",
"babelify": "^7.3.0",

@@ -28,0 +30,0 @@ "budo": "^8.3.0",

+120
-9
# curve-store
## Examples
A store for dealing with continuous values. Useful for complex animations.
Continue reading for background and example usage. [Click here for a demo](https://mathisonian.github.io/curve-store/).
### Simple Usage
## Motivation
*The idea for this module came from discussions with [@mikolalysenko](https://github.com/mikolalysenko/).
Credit largely goes to him. See [filtered-vector](https://github.com/mikolalysenko/filtered-vector) for
prior art.*
Curve store is a state container that intelligently deals with mapping discrete
values to a continuous curve. Its primary intended use case is for dealing with
complex animations over time, though there may be other applications.
The idea is that animation can be defined as a series of positions over time:
given an object at position `x, y` at time `t`, we should be able to define an
animation by promising that the object will be at some other position `x', y'` at
some future time `t'`, and infer the points along the path.
This is what `curve-store` gives you, a way to define state at a given time:
```js
store.set(currentTime, { x: x, y: y});
store.set(currentTime + 1000, { x: xprime, y: yprime});
```
and a way to sample points in between:
```js
store.sample(currentTime + 500);
// give { x: xval, y: yval } interpolated based on the points set above
```
Users can define how they want the interpolation to be handled. There are a few
built in helpers, for example:
```js
import { createStore } from 'curve-store';
import { linear } from 'curve-store/samplers';
const store = createStore({
x: linear('x'),
y: linear('y')
})
```
defines basic linear interpolation. There are also calculus functions to help
build out more complicated curves:
```js
import { createStore } from 'curve-store';
import { linear, derivative, integral } from 'curve-store/samplers';
const store = createStore({
position: {
x: linear('x'),
y: linear('y')
},
velocity: {
x: derivative('x'),
y: derivative('y')
},
distance: {
x: integral('x'),
y: integral('y')
}
});
```
You can also provide custom sampling functions, to get e.g. different easing curves
(see below for more details).
## Installation
```
$ npm install --save curve-store
```
## Simple Example
```js
import { createStore } from 'curve-store';
import { linear, derivative } from 'curve-store/samplers';

@@ -24,2 +99,45 @@

## API
### `createStore(samplers)`
Creates a new `curve-store` that maps discrete input values onto a set
of continuous output values. The samplers object defines this mapping and defines
how to interpolate between points.
Basic usage:
```js
const store = createStore({
outputX: linear('inputX')
});
```
### `store.set(time, values)`
Set values at a particular point in time.
Example:
```js
store.set(0, { inputX: 0 });
store.set(1, { inputX: 0 });
```
### store.sample(time)
Sample points at a particular time.
Example:
```js
store.sample(0.5);
// -> outputs { outputX: 0.5 }
```
The way that sampling occurs is defined based on the samplers object passed
to `createStore`.
### Custom sampling

@@ -51,11 +169,4 @@

## Installation
```
$ npm install --save curve-store
```
## LICENSE
MIT
import { setAsLastPoint } from './utils';
import { linear } from './samplers';
import { isArray, isObject, isFunction } from 'lodash';
const runSampler = (sampler, time, state, sample) => {
if (isFunction(sampler)) {
return sampler(time, state, sample);
} else if (isArray(sampler)) {
return sampler.map((s) => { runSampler(s, time, state, sample); });
} else if (isObject(sampler)) {
const retObj = {};
Object.keys(sampler).forEach((key) => {
retObj[key] = runSampler(sampler[key], time, state, sample);
});
return retObj;
}
};
export default (samplers) => {

@@ -20,10 +36,12 @@ const state = {};

if (typeof keys === 'string') {
return samplers[keys](time, state);
const s = isFunction(samplers[keys]) ? samplers[keys] : linear(keys);
return runSampler(s, time, state);
}
const checkKeys = Array.isArray(keys);
const checkKeys = isArray(keys);
Object.keys(samplers).forEach((samplerName) => {
if (!checkKeys || keys.indexOf(samplerName)) {
ret[samplerName] = samplers[samplerName](time, state, sample);
const sampler = samplers[samplerName];
ret[samplerName] = runSampler(sampler, time, state, sample);
}

@@ -30,0 +48,0 @@ });

@@ -1,2 +0,3 @@

import { getPointBefore, getPointAfter } from '../utils';
import { getPointBefore, getPointAfter, snap } from '../utils';
import { isFunction, memoize } from 'lodash';

@@ -21,8 +22,14 @@ const linear = (name) => {

const derivative = (name, delta) => {
delta = delta || 0.001;
return (t, state, sample) => {
const delta = delta || 0.0001;
const x1 = sample(t - delta, name);
const x2 = sample(t, name);
let x1;
let x2;
if (isFunction(name)) {
x1 = name(t - delta, state, sample);
x2 = name(t, state, sample);
} else {
x1 = sample(t - delta, name);
x2 = sample(t, name);
}
return (x2 - x1) / delta;

@@ -32,5 +39,25 @@ };

const integral = (name, delta) => {
delta = delta || 0.01;
const recursiveIntegral = memoize((t, state, sample) => {
if (t === 0) {
return 0;
}
const snapped = snap(t, delta);
if (snapped === t) {
return (delta * (sample(t, name) + sample(t - delta, name)) / 2) + recursiveIntegral(t - delta, state, sample);
}
return ((t - snapped) * (sample(t, name) + sample(snapped, name)) / 2) + recursiveIntegral(snapped, state, sample);
});
return recursiveIntegral;
};
export {
linear,
derivative
derivative,
integral
};

@@ -1,6 +0,6 @@

import sortedIndex from 'lodash.sortedindexby';
import { sortedIndexBy } from 'lodash';
const set = (array, time, value) => {
const arrayObj = { time, value };
const index = sortedIndex(array, arrayObj, 'time');
const index = sortedIndexBy(array, arrayObj, 'time');
array.splice(index, 0, value);

@@ -11,3 +11,3 @@ };

const arrayObj = { time, value };
const index = sortedIndex(array, arrayObj, 'time');
const index = sortedIndexBy(array, arrayObj, 'time');
array.splice(index, array.length - index, arrayObj);

@@ -17,3 +17,3 @@ };

const getPointsBefore = (array, time, n) => {
const index = sortedIndex(array, { time }, 'time');
const index = sortedIndexBy(array, { time }, 'time');
return array.slice(Math.max(0, index - n), index);

@@ -23,3 +23,3 @@ };

const getPointsAfter = (array, time, n) => {
const index = sortedIndex(array, { time }, 'time');
const index = sortedIndexBy(array, { time }, 'time');
return array.slice(index, index + n);

@@ -38,4 +38,20 @@ };

const snap = (t, delta) => {
let factor = 1;
if (delta < 0) {
factor = 1 / delta;
}
const scaledT = factor * t;
const modT = scaledT % (delta * factor);
if (modT === 0) {
return t;
}
return (scaledT - modT) / factor;
};
export {
set,
snap,
setAsLastPoint,

@@ -42,0 +58,0 @@ getPointAfter,

@@ -5,5 +5,7 @@ /*global describe, it */

import { createStore } from '../src';
import { getPointBefore, getPointAfter } from '../src/utils';
import { linear } from '../src/samplers';
import { getPointBefore, getPointAfter, snap } from '../src/utils';
import { linear, derivative, integral } from '../src/samplers';
const epsilon = 0.00001;
describe('curve-store tests', () => {

@@ -88,2 +90,18 @@ it('should create a new store', () => {

it('should handle nested samplers', () => {
const store = createStore({
x: {
position: linear('x'),
velocity: derivative('x')
}
});
store.set(0, { x: 0 });
store.set(1, { x: 1 });
let sample = store.sample(0.25);
expect(sample.x.position).toEqual(0.25);
expect(Math.abs(sample.x.velocity - 1)).toBeLessThan(epsilon);
});
it('should sample values correctly at points', () => {

@@ -103,2 +121,56 @@ const store = createStore({

});
it('should get derivative correctly', () => {
const store = createStore({
d: derivative('myKey')
});
store.set(0, { myKey: 0 });
store.set(1, { myKey: 1 });
let sample = store.sample(0.5);
expect(Math.abs(sample.d - 1)).toBeLessThan(epsilon);
});
it('should get the second derivative correctly', () => {
const store = createStore({
d: derivative(derivative('myKey'))
});
store.set(0, { myKey: 0 });
store.set(1, { myKey: 1 });
let sample = store.sample(0.5);
expect(sample).toEqual({ d: 0 });
});
it('should snap to a value correctly', () => {
const t = 0.015;
const s = snap(t, 0.01);
expect(s).toEqual(0.01);
});
it('should snap to a presnapped value correctly', () => {
const t = 0.01;
const s = snap(t, 0.01);
expect(s).toEqual(0.01);
});
it('should compute an integral correctly', () => {
const store = createStore({
i: integral('myKey')
});
store.set(0, { myKey: 0 });
store.set(1, { myKey: 1 });
let sample = store.sample(0.25);
expect(Math.abs(sample.i - 0.03125)).toBeLessThan(epsilon);
sample = store.sample(0.5);
expect(Math.abs(sample.i - 0.125)).toBeLessThan(epsilon);
sample = store.sample(1);
expect(Math.abs(sample.i - 0.5)).toBeLessThan(epsilon);
});
});