aboutsummaryrefslogtreecommitdiff
path: root/raylib/src/rshapes.c
diff options
context:
space:
mode:
Diffstat (limited to 'raylib/src/rshapes.c')
-rw-r--r--raylib/src/rshapes.c2414
1 files changed, 2414 insertions, 0 deletions
diff --git a/raylib/src/rshapes.c b/raylib/src/rshapes.c
new file mode 100644
index 0000000..ece5513
--- /dev/null
+++ b/raylib/src/rshapes.c
@@ -0,0 +1,2414 @@
1/**********************************************************************************************
2*
3* rshapes - Basic functions to draw 2d shapes and check collisions
4*
5* ADDITIONAL NOTES:
6* Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS.
7* Some functions implement two drawing options: TRIANGLES and QUADS, by default TRIANGLES
8* are used but QUADS implementation can be selected with SUPPORT_QUADS_DRAW_MODE define
9*
10* Some functions define texture coordinates (rlTexCoord2f()) for the shapes and use a
11* user-provided texture with SetShapesTexture(), the pourpouse of this implementation
12* is allowing to reduce draw calls when combined with a texture-atlas.
13*
14* By default, raylib sets the default texture and rectangle at InitWindow()[rcore] to one
15* white character of default font [rtext], this way, raylib text and shapes can be draw with
16* a single draw call and it also allows users to configure it the same way with their own fonts.
17*
18* CONFIGURATION:
19* #define SUPPORT_MODULE_RSHAPES
20* rshapes module is included in the build
21*
22* #define SUPPORT_QUADS_DRAW_MODE
23* Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES
24*
25*
26* LICENSE: zlib/libpng
27*
28* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
29*
30* This software is provided "as-is", without any express or implied warranty. In no event
31* will the authors be held liable for any damages arising from the use of this software.
32*
33* Permission is granted to anyone to use this software for any purpose, including commercial
34* applications, and to alter it and redistribute it freely, subject to the following restrictions:
35*
36* 1. The origin of this software must not be misrepresented; you must not claim that you
37* wrote the original software. If you use this software in a product, an acknowledgment
38* in the product documentation would be appreciated but is not required.
39*
40* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
41* as being the original software.
42*
43* 3. This notice may not be removed or altered from any source distribution.
44*
45**********************************************************************************************/
46
47#include "raylib.h" // Declares module functions
48
49// Check if config flags have been externally provided on compilation line
50#if !defined(EXTERNAL_CONFIG_FLAGS)
51 #include "config.h" // Defines module configuration flags
52#endif
53
54#if defined(SUPPORT_MODULE_RSHAPES)
55
56#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
57
58#include <math.h> // Required for: sinf(), asinf(), cosf(), acosf(), sqrtf(), fabsf()
59#include <float.h> // Required for: FLT_EPSILON
60#include <stdlib.h> // Required for: RL_FREE
61
62//----------------------------------------------------------------------------------
63// Defines and Macros
64//----------------------------------------------------------------------------------
65// Error rate to calculate how many segments we need to draw a smooth circle,
66// taken from https://stackoverflow.com/a/2244088
67#ifndef SMOOTH_CIRCLE_ERROR_RATE
68 #define SMOOTH_CIRCLE_ERROR_RATE 0.5f // Circle error rate
69#endif
70#ifndef SPLINE_SEGMENT_DIVISIONS
71 #define SPLINE_SEGMENT_DIVISIONS 24 // Spline segment divisions
72#endif
73
74//----------------------------------------------------------------------------------
75// Types and Structures Definition
76//----------------------------------------------------------------------------------
77// Not here...
78
79//----------------------------------------------------------------------------------
80// Global Variables Definition
81//----------------------------------------------------------------------------------
82static Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (white pixel loaded by rlgl)
83static Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing
84
85//----------------------------------------------------------------------------------
86// Module specific Functions Declaration
87//----------------------------------------------------------------------------------
88static float EaseCubicInOut(float t, float b, float c, float d); // Cubic easing
89
90//----------------------------------------------------------------------------------
91// Module Functions Definition
92//----------------------------------------------------------------------------------
93
94// Set texture and rectangle to be used on shapes drawing
95// NOTE: It can be useful when using basic shapes and one single font,
96// defining a font char white rectangle would allow drawing everything in a single draw call
97void SetShapesTexture(Texture2D texture, Rectangle source)
98{
99 // Reset texture to default pixel if required
100 // WARNING: Shapes texture should be probably better validated,
101 // it can break the rendering of all shapes if misused
102 if ((texture.id == 0) || (source.width == 0) || (source.height == 0))
103 {
104 texShapes = (Texture2D){ 1, 1, 1, 1, 7 };
105 texShapesRec = (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f };
106 }
107 else
108 {
109 texShapes = texture;
110 texShapesRec = source;
111 }
112}
113
114// Get texture that is used for shapes drawing
115Texture2D GetShapesTexture(void)
116{
117 return texShapes;
118}
119
120// Get texture source rectangle that is used for shapes drawing
121Rectangle GetShapesTextureRectangle(void)
122{
123 return texShapesRec;
124}
125
126// Draw a pixel
127void DrawPixel(int posX, int posY, Color color)
128{
129 DrawPixelV((Vector2){ (float)posX, (float)posY }, color);
130}
131
132// Draw a pixel (Vector version)
133void DrawPixelV(Vector2 position, Color color)
134{
135#if defined(SUPPORT_QUADS_DRAW_MODE)
136 rlSetTexture(GetShapesTexture().id);
137 Rectangle shapeRect = GetShapesTextureRectangle();
138
139 rlBegin(RL_QUADS);
140
141 rlNormal3f(0.0f, 0.0f, 1.0f);
142 rlColor4ub(color.r, color.g, color.b, color.a);
143
144 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
145 rlVertex2f(position.x, position.y);
146
147 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
148 rlVertex2f(position.x, position.y + 1);
149
150 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
151 rlVertex2f(position.x + 1, position.y + 1);
152
153 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
154 rlVertex2f(position.x + 1, position.y);
155
156 rlEnd();
157
158 rlSetTexture(0);
159#else
160 rlBegin(RL_TRIANGLES);
161
162 rlColor4ub(color.r, color.g, color.b, color.a);
163
164 rlVertex2f(position.x, position.y);
165 rlVertex2f(position.x, position.y + 1);
166 rlVertex2f(position.x + 1, position.y);
167
168 rlVertex2f(position.x + 1, position.y);
169 rlVertex2f(position.x, position.y + 1);
170 rlVertex2f(position.x + 1, position.y + 1);
171
172 rlEnd();
173#endif
174}
175
176// Draw a line (using gl lines)
177void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color)
178{
179 rlBegin(RL_LINES);
180 rlColor4ub(color.r, color.g, color.b, color.a);
181 rlVertex2f((float)startPosX, (float)startPosY);
182 rlVertex2f((float)endPosX, (float)endPosY);
183 rlEnd();
184}
185
186// Draw a line (using gl lines)
187void DrawLineV(Vector2 startPos, Vector2 endPos, Color color)
188{
189 rlBegin(RL_LINES);
190 rlColor4ub(color.r, color.g, color.b, color.a);
191 rlVertex2f(startPos.x, startPos.y);
192 rlVertex2f(endPos.x, endPos.y);
193 rlEnd();
194}
195
196// Draw lines sequuence (using gl lines)
197void DrawLineStrip(const Vector2 *points, int pointCount, Color color)
198{
199 if (pointCount < 2) return; // Security check
200
201 rlBegin(RL_LINES);
202 rlColor4ub(color.r, color.g, color.b, color.a);
203
204 for (int i = 0; i < pointCount - 1; i++)
205 {
206 rlVertex2f(points[i].x, points[i].y);
207 rlVertex2f(points[i + 1].x, points[i + 1].y);
208 }
209 rlEnd();
210}
211
212// Draw line using cubic-bezier spline, in-out interpolation, no control points
213void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
214{
215 Vector2 previous = startPos;
216 Vector2 current = { 0 };
217
218 Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
219
220 for (int i = 1; i <= SPLINE_SEGMENT_DIVISIONS; i++)
221 {
222 // Cubic easing in-out
223 // NOTE: Easing is calculated only for y position value
224 current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)SPLINE_SEGMENT_DIVISIONS);
225 current.x = previous.x + (endPos.x - startPos.x)/(float)SPLINE_SEGMENT_DIVISIONS;
226
227 float dy = current.y - previous.y;
228 float dx = current.x - previous.x;
229 float size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
230
231 if (i == 1)
232 {
233 points[0].x = previous.x + dy*size;
234 points[0].y = previous.y - dx*size;
235 points[1].x = previous.x - dy*size;
236 points[1].y = previous.y + dx*size;
237 }
238
239 points[2*i + 1].x = current.x - dy*size;
240 points[2*i + 1].y = current.y + dx*size;
241 points[2*i].x = current.x + dy*size;
242 points[2*i].y = current.y - dx*size;
243
244 previous = current;
245 }
246
247 DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
248}
249
250// Draw a line defining thickness
251void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
252{
253 Vector2 delta = { endPos.x - startPos.x, endPos.y - startPos.y };
254 float length = sqrtf(delta.x*delta.x + delta.y*delta.y);
255
256 if ((length > 0) && (thick > 0))
257 {
258 float scale = thick/(2*length);
259
260 Vector2 radius = { -scale*delta.y, scale*delta.x };
261 Vector2 strip[4] = {
262 { startPos.x - radius.x, startPos.y - radius.y },
263 { startPos.x + radius.x, startPos.y + radius.y },
264 { endPos.x - radius.x, endPos.y - radius.y },
265 { endPos.x + radius.x, endPos.y + radius.y }
266 };
267
268 DrawTriangleStrip(strip, 4, color);
269 }
270}
271
272// Draw a color-filled circle
273void DrawCircle(int centerX, int centerY, float radius, Color color)
274{
275 DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color);
276}
277
278// Draw a color-filled circle (Vector version)
279// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues
280void DrawCircleV(Vector2 center, float radius, Color color)
281{
282 DrawCircleSector(center, radius, 0, 360, 36, color);
283}
284
285// Draw a piece of a circle
286void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
287{
288 if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero
289
290 // Function expects (endAngle > startAngle)
291 if (endAngle < startAngle)
292 {
293 // Swap values
294 float tmp = startAngle;
295 startAngle = endAngle;
296 endAngle = tmp;
297 }
298
299 int minSegments = (int)ceilf((endAngle - startAngle)/90);
300
301 if (segments < minSegments)
302 {
303 // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
304 float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1);
305 segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360);
306
307 if (segments <= 0) segments = minSegments;
308 }
309
310 float stepLength = (endAngle - startAngle)/(float)segments;
311 float angle = startAngle;
312
313#if defined(SUPPORT_QUADS_DRAW_MODE)
314 rlSetTexture(GetShapesTexture().id);
315 Rectangle shapeRect = GetShapesTextureRectangle();
316
317 rlBegin(RL_QUADS);
318
319 // NOTE: Every QUAD actually represents two segments
320 for (int i = 0; i < segments/2; i++)
321 {
322 rlColor4ub(color.r, color.g, color.b, color.a);
323
324 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
325 rlVertex2f(center.x, center.y);
326
327 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
328 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius);
329
330 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
331 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
332
333 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
334 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
335
336 angle += (stepLength*2.0f);
337 }
338
339 // NOTE: In case number of segments is odd, we add one last piece to the cake
340 if ((((unsigned int)segments)%2) == 1)
341 {
342 rlColor4ub(color.r, color.g, color.b, color.a);
343
344 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
345 rlVertex2f(center.x, center.y);
346
347 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
348 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
349
350 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
351 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
352
353 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
354 rlVertex2f(center.x, center.y);
355 }
356
357 rlEnd();
358
359 rlSetTexture(0);
360#else
361 rlBegin(RL_TRIANGLES);
362 for (int i = 0; i < segments; i++)
363 {
364 rlColor4ub(color.r, color.g, color.b, color.a);
365
366 rlVertex2f(center.x, center.y);
367 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
368 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
369
370 angle += stepLength;
371 }
372 rlEnd();
373#endif
374}
375
376// Draw a piece of a circle outlines
377void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
378{
379 if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero issue
380
381 // Function expects (endAngle > startAngle)
382 if (endAngle < startAngle)
383 {
384 // Swap values
385 float tmp = startAngle;
386 startAngle = endAngle;
387 endAngle = tmp;
388 }
389
390 int minSegments = (int)ceilf((endAngle - startAngle)/90);
391
392 if (segments < minSegments)
393 {
394 // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
395 float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1);
396 segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360);
397
398 if (segments <= 0) segments = minSegments;
399 }
400
401 float stepLength = (endAngle - startAngle)/(float)segments;
402 float angle = startAngle;
403 bool showCapLines = true;
404
405 rlBegin(RL_LINES);
406 if (showCapLines)
407 {
408 rlColor4ub(color.r, color.g, color.b, color.a);
409 rlVertex2f(center.x, center.y);
410 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
411 }
412
413 for (int i = 0; i < segments; i++)
414 {
415 rlColor4ub(color.r, color.g, color.b, color.a);
416
417 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
418 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
419
420 angle += stepLength;
421 }
422
423 if (showCapLines)
424 {
425 rlColor4ub(color.r, color.g, color.b, color.a);
426 rlVertex2f(center.x, center.y);
427 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
428 }
429 rlEnd();
430}
431
432// Draw a gradient-filled circle
433void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer)
434{
435 rlBegin(RL_TRIANGLES);
436 for (int i = 0; i < 360; i += 10)
437 {
438 rlColor4ub(inner.r, inner.g, inner.b, inner.a);
439 rlVertex2f((float)centerX, (float)centerY);
440 rlColor4ub(outer.r, outer.g, outer.b, outer.a);
441 rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radius, (float)centerY + sinf(DEG2RAD*(i + 10))*radius);
442 rlColor4ub(outer.r, outer.g, outer.b, outer.a);
443 rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radius, (float)centerY + sinf(DEG2RAD*i)*radius);
444 }
445 rlEnd();
446}
447
448// Draw circle outline
449void DrawCircleLines(int centerX, int centerY, float radius, Color color)
450{
451 DrawCircleLinesV((Vector2){ (float)centerX, (float)centerY }, radius, color);
452}
453
454// Draw circle outline (Vector version)
455void DrawCircleLinesV(Vector2 center, float radius, Color color)
456{
457 rlBegin(RL_LINES);
458 rlColor4ub(color.r, color.g, color.b, color.a);
459
460 // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360)
461 for (int i = 0; i < 360; i += 10)
462 {
463 rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius);
464 rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius);
465 }
466 rlEnd();
467}
468
469// Draw ellipse
470void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color)
471{
472 rlBegin(RL_TRIANGLES);
473 for (int i = 0; i < 360; i += 10)
474 {
475 rlColor4ub(color.r, color.g, color.b, color.a);
476 rlVertex2f((float)centerX, (float)centerY);
477 rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radiusH, (float)centerY + sinf(DEG2RAD*(i + 10))*radiusV);
478 rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radiusH, (float)centerY + sinf(DEG2RAD*i)*radiusV);
479 }
480 rlEnd();
481}
482
483// Draw ellipse outline
484void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color)
485{
486 rlBegin(RL_LINES);
487 for (int i = 0; i < 360; i += 10)
488 {
489 rlColor4ub(color.r, color.g, color.b, color.a);
490 rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radiusH, centerY + sinf(DEG2RAD*(i + 10))*radiusV);
491 rlVertex2f(centerX + cosf(DEG2RAD*i)*radiusH, centerY + sinf(DEG2RAD*i)*radiusV);
492 }
493 rlEnd();
494}
495
496// Draw ring
497void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color)
498{
499 if (startAngle == endAngle) return;
500
501 // Function expects (outerRadius > innerRadius)
502 if (outerRadius < innerRadius)
503 {
504 float tmp = outerRadius;
505 outerRadius = innerRadius;
506 innerRadius = tmp;
507
508 if (outerRadius <= 0.0f) outerRadius = 0.1f;
509 }
510
511 // Function expects (endAngle > startAngle)
512 if (endAngle < startAngle)
513 {
514 // Swap values
515 float tmp = startAngle;
516 startAngle = endAngle;
517 endAngle = tmp;
518 }
519
520 int minSegments = (int)ceilf((endAngle - startAngle)/90);
521
522 if (segments < minSegments)
523 {
524 // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
525 float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/outerRadius, 2) - 1);
526 segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360);
527
528 if (segments <= 0) segments = minSegments;
529 }
530
531 // Not a ring
532 if (innerRadius <= 0.0f)
533 {
534 DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, color);
535 return;
536 }
537
538 float stepLength = (endAngle - startAngle)/(float)segments;
539 float angle = startAngle;
540
541#if defined(SUPPORT_QUADS_DRAW_MODE)
542 rlSetTexture(GetShapesTexture().id);
543 Rectangle shapeRect = GetShapesTextureRectangle();
544
545 rlBegin(RL_QUADS);
546 for (int i = 0; i < segments; i++)
547 {
548 rlColor4ub(color.r, color.g, color.b, color.a);
549
550 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
551 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
552
553 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
554 rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
555
556 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
557 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
558
559 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
560 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
561
562 angle += stepLength;
563 }
564 rlEnd();
565
566 rlSetTexture(0);
567#else
568 rlBegin(RL_TRIANGLES);
569 for (int i = 0; i < segments; i++)
570 {
571 rlColor4ub(color.r, color.g, color.b, color.a);
572
573 rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
574 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
575 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
576
577 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
578 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
579 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
580
581 angle += stepLength;
582 }
583 rlEnd();
584#endif
585}
586
587// Draw ring outline
588void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color)
589{
590 if (startAngle == endAngle) return;
591
592 // Function expects (outerRadius > innerRadius)
593 if (outerRadius < innerRadius)
594 {
595 float tmp = outerRadius;
596 outerRadius = innerRadius;
597 innerRadius = tmp;
598
599 if (outerRadius <= 0.0f) outerRadius = 0.1f;
600 }
601
602 // Function expects (endAngle > startAngle)
603 if (endAngle < startAngle)
604 {
605 // Swap values
606 float tmp = startAngle;
607 startAngle = endAngle;
608 endAngle = tmp;
609 }
610
611 int minSegments = (int)ceilf((endAngle - startAngle)/90);
612
613 if (segments < minSegments)
614 {
615 // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
616 float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/outerRadius, 2) - 1);
617 segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360);
618
619 if (segments <= 0) segments = minSegments;
620 }
621
622 if (innerRadius <= 0.0f)
623 {
624 DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, color);
625 return;
626 }
627
628 float stepLength = (endAngle - startAngle)/(float)segments;
629 float angle = startAngle;
630 bool showCapLines = true;
631
632 rlBegin(RL_LINES);
633 if (showCapLines)
634 {
635 rlColor4ub(color.r, color.g, color.b, color.a);
636 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
637 rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
638 }
639
640 for (int i = 0; i < segments; i++)
641 {
642 rlColor4ub(color.r, color.g, color.b, color.a);
643
644 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
645 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
646
647 rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
648 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
649
650 angle += stepLength;
651 }
652
653 if (showCapLines)
654 {
655 rlColor4ub(color.r, color.g, color.b, color.a);
656 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
657 rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
658 }
659 rlEnd();
660}
661
662// Draw a color-filled rectangle
663void DrawRectangle(int posX, int posY, int width, int height, Color color)
664{
665 DrawRectangleV((Vector2){ (float)posX, (float)posY }, (Vector2){ (float)width, (float)height }, color);
666}
667
668// Draw a color-filled rectangle (Vector version)
669// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues
670void DrawRectangleV(Vector2 position, Vector2 size, Color color)
671{
672 DrawRectanglePro((Rectangle){ position.x, position.y, size.x, size.y }, (Vector2){ 0.0f, 0.0f }, 0.0f, color);
673}
674
675// Draw a color-filled rectangle
676void DrawRectangleRec(Rectangle rec, Color color)
677{
678 DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, color);
679}
680
681// Draw a color-filled rectangle with pro parameters
682void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color)
683{
684 Vector2 topLeft = { 0 };
685 Vector2 topRight = { 0 };
686 Vector2 bottomLeft = { 0 };
687 Vector2 bottomRight = { 0 };
688
689 // Only calculate rotation if needed
690 if (rotation == 0.0f)
691 {
692 float x = rec.x - origin.x;
693 float y = rec.y - origin.y;
694 topLeft = (Vector2){ x, y };
695 topRight = (Vector2){ x + rec.width, y };
696 bottomLeft = (Vector2){ x, y + rec.height };
697 bottomRight = (Vector2){ x + rec.width, y + rec.height };
698 }
699 else
700 {
701 float sinRotation = sinf(rotation*DEG2RAD);
702 float cosRotation = cosf(rotation*DEG2RAD);
703 float x = rec.x;
704 float y = rec.y;
705 float dx = -origin.x;
706 float dy = -origin.y;
707
708 topLeft.x = x + dx*cosRotation - dy*sinRotation;
709 topLeft.y = y + dx*sinRotation + dy*cosRotation;
710
711 topRight.x = x + (dx + rec.width)*cosRotation - dy*sinRotation;
712 topRight.y = y + (dx + rec.width)*sinRotation + dy*cosRotation;
713
714 bottomLeft.x = x + dx*cosRotation - (dy + rec.height)*sinRotation;
715 bottomLeft.y = y + dx*sinRotation + (dy + rec.height)*cosRotation;
716
717 bottomRight.x = x + (dx + rec.width)*cosRotation - (dy + rec.height)*sinRotation;
718 bottomRight.y = y + (dx + rec.width)*sinRotation + (dy + rec.height)*cosRotation;
719 }
720
721#if defined(SUPPORT_QUADS_DRAW_MODE)
722 rlSetTexture(GetShapesTexture().id);
723 Rectangle shapeRect = GetShapesTextureRectangle();
724
725 rlBegin(RL_QUADS);
726
727 rlNormal3f(0.0f, 0.0f, 1.0f);
728 rlColor4ub(color.r, color.g, color.b, color.a);
729
730 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
731 rlVertex2f(topLeft.x, topLeft.y);
732
733 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
734 rlVertex2f(bottomLeft.x, bottomLeft.y);
735
736 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
737 rlVertex2f(bottomRight.x, bottomRight.y);
738
739 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
740 rlVertex2f(topRight.x, topRight.y);
741
742 rlEnd();
743
744 rlSetTexture(0);
745#else
746 rlBegin(RL_TRIANGLES);
747
748 rlColor4ub(color.r, color.g, color.b, color.a);
749
750 rlVertex2f(topLeft.x, topLeft.y);
751 rlVertex2f(bottomLeft.x, bottomLeft.y);
752 rlVertex2f(topRight.x, topRight.y);
753
754 rlVertex2f(topRight.x, topRight.y);
755 rlVertex2f(bottomLeft.x, bottomLeft.y);
756 rlVertex2f(bottomRight.x, bottomRight.y);
757
758 rlEnd();
759#endif
760}
761
762// Draw a vertical-gradient-filled rectangle
763void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom)
764{
765 DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, top, bottom, bottom, top);
766}
767
768// Draw a horizontal-gradient-filled rectangle
769void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right)
770{
771 DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, left, left, right, right);
772}
773
774// Draw a gradient-filled rectangle
775void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight)
776{
777 rlSetTexture(GetShapesTexture().id);
778 Rectangle shapeRect = GetShapesTextureRectangle();
779
780 rlBegin(RL_QUADS);
781 rlNormal3f(0.0f, 0.0f, 1.0f);
782
783 // NOTE: Default raylib font character 95 is a white square
784 rlColor4ub(topLeft.r, topLeft.g, topLeft.b, topLeft.a);
785 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
786 rlVertex2f(rec.x, rec.y);
787
788 rlColor4ub(bottomLeft.r, bottomLeft.g, bottomLeft.b, bottomLeft.a);
789 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
790 rlVertex2f(rec.x, rec.y + rec.height);
791
792 rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a);
793 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
794 rlVertex2f(rec.x + rec.width, rec.y + rec.height);
795
796 rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a);
797 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
798 rlVertex2f(rec.x + rec.width, rec.y);
799 rlEnd();
800
801 rlSetTexture(0);
802}
803
804// Draw rectangle outline
805// WARNING: All Draw*Lines() functions use RL_LINES for drawing,
806// it implies flushing the current batch and changing draw mode to RL_LINES
807// but it solves another issue: https://github.com/raysan5/raylib/issues/3884
808void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
809{
810 Matrix mat = rlGetMatrixModelview();
811 float zoomFactor = 0.5f/mat.m0;
812 rlBegin(RL_LINES);
813 rlColor4ub(color.r, color.g, color.b, color.a);
814 rlVertex2f((float)posX - zoomFactor, (float)posY);
815 rlVertex2f((float)posX + (float)width + zoomFactor, (float)posY);
816
817 rlVertex2f((float)posX + (float)width, (float)posY - zoomFactor);
818 rlVertex2f((float)posX + (float)width, (float)posY + (float)height + zoomFactor);
819
820 rlVertex2f((float)posX + (float)width + zoomFactor, (float)posY + (float)height);
821 rlVertex2f((float)posX - zoomFactor, (float)posY + (float)height);
822
823 rlVertex2f((float)posX, (float)posY + (float)height + zoomFactor);
824 rlVertex2f((float)posX, (float)posY - zoomFactor);
825 rlEnd();
826/*
827// Previous implementation, it has issues... but it does not require view matrix...
828#if defined(SUPPORT_QUADS_DRAW_MODE)
829 DrawRectangle(posX, posY, width, 1, color);
830 DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
831 DrawRectangle(posX, posY + height - 1, width, 1, color);
832 DrawRectangle(posX, posY + 1, 1, height - 2, color);
833#else
834 rlBegin(RL_LINES);
835 rlColor4ub(color.r, color.g, color.b, color.a);
836 rlVertex2f((float)posX, (float)posY);
837 rlVertex2f((float)posX + (float)width, (float)posY + 1);
838
839 rlVertex2f((float)posX + (float)width, (float)posY + 1);
840 rlVertex2f((float)posX + (float)width, (float)posY + (float)height);
841
842 rlVertex2f((float)posX + (float)width, (float)posY + (float)height);
843 rlVertex2f((float)posX + 1, (float)posY + (float)height);
844
845 rlVertex2f((float)posX + 1, (float)posY + (float)height);
846 rlVertex2f((float)posX + 1, (float)posY + 1);
847 rlEnd();
848//#endif
849*/
850}
851
852// Draw rectangle outline with extended parameters
853void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color)
854{
855 if ((lineThick > rec.width) || (lineThick > rec.height))
856 {
857 if (rec.width >= rec.height) lineThick = rec.height/2;
858 else if (rec.width <= rec.height) lineThick = rec.width/2;
859 }
860
861 // When rec = { x, y, 8.0f, 6.0f } and lineThick = 2, the following
862 // four rectangles are drawn ([T]op, [B]ottom, [L]eft, [R]ight):
863 //
864 // TTTTTTTT
865 // TTTTTTTT
866 // LL RR
867 // LL RR
868 // BBBBBBBB
869 // BBBBBBBB
870 //
871
872 Rectangle top = { rec.x, rec.y, rec.width, lineThick };
873 Rectangle bottom = { rec.x, rec.y - lineThick + rec.height, rec.width, lineThick };
874 Rectangle left = { rec.x, rec.y + lineThick, lineThick, rec.height - lineThick*2.0f };
875 Rectangle right = { rec.x - lineThick + rec.width, rec.y + lineThick, lineThick, rec.height - lineThick*2.0f };
876
877 DrawRectangleRec(top, color);
878 DrawRectangleRec(bottom, color);
879 DrawRectangleRec(left, color);
880 DrawRectangleRec(right, color);
881}
882
883// Draw rectangle with rounded edges
884void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color)
885{
886 // Not a rounded rectangle
887 if ((roundness <= 0.0f) || (rec.width < 1) || (rec.height < 1 ))
888 {
889 DrawRectangleRec(rec, color);
890 return;
891 }
892
893 if (roundness >= 1.0f) roundness = 1.0f;
894
895 // Calculate corner radius
896 float radius = (rec.width > rec.height)? (rec.height*roundness)/2 : (rec.width*roundness)/2;
897 if (radius <= 0.0f) return;
898
899 // Calculate number of segments to use for the corners
900 if (segments < 4)
901 {
902 // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
903 float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1);
904 segments = (int)(ceilf(2*PI/th)/4.0f);
905 if (segments <= 0) segments = 4;
906 }
907
908 float stepLength = 90.0f/(float)segments;
909
910 /*
911 Quick sketch to make sense of all of this,
912 there are 9 parts to draw, also mark the 12 points we'll use
913
914 P0____________________P1
915 /| |\
916 /1| 2 |3\
917 P7 /__|____________________|__\ P2
918 | |P8 P9| |
919 | 8 | 9 | 4 |
920 | __|____________________|__ |
921 P6 \ |P11 P10| / P3
922 \7| 6 |5/
923 \|____________________|/
924 P5 P4
925 */
926 // Coordinates of the 12 points that define the rounded rect
927 const Vector2 point[12] = {
928 {(float)rec.x + radius, rec.y}, {(float)(rec.x + rec.width) - radius, rec.y}, { rec.x + rec.width, (float)rec.y + radius }, // PO, P1, P2
929 {rec.x + rec.width, (float)(rec.y + rec.height) - radius}, {(float)(rec.x + rec.width) - radius, rec.y + rec.height}, // P3, P4
930 {(float)rec.x + radius, rec.y + rec.height}, { rec.x, (float)(rec.y + rec.height) - radius}, {rec.x, (float)rec.y + radius}, // P5, P6, P7
931 {(float)rec.x + radius, (float)rec.y + radius}, {(float)(rec.x + rec.width) - radius, (float)rec.y + radius}, // P8, P9
932 {(float)(rec.x + rec.width) - radius, (float)(rec.y + rec.height) - radius}, {(float)rec.x + radius, (float)(rec.y + rec.height) - radius} // P10, P11
933 };
934
935 const Vector2 centers[4] = { point[8], point[9], point[10], point[11] };
936 const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
937
938#if defined(SUPPORT_QUADS_DRAW_MODE)
939 rlSetTexture(GetShapesTexture().id);
940 Rectangle shapeRect = GetShapesTextureRectangle();
941
942 rlBegin(RL_QUADS);
943 // Draw all the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
944 for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
945 {
946 float angle = angles[k];
947 const Vector2 center = centers[k];
948
949 // NOTE: Every QUAD actually represents two segments
950 for (int i = 0; i < segments/2; i++)
951 {
952 rlColor4ub(color.r, color.g, color.b, color.a);
953 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
954 rlVertex2f(center.x, center.y);
955
956 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
957 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius);
958
959 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
960 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
961
962 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
963 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
964
965 angle += (stepLength*2);
966 }
967
968 // NOTE: In case number of segments is odd, we add one last piece to the cake
969 if (segments%2)
970 {
971 rlColor4ub(color.r, color.g, color.b, color.a);
972 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
973 rlVertex2f(center.x, center.y);
974
975 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
976 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
977
978 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
979 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
980
981 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
982 rlVertex2f(center.x, center.y);
983 }
984 }
985
986 // [2] Upper Rectangle
987 rlColor4ub(color.r, color.g, color.b, color.a);
988 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
989 rlVertex2f(point[0].x, point[0].y);
990 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
991 rlVertex2f(point[8].x, point[8].y);
992 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
993 rlVertex2f(point[9].x, point[9].y);
994 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
995 rlVertex2f(point[1].x, point[1].y);
996
997 // [4] Right Rectangle
998 rlColor4ub(color.r, color.g, color.b, color.a);
999 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1000 rlVertex2f(point[2].x, point[2].y);
1001 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1002 rlVertex2f(point[9].x, point[9].y);
1003 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1004 rlVertex2f(point[10].x, point[10].y);
1005 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1006 rlVertex2f(point[3].x, point[3].y);
1007
1008 // [6] Bottom Rectangle
1009 rlColor4ub(color.r, color.g, color.b, color.a);
1010 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1011 rlVertex2f(point[11].x, point[11].y);
1012 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1013 rlVertex2f(point[5].x, point[5].y);
1014 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1015 rlVertex2f(point[4].x, point[4].y);
1016 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1017 rlVertex2f(point[10].x, point[10].y);
1018
1019 // [8] Left Rectangle
1020 rlColor4ub(color.r, color.g, color.b, color.a);
1021 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1022 rlVertex2f(point[7].x, point[7].y);
1023 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1024 rlVertex2f(point[6].x, point[6].y);
1025 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1026 rlVertex2f(point[11].x, point[11].y);
1027 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1028 rlVertex2f(point[8].x, point[8].y);
1029
1030 // [9] Middle Rectangle
1031 rlColor4ub(color.r, color.g, color.b, color.a);
1032 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1033 rlVertex2f(point[8].x, point[8].y);
1034 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1035 rlVertex2f(point[11].x, point[11].y);
1036 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1037 rlVertex2f(point[10].x, point[10].y);
1038 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1039 rlVertex2f(point[9].x, point[9].y);
1040
1041 rlEnd();
1042 rlSetTexture(0);
1043#else
1044 rlBegin(RL_TRIANGLES);
1045
1046 // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
1047 for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
1048 {
1049 float angle = angles[k];
1050 const Vector2 center = centers[k];
1051 for (int i = 0; i < segments; i++)
1052 {
1053 rlColor4ub(color.r, color.g, color.b, color.a);
1054 rlVertex2f(center.x, center.y);
1055 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
1056 rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
1057 angle += stepLength;
1058 }
1059 }
1060
1061 // [2] Upper Rectangle
1062 rlColor4ub(color.r, color.g, color.b, color.a);
1063 rlVertex2f(point[0].x, point[0].y);
1064 rlVertex2f(point[8].x, point[8].y);
1065 rlVertex2f(point[9].x, point[9].y);
1066 rlVertex2f(point[1].x, point[1].y);
1067 rlVertex2f(point[0].x, point[0].y);
1068 rlVertex2f(point[9].x, point[9].y);
1069
1070 // [4] Right Rectangle
1071 rlColor4ub(color.r, color.g, color.b, color.a);
1072 rlVertex2f(point[9].x, point[9].y);
1073 rlVertex2f(point[10].x, point[10].y);
1074 rlVertex2f(point[3].x, point[3].y);
1075 rlVertex2f(point[2].x, point[2].y);
1076 rlVertex2f(point[9].x, point[9].y);
1077 rlVertex2f(point[3].x, point[3].y);
1078
1079 // [6] Bottom Rectangle
1080 rlColor4ub(color.r, color.g, color.b, color.a);
1081 rlVertex2f(point[11].x, point[11].y);
1082 rlVertex2f(point[5].x, point[5].y);
1083 rlVertex2f(point[4].x, point[4].y);
1084 rlVertex2f(point[10].x, point[10].y);
1085 rlVertex2f(point[11].x, point[11].y);
1086 rlVertex2f(point[4].x, point[4].y);
1087
1088 // [8] Left Rectangle
1089 rlColor4ub(color.r, color.g, color.b, color.a);
1090 rlVertex2f(point[7].x, point[7].y);
1091 rlVertex2f(point[6].x, point[6].y);
1092 rlVertex2f(point[11].x, point[11].y);
1093 rlVertex2f(point[8].x, point[8].y);
1094 rlVertex2f(point[7].x, point[7].y);
1095 rlVertex2f(point[11].x, point[11].y);
1096
1097 // [9] Middle Rectangle
1098 rlColor4ub(color.r, color.g, color.b, color.a);
1099 rlVertex2f(point[8].x, point[8].y);
1100 rlVertex2f(point[11].x, point[11].y);
1101 rlVertex2f(point[10].x, point[10].y);
1102 rlVertex2f(point[9].x, point[9].y);
1103 rlVertex2f(point[8].x, point[8].y);
1104 rlVertex2f(point[10].x, point[10].y);
1105 rlEnd();
1106#endif
1107}
1108
1109// Draw rectangle with rounded edges
1110// TODO: This function should be refactored to use RL_LINES, for consistency with other Draw*Lines()
1111void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color)
1112{
1113 DrawRectangleRoundedLinesEx(rec, roundness, segments, 1.0f, color);
1114}
1115
1116// Draw rectangle with rounded edges outline
1117void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float lineThick, Color color)
1118{
1119 if (lineThick < 0) lineThick = 0;
1120
1121 // Not a rounded rectangle
1122 if (roundness <= 0.0f)
1123 {
1124 DrawRectangleLinesEx((Rectangle){rec.x-lineThick, rec.y-lineThick, rec.width+2*lineThick, rec.height+2*lineThick}, lineThick, color);
1125 return;
1126 }
1127
1128 if (roundness >= 1.0f) roundness = 1.0f;
1129
1130 // Calculate corner radius
1131 float radius = (rec.width > rec.height)? (rec.height*roundness)/2 : (rec.width*roundness)/2;
1132 if (radius <= 0.0f) return;
1133
1134 // Calculate number of segments to use for the corners
1135 if (segments < 4)
1136 {
1137 // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
1138 float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1);
1139 segments = (int)(ceilf(2*PI/th)/2.0f);
1140 if (segments <= 0) segments = 4;
1141 }
1142
1143 float stepLength = 90.0f/(float)segments;
1144 const float outerRadius = radius + lineThick, innerRadius = radius;
1145
1146 /*
1147 Quick sketch to make sense of all of this,
1148 marks the 16 + 4(corner centers P16-19) points we'll use
1149
1150 P0 ================== P1
1151 // P8 P9 \\
1152 // \\
1153 P7 // P15 P10 \\ P2
1154 || *P16 P17* ||
1155 || ||
1156 || P14 P11 ||
1157 P6 \\ *P19 P18* // P3
1158 \\ //
1159 \\ P13 P12 //
1160 P5 ================== P4
1161 */
1162 const Vector2 point[16] = {
1163 {(float)rec.x + innerRadius, rec.y - lineThick}, {(float)(rec.x + rec.width) - innerRadius, rec.y - lineThick}, { rec.x + rec.width + lineThick, (float)rec.y + innerRadius }, // PO, P1, P2
1164 {rec.x + rec.width + lineThick, (float)(rec.y + rec.height) - innerRadius}, {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height + lineThick}, // P3, P4
1165 {(float)rec.x + innerRadius, rec.y + rec.height + lineThick}, { rec.x - lineThick, (float)(rec.y + rec.height) - innerRadius}, {rec.x - lineThick, (float)rec.y + innerRadius}, // P5, P6, P7
1166 {(float)rec.x + innerRadius, rec.y}, {(float)(rec.x + rec.width) - innerRadius, rec.y}, // P8, P9
1167 { rec.x + rec.width, (float)rec.y + innerRadius }, {rec.x + rec.width, (float)(rec.y + rec.height) - innerRadius}, // P10, P11
1168 {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height}, {(float)rec.x + innerRadius, rec.y + rec.height}, // P12, P13
1169 { rec.x, (float)(rec.y + rec.height) - innerRadius}, {rec.x, (float)rec.y + innerRadius} // P14, P15
1170 };
1171
1172 const Vector2 centers[4] = {
1173 {(float)rec.x + innerRadius, (float)rec.y + innerRadius}, {(float)(rec.x + rec.width) - innerRadius, (float)rec.y + innerRadius}, // P16, P17
1174 {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19
1175 };
1176
1177 const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
1178
1179 if (lineThick > 1)
1180 {
1181#if defined(SUPPORT_QUADS_DRAW_MODE)
1182 rlSetTexture(GetShapesTexture().id);
1183 Rectangle shapeRect = GetShapesTextureRectangle();
1184
1185 rlBegin(RL_QUADS);
1186
1187 // Draw all the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
1188 for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
1189 {
1190 float angle = angles[k];
1191 const Vector2 center = centers[k];
1192 for (int i = 0; i < segments; i++)
1193 {
1194 rlColor4ub(color.r, color.g, color.b, color.a);
1195
1196 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1197 rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
1198
1199 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1200 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
1201
1202 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1203 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
1204
1205 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1206 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
1207
1208 angle += stepLength;
1209 }
1210 }
1211
1212 // Upper rectangle
1213 rlColor4ub(color.r, color.g, color.b, color.a);
1214 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1215 rlVertex2f(point[0].x, point[0].y);
1216 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1217 rlVertex2f(point[8].x, point[8].y);
1218 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1219 rlVertex2f(point[9].x, point[9].y);
1220 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1221 rlVertex2f(point[1].x, point[1].y);
1222
1223 // Right rectangle
1224 rlColor4ub(color.r, color.g, color.b, color.a);
1225 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1226 rlVertex2f(point[2].x, point[2].y);
1227 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1228 rlVertex2f(point[10].x, point[10].y);
1229 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1230 rlVertex2f(point[11].x, point[11].y);
1231 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1232 rlVertex2f(point[3].x, point[3].y);
1233
1234 // Lower rectangle
1235 rlColor4ub(color.r, color.g, color.b, color.a);
1236 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1237 rlVertex2f(point[13].x, point[13].y);
1238 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1239 rlVertex2f(point[5].x, point[5].y);
1240 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1241 rlVertex2f(point[4].x, point[4].y);
1242 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1243 rlVertex2f(point[12].x, point[12].y);
1244
1245 // Left rectangle
1246 rlColor4ub(color.r, color.g, color.b, color.a);
1247 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1248 rlVertex2f(point[15].x, point[15].y);
1249 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1250 rlVertex2f(point[7].x, point[7].y);
1251 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1252 rlVertex2f(point[6].x, point[6].y);
1253 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1254 rlVertex2f(point[14].x, point[14].y);
1255
1256 rlEnd();
1257 rlSetTexture(0);
1258#else
1259 rlBegin(RL_TRIANGLES);
1260
1261 // Draw all of the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
1262 for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
1263 {
1264 float angle = angles[k];
1265 const Vector2 center = centers[k];
1266
1267 for (int i = 0; i < segments; i++)
1268 {
1269 rlColor4ub(color.r, color.g, color.b, color.a);
1270
1271 rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius);
1272 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
1273 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
1274
1275 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius);
1276 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
1277 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
1278
1279 angle += stepLength;
1280 }
1281 }
1282
1283 // Upper rectangle
1284 rlColor4ub(color.r, color.g, color.b, color.a);
1285 rlVertex2f(point[0].x, point[0].y);
1286 rlVertex2f(point[8].x, point[8].y);
1287 rlVertex2f(point[9].x, point[9].y);
1288 rlVertex2f(point[1].x, point[1].y);
1289 rlVertex2f(point[0].x, point[0].y);
1290 rlVertex2f(point[9].x, point[9].y);
1291
1292 // Right rectangle
1293 rlColor4ub(color.r, color.g, color.b, color.a);
1294 rlVertex2f(point[10].x, point[10].y);
1295 rlVertex2f(point[11].x, point[11].y);
1296 rlVertex2f(point[3].x, point[3].y);
1297 rlVertex2f(point[2].x, point[2].y);
1298 rlVertex2f(point[10].x, point[10].y);
1299 rlVertex2f(point[3].x, point[3].y);
1300
1301 // Lower rectangle
1302 rlColor4ub(color.r, color.g, color.b, color.a);
1303 rlVertex2f(point[13].x, point[13].y);
1304 rlVertex2f(point[5].x, point[5].y);
1305 rlVertex2f(point[4].x, point[4].y);
1306 rlVertex2f(point[12].x, point[12].y);
1307 rlVertex2f(point[13].x, point[13].y);
1308 rlVertex2f(point[4].x, point[4].y);
1309
1310 // Left rectangle
1311 rlColor4ub(color.r, color.g, color.b, color.a);
1312 rlVertex2f(point[7].x, point[7].y);
1313 rlVertex2f(point[6].x, point[6].y);
1314 rlVertex2f(point[14].x, point[14].y);
1315 rlVertex2f(point[15].x, point[15].y);
1316 rlVertex2f(point[7].x, point[7].y);
1317 rlVertex2f(point[14].x, point[14].y);
1318 rlEnd();
1319#endif
1320 }
1321 else
1322 {
1323 // Use LINES to draw the outline
1324 rlBegin(RL_LINES);
1325
1326 // Draw all the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
1327 for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
1328 {
1329 float angle = angles[k];
1330 const Vector2 center = centers[k];
1331
1332 for (int i = 0; i < segments; i++)
1333 {
1334 rlColor4ub(color.r, color.g, color.b, color.a);
1335 rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
1336 rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
1337 angle += stepLength;
1338 }
1339 }
1340
1341 // And now the remaining 4 lines
1342 for (int i = 0; i < 8; i += 2)
1343 {
1344 rlColor4ub(color.r, color.g, color.b, color.a);
1345 rlVertex2f(point[i].x, point[i].y);
1346 rlVertex2f(point[i + 1].x, point[i + 1].y);
1347 }
1348
1349 rlEnd();
1350 }
1351}
1352
1353// Draw a triangle
1354// NOTE: Vertex must be provided in counter-clockwise order
1355void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
1356{
1357#if defined(SUPPORT_QUADS_DRAW_MODE)
1358 rlSetTexture(GetShapesTexture().id);
1359 Rectangle shapeRect = GetShapesTextureRectangle();
1360
1361 rlBegin(RL_QUADS);
1362 rlColor4ub(color.r, color.g, color.b, color.a);
1363
1364 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1365 rlVertex2f(v1.x, v1.y);
1366
1367 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1368 rlVertex2f(v2.x, v2.y);
1369
1370 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1371 rlVertex2f(v2.x, v2.y);
1372
1373 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1374 rlVertex2f(v3.x, v3.y);
1375 rlEnd();
1376
1377 rlSetTexture(0);
1378#else
1379 rlBegin(RL_TRIANGLES);
1380 rlColor4ub(color.r, color.g, color.b, color.a);
1381 rlVertex2f(v1.x, v1.y);
1382 rlVertex2f(v2.x, v2.y);
1383 rlVertex2f(v3.x, v3.y);
1384 rlEnd();
1385#endif
1386}
1387
1388// Draw a triangle using lines
1389// NOTE: Vertex must be provided in counter-clockwise order
1390void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
1391{
1392 rlBegin(RL_LINES);
1393 rlColor4ub(color.r, color.g, color.b, color.a);
1394 rlVertex2f(v1.x, v1.y);
1395 rlVertex2f(v2.x, v2.y);
1396
1397 rlVertex2f(v2.x, v2.y);
1398 rlVertex2f(v3.x, v3.y);
1399
1400 rlVertex2f(v3.x, v3.y);
1401 rlVertex2f(v1.x, v1.y);
1402 rlEnd();
1403}
1404
1405// Draw a triangle fan defined by points
1406// NOTE: First vertex provided is the center, shared by all triangles
1407// By default, following vertex should be provided in counter-clockwise order
1408void DrawTriangleFan(const Vector2 *points, int pointCount, Color color)
1409{
1410 if (pointCount >= 3)
1411 {
1412 rlSetTexture(GetShapesTexture().id);
1413 Rectangle shapeRect = GetShapesTextureRectangle();
1414
1415 rlBegin(RL_QUADS);
1416 rlColor4ub(color.r, color.g, color.b, color.a);
1417
1418 for (int i = 1; i < pointCount - 1; i++)
1419 {
1420 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1421 rlVertex2f(points[0].x, points[0].y);
1422
1423 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1424 rlVertex2f(points[i].x, points[i].y);
1425
1426 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1427 rlVertex2f(points[i + 1].x, points[i + 1].y);
1428
1429 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1430 rlVertex2f(points[i + 1].x, points[i + 1].y);
1431 }
1432 rlEnd();
1433 rlSetTexture(0);
1434 }
1435}
1436
1437// Draw a triangle strip defined by points
1438// NOTE: Every new vertex connects with previous two
1439void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color)
1440{
1441 if (pointCount >= 3)
1442 {
1443 rlBegin(RL_TRIANGLES);
1444 rlColor4ub(color.r, color.g, color.b, color.a);
1445
1446 for (int i = 2; i < pointCount; i++)
1447 {
1448 if ((i%2) == 0)
1449 {
1450 rlVertex2f(points[i].x, points[i].y);
1451 rlVertex2f(points[i - 2].x, points[i - 2].y);
1452 rlVertex2f(points[i - 1].x, points[i - 1].y);
1453 }
1454 else
1455 {
1456 rlVertex2f(points[i].x, points[i].y);
1457 rlVertex2f(points[i - 1].x, points[i - 1].y);
1458 rlVertex2f(points[i - 2].x, points[i - 2].y);
1459 }
1460 }
1461 rlEnd();
1462 }
1463}
1464
1465// Draw a regular polygon of n sides (Vector version)
1466void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color)
1467{
1468 if (sides < 3) sides = 3;
1469 float centralAngle = rotation*DEG2RAD;
1470 float angleStep = 360.0f/(float)sides*DEG2RAD;
1471
1472#if defined(SUPPORT_QUADS_DRAW_MODE)
1473 rlSetTexture(GetShapesTexture().id);
1474 Rectangle shapeRect = GetShapesTextureRectangle();
1475
1476 rlBegin(RL_QUADS);
1477 for (int i = 0; i < sides; i++)
1478 {
1479 rlColor4ub(color.r, color.g, color.b, color.a);
1480 float nextAngle = centralAngle + angleStep;
1481
1482 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1483 rlVertex2f(center.x, center.y);
1484
1485 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1486 rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
1487
1488 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1489 rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
1490
1491 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1492 rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
1493
1494 centralAngle = nextAngle;
1495 }
1496 rlEnd();
1497 rlSetTexture(0);
1498#else
1499 rlBegin(RL_TRIANGLES);
1500 for (int i = 0; i < sides; i++)
1501 {
1502 rlColor4ub(color.r, color.g, color.b, color.a);
1503
1504 rlVertex2f(center.x, center.y);
1505 rlVertex2f(center.x + cosf(centralAngle + angleStep)*radius, center.y + sinf(centralAngle + angleStep)*radius);
1506 rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
1507
1508 centralAngle += angleStep;
1509 }
1510 rlEnd();
1511#endif
1512}
1513
1514// Draw a polygon outline of n sides
1515void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color)
1516{
1517 if (sides < 3) sides = 3;
1518 float centralAngle = rotation*DEG2RAD;
1519 float angleStep = 360.0f/(float)sides*DEG2RAD;
1520
1521 rlBegin(RL_LINES);
1522 for (int i = 0; i < sides; i++)
1523 {
1524 rlColor4ub(color.r, color.g, color.b, color.a);
1525
1526 rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
1527 rlVertex2f(center.x + cosf(centralAngle + angleStep)*radius, center.y + sinf(centralAngle + angleStep)*radius);
1528
1529 centralAngle += angleStep;
1530 }
1531 rlEnd();
1532}
1533
1534void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color)
1535{
1536 if (sides < 3) sides = 3;
1537 float centralAngle = rotation*DEG2RAD;
1538 float exteriorAngle = 360.0f/(float)sides*DEG2RAD;
1539 float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f));
1540
1541#if defined(SUPPORT_QUADS_DRAW_MODE)
1542 rlSetTexture(GetShapesTexture().id);
1543 Rectangle shapeRect = GetShapesTextureRectangle();
1544
1545 rlBegin(RL_QUADS);
1546 for (int i = 0; i < sides; i++)
1547 {
1548 rlColor4ub(color.r, color.g, color.b, color.a);
1549 float nextAngle = centralAngle + exteriorAngle;
1550
1551 rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1552 rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
1553
1554 rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
1555 rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius);
1556
1557 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
1558 rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius);
1559
1560 rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
1561 rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
1562
1563 centralAngle = nextAngle;
1564 }
1565 rlEnd();
1566 rlSetTexture(0);
1567#else
1568 rlBegin(RL_TRIANGLES);
1569 for (int i = 0; i < sides; i++)
1570 {
1571 rlColor4ub(color.r, color.g, color.b, color.a);
1572 float nextAngle = centralAngle + exteriorAngle;
1573
1574 rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
1575 rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius);
1576 rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius);
1577
1578 rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius);
1579 rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius);
1580 rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius);
1581
1582 centralAngle = nextAngle;
1583 }
1584 rlEnd();
1585#endif
1586}
1587
1588//----------------------------------------------------------------------------------
1589// Module Functions Definition - Splines functions
1590//----------------------------------------------------------------------------------
1591
1592// Draw spline: linear, minimum 2 points
1593void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color)
1594{
1595 if (pointCount < 2) return;
1596
1597#if defined(SUPPORT_SPLINE_MITERS)
1598 Vector2 prevNormal = (Vector2){-(points[1].y - points[0].y), (points[1].x - points[0].x)};
1599 float prevLength = sqrtf(prevNormal.x*prevNormal.x + prevNormal.y*prevNormal.y);
1600
1601 if (prevLength > 0.0f)
1602 {
1603 prevNormal.x /= prevLength;
1604 prevNormal.y /= prevLength;
1605 }
1606 else
1607 {
1608 prevNormal.x = 0.0f;
1609 prevNormal.y = 0.0f;
1610 }
1611
1612 Vector2 prevRadius = { 0.5f*thick*prevNormal.x, 0.5f*thick*prevNormal.y };
1613
1614 for (int i = 0; i < pointCount - 1; i++)
1615 {
1616 Vector2 normal = { 0 };
1617
1618 if (i < pointCount - 2)
1619 {
1620 normal = (Vector2){-(points[i + 2].y - points[i + 1].y), (points[i + 2].x - points[i + 1].x)};
1621 float normalLength = sqrtf(normal.x*normal.x + normal.y*normal.y);
1622
1623 if (normalLength > 0.0f)
1624 {
1625 normal.x /= normalLength;
1626 normal.y /= normalLength;
1627 }
1628 else
1629 {
1630 normal.x = 0.0f;
1631 normal.y = 0.0f;
1632 }
1633 }
1634 else
1635 {
1636 normal = prevNormal;
1637 }
1638
1639 Vector2 radius = { prevNormal.x + normal.x, prevNormal.y + normal.y };
1640 float radiusLength = sqrtf(radius.x*radius.x + radius.y*radius.y);
1641
1642 if (radiusLength > 0.0f)
1643 {
1644 radius.x /= radiusLength;
1645 radius.y /= radiusLength;
1646 }
1647 else
1648 {
1649 radius.x = 0.0f;
1650 radius.y = 0.0f;
1651 }
1652
1653 float cosTheta = radius.x*normal.x + radius.y*normal.y;
1654
1655 if (cosTheta != 0.0f)
1656 {
1657 radius.x *= (thick*0.5f/cosTheta);
1658 radius.y *= (thick*0.5f/cosTheta);
1659 }
1660 else
1661 {
1662 radius.x = 0.0f;
1663 radius.y = 0.0f;
1664 }
1665
1666 Vector2 strip[4] = {
1667 { points[i].x - prevRadius.x, points[i].y - prevRadius.y },
1668 { points[i].x + prevRadius.x, points[i].y + prevRadius.y },
1669 { points[i + 1].x - radius.x, points[i + 1].y - radius.y },
1670 { points[i + 1].x + radius.x, points[i + 1].y + radius.y }
1671 };
1672
1673 DrawTriangleStrip(strip, 4, color);
1674
1675 prevRadius = radius;
1676 prevNormal = normal;
1677 }
1678
1679#else // !SUPPORT_SPLINE_MITERS
1680
1681 Vector2 delta = { 0 };
1682 float length = 0.0f;
1683 float scale = 0.0f;
1684
1685 for (int i = 0; i < pointCount - 1; i++)
1686 {
1687 delta = (Vector2){ points[i + 1].x - points[i].x, points[i + 1].y - points[i].y };
1688 length = sqrtf(delta.x*delta.x + delta.y*delta.y);
1689
1690 if (length > 0) scale = thick/(2*length);
1691
1692 Vector2 radius = { -scale*delta.y, scale*delta.x };
1693 Vector2 strip[4] = {
1694 { points[i].x - radius.x, points[i].y - radius.y },
1695 { points[i].x + radius.x, points[i].y + radius.y },
1696 { points[i + 1].x - radius.x, points[i + 1].y - radius.y },
1697 { points[i + 1].x + radius.x, points[i + 1].y + radius.y }
1698 };
1699
1700 DrawTriangleStrip(strip, 4, color);
1701 }
1702#endif
1703
1704#if defined(SUPPORT_SPLINE_SEGMENT_CAPS)
1705 // TODO: Add spline segment rounded caps at the begin/end of the spline
1706#endif
1707}
1708
1709// Draw spline: B-Spline, minimum 4 points
1710void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color)
1711{
1712 if (pointCount < 4) return;
1713
1714 float a[4] = { 0 };
1715 float b[4] = { 0 };
1716 float dy = 0.0f;
1717 float dx = 0.0f;
1718 float size = 0.0f;
1719
1720 Vector2 currentPoint = { 0 };
1721 Vector2 nextPoint = { 0 };
1722 Vector2 vertices[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
1723
1724 for (int i = 0; i < (pointCount - 3); i++)
1725 {
1726 float t = 0.0f;
1727 Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3];
1728
1729 a[0] = (-p1.x + 3.0f*p2.x - 3.0f*p3.x + p4.x)/6.0f;
1730 a[1] = (3.0f*p1.x - 6.0f*p2.x + 3.0f*p3.x)/6.0f;
1731 a[2] = (-3.0f*p1.x + 3.0f*p3.x)/6.0f;
1732 a[3] = (p1.x + 4.0f*p2.x + p3.x)/6.0f;
1733
1734 b[0] = (-p1.y + 3.0f*p2.y - 3.0f*p3.y + p4.y)/6.0f;
1735 b[1] = (3.0f*p1.y - 6.0f*p2.y + 3.0f*p3.y)/6.0f;
1736 b[2] = (-3.0f*p1.y + 3.0f*p3.y)/6.0f;
1737 b[3] = (p1.y + 4.0f*p2.y + p3.y)/6.0f;
1738
1739 currentPoint.x = a[3];
1740 currentPoint.y = b[3];
1741
1742 if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color); // Draw init line circle-cap
1743
1744 if (i > 0)
1745 {
1746 vertices[0].x = currentPoint.x + dy*size;
1747 vertices[0].y = currentPoint.y - dx*size;
1748 vertices[1].x = currentPoint.x - dy*size;
1749 vertices[1].y = currentPoint.y + dx*size;
1750 }
1751
1752 for (int j = 1; j <= SPLINE_SEGMENT_DIVISIONS; j++)
1753 {
1754 t = ((float)j)/((float)SPLINE_SEGMENT_DIVISIONS);
1755
1756 nextPoint.x = a[3] + t*(a[2] + t*(a[1] + t*a[0]));
1757 nextPoint.y = b[3] + t*(b[2] + t*(b[1] + t*b[0]));
1758
1759 dy = nextPoint.y - currentPoint.y;
1760 dx = nextPoint.x - currentPoint.x;
1761 size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
1762
1763 if ((i == 0) && (j == 1))
1764 {
1765 vertices[0].x = currentPoint.x + dy*size;
1766 vertices[0].y = currentPoint.y - dx*size;
1767 vertices[1].x = currentPoint.x - dy*size;
1768 vertices[1].y = currentPoint.y + dx*size;
1769 }
1770
1771 vertices[2*j + 1].x = nextPoint.x - dy*size;
1772 vertices[2*j + 1].y = nextPoint.y + dx*size;
1773 vertices[2*j].x = nextPoint.x + dy*size;
1774 vertices[2*j].y = nextPoint.y - dx*size;
1775
1776 currentPoint = nextPoint;
1777 }
1778
1779 DrawTriangleStrip(vertices, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
1780 }
1781
1782 // Cap circle drawing at the end of every segment
1783 DrawCircleV(currentPoint, thick/2.0f, color);
1784}
1785
1786// Draw spline: Catmull-Rom, minimum 4 points
1787void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color)
1788{
1789 if (pointCount < 4) return;
1790
1791 float dy = 0.0f;
1792 float dx = 0.0f;
1793 float size = 0.0f;
1794
1795 Vector2 currentPoint = points[1];
1796 Vector2 nextPoint = { 0 };
1797 Vector2 vertices[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
1798
1799 DrawCircleV(currentPoint, thick/2.0f, color); // Draw init line circle-cap
1800
1801 for (int i = 0; i < (pointCount - 3); i++)
1802 {
1803 float t = 0.0f;
1804 Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3];
1805
1806 if (i > 0)
1807 {
1808 vertices[0].x = currentPoint.x + dy*size;
1809 vertices[0].y = currentPoint.y - dx*size;
1810 vertices[1].x = currentPoint.x - dy*size;
1811 vertices[1].y = currentPoint.y + dx*size;
1812 }
1813
1814 for (int j = 1; j <= SPLINE_SEGMENT_DIVISIONS; j++)
1815 {
1816 t = ((float)j)/((float)SPLINE_SEGMENT_DIVISIONS);
1817
1818 float q0 = (-1.0f*t*t*t) + (2.0f*t*t) + (-1.0f*t);
1819 float q1 = (3.0f*t*t*t) + (-5.0f*t*t) + 2.0f;
1820 float q2 = (-3.0f*t*t*t) + (4.0f*t*t) + t;
1821 float q3 = t*t*t - t*t;
1822
1823 nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3));
1824 nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3));
1825
1826 dy = nextPoint.y - currentPoint.y;
1827 dx = nextPoint.x - currentPoint.x;
1828 size = (0.5f*thick)/sqrtf(dx*dx + dy*dy);
1829
1830 if ((i == 0) && (j == 1))
1831 {
1832 vertices[0].x = currentPoint.x + dy*size;
1833 vertices[0].y = currentPoint.y - dx*size;
1834 vertices[1].x = currentPoint.x - dy*size;
1835 vertices[1].y = currentPoint.y + dx*size;
1836 }
1837
1838 vertices[2*j + 1].x = nextPoint.x - dy*size;
1839 vertices[2*j + 1].y = nextPoint.y + dx*size;
1840 vertices[2*j].x = nextPoint.x + dy*size;
1841 vertices[2*j].y = nextPoint.y - dx*size;
1842
1843 currentPoint = nextPoint;
1844 }
1845
1846 DrawTriangleStrip(vertices, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
1847 }
1848
1849 // Cap circle drawing at the end of every segment
1850 DrawCircleV(currentPoint, thick/2.0f, color);
1851}
1852
1853// Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
1854void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color)
1855{
1856 if (pointCount >= 3)
1857 {
1858 for (int i = 0; i < pointCount - 2; i += 2) DrawSplineSegmentBezierQuadratic(points[i], points[i + 1], points[i + 2], thick, color);
1859
1860 // Cap circle drawing at the end of every segment
1861 //for (int i = 2; i < pointCount - 2; i += 2) DrawCircleV(points[i], thick/2.0f, color);
1862 }
1863}
1864
1865// Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
1866void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color)
1867{
1868 if (pointCount >= 4)
1869 {
1870 for (int i = 0; i < pointCount - 3; i += 3) DrawSplineSegmentBezierCubic(points[i], points[i + 1], points[i + 2], points[i + 3], thick, color);
1871
1872 // Cap circle drawing at the end of every segment
1873 //for (int i = 3; i < pointCount - 3; i += 3) DrawCircleV(points[i], thick/2.0f, color);
1874 }
1875}
1876
1877// Draw spline segment: Linear, 2 points
1878void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color)
1879{
1880 // NOTE: For the linear spline we don't use subdivisions, just a single quad
1881
1882 Vector2 delta = { p2.x - p1.x, p2.y - p1.y };
1883 float length = sqrtf(delta.x*delta.x + delta.y*delta.y);
1884
1885 if ((length > 0) && (thick > 0))
1886 {
1887 float scale = thick/(2*length);
1888
1889 Vector2 radius = { -scale*delta.y, scale*delta.x };
1890 Vector2 strip[4] = {
1891 { p1.x - radius.x, p1.y - radius.y },
1892 { p1.x + radius.x, p1.y + radius.y },
1893 { p2.x - radius.x, p2.y - radius.y },
1894 { p2.x + radius.x, p2.y + radius.y }
1895 };
1896
1897 DrawTriangleStrip(strip, 4, color);
1898 }
1899}
1900
1901// Draw spline segment: B-Spline, 4 points
1902void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color)
1903{
1904 const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS;
1905
1906 Vector2 currentPoint = { 0 };
1907 Vector2 nextPoint = { 0 };
1908 float t = 0.0f;
1909
1910 Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
1911
1912 float a[4] = { 0 };
1913 float b[4] = { 0 };
1914
1915 a[0] = (-p1.x + 3*p2.x - 3*p3.x + p4.x)/6.0f;
1916 a[1] = (3*p1.x - 6*p2.x + 3*p3.x)/6.0f;
1917 a[2] = (-3*p1.x + 3*p3.x)/6.0f;
1918 a[3] = (p1.x + 4*p2.x + p3.x)/6.0f;
1919
1920 b[0] = (-p1.y + 3*p2.y - 3*p3.y + p4.y)/6.0f;
1921 b[1] = (3*p1.y - 6*p2.y + 3*p3.y)/6.0f;
1922 b[2] = (-3*p1.y + 3*p3.y)/6.0f;
1923 b[3] = (p1.y + 4*p2.y + p3.y)/6.0f;
1924
1925 currentPoint.x = a[3];
1926 currentPoint.y = b[3];
1927
1928 for (int i = 0; i <= SPLINE_SEGMENT_DIVISIONS; i++)
1929 {
1930 t = step*(float)i;
1931
1932 nextPoint.x = a[3] + t*(a[2] + t*(a[1] + t*a[0]));
1933 nextPoint.y = b[3] + t*(b[2] + t*(b[1] + t*b[0]));
1934
1935 float dy = nextPoint.y - currentPoint.y;
1936 float dx = nextPoint.x - currentPoint.x;
1937 float size = (0.5f*thick)/sqrtf(dx*dx + dy*dy);
1938
1939 if (i == 1)
1940 {
1941 points[0].x = currentPoint.x + dy*size;
1942 points[0].y = currentPoint.y - dx*size;
1943 points[1].x = currentPoint.x - dy*size;
1944 points[1].y = currentPoint.y + dx*size;
1945 }
1946
1947 points[2*i + 1].x = nextPoint.x - dy*size;
1948 points[2*i + 1].y = nextPoint.y + dx*size;
1949 points[2*i].x = nextPoint.x + dy*size;
1950 points[2*i].y = nextPoint.y - dx*size;
1951
1952 currentPoint = nextPoint;
1953 }
1954
1955 DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS+2, color);
1956}
1957
1958// Draw spline segment: Catmull-Rom, 4 points
1959void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color)
1960{
1961 const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS;
1962
1963 Vector2 currentPoint = p1;
1964 Vector2 nextPoint = { 0 };
1965 float t = 0.0f;
1966
1967 Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
1968
1969 for (int i = 0; i <= SPLINE_SEGMENT_DIVISIONS; i++)
1970 {
1971 t = step*(float)i;
1972
1973 float q0 = (-1*t*t*t) + (2*t*t) + (-1*t);
1974 float q1 = (3*t*t*t) + (-5*t*t) + 2;
1975 float q2 = (-3*t*t*t) + (4*t*t) + t;
1976 float q3 = t*t*t - t*t;
1977
1978 nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3));
1979 nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3));
1980
1981 float dy = nextPoint.y - currentPoint.y;
1982 float dx = nextPoint.x - currentPoint.x;
1983 float size = (0.5f*thick)/sqrtf(dx*dx + dy*dy);
1984
1985 if (i == 1)
1986 {
1987 points[0].x = currentPoint.x + dy*size;
1988 points[0].y = currentPoint.y - dx*size;
1989 points[1].x = currentPoint.x - dy*size;
1990 points[1].y = currentPoint.y + dx*size;
1991 }
1992
1993 points[2*i + 1].x = nextPoint.x - dy*size;
1994 points[2*i + 1].y = nextPoint.y + dx*size;
1995 points[2*i].x = nextPoint.x + dy*size;
1996 points[2*i].y = nextPoint.y - dx*size;
1997
1998 currentPoint = nextPoint;
1999 }
2000
2001 DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
2002}
2003
2004// Draw spline segment: Quadratic Bezier, 2 points, 1 control point
2005void DrawSplineSegmentBezierQuadratic(Vector2 p1, Vector2 c2, Vector2 p3, float thick, Color color)
2006{
2007 const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS;
2008
2009 Vector2 previous = p1;
2010 Vector2 current = { 0 };
2011 float t = 0.0f;
2012
2013 Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
2014
2015 for (int i = 1; i <= SPLINE_SEGMENT_DIVISIONS; i++)
2016 {
2017 t = step*(float)i;
2018
2019 float a = powf(1.0f - t, 2);
2020 float b = 2.0f*(1.0f - t)*t;
2021 float c = powf(t, 2);
2022
2023 // NOTE: The easing functions aren't suitable here because they don't take a control point
2024 current.y = a*p1.y + b*c2.y + c*p3.y;
2025 current.x = a*p1.x + b*c2.x + c*p3.x;
2026
2027 float dy = current.y - previous.y;
2028 float dx = current.x - previous.x;
2029 float size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
2030
2031 if (i == 1)
2032 {
2033 points[0].x = previous.x + dy*size;
2034 points[0].y = previous.y - dx*size;
2035 points[1].x = previous.x - dy*size;
2036 points[1].y = previous.y + dx*size;
2037 }
2038
2039 points[2*i + 1].x = current.x - dy*size;
2040 points[2*i + 1].y = current.y + dx*size;
2041 points[2*i].x = current.x + dy*size;
2042 points[2*i].y = current.y - dx*size;
2043
2044 previous = current;
2045 }
2046
2047 DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
2048}
2049
2050// Draw spline segment: Cubic Bezier, 2 points, 2 control points
2051void DrawSplineSegmentBezierCubic(Vector2 p1, Vector2 c2, Vector2 c3, Vector2 p4, float thick, Color color)
2052{
2053 const float step = 1.0f/SPLINE_SEGMENT_DIVISIONS;
2054
2055 Vector2 previous = p1;
2056 Vector2 current = { 0 };
2057 float t = 0.0f;
2058
2059 Vector2 points[2*SPLINE_SEGMENT_DIVISIONS + 2] = { 0 };
2060
2061 for (int i = 1; i <= SPLINE_SEGMENT_DIVISIONS; i++)
2062 {
2063 t = step*(float)i;
2064
2065 float a = powf(1.0f - t, 3);
2066 float b = 3.0f*powf(1.0f - t, 2)*t;
2067 float c = 3.0f*(1.0f - t)*powf(t, 2);
2068 float d = powf(t, 3);
2069
2070 current.y = a*p1.y + b*c2.y + c*c3.y + d*p4.y;
2071 current.x = a*p1.x + b*c2.x + c*c3.x + d*p4.x;
2072
2073 float dy = current.y - previous.y;
2074 float dx = current.x - previous.x;
2075 float size = 0.5f*thick/sqrtf(dx*dx+dy*dy);
2076
2077 if (i == 1)
2078 {
2079 points[0].x = previous.x + dy*size;
2080 points[0].y = previous.y - dx*size;
2081 points[1].x = previous.x - dy*size;
2082 points[1].y = previous.y + dx*size;
2083 }
2084
2085 points[2*i + 1].x = current.x - dy*size;
2086 points[2*i + 1].y = current.y + dx*size;
2087 points[2*i].x = current.x + dy*size;
2088 points[2*i].y = current.y - dx*size;
2089
2090 previous = current;
2091 }
2092
2093 DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
2094}
2095
2096// Get spline point for a given t [0.0f .. 1.0f], Linear
2097Vector2 GetSplinePointLinear(Vector2 startPos, Vector2 endPos, float t)
2098{
2099 Vector2 point = { 0 };
2100
2101 point.x = startPos.x*(1.0f - t) + endPos.x*t;
2102 point.y = startPos.y*(1.0f - t) + endPos.y*t;
2103
2104 return point;
2105}
2106
2107// Get spline point for a given t [0.0f .. 1.0f], B-Spline
2108Vector2 GetSplinePointBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t)
2109{
2110 Vector2 point = { 0 };
2111
2112 float a[4] = { 0 };
2113 float b[4] = { 0 };
2114
2115 a[0] = (-p1.x + 3*p2.x - 3*p3.x + p4.x)/6.0f;
2116 a[1] = (3*p1.x - 6*p2.x + 3*p3.x)/6.0f;
2117 a[2] = (-3*p1.x + 3*p3.x)/6.0f;
2118 a[3] = (p1.x + 4*p2.x + p3.x)/6.0f;
2119
2120 b[0] = (-p1.y + 3*p2.y - 3*p3.y + p4.y)/6.0f;
2121 b[1] = (3*p1.y - 6*p2.y + 3*p3.y)/6.0f;
2122 b[2] = (-3*p1.y + 3*p3.y)/6.0f;
2123 b[3] = (p1.y + 4*p2.y + p3.y)/6.0f;
2124
2125 point.x = a[3] + t*(a[2] + t*(a[1] + t*a[0]));
2126 point.y = b[3] + t*(b[2] + t*(b[1] + t*b[0]));
2127
2128 return point;
2129}
2130
2131// Get spline point for a given t [0.0f .. 1.0f], Catmull-Rom
2132Vector2 GetSplinePointCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float t)
2133{
2134 Vector2 point = { 0 };
2135
2136 float q0 = (-1*t*t*t) + (2*t*t) + (-1*t);
2137 float q1 = (3*t*t*t) + (-5*t*t) + 2;
2138 float q2 = (-3*t*t*t) + (4*t*t) + t;
2139 float q3 = t*t*t - t*t;
2140
2141 point.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3));
2142 point.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3));
2143
2144 return point;
2145}
2146
2147// Get spline point for a given t [0.0f .. 1.0f], Quadratic Bezier
2148Vector2 GetSplinePointBezierQuad(Vector2 startPos, Vector2 controlPos, Vector2 endPos, float t)
2149{
2150 Vector2 point = { 0 };
2151
2152 float a = powf(1.0f - t, 2);
2153 float b = 2.0f*(1.0f - t)*t;
2154 float c = powf(t, 2);
2155
2156 point.y = a*startPos.y + b*controlPos.y + c*endPos.y;
2157 point.x = a*startPos.x + b*controlPos.x + c*endPos.x;
2158
2159 return point;
2160}
2161
2162// Get spline point for a given t [0.0f .. 1.0f], Cubic Bezier
2163Vector2 GetSplinePointBezierCubic(Vector2 startPos, Vector2 startControlPos, Vector2 endControlPos, Vector2 endPos, float t)
2164{
2165 Vector2 point = { 0 };
2166
2167 float a = powf(1.0f - t, 3);
2168 float b = 3.0f*powf(1.0f - t, 2)*t;
2169 float c = 3.0f*(1.0f - t)*powf(t, 2);
2170 float d = powf(t, 3);
2171
2172 point.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y;
2173 point.x = a*startPos.x + b*startControlPos.x + c*endControlPos.x + d*endPos.x;
2174
2175 return point;
2176}
2177
2178//----------------------------------------------------------------------------------
2179// Module Functions Definition - Collision Detection functions
2180//----------------------------------------------------------------------------------
2181
2182// Check if point is inside rectangle
2183bool CheckCollisionPointRec(Vector2 point, Rectangle rec)
2184{
2185 bool collision = false;
2186
2187 if ((point.x >= rec.x) && (point.x < (rec.x + rec.width)) && (point.y >= rec.y) && (point.y < (rec.y + rec.height))) collision = true;
2188
2189 return collision;
2190}
2191
2192// Check if point is inside circle
2193bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius)
2194{
2195 bool collision = false;
2196
2197 float distanceSquared = (point.x - center.x)*(point.x - center.x) + (point.y - center.y)*(point.y - center.y);
2198
2199 if (distanceSquared <= radius*radius) collision = true;
2200
2201 return collision;
2202}
2203
2204// Check if point is inside a triangle defined by three points (p1, p2, p3)
2205bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3)
2206{
2207 bool collision = false;
2208
2209 float alpha = ((p2.y - p3.y)*(point.x - p3.x) + (p3.x - p2.x)*(point.y - p3.y)) /
2210 ((p2.y - p3.y)*(p1.x - p3.x) + (p3.x - p2.x)*(p1.y - p3.y));
2211
2212 float beta = ((p3.y - p1.y)*(point.x - p3.x) + (p1.x - p3.x)*(point.y - p3.y)) /
2213 ((p2.y - p3.y)*(p1.x - p3.x) + (p3.x - p2.x)*(p1.y - p3.y));
2214
2215 float gamma = 1.0f - alpha - beta;
2216
2217 if ((alpha > 0) && (beta > 0) && (gamma > 0)) collision = true;
2218
2219 return collision;
2220}
2221
2222// Check if point is within a polygon described by array of vertices
2223// NOTE: Based on http://jeffreythompson.org/collision-detection/poly-point.php
2224bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount)
2225{
2226 bool inside = false;
2227
2228 if (pointCount > 2)
2229 {
2230 for (int i = 0, j = pointCount - 1; i < pointCount; j = i++)
2231 {
2232 if ((points[i].y > point.y) != (points[j].y > point.y) &&
2233 (point.x < (points[j].x - points[i].x)*(point.y - points[i].y)/(points[j].y - points[i].y) + points[i].x))
2234 {
2235 inside = !inside;
2236 }
2237 }
2238 }
2239
2240 return inside;
2241}
2242
2243// Check collision between two rectangles
2244bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2)
2245{
2246 bool collision = false;
2247
2248 if ((rec1.x < (rec2.x + rec2.width) && (rec1.x + rec1.width) > rec2.x) &&
2249 (rec1.y < (rec2.y + rec2.height) && (rec1.y + rec1.height) > rec2.y)) collision = true;
2250
2251 return collision;
2252}
2253
2254// Check collision between two circles
2255bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2)
2256{
2257 bool collision = false;
2258
2259 float dx = center2.x - center1.x; // X distance between centers
2260 float dy = center2.y - center1.y; // Y distance between centers
2261
2262 float distanceSquared = dx*dx + dy*dy; // Distance between centers squared
2263 float radiusSum = radius1 + radius2;
2264
2265 collision = (distanceSquared <= (radiusSum*radiusSum));
2266
2267 return collision;
2268}
2269
2270// Check collision between circle and rectangle
2271// NOTE: Reviewed version to take into account corner limit case
2272bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
2273{
2274 bool collision = false;
2275
2276 float recCenterX = rec.x + rec.width/2.0f;
2277 float recCenterY = rec.y + rec.height/2.0f;
2278
2279 float dx = fabsf(center.x - recCenterX);
2280 float dy = fabsf(center.y - recCenterY);
2281
2282 if (dx > (rec.width/2.0f + radius)) { return false; }
2283 if (dy > (rec.height/2.0f + radius)) { return false; }
2284
2285 if (dx <= (rec.width/2.0f)) { return true; }
2286 if (dy <= (rec.height/2.0f)) { return true; }
2287
2288 float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
2289 (dy - rec.height/2.0f)*(dy - rec.height/2.0f);
2290
2291 collision = (cornerDistanceSq <= (radius*radius));
2292
2293 return collision;
2294}
2295
2296// Check the collision between two lines defined by two points each, returns collision point by reference
2297bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint)
2298{
2299 bool collision = false;
2300
2301 float div = (endPos2.y - startPos2.y)*(endPos1.x - startPos1.x) - (endPos2.x - startPos2.x)*(endPos1.y - startPos1.y);
2302
2303 if (fabsf(div) >= FLT_EPSILON)
2304 {
2305 collision = true;
2306
2307 float xi = ((startPos2.x - endPos2.x)*(startPos1.x*endPos1.y - startPos1.y*endPos1.x) - (startPos1.x - endPos1.x)*(startPos2.x*endPos2.y - startPos2.y*endPos2.x))/div;
2308 float yi = ((startPos2.y - endPos2.y)*(startPos1.x*endPos1.y - startPos1.y*endPos1.x) - (startPos1.y - endPos1.y)*(startPos2.x*endPos2.y - startPos2.y*endPos2.x))/div;
2309
2310 if (((fabsf(startPos1.x - endPos1.x) > FLT_EPSILON) && (xi < fminf(startPos1.x, endPos1.x) || (xi > fmaxf(startPos1.x, endPos1.x)))) ||
2311 ((fabsf(startPos2.x - endPos2.x) > FLT_EPSILON) && (xi < fminf(startPos2.x, endPos2.x) || (xi > fmaxf(startPos2.x, endPos2.x)))) ||
2312 ((fabsf(startPos1.y - endPos1.y) > FLT_EPSILON) && (yi < fminf(startPos1.y, endPos1.y) || (yi > fmaxf(startPos1.y, endPos1.y)))) ||
2313 ((fabsf(startPos2.y - endPos2.y) > FLT_EPSILON) && (yi < fminf(startPos2.y, endPos2.y) || (yi > fmaxf(startPos2.y, endPos2.y))))) collision = false;
2314
2315 if (collision && (collisionPoint != 0))
2316 {
2317 collisionPoint->x = xi;
2318 collisionPoint->y = yi;
2319 }
2320 }
2321
2322 return collision;
2323}
2324
2325// Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
2326bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold)
2327{
2328 bool collision = false;
2329
2330 float dxc = point.x - p1.x;
2331 float dyc = point.y - p1.y;
2332 float dxl = p2.x - p1.x;
2333 float dyl = p2.y - p1.y;
2334 float cross = dxc*dyl - dyc*dxl;
2335
2336 if (fabsf(cross) < (threshold*fmaxf(fabsf(dxl), fabsf(dyl))))
2337 {
2338 if (fabsf(dxl) >= fabsf(dyl)) collision = (dxl > 0)? ((p1.x <= point.x) && (point.x <= p2.x)) : ((p2.x <= point.x) && (point.x <= p1.x));
2339 else collision = (dyl > 0)? ((p1.y <= point.y) && (point.y <= p2.y)) : ((p2.y <= point.y) && (point.y <= p1.y));
2340 }
2341
2342 return collision;
2343}
2344
2345// Check if circle collides with a line created betweeen two points [p1] and [p2]
2346RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2)
2347{
2348 float dx = p1.x - p2.x;
2349 float dy = p1.y - p2.y;
2350
2351 if ((fabsf(dx) + fabsf(dy)) <= FLT_EPSILON)
2352 {
2353 return CheckCollisionCircles(p1, 0, center, radius);
2354 }
2355
2356 float lengthSQ = ((dx*dx) + (dy*dy));
2357 float dotProduct = (((center.x - p1.x)*(p2.x - p1.x)) + ((center.y - p1.y)*(p2.y - p1.y)))/(lengthSQ);
2358
2359 if (dotProduct > 1.0f) dotProduct = 1.0f;
2360 else if (dotProduct < 0.0f) dotProduct = 0.0f;
2361
2362 float dx2 = (p1.x - (dotProduct*(dx))) - center.x;
2363 float dy2 = (p1.y - (dotProduct*(dy))) - center.y;
2364 float distanceSQ = ((dx2*dx2) + (dy2*dy2));
2365
2366 return (distanceSQ <= radius*radius);
2367}
2368
2369// Get collision rectangle for two rectangles collision
2370Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
2371{
2372 Rectangle overlap = { 0 };
2373
2374 float left = (rec1.x > rec2.x)? rec1.x : rec2.x;
2375 float right1 = rec1.x + rec1.width;
2376 float right2 = rec2.x + rec2.width;
2377 float right = (right1 < right2)? right1 : right2;
2378 float top = (rec1.y > rec2.y)? rec1.y : rec2.y;
2379 float bottom1 = rec1.y + rec1.height;
2380 float bottom2 = rec2.y + rec2.height;
2381 float bottom = (bottom1 < bottom2)? bottom1 : bottom2;
2382
2383 if ((left < right) && (top < bottom))
2384 {
2385 overlap.x = left;
2386 overlap.y = top;
2387 overlap.width = right - left;
2388 overlap.height = bottom - top;
2389 }
2390
2391 return overlap;
2392}
2393
2394//----------------------------------------------------------------------------------
2395// Module specific Functions Definition
2396//----------------------------------------------------------------------------------
2397
2398// Cubic easing in-out
2399// NOTE: Used by DrawLineBezier() only
2400static float EaseCubicInOut(float t, float b, float c, float d)
2401{
2402 float result = 0.0f;
2403
2404 if ((t /= 0.5f*d) < 1) result = 0.5f*c*t*t*t + b;
2405 else
2406 {
2407 t -= 2;
2408 result = 0.5f*c*(t*t*t + 2.0f) + b;
2409 }
2410
2411 return result;
2412}
2413
2414#endif // SUPPORT_MODULE_RSHAPES

Generated with cgit - Back to sebastiano.tronto.net