Simple Spline Module

Share your advanced PureBasic knowledge/code with the community.
Mr.L
Enthusiast
Enthusiast
Posts: 146
Joined: Sun Oct 09, 2011 7:39 am

Simple Spline Module

Post by Mr.L »

Here is a Spline Module without the need to use InitEngine3D().
It is ported from Ogres SimpleSpline Class, so it "should" produce the same output as the PureBasic functions.

Code: Select all

DeclareModule Spline
	Structure Vec3
		x.f
		y.f
		z.f
	EndStructure
	
	Macro _Spline_SetVec3_(v_, x_, y_, z_)
		v_\x = x_
		v_\y = y_
		v_\z = z_
	EndMacro
	
	Declare Create()
	Declare AddPoint(*spline, x.f, y.f, z.f)
	Declare Close(*spline)
	Declare.f PointX(*spline, index.i)
	Declare.f PointY(*spline, index.i)
	Declare.f PointZ(*spline, index.i)
	Declare.f ComputeX(*spline)
	Declare.f ComputeY(*spline)
	Declare.f ComputeZ(*spline)
	Declare Count(*spline)
	Declare Update(*spline, index.i, x.f, y.f, z.f)
	Declare Compute(*spline, t.f)
	Declare Clear(*spline)
	Declare Free(*spline)
EndDeclareModule

