From 820b971eff965bc6b917f27a0addab8adc6c8edc Mon Sep 17 00:00:00 2001 From: Matthew Roush Date: Tue, 25 Aug 2026 13:26:42 -0400 Subject: [PATCH] [rshapes] Add some missing `Draw*LinesEx()` functions and new example `shapes_outlines_testbed` (#6072) * [rshapes] Add `DrawEllipseLinesEx()` Add DrawEllipseLinesEx() to the API for consistency with other shape drawing functions. * [rshapes] Add `DrawTriangleLinesEx()` Add DrawTriangleLinesEx() to the API for consistency with other shape drawing functions. * [rshapes] Add `DrawCircleSectorLinesEx()` Add DrawCircleSectorLinesEx() to the API for consistency with other shape drawing functions. * [rshapes] Add `DrawRingLinesEx()` Add DrawRingLinesEx() to the API for consistency with other shape drawing functions. * [examples] Add shapes_outlines_testbed This example stress tests the previously existing and newly added shape outline functions. --- examples/shapes/shapes_outlines_testbed.c | 407 ++++ examples/shapes/shapes_outlines_testbed.png | Bin 0 -> 24683 bytes src/raylib.h | 4 + src/rshapes.c | 1842 +++++++++++++++++++ 4 files changed, 2253 insertions(+) create mode 100644 examples/shapes/shapes_outlines_testbed.c create mode 100644 examples/shapes/shapes_outlines_testbed.png diff --git a/examples/shapes/shapes_outlines_testbed.c b/examples/shapes/shapes_outlines_testbed.c new file mode 100644 index 000000000..22dcc0b5d --- /dev/null +++ b/examples/shapes/shapes_outlines_testbed.c @@ -0,0 +1,407 @@ +/******************************************************************************************* +* +* raylib [shapes] example - outlines testbed +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 6.1, last time updated with raylib 6.1 +* +* Example contributed by Matthew Roush (@MatthewRoush) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2026 Matthew Roush (@MatthewRoush) +* +********************************************************************************************/ + +#include "raylib.h" + +#include "raymath.h" + +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" // Required for GUI controls + +#include + +#define COLOR_FILLED DARKBLUE +#define COLOR_OUTLINE YELLOW + +#define SHAPE_SPACING 14.0f +#define SHAPE_SIZE 46.0f +#define BOX_SPACING 10.0f + +#define MOUSE_CAMERA_ZOOM_SPEED 0.3f +#define KEYBOARD_CAMERA_MOVE_SPEED 10.0f +#define KEYBOARD_CAMERA_ZOOM_SPEED 0.1f + +#define CAMERA_ZOOM_MIN 0.1f +#define CAMERA_ZOOM_MAX 1000.0f + +// The shapes are ordered according to their order in this enum, left to right +enum { + ORDER_RECTANGLE = 0, + ORDER_RECTANGLE_ROUNDED, + ORDER_CIRCLE, + ORDER_ELLIPSE, + ORDER_CIRCLE_SECTOR, + ORDER_RING, + ORDER_TRIANGLE, + ORDER_POLYGON, + COUNT_SHAPES +}; + +// The line style groups are ordered according to their order in this enum, top to bottom +enum { + ORDER_LINES = 0, + ORDER_LINES_EX_WORLD, + ORDER_LINES_EX_SCREEN, +}; + +#define BOX_WIDTH (SHAPE_SIZE*COUNT_SHAPES + SHAPE_SPACING*(COUNT_SHAPES - 1) + BOX_SPACING*2.0f) +#define BOX_HEIGHT (SHAPE_SIZE + BOX_SPACING*2.0f) + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - outlines testbed"); + + Camera2D camera = { .zoom = 1 }; + + // User configurable options while running the program. + float lineOpacity = 130; + float lineThickness = 4.0f; + float rectangleRoundness = 0.4f; + float rectangleSegments = 9; + float ellipseRadiusY = 0.5f; + float circleStartAngle = 20.0f; + float circleEndAngle = 270.0f; + float circleSegments = 36; + float ringInnerRadiusScale = 0.3f; + float polygonSides = 6; + + bool disableMouseControl = false; + + const Rectangle optionsBackground = { 510, 0, 290, 450 }; + + const Vector2 zoomPoint = {((float)screenWidth - optionsBackground.width)/2.0f, (float)screenHeight/2.0f}; + + GuiSetStyle(LABEL, TEXT_COLOR_NORMAL, ColorToInt(WHITE)); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + const Vector2 mousePosScreen = GetMousePosition(); + const Vector2 mouseWheelVec = GetMouseWheelMoveV(); + + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(mousePosScreen, optionsBackground)) disableMouseControl = true; + + if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) disableMouseControl = false; + + if (!disableMouseControl) { + if (mouseWheelVec.y != 0.0f) { + const Vector2 prevWorldZoomPoint = GetScreenToWorld2D(zoomPoint, camera); + + camera.zoom *= exp2f(MOUSE_CAMERA_ZOOM_SPEED*mouseWheelVec.y); // Constant zoom rate + camera.zoom = Clamp(camera.zoom, CAMERA_ZOOM_MIN, CAMERA_ZOOM_MAX); + + const Vector2 worldZoomPoint = GetScreenToWorld2D(zoomPoint, camera); + camera.target = Vector2Add(camera.target, Vector2Subtract(prevWorldZoomPoint, worldZoomPoint)); + } + + if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { + const Vector2 mouseDelta = GetMouseDelta(); + camera.target.x -= mouseDelta.x/camera.zoom; + camera.target.y -= mouseDelta.y/camera.zoom; + + const int mouseMaxX = (int)optionsBackground.x; + const int mouseMaxY = screenHeight; + + int newX = (int)mousePosScreen.x; + int newY = (int)mousePosScreen.y; + + // In C, the '%' operator computes the remainder, we want the modulus + newX = (newX%mouseMaxX + mouseMaxX)%mouseMaxX; + newY = (newY%mouseMaxY + mouseMaxY)%mouseMaxY; + + if ((newX != (int)mousePosScreen.x) || (newY != (int)mousePosScreen.y)) SetMousePosition(newX, newY); + } + } + + if (IsKeyDown(KEY_A)) camera.target.x -= KEYBOARD_CAMERA_MOVE_SPEED/camera.zoom; + if (IsKeyDown(KEY_D)) camera.target.x += KEYBOARD_CAMERA_MOVE_SPEED/camera.zoom; + if (IsKeyDown(KEY_W)) camera.target.y -= KEYBOARD_CAMERA_MOVE_SPEED/camera.zoom; + if (IsKeyDown(KEY_S)) camera.target.y += KEYBOARD_CAMERA_MOVE_SPEED/camera.zoom; + + if (IsKeyDown(KEY_UP)) + { + const Vector2 prevWorldZoomPoint = GetScreenToWorld2D(zoomPoint, camera); + + camera.zoom *= exp2f(KEYBOARD_CAMERA_ZOOM_SPEED); + camera.zoom = Clamp(camera.zoom, CAMERA_ZOOM_MIN, CAMERA_ZOOM_MAX); + + const Vector2 worldZoomPoint = GetScreenToWorld2D(zoomPoint, camera); + camera.target = Vector2Add(camera.target, Vector2Subtract(prevWorldZoomPoint, worldZoomPoint)); + } + if (IsKeyDown(KEY_DOWN)) + { + const Vector2 prevWorldZoomPoint = GetScreenToWorld2D(zoomPoint, camera); + + camera.zoom *= exp2f(-KEYBOARD_CAMERA_ZOOM_SPEED); + camera.zoom = Clamp(camera.zoom, CAMERA_ZOOM_MIN, CAMERA_ZOOM_MAX); + + const Vector2 worldZoomPoint = GetScreenToWorld2D(zoomPoint, camera); + camera.target = Vector2Add(camera.target, Vector2Subtract(prevWorldZoomPoint, worldZoomPoint)); + } + + if (IsKeyPressed(KEY_Z)) + { + const Vector2 zoomPoint = {((float)screenWidth - optionsBackground.width)/2.0f, (float)screenHeight/2.0f}; + const Vector2 prevWorldZoomPoint = GetScreenToWorld2D(zoomPoint, camera); + + camera.zoom = 1.0f; + + const Vector2 worldZoomPoint = GetScreenToWorld2D(zoomPoint, camera); + camera.target = Vector2Add(camera.target, Vector2Subtract(prevWorldZoomPoint, worldZoomPoint)); + } + + if (IsKeyPressed(KEY_C)) camera.target = (Vector2){ 0 }; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground((Color){ 50, 50, 55, 255 }); + + Color colorOutline = COLOR_OUTLINE; + colorOutline.a = (unsigned char)lineOpacity; + + BeginMode2D(camera); + + const float shapeOffset = BOX_SPACING*2.0f; + const float shapePaddingX = SHAPE_SIZE + SHAPE_SPACING; + const float shapePaddingY = SHAPE_SIZE + SHAPE_SPACING + BOX_SPACING*2.0f; + + const float radiusX = SHAPE_SIZE/2.0f; + const float radiusY = radiusX*ellipseRadiusY; + + const float ringOuterRadius = SHAPE_SIZE/2.0f; + const float ringInnerRadius = ringOuterRadius*ringInnerRadiusScale; + + const Vector2 triangleVertex0Offset = { 0.0f, SHAPE_SIZE*0.8f }; + const Vector2 triangleVertex1Offset = { SHAPE_SIZE*0.6f, SHAPE_SIZE }; + const Vector2 triangleVertex2Offset = { SHAPE_SIZE, 0.0f }; + + // ---------------------------------------- + // Draw*Lines() + float posY = shapeOffset + shapePaddingY*ORDER_LINES; + + // Rectangle + float posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE; + DrawRectangleRec((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, COLOR_FILLED); + DrawRectangleLines(posX, posY, SHAPE_SIZE, SHAPE_SIZE, colorOutline); + + // Rectangle Rounded + posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE_ROUNDED; + DrawRectangleRounded((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, COLOR_FILLED); + DrawRectangleRoundedLines((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, colorOutline); + + // Circle + posX = shapeOffset + shapePaddingX*ORDER_CIRCLE; + DrawCircleV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, COLOR_FILLED); + DrawCircleLinesV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, colorOutline); + + // Ellipse + posX = shapeOffset + shapePaddingX*ORDER_ELLIPSE; + DrawEllipseV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, COLOR_FILLED); + DrawEllipseLinesV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, colorOutline); + + // Circle Sector + posX = shapeOffset + shapePaddingX*ORDER_CIRCLE_SECTOR; + DrawCircleSector((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED); + DrawCircleSectorLines((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, colorOutline); + + // Ring + posX = shapeOffset + shapePaddingX*ORDER_RING; + DrawRing((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED); + DrawRingLines((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, colorOutline); + + // Triangle + posX = shapeOffset + shapePaddingX*ORDER_TRIANGLE; + DrawTriangle((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y }, + (Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y }, + (Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y }, + COLOR_FILLED); + DrawTriangleLines((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y }, + (Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y }, + (Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y }, + colorOutline); + + // Polygon + posX = shapeOffset + shapePaddingX*ORDER_POLYGON; + DrawPoly((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, COLOR_FILLED); + DrawPolyLines((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, colorOutline); + + // Group the shapes + GuiGroupBox((Rectangle){ shapeOffset - BOX_SPACING, posY - BOX_SPACING, BOX_WIDTH, BOX_HEIGHT }, "Draw*Lines()"); + // ---------------------------------------- + + // ---------------------------------------- + // Draw*LinesEx() with world space pixel thickness + posY = shapeOffset + shapePaddingY*ORDER_LINES_EX_WORLD; + + // Rectangle + posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE; + DrawRectangleRec((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, COLOR_FILLED); + DrawRectangleLinesEx((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, lineThickness, colorOutline); + + // Rectangle Rounded + posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE_ROUNDED; + DrawRectangleRounded((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, COLOR_FILLED); + DrawRectangleRoundedLinesEx((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, lineThickness, colorOutline); + + // Circle + posX = shapeOffset + shapePaddingX*ORDER_CIRCLE; + DrawCircleV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, COLOR_FILLED); + DrawCircleLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, lineThickness, colorOutline); + + // Ellipse + posX = shapeOffset + shapePaddingX*ORDER_ELLIPSE; + DrawEllipseV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, COLOR_FILLED); + DrawEllipseLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, lineThickness, colorOutline); + + // Circle Sector + posX = shapeOffset + shapePaddingX*ORDER_CIRCLE_SECTOR; + DrawCircleSector((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED); + DrawCircleSectorLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, lineThickness, colorOutline); + + // Ring + posX = shapeOffset + shapePaddingX*ORDER_RING; + DrawRing((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED); + DrawRingLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, lineThickness, colorOutline); + + // Triangle + posX = shapeOffset + shapePaddingX*ORDER_TRIANGLE; + DrawTriangle((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y }, + (Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y }, + (Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y }, + COLOR_FILLED); + DrawTriangleLinesEx((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y }, + (Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y }, + (Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y }, + lineThickness, colorOutline); + + // Polygon + posX = shapeOffset + shapePaddingX*ORDER_POLYGON; + DrawPoly((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, COLOR_FILLED); + DrawPolyLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, lineThickness, colorOutline); + + // Group the shapes + GuiGroupBox((Rectangle){ shapeOffset - BOX_SPACING, posY - BOX_SPACING, BOX_WIDTH, BOX_HEIGHT }, "Draw*LinesEx() with *world space* pixel thickness"); + // ---------------------------------------- + + // ---------------------------------------- + // Draw*LinesEx() with screen space pixel thickness + posY = shapeOffset + shapePaddingY*ORDER_LINES_EX_SCREEN; + + float constantThickness = lineThickness/camera.zoom; + + // Rectangle + posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE; + DrawRectangleRec((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, COLOR_FILLED); + DrawRectangleLinesEx((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, constantThickness, colorOutline); + + // Rectangle Rounded + posX = shapeOffset + shapePaddingX*ORDER_RECTANGLE_ROUNDED; + DrawRectangleRounded((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, COLOR_FILLED); + DrawRectangleRoundedLinesEx((Rectangle){ posX, posY, SHAPE_SIZE, SHAPE_SIZE }, rectangleRoundness, (int)rectangleSegments, constantThickness, colorOutline); + + // Circle + posX = shapeOffset + shapePaddingX*ORDER_CIRCLE; + DrawCircleV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, COLOR_FILLED); + DrawCircleLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, constantThickness, colorOutline); + + // Ellipse + posX = shapeOffset + shapePaddingX*ORDER_ELLIPSE; + DrawEllipseV((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, COLOR_FILLED); + DrawEllipseLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, radiusY, constantThickness, colorOutline); + + // Circle Sector + posX = shapeOffset + shapePaddingX*ORDER_CIRCLE_SECTOR; + DrawCircleSector((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED); + DrawCircleSectorLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, radiusX, circleStartAngle, circleEndAngle, (int)circleSegments, constantThickness, colorOutline); + + // Ring + posX = shapeOffset + shapePaddingX*ORDER_RING; + DrawRing((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, COLOR_FILLED); + DrawRingLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, ringInnerRadius, ringOuterRadius, circleStartAngle, circleEndAngle, (int)circleSegments, constantThickness, colorOutline); + + // Triangle + posX = shapeOffset + shapePaddingX*ORDER_TRIANGLE; + DrawTriangle((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y }, + (Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y }, + (Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y }, + COLOR_FILLED); + DrawTriangleLinesEx((Vector2){ posX + triangleVertex0Offset.x, posY + triangleVertex0Offset.y }, + (Vector2){ posX + triangleVertex1Offset.x, posY + triangleVertex1Offset.y }, + (Vector2){ posX + triangleVertex2Offset.x, posY + triangleVertex2Offset.y }, + constantThickness, colorOutline); + + // Polygon + posX = shapeOffset + shapePaddingX*ORDER_POLYGON; + DrawPoly((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, COLOR_FILLED); + DrawPolyLinesEx((Vector2){ posX + SHAPE_SIZE/2.0f, posY + SHAPE_SIZE/2.0f }, (int)polygonSides, radiusX, 0.0f, constantThickness, colorOutline); + + // Group the shapes + GuiGroupBox((Rectangle){ shapeOffset - BOX_SPACING, posY - BOX_SPACING, BOX_WIDTH, BOX_HEIGHT }, "Draw*LinesEx() with *screen space* pixel thickness"); + // ---------------------------------------- + + EndMode2D(); + + DrawRectangleRec(optionsBackground, Fade(DARKGRAY, 0.75f)); + + DrawRectangleRec((Rectangle){ optionsBackground.x, 360, optionsBackground.width, 90 }, Fade(BLACK, 0.4f)); + DrawLineEx((Vector2){ optionsBackground.x, 360 }, (Vector2){ optionsBackground.x + optionsBackground.width, 360 }, 1.0f, BLACK); + + DrawText("Move with the mouse or WASD keys", (int)optionsBackground.x + 10, 370, 10, ORANGE); + DrawText("Zoom with the mouse or UP and DOWN keys", (int)optionsBackground.x + 10, 390, 10, ORANGE); + DrawText("Press C to reset position", (int)optionsBackground.x + 10, 410, 10, ORANGE); + DrawText("Press Z to reset zoom", (int)optionsBackground.x + 10, 430, 10, ORANGE); + + DrawLineEx((Vector2){ optionsBackground.x, optionsBackground.y }, (Vector2){ optionsBackground.x, optionsBackground.y + optionsBackground.height }, 1.0f, BLACK); + + GuiSliderBar((Rectangle){ 605, 10, 150, 25 }, "Line Opacity" , TextFormat("%d" , (int)lineOpacity) , &lineOpacity , 0 , 255); + GuiSliderBar((Rectangle){ 605, 45, 150, 25 }, "Thickness" , TextFormat("%.2f", lineThickness) , &lineThickness , -20.0f , 40.0f); + GuiSliderBar((Rectangle){ 605, 80, 150, 25 }, "Rect Roundness" , TextFormat("%.2f", rectangleRoundness) , &rectangleRoundness , -1.0f , 2.0f); + GuiSliderBar((Rectangle){ 605, 115, 150, 25 }, "Rect Segments" , TextFormat("%d" , (int)rectangleSegments), &rectangleSegments , -1 , 100); + GuiSliderBar((Rectangle){ 605, 150, 150, 25 }, "Ellipse Radius Y", TextFormat("%.2f", ellipseRadiusY) , &ellipseRadiusY , 0.0f , 1.25f); + GuiSliderBar((Rectangle){ 605, 185, 150, 25 }, "Start Angle" , TextFormat("%.2f", circleStartAngle) , &circleStartAngle , -360.0f, 360.0f); + GuiSliderBar((Rectangle){ 605, 220, 150, 25 }, "End Angle" , TextFormat("%.2f", circleEndAngle) , &circleEndAngle , -360.0f, 360.0f); + GuiSliderBar((Rectangle){ 605, 255, 150, 25 }, "Circle Segments" , TextFormat("%d" , (int)circleSegments) , &circleSegments , -1 , 100); + GuiSliderBar((Rectangle){ 605, 290, 150, 25 }, "Ring Radius" , TextFormat("%.2f", ringInnerRadiusScale) , &ringInnerRadiusScale, -1.0f , 2.0f); + GuiSliderBar((Rectangle){ 605, 325, 150, 25 }, "Poly Sides" , TextFormat("%d" , (int)polygonSides) , &polygonSides , -1 , 100); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/shapes/shapes_outlines_testbed.png b/examples/shapes/shapes_outlines_testbed.png new file mode 100644 index 0000000000000000000000000000000000000000..02d58449eda279ccbea5fdb3ccab033ebfd748a2 GIT binary patch literal 24683 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 I&MBb@05>f2ZU6uP literal 0 HcmV?d00001 diff --git a/src/raylib.h b/src/raylib.h index 154158131..d2a52420c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1284,6 +1284,7 @@ RLAPI void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int sp RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle, counter-clockwise vertex order RLAPI void DrawTriangleGradient(Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors, counter-clockwise vertex/color order RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline, counter-clockwise vertex order +RLAPI void DrawTriangleLinesEx(Vector2 v1, Vector2 v2, Vector2 v3, float thick, Color color); // Draw triangle outline with line thickness, counter-clockwise vertex order RLAPI void DrawTriangleFan(const Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points (first vertex is the center) RLAPI void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle @@ -1306,6 +1307,7 @@ RLAPI void DrawCircleV(Vector2 center, float radius, Color color); RLAPI void DrawCircleGradient(Vector2 center, float radius, Color inner, Color outer); // Draw a gradient-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 +RLAPI void DrawCircleSectorLinesEx(Vector2 center, float radius, float startAngle, float endAngle, int segments, float thick, Color color); // Draw circle sector outline with thickness RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version) RLAPI void DrawCircleLinesEx(Vector2 center, float radius, float thick, Color color); // Draw circle outline with line thickness @@ -1313,8 +1315,10 @@ RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, C RLAPI void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse (Vector version) RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline RLAPI void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color); // Draw ellipse outline (Vector version) +RLAPI void DrawEllipseLinesEx(Vector2 center, float radiusH, float radiusV, float thick, Color color); // Draw ellipse outline with line thickness RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring outline +RLAPI void DrawRingLinesEx(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, float thick, Color color); // Draw ring outline with line thickness // Splines drawing functions RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color); // Draw spline: Linear, minimum 2 points diff --git a/src/rshapes.c b/src/rshapes.c index 65040f452..466cff72d 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -375,6 +375,166 @@ void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) rlEnd(); } +// Draw a triangle using lines with thickness +// NOTE: Vertex must be provided in counter-clockwise order +void DrawTriangleLinesEx(Vector2 v1, Vector2 v2, Vector2 v3, float thick, Color color) +{ + /* + A sketch to make things simpler + + The exterior points are v1-3, the interior points are v4-6, and the exterior edges are e1-3 + + v1 + /\ + /v4\ + // \\ + // \\ + e3 // \\ e2 + // \\ + // \\ + //v5 v6\\ + v2==============v3 + e1 + */ + + Vector2 e1 = {v2.x - v3.x, v2.y - v3.y}; + Vector2 e2 = {v3.x - v1.x, v3.y - v1.y}; + Vector2 e3 = {v1.x - v2.x, v1.y - v2.y}; + + float e1Length = sqrtf(e1.x*e1.x + e1.y*e1.y); + float e2Length = sqrtf(e2.x*e2.x + e2.y*e2.y); + float e3Length = sqrtf(e3.x*e3.x + e3.y*e3.y); + + float perimeter = e1Length + e2Length + e3Length; + float semiperimeter = perimeter/2.0f; + + // The incenter of a triangle is equidistant from each edge, which is useful for drawing a nice looking outline + Vector2 incenter = { + (e1Length*v1.x + e2Length*v2.x + e3Length*v3.x)/perimeter, + (e1Length*v1.y + e2Length*v2.y + e3Length*v3.y)/perimeter + }; + + // The inradius of a triangle is the radius of the biggest circle that can fit inside of said triangle + // That circle is also centered on the incenter + float inradius = sqrtf(((semiperimeter - e1Length)*(semiperimeter - e2Length)*(semiperimeter - e3Length))/semiperimeter); + + // The triangle (v1, v2, v3) will be scaled by this to get (v4, v5, v6) + float scale = 1.0f - thick/inradius; + + // Just a filled-in triangle + if (scale <= 0.0f) + { + DrawTriangle(v1, v2, v3, color); + return; + } + + // In order for the scaling to be correct, the incenter has to be at the origin (0, 0) when scaling + Vector2 v4 = {incenter.x + (v1.x - incenter.x)*scale, incenter.y + (v1.y - incenter.y)*scale}; + Vector2 v5 = {incenter.x + (v2.x - incenter.x)*scale, incenter.y + (v2.y - incenter.y)*scale}; + Vector2 v6 = {incenter.x + (v3.x - incenter.x)*scale, incenter.y + (v3.y - incenter.y)*scale}; + + // Swap the vertices so the winding order is correct + if (thick < 0.0f) + { + Vector2 temp = v1; + v1 = v4; + v4 = temp; + + temp = v2; + v2 = v5; + v5 = temp; + + temp = v3; + v3 = v6; + v6 = temp; + } + +#if SUPPORT_QUADS_DRAW_MODE + rlSetTexture(GetShapesTexture().id); + Rectangle shapeRect = GetShapesTextureRectangle(); + + rlBegin(RL_QUADS); + + rlColor4ub(color.r, color.g, color.b, color.a); + + // Edge 3 + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(v1.x, v1.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(v2.x, v2.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(v5.x, v5.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(v4.x, v4.y); + + // Edge 1 + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(v2.x, v2.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(v3.x, v3.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(v6.x, v6.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(v5.x, v5.y); + + // Edge 2 + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(v3.x, v3.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(v1.x, v1.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(v4.x, v4.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(v6.x, v6.y); + + rlEnd(); + + rlSetTexture(0); +#else + rlBegin(RL_TRIANGLES); + + rlColor4ub(color.r, color.g, color.b, color.a); + + // Edge 3 + rlVertex2f(v1.x, v1.y); + rlVertex2f(v2.x, v2.y); + rlVertex2f(v4.x, v4.y); + + rlVertex2f(v2.x, v2.y); + rlVertex2f(v5.x, v5.y); + rlVertex2f(v4.x, v4.y); + + // Edge 1 + rlVertex2f(v2.x, v2.y); + rlVertex2f(v3.x, v3.y); + rlVertex2f(v5.x, v5.y); + + rlVertex2f(v3.x, v3.y); + rlVertex2f(v6.x, v6.y); + rlVertex2f(v5.x, v5.y); + + // Edge 2 + rlVertex2f(v3.x, v3.y); + rlVertex2f(v1.x, v1.y); + rlVertex2f(v4.x, v4.y); + + rlVertex2f(v3.x, v3.y); + rlVertex2f(v4.x, v4.y); + rlVertex2f(v6.x, v6.y); + + rlEnd(); +#endif +} + // Draw a triangle fan defined by points // NOTE: First vertex provided is the center, shared by all triangles // By default, following vertex should be provided in counter-clockwise order @@ -1625,6 +1785,738 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float rlEnd(); } +// Draw a piece of a circle outlines with thickness +void DrawCircleSectorLinesEx(Vector2 center, float radius, float startAngle, float endAngle, int segments, float thick, Color color) +{ + if (startAngle == endAngle) return; + 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; + } + + bool showCapLines = true; + // Drawing a whole circle, things get weird without limiting the circle to 360 degrees + if (endAngle - startAngle >= 360.0f) + { + showCapLines = thick >= 0.0f; + endAngle = startAngle + 360.0f; + } + + 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)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); + + if (segments <= 0) segments = minSegments; + } + + float stepLength = (endAngle - startAngle)/(float)segments; + float angle = startAngle; + + /* + A sketch to help make things clearer + + NOTE: Some considerations are different when `thick` is negative + The vertices used here are still relevant, but would instead be outside of the circle + S0 is always the center + + The circle sector outline is drawn in 3 main pieces, the circle outline, cap 1, and cap 2 + The circle outline is self explanatory + Cap 1 covers the `startAngle` edge and cap 2 covers the `endAngle` edge + S0 is the first shared point between the caps, and also the circle's center + S1 is the second shared point (sometimes not shared) between the caps + S1 is also C0 and C3 in this sketch. In certain cases, S1 goes outside of + the circle and C0 and C3 become different points + C1 is one of cap 1's vertices that is on the inside edge of the circle outline + C2 is like C1, but is also on the `startAngle` edge + C4 is cap 2's vertex that corresponds with C1 + C5 is cap 2's vertex that corresponds with C2, except on the `endAngle` edge + + [][][][][] + [][] [] + [] []C4[]C5 + [] [][] {} {} + [] [] {} {} + [] [] {}C {} <- endAngle + [] [] {}a {} + [] [] {}p {} + [] [] {}2 {} + [] [] {} {} + [] [] {} {} startAngle + [] [] {} S0{}{}{}{}{}{}{}{}C2[][] + [] [] {}{} Cap1 [] [] + [] [] S1{}{}{}{}{}{}{}{}{}{}C1 [] + [] [] [] [] + [] [] [] [] + [] [] Not filled in [] [] + [] [] [] [] + [] [] [] [] + [] [][] [][] [] + [] [][][][][][][] [] + [][] Circle outline [][] + [][][][][][][][][] + + [] = Circle outline edge pixel + {} = Cap outline edge pixel + */ + + // We are not drawing a circle, we are drawing an n-sided polygon + // So, we need to adjust the outline thickness of the "circle" for it to look correct with fewer segments + float apothem = radius*cosf(DEG2RAD*((endAngle - startAngle)/2.0f)/(float)segments); + float radiusThick = thick*(radius/apothem); + + float outerRadius = radius; + float innerRadius = radius - radiusThick; + + if (thick >= 0.0f) + { + if (thick >= innerRadius) + { + DrawCircleSector(center, radius, startAngle, endAngle, segments, color); + return; + } + } + else + { + float tmp = outerRadius; + outerRadius = innerRadius; + innerRadius = tmp; + } + + // Cap 1 vertices + Vector2 c0 = { 0 }; + Vector2 c1 = { 0 }; + Vector2 c2 = { 0 }; + + // Cap 2 vertices + Vector2 c3 = { 0 }; + Vector2 c4 = { 0 }; + Vector2 c5 = { 0 }; + + // The number of angle steps that come before C1 (from `startAngle`, counter clockwise) + int stepsBeforeC1 = 0; + bool s1OutsideOfCircle = false; + // The number of angle steps that come before C0 (from `startAngle`, counter clockwise) + // Only used if S1 is outside of the circle + int stepsBeforeC0 = 0; + + if (showCapLines) + { + if (thick >= 0.0f) + { + c2 = (Vector2){ center.x + cosf(DEG2RAD*startAngle)*innerRadius, center.y + sinf(DEG2RAD*startAngle)*innerRadius }; + c5 = (Vector2){ center.x + cosf(DEG2RAD*endAngle)*innerRadius, center.y + sinf(DEG2RAD*endAngle)*innerRadius }; + + // For C1 and C4, we need to find the point that lies on the circle (n-sided polygon, actually) + // We want C1 and C4 to be `thick` pixels perpendicularly from the `startAngle` and `endAngle` edges + // and to be on the `innerRadius` edge + + float c1Angle = RAD2DEG*asinf(thick/innerRadius); + + // There are more segments before C1 than there are segments being drawn, + // so the whole circle sector must be covered + if (c1Angle/stepLength >= (float)segments) + { + DrawCircleSector(center, radius, startAngle, endAngle, segments, color); + return; + } + + // Do this after the previous check just in case `stepLength` is really small and + // dividing by it produces a very large number + stepsBeforeC1 = (int)(c1Angle/stepLength); + + // The angles of the vertices on the circle outline before and after C1 + float vertexAngleBeforeC1 = stepLength*(float)stepsBeforeC1; + float vertexAngleAfterC1 = stepLength*((float)(stepsBeforeC1 + 1)); + + /* + Here is another sketch + + We know which outline line segment C1 is on (`vertexAngleBeforeC1` and `vertexAngleAfterC1`) + Now we just need to know where on that line segment C1 is + + We can change our frame of reference so that `startAngle` is 0 degrees and `center` is at the origin (0, 0) + This makes the math much simpler because now we can just go straight down by `thick` pixels and + use the horizontal line that passes through that point to determine where C1 is on our line segment + + The line segment is defined by p1 and p2, we need C1, which is on that edge + The 'y' axis of C1 is equal to `thick` (within this modified frame of reference) + + p1 + /| + / | + / | + / | + / | + / | + / | + / | + C1--------- <-- y axis = `thick` + / + / + p2 + */ + + Vector2 p1 = { cosf(DEG2RAD*vertexAngleBeforeC1)*innerRadius, sinf(DEG2RAD*vertexAngleBeforeC1)*innerRadius }; + Vector2 p2 = { cosf(DEG2RAD*vertexAngleAfterC1)*innerRadius, sinf(DEG2RAD*vertexAngleAfterC1)*innerRadius }; + + // Find the `t` of C1 between p1 and p2 ('t' as in `Lerp(start, end, t)`) + // This is used to lerp between the actual vertices (outside of our modified frame of reference) + // before and after C1 + float t = (p1.y - thick)/(p1.y - p2.y); + + Vector2 vertexBeforeCap1Vertex = { center.x + cosf(DEG2RAD*(startAngle + vertexAngleBeforeC1))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + vertexAngleBeforeC1))*innerRadius }; + Vector2 vertexAfterCap1Vertex = { center.x + cosf(DEG2RAD*(startAngle + vertexAngleAfterC1))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + vertexAngleAfterC1))*innerRadius }; + + c1.x = vertexBeforeCap1Vertex.x + (vertexAfterCap1Vertex.x - vertexBeforeCap1Vertex.x)*t; + c1.y = vertexBeforeCap1Vertex.y + (vertexAfterCap1Vertex.y - vertexBeforeCap1Vertex.y)*t; + + Vector2 vertexBeforeCap2Vertex = { center.x + cosf(DEG2RAD*(endAngle - vertexAngleBeforeC1))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - vertexAngleBeforeC1))*innerRadius }; + Vector2 vertexAfterCap2Vertex = { center.x + cosf(DEG2RAD*(endAngle - vertexAngleAfterC1))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - vertexAngleAfterC1))*innerRadius }; + + c4.x = vertexBeforeCap2Vertex.x + (vertexAfterCap2Vertex.x - vertexBeforeCap2Vertex.x)*t; + c4.y = vertexBeforeCap2Vertex.y + (vertexAfterCap2Vertex.y - vertexBeforeCap2Vertex.y)*t; + + /* + Another sketch couldn't hurt + + This is a "zoomed in" view of the center of the circle sector + You can see where S0 is, and where we want S1 to be + `innerAngleBetweenCapEnds` is the angle of the diagonal line ('//') between the cap ends + `S1Length` is the length of that line + + Since the caps are always parallel to `startAngle` and `endAngle`, + we always have a right triangle we can use to determine where S1 is + + [] [] + [] [] + [] C [] <- endAngle + [] a [] + [] p [] + [] 2 [] startAngle + [][][][]S0[][][][][][] + //[] + // [] Cap 1 + // [] + S1 [][][][][][][] + */ + + float innerAngleBetweenCapEnds = ((endAngle - 90.0f) - (startAngle + 90.0f))/2.0f; + float s1Length = thick/cosf(DEG2RAD*innerAngleBetweenCapEnds); + + // As `startAngle` and `endAngle` draw more of a circle, S1 goes further out from the center + // It can go so far that it is outside of the circle, by a lot + // This case needs to be detected and handled + // If S1 is within the circle, nothing special needs to happen + // But, if S1 is outside of the circle, we need to find the two points (C0 and C3) where + // the line segments C0->C1 and C0->C3 intersect the circle outline, + // using the same method we used to find C1 and C4 + if ((innerAngleBetweenCapEnds < 90.0f) && (s1Length <= innerRadius)) + { + // S1 is inside of the circle + + float betweenStartAndEndAngle = (endAngle + startAngle)/2.0f; + c0 = (Vector2){ center.x + cosf(DEG2RAD*betweenStartAndEndAngle)*s1Length, center.y + sinf(DEG2RAD*betweenStartAndEndAngle)*s1Length }; + c3 = c0; + + p1 = (Vector2){ c1.x - center.x, c1.y - center.y }; + p2 = (Vector2){ c0.x - center.x, c0.y - center.y }; + + // Copied from "raymath.h" Vector2Angle() + float dot = p1.x*p2.x + p1.y*p2.y; + float det = p1.x*p2.y - p1.y*p2.x; + float c1ToS1Angle = atan2f(det, dot); + + // If C1 and C4 are on the wrong side of S1, the whole circle sector is covered + if (c1ToS1Angle < 0.0f) + { + DrawCircleSector(center, radius, startAngle, endAngle, segments, color); + return; + } + } + else + { + // S1 is outside of the circle + + if (endAngle - startAngle <= 180.0f) + { + DrawCircleSector(center, radius, startAngle, endAngle, segments, color); + return; + } + + s1OutsideOfCircle = true; + + stepsBeforeC0 = (int)((180.0f + RAD2DEG*asinf(thick/-innerRadius))/stepLength); + + // Reuse the code for finding C1 and C4 to find C0 and C3 + + float vertexAngleBeforeC0 = stepLength*(float)stepsBeforeC0; + float vertexAngleAfterC0 = stepLength*((float)(stepsBeforeC0 + 1)); + + p1 = (Vector2){ cosf(DEG2RAD*vertexAngleBeforeC0)*innerRadius, sinf(DEG2RAD*vertexAngleBeforeC0)*innerRadius }; + p2 = (Vector2){ cosf(DEG2RAD*vertexAngleAfterC0)*innerRadius, sinf(DEG2RAD*vertexAngleAfterC0)*innerRadius }; + + t = (p1.y - thick)/(p1.y - p2.y); + + vertexBeforeCap1Vertex = (Vector2){ center.x + cosf(DEG2RAD*(startAngle + vertexAngleBeforeC0))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + vertexAngleBeforeC0))*innerRadius }; + vertexAfterCap1Vertex = (Vector2){ center.x + cosf(DEG2RAD*(startAngle + vertexAngleAfterC0))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + vertexAngleAfterC0))*innerRadius }; + + c0.x = vertexBeforeCap1Vertex.x + (vertexAfterCap1Vertex.x - vertexBeforeCap1Vertex.x)*t; + c0.y = vertexBeforeCap1Vertex.y + (vertexAfterCap1Vertex.y - vertexBeforeCap1Vertex.y)*t; + + vertexBeforeCap2Vertex = (Vector2){ center.x + cosf(DEG2RAD*(endAngle - vertexAngleBeforeC0))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - vertexAngleBeforeC0))*innerRadius }; + vertexAfterCap2Vertex = (Vector2){ center.x + cosf(DEG2RAD*(endAngle - vertexAngleAfterC0))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - vertexAngleAfterC0))*innerRadius }; + + c3.x = vertexBeforeCap2Vertex.x + (vertexAfterCap2Vertex.x - vertexBeforeCap2Vertex.x)*t; + c3.y = vertexBeforeCap2Vertex.y + (vertexAfterCap2Vertex.y - vertexBeforeCap2Vertex.y)*t; + } + } + else + { + float outerAngleBetweenCapEnds = ((endAngle + 90.0f) - (startAngle - 90.0f))/2.0f; + float s1Length = thick/cosf(DEG2RAD*outerAngleBetweenCapEnds); + float betweenStartAndEndAngle = 180.0f + (endAngle + startAngle)/2.0f; + c0 = (Vector2){ center.x + cosf(DEG2RAD*betweenStartAndEndAngle)*s1Length, center.y + sinf(DEG2RAD*betweenStartAndEndAngle)*s1Length }; + c3 = c0; + + c2 = (Vector2){ center.x + cosf(DEG2RAD*startAngle)*outerRadius, center.y + sinf(DEG2RAD*startAngle)*outerRadius }; + c5 = (Vector2){ center.x + cosf(DEG2RAD*endAngle)*outerRadius, center.y + sinf(DEG2RAD*endAngle)*outerRadius }; + + // Change the frame of reference so that `center` is the origin and `startAngle` is 0 degrees + + Vector2 c0Translated = { c0.x - center.x, c0.y - center.y }; + Vector2 circleVertex1 = { c2.x - center.x, c2.y - center.y }; + Vector2 circleVertex2 = { cosf(DEG2RAD*(startAngle + stepLength))*outerRadius, sinf(DEG2RAD*(startAngle + stepLength))*outerRadius }; + + // Copied from "raymath.h" Vector2Rotate() + float tempX = c0Translated.x; + c0Translated.x = cosf(-DEG2RAD*startAngle)*tempX - sinf(-DEG2RAD*startAngle)*c0Translated.y; + c0Translated.y = sinf(-DEG2RAD*startAngle)*tempX + cosf(-DEG2RAD*startAngle)*c0Translated.y; + + tempX = circleVertex1.x; + circleVertex1.x = cosf(-DEG2RAD*startAngle)*tempX - sinf(-DEG2RAD*startAngle)*circleVertex1.y; + circleVertex1.y = sinf(-DEG2RAD*startAngle)*tempX + cosf(-DEG2RAD*startAngle)*circleVertex1.y; + + tempX = circleVertex2.x; + circleVertex2.x = cosf(-DEG2RAD*startAngle)*tempX - sinf(-DEG2RAD*startAngle)*circleVertex2.y; + circleVertex2.y = sinf(-DEG2RAD*startAngle)*tempX + cosf(-DEG2RAD*startAngle)*circleVertex2.y; + + // Figure out the line that `circleVertex1` and `circleVertex2` are on + float rise = circleVertex1.y - circleVertex2.y; + float run = circleVertex1.x - circleVertex2.x; + // Get where that line intersects the horizontal line that `c0Translated` is on + float c1Rise = c0Translated.y - circleVertex1.y; + float c1Run = (c1Rise/rise)*run; + float c1DistanceFromC0 = (circleVertex1.x + c1Run) - c0Translated.x; + + c1 = (Vector2){ c0.x + cosf(DEG2RAD*startAngle)*c1DistanceFromC0, c0.y + sinf(DEG2RAD*startAngle)*c1DistanceFromC0 }; + c4 = (Vector2){ c0.x + cosf(DEG2RAD*endAngle)*c1DistanceFromC0, c0.y + sinf(DEG2RAD*endAngle)*c1DistanceFromC0 }; + + if (c1DistanceFromC0 < 0.0f) + { + // The caps are intersecting each other + + Vector2 circleVertex3 = { c5.x - center.x, c5.y - center.y }; + Vector2 circleVertex4 = { cosf(DEG2RAD*(endAngle - stepLength))*outerRadius, sinf(DEG2RAD*(endAngle - stepLength))*outerRadius }; + + tempX = circleVertex3.x; + circleVertex3.x = cosf(-DEG2RAD*startAngle)*tempX - sinf(-DEG2RAD*startAngle)*circleVertex3.y; + circleVertex3.y = sinf(-DEG2RAD*startAngle)*tempX + cosf(-DEG2RAD*startAngle)*circleVertex3.y; + + tempX = circleVertex4.x; + circleVertex4.x = cosf(-DEG2RAD*startAngle)*tempX - sinf(-DEG2RAD*startAngle)*circleVertex4.y; + circleVertex4.y = sinf(-DEG2RAD*startAngle)*tempX + cosf(-DEG2RAD*startAngle)*circleVertex4.y; + + // `startAngle` is 0 degrees within this frame of reference, + // so C1 just goes horizontally out from C0 + Vector2 c1Translated = { c0Translated.x + c1DistanceFromC0, c0Translated.y }; + + // Make `circleVertex2` the origin + circleVertex1.x -= circleVertex2.x; + circleVertex1.y -= circleVertex2.y; + circleVertex3.x -= circleVertex2.x; + circleVertex3.y -= circleVertex2.y; + circleVertex4.x -= circleVertex2.x; + circleVertex4.y -= circleVertex2.y; + c1Translated.x -= circleVertex2.x; + c1Translated.y -= circleVertex2.y; + + // Make the line between `circleVertex1` and `circleVertex2` a horizontal line + float theta = atan2f(circleVertex1.y, circleVertex1.x); + + // Copied from "raymath.h" Vector2Rotate() + tempX = circleVertex1.x; + circleVertex1.x = cosf(-theta)*tempX - sinf(-theta)*circleVertex1.y; + circleVertex1.y = sinf(-theta)*tempX + cosf(-theta)*circleVertex1.y; + + tempX = circleVertex3.x; + circleVertex3.x = cosf(-theta)*tempX - sinf(-theta)*circleVertex3.y; + circleVertex3.y = sinf(-theta)*tempX + cosf(-theta)*circleVertex3.y; + + tempX = circleVertex4.x; + circleVertex4.x = cosf(-theta)*tempX - sinf(-theta)*circleVertex4.y; + circleVertex4.y = sinf(-theta)*tempX + cosf(-theta)*circleVertex4.y; + + tempX = c1Translated.x; + c1Translated.x = cosf(-theta)*tempX - sinf(-theta)*c1Translated.y; + c1Translated.y = sinf(-theta)*tempX + cosf(-theta)*c1Translated.y; + + // Find where the line that `circleVertex3` and `circleVertex4` are on would intersect the + // line segment defined by `circleVertex1` and `c1Translated` + rise = circleVertex3.y - circleVertex4.y; + run = circleVertex3.x - circleVertex4.x; + float targetRise = -circleVertex3.y; + float targetX = circleVertex3.x + (targetRise/rise)*run; + + float t = (c1Translated.x - targetX)/(c1Translated.x - circleVertex1.x); + + c1 = (Vector2){ c1.x + (c2.x - c1.x)*t, c1.y + (c2.y - c1.y)*t }; + c4 = c1; + c0 = c1; + c3 = c1; + } + + // Swap vertices to correct the winding order + Vector2 temp = c0; + c0 = c2; + c2 = temp; + + temp = c3; + c3 = c5; + c5 = temp; + } + } + +#if SUPPORT_QUADS_DRAW_MODE + rlSetTexture(GetShapesTexture().id); + Rectangle shapeRect = GetShapesTextureRectangle(); + + rlBegin(RL_QUADS); + + rlColor4ub(color.r, color.g, color.b, color.a); + + // Draw the circle outline + for (int i = 0; i < segments; i++) + { + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + + angle += stepLength; + } + + // Draw the caps + if (showCapLines) + { + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(c0.x, c0.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(c1.x, c1.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(c2.x, c2.y); + + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(c5.x, c5.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(c4.x, c4.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(c3.x, c3.y); + + // Some extra work may be needed when `thick` is positive + if (thick >= 0.0f) + { + // Fill in the gaps between cap 1 and the circle outline and cap 2 and the circle outline + if (stepsBeforeC1 > 0) + { + // Draw quads using pairs of vertices on the circle outline + angle = 0; + for (int i = 0; i < stepsBeforeC1/2; i++) + { + // Cap1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(c1.x, c1.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + (angle + stepLength*2.0f)))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + (angle + stepLength*2.0f)))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + (angle + stepLength)))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + (angle + stepLength)))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*innerRadius); + + // Cap2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(c4.x, c4.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - (angle + stepLength)))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - (angle + stepLength)))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - (angle + stepLength*2.0f)))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - (angle + stepLength*2.0f)))*innerRadius); + + angle += stepLength*2.0f; + } + + if (stepsBeforeC1%2 == 1) + { + // Cap1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(c1.x, c1.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(c1.x, c1.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + (angle + stepLength)))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + (angle + stepLength)))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*innerRadius); + + // Cap2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(c4.x, c4.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(c4.x, c4.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - (angle + stepLength)))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - (angle + stepLength)))*innerRadius); + } + } + + // Fill in the gap between C0, C3 and the circle outline + if (s1OutsideOfCircle) + { + int verticesBetweenC0andC3 = (segments - stepsBeforeC0*2) - 1; + + // No gap to fill + if (verticesBetweenC0andC3 == 0) + { + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(c3.x, c3.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(c0.x, c0.y); + } + // There's a gap to fill + else + { + // Triangle touching C0 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + stepLength*(float)(stepsBeforeC0 + 1)))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + stepLength*(float)(stepsBeforeC0 + 1)))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(c0.x, c0.y); + + // Triangle touching C3 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(c3.x, c3.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - stepLength*(float)(stepsBeforeC0 + 1)))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - stepLength*(float)(stepsBeforeC0 + 1)))*innerRadius); + + // Triangles between the previous two + verticesBetweenC0andC3 -= 1; + angle = startAngle + stepLength*(stepsBeforeC0 + 1); + for (int i = 0; i < verticesBetweenC0andC3/2; i++) + { + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + + angle += stepLength*2.0f; + } + + if (verticesBetweenC0andC3%2 == 1) + { + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + } + } + } + } + } + rlEnd(); + + rlSetTexture(0); +#else + rlBegin(RL_TRIANGLES); + + rlColor4ub(color.r, color.g, color.b, color.a); + + // Draw the circle outline + for (int i = 0; i < segments; i++) + { + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + + angle += stepLength; + } + + // Draw the caps + if (showCapLines) + { + // Cap 1 + rlVertex2f(center.x, center.y); + rlVertex2f(c0.x, c0.y); + rlVertex2f(c1.x, c1.y); + + rlVertex2f(center.x, center.y); + rlVertex2f(c1.x, c1.y); + rlVertex2f(c2.x, c2.y); + + // Cap 2 + rlVertex2f(center.x, center.y); + rlVertex2f(c5.x, c5.y); + rlVertex2f(c4.x, c4.y); + + rlVertex2f(center.x, center.y); + rlVertex2f(c4.x, c4.y); + rlVertex2f(c3.x, c3.y); + + // Some extra work may be needed when `thick` is positive + if (thick >= 0.0f) + { + // Fill in the gaps between cap 1 and the circle outline and cap 2 and the circle outline + if (stepsBeforeC1 > 0) + { + angle = 0; + for (int i = 0; i < stepsBeforeC1; i++) + { + // Cap 1 + rlVertex2f(c1.x, c1.y); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + (angle + stepLength)))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + (angle + stepLength)))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*innerRadius); + + // Cap 2 + rlVertex2f(c4.x, c4.y); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - (angle + stepLength)))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - (angle + stepLength)))*innerRadius); + + angle += stepLength; + } + } + + // Fill in the gap between C0, C3 and the circle outline + if (s1OutsideOfCircle) + { + int verticesBetweenC0andC3 = (segments - stepsBeforeC0*2) - 1; + + // No gap to fill + if (verticesBetweenC0andC3 == 0) + { + rlVertex2f(center.x, center.y); + rlVertex2f(c3.x, c3.y); + rlVertex2f(c0.x, c0.y); + } + // There's a gap to fill + else + { + // Triangle touching C0 + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + stepLength*(float)(stepsBeforeC0 + 1)))*innerRadius, center.y + sinf(DEG2RAD*(startAngle + stepLength*(float)(stepsBeforeC0 + 1)))*innerRadius); + rlVertex2f(c0.x, c0.y); + + // Triangle touching C3 + rlVertex2f(center.x, center.y); + rlVertex2f(c3.x, c3.y); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - stepLength*(float)(stepsBeforeC0 + 1)))*innerRadius, center.y + sinf(DEG2RAD*(endAngle - stepLength*(float)(stepsBeforeC0 + 1)))*innerRadius); + + // Triangles between the previous two + verticesBetweenC0andC3 -= 1; + angle = startAngle + stepLength*(stepsBeforeC0 + 1); + for (int i = 0; i < verticesBetweenC0andC3; i++) + { + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + + angle += stepLength; + } + } + } + } + } + + rlEnd(); +#endif +} + // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) { @@ -1690,6 +2582,76 @@ void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color rlEnd(); } +// Draw ellipse outline with thickness +void DrawEllipseLinesEx(Vector2 center, float radiusH, float radiusV, float thick, Color color) +{ + float outerRadiusH = radiusH, innerRadiusH = radiusH - thick; + float outerRadiusV = radiusV, innerRadiusV = radiusV - thick; + + if (thick >= 0.0f) { + // Just a filled-in ellipse + if (innerRadiusH <= 0.0f || innerRadiusV <= 0.0f) + { + DrawEllipseV(center, radiusH, radiusV, color); + return; + } + } + else + { + // The outline is growing outside of the ellipse, so swap the inner and outer radius + float tmp = outerRadiusH; + outerRadiusH = innerRadiusH; + innerRadiusH = tmp; + + tmp = outerRadiusV; + outerRadiusV = innerRadiusV; + innerRadiusV = tmp; + } + +#if SUPPORT_QUADS_DRAW_MODE + rlSetTexture(GetShapesTexture().id); + Rectangle shapeRect = GetShapesTextureRectangle(); + + rlBegin(RL_QUADS); + + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < 360; i += 10) + { + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*i)*innerRadiusH, center.y + sinf(DEG2RAD*i)*innerRadiusV); + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*innerRadiusH, center.y + sinf(DEG2RAD*(i + 10))*innerRadiusV); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*outerRadiusH, center.y + sinf(DEG2RAD*(i + 10))*outerRadiusV); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*i)*outerRadiusH, center.y + sinf(DEG2RAD*i)*outerRadiusV); + } + rlEnd(); + + rlSetTexture(0); +#else + rlBegin(RL_TRIANGLES); + + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < 360; i += 10) + { + rlVertex2f(center.x + cosf(DEG2RAD*i)*innerRadiusH, center.y + sinf(DEG2RAD*i)*innerRadiusV); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*innerRadiusH, center.y + sinf(DEG2RAD*(i + 10))*innerRadiusV); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*outerRadiusH, center.y + sinf(DEG2RAD*(i + 10))*outerRadiusV); + + rlVertex2f(center.x + cosf(DEG2RAD*i)*innerRadiusH, center.y + sinf(DEG2RAD*i)*innerRadiusV); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*outerRadiusH, center.y + sinf(DEG2RAD*(i + 10))*outerRadiusV); + rlVertex2f(center.x + cosf(DEG2RAD*i)*outerRadiusH, center.y + sinf(DEG2RAD*i)*outerRadiusV); + } + rlEnd(); +#endif +} + // Draw ring void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color) { @@ -1866,6 +2828,886 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s rlEnd(); } +// Draw ring outline with line thickness +void DrawRingLinesEx(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, float thick, Color color) +{ + if (startAngle == endAngle) return; + + // Function expects (outerRadius > innerRadius) + if (outerRadius < innerRadius) + { + float tmp = outerRadius; + outerRadius = innerRadius; + innerRadius = tmp; + + if (outerRadius <= 0.0f) outerRadius = 0.1f; + } + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + float tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + bool showCapLines = true; + // Drawing a whole circle, things get weird without limiting the circle to 360 degrees + if (endAngle - startAngle >= 360.0f) + { + showCapLines = thick >= 0.0f; + endAngle = startAngle + 360.0f; + } + + 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/outerRadius, 2) - 1); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); + + if (segments <= 0) segments = minSegments; + } + + float stepLength = (endAngle - startAngle)/(float)segments; + + // We are not drawing a circle, we are drawing an n-sided polygon + // So, we need to adjust the outline thickness of the "circle" for it to look correct with fewer segments + float apothem = outerRadius*cosf(DEG2RAD*((endAngle - startAngle)/2.0f)/(float)segments); + float radiusThick = thick*(outerRadius/apothem); + + // These names can be confusing, but they are useful + // Since 2 rings are being drawn, there are 4 radiuses (or radii) + // "Inner" means closer to the center, "outer" means farther from the center + // Sorted from farthest to closest you get: + // 1. outerOuterRadius (farthest) + // 2. innerOuterRadius + // 3. outerInnerRadius + // 4. innerInnerRadius (closest) + float innerOuterRadius = 0.0f; + float outerOuterRadius = 0.0f; + float innerInnerRadius = 0.0f; + float outerInnerRadius = 0.0f; + + if (thick >= 0.0f) + { + innerRadius = fmaxf(0.0f, innerRadius); + + // Just a filled-in ring + if (radiusThick > (outerRadius - innerRadius)/2.0f) + { + DrawRing(center, innerRadius, outerRadius, startAngle, endAngle, segments, color); + return; + } + + innerInnerRadius = innerRadius; + outerInnerRadius = innerInnerRadius + radiusThick; + + outerOuterRadius = outerRadius; + innerOuterRadius = outerOuterRadius - radiusThick; + } + else + { + // Just a circle sector outline + if (innerRadius <= 0.0f) + { + DrawCircleSectorLinesEx(center, outerRadius, startAngle, endAngle, segments, thick, color); + return; + } + + outerInnerRadius = innerRadius; + innerInnerRadius = fmaxf(0.0f, outerInnerRadius + radiusThick); + + innerOuterRadius = outerRadius; + outerOuterRadius = innerOuterRadius - radiusThick; + } + + // For positive `thick` values + int stepsBeforeInner = 0; + int stepsBeforeOuter = 0; + float tInner = 0.0f; + float tOuter = 0.0f; + bool innerAnglesCrossEachOther = false; + + // For negative `thick` values + Vector2 cap1SecondInnerVertex = { 0 }; + Vector2 cap1SecondOuterVertex = { 0 }; + Vector2 cap2SecondInnerVertex = { 0 }; + Vector2 cap2SecondOuterVertex = { 0 }; + bool capsIntersect = false; + Vector2 capIntersectionVertex = { 0 }; + + if (showCapLines) + { + if (thick >= 0.0f) + { + // Get the angle of the arc that has `thick` length along the inner and outer radii + float cap1InnerAngleEnd = RAD2DEG*(thick/outerInnerRadius); + float cap1OuterAngleEnd = RAD2DEG*(thick/innerOuterRadius); + + // Just a filled-in ring + if (endAngle - startAngle < cap1OuterAngleEnd*2.0f) + { + DrawRing(center, innerRadius, outerRadius, startAngle, endAngle, segments, color); + return; + } + + if (endAngle - startAngle < cap1InnerAngleEnd*2.0f) innerAnglesCrossEachOther = true; + + stepsBeforeInner = (int)(cap1InnerAngleEnd/stepLength); + stepsBeforeOuter = (int)(cap1OuterAngleEnd/stepLength); + + // We need to find where `cap1InnerAngleEnd` intersects the edge defined + // by `beforeInnerVertex` and `afterInnerVertex` + // + // We can make this easy by making `center` the origin (0, 0) and + // making `cap1InnerAngleEnd` 0 degrees (a horizontal line) + // + // With that, we know these lines intersect when 'y' equals 0, + // so we just need to solve for 't' (as in `Lerp(start, end, t)`) + Vector2 beforeInnerVertex = { cosf(DEG2RAD*((float)stepsBeforeInner*stepLength - cap1InnerAngleEnd))*outerInnerRadius, sinf(DEG2RAD*((float)stepsBeforeInner*stepLength - cap1InnerAngleEnd))*outerInnerRadius }; + Vector2 afterInnerVertex = { cosf(DEG2RAD*((float)(stepsBeforeInner + 1)*stepLength - cap1InnerAngleEnd))*outerInnerRadius, sinf(DEG2RAD*((float)(stepsBeforeInner + 1)*stepLength - cap1InnerAngleEnd))*outerInnerRadius }; + tInner = beforeInnerVertex.y/(beforeInnerVertex.y - afterInnerVertex.y); + + // The same as above, but for the outer edge + Vector2 beforeOuterVertex = { cosf(DEG2RAD*((float)stepsBeforeOuter*stepLength - cap1OuterAngleEnd))*innerOuterRadius, sinf(DEG2RAD*((float)stepsBeforeOuter*stepLength - cap1OuterAngleEnd))*innerOuterRadius }; + Vector2 afterOuterVertex = { cosf(DEG2RAD*((float)(stepsBeforeOuter + 1)*stepLength - cap1OuterAngleEnd))*innerOuterRadius, sinf(DEG2RAD*((float)(stepsBeforeOuter + 1)*stepLength - cap1OuterAngleEnd))*innerOuterRadius }; + tOuter = beforeOuterVertex.y/(beforeOuterVertex.y - afterOuterVertex.y); + } + else + { + // "Cap 1" is the outline on `startAngle` and "Cap 2" is the outline on `endAngle` + + /* + A sketch to help make all this a little more understandable + (This is an overly simplified representation of cap 1) + + I2[][][][][]O2 <- y = thick + [] [] + [] [] + I0----------O0 <- angle = 0 degrees, y = 0 + [] [] + I1 O1 <- angle = stepLength + + Cap 2 is a mirror copy of cap 1, the inside and outside vertices switch sides + + We're using a frame of reference where `center` is (0, 0) and `startAngle` is 0 degrees + + I0 is `innerInnerRadius` distance from `center` at `starAngle` + I1 is `innerInnerRadius` distance from `center` at `starAngle + stepLength` + I2 goes out from I0 perpendicular to `startAngle` + O0 is the same as I0, except using `outerOuterRadius` instead of `innerInnerRadius` + O1 is the same as I1, except using `outerOuterRadius` instead of `innerInnerRadius` + O2 is the same as I2, except goes out from O0 + + The intersection cases between the caps edges are: + 1. No intersections, easy + 2. The I0->I2 and I2->O2 edges intersect between the caps + 3. The I0->I2 and O0->O2 edges intersect between the caps + + Notice that cap 1 and 2's I2->O2 and O0->O2 edges can't intersect at the same time, + and, if there's any intersection, I0->I2 is one of the edges + */ + + Vector2 cap1O0 = { outerOuterRadius, 0.0f }; + Vector2 cap1O1 = { cosf(DEG2RAD*stepLength)*outerOuterRadius, sinf(DEG2RAD*stepLength)*outerOuterRadius }; + + // Assuming a linear interpolation such as `value = Lerp(start, end, t)` + // We can find O2 by getting its 't' between O1.y and O0.y (which is always greater than 1) + // We can solve for `t` using `t = (start - value)/(start - end)` + // Since we know `end = 0` we can simplify it to `t = (start - value)/start` + float tOuter = (cap1O1.y - thick)/cap1O1.y; + Vector2 cap1O2 = { cap1O1.x + (cap1O0.x - cap1O1.x)*tOuter, thick }; + + Vector2 cap1I0 = { innerInnerRadius, 0.0f }; + + float capLongEdgeLength = outerOuterRadius - innerInnerRadius; + Vector2 cap1I2 = { cap1O2.x - capLongEdgeLength, thick }; + + Vector2 cap2O0 = { cosf(DEG2RAD*(endAngle - startAngle))*outerOuterRadius, sinf(DEG2RAD*(endAngle - startAngle))*outerOuterRadius }; + Vector2 cap2O1 = { cosf(DEG2RAD*(endAngle - startAngle - stepLength))*outerOuterRadius, sinf(DEG2RAD*(endAngle - startAngle - stepLength))*outerOuterRadius }; + Vector2 cap2O2 = { cap2O1.x + (cap2O0.x - cap2O1.x)*tOuter, cap2O1.y + (cap2O0.y - cap2O1.y)*tOuter }; + + Vector2 cap2I0 = { cosf(DEG2RAD*(endAngle - startAngle))*innerInnerRadius, sinf(DEG2RAD*(endAngle - startAngle))*innerInnerRadius }; + Vector2 cap2I2 = { cap2O2.x - cosf(DEG2RAD*(endAngle - startAngle))*capLongEdgeLength, cap2O2.y - sinf(DEG2RAD*(endAngle - startAngle))*capLongEdgeLength}; + + // The 't' of the intersection between I2 and O2 (`Lerp(I2, O2, t)`) + float tCapLongEdgeCross = -1.0f; + // Avoid division by zero + if (cap2I2.y - cap2O2.y != 0.0f) + { + // Find where the long edge of cap 2 intersects the long edge of cap 1 + tCapLongEdgeCross = (cap2I2.y - thick)/(cap2I2.y - cap2O2.y); + if ((tCapLongEdgeCross >= 0.0f) && (tCapLongEdgeCross <= 1.0f)) capsIntersect = true; + } + + // Rotate the frame of reference so that cap 1's I0->I2 edge is a vertical line + float rotateBy = -DEG2RAD*stepLength/2.0f; + + // Copied from "raymath.h" Vector2Rotate() + // Though we only use the x axis, so we ignore the y axis + float cosres = cosf(rotateBy); + float sinres = sinf(rotateBy); + + cap1I2.x = cap1I2.x*cosres - cap1I2.y*sinres; + cap1O2.x = cap1O2.x*cosres - cap1O2.y*sinres; + cap2I0.x = cap2I0.x*cosres - cap2I0.y*sinres; + cap2I2.x = cap2I2.x*cosres - cap2I2.y*sinres; + cap2O0.x = cap2O0.x*cosres - cap2O0.y*sinres; + cap2O2.x = cap2O2.x*cosres - cap2O2.y*sinres; + + // The 't' of the intersection between I0 and I2 (`Lerp(I0, I2, t)`) + float tCrossInner = -1.0f; + // Avoid division by zero + if (cap2I0.x - cap2I2.x != 0.0f) tCrossInner = (cap2I0.x - cap1I2.x)/(cap2I0.x - cap2I2.x); + // Make sure `tCrossInner` is 0 when it should be (mitigate floating-point rounding woes) + if (innerInnerRadius <= 0.0f) tCrossInner = 0.0f; + + // The 't' of the intersection between O0 and O2 (`Lerp(O0, O2, t)`) + float tCrossOuter = -1.0f; + // Avoid division by zero + if (cap2O0.x - cap2O2.x != 0.0f) tCrossOuter = (cap2O0.x - cap1O2.x)/(cap2O0.x - cap2O2.x); + + // With our additional information, calculate the vertices we need + // outside of our modified frame of reference + + cap1O0 = (Vector2){ center.x + cosf(DEG2RAD*startAngle)*outerOuterRadius, center.y + sinf(DEG2RAD*startAngle)*outerOuterRadius }; + cap1O1 = (Vector2){ center.x + cosf(DEG2RAD*(startAngle + stepLength))*outerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + stepLength))*outerOuterRadius }; + cap1O2 = (Vector2){ cap1O1.x + (cap1O0.x - cap1O1.x)*tOuter, cap1O1.y + (cap1O0.y - cap1O1.y)*tOuter }; + + cap2O0 = (Vector2){ center.x + cosf(DEG2RAD*endAngle)*outerOuterRadius, center.y + sinf(DEG2RAD*endAngle)*outerOuterRadius }; + cap2O1 = (Vector2){ center.x + cosf(DEG2RAD*(endAngle - stepLength))*outerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - stepLength))*outerOuterRadius }; + cap2O2 = (Vector2){ cap2O1.x + (cap2O0.x - cap2O1.x)*tOuter, cap2O1.y + (cap2O0.y - cap2O1.y)*tOuter }; + + cap1I0 = (Vector2){ center.x + cosf(DEG2RAD*startAngle)*innerInnerRadius, center.y + sinf(DEG2RAD*startAngle)*innerInnerRadius }; + cap1I2 = (Vector2){ cap1O2.x - cosf(DEG2RAD*startAngle)*capLongEdgeLength, cap1O2.y - sinf(DEG2RAD*startAngle)*capLongEdgeLength }; + + cap2I0 = (Vector2){ center.x + cosf(DEG2RAD*endAngle)*innerInnerRadius, center.y + sinf(DEG2RAD*endAngle)*innerInnerRadius }; + cap2I2 = (Vector2){ cap2O2.x - cosf(DEG2RAD*endAngle)*capLongEdgeLength, cap2O2.y - sinf(DEG2RAD*endAngle)*capLongEdgeLength }; + + if (capsIntersect) + { + capIntersectionVertex = (Vector2){ cap2I2.x + (cap2O2.x - cap2I2.x)*tCapLongEdgeCross, cap2I2.y + (cap2O2.y - cap2I2.y)*tCapLongEdgeCross }; + + cap2I2 = (Vector2){ cap2I0.x + (cap2I2.x - cap2I0.x)*tCrossInner, cap2I0.y + (cap2I2.y - cap2I0.y)*tCrossInner }; + cap1I2 = cap2I2; + } + else if ((tCrossOuter >= 0.0f) && (tCrossOuter <= 1.0f)) + { + cap2O2 = (Vector2){ cap2O0.x + (cap2O2.x - cap2O0.x)*tCrossOuter, cap2O0.y + (cap2O2.y - cap2O0.y)*tCrossOuter }; + cap1O2 = cap2O2; + + cap2I2 = (Vector2){ cap2I0.x + (cap2I2.x - cap2I0.x)*tCrossInner, cap2I0.y + (cap2I2.y - cap2I0.y)*tCrossInner }; + cap1I2 = cap2I2; + } + + cap1SecondInnerVertex = cap1I2; + cap1SecondOuterVertex = cap1O2; + cap2SecondInnerVertex = cap2I2; + cap2SecondOuterVertex = cap2O2; + } + } + + float angle = startAngle; + +#if SUPPORT_QUADS_DRAW_MODE + rlSetTexture(GetShapesTexture().id); + Rectangle shapeRect = GetShapesTextureRectangle(); + + rlBegin(RL_QUADS); + + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < segments; i++) + { + // `innerRadius` outline + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerInnerRadius, center.y + sinf(DEG2RAD*angle)*outerInnerRadius); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerInnerRadius, center.y + sinf(DEG2RAD*angle)*innerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerInnerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerInnerRadius); + + // `outerRadius` outline + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerOuterRadius, center.y + sinf(DEG2RAD*angle)*outerOuterRadius); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerOuterRadius, center.y + sinf(DEG2RAD*angle)*innerOuterRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerOuterRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerOuterRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerOuterRadius); + + angle += stepLength; + } + + if (showCapLines) + { + if (thick >= 0.0f) + { + angle = 0.0f; + + for (int i = 0; i < stepsBeforeOuter; i++) + { + // Cap 1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*innerOuterRadius); + + // Cap 2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*outerInnerRadius); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*innerOuterRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius); + + angle += stepLength; + } + + // We've already moved `stepsBeforeOuter` steps from each end + int totalStepsLeft = segments - stepsBeforeOuter*2; + int innerStepsLeft = stepsBeforeInner - stepsBeforeOuter; + + // Cap 1 + Vector2 cap1OuterVertexBeforeEnd = { center.x + cosf(DEG2RAD*(startAngle + angle))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*innerOuterRadius }; + Vector2 cap1OuterVertexAfterEnd = { center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius }; + Vector2 cap1InnerVertexBeforeEnd = { center.x + cosf(DEG2RAD*(startAngle + angle + (float)innerStepsLeft*stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + (float)innerStepsLeft*stepLength))*outerInnerRadius }; + Vector2 cap1InnerVertexAfterEnd = { center.x + cosf(DEG2RAD*(startAngle + angle + (float)(innerStepsLeft + 1)*stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + (float)(innerStepsLeft + 1)*stepLength))*outerInnerRadius }; + Vector2 cap1InnerVertexEnd = { cap1InnerVertexBeforeEnd.x + (cap1InnerVertexAfterEnd.x - cap1InnerVertexBeforeEnd.x)*tInner, cap1InnerVertexBeforeEnd.y + (cap1InnerVertexAfterEnd.y - cap1InnerVertexBeforeEnd.y)*tInner }; + Vector2 cap1OuterVertexEnd = { cap1OuterVertexBeforeEnd.x + (cap1OuterVertexAfterEnd.x - cap1OuterVertexBeforeEnd.x)*tOuter, cap1OuterVertexBeforeEnd.y + (cap1OuterVertexAfterEnd.y - cap1OuterVertexBeforeEnd.y)*tOuter }; + + // Cap 2 + Vector2 cap2OuterVertexBeforeEnd = { center.x + cosf(DEG2RAD*(endAngle - angle))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*innerOuterRadius }; + Vector2 cap2OuterVertexAfterEnd = { center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius }; + Vector2 cap2InnerVertexBeforeEnd = { center.x + cosf(DEG2RAD*(endAngle - angle - (float)innerStepsLeft*stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - (float)innerStepsLeft*stepLength))*outerInnerRadius }; + Vector2 cap2InnerVertexAfterEnd = { center.x + cosf(DEG2RAD*(endAngle - angle - (float)(innerStepsLeft + 1)*stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - (float)(innerStepsLeft + 1)*stepLength))*outerInnerRadius }; + Vector2 cap2InnerVertexEnd = { cap2InnerVertexBeforeEnd.x + (cap2InnerVertexAfterEnd.x - cap2InnerVertexBeforeEnd.x)*tInner, cap2InnerVertexBeforeEnd.y + (cap2InnerVertexAfterEnd.y - cap2InnerVertexBeforeEnd.y)*tInner }; + Vector2 cap2OuterVertexEnd = { cap2OuterVertexBeforeEnd.x + (cap2OuterVertexAfterEnd.x - cap2OuterVertexBeforeEnd.x)*tOuter, cap2OuterVertexBeforeEnd.y + (cap2OuterVertexAfterEnd.y - cap2OuterVertexBeforeEnd.y)*tOuter }; + + int stepsCount = (innerAnglesCrossEachOther)? totalStepsLeft/2 : innerStepsLeft; + + // Iterate over pairs of steps + for (int i = 0; i < stepsCount/2; i++) + { + // Cap 1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength*2.0f))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength*2.0f))*outerInnerRadius); + + // Cap 2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength*2.0f))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength*2.0f))*outerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*outerInnerRadius); + + angle += stepLength*2.0f; + } + + // Handle the last step if there's an odd amount + if (stepsCount%2 == 1) + { + // Cap 1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius); + + // Cap 2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*outerInnerRadius); + + angle += stepLength; + } + + // When the inner angles coming from `startAngle` and `endAngle` cross each other, + // the `*innerVertexEnd` vertices go past each other and cause the geometry to intersect itself + if (innerAnglesCrossEachOther) + { + // We need to find where the line defined by `cap1InnerVertexEnd` and `cap1OuterVertexEnd` intersects + // the line defined by `cap2InnerVertexEnd` and `cap2OuterVertexEnd` + // That point is then used instead to prevent the outline from intersecting itself + + // Make `cap1InnerVertexEnd` the origin and the angle to `cap1OuterVertexEnd` 0 degrees + Vector2 tempCap1OuterVertexEnd = { cap1OuterVertexEnd.x - cap1InnerVertexEnd.x, cap1OuterVertexEnd.y - cap1InnerVertexEnd.y }; + Vector2 tempCap2InnerVertexEnd = { cap2InnerVertexEnd.x - cap1InnerVertexEnd.x, cap2InnerVertexEnd.y - cap1InnerVertexEnd.y }; + Vector2 tempCap2OuterVertexEnd = { cap2OuterVertexEnd.x - cap1InnerVertexEnd.x, cap2OuterVertexEnd.y - cap1InnerVertexEnd.y }; + + float rotateBy = -atan2f(tempCap1OuterVertexEnd.y, tempCap1OuterVertexEnd.x); + // We only need the y coordinates, so only rotate the y coordinates + float start = sinf(rotateBy)*tempCap2InnerVertexEnd.x + cosf(rotateBy)*tempCap2InnerVertexEnd.y; + float end = sinf(rotateBy)*tempCap2OuterVertexEnd.x + cosf(rotateBy)*tempCap2OuterVertexEnd.y; + float tCross = start/(start - end); + + Vector2 intersection = { cap2InnerVertexEnd.x + (cap2OuterVertexEnd.x - cap2InnerVertexEnd.x)*tCross, cap2InnerVertexEnd.y + (cap2OuterVertexEnd.y - cap2InnerVertexEnd.y)*tCross }; + + if (segments%2 == 0) + { + // There are an even number of segments, so there's 1 vertex exactly in the middle + + Vector2 middleInnerVertex = { center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius }; + + // Cap 1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(intersection.x, intersection.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1OuterVertexEnd.x, cap1OuterVertexEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(middleInnerVertex.x, middleInnerVertex.y); + + // Cap 2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(intersection.x, intersection.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(middleInnerVertex.x, middleInnerVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2OuterVertexEnd.x, cap2OuterVertexEnd.y); + } + else + { + // There are an odd number of segments, so there are 2 vertices in the middle + + Vector2 middleInnerVertex1 = { center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius }; + Vector2 middleInnerVertex2 = { center.x + cosf(DEG2RAD*(endAngle - angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*outerInnerRadius }; + + // Cap 1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(intersection.x, intersection.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1OuterVertexEnd.x, cap1OuterVertexEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(middleInnerVertex1.x, middleInnerVertex1.y); + + // Cap 2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(intersection.x, intersection.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(middleInnerVertex2.x, middleInnerVertex2.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2OuterVertexEnd.x, cap2OuterVertexEnd.y); + + // Triangle between the caps + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(intersection.x, intersection.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(intersection.x, intersection.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(middleInnerVertex1.x, middleInnerVertex1.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(middleInnerVertex2.x, middleInnerVertex2.y); + } + } + else + { + // Cap 1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1InnerVertexBeforeEnd.x, cap1InnerVertexBeforeEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1InnerVertexEnd.x, cap1InnerVertexEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap1OuterVertexEnd.x, cap1OuterVertexEnd.y); + + // Cap 2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap2OuterVertexEnd.x, cap2OuterVertexEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap2InnerVertexEnd.x, cap2InnerVertexEnd.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2InnerVertexBeforeEnd.x, cap2InnerVertexBeforeEnd.y); + } + } + else + { + // Cap 1 + Vector2 cap1FirstInnerVertex = { center.x + cosf(DEG2RAD*startAngle)*innerInnerRadius, center.y + sinf(DEG2RAD*startAngle)*innerInnerRadius }; + Vector2 cap1FirstOuterVertex = { center.x + cosf(DEG2RAD*startAngle)*outerOuterRadius, center.y + sinf(DEG2RAD*startAngle)*outerOuterRadius }; + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap1FirstInnerVertex.x, cap1FirstInnerVertex.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1FirstOuterVertex.x, cap1FirstOuterVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1SecondOuterVertex.x, cap1SecondOuterVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap1SecondInnerVertex.x, cap1SecondInnerVertex.y); + + // Cap 2 + Vector2 cap2FirstInnerVertex = { center.x + cosf(DEG2RAD*endAngle)*innerInnerRadius, center.y + sinf(DEG2RAD*endAngle)*innerInnerRadius }; + Vector2 cap2FirstOuterVertex = { center.x + cosf(DEG2RAD*endAngle)*outerOuterRadius, center.y + sinf(DEG2RAD*endAngle)*outerOuterRadius }; + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2FirstInnerVertex.x, cap2FirstInnerVertex.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap2SecondInnerVertex.x, cap2SecondInnerVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap2SecondOuterVertex.x, cap2SecondOuterVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2FirstOuterVertex.x, cap2FirstOuterVertex.y); + + if (capsIntersect) + { + // Cap 1 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap1SecondInnerVertex.x, cap1SecondInnerVertex.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1SecondInnerVertex.x, cap1SecondInnerVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap1SecondOuterVertex.x, cap1SecondOuterVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(capIntersectionVertex.x, capIntersectionVertex.y); + + // Cap 2 + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2SecondInnerVertex.x, cap2SecondInnerVertex.y); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(capIntersectionVertex.x, capIntersectionVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(cap2SecondOuterVertex.x, cap2SecondOuterVertex.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(cap2SecondInnerVertex.x, cap2SecondInnerVertex.y); + } + } + } + rlEnd(); +#else + rlBegin(RL_TRIANGLES); + + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < segments; i++) + { + // `innerRadius` outline + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerInnerRadius, center.y + sinf(DEG2RAD*angle)*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerInnerRadius, center.y + sinf(DEG2RAD*angle)*innerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerInnerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerInnerRadius); + + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerInnerRadius, center.y + sinf(DEG2RAD*angle)*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerInnerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerInnerRadius); + + // `outerRadius` outline + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerOuterRadius, center.y + sinf(DEG2RAD*angle)*outerOuterRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerOuterRadius, center.y + sinf(DEG2RAD*angle)*innerOuterRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerOuterRadius); + + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerOuterRadius, center.y + sinf(DEG2RAD*angle)*outerOuterRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerOuterRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerOuterRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerOuterRadius); + + angle += stepLength; + } + + if (showCapLines) + { + if (thick >= 0.0f) + { + angle = 0.0f; + + for (int i = 0; i < stepsBeforeOuter; i++) + { + // Cap 1 + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius); + + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*innerOuterRadius); + + // Cap 2 + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*innerOuterRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius); + + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius); + + angle += stepLength; + } + + // We've already moved `stepsBeforeOuter` steps from each end + int totalStepsLeft = segments - stepsBeforeOuter*2; + int innerStepsLeft = stepsBeforeInner - stepsBeforeOuter; + + // Cap 1 + Vector2 cap1OuterVertexBeforeEnd = { center.x + cosf(DEG2RAD*(startAngle + angle))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*innerOuterRadius }; + Vector2 cap1OuterVertexAfterEnd = { center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*innerOuterRadius }; + Vector2 cap1InnerVertexBeforeEnd = { center.x + cosf(DEG2RAD*(startAngle + angle + (float)innerStepsLeft*stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + (float)innerStepsLeft*stepLength))*outerInnerRadius }; + Vector2 cap1InnerVertexAfterEnd = { center.x + cosf(DEG2RAD*(startAngle + angle + (float)(innerStepsLeft + 1)*stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + (float)(innerStepsLeft + 1)*stepLength))*outerInnerRadius }; + Vector2 cap1InnerVertexEnd = { cap1InnerVertexBeforeEnd.x + (cap1InnerVertexAfterEnd.x - cap1InnerVertexBeforeEnd.x)*tInner, cap1InnerVertexBeforeEnd.y + (cap1InnerVertexAfterEnd.y - cap1InnerVertexBeforeEnd.y)*tInner }; + Vector2 cap1OuterVertexEnd = { cap1OuterVertexBeforeEnd.x + (cap1OuterVertexAfterEnd.x - cap1OuterVertexBeforeEnd.x)*tOuter, cap1OuterVertexBeforeEnd.y + (cap1OuterVertexAfterEnd.y - cap1OuterVertexBeforeEnd.y)*tOuter }; + + // Cap 2 + Vector2 cap2OuterVertexBeforeEnd = { center.x + cosf(DEG2RAD*(endAngle - angle))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*innerOuterRadius }; + Vector2 cap2OuterVertexAfterEnd = { center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*innerOuterRadius }; + Vector2 cap2InnerVertexBeforeEnd = { center.x + cosf(DEG2RAD*(endAngle - angle - (float)innerStepsLeft*stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - (float)innerStepsLeft*stepLength))*outerInnerRadius }; + Vector2 cap2InnerVertexAfterEnd = { center.x + cosf(DEG2RAD*(endAngle - angle - (float)(innerStepsLeft + 1)*stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - (float)(innerStepsLeft + 1)*stepLength))*outerInnerRadius }; + Vector2 cap2InnerVertexEnd = { cap2InnerVertexBeforeEnd.x + (cap2InnerVertexAfterEnd.x - cap2InnerVertexBeforeEnd.x)*tInner, cap2InnerVertexBeforeEnd.y + (cap2InnerVertexAfterEnd.y - cap2InnerVertexBeforeEnd.y)*tInner }; + Vector2 cap2OuterVertexEnd = { cap2OuterVertexBeforeEnd.x + (cap2OuterVertexAfterEnd.x - cap2OuterVertexBeforeEnd.x)*tOuter, cap2OuterVertexBeforeEnd.y + (cap2OuterVertexAfterEnd.y - cap2OuterVertexBeforeEnd.y)*tOuter }; + + int stepsCount = (innerAnglesCrossEachOther)? totalStepsLeft/2 : innerStepsLeft; + + for (int i = 0; i < stepsCount; i++) + { + // Cap 1 + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle + stepLength))*outerInnerRadius); + + // Cap 2 + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle - stepLength))*outerInnerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(endAngle - angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*outerInnerRadius); + + angle += stepLength; + } + + // When the inner angles coming from `startAngle` and `endAngle` cross each other, + // the `*innerVertexEnd` vertices go past each other and cause the geometry to intersect itself + if (innerAnglesCrossEachOther) + { + // We need to find where the line defined by `cap1InnerVertexEnd` and `cap1OuterVertexEnd` intersects + // the line defined by `cap2InnerVertexEnd` and `cap2OuterVertexEnd` + // That point is then used instead to prevent the outline from intersecting itself + + // Make `cap1InnerVertexEnd` the origin and the angle to `cap1OuterVertexEnd` 0 degrees + Vector2 tempCap1OuterVertexEnd = { cap1OuterVertexEnd.x - cap1InnerVertexEnd.x, cap1OuterVertexEnd.y - cap1InnerVertexEnd.y }; + Vector2 tempCap2InnerVertexEnd = { cap2InnerVertexEnd.x - cap1InnerVertexEnd.x, cap2InnerVertexEnd.y - cap1InnerVertexEnd.y }; + Vector2 tempCap2OuterVertexEnd = { cap2OuterVertexEnd.x - cap1InnerVertexEnd.x, cap2OuterVertexEnd.y - cap1InnerVertexEnd.y }; + + float rotateBy = -atan2f(tempCap1OuterVertexEnd.y, tempCap1OuterVertexEnd.x); + // We only need the y coordinates, so only rotate the y coordinates + float start = sinf(rotateBy)*tempCap2InnerVertexEnd.x + cosf(rotateBy)*tempCap2InnerVertexEnd.y; + float end = sinf(rotateBy)*tempCap2OuterVertexEnd.x + cosf(rotateBy)*tempCap2OuterVertexEnd.y; + float tCross = start/(start - end); + + Vector2 intersection = { cap2InnerVertexEnd.x + (cap2OuterVertexEnd.x - cap2InnerVertexEnd.x)*tCross, cap2InnerVertexEnd.y + (cap2OuterVertexEnd.y - cap2InnerVertexEnd.y)*tCross }; + + if (segments%2 == 0) + { + // There are an even number of segments, so there's 1 vertex exactly in the middle + + Vector2 middleInnerVertex = { center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius }; + + // Cap 1 + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(cap1OuterVertexEnd.x, cap1OuterVertexEnd.y); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + rlVertex2f(middleInnerVertex.x, middleInnerVertex.y); + + // Cap 2 + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(middleInnerVertex.x, middleInnerVertex.y); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + rlVertex2f(cap2OuterVertexEnd.x, cap2OuterVertexEnd.y); + } + else + { + // There are an odd number of segments, so there are 2 vertices in the middle + + Vector2 middleInnerVertex1 = { center.x + cosf(DEG2RAD*(startAngle + angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(startAngle + angle))*outerInnerRadius }; + Vector2 middleInnerVertex2 = { center.x + cosf(DEG2RAD*(endAngle - angle))*outerInnerRadius, center.y + sinf(DEG2RAD*(endAngle - angle))*outerInnerRadius }; + + // Cap 1 + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(cap1OuterVertexEnd.x, cap1OuterVertexEnd.y); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + rlVertex2f(middleInnerVertex1.x, middleInnerVertex1.y); + + // Cap 2 + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(middleInnerVertex2.x, middleInnerVertex2.y); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + rlVertex2f(cap2OuterVertexEnd.x, cap2OuterVertexEnd.y); + + // Triangle between the caps + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(middleInnerVertex1.x, middleInnerVertex1.y); + + rlVertex2f(intersection.x, intersection.y); + rlVertex2f(middleInnerVertex1.x, middleInnerVertex1.y); + rlVertex2f(middleInnerVertex2.x, middleInnerVertex2.y); + } + } + else + { + // Cap 1 + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + rlVertex2f(cap1InnerVertexBeforeEnd.x, cap1InnerVertexBeforeEnd.y); + rlVertex2f(cap1InnerVertexEnd.x, cap1InnerVertexEnd.y); + + rlVertex2f(cap1OuterVertexBeforeEnd.x, cap1OuterVertexBeforeEnd.y); + rlVertex2f(cap1InnerVertexEnd.x, cap1InnerVertexEnd.y); + rlVertex2f(cap1OuterVertexEnd.x, cap1OuterVertexEnd.y); + + // Cap 2 + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + rlVertex2f(cap2OuterVertexEnd.x, cap2OuterVertexEnd.y); + rlVertex2f(cap2InnerVertexEnd.x, cap2InnerVertexEnd.y); + + rlVertex2f(cap2OuterVertexBeforeEnd.x, cap2OuterVertexBeforeEnd.y); + rlVertex2f(cap2InnerVertexEnd.x, cap2InnerVertexEnd.y); + rlVertex2f(cap2InnerVertexBeforeEnd.x, cap2InnerVertexBeforeEnd.y); + } + } + else + { + // Cap 1 + Vector2 cap1FirstInnerVertex = { center.x + cosf(DEG2RAD*startAngle)*innerInnerRadius, center.y + sinf(DEG2RAD*startAngle)*innerInnerRadius }; + Vector2 cap1FirstOuterVertex = { center.x + cosf(DEG2RAD*startAngle)*outerOuterRadius, center.y + sinf(DEG2RAD*startAngle)*outerOuterRadius }; + + rlVertex2f(cap1FirstInnerVertex.x, cap1FirstInnerVertex.y); + rlVertex2f(cap1FirstOuterVertex.x, cap1FirstOuterVertex.y); + rlVertex2f(cap1SecondOuterVertex.x, cap1SecondOuterVertex.y); + + rlVertex2f(cap1FirstInnerVertex.x, cap1FirstInnerVertex.y); + rlVertex2f(cap1SecondOuterVertex.x, cap1SecondOuterVertex.y); + rlVertex2f(cap1SecondInnerVertex.x, cap1SecondInnerVertex.y); + + // Cap 2 + Vector2 cap2FirstInnerVertex = { center.x + cosf(DEG2RAD*endAngle)*innerInnerRadius, center.y + sinf(DEG2RAD*endAngle)*innerInnerRadius }; + Vector2 cap2FirstOuterVertex = { center.x + cosf(DEG2RAD*endAngle)*outerOuterRadius, center.y + sinf(DEG2RAD*endAngle)*outerOuterRadius }; + + rlVertex2f(cap2FirstInnerVertex.x, cap2FirstInnerVertex.y); + rlVertex2f(cap2SecondInnerVertex.x, cap2SecondInnerVertex.y); + rlVertex2f(cap2SecondOuterVertex.x, cap2SecondOuterVertex.y); + + rlVertex2f(cap2FirstInnerVertex.x, cap2FirstInnerVertex.y); + rlVertex2f(cap2SecondOuterVertex.x, cap2SecondOuterVertex.y); + rlVertex2f(cap2FirstOuterVertex.x, cap2FirstOuterVertex.y); + + if (capsIntersect) + { + // Cap 1 + rlVertex2f(cap1SecondInnerVertex.x, cap1SecondInnerVertex.y); + rlVertex2f(cap1SecondOuterVertex.x, cap1SecondOuterVertex.y); + rlVertex2f(capIntersectionVertex.x, capIntersectionVertex.y); + + // Cap 2 + rlVertex2f(cap2SecondInnerVertex.x, cap2SecondInnerVertex.y); + rlVertex2f(capIntersectionVertex.x, capIntersectionVertex.y); + rlVertex2f(cap2SecondOuterVertex.x, cap2SecondOuterVertex.y); + } + } + } + + rlEnd(); +#endif +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Splines functions //----------------------------------------------------------------------------------