Module Spline

Partagez votre expérience de PureBasic avec les autres utilisateurs.
comtois
Messages : 5172
Inscription : mer. 21/janv./2004 17:48
Contact :

Module Spline

Message par comtois »

Image

ModuleSpline.pb

Code : Tout sélectionner

DeclareModule Spline
  Declare AddPoint(Spline, x.f, y.f, z.f)
  Declare Clear(Spline)
  Declare Compute(Spline, t.f)
  Declare CountPoints(Spline)
  Declare Create()
  Declare Free(Spline)
  Declare.f PointX(Spline, index)
  Declare.f PointY(Spline, index)
  Declare.f PointZ(Spline, index)
  Declare.f X(Spline)
  Declare.f Y(Spline)
  Declare.f Z(Spline)
EndDeclareModule

Module Spline
  #Epsilon = 0.01	
  
  Structure Vector3
    x.f
    y.f
    z.f
  EndStructure
  
  Structure s_Spline
    AutoCalc.i
    Ret.Vector3
    Array Points.Vector3(0)
    Array Tangents.Vector3(0)
    Array Coeffs.f(3,3)
  EndStructure
  
  Declare recalcTangents(*This.s_Spline)
  Declare interpolate2(*This.s_Spline, fromIndex, t.f)
  
  Macro CopyVector3(a, b)
    a\x = b\x
    a\y = b\y
    a\z = b\z
  EndMacro

  Macro EqualVector3(a, b)
    a\x<=b\x+#Epsilon And a\x>=b\x-#Epsilon And a\y<=b\y+#Epsilon And a\y>=b\y-#Epsilon And a\z<=b\z+#Epsilon And a\z>=b\z-#Epsilon
  EndMacro  
  
  Procedure Create()
    Protected *Spline.s_Spline = AllocateMemory(SizeOf(s_Spline))
    If *Spline
      InitializeStructure(*Spline, s_Spline)
      
      ; Set up matrix Hermite polynomial
      With *Spline
        \Coeffs(0,0) = 2
        \Coeffs(0,1) = -2
        \Coeffs(0,2) = 1
        \Coeffs(0,3) = 1
        
        \Coeffs(1,0) = -3
        \Coeffs(1,1) = 3
        \Coeffs(1,2) = -2
        \Coeffs(1,3) = -1
        
        \Coeffs(2,0) = 0
        \Coeffs(2,1) = 0
        \Coeffs(2,2) = 1
        \Coeffs(2,3) = 0
        
        \Coeffs(3,0) = 1
        \Coeffs(3,1) = 0
        \Coeffs(3,2) = 0
        \Coeffs(3,3) = 0
        
        \AutoCalc = #True
      EndWith
      ProcedureReturn *Spline
    Else
      ProcedureReturn #False
    EndIf  
  EndProcedure
  
  Procedure Free(*This.s_Spline)
    If *This
      ClearStructure(*This, s_Spline)
      FreeMemory(*This)
    EndIf 
  EndProcedure
  
  Procedure Clear(*This.s_Spline)
    If *This
      Dim *This\Points.Vector3(0)
      Dim *This\Tangents.Vector3(0)
    EndIf
  EndProcedure
  
  Procedure concatenate(Array r.f(2), Array m.f(2), Array m2.f(2))
    r(0,0) = m(0,0) * m2(0,0) + m(0,1) * m2(1,0) + m(0,2) * m2(2,0) + m(0,3) * m2(3,0);
    r(0,1) = m(0,0) * m2(0,1) + m(0,1) * m2(1,1) + m(0,2) * m2(2,1) + m(0,3) * m2(3,1);
    r(0,2) = m(0,0) * m2(0,2) + m(0,1) * m2(1,2) + m(0,2) * m2(2,2) + m(0,3) * m2(3,2);
    r(0,3) = m(0,0) * m2(0,3) + m(0,1) * m2(1,3) + m(0,2) * m2(2,3) + m(0,3) * m2(3,3);
    
    r(1,0) = m(1,0) * m2(0,0) + m(1,1) * m2(1,0) + m(1,2) * m2(2,0) + m(1,3) * m2(3,0);
    r(1,1) = m(1,0) * m2(0,1) + m(1,1) * m2(1,1) + m(1,2) * m2(2,1) + m(1,3) * m2(3,1);
    r(1,2) = m(1,0) * m2(0,2) + m(1,1) * m2(1,2) + m(1,2) * m2(2,2) + m(1,3) * m2(3,2);
    r(1,3) = m(1,0) * m2(0,3) + m(1,1) * m2(1,3) + m(1,2) * m2(2,3) + m(1,3) * m2(3,3);
    
    r(2,0) = m(2,0) * m2(0,0) + m(2,1) * m2(1,0) + m(2,2) * m2(2,0) + m(2,3) * m2(3,0);
    r(2,1) = m(2,0) * m2(0,1) + m(2,1) * m2(1,1) + m(2,2) * m2(2,1) + m(2,3) * m2(3,1);
    r(2,2) = m(2,0) * m2(0,2) + m(2,1) * m2(1,2) + m(2,2) * m2(2,2) + m(2,3) * m2(3,2);
    r(2,3) = m(2,0) * m2(0,3) + m(2,1) * m2(1,3) + m(2,2) * m2(2,3) + m(2,3) * m2(3,3);
    
    r(3,0) = m(3,0) * m2(0,0) + m(3,1) * m2(1,0) + m(3,2) * m2(2,0) + m(3,3) * m2(3,0);
    r(3,1) = m(3,0) * m2(0,1) + m(3,1) * m2(1,1) + m(3,2) * m2(2,1) + m(3,3) * m2(3,1);
    r(3,2) = m(3,0) * m2(0,2) + m(3,1) * m2(1,2) + m(3,2) * m2(2,2) + m(3,3) * m2(3,2);
    r(3,3) = m(3,0) * m2(0,3) + m(3,1) * m2(1,3) + m(3,2) * m2(2,3) + m(3,3) * m2(3,3);
  EndProcedure
  
  Procedure Multiplication(Array r.f(1), Array v.f(1), Array m.f(2))
    r(0) = m(0,0) * v(0) + m(1,0) * v(1) + m(2,0) * v(2) + m(3,0) * v(3)
    r(1) = m(0,1) * v(0) + m(1,1) * v(1) + m(2,1) * v(2) + m(3,1) * v(3)
    r(2) = m(0,2) * v(0) + m(1,2) * v(1) + m(2,2) * v(2) + m(3,2) * v(3)
    r(3) = m(0,3) * v(0) + m(1,3) * v(1) + m(2,3) * v(2) + m(3,3) * v(3)
  EndProcedure
  
  Procedure addPoint(*This.s_Spline, x.f, y.f, z.f)
    Size = ArraySize(*This\Points())
    ReDim *This\Points(Size+1)
    *This\Points(Size)\x = x
    *This\Points(Size)\y = y
    *This\Points(Size)\z = z
    
    If (*This\AutoCalc)
      recalcTangents(*This)
    EndIf
    
  EndProcedure
  
  Procedure Compute(*This.s_Spline, t.f)
    
    ;Currently assumes points are evenly spaced, will cause velocity
    ;change where this is Not the Case
    ;TODO: base on arclength?
    
    
    ; Work out which segment this is in
    fSeg.f = t * (ArraySize(*This\Points()) - 1)
    segIdx = Int(fSeg)
    ; Apportion t
    t = fSeg - segIdx
    interpolate2(*This, segIdx, t)
    
  EndProcedure
  
  Procedure interpolate2(*This.s_Spline, fromIndex, t.f)
    
    ;// Bounds check
    ;assert (fromIndex < mPoints.size() && "fromIndex out of bounds")
    If ArraySize(*This\Points()) = 0 Or fromIndex >=ArraySize(*This\Points())
      ProcedureReturn
    EndIf
    
    If (fromIndex + 1) = ArraySize(*This\Points())
      
      ;// Duff request, cannot blend To nothing
      ;// Just Return source
      CopyVector3(*This\Ret, *This\Points(fromIndex))
      ProcedureReturn
      
    EndIf
    
    ;// Fast special cases
    If t = 0.0
      
      CopyVector3(*This\Ret, *This\Points(fromIndex))
      ProcedureReturn
      
    ElseIf t = 1.0
      CopyVector3(*This\Ret, *This\Points(fromIndex + 1))
      ProcedureReturn
    EndIf
    
    ;// Real interpolation
    ;// Form a vector of powers of t
    Define.f t2, t3
    t2 = t * t
    t3 = t2 * t
    Dim powers.f(3)
    powers(0) = t3
    powers(1) = t2
    powers(2) = t
    powers(3) = 1
    
    ;// Algorithm is ret = powers * Coeffs * Matrix4(point1, point2, tangent1, tangent2)
    Define.Vector3 point1, point2, tan1, tan2
    CopyVector3(point1, *This\Points(fromIndex))
    CopyVector3(point2, *This\Points(fromIndex+1))
    CopyVector3(tan1  , *This\Tangents(fromIndex))
    CopyVector3(tan2  , *This\Tangents(fromIndex+1))
    
    Dim pt.f(3,3)
    
    pt(0,0) = point1\x
    pt(0,1) = point1\y
    pt(0,2) = point1\z
    pt(0,3) = 1.0
    
    pt(1,0) = point2\x
    pt(1,1) = point2\y
    pt(1,2) = point2\z
    pt(1,3) = 1.0
    
    pt(2,0) = tan1\x
    pt(2,1) = tan1\y
    pt(2,2) = tan1\z
    pt(2,3) = 1.0
    
    pt(3,0) = tan2\x
    pt(3,1) = tan2\y
    pt(3,2) = tan2\z
    pt(3,3) = 1.0
    
    Dim ret.f(3)
    Dim r.f(3,3)
    ;ret = powers * Coeffs * pt;
    concatenate(r(), *This\Coeffs(), pt())
    Multiplication(ret(), powers(), r())
    
    *This\Ret\x = ret(0)   
    *This\Ret\y = ret(1)
    *This\Ret\z = ret(2)
    
  EndProcedure
  
  Procedure recalcTangents(*This.s_Spline)
    
    ;         // Catmull-Rom approach
    ;         //
    ;         // tangent[i] = 0.5 * (point[i+1] - point[i-1])
    ;         //
    ;         // Assume endpoint tangents are parallel With line With neighbour
    
    Define.i i, numPoints, isClosed
    
    numPoints = ArraySize(*This\Points())
    If numPoints < 2
      
      ; Can't do anything yet
      ProcedureReturn
    EndIf
    
    ; Closed Or open?
    
    If EqualVector3(*This\Points(0), *This\Points(numPoints-1))  
      
      isClosed = #True
      
    Else
      
      isClosed = #False
    EndIf
    
    Dim *This\Tangents.Vector3(numPoints)
    
    
    
    For i = 0 To numPoints-1
      
      If i =0
        
        ; Special Case start
        If isClosed
          ; Use numPoints-2 since numPoints-1 is the last point And == [0]
          *This\Tangents(i)\x = 0.5 * (*This\Points(1)\x - *This\Points(numPoints-2)\x)
          *This\Tangents(i)\y = 0.5 * (*This\Points(1)\y - *This\Points(numPoints-2)\y)
          *This\Tangents(i)\z = 0.5 * (*This\Points(1)\z - *This\Points(numPoints-2)\z)
        Else
          
          *This\Tangents(i)\x = 0.5 * (*This\Points(1)\x - *This\Points(0)\x)
          *This\Tangents(i)\y = 0.5 * (*This\Points(1)\y - *This\Points(0)\y)
          *This\Tangents(i)\z = 0.5 * (*This\Points(1)\z - *This\Points(0)\z)
        EndIf
        
      ElseIf i = numPoints-1
        
        ; Special Case End
        If isClosed
          
          ; Use same tangent As already calculated For [0]
          *This\Tangents(i)\x = *This\Tangents(0)\x
          *This\Tangents(i)\y = *This\Tangents(0)\y
          *This\Tangents(i)\z = *This\Tangents(0)\z
          
        Else
          
          *This\Tangents(i)\x = 0.5 * (*This\Points(i)\x - *This\Points(i-1)\x)
          *This\Tangents(i)\y = 0.5 * (*This\Points(i)\y - *This\Points(i-1)\y)
          *This\Tangents(i)\z = 0.5 * (*This\Points(i)\z - *This\Points(i-1)\z)
        EndIf
        
      Else
        *This\Tangents(i)\x = 0.5 * (*This\Points(i+1)\x - *This\Points(i-1)\x)
        *This\Tangents(i)\y = 0.5 * (*This\Points(i+1)\y - *This\Points(i-1)\y)
        *This\Tangents(i)\z = 0.5 * (*This\Points(i+1)\z - *This\Points(i-1)\z)
      EndIf
      
    Next
    
  EndProcedure
  
  Procedure CountPoints(*This.s_Spline)
    If *This
      ProcedureReturn ArraySize(*This\Points())
    EndIf  
  EndProcedure
  
  Procedure.f PointX(*This.s_Spline, index)
    If *This
      If index >= 0 And index < ArraySize(*This\Points())
        ProcedureReturn *This\Points(index)\x
      EndIf  
    EndIf  
  EndProcedure
  
  Procedure.f PointY(*This.s_Spline, index)
    If *This
      If index >= 0 And index < ArraySize(*This\Points())
        ProcedureReturn *This\Points(index)\y
      EndIf  
    EndIf  
  EndProcedure
  
  Procedure.f PointZ(*This.s_Spline, index)
    If *This
      If index >= 0 And index < ArraySize(*This\Points())
        ProcedureReturn *This\Points(index)\z
      EndIf  
    EndIf  
  EndProcedure
  
  Procedure.f X(*This.s_Spline)
    If *This
      ProcedureReturn *This\Ret\x
    EndIf  
  EndProcedure
  
  Procedure.f Y(*This.s_Spline)
    If *This
      ProcedureReturn *This\Ret\y
    EndIf  
  EndProcedure
  
  Procedure.f Z(*This.s_Spline)
    If *This
      ProcedureReturn *This\Ret\z
    EndIf  
  EndProcedure
