Skip to content

osuushi/Smooth.js

Repository files navigation

Smooth.js

Table of Contents

        What is this for?
        How do I use it?
                Configuration
                        Interpolation Methods
                        Clipping Modes
                        Scaling
                        Validation
                Interpolating Vectors
                Function Properties
        Future Plans

Smooth.js takes an array of numbers or vectors and returns a parametric function that continuously interpolates that array. Smooth.js supports several interpolation methods, and flexible options for boundary behavior.

Smooth.js is written in clean, easy-to-read CoffeeScript, and has no external dependencies. It is licensed under the permissive MIT license, so you can use it in just about any project.

This demo (requires a modern browser) gives a visualization of the interpolation Smooth.js performs.

You can compile to javascript from the Smooth.coffee source file, or download the latest compiled release

Smooth.js exposes one public function, Smooth. The simplest use case is like this:

var s = Smooth([1, 2, 3, 4]);
console.log(s(1));			// => 2
console.log(s(1.5));		// => 2.5

The first line will make s a function that interpolates the array [1,2,3,4] as a cubic spline. the second line will print out index 1 of the array, which is 2. The third line interpolates halfway between indexes 1 and 2 of the array, yielding 2.5

The Smooth function can take an object as an optional second argument which specifies the configuration options described below.

        (For visual illustrations of these interpolation methods see the wiki)

The method config option specifies the interpolation method. There are three possible values for this option:

Nearest Neighbor

Smooth.METHOD_NEAREST = 'nearest'

This interpolation method is like stair steps. The parameter is simply rounded to the nearest integer and that element of the array is returned.

Time complexity to interpolate a point: O(1)

Linear

Smooth.METHOD_LINEAR = 'linear'

Linear interpolation creates line segments between the input points and interpolates along those segments. While smoother than nearest neighbor, this interpolation method produces sharp corners where the parameter is an integer.

Time complexity to interpolate a point: O(1)

Cubic

Smooth.METHOD_CUBIC = 'cubic'

This is the default interpolation method, which turns the array into a cubic Hermite spline. This method is very smooth and will not produce sharp corners.

The cubic Hermite spline used by Smooth.js is known as a cardinal spline. This kind of spline allows you to choose a "tension" parameter as the cubicTension field of the config object. Two constants are provided for this value: Smooth.CUBIC_TENSION_DEFAULT and Smooth.CUBIC_TENSION_CATMULL_ROM, but you can use any value between 0 and 1.

Smooth.CUBIC_TENSION_CATMULL_ROM produces a Catmull-Rom spline, which is commonly used for inbetweening keyframe animations. It is equal to a tension parameter of zero.

Smooth.CUBIC_TENSION_DEFAULT is an alias for CUBIC_TENSION_CATMULL_ROM.

Time complexity to interpolate a point: O(1)

Windowed sinc filter

Smooth.METHOD_SINC = 'sinc'

Interpolate by applying a windowed version of the sinc filter.

You can specify the size of the window with the sincFilterSize config parameter. The window will extend by this value in either direction from the origin. This value must be a positive integer. The default is 2.

You must also provide a window function via the sincWindow configuration option. This function should take one numeric parameter and return a numeric value. For example:

var s = Smooth([1, 2, 3], {
	method: 'sinc',
	sincFilterSize: 2
	sincWindow: function(x) { return Math.exp(-x * x); }
});

will create a sinc filter with a Gaussian window function.

The window function is implicitly further multiplied by a rectangular window determined by sincFilterSize, so

	sincWindow: function(x) { return 1; }

will create a sinc filter with a simple rectangular window function.

Time complexity to interpolate a point: O(N), where N = sincFilterSize (assuming your window function is O(1))

Lanczos

Smooth.METHOD_LANCZOS = 'lanczos'

Interpolate via Lanczos resampling. Convolves the input array by a Lanczos kernel to produce intermediate points.

The size of the Lanczos kernel can be specified via the lanczosFilterSize config parameter (default = 2). This parameter should be a positive integer.

Note: This filter is actually a specific case of the sinc filter. The lanczosFilterSize config option is an alias for sincFilterSize, and the Lanczos window function is automatically created for you based on this parameter.

