Browse Source

ADDED: `GetSplinePoint*()` functions for spline evaluation

RENAMED: `DrawLine<spline_type>()` to `DrawSpline<spline_type>()` for more consistent and clear naming
REVIEWED: Bezier drawing parameters order, more consistent
REVIEWED: Spline-based examples -WIP-
Ray 1 year ago
parent
commit
f01d3db739
5 changed files with 566 additions and 395 deletions
  1. 3 3
      CHANGELOG
  2. 5 3
      examples/shapes/shapes_lines_bezier.c
  3. 80 34
      examples/shapes/shapes_splines_drawing.c
  4. 18 8
      src/raylib.h
  5. 460 347
      src/rshapes.c

+ 3 - 3
CHANGELOG

@@ -52,7 +52,7 @@ Detailed changes:
 [rlgl] REVIEWED: rLoadTexture() UBSAN complaints #1891 (#3321) by @Codom
 [rlgl] REVIEWED: glInternalFormat as unsigned int
 [rshapes] ADDED: Spline drawing functions by @raysan5
-[rshapes] REVIEWED: DrawLineCatmullRom() by @raysan5
+[rshapes] REVIEWED: DrawSplineCatmullRom() by @raysan5
 [rshapes] REVIEWED: Minor fix in DrawLineBezier* (#3006) by @eternalStudent
 [rshapes] REVIEWED: GetCollisionRec(), more performant (#3052) by @manuel5975p
 [rshapes] REVIEWED: Fix off-by-one error in CheckCollisionPointRec() (#3022) by @dbechrd
@@ -651,7 +651,7 @@ Detailed changes:
 [raymath] REVIEWED: QuaternionFromAxisAngle() (#1892)
 [raymath] REVIEWED: QuaternionToMatrix() returning transposed result. (#1793) by @object71
 [shapes] ADDED: RenderPolyLinesEx() (#1758) by @lambertwang
-[shapes] ADDED: DrawLineBezierCubic() (#2021) by @SAOMDVN
+[shapes] ADDED: DrawSplineBezierCubic() (#2021) by @SAOMDVN
 [textures] ADDED: GetImageColor() #2024
 [textures] REMOVED: GenImagePerlinNoise()
 [textures] RENAMED: GetTextureData() to LoadImageFromTexture()
@@ -789,7 +789,7 @@ Detailed changes:
 [rlgl] REDESIGNED: rlLoadExtensions(), more details exposed
 [raymath] REVIEWED: QuaternionFromEuler() (#1651)
 [raymath] REVIEWED: MatrixRotateZYX() (#1642)
-[shapes] ADDED: DrawLineBezierQuad() (#1468) by @epsilon-phase
+[shapes] ADDED: DrawSplineBezierQuad() (#1468) by @epsilon-phase
 [shapes] ADDED: CheckCollisionLines()
 [shapes] ADDED: CheckCollisionPointLine() by @mkupiec1
 [shapes] REVIEWED: CheckCollisionPointTriangle() by @mkupiec1

+ 5 - 3
examples/shapes/shapes_lines_bezier.c

@@ -30,7 +30,7 @@ int main(void)
     Vector2 end = { (float)screenWidth, (float)screenHeight };
     
     Vector2 startControl = { 100, 0 };
-    Vector2 endControl = { (float)GetScreenWidth() - 100, (float)GetScreenHeight() };
+    Vector2 endControl = { GetScreenWidth() - 100, GetScreenHeight() };
 
     SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
@@ -60,9 +60,11 @@ int main(void)
 
             DrawText("USE MOUSE LEFT-RIGHT CLICK to DEFINE LINE START and END POINTS", 15, 20, 20, GRAY);
 
-            //DrawLineBezier(start, end, 2.0f, RED);
+            // Draw line cubic-bezier, in-out interpolation (easing), no control points
+            DrawLineBezier(start, end, 3.0f, BLUE);
             
-            DrawLineBezierCubic(start, end, startControl, endControl, 2.0f, RED);
+            // Draw spline cubic-bezier with control points
+            DrawSplineBezierCubic(start, startControl, endControl, end, 2.0f, RED);
             
             DrawLineEx(start, startControl, 1.0, LIGHTGRAY);
             DrawLineEx(end, endControl, 1.0, LIGHTGRAY);

+ 80 - 34
examples/shapes/shapes_lines_splines.c → examples/shapes/shapes_splines_drawing.c

@@ -13,13 +13,25 @@
 
 #include "raylib.h"
 
-#define MAX_CONTROL_POINTS      32
+#include <stdlib.h>     // Required for: NULL
 
+#define MAX_SPLINE_POINTS      32
+
+// Bezier spline control points
+// NOTE: Every segment has two control points 
 typedef struct {
     Vector2 start;
     Vector2 end;
 } ControlPoint;
 
+// Spline types
+typedef enum {
+    SPLINE_LINEAR = 0,
+    SPLINE_BASIS,       // B-Spline
+    SPLINE_CATMULLROM,
+    SPLINE_BEZIER
+} SplineType;
+
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
@@ -33,7 +45,7 @@ int main(void)
     SetConfigFlags(FLAG_MSAA_4X_HINT);
     InitWindow(screenWidth, screenHeight, "raylib [shapes] example - splines drawing");
 
-    Vector2 points[MAX_CONTROL_POINTS] = {
+    Vector2 points[MAX_SPLINE_POINTS] = {
         { 100.0f, 200.0f },
         { 300.0f, 400.0f },
         { 500.0f, 300.0f },
@@ -43,15 +55,18 @@ int main(void)
     
     int pointCount = 5;
     int selectedPoint = -1;
+    int focusedPoint = -1;
+    Vector2 *selectedControlPoint = NULL;
+    Vector2 *focusedControlPoint = NULL;
     
-    int splineType = 0;             // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier
+    int splineType = SPLINE_LINEAR; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier
     
-    // Cubic Bezier control points
-    ControlPoint control[MAX_CONTROL_POINTS] = { 0 };
+    // Cubic Bezier control points initialization
+    ControlPoint control[MAX_SPLINE_POINTS] = { 0 };
     for (int i = 0; i < pointCount - 1; i++)
     {
-        control[i].start = points[i];
-        control[i].end = points[i + 1];
+        control[i].start = (Vector2){ points[i].x - 20, points[i].y - 20 };
+        control[i].end = (Vector2){ points[i + 1].x + 20, points[i + 1].y + 20 };
     }
     
     SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
@@ -62,30 +77,60 @@ int main(void)
     {
         // Update
         //----------------------------------------------------------------------------------
-        // Points movement logic
-        if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON) && (pointCount < MAX_CONTROL_POINTS))
+        // Spline points creation logic (at the end of spline)
+        if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON) && (pointCount < MAX_SPLINE_POINTS))
         {
             points[pointCount] = GetMousePosition();
             pointCount++;
         }
 
+        // Spline point focus and selection logic
         for (int i = 0; i < pointCount; i++)
         {
-            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && CheckCollisionPointCircle(GetMousePosition(), points[i], 6.0f))
+            if (CheckCollisionPointCircle(GetMousePosition(), points[i], 8.0f))
             {
-                selectedPoint = i;
+                focusedPoint = i;
+                if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedPoint = i; 
                 break;
             }
+            else focusedPoint = -1;
         }
-
+        
+        // Spline point movement logic
         if (selectedPoint >= 0)
         {
             points[selectedPoint] = GetMousePosition();
             if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedPoint = -1;
         }
         
-        // TODO: Cubic Bezier spline control points logic
-        
+        // Cubic Bezier spline control points logic
+        if ((splineType == SPLINE_BEZIER) && (focusedPoint == -1))
+        {
+            // Spline control point focus and selection logic
+            for (int i = 0; i < pointCount; i++)
+            {
+                if (CheckCollisionPointCircle(GetMousePosition(), control[i].start, 6.0f))
+                {
+                    focusedControlPoint = &control[i].start;
+                    if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedControlPoint = &control[i].start; 
+                    break;
+                }
+                else if (CheckCollisionPointCircle(GetMousePosition(), control[i].end, 6.0f))
+                {
+                    focusedControlPoint = &control[i].end;
+                    if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedControlPoint = &control[i].end; 
+                    break;
+                }
+                else focusedControlPoint = NULL;
+            }
+            
+            // Spline control point movement logic
+            if (selectedControlPoint != NULL)
+            {
+                *selectedControlPoint = GetMousePosition();
+                if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedControlPoint = NULL;
+            }
+        }
         
         // Spline selection logic
         if (IsKeyPressed(KEY_ONE)) splineType = 0;
@@ -100,47 +145,48 @@ int main(void)
 
             ClearBackground(RAYWHITE);
         
-            if (splineType == 0)        // Linear
+            if (splineType == SPLINE_LINEAR)
             {
-                // Draw linear spline
-                for (int i = 0; i < pointCount - 1; i++)
-                {
-                    DrawLineEx(points[i], points[i + 1], 2.0f, RED);
-                }
+                // Draw spline: linear
+                DrawSplineLinear(points, pointCount, 2.0f, RED);
             }
-            else if (splineType == 1)   // B-Spline
+            else if (splineType == SPLINE_BASIS)
             {
-                // Draw b-spline
-                DrawLineBSpline(points, pointCount, 2.0f, RED);
-                //for (int i = 0; i < (pointCount - 3); i++) DrawLineBSplineSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, BLUE);
+                // Draw spline: basis
+                DrawSplineBasis(points, pointCount, 2.0f, RED);
+                //for (int i = 0; i < (pointCount - 3); i++) DrawSplineBasisSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, BLUE);
             }
-            else if (splineType == 2)   // CatmullRom Spline
+            else if (splineType == SPLINE_CATMULLROM)
             {
                 // Draw spline: catmull-rom
-                DrawLineCatmullRom(points, pointCount, 2.0f, RED);
-                //for (int i = 0; i < (pointCount - 3); i++) DrawLineCatmullRomSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, Fade(BLUE, 0.4f));
+                DrawSplineCatmullRom(points, pointCount, 2.0f, RED);
+                //for (int i = 0; i < (pointCount - 3); i++) DrawSplineCatmullRomSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, Fade(BLUE, 0.4f));
             }
-            else if (splineType == 3)   // Cubic Bezier
+            else if (splineType == SPLINE_BEZIER)
             {
-                // Draw line bezier cubic (with control points)
+                // Draw spline: cubic-bezier (with control points)
                 for (int i = 0; i < pointCount - 1; i++)
                 {
-                    DrawLineBezierCubic(points[i], points[i + 1], control[i].start, control[i + 1].end, 2.0f, RED);
+                    DrawSplineBezierCubic(points[i], control[i].start, control[i].end, points[i + 1], 2.0f, RED);
 
-                    // TODO: Every cubic bezier point should have two control points
+                    // Every cubic bezier point should have two control points
                     DrawCircleV(control[i].start, 4, GOLD);
                     DrawCircleV(control[i].end, 4, GOLD);
+                    if (focusedControlPoint == &control[i].start) DrawCircleV(control[i].start, 6, GREEN);
+                    else if (focusedControlPoint == &control[i].end) DrawCircleV(control[i].end, 6, GREEN);
                     DrawLineEx(points[i], control[i].start, 1.0, LIGHTGRAY);
                     DrawLineEx(points[i + 1], control[i].end, 1.0, LIGHTGRAY);
                 }
             }
 
-            // Draw control points
+            // Draw spline key-points
             for (int i = 0; i < pointCount; i++)
             {
-                DrawCircleV(points[i], 6.0f, RED);
-                if ((splineType != 0) && (i < pointCount - 1)) DrawLineV(points[i], points[i + 1], GRAY);
+                DrawCircleV(points[i], (focusedPoint == i)? 8.0f : 5.0f, (focusedPoint == i)? BLUE: RED);
+                if ((splineType != 0) && (i < pointCount - 1)) DrawLineV(points[i], points[i + 1], LIGHTGRAY);
             }
+            
+            // TODO: Draw help
 
         EndDrawing();
         //----------------------------------------------------------------------------------

+ 18 - 8
src/raylib.h

@@ -1221,14 +1221,10 @@ RLAPI void SetShapesTexture(Texture2D texture, Rectangle source);       // Set t
 RLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel
 RLAPI void DrawPixelV(Vector2 position, Color color);                                                    // Draw a pixel (Vector version)
 RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color);                // Draw a line
-RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color);                                     // Draw a line (Vector version)
-RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color);                       // Draw a line defining thickness
-RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color);                   // Draw a line using cubic-bezier curves in-out
-RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point
-RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points
-RLAPI void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color);                   // Draw a B-Spline line, minimum 4 points
-RLAPI void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color);                // Draw a Catmull Rom spline line, minimum 4 points
-RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color);                                  // Draw lines sequence
+RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color);                                     // Draw a line (using gl lines)
+RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color);                       // Draw a line (using triangles/quads)
+RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color);                                  // Draw lines sequence (using gl lines)
+RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color);                   // Draw line segment cubic-bezier in-out interpolation
 RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color);                              // Draw a color-filled circle
 RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color);      // Draw a piece of a circle
 RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline
