Bladeren bron

REDESIGNED: Spline drawing functionality

This change allows more versatile and consistent splines drawing. It also gives more control to advance users to draw splines as individual segments.
Ray 1 jaar geleden
bovenliggende
commit
bd3ffa7db3
3 gewijzigde bestanden met toevoegingen van 206 en 46 verwijderingen
  1. 4 0
      src/config.h
  2. 14 9
      src/raylib.h
  3. 188 37
      src/rshapes.c

+ 4 - 0
src/config.h

@@ -138,6 +138,10 @@
 // Some lines-based shapes could still use lines
 #define SUPPORT_QUADS_DRAW_MODE         1
 
+// rshapes: Configuration values
+//------------------------------------------------------------------------------------
+#define SPLINE_SEGMENT_DIVISIONS       24       // Spline segments subdivisions
+
 
 //------------------------------------------------------------------------------------
 // Module: rtextures - Configuration Flags

+ 14 - 9
src/raylib.h

@@ -1256,18 +1256,23 @@ RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation
 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 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 void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color color);              // Draw spline: Catmull-Rom, minimum 4 points
+RLAPI void DrawSplineBezierQuadratic(Vector2 *points, int pointCount, float thick, Color color);         // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
+RLAPI void DrawSplineBezierCubic(Vector2 *points, int pointCount, float thick, Color color);             // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
+RLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color);                    // Draw spline segment: Linear, 2 points
+RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points
+RLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points
+RLAPI void DrawSplineSegmentBezierQuadratic(Vector2 p1, Vector2 c2, Vector2 p3, float thick, Color color); // Draw spline segment: Quadratic Bezier, 2 points, 1 control point
+RLAPI void DrawSplineSegmentBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float thick, Color color); // Draw spline segment: Cubic Bezier, 2 points, 2 control points
+
+// Spline segment point evaluation functions, 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
+RLAPI Vector2 GetSplinePointBezierQuad(Vector2 p1, Vector2 c2, Vector2 p3, float t);                     // Get (evaluate) spline point: Quadratic Bezier
+RLAPI Vector2 GetSplinePointBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, 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

+ 188 - 37
src/rshapes.c

@@ -65,10 +65,10 @@
 // Error rate to calculate how many segments we need to draw a smooth circle,
 // taken from https://stackoverflow.com/a/2244088
 #ifndef SMOOTH_CIRCLE_ERROR_RATE
-    #define SMOOTH_CIRCLE_ERROR_RATE    0.5f    // Circle error rate
+    #define SMOOTH_CIRCLE_ERROR_RATE    0.5f      // Circle error rate
 #endif
-#ifndef SPLINE_LINE_DIVISIONS
-    #define SPLINE_LINE_DIVISIONS       24      // Spline lines segment divisions
+#ifndef SPLINE_SEGMENT_DIVISIONS
+    #define SPLINE_SEGMENT_DIVISIONS      24      // Spline segment divisions
 #endif
 
 
@@ -204,14 +204,14 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
     Vector2 previous = startPos;
     Vector2 current = { 0 };
 
-    Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+    Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
 
-    for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
+    for (int i = 1; i <= SPLINE_SEGMENT_DIVISIONS; i++)
     {
         // Cubic easing in-out
         // NOTE: Easing is calculated only for y position value
-        current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)SPLINE_LINE_DIVISIONS);
-        current.x = previous.x + (endPos.x - startPos.x)/(float)SPLINE_LINE_DIVISIONS;
+        current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)SPLINE_SEGMENT_DIVISIONS);
+        current.x = previous.x + (endPos.x - startPos.x)/(float)SPLINE_SEGMENT_DIVISIONS;
 
         float dy = current.y - previous.y;
         float dx = current.x - previous.x;
@@ -233,7 +233,7 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
         previous = current;
     }
 
-    DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
+    DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
 }
 
 // Draw a line defining thickness
@@ -1567,6 +1567,9 @@ void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color)
 
         DrawTriangleStrip(strip, 4, color);
     }
+#if defined(SUPPORT_SPLINE_SEGMENT_CAPS)
+    
+#endif
 }
 
 // Draw spline: B-Spline, minimum 4 points
@@ -1582,7 +1585,7 @@ void DrawSplineBasis(Vector2 *points, int pointCount, float thick, Color color)
 
     Vector2 currentPoint = { 0 };
     Vector2 nextPoint = { 0 };
