1 /++ 2 Collision checker and collision points between forms. 3 4 Macros: 5 LREF = <a href="#$1">$1</a> 6 HREF = <a href="$1">$2</a> 7 8 Authors: $(HREF https://github.com/TodNaz,TodNaz) 9 Copyright: Copyright (c) 2020 - 2021, TodNaz. 10 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 11 +/ 12 module tida.collision; 13 14 import tida.shape, tida.vector; 15 16 /++ 17 Checks if there is a collision between the lines. 18 19 Params: 20 first = First line vertexs. 21 second = Second line vertexs. 22 +/ 23 bool lineLineImpl(const Vecf[] first, const Vecf[] second) @safe nothrow pure 24 { 25 const a = first[0]; 26 const b = first[1]; 27 const c = second[0]; 28 const d = second[1]; 29 30 const denominator = ((b.x - a.x) * (d.y - c.y)) - ((b.y - a.y) * (d.x - c.x)); 31 32 const numerator1 = ((a.y - c.y) * (d.x - c.x)) - ((a.x - c.x) * (d.y - c.y)); 33 const numerator2 = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y)); 34 35 if (denominator == 0) return numerator1 == 0 && numerator2 == 0; 36 37 const r = numerator1 / denominator; 38 const s = numerator2 / denominator; 39 40 return (r >= 0 && r <= 1) && (s >= 0 && s <= 1); 41 } 42 43 /++ 44 Checks if there is a collision between a point and a line. 45 46 Params: 47 point = Point position. 48 line = Line vertexs. 49 +/ 50 bool pointLineImpl(const Vecf point, const Vecf[] line) @safe nothrow pure 51 { 52 import tida.each; 53 54 if (point == line[0] || 55 point == line[1]) 56 return true; 57 58 bool result = false; 59 60 foreach(x,y; LineNoThrowImpl(line[0], line[1])) { 61 if (cast(int) point.x == x && 62 cast(int) point.y == y) { 63 result = true; 64 break; 65 } 66 } 67 68 return result; 69 } 70 71 /++ 72 Checks if there is a collision between a point and a rectange. 73 74 Params: 75 a = Point position. 76 b = Rectangle vertexs. 77 +/ 78 bool pointRectImpl(const Vecf a, const Vecf[] b) @safe nothrow pure 79 { 80 return a.x > b[0].x && 81 a.y > b[0].y && 82 a.x < b[1].x && 83 a.y < b[1].y; 84 } 85 86 /++ 87 Checks if there is a collision between a line and a rectangle. 88 89 Params: 90 a = Line vertexs. 91 b = Rectangle vertexs. 92 +/ 93 bool lineRectImpl(const Vecf[] a, const Vecf[] b) @safe nothrow pure 94 { 95 import tida.each; 96 97 if (b[0] == a[0] || 98 b[0] == a[1] || 99 b[1] == a[0] || 100 b[1] == a[1]) 101 return true; 102 103 bool result = false; 104 105 foreach(x,y; LineNoThrowImpl(a[0], a[1])) { 106 if (x > b[0].x && 107 x < b[1].x && 108 y > b[0].y && 109 y < b[1].y) 110 { 111 result = true; 112 break; 113 } 114 } 115 116 return result; 117 } 118 119 /++ 120 Checks if there is a collision between a point and a Circle. 121 122 Params: 123 a = Point position. 124 circlePos = Circle position. 125 circleRadius = Circle radius. 126 +/ 127 bool pointCircleImpl(const Vecf a, const Vecf circlePos, const float circleRadius) @safe nothrow pure 128 { 129 return a.distance(circlePos) <= circleRadius; 130 } 131 132 /++ 133 Checks if there is a collision between a line and a Circle. 134 135 Params: 136 a = Line vertexs. 137 circlePos = Circle position. 138 circleRadius = Circle radius. 139 +/ 140 bool lineCircleImpl(const Vecf[] a, const Vecf circlePos, const float circleRadius) @safe nothrow pure 141 { 142 bool inside1 = pointCircleImpl(a[0], circlePos, circleRadius); 143 bool inside2 = pointCircleImpl(a[1], circlePos, circleRadius); 144 if (inside1 || inside2) return true; 145 146 float len = a.length; 147 148 float dot = ( (circlePos.x - a[0].x) * (a[1].x - a[0].x)) + 149 ( (circlePos.y - a[0].y) * (a[1].y - a[0].y)) / (len * len); 150 151 const closest = a[0] + ((a[1] - a[0]) * dot); 152 153 bool onSegment = pointLineImpl(closest, a); 154 if (onSegment) return true; 155 156 const dist = closest - circlePos; 157 len = dist.length; 158 159 return (len <= circleRadius); 160 } 161 162 /++ 163 Checks if there is a collision between a rectangle and a circle. 164 165 Params: 166 a = Rectange vertexs. 167 circlePos = Circle position. 168 circleRadius = Circle radius. 169 +/ 170 bool rectCircleImpl(const Vecf[] a, const Vecf circlePos, const float circleRadius) @safe nothrow pure 171 { 172 Vecf temp = circlePos; 173 174 if (circlePos.x < a[0].x) temp.x = a[0].x; else 175 if (circlePos.x > a[1].x) temp.y = a[1].y; 176 177 if (circlePos.y < a[0].y) temp.y = a[0].y; else 178 if (circlePos.y > a[1].y) temp.y = a[1].y; 179 180 immutable dist = circlePos - temp; 181 immutable len = dist.length; 182 183 return len <= circleRadius; 184 } 185 186 bool trianglePointImpl(const Vecf[3] a, const Vecf b) @safe nothrow pure 187 { 188 import std.math : abs; 189 190 const area = abs ( (a[1].x - a[0].x) * (a[2].y - a[0].y) - 191 (a[2].x - a[0].x) * (a[1].y - a[0].y) ); 192 const(float)[] areas = 193 [ 194 abs( (a[0].x - b.x) * (a[1].y - b.y) - (a[1].x - b.x) * (a[0].y - b.y) ), 195 abs( (a[1].x - b.x) * (a[2].y - b.y) - (a[2].x - b.x) * (a[1].y - b.y) ), 196 abs( (a[2].x - b.x) * (a[0].y - b.y) - (a[0].x - b.x) * (a[2].y - b.y) ) 197 ]; 198 199 return (areas[0] + areas[1] + areas[2]) == area; 200 } 201 202 bool triangleLineImpl(const Vecf[3] a, const Vecf[2] b) @safe nothrow pure 203 { 204 import tida.each; 205 206 bool iscollision = false; 207 208 foreach(x, y; LineNoThrowImpl(b[0], b[1])) 209 { 210 if (trianglePointImpl(a, vecf(x, y))) { 211 iscollision = true; 212 break; 213 } 214 } 215 216 return iscollision; 217 } 218 219 /++ 220 A function to check the intersection of two shapes. It does not give an intersection point, 221 it gives a state that informs if there is an intersection. 222 223 Params: 224 first = First shape. 225 second = Second shape. 226 firstPos = Position first shape. 227 secondPos = Position second shape. 228 229 Returns: 230 Gives a state indicating if there is an intersection. 231 232 Example: 233 --- 234 isCollide(Shape.Rectangle(vecf(32,32),vecf(48,48)), 235 Shape.Line(vecf(48,32),vecf(32,48))); 236 --- 237 +/ 238 bool isCollide( Shape!float first, 239 Shape!float second, 240 Vecf firstPos = vecf(0,0), 241 Vecf secondPos = vecf(0,0)) @safe nothrow pure 242 { 243 import std.conv : to; 244 import std.math : abs, sqrt; 245 246 first.begin = first.begin + firstPos; 247 second.begin = second.begin + secondPos; 248 249 if (first.type == ShapeType.line || first.type == ShapeType.rectangle) 250 first.end = first.end + firstPos; 251 252 if (second.type == ShapeType.line || second.type == ShapeType.rectangle) 253 second.end = second.end + secondPos; 254 255 switch(first.type) 256 { 257 case ShapeType.point: 258 259 switch(second.type) 260 { 261 case ShapeType.point: 262 return first.begin == second.begin; 263 264 case ShapeType.line: 265 return pointLineImpl(first.begin, second.to!(Vecf[])); 266 267 case ShapeType.rectangle: 268 return pointRectImpl(first.begin, second.to!(Vecf[])); 269 270 case ShapeType.circle: 271 return pointCircleImpl(first.begin, second.begin, second.radius); 272 273 case ShapeType.polygon: 274 return isPolygonAndPoint(second.data, first.begin); 275 276 case ShapeType.multi: 277 foreach(shape; second.shapes) { 278 if (isCollide(first, shape,vecf(0,0), second.begin)) 279 return true; 280 } 281 282 return false; 283 284 default: 285 return false; 286 } 287 288 case ShapeType.line: 289 290 switch(second.type) 291 { 292 case ShapeType.point: 293 return pointLineImpl(second.begin, first.to!(Vecf[])); 294 295 case ShapeType.line: 296 return lineLineImpl(first.to!(Vecf[]), second.to!(Vecf[])); 297 298 case ShapeType.rectangle: 299 return lineRectImpl(first.to!(Vecf[]), second.to!(Vecf[])); 300 301 case ShapeType.circle: 302 return lineCircleImpl(first.to!(Vecf[]), second.begin, second.radius); 303 304 case ShapeType.polygon: 305 return isPolygonAndLine(second.data, first.to!(Vecf[])); 306 307 case ShapeType.multi: 308 foreach(shape; second.shapes) { 309 if (isCollide(first,shape,vecf(0,0),second.begin)) 310 return true; 311 } 312 313 return false; 314 315 default: 316 return false; 317 } 318 319 case ShapeType.rectangle: 320 321 switch(second.type) 322 { 323 case ShapeType.point: 324 return pointRectImpl(second.begin, first.to!(Vecf[])); 325 326 case ShapeType.line: 327 return lineRectImpl(second.to!(Vecf[]), first.to!(Vecf[])); 328 329 case ShapeType.rectangle: 330 const a = first.begin; 331 const b = first.end; 332 const c = second.begin; 333 const d = second.end; 334 335 return 336 ( 337 a.x + (b.x-a.x) >= c.x && 338 a.x <= c.x + (d.x-c.x) && 339 a.y + (b.y - a.y) >= c.y && 340 a.y <= c.y + (d.y - c.y) 341 ); 342 343 case ShapeType.circle: 344 return rectCircleImpl(first.to!(Vecf[]), second.begin, second.radius); 345 346 case ShapeType.polygon: 347 return isPolygonAndRect(second.data, first.to!(Vecf[])); 348 349 case ShapeType.multi: 350 foreach(shape; second.shapes) { 351 if (isCollide(first, shape,vecf(0,0),second.begin)) 352 return true; 353 } 354 355 return false; 356 357 default: 358 return false; 359 } 360 361 case ShapeType.circle: 362 switch(second.type) 363 { 364 case ShapeType.point: 365 return pointCircleImpl(second.begin, first.begin, first.radius); 366 367 case ShapeType.line: 368 return lineCircleImpl(second.to!(Vecf[]), first.begin, first.radius); 369 370 case ShapeType.rectangle: 371 return rectCircleImpl(second.to!(Vecf[]), first.begin, first.radius); 372 373 case ShapeType.circle: 374 immutable dist = first.begin - second.begin; 375 376 return dist.length <= first.radius + second.radius; 377 378 case ShapeType.polygon: 379 return isPolygonAndCircle(second.data, first.begin, first.radius); 380 381 case ShapeType.multi: 382 foreach(shape; second.shapes) { 383 if (isCollide(first,shape,vecf(0,0),second.begin)) 384 return true; 385 } 386 387 return false; 388 389 default: 390 return false; 391 } 392 393 case ShapeType.polygon: 394 switch(second.type) 395 { 396 case ShapeType.point: 397 return isPolygonAndPoint(first.data, second.begin); 398 399 case ShapeType.line: 400 return isPolygonAndLine(first.data, second.to!(Vecf[])); 401 402 case ShapeType.rectangle: 403 return isPolygonAndRect(first.data, second.to!(Vecf[])); 404 405 case ShapeType.circle: 406 return isPolygonAndCircle(first.data, second.begin, second.radius); 407 408 case ShapeType.polygon: 409 return isPolygonsCollision(first.data, second.data); 410 411 case ShapeType.multi: 412 foreach(shape; second.shapes) { 413 if (isCollide(shape, first, second.begin, vecf(0,0))) 414 return true; 415 } 416 417 return false; 418 419 default: 420 return false; 421 } 422 423 case ShapeType.multi: 424 foreach(shape; first.shapes) { 425 if (isCollide(shape, second,first.begin,vecf(0,0))) 426 return true; 427 } 428 429 return false; 430 431 default: 432 return false; 433 } 434 } 435 436 unittest 437 { 438 assert(isCollide( 439 Shapef.Rectangle(vecf(32,32),vecf(64,64)), 440 Shapef.Line(vecf(48,48),vecf(96,96)) 441 )); 442 443 444 assert(isCollide( 445 Shapef.Rectangle(vecf(32,32),vecf(64,64)), 446 Shapef.Rectangle(vecf(48,48),vecf(72,72)) 447 )); 448 449 assert(isCollide( 450 Shapef.Line(vecf(32,32),vecf(64,64)), 451 Shapef.Line(vecf(64,32),vecf(32,64)) 452 )); 453 } 454 455 /++ 456 Checks collision between polygon and point. 457 458 Params: 459 first = Polygon vertices. 460 second = Point position. 461 +/ 462 bool isPolygonAndPoint(Vecf[] first, Vecf second) @safe nothrow pure 463 { 464 bool collision = false; 465 466 int next = 0; 467 for(int current = 0; current < first.length; current++) 468 { 469 next = current + 1; 470 471 if (next == first.length) next = 0; 472 473 Vecf vc = first[current]; 474 Vecf vn = first[next]; 475 476 if(((vc.y >= second.y && vn.y <= second.y) || (vc.y <= second.y && vn.y >= second.y)) && 477 (second.x < (vn.x - vc.x) * (second.y - vc.y) / (vn.y - vc.y) + vc.x)) { 478 collision = !collision; 479 } 480 } 481 482 return collision; 483 } 484 485 /++ 486 Checks collision between polygon and line. 487 488 Params: 489 first = Polygon vertices. 490 second = Line vertices. 491 +/ 492 bool isPolygonAndLine(Vecf[] first, Vecf[] second) @safe nothrow pure 493 { 494 int next = 0; 495 for(int current = 0; current < first.length; current++) 496 { 497 next = current + 1; 498 499 if (next == first.length) next = 0; 500 501 Vecf vc = first[current]; 502 Vecf vn = first[next]; 503 504 bool hit = lineLineImpl(second, [vc, vn]); 505 506 if (hit) return true; 507 } 508 509 return false; 510 } 511 512 /++ 513 Check collision between polygon and rectangle. 514 515 Params: 516 first = Polygon vertices. 517 second = Rectangle vertices. 518 +/ 519 bool isPolygonAndRect(Vecf[] first, Vecf[] second) @safe nothrow pure 520 { 521 int next = 0; 522 for(int current = 0; current < first.length; current++) 523 { 524 next = current + 1; 525 526 if (next == first.length) next = 0; 527 528 Vecf vc = first[current]; 529 Vecf vn = first[next]; 530 531 bool hit = lineRectImpl([vc, vn], second); 532 533 if (hit) return true; 534 } 535 536 return false; 537 } 538 539 /++ 540 Check collision between polygon and circle. 541 542 Params: 543 first = Polygon vertices. 544 second = The position of the center of the circle. 545 r = Circle radius. 546 +/ 547 bool isPolygonAndCircle(Vecf[] first, Vecf second, float r) @safe nothrow pure 548 { 549 int next = 0; 550 for(int current = 0; current < first.length; current++) 551 { 552 next = current + 1; 553 554 if (next == first.length) next = 0; 555 556 Vecf vc = first[current]; 557 Vecf vn = first[next]; 558 559 bool hit = lineCircleImpl([vc, vn], second, r); 560 561 if (hit) return true; 562 } 563 564 return false; 565 } 566 567 /++ 568 Checking the collision of two polygons. 569 570 Params: 571 first = First polygon vertices. 572 second = Second polygon vertices. 573 +/ 574 bool isPolygonsCollision(Vecf[] first, Vecf[] second) @safe nothrow pure 575 { 576 int next = 0; 577 for(int current = 0; current < first.length; current++) 578 { 579 next = current + 1; 580 581 if (next == first.length) next = 0; 582 583 Vecf vc = first[current]; 584 Vecf vn = first[next]; 585 586 bool hit = isPolygonAndLine(second, [vc, vn]); 587 if (hit) return true; 588 589 hit = isPolygonAndPoint(first, second[0]); 590 if (hit) return true; 591 } 592 593 return false; 594 } 595 596 unittest 597 { 598 Vecf[] first = [ 599 vecf(32, 32), 600 vecf(64, 48), 601 vecf(64, 128), 602 vecf(32, 112) 603 ]; 604 605 assert(isPolygonAndPoint(first, vecf(33, 33))); 606 607 assert(isPolygonAndLine(first, [vecf(16, 16), vecf(48, 48)])); 608 609 assert(isPolygonAndRect(first, [vecf(16, 16), vecf(128, 128)])); 610 611 assert(isPolygonAndCircle(first, vecf(128, 128), 64)); 612 613 assert(isPolygonsCollision(first, [ 614 vecf(48, 48), 615 vecf(64, 64), 616 vecf(32, 64), 617 vecf(32, 32), 618 vecf(32, 48) 619 ])); 620 } 621 622 /++ 623 Gives the place of collision of a line and a point. 624 625 Params: 626 point = Point vector. 627 line = Line vectors. 628 629 Returns: 630 Place of collision. 631 +/ 632 const(Vecf) placePointLineImpl(const Vecf point, const Vecf[] line) @safe nothrow pure 633 { 634 import tida.each; 635 636 if (point == line[0] || 637 point == line[1]) 638 return point; 639 640 Vecf result = vecfNaN; 641 642 foreach(x,y; LineNoThrowImpl(line[0], line[1])) { 643 if (cast(int) point.x == x && 644 cast(int) point.y == y) { 645 result = vecf(x, y); 646 break; 647 } 648 } 649 650 return result; 651 } 652 653 /++ 654 Gives the place of collision of a point and a rectangle. 655 656 Params: 657 point = Point vector. 658 rectangle = rectangle vectors. 659 660 Returns: 661 Place of collision. 662 +/ 663 const(Vecf) placePointRectImpl(const Vecf point, const Vecf[] rectangle) @safe nothrow pure 664 { 665 Vecf place = rectangle[0] - point; 666 667 if( place.x < 0 || place.y < 0 || 668 place.x > (rectangle[1] - rectangle[0]).x || 669 place.y > (rectangle[1] - rectangle[0]).y) { 670 return vecfNaN; 671 } else { 672 return rectangle[0] + place; 673 } 674 } 675 676 /++ 677 Gives the place of collision of a point and a circle. 678 679 Params: 680 point = Point vector. 681 circlePos = Cirlce position. 682 circleRadius = Circle radius. 683 684 Returns: 685 Place of collision. 686 +/ 687 const(Vecf) placePointCircleImpl(const Vecf point, const Vecf circlePos, const float circleRadius) @safe nothrow pure 688 { 689 return point.distance(circlePos) > circleRadius ? vecfNaN : point; 690 } 691 692 /++ 693 Gives the place of collision of a line and a line. 694 695 Params: 696 first = Line vector. 697 second = Line vectors. 698 699 Returns: 700 Place of collision. 701 +/ 702 const(Vecf) placeLineLineImpl(const Vecf[] first, const Vecf[] second) @safe nothrow pure 703 { 704 const a = first[0]; 705 const b = first[1]; 706 const c = second[0]; 707 const d = second[1]; 708 709 const denominator = ((b.x - a.x) * (d.y - c.y)) - ((b.y - a.y) * (d.x - c.x)); 710 711 const numerator1 = ((a.y - c.y) * (d.x - c.x)) - ((a.x - c.x) * (d.y - c.y)); 712 const numerator2 = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y)); 713 714 const r = numerator1 / denominator; 715 const s = numerator2 / denominator; 716 717 if((r >= 0 && r <= 1) && (s >= 0 && s <= 1)) 718 return first[0] - ((first[1] - first[0]) * r); 719 else 720 return vecfNaN; 721 } 722 723 /++ 724 Gives the place of collision of a line and a rectangle. 725 726 Params: 727 a = Line vector. 728 b = Rectangle vectors. 729 730 Returns: 731 Place of collision. 732 +/ 733 const(Vecf) placeLineRectImpl(const Vecf[] a, const Vecf[] b) @safe nothrow pure 734 { 735 import tida.each; 736 737 if (b[0] == a[0] || 738 b[0] == a[1] || 739 b[1] == a[0] || 740 b[1] == a[1]) 741 return a[0]; 742 743 Vecf result = vecfNaN; 744 745 foreach(x,y; LineNoThrowImpl(a[0], a[1])) { 746 if (x > b[0].x && 747 x < b[1].x && 748 y > b[0].y && 749 y < b[1].y) 750 { 751 return vecf(x, y); 752 } 753 } 754 755 return result; 756 } 757 758 /++ 759 Gives the place of collision of a line and a circle. 760 761 Params: 762 line = line vector. 763 circlePos = Circle position. 764 circleRadius = Circle radius. 765 766 Returns: 767 Place of collision. 768 +/ 769 const(Vecf) placeLineCircleImpl(const Vecf[] line, const Vecf circlePos, const float circleRadius) @safe nothrow pure 770 { 771 import tida.each; 772 773 Vecf place = vecfNaN; 774 775 foreach(x, y; LineNoThrowImpl(line[0], line[1])) { 776 if(!(place = placePointCircleImpl(vecf(x, y), circlePos, circleRadius)).isVecfNaN) 777 return place; 778 } 779 780 return vecfNaN; 781 } 782 783 /++ 784 Gives the place of collision of a rectangle and a rectangle. 785 786 Params: 787 a = Rectangle vector. 788 b = Rectangle vectors. 789 isRecurse = Used only for recursion. 790 791 Returns: 792 Place of collision. 793 +/ 794 const(Vecf) placeRectRectImpl(const Vecf[] a, const Vecf[] b,bool isRecurse = false) @safe nothrow pure 795 { 796 if (lineRectImpl([a[0], a[0] + vecf(a[1].x, 0)], b)) 797 return placeLineRectImpl([a[0], a[0] + vecf(a[1].x, 0)], b); 798 if (lineRectImpl([a[0] + vecf(a[1].x, 0), a[1]], b)) 799 return placeLineRectImpl([a[0] + vecf(a[1].x, 0), a[1]], b); 800 if (lineRectImpl([a[0], a[0] + vecf(0, a[1].y)], b)) 801 return placeLineRectImpl([a[0], a[0] + vecf(0, a[1].y)], b); 802 if (lineRectImpl([a[0] + vecf(0, a[1].y), a[1]], b)) 803 return placeLineRectImpl([a[0] + vecf(0, a[1].y), a[1]], b); 804 805 if(!isRecurse) 806 return placeRectRectImpl(b, a, true); 807 else 808 return vecfNaN; 809 } 810 811 /++ 812 Gives the place of collision of a rectangle and a circle. 813 814 Params: 815 a = Rectangle vectors. 816 circlePos = Circle position. 817 circleRadius = Circle radius. 818 819 Returns: 820 Place of collision. 821 +/ 822 const(Vecf) placeRectCircleImpl(const Vecf[] a, const Vecf circlePos, const float circleRadius) @safe nothrow pure 823 { 824 if (lineCircleImpl([a[0], a[0] + vecf(a[1].x, 0)], circlePos, circleRadius)) 825 return placeLineCircleImpl([a[0], a[0] + vecf(a[1].x, 0)], circlePos, circleRadius); 826 if (lineCircleImpl([a[0] + vecf(a[1].x, 0), a[1]], circlePos, circleRadius)) 827 return placeLineCircleImpl([a[0] + vecf(a[1].x, 0), a[1]], circlePos, circleRadius); 828 if (lineCircleImpl([a[0], a[0] + vecf(0, a[1].y)], circlePos, circleRadius)) 829 return placeLineCircleImpl([a[0], a[0] + vecf(0, a[1].y)], circlePos, circleRadius); 830 if (lineCircleImpl([a[0] + vecf(0, a[1].y), a[1]], circlePos, circleRadius)) 831 return placeLineCircleImpl([a[0] + vecf(0, a[1].y), a[1]], circlePos, circleRadius); 832 833 Vecf place = vecfNaN; 834 Vecf[] line = [ 835 circlePos - vecf(0, circleRadius), 836 circlePos + vecf(0, circleRadius) 837 ]; 838 839 if(!(place = placeLineRectImpl(line, a)).isVecfNaN) return place; 840 line = [ 841 circlePos - vecf(circleRadius, 0), 842 circlePos + vecf(circleRadius, 0) 843 ]; 844 845 return placeLineRectImpl(line, a); 846 } 847 848 /++ 849 Gives the place of collision of a circle and a circle. 850 851 Params: 852 fPos = First circle position. 853 fRadius = First circle radius. 854 fPos = Second circle position. 855 fRadius = Second circle radius. 856 857 Returns: 858 Place of collision. 859 +/ 860 const(Vecf) placeCircleCircleImpl( const Vecf fPos, 861 const float fRadius, 862 const Vecf sPos, 863 const float sRadius) 864 @safe nothrow pure 865 { 866 //immutable dist = first.begin - second.begin; 867 //return dist.length <= first.radius + second.radius; 868 869 if((fPos - sPos).length <= fRadius + sRadius) { 870 return Vecf((fPos.x * sRadius + sPos.x * fRadius) / (fRadius + sRadius), 871 (fPos.y * sRadius + sPos.y * fRadius) / (fRadius + sRadius)); 872 } else { 873 return vecfNaN; 874 } 875 } 876 877 /++ 878 Finds the point of contact between two forms. 879 880 Params: 881 first = First shape. 882 second = Second shape. 883 firstPos = First shape position. 884 secondPos = Second shape position. 885 886 Returns: 887 point of contact between two forms. 888 +/ 889 const(Vecf) placeofTangents(Shape!float first, 890 Shape!float second, 891 Vecf firstPos = vecf(0, 0), 892 Vecf secondPos = vecf(0, 0)) 893 @safe nothrow pure 894 in(first.type != ShapeType.unknown && second.type != ShapeType.unknown) 895 in(first.type != ShapeType.triangle && second.type != ShapeType.triangle) 896 do 897 { 898 first.begin = first.begin + firstPos; 899 second.begin = second.begin + secondPos; 900 901 if (first.type == ShapeType.line || first.type == ShapeType.rectangle) 902 first.end = first.end + firstPos; 903 904 if (second.type == ShapeType.line || second.type == ShapeType.rectangle) 905 second.end = second.end + secondPos; 906 907 switch (first.type) 908 { 909 case ShapeType.point: 910 switch(second.type) { 911 case ShapeType.point: 912 return first.begin == second.begin ? first.begin : vecfNaN; 913 914 case ShapeType.line: 915 return placePointLineImpl(first.begin, second.to!(Vecf[])); 916 917 case ShapeType.rectangle: 918 return placePointRectImpl(first.begin, second.to!(Vecf[])); 919 920 case ShapeType.circle: 921 return placePointCircleImpl(first.begin, second.begin, second.radius); 922 923 case ShapeType.polygon: 924 return placePolygonAndPoint(second.data, first.begin); 925 926 case ShapeType.multi: 927 Vecf place = vecfNaN; 928 929 foreach(shape; second.shapes) { 930 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 931 return place; 932 } 933 934 return vecfNaN; 935 936 default: 937 return vecfNaN; 938 } 939 940 case ShapeType.line: 941 switch(second.type) 942 { 943 case ShapeType.point: 944 return placePointLineImpl(second.begin, first.to!(Vecf[])); 945 946 case ShapeType.line: 947 return placeLineLineImpl(first.to!(Vecf[]), second.to!(Vecf[])); 948 949 case ShapeType.rectangle: 950 return placeLineRectImpl(second.to!(Vecf[]), first.to!(Vecf[])); 951 952 case ShapeType.circle: 953 return placeLineCircleImpl(first.to!(Vecf[]), second.begin, second.radius); 954 955 case ShapeType.polygon: 956 return placePolygonAndLine(second.data, first.to!(Vecf[])); 957 958 case ShapeType.multi: 959 Vecf place = vecfNaN; 960 961 foreach(shape; second.shapes) { 962 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 963 return place; 964 } 965 966 return vecfNaN; 967 968 default: 969 return vecfNaN; 970 } 971 972 case ShapeType.rectangle: 973 switch(second.type) 974 { 975 case ShapeType.point: 976 return placePointRectImpl(second.begin, first.to!(Vecf[])); 977 978 case ShapeType.line: 979 return placeLineRectImpl(second.to!(Vecf[]), first.to!(Vecf[])); 980 981 case ShapeType.rectangle: 982 return placeRectRectImpl(first.to!(Vecf[]), second.to!(Vecf[])); 983 984 case ShapeType.circle: 985 return placeRectCircleImpl(first.to!(Vecf[]), second.begin, second.radius); 986 987 case ShapeType.polygon: 988 return placePolygonAndRect(second.data, first.to!(Vecf[])); 989 990 case ShapeType.multi: 991 Vecf place = vecfNaN; 992 993 foreach(shape; second.shapes) { 994 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 995 return place; 996 } 997 998 return vecfNaN; 999 1000 default: 1001 return vecfNaN; 1002 } 1003 1004 case ShapeType.circle: 1005 switch(second.type) 1006 { 1007 case ShapeType.point: 1008 return placePointCircleImpl(second.begin, first.begin, first.radius); 1009 1010 case ShapeType.line: 1011 return placeLineCircleImpl(second.to!(Vecf[]), first.begin, first.radius); 1012 1013 case ShapeType.rectangle: 1014 return placeRectCircleImpl(second.to!(Vecf[]), first.begin, first.radius); 1015 1016 case ShapeType.circle: 1017 return placeCircleCircleImpl(first.begin, first.radius, second.begin, second.radius); 1018 1019 case ShapeType.polygon: 1020 return placePolygonAndCircle(second.data, first.begin, first.radius); 1021 1022 case ShapeType.multi: 1023 Vecf place = vecfNaN; 1024 1025 foreach(shape; second.shapes) { 1026 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 1027 return place; 1028 } 1029 1030 return vecfNaN; 1031 1032 default: 1033 return vecfNaN; 1034 } 1035 1036 case ShapeType.polygon: 1037 switch(second.type) 1038 { 1039 case ShapeType.point: 1040 return placePolygonAndPoint(first.data, second.begin); 1041 1042 case ShapeType.line: 1043 return placePolygonAndLine(first.data, second.to!(Vecf[])); 1044 1045 case ShapeType.rectangle: 1046 return placePolygonAndRect(first.data, second.to!(Vecf[])); 1047 1048 case ShapeType.circle: 1049 return placePolygonAndCircle(first.data, second.begin, second.radius); 1050 1051 case ShapeType.polygon: 1052 return placePolygonsCollision(first.data, second.data); 1053 1054 case ShapeType.multi: 1055 Vecf place = vecfNaN; 1056 1057 foreach(shape; second.shapes) { 1058 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 1059 return place; 1060 } 1061 1062 return vecfNaN; 1063 1064 default: 1065 return vecfNaN; 1066 } 1067 1068 case ShapeType.multi: 1069 Vecf place = vecfNaN; 1070 1071 foreach(shape; first.shapes) { 1072 if(!((place = placeofTangents(shape, second, first.begin, 1073 vecf(0, 0))).isVecfNaN)) 1074 return place; 1075 } 1076 1077 return vecfNaN; 1078 1079 default: 1080 return vecfNaN; 1081 } 1082 } 1083 1084 /++ 1085 The point of contact between the polygon and the point. 1086 1087 Params: 1088 first = Polygon vertexs. 1089 second = Point position. 1090 1091 Returns: 1092 Point of contact between shapes. 1093 +/ 1094 Vecf placePolygonAndPoint(Vecf[] first, Vecf second) @safe nothrow pure 1095 { 1096 int next = 0; 1097 for(int current = 0; current < first.length; current++) 1098 { 1099 next = current + 1; 1100 1101 if (next == first.length) next = 0; 1102 1103 Vecf vc = first[current]; 1104 Vecf vn = first[next]; 1105 1106 if(((vc.y >= second.y && vn.y <= second.y) || (vc.y <= second.y && vn.y >= second.y)) && 1107 (second.x < (vn.x - vc.x) * (second.y - vc.y) / (vn.y - vc.y) + vc.x)) { 1108 return second; 1109 } 1110 } 1111 1112 return vecfNaN; 1113 } 1114 1115 /++ 1116 The point of contact between the polygon and the line. 1117 1118 Params: 1119 first = Polygon vertexs. 1120 second = line position. 1121 1122 Returns: 1123 Point of contact between shapes. 1124 +/ 1125 Vecf placePolygonAndLine(Vecf[] first, Vecf[] second) @safe nothrow pure 1126 { 1127 int next = 0; 1128 for(int current = 0; current < first.length; current++) 1129 { 1130 next = current + 1; 1131 1132 if (next == first.length) next = 0; 1133 1134 Vecf vc = first[current]; 1135 Vecf vn = first[next]; 1136 1137 Vecf place = placeLineLineImpl(second, [vc, vn]); 1138 if(!place.isVecfNaN) return place; 1139 } 1140 1141 return vecfNaN; 1142 } 1143 1144 /++ 1145 The point of contact between the polygon and the rectangle. 1146 1147 Params: 1148 first = Polygon vertexs. 1149 second = Rectangle vertexs. 1150 1151 Returns: 1152 Point of contact between shapes. 1153 +/ 1154 Vecf placePolygonAndRect(Vecf[] first, Vecf[] second) @safe nothrow pure 1155 { 1156 int next = 0; 1157 for(int current = 0; current < first.length; current++) 1158 { 1159 next = current + 1; 1160 1161 if (next == first.length) next = 0; 1162 1163 Vecf vc = first[current]; 1164 Vecf vn = first[next]; 1165 1166 Vecf place = placeLineRectImpl([vc, vn], second); 1167 if(!place.isVecfNaN) return place; 1168 } 1169 1170 return vecfNaN; 1171 } 1172 1173 /++ 1174 The point of contact between the polygon and the circle. 1175 1176 Params: 1177 first = Polygon vertexs. 1178 second = Circle position. 1179 r = Circle radius. 1180 1181 Returns: 1182 Point of contact between shapes. 1183 +/ 1184 Vecf placePolygonAndCircle(Vecf[] first, Vecf second, float r) @safe nothrow pure 1185 { 1186 int next = 0; 1187 for(int current = 0; current < first.length; current++) 1188 { 1189 next = current + 1; 1190 1191 if (next == first.length) next = 0; 1192 1193 Vecf vc = first[current]; 1194 Vecf vn = first[next]; 1195 1196 Vecf place = placeLineCircleImpl([vc, vn], second, r); 1197 if(!place.isVecfNaN) return place; 1198 } 1199 1200 return vecfNaN; 1201 } 1202 1203 /++ 1204 The point of contact between the polygon and the polygon. 1205 1206 Params: 1207 first = First polygon vertexs. 1208 second = Second polygon vertexs. 1209 1210 Returns: 1211 Point of contact between shapes. 1212 +/ 1213 Vecf placePolygonsCollision(Vecf[] first, Vecf[] second) @safe nothrow pure 1214 { 1215 int next = 0; 1216 for (int current = 0; current < first.length; current++) 1217 { 1218 next = current + 1; 1219 1220 if (next == first.length) next = 0; 1221 1222 Vecf vc = first[current]; 1223 Vecf vn = first[next]; 1224 1225 Vecf place = placePolygonAndLine(second, [vc, vn]); 1226 if(!place.isVecfNaN) return place; 1227 1228 place = placePolygonAndPoint(first, second[0]); 1229 if (!place.isVecfNaN) return place; 1230 } 1231 1232 return vecfNaN; 1233 }