• # Spline1D.js

• Camtull-Rom spline implementation
Inspired by code from Tween.js

• ## Example use

``````var points = [
-2,
-1,
1,
2
];

var spline = new Spline1D(points);

spline.getPointAt(0.25);
``````
• ## Reference

• ### Spline1D ( points, [ closed ] )

`points` - { Array of Vec3 } = [ ]
`closed` - is the spline a closed loop? { Boolean } = false

```function Spline1D(points, closed) {
this.points = points || [];
this.dirtyLength = true;
this.closed = closed || false;
this.samplesCount = 2000;
}```
• ### getPoint ( t )

Gets position based on t-value. It is fast, but resulting points will not be evenly distributed.

`t` - { Number } <0, 1>

```Spline1D.prototype.getPoint = function ( t ) {
if (this.closed) {
t = (t + 1 ) % 1;
}
else {
t = Math.max(0, Math.min(t, 1));
}

var points = this.points;
var len = this.closed ? points.length : points.length - 1;
var point = t * len;
var intPoint = Math.floor( point );
var weight = point - intPoint;

var c0, c1, c2, c3;
if (this.closed) {
c0 = (intPoint - 1 + points.length ) % points.length;
c1 = intPoint % points.length;
c2 = (intPoint + 1 ) % points.length;
c3 = (intPoint + 2 ) % points.length;
}
else {
c0 = intPoint == 0 ? intPoint : intPoint - 1;
c1 = intPoint;
c2 = intPoint > points.length - 2 ? intPoint : intPoint + 1;
c3 = intPoint > points.length - 3 ? intPoint : intPoint + 2;
}

return this.interpolate( points[ c0 ], points[ c1 ], points[ c2 ], points[ c3 ], weight );
}```
• ### addPoint ( p )

`p` - point to be added { Vec3 }

```Spline1D.prototype.addPoint = function ( p ) {
this.dirtyLength = true;
this.points.push(p)
}```
• ### getPointAt ( d )

Gets position based on d-th of total length of the curve. Precise but might be slow at the first use due to need to precalculate length.

`d` - { Number } <0, 1>

```Spline1D.prototype.getPointAt = function ( d ) {
if (this.closed) {
d = (d + 1 ) % 1;
}
else {
d = Math.max(0, Math.min(d, 1));
}

if (this.dirtyLength) {
this.precalculateLength();
}```
• TODO: try binary search

```  var k = 0;
for(var i=0; i<this.accumulatedLengthRatios.length; i++) {
if (this.accumulatedLengthRatios[i] > d - 1/this.samplesCount) {
k = this.accumulatedRatios[i];
break;
}
}

return this.getPoint(k);
}```
• ### getPointAtIndex ( i )

Returns position of i-th point forming the curve

`i` - { Number } <0, Spline1D.points.length)

```Spline1D.prototype.getPointAtIndex = function ( i ) {
if (i < this.points.length) {
return this.points[i];
}
else {
return null;
}
}```
• ### getNumPoints ( )

Return number of base points in the spline

```Spline1D.prototype.getNumPoints = function() {
return this.points.length;
}```
• ### getLength ( )

Returns the total length of the spline.

```Spline1D.prototype.getLength = function() {
if (this.dirtyLength) {
this.precalculateLength();
}
return this.length;
}```
• ### precalculateLength ( )

Goes through all the segments of the curve and calculates total length and the ratio of each segment.

```Spline1D.prototype.precalculateLength = function() {
var step = 1/this.samplesCount;
var k = 0;
var totalLength = 0;
this.accumulatedRatios = [];
this.accumulatedLengthRatios = [];
this.accumulatedLengths = [];

var point;
var prevPoint;
var k = 0;
for(var i=0; i<this.samplesCount; i++) {
prevPoint = point;
point = this.getPoint(k);

if (i > 0) {
var len = Math.sqrt(1 + (point - prevPoint)*(point - prevPoint));
totalLength += len;
}

this.accumulatedRatios.push(k);
this.accumulatedLengths.push(totalLength)

k += step;
}

for(var i=0; i<this.samplesCount; i++) {
this.accumulatedLengthRatios.push(this.accumulatedLengths[i] / totalLength);
}

this.length = totalLength;
this.dirtyLength = false;
}```
• ### close ( )

Closes the spline. It will form a closed now.

```Spline1D.prototype.close = function( ) {
this.closed = true;
}```
• ### isClosed ( )

Returns true if spline is closed (forms a closed) { Boolean }

```Spline1D.prototype.isClosed = function() {
return this.closed;
}```
• ### interpolate ( p0, p1, p2, p3, t)

Helper function to calculate Catmul-Rom spline equation

`p0` - previous value { Number }
`p1` - current value { Number }
`p2` - next value { Number }
`p3` - next next value { Number }
`t` - parametric distance between p1 and p2 { Number } <0, 1>

```Spline1D.prototype.interpolate = function(p0, p1, p2, p3, t) {
var v0 = ( p2 - p0 ) * 0.5;
var v1 = ( p3 - p1 ) * 0.5;
var t2 = t * t;
var t3 = t * t2;
return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;
}

module.exports = Spline1D;```