|
|
@@ -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
|
|
|
+ // 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;
|
|
|
+ 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;
|
|
|
+ // 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 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];
|
|
|
- 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, 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);
|
|
|
- }
|
|
|
+ // 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);
|
|
|
- }
|
|
|
-
|
|
|
- startAngle += sweepAngle;
|
|
|
- }
|
|
|
- //------------------------------------------------------------------------------
|
|
|
-
|
|
|
- // UI control panel
|
|
|
- //------------------------------------------------------------------------------
|
|
|
- DrawRectangleRec(panelRect, Fade(LIGHTGRAY, 0.5f));
|
|
|
- DrawRectangleLinesEx(panelRect, 1.0f, GRAY);
|
|
|
+ // TODO: This is a hacky solution, better use DrawRing()
|
|
|
+ if (showDonut) DrawCircle(center.x, center.y, donutInnerRadius, RAYWHITE);
|
|
|
|
|
|
- int currentY = (int)panelPos.y + 12; // Start a bit lower for margin
|
|
|
+ startAngle += sweepAngle;
|
|
|
+ }
|
|
|
|
|
|
- GuiSpinner((Rectangle){ panelPos.x + 95, (float)currentY, 125, 25 }, "Slices ", &sliceCount, 1, MAX_SLICES, false);
|
|
|
- currentY += 40;
|
|
|
+ // UI control panel
|
|
|
+ DrawRectangleRec(panelRect, Fade(LIGHTGRAY, 0.5f));
|
|
|
+ DrawRectangleLinesEx(panelRect, 1.0f, GRAY);
|
|
|
|
|
|
- GuiCheckBox((Rectangle){ panelPos.x + 20, (float)currentY, 20, 20 }, "Show Values", &showValues);
|
|
|
- currentY += 30;
|
|
|
+ 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);
|
|
|
|
|
|
- GuiCheckBox((Rectangle){ panelPos.x + 20, (float)currentY, 20, 20 }, "Show Percentages", &showPercentages);
|
|
|
- currentY += 30;
|
|
|
+ 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();
|
|
|
|
|
|
- GuiCheckBox((Rectangle){ panelPos.x + 20, (float)currentY, 20, 20 }, "Make Donut", &showDonut);
|
|
|
- currentY += 30;
|
|
|
+ GuiLine((Rectangle){ panelPos.x + 10, (float)panelPos.y + 12 + 170, panelRect.width - 20, 1 }, NULL);
|
|
|
|
|
|
- if(showDonut)
|
|
|
- {
|
|
|
- GuiSliderBar((Rectangle){ panelPos.x + 80, (float)currentY, panelRect.width - 100, 30 },
|
|
|
- "Inner Radius", NULL, &donutInnerRadius, 5.0f, radius - 10.0f);
|
|
|
- currentY += 40;
|
|
|
- }
|
|
|
+ // 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;
|
|
|
|
|
|
- GuiLine((Rectangle){ panelPos.x + 10, (float)currentY, panelRect.width - 20, 1 }, NULL);
|
|
|
- currentY += 20;
|
|
|
+ GuiScrollPanel(scrollPanelBounds, NULL,
|
|
|
+ (Rectangle){ 0, 0, panelRect.width - 25, (float)contentHeight },
|
|
|
+ &scrollContentOffset, &view);
|
|
|
|
|
|
-
|
|
|
+ const float contentX = view.x + scrollContentOffset.x; // Left of content
|
|
|
+ const float contentY = view.y + scrollContentOffset.y; // Top of content
|
|
|
|
|
|
- // 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;
|
|
|
+ BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height);
|
|
|
|
|
|
- GuiScrollPanel(scrollPanelBounds, NULL,
|
|
|
- (Rectangle){ 0, 0, panelRect.width - 25, (float)contentHeight },
|
|
|
- &scrollContentOffset, &view);
|
|
|
-
|
|
|
- const float contentX = view.x + scrollContentOffset.x; // left of content
|
|
|
- const float contentY = view.y + scrollContentOffset.y; // top of content
|
|
|
+ for (int i = 0; i < sliceCount; i++)
|
|
|
+ {
|
|
|
+ const int rowY = (int)(contentY + 5 + i*35);
|
|
|
|
|
|
- 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);
|
|
|
|
|
|
- // 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];
|
|
|
|
|
|
- // 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);
|
|
|
+ }
|
|
|
|
|
|
- GuiSliderBar((Rectangle){contentX + 130, (float)rowY, 110, 30},
|
|
|
- NULL, NULL, &values[i], 0.0f, 1000.0f);
|
|
|
- }
|
|
|
- EndScissorMode();
|
|
|
+ EndScissorMode();
|
|
|
|
|
|
EndDrawing();
|
|
|
//----------------------------------------------------------------------------------
|