shapes_splines_drawing.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*******************************************************************************************
  2. *
  3. * raylib [shapes] example - splines drawing
  4. *
  5. * Example originally created with raylib 5.0, last time updated with raylib 5.0
  6. *
  7. * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
  8. * BSD-like license that allows static linking with closed source software
  9. *
  10. * Copyright (c) 2023 Ramon Santamaria (@raysan5)
  11. *
  12. ********************************************************************************************/
  13. #include "raylib.h"
  14. #define RAYGUI_IMPLEMENTATION
  15. #include "raygui.h" // Required for UI controls
  16. #include <stdlib.h> // Required for: NULL
  17. #define MAX_SPLINE_POINTS 32
  18. // Cubic Bezier spline control points
  19. // NOTE: Every segment has two control points
  20. typedef struct {
  21. Vector2 start;
  22. Vector2 end;
  23. } ControlPoint;
  24. // Spline types
  25. typedef enum {
  26. SPLINE_LINEAR = 0, // Linear
  27. SPLINE_BASIS, // B-Spline
  28. SPLINE_CATMULLROM, // Catmull-Rom
  29. SPLINE_BEZIER // Cubic Bezier
  30. } SplineType;
  31. //------------------------------------------------------------------------------------
  32. // Program main entry point
  33. //------------------------------------------------------------------------------------
  34. int main(void)
  35. {
  36. // Initialization
  37. //--------------------------------------------------------------------------------------
  38. const int screenWidth = 800;
  39. const int screenHeight = 450;
  40. SetConfigFlags(FLAG_MSAA_4X_HINT);
  41. InitWindow(screenWidth, screenHeight, "raylib [shapes] example - splines drawing");
  42. Vector2 points[MAX_SPLINE_POINTS] = {
  43. { 50.0f, 400.0f },
  44. { 160.0f, 220.0f },
  45. { 340.0f, 380.0f },
  46. { 520.0f, 60.0f },
  47. { 710.0f, 260.0f },
  48. };
  49. int pointCount = 5;
  50. int selectedPoint = -1;
  51. int focusedPoint = -1;
  52. Vector2 *selectedControlPoint = NULL;
  53. Vector2 *focusedControlPoint = NULL;
  54. // Cubic Bezier control points initialization
  55. ControlPoint control[MAX_SPLINE_POINTS] = { 0 };
  56. for (int i = 0; i < pointCount - 1; i++)
  57. {
  58. control[i].start = (Vector2){ points[i].x + 50, points[i].y };
  59. control[i].end = (Vector2){ points[i + 1].x - 50, points[i + 1].y };
  60. }
  61. // Spline config variables
  62. float splineThickness = 8.0f;
  63. int splineTypeActive = SPLINE_LINEAR; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier
  64. bool splineTypeEditMode = false;
  65. bool splineHelpersActive = true;
  66. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  67. //--------------------------------------------------------------------------------------
  68. // Main game loop
  69. while (!WindowShouldClose()) // Detect window close button or ESC key
  70. {
  71. // Update
  72. //----------------------------------------------------------------------------------
  73. // Spline points creation logic (at the end of spline)
  74. if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON) && (pointCount < MAX_SPLINE_POINTS))
  75. {
  76. points[pointCount] = GetMousePosition();
  77. pointCount++;
  78. }
  79. // Spline point focus and selection logic
  80. for (int i = 0; i < pointCount; i++)
  81. {
  82. if (CheckCollisionPointCircle(GetMousePosition(), points[i], 8.0f))
  83. {
  84. focusedPoint = i;
  85. if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedPoint = i;
  86. break;
  87. }
  88. else focusedPoint = -1;
  89. }
  90. // Spline point movement logic
  91. if (selectedPoint >= 0)
  92. {
  93. points[selectedPoint] = GetMousePosition();
  94. if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedPoint = -1;
  95. }
  96. // Cubic Bezier spline control points logic
  97. if ((splineTypeActive == SPLINE_BEZIER) && (focusedPoint == -1))
  98. {
  99. // Spline control point focus and selection logic
  100. for (int i = 0; i < pointCount; i++)
  101. {
  102. if (CheckCollisionPointCircle(GetMousePosition(), control[i].start, 6.0f))
  103. {
  104. focusedControlPoint = &control[i].start;
  105. if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedControlPoint = &control[i].start;
  106. break;
  107. }
  108. else if (CheckCollisionPointCircle(GetMousePosition(), control[i].end, 6.0f))
  109. {
  110. focusedControlPoint = &control[i].end;
  111. if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedControlPoint = &control[i].end;
  112. break;
  113. }
  114. else focusedControlPoint = NULL;
  115. }
  116. // Spline control point movement logic
  117. if (selectedControlPoint != NULL)
  118. {
  119. *selectedControlPoint = GetMousePosition();
  120. if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedControlPoint = NULL;
  121. }
  122. }
  123. // Spline selection logic
  124. if (IsKeyPressed(KEY_ONE)) splineTypeActive = 0;
  125. else if (IsKeyPressed(KEY_TWO)) splineTypeActive = 1;
  126. else if (IsKeyPressed(KEY_THREE)) splineTypeActive = 2;
  127. else if (IsKeyPressed(KEY_FOUR)) splineTypeActive = 3;
  128. //----------------------------------------------------------------------------------
  129. // Draw
  130. //----------------------------------------------------------------------------------
  131. BeginDrawing();
  132. ClearBackground(RAYWHITE);
  133. if (splineTypeActive == SPLINE_LINEAR)
  134. {
  135. // Draw spline: linear
  136. DrawSplineLinear(points, pointCount, splineThickness, RED);
  137. }
  138. else if (splineTypeActive == SPLINE_BASIS)
  139. {
  140. // Draw spline: basis
  141. DrawSplineBasis(points, pointCount, splineThickness, RED); // Provide connected points array
  142. /*
  143. for (int i = 0; i < (pointCount - 3); i++)
  144. {
  145. // Drawing individual segments, not considering thickness connection compensation
  146. DrawSplineSegmentBasis(points[i], points[i + 1], points[i + 2], points[i + 3], splineThickness, MAROON);
  147. }
  148. */
  149. }
  150. else if (splineTypeActive == SPLINE_CATMULLROM)
  151. {
  152. // Draw spline: catmull-rom
  153. DrawSplineCatmullRom(points, pointCount, splineThickness, RED); // Provide connected points array
  154. /*
  155. for (int i = 0; i < (pointCount - 3); i++)
  156. {
  157. // Drawing individual segments, not considering thickness connection compensation
  158. DrawSplineSegmentCatmullRom(points[i], points[i + 1], points[i + 2], points[i + 3], splineThickness, MAROON);
  159. }
  160. */
  161. }
  162. else if (splineTypeActive == SPLINE_BEZIER)
  163. {
  164. // Draw spline: cubic-bezier (with control points)
  165. for (int i = 0; i < pointCount - 1; i++)
  166. {
  167. // Drawing individual segments, not considering thickness connection compensation
  168. DrawSplineSegmentBezierCubic(points[i], control[i].start, control[i].end, points[i + 1], splineThickness, RED);
  169. // Every cubic bezier point should have two control points
  170. DrawCircleV(control[i].start, 6, GOLD);
  171. DrawCircleV(control[i].end, 6, GOLD);
  172. if (focusedControlPoint == &control[i].start) DrawCircleV(control[i].start, 8, GREEN);
  173. else if (focusedControlPoint == &control[i].end) DrawCircleV(control[i].end, 8, GREEN);
  174. DrawLineEx(points[i], control[i].start, 1.0f, LIGHTGRAY);
  175. DrawLineEx(points[i + 1], control[i].end, 1.0f, LIGHTGRAY);
  176. // Draw spline control lines
  177. DrawLineV(points[i], control[i].start, GRAY);
  178. //DrawLineV(control[i].start, control[i].end, LIGHTGRAY);
  179. DrawLineV(control[i].end, points[i + 1], GRAY);
  180. }
  181. }
  182. if (splineHelpersActive)
  183. {
  184. // Draw spline point helpers
  185. for (int i = 0; i < pointCount; i++)
  186. {
  187. DrawCircleLinesV(points[i], (focusedPoint == i)? 12.0f : 8.0f, (focusedPoint == i)? BLUE: DARKBLUE);
  188. if ((splineTypeActive != SPLINE_LINEAR) &&
  189. (splineTypeActive != SPLINE_BEZIER) &&
  190. (i < pointCount - 1)) DrawLineV(points[i], points[i + 1], GRAY);
  191. DrawText(TextFormat("[%.0f, %.0f]", points[i].x, points[i].y), points[i].x, points[i].y + 10, 10, BLACK);
  192. }
  193. }
  194. // Check all possible UI states that require controls lock
  195. if (splineTypeEditMode) GuiLock();
  196. // Draw spline config
  197. GuiLabel((Rectangle){ 12, 62, 140, 24 }, TextFormat("Spline thickness: %i", (int)splineThickness));
  198. GuiSliderBar((Rectangle){ 12, 60 + 24, 140, 16 }, NULL, NULL, &splineThickness, 1.0f, 40.0f);
  199. GuiCheckBox((Rectangle){ 12, 110, 20, 20 }, "Show point helpers", &splineHelpersActive);
  200. GuiUnlock();
  201. GuiLabel((Rectangle){ 12, 10, 140, 24 }, "Spline type:");
  202. if (GuiDropdownBox((Rectangle){ 12, 8 + 24, 140, 28 }, "LINEAR;BSPLINE;CATMULLROM;BEZIER", &splineTypeActive, splineTypeEditMode)) splineTypeEditMode = !splineTypeEditMode;
  203. EndDrawing();
  204. //----------------------------------------------------------------------------------
  205. }
  206. // De-Initialization
  207. //--------------------------------------------------------------------------------------
  208. CloseWindow(); // Close window and OpenGL context
  209. //--------------------------------------------------------------------------------------
  210. return 0;
  211. }