Gitignore zig crap
This commit is contained in:
@ -1,477 +0,0 @@
|
||||
/*******************************************************************************************
|
||||
*
|
||||
* Animation curves - An example demo for animation curves
|
||||
*
|
||||
* DEPENDENCIES:
|
||||
* raylib 4.0 - Windowing/input management and drawing.
|
||||
* raygui 3.0 - Immediate-mode GUI controls.
|
||||
*
|
||||
* COMPILATION (Windows - MinGW):
|
||||
* gcc -o $(NAME_PART).exe $(FILE_NAME) -I../../src -lraylib -lopengl32 -lgdi32 -std=c99
|
||||
*
|
||||
* LICENSE: zlib/libpng
|
||||
*
|
||||
* Copyright (c) 2023 Pierre Jaffuer (@smallcluster)
|
||||
*
|
||||
**********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24
|
||||
#define RAYGUI_IMPLEMENTATION
|
||||
#include "../../src/raygui.h"
|
||||
|
||||
// raygui embedded styles
|
||||
#include "../styles/style_cyber.h" // raygui style: cyber
|
||||
#include "../styles/style_jungle.h" // raygui style: jungle
|
||||
#include "../styles/style_lavanda.h" // raygui style: lavanda
|
||||
#include "../styles/style_dark.h" // raygui style: dark
|
||||
#include "../styles/style_bluish.h" // raygui style: bluish
|
||||
#include "../styles/style_terminal.h" // raygui style: terminal
|
||||
|
||||
#undef RAYGUI_IMPLEMENTATION // Avoid including raygui implementation again
|
||||
|
||||
#define GUI_CURVE_EDITOR_IMPLEMENTATION
|
||||
#include "gui_curve_editor.h"
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Helper function
|
||||
//------------------------------------------------------------------------------------
|
||||
void LoadCurveDefaults(GuiCurveEditorState curves[]);
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
int main()
|
||||
{
|
||||
// Initialization
|
||||
//---------------------------------------------------------------------------------------
|
||||
const int screenWidth = 800;
|
||||
const int screenHeight = 540;
|
||||
|
||||
InitWindow(screenWidth, screenHeight, "raygui - animation curves");
|
||||
SetTargetFPS(60);
|
||||
|
||||
// Gui style
|
||||
GuiLoadStyleDefault();
|
||||
int visualStyleActive = 0;
|
||||
int prevVisualStyleActive = 0;
|
||||
|
||||
float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE);
|
||||
const float margin = 8;
|
||||
|
||||
// Gui states
|
||||
Vector2 scrollOffset = (Vector2){ 0, 0 };
|
||||
Rectangle contentRect = (Rectangle){ 0, 0, 0, 0 };
|
||||
bool moveSlider = false;
|
||||
bool sectionActive[5] = { 0 };
|
||||
sectionActive[0] = true;
|
||||
const char *sectionNames[5] = { "X Position", "Y Position", "Width", "Height", "Rotation" };
|
||||
bool editValueBox[5][4] = { 0 };
|
||||
char *valTextBox[5][4][20] = { 0 };
|
||||
bool playAnimation = true;
|
||||
bool showHelp = true;
|
||||
|
||||
Rectangle settingsRect = (Rectangle){ screenWidth - screenWidth/3, 0, screenWidth/3, screenHeight };
|
||||
|
||||
// Animation curves
|
||||
// 0 -> Ball X position
|
||||
// 1 -> Ball Y position
|
||||
// 2 -> Ball Width
|
||||
// 3 -> Ball Height
|
||||
// 4 -> Ball rotation
|
||||
GuiCurveEditorState curves[5] = { 0 };
|
||||
LoadCurveDefaults(curves);
|
||||
|
||||
// Animation time
|
||||
float time = 0.0f;
|
||||
float animationTime = 4.0f;
|
||||
|
||||
//SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
if (playAnimation) time += GetFrameTime();
|
||||
|
||||
// Reset timer
|
||||
if (time > animationTime) time = 0;
|
||||
|
||||
// Ball animation
|
||||
const float t = time/animationTime;
|
||||
Vector2 ballPos = (Vector2){ GuiCurveEval(&curves[0], t), GuiCurveEval(&curves[1], t) };
|
||||
Vector2 ballSize = (Vector2){ GuiCurveEval(&curves[2], t), GuiCurveEval(&curves[3], t) };
|
||||
float ballRotation = GuiCurveEval(&curves[4], t);
|
||||
|
||||
// Update style
|
||||
if (visualStyleActive != prevVisualStyleActive)
|
||||
{
|
||||
switch (visualStyleActive)
|
||||
{
|
||||
case 0: GuiLoadStyleDefault(); break;
|
||||
case 1: GuiLoadStyleJungle(); break;
|
||||
case 2: GuiLoadStyleLavanda(); break;
|
||||
case 3: GuiLoadStyleDark(); break;
|
||||
case 4: GuiLoadStyleBluish(); break;
|
||||
case 5: GuiLoadStyleCyber(); break;
|
||||
case 6: GuiLoadStyleTerminal(); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE);
|
||||
prevVisualStyleActive = visualStyleActive;
|
||||
}
|
||||
|
||||
// Update settings panel rect
|
||||
Rectangle sliderRect = (Rectangle){ settingsRect.x - 4, settingsRect.y, 4, settingsRect.height };
|
||||
if (CheckCollisionPointRec(GetMousePosition(), sliderRect) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) moveSlider = true;
|
||||
if (IsMouseButtonUp(MOUSE_BUTTON_LEFT)) moveSlider = false;
|
||||
|
||||
if (moveSlider)
|
||||
{
|
||||
settingsRect.x = GetMouseX();
|
||||
|
||||
// Minimum-Maximum size
|
||||
if (settingsRect.x > (screenWidth - 4)) settingsRect.x = screenWidth - 4;
|
||||
else if (settingsRect.x < 4) settingsRect.x = 4;
|
||||
|
||||
settingsRect.width = screenWidth - settingsRect.x;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(GetColor( GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));
|
||||
|
||||
// Scene
|
||||
//----------------------------------------------------------------------------------
|
||||
DrawRectangle(curves[0].start, curves[1].end, curves[0].end-curves[0].start, curves[1].start-curves[1].end, BLUE); // Sky
|
||||
|
||||
DrawRectangle(curves[0].start, curves[1].start, curves[0].end-curves[0].start, 32, DARKGREEN); // Ground
|
||||
|
||||
BeginScissorMode(curves[0].start, curves[1].end, curves[0].end-curves[0].start, curves[1].start-curves[1].end+32);
|
||||
|
||||
DrawRectanglePro((Rectangle){ballPos.x, ballPos.y, ballSize.x, ballSize.y}, (Vector2){ballSize.x/2.f,ballSize.y/2.f}, ballRotation, PINK); // Ball
|
||||
|
||||
DrawLine(ballPos.x, ballPos.y, ballPos.x + cosf(ballRotation*DEG2RAD)*ballSize.x, ballPos.y +sinf(ballRotation*DEG2RAD)*ballSize.y, RED);
|
||||
DrawLine(ballPos.x, ballPos.y, ballPos.x + cosf((ballRotation+90)*DEG2RAD)*ballSize.x, ballPos.y +sinf((ballRotation+90)*DEG2RAD)*ballSize.y, GREEN);
|
||||
|
||||
EndScissorMode();
|
||||
|
||||
// Bounds
|
||||
DrawRectangleLines(curves[0].start, curves[1].end, curves[0].end-curves[0].start, curves[1].start-curves[1].end+32, GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_NORMAL)));
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// GUI
|
||||
//----------------------------------------------------------------------------------
|
||||
if (showHelp)
|
||||
{
|
||||
if (GuiWindowBox((Rectangle) {margin, margin, settingsRect.x-2*margin, curves[1].end-2*margin}, "help")) showHelp = false;
|
||||
|
||||
Rectangle helpTextRect = (Rectangle) { 2*margin, 2*margin+RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT, settingsRect.x - 4 - 4*margin, 0 };
|
||||
GuiLabel((Rectangle) {helpTextRect.x, helpTextRect.y+helpTextRect.height, helpTextRect.width, fontSize}, "Curve widget controls:");
|
||||
helpTextRect.height += fontSize+margin;
|
||||
GuiLabel((Rectangle) {helpTextRect.x, helpTextRect.y+helpTextRect.height, helpTextRect.width, fontSize}, "- Left click to move/add point or move tangents");
|
||||
helpTextRect.height += fontSize+margin/2;
|
||||
GuiLabel((Rectangle) {helpTextRect.x, helpTextRect.y+helpTextRect.height, helpTextRect.width, fontSize}, "- While moving a tangent, hold SHIFT to disable tangent symetry");
|
||||
helpTextRect.height += fontSize+margin/2;
|
||||
GuiLabel((Rectangle) {helpTextRect.x, helpTextRect.y+helpTextRect.height, helpTextRect.width, fontSize}, "- Right click to remove a point");
|
||||
helpTextRect.height += fontSize+margin/2;
|
||||
DrawRectangleGradientV(margin, margin+curves[1].end - 2*margin, settingsRect.x - 2*margin, 12, (Color){ 0,0,0,100 }, BLANK);
|
||||
}
|
||||
|
||||
// Settings panel
|
||||
GuiScrollPanel(settingsRect, "Settings", contentRect, &scrollOffset, NULL);
|
||||
|
||||
BeginScissorMode(settingsRect.x, settingsRect.y+RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT, settingsRect.width, settingsRect.height);
|
||||
|
||||
// Rebuild the content Rect
|
||||
contentRect = (Rectangle){ settingsRect.x + margin, RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT+margin, settingsRect.width - 2*margin - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), 0 };
|
||||
|
||||
// Help button
|
||||
if (GuiButton((Rectangle){ contentRect.x, contentRect.y + contentRect.height + scrollOffset.y, contentRect.width, 1.5*fontSize }, GuiIconText(showHelp? ICON_EYE_ON : ICON_EYE_OFF, "Curve controls help"))) showHelp = !showHelp;
|
||||
|
||||
contentRect.height += 1.5*fontSize + margin;
|
||||
|
||||
// Animation Time slider
|
||||
GuiSlider((Rectangle){ contentRect.x, contentRect.y+contentRect.height+scrollOffset.y, contentRect.width/2, fontSize }, NULL, TextFormat("Animation Time: %.2fs", animationTime), &animationTime, 1, 8);
|
||||
contentRect.height += fontSize + margin;
|
||||
|
||||
// Load default curves
|
||||
if (GuiButton((Rectangle){ contentRect.x, contentRect.y+contentRect.height+scrollOffset.y, contentRect.width, 1.5*fontSize }, "Load default"))
|
||||
{
|
||||
LoadCurveDefaults(curves);
|
||||
animationTime = 4.0f;
|
||||
time = 0.0f;
|
||||
}
|
||||
contentRect.height += 1.5f*fontSize + margin;
|
||||
|
||||
// Styles
|
||||
GuiLabel((Rectangle){ contentRect.x, contentRect.y + contentRect.height + scrollOffset.y, contentRect.width, fontSize }, "Style:");
|
||||
contentRect.height += fontSize;
|
||||
GuiComboBox((Rectangle){contentRect.x, contentRect.y+contentRect.height+scrollOffset.y, contentRect.width, 1.5*fontSize }, "default;Jungle;Lavanda;Dark;Bluish;Cyber;Terminal", &visualStyleActive);
|
||||
contentRect.height += 1.5f*fontSize + margin;
|
||||
|
||||
// Draw curves with their controls
|
||||
//----------------------------------------------------------------------------------
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
// Collapsing section
|
||||
Rectangle headerRect = (Rectangle){ contentRect.x, contentRect.y + contentRect.height+scrollOffset.y, contentRect.width, 1.5f*fontSize };
|
||||
GuiStatusBar(headerRect, NULL);
|
||||
|
||||
if (GuiLabelButton(headerRect, GuiIconText(sectionActive[i] ? ICON_ARROW_DOWN_FILL : ICON_ARROW_RIGHT_FILL, sectionNames[i]))) sectionActive[i] = !sectionActive[i];
|
||||
|
||||
contentRect.height += 1.5f*fontSize + margin;
|
||||
|
||||
// Skip this section
|
||||
if (!sectionActive[i]) continue;
|
||||
|
||||
// Draw curve control
|
||||
Rectangle curveRect = (Rectangle){ contentRect.x, contentRect.y+contentRect.height + scrollOffset.y, contentRect.width, fontSize*12 };
|
||||
EndScissorMode(); // Stop clipping from setting rect
|
||||
|
||||
// Curves can leaks from control boundary... scissor it !
|
||||
BeginScissorMode(curveRect.x, curveRect.y, curveRect.width, curveRect.height);
|
||||
GuiCurveEditor(&curves[i], curveRect);
|
||||
EndScissorMode();
|
||||
|
||||
// Resume clipping from setting rect
|
||||
BeginScissorMode(settingsRect.x, settingsRect.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT, settingsRect.width, settingsRect.height);
|
||||
contentRect.height += fontSize*12 + margin;
|
||||
|
||||
// Draw selected point controls
|
||||
GuiCurveEditorPoint *p = &(curves[i].points[curves[i].selectedIndex]);
|
||||
GuiCheckBox((Rectangle){ contentRect.x, contentRect.y + contentRect.height + scrollOffset.y, 1.5f*fontSize, 1.5f*fontSize }, "Left Linear", &p->leftLinear);
|
||||
GuiCheckBox((Rectangle){ contentRect.x+contentRect.width/2, contentRect.y + contentRect.height + scrollOffset.y, 1.5f*fontSize, 1.5f*fontSize }, "Right Linear", &p->rightLinear);
|
||||
contentRect.height += 1.5f*fontSize + margin;
|
||||
|
||||
// Positions
|
||||
GuiLabel((Rectangle){ contentRect.x, contentRect.y + contentRect.height + scrollOffset.y, contentRect.width, fontSize }, "Position");
|
||||
contentRect.height += fontSize;
|
||||
|
||||
if (!editValueBox[i][0]) gcvt(p->position.x, 6, (char *)valTextBox[i][0]); // Transform x position to string
|
||||
|
||||
if (!editValueBox[i][1]) gcvt(curves[i].start + (curves[i].end-curves[i].start)*p->position.y, 6, (char *)valTextBox[i][1]); // Transform y position to string
|
||||
|
||||
// X pos
|
||||
if (GuiTextBox((Rectangle){ contentRect.x, contentRect.y + contentRect.height + scrollOffset.y, contentRect.width/2-margin, 1.5f*fontSize }, (char *)valTextBox[i][0], 20, editValueBox[i][0]))
|
||||
{
|
||||
editValueBox[i][0] = !editValueBox[i][0];
|
||||
|
||||
// Input ended
|
||||
if (!editValueBox[i][0])
|
||||
{
|
||||
// Try to convert text to float and assign it to the point
|
||||
char *endPtr = NULL;
|
||||
double value = strtod((char *)valTextBox[i][0], &endPtr);
|
||||
|
||||
if (endPtr != (char *)valTextBox[i][0]) p->position.x = (value < 0)? 0 : (value > 1)? 1 : value;
|
||||
}
|
||||
}
|
||||
|
||||
// Y pos
|
||||
if (GuiTextBox((Rectangle){ contentRect.x + contentRect.width/2, contentRect.y + contentRect.height + scrollOffset.y, contentRect.width/2.0f, 1.5f*fontSize }, (char *)valTextBox[i][1], 20, editValueBox[i][1]))
|
||||
{
|
||||
editValueBox[i][1] = !editValueBox[i][1];
|
||||
|
||||
// Input ended
|
||||
if (!editValueBox[i][1])
|
||||
{
|
||||
// Try to convert text to float and assign it to the point
|
||||
char *endPtr = NULL;
|
||||
double value = strtod((char *)valTextBox[i][1], &endPtr);
|
||||
|
||||
if (endPtr != (char *)valTextBox[i][1])
|
||||
{
|
||||
float normalizedVal = (value - curves[i].start)/(curves[i].end - curves[i].start);
|
||||
p->position.y = (normalizedVal < 0)? 0 : (normalizedVal > 1)? 1 : normalizedVal;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contentRect.height += 1.5f*fontSize + margin;
|
||||
|
||||
// Tangents
|
||||
GuiLabel((Rectangle){ contentRect.x, contentRect.y + contentRect.height + scrollOffset.y, contentRect.width, fontSize }, "Tangents");
|
||||
contentRect.height += fontSize;
|
||||
|
||||
if (!editValueBox[i][2]) gcvt(p->tangents.x, 6, (char *)valTextBox[i][2]); // Transform left tangent to string
|
||||
|
||||
if (!editValueBox[i][3]) gcvt(p->tangents.y, 6, (char *)valTextBox[i][3]); // Transform right tangent to string
|
||||
|
||||
// Left tan
|
||||
if (GuiTextBox((Rectangle){ contentRect.x, contentRect.y + contentRect.height + scrollOffset.y, contentRect.width/2 - margin, 1.5f*fontSize }, (char *)valTextBox[i][2], 20, editValueBox[i][2]))
|
||||
{
|
||||
editValueBox[i][2] = !editValueBox[i][2];
|
||||
|
||||
// Input ended
|
||||
if (!editValueBox[i][2])
|
||||
{
|
||||
// Try to convert text to float and assign it to the point
|
||||
char *endPtr = NULL;
|
||||
double value = strtod((char *)valTextBox[i][2], &endPtr);
|
||||
if (endPtr != (char *)valTextBox[i][2]) p->tangents.x = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Right tan
|
||||
if (GuiTextBox((Rectangle){ contentRect.x + contentRect.width/2.0f, contentRect.y + contentRect.height + scrollOffset.y, contentRect.width/2.0f, 1.5f*fontSize }, (char *)valTextBox[i][3], 20, editValueBox[i][3]))
|
||||
{
|
||||
editValueBox[i][3] = !editValueBox[i][3];
|
||||
|
||||
// Input ended
|
||||
if (!editValueBox[i][3])
|
||||
{
|
||||
// Try to convert text to float and assign it to the point
|
||||
char *endPtr = NULL;
|
||||
double value = strtod((char *)valTextBox[i][3], &endPtr);
|
||||
if (endPtr != (char *)valTextBox[i][3]) p->tangents.y = value;
|
||||
}
|
||||
}
|
||||
|
||||
contentRect.height += 1.5*fontSize + margin;
|
||||
}
|
||||
|
||||
contentRect.height += margin;
|
||||
|
||||
EndScissorMode();
|
||||
|
||||
// Settings panel shadow
|
||||
DrawRectangleGradientH(settingsRect.x - 12, 0, 12, settingsRect.height, BLANK, (Color){ 0, 0, 0, 100 });
|
||||
|
||||
// Slider
|
||||
if (moveSlider) DrawRectangle(sliderRect.x, sliderRect.y, sliderRect.width, sliderRect.height, GetColor(GuiGetStyle(DEFAULT, BASE_COLOR_PRESSED)));
|
||||
else if(CheckCollisionPointRec(GetMousePosition(), sliderRect)) DrawRectangle(sliderRect.x, sliderRect.y, sliderRect.width, sliderRect.height, GetColor(GuiGetStyle(DEFAULT, BASE_COLOR_FOCUSED)));
|
||||
|
||||
// Draw Time controls
|
||||
//----------------------------------------------------------------------------------
|
||||
Rectangle timeLineRect = (Rectangle){ 0, screenHeight-4*fontSize, settingsRect.x, 4*fontSize };
|
||||
GuiPanel((Rectangle){ timeLineRect.x, timeLineRect.y, timeLineRect.width, 2*fontSize }, NULL);
|
||||
GuiLabel((Rectangle){ timeLineRect.x, timeLineRect.y, timeLineRect.width, 2*fontSize }, TextFormat("Normalized Time: %.3f", time/animationTime));
|
||||
if (GuiButton((Rectangle){ timeLineRect.x+timeLineRect.width/2 - 2*fontSize - margin/4, timeLineRect.y, 2*fontSize, 2*fontSize }, GuiIconText((playAnimation? ICON_PLAYER_PAUSE : ICON_PLAYER_PLAY), ""))) playAnimation = !playAnimation;
|
||||
|
||||
if (GuiButton((Rectangle){ timeLineRect.x+timeLineRect.width/2 + margin/4, timeLineRect.y, 2*fontSize, 2*fontSize }, GuiIconText(ICON_PLAYER_STOP, "")))
|
||||
{
|
||||
playAnimation = false;
|
||||
time = 0;
|
||||
}
|
||||
|
||||
float animTime = time/animationTime;
|
||||
GuiSlider((Rectangle){timeLineRect.x, timeLineRect.y + 2*fontSize, timeLineRect.width, timeLineRect.height - 2*fontSize }, NULL, NULL, &animTime, 0, 1);
|
||||
time = animationTime*animTime;
|
||||
|
||||
// Time panel shadow
|
||||
DrawRectangleGradientV(timeLineRect.x, timeLineRect.y - 12, timeLineRect.width, 12, BLANK, (Color){ 0, 0, 0, 100 });
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LoadCurveDefaults(GuiCurveEditorState curves[])
|
||||
{
|
||||
// X pos
|
||||
curves[0].start = 28;
|
||||
curves[0].end = 506;
|
||||
curves[0].numPoints = 4;
|
||||
curves[0].selectedIndex = 0;
|
||||
curves[0].editLeftTangent = false;
|
||||
curves[0].editRightTangent = false;
|
||||
curves[0].points[0].position =(Vector2) {0.000000, 0.000000}; curves[0].points[0].tangents = (Vector2) {0.000000, 1.515101}; curves[0].points[0].leftLinear = 1;curves[0].points[0].rightLinear = 1;
|
||||
curves[0].points[1].position =(Vector2) {0.422414, 0.640000}; curves[0].points[1].tangents = (Vector2) {-2.824348, -4.494999};curves[0].points[1].leftLinear = 0;curves[0].points[1].rightLinear = 0;
|
||||
curves[0].points[2].position =(Vector2) {0.732759, 0.210000}; curves[0].points[2].tangents = (Vector2) {0.000000, 2.956133}; curves[0].points[2].leftLinear = 0;curves[0].points[2].rightLinear = 1;
|
||||
curves[0].points[3].position =(Vector2) {1.000000, 1.000000}; curves[0].points[3].tangents = (Vector2) {2.956133, 0.000000}; curves[0].points[3].leftLinear = 1;curves[0].points[3].rightLinear = 1;
|
||||
|
||||
// Y pos
|
||||
curves[1].start = 405;
|
||||
curves[1].end = 135;
|
||||
curves[1].numPoints = 7;
|
||||
curves[1].selectedIndex = 0;
|
||||
curves[1].editLeftTangent = false;
|
||||
curves[1].editRightTangent = false;
|
||||
curves[1].points[0].position = (Vector2) {0.000000, 1.000000};curves[1].points[0].tangents = (Vector2) { 0.000000 , 0.000000};curves[1].points[0].leftLinear = 0;curves[1].points[0].rightLinear = 0;
|
||||
curves[1].points[1].position = (Vector2) {0.140000, 0.000000};curves[1].points[1].tangents = (Vector2) {-10.000000 ,10.000000};curves[1].points[1].leftLinear = 0;curves[1].points[1].rightLinear = 0;
|
||||
curves[1].points[2].position = (Vector2) {0.450000, 0.000000};curves[1].points[2].tangents = (Vector2) {-10.000000 ,10.000000};curves[1].points[2].leftLinear = 0;curves[1].points[2].rightLinear = 0;
|
||||
curves[1].points[3].position = (Vector2) {0.670000, 0.000000};curves[1].points[3].tangents = (Vector2) {-10.000000 ,10.000000};curves[1].points[3].leftLinear = 0;curves[1].points[3].rightLinear = 0;
|
||||
curves[1].points[4].position = (Vector2) {0.830000, 0.000000};curves[1].points[4].tangents = (Vector2) {-10.000000 ,10.000000};curves[1].points[4].leftLinear = 0;curves[1].points[4].rightLinear = 0;
|
||||
curves[1].points[5].position = (Vector2) {0.940000, 0.000000};curves[1].points[5].tangents = (Vector2) {-10.000000 ,10.000000};curves[1].points[5].leftLinear = 0;curves[1].points[5].rightLinear = 0;
|
||||
curves[1].points[6].position = (Vector2) {1.000000, 0.000000};curves[1].points[6].tangents = (Vector2) {-10.000000 , 0.000000};curves[1].points[6].leftLinear = 0;curves[1].points[6].rightLinear = 0;
|
||||
|
||||
// X size
|
||||
curves[2].start = 1;
|
||||
curves[2].end = 64;
|
||||
curves[2].numPoints = 16;
|
||||
curves[2].selectedIndex = 0;
|
||||
curves[2].editLeftTangent = false;
|
||||
curves[2].editRightTangent = false;
|
||||
curves[2].points[0].position = (Vector2) {0.000000, 0.492063}; curves[2].points[0].tangents = (Vector2) {0,0}; curves[2].points[0].leftLinear = 0; curves[2].points[0].rightLinear = 0;
|
||||
curves[2].points[1].position = (Vector2) {0.130000, 0.492063}; curves[2].points[1].tangents = (Vector2) {0,0}; curves[2].points[1].leftLinear = 0; curves[2].points[1].rightLinear = 0;
|
||||
curves[2].points[2].position = (Vector2) {0.140000, 0.746032}; curves[2].points[2].tangents = (Vector2) {0,0}; curves[2].points[2].leftLinear = 0; curves[2].points[2].rightLinear = 0;
|
||||
curves[2].points[3].position = (Vector2) {0.150000, 0.492063}; curves[2].points[3].tangents = (Vector2) {0,0}; curves[2].points[3].leftLinear = 0; curves[2].points[3].rightLinear = 0;
|
||||
curves[2].points[4].position = (Vector2) {0.440000, 0.490000}; curves[2].points[4].tangents = (Vector2) {0,0}; curves[2].points[4].leftLinear = 0; curves[2].points[4].rightLinear = 0;
|
||||
curves[2].points[5].position = (Vector2) {0.450000, 0.682540}; curves[2].points[5].tangents = (Vector2) {0,0}; curves[2].points[5].leftLinear = 0; curves[2].points[5].rightLinear = 0;
|
||||
curves[2].points[6].position = (Vector2) {0.460000, 0.480000}; curves[2].points[6].tangents = (Vector2) {0,0}; curves[2].points[6].leftLinear = 0; curves[2].points[6].rightLinear = 0;
|
||||
curves[2].points[7].position = (Vector2) {0.660000, 0.492063}; curves[2].points[7].tangents = (Vector2) {0,0}; curves[2].points[7].leftLinear = 0; curves[2].points[7].rightLinear = 0;
|
||||
curves[2].points[8].position = (Vector2) {0.670000, 0.619048}; curves[2].points[8].tangents = (Vector2) {0,0}; curves[2].points[8].leftLinear = 0; curves[2].points[8].rightLinear = 0;
|
||||
curves[2].points[9].position = (Vector2) {0.680000, 0.492063}; curves[2].points[9].tangents = (Vector2) {0,0}; curves[2].points[9].leftLinear = 0; curves[2].points[9].rightLinear = 0;
|
||||
curves[2].points[10].position = (Vector2) {0.820000, 0.492063}; curves[2].points[10].tangents = (Vector2) {0,0}; curves[2].points[10].leftLinear = 0; curves[2].points[10].rightLinear = 0;
|
||||
curves[2].points[11].position = (Vector2) {0.830000, 0.619048}; curves[2].points[11].tangents = (Vector2) {0,0}; curves[2].points[11].leftLinear = 0; curves[2].points[11].rightLinear = 0;
|
||||
curves[2].points[12].position = (Vector2) {0.840000, 0.492063}; curves[2].points[12].tangents = (Vector2) {0,0}; curves[2].points[12].leftLinear = 0; curves[2].points[12].rightLinear = 0;
|
||||
curves[2].points[13].position = (Vector2) {0.930000, 0.492063}; curves[2].points[13].tangents = (Vector2) {0,0}; curves[2].points[13].leftLinear = 0; curves[2].points[13].rightLinear = 0;
|
||||
curves[2].points[14].position = (Vector2) {0.940000, 0.619048}; curves[2].points[14].tangents = (Vector2) {0,0}; curves[2].points[14].leftLinear = 0; curves[2].points[14].rightLinear = 0;
|
||||
curves[2].points[15].position = (Vector2) {0.950000, 0.492063}; curves[2].points[15].tangents = (Vector2) {0,0}; curves[2].points[15].leftLinear = 0; curves[2].points[15].rightLinear = 0;
|
||||
|
||||
// Y Size
|
||||
curves[3].start = 1;
|
||||
curves[3].end = 64;
|
||||
curves[3].numPoints = 16;
|
||||
curves[3].selectedIndex = 0;
|
||||
curves[3].editLeftTangent = false;
|
||||
curves[3].editRightTangent = false;
|
||||
curves[3].points[0].position = (Vector2) {0.000000, 0.492063};curves[3].points[0].tangents = (Vector2) {0,0};curves[3].points[0].leftLinear = 0;curves[3].points[0].rightLinear = 0;
|
||||
curves[3].points[1].position = (Vector2) {0.130000, 0.492063};curves[3].points[1].tangents = (Vector2) {0,0};curves[3].points[1].leftLinear = 0;curves[3].points[1].rightLinear = 0;
|
||||
curves[3].points[2].position = (Vector2) {0.140000, 0.238095};curves[3].points[2].tangents = (Vector2) {0,0};curves[3].points[2].leftLinear = 0;curves[3].points[2].rightLinear = 0;
|
||||
curves[3].points[3].position = (Vector2) {0.150000, 0.492063};curves[3].points[3].tangents = (Vector2) {0,0};curves[3].points[3].leftLinear = 0;curves[3].points[3].rightLinear = 0;
|
||||
curves[3].points[4].position = (Vector2) {0.440000, 0.492063};curves[3].points[4].tangents = (Vector2) {0,0};curves[3].points[4].leftLinear = 0;curves[3].points[4].rightLinear = 0;
|
||||
curves[3].points[5].position = (Vector2) {0.450000, 0.301587};curves[3].points[5].tangents = (Vector2) {0,0};curves[3].points[5].leftLinear = 0;curves[3].points[5].rightLinear = 0;
|
||||
curves[3].points[6].position = (Vector2) {0.460000, 0.492063};curves[3].points[6].tangents = (Vector2) {0,0};curves[3].points[6].leftLinear = 0;curves[3].points[6].rightLinear = 0;
|
||||
curves[3].points[7].position = (Vector2) {0.660000, 0.492063};curves[3].points[7].tangents = (Vector2) {0,0};curves[3].points[7].leftLinear = 0;curves[3].points[7].rightLinear = 0;
|
||||
curves[3].points[8].position = (Vector2) {0.670000, 0.365079};curves[3].points[8].tangents = (Vector2) {0,0};curves[3].points[8].leftLinear = 0;curves[3].points[8].rightLinear = 0;
|
||||
curves[3].points[9].position = (Vector2) {0.680000, 0.492063};curves[3].points[9].tangents = (Vector2) {0,0};curves[3].points[9].leftLinear = 0;curves[3].points[9].rightLinear = 0;
|
||||
curves[3].points[10].position = (Vector2) {0.820000, 0.492063};curves[3].points[10].tangents = (Vector2) {0,0};curves[3].points[10].leftLinear = 0;curves[3].points[10].rightLinear = 0;
|
||||
curves[3].points[11].position = (Vector2) {0.830000, 0.365079};curves[3].points[11].tangents = (Vector2) {0,0};curves[3].points[11].leftLinear = 0;curves[3].points[11].rightLinear = 0;
|
||||
curves[3].points[12].position = (Vector2) {0.840000, 0.492063};curves[3].points[12].tangents = (Vector2) {0,0};curves[3].points[12].leftLinear = 0;curves[3].points[12].rightLinear = 0;
|
||||
curves[3].points[13].position = (Vector2) {0.930000, 0.492063};curves[3].points[13].tangents = (Vector2) {0,0};curves[3].points[13].leftLinear = 0;curves[3].points[13].rightLinear = 0;
|
||||
curves[3].points[14].position = (Vector2) {0.940000, 0.365079};curves[3].points[14].tangents = (Vector2) {0,0};curves[3].points[14].leftLinear = 0;curves[3].points[14].rightLinear = 0;
|
||||
curves[3].points[15].position = (Vector2) {0.950000, 0.507937};curves[3].points[15].tangents = (Vector2) {0,0};curves[3].points[15].leftLinear = 0;curves[3].points[15].rightLinear = 0;
|
||||
|
||||
// Rotation
|
||||
curves[4].start = -360;
|
||||
curves[4].end = 360;
|
||||
curves[4].numPoints = 9;
|
||||
curves[4].selectedIndex = 0;
|
||||
curves[4].editLeftTangent = false;
|
||||
curves[4].editRightTangent = false;
|
||||
curves[4].points[0].position = (Vector2) {0.140000, 0.500000};curves[4].points[0].tangents = (Vector2) {0,0};curves[4].points[0].leftLinear = 0;curves[4].points[0].rightLinear = 0;
|
||||
curves[4].points[1].position = (Vector2) {0.450000, 0.500000};curves[4].points[1].tangents = (Vector2) {0,0};curves[4].points[1].leftLinear = 0;curves[4].points[1].rightLinear = 0;
|
||||
curves[4].points[2].position = (Vector2) {0.670000, 0.500000};curves[4].points[2].tangents = (Vector2) {0,0};curves[4].points[2].leftLinear = 0;curves[4].points[2].rightLinear = 0;
|
||||
curves[4].points[3].position = (Vector2) {0.830000, 0.500000};curves[4].points[3].tangents = (Vector2) {0,0};curves[4].points[3].leftLinear = 0;curves[4].points[3].rightLinear = 0;
|
||||
curves[4].points[4].position = (Vector2) {0.940000, 0.500000};curves[4].points[4].tangents = (Vector2) {0,0};curves[4].points[4].leftLinear = 0;curves[4].points[4].rightLinear = 0;
|
||||
curves[4].points[5].position = (Vector2) {1.000000, 0.500000};curves[4].points[5].tangents = (Vector2) {0,0};curves[4].points[5].leftLinear = 0;curves[4].points[5].rightLinear = 0;
|
||||
curves[4].points[6].position = (Vector2) {0.000000, 0.472222};curves[4].points[6].tangents = (Vector2) {0,0};curves[4].points[6].leftLinear = 0;curves[4].points[6].rightLinear = 0;
|
||||
curves[4].points[7].position = (Vector2) {0.302752, 0.527778};curves[4].points[7].tangents = (Vector2) {0,0};curves[4].points[7].leftLinear = 0;curves[4].points[7].rightLinear = 0;
|
||||
curves[4].points[8].position = (Vector2) {0.577982, 0.472222};curves[4].points[8].tangents = (Vector2) {0,0};curves[4].points[8].leftLinear = 0;curves[4].points[8].rightLinear = 0;
|
||||
}
|
||||
@ -1,543 +0,0 @@
|
||||
/*******************************************************************************************
|
||||
*
|
||||
* CurveEdit v1.0 - A cubic Hermite editor for making animation curves
|
||||
*
|
||||
* MODULE USAGE:
|
||||
* #define GUI_CURVE_EDITOR_IMPLEMENTATION
|
||||
* #include "gui_curve_edit.h"
|
||||
*
|
||||
* INIT: GuiCurveEditState state = InitCurveEdit();
|
||||
* EVALUATE: float y = EvalGuiCurve(&state, t); // 0 <= t <= 1
|
||||
* DRAW: BeginScissorMode(bounds.x,bounds.y,bounds.width,bounds.height);
|
||||
* GuiCurveEdit(&state, bounds, pointSize);
|
||||
* EndScissorMode();
|
||||
*
|
||||
* NOTE: See 'Module Structures Declaration' section for more informations.
|
||||
*
|
||||
* NOTE: This module uses functions of the stdlib:
|
||||
* - qsort
|
||||
*
|
||||
* NOTE: Built-in interactions:
|
||||
* - Left click to move/add point or move tangents
|
||||
* - While moving a tangent, hold (left/right) SHIFT to disable tangent symetry
|
||||
* - Right click to remove a point
|
||||
*
|
||||
*
|
||||
* LICENSE: zlib/libpng
|
||||
*
|
||||
* Copyright (c) 2023 Pierre Jaffuer (@smallcluster)
|
||||
*
|
||||
* This software is provided "as-is", without any express or implied warranty. In no event
|
||||
* will the authors be held liable for any damages arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose, including commercial
|
||||
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not claim that you
|
||||
* wrote the original software. If you use this software in a product, an acknowledgment
|
||||
* in the product documentation would be appreciated but is not required.
|
||||
*
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
|
||||
* as being the original software.
|
||||
*
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
**********************************************************************************************/
|
||||
|
||||
#include "raylib.h"
|
||||
|
||||
#ifndef GUI_CURVE_EDITOR_H
|
||||
#define GUI_CURVE_EDITOR_H
|
||||
|
||||
|
||||
#ifndef GUI_CURVE_EDITOR_MAX_POINTS
|
||||
#define GUI_CURVE_EDITOR_MAX_POINTS 30
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Structures Declaration
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
Vector2 position; // In normalized space [0.0f, 1.0f]
|
||||
Vector2 tangents; // The derivatives (left/right) of the 1D curve
|
||||
|
||||
// Let the curve editor calculate tangents to linearize part of the curve
|
||||
bool leftLinear;
|
||||
bool rightLinear;
|
||||
} GuiCurveEditorPoint;
|
||||
|
||||
typedef struct {
|
||||
float start; // Value at y = 0
|
||||
float end; // Value at y = 1
|
||||
|
||||
// Always valid (unless you manualy change state's point array). Make sure to set it to -1 before init
|
||||
int selectedIndex;
|
||||
|
||||
// Unsorted array with at least one point (constant curve)
|
||||
GuiCurveEditorPoint points[GUI_CURVE_EDITOR_MAX_POINTS];
|
||||
int numPoints;
|
||||
|
||||
// Private variables
|
||||
bool editLeftTangent;
|
||||
bool editRightTangent;
|
||||
Vector2 mouseOffset;
|
||||
} GuiCurveEditorState;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { // Prevents name mangling of functions
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Declaration
|
||||
//----------------------------------------------------------------------------------
|
||||
GuiCurveEditorState InitGuiCurveEditor(); // Initialize curve editor state
|
||||
void GuiCurveEditor(GuiCurveEditorState *state, Rectangle bounds); // Draw and update curve control
|
||||
|
||||
// 1D Interpolation
|
||||
// Returns the y value (in [start, end]) of the curve at x = t
|
||||
// t must be normalized [0.f, 1.f]
|
||||
float GuiCurveEval(GuiCurveEditorState *state, float t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // GUI_CURVE_EDITOR_H
|
||||
|
||||
/***********************************************************************************
|
||||
*
|
||||
* GUI_CURVE_EDITOR IMPLEMENTATION
|
||||
*
|
||||
************************************************************************************/
|
||||
#if defined(GUI_CURVE_EDITOR_IMPLEMENTATION)
|
||||
|
||||
#include "../../src/raygui.h" // Change this to fit your project
|
||||
|
||||
#include "stdlib.h" // Required for qsort
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
GuiCurveEditorState InitGuiCurveEditor()
|
||||
{
|
||||
GuiCurveEditorState state = { 0 };
|
||||
|
||||
state.start = 0;
|
||||
state.end = 1;
|
||||
state.selectedIndex = 0;
|
||||
state.editLeftTangent = false;
|
||||
state.editRightTangent = false;
|
||||
state.mouseOffset = (Vector2){ 0.0f, 0.0f };
|
||||
|
||||
// At least one point (AVG by default)
|
||||
state.numPoints = 1;
|
||||
state.points[0].position = (Vector2){ 0.5f, 0.5f };
|
||||
state.points[0].tangents = (Vector2){ 0.0f, 0.0f };
|
||||
state.points[0].leftLinear = false;
|
||||
state.points[0].rightLinear = false;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static int CompareGuiCurveEditPointPtr(const void *a, const void *b)
|
||||
{
|
||||
float fa = (*(GuiCurveEditorPoint**)a)->position.x;
|
||||
float fb = (*(GuiCurveEditorPoint**)b)->position.x;
|
||||
|
||||
return ((fa > fb) - (fa < fb));
|
||||
}
|
||||
|
||||
float GuiCurveEval(GuiCurveEditorState *state, float t)
|
||||
{
|
||||
// Sort points
|
||||
GuiCurveEditorPoint* sortedPoints[GUI_CURVE_EDITOR_MAX_POINTS];
|
||||
|
||||
for (int i=0; i < state->numPoints; i++) sortedPoints[i] = &state->points[i];
|
||||
|
||||
qsort(sortedPoints, state->numPoints, sizeof(GuiCurveEditorPoint*), CompareGuiCurveEditPointPtr);
|
||||
|
||||
if (state->numPoints == 0) return state->start;
|
||||
|
||||
// Constants part on edges
|
||||
if (t <= sortedPoints[0]->position.x) return state->start + (state->end-state->start)*sortedPoints[0]->position.y;
|
||||
if (t >= sortedPoints[state->numPoints-1]->position.x) return state->start + (state->end-state->start)*sortedPoints[state->numPoints-1]->position.y;
|
||||
|
||||
// Find curve portion
|
||||
for (int i=0; i < state->numPoints-1; i++)
|
||||
{
|
||||
const GuiCurveEditorPoint *p1 = sortedPoints[i];
|
||||
const GuiCurveEditorPoint *p2 = sortedPoints[i+1];
|
||||
|
||||
// Skip this range
|
||||
if (!((t >= p1->position.x) && (t < p2->position.x)) || (p1->position.x == p2->position.x)) continue;
|
||||
|
||||
float scale = (p2->position.x-p1->position.x);
|
||||
float T = (t-p1->position.x)/scale;
|
||||
float startTangent = scale*p1->tangents.y;
|
||||
float endTangent = scale*p2->tangents.x;
|
||||
float T2 = T*T;
|
||||
float T3 = T*T*T;
|
||||
|
||||
return (state->start + (state->end-state->start)*((2*T3 - 3*T2 + 1)*p1->position.y + (T3 - 2*T2 + T)*startTangent + (3*T2 - 2*T3)*p2->position.y + (T3 - T2)*endTangent));
|
||||
}
|
||||
|
||||
return state->start;
|
||||
}
|
||||
|
||||
void GuiCurveEditor(GuiCurveEditorState *state, Rectangle bounds)
|
||||
{
|
||||
// CONST
|
||||
//----------------------------------------------------------------------------------
|
||||
const float pointSize = 10.0f;
|
||||
const float fontSize = GuiGetStyle(DEFAULT, TEXT_SIZE);
|
||||
const float handleLength = pointSize*2.5f;
|
||||
const float handleSize = pointSize/1.5f;
|
||||
|
||||
const Rectangle innerBounds = (Rectangle){ bounds.x + fontSize, bounds.y + fontSize, bounds.width - 2*fontSize, bounds.height - 2*fontSize };
|
||||
const Vector2 mouse = GetMousePosition();
|
||||
const Vector2 mouseLocal = (Vector2){ (mouse.x - innerBounds.x)/innerBounds.width, (innerBounds.y + innerBounds.height-mouse.y)/innerBounds.height};
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// UPDATE STATE
|
||||
//----------------------------------------------------------------------------------
|
||||
// Find first point under mouse (-1 if not found)
|
||||
int hoveredPointIndex = -1;
|
||||
for (int i = 0; i < state->numPoints; i++)
|
||||
{
|
||||
const GuiCurveEditorPoint *p = &state->points[i];
|
||||
const Vector2 screenPos = (Vector2){ p->position.x*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height-p->position.y*innerBounds.height };
|
||||
const Rectangle pointRect = (Rectangle){ screenPos.x - pointSize/2.0f, screenPos.y - pointSize/2.0f, pointSize, pointSize };
|
||||
|
||||
if (CheckCollisionPointRec(mouse, pointRect))
|
||||
{
|
||||
hoveredPointIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Unselect tangents
|
||||
if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
|
||||
{
|
||||
state->editLeftTangent = false;
|
||||
state->editRightTangent = false;
|
||||
}
|
||||
|
||||
// Select a tangent if possible
|
||||
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (state->selectedIndex != -1) && CheckCollisionPointRec(mouse, bounds))
|
||||
{
|
||||
const GuiCurveEditorPoint* p = &state->points[state->selectedIndex];
|
||||
const Vector2 screenPos = (Vector2){ p->position.x*innerBounds.width+innerBounds.x, innerBounds.y+innerBounds.height-p->position.y*innerBounds.height };
|
||||
|
||||
// Left control
|
||||
Vector2 target = (Vector2){ (p->position.x-1)*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - (p->position.y-p->tangents.x)*innerBounds.height };
|
||||
Vector2 dir = (Vector2){ target.x-screenPos.x, target.y-screenPos.y };
|
||||
float d = sqrt(dir.x*dir.x + dir.y*dir.y);
|
||||
Vector2 control = (Vector2){ screenPos.x + dir.x/d*handleLength, screenPos.y + dir.y/d*handleLength };
|
||||
Rectangle controlRect = (Rectangle){ control.x - handleSize/2.0f, control.y - handleSize/2.0f, handleSize, handleSize };
|
||||
|
||||
// Edit left tangent
|
||||
if (CheckCollisionPointRec(mouse, controlRect)) state->editLeftTangent = true;
|
||||
|
||||
// Right control
|
||||
target = (Vector2){ (p->position.x + 1)*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - (p->position.y + p->tangents.y)*innerBounds.height };
|
||||
dir = (Vector2){ target.x-screenPos.x, target.y-screenPos.y };
|
||||
d = sqrt(dir.x*dir.x + dir.y*dir.y);
|
||||
control = (Vector2){ screenPos.x + dir.x/d*handleLength, screenPos.y + dir.y/d*handleLength };
|
||||
controlRect = (Rectangle){ control.x - handleSize/2.0f, control.y - handleSize/2.0f, handleSize, handleSize };
|
||||
|
||||
// Edit right tangent
|
||||
if (CheckCollisionPointRec(mouse, controlRect)) state->editRightTangent = true;
|
||||
}
|
||||
|
||||
// Move tangents
|
||||
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && state->editRightTangent)
|
||||
{
|
||||
// editRightTangent == true implies selectedIndex != -1
|
||||
GuiCurveEditorPoint *p = &state->points[state->selectedIndex];
|
||||
const Vector2 dir = (Vector2){ mouseLocal.x - p->position.x, mouseLocal.y - p->position.y};
|
||||
|
||||
// Calculate right tangent slope
|
||||
p->tangents.y = (dir.x < 0.001f)? dir.y/0.001f : dir.y/dir.x;
|
||||
p->rightLinear = false; // Stop right linearization update
|
||||
|
||||
// Tangents are symetric by default unless SHIFT is pressed
|
||||
if (!(IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT)))
|
||||
{
|
||||
p->tangents.x = p->tangents.y;
|
||||
p->leftLinear = false; // Stop left linearization update
|
||||
}
|
||||
|
||||
}
|
||||
else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && state->editLeftTangent)
|
||||
{
|
||||
// editLeftTangent == true implies selectedIndex != -1
|
||||
GuiCurveEditorPoint *p = &state->points[state->selectedIndex];
|
||||
const Vector2 dir = (Vector2){ mouseLocal.x - p->position.x, mouseLocal.y - p->position.y };
|
||||
|
||||
// Calculate left tangent slope
|
||||
p->tangents.x = (dir.x > -0.001f)? dir.y/(-0.001f) : dir.y/dir.x;
|
||||
p->leftLinear = false; // Stop left linearization update
|
||||
|
||||
// Tangents are symetric by default unless SHIFT is pressed
|
||||
if (!(IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT)))
|
||||
{
|
||||
p->tangents.y = p->tangents.x;
|
||||
p->rightLinear = false; // Stop right linearization update
|
||||
}
|
||||
}
|
||||
// Select a point
|
||||
else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (hoveredPointIndex != -1) && CheckCollisionPointRec(mouse, bounds))
|
||||
{
|
||||
state->selectedIndex = hoveredPointIndex;
|
||||
const GuiCurveEditorPoint *p = &state->points[state->selectedIndex];
|
||||
state->mouseOffset = (Vector2){ p->position.x - mouseLocal.x, p->position.y - mouseLocal.y };
|
||||
}
|
||||
// Remove a point (check against bounds)
|
||||
else if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT) && (hoveredPointIndex != -1) && CheckCollisionPointRec(mouse, bounds) && (state->numPoints > 1))
|
||||
{
|
||||
// Deselect everything
|
||||
state->selectedIndex = 0; // select first point by default
|
||||
state->editLeftTangent = false;
|
||||
state->editRightTangent = false;
|
||||
|
||||
// Remove point
|
||||
state->numPoints -= 1;
|
||||
for (int i = hoveredPointIndex; i < state->numPoints; i++) state->points[i] = state->points[i+1];
|
||||
}
|
||||
// Add a point (check against innerBounds)
|
||||
else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(mouse, innerBounds) && (state->numPoints < GUI_CURVE_EDITOR_MAX_POINTS))
|
||||
{
|
||||
state->editLeftTangent = false;
|
||||
state->editRightTangent = false;
|
||||
|
||||
// Create new point
|
||||
GuiCurveEditorPoint p;
|
||||
p.tangents = (Vector2){ 0.0f, 0.0f };
|
||||
p.position = mouseLocal;
|
||||
p.leftLinear = false;
|
||||
p.rightLinear = false;
|
||||
|
||||
// Append point
|
||||
state->points[state->numPoints] = p;
|
||||
state->selectedIndex = state->numPoints; // select new point
|
||||
state->numPoints += 1;
|
||||
|
||||
// Point is add on mouse pos
|
||||
state->mouseOffset = (Vector2){ 0, 0 };
|
||||
}
|
||||
// Move selected point
|
||||
else if ((state->selectedIndex != -1) && IsMouseButtonDown(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(mouse, bounds))
|
||||
{
|
||||
GuiCurveEditorPoint *p = &state->points[state->selectedIndex];
|
||||
|
||||
// use mouse offset on click to prevent point teleporting to mouse
|
||||
const Vector2 newLocalPos = (Vector2){ mouseLocal.x + state->mouseOffset.x, mouseLocal.y + state->mouseOffset.y };
|
||||
|
||||
// Clamp to innerbounds
|
||||
p->position.x = (newLocalPos.x < 0)? 0 : ((newLocalPos.x > 1)? 1 : newLocalPos.x);
|
||||
p->position.y = (newLocalPos.y < 0)? 0 : ((newLocalPos.y > 1)? 1 : newLocalPos.y);
|
||||
}
|
||||
|
||||
// Sort points
|
||||
GuiCurveEditorPoint *sortedPoints[GUI_CURVE_EDITOR_MAX_POINTS] = { 0 };
|
||||
for (int i = 0; i < state->numPoints; i++) sortedPoints[i] = &state->points[i];
|
||||
qsort(sortedPoints, state->numPoints, sizeof(GuiCurveEditorPoint*), CompareGuiCurveEditPointPtr);
|
||||
|
||||
// Update linear tangents
|
||||
for (int i = 0; i < state->numPoints; i++)
|
||||
{
|
||||
GuiCurveEditorPoint *p = sortedPoints[i];
|
||||
|
||||
// Left tangent
|
||||
if ((i > 0) && p->leftLinear)
|
||||
{
|
||||
const GuiCurveEditorPoint *p2 = sortedPoints[i - 1];
|
||||
Vector2 dir = (Vector2){ p2->position.x - p->position.x, p2->position.y - p->position.y };
|
||||
p->tangents.x = (dir.x == 0)? 0 : dir.y/dir.x;
|
||||
}
|
||||
|
||||
// Right tangent
|
||||
if ((i < state->numPoints - 1) && p->rightLinear)
|
||||
{
|
||||
const GuiCurveEditorPoint *p2 = sortedPoints[i + 1];
|
||||
Vector2 dir = (Vector2){ p2->position.x - p->position.x, p2->position.y - p->position.y };
|
||||
p->tangents.y = (dir.x == 0)? 0 : dir.y/dir.x;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// DRAWING
|
||||
//----------------------------------------------------------------------------------
|
||||
DrawRectangle(bounds.x, bounds.y, bounds.width, bounds.height, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));
|
||||
|
||||
// Draw grid
|
||||
// H lines
|
||||
const Color lineColor = GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_NORMAL));
|
||||
DrawLine(bounds.x, innerBounds.y, bounds.x+bounds.width, innerBounds.y, lineColor); // end
|
||||
DrawLine(bounds.x, innerBounds.y+innerBounds.height/2, bounds.x+bounds.width, innerBounds.y+innerBounds.height/2, lineColor); // avg
|
||||
DrawLine(bounds.x, innerBounds.y+innerBounds.height, bounds.x+bounds.width, innerBounds.y+innerBounds.height, lineColor); // start
|
||||
|
||||
// V lines
|
||||
DrawLine(innerBounds.x, bounds.y, innerBounds.x, bounds.y+bounds.height, lineColor); // 0
|
||||
DrawLine(innerBounds.x + innerBounds.width/4, bounds.y, innerBounds.x + innerBounds.width/4, bounds.y + bounds.height, lineColor); // 0.25
|
||||
DrawLine(innerBounds.x + innerBounds.width/2, bounds.y, innerBounds.x + innerBounds.width/2, bounds.y + bounds.height, lineColor); // 0.5
|
||||
DrawLine(innerBounds.x + 3*innerBounds.width/4, bounds.y, innerBounds.x + 3*innerBounds.width/4, bounds.y + bounds.height, lineColor); // 0.75
|
||||
DrawLine(innerBounds.x + innerBounds.width, bounds.y, innerBounds.x + innerBounds.width, bounds.y + bounds.height, lineColor); // 1
|
||||
|
||||
Font font = GuiGetFont();
|
||||
// V labels
|
||||
DrawTextEx(font, "0", (Vector2){ innerBounds.x, bounds.y + bounds.height-fontSize}, fontSize, GuiGetStyle(DEFAULT, TEXT_SPACING), lineColor);
|
||||
DrawTextEx(font, "0.25", (Vector2){ innerBounds.x + innerBounds.width/4.0f, bounds.y + bounds.height - fontSize}, fontSize, GuiGetStyle(DEFAULT, TEXT_SPACING), lineColor);
|
||||
DrawTextEx(font, "0.5", (Vector2){ innerBounds.x + innerBounds.width/2.0f, bounds.y + bounds.height - fontSize}, fontSize, GuiGetStyle(DEFAULT, TEXT_SPACING), lineColor);
|
||||
DrawTextEx(font, "0.75", (Vector2){ innerBounds.x + 3.0f*innerBounds.width/4.0f, bounds.y + bounds.height-fontSize}, fontSize, GuiGetStyle(DEFAULT, TEXT_SPACING), lineColor);
|
||||
DrawTextEx(font, "1", (Vector2){ innerBounds.x + innerBounds.width, bounds.y+bounds.height - fontSize}, fontSize, GuiGetStyle(DEFAULT, TEXT_SPACING), lineColor);
|
||||
|
||||
// H labels
|
||||
DrawTextEx(font, TextFormat("%.2f", state->start), (Vector2){ innerBounds.x, innerBounds.y - fontSize+innerBounds.height }, fontSize, GuiGetStyle(DEFAULT, TEXT_SPACING), lineColor);
|
||||
DrawTextEx(font, TextFormat("%.2f", state->start + (state->end-state->start)/2.f), (Vector2){ innerBounds.x, innerBounds.y - fontSize + innerBounds.height/2.0f }, fontSize, GuiGetStyle(DEFAULT, TEXT_SPACING), lineColor);
|
||||
DrawTextEx(font, TextFormat("%.2f", state->end), (Vector2){ innerBounds.x, innerBounds.y }, fontSize, GuiGetStyle(DEFAULT, TEXT_SPACING), lineColor);
|
||||
|
||||
// Draw contours
|
||||
if (CheckCollisionPointRec(mouse, bounds)) DrawRectangleLines(bounds.x, bounds.y, bounds.width, bounds.height, GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_FOCUSED)));
|
||||
else DrawRectangleLines(bounds.x, bounds.y, bounds.width, bounds.height, GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_NORMAL)));
|
||||
|
||||
// Draw points
|
||||
for (int i = 0; i < state->numPoints; i++)
|
||||
{
|
||||
const GuiCurveEditorPoint *p = sortedPoints[i];
|
||||
|
||||
const Vector2 screenPos = (Vector2){ p->position.x*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - p->position.y*innerBounds.height };
|
||||
const Rectangle pointRect = (Rectangle){ screenPos.x - pointSize/2.0f, screenPos.y - pointSize/2.0f, pointSize, pointSize };
|
||||
|
||||
Color pointColor = { 0 };
|
||||
Color pointBorderColor = { 0 };
|
||||
|
||||
// Draw point
|
||||
if (&state->points[state->selectedIndex] == p)
|
||||
{
|
||||
// Draw left handle
|
||||
if (i > 0)
|
||||
{
|
||||
const Vector2 target = (Vector2){ (p->position.x - 1)*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - (p->position.y - p->tangents.x)*innerBounds.height };
|
||||
const Vector2 dir = (Vector2){ target.x - screenPos.x, target.y - screenPos.y };
|
||||
const float d = sqrt(dir.x*dir.x + dir.y*dir.y);
|
||||
const Vector2 control = (Vector2){ screenPos.x + dir.x/d*handleLength, screenPos.y + dir.y/d*handleLength };
|
||||
const Rectangle controlRect = (Rectangle){ control.x - handleSize/2.0f, control.y - handleSize/2.0f, handleSize, handleSize };
|
||||
|
||||
Color controlColor = { 0 };
|
||||
|
||||
if (state->editLeftTangent)
|
||||
{
|
||||
controlColor = GetColor(GuiGetStyle(DEFAULT, BASE_COLOR_PRESSED));
|
||||
}
|
||||
else if (CheckCollisionPointRec(mouse, controlRect))
|
||||
{
|
||||
controlColor = GetColor(GuiGetStyle(DEFAULT, BASE_COLOR_FOCUSED));
|
||||
}
|
||||
else
|
||||
{
|
||||
controlColor = GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL));
|
||||
}
|
||||
|
||||
DrawLine(screenPos.x,screenPos.y, control.x, control.y, controlColor);
|
||||
DrawRectangle(controlRect.x, controlRect.y, controlRect.width, controlRect.height, controlColor);
|
||||
DrawRectangleLines(controlRect.x, controlRect.y, controlRect.width, controlRect.height, controlColor);
|
||||
}
|
||||
|
||||
// Draw right handle
|
||||
if (i < state->numPoints - 1)
|
||||
{
|
||||
const Vector2 target = (Vector2){ (p->position.x + 1)*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - (p->position.y + p->tangents.y)*innerBounds.height };
|
||||
const Vector2 dir = (Vector2){ target.x - screenPos.x, target.y - screenPos.y };
|
||||
const float d = sqrt(dir.x*dir.x + dir.y*dir.y);
|
||||
const Vector2 control = (Vector2){ screenPos.x + dir.x/d*handleLength, screenPos.y + dir.y/d*handleLength };
|
||||
const Rectangle controlRect = (Rectangle){ control.x - handleSize/2.0f, control.y - handleSize/2.0f, handleSize, handleSize };
|
||||
|
||||
Color controlColor = { 0 };
|
||||
|
||||
if (state->editRightTangent)
|
||||
{
|
||||
controlColor = GetColor(GuiGetStyle(DEFAULT, BASE_COLOR_PRESSED));
|
||||
}
|
||||
else if (CheckCollisionPointRec(mouse, controlRect))
|
||||
{
|
||||
controlColor = GetColor(GuiGetStyle(DEFAULT, BASE_COLOR_FOCUSED));
|
||||
}
|
||||
else
|
||||
{
|
||||
controlColor = GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL));
|
||||
}
|
||||
|
||||
DrawLine(screenPos.x,screenPos.y, control.x, control.y, controlColor);
|
||||
DrawRectangle(controlRect.x, controlRect.y, controlRect.width, controlRect.height, controlColor);
|
||||
DrawRectangleLines(controlRect.x, controlRect.y, controlRect.width, controlRect.height, controlColor);
|
||||
}
|
||||
|
||||
pointColor = GetColor(GuiGetStyle(DEFAULT, BASE_COLOR_PRESSED));
|
||||
pointBorderColor = GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_NORMAL));
|
||||
|
||||
}
|
||||
else if (&state->points[hoveredPointIndex] == p)
|
||||
{
|
||||
pointColor = GetColor(GuiGetStyle(DEFAULT, BASE_COLOR_FOCUSED));
|
||||
pointBorderColor = GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_NORMAL));
|
||||
}
|
||||
else
|
||||
{
|
||||
pointColor = GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL));
|
||||
pointBorderColor = GetColor(GuiGetStyle(BUTTON, BORDER_COLOR_NORMAL));
|
||||
}
|
||||
|
||||
DrawRectangle(pointRect.x, pointRect.y, pointRect.width, pointRect.height, pointColor);
|
||||
DrawRectangleLines(pointRect.x, pointRect.y, pointRect.width, pointRect.height, pointBorderColor);
|
||||
}
|
||||
|
||||
// Draw curve
|
||||
Color curveColor = GetColor(GuiGetStyle(LABEL, TEXT_COLOR_FOCUSED));
|
||||
|
||||
if (state->numPoints == 1)
|
||||
{
|
||||
const GuiCurveEditorPoint *p = sortedPoints[0];
|
||||
const Vector2 screenPos = (Vector2){ p->position.x*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - p->position.y*innerBounds.height };
|
||||
DrawLine(innerBounds.x, screenPos.y, innerBounds.x+innerBounds.width, screenPos.y, curveColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < state->numPoints - 1; i++)
|
||||
{
|
||||
const GuiCurveEditorPoint *p1 = sortedPoints[i];
|
||||
const GuiCurveEditorPoint *p2 = sortedPoints[i + 1];
|
||||
const Vector2 screenPos1 = (Vector2){ p1->position.x*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - p1->position.y*innerBounds.height };
|
||||
const Vector2 screenPos2 = (Vector2){ p2->position.x*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - p2->position.y*innerBounds.height };
|
||||
|
||||
// Constant on edge
|
||||
if ((screenPos1.x > innerBounds.x) && (i == 0))
|
||||
{
|
||||
DrawLine(innerBounds.x, screenPos1.y, screenPos1.x, screenPos1.y, curveColor);
|
||||
}
|
||||
if ((screenPos2.x < innerBounds.x + innerBounds.width) && (i == (state->numPoints - 2)))
|
||||
{
|
||||
DrawLine(screenPos2.x, screenPos2.y, innerBounds.x+innerBounds.width, screenPos2.y, curveColor);
|
||||
}
|
||||
|
||||
// Draw cubic Hermite curve
|
||||
const float scale = (p2->position.x - p1->position.x)/3.0f;
|
||||
const Vector2 offset1 = (Vector2){ scale, scale*p1->tangents.y };
|
||||
// negative endTangent => top part => need to invert value to calculate offset
|
||||
const Vector2 offset2 = (Vector2){ -scale, -scale*p2->tangents.x };
|
||||
|
||||
const Vector2 c1 = (Vector2){ p1->position.x + offset1.x, p1->position.y + offset1.y };
|
||||
const Vector2 c2 = (Vector2){ p2->position.x + offset2.x, p2->position.y + offset2.y };
|
||||
|
||||
const Vector2 screenC1 = (Vector2){ c1.x*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - c1.y*innerBounds.height };
|
||||
const Vector2 screenC2 = (Vector2){ c2.x*innerBounds.width + innerBounds.x, innerBounds.y + innerBounds.height - c2.y*innerBounds.height };
|
||||
|
||||
DrawSplineSegmentBezierCubic(screenPos1, screenC1, screenC2, screenPos2, 1, curveColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GUI_CURVE_EDITOR_IMPLEMENTATION
|
||||
|
||||
Reference in New Issue
Block a user