interpolate

SCView extensions


part of wslib


a number of interpolation functions.


there are a number of types of interpolation available, and a number of ways to do it.


interpolation types


cubic interpolation:

'spline' : basic spline interpolation with automatic control points; extra argument: amt for the control points.

amt = 0 will result in sine-like interpolation. 

the default amt is ( 0.75 / (1.9**0.5) ), for 2D circle approximation

'hermite' : simplified spline interpolation with fixed amt = 1/3, as used by many UGens ( DelayC, PlayBuf etc)

'bspline' : better curves but higher cpu. bspline also has an amt argument, which defines the order.

the default amt is 4.


other interpolation:

'quad' : not correct yet

'sine' : wraps s-shaped sine curves between steps

'linear' : linear interpolation

'step' : step interpolation


There is also the possibility of entering your own control points for the cubic interpolation.


methods


Array:intAt( index, type, loop, extra )

returns an interpolated value from the array at index with type interpolation. if loop == true the curve after the last point will bend towards the first. extra contains the extra arguments for specific types.


[0,1,5,2].intAt( 2.5 );

[0,1,5,2].intAt( 2.5, \hermite );

[0,1,5,2].intAt( 2.5, \bspline );


Array:atL ( index, loop ), atH ( index, loop ), atS ( index, loop, extra ), 

atB ( index, loop, extra ), atSin ( index, loop )

shortcuts for all intAt types


Array:resize ( newSize, type, loop, extra )

resizes the array to newSize with type interpolation. 


[0,1,5,2].resize( 100, \hermite ).plot;


(

a = { 1.0.rand2 } ! 10;

({ |i| a.resize( 200, [\linear, \step, \hermite, \spline, \bspline][i]  ) } ! 5)

.flop.flat.plot(numChannels: 5);

)


Array:interpolate ( division, type, loop, extra )

expands the array to division values per input value with type interpolation.


[0,1,5,2].interpolate( 10, \hermite ).size;

[0,1,5,2].interpolate( 10, \hermite ).plot;


Array:splineIntFunction ( i, x1, x2 )

the basic cubic interpolation function with user given control points. The input aray should be of size = 2.

If you use an array for i, the function will run optimized ( calculates controls only once ).


[0,1].splineIntFunction( (0,0.01..1), 0.125, 1.75 ).plot;


examples


(

// hands on interpolation

// move the points around..

w = SCWindow( "y-axis interpolation" ).front;

u = SCUserView( w, w.view.bounds ).resize_(5);

w.decorate;

w.view.background_( Color.white );

m = SCPopUpMenu( w, 160@20 ).items_([ 

"cubic with controls", 

"bspline",

"spline",

"hermite",

"linear", 

"sine",

"step" ])

.action_({ |pu| 

if( (pu.value == 2) or: (pu.value == 1) )

{ c.visible = true }

{ c.visible = false };

w.refresh; });

c = SCSlider( w, 200@20 )

.value_( (0.75 / (1.9**0.5)) )

.action_({ w.refresh })

.visible_( false );


p = [ 12@200, 138@50, 262@50, 388@200 ];

j = nil;

r = 7;


u.drawFunc_({ |uvw|

Color.black.alpha_(0.5).set; 

Pen.line( 0@200, 400@200 ).stroke;

p[[0,3]].do({ |point, i| Pen.addArc( point, r, 0, 2pi ).stroke; });

if( m.value == 0 )

{ Color.red.set;

Pen.line( p[0], p[1] ).stroke;

Pen.line( p[2], p[3] ).stroke;

};

p[[1,2]].do({ |point, i| Pen.addArc( point, r, 0, 2pi ).stroke; });

Color.blue.set;

Pen.moveTo( p[0] );

switch( m.value,

0, { p[[0,3]].collect( _.y )

.splineIntFunction( (0,1/50..1), p[1].y, p[2].y )

.do({ |item, i| 

Pen.lineTo(

i.linlin( 0, 50, p[0].x, p[3].x, \none )@item )

});

},

1, { p.collect(_.y).interpolate(15, 'bspline', false, c.value.linlin( 0,1,2,8 ).asInt )

.do({ |item, i| Pen.lineTo( i.linlin( 0, 59, p[0].x, p[3].x, \none )@item ) }); 

},

2, { p.collect(_.y).interpolate(15, 'spline', false, c.value )

.do({ |item, i| Pen.lineTo( i.linlin( 0, 59, p[0].x, p[3].x, \none )@item ) }); 

},


3, { p.collect(_.y).interpolate(15, 'hermite', false)

.do({ |item, i| Pen.lineTo( i.linlin( 0, 59, p[0].x, p[3].x, \none )@item ) }); 

},

4, { p.collect(_.y).interpolate(10, 'linear', false)

.do({ |item, i| Pen.lineTo( i.linlin( 0, 39, p[0].x, p[3].x, \none )@item ) });

},

5, { p.collect(_.y).interpolate(15, 'sine', false)

.do({ |item, i| Pen.lineTo( i.linlin( 0, 59, p[0].x, p[3].x, \none )@item ) });

},

6, { p.collect(_.y).interpolate(15, 'step', false)

.do({ |item, i| Pen.lineTo( i.linlin( 0, 59, p[0].x, p[3].x, \none )@item ) });

}

 

  );

Pen.stroke;

});

u.mouseBeginTrackFunc_({ |uvw, x, y|

var distances;

distances = p.collect({ |pt| pt.dist( x@y ) });

j = distances.detectIndex( _ < (r+1) );

//j.postln;

});

u.mouseTrackFunc_({ |uvw, x,y| if( j.notNil ) { p[j] = (p[j].x)@(y.max(0)); w.refresh; }; });

)