Time complexity to interpolate a point: O(N), where N = lanczosFilterSize

In addition to interpolating an array, Smooth.js allows you to specify the behavior of the output function when the parameter is outside the array's bounds. This also has an effect on cubic and sinc interpolation when interpolating near the array's bounds.

The clip config option specifies the clipping mode, and can take the following values:

Clamp

Smooth.CLIP_CLAMP = 'clamp'

The default clipping mode; the ends of the array are simply repeated to infinity.

Zero

Smooth.CLIP_ZERO = 'zero'

Outside the array bounds, the value drops to zero.

Periodic

Smooth.CLIP_PERIODIC = 'periodic'

The whole array repeats infinitely in both directions. This is useful, for example, if you want values for a looping animation.

Mirror

Smooth.CLIP_MIRROR = 'mirror'

Repeats the array infinitely in both directions, reflecting each time. For example, if you applied this to [1, 2, 3, 4] then the result would be [1, 2, 3, 4, 3, 2, 1, 2, 3, 4...]. Useful for "loop back and forth" style animations, for example.

The scaleTo config option allows you to scale the domain of the function. The default value is 0, which tells Smooth.js to leave the domain like the original array, so that for any integer i, s(i) == arr[i].

Setting the scaleTo option to non-zero will scale the domain to that value. For example:

var s = Smooth([1, 2, 3], { scaleTo: 1 });
console.log(s(0));		// => 1
console.log(s(1 / 2));		// => 2
console.log(s(1));		// => 3

You can also provide a range for the scaleTo option, as an array of two numbers. This will scale the function to fit in that range. For example

var s = Smooth([1, 2, 3], { scaleTo: [10, 12] });
console.log(s(10));		// => 1
console.log(s(12));		// => 2
console.log(s(14));		// => 3

When using Smooth.CLIP_PERIODIC, the behavior of the scaleTo option is slightly different; instead of scaling to place the end of the array at the value of scaleTo, the value is used as the period of the function.

For the sake of readability, the period config option is aliased to scaleTo. Thus:

var s = Smooth([1, 2, 3], { period: 1, clip:Smooth.CLIP_PERIODIC });
console.log(s(0));		// => 1
console.log(s(1 / 3));		// => 2
console.log(s(2 / 3));		// => 3
console.log(s(1));		// => 1

By default the input array you pass to Smooth will be examined thoroughly to make sure that the input is valid, and exceptions will be thrown if any problems are found. This can be a performance consideration if you are dealing with large amounts of data.

This deep validation behavior can be disabled globally like so:

Smooth.deepValidation = false;

This will cause the Smooth function to only validate the first element of each array, and only minimally.

So far all of the example code we've seen has used scalar arrays, but Smooth.js supports interpolation of vectors of arbitrary dimension. Simply supply the vectors as arrays. For example, this code:

var points = [
	[0, 1],
	[4, 5],
	[5, 3],
	[2, 0]
];

var path = Smooth(points, {
	method: Smooth.METHOD_CUBIC, 
	clip: Smooth.CLIP_PERIODIC, 
	cubicTension: Smooth.CUBIC_TENSION_CATMULL_ROM
});

could be used to create a path function along which to animate a sprite in a loop.

The function returned by Smooth() has a few properties which provide information about it. Changing these properties has no effect on the function.

s.config : a shallow copy of the config object you provided when creating the function. If you did not provide a config object, s.config will be an empty object. Note that this is a shallow copy, so any modifications you make to object properties of the config will be reflected by s.config, although the behavior of s itself will not be affected.

s.domain : The interval on which the function is defined. Outside of this interval, the function's behavior is determined by the clipping mode. This property is affected by the scaleTo parameter.

s.count : The number of elements in the input array.

s.dimension : If the input array contains scalar numbers, s.dimension will be 'scalar'. If the input array contains vectors, s.dimension will be the vector size.

  • Interpolation of non-uniform arrays (objects with arbitrary numeric indexes)
  • More interpolation methods
  • Custom interpolation methods (maybe)

About

Turn arrays into smooth functions.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published