Ooops
January 24, 2021, 01:10:41 PM

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

#### 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. '------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

`' gives you a user defined type at hand for easy generating of curves or pathes with' cubic splinesSuperStrictGraphics 640,480,0,0SetClsColor 77, 100, 77Local CKSplineX:TSpline =New TSplineLocal CKSplineY:TSpline =New TSplineLocal 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 pointDefData 600,200, 200,50, 50,240, 200,430, 600,200  Local tPos:Float =1 ' 1 is always the first point in the spline' Main loopSetBlend alphablendRepeat  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 MethodEnd TypeFunction 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] NextEnd Function`