Repository: osuushi/Smooth.js
Branch: master
Commit: 62233300e50f
Files: 22
Total size: 36.7 KB
Directory structure:
gitextract_dud26c8f/
├── ChangeLog.md
├── Readme.md
├── Smooth.coffee
├── Smooth.js MIT license.txt
├── bower.json
├── sindemo.coffee
└── test/
├── specs/
│ ├── clip-clamp.spec.coffee
│ ├── clip-mirror.spec.coffee
│ ├── clip-perodic.spec.coffee
│ ├── clip-zero.spec.coffee
│ ├── cubic.spec.coffee
│ ├── exceptions.spec.coffee
│ ├── lanczos.spec.coffee
│ ├── linear.spec.coffee
│ ├── misc.spec.coffee
│ ├── nearest.spec.coffee
│ ├── properties.spec.coffee
│ ├── scale.spec.coffee
│ ├── sinc.spec.coffee
│ ├── util.coffee
│ └── vector.spec.coffee
└── tests.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: ChangeLog.md
================================================
#Smooth.js Change Log
This is a log of changes to the library itself. Other changes, like tests, are omitted.
##0.1.7
* Added properties to smoothed functions: `config`, `domain`, `count`, and `dimension`.
* Made code more concise.
##0.1.6
* Fixed bug where `Smooth()` would modify the config object passed to it, rather than working on a copy
##0.1.5
* Lanczos interpolation. See [Lanczos resampling](http://en.wikipedia.org/wiki/Lanczos_resampling).
* Windowed sinc filter interpolation. See [sinc filter](http://en.wikipedia.org/wiki/Sinc_filter).
* Deep input validation; input arrays are now thoroughly examined when calling `Smooth()`. Disable with
`Smooth.deepValidation = false`.
* `scaleTo` with ranges; you can now scale the function's domain to fit to a specific range.
##0.1.3
* New `scaleTo` config option scales the output function's domain.
* Changed enums to string constants. For example, you can use either `Smooth.METHOD_CUBIC` or just `'cubic'`.
##0.1.2
* Fixed tension parameter bug in cubic splines. Catmull-Rom splines are now the default.
##0.1.0
(Initial release)
================================================
FILE: Readme.md
================================================

#### Table of Contents
[ What is this for?](#rm-what)<br/>
[ How do I use it?](#rm-how)<br/>
[ Configuration](#rm-config)<br/>
[ Interpolation Methods](#rm-method)<br/>
[ Clipping Modes](#rm-clip)<br/>
[ Scaling](#rm-scale)<br/>
[ Validation](#rm-valid)<br/>
[ Interpolating Vectors](#rm-vec)<br/>
[ Function Properties](#rm-prop)<br/>
[ Future Plans](#rm-future)<br/>
<a name = "rm-what" />
# What is this for?
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](http://osuushi.github.com/plotdemo016.html) (requires a modern browser) gives a
visualization of the interpolation Smooth.js performs.
<a name = "rm-how" />
# How do I use it?
You can compile to javascript from the Smooth.coffee source file, or
[download the latest compiled release](https://github.com/downloads/osuushi/Smooth.js/Smooth-0.1.7.js)
Smooth.js exposes one public function, `Smooth`. The simplest use case is like this:
```js
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
<a name = "rm-config" />
## Configuration
The `Smooth` function can take an object as an optional second argument which specifies the configuration
options described below.
<a name = "rm-method" />
### Interpolation Methods
(For visual illustrations of these interpolation methods see
[the wiki](https://github.com/osuushi/Smooth.js/wiki/Interpolation-Methods))
The `method` config option specifies the interpolation method. There are three possible values for this
option:
#### Nearest Neighbor
```js
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
```js
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
```js
Smooth.METHOD_CUBIC = 'cubic'
```
This is the default interpolation method, which turns the array into a
[cubic Hermite spline](http://en.wikipedia.org/wiki/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](http://en.wikipedia.org/wiki/Cubic_hermite_spline#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](http://en.wikipedia.org/wiki/Cubic_hermite_spline#Catmull.E2.80.93Rom_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
```js
Smooth.METHOD_SINC = 'sinc'
```
Interpolate by applying a windowed version of the [sinc filter](http://en.wikipedia.org/wiki/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:
```js
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
```js
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
```js
Smooth.METHOD_LANCZOS = 'lanczos'
```
Interpolate via [Lanczos resampling](http://en.wikipedia.org/wiki/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`
<a name = "rm-clip" />
### Clipping modes
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
```js
Smooth.CLIP_CLAMP = 'clamp'
```
The default clipping mode; the ends of the array are simply repeated to infinity.
#### Zero
```js
Smooth.CLIP_ZERO = 'zero'
```
Outside the array bounds, the value drops to zero.
#### Periodic
```js
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
```js
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.
<a name = "rm-scale" />
### Scaling
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:
```js
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
```js
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:
```js
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
```
<a name="rm-valid" />
### Validation
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:
```js
Smooth.deepValidation = false;
```
This will cause the Smooth function to only validate the first element of each array, and only minimally.
<a name = "rm-vec" />
## Interpolating Vectors
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:
```js
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.
<a name = "rm-prop" />
## Function Properties
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.
<a name = "rm-future" />
# Future Plans
* Interpolation of non-uniform arrays (objects with arbitrary numeric indexes)
* More interpolation methods
* Custom interpolation methods (maybe)
================================================
FILE: Smooth.coffee
================================================
###
Smooth.js version 0.1.7
Turn arrays into smooth functions.
Copyright 2012 Spencer Cohen
Licensed under MIT license (see "Smooth.js MIT license.txt")
###
###Constants (these are accessible by Smooth.WHATEVER in user space)###
Enum =
###Interpolation methods###
METHOD_NEAREST: 'nearest' #Rounds to nearest whole index
METHOD_LINEAR: 'linear'
METHOD_CUBIC: 'cubic' # Default: cubic interpolation
METHOD_LANCZOS: 'lanczos'
METHOD_SINC: 'sinc'
###Input clipping modes###
CLIP_CLAMP: 'clamp' # Default: clamp to [0, arr.length-1]
CLIP_ZERO: 'zero' # When out of bounds, clip to zero
CLIP_PERIODIC: 'periodic' # Repeat the array infinitely in either direction
CLIP_MIRROR: 'mirror' # Repeat infinitely in either direction, flipping each time
### Constants for control over the cubic interpolation tension ###
CUBIC_TENSION_DEFAULT: 0 # Default tension value
CUBIC_TENSION_CATMULL_ROM: 0
defaultConfig =
method: Enum.METHOD_CUBIC #The interpolation method
cubicTension: Enum.CUBIC_TENSION_DEFAULT #The cubic tension parameter
clip: Enum.CLIP_CLAMP #The clipping mode
scaleTo: 0 #The scale-to value (0 means don't scale) (can also be a range)
sincFilterSize: 2 #The size of the sinc filter kernel (must be an integer)
sincWindow: undefined #The window function for the sinc filter
###Index clipping functions###
clipClamp = (i, n) -> Math.max 0, Math.min i, n - 1
clipPeriodic = (i, n) ->
i = i % n #wrap
i += n if i < 0 #if negative, wrap back around
i
clipMirror = (i, n) ->
period = 2*(n - 1) #period of index mirroring function
i = clipPeriodic i, period
i = period - i if i > n - 1 #flip when out of bounds
i
###
Abstract scalar interpolation class which provides common functionality for all interpolators
Subclasses must override interpolate().
###
class AbstractInterpolator
constructor: (array, config) ->
@array = array.slice 0 #copy the array
@length = @array.length #cache length
#Set the clipping helper method
throw "Invalid clip: #{config.clip}" unless @clipHelper = {
clamp: @clipHelperClamp
zero: @clipHelperZero
periodic: @clipHelperPeriodic
mirror: @clipHelperMirror
}[config.clip]
# Get input array value at i, applying the clipping method
getClippedInput: (i) ->
#Normal behavior for indexes within bounds
if 0 <= i < @length
@array[i]
else
@clipHelper i
clipHelperClamp: (i) -> @array[clipClamp i, @length]
clipHelperZero: (i) -> 0
clipHelperPeriodic: (i) -> @array[clipPeriodic i, @length]
clipHelperMirror: (i) -> @array[clipMirror i, @length]
interpolate: (t) -> throw 'Subclasses of AbstractInterpolator must override the interpolate() method.'
#Nearest neighbor interpolator (round to whole index)
class NearestInterpolator extends AbstractInterpolator
interpolate: (t) -> @getClippedInput Math.round t
#Linear interpolator (first order Bezier)
class LinearInterpolator extends AbstractInterpolator
interpolate: (t) ->
k = Math.floor t
#Translate t to interpolate between k and k+1
t -= k
return (1-t)*@getClippedInput(k) + (t)*@getClippedInput(k+1)
class CubicInterpolator extends AbstractInterpolator
constructor: (array, config)->
#clamp cubic tension to [0,1] range
@tangentFactor = 1 - Math.max 0, Math.min 1, config.cubicTension
super
# Cardinal spline with tension 0.5)
getTangent: (k) -> @tangentFactor*(@getClippedInput(k + 1) - @getClippedInput(k - 1))/2
interpolate: (t) ->
k = Math.floor t
m = [(@getTangent k), (@getTangent k+1)] #get tangents
p = [(@getClippedInput k), (@getClippedInput k+1)] #get points
#Translate t to interpolate between k and k+1
t -= k
t2 = t*t #t^2
t3 = t*t2 #t^3
#Apply cubic hermite spline formula
return (2*t3 - 3*t2 + 1)*p[0] + (t3 - 2*t2 + t)*m[0] + (-2*t3 + 3*t2)*p[1] + (t3 - t2)*m[1]
{sin, PI} = Math
#Normalized sinc function
sinc = (x) -> if x is 0 then 1 else sin(PI*x)/(PI*x)
#Make a lanczos window function for a given filter size 'a'
makeLanczosWindow = (a) -> (x) -> sinc(x/a)
#Make a sinc kernel function by multiplying the sinc function by a window function
makeSincKernel = (window) -> (x) -> sinc(x)*window(x)
class SincFilterInterpolator extends AbstractInterpolator
constructor: (array, config) ->
super
#Create the lanczos kernel function
@a = config.sincFilterSize
#Cannot make sinc filter without a window function
throw 'No sincWindow provided' unless config.sincWindow
#Window the sinc function to make the kernel
@kernel = makeSincKernel config.sincWindow
interpolate: (t) ->
k = Math.floor t
#Convolve with Lanczos kernel
sum = 0
sum += @kernel(t - n)*@getClippedInput(n) for n in [(k - @a + 1)..(k + @a)]
sum
#Extract a column from a two dimensional array
getColumn = (arr, i) -> (row[i] for row in arr)
#Take a function with one parameter and apply a scale factor to its parameter
makeScaledFunction = (f, baseScale, scaleRange) ->
if scaleRange.join is '0,1'
f #don't wrap the function unecessarily
else
scaleFactor = baseScale/(scaleRange[1] - scaleRange[0])
translation = scaleRange[0]
(t) -> f scaleFactor*(t - translation)
getType = (x) -> Object::toString.call(x)[('[object '.length)...-1]
#Throw exception if input is not a number
validateNumber = (n) ->
throw 'NaN in Smooth() input' if isNaN n
throw 'Non-number in Smooth() input' unless getType(n) is 'Number'
throw 'Infinity in Smooth() input' unless isFinite n
#Throw an exception if input is not a vector of numbers which is the correct length
validateVector = (v, dimension) ->
throw 'Non-vector in Smooth() input' unless getType(v) is 'Array'
throw 'Inconsistent dimension in Smooth() input' unless v.length is dimension
validateNumber n for n in v
return
isValidNumber = (n) -> (getType(n) is 'Number') and isFinite(n) and not isNaN(n)
normalizeScaleTo = (s) ->
invalidErr = "scaleTo param must be number or array of two numbers"
switch getType s
when 'Number'
throw invalidErr unless isValidNumber s
s = [0, s]
when 'Array'
throw invalidErr unless s.length is 2
throw invalidErr unless isValidNumber(s[0]) and isValidNumber(s[1])
else throw invalidErr
return s
shallowCopy = (obj) ->
copy = {}
copy[k] = v for own k,v of obj
copy
Smooth = (arr, config = {}) ->
#Properties to copy to the function once it is created
properties = {}
#Make a copy of the config object to modify
config = shallowCopy config
#Make another copy of the config object to save to the function
properties.config = shallowCopy config
#Alias 'period' to 'scaleTo'
config.scaleTo ?= config.period
#Alias lanczosFilterSize to sincFilterSize
config.sincFilterSize ?= config.lanczosFilterSize
config[k] ?= v for own k,v of defaultConfig #fill in defaults
#Get the interpolator class according to the configuration
throw "Invalid method: #{config.method}" unless interpolatorClass = {
nearest: NearestInterpolator
linear: LinearInterpolator
cubic: CubicInterpolator
lanczos: SincFilterInterpolator #lanczos is a specific case of sinc filter
sinc: SincFilterInterpolator
}[config.method]
if config.method is 'lanczos'
#Setup lanczos window
config.sincWindow = makeLanczosWindow config.sincFilterSize
#Make sure there's at least one element in the input array
throw 'Array must have at least two elements' if arr.length < 2
#save count property
properties.count = arr.length
#See what type of data we're dealing with
smoothFunc = switch getType arr[0]
when 'Number' #scalar
properties.dimension = 'scalar'
#Validate all input if deep validation is on
validateNumber n for n in arr if Smooth.deepValidation
#Create the interpolator
interpolator = new interpolatorClass arr, config
#make function that runs the interpolator
(t) -> interpolator.interpolate t
when 'Array' # vector
properties.dimension = dimension = arr[0].length
throw 'Vectors must be non-empty' unless dimension
#Validate all input if deep validation is on
validateVector v, dimension for v in arr if Smooth.deepValidation
#Create interpolator for each column
interpolators = (new interpolatorClass(getColumn(arr, i), config) for i in [0...dimension])
#make function that runs the interpolators and puts them into an array
(t) -> (interpolator.interpolate(t) for interpolator in interpolators)
else throw "Invalid element type: #{getType arr[0]}"
# Determine the end of the original function's domain
if config.clip is 'periodic' then baseDomainEnd = arr.length #after last element for periodic
else baseDomainEnd = arr.length - 1 #at last element for non-periodic
config.scaleTo ||= baseDomainEnd #default scales to the end of the original domain for no effect
properties.domain = normalizeScaleTo config.scaleTo
smoothFunc = makeScaledFunction smoothFunc, baseDomainEnd, properties.domain
properties.domain.sort()
###copy properties###
smoothFunc[k] = v for own k,v of properties
return smoothFunc
#Copy enums to Smooth
Smooth[k] = v for own k,v of Enum
Smooth.deepValidation = true
(exports ? window).Smooth = Smooth
================================================
FILE: Smooth.js MIT license.txt
================================================
Smooth.js Copyright (c) 2012 Spencer Cohen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without
limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
================================================
FILE: bower.json
================================================
{
"name": "smooth.js",
"homepage": "https://github.com/osuushi/Smooth.js",
"authors": [
"yournamehere <someone@somewhere.com>"
],
"description": "Smooth.js is an array interpolation library for Javascript",
"main": "",
"moduleType": [],
"keywords": [
"interpolation",
"javascript",
"smoothjs"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}
================================================
FILE: sindemo.coffee
================================================
###
sindemo
Sample and cubic interpolate the sin function, then print out max and average error.
###
{Smooth} = require './Smooth'
s = (Math.sin 2*Math.PI*x for x in [0...1] by 1/8)
#Scale the function
smooth_sin = ((f) ->
scaleVal = 0.5*s.length/Math.PI
return (x) -> f x*scaleVal
) Smooth s, method:Smooth.METHOD_CUBIC, clip:Smooth.CLIP_PERIODIC
totalError = 0
count = 0
maxError = 0
for x in [-10..10] by .001
error = Math.abs Math.sin(x) - smooth_sin(x)
maxError = Math.max error, maxError
totalError += error
count++
console.log "Max Error:\t #{(100*maxError).toFixed(10)}%"
console.log "Average Error:\t #{(100*totalError/count).toFixed(10)}%"
================================================
FILE: test/specs/clip-clamp.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
describe "Clip Clamp", ->
arr = [1,2,3,4]
len = arr.length
s = Smooth arr, clip:Smooth.CLIP_CLAMP
it 'should extend first value to negative infinity', ->
expect(s -1).toEqual arr[0]
expect(s -5.8).toEqual arr[0]
expect(s -100.3).toEqual arr[0]
it 'should extend last value to positive infinity', ->
expect(s len+1).toEqual arr[len-1]
expect(s len+12.2).toEqual arr[len-1]
expect(s len+1000).toEqual arr[len-1]
it 'should leave in-bounds values untouched', ->
expect(s 0).toEqual arr[0]
expect(s 1).toEqual arr[1]
expect(s 2).toEqual arr[2]
================================================
FILE: test/specs/clip-mirror.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
describe "Clip Mirror", ->
arr = [1,2,3,4]
len = arr.length
s = Smooth arr, clip:Smooth.CLIP_MIRROR
it 'should reflect across the origin', ->
expect(s -1).toEqual arr[1]
expect(s -2).toEqual arr[2]
expect(s -3).toEqual arr[3]
it 'should produce a predictable pattern', ->
mirrorArray = arr.concat (arr[i] for i in [len-2...0]) #create pattern to repeat
expect( (s i for i in [0...20]).join() )
.toBe (mirrorArray[i%mirrorArray.length] for i in [0...20]).join()
it 'should leave in-bounds values untouched', ->
expect(s 0).toEqual arr[0]
expect(s 1).toEqual arr[1]
expect(s 3).toEqual arr[3]
================================================
FILE: test/specs/clip-perodic.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
describe "Clip Periodic", ->
arr = [1,2,3,4]
len = arr.length
s = Smooth arr, clip:Smooth.CLIP_PERIODIC
it 'should repeat the same value for a shift by any integer multiple of the array length', ->
expect(s 0 - 98*len).toEqual arr[0]
expect(s 1 + 12*len).toEqual arr[1]
expect(s 2 - 13*len).toEqual arr[2]
expect(s 3 + 47*len).toEqual arr[3]
it 'should leave in-bounds values untouched', ->
expect(s 0).toEqual arr[0]
expect(s 1).toEqual arr[1]
expect(s 3).toEqual arr[3]
================================================
FILE: test/specs/clip-zero.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
describe "Clip Zero", ->
arr = [1,2,3,4]
len = arr.length
s = Smooth arr, clip:Smooth.CLIP_ZERO
it 'should be zero when out of bounds', ->
expect(s -1).toEqual 0
expect(s -100.3).toEqual 0
expect(s len+1).toEqual 0
expect(s len+12.2).toEqual 0
expect(s len+1000).toEqual 0
it 'should leave in-bounds values untouched', ->
expect(s 0).toEqual arr[0]
expect(s 1).toEqual arr[1]
expect(s 2).toEqual arr[2]
================================================
FILE: test/specs/cubic.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
{deriv} = require './util.coffee'
describe 'Cubic Interpolator', ->
describe 'Catmull-Rom', ->
arr = [4,6,-2,3]
s = Smooth arr, method:Smooth.METHOD_CUBIC, cubicTension: Smooth.CATMULL_ROM
it 'should match integer indexes', ->
expect(s 0).toEqual arr[0]
expect(s 1).toEqual arr[1]
expect(s 2).toEqual arr[2]
expect(s 3).toEqual arr[3]
it 'should have neighbor-slope tangent at integers', ->
expect(deriv(s) 1).toBeCloseTo (arr[2]-arr[0])/2
expect(deriv(s) 2).toBeCloseTo (arr[3]-arr[1])/2
it 'should repeat when periodic', ->
p = Smooth arr, method:'cubic', cubicTension: Smooth.CATMULL_ROM, clip:'periodic', scaleTo: 1
for i in [-2..2] by 1/16
expect(p i).toEqual p i - Math.floor i
describe 'Tension=1', ->
arr = [8,2,-4,9]
s = Smooth arr, method:Smooth.METHOD_CUBIC, cubicTension: 1
it 'should match integer indexes', ->
expect(s 0).toEqual arr[0]
expect(s 1).toEqual arr[1]
expect(s 2).toEqual arr[2]
expect(s 3).toEqual arr[3]
it 'should have zero derivatives at integers', ->
expect(deriv(s) 0).toBeCloseTo 0
expect(deriv(s) 1).toBeCloseTo 0
expect(deriv(s) 2).toBeCloseTo 0
expect(deriv(s) 3).toBeCloseTo 0
================================================
FILE: test/specs/exceptions.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
describe 'Exceptions', ->
it 'should throw for invalid methods', ->
expect(-> Smooth [1,2], method: 'lanscoz').toThrow "Invalid method: lanscoz"
expect(-> Smooth [1,2], method: 'Cubic').toThrow "Invalid method: Cubic"
it 'should throw for invalid clipping mode', ->
expect(-> Smooth [1,2], clip: 'mirorr').toThrow "Invalid clip: mirorr"
expect(-> Smooth [1,2], clip: 'Linear').toThrow "Invalid clip: Linear"
it 'should throw for invalid arrays', ->
expect(-> Smooth []).toThrow 'Array must have at least two elements'
expect(-> Smooth [0]).toThrow 'Array must have at least two elements'
it 'should throw for bad input', ->
expect(-> Smooth ['a','b']).toThrow 'Invalid element type: String'
expect(-> Smooth [(->), (->)]).toThrow 'Invalid element type: Function'
expect(-> Smooth [[], []]).toThrow 'Vectors must be non-empty'
it 'should throw for sinc filter with no window', ->
expect(-> Smooth [1,2], method:'sinc').toThrow 'No sincWindow provided'
it 'should throw for invalid scaleTo', ->
expect(-> Smooth [1,2], scaleTo:[1]).toThrow 'scaleTo param must be number or array of two numbers'
expect(-> Smooth [1,2], scaleTo:[1,2,3]).toThrow 'scaleTo param must be number or array of two numbers'
expect(-> Smooth [1,2], scaleTo:'a').toThrow 'scaleTo param must be number or array of two numbers'
expect(-> Smooth [1,2], scaleTo:[1,'x']).toThrow 'scaleTo param must be number or array of two numbers'
expect(-> Smooth [1,2], scaleTo:Infinity).toThrow 'scaleTo param must be number or array of two numbers'
describe 'With deep validation on...', ->
it 'should throw for bad input deep inside the input', ->
Smooth.deepValidation = true
expect(-> Smooth [1,2,'a']).toThrow 'NaN in Smooth() input'
expect(-> Smooth [1,2,'3']).toThrow 'Non-number in Smooth() input'
expect(-> Smooth [1,2, Infinity]).toThrow 'Infinity in Smooth() input'
expect(-> Smooth [[1],[2],['a']]).toThrow 'NaN in Smooth() input'
expect(-> Smooth [[1],[2],['3']]).toThrow 'Non-number in Smooth() input'
expect(-> Smooth [[1],[2], [Infinity]]).toThrow 'Infinity in Smooth() input'
expect(-> Smooth [[1], 1]).toThrow 'Non-vector in Smooth() input'
expect(-> Smooth [[1], [1,2]]).toThrow 'Inconsistent dimension in Smooth() input'
describe 'With deep validation off...', ->
it 'should not throw for bad input deep inside the input', ->
Smooth.deepValidation = false
expect(Smooth [1,2,'a']).toBeTruthy()
expect(Smooth [1,2,'3']).toBeTruthy()
expect(Smooth [1,2, Infinity]).toBeTruthy()
expect(Smooth [[1],[2],['a']]).toBeTruthy()
expect(Smooth [[1],[2],['3']]).toBeTruthy()
expect(Smooth [[1],[2], [Infinity]]).toBeTruthy()
expect(Smooth [[1], 1]).toBeTruthy()
expect(Smooth [[1], [1,2]]).toBeTruthy()
================================================
FILE: test/specs/lanczos.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
{deriv} = require './util.coffee'
describe 'Lanczos Interpolator', ->
arr = [1,3,-2,8]
s = Smooth arr, method: 'lanczos'
it 'should be close to array at integer indexes', ->
for i in [0...arr.length]
expect(s i).toBeCloseTo arr[i]
it 'should be continuous everywhere', ->
delta = 0.00001
for i in [-1..arr.length] by 1/64
expect(s i).toBeCloseTo s(i-delta), 0
it 'should be differentiable everywhere', ->
delta = 0.00001
ds = deriv s
for i in [-1..arr.length] by 1/64
expect(ds i).toBeCloseTo ds(i-delta), 0
it 'should repeat when periodic', ->
p = Smooth arr, method:'lanczos', clip: 'periodic', scaleTo: 1
for i in [-2..2] by 1/16
expect(p i).toEqual p i - Math.floor i
================================================
FILE: test/specs/linear.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
{deriv} = require './util.coffee'
describe 'Linear Interpolator', ->
arr = [1,4,3,8]
s = Smooth arr, method:Smooth.METHOD_LINEAR
it 'should match integer indexes', ->
expect(s 0).toEqual arr[0]
expect(s 1).toEqual arr[1]
expect(s 2).toEqual arr[2]
expect(s 3).toEqual arr[3]
it 'should have arithmetic mean for midpoints', ->
expect(s 0.5).toBeCloseTo (arr[0]+arr[1])/2
expect(s 1.5).toBeCloseTo (arr[1]+arr[2])/2
expect(s 2.5).toBeCloseTo (arr[2]+arr[3])/2
it 'should have derivatives equal to point differences', ->
expect(deriv(s) 0.2).toBeCloseTo arr[1] - arr[0]
expect(deriv(s) 0.8).toBeCloseTo arr[1] - arr[0]
expect(deriv(s) 1.4).toBeCloseTo arr[2] - arr[1]
expect(deriv(s) 2.7).toBeCloseTo arr[3] - arr[2]
it 'should repeat when periodic', ->
p = Smooth arr, method:'linear', clip: 'periodic', scaleTo: 1
for i in [-2..2] by 1/16
expect(p i).toEqual p i - Math.floor i
================================================
FILE: test/specs/misc.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
util = require './util'
describe "Misc...", ->
it 'should not modify the original config object', ->
config = lanczosFilterSize: 2
configCopy = util.shallowCopy config
s = Smooth [1,2,3], config
#check object equality
expect(config).toEqual configCopy
================================================
FILE: test/specs/nearest.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
{deriv} = require './util.coffee'
describe 'Nearest Neighbor Interpolator', ->
arr = [1,2,3,4]
s = Smooth arr, method:Smooth.METHOD_NEAREST
it 'should match integer indexes', ->
expect(s 0).toEqual arr[0]
expect(s 1).toEqual arr[1]
expect(s 2).toEqual arr[2]
expect(s 3).toEqual arr[3]
it 'should round fractional parameter', ->
expect(s 0.1).toEqual arr[0]
expect(s 1.9).toEqual arr[2]
expect(s 2.2).toEqual arr[2]
expect(s 2.8).toEqual arr[3]
it 'should have zero derivatives where fraction != .5', ->
expect(deriv(s) 1).toBeCloseTo 0
expect(deriv(s) 1.1).toBeCloseTo 0
expect(deriv(s) 1.9).toBeCloseTo 0
expect(deriv(s) 3.2).toBeCloseTo 0
it 'should repeat when periodic', ->
p = Smooth arr, method:'nearest', clip: 'periodic', scaleTo: 1
for i in [-2..2] by 1/16
expect(p i).toEqual p i - Math.floor i
================================================
FILE: test/specs/properties.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
util = require './util'
describe "Smooth function properties...", ->
it 'should save a shallow copy of the config passed in by the user', ->
config = cubicTension: 1, method: 'cubic', clip: 'zero'
s = Smooth [1,2,3], config
expect(s.config).toEqual config
it 'should save the domain correctly', ->
expect((Smooth [1,1,1]).domain).toEqual [0, 2]
expect((Smooth [1,1,1,1], clip:'periodic').domain).toEqual [0, 4]
expect((Smooth [1,1,1], scaleTo:2).domain).toEqual [0, 2]
expect((Smooth [1,1,1], scaleTo:[1,5]).domain).toEqual [1, 5]
expect((Smooth [1,1,1], clip:'periodic', scaleTo:[1,5]).domain).toEqual [1, 5]
expect((Smooth [1,1,1], scaleTo:[5,1]).domain).toEqual [1, 5]
it 'should save the count correctly', ->
expect(Smooth([1,1,3]).count).toBe 3
expect(Smooth([1,2,3], scaleTo:5).count).toBe 3
it 'should save the dimension correctly', ->
expect(Smooth([1,2,3]).dimension).toBe 'scalar'
expect(Smooth([[1],[2],[3]]).dimension).toBe 1
expect(Smooth([[1,2],[2,3],[3,4]]).dimension).toBe 2
================================================
FILE: test/specs/scale.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
describe "Scale to...", ->
arr = [1,2,3,4]
it 'should scale to [0,1]', ->
s = Smooth arr, scaleTo: 1
expect(s 0).toBeCloseTo arr[0]
expect(s 1/3).toBeCloseTo arr[1]
expect(s 2/3).toBeCloseTo arr[2]
expect(s 1).toBeCloseTo arr[3]
it 'should scale to [0, length-1] with no change from unscaled', ->
s_noscale = Smooth arr
s_scale = Smooth arr, scaleTo: arr.length-1
expect(s_scale 0).toBeCloseTo s_noscale 0
expect(s_scale 2).toBeCloseTo s_noscale 2
expect(s_scale 2.5).toBeCloseTo s_noscale 2.5
expect(s_scale 3).toBeCloseTo s_noscale 3
it 'should scale to [0, 9]', ->
s = Smooth arr, scaleTo: 9
expect(s 0).toBeCloseTo arr[0]
expect(s 3).toBeCloseTo arr[1]
expect(s 6).toBeCloseTo arr[2]
expect(s 9).toBeCloseTo arr[3]
it 'should reflect when scaling to -(length-1)', ->
s = Smooth arr, scaleTo: -(arr.length - 1)
expect(s 0).toBeCloseTo arr[0]
expect(s -1).toBeCloseTo arr[1]
expect(s -2).toBeCloseTo arr[2]
expect(s -3).toBeCloseTo arr[3]
it 'should scale to the next cycle for periodic functions', ->
s = Smooth arr, scaleTo: 1, clip:Smooth.CLIP_PERIODIC
expect(s 0).toBeCloseTo s 1
expect(s 0.5).toBeCloseTo s 2.5
expect(s 3.8).toBeCloseTo s -9.2
describe 'range scaling', ->
it 'should scale to [-1, 1]', ->
s = Smooth arr, scaleTo: [-1, 1]
expect(s -1).toBeCloseTo arr[0]
expect(s -1/3).toBeCloseTo arr[1]
expect(s 1/3).toBeCloseTo arr[2]
expect(s 1).toBeCloseTo arr[3]
it 'should scale periodic to [0.5, 1.5]', ->
s = Smooth arr, scaleTo: [0.5, 1.5], clip:'periodic'
expect(s 0).toBeCloseTo s 1
expect(s 0.5).toBeCloseTo s 2.5
expect(s 3.8).toBeCloseTo s -9.2
================================================
FILE: test/specs/sinc.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
{deriv} = require './util.coffee'
describe 'Sinc Filter Interpolator', ->
arr = [1,3,-2,8]
describe 'Gaussian window', ->
s = Smooth arr, method: 'sinc', sincWindow: (x) -> Math.exp -x*x
it 'should be close to array at integer indexes', ->
for i in [0...arr.length]
expect(s i).toBeCloseTo arr[i]
it 'should be continuous everywhere', ->
delta = 0.00001
for i in [-1..arr.length] by 1/64
expect(s i).toBeCloseTo s(i-delta), 0
it 'should be differentiable everywhere', ->
delta = 0.00001
ds = deriv s
for i in [-1..arr.length] by 1/64
expect(ds i).toBeCloseTo ds(i-delta), 0
it 'should repeat when periodic', ->
p = Smooth arr, method: 'sinc', scaleTo: 1, clip: 'periodic', sincWindow: (x) -> Math.exp -x*x
for i in [-2..2] by 1/16
expect(p i).toEqual p i - Math.floor i
describe 'Circular window', ->
s = Smooth arr, method: 'sinc', sincWindow: (x) -> Math.sqrt(1 - x*x/4)
it 'should be close to array at integer indexes', ->
for i in [0...arr.length]
expect(s i).toBeCloseTo arr[i]
it 'should be continuous everywhere', ->
delta = 0.00001
for i in [-1..arr.length] by 1/64
expect(s i).toBeCloseTo s(i-delta), 0
it 'should be differentiable everywhere', ->
delta = 0.00001
ds = deriv s
for i in [-1..arr.length] by 1/64
expect(ds i).toBeCloseTo ds(i-delta), 0
it 'should repeat when periodic', ->
p = Smooth arr, method: 'sinc', scaleTo: 1, clip: 'periodic', sincWindow: (x) -> Math.sqrt(1 - x*x/4)
for i in [-2..2] by 1/16
expect(p i).toEqual p i - Math.floor i
================================================
FILE: test/specs/util.coffee
================================================
### Helper code for tests ###
#Approximate derivative function with some delta value
exports.deriv = (f, delta = 0.0001) ->
(t) -> ((f t+delta) - (f t))/delta
exports.distance = (a, b) ->
sqDist = 0
l = a.length
sqDist += Math.pow a[i]-b[i], 2 for i in [0...l]
return Math.sqrt sqDist
exports.shallowCopy = (obj) ->
copy = {}
copy[k] = v for own k,v of obj
copy
================================================
FILE: test/specs/vector.spec.coffee
================================================
{Smooth} = require '../../Smooth.coffee'
{distance} = require './util.coffee'
describe 'Vector', ->
it 'should approximate a unit circle', ->
{sin, cos, PI} = Math
circle_points = ([cos(PI*t), sin(PI*t)] for t in [0...2] by 1/6)
#Make into a function
circle = Smooth circle_points, period:1, clip:Smooth.CLIP_PERIODIC
#Integrate arc length
l = 0
start = [1,0]
for t in [0..1] by .0001
end = circle t
l += distance start, end
start = end
#Result length should be approximately 2*pi
expect(l).toBeCloseTo 2*Math.PI
================================================
FILE: test/tests.sh
================================================
#!/usr/bin/env bash
# (requires jasmine-node)
jasmine-node --coffee --verbose `dirname $0`/specs
gitextract_dud26c8f/
├── ChangeLog.md
├── Readme.md
├── Smooth.coffee
├── Smooth.js MIT license.txt
├── bower.json
├── sindemo.coffee
└── test/
├── specs/
│ ├── clip-clamp.spec.coffee
│ ├── clip-mirror.spec.coffee
│ ├── clip-perodic.spec.coffee
│ ├── clip-zero.spec.coffee
│ ├── cubic.spec.coffee
│ ├── exceptions.spec.coffee
│ ├── lanczos.spec.coffee
│ ├── linear.spec.coffee
│ ├── misc.spec.coffee
│ ├── nearest.spec.coffee
│ ├── properties.spec.coffee
│ ├── scale.spec.coffee
│ ├── sinc.spec.coffee
│ ├── util.coffee
│ └── vector.spec.coffee
└── tests.sh
Condensed preview — 22 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (41K chars).
[
{
"path": "ChangeLog.md",
"chars": 1111,
"preview": "#Smooth.js Change Log\n\nThis is a log of changes to the library itself. Other changes, like tests, are omitted.\n\n##0.1.7\n"
},
{
"path": "Readme.md",
"chars": 10414,
"preview": "\n\n#### Table of Contents\n[ What is this for?](#rm-what)<br/>\n[ How do I"
},
{
"path": "Smooth.coffee",
"chars": 9243,
"preview": "###\nSmooth.js version 0.1.7\n\nTurn arrays into smooth functions.\n\nCopyright 2012 Spencer Cohen\nLicensed under MIT license"
},
{
"path": "Smooth.js MIT license.txt",
"chars": 1067,
"preview": "Smooth.js Copyright (c) 2012 Spencer Cohen\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
},
{
"path": "bower.json",
"chars": 451,
"preview": "{\n \"name\": \"smooth.js\",\n \"homepage\": \"https://github.com/osuushi/Smooth.js\",\n \"authors\": [\n \"yournamehere <someone"
},
{
"path": "sindemo.coffee",
"chars": 662,
"preview": "###\nsindemo\n\nSample and cubic interpolate the sin function, then print out max and average error.\n###\n\n{Smooth} = requir"
},
{
"path": "test/specs/clip-clamp.spec.coffee",
"chars": 608,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\ndescribe \"Clip Clamp\", ->\n\tarr = [1,2,3,4]\n\tlen = arr.length\n\ts = Smooth arr, "
},
{
"path": "test/specs/clip-mirror.spec.coffee",
"chars": 662,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\ndescribe \"Clip Mirror\", ->\n\tarr = [1,2,3,4]\n\tlen = arr.length\n\ts = Smooth arr,"
},
{
"path": "test/specs/clip-perodic.spec.coffee",
"chars": 534,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\ndescribe \"Clip Periodic\", ->\n\tarr = [1,2,3,4]\n\tlen = arr.length\n\ts = Smooth ar"
},
{
"path": "test/specs/clip-zero.spec.coffee",
"chars": 467,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\ndescribe \"Clip Zero\", ->\n\tarr = [1,2,3,4]\n\tlen = arr.length\n\ts = Smooth arr, c"
},
{
"path": "test/specs/cubic.spec.coffee",
"chars": 1233,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\n{deriv} = require './util.coffee'\n\n\ndescribe 'Cubic Interpolator', ->\n\tdescrib"
},
{
"path": "test/specs/exceptions.spec.coffee",
"chars": 2818,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\ndescribe 'Exceptions', ->\n\tit 'should throw for invalid methods', ->\n\t\texpect("
},
{
"path": "test/specs/lanczos.spec.coffee",
"chars": 752,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\n{deriv} = require './util.coffee'\n\ndescribe 'Lanczos Interpolator', ->\n\tarr = "
},
{
"path": "test/specs/linear.spec.coffee",
"chars": 959,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\n{deriv} = require './util.coffee'\n\n\ndescribe 'Linear Interpolator', ->\n\tarr = "
},
{
"path": "test/specs/misc.spec.coffee",
"chars": 306,
"preview": "{Smooth} = require '../../Smooth.coffee'\nutil = require './util'\n\ndescribe \"Misc...\", ->\n\tit 'should not modify the orig"
},
{
"path": "test/specs/nearest.spec.coffee",
"chars": 891,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\n{deriv} = require './util.coffee'\n\ndescribe 'Nearest Neighbor Interpolator', -"
},
{
"path": "test/specs/properties.spec.coffee",
"chars": 1069,
"preview": "{Smooth} = require '../../Smooth.coffee'\nutil = require './util'\n\ndescribe \"Smooth function properties...\", ->\n\tit 'shou"
},
{
"path": "test/specs/scale.spec.coffee",
"chars": 1707,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\ndescribe \"Scale to...\", ->\n\tarr = [1,2,3,4]\n\n\tit 'should scale to [0,1]', ->\n\t"
},
{
"path": "test/specs/sinc.spec.coffee",
"chars": 1619,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\n{deriv} = require './util.coffee'\n\ndescribe 'Sinc Filter Interpolator', ->\n\tar"
},
{
"path": "test/specs/util.coffee",
"chars": 376,
"preview": "### Helper code for tests ###\n\n\n#Approximate derivative function with some delta value\nexports.deriv = (f, delta = 0.000"
},
{
"path": "test/specs/vector.spec.coffee",
"chars": 548,
"preview": "{Smooth} = require '../../Smooth.coffee'\n\n{distance} = require './util.coffee'\n\ndescribe 'Vector', ->\n\tit 'should approx"
},
{
"path": "test/tests.sh",
"chars": 99,
"preview": "#!/usr/bin/env bash\n\n# (requires jasmine-node)\n\njasmine-node --coffee --verbose `dirname $0`/specs\n"
}
]
About this extraction
This page contains the full source code of the osuushi/Smooth.js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 22 files (36.7 KB), approximately 11.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.