We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexDiscussionExhibition › Playing with cubic splines
Page Index Toggle Pages: 1
Playing with cubic splines (Read 3023 times)
Playing with cubic splines
Dec 12th, 2005, 6:07pm
 
I've written the first pass of a cubic spline object. I started simple, implementing a natural cubic spline with equally spaced knots. You pass it two vectors; a set of x-values (which must be equally spaced for it to look right... sorry no error checking on this) and corresponding y-values. Soon I'll have non-uniform grids enabled. I have an example up at http://homepage.mac.com/cchoge/SplineWave

The code:
Code:

class Spline
{
 float[] m_x;
 float[] m_y;

 float[] m_a;

 float[] m_u;
 float[] m_d;
 float[] m_l;

 Cubic[] m_funcs;

 Spline( float[] x, float[] y )
 {
   m_x = x;
   m_y = y;

   m_a = new float[ m_x.length ];
   m_a[0] = m_y[1] - m_y[0];
   for ( int i = 1; i < ( m_x.length - 1 ); ++i )
   {
     m_a[i] = 3.0 * (m_y[i+1] - m_y[i-1]);
   }
   m_a[m_x.length - 1] = 3.0 * (m_y[ m_x.length-1 ] - m_y[ m_x.length-2 ] );

   m_u = new float[ m_x.length ];
   m_d = new float[ m_x.length ];
   m_l = new float[ m_x.length ];

   m_l[0] = 0;
   m_d[0] = 2;
   m_u[0] = 1;
   for ( int i = 0; i < m_x.length-1; ++i )
   {
     m_l[i] = 1;
     m_d[i] = 4;
     m_u[i] = 1;
   }
   m_l[ m_x.length-1 ] = 1;
   m_d[ m_x.length-1] = 4;
   m_u[ m_x.length-1] = 0;

   float beta;
   for ( int i = 1; i < m_x.length; ++i )
   {
     beta = ( -1.0 / m_d[i-1] ) * m_l[i];
     m_l[i] = 0;
     m_d[i] += m_u[i-1] * beta;
     m_a[i] += m_a[i-1] * beta;
   }

   m_a[ m_x.length-1 ] /= m_d[ m_x.length-1 ];
   m_d[ m_x.length-1 ] = 1;
   for ( int i = m_x.length-2; i >= 0; --i )
   {
     beta = ( -1.0 * m_u[i] );
     m_u[i] = 0;
     m_a[i] += (m_a[i+1] * beta);
     m_a[i] /= m_d[i];
     m_d[i] = 1.0;
   }

   m_funcs = new Cubic[ m_x.length - 1 ];
   float a, b, c, d;
   for ( int i = 0; i < m_funcs.length; ++i )
   {
     a = m_y[i];
     b = m_a[i];
     c = 3 * ( m_y[i+1] - m_y[i] ) - 2 * m_a[i] - m_a[i+1];
     d = 2 * ( m_y[i] - m_y[ i+1 ] ) + m_a[i] + m_a[ i+1 ];
     m_funcs[i] = new Cubic( a, b, c, d, m_x[i], m_x[i+1] );
   }
 }

 float evaluate( float x )
 {
   if ( x < m_x[0] ) return m_y[0];
   if ( x > m_x[m_x.length-1] ) return m_y[ m_x.length-1 ];
   int i = 0;
   while( x > m_x[i+1] ) ++i;
   return m_funcs[i].evaluate( x );
 }
}

class Cubic
{
 float m_a;
 float m_b;
 float m_c;
 float m_d;

 float m_x0;
 float m_x1;
 float m_dist;

 Cubic( float a, float b, float c, float d, float x0, float x1 )
 {
   m_a = a;
   m_b = b;
   m_c = c;
   m_d = d;
   m_x0 = x0;
   m_x1 = x1;
   m_dist = 1.0/(m_x1 - m_x0);
 }

 boolean in( float x )
 {
   return ( (x >= m_x0) && (x <= m_x1) );
 }

 float evaluate( float x )
 {
   float t = (x - m_x0) * m_dist;
   float y = m_a + t * ( m_b + t * ( m_c + t * m_d ) );
   return y;
 }

}
Re: Playing with cubic splines
Reply #1 - Dec 13th, 2005, 2:07pm
 
You're ahead of me on the math dude, but I've noticed an opportunity for a slight speed increase.

