From ffc405325f51a39d1ed3770c797f11bac8e3c089 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 17 Oct 2025 12:54:46 +0200 Subject: [PATCH] Update shapes_pie_chart.c --- examples/shapes/shapes_pie_chart.c | 242 ++++++++++++++--------------- 1 file changed, 113 insertions(+), 129 deletions(-) diff --git a/examples/shapes/shapes_pie_chart.c b/examples/shapes/shapes_pie_chart.c index 1fa938905..e2f6900eb 100644 --- a/examples/shapes/shapes_pie_chart.c +++ b/examples/shapes/shapes_pie_chart.c @@ -22,6 +22,8 @@ #define RAYGUI_IMPLEMENTATION #include "raygui.h" +#define MAX_PIE_SLICES 10 // Max pie slices + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -34,15 +36,14 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - pie chart"); - #define MAX_SLICES 10 int sliceCount = 7; float donutInnerRadius = 25.0f; - float values[MAX_SLICES] = {300.0f, 100.0f, 450.0f, 350.0f, 600.0f, 380.0f, 750.0f}; //initial slice values - char labels[MAX_SLICES][32]; - bool editingLabel[MAX_SLICES] = {false}; + float values[MAX_PIE_SLICES] = { 300.0f, 100.0f, 450.0f, 350.0f, 600.0f, 380.0f, 750.0f }; // Initial slice values + char labels[MAX_PIE_SLICES][32] = { 0 }; + bool editingLabel[MAX_PIE_SLICES] = { 0 }; - for (int i = 0; i < MAX_SLICES; i++) - snprintf(labels[i], 32, "Slice %i", i + 1); + for (int i = 0; i < MAX_PIE_SLICES; i++) + snprintf(labels[i], 32, "Slice %02i", i + 1); bool showValues = true; bool showPercentages = false; @@ -50,7 +51,32 @@ int main(void) int hoveredSlice = -1; Rectangle scrollPanelBounds = {0}; Vector2 scrollContentOffset = {0}; - Rectangle view = {0}; + Rectangle view = { 0 }; + + // UI layout parameters + const int panelWidth = 270; + const int panelMargin = 5; + + // UI Panel top-left anchor + const Vector2 panelPos = { + (float)screenWidth - panelMargin - panelWidth, + (float)panelMargin + }; + + // UI Panel rectangle + const Rectangle panelRect = { + panelPos.x, panelPos.y, + (float)panelWidth, + (float)screenHeight - 2.0f*panelMargin + }; + + // Pie chart geometry + const Rectangle canvas = { 0, 0, panelPos.x, (float)screenHeight }; + const Vector2 center = { canvas.width/2.0f, canvas.height/2.0f}; + const float radius = 205.0f; + + // Total value for percentage calculations + float totalValue = 0.0f; SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -60,32 +86,9 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - //UI layout parameters - const int panelWidth = 270; - const int panelMargin = 5; - - // UI Panel top-left anchor - const Vector2 panelPos = { - (float)screenWidth - panelMargin - panelWidth, - (float)panelMargin - }; - - // UI Panel rectangle - const Rectangle panelRect = { - panelPos.x, panelPos.y, - (float)panelWidth, - (float)screenHeight - 2.0f*panelMargin - }; - - // Pie chart geometry - const Rectangle canvas = { 0, 0, panelPos.x, (float)screenHeight }; - const Vector2 center = {canvas.width / 2.0f, canvas.height / 2.0f}; - const float radius = 205.0f; - // Calculate total value for percentage calculations - float totalValue = 0.0f; - for (int i = 0; i < sliceCount; i++) - totalValue += values[i]; + totalValue = 0.0f; + for (int i = 0; i < sliceCount; i++) totalValue += values[i]; // Check for mouse hover over slices hoveredSlice = -1; // Reset hovered slice @@ -94,23 +97,24 @@ int main(void) { float dx = mousePos.x - center.x; float dy = mousePos.y - center.y; - float distance = sqrtf(dx * dx + dy * dy); + float distance = sqrtf(dx*dx + dy*dy); if (distance <= radius) // Inside the pie radius { - float angle = atan2f(dy, dx) * RAD2DEG; - if (angle < 0) - angle += 360; + float angle = atan2f(dy, dx)*RAD2DEG; + if (angle < 0) angle += 360; float currentAngle = 0.0f; for (int i = 0; i < sliceCount; i++) { - float sweep = (totalValue > 0) ? (values[i] / totalValue) * 360.0f : 0.0f; - if (angle >= currentAngle && angle < (currentAngle + sweep)) + float sweep = (totalValue > 0)? (values[i]/totalValue)*360.0f : 0.0f; + + if ((angle >= currentAngle) && (angle < (currentAngle + sweep))) { hoveredSlice = i; break; } + currentAngle += sweep; } } @@ -120,116 +124,96 @@ int main(void) // Draw //---------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(RAYWHITE); + ClearBackground(RAYWHITE); - // Draw the pie chart on the canvas - //------------------------------------------------------------------------------ - float startAngle = 0.0f; - for (int i = 0; i < sliceCount; i++) - { - float sweepAngle = (totalValue > 0) ? (values[i] / totalValue) * 360.0f : 0.0f; - float midAngle = startAngle + sweepAngle / 2.0f; // Middle angle for label positioning - - Color color = ColorFromHSV((float)i / sliceCount * 360.0f, 0.75f, 0.9f); - float currentRadius = radius; - - // Make the hovered slice pop out by adding 5 pixels to its radius - if (i == hoveredSlice) - currentRadius += 5.0f; - - // Draw the pie slice using raylib's DrawCircleSector function - DrawCircleSector(center, currentRadius, startAngle, startAngle + sweepAngle, 120, color); - - // Draw the label for the current slice - if (values[i] > 0) + // Draw the pie chart on the canvas + float startAngle = 0.0f; + for (int i = 0; i < sliceCount; i++) { - char labelText[64]; - if (showValues && showPercentages) - snprintf(labelText, 64, "%.1f (%.0f%%)", values[i], (values[i] / totalValue) * 100.0f); - else if (showValues) - snprintf(labelText, 64, "%.1f", values[i]); - else if (showPercentages) - snprintf(labelText, 64, "%.0f%%", (values[i] / totalValue) * 100.0f); - else - labelText[0] = '\0'; + float sweepAngle = (totalValue > 0)? (values[i]/totalValue)*360.0f : 0.0f; + float midAngle = startAngle + sweepAngle/2.0f; // Middle angle for label positioning - Vector2 textSize = MeasureTextEx(GetFontDefault(), labelText, 18, 1); - float labelRadius = radius * 0.7f; - Vector2 labelPos = { - center.x + cosf(midAngle * DEG2RAD) * labelRadius - textSize.x / 2, - center.y + sinf(midAngle * DEG2RAD) * labelRadius - textSize.y / 2}; - DrawText(labelText, (int)labelPos.x, (int)labelPos.y, 18, WHITE); - } + Color color = ColorFromHSV((float)i/sliceCount*360.0f, 0.75f, 0.9f); + float currentRadius = radius; + + // Make the hovered slice pop out by adding 5 pixels to its radius + if (i == hoveredSlice) currentRadius += 20.0f; + + // Draw the pie slice using raylib's DrawCircleSector function + DrawCircleSector(center, currentRadius, startAngle, startAngle + sweepAngle, 120, color); + + // Draw the label for the current slice + if (values[i] > 0) + { + char labelText[64] = { 0 }; + if (showValues && showPercentages) snprintf(labelText, 64, "%.1f (%.0f%%)", values[i], (values[i]/totalValue)*100.0f); + else if (showValues) snprintf(labelText, 64, "%.1f", values[i]); + else if (showPercentages) snprintf(labelText, 64, "%.0f%%", (values[i]/totalValue)*100.0f); + else labelText[0] = '\0'; + + Vector2 textSize = MeasureTextEx(GetFontDefault(), labelText, 20, 1); + float labelRadius = radius*0.7f; + Vector2 labelPos = { center.x + cosf(midAngle*DEG2RAD)*labelRadius - textSize.x/2.0f, + center.y + sinf(midAngle*DEG2RAD)*labelRadius - textSize.y/2.0f }; + DrawText(labelText, (int)labelPos.x, (int)labelPos.y, 20, WHITE); + } - if(showDonut) - { // Draw inner circle to create donut effect - DrawCircle(center.x, center.y, donutInnerRadius, RAYWHITE); + // TODO: This is a hacky solution, better use DrawRing() + if (showDonut) DrawCircle(center.x, center.y, donutInnerRadius, RAYWHITE); + + startAngle += sweepAngle; } - startAngle += sweepAngle; - } - //------------------------------------------------------------------------------ + // UI control panel + DrawRectangleRec(panelRect, Fade(LIGHTGRAY, 0.5f)); + DrawRectangleLinesEx(panelRect, 1.0f, GRAY); - // UI control panel - //------------------------------------------------------------------------------ - DrawRectangleRec(panelRect, Fade(LIGHTGRAY, 0.5f)); - DrawRectangleLinesEx(panelRect, 1.0f, GRAY); + GuiSpinner((Rectangle){ panelPos.x + 95, (float)panelPos.y + 12, 125, 25 }, "Slices ", &sliceCount, 1, MAX_PIE_SLICES, false); + GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 40, 20, 20 }, "Show Values", &showValues); + GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 70, 20, 20 }, "Show Percentages", &showPercentages); + GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 100, 20, 20 }, "Make Donut", &showDonut); - int currentY = (int)panelPos.y + 12; // Start a bit lower for margin + if (showDonut) GuiDisable(); + GuiSliderBar((Rectangle){ panelPos.x + 80, (float)panelPos.y + 12 + 130, panelRect.width - 100, 30 }, + "Inner Radius", NULL, &donutInnerRadius, 5.0f, radius - 10.0f); + GuiEnable(); - GuiSpinner((Rectangle){ panelPos.x + 95, (float)currentY, 125, 25 }, "Slices ", &sliceCount, 1, MAX_SLICES, false); - currentY += 40; + GuiLine((Rectangle){ panelPos.x + 10, (float)panelPos.y + 12 + 170, panelRect.width - 20, 1 }, NULL); - GuiCheckBox((Rectangle){ panelPos.x + 20, (float)currentY, 20, 20 }, "Show Values", &showValues); - currentY += 30; + // Scrollable area for slice editors + scrollPanelBounds = (Rectangle){ + panelPos.x + panelMargin, + (float)panelPos.y + 12 + 190, + panelRect.width - panelMargin*2, + panelRect.y + panelRect.height - panelPos.y + 12 + 190 - panelMargin + }; + int contentHeight = sliceCount*35; - GuiCheckBox((Rectangle){ panelPos.x + 20, (float)currentY, 20, 20 }, "Show Percentages", &showPercentages); - currentY += 30; + GuiScrollPanel(scrollPanelBounds, NULL, + (Rectangle){ 0, 0, panelRect.width - 25, (float)contentHeight }, + &scrollContentOffset, &view); - GuiCheckBox((Rectangle){ panelPos.x + 20, (float)currentY, 20, 20 }, "Make Donut", &showDonut); - currentY += 30; + const float contentX = view.x + scrollContentOffset.x; // Left of content + const float contentY = view.y + scrollContentOffset.y; // Top of content - if(showDonut) - { - GuiSliderBar((Rectangle){ panelPos.x + 80, (float)currentY, panelRect.width - 100, 30 }, - "Inner Radius", NULL, &donutInnerRadius, 5.0f, radius - 10.0f); - currentY += 40; - } + BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height); - GuiLine((Rectangle){ panelPos.x + 10, (float)currentY, panelRect.width - 20, 1 }, NULL); - currentY += 20; + for (int i = 0; i < sliceCount; i++) + { + const int rowY = (int)(contentY + 5 + i*35); - + // Color indicator + Color color = ColorFromHSV((float)i/sliceCount*360.0f, 0.75f, 0.9f); + DrawRectangle((int)(contentX + 15), rowY + 5, 20, 20, color); - // Scrollable area for slice editors - scrollPanelBounds = (Rectangle){ panelPos.x+panelMargin, (float)currentY, panelRect.width-panelMargin*2, panelRect.y + panelRect.height - currentY - panelMargin }; - int contentHeight = sliceCount * 35; + // Label textbox + if (GuiTextBox((Rectangle){ contentX + 45, (float)rowY, 75, 30 }, labels[i], 32, editingLabel[i])) editingLabel[i] = !editingLabel[i]; - GuiScrollPanel(scrollPanelBounds, NULL, - (Rectangle){ 0, 0, panelRect.width - 25, (float)contentHeight }, - &scrollContentOffset, &view); + GuiSliderBar((Rectangle){ contentX + 130, (float)rowY, 110, 30 }, NULL, NULL, &values[i], 0.0f, 1000.0f); + } - const float contentX = view.x + scrollContentOffset.x; // left of content - const float contentY = view.y + scrollContentOffset.y; // top of content - - BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height); - for (int i = 0; i < sliceCount; i++) - { - const int rowY = (int)(contentY + 5 + i * 35); - - // Color indicator - Color color = ColorFromHSV((float)i / sliceCount * 360.0f, 0.75f, 0.9f); - DrawRectangle((int)(contentX + 15), rowY + 5, 20, 20, color); - - // Label textbox - if (GuiTextBox((Rectangle){contentX + 45, (float)rowY, 75, 30}, labels[i], 32, editingLabel[i])) - editingLabel[i] = !editingLabel[i]; - - GuiSliderBar((Rectangle){contentX + 130, (float)rowY, 110, 30}, - NULL, NULL, &values[i], 0.0f, 1000.0f); - } - EndScissorMode(); + EndScissorMode(); EndDrawing(); //----------------------------------------------------------------------------------