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