Advertisement
jostmey

Simple collision detection in JavaScript

Feb 19th, 2012
4,792
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * Author: Jared Lee Ostmeyer
  3.  * Email Address: jostmey@gmail.com
  4.  * Date Started: 2012-02-06
  5.  * Purpose: Simple collision detection
  6.  * License: http://www.gnu.org/licenses/lgpl.txt
  7.  *
  8.  * This package is designed to create models of geometric shapes. The shapes can be
  9.  * placed anywhere in the cartesian plane, moved around, and combined together to form
  10.  * more complicated composite shapes. In addition, collision detection is provided to
  11.  * determine if any two shapes overlap or intersect on another.
  12.  *
  13.  * By default the package comes with three basic shapes. The example below illustrates
  14.  * the three available shapes.
  15.  *
  16.  *  var point = Shapes().point(4, 5);
  17.  *  var circle = Shapes().circle(10, 12, 4);
  18.  *  var rectangle = Shapes().rectangle(3, 0, 15, 24);
  19.  *
  20.  * In this example, the first line creates a point with coordinate (4, 5). The second
  21.  * line creates a circle centered at thecoordinate (10, 12) with radius 4. The last line
  22.  * creates a rectangle centered at the coordinate (3, 0) with length 15 and height 24.
  23.  *
  24.  * Each shapes contains a set of methods for manipulating and retriving information about
  25.  * the shape.
  26.  *
  27.  *  getCenter()  -> { x: FLOAT, y: FLOAT }
  28.  *  setCenter(x_center, y_center)
  29.  *  translate(dx, dy)
  30.  *  intersect(shape)  -> BOOLEAN
  31.  *
  32.  * These methods are easy to use. As an example, suppose you want to check if one shape
  33.  * intersects or overlaps another shape. The "intersect" command will accomplish this
  34.  * task, as shown below.
  35.  *
  36.  *  circle.intersect(rectangle)
  37.  *
  38.  * This snippet of code will return a boolean value indicating if the circle from the
  39.  * first example collides with the rectangle (also from the first example).
  40.  *
  41.  * Sometimes it may be advantageous to create additional shapes. This can easily be done
  42.  * by using the "build" command. This command creates a composite shape out of the three
  43.  * basic shapes already provided. Here is an example of how this command may be used to
  44.  * define a new composite shape.
  45.  *
  46.  *  doubleCircle = function(x, y, r) {
  47.  *      return Shapes().build([
  48.  *          Shapes().circle(x-r, y, r),
  49.  *          Shapes().circle(x+r, y, r)
  50.  *      ]);
  51.  *  }
  52.  *
  53.  * The new shape can then be created using the line of code below. The new composite
  54.  * shape will contain all the same methods as the already existing default shapes
  55.  * provided.
  56.  *
  57.  *  var compositeShape = doubleCircle(0, 10, 4);
  58.  *
  59.  */
  60.  
  61. function Shapes() {
  62.  
  63.     var shapes = {
  64.  
  65.         /* Create a point centered at the coordinate (x, y). */
  66.  
  67.         point: function(x, y) {
  68.  
  69.             return {
  70.  
  71.                 __private: { x: x, y: y, type: "point" },
  72.  
  73.                 getCenter: function() { return { x: this.__private.x, y: this.__private.y }; },
  74.  
  75.                 setCenter: function(x, y) { this.__private.x = x; this.__private.y = y; },
  76.  
  77.                 translate: function(dx, dy) { this.__private.x += dx; this.__private.y += dy; },
  78.  
  79.                 intersect: function(shape) {
  80.                     if(shape.__private.type)
  81.                         switch(shape.__private.type) {
  82.                             case "point": return intersect.pointPoint(this, shape);
  83.                             case "circle": return intersect.pointCircle(this, shape);
  84.                             case "rectangle": return intersect.pointRectangle(this, shape);
  85.                         }
  86.                     else if(shape.__private.primitives)
  87.                         for(var index in shape.__private.primitives)
  88.                             if(this.intersect(shape.__private.primitives[index]))
  89.                                 return true;
  90.                     return false;
  91.                 }
  92.             };
  93.         },
  94.  
  95.         /* Create a circle centered at the coordinate (x, y) with radius r. */
  96.  
  97.         circle: function(x, y, r) {
  98.  
  99.             return {
  100.  
  101.                 __private: { x: x, y: y, r: r, type: "circle" },
  102.  
  103.                 getCenter: function() { return { x: this.__private.x, y: this.__private.y }; },
  104.  
  105.                 setCenter: function(x, y) { this.__private.x = x; this.__private.y = y; },
  106.  
  107.                 translate: function(dx, dy) { this.__private.x += dx; this.__private.y += dy; },
  108.  
  109.                 intersect: function(shape) {
  110.                     if(shape.__private.type)
  111.                         switch(shape.__private.type) {
  112.                             case "point": return intersect.pointCircle(shape, this);
  113.                             case "circle": return intersect.circleCircle(this, shape);
  114.                             case "rectangle": return intersect.circleRectangle(this, shape);
  115.                         }
  116.                     else if(shape.__private.primitives)
  117.                         for(var index in shape.__private.primitives)
  118.                             if(this.intersect(shape.__private.primitives[index]))
  119.                                 return true;
  120.                     return false;
  121.                 }
  122.             };
  123.         },
  124.  
  125.         /* Create a rectangle centered at the coordinate (x, y) with width dy and height dy. */
  126.  
  127.         rectangle: function(x, y, dx, dy) {
  128.  
  129.             return  {
  130.  
  131.                 __private: { x: x, y: y, dx: dx, dy: dy, type: "rectangle" },
  132.  
  133.                 getCenter: function() { return { x: this.__private.x, y: this.__private.y }; },
  134.  
  135.                 setCenter: function(x, y) { this.__private.x = x; this.__private.y = y; },
  136.  
  137.                 translate: function(dx, dy) { this.__private.x += dx; this.__private.y += dy; },
  138.  
  139.                 intersect: function(shape) {
  140.                     if(shape.__private.type)
  141.                         switch(shape.__private.type) {
  142.                             case "point": return intersect.pointRectangle(shape, this);
  143.                             case "circle": return intersect.circleRectangle(shape, this);
  144.                             case "rectangle": return intersect.rectangleRectangle(this, shape);
  145.                         }
  146.                     else if(shape.__private.primitives)
  147.                         for(var index in shape.__private.primitives)
  148.                             if(this.intersect(shape.__private.primitives[index]))
  149.                                 return true;
  150.                     return false;
  151.                 }
  152.             };
  153.         },
  154.  
  155.         /* Command useful for creating composite shapes out of the ones that already exist. */
  156.  
  157.         build: function(primitives) {
  158.             return {
  159.  
  160.                 __private: { primitives: primitives },
  161.  
  162.                 getCenter: function() {
  163.                     var x = 0.0, y = 0.0, count = 0;
  164.                     for(var index in this.__private.primitives) {
  165.                         var r = this.__private.primitives[index].getCenter();
  166.                         x += r.x; y += r.y; count++;
  167.                     }
  168.                     x /= count; y /= count;
  169.                     return { x: x, y: y };
  170.                 },
  171.  
  172.                 setCenter: function(x, y) {
  173.                     var r = this.getCenter();
  174.                     for(var index in this.__private.primitives) {
  175.                         var r_ = this.__private.primitives[index].getCenter();
  176.                         this.__private.primitives[index].setCenter(x+(r_.x-r.x), y+(r_.y-r.y));
  177.                     }
  178.                 },
  179.  
  180.                 translate: function(dx, dy) {
  181.                     for(var index in this.__private.primitives)
  182.                         this.__private.primitives[index].translate(dx, dy);
  183.                 },
  184.  
  185.                 intersect: function(shape) {
  186.                     for(var index in this.__private.primitives)
  187.                         if(this.__private.primitives[index].intersect(shape))
  188.                             return true;
  189.                     return false;
  190.                 }
  191.             }
  192.         }
  193.     };
  194.  
  195.     var intersect = {
  196.  
  197.         /* Methods for detecting if a point intersects with any of the other primitive shapes. */
  198.  
  199.         pointPoint: function(point, point_) {
  200.             return (point.__private.x == point_.__private.x && point.__private.y == point_.__private.y);
  201.         },
  202.  
  203.         pointCircle: function(point, circle) {
  204.             var dx = circle.__private.x-point.__private.x, dy = circle.__private.y-point.__private.y;
  205.             if(dx*dx+dy*dy <= circle.__private.r*circle.__private.r) return true;
  206.             return false;
  207.         },
  208.  
  209.         pointRectangle: function(point, rectangle) {
  210.             if(point.__private.x < rectangle.__private.x-rectangle.__private.dx/2.0 ||
  211.                 rectangle.__private.x+rectangle.__private.dx/2.0 < point.__private.x) return false;
  212.             if(point.__private.y < rectangle.__private.y-rectangle.__private.dy/2.0 ||
  213.                 rectangle.__private.y+rectangle.__private.dy/2.0 < point.__private.y) return false;
  214.             return true;
  215.         },
  216.  
  217.         /* Methods for detecting if a circle intersects with any of the remaining primitive shapes. */
  218.  
  219.         circleCircle: function(circle, circle_) {
  220.             var dx = circle.__private.x-circle_.__private.x;
  221.             var dy = circle.__private.y-circle_.__private.y;
  222.             var r_sum = circle.__private.r+circle_.__private.r;
  223.             if(dx*dx+dy*dy <= r_sum*r_sum) return true;
  224.             return false;
  225.         },
  226.  
  227.         circleRectangle: function(circle, rectangle) {
  228.             if(
  229.                 this.pointRectangle(
  230.                     shapes.point(circle.__private.x, circle.__private.y),
  231.                     shapes.rectangle(
  232.                         rectangle.__private.x, rectangle.__private.y,
  233.                         rectangle.__private.dx, rectangle.__private.dy+2*circle.__private.r
  234.                     )
  235.                 ) ||
  236.                 this.pointRectangle(
  237.                     shapes.point(circle.__private.x, circle.__private.y),
  238.                     shapes.rectangle(
  239.                         rectangle.__private.x, rectangle.__private.y,
  240.                         rectangle.__private.dx+2*circle.__private.r, rectangle.__private.dy
  241.                     )
  242.                 ) ||
  243.                 this.pointCircle(
  244.                     shapes.point(
  245.                         rectangle.__private.x-rectangle.__private.dx/2.0,
  246.                         rectangle.__private.y-rectangle.__private.dy/2.0
  247.                     ),
  248.                     circle
  249.                 ) ||
  250.                 this.pointCircle(
  251.                     shapes.point(
  252.                         rectangle.__private.x-rectangle.__private.dx/2.0,
  253.                         rectangle.__private.y+rectangle.__private.dy/2.0
  254.                     ),
  255.                     circle
  256.                 ) ||
  257.                 this.pointCircle(
  258.                     shapes.point(
  259.                         rectangle.__private.x+rectangle.__private.dx/2.0,
  260.                         rectangle.__private.y-rectangle.__private.dy/2.0
  261.                     ),
  262.                     circle
  263.                 ) ||
  264.                 this.pointCircle(
  265.                     shapes.point(
  266.                         rectangle.__private.x+rectangle.__private.dx/2.0,
  267.                         rectangle.__private.y+rectangle.__private.dy/2.0
  268.                     ),
  269.                     circle
  270.                 )
  271.             ) return true;
  272.             return false;
  273.         },
  274.  
  275.         /* Methods for detecting if a rectangle intersects with any of the remaining primitive shapes. */
  276.  
  277.         rectangleRectangle: function(rectangle, rectangle_) {
  278.             if(rectangle_.__private.x+rectangle_.__private.dx/2.0 < rectangle.__private.x-rectangle.__private.dx/2.0 ||
  279.                 rectangle.__private.x+rectangle.__private.dx/2.0 < rectangle_.__private.x-rectangle_.__private.dx/2.0)
  280.                 return false;
  281.             if(rectangle_.__private.y+rectangle_.__private.dy/2.0 < rectangle.__private.y-rectangle.__private.dy/2.0 ||
  282.                 rectangle.__private.y+rectangle.__private.dy/2.0 < rectangle_.__private.y-rectangle_.__private.dy/2.0)
  283.                 return false;
  284.             return true;
  285.         }
  286.     };
  287.  
  288.     return shapes;
  289. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement