Code archives/Algorithms/Type for Cubic Splines

This code has been declared by its author to be Public Domain code.

Download source code

Type for Cubic Splines by xMicky2006
Curves and pathes with cubic splines
' gives you a user defined type at hand for easy generating of curves or pathes with cubic splines
Strict

Graphics 640,480,0,0
SetClsColor 77, 77, 77

Local curKubSplineX:appKubSpline =New appKubSpline
Local curKubSplineY:appKubSpline =New appKubSpline

curKubSplineX.GetDataInt([1, 2, 3, 4], [100, 400, 400, 100])
curKubSplineY.GetDataInt([1, 2, 3, 4], [100, 400, 100, 100])

Local rCurve:Int =Rand(0, 255)
Local gCurve:Int =Rand(0, 255)
Local bCurve:Int =255 -rCurve
Local curvStep:Float =.05

Local tPos:Float =1
Local lastTChange:Int =MilliSecs()
Local setNewCurve:Int =False

Local newDataT:Int[]
Local newDataX:Int[]
Local newDataY:Int[]

Local constSpeed:Int =False
Local pixelPerSecond:Float =200

' Main loop
Repeat
  Cls  
    
  If KeyHit(KEY_SPACE)
		setNewCurve =Not(setNewCurve)
    newDataT =newDataT[..0]
 	  newDataX =newDataX[..0]
 	  newDataY =newDataY[..0]
	EndIf
	
	If setNewCurve Then
	  If MouseHit(1) Then
	    newDataT =newDataT[..newDataT.Length +1]
    	newDataX =newDataX[..newDataT.Length]
    	newDataY =newDataY[..newDataT.Length]
      newDataT[newDataT.Length -1] =newDataT.Length
      newDataX[newDataT.Length -1] =MouseX()
      newDataY[newDataT.Length -1] =MouseY()
	  End If 
	
  	SetColor 200, 200, 0
    For Local curT:Float =1 To newDataT.Length
      DrawOval newDataX[curT -1], newDataY[curT -1], 20, 20
    Next

	  If KeyHit(KEY_RETURN)
	    If newDataT.Length >0 Then
      	newDataT =newDataT[..newDataT.Length +1]
    	  newDataX =newDataX[..newDataT.Length]
    	  newDataY =newDataY[..newDataT.Length]
        newDataT[newDataT.Length -1] =newDataT.Length
        newDataX[newDataT.Length -1] =newDataX[0]
        newDataY[newDataT.Length -1] =newDataY[0]

  	    curKubSplineX.GetDataInt(newDataT, newDataX)
        curKubSplineY.GetDataInt(newDataT, newDataY)

        rCurve =Rand(0, 255)
        gCurve =Rand(0, 255)
        bCurve =255 -rCurve
      End If
      setNewCurve =False
	  End If
	End If 

  ' draw the curve
  SetColor rCurve, gCurve, bCurve
  SetLineWidth(5) 
  Local curT:Float =curKubSplineX.dataX[0]
  While curT <curKubSplineX.dataX[curKubSplineX.dataCount -1]
    DrawLine curKubSplineX.ValueInt(curT), curKubSplineY.ValueInt(curT), curKubSplineX.ValueInt(curT +curvStep), curKubSplineY.ValueInt(curT +curvStep)
    curT :+curvStep
  Wend

  ' draw the given points
  SetColor 0, 200, 0
  For Local curT:Float =curKubSplineX.dataX[0] To curKubSplineX.dataX[curKubSplineX.dataCount -1]
    DrawOval curKubSplineX.ValueInt(curT), curKubSplineY.ValueInt(curT), 20, 20
  Next

  ' draw a point running through the curve
  SetColor 255, 0, 0
  DrawOval curKubSplineX.ValueInt(tPos), curKubSplineY.ValueInt(tPos), 20, 20

  If constSpeed Then
    If KeyHit(KEY_V) Then
      constSpeed =False
    End If
    Local nextTPos:Float
    Local curDist:Float =0
    For nextTPos =tPos To tPos +curKubSplineX.dataX[curKubSplineX.dataCount -1] -.001 Step .001
      curDist :+Sqr((curKubSplineX.Value(nextTPos +.001) -curKubSplineX.Value(nextTPos)) ^2 +(curKubSplineY.Value(nextTPos +.001) -curKubSplineY.Value(nextTPos)) ^2) 
      If curDist =>pixelPerSecond Then
        Exit
      End If 
    Next
    tPos :+(nextTPos -tPos) *(MilliSecs() -lastTChange) /1000 
    lastTChange =MilliSecs()
  Else 
    If KeyHit(KEY_C) Then
      constSpeed =True
      lastTChange =MilliSecs()
    End If
    tPos :+.001 *Float(curKubSplineX.dataCount)
    If tPos >curKubSplineX.dataX[curKubSplineX.dataCount -1] Then
      tPos =curKubSplineX.dataX[0]
    End If
  End If

  SetColor 255, 255, 255
  Local t:String
  If Not(setNewCurve) Then
     t= "Space - Start new curve (set points with left mouse clicks)"
	  DrawText t, 10, 10
	Else  
  	t ="Return -finish New curve"
	  DrawText t, 10, 10
	End If  
	If Not(constSpeed) Then 
  	t ="c -change to constant speed 200 pixel/second"
	  DrawText t, 10, 30
	Else
		t ="v -change to variable speed depending on the points distances"
	  DrawText t, 10, 30
	End If

  Flip

Until KeyHit(KEY_ESCAPE)