@@ -1259,6 +1255,20 @@ RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Col
 RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color);          // Draw a polygon outline of n sides
 RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters
 
+// Splines drawing functions
+RLAPI void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color);                  // Draw spline: linear, minimum 2 points
+RLAPI void DrawSplineBasis(Vector2 *points, int pointCount, float thick, Color color);                   // Draw spline: B-Spline, minimum 4 points
+RLAPI void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color color);              // Draw spline: Catmull Rom, minimum 4 points
+RLAPI void DrawSplineBezierQuad(Vector2 startPos, Vector2 controlPos, Vector2 endPos, float thick, Color color); // Draw spline segment: quadratic-bezier, one control point
+RLAPI void DrawSplineBezierCubic(Vector2 startPos, Vector2 startControlPos, Vector2 endControlPos, Vector2 endPos, float thick, Color color); // Draw spline segment: cubic-bezier, two control points
+
+// Get (evaluate) spline point for a given t [0.0f .. 1.0f]
+RLAPI Vector2 GetSplinePointLinear(Vector2 startPos, Vector2 endPos, float t);                           // Get (evaluate) spline point: linear
+RLAPI Vector2 GetSplinePointBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t);              // Get (evaluate) spline point: B-Spline
+RLAPI Vector2 GetSplinePointCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t);         // Get (evaluate) spline point: Catmull-Rom
+RLAPI Vector2 GetSplinePointBezierQuad(Vector2 startPos, Vector2 controlPos, Vector2 endPos, float t);   // Get (evaluate) spline point: quadratic-bezier
+RLAPI Vector2 GetSplinePointBezierCubic(Vector2 startPos, Vector2 startControlPos, Vector2 endControlPos, Vector2 endPos, float t); // Get (evaluate) spline point: cubic-bezier
+
 // Basic shapes collision detection functions
 RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);                                           // Check collision between two rectangles
 RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2);        // Check collision between two circles

