Ooops
November 28, 2020, 01:39:12 PM

Author Topic: [bmx] Type for Cubic Splines by xMicky [ 1+ years ago ]  (Read 414 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
[bmx] Type for Cubic Splines by xMicky [ 1+ years ago ]
« on: June 29, 2017, 12:28:41 AM »
Title : Type for Cubic Splines
Author : xMicky
Posted : 1+ years ago

Description : Curves and pathes with cubic splines

Code :
Code: BlitzMax
  1. ' gives you a user defined type at hand for easy generating of curves or pathes with cubic splines
  2. Strict
  3.  
  4. Graphics 640,480,0,0
  5. SetClsColor 77, 77, 77
  6.  
  7. Local curKubSplineX:appKubSpline =New appKubSpline
  8. Local curKubSplineY:appKubSpline =New appKubSpline
  9.  
  10. curKubSplineX.GetDataInt([1, 2, 3, 4], [100, 400, 400, 100])
  11. curKubSplineY.GetDataInt([1, 2, 3, 4], [100, 400, 100, 100])
  12.  
  13. Local rCurve:Int =Rand(0, 255)
  14. Local gCurve:Int =Rand(0, 255)
  15. Local bCurve:Int =255 -rCurve
  16. Local curvStep:Float =.05
  17.  
  18. Local tPos:Float =1
  19. Local lastTChange:Int =MilliSecs()
  20. Local setNewCurve:Int =False
  21.  
  22. Local newDataT:Int[]
  23. Local newDataX:Int[]
  24. Local newDataY:Int[]
  25.  
  26. Local constSpeed:Int =False
  27. Local pixelPerSecond:Float =200
  28.  
  29. ' Main loop
  30. Repeat
  31.   Cls  
  32.    
  33.   If KeyHit(KEY_SPACE)
  34.                 setNewCurve =Not(setNewCurve)
  35.     newDataT =newDataT[..0]
  36.           newDataX =newDataX[..0]
  37.           newDataY =newDataY[..0]
  38.         EndIf
  39.        
  40.         If setNewCurve Then
  41.           If MouseHit(1) Then
  42.             newDataT =newDataT[..newDataT.Length +1]
  43.         newDataX =newDataX[..newDataT.Length]
  44.         newDataY =newDataY[..newDataT.Length]
  45.       newDataT[newDataT.Length -1] =newDataT.Length
  46.       newDataX[newDataT.Length -1] =MouseX()
  47.       newDataY[newDataT.Length -1] =MouseY()
  48.           End If
  49.        
  50.         SetColor 200, 200, 0
  51.     For Local curT:Float =1 To newDataT.Length
  52.       DrawOval newDataX[curT -1], newDataY[curT -1], 20, 20
  53.     Next
  54.  
  55.           If KeyHit(KEY_RETURN)
  56.             If newDataT.Length >0 Then
  57.         newDataT =newDataT[..newDataT.Length +1]
  58.           newDataX =newDataX[..newDataT.Length]
  59.           newDataY =newDataY[..newDataT.Length]
  60.         newDataT[newDataT.Length -1] =newDataT.Length
  61.         newDataX[newDataT.Length -1] =newDataX[0]
  62.         newDataY[newDataT.Length -1] =newDataY[0]
  63.  
  64.             curKubSplineX.GetDataInt(newDataT, newDataX)
  65.         curKubSplineY.GetDataInt(newDataT, newDataY)
  66.  
  67.         rCurve =Rand(0, 255)
  68.         gCurve =Rand(0, 255)
  69.         bCurve =255 -rCurve
  70.       End If
  71.       setNewCurve =False
  72.           End If
  73.         End If
  74.  
  75.   ' draw the curve
  76.   SetColor rCurve, gCurve, bCurve
  77.   SetLineWidth(5)
  78.   Local curT:Float =curKubSplineX.dataX[0]
  79.   While curT <curKubSplineX.dataX[curKubSplineX.dataCount -1]
  80.     DrawLine curKubSplineX.ValueInt(curT), curKubSplineY.ValueInt(curT), curKubSplineX.ValueInt(curT +curvStep), curKubSplineY.ValueInt(curT +curvStep)
  81.     curT :+curvStep
  82.   Wend
  83.  
  84.   ' draw the given points
  85.   SetColor 0, 200, 0
  86.   For Local curT:Float =curKubSplineX.dataX[0] To curKubSplineX.dataX[curKubSplineX.dataCount -1]
  87.     DrawOval curKubSplineX.ValueInt(curT), curKubSplineY.ValueInt(curT), 20, 20
  88.   Next
  89.  
  90.   ' draw a point running through the curve
  91.   SetColor 255, 0, 0
  92.   DrawOval curKubSplineX.ValueInt(tPos), curKubSplineY.ValueInt(tPos), 20, 20
  93.  
  94.   If constSpeed Then
  95.     If KeyHit(KEY_V) Then
  96.       constSpeed =False
  97.     End If
  98.     Local nextTPos:Float
  99.     Local curDist:Float =0
  100.     For nextTPos =tPos To tPos +curKubSplineX.dataX[curKubSplineX.dataCount -1] -.001 Step .001
  101.       curDist :+Sqr((curKubSplineX.Value(nextTPos +.001) -curKubSplineX.Value(nextTPos)) ^2 +(curKubSplineY.Value(nextTPos +.001) -curKubSplineY.Value(nextTPos)) ^2)
  102.       If curDist =>pixelPerSecond Then
  103.         Exit
  104.       End If
  105.     Next
  106.     tPos :+(nextTPos -tPos) *(MilliSecs() -lastTChange) /1000
  107.     lastTChange =MilliSecs()
  108.   Else
  109.     If KeyHit(KEY_C) Then
  110.       constSpeed =True
  111.       lastTChange =MilliSecs()
  112.     End If
  113.     tPos :+.001 *Float(curKubSplineX.dataCount)
  114.     If tPos >curKubSplineX.dataX[curKubSplineX.dataCount -1] Then
  115.       tPos =curKubSplineX.dataX[0]
  116.     End If
  117.   End If
  118.  
  119.   SetColor 255, 255, 255
  120.   Local t:String
  121.   If Not(setNewCurve) Then
  122.      t= "Space - Start new curve (set points with left mouse clicks)"
  123.           DrawText t, 10, 10
  124.         Else  
  125.         t ="Return -finish New curve"
  126.           DrawText t, 10, 10
  127.         End If  
  128.         If Not(constSpeed) Then
  129.         t ="c -change to constant speed 200 pixel/second"
  130.           DrawText t, 10, 30
  131.         Else
  132.                 t ="v -change to variable speed depending on the points distances"
  133.           DrawText t, 10, 30
  134.         End If
  135.  
  136.   Flip
  137.  
  138. Until KeyHit(KEY_ESCAPE)
  139.  
  140. End
  141. '==================================================================================================================================
  142. Type appKubSpline
  143.   Field dataX:Float[]
  144.   Field dataY:Float[]
  145.   Field dataCount:Int =0
  146.   Field koeffB:Float[]
  147.   Field koeffC:Float[]
  148.   Field koeffD:Float[]
  149.   '------------------------------------------------------------------------------------------------------------
  150.   ' gets data as FLOAT and calculates the cubic splines
  151.   ' if x-, y-arrays size is different, only the smaller count is taken
  152.   ' data must be sorted uprising for x
  153.   Method GetData(x:Float[], y:Float[])
  154.  
  155.     Local count:Int =Min(x.Length, y.Length)
  156.     dataX =x[..]
  157.     dataX =x[..count]
  158.     dataY =y[..]
  159.     dataY =y[..count]
  160.     koeffB =koeffB[..count]
  161.     koeffC =koeffC[..count]
  162.     koeffD =koeffD[..count]
  163.  
  164.     Local m:Int =count -2
  165.     Local s:Float
  166.     Local r:Float
  167.     For Local i:Int =0 To m
  168.       koeffD[i] =dataX[i +1] -dataX[i]
  169.       r =(dataY[i +1] -dataY[i]) /koeffD[i]
  170.       koeffC[i] =r -s
  171.       s =r
  172.     Next    
  173.     s =0
  174.     r =0
  175.     koeffC[0] =0
  176.     koeffC[count -1] =0
  177.     For Local i:Int =1 To m
  178.       koeffC[i] =koeffC[i] +r *koeffC[i -1]
  179.       koeffB[i] =(dataX[i -1] -dataX[i +1]) *2 -r *s
  180.       s =koeffD[i]
  181.       r =s /koeffB[i]
  182.     Next
  183.     For Local i:Int =m To 1 Step -1
  184.       koeffC[i] =(koeffD[i] *koeffC[i +1] -koeffC[i]) /koeffB[i]
  185.     Next
  186.     For Local i:Int =0 To m
  187.       s =koeffD[i]
  188.       r =koeffC[i +1] -koeffC[i]
  189.       koeffD[i] =r /s
  190.       koeffC[i] =koeffC[i] *3
  191.       koeffB[i] =(dataY[i +1] -dataY[i]) /s -(koeffC[i] +r) *s
  192.     Next
  193.  
  194.     dataCount =count
  195.  
  196.   End Method
  197.   '------------------------------------------------------------------------------------------------------------
  198.   ' gets data as INT and calculates the cubic splines
  199.   ' if x-, y-arrays size is different, only the smaller count is taken
  200.   ' data must be sorted uprising for x
  201.   Method GetDataInt(x:Int[], y:Int[])
  202.  
  203.     Local count:Int =Min(x.Length, y.Length)
  204.    
  205.     dataX =dataX[..count]
  206.     For Local z:Int =1 To count
  207.       dataX[z -1] =Float(x[z -1])
  208.     Next
  209.     dataY =dataY[..count]
  210.     For Local z:Int =1 To count
  211.       dataY[z -1] =Float(y[z -1])
  212.     Next
  213.     koeffB =koeffB[..count]
  214.     koeffC =koeffC[..count]
  215.     koeffD =koeffD[..count]
  216.  
  217.     Local m:Int =count -2
  218.     Local s:Float
  219.     Local r:Float
  220.     For Local i:Int =0 To m
  221.       koeffD[i] =dataX[i +1] -dataX[i]
  222.       r =(dataY[i +1] -dataY[i]) /koeffD[i]
  223.       koeffC[i] =r -s
  224.       s =r
  225.     Next    
  226.     s =0
  227.     r =0
  228.     koeffC[0] =0
  229.     koeffC[count -1] =0
  230.     For Local i:Int =1 To m
  231.       koeffC[i] =koeffC[i] +r *koeffC[i -1]
  232.       koeffB[i] =(dataX[i -1] -dataX[i +1]) *2 -r *s
  233.       s =koeffD[i]
  234.       r =s /koeffB[i]
  235.     Next
  236.     For Local i:Int =m To 1 Step -1
  237.       koeffC[i] =(koeffD[i] *koeffC[i +1] -koeffC[i]) /koeffB[i]
  238.     Next
  239.     For Local i:Int =0 To m
  240.       s =koeffD[i]
  241.       r =koeffC[i +1] -koeffC[i]
  242.       koeffD[i] =r /s
  243.       koeffC[i] =koeffC[i] *3
  244.       koeffB[i] =(dataY[i +1] -dataY[i]) /s -(koeffC[i] +r) *s
  245.     Next
  246.  
  247.     dataCount =count
  248.  
  249.   End Method
  250.   '------------------------------------------------------------------------------------------------------------
  251.   ' returns kubic splines value as FLOAT at given x -position
  252.    'or always 0 if currently no data is loaded
  253.   Method Value:Float(x:Float)
  254.  
  255.     If dataCount =0 Then Return 0
  256.  
  257.     If x <dataX[0] Then
  258.       Repeat
  259.         x :+dataX[dataCount -1] -dataX[0]
  260.       Until x =>dataX[0]
  261.     ElseIf x >dataX[dataCount -1] Then
  262.       Repeat
  263.         x :-dataX[dataCount -1] -dataX[0]
  264.       Until x <=dataX[dataCount -1]
  265.     End If
  266.  
  267.     Local q:Float =Sgn(dataX[dataCount -1] -dataX[0])
  268.     Local k:Int =-1
  269.     Local i:Int
  270.     Repeat
  271.       i =k
  272.       k :+1
  273.     Until (q *x <q *dataX[k]) Or k =dataCount -1
  274.  
  275.     q =x - dataX[i]
  276.     Return ((koeffD[i] *q +koeffC[i]) *q +koeffB[i]) *q +dataY[i]
  277.  
  278.   End Method
  279.   '------------------------------------------------------------------------------------------------------------
  280.   ' returns kubic splines value as rounded INT at given x -position
  281.    'or always 0 if currently no data is loaded
  282.   Method ValueInt:Int(x:Float)
  283.  
  284.     If dataCount =0 Then Return 0
  285.  
  286.     If x <dataX[0] Then
  287.       Repeat
  288.         x :+dataX[dataCount -1] -dataX[0]
  289.       Until x =>dataX[0]
  290.     ElseIf x >dataX[dataCount -1] Then
  291.       Repeat
  292.         x :-dataX[dataCount -1] -dataX[0]
  293.       Until x <=dataX[dataCount -1]
  294.     End If
  295.  
  296.     Local q:Float =Sgn(dataX[dataCount -1] -dataX[0])
  297.     Local k:Int =-1
  298.     Local i:Int
  299.     Repeat
  300.       i =k
  301.       k :+1
  302.     Until (q *x <q *dataX[k]) Or k =dataCount -1
  303.  
  304.     q =x - dataX[i]
  305.     Local tmpResult:Float =((koeffD[i] *q +koeffC[i]) *q +koeffB[i]) *q +dataY[i]
  306.     If tmpResult -Floor(tmpResult) <=.5 Then
  307.       Return Floor(tmpResult)
  308.     Else
  309.       Return Floor(tmpResult) +1
  310.     End If
  311.  
  312.   End Method
  313.   '------------------------------------------------------------------------------------------------------------
  314.  
  315. End Type
  316. '------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Comments :


Jesse(Posted 1+ years ago)

 modified for easy use:
Code: [Select]


' gives you a user defined type at hand for easy generating of curves or pathes with
' cubic splines
SuperStrict

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

Local CKSplineX:TSpline =New TSpline
Local CKSplineY:TSpline =New TSpline
Local x:Int[]
Local y:Int[]
GetSplineDataInt(x,y)
CKSplineX.GetDataInt(x)
CKSplineY.GetDataInt(y)

#curve
DefData 5 'number of x,y points
'   x,y, x,y.. last point must match first point
DefData 600,200, 200,50, 50,240, 200,430, 600,200
 

Local tPos:Float =1 ' 1 is always the first point in the spline
' Main loop
SetBlend alphablend
Repeat
  Cls  
   
  ' draw the curve
 SetLineWidth(5)
 Local curT:Float
' draw a point running through the curve
SetColor 255, 0, 0
DrawOval CKSplineX.ValueInt(tPos)-10, CKSplineY.ValueInt(tPos)-10, 20, 20
' If CKSplineX.NextLoopStep(Tpos,CKSplineY) End 'returns true after cycling trough all points
If CKSplineX.NextTimeStep(Tpos,CKSplineY) End 'returns true after cycling trough all points
Flip()

Until KeyHit(KEY_ESCAPE)

End
'==================================================================

Type TSpline
  Field dataX:Float[]
  Field dataY:Float[]
  Field koeffB:Float[]
  Field koeffC:Float[]
  Field koeffD:Float[]
  Field dataCount:Int =0
  Field OldTPos:Float
  Global pixelPerSecond:Float = 200
  Global lastTChange:Int = MilliSecs()

  '----------------------------------------------------------------
  ' 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( y:Float[])

    Local count:Int = y.Length
    dataX = New Float[count]
    For Local i:Int = 0 Until count
dataX[i] = i+1
Next
    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
lastTChange:Int = MilliSecs()

  End Method
  '----------------------------------------------------------------
  ' gets data as INT and calculates the cubic splines
  ' data must be sorted uprising for x
  Method GetDataInt(y:Int[])

    Local count:Int =y.Length
   
    dataX = New Float[count]

    For Local z:Int =0 Until count
dataX[z] = z+1
    Next
 
    dataY =New Float[count]

    For Local z:Int =0 Until count
      dataY[z] =Float(y[z])
    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

'---------------------Loop Step--------------------------------------------------------

Method NextLoopStep:Int(Tpos:Float Var,CKSplineY:TSpline)
Local nextTPos:Float
Local curDist:Float =0
For nextTPos =tPos To tPos +dataX[dataCount -1] Step .001
Local x:Float =Value(nextTPos +.001) -Value(nextTPos)
Local y:Float =CKSplineY.Value(nextTPos +.001) -CKSplineY.Value(nextTPos)
curDist :+Sqr(x ^2+y ^2)
If curDist =>pixelPerSecond Then Exit
Next
TPos :+(nextTPos -tPos) /1000
TPos = TPos Mod (DataX.length)
If Tpos < OldTPos
TPos = 1.0
OldTpos = TPos
Return True
EndIf
OldTPos = TPos
Return False
End Method

'------------------------time step-----------------------------------------------------

Method NextTimeStep:Int(Tpos:Float Var, CKSplineY:TSpline)
Local nextTPos:Float
Local curDist:Float = 0
For nextTPos =tPos To tPos + dataX[dataCount -1] Step .001
Local x:Float = Value(nextTPos +.001) -Value(nextTPos)
Local y:Float = CKSplineY.Value(nextTPos +.001) -CKSplineY.Value(nextTPos)
curDist :+Sqr(x^2 + y^2)
If curDist =>pixelPerSecond Then Exit
Next

tPos :+(nextTPos -tPos) *(MilliSecs() -lastTChange) /1000
TPos = TPos Mod (DataX.length)
lastTChange =MilliSecs()
If Tpos < OldTpos
Tpos = 0.0
OldTpos = Tpos
Return True
EndIf
OldTPos = TPos
Return False
End Method

End Type


Function GetSplineDataInt(x:Int[] Var ,y:Int[] Var)
Local count:Int
RestoreData curve
ReadData count
x = New Int[count]
y = New Int[count]
For Local i:Int = 0 Until count
ReadData x[i],y[i]
Next
End Function


 

SimplePortal 2.3.6 © 2008-2014, SimplePortal