Catmull-Rom Splines

BlitzMax Forums/BlitzMax Beginners Area/Catmull-Rom Splines

Chroma(Posted 2011) [#1]
Here's what I'm trying to do. Have a set number of control points and have images pass through them but at spaced intervals. Also, when they get to the last control point, they start over again at the first control point with no jumping.

Any help is appreciated.


Warpy(Posted 2011) [#2]
A couple of bits of code to get you started.


Chroma(Posted 2011) [#3]
Yeah I saw your code before and the problem I have is how to get a number of images to move along the points at a spaced interval in a smooth manner and just keep circling infinitely. I've been trying to tackle this one for quite awhile unfortunately. :(

Last edited 2011


Jesse(Posted 2011) [#4]
I hacked mines to work with time intervals no physics formula but it works.

I dont know how well you understand OOP but you you can probably use it with minor modifications. Movement is at time spaced intervals but it doesn't restart at modulus 1. you would have to modify it for that(apply your own delta timing too):


'****************************************************************
'	this spline uses x, y, dir and speed 	from Tmovement
'	calculates the current position and angle. x,y is the new Position
'****************************************************************

Type Tbezier Extends Tmovement
	Field ax:Float
	Field ay:Float
	Field bx:Float
	Field by:Float
	Field cx:Float
	Field cy:Float
	Field dx:Float
	Field dy:Float
	Field t1:Float
	Field t2:Float
	Field x2:Float
	Field y2:Float
	Field d:Float
	Field t:Float
	Field rate:Float
	Field old_x:Float
	Field old_y:Float
	Field old_dir:Float
	Field fixed:Int
	
	Method New()
		name = "BEZIER"
	End Method
	' (ax,ay) line 1 point 1, (bx,by) line 1 point 2,(cx,cy) line 2 point 1, (dx,dy) line 2 point 2, fixed(true/false) fixed speed/position speed 
	Function Create:Tbezier(ax:Float, ay:Float, bx:Float, by:Float, cx:Float, cy:Float, dx:Float, dy:Float,_speed:Float = .5,fixed:Int = True)
	
		Local b:Tbezier = New Tbezier
		b.ax = ax
		b.ay = ay
		b.bx = bx
		b.by = by
		b.cx = cx
		b.cy = cy
		b.dx = dx
		b.dy = dy
		b.t1 = 0
		b.t2 = 0
		b.speed = 0 
		b.x = ax
		b.y = ay
		b.x2 = ax
		b.y2 = ay
		b.rate = _speed
		b.fixed = fixed
		Return b

	End Function
	
	Method init:Tbezier(ax:Float, ay:Float, bx:Float, by:Float, cx:Float, cy:Float, dx:Float, dy:Float,_speed:Float = .5,fixed:Int = True)
		Self.ax = ax
		Self.ay = ay
		Self.bx = bx
		Self.by = by
		Self.cx = cx
		Self.cy = cy
		Self.dx = dx
		Self.dy = dy
		Self.t1 = 0
		Self.t2 = 0
		Self.speed = 0 
		Self.x = ax
		Self.y = ay
		Self.x2 = ax
		Self.y2 = ay
		Self.rate = _speed
		Self.fixed = fixed
	End Method
'**************************************************************************
'		displays the complete spline includes points					
'**************************************************************************	

	Method DrawSpline()
		
		Local t:Float,b:Float
		SetColor 255,0,0
		SetRotation 0
		DrawLine ax,ay,bx,by
		DrawLine cx,cy,dx,dy
		Local ox:Float=ax
		Local oy:Float=ay
		SetColor 100,100,100
		For t=0 To 1 Step .01
			b=1-t
			Local px:Float=ax*b^3 + 3*bx*b^2*t + 3*cx*b*t^2 + dx*t^3
			Local py:Float=ay*b^3 + 3*by*b^2*t + 3*cy*b*t^2 + dy*t^3
			DrawRect px-1,py-1,3,3
			DrawLine ox,oy,px,py
			ox=px
			oy=py
		Next
		
	End Method

	Method OnStandBy()
	End Method

	'compute classic spline movement.
	'returns true after cycle has been completed
	'returns false while in cycle.
	Method update:Int()
	
			Local vx:Float
			Local vy:Float
			
			Local ox:Float = x
			Local oy:Float = y
			Local ox2:Float = x2
			Local oy2:Float = y2
			
			t1:+speed*FRL.delta
			
			If t1>1 Then Return False
			
			Local a:Float = t1
			Local b:Float = 1-t1
		
			old_x = x
			old_y = y
			old_dir# = dir#
			x=ax*b^3 + 3*bx*b^2*a + 3*cx*b*a^2 + dx * a^3
			y=ay*b^3 + 3*by*b^2*a + 3*cy*b*a^2 + dy * a^3			
			dir = ATan2(y - old_y,x - old_x)
			If fixed = False
				d = Sqr(x*x+y*y)
				speed#= rate/d/4
			Else
				vx=-3*ax*b*b + 3*bx*b*(b-2*a) + 3*cx*a*(2*b-a) + dx*3*a*a
				vy=-3*ay*b*b + 3*by*b*(b-2*a) + 3*cy*a*(2*b-a) + dy*3*a*a
				d=Sqr(vx*vx+vy*vy)
'                               adjust the one 1.5 to a more consistent value to the normal movement.
				speed#= rate/d/1.5
			EndIf
			Return True 
	End Method

End Type



Uncle(Posted 2011) [#5]
I had the same problem, plus I wanted to make sure the image travelled at a fixed rate along the path i.e. didn't speed up at the corners. The best solution I found was just to use the curve as template, and to actually make a list of points from the curve at equally spaced intervals. Then you can use iterate through this list of points to position your image. Once at the end of the list you can simply restart go back to the beginning of the list.

Here's my code for compiling the list points from a list of curves...

	Method compile(spacing:Float = 3)
		Self.compiledList.Clear()
		Local currentDist:Float = 0
		Local lastX:Float = -999
		Local lastY:Float = -999
		
		If Self.curves.IsEmpty() = False
			'loop through each curve

			
			For Local temp:oBeizer = EachIn Self.curves
				'loop through each point on the curve
				If lastX = -999 Then
					temp.getPointOnGrid(0)
					lastX = temp.retX
					lastY = temp.retY
				End If
				
				For Local t:Float = 0 To 1 Step 0.0001
					temp.getPointOnGrid(t)
					currentDist = currentDist + distance(temp.retX, temp.retY, lastX, lastY)
					If currentDist >= spacing
						currentDist = currentDist - spacing
						'record position
						Local temppoint:oPoint = New oPoint
						temppoint.x = temp.retX
						temppoint.y = temp.retY
						Self.compiledList.AddLast(temppoint)
					End If
					lastX = temp.retX
					lastY = temp.retY
				Next
			Next
		End If
	End Method


Not sure if this helps you.


Chroma(Posted 2011) [#6]
Hmm...I think if you feed the 4 points into this and provide t (0 to 1) for distance along the path....it might work.

Function Catmull:TVec2(p0:TVec2, p1:TVec2, p2:TVec3, p3:TVec2, t#)
	Local x#, y#
	x=.5*((2*p1.x)+(p2.x-p0.x)*t+(2*p0.x-5*p1.x+4*p2.x-p3.x)*t*t+(3*p1.x-p0.x-3*p2.x+p3.x)*t*t*t)
   	y=.5*((2*p1.y)+(p2.y-p0.y)*t+(2*p0.y-5*p1.y+4*p2.y-p3.y)*t*t+(3*p1.y-p0.y-3*p2.y+p3.y)*t*t*t)
	Return Vec2(x, y)
End Function



Chroma(Posted 2011) [#7]
In order to move along a series of points that form a complete track, the points would have to be equally spaced. Then it would just be plugging in 4 sets of points into the above function starting at p1 and moving to p2. Then when you got to p2, just slide the points down one and p2 would become p1 and p3 would become p2. That make sense?

Last edited 2011