This is fairly small 3D Spline module derivated from Guimauve's General Maths Matrix calculation.
For the moment, it's a Bezier spline implementation but I'm working on B-Spline curves but it's not working just yet.
By the way, this module reuse the Vector3 module from my Gaming Maths Library available here https://www.dropbox.com/s/ei1cckqn22r9b ... y.zip?dl=0
Edit : A little addition, the tangent vector along the Path. Obviously you have to use the same definition polygon and the same amount of segment when you call Spline3D::BezierPath(PolygoneBezier(), 40, Path()) and Spline3D::BezierTangent(PolygoneBezier(), 40, Tangent())
Best regards
StarBootics
Code: Select all
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : Spline3D - Module
; File Name : Spline3D - Module.pb
; File version: 0.0.2
; Programming : OK
; Programmed by : StarBootics
; Date : 12-10-2016
; Last Update : 13-10-2016
; PureBasic code : V5.50
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; This code was excerpt of Guimauve's General Maths Matrix calculation library.
;
; http://www.purebasic.fr/english/viewtopic.php?f=12&t=47084
;
; Very nice lib by the way, that being said I just need the bezier spline
; calculation. I will try to 3D BSpline curves as well.
;
; Please note, my Gaming Maths library is needed :
; https://www.dropbox.com/s/ei1cckqn22r9brh/Gaming_Maths_Library.zip?dl=0
;
; This code is free to be use where ever you like but you use it at your own
; risk.
;
; The author can in no way be held responsible for data loss, damage or other
; annoying situations that may occur.
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
CompilerIf #PB_Compiler_IsMainFile
IncludeFile "Vector3 - Module.pb"
CompilerEndIf
DeclareModule Spline3D
Declare BezierPath(List DefinitionPolygon.Vector3::Vector3(), SegmentCount.l, List Path.Vector3::Vector3())
Declare BezierTangent(List DefinitionPolygon.Vector3::Vector3(), SegmentCount.l, List Tangent.Vector3::Vector3())
EndDeclareModule
Module Spline3D
Macro LinearlySpacedValue(IncrementID, IncrementMax, MinValue, MaxValue)
((MinValue) + ((MaxValue) - (MinValue)) * ((IncrementID) / (IncrementMax)))
EndMacro
Procedure.d BernsteinPolynomial(P_n.l, P_i.l, P_u.d)
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The Bernstein Polynom function are based on Combinations
; calculation routine from Dr. Dri (French Forum)
If P_n >= 0 And P_i >= 0
Part.d = Pow(P_u, P_i) * Pow(1 - P_u, P_n - P_i)
If P_n >= P_i
Combinations.d = 1.0
If P_i > P_n >> 1
P_i = P_n - P_i
EndIf
While P_i > 0
Combinations * P_n / P_i
P_n - 1
P_i - 1
Wend
If Combinations - Round(Combinations, 0) > 0.5
Combinations + 1
EndIf
EndIf
EndIf
ProcedureReturn Round(Combinations, 0) * Part
EndProcedure
Procedure.d BernsteinPolynomialDerivate1st(P_n.l, P_i.l, P_u.d)
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The Bernstein Polynom First Derivate function are based on Combinations
; calculation routine from Dr. Dri (French Forum)
If P_n >= 0 And P_i >= 0
Part.d = (P_i / P_u - (P_i - P_n) / (P_u - 1)) * Pow(P_u, P_i) * Pow(1 - P_u, P_n - P_i)
If P_n >= P_i
Combinations.d = 1.0
If P_i > P_n >> 1
P_i = P_n - P_i
EndIf
While P_i > 0
Combinations * P_n / P_i
P_n - 1
P_i - 1
Wend
If Combinations - Round(Combinations, 0) > 0.5
Combinations + 1
EndIf
EndIf
EndIf
ProcedureReturn Round(Combinations, 0) * Part
EndProcedure
Procedure BezierPath(List DefinitionPolygon.Vector3::Vector3(), SegmentCount.l, List Path.Vector3::Vector3())
ClearList(Path())
P_n = ListSize(DefinitionPolygon()) - 1
For U_ID = 0 To SegmentCount
ForEach DefinitionPolygon()
BP.d = BernsteinPolynomial(P_n, ListIndex(DefinitionPolygon()), LinearlySpacedValue(U_ID, SegmentCount, 0.0, 1.0))
SumX.d = SumX + BP * Vector3::GetI(DefinitionPolygon())
SumY.d = SumY + BP * Vector3::GetJ(DefinitionPolygon())
SumZ.d = SumZ + BP * Vector3::GetK(DefinitionPolygon())
Next
AddElement(Path())
Vector3::Update(Path(), SumX, SumY, SumZ)
SumX = 0.0
SumY = 0.0
SumZ = 0.0
Next
EndProcedure
Procedure BezierTangent(List DefinitionPolygon.Vector3::Vector3(), SegmentCount.l, List Tangent.Vector3::Vector3())
ClearList(Tangent())
P_n = ListSize(DefinitionPolygon()) - 1
For U_ID = 0 To SegmentCount
ForEach DefinitionPolygon()
BPD1st.d = BernsteinPolynomialDerivate1st(P_n, ListIndex(DefinitionPolygon()), LinearlySpacedValue(U_ID, SegmentCount, 0.0, 1.0))
SumXP.d = SumXP + BPD1st * Vector3::GetI(DefinitionPolygon())
SumYP.d = SumYP + BPD1st * Vector3::GetJ(DefinitionPolygon())
SumZP.d = SumZP + BPD1st * Vector3::GetK(DefinitionPolygon())
Next
AddElement(Tangent())
If Not IsNAN(SumXP)
Vector3::Update(Tangent(), SumXP, SumYP, SumZP)
Vector3::Unit(Tangent())
Else
Vector3::Zero(Tangent())
EndIf
SumXP = 0.0
SumYP = 0.0
SumZP = 0.0
Next
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Now you can call me a cheater if you wish but we know for a fact that
; the Bézier Spline is tangent at P0 with the definition polygon so we
; are using this information to compute the tangent vector at P0. It's
; the same deal at PN.
; Let's select the first Tangent
FirstElement(Tangent())
FirstElement(DefinitionPolygon())
Vector3::Copy(DefinitionPolygon(), P0.Vector3::Vector3)
NextElement(DefinitionPolygon())
Vector3::Copy(DefinitionPolygon(), P1.Vector3::Vector3)
Vector3::Minus(Tangent(), P1, P0)
Vector3::Unit(Tangent())
; Let's select the last Tangent
LastElement(Tangent())
LastElement(DefinitionPolygon())
Vector3::Copy(DefinitionPolygon(), P4.Vector3::Vector3)
PreviousElement(DefinitionPolygon())
Vector3::Copy(DefinitionPolygon(), P3.Vector3::Vector3)
Vector3::Minus(Tangent(), P4, P3)
Vector3::Unit(Tangent())
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
NewList PolygoneBezier.Vector3::Vector3()
NewList Path.Vector3::Vector3()
AddElement(PolygoneBezier())
Vector3::Update(PolygoneBezier(), 0,0,0)
AddElement(PolygoneBezier())
Vector3::Update(PolygoneBezier(), 15,9,4)
AddElement(PolygoneBezier())
Vector3::Update(PolygoneBezier(), 19,25,-20)
AddElement(PolygoneBezier())
Vector3::Update(PolygoneBezier(), 34,15,-13)
AddElement(PolygoneBezier())
Vector3::Update(PolygoneBezier(), 26,-5,2)
AddElement(PolygoneBezier())
Vector3::Update(PolygoneBezier(), 20,-10,-9)
Spline3D::BezierPath(PolygoneBezier(), 40, Path())
; Lets create csv points files to load them up in Rhinoceros 3D to see the results
If CreateFile(0, "Polygone Bezier 3D.csv")
ForEach PolygoneBezier()
WriteStringN(0, Chr(34) + StrD(Vector3::GetI(PolygoneBezier()), 6) + Chr(34) + ", " + Chr(34) + StrD(Vector3::GetJ(PolygoneBezier()), 6) + Chr(34) + ", " + Chr(34) + StrD(Vector3::GetK(PolygoneBezier()), 6) + Chr(34) )
Next
CloseFile(0)
EndIf
If CreateFile(0, "Bezier Path 3D.csv")
ForEach Path()
WriteStringN(0, Chr(34) + StrD(Vector3::GetI(Path()), 6) + Chr(34) + ", " + Chr(34) + StrD(Vector3::GetJ(Path()), 6) + Chr(34) + ", " + Chr(34) + StrD(Vector3::GetK(Path()), 6) + Chr(34) )
Next
CloseFile(0)
EndIf
CompilerEndIf
; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<