Module Spline	
	EnableExplicit
	
	Structure SPLINE
		nrPoints.l
		Array points.Vec3(0)
		Array tangents.Vec3(0)
		splinePos.Vec3
	EndStructure
	
	Procedure UpdateTangents(*spline.SPLINE)
		If *spline And *spline\nrPoints > 1
			Protected i
			Protected nrPoints = *spline\nrPoints
			
			If (*spline\points(0)\x = *spline\points(nrPoints - 1)\x) And 
			   (*spline\points(0)\y = *spline\points(nrPoints - 1)\y) And
			   (*spline\points(0)\z = *spline\points(nrPoints - 1)\z)
				
				; spline is closed
				_Spline_SetVec3_(*spline\tangents(0),
				                 0.5 * (*spline\points(1)\x - *spline\points(nrPoints - 2)\x),
				                 0.5 * (*spline\points(1)\y - *spline\points(nrPoints - 2)\y),
				                 0.5 * (*spline\points(1)\z - *spline\points(nrPoints - 2)\z))
				_Spline_SetVec3_(*spline\tangents(nrPoints - 1),
				                 *spline\tangents(0)\x, *spline\tangents(0)\y, *spline\tangents(0)\z)
				For i = 1 To nrPoints - 2
					_Spline_SetVec3_(*spline\tangents(i), 
					                 0.5 * (*spline\points(i + 1)\x - *spline\points(i - 1)\x),
					                 0.5 * (*spline\points(i + 1)\y - *spline\points(i - 1)\y),
					                 0.5 * (*spline\points(i + 1)\z - *spline\points(i - 1)\z))
				Next
			Else
				; spline is not closed
				_Spline_SetVec3_(*spline\tangents(0),
				                 0.5 * (*spline\points(1)\x - *spline\points(0)\x),
				                 0.5 * (*spline\points(1)\y - *spline\points(0)\y),
				                 0.5 * (*spline\points(1)\z - *spline\points(0)\z))
				_Spline_SetVec3_(*spline\tangents(nrPoints - 1),
				                 0.5 * (*spline\points(nrPoints - 1)\x - *spline\points(nrPoints - 2)\x),
				                 0.5 * (*spline\points(nrPoints - 1)\y - *spline\points(nrPoints - 2)\y),
				                 0.5 * (*spline\points(nrPoints - 1)\z - *spline\points(nrPoints - 2)\z))
				For i = 1 To nrPoints - 2
					_Spline_SetVec3_(*spline\tangents(i),
					                 0.5 * (*spline\points(i+1)\x - *spline\points(i - 1)\x),
					                 0.5 * (*spline\points(i+1)\y - *spline\points(i - 1)\y),
					                 0.5 * (*spline\points(i+1)\z - *spline\points(i - 1)\z))
				Next
			EndIf
		EndIf
	EndProcedure
	
	Procedure Interpolate(*spline.SPLINE, index.i, t.f)
		If index < *spline\nrPoints
			Protected *point1.Vec3 = @*spline\points(index)
			Protected *point2.Vec3 = @*spline\points(index + 1)
			
			If (index + 1) = *spline\nrPoints
				_Spline_SetVec3_(*spline\splinePos, *point1\x, *point1\y, *point1\z)
			ElseIf t = 0
				_Spline_SetVec3_(*spline\splinePos, *point1\x, *point1\y, *point1\z)
			ElseIf t = 1
				_Spline_SetVec3_(*spline\splinePos, *point2\x, *point2\y, *point2\z)
			Else
				Protected.f t2 = t * t
				Protected.f t3 = t2 * t
				
				Protected *tan1.Vec3 = @*spline\tangents(index)
				Protected *tan2.Vec3 = @*spline\tangents(index + 1)
				
				_Spline_SetVec3_(*spline\splinePos,
				                 t3 * (2 * *point1\x - 2 * *point2\x + *tan1\x + *tan2\x) + 
				                 t2 * (-3 * *point1\x + 3 * *point2\x - 2 * *tan1\x - *tan2\x) +
				                 t * *tan1\x + *point1\x,
				                 t3 * (2 * *point1\y - 2 * *point2\y + *tan1\y + *tan2\y) +
				                 t2 * (-3 * *point1\y + 3 * *point2\y - 2 * *tan1\y - *tan2\y) +
				                 t * *tan1\y + *point1\y,
				                 t3 * (2 * *point1\z - 2 * *point2\z + *tan1\z + *tan2\z) +
				                 t2 * (-3 * *point1\z + 3 * *point2\z - 2 * *tan1\z - *tan2\z) +
				                 t * *tan1\z + *point1\z)
			EndIf
		EndIf
	EndProcedure
	
	Procedure Create()
		Protected *spline.SPLINE = AllocateStructure(SPLINE)
		ProcedureReturn *spline
	EndProcedure
	
	Procedure AddPoint(*spline.SPLINE, x.f, y.f, z.f)
		If *spline
			If *spline\nrPoints >= ArraySize(*spline\points()) 
				ReDim *spline\points(*spline\nrPoints + 4)
				ReDim *spline\tangents(*spline\nrPoints + 4)
			EndIf
			
			_Spline_SetVec3_(*spline\points(*spline\nrPoints), x, y, z)
			*spline\nrPoints + 1
			
			UpdateTangents(*spline)
		EndIf
	EndProcedure
	
	Procedure Close(*spline.SPLINE)
		If *spline And *spline\nrPoints > 0
			If *spline\points(0)\x <> *spline\points(*spline\nrPoints - 1)\x Or
			   *spline\points(0)\y <> *spline\points(*spline\nrPoints - 1)\y Or
			   *spline\points(0)\z <> *spline\points(*spline\nrPoints - 1)\z
				Spline::AddPoint(*spline, *spline\points(0)\x, *spline\points(0)\y, *spline\points(0)\z)
			EndIf
		EndIf
	EndProcedure
	
	Procedure.f PointX(*spline.SPLINE, index.i)
		If *spline And index < *spline\nrPoints
			ProcedureReturn *spline\points(index)\x
		EndIf
	EndProcedure
	
	Procedure.f PointY(*spline.SPLINE, index.i)
		If *spline And index < *spline\nrPoints
			ProcedureReturn *spline\points(index)\y
		EndIf
	EndProcedure
	
	Procedure.f PointZ(*spline.SPLINE, index.i)
		If *spline And index < *spline\nrPoints
			ProcedureReturn *spline\points(index)\z
		EndIf
	EndProcedure
	
	Procedure.f ComputeX(*spline.SPLINE)
		If *spline
			ProcedureReturn *spline\splinePos\x
		EndIf
	EndProcedure
	
	Procedure.f ComputeY(*spline.SPLINE)
		If *spline
			ProcedureReturn *spline\splinePos\y
		EndIf
	EndProcedure
	
	Procedure.f ComputeZ(*spline.SPLINE)
		If *spline
			ProcedureReturn *spline\splinePos\z
		EndIf
	EndProcedure
	
	Procedure Count(*spline.SPLINE)
		If *spline
			ProcedureReturn *spline\nrPoints
		EndIf
	EndProcedure
	
	Procedure Clear(*spline.SPLINE)
		If *spline
			*spline\nrPoints = 0
			Dim *spline\points(0)
			Dim *spline\tangents(0)
		EndIf
	EndProcedure
	
	Procedure Free(*spline.SPLINE)
		If *spline
			FreeStructure(*spline)
		EndIf
	EndProcedure
	
	Procedure Update(*spline.SPLINE, index.i, x.f, y.f, z.f)
		If *spline And index < *spline\nrPoints
			_Spline_SetVec3_(*spline\points(index), x, y, z)
			UpdateTangents(*spline)
		EndIf
	EndProcedure
	
	Procedure Compute(*spline.SPLINE, t.f)
		If *spline
			Protected fSeg.f = t * (*spline\nrPoints - 1);
			Protected segIdx = Int(fSeg)
			Interpolate(*spline, segIdx, fSeg - segIdx)
		EndIf
	EndProcedure
	
	DisableExplicit