///////////////////


(

// 2 dimensional interpolation

w = SCWindow( "2D interpolation" ).front;

u = SCUserView( w, w.view.bounds ).resize_(5);

w.decorate;

m = SCPopUpMenu( w, 160@20 ).items_([ 

"cubic with controls", 

"bspline",

"spline",

"hermite",

"linear" ])

.action_({ |pu|

if( (pu.value == 2) or: (pu.value == 1) )

{ c.visible = true }

{ c.visible = false };

w.refresh; });

c = SCSlider( w, 200@20 )

.value_( (0.75 / (1.9**0.5)) )

.action_({ w.refresh })

.visible_( false );


p = [ 10@200, 100@50, 300@50, 390@200 ];

j = nil;

r = 7;


u.drawFunc_({ |uvw|

Color.black.alpha_(0.5).set; 

p[[0,3]].do({ |point, i| Pen.addArc( point, r, 0, 2pi ).stroke; });

if( m.value == 0 )

{ Color.red.set;

Pen.line( p[0], p[1] ).stroke;

Pen.line( p[2], p[3] ).stroke;

};

p[[1,2]].do({ |point, i| Pen.addArc( point, r, 0, 2pi ).stroke; });

Color.blue.set;

Pen.moveTo( p[0] );

switch( m.value,

0, { p[[0,3]].collect( _.asArray )

.flop.collect({ |item,i|

item.splineIntFunction( (0,1/50..1), p[1].asArray[i], p[2].asArray[i] )

}).flop

.do({ |item| Pen.lineTo( item.asPoint ) });

},

/*

0, { 50.do({ |i| Pen.lineTo( p[[0,3]].collect( _.asArray )

.splineIntFunction( i/49, p[1].asArray, p[2].asArray ).asPoint )

}); },

*/

1, { p.collect(_.asArray).interpolate(15, 'bspline', false, c.value.linlin( 0,1,2,8) )

.do({ |item, i| Pen.lineTo( item.asPoint ) }); 

},

2, { p.collect(_.asArray).interpolate(15, 'spline', false, c.value )

.do({ |item, i| Pen.lineTo( item.asPoint ) }); 

},

3, { p.collect(_.asArray).interpolate(15, 'hermite', false, c.value )

.do({ |item, i| Pen.lineTo( item.asPoint ) }); 

},


4, { p.collect(_.asArray).interpolate(10, 'linear', false)

.do({ |item, i| Pen.lineTo( item.asPoint ) });

}

  );

Pen.stroke;

});

u.mouseBeginTrackFunc_({ |uvw, x, y|

var distances;

distances = p.collect({ |pt| pt.dist( x@y ) });

j = distances.detectIndex( _ < (r+1) );

//j.postln;

});

u.mouseTrackFunc_({ |uvw, x,y| if( j.notNil ) { p[j] = x@y; w.refresh; }; });

)