+ 460 - 347
src/rshapes.c

@@ -161,7 +161,7 @@ void DrawPixelV(Vector2 position, Color color)
 #endif
 }
 
-// Draw a line
+// Draw a line (using gl lines)
 void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color)
 {
     rlBegin(RL_LINES);
@@ -171,7 +171,7 @@ void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color colo
     rlEnd();
 }
 
-// Draw a line  (Vector version)
+// Draw a line (using gl lines)
 void DrawLineV(Vector2 startPos, Vector2 endPos, Color color)
 {
     rlBegin(RL_LINES);
@@ -181,29 +181,24 @@ void DrawLineV(Vector2 startPos, Vector2 endPos, Color color)
     rlEnd();
 }
 
-// Draw a line defining thickness
-void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
+// Draw lines sequuence (using gl lines)
+void DrawLineStrip(Vector2 *points, int pointCount, Color color)
 {
-    Vector2 delta = { endPos.x - startPos.x, endPos.y - startPos.y };
-    float length = sqrtf(delta.x*delta.x + delta.y*delta.y);
-
-    if ((length > 0) && (thick > 0))
+    if (pointCount >= 2)
     {
-        float scale = thick/(2*length);
-
-        Vector2 radius = { -scale*delta.y, scale*delta.x };
-        Vector2 strip[4] = {
-            { startPos.x - radius.x, startPos.y - radius.y },
-            { startPos.x + radius.x, startPos.y + radius.y },
-            { endPos.x - radius.x, endPos.y - radius.y },
-            { endPos.x + radius.x, endPos.y + radius.y }
-        };
+        rlBegin(RL_LINES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
 
-        DrawTriangleStrip(strip, 4, color);
+            for (int i = 0; i < pointCount - 1; i++)
+            {
+                rlVertex2f(points[i].x, points[i].y);
+                rlVertex2f(points[i + 1].x, points[i + 1].y);
+            }
+        rlEnd();
     }
 }
 
-// Draw line using cubic-bezier curves in-out
+// Draw line using cubic-bezier spline, in-out interpolation, no control points
 void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
 {
     Vector2 previous = startPos;
@@ -241,375 +236,146 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
     DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
 }
 
-// Draw line using quadratic bezier curves with a control point
-void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color)
+// Draw a line defining thickness
+void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
 {
-    const float step = 1.0f/SPLINE_LINE_DIVISIONS;
-
-    Vector2 previous = startPos;
-    Vector2 current = { 0 };
-    float t = 0.0f;
-
-    Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+    Vector2 delta = { endPos.x - startPos.x, endPos.y - startPos.y };
+    float length = sqrtf(delta.x*delta.x + delta.y*delta.y);
 
-    for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
+    if ((length > 0) && (thick > 0))
     {
-        t = step*i;
-
-        float a = powf(1.0f - t, 2);
-        float b = 2.0f*(1.0f - t)*t;
-        float c = powf(t, 2);
-
-        // NOTE: The easing functions aren't suitable here because they don't take a control point
-        current.y = a*startPos.y + b*controlPos.y + c*endPos.y;
-        current.x = a*startPos.x + b*controlPos.x + c*endPos.x;
-
-        float dy = current.y - previous.y;
-        float dx = current.x - previous.x;
-        float size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
-
-        if (i == 1)
-        {
-            points[0].x = previous.x + dy*size;
-            points[0].y = previous.y - dx*size;
-            points[1].x = previous.x - dy*size;
-            points[1].y = previous.y + dx*size;
-        }
+        float scale = thick/(2*length);
 
-        points[2*i + 1].x = current.x - dy*size;
-        points[2*i + 1].y = current.y + dx*size;
-        points[2*i].x = current.x + dy*size;
-        points[2*i].y = current.y - dx*size;
+        Vector2 radius = { -scale*delta.y, scale*delta.x };
+        Vector2 strip[4] = {
+            { startPos.x - radius.x, startPos.y - radius.y },
+            { startPos.x + radius.x, startPos.y + radius.y },
+            { endPos.x - radius.x, endPos.y - radius.y },
+            { endPos.x + radius.x, endPos.y + radius.y }
+        };
 
-        previous = current;
+        DrawTriangleStrip(strip, 4, color);
     }
-
-    DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
 }
 