EndModule


CompilerIf #PB_Compiler_IsMainFile
	OpenWindow(0,0,0,600,600,"SimpleSpline")
	CanvasGadget(0,0,0,600,600)
	
	spline = Spline::Create()
	For r = 0 To 359 Step 18
		rad = 150 + Sin(Radian(r * 5)) * 100
		Spline::AddPoint(spline, 300 + Sin(Radian(r)) * rad, 300 + Cos(Radian(r)) * rad, 0)
	Next
	Spline::Close(spline)
	
	If StartVectorDrawing(CanvasVectorOutput(0))
		Spline::Compute(spline, 0)
		MovePathCursor(Spline::ComputeX(spline), Spline::ComputeY(spline))
		t.f = 0.005 : Repeat
			Spline::Compute(spline, t)
			AddPathLine(Spline::ComputeX(spline), Spline::ComputeY(spline))
			t + 0.005
		Until t > 1
		AddPathCircle(300, 300, 30)
		VectorSourceColor(RGBA(225,225,255,255))
		FillPath(#PB_Path_Winding | #PB_Path_Preserve)
		VectorSourceColor(RGBA(0,0,0,255))
		StrokePath(5)
		StopVectorDrawing()
	EndIf
	
	Repeat
	Until WaitWindowEvent(50) = #PB_Event_CloseWindow
	
	Spline::Free(spline)
CompilerEndIf
Last edited by Mr.L on Tue Jun 25, 2024 10:54 am, edited 1 time in total.
Fred
Administrator
Administrator
Posts: 18220
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Simple Spline Module

Post by Fred »

Comtois already did something similar: https://www.purebasic.fr/english/viewto ... fc4259cb1a :)
Mr.L
Enthusiast
Enthusiast
Posts: 146
Joined: Sun Oct 09, 2011 7:39 am

Re: Simple Spline Module

Post by Mr.L »

Fred wrote: Tue Jun 25, 2024 10:53 am Comtois already did something similar
That's true, but my "Simple Spline" is made simpler :lol:
I have implemented the whole Matrix calculation part into the Interpolate Procedure, and added a Spline::Close Function.
User avatar
idle
Always Here
Always Here
Posts: 5897
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Simple Spline Module

Post by idle »

Thanks, It's a useful module.
Post Reply