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 in(first.type != ShapeType.unknown && second.type != ShapeType.unknown) 243 in(first.type != ShapeType.triangle && second.type != ShapeType.triangle) 244 do 245 { 246 import std.conv : to; 247 import std.math : abs, sqrt; 248 249 first.begin = first.begin + firstPos; 250 second.begin = second.begin + secondPos; 251 252 if (first.type == ShapeType.line || first.type == ShapeType.rectangle) 253 first.end = first.end + firstPos; 254 255 if (second.type == ShapeType.line || second.type == ShapeType.rectangle) 256 second.end = second.end + secondPos; 257 258 switch(first.type) 259 { 260 case ShapeType.point: 261 262 switch(second.type) 263 { 264 case ShapeType.point: 265 return first.begin == second.begin; 266 267 case ShapeType.line: 268 return pointLineImpl(first.begin, second.to!(Vecf[])); 269 270 case ShapeType.rectangle: 271 return pointRectImpl(first.begin, second.to!(Vecf[])); 272 273 case ShapeType.circle: 274 return pointCircleImpl(first.begin, second.begin, second.radius); 275 276 case ShapeType.polygon: 277 return isPolygonAndPoint(second.data, first.begin); 278 279 case ShapeType.multi: 280 foreach(shape; second.shapes) { 281 if (isCollide(first, shape,vecf(0,0), second.begin)) 282 return true; 283 } 284 285 return false; 286 287 default: 288 return false; 289 } 290 291 case ShapeType.line: 292 293 switch(second.type) 294 { 295 case ShapeType.point: 296 return pointLineImpl(second.begin, first.to!(Vecf[])); 297 298 case ShapeType.line: 299 return lineLineImpl(first.to!(Vecf[]), second.to!(Vecf[])); 300 301 case ShapeType.rectangle: 302 return lineRectImpl(first.to!(Vecf[]), second.to!(Vecf[])); 303 304 case ShapeType.circle: 305 return lineCircleImpl(first.to!(Vecf[]), second.begin, second.radius); 306 307 case ShapeType.polygon: 308 return isPolygonAndLine(second.data, first.to!(Vecf[])); 309 310 case ShapeType.multi: 311 foreach(shape; second.shapes) { 312 if (isCollide(first,shape,vecf(0,0),second.begin)) 313 return true; 314 } 315 316 return false; 317 318 default: 319 return false; 320 } 321 322 case ShapeType.rectangle: 323 324 switch(second.type) 325 { 326 case ShapeType.point: 327 return pointRectImpl(second.begin, first.to!(Vecf[])); 328 329 case ShapeType.line: 330 return lineRectImpl(second.to!(Vecf[]), first.to!(Vecf[])); 331 332 case ShapeType.rectangle: 333 const a = first.begin; 334 const b = first.end; 335 const c = second.begin; 336 const d = second.end; 337 338 return 339 ( 340 a.x + (b.x-a.x) >= c.x && 341 a.x <= c.x + (d.x-c.x) && 342 a.y + (b.y - a.y) >= c.y && 343 a.y <= c.y + (d.y - c.y) 344 ); 345 346 case ShapeType.circle: 347 return rectCircleImpl(first.to!(Vecf[]), second.begin, second.radius); 348 349 case ShapeType.polygon: 350 return isPolygonAndRect(second.data, first.to!(Vecf[])); 351 352 case ShapeType.multi: 353 foreach(shape; second.shapes) { 354 if (isCollide(first, shape,vecf(0,0),second.begin)) 355 return true; 356 } 357 358 return false; 359 360 default: 361 return false; 362 } 363 364 case ShapeType.circle: 365 switch(second.type) 366 { 367 case ShapeType.point: 368 return pointCircleImpl(second.begin, first.begin, first.radius); 369 370 case ShapeType.line: 371 return lineCircleImpl(second.to!(Vecf[]), first.begin, first.radius); 372 373 case ShapeType.rectangle: 374 return rectCircleImpl(second.to!(Vecf[]), first.begin, first.radius); 375 376 case ShapeType.circle: 377 immutable dist = first.begin - second.begin; 378 379 return dist.length <= first.radius + second.radius; 380 381 case ShapeType.polygon: 382 return isPolygonAndCircle(second.data, first.begin, first.radius); 383 384 case ShapeType.multi: 385 foreach(shape; second.shapes) { 386 if (isCollide(first,shape,vecf(0,0),second.begin)) 387 return true; 388 } 389 390 return false; 391 392 default: 393 return false; 394 } 395 396 case ShapeType.polygon: 397 switch(second.type) 398 { 399 case ShapeType.point: 400 return isPolygonAndPoint(first.data, second.begin); 401 402 case ShapeType.line: 403 return isPolygonAndLine(first.data, second.to!(Vecf[])); 404 405 case ShapeType.rectangle: 406 return isPolygonAndRect(first.data, second.to!(Vecf[])); 407 408 case ShapeType.circle: 409 return isPolygonAndCircle(first.data, second.begin, second.radius); 410 411 case ShapeType.polygon: 412 return isPolygonsCollision(first.data, second.data); 413 414 case ShapeType.multi: 415 foreach(shape; second.shapes) { 416 if (isCollide(shape, first, second.begin, vecf(0,0))) 417 return true; 418 } 419 420 return false; 421 422 default: 423 return false; 424 } 425 426 case ShapeType.multi: 427 foreach(shape; first.shapes) { 428 if (isCollide(shape, second,first.begin,vecf(0,0))) 429 return true; 430 } 431 432 return false; 433 434 default: 435 return false; 436 } 437 } 438 439 unittest 440 { 441 assert( 442 isCollide( 443 Shapef.Rectangle(vecf(32,32),vecf(64,64)), 444 Shapef.Line(vecf(48,48),vecf(96,96)) 445 ) 446 ); 447 448 assert( 449 isCollide( 450 Shapef.Rectangle(vecf(32,32),vecf(64,64)), 451 Shapef.Rectangle(vecf(48,48),vecf(72,72)) 452 ) 453 ); 454 455 assert( 456 isCollide( 457 Shapef.Line(vecf(32,32),vecf(64,64)), 458 Shapef.Line(vecf(64,32),vecf(32,64)) 459 ) 460 ); 461 } 462 463 /++ 464 Checks collision between polygon and point. 465 466 Params: 467 first = Polygon vertices. 468 second = Point position. 469 +/ 470 bool isPolygonAndPoint(Vecf[] first, Vecf second) @safe nothrow pure 471 { 472 bool collision = false; 473 474 int next = 0; 475 for(int current = 0; current < first.length; current++) 476 { 477 next = current + 1; 478 479 if (next == first.length) next = 0; 480 481 Vecf vc = first[current]; 482 Vecf vn = first[next]; 483 484 if(((vc.y >= second.y && vn.y <= second.y) || (vc.y <= second.y && vn.y >= second.y)) && 485 (second.x < (vn.x - vc.x) * (second.y - vc.y) / (vn.y - vc.y) + vc.x)) { 486 collision = !collision; 487 } 488 } 489 490 return collision; 491 } 492 493 /++ 494 Checks collision between polygon and line. 495 496 Params: 497 first = Polygon vertices. 498 second = Line vertices. 499 +/ 500 bool isPolygonAndLine(Vecf[] first, Vecf[] second) @safe nothrow pure 501 { 502 int next = 0; 503 for(int current = 0; current < first.length; current++) 504 { 505 next = current + 1; 506 507 if (next == first.length) next = 0; 508 509 Vecf vc = first[current]; 510 Vecf vn = first[next]; 511 512 bool hit = lineLineImpl(second, [vc, vn]); 513 514 if (hit) return true; 515 } 516 517 return false; 518 } 519 520 /++ 521 Check collision between polygon and rectangle. 522 523 Params: 524 first = Polygon vertices. 525 second = Rectangle vertices. 526 +/ 527 bool isPolygonAndRect(Vecf[] first, Vecf[] second) @safe nothrow pure 528 { 529 int next = 0; 530 for(int current = 0; current < first.length; current++) 531 { 532 next = current + 1; 533 534 if (next == first.length) next = 0; 535 536 Vecf vc = first[current]; 537 Vecf vn = first[next]; 538 539 bool hit = lineRectImpl([vc, vn], second); 540 541 if (hit) return true; 542 } 543 544 return false; 545 } 546 547 /++ 548 Check collision between polygon and circle. 549 550 Params: 551 first = Polygon vertices. 552 second = The position of the center of the circle. 553 r = Circle radius. 554 +/ 555 bool isPolygonAndCircle(Vecf[] first, Vecf second, float r) @safe nothrow pure 556 { 557 int next = 0; 558 for(int current = 0; current < first.length; current++) 559 { 560 next = current + 1; 561 562 if (next == first.length) next = 0; 563 564 Vecf vc = first[current]; 565 Vecf vn = first[next]; 566 567 bool hit = lineCircleImpl([vc, vn], second, r); 568 569 if (hit) return true; 570 } 571 572 return false; 573 } 574 575 /++ 576 Checking the collision of two polygons. 577 578 Params: 579 first = First polygon vertices. 580 second = Second polygon vertices. 581 +/ 582 bool isPolygonsCollision(Vecf[] first, Vecf[] second) @safe nothrow pure 583 { 584 int next = 0; 585 for(int current = 0; current < first.length; current++) 586 { 587 next = current + 1; 588 589 if (next == first.length) next = 0; 590 591 Vecf vc = first[current]; 592 Vecf vn = first[next]; 593 594 bool hit = isPolygonAndLine(second, [vc, vn]); 595 if (hit) return true; 596 597 hit = isPolygonAndPoint(first, second[0]); 598 if (hit) return true; 599 } 600 601 return false; 602 } 603 604 unittest 605 { 606 Vecf[] first = [ 607 vecf(32, 32), 608 vecf(64, 48), 609 vecf(64, 128), 610 vecf(32, 112) 611 ]; 612 613 assert(isPolygonAndPoint(first, vecf(33, 33))); 614 assert(isPolygonAndLine(first, [vecf(16, 16), vecf(48, 48)])); 615 assert(isPolygonAndRect(first, [vecf(16, 16), vecf(128, 128)])); 616 assert(isPolygonAndCircle(first, vecf(128, 128), 64)); 617 assert(isPolygonsCollision(first, [ 618 vecf(48, 48), 619 vecf(64, 64), 620 vecf(32, 64), 621 vecf(32, 32), 622 vecf(32, 48) 623 ])); 624 } 625 626 /++ 627 Gives the place of collision of a line and a point. 628 629 Params: 630 point = Point vector. 631 line = Line vectors. 632 633 Returns: 634 Place of collision. 635 +/ 636 const(Vecf) placePointLineImpl(const Vecf point, const Vecf[] line) @safe nothrow pure 637 { 638 import tida.each; 639 640 if (point == line[0] || 641 point == line[1]) 642 return point; 643 644 Vecf result = vecfNaN; 645 646 foreach(x,y; LineNoThrowImpl(line[0], line[1])) { 647 if (cast(int) point.x == x && 648 cast(int) point.y == y) { 649 result = vecf(x, y); 650 break; 651 } 652 } 653 654 return result; 655 } 656 657 /++ 658 Gives the place of collision of a point and a rectangle. 659 660 Params: 661 point = Point vector. 662 rectangle = rectangle vectors. 663 664 Returns: 665 Place of collision. 666 +/ 667 const(Vecf) placePointRectImpl(const Vecf point, const Vecf[] rectangle) @safe nothrow pure 668 { 669 Vecf place = rectangle[0] - point; 670 671 if( place.x < 0 || place.y < 0 || 672 place.x > (rectangle[1] - rectangle[0]).x || 673 place.y > (rectangle[1] - rectangle[0]).y) { 674 return vecfNaN; 675 } else { 676 return rectangle[0] + place; 677 } 678 } 679 680 /++ 681 Gives the place of collision of a point and a circle. 682 683 Params: 684 point = Point vector. 685 circlePos = Cirlce position. 686 circleRadius = Circle radius. 687 688 Returns: 689 Place of collision. 690 +/ 691 const(Vecf) placePointCircleImpl(const Vecf point, const Vecf circlePos, const float circleRadius) @safe nothrow pure 692 { 693 return point.distance(circlePos) > circleRadius ? vecfNaN : point; 694 } 695 696 /++ 697 Gives the place of collision of a line and a line. 698 699 Params: 700 first = Line vector. 701 second = Line vectors. 702 703 Returns: 704 Place of collision. 705 +/ 706 const(Vecf) placeLineLineImpl(const Vecf[] first, const Vecf[] second) @safe nothrow pure 707 { 708 const a = first[0]; 709 const b = first[1]; 710 const c = second[0]; 711 const d = second[1]; 712 713 const denominator = ((b.x - a.x) * (d.y - c.y)) - ((b.y - a.y) * (d.x - c.x)); 714 715 const numerator1 = ((a.y - c.y) * (d.x - c.x)) - ((a.x - c.x) * (d.y - c.y)); 716 const numerator2 = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y)); 717 718 const r = numerator1 / denominator; 719 const s = numerator2 / denominator; 720 721 if((r >= 0 && r <= 1) && (s >= 0 && s <= 1)) 722 return first[0] - ((first[1] - first[0]) * r); 723 else 724 return vecfNaN; 725 } 726 727 /++ 728 Gives the place of collision of a line and a rectangle. 729 730 Params: 731 a = Line vector. 732 b = Rectangle vectors. 733 734 Returns: 735 Place of collision. 736 +/ 737 const(Vecf) placeLineRectImpl(const Vecf[] a, const Vecf[] b) @safe nothrow pure 738 { 739 import tida.each; 740 741 if (b[0] == a[0] || 742 b[0] == a[1] || 743 b[1] == a[0] || 744 b[1] == a[1]) 745 return a[0]; 746 747 Vecf result = vecfNaN; 748 749 foreach(x,y; LineNoThrowImpl(a[0], a[1])) { 750 if (x > b[0].x && 751 x < b[1].x && 752 y > b[0].y && 753 y < b[1].y) 754 { 755 return vecf(x, y); 756 } 757 } 758 759 return result; 760 } 761 762 /++ 763 Gives the place of collision of a line and a circle. 764 765 Params: 766 line = line vector. 767 circlePos = Circle position. 768 circleRadius = Circle radius. 769 770 Returns: 771 Place of collision. 772 +/ 773 const(Vecf) placeLineCircleImpl(const Vecf[] line, const Vecf circlePos, const float circleRadius) @safe nothrow pure 774 { 775 import tida.each; 776 777 Vecf place = vecfNaN; 778 779 foreach(x, y; LineNoThrowImpl(line[0], line[1])) { 780 if(!(place = placePointCircleImpl(vecf(x, y), circlePos, circleRadius)).isVecfNaN) 781 return place; 782 } 783 784 return vecfNaN; 785 } 786 787 /++ 788 Gives the place of collision of a rectangle and a rectangle. 789 790 Params: 791 a = Rectangle vector. 792 b = Rectangle vectors. 793 isRecurse = Used only for recursion. 794 795 Returns: 796 Place of collision. 797 +/ 798 const(Vecf) placeRectRectImpl(const Vecf[] a, const Vecf[] b,bool isRecurse = false) @safe nothrow pure 799 { 800 if (lineRectImpl([a[0], a[0] + vecf(a[1].x, 0)], b)) 801 return placeLineRectImpl([a[0], a[0] + vecf(a[1].x, 0)], b); 802 if (lineRectImpl([a[0] + vecf(a[1].x, 0), a[1]], b)) 803 return placeLineRectImpl([a[0] + vecf(a[1].x, 0), a[1]], b); 804 if (lineRectImpl([a[0], a[0] + vecf(0, a[1].y)], b)) 805 return placeLineRectImpl([a[0], a[0] + vecf(0, a[1].y)], b); 806 if (lineRectImpl([a[0] + vecf(0, a[1].y), a[1]], b)) 807 return placeLineRectImpl([a[0] + vecf(0, a[1].y), a[1]], b); 808 809 if(!isRecurse) 810 return placeRectRectImpl(b, a, true); 811 else 812 return vecfNaN; 813 } 814 815 /++ 816 Gives the place of collision of a rectangle and a circle. 817 818 Params: 819 a = Rectangle vectors. 820 circlePos = Circle position. 821 circleRadius = Circle radius. 822 823 Returns: 824 Place of collision. 825 +/ 826 const(Vecf) placeRectCircleImpl(const Vecf[] a, const Vecf circlePos, const float circleRadius) @safe nothrow pure 827 { 828 if (lineCircleImpl([a[0], a[0] + vecf(a[1].x, 0)], circlePos, circleRadius)) 829 return placeLineCircleImpl([a[0], a[0] + vecf(a[1].x, 0)], circlePos, circleRadius); 830 if (lineCircleImpl([a[0] + vecf(a[1].x, 0), a[1]], circlePos, circleRadius)) 831 return placeLineCircleImpl([a[0] + vecf(a[1].x, 0), a[1]], circlePos, circleRadius); 832 if (lineCircleImpl([a[0], a[0] + vecf(0, a[1].y)], circlePos, circleRadius)) 833 return placeLineCircleImpl([a[0], a[0] + vecf(0, a[1].y)], circlePos, circleRadius); 834 if (lineCircleImpl([a[0] + vecf(0, a[1].y), a[1]], circlePos, circleRadius)) 835 return placeLineCircleImpl([a[0] + vecf(0, a[1].y), a[1]], circlePos, circleRadius); 836 837 Vecf place = vecfNaN; 838 Vecf[] line = [ 839 circlePos - vecf(0, circleRadius), 840 circlePos + vecf(0, circleRadius) 841 ]; 842 843 if(!(place = placeLineRectImpl(line, a)).isVecfNaN) return place; 844 line = [ 845 circlePos - vecf(circleRadius, 0), 846 circlePos + vecf(circleRadius, 0) 847 ]; 848 849 return placeLineRectImpl(line, a); 850 } 851 852 /++ 853 Gives the place of collision of a circle and a circle. 854 855 Params: 856 fPos = First circle position. 857 fRadius = First circle radius. 858 fPos = Second circle position. 859 fRadius = Second circle radius. 860 861 Returns: 862 Place of collision. 863 +/ 864 const(Vecf) placeCircleCircleImpl( const Vecf fPos, 865 const float fRadius, 866 const Vecf sPos, 867 const float sRadius) 868 @safe nothrow pure 869 { 870 //immutable dist = first.begin - second.begin; 871 //return dist.length <= first.radius + second.radius; 872 873 if((fPos - sPos).length <= fRadius + sRadius) { 874 return Vecf((fPos.x * sRadius + sPos.x * fRadius) / (fRadius + sRadius), 875 (fPos.y * sRadius + sPos.y * fRadius) / (fRadius + sRadius)); 876 } else { 877 return vecfNaN; 878 } 879 } 880 881 /++ 882 Finds the point of contact between two forms. 883 884 Params: 885 first = First shape. 886 second = Second shape. 887 firstPos = First shape position. 888 secondPos = Second shape position. 889 890 Returns: 891 point of contact between two forms. 892 +/ 893 const(Vecf) placeofTangents(Shape!float first, 894 Shape!float second, 895 Vecf firstPos = vecf(0, 0), 896 Vecf secondPos = vecf(0, 0)) 897 @safe nothrow pure 898 in(first.type != ShapeType.unknown && second.type != ShapeType.unknown) 899 in(first.type != ShapeType.triangle && second.type != ShapeType.triangle) 900 do 901 { 902 first.begin = first.begin + firstPos; 903 second.begin = second.begin + secondPos; 904 905 if (first.type == ShapeType.line || first.type == ShapeType.rectangle) 906 first.end = first.end + firstPos; 907 908 if (second.type == ShapeType.line || second.type == ShapeType.rectangle) 909 second.end = second.end + secondPos; 910 911 switch (first.type) 912 { 913 case ShapeType.point: 914 switch(second.type) { 915 case ShapeType.point: 916 return first.begin == second.begin ? first.begin : vecfNaN; 917 918 case ShapeType.line: 919 return placePointLineImpl(first.begin, second.to!(Vecf[])); 920 921 case ShapeType.rectangle: 922 return placePointRectImpl(first.begin, second.to!(Vecf[])); 923 924 case ShapeType.circle: 925 return placePointCircleImpl(first.begin, second.begin, second.radius); 926 927 case ShapeType.polygon: 928 return placePolygonAndPoint(second.data, first.begin); 929 930 case ShapeType.multi: 931 Vecf place = vecfNaN; 932 933 foreach(shape; second.shapes) { 934 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 935 return place; 936 } 937 938 return vecfNaN; 939 940 default: 941 return vecfNaN; 942 } 943 944 case ShapeType.line: 945 switch(second.type) 946 { 947 case ShapeType.point: 948 return placePointLineImpl(second.begin, first.to!(Vecf[])); 949 950 case ShapeType.line: 951 return placeLineLineImpl(first.to!(Vecf[]), second.to!(Vecf[])); 952 953 case ShapeType.rectangle: 954 return placeLineRectImpl(second.to!(Vecf[]), first.to!(Vecf[])); 955 956 case ShapeType.circle: 957 return placeLineCircleImpl(first.to!(Vecf[]), second.begin, second.radius); 958 959 case ShapeType.polygon: 960 return placePolygonAndLine(second.data, first.to!(Vecf[])); 961 962 case ShapeType.multi: 963 Vecf place = vecfNaN; 964 965 foreach(shape; second.shapes) { 966 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 967 return place; 968 } 969 970 return vecfNaN; 971 972 default: 973 return vecfNaN; 974 } 975 976 case ShapeType.rectangle: 977 switch(second.type) 978 { 979 case ShapeType.point: 980 return placePointRectImpl(second.begin, first.to!(Vecf[])); 981 982 case ShapeType.line: 983 return placeLineRectImpl(second.to!(Vecf[]), first.to!(Vecf[])); 984 985 case ShapeType.rectangle: 986 return placeRectRectImpl(first.to!(Vecf[]), second.to!(Vecf[])); 987 988 case ShapeType.circle: 989 return placeRectCircleImpl(first.to!(Vecf[]), second.begin, second.radius); 990 991 case ShapeType.polygon: 992 return placePolygonAndRect(second.data, first.to!(Vecf[])); 993 994 case ShapeType.multi: 995 Vecf place = vecfNaN; 996 997 foreach(shape; second.shapes) { 998 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 999 return place; 1000 } 1001 1002 return vecfNaN; 1003 1004 default: 1005 return vecfNaN; 1006 } 1007 1008 case ShapeType.circle: 1009 switch(second.type) 1010 { 1011 case ShapeType.point: 1012 return placePointCircleImpl(second.begin, first.begin, first.radius); 1013 1014 case ShapeType.line: 1015 return placeLineCircleImpl(second.to!(Vecf[]), first.begin, first.radius); 1016 1017 case ShapeType.rectangle: 1018 return placeRectCircleImpl(second.to!(Vecf[]), first.begin, first.radius); 1019 1020 case ShapeType.circle: 1021 return placeCircleCircleImpl(first.begin, first.radius, second.begin, second.radius); 1022 1023 case ShapeType.polygon: 1024 return placePolygonAndCircle(second.data, first.begin, first.radius); 1025 1026 case ShapeType.multi: 1027 Vecf place = vecfNaN; 1028 1029 foreach(shape; second.shapes) { 1030 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 1031 return place; 1032 } 1033 1034 return vecfNaN; 1035 1036 default: 1037 return vecfNaN; 1038 } 1039 1040 case ShapeType.polygon: 1041 switch(second.type) 1042 { 1043 case ShapeType.point: 1044 return placePolygonAndPoint(first.data, second.begin); 1045 1046 case ShapeType.line: 1047 return placePolygonAndLine(first.data, second.to!(Vecf[])); 1048 1049 case ShapeType.rectangle: 1050 return placePolygonAndRect(first.data, second.to!(Vecf[])); 1051 1052 case ShapeType.circle: 1053 return placePolygonAndCircle(first.data, second.begin, second.radius); 1054 1055 case ShapeType.polygon: 1056 return placePolygonsCollision(first.data, second.data); 1057 1058 case ShapeType.multi: 1059 Vecf place = vecfNaN; 1060 1061 foreach(shape; second.shapes) { 1062 if(!((place = placeofTangents(first, shape,vecf(0,0), second.begin)).isVecfNaN)) 1063 return place; 1064 } 1065 1066 return vecfNaN; 1067 1068 default: 1069 return vecfNaN; 1070 } 1071 1072 case ShapeType.multi: 1073 Vecf place = vecfNaN; 1074 1075 foreach(shape; first.shapes) { 1076 if(!((place = placeofTangents(shape, second, first.begin, 1077 vecf(0, 0))).isVecfNaN)) 1078 return place; 1079 } 1080 1081 return vecfNaN; 1082 1083 default: 1084 return vecfNaN; 1085 } 1086 } 1087 1088 /++ 1089 The point of contact between the polygon and the point. 1090 1091 Params: 1092 first = Polygon vertexs. 1093 second = Point position. 1094 1095 Returns: 1096 Point of contact between shapes. 1097 +/ 1098 Vecf placePolygonAndPoint(Vecf[] first, Vecf second) @safe nothrow pure 1099 { 1100 int next = 0; 1101 for(int current = 0; current < first.length; current++) 1102 { 1103 next = current + 1; 1104 1105 if (next == first.length) next = 0; 1106 1107 Vecf vc = first[current]; 1108 Vecf vn = first[next]; 1109 1110 if(((vc.y >= second.y && vn.y <= second.y) || (vc.y <= second.y && vn.y >= second.y)) && 1111 (second.x < (vn.x - vc.x) * (second.y - vc.y) / (vn.y - vc.y) + vc.x)) { 1112 return second; 1113 } 1114 } 1115 1116 return vecfNaN; 1117 } 1118 1119 /++ 1120 The point of contact between the polygon and the line. 1121 1122 Params: 1123 first = Polygon vertexs. 1124 second = line position. 1125 1126 Returns: 1127 Point of contact between shapes. 1128 +/ 1129 Vecf placePolygonAndLine(Vecf[] first, Vecf[] second) @safe nothrow pure 1130 { 1131 int next = 0; 1132 for(int current = 0; current < first.length; current++) 1133 { 1134 next = current + 1; 1135 1136 if (next == first.length) next = 0; 1137 1138 Vecf vc = first[current]; 1139 Vecf vn = first[next]; 1140 1141 Vecf place = placeLineLineImpl(second, [vc, vn]); 1142 if(!place.isVecfNaN) return place; 1143 } 1144 1145 return vecfNaN; 1146 } 1147 1148 /++ 1149 The point of contact between the polygon and the rectangle. 1150 1151 Params: 1152 first = Polygon vertexs. 1153 second = Rectangle vertexs. 1154 1155 Returns: 1156 Point of contact between shapes. 1157 +/ 1158 Vecf placePolygonAndRect(Vecf[] first, Vecf[] second) @safe nothrow pure 1159 { 1160 int next = 0; 1161 for(int current = 0; current < first.length; current++) 1162 { 1163 next = current + 1; 1164 1165 if (next == first.length) next = 0; 1166 1167 Vecf vc = first[current]; 1168 Vecf vn = first[next]; 1169 1170 Vecf place = placeLineRectImpl([vc, vn], second); 1171 if(!place.isVecfNaN) return place; 1172 } 1173 1174 return vecfNaN; 1175 } 1176 1177 /++ 1178 The point of contact between the polygon and the circle. 1179 1180 Params: 1181 first = Polygon vertexs. 1182 second = Circle position. 1183 r = Circle radius. 1184 1185 Returns: 1186 Point of contact between shapes. 1187 +/ 1188 Vecf placePolygonAndCircle(Vecf[] first, Vecf second, float r) @safe nothrow pure 1189 { 1190 int next = 0; 1191 for(int current = 0; current < first.length; current++) 1192 { 1193 next = current + 1; 1194 1195 if (next == first.length) next = 0; 1196 1197 Vecf vc = first[current]; 1198 Vecf vn = first[next]; 1199 1200 Vecf place = placeLineCircleImpl([vc, vn], second, r); 1201 if(!place.isVecfNaN) return place; 1202 } 1203 1204 return vecfNaN; 1205 } 1206 1207 /++ 1208 The point of contact between the polygon and the polygon. 1209 1210 Params: 1211 first = First polygon vertexs. 1212 second = Second polygon vertexs. 1213 1214 Returns: 1215 Point of contact between shapes. 1216 +/ 1217 Vecf placePolygonsCollision(Vecf[] first, Vecf[] second) @safe nothrow pure 1218 { 1219 int next = 0; 1220 for (int current = 0; current < first.length; current++) 1221 { 1222 next = current + 1; 1223 1224 if (next == first.length) next = 0; 1225 1226 Vecf vc = first[current]; 1227 Vecf vn = first[next]; 1228 1229 Vecf place = placePolygonAndLine(second, [vc, vn]); 1230 if(!place.isVecfNaN) return place; 1231 1232 place = placePolygonAndPoint(first, second[0]); 1233 if (!place.isVecfNaN) return place; 1234 } 1235 1236 return vecfNaN; 1237 }