| @ -0,0 +1,285 @@ | |||
| /******************************************************************************************* | |||
| * | |||
| * raylib [shapes] example - rlgl color wheel | |||
| * | |||
| * Example complexity rating: [★★★☆] 3/4 | |||
| * | |||
| * Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev | |||
| * | |||
| * Example contributed by Robin (@RobinsAviary) 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-2025 Robin (@RobinsAviary) | |||
| * | |||
| ********************************************************************************************/ | |||
| #include "raylib.h" | |||
| #include "rlgl.h" | |||
| #include "raymath.h" | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #define RAYGUI_IMPLEMENTATION | |||
| #include "raygui.h" | |||
| //------------------------------------------------------------------------------------ | |||
| // Program main entry point | |||
| //------------------------------------------------------------------------------------ | |||
| int main(void) | |||
| { | |||
| // Initialization | |||
| //-------------------------------------------------------------------------------------- | |||
| const int screenWidth = 800; | |||
| const int screenHeight = 450; | |||
| // The minimum/maximum points the circle can have | |||
| const unsigned int pointsMin = 3; | |||
| const unsigned int pointsMax = 256; | |||
| // The current number of points and the radius of the circle | |||
| unsigned int triangleCount = 64; | |||
| float pointScale = 150.0f; | |||
| // Slider value, literally maps to value in HSV | |||
| float value = 1.0f; | |||
| // The center of the screen | |||
| Vector2 center = { (float)screenWidth / 2.0f, (float)screenHeight / 2.0f }; | |||
| // The location of the color wheel | |||
| Vector2 circlePosition = center; | |||
| // The currently selected color | |||
| Color color = { 255, 255, 255, 255 }; | |||
| // Indicates if the slider is being clicked | |||
| bool sliderClicked = false; | |||
| // Indicates if the current color going to be updated, as well as the handle position | |||
| bool settingColor = false; | |||
| // How the color wheel will be rendered | |||
| unsigned int renderType = RL_TRIANGLES; | |||
| // Enable anti-aliasing | |||
| SetConfigFlags(FLAG_MSAA_4X_HINT); | |||
| InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rlgl color wheel"); | |||
| SetTargetFPS(60); | |||
| //-------------------------------------------------------------------------------------- | |||
| // Main game loop | |||
| while (!WindowShouldClose()) // Detect window close button or ESC key | |||
| { | |||
| // Update | |||
| //---------------------------------------------------------------------------------- | |||
| triangleCount += (unsigned int)GetMouseWheelMove(); | |||
| triangleCount = (unsigned int)Clamp((float)triangleCount, (float)pointsMin, (float)pointsMax); | |||
| Rectangle sliderRectangle = { 42.0f, 16.0f + 64.0f + 45.0f, 64.0f, 16.0f }; | |||
| Vector2 mousePosition = GetMousePosition(); | |||
| // Checks if the user is hovering over the value slider | |||
| bool sliderHover = (mousePosition.x >= sliderRectangle.x && mousePosition.y >= sliderRectangle.y && mousePosition.x < sliderRectangle.x + sliderRectangle.width && mousePosition.y < sliderRectangle.y + sliderRectangle.height); | |||
| // Copy color as hex | |||
| if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyDown(KEY_C)) { | |||
| if (IsKeyPressed(KEY_C)) { | |||
| SetClipboardText(TextFormat("#%02X%02X%02X", color.r, color.g, color.b)); | |||
| } | |||
| } | |||
| // Scale up the color wheel, adjusting the handle visually | |||
| if (IsKeyDown(KEY_UP)) | |||
| { | |||
| pointScale *= 1.025f; | |||
| if (pointScale > (float)screenHeight/2.0f) | |||
| { | |||
| pointScale = (float)screenHeight/2.0f; | |||
| } | |||
| else | |||
| { | |||
| circlePosition = Vector2Add(Vector2Multiply(Vector2Subtract(circlePosition, center), (Vector2){ 1.025f, 1.025f }), center); | |||
| } | |||
| } | |||
| // Scale down the wheel, adjusting the handle visually | |||
| if (IsKeyDown(KEY_DOWN)) | |||
| { | |||
| pointScale *= 0.975f; | |||
| if (pointScale < 32.0f) { | |||
| pointScale = 32.0f; | |||
| } | |||
| else | |||
| { | |||
| circlePosition = Vector2Add(Vector2Multiply(Vector2Subtract(circlePosition, center), (Vector2){ 0.975f, 0.975f }), center); | |||
| } | |||
| float distance = Vector2Distance(center, circlePosition) / pointScale; | |||
| float angle = ((Vector2Angle((Vector2){ 0.0f, -pointScale }, Vector2Subtract(center, circlePosition)) / PI + 1.0f) / 2.0f); | |||
| if (distance > 1.0f) | |||
| { | |||
| circlePosition = Vector2Add((Vector2){ sinf(angle * (PI * 2.0f)) * pointScale, -cosf(angle * (PI * 2.0f)) * pointScale }, center); | |||
| } | |||
| } | |||
| // Checks if the user clicked on the color wheel | |||
| if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && Vector2Distance(GetMousePosition(), center) <= pointScale + 10.0f) { | |||
| settingColor = true; | |||
| } | |||
| // Update flag when mouse button is released | |||
| if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) settingColor = false; | |||
| // Check if the user clicked/released the slider for the color's value | |||
| if (sliderHover && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) sliderClicked = true; | |||
| if (sliderClicked && IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) sliderClicked = false; | |||
| // Update the current color based on the handle position | |||
| if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && settingColor) | |||
| { | |||
| circlePosition = GetMousePosition(); | |||
| float distance = Vector2Distance(center, circlePosition) / pointScale; | |||
| float angle = ((Vector2Angle((Vector2){ 0.0f, -pointScale }, Vector2Subtract(center, circlePosition)) / PI + 1.0f) / 2.0f); | |||
| float angle360 = angle * 360.0f; | |||
| if (distance > 1.0f) | |||
| { | |||
| circlePosition = Vector2Add((Vector2){ sinf(angle * (PI * 2.0f)) * pointScale, -cosf(angle * (PI * 2.0f)) * pointScale }, center); | |||
| } | |||
| float valueActual = Clamp(distance, 0.0f, 1.0f); | |||
| color = ColorLerp((Color){ (int)(value * 255.0f), (int)(value * 255.0f), (int)(value * 255.0f), 255 }, ColorFromHSV(angle360, Clamp(distance, 0.0f, 1.0f), 1.0f), valueActual); | |||
| } | |||
| // Update render mode accordingly | |||
| if (IsKeyPressed(KEY_SPACE)) renderType = RL_LINES; | |||
| if (IsKeyReleased(KEY_SPACE)) renderType = RL_TRIANGLES; | |||
| //---------------------------------------------------------------------------------- | |||
| // Draw | |||
| //---------------------------------------------------------------------------------- | |||
| BeginDrawing(); | |||
| ClearBackground(RAYWHITE); | |||
| // Begin rendering color wheel | |||
| rlBegin(renderType); | |||
| for (unsigned int i = 0; i < triangleCount; i++) | |||
| { | |||
| float angleOffset = ((PI * 2.0f) / (float)triangleCount); | |||
| float angle = angleOffset * (float)i; | |||
| float angleOffsetCalculated = ((float)i + 1) * angleOffset; | |||
| Vector2 scale = (Vector2){ pointScale, pointScale }; | |||
| Vector2 offset = Vector2Multiply((Vector2){ sinf(angle), -cosf(angle) }, scale); | |||
| Vector2 offset2 = Vector2Multiply((Vector2){ sinf(angleOffsetCalculated), -cosf(angleOffsetCalculated) }, scale); | |||
| Vector2 position = Vector2Add(center, offset); | |||
| Vector2 position2 = Vector2Add(center, offset2); | |||
| float angleNonRadian = (angle / (2.0f * PI)) * 360.0f; | |||
| float angleNonRadianOffset = (angleOffset / (2.0f * PI)) * 360.0f; | |||
| Color color = ColorFromHSV(angleNonRadian, 1.0f, 1.0f); | |||
| Color offsetColor = ColorFromHSV(angleNonRadian + angleNonRadianOffset, 1.0f, 1.0f); | |||
| // Input vertices differently depending on mode | |||
| if (renderType == RL_TRIANGLES) | |||
| { | |||
| // RL_TRIANGLES expects three vertices per triangle | |||
| rlColor4ub(color.r, color.g, color.b, color.a); | |||
| rlVertex2f(position.x, position.y); | |||
| rlColor4f(value, value, value, 1.0f); | |||
| rlVertex2f(center.x, center.y); | |||
| rlColor4ub(offsetColor.r, offsetColor.g, offsetColor.b, offsetColor.a); | |||
| rlVertex2f(position2.x, position2.y); | |||
| } | |||
| else if (renderType == RL_LINES) | |||
| { | |||
| // RL_LINES expects two vertices per line | |||
| rlColor4ub(color.r, color.g, color.b, color.a); | |||
| rlVertex2f(position.x, position.y); | |||
| rlColor4ub(WHITE.r, WHITE.g, WHITE.b, WHITE.a); | |||
| rlVertex2f(center.x, center.y); | |||
| rlVertex2f(center.x, center.y); | |||
| rlColor4ub(offsetColor.r, offsetColor.g, offsetColor.b, offsetColor.a); | |||
| rlVertex2f(position2.x, position2.y); | |||
| rlVertex2f(position2.x, position2.y); | |||
| rlColor4ub(color.r, color.g, color.b, color.a); | |||
| rlVertex2f(position.x, position.y); | |||
| } | |||
| } | |||
| rlEnd(); | |||
| // Make the handle slightly more visible overtop darker colors | |||
| Color handleColor = BLACK; | |||
| if (Vector2Distance(center, circlePosition) / pointScale <= 0.5f && value <= 0.5f) | |||
| { | |||
| handleColor = DARKGRAY; | |||
| } | |||
| // Draw the color handle | |||
| DrawCircleLinesV(circlePosition, 4.0f, handleColor); | |||
| // Draw the color in a preview, with a darkened outline. | |||
| DrawRectangleV((Vector2){8.0f, 8.0f}, (Vector2){64.0f, 64.0f}, color); | |||
| DrawRectangleLinesEx((Rectangle){8.0f, 8.0f, 64.0f, 64.0f}, 2.0f, ColorLerp(color, BLACK, 0.5f)); | |||
| // Draw current color as hex and decimal | |||
| DrawText(TextFormat("#%02X%02X%02X\n(%d, %d, %d)", color.r, color.g, color.b, color.r, color.g, color.b), 8, 8 + 64 + 8, 20, DARKGRAY); | |||
| // Update the visuals for the copying text | |||
| Color copyColor = DARKGRAY; | |||
| unsigned int offset = 0; | |||
| if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyDown(KEY_C)) | |||
| { | |||
| copyColor = DARKGREEN; | |||
| offset = 4; | |||
| } | |||
| // Draw the copying text | |||
| DrawText("press ctrl+c to copy!", 8, 425 - offset, 20, copyColor); | |||
| // Display the number of rendered triangles | |||
| DrawText(TextFormat("triangle count: %d", triangleCount), 8, 395, 20, DARKGRAY); | |||
| // Slider to change color's value | |||
| GuiSliderBar(sliderRectangle, "value: ", "", &value, 0.0f, 1.0f); | |||
| // If the slider was clicked, update the current color | |||
| if (sliderClicked) { | |||
| float distance = Vector2Distance(center, circlePosition) / pointScale; | |||
| float angle = ((Vector2Angle((Vector2){ 0.0f, -pointScale }, Vector2Subtract(center, circlePosition)) / PI + 1.0f) / 2.0f); | |||
| float angle360 = angle * 360.0f; | |||
| float valueActual = Clamp(distance, 0.0f, 1.0f); | |||
| color = ColorLerp((Color){ (int)(value * 255.0f), (int)(value * 255.0f), (int)(value * 255.0f), 255 }, ColorFromHSV(angle360, Clamp(distance, 0.0f, 1.0f), 1.0f), valueActual); | |||
| } | |||
| // Draw FPS next to outlined color preview | |||
| DrawFPS(64 + 16, 8); | |||
| EndDrawing(); | |||
| //---------------------------------------------------------------------------------- | |||
| } | |||
| // De-Initialization | |||
| //-------------------------------------------------------------------------------------- | |||
| CloseWindow(); // Close window and OpenGL context | |||
| //-------------------------------------------------------------------------------------- | |||
| return 0; | |||
| } | |||