rshapes.c (92430B)
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 //---------------------------------------------------------------------------------- 82 static Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (white pixel loaded by rlgl) 83 static 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 //---------------------------------------------------------------------------------- 88 static 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 97 void 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 115 Texture2D GetShapesTexture(void) 116 { 117 return texShapes; 118 } 119 120 // Get texture source rectangle that is used for shapes drawing 121 Rectangle GetShapesTextureRectangle(void) 122 { 123 return texShapesRec; 124 } 125 126 // Draw a pixel 127 void DrawPixel(int posX, int posY, Color color) 128 { 129 DrawPixelV((Vector2){ (float)posX, (float)posY }, color); 130 } 131 132 // Draw a pixel (Vector version) 133 void 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) 177 void 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) 187 void 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) 197 void 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 213 void 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 251 void 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 273 void 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 280 void 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 286 void 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 377 void 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 433 void 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 449 void 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) 455 void 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 470 void 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 484 void 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 497 void 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 588 void 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 663 void 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 670 void 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 676 void 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 682 void 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 763 void 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 769 void 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 775 void 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 808 void 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 853 void 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 884 void 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() 1111 void 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 1117 void 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 1355 void 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 1390 void 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 1408 void 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 1439 void 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) 1466 void 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 1515 void 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 1534 void 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 1593 void 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 1710 void 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 1787 void 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...] 1854 void 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...] 1866 void 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 1878 void 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 1902 void 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 1959 void 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 2005 void 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 2051 void 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 2097 Vector2 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 2108 Vector2 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 2132 Vector2 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 2148 Vector2 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 2163 Vector2 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 2183 bool 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 2193 bool 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) 2205 bool 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 2224 bool 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 2244 bool 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 2255 bool 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 2272 bool 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 2297 bool 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] 2326 bool 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] 2346 RLAPI 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 2370 Rectangle 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 2400 static 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