-// Draw line using cubic bezier curves with 2 control points
-void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color)
+// Draw a color-filled circle
+void DrawCircle(int centerX, int centerY, float radius, Color color)
 {
-    const float step = 1.0f/SPLINE_LINE_DIVISIONS;
+    DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color);
+}
 
-    Vector2 previous = startPos;
-    Vector2 current = { 0 };
-    float t = 0.0f;
+// Draw a color-filled circle (Vector version)
+// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues
+void DrawCircleV(Vector2 center, float radius, Color color)
+{
+    DrawCircleSector(center, radius, 0, 360, 36, color);
+}
 
-    Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+// Draw a piece of a circle
+void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
+{
+    if (radius <= 0.0f) radius = 0.1f;  // Avoid div by zero
 
-    for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
+    // Function expects (endAngle > startAngle)
+    if (endAngle < startAngle)
     {
-        t = step*i;
-
-        float a = powf(1.0f - t, 3);
-        float b = 3.0f*powf(1.0f - t, 2)*t;
-        float c = 3.0f*(1.0f - t)*powf(t, 2);
-        float d = powf(t, 3);
-
-        current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y;
-        current.x = a*startPos.x + b*startControlPos.x + c*endControlPos.x + d*endPos.x;
-
-        float dy = current.y - previous.y;
-        float dx = current.x - previous.x;
-        float size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
+        // Swap values
+        float tmp = startAngle;
+        startAngle = endAngle;
+        endAngle = tmp;
+    }
 
-        if (i == 1)
-        {
-            points[0].x = previous.x + dy*size;
-            points[0].y = previous.y - dx*size;
-            points[1].x = previous.x - dy*size;
-            points[1].y = previous.y + dx*size;
-        }
+    int minSegments = (int)ceilf((endAngle - startAngle)/90);
 
-        points[2*i + 1].x = current.x - dy*size;
-        points[2*i + 1].y = current.y + dx*size;
-        points[2*i].x = current.x + dy*size;
-        points[2*i].y = current.y - dx*size;
+    if (segments < minSegments)
+    {
+        // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
+        float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1);
+        segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360);
 
-        previous = current;
+        if (segments <= 0) segments = minSegments;
     }
 
-    DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
-}
-
-// Draw a B-Spline line, minimum 4 points
-void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color)
-{
-    if (pointCount < 4) return;
+    float stepLength = (endAngle - startAngle)/(float)segments;
+    float angle = startAngle;
 
-    float a[4] = { 0 };
-    float b[4] = { 0 };
-    float dy = 0.0f;
-    float dx = 0.0f;
-    float size = 0.0f;
+#if defined(SUPPORT_QUADS_DRAW_MODE)
+    rlSetTexture(texShapes.id);
 
-    Vector2 currentPoint = { 0 };
-    Vector2 nextPoint = { 0 };
-    Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+    rlBegin(RL_QUADS);
 
-    for (int i = 0; i < (pointCount - 3); i++)
-    {
-        float t = 0.0f;
-        Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3];
+        // NOTE: Every QUAD actually represents two segments
+        for (int i = 0; i < segments/2; i++)
+        {
+            rlColor4ub(color.r, color.g, color.b, color.a);
 
-        a[0] = (-p1.x + 3.0f*p2.x - 3.0f*p3.x + p4.x)/6.0f;
-        a[1] = (3.0f*p1.x - 6.0f*p2.x + 3.0f*p3.x)/6.0f;
-        a[2] = (-3.0f*p1.x + 3.0f*p3.x)/6.0f;
-        a[3] = (p1.x + 4.0f*p2.x + p3.x)/6.0f;
+            rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
+            rlVertex2f(center.x, center.y);
 
-        b[0] = (-p1.y + 3.0f*p2.y - 3.0f*p3.y + p4.y)/6.0f;
-        b[1] = (3.0f*p1.y - 6.0f*p2.y + 3.0f*p3.y)/6.0f;
-        b[2] = (-3.0f*p1.y + 3.0f*p3.y)/6.0f;
-        b[3] = (p1.y + 4.0f*p2.y + p3.y)/6.0f;
+            rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
+            rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius);
 
-        currentPoint.x = a[3];
-        currentPoint.y = b[3];
+            rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
+            rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
 
-        if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color);   // Draw init line circle-cap
+            rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
+            rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
 
-        if (i > 0)
-        {
-            vertices[0].x = currentPoint.x + dy*size;
-            vertices[0].y = currentPoint.y - dx*size;
-            vertices[1].x = currentPoint.x - dy*size;
-            vertices[1].y = currentPoint.y + dx*size;
+            angle += (stepLength*2.0f);
         }
 
-        for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++)
+        // NOTE: In case number of segments is odd, we add one last piece to the cake
+        if ((segments%2) == 1)
         {
-            t = ((float)j)/((float)SPLINE_LINE_DIVISIONS);
-
-            nextPoint.x = a[3] + t*(a[2] + t*(a[1] + t*a[0]));
-            nextPoint.y = b[3] + t*(b[2] + t*(b[1] + t*b[0]));
+            rlColor4ub(color.r, color.g, color.b, color.a);
 
-            dy = nextPoint.y - currentPoint.y;
-            dx = nextPoint.x - currentPoint.x;
-            size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
+            rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
+            rlVertex2f(center.x, center.y);
 
-            if ((i == 0) && (j == 1))
-            {
-                vertices[0].x = currentPoint.x + dy*size;
-                vertices[0].y = currentPoint.y - dx*size;
-                vertices[1].x = currentPoint.x - dy*size;
-                vertices[1].y = currentPoint.y + dx*size;
-            }
+            rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
+            rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
 
-            vertices[2*j + 1].x = nextPoint.x - dy*size;
-            vertices[2*j + 1].y = nextPoint.y + dx*size;
-            vertices[2*j].x = nextPoint.x + dy*size;
-            vertices[2*j].y = nextPoint.y - dx*size;
+            rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
+            rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
 
-            currentPoint = nextPoint;
+            rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
+            rlVertex2f(center.x, center.y);
         }
 
-        DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color);
-    }
+    rlEnd();
 
-    DrawCircleV(currentPoint, thick/2.0f, color);   // Draw end line circle-cap
+    rlSetTexture(0);
+#else
+    rlBegin(RL_TRIANGLES);
+        for (int i = 0; i < segments; i++)
+        {
+            rlColor4ub(color.r, color.g, color.b, color.a);
+
+            rlVertex2f(center.x, center.y);
+            rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
+            rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
+
+            angle += stepLength;
+        }
+    rlEnd();
+#endif
 }
 
-// Draw a Catmull Rom spline line, minimum 4 points
-void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color)
+// Draw a piece of a circle outlines
+void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
 {
-    if (pointCount < 4) return;
+    if (radius <= 0.0f) radius = 0.1f;  // Avoid div by zero issue
 
-    float dy = 0.0f;
-    float dx = 0.0f;
-    float size = 0.0f;
+    // Function expects (endAngle > startAngle)
+    if (endAngle < startAngle)
+    {
+        // Swap values
+        float tmp = startAngle;
+        startAngle = endAngle;
+        endAngle = tmp;
+    }
 
-    Vector2 currentPoint = points[1];
-    Vector2 nextPoint = { 0 };
-    Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
-
-    DrawCircleV(currentPoint, thick/2.0f, color);   // Draw init line circle-cap
-
-    for (int i = 0; i < (pointCount - 3); i++)
-    {
-        float t = 0.0f;
-        Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3];
-
-        if (i > 0)
-        {
-            vertices[0].x = currentPoint.x + dy*size;
-            vertices[0].y = currentPoint.y - dx*size;
-            vertices[1].x = currentPoint.x - dy*size;
-            vertices[1].y = currentPoint.y + dx*size;
-        }
-
-        for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++)
-        {
-            t = ((float)j)/((float)SPLINE_LINE_DIVISIONS);
-
-            float q0 = (-1.0f*t*t*t) + (2.0f*t*t) + (-1.0f*t);
-            float q1 = (3.0f*t*t*t) + (-5.0f*t*t) + 2.0f;
-            float q2 = (-3.0f*t*t*t) + (4.0f*t*t) + t;
-            float q3 = t*t*t - t*t;
-
-            nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3));
-            nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3));
-
-            dy = nextPoint.y - currentPoint.y;
-            dx = nextPoint.x - currentPoint.x;
-            size = (0.5f*thick)/sqrtf(dx*dx + dy*dy);
-
-            if ((i == 0) && (j == 1))
-            {
-                vertices[0].x = currentPoint.x + dy*size;
-                vertices[0].y = currentPoint.y - dx*size;
-                vertices[1].x = currentPoint.x - dy*size;
-                vertices[1].y = currentPoint.y + dx*size;
-            }
-
-            vertices[2*j + 1].x = nextPoint.x - dy*size;
-            vertices[2*j + 1].y = nextPoint.y + dx*size;
-            vertices[2*j].x = nextPoint.x + dy*size;
-            vertices[2*j].y = nextPoint.y - dx*size;
-
-            currentPoint = nextPoint;
-        }
-
-        DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color);
-    }
-
-    DrawCircleV(currentPoint, thick/2.0f, color);   // Draw end line circle-cap
-}
-
-// Draw lines sequence
-void DrawLineStrip(Vector2 *points, int pointCount, Color color)
-{
-    if (pointCount >= 2)
-    {
-        rlBegin(RL_LINES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            for (int i = 0; i < pointCount - 1; i++)
-            {
-                rlVertex2f(points[i].x, points[i].y);
-                rlVertex2f(points[i + 1].x, points[i + 1].y);
-            }
-        rlEnd();
-    }
-}
-
-// Draw a color-filled circle
-void DrawCircle(int centerX, int centerY, float radius, Color color)
-{
-    DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color);
-}
-
-// Draw a color-filled circle (Vector version)
-// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues
-void DrawCircleV(Vector2 center, float radius, Color color)
-{
-    DrawCircleSector(center, radius, 0, 360, 36, color);
-}
-
-// Draw a piece of a circle
-void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
-{
-    if (radius <= 0.0f) radius = 0.1f;  // Avoid div by zero
-
-    // Function expects (endAngle > startAngle)
-    if (endAngle < startAngle)
-    {
-        // Swap values
-        float tmp = startAngle;
-        startAngle = endAngle;
-        endAngle = tmp;
-    }
-
-    int minSegments = (int)ceilf((endAngle - startAngle)/90);
-
-    if (segments < minSegments)
-    {
-        // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
-        float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1);
-        segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360);
-
-        if (segments <= 0) segments = minSegments;
-    }
-
-    float stepLength = (endAngle - startAngle)/(float)segments;
-    float angle = startAngle;
-
-#if defined(SUPPORT_QUADS_DRAW_MODE)
-    rlSetTexture(texShapes.id);
-
-    rlBegin(RL_QUADS);
-
-        // NOTE: Every QUAD actually represents two segments
-        for (int i = 0; i < segments/2; i++)
-        {
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
-            rlVertex2f(center.x, center.y);
-
-            rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
-            rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius);
-
-            rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
-            rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
-
-            rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
-            rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
-
-            angle += (stepLength*2.0f);
-        }
-
-        // NOTE: In case number of segments is odd, we add one last piece to the cake
-        if ((segments%2) == 1)
-        {
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height);
-            rlVertex2f(center.x, center.y);
-
-            rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
-            rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
-
-            rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height);
-            rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
-
-            rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height);
-            rlVertex2f(center.x, center.y);
-        }
-
-    rlEnd();
-
-    rlSetTexture(0);
-#else
-    rlBegin(RL_TRIANGLES);
-        for (int i = 0; i < segments; i++)
-        {
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            rlVertex2f(center.x, center.y);
-            rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
-            rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
-
-            angle += stepLength;
-        }
-    rlEnd();
-#endif
-}
-
-// Draw a piece of a circle outlines
-void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
-{
-    if (radius <= 0.0f) radius = 0.1f;  // Avoid div by zero issue
-
-    // Function expects (endAngle > startAngle)
-    if (endAngle < startAngle)
-    {
-        // Swap values
-        float tmp = startAngle;
-        startAngle = endAngle;
-        endAngle = tmp;
-    }
-
-    int minSegments = (int)ceilf((endAngle - startAngle)/90);
+    int minSegments = (int)ceilf((endAngle - startAngle)/90);
 
     if (segments < minSegments)
     {
@@ -1773,6 +1539,353 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl
 #endif
 }
 
+//----------------------------------------------------------------------------------
+// Module Functions Definition - Splines functions
+//----------------------------------------------------------------------------------
+
+// Draw spline: linear, minimum 2 points
+void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color)
+{
+    Vector2 delta = { 0 };
+    float length = 0.0f;
+    float scale = 0.0f;
+    
+    for (int i = 0; i < pointCount - 1; i++)
+    {
+        delta = (Vector2){ points[i + 1].x - points[i].x, points[i + 1].y - points[i].y };
+        length = sqrtf(delta.x*delta.x + delta.y*delta.y);
+
+        if (length > 0) scale = thick/(2*length);
+
+        Vector2 radius = { -scale*delta.y, scale*delta.x };
+        Vector2 strip[4] = {
+            { points[i].x - radius.x, points[i].y - radius.y },
+            { points[i].x + radius.x, points[i].y + radius.y },
+            { points[i + 1].x - radius.x, points[i + 1].y - radius.y },
+            { points[i + 1].x + radius.x, points[i + 1].y + radius.y }
+        };
+
+        DrawTriangleStrip(strip, 4, color);
+    }
+}
+
+// Draw spline: B-Spline, minimum 4 points
+void DrawSplineBasis(Vector2 *points, int pointCount, float thick, Color color)
+{
+    if (pointCount < 4) return;
+
+    float a[4] = { 0 };
+    float b[4] = { 0 };
+    float dy = 0.0f;
+    float dx = 0.0f;
+    float size = 0.0f;
+
+    Vector2 currentPoint = { 0 };
+    Vector2 nextPoint = { 0 };
+    Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+
+    for (int i = 0; i < (pointCount - 3); i++)
+    {
+        float t = 0.0f;
+        Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3];
+
+        a[0] = (-p1.x + 3.0f*p2.x - 3.0f*p3.x + p4.x)/6.0f;
+        a[1] = (3.0f*p1.x - 6.0f*p2.x + 3.0f*p3.x)/6.0f;
+        a[2] = (-3.0f*p1.x + 3.0f*p3.x)/6.0f;
+        a[3] = (p1.x + 4.0f*p2.x + p3.x)/6.0f;
+
+        b[0] = (-p1.y + 3.0f*p2.y - 3.0f*p3.y + p4.y)/6.0f;
+        b[1] = (3.0f*p1.y - 6.0f*p2.y + 3.0f*p3.y)/6.0f;
+        b[2] = (-3.0f*p1.y + 3.0f*p3.y)/6.0f;
+        b[3] = (p1.y + 4.0f*p2.y + p3.y)/6.0f;
+
+        currentPoint.x = a[3];
+        currentPoint.y = b[3];
+
+        if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color);   // Draw init line circle-cap
+
+        if (i > 0)
+        {
+            vertices[0].x = currentPoint.x + dy*size;
+            vertices[0].y = currentPoint.y - dx*size;
+            vertices[1].x = currentPoint.x - dy*size;
+            vertices[1].y = currentPoint.y + dx*size;
+        }
+
+        for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++)
+        {
+            t = ((float)j)/((float)SPLINE_LINE_DIVISIONS);
+
+            nextPoint.x = a[3] + t*(a[2] + t*(a[1] + t*a[0]));
+            nextPoint.y = b[3] + t*(b[2] + t*(b[1] + t*b[0]));
+
+            dy = nextPoint.y - currentPoint.y;
+            dx = nextPoint.x - currentPoint.x;
+            size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
+
+            if ((i == 0) && (j == 1))
+            {
+                vertices[0].x = currentPoint.x + dy*size;
+                vertices[0].y = currentPoint.y - dx*size;
+                vertices[1].x = currentPoint.x - dy*size;
+                vertices[1].y = currentPoint.y + dx*size;
+            }
+
+            vertices[2*j + 1].x = nextPoint.x - dy*size;
+            vertices[2*j + 1].y = nextPoint.y + dx*size;
+            vertices[2*j].x = nextPoint.x + dy*size;
+            vertices[2*j].y = nextPoint.y - dx*size;
+
+            currentPoint = nextPoint;
+        }
+
+        DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color);
+    }
+
+    DrawCircleV(currentPoint, thick/2.0f, color);   // Draw end line circle-cap
+}
+
+// Draw spline: Catmull Rom, minimum 4 points
+void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color color)
+{
+    if (pointCount < 4) return;
+
+    float dy = 0.0f;
+    float dx = 0.0f;
+    float size = 0.0f;
+
+    Vector2 currentPoint = points[1];
+    Vector2 nextPoint = { 0 };
+    Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+
+    DrawCircleV(currentPoint, thick/2.0f, color);   // Draw init line circle-cap
+
+    for (int i = 0; i < (pointCount - 3); i++)
+    {
+        float t = 0.0f;
+        Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3];
+
+        if (i > 0)
+        {
+            vertices[0].x = currentPoint.x + dy*size;
+            vertices[0].y = currentPoint.y - dx*size;
+            vertices[1].x = currentPoint.x - dy*size;
+            vertices[1].y = currentPoint.y + dx*size;
+        }
+
+        for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++)
+        {
+            t = ((float)j)/((float)SPLINE_LINE_DIVISIONS);
+
+            float q0 = (-1.0f*t*t*t) + (2.0f*t*t) + (-1.0f*t);
+            float q1 = (3.0f*t*t*t) + (-5.0f*t*t) + 2.0f;
+            float q2 = (-3.0f*t*t*t) + (4.0f*t*t) + t;
+            float q3 = t*t*t - t*t;
+
+            nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3));
+            nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3));
+
+            dy = nextPoint.y - currentPoint.y;
+            dx = nextPoint.x - currentPoint.x;
+            size = (0.5f*thick)/sqrtf(dx*dx + dy*dy);
+
+            if ((i == 0) && (j == 1))
+            {
+                vertices[0].x = currentPoint.x + dy*size;
+                vertices[0].y = currentPoint.y - dx*size;
+                vertices[1].x = currentPoint.x - dy*size;
+                vertices[1].y = currentPoint.y + dx*size;
+            }
+
+            vertices[2*j + 1].x = nextPoint.x - dy*size;
+            vertices[2*j + 1].y = nextPoint.y + dx*size;
+            vertices[2*j].x = nextPoint.x + dy*size;
+            vertices[2*j].y = nextPoint.y - dx*size;
+
+            currentPoint = nextPoint;
+        }
+
+        DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color);
+    }
+
+    DrawCircleV(currentPoint, thick/2.0f, color);   // Draw end line circle-cap
+}
+
+
+// Draw spline segment: quadratic-bezier, one control point
+void DrawSplineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color)
+{
+    const float step = 1.0f/SPLINE_LINE_DIVISIONS;
+
+    Vector2 previous = startPos;
+    Vector2 current = { 0 };
+    float t = 0.0f;
+
+    Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+
+    for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
+    {
+        t = step*i;
+
+        float a = powf(1.0f - t, 2);
+        float b = 2.0f*(1.0f - t)*t;
+        float c = powf(t, 2);
+
+        // NOTE: The easing functions aren't suitable here because they don't take a control point
+        current.y = a*startPos.y + b*controlPos.y + c*endPos.y;
+        current.x = a*startPos.x + b*controlPos.x + c*endPos.x;
+
+        float dy = current.y - previous.y;
+        float dx = current.x - previous.x;
+        float size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
+
+        if (i == 1)
+        {
+            points[0].x = previous.x + dy*size;
+            points[0].y = previous.y - dx*size;
+            points[1].x = previous.x - dy*size;
+            points[1].y = previous.y + dx*size;
+        }
+
+        points[2*i + 1].x = current.x - dy*size;
+        points[2*i + 1].y = current.y + dx*size;
+        points[2*i].x = current.x + dy*size;
+        points[2*i].y = current.y - dx*size;
+
+        previous = current;
+    }
+
+    DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
+}
+
+// Draw spline segment: cubic-bezier, two control point
+void DrawSplineBezierCubic(Vector2 startPos, Vector2 startControlPos, Vector2 endControlPos, Vector2 endPos, float thick, Color color)
+{
+    const float step = 1.0f/SPLINE_LINE_DIVISIONS;
+
+    Vector2 previous = startPos;
+    Vector2 current = { 0 };
+    float t = 0.0f;
+
+    Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+
+    for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
+    {
+        t = step*i;
+
+        float a = powf(1.0f - t, 3);
+        float b = 3.0f*powf(1.0f - t, 2)*t;
+        float c = 3.0f*(1.0f - t)*powf(t, 2);
+        float d = powf(t, 3);
+
+        current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y;
+        current.x = a*startPos.x + b*startControlPos.x + c*endControlPos.x + d*endPos.x;
+
+        float dy = current.y - previous.y;
+        float dx = current.x - previous.x;
+        float size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
+
+        if (i == 1)
+        {
+            points[0].x = previous.x + dy*size;
+            points[0].y = previous.y - dx*size;
+            points[1].x = previous.x - dy*size;
+            points[1].y = previous.y + dx*size;
+        }
+
+        points[2*i + 1].x = current.x - dy*size;
+        points[2*i + 1].y = current.y + dx*size;
+        points[2*i].x = current.x + dy*size;
+        points[2*i].y = current.y - dx*size;
+
+        previous = current;
+    }
+
+    DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
+}
+
+// Get spline point for a given t [0.0f .. 1.0f], Linear
+Vector2 GetSplinePointLinear(Vector2 startPos, Vector2 endPos, float t)
+{
+    Vector2 point = { 0 };
+
+    point.x = startPos.x*(1.0f - t) + endPos.x*t;
+    point.y = startPos.y*(1.0f - t) + endPos.y*t;
+
+    return point;
+}
+
+// Get spline point for a given t [0.0f .. 1.0f], B-Spline
+Vector2 GetSplinePointBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t)
+{
+    Vector2 point = { 0 };
+
+    float a[4] = { 0 };
+    float b[4] = { 0 };
+
+    a[0] = (-p1.x + 3*p2.x - 3*p3.x + p4.x)/6.0f;
+    a[1] = (3*p1.x - 6*p2.x + 3*p3.x)/6.0f;
+    a[2] = (-3*p1.x + 3*p3.x)/6.0f;
+    a[3] = (p1.x + 4*p2.x + p3.x)/6.0f;
+
+    b[0] = (-p1.y + 3*p2.y - 3*p3.y + p4.y)/6.0f;
+    b[1] = (3*p1.y - 6*p2.y + 3*p3.y)/6.0f;
+    b[2] = (-3*p1.y + 3*p3.y)/6.0f;
+    b[3] = (p1.y + 4*p2.y + p3.y)/6.0f;
+
+    point.x = a[3] + t*(a[2] + t*(a[1] + t*a[0]));
+    point.y = b[3] + t*(b[2] + t*(b[1] + t*b[0]));
+
+    return point;
+}
+
+// Get spline point for a given t [0.0f .. 1.0f], Catmull-Rom
+Vector2 GetSplinePointCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t)
+{
+    Vector2 point = { 0 };
+
+    float q0 = (-1*t*t*t) + (2*t*t) + (-1*t);
+    float q1 = (3*t*t*t) + (-5*t*t) + 2;
+    float q2 = (-3*t*t*t) + (4*t*t) + t;
+    float q3 = t*t*t - t*t;
+
+    point.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3));
+    point.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3));
+
+    return point;
+}
+
+// Get spline point for a given t [0.0f .. 1.0f], Quadratic Bezier
+Vector2 GetSplinePointBezierQuad(Vector2 startPos, Vector2 controlPos, Vector2 endPos, float t)
+{
+    Vector2 point = { 0 };
+
+    float a = powf(1.0f - t, 2);
+    float b = 2.0f*(1.0f - t)*t;
+    float c = powf(t, 2);
+
+    point.y = a*startPos.y + b*controlPos.y + c*endPos.y;
+    point.x = a*startPos.x + b*controlPos.x + c*endPos.x;
+
+    return point;
+}
+
+// Get spline point for a given t [0.0f .. 1.0f], Cubic Bezier
+Vector2 GetSplinePointBezierCubic(Vector2 startPos, Vector2 startControlPos, Vector2 endControlPos, Vector2 endPos, float t)
+{
+    Vector2 point = { 0 };
+
+    float a = powf(1.0f - t, 3);
+    float b = 3.0f*powf(1.0f - t, 2)*t;
+    float c = 3.0f*(1.0f - t)*powf(t, 2);
+    float d = powf(t, 3);
+
+    point.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y;
+    point.x = a*startPos.x + b*startControlPos.x + c*endControlPos.x + d*endPos.x;
+
+    return point;
+}
+
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Collision Detection functions
 //----------------------------------------------------------------------------------