diff --git a/examples/shapes/shapes_splines_drawing.c b/examples/shapes/shapes_splines_drawing.c index 4ba63a18f..605011a78 100644 --- a/examples/shapes/shapes_splines_drawing.c +++ b/examples/shapes/shapes_splines_drawing.c @@ -35,6 +35,7 @@ typedef enum { SPLINE_BASIS, // B-Spline SPLINE_CATMULLROM, // Catmull-Rom SPLINE_BEZIER, // Cubic Bezier + SPLINE_LINEAR_VAR, // Linear, variable thickness SPLINE_BEZIER_VAR // Cubic Bezier, variable thickness } SplineType; @@ -79,7 +80,8 @@ int main(void) // Spline config variables float splineThickness = 8.0f; - int splineTypeActive = SPLINE_BEZIER_VAR; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier, 4-BezierVar + int splineTypeActive = SPLINE_LINEAR_VAR; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier, 4-LinearVar, 5-BezierVar + // TODO: Change the default back to SPLINE_LINEAR when finished testing bool splineTypeEditMode = false; bool splineHelpersActive = true; @@ -155,6 +157,7 @@ int main(void) else if (IsKeyPressed(KEY_THREE)) splineTypeActive = 2; else if (IsKeyPressed(KEY_FOUR)) splineTypeActive = 3; else if (IsKeyPressed(KEY_FIVE)) splineTypeActive = 4; + else if (IsKeyPressed(KEY_SIX)) splineTypeActive = 5; //---------------------------------------------------------------------------------- // Draw @@ -218,6 +221,21 @@ int main(void) } */ } + else if (splineTypeActive == SPLINE_LINEAR_VAR) + { + float thicks[] = { + 0.0f, + splineThickness, + splineThickness, + 0.0f, + }; + + // Draw spline: variable-width linear + for (int i = 0; i < pointCount - 1; ++i) + { + DrawSplineSegmentLinearVar(points[i], points[i+1], thicks, 4, RED); + } + } else if (splineTypeActive == SPLINE_BEZIER_VAR) { float thicks[] = { @@ -238,7 +256,7 @@ int main(void) pointsInterleaved[3*(pointCount - 1)] = points[pointCount - 1]; - // Draw spline: cubic-bezier (with control points) + // Draw spline: variable-width cubic-bezier (with control points) for (int i = 0; i < pointCount - 1; ++i) { DrawSplineSegmentBezierCubicVar(points[i], control[i].start, control[i].end, points[i+1], thicks, 4, RED); @@ -272,6 +290,7 @@ int main(void) { DrawCircleLinesV(points[i], (focusedPoint == i)? 12.0f : 8.0f, (focusedPoint == i)? BLUE: DARKBLUE); if ((splineTypeActive != SPLINE_LINEAR) && + (splineTypeActive != SPLINE_LINEAR_VAR) && (splineTypeActive != SPLINE_BEZIER) && (splineTypeActive != SPLINE_BEZIER_VAR) && (i < pointCount - 1)) DrawLineV(points[i], points[i + 1], GRAY); @@ -292,7 +311,7 @@ int main(void) GuiUnlock(); GuiLabel((Rectangle){ 12, 10, 140, 24 }, "Spline type:"); - if (GuiDropdownBox((Rectangle){ 12, 8 + 24, 140, 28 }, "LINEAR;BSPLINE;CATMULLROM;BEZIER;BEZIER VARIABLE", &splineTypeActive, splineTypeEditMode)) splineTypeEditMode = !splineTypeEditMode; + if (GuiDropdownBox((Rectangle){ 12, 8 + 24, 140, 28 }, "LINEAR;BSPLINE;CATMULLROM;BEZIER;LINEAR VARIABLE;BEZIER VARIABLE", &splineTypeActive, splineTypeEditMode)) splineTypeEditMode = !splineTypeEditMode; EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index 16677041b..1e9e8da55 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1298,6 +1298,7 @@ RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4 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 +RLAPI void DrawSplineSegmentLinearVar(Vector2 p1, Vector2 p2, const float* thicks, int thickCount, Color color); // Draw spline segment with variable thickness: Linear Bezier, 2 points RLAPI void DrawSplineSegmentBezierCubicVar(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, const float* thicks, int thickCount, Color color); // Draw spline segment with variable thickness: Cubic Bezier, 2 points, 2 control points // Spline segment point evaluation functions, for a given t [0.0f .. 1.0f] diff --git a/src/rshapes.c b/src/rshapes.c index aedb58d1c..8852fea59 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2107,6 +2107,89 @@ void DrawSplineSegmentBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4 DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color); } +// Draw spline segment with variable thickness: Linear, 2 points +void DrawSplineSegmentLinearVar(Vector2 p1, Vector2 p2, const float* thicks, int thickCount, Color color) +{ + if (thickCount >= 4) + { + const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS; + + Vector2 previous[2] = { 0 }; + Vector2 current[2] = { 0 }; + float t = 0.0f; + + // Linear velocity does not change across the curve + Vector2 tangent = { 0 }; + + tangent.x = p2.x - p1.x; + tangent.y = p2.y - p1.y; + + float speedSqr = tangent.x*tangent.x + tangent.y*tangent.y; + + if (speedSqr > 0) + { + float speedInv = 1.0f/sqrtf(speedSqr); + tangent.x *= speedInv; + tangent.y *= speedInv; + + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i <= SPLINE_SEGMENT_DIVISIONS; i++) + { + t = step*(float)i; + + Vector2 point = { 0 }; + + point.x = p1.x*(1.0f - t) + p2.x*t; + point.y = p1.y*(1.0f - t) + p2.y*t; + + // TODO: Doesn't seem to be working properly for more than 3 distinct values + float thick; + { + float tMajor = t*(float)thickCount; + int tIndex = (int)tMajor; + float tMinor = tMajor - (float)tIndex; + tIndex *= 3; + if (tIndex >= thickCount - 3) + { + tIndex = thickCount - 4; + tMinor = 1.0f; + } + float a = powf(1.0f - t, 3); + float b = 3.0f*powf(1.0f - t, 2)*t; + float c = 3.0f*(1.0f - t)*t*t; + float d = t*t*t; + + thick = a*thicks[tIndex] + b*thicks[tIndex + 1] + c*thicks[tIndex + 2] + d*thicks[tIndex + 3]; + } + + current[0].x = point.x + thick*tangent.y; + current[0].y = point.y - thick*tangent.x; + + current[1].x = point.x - thick*tangent.y; + current[1].y = point.y + thick*tangent.x; + + if (i > 0) + { + rlVertex2f(current[0].x, current[0].y); + rlVertex2f(previous[0].x, previous[0].y); + rlVertex2f(previous[1].x, previous[1].y); + + rlVertex2f(current[1].x, current[1].y); + rlVertex2f(current[0].x, current[0].y); + rlVertex2f(previous[1].x, previous[1].y); + } + + previous[0] = current[0]; + previous[1] = current[1]; + } + + rlEnd(); + } + } +} + // Draw spline segment with variable thickness: Cubic Bezier, 2 points, 2 control points void DrawSplineSegmentBezierCubicVar(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, const float* thicks, int thickCount, Color color) { @@ -2138,8 +2221,8 @@ void DrawSplineSegmentBezierCubicVar(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 float speedSqr = (tangent.x*tangent.x + tangent.y*tangent.y); if (speedSqr == 0) continue; float speedInv = 1.0f/sqrtf(speedSqr); - tangent.x = tangent.x*speedInv; - tangent.y = tangent.y*speedInv; + tangent.x *= speedInv; + tangent.y *= speedInv; Vector2 point = { 0 }; { @@ -2178,7 +2261,7 @@ void DrawSplineSegmentBezierCubicVar(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 current[1].x = point.x - thick*tangent.y; current[1].y = point.y + thick*tangent.x; - if (i > 0) + if (i > 0) // TODO: `previous` may be unassigned in i=1 if i=0 had a `speedSqr` of 0 { rlVertex2f(current[0].x, current[0].y); rlVertex2f(previous[0].x, previous[0].y);