End
'==================================================================================================================================
Type appKubSpline
  Field dataX:Float[]
  Field dataY:Float[]
  Field dataCount:Int =0
  Field koeffB:Float[]
  Field koeffC:Float[]
  Field koeffD:Float[]
  '------------------------------------------------------------------------------------------------------------
  ' gets data as FLOAT and calculates the cubic splines
  ' if x-, y-arrays size is different, only the smaller count is taken
  ' data must be sorted uprising for x
  Method GetData(x:Float[], y:Float[])

    Local count:Int =Min(x.Length, y.Length)
    dataX =x[..]
    dataX =x[..count]
    dataY =y[..]
    dataY =y[..count]
    koeffB =koeffB[..count]
    koeffC =koeffC[..count]
    koeffD =koeffD[..count]

    Local m:Int =count -2
    Local s:Float
    Local r:Float
    For Local i:Int =0 To m
      koeffD[i] =dataX[i +1] -dataX[i]
      r =(dataY[i +1] -dataY[i]) /koeffD[i]
      koeffC[i] =r -s
      s =r 
    Next    
    s =0
    r =0
    koeffC[0] =0
    koeffC[count -1] =0
    For Local i:Int =1 To m
      koeffC[i] =koeffC[i] +r *koeffC[i -1]
      koeffB[i] =(dataX[i -1] -dataX[i +1]) *2 -r *s
      s =koeffD[i]
      r =s /koeffB[i]
    Next
    For Local i:Int =m To 1 Step -1
      koeffC[i] =(koeffD[i] *koeffC[i +1] -koeffC[i]) /koeffB[i]
    Next
    For Local i:Int =0 To m
      s =koeffD[i]
      r =koeffC[i +1] -koeffC[i]
      koeffD[i] =r /s
      koeffC[i] =koeffC[i] *3
      koeffB[i] =(dataY[i +1] -dataY[i]) /s -(koeffC[i] +r) *s
    Next

    dataCount =count

  End Method
  '------------------------------------------------------------------------------------------------------------
  ' gets data as INT and calculates the cubic splines
  ' if x-, y-arrays size is different, only the smaller count is taken
  ' data must be sorted uprising for x
  Method GetDataInt(x:Int[], y:Int[])

    Local count:Int =Min(x.Length, y.Length)
    
    dataX =dataX[..count]
    For Local z:Int =1 To count
      dataX[z -1] =Float(x[z -1])
    Next
    dataY =dataY[..count]
    For Local z:Int =1 To count
      dataY[z -1] =Float(y[z -1])
    Next
    koeffB =koeffB[..count]
    koeffC =koeffC[..count]
    koeffD =koeffD[..count]

    Local m:Int =count -2
    Local s:Float
    Local r:Float
    For Local i:Int =0 To m
      koeffD[i] =dataX[i +1] -dataX[i]
      r =(dataY[i +1] -dataY[i]) /koeffD[i]
      koeffC[i] =r -s
      s =r 
    Next    
    s =0
    r =0
    koeffC[0] =0
    koeffC[count -1] =0
    For Local i:Int =1 To m
      koeffC[i] =koeffC[i] +r *koeffC[i -1]
      koeffB[i] =(dataX[i -1] -dataX[i +1]) *2 -r *s
      s =koeffD[i]
      r =s /koeffB[i]
    Next
    For Local i:Int =m To 1 Step -1
      koeffC[i] =(koeffD[i] *koeffC[i +1] -koeffC[i]) /koeffB[i]
    Next
    For Local i:Int =0 To m
      s =koeffD[i]
      r =koeffC[i +1] -koeffC[i]
      koeffD[i] =r /s
      koeffC[i] =koeffC[i] *3
      koeffB[i] =(dataY[i +1] -dataY[i]) /s -(koeffC[i] +r) *s
    Next

    dataCount =count

  End Method
  '------------------------------------------------------------------------------------------------------------
  ' returns kubic splines value as FLOAT at given x -position 
   'or always 0 if currently no data is loaded
  Method Value:Float(x:Float)

    If dataCount =0 Then Return 0

    If x <dataX[0] Then 
      Repeat
        x :+dataX[dataCount -1] -dataX[0]
      Until x =>dataX[0]
    ElseIf x >dataX[dataCount -1] Then
      Repeat
        x :-dataX[dataCount -1] -dataX[0]
      Until x <=dataX[dataCount -1]
    End If

    Local q:Float =Sgn(dataX[dataCount -1] -dataX[0])
    Local k:Int =-1
    Local i:Int
    Repeat
      i =k
      k :+1
    Until (q *x <q *dataX[k]) Or k =dataCount -1

    q =x - dataX[i]
    Return ((koeffD[i] *q +koeffC[i]) *q +koeffB[i]) *q +dataY[i]

  End Method
  '------------------------------------------------------------------------------------------------------------
  ' returns kubic splines value as rounded INT at given x -position 
   'or always 0 if currently no data is loaded
  Method ValueInt:Int(x:Float)

    If dataCount =0 Then Return 0

    If x <dataX[0] Then 
      Repeat
        x :+dataX[dataCount -1] -dataX[0]
      Until x =>dataX[0]
    ElseIf x >dataX[dataCount -1] Then
      Repeat
        x :-dataX[dataCount -1] -dataX[0]
      Until x <=dataX[dataCount -1]
    End If

    Local q:Float =Sgn(dataX[dataCount -1] -dataX[0])
    Local k:Int =-1
    Local i:Int
    Repeat
      i =k
      k :+1
    Until (q *x <q *dataX[k]) Or k =dataCount -1

    q =x - dataX[i]
    Local tmpResult:Float =((koeffD[i] *q +koeffC[i]) *q +koeffB[i]) *q +dataY[i]
    If tmpResult -Floor(tmpResult) <=.5 Then
      Return Floor(tmpResult)
    Else
      Return Floor(tmpResult) +1
    End If

  End Method
  '------------------------------------------------------------------------------------------------------------

End Type
'------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Comments

Jesse2008
modified for easy use:



Code Archives Forum