Check this out.. Made by SCOUSE (not me)
Type vec2d
Field x#,Y#
Method set(xx#,yy#)
x=xx
Y=yy
End Method
Method add(v:vec2d)
x:+ v.x
Y:+ v.Y
End Method
Method sub(v:vec2d)
x:- v.x
Y:- v.Y
End Method
Method mulS(f#)
x:* f
Y:* f
End Method
Method negate()
x= -x
Y= -Y
End Method
Method Copy(s:vec2d)
x=s.x
Y=s.Y
End Method
Method mag:Float(v:vec2d)
Local xx#,yy#
xx=v.x - x
yy=v.Y - Y
Return sqr(xx*xx + yy*yy)
End Method
End Type
Type cSpline
Global cSplineList:TList
Field pnt:vec2d[]
Field segLen:Float[] 'the length of each segment
Field segPerc:Float[] 'What percentage of the overall length, this segment takes up
Field totalLength:Float
Field outMulti:Float
Field inMulti:Float
Field link:TLink
Method New()
If not cSplineList Then cSplineList = CreateList()
link = ListAddLast(cSplineList,Self)
End Method
Function Create:cSpline(pntCount = 4)
If pntCount<4 Then pntCount = 4
Local c:cSpline = New cSpline
c.pnt = New vec2d[pntCount]
For Local i:Int = 0 To pntCount-1
c.pnt[i] = New vec2d
Next
'segments are always the pointcount - 3
' 1....2----3----4----5----6....7 <7 points, 4 segments
c.segLen = New Float[pntCount-3]
c.segPerc = New Float[pntCount-3]
c.outMulti = 0.5
c.inMulti = 0.5
Return c
End Function
'Adds a point to this curve. Default index of
'-1 will add the point to the end
Method AddPoint(x:Float, Y:Float, index:Int = -1)
If index > pnt.length Then index = -1
If index = -1
pnt = pnt[..pnt.length+1]
pnt[pnt.length-1] = New vec2d
pnt[pnt.length-1].set(x,Y)
segPerc = segPerc[..segPerc.length+1]
segLen = segLen[..segLen.length+1]
Else
End If
UpdateCurve()
End Method
'Makes a cSpline appear closed
Method Close(mergeEnds:Byte = False)
If pnt.length < 6 Return
If mergeEnds 'average the start and end points
Local v:vec2d = New vec2d
v.Copy(pnt[pnt.length-2])
v.sub(pnt[1])
v.mulS(.5)
pnt[1].x:+ v.x
pnt[1].Y:+ v.Y
pnt[pnt.length-2].x:- v.x
pnt[pnt.length-2].Y:- v.Y
End If
pnt[pnt.length-2].Copy(pnt[1])
pnt[pnt.length-1].Copy(pnt[2])
pnt[0].Copy(pnt[pnt.length-3])
UpdateCurve()
End Method
Method DrawCurve(steps:Int = 100)
SetScale 1,1
SetLineWidth 1
Local i:Int,j:Int
Local s:Float
Local t:Float = 1.0 / steps
Local Loop:Float
Local sX:Float = 0, sY:Float = 0
Local lX:Float = 0, lY:Float = 0
lX = pnt[1].x
lY = pnt[1].Y
For i = 0 To pnt.length-4
While Loop <= 1.0
For j = 0 To 3
sX:+ interp(j,Loop) * pnt[i+j].x
sY:+ interp(j,Loop) * pnt[i+j].Y
Next
DrawLine lX,lY,sX,sY
lX = sX
lY = sY
sX = 0
sY = 0
Loop:+ t
Wend
Loop = 0
Next
End Method
Method UpdateCurve(steps:Int=500)
Local i:Int,j:Int
Local cLen:Float = 0
Local t:Float = 1.0 / steps
Local delta:Float = 0
Local lastX#=0,lastY#=0
Local tX#=0,tY#=0
Local sX#=0,sY#=0
totalLength = 0
For i = 0 To pnt.length-4
lastX = pnt[i+1].x
lastY = pnt[i+1].Y
While delta < 1.0
'get the position at point 'delta'
For j = 0 To 3
sX:+ interp(j,delta) * pnt[i+j].x
sY:+ interp(j,delta) * pnt[i+j].Y
Next
tX = sX - lastX
tY = sY - lastY
'Add the length of this step
cLen:+ sqr(tX*tX + tY*tY)
'Record last position
lastX = sX
lastY = sY
sX = 0
sY = 0
delta:+ t
Wend
segLen[i] = cLen
totalLength:+ cLen
cLen = 0
delta = 0
Next
'Update segment percentages
For i = 0 To segLen.length-1
segPerc[i] = segLen[i] / totalLength
Next
End Method
Method GetHeading:Float(Pos:Float)
Local ahead:Float
Local ax:Float,ay:Float
Local bx:Float,by:Float
Local angle:Float
If Pos < .99
ahead = Pos + .01
Else
ahead = Pos - .01
End If
Local a:vec2d = GetPosition(Pos)
Local b:vec2d = GetPosition(ahead)
a.sub(b)
angle = ATan2(-a.x,a.Y)
If Pos < .99
Return angle
Else
Return angle+180
End If
End Method
Method PlotCurve(steps:Int = 100)
SetColor 0,255,0
SetScale 1,1
Local p:vec2d
Local d:Float = 0
Local stp:Float = 1.0 / steps
While d <= 1.0
p = GetPosition(d)
DrawOval p.x-1,p.Y-1,2,2
d:+ stp
Wend
SetColor 255,255,255
End Method
Method GetPosition:vec2d(tPerc:Float)
If tPerc>1 Then tPerc = 1
If tPerc<0 Then tPerc = 0
Local p:vec2d = New vec2d
Local i:Int,j:Int
Local tempTotal:Float = 0
Local tempPerc:Float = 0
'find which segment the target percentage is in
For i = 0 To segPerc.length-1
tempTotal:+ segPerc[i]
If tPerc = tempTotal
p.x = pnt[i+1].x
p.Y = pnt[i+1].Y
Return p
Else If tPerc < tempTotal
Exit
End If
tempPerc:+ segPerc[i]
Next
' i = the segment in which the target percentage falls into
'transform the target 'global' percentage, into a local segment percentage
Local getPerc# = (tPerc - tempPerc) * (1.0 / segPerc[i])
For j = 0 To 3
p.x:+ interp(j,getPerc) * pnt[i+j].x
p.Y:+ interp(j,getPerc) * pnt[i+j].Y
Next
posSeg = i+1
Return p
End Method
Method interp:Float(i:Int, t:Float)
Select i
Case 0
Return ( (-t+2) * t - 1) * t * .5
Case 1
Return (( (3*t-5) *t) * t + 2) * .5
Case 2
Return ( (-3*t+4) * t + 1) * t * .5
Case 3
Return ( (t-1) * t^2) * .5
Default
Return 0
End Select
End Method
End Type
'----------------------------------------------------------------------------------- MAIN PROGRAM
Global cpm#=.5,pm#=.5
'Create a curve with 7 points (first and last are always ghost control points only!)
Local c:cSpline = cSpline.Create(7)
c.pnt[0].set(100,200)
c.pnt[1].set(200,400)
c.pnt[2].set(300,200)
c.pnt[3].set(400,400)
c.pnt[4].set(500,200)
c.pnt[5].set(600,400)
c.pnt[6].set(700,200)
c.UpdateCurve()
Graphics 800,600,0
'make a sprite
Local player:TImage = CreateImage(32,32,1,DYNAMICIMAGE|MASKEDIMAGE)
SetImageHandle player,16,16
DrawOval 0,0,32,32
SetLineWidth 3
SetColor 255,0,0
DrawLine 16,0,10,6
DrawLine 16,0,22,6
DrawLine 10,6,22,6
GrabImage player,0,0,0
SetColor 255,255,255
Local curPoint=0
Local Pos:vec2d = New vec2d
Local playerPos# = 0
Local time%
Local doPlot:Byte = False, doCurve:Byte = True
Local Last:vec2d = New vec2d
Last.Copy(c.pnt[1])
Global posSeg:Int = 0
While not KeyHit(KEY_ESCAPE)
Cls
If KeyDown(KEY_1) curPoint = 0
If KeyDown(KEY_2) curPoint = 1
If KeyDown(KEY_3) curPoint = 2
If KeyDown(KEY_4) curPoint = 3
If KeyDown(KEY_5) curPoint = 4
If KeyDown(KEY_6) curPoint = 5
If KeyDown(KEY_7) curPoint = 6
If KeyDown(KEY_8) curPoint = 7
If KeyDown(KEY_9) curPoint = 8
If KeyDown(KEY_0) curPoint = 9
'curve multiplier adjustment
If KeyDown(KEY_LEFT) c.outMulti:+ .01
If KeyDown(KEY_RIGHT) c.outMulti:- .01
If KeyDown(KEY_UP) c.inMulti:- .01
If KeyDown(KEY_DOWN) c.inMulti:+ .01
If KeyHit(KEY_SPACE) Then c.Close(True)
If KeyHit(KEY_P) Then doPlot = not doPlot
If KeyHit(KEY_C) Then doCurve = not doCurve
If MouseDown(1)
c.pnt[curPoint].set(MouseX(),MouseY())
c.UpdateCurve()
End If
If MouseHit(2)
c.AddPoint(MouseX(),MouseY())
End If
Local n:Int
For n = 0 To c.pnt.length-1
If n=curPoint
SetScale 6,6
SetColor 100,100,100
DrawOval c.pnt[n].x - 7,c.pnt[n].Y - 7,3,3
End If
If n=0 or n=c.pnt.length-1
SetColor 0,255,0
Else
SetColor 255,0,0
End If
SetScale 3,3
DrawOval c.pnt[n].x - 2,c.pnt[n].Y - 2,2,2
SetScale 1,1
SetColor 255,255,255
DrawText "p"+(n+1),c.pnt[n].x,c.pnt[n].Y+2
Next
SetColor 255,255,255
SetScale 1,1
DrawText "Press 1,2,3,4,5,6 or 7 to select point, 'p' & 'c' toggle plot/curve drawing",0,0
DrawText "Current move point: "+(curPoint+1),0,20
DrawText "Approx length of curve: "+c.totalLength,0,40
'For Local i:Int = 0 To c.segPerc.length-1
' DrawText "Segment percentage "+(i+1)+" = : "+c.segPerc[i],0,80+(i*14)
'Next
Pos = c.GetPosition(playerPos)
DrawText Int(100*playerpos)+"%",Pos.x,Pos.Y+20
SetRotation c.GetHeading(playerpos)
DrawImage player,Pos.x,Pos.Y
SetRotation 0
If doCurve Then c.DrawCurve(10) '10 lines per segment
If doPlot Then c.PlotCurve(50)
Flip
FlushMem
playerPos:+ .005'.0025
If playerPos > 1.0 Then playerPos = 0
Wend
copy paste all that and you'll have the coolest spline ever.
only problem is I cannot comprehend his code (its seems to be way too complex)
any chance someone can make a much simpler version of the "cspline?" so I can disect it and add this to my editor?