You can make those classes of yours static. (I put the code from your app into the API and just wrote static in front of the classes. Either a mild increase in speed or my hangover is really bad.)

Sorry if you knew this already.
Re: Playing with cubic splines
Reply #2 - Dec 13th, 2005, 6:28pm
 
And the code doesn't do a very good job of revealing the mathematics. To be fair, I drew most of the code from Mathworld, http://mathworld.wolfram.com/CubicSpline.html. Thanks for the static tip. I'm developing the spline class for a data visualization project that I'm working on. Drawing straight lines between data points just looks so ugly to me.

I've updated the code to work on a non-uniform grid spacing (making it much more useful for general plotting). The updated source is here: http://homepage.mac.com/cchoge/Splinewave2/. I'll try adding the static to that also to see if that helps with rendering speed.
Re: Playing with cubic splines
Reply #3 - Dec 14th, 2005, 1:17am
 
Ah, I see what it does now. This actually solves a a problem I had trying to do a point to curve conversion, I was under the impression I would have to fit bezier curves to the points. Very nice.
Re: Playing with cubic splines
Reply #4 - Dec 14th, 2005, 5:28pm
 
The spline I wrote is a one-dimensional spline (where the one shown at the top of the mathworld article is a two-dimensional spline. If you want to have a 2-D spline you will want functions for both x and y as functions of some parameter t. With the classes I wrote you would do it this way:

Code:

float[] t = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
float[] x;
float[] y;

Spline xSpline;
Spline ySpline;

void setup()
{
size( 400, 400 );
x = new float[ t.length ];
y = new float[ t.length ];
for ( int i = 0; i < t.length; ++i )
{
x[i] = random( 0, width );
y[i] = random( 0, height );
}

xSpline = new Spline( t, x );
ySpline = new Spline( t, y );
}

void draw()
{
background(0);
stroke( 255 );
strokeWeight( 1 );
for ( float i = t[0]; i < t[t.length-1]; i += 0.01 )
{
line( xSpline.evaluate(i), ySpline.evaluate(i), xSpline.evaluate(i+0.01), ySpline.evaluate(i+0.01) );
}

stroke( 100, 240, 230 );
strokeWeight( 5 );
for ( int i = 0; i < t.length; ++i )
{
point( x[i], y[i] );
}

}
Re: Playing with cubic splines
Reply #5 - Jan 9th, 2006, 8:04pm
 
cchoge, is there an advantage to implementing cubic splines, rather than using processing's built-in curve() method, which is an implementation of catmull-rom?
Re: Playing with cubic splines
Reply #6 - Jan 9th, 2006, 10:03pm
 
There are a few advantages over the curve function provided by Processing. The first is that the vertices match at both the control points, the first derivatives, and the second derivatives. The second is that mine is more object-oriented (having the curve be an object rather than some OpenGL-like abstraction). The third is that the coefficients for the spline are saved rather than recomputed at every invocation.

The one caveat my code is mainly in the choice of the spline; Catmull-Rom splines give you local control; changes to a knot only change the curve locally (at the cost of second-deriviative continuity), while changes to a knot in natural splines change the entire curve.

I wouldn't say that the advantages are great, but it's nice to have a different point of reference for this functionality.
Re: Playing with cubic splines
Reply #7 - May 3rd, 2006, 11:56am
 
Hello cchoge

Any luck building a non uniform bspline?

I took a look at your thing and even hacked it up. It works great except it only draws a line from start to finish, with splines in-between.

I am currently writing a maya curve importer, but running into trouble since Maya saves data as nurbs knots (I think? I'm not even sure what a knot is!!) and processing currently supports drawing bezier and catmull-rom but not NURBS data.

I've been literally hunting day and night at proper source code (commented, understandable) for NURBS bspline curves with no success. I'm also TRYING to read a lot of these math pages, wiki, etc unfortunately the math equations look like total gibberish to me (cannot understand it at all... even after looking up mathematical shorthand reference...).

Anyways, great work. Hope to hear back from you!
Re: Playing with cubic splines
Reply #8 - Jul 25th, 2006, 10:26pm
 
Thanks for sharing! I was going to make a spline class but you saved me the effort ... here are a couple of interactive examples I've made using it:

http://www.zehao.com/code/splinestring/
http://www.zehao.com/code/splinestring2/
Page Index Toggle Pages: 1