mirror of
				https://github.com/raysan5/raylib.git
				synced 2025-10-26 12:27:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			228 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*******************************************************************************************
 | |
| *
 | |
| *   raylib [shapes] example - pie chart
 | |
| *
 | |
| *   Example complexity rating: [★★★☆] 3/4
 | |
| *
 | |
| *   Example originally created with raylib 5.5, last time updated with raylib 5.6
 | |
| *
 | |
| *   Example contributed by Gideon Serfontein (@GideonSerf) 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) 2025 Gideon Serfontein (@GideonSerf)
 | |
| *
 | |
| ********************************************************************************************/
 | |
| 
 | |
| #include "raylib.h"
 | |
| #include <math.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #define RAYGUI_IMPLEMENTATION
 | |
| #include "raygui.h"
 | |
| 
 | |
| #define MAX_PIE_SLICES  10       // Max pie slices
 | |
| 
 | |
| //------------------------------------------------------------------------------------
 | |
| // Program main entry point
 | |
| //------------------------------------------------------------------------------------
 | |
| int main(void)
 | |
| {
 | |
|     // Initialization
 | |
|     //--------------------------------------------------------------------------------------
 | |
|     const int screenWidth = 800;
 | |
|     const int screenHeight = 450;
 | |
| 
 | |
|     InitWindow(screenWidth, screenHeight, "raylib [shapes] example - pie chart");
 | |
| 
 | |
|     int sliceCount = 7;
 | |
|     float donutInnerRadius = 25.0f;
 | |
|     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_PIE_SLICES; i++)
 | |
|         snprintf(labels[i], 32, "Slice %02i", i + 1);
 | |
| 
 | |
|     bool showValues = true;
 | |
|     bool showPercentages = false;
 | |
|     bool showDonut = false;
 | |
|     int hoveredSlice = -1;
 | |
|     Rectangle scrollPanelBounds = {0};
 | |
|     Vector2 scrollContentOffset = {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);
 | |
|     //--------------------------------------------------------------------------------------
 | |
| 
 | |
|     // Main game loop
 | |
|     while (!WindowShouldClose())
 | |
|     {
 | |
|         // Update
 | |
|         //----------------------------------------------------------------------------------
 | |
|         // Calculate total value for percentage calculations
 | |
|         totalValue = 0.0f;
 | |
|         for (int i = 0; i < sliceCount; i++) totalValue += values[i];
 | |
| 
 | |
|         // Check for mouse hover over slices
 | |
|         hoveredSlice = -1; // Reset hovered slice
 | |
|         Vector2 mousePos = GetMousePosition();
 | |
|         if (CheckCollisionPointRec(mousePos, canvas)) // Only check if mouse is inside the canvas
 | |
|         {
 | |
|             float dx = mousePos.x - center.x;
 | |
|             float dy = mousePos.y - center.y;
 | |
|             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 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)))
 | |
|                     {
 | |
|                         hoveredSlice = i;
 | |
|                         break;
 | |
|                     }
 | |
|                     
 | |
|                     currentAngle += sweep;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         //----------------------------------------------------------------------------------
 | |
| 
 | |
|         // Draw
 | |
|         //----------------------------------------------------------------------------------
 | |
|         BeginDrawing();
 | |
|             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 += 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);
 | |
|                 }
 | |
| 
 | |
|                 // Draw inner circle to create donut effect
 | |
|                 // TODO: This is a hacky solution, better use DrawRing()
 | |
|                 if (showDonut) DrawCircle(center.x, center.y, donutInnerRadius, RAYWHITE);
 | |
| 
 | |
|                 startAngle += sweepAngle;
 | |
|             }
 | |
| 
 | |
|             // 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);
 | |
| 
 | |
|             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();
 | |
| 
 | |
|             GuiLine((Rectangle){ panelPos.x + 10, (float)panelPos.y + 12 + 170, panelRect.width - 20, 1 }, NULL);
 | |
| 
 | |
|             // 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;
 | |
| 
 | |
|             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
 | |
| 
 | |
|             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();
 | |
| 
 | |
|         EndDrawing();
 | |
|         //----------------------------------------------------------------------------------
 | |
|     }
 | |
| 
 | |
|     // De-Initialization
 | |
|     //--------------------------------------------------------------------------------------
 | |
|     CloseWindow(); // Close window and OpenGL context
 | |
|     //--------------------------------------------------------------------------------------
 | |
| 
 | |
|     return 0;
 | |
| } | 