-    Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+    Vector2 vertices[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
 
     for (int i = 0; i < (pointCount - 3); i++)
     {
@@ -1612,9 +1615,9 @@ void DrawSplineBasis(Vector2 *points, int pointCount, float thick, Color color)
             vertices[1].y = currentPoint.y + dx*size;
         }
 
-        for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++)
+        for (int j = 1; j <= SPLINE_SEGMENT_DIVISIONS; j++)
         {
-            t = ((float)j)/((float)SPLINE_LINE_DIVISIONS);
+            t = ((float)j)/((float)SPLINE_SEGMENT_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]));
@@ -1639,13 +1642,13 @@ void DrawSplineBasis(Vector2 *points, int pointCount, float thick, Color color)
             currentPoint = nextPoint;
         }
 
-        DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color);
+        DrawTriangleStrip(vertices, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
     }
 
     DrawCircleV(currentPoint, thick/2.0f, color);   // Draw end line circle-cap
 }
 
-// Draw spline: Catmull Rom, minimum 4 points
+// Draw spline: Catmull-Rom, minimum 4 points
 void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color color)
 {
     if (pointCount < 4) return;
@@ -1656,7 +1659,7 @@ void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color co
 
     Vector2 currentPoint = points[1];
     Vector2 nextPoint = { 0 };
-    Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+    Vector2 vertices[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
 
     DrawCircleV(currentPoint, thick/2.0f, color);   // Draw init line circle-cap
 
@@ -1673,9 +1676,9 @@ void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color co
             vertices[1].y = currentPoint.y + dx*size;
         }
 
-        for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++)
+        for (int j = 1; j <= SPLINE_SEGMENT_DIVISIONS; j++)
         {
-            t = ((float)j)/((float)SPLINE_LINE_DIVISIONS);
+            t = ((float)j)/((float)SPLINE_SEGMENT_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;
@@ -1705,35 +1708,183 @@ void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color co
             currentPoint = nextPoint;
         }
 
-        DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color);
+        DrawTriangleStrip(vertices, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
     }
 
     DrawCircleV(currentPoint, thick/2.0f, color);   // Draw end line circle-cap
 }
 
+// Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
+void DrawSplineBezierQuadratic(Vector2 *points, int pointCount, float thick, Color color)
+{
+    if (pointCount < 3) return;
+    
+    for (int i = 0; i < pointCount - 2; i++)
+    {
+        DrawSplineSegmentBezierQuadratic(points[i], points[i + 1], points[i + 2], thick, color);
+    }
+}
+
+// Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
+void DrawSplineBezierCubic(Vector2 *points, int pointCount, float thick, Color color)
+{
+    if (pointCount < 4) return;
+    
+    for (int i = 0; i < pointCount - 3; i++)
+    {
+        DrawSplineSegmentBezierCubic(points[i], points[i + 1], points[i + 2], points[i + 3], thick, color);
+    }
+}
 