EndModule
Et un petit exemple (Bouton gauche pour créer un chemin, et bouton droit pour effacer la spline)

Code : Tout sélectionner

XIncludeFile "ModuleSpline.pb"

InitSprite()
InitKeyboard()
InitMouse()
ExamineDesktops()
Scx = DesktopWidth(0)
Scy = DesktopHeight(0)
OpenWindow(0,0,0,Scx, Scy, "Test Spline", #PB_Window_BorderLess)
OpenWindowedScreen(WindowID(0), 0,0,Scx,Scy)

MySpline = Spline::Create()

xd.f = 0
yd.f = 0
pas.f = 1
time.f = 0

CreateSprite(0, 64,64)
StartDrawing(SpriteOutput(0))
Line(0,16,1,32, RGB(255,255,0))
LineXY(0,16,64,32, RGB(255,255,0))
LineXY(0,64-16,64,32, RGB(255,255,0))
FillArea(32,32,RGB(255,255,0),RGB(85,85,0))
StopDrawing()

Repeat
  While WindowEvent()
  Wend
  
  ClearScreen(0)
  ExamineMouse()
  If MouseButton(1)
    If flag = 0
      Flag = 1
      MouseX.f = MouseX()
      MouseY.f = MouseY()
      Spline::AddPoint(MySpline, MouseX, 0, MouseY)
      If xd = 0 And yd = 0
        xd = MouseX
        yd = MouseY
      EndIf
    EndIf
  Else
    flag = 0
  EndIf
  If MouseButton(2)
    Spline::Clear(MySpline)
  EndIf
  StartDrawing(ScreenOutput())
  For t=0 To 300
    
    Spline::Compute(MySpline, t/300.0)
    x.f = Spline::X(MySpline)
    z.f = Spline::Z(MySpline)
    If t > 0
      LineXY(xd, yd, x, z, $AABBCC)
    EndIf
    xd = x
    yd = z   
  Next t
  
  For i=0 To Spline::CountPoints(MySpline)-1
    Circle(Spline::PointX(MySpline, i), Spline::PointZ(MySpline, i), 2, $FF)
  Next   
  
  LineXY(0, MouseY(), Scx, MouseY(), $AAAAAA)
  LineXY(MouseX(), 0, MouseX(), Scy, $AAAAAA)
  StopDrawing()
  
  ;-Sprite
  Spline::Compute(MySpline, time)
  x.f = Spline::X(MySpline)
  z.f = Spline::Z(MySpline)
  Spline::Compute(MySpline, time-0.001)
  x1.f = Spline::X(MySpline)
  z1.f = Spline::Z(MySpline)
  DisplayTransparentSprite(0,x-32, z-32)  
  
  DirectionX.f = x-x1
  DirectionZ.f = z-z1 
  Angle.f = Degree(ATan2(DirectionX, DirectionZ))
  
  RotateSprite(0,Angle,#PB_Absolute)
  time + pas * 0.02 / Spline::CountPoints(MySpline)
  
  If time > 1
    Time = 1
    pas = - pas
  ElseIf time < 0
    Time = 0
    pas = - pas
  EndIf
  
  ExamineKeyboard()
  FlipBuffers()
  
Until KeyboardPushed(#PB_Key_Escape)

Spline::Free(MySpline)
[EDIT]
La correction est faite
Dernière modification par comtois le mer. 03/juin/2015 17:44, modifié 2 fois.
http://purebasic.developpez.com/
Je ne réponds à aucune question technique en PV, utilisez le forum, il est fait pour ça, et la réponse peut profiter à tous.
G-Rom
Messages : 3626
Inscription : dim. 10/janv./2010 5:29

Re: Module Spline

Message par G-Rom »

Sympa le code, deviens "anguleux" avec un grand nombre de points ;)
Avatar de l’utilisateur
Huitbit
Messages : 939
Inscription : jeu. 08/déc./2005 5:19
Localisation : Guadeloupe

Re: Module Spline

Message par Huitbit »

Hello,

Ca marche impeccable, il ne reste plus qu'à normer la courbe
pour avoir la maîtrise de l'accélération 8) .

Well done !


Hasta la vista !
Elevé au MSX !
Avatar de l’utilisateur
falsam
Messages : 7244
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Module Spline

Message par falsam »

Huitbit a écrit :l ne reste plus qu'à normer la courbe pour avoir la maîtrise de l'accélération
Moi je ralenti dans les virages pour éviter l'accident :mrgreen:

J'ai essayé et adopté. Merci Comtois.
Configuration : Windows 11 Famille 64-bit - PB 6.03 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
majikeyric
Messages : 602
Inscription : dim. 08/déc./2013 23:19
Contact :

Re: Module Spline

Message par majikeyric »

Merci Comtois!
comtois
Messages : 5172
Inscription : mer. 21/janv./2004 17:48
Contact :

Re: Module Spline

Message par comtois »

G-Rom a écrit :Sympa le code, deviens "anguleux" avec un grand nombre de points ;)
Ta remarque m'avait tracassé, je n'avais pas pris le temps d'analyser, ce soir c'est fait.
C'est lié aux nombres de points calculés dans la boucle d'affichage des lignes (nombre de points intermédiaires)

Une solution possible pour corriger le phénomène :

Code : Tout sélectionner

  n = Spline::CountPoints(MySpline)*10
  For t=0 To n
    
    Spline::Compute(MySpline, t/n)
    x.f = Spline::X(MySpline)
    z.f = Spline::Z(MySpline)
    If t > 0
      LineXY(xd, yd, x, z, $AABBCC)
    EndIf
    xd = x
    yd = z   
  Next t
le déplacement du sprite est calculé autrement , il ne doit pas être concerné.

Autre sujet, j'ai constaté un bogue

Code : Tout sélectionner

  Procedure recalcTangents(*This.s_Spline)
   
    ; Closed Or open?
   
    If *This\Points(0) = *This\Points(numPoints-1)
ce test If *This\Points(0) = *This\Points(numPoints-1) n'est pas correct, pour savoir si on reboucle il faudrait faire (en principe) :

Code : Tout sélectionner

If *This\Points(0)\x = *This\Points(numPoints-1)\x And *This\Points(0)\y = *This\Points(numPoints-1)\y And *This\Points(0)\z = *This\Points(numPoints-1)\z  
Mais en réalité, comme x,y et z sont des flottants, faudrait faire un encadrement en fonction d'une valeur epsilon !

[EDIT]
La correction est faite, il est possible de reboucler convenablement un circuit
http://purebasic.developpez.com/
Je ne réponds à aucune question technique en PV, utilisez le forum, il est fait pour ça, et la réponse peut profiter à tous.
comtois
Messages : 5172
Inscription : mer. 21/janv./2004 17:48
Contact :

Re: Module Spline

Message par comtois »

Huitbit a écrit :Ca marche impeccable, il ne reste plus qu'à normer la courbe
pour avoir la maîtrise de l'accélération 8) .
Je pensais plutôt faire ça
mais ça n'a pas l'air de fonctionner correctement, ou alors j'ai un bogue dans ma fonction ? pas vraiment pris le temps de vérifier, si ça intéresse quelqu'un , voici le nouveau module avec cette fonction getEqualDistanceSpline() :

Code : Tout sélectionner

DeclareModule Spline
  Structure Vector3
    x.f
    y.f
    z.f
  EndStructure
  Declare AddPoint(Spline, x.f, y.f, z.f)
  Declare Clear(Spline)
  Declare Compute(Spline, t.f)
  Declare CountPoints(Spline)
  Declare Create()
  Declare Free(Spline)
  Declare.f PointX(Spline, index)
  Declare.f PointY(Spline, index)
  Declare.f PointZ(Spline, index)
  Declare GetPoint(Spline, index, *point.vector3)
  Declare getEqualDistanceSpline(SplineSrc,  SplineDest,  wantedDistance.f)
  Declare.f X(Spline)
  Declare.f Y(Spline)
  Declare.f Z(Spline)
EndDeclareModule

Module Spline
 	#Epsilon = 0.01	
  Structure s_Spline
    AutoCalc.i
    Ret.Vector3
    Array Points.Vector3(0)
    Array Tangents.Vector3(0)
    Array Coeffs.f(3,3)
  EndStructure
  
  Declare recalcTangents(*This.s_Spline)
  Declare interpolate2(*This.s_Spline, fromIndex, t.f)
  
  Macro CopyVector3(a, b)
    a\x = b\x
    a\y = b\y
    a\z = b\z
  EndMacro
  Macro EqualVector3(a, b)
    a\x<=b\x+#Epsilon And a\x>=b\x-#Epsilon And a\y<=b\y+#Epsilon And a\y>=b\y-#Epsilon And a\z<=b\z+#Epsilon And a\z>=b\z-#Epsilon
  EndMacro  
  
  Procedure Create()
    Protected *Spline.s_Spline = AllocateMemory(SizeOf(s_Spline))
    If *Spline
      InitializeStructure(*Spline, s_Spline)
      
      ; Set up matrix Hermite polynomial
      With *Spline
        \Coeffs(0,0) = 2
        \Coeffs(0,1) = -2
        \Coeffs(0,2) = 1
        \Coeffs(0,3) = 1
        
        \Coeffs(1,0) = -3
        \Coeffs(1,1) = 3
        \Coeffs(1,2) = -2
        \Coeffs(1,3) = -1
        
        \Coeffs(2,0) = 0
        \Coeffs(2,1) = 0
        \Coeffs(2,2) = 1
        \Coeffs(2,3) = 0
        
        \Coeffs(3,0) = 1
        \Coeffs(3,1) = 0
        \Coeffs(3,2) = 0
        \Coeffs(3,3) = 0
        
        \AutoCalc = #True
      EndWith
      ProcedureReturn *Spline
    Else
      ProcedureReturn #False
    EndIf  
  EndProcedure
  
  Procedure Free(*This.s_Spline)
    If *This
      ClearStructure(*This, s_Spline)
      FreeMemory(*This)
    EndIf 
  EndProcedure
  
  Procedure Clear(*This.s_Spline)
    If *This
      Dim *This\Points.Vector3(0)
      Dim *This\Tangents.Vector3(0)
    EndIf
  EndProcedure
  
  Procedure concatenate(Array r.f(2), Array m.f(2), Array m2.f(2))
    r(0,0) = m(0,0) * m2(0,0) + m(0,1) * m2(1,0) + m(0,2) * m2(2,0) + m(0,3) * m2(3,0);
    r(0,1) = m(0,0) * m2(0,1) + m(0,1) * m2(1,1) + m(0,2) * m2(2,1) + m(0,3) * m2(3,1);
    r(0,2) = m(0,0) * m2(0,2) + m(0,1) * m2(1,2) + m(0,2) * m2(2,2) + m(0,3) * m2(3,2);
    r(0,3) = m(0,0) * m2(0,3) + m(0,1) * m2(1,3) + m(0,2) * m2(2,3) + m(0,3) * m2(3,3);
    
    r(1,0) = m(1,0) * m2(0,0) + m(1,1) * m2(1,0) + m(1,2) * m2(2,0) + m(1,3) * m2(3,0);
    r(1,1) = m(1,0) * m2(0,1) + m(1,1) * m2(1,1) + m(1,2) * m2(2,1) + m(1,3) * m2(3,1);
    r(1,2) = m(1,0) * m2(0,2) + m(1,1) * m2(1,2) + m(1,2) * m2(2,2) + m(1,3) * m2(3,2);
    r(1,3) = m(1,0) * m2(0,3) + m(1,1) * m2(1,3) + m(1,2) * m2(2,3) + m(1,3) * m2(3,3);
    
    r(2,0) = m(2,0) * m2(0,0) + m(2,1) * m2(1,0) + m(2,2) * m2(2,0) + m(2,3) * m2(3,0);
    r(2,1) = m(2,0) * m2(0,1) + m(2,1) * m2(1,1) + m(2,2) * m2(2,1) + m(2,3) * m2(3,1);
    r(2,2) = m(2,0) * m2(0,2) + m(2,1) * m2(1,2) + m(2,2) * m2(2,2) + m(2,3) * m2(3,2);
    r(2,3) = m(2,0) * m2(0,3) + m(2,1) * m2(1,3) + m(2,2) * m2(2,3) + m(2,3) * m2(3,3);
    
    r(3,0) = m(3,0) * m2(0,0) + m(3,1) * m2(1,0) + m(3,2) * m2(2,0) + m(3,3) * m2(3,0);
    r(3,1) = m(3,0) * m2(0,1) + m(3,1) * m2(1,1) + m(3,2) * m2(2,1) + m(3,3) * m2(3,1);
    r(3,2) = m(3,0) * m2(0,2) + m(3,1) * m2(1,2) + m(3,2) * m2(2,2) + m(3,3) * m2(3,2);
    r(3,3) = m(3,0) * m2(0,3) + m(3,1) * m2(1,3) + m(3,2) * m2(2,3) + m(3,3) * m2(3,3);
  EndProcedure
  
  Procedure Multiplication(Array r.f(1), Array v.f(1), Array m.f(2))
    r(0) = m(0,0) * v(0) + m(1,0) * v(1) + m(2,0) * v(2) + m(3,0) * v(3)
    r(1) = m(0,1) * v(0) + m(1,1) * v(1) + m(2,1) * v(2) + m(3,1) * v(3)
    r(2) = m(0,2) * v(0) + m(1,2) * v(1) + m(2,2) * v(2) + m(3,2) * v(3)
    r(3) = m(0,3) * v(0) + m(1,3) * v(1) + m(2,3) * v(2) + m(3,3) * v(3)
  EndProcedure
  
  Procedure addPoint(*This.s_Spline, x.f, y.f, z.f)
    Size = ArraySize(*This\Points())
    ReDim *This\Points(Size+1)
    *This\Points(Size)\x = x
    *This\Points(Size)\y = y
    *This\Points(Size)\z = z
    
    If (*This\AutoCalc)
      recalcTangents(*This)
    EndIf
    
  EndProcedure
  
  Procedure Compute(*This.s_Spline, t.f)
    
    ;Currently assumes points are evenly spaced, will cause velocity
    ;change where this is Not the Case
    ;TODO: base on arclength?
    
    
    ; Work out which segment this is in
    fSeg.f = t * (ArraySize(*This\Points()) - 1)
    segIdx = Int(fSeg)
    ; Apportion t
    t = fSeg - segIdx
    interpolate2(*This, segIdx, t)
    
  EndProcedure
  
  Procedure interpolate2(*This.s_Spline, fromIndex, t.f)
    
    ;// Bounds check
    ;assert (fromIndex < mPoints.size() && "fromIndex out of bounds")
    If ArraySize(*This\Points()) = 0 Or fromIndex >=ArraySize(*This\Points())
      ProcedureReturn
    EndIf
    
    If (fromIndex + 1) = ArraySize(*This\Points())
      
      ;// Duff request, cannot blend To nothing
      ;// Just Return source
      CopyVector3(*This\Ret, *This\Points(fromIndex))
      ProcedureReturn
      
    EndIf
    
    ;// Fast special cases
    If t = 0.0
      
      CopyVector3(*This\Ret, *This\Points(fromIndex))
      ProcedureReturn
      
    ElseIf t = 1.0
      CopyVector3(*This\Ret, *This\Points(fromIndex + 1))
      ProcedureReturn
    EndIf
    
    ;// Real interpolation
    ;// Form a vector of powers of t
    Define.f t2, t3
    t2 = t * t
    t3 = t2 * t
    Dim powers.f(3)
    powers(0) = t3
    powers(1) = t2
    powers(2) = t
    powers(3) = 1
    
    ;// Algorithm is ret = powers * Coeffs * Matrix4(point1, point2, tangent1, tangent2)
    Define.Vector3 point1, point2, tan1, tan2
    CopyVector3(point1, *This\Points(fromIndex))
    CopyVector3(point2, *This\Points(fromIndex+1))
    CopyVector3(tan1  , *This\Tangents(fromIndex))
    CopyVector3(tan2  , *This\Tangents(fromIndex+1))
    
    Dim pt.f(3,3)
    
    pt(0,0) = point1\x
    pt(0,1) = point1\y
    pt(0,2) = point1\z
    pt(0,3) = 1.0
    
    pt(1,0) = point2\x
    pt(1,1) = point2\y
    pt(1,2) = point2\z
    pt(1,3) = 1.0
    
    pt(2,0) = tan1\x
    pt(2,1) = tan1\y
    pt(2,2) = tan1\z
    pt(2,3) = 1.0
    
    pt(3,0) = tan2\x
    pt(3,1) = tan2\y
    pt(3,2) = tan2\z
    pt(3,3) = 1.0
    
    Dim ret.f(3)
    Dim r.f(3,3)
    ;ret = powers * Coeffs * pt;
    concatenate(r(), *This\Coeffs(), pt())
    Multiplication(ret(), powers(), r())
    
    *This\Ret\x = ret(0)   
    *This\Ret\y = ret(1)
    *This\Ret\z = ret(2)
    
  EndProcedure
  
  Procedure recalcTangents(*This.s_Spline)
    
    ;         // Catmull-Rom approach
    ;         //
    ;         // tangent[i] = 0.5 * (point[i+1] - point[i-1])
    ;         //
    ;         // Assume endpoint tangents are parallel With line With neighbour
    
    Define.i i, numPoints, isClosed
    
    numPoints = ArraySize(*This\Points())
    If numPoints < 2
      
      ; Can't do anything yet
      ProcedureReturn
    EndIf
    
    ; Closed Or open?
    
    If EqualVector3(*This\Points(0), *This\Points(numPoints-1))  
      
    	isClosed = #True
   
    Else
      
      isClosed = #False
    EndIf
    
    Dim *This\Tangents.Vector3(numPoints)
    
    
    
    For i = 0 To numPoints-1
      
      If i =0
        
        ; Special Case start
        If isClosed
          ; Use numPoints-2 since numPoints-1 is the last point And == [0]
          *This\Tangents(i)\x = 0.5 * (*This\Points(1)\x - *This\Points(numPoints-2)\x)
          *This\Tangents(i)\y = 0.5 * (*This\Points(1)\y - *This\Points(numPoints-2)\y)
          *This\Tangents(i)\z = 0.5 * (*This\Points(1)\z - *This\Points(numPoints-2)\z)
        Else
          
          *This\Tangents(i)\x = 0.5 * (*This\Points(1)\x - *This\Points(0)\x)
          *This\Tangents(i)\y = 0.5 * (*This\Points(1)\y - *This\Points(0)\y)
          *This\Tangents(i)\z = 0.5 * (*This\Points(1)\z - *This\Points(0)\z)
        EndIf
        
      ElseIf i = numPoints-1
        
        ; Special Case End
        If isClosed
          
          ; Use same tangent As already calculated For [0]
          *This\Tangents(i)\x = *This\Tangents(0)\x
          *This\Tangents(i)\y = *This\Tangents(0)\y
          *This\Tangents(i)\z = *This\Tangents(0)\z
          
        Else
          
          *This\Tangents(i)\x = 0.5 * (*This\Points(i)\x - *This\Points(i-1)\x)
          *This\Tangents(i)\y = 0.5 * (*This\Points(i)\y - *This\Points(i-1)\y)
          *This\Tangents(i)\z = 0.5 * (*This\Points(i)\z - *This\Points(i-1)\z)
        EndIf
        
      Else
        *This\Tangents(i)\x = 0.5 * (*This\Points(i+1)\x - *This\Points(i-1)\x)
        *This\Tangents(i)\y = 0.5 * (*This\Points(i+1)\y - *This\Points(i-1)\y)
        *This\Tangents(i)\z = 0.5 * (*This\Points(i+1)\z - *This\Points(i-1)\z)
      EndIf
      
    Next
    
  EndProcedure
  
  Procedure CountPoints(*This.s_Spline)
    If *This
      ProcedureReturn ArraySize(*This\Points())
    EndIf  
  EndProcedure
  
  Procedure.f PointX(*This.s_Spline, index)
    If *This
      If index >= 0 And index < ArraySize(*This\Points())
        ProcedureReturn *This\Points(index)\x
      EndIf  
    EndIf  
  EndProcedure
  
  Procedure.f PointY(*This.s_Spline, index)
    If *This
      If index >= 0 And index < ArraySize(*This\Points())
        ProcedureReturn *This\Points(index)\y
      EndIf  
    EndIf  
  EndProcedure
  
  Procedure.f PointZ(*This.s_Spline, index)
    If *This
      If index >= 0 And index < ArraySize(*This\Points())
        ProcedureReturn *This\Points(index)\z
      EndIf  
    EndIf  
  EndProcedure
  
  Procedure GetPoint(*This.s_Spline, index, *point.vector3)
    If *This
      If index >= 0 And index < ArraySize(*This\Points())
        *point\x = *This\Points(index)\x
        *point\y = *This\Points(index)\y
        *point\z = *This\Points(index)\z
        
        ProcedureReturn #True
      EndIf  
    EndIf  
  EndProcedure
  
  Procedure.f X(*This.s_Spline)
    If *This
      ProcedureReturn *This\Ret\x
    EndIf  
  EndProcedure
  
  Procedure.f Y(*This.s_Spline)
    If *This
      ProcedureReturn *This\Ret\y
    EndIf  
  EndProcedure
  
  Procedure.f Z(*This.s_Spline)
    If *This
      ProcedureReturn *This\Ret\z
    EndIf  
  EndProcedure
  
  Procedure.f squaredLength(*V.Vector3)
    ProcedureReturn (*V\x * *V\x + *V\y * *V\y)
  EndProcedure
  
  Procedure.f Length(*V.Vector3)
    ProcedureReturn Sqr(*V\x * *V\x + *V\y * *V\y)
  EndProcedure
  
  Procedure setAutoCalculate(*This.s_Spline, val)
    If *This
       *This\AutoCalc = val
    EndIf      
  EndProcedure
  
  ;http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Equal+Length+Spline
  Procedure getEqualDistanceSpline(*splineSrc.s_Spline,  *splineDest.s_Spline,  wantedDistance.f)
    lastInterpPoint.f = 0.0;
    length.f
    start.Vector3 
    GetPoint(*splineSrc, 0, @start)
    End_.Vector3
    wantedDistanceSquared.f = wantedDistance*wantedDistance
 
    setAutoCalculate(*splineDest, #False)
    
    addPoint(*splineDest, start\x, start\y, start\z)

    For j = 1 To CountPoints(*splineSrc)

        ; first find the points where the length exceed wanted length..
      GetPoint(*splineSrc, j, @End_)
      
      inter.Vector3
      inter\x=End_\x - start\x
      inter\y=End_\y - start\y
      inter\z=End_\z - start\z
      
       length = squaredLength(@inter);

       While (length < wantedDistanceSquared And j < Countpoints(*splineSrc)-1) 
            j+1
            getPoint(*splineSrc, j, @End_)
            inter\x=End_\x - start\x
            inter\y=End_\y - start\y
            inter\z=End_\z - start\z
          
           length = squaredLength(@inter);
   
            ; If enter the loops then we have To reset lastInterPoint..
            lastInterpPoint = 0.0;
        Wend
 
        If j = CountPoints(*splineSrc) -1
            Break
        EndIf
        ; okay found it.. lets refine
        partStart.f = lastInterpPoint
        partEnd.f = 1.0
        partMid.f
        partPoint.Vector3
        partLen.f
             
        interpolate2(*splineSrc, j-1, lastInterpPoint)
        refPoint.Vector3
        CopyVector3(refPoint, *splineSrc\Ret)
               
        inter.Vector3
        inter\x=start\x - refPoint\x
        inter\y=start\y - refPoint\y
        inter\z=start\z - refPoint\z
            
      
        squaredDist.f = wantedDistance-Length(@inter)
        squaredDist * squaredDist
 
        Repeat
            partMid = (partStart+partEnd)/2;
            interpolate2(*splineSrc,j-1, partMid)
            CopyVector3(partPoint, *splineSrc\Ret)
            
            inter.Vector3
            inter\x=partPoint\x - refPoint\x
            inter\y=partPoint\y - refPoint\y
            inter\z=partPoint\z - refPoint\z
            partLen = squaredLength(@inter)
            If (Abs(partLen-squaredDist)< 1 Or Abs(partStart-partEnd) < 1e-5)
              Break;
             EndIf 
            If (partLen > squaredDist)
                partEnd = partMid
            Else
              partStart = partMid
            EndIf  
        Until #True
 
        ; once we reach here.. the exact point has been discovered..
        interpolate2(*splineSrc,j-1, partMid)
        CopyVector3(start, *splineSrc\Ret)
         ;Log("\tstart = " + StringConverter::toString(start) + ", lastInterpPoint = " + StringConverter::toString(partMid));
        ; And remember the last interpolation point
        lastInterpPoint = partMid;
 
        addPoint(*splineDest,start\x,start\y,start\z)
    Next
    recalcTangents(*splineDest)
EndProcedure
  
EndModule
http://purebasic.developpez.com/
Je ne réponds à aucune question technique en PV, utilisez le forum, il est fait pour ça, et la réponse peut profiter à tous.
Répondre