-// Draw spline segment: quadratic-bezier, one control point
-void DrawSplineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color)
+// Draw spline segment: Linear, 2 points
+void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color)
 {
-    const float step = 1.0f/SPLINE_LINE_DIVISIONS;
+    // NOTE: For the linear spline we don't use subdivisions, just a single quad
+    
+    Vector2 delta = { p2.x - p1.x, p2.y - p1.y };
+    float length = sqrtf(delta.x*delta.x + delta.y*delta.y);
 
-    Vector2 previous = startPos;
+    if ((length > 0) && (thick > 0))
+    {
+        float scale = thick/(2*length);
+
+        Vector2 radius = { -scale*delta.y, scale*delta.x };
+        Vector2 strip[4] = {
+            { p1.x - radius.x, p1.y - radius.y },
+            { p1.x + radius.x, p1.y + radius.y },
+            { p2.x - radius.x, p2.y - radius.y },
+            { p2.x + radius.x, p2.y + radius.y }
+        };
+
+        DrawTriangleStrip(strip, 4, color);
+    }
+}
+
+// Draw spline segment: B-Spline, 4 points
+void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color)
+{
+    const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS;
+
+    Vector2 currentPoint = { 0 };
+    Vector2 nextPoint = { 0 };
+    float t = 0.0f;
+    
+    Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 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;
+
+    currentPoint.x = a[3];
+    currentPoint.y = b[3];
+
+    for (int i = 0; i <= SPLINE_SEGMENT_DIVISIONS; i++)
+    {
+        t = step*(float)i;
+
+        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]));
+
+        float dy = nextPoint.y - currentPoint.y;
+        float dx = nextPoint.x - currentPoint.x;
+        float size = (0.5f*thick)/sqrtf(dx*dx + dy*dy);
+
+        if (i == 1)
+        {
+            points[0].x = currentPoint.x + dy*size;
+            points[0].y = currentPoint.y - dx*size;
+            points[1].x = currentPoint.x - dy*size;
+            points[1].y = currentPoint.y + dx*size;
+        }
+
+        points[2*i + 1].x = nextPoint.x - dy*size;
+        points[2*i + 1].y = nextPoint.y + dx*size;
+        points[2*i].x = nextPoint.x + dy*size;
+        points[2*i].y = nextPoint.y - dx*size;
+
+        currentPoint = nextPoint;
+    }
+
+    DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS+2, color);
+}
+
+// Draw spline segment: Catmull-Rom, 4 points
+void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color)
+{
+    const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS;
+
+    Vector2 currentPoint = p1;
+    Vector2 nextPoint = { 0 };
+    float t = 0.0f;
+    
+    Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
+
+    for (int i = 0; i <= SPLINE_SEGMENT_DIVISIONS; i++)
+    {
+        t = step*(float)i;
+
+        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;
+
+        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));
+
+        float dy = nextPoint.y - currentPoint.y;
+        float dx = nextPoint.x - currentPoint.x;
+        float size = (0.5f*thick)/sqrtf(dx*dx + dy*dy);
+
+        if (i == 1)
+        {
+            points[0].x = currentPoint.x + dy*size;
+            points[0].y = currentPoint.y - dx*size;
+            points[1].x = currentPoint.x - dy*size;
+            points[1].y = currentPoint.y + dx*size;
+        }
+
+        points[2*i + 1].x = nextPoint.x - dy*size;
+        points[2*i + 1].y = nextPoint.y + dx*size;
+        points[2*i].x = nextPoint.x + dy*size;
+        points[2*i].y = nextPoint.y - dx*size;
+
+        currentPoint = nextPoint;
+    }
+
+    DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
+}
+
+// Draw spline segment: Quadratic Bezier, 2 points, 1 control point
+void DrawSplineSegmentBezierQuadratic(Vector2 p1, Vector2 c2, Vector2 p3, float thick, Color color)
+{
+    const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS;
+
+    Vector2 previous = p1;
     Vector2 current = { 0 };
     float t = 0.0f;
 
-    Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+    Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
 
-    for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
+    for (int i = 1; i <= SPLINE_SEGMENT_DIVISIONS; i++)
     {
-        t = step*i;
+        t = step*(float)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;
+        current.y = a*p1.y + b*c2.y + c*p3.y;
+        current.x = a*p1.x + b*c2.x + c*p3.x;
 
         float dy = current.y - previous.y;
         float dx = current.x - previous.x;
@@ -1755,31 +1906,31 @@ void DrawSplineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos,
         previous = current;
     }
 
-    DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
+    DrawTriangleStrip(points, 2*SPLINE_SEGMENT_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)
+// Draw spline segment: Cubic Bezier, 2 points, 2 control points
+void DrawSplineSegmentBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float thick, Color color)
 {
-    const float step = 1.0f/SPLINE_LINE_DIVISIONS;
+    const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS;
 
-    Vector2 previous = startPos;
+    Vector2 previous = p1;
     Vector2 current = { 0 };
     float t = 0.0f;
 
-    Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 };
+    Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
 
-    for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++)
+    for (int i = 1; i <= SPLINE_SEGMENT_DIVISIONS; i++)
     {
-        t = step*i;
+        t = step*(float)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;
+        current.y = a*p1.y + b*c2.y + c*c3.y + d*p4.y;
+        current.x = a*p1.x + b*c2.x + c*c3.x + d*p4.x;
 
         float dy = current.y - previous.y;
         float dx = current.x - previous.x;
@@ -1801,7 +1952,7 @@ void DrawSplineBezierCubic(Vector2 startPos, Vector2 startControlPos, Vector2 en
         previous = current;
     }
 
-    DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color);
+    DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
 }
 
 // Get spline point for a given t [0.0f .. 1.0f], Linear