├── .gitignore ├── CollisionDiagrams.ai ├── Website ├── images │ ├── challenge.jpg │ ├── license.jpg │ ├── line-rect.jpg │ ├── rect-rect.jpg │ ├── tri-point.jpg │ ├── line-point.jpg │ ├── poly-point.jpg │ ├── bounding-box.jpg │ ├── line-of-sight.jpg │ ├── point-circle.jpg │ ├── bounding-circle.jpg │ ├── social-thumbnail.jpg │ ├── rect-bounding-box.jpg │ ├── table-of-contents.jpg │ └── where-are-other-triangle-examples.jpg ├── rect-rect_test_edges.php ├── section_5_challenges.php ├── where_are_the_other_triangle_examples.php ├── section_4_challenges.php ├── includes │ └── footer.php ├── section_2_challenges.php ├── section_1_challenges.php ├── section_3_challenges.php ├── js │ └── smartquotes.min.js ├── license.php ├── thanks.php ├── table_of_contents.php ├── point-point.php ├── circle-circle.php ├── index.php ├── tri-point.php ├── line-line.php ├── point-rect.php ├── circle-rect.php ├── line-point.php ├── rect-rect.php ├── what_you_should_already_know.php ├── point-circle.php ├── line-rect.php ├── poly-line.php ├── poly-rect.php ├── poly-poly.php ├── object_oriented_collision.php ├── line-circle.php ├── poly-point.php └── css │ └── stylesheet.css ├── CodeExamples ├── ObjectOrientedCollision │ ├── Circle.pde │ ├── Rectangle.pde │ ├── CircleRectCollision.pde │ ├── ObjectOrientedCollision.pde │ └── web-export │ │ └── ObjectOrientedCollision.pde ├── Introduction │ ├── Circle.pde │ ├── Rectangle.pde │ ├── Line.pde │ ├── Introduction.pde │ └── CollisionFunctions.pde ├── PointPoint │ ├── PointPoint.pde │ └── web-export │ │ └── PointPoint.pde ├── CircleCircle │ ├── web-export │ │ └── CircleCircle.pde │ └── CircleCircle.pde ├── PointRect │ ├── PointRect.pde │ └── web-export │ │ └── PointRect.pde ├── RectRect │ ├── RectRect.pde │ └── web-export │ │ └── RectRect.pde ├── PointCircle │ ├── PointCircle.pde │ └── web-export │ │ └── PointCircle.pde ├── TriPoint │ ├── TriPoint.pde │ └── web-export │ │ └── TriPoint.pde ├── LinePoint │ ├── LinePoint.pde │ └── web-export │ │ └── LinePoint.pde ├── CircleRect │ ├── CircleRect.pde │ └── web-export │ │ └── CircleRect.pde ├── LineLine │ ├── web-export │ │ └── LineLine.pde │ └── LineLine.pde ├── PolyPoint │ ├── PolyPoint.pde │ └── web-export │ │ └── PolyPoint.pde ├── LineRect │ ├── LineRect.pde │ └── web-export │ │ └── LineRect.pde ├── PolyLine │ ├── PolyLine.pde │ └── web-export │ │ └── PolyLine.pde ├── LineCircle │ ├── LineCircle.pde │ └── web-export │ │ └── LineCircle.pde ├── PolyRect │ ├── PolyRect.pde │ └── web-export │ │ └── PolyRect.pde ├── PolyPoly │ ├── PolyPoly.pde │ └── web-export │ │ └── PolyPoly.pde └── PolyCircle │ ├── PolyCircle.pde │ └── web-export │ └── PolyCircle.pde └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ToDo.txt 2 | markdown.pl 3 | _OldVersions/ 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /CollisionDiagrams.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/CollisionDiagrams.ai -------------------------------------------------------------------------------- /Website/images/challenge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/challenge.jpg -------------------------------------------------------------------------------- /Website/images/license.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/license.jpg -------------------------------------------------------------------------------- /Website/images/line-rect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/line-rect.jpg -------------------------------------------------------------------------------- /Website/images/rect-rect.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/rect-rect.jpg -------------------------------------------------------------------------------- /Website/images/tri-point.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/tri-point.jpg -------------------------------------------------------------------------------- /Website/images/line-point.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/line-point.jpg -------------------------------------------------------------------------------- /Website/images/poly-point.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/poly-point.jpg -------------------------------------------------------------------------------- /Website/images/bounding-box.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/bounding-box.jpg -------------------------------------------------------------------------------- /Website/images/line-of-sight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/line-of-sight.jpg -------------------------------------------------------------------------------- /Website/images/point-circle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/point-circle.jpg -------------------------------------------------------------------------------- /Website/images/bounding-circle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/bounding-circle.jpg -------------------------------------------------------------------------------- /Website/images/social-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/social-thumbnail.jpg -------------------------------------------------------------------------------- /Website/images/rect-bounding-box.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/rect-bounding-box.jpg -------------------------------------------------------------------------------- /Website/images/table-of-contents.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/table-of-contents.jpg -------------------------------------------------------------------------------- /Website/images/where-are-other-triangle-examples.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffThompson/CollisionDetection/HEAD/Website/images/where-are-other-triangle-examples.jpg -------------------------------------------------------------------------------- /Website/rect-rect_test_edges.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

RECTANGLE/RECTANGLE (TEST EDGES)

4 | 5 | 6 | -------------------------------------------------------------------------------- /CodeExamples/ObjectOrientedCollision/Circle.pde: -------------------------------------------------------------------------------- 1 | 2 | class Circle { 3 | float x, y; // position 4 | float r; // radius 5 | 6 | Circle (float _x, float _y, float _r) { 7 | x = _x; 8 | y = _y; 9 | r = _r; 10 | } 11 | 12 | // move into mouse position 13 | void update() { 14 | x = mouseX; 15 | y = mouseY; 16 | } 17 | 18 | // draw 19 | void display() { 20 | fill(0, 150); 21 | noStroke(); 22 | ellipse(x,y, r*2, r*2); 23 | } 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /CodeExamples/Introduction/Circle.pde: -------------------------------------------------------------------------------- 1 | 2 | class Circle { 3 | float x, y; 4 | float r; 5 | float speed; 6 | boolean hit = false; 7 | 8 | Circle(float _x, float _y) { 9 | x = _x; 10 | y = _y; 11 | r = random(8,20); 12 | speed = random(0.5, 2); 13 | } 14 | 15 | void update() { 16 | y += speed; 17 | hit = circleCircle(x,y,r, cx,cy,cr); 18 | } 19 | 20 | void display() { 21 | if (!hit) fill(0,150,255, 150); 22 | else fill(255,150,0, 150); 23 | noStroke(); 24 | ellipse(x,y, r*2,r*2); 25 | } 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /Website/section_5_challenges.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

SECTION 5

6 | 7 |

CHALLENGES

8 | 9 |

Try these challenge prompts to modify the code we've written:

10 | 11 |

    12 |
  1. Can you build the missing triangle-specific functions?
  2. 13 |
  3. Spikes! Can you make a simple game where the player jumps over pits of spikes?
  4. 14 |

15 | 16 | 17 | -------------------------------------------------------------------------------- /CodeExamples/Introduction/Rectangle.pde: -------------------------------------------------------------------------------- 1 | 2 | class Rectangle { 3 | float x, y; 4 | float w, h; 5 | float speed; 6 | boolean hit = false; 7 | 8 | Rectangle(float _x, float _y) { 9 | x = _x; 10 | y = _y; 11 | w = random(8,20); 12 | h = random(8,20); 13 | speed = random(0.5, 2); 14 | } 15 | 16 | void update() { 17 | y += speed; 18 | hit = circleRect(cx,cy,cr, x,y,w,h); 19 | } 20 | 21 | void display() { 22 | if (!hit) fill(0,150,255, 150); 23 | else fill(255,150,0, 150); 24 | noStroke(); 25 | rect(x,y, w,h); 26 | } 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /CodeExamples/Introduction/Line.pde: -------------------------------------------------------------------------------- 1 | 2 | class Line { 3 | float x1, y1, x2, y2; 4 | float speed; 5 | boolean hit = false; 6 | 7 | Line(float _x1, float _y1, float _x2, float _y2) { 8 | x1 = _x1; 9 | y1 = _y1; 10 | x2 = _x2; 11 | y2 = _y2; 12 | speed = random(0.5, 2); 13 | } 14 | 15 | void update() { 16 | y1 += speed; 17 | y2 += speed; 18 | hit = lineCircle(x1,y1,x2,y2, cx,cy,cr); 19 | } 20 | 21 | void display() { 22 | if (!hit) stroke(0,150,255, 150); 23 | else stroke(255,150,0, 150); 24 | strokeWeight(5); 25 | line(x1,y1, x2,y2); 26 | } 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /Website/where_are_the_other_triangle_examples.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Triangle with a question mark

4 | 5 |

WHERE ARE THE OTHER TRIANGLE EXAMPLES?

6 | 7 |

You might be wondering where all the other triangle collision examples are. The answer: we've already made them! The smallest number of sides a polygon can have is three, so all the polygon code will work for triangles too.

8 | 9 |

However, since our polygon code is meant to work with PVectors, you will need to define your triangles using three PVector objects, or modify the collision functions.

10 | 11 | 12 | -------------------------------------------------------------------------------- /CodeExamples/ObjectOrientedCollision/Rectangle.pde: -------------------------------------------------------------------------------- 1 | 2 | class Rectangle { 3 | float x, y; // position 4 | float w, h; // size 5 | boolean hit = false; // is it hit? 6 | 7 | Rectangle (float _x, float _y, float _w, float _h) { 8 | x = _x; 9 | y = _y; 10 | w = _w; 11 | h = _h; 12 | } 13 | 14 | // check for collision with the circle using the 15 | // Circle/Rect function we made in the beginning 16 | void checkCollision(Circle c) { 17 | hit = circleRect(c.x,c.y,c.r, x,y,w,h); 18 | } 19 | 20 | // draw the rectangle 21 | // if hit, change the fill color 22 | void display() { 23 | if (hit) fill(255,150,0); 24 | else fill(0,150,255); 25 | noStroke(); 26 | rect(x,y, w,h); 27 | } 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /Website/section_4_challenges.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

SECTION 4

6 | 7 |

CHALLENGES

8 | 9 |

Try these challenge prompts to modify the code we've written:

10 | 11 |

    12 |
  1. Can you add to the code so the polygon edge that is hit gets highlighted?
  2. 13 |
  3. Can you make polygons whose vertices change position over time and watch them collide? Hint: you can use random(), but for more natural motion, look at the Perlin noise example in Basics > Math > Noise2D in the Processing IDE.
  4. 14 |
  5. Randomized polygons make great abstract monsters! Can you build a simple game with polygonal monsters chasing each other?
  6. 15 |

16 | 17 | 18 | -------------------------------------------------------------------------------- /CodeExamples/ObjectOrientedCollision/CircleRectCollision.pde: -------------------------------------------------------------------------------- 1 | 2 | // CIRCLE/RECTANGLE 3 | boolean circleRect(float cx, float cy, float radius, float rx, float ry, float rw, float rh) { 4 | 5 | // temporary variables to set edges for testing 6 | float testX = cx; 7 | float testY = cy; 8 | 9 | // which edge is closest? 10 | if (cx < rx) testX = rx; // compare to left edge 11 | else if (cx > rx+rw) testX = rx+rw; // right edge 12 | if (cy < ry) testY = ry; // top edge 13 | else if (cy > ry+rh) testY = ry+rh; // bottom edge 14 | 15 | // get distance from closest edges 16 | float distX = cx-testX; 17 | float distY = cy-testY; 18 | float distance = sqrt( (distX*distX) + (distY*distY) ); 19 | 20 | // if the distance is less than the radius, collision! 21 | if (distance <= radius) { 22 | return true; 23 | } 24 | return false; 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /Website/includes/footer.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

NEXT: ' . $next_onscreen . '

'; 7 | } 8 | ?> 9 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Website/section_2_challenges.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

SECTION 2

6 | 7 |

CHALLENGES

8 | 9 |

Try these challenge prompts to modify the code we've written:

10 | 11 |

    12 |
  1. Can you make an object change color gradually as the mouse gets closer? Hint: use map() to convert the distance to a useful range. You can also use lerpColor() to create smooth color transitions.
  2. 13 |
  3. As noted, these examples assume the default rectMode(CENTER). Can you make new versions to work with rectMode(CENTER) instead?
  4. 14 |
  5. With Circle/Rectangle collision covered, we have enough to build Pong! Can you create a simple version with rectangles as paddles and a circle as the ball?
  6. 15 |

16 | 17 | 18 | -------------------------------------------------------------------------------- /Website/section_1_challenges.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

SECTION 1

6 | 7 |

CHALLENGES

8 | 9 |

Try these challenge prompts to modify the code we've written. Don't get frustrated if you can't figure them all out — some are intended for more experienced programmers:

10 | 11 |

    12 |
  1. Try changing the colors of the objects when they collide. Hint: look in the draw() section for the fill() command.
  2. 13 |
  3. Can you make something else interesting happen when collision occurs? Objects change size, move across the screen, etc?
  4. 14 |
  5. Our Point/Point collision is very finicky: you have to be in the exact location of the other point for the collision to trigger. Can you modify the code to include a "buffer zone" where collision happens, even if the two points aren't actually touching?
  6. 15 |

16 | 17 | 18 | -------------------------------------------------------------------------------- /CodeExamples/ObjectOrientedCollision/ObjectOrientedCollision.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | OBJECT-ORIENTED COLLISION 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | // a single Circle object, controlled by the mouse 9 | Circle circle; 10 | 11 | // a list of rectangles 12 | Rectangle[] rects = new Rectangle[8]; 13 | 14 | 15 | void setup() { 16 | size(600,400); 17 | 18 | // create a new Circle with 30px radius 19 | circle = new Circle(0,0, 30); 20 | 21 | // generate rectangles in random locations 22 | for (int i=0; i 2 | 3 | 4 | 5 |

SECTION 3

6 | 7 |

CHALLENGES

8 | 9 |

Try these challenge prompts to modify the code we've written:

10 | 11 |

    12 |
  1. Lasers! Can you build a simple game where the player uses the mouse to navigate an array of lasers and get to the other side of a room? Draw your character as a simple circle or rectangle, or replace them with an image but test the collision as a bounding circle or box.
  2. 13 |
  3. Using Line/Circle collision, we can simulate a ball bouncing off an angled surface. Can you add gravity, plus make the circle bounce off in a direction based on the angle of the line? Hint: to get the bounce direction, get the angle between the ball and the closest point on the line. For this, you can use Processing's angleBetween() function.
  4. 14 |

15 | 16 | 17 | -------------------------------------------------------------------------------- /CodeExamples/PointPoint/PointPoint.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POINT POINT 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px, py; // point controlled by the mouse 9 | float targetX = 300; // target point coordinates 10 | float targetY = 200; 11 | 12 | 13 | void setup() { 14 | size(600, 400); 15 | noCursor(); 16 | 17 | strokeWeight(5); // thicker stroke so points are easier to see 18 | } 19 | 20 | 21 | void draw() { 22 | 23 | // update point position to mouse coordinates 24 | px = mouseX; 25 | py = mouseY; 26 | 27 | // check for collision! 28 | // if hit, make background orange; if not, make it white 29 | boolean colliding = pointPoint(px, py, targetX, targetY); 30 | if (colliding) { 31 | background(255, 150, 0); 32 | } 33 | else { 34 | background(255); 35 | } 36 | 37 | // draw the two points 38 | stroke(0,150,255); 39 | point(targetX, targetY); 40 | 41 | stroke(0,150); 42 | point(px, py); 43 | } 44 | 45 | 46 | // POINT/POINT 47 | boolean pointPoint(float x1, float y1, float x2, float y2) { 48 | 49 | // are the two points in the same location? 50 | if (x1 == x2 && y1 == y2) { 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /Website/js/smartquotes.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * smartquotes.js v0.1.4 3 | * http://github.com/kellym/smartquotesjs 4 | * MIT licensed 5 | * 6 | * Copyright (C) 2013 Kelly Martin, http://kelly-martin.com 7 | */ 8 | (typeof("jQuery")=="function"?jQuery:function(d){var b=document.addEventListener||document.attachEvent,c=document.removeEventListener?"removeEventListener":"detachEvent",a=document.addEventListener?"DOMContentLoaded":"onreadystatechange";b.call(document,a,function(){document[c](a,arguments.callee,false);d()},false)})(function(){var a=document.body;var b=a.childNodes[0];while(b!=null){if(b.nodeType==3){b.nodeValue=b.nodeValue.replace(/(\W|^)"(\S)/g,"$1\u201c$2").replace(/(\u201c[^"]*)"([^"]*$|[^\u201c"]*\u201c)/g,"$1\u201d$2").replace(/([^0-9])"/g,"$1\u201d").replace(/(\W|^)'(\S)/g,"$1\u2018$2").replace(/([a-z])'([a-z])/ig,"$1\u2019$2").replace(/((\u2018[^']*)|[a-z])'([^0-9]|$)/ig,"$1\u2019$3").replace(/(\u2018)([0-9]{2}[^\u2019]*)(\u2018([^0-9]|$)|$|\u2019[a-z])/ig,"\u2019$2$3").replace(/(\B|^)\u2018(?=([^\u2019]*\u2019\b)*([^\u2019\u2018]*\W[\u2019\u2018]\b|[^\u2019\u2018]*$))/ig,"$1\u2019").replace(/'''/g,"\u2034").replace(/("|'')/g,"\u2033").replace(/'/g,"\u2032")}if(b.hasChildNodes()&&b.firstChild.nodeName!="CODE"){b=b.firstChild}else{do{while(b.nextSibling==null&&b!=a){b=b.parentNode}b=b.nextSibling}while(b&&(b.nodeName=="CODE"||b.nodeName=="SCRIPT"||b.nodeName=="STYLE"))}}}); 9 | -------------------------------------------------------------------------------- /CodeExamples/CircleCircle/web-export/CircleCircle.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | CIRCLE/CIRCLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | int c1x = 0; // circle 1 position (controlled by mouse) 9 | int c1y = 0; 10 | int c1r = 30; // radius 11 | 12 | int c2x, c2y; // circle 2 position 13 | int c2r = 100; 14 | 15 | 16 | void setup() { 17 | size($("#wrapper").width(), 400); 18 | noStroke(); 19 | 20 | c2x = width/2; 21 | c2y = height/2; 22 | } 23 | 24 | 25 | void draw() { 26 | background(255); 27 | 28 | // update position to mouse coordinates 29 | c1x = mouseX; 30 | c1y = mouseY; 31 | 32 | // check for collision 33 | // if hit, change color 34 | boolean hit = circleCircle(c1x,c1y,c1r, c2x,c2y,c2r); 35 | if (hit) { 36 | fill(255,150,0); 37 | } 38 | else { 39 | fill(0,150,255); 40 | } 41 | ellipse(c2x,c2y, c2r*2,c2r*2); 42 | 43 | // other circle, controlled by mouse 44 | fill(0, 150); 45 | ellipse(c1x,c1y, c1r*2,c1r*2); 46 | } 47 | 48 | 49 | // check if circles are touching 50 | boolean circleCircle(int c1x, int c1y, int c1r, int c2x, int c2y, int c2r) { 51 | int distX = c1x - c2x; 52 | int distY = c1y - c2y; 53 | int distance = sqrt( (distX*distX) + (distY*distY) ); 54 | 55 | if (distance <= c1r+c2r) { 56 | return true; 57 | } 58 | else { 59 | return false; 60 | } 61 | } 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /CodeExamples/PointRect/PointRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POINT/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px = 0; // point position (move with mouse) 9 | float py = 0; 10 | 11 | float sx = 200; // square position 12 | float sy = 100; 13 | float sw = 200; // and dimensions 14 | float sh = 200; 15 | 16 | 17 | void setup() { 18 | size(600,400); 19 | noCursor(); 20 | 21 | strokeWeight(5); // thicker stroke so points are easier to see 22 | } 23 | 24 | 25 | void draw() { 26 | background(255); 27 | 28 | // update point to mouse coordinates 29 | px = mouseX; 30 | py = mouseY; 31 | 32 | // check for collision 33 | // if hit, change rectangle color 34 | boolean hit = pointRect(px,py, sx,sy,sw,sh); 35 | if (hit) { 36 | fill(255,150,0); 37 | } 38 | else { 39 | fill(0,150,255); 40 | } 41 | noStroke(); 42 | rect(sx,sy, sw,sh); 43 | 44 | // draw the point 45 | stroke(0); 46 | point(px,py); 47 | } 48 | 49 | 50 | // POINT/RECTANGLE 51 | boolean pointRect(float px, float py, float rx, float ry, float rw, float rh) { 52 | 53 | // is the point inside the rectangle's bounds? 54 | if (px >= rx && // right of the left edge AND 55 | px <= rx + rw && // left of the right edge AND 56 | py >= ry && // below the top AND 57 | py <= ry + rh) { // above the bottom 58 | return true; 59 | } 60 | return false; 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /CodeExamples/CircleCircle/CircleCircle.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | CIRCLE/CIRCLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float c1x = 0; // circle 1 position (controlled by mouse) 9 | float c1y = 0; 10 | float c1r = 30; // radius 11 | 12 | float c2x = 300; // circle 2 position 13 | float c2y = 200; 14 | float c2r = 100; 15 | 16 | 17 | void setup() { 18 | size(600,400); 19 | noStroke(); 20 | } 21 | 22 | 23 | void draw() { 24 | background(255); 25 | 26 | // update position to mouse coordinates 27 | c1x = mouseX; 28 | c1y = mouseY; 29 | 30 | // check for collision 31 | // if hit, change color 32 | boolean hit = circleCircle(c1x,c1y,c1r, c2x,c2y,c2r); 33 | if (hit) { 34 | fill(255,150,0); 35 | } 36 | else { 37 | fill(0,150,255); 38 | } 39 | ellipse(c2x,c2y, c2r*2,c2r*2); 40 | 41 | // other circle, controlled by mouse 42 | fill(0, 150); 43 | ellipse(c1x,c1y, c1r*2,c1r*2); 44 | } 45 | 46 | 47 | // CIRCLE/CIRCLE 48 | boolean circleCircle(float c1x, float c1y, float c1r, float c2x, float c2y, float c2r) { 49 | 50 | // get distance between the circle's centers 51 | // use the Pythagorean Theorem to compute the distance 52 | float distX = c1x - c2x; 53 | float distY = c1y - c2y; 54 | float distance = sqrt( (distX*distX) + (distY*distY) ); 55 | 56 | // if the distance is less than the sum of the circle's 57 | // radii, the circles are touching! 58 | if (distance <= c1r+c2r) { 59 | return true; 60 | } 61 | return false; 62 | } 63 | 64 | 65 | -------------------------------------------------------------------------------- /CodeExamples/PointRect/web-export/PointRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POINT/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px = 0; // point position (move with mouse) 9 | float py = 0; 10 | 11 | float sx, sy; // square position 12 | float sw = 200; // and dimensions 13 | float sh = 200; 14 | 15 | 16 | void setup() { 17 | size($("#wrapper").width(), 400); 18 | noCursor(); 19 | 20 | strokeWeight(15); // thicker stroke so points are easier to see 21 | 22 | sx = width/2-sw/2; 23 | sy = height/2-sh/2; 24 | } 25 | 26 | 27 | void draw() { 28 | background(255); 29 | 30 | // update point to mouse coordinates 31 | px = mouseX; 32 | py = mouseY; 33 | 34 | // check for collision 35 | // if hit, change rectangle color 36 | boolean hit = pointRect(px,py, sx,sy,sw,sh); 37 | if (hit) { 38 | fill(255,150,0); 39 | } 40 | else { 41 | fill(0,150,255); 42 | } 43 | noStroke(); 44 | rect(sx,sy, sw,sh); 45 | 46 | // draw the point 47 | stroke(0); 48 | point(px,py); 49 | } 50 | 51 | 52 | // POINT/RECTANGLE 53 | boolean pointRect(float px, float py, float rx, float ry, float rw, float rh) { 54 | 55 | // is the point inside the rectangle's bounds? 56 | if (px >= rx && // right of the left edge AND 57 | px <= rx + rw && // left of the right edge AND 58 | py >= ry && // below the top AND 59 | py <= ry + rh) { // above the bottom 60 | return true; 61 | } 62 | return false; 63 | } 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /CodeExamples/RectRect/RectRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | RECTANGLE/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float s1x = 0; // square position (move with mouse) 9 | float s1y = 0; 10 | float s1w = 30; // and dimensions 11 | float s1h = 30; 12 | 13 | float s2x = 200; // same for second square 14 | float s2y = 100; 15 | float s2w = 200; 16 | float s2h = 200; 17 | 18 | 19 | void setup() { 20 | size(600,400); 21 | noStroke(); 22 | } 23 | 24 | 25 | void draw() { 26 | background(255); 27 | 28 | // update square to mouse coordinates 29 | s1x = mouseX; 30 | s1y = mouseY; 31 | 32 | // check for collision 33 | // if hit, change rectangle color 34 | boolean hit = rectRect(s1x,s1y,s1w,s1h, s2x,s2y,s2w,s2h); 35 | if (hit) { 36 | fill(255,150,0); 37 | } 38 | else { 39 | fill(0,150,255); 40 | } 41 | rect(s2x,s2y, s2w,s2h); 42 | 43 | // draw the other square 44 | fill(0, 150); 45 | rect(s1x,s1y, s1w,s1h); 46 | } 47 | 48 | 49 | // RECTANGLE/RECTANGLE 50 | boolean rectRect(float r1x, float r1y, float r1w, float r1h, float r2x, float r2y, float r2w, float r2h) { 51 | 52 | // are the sides of one rectangle touching the other? 53 | 54 | if (r1x + r1w >= r2x && // r1 right edge past r2 left 55 | r1x <= r2x + r2w && // r1 left edge past r2 right 56 | r1y + r1h >= r2y && // r1 top edge past r2 bottom 57 | r1y <= r2y + r2h) { // r1 bottom edge past r2 top 58 | return true; 59 | } 60 | return false; 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /CodeExamples/PointCircle/PointCircle.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POINT/CIRCLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px = 0; // point position 9 | float py = 0; 10 | 11 | float cx = 300; // circle center position 12 | float cy = 200; 13 | float radius = 100; // circle's radius 14 | 15 | 16 | void setup() { 17 | size(600,400); 18 | noCursor(); 19 | 20 | strokeWeight(5); // thicker stroke so points are easier to see 21 | } 22 | 23 | 24 | void draw() { 25 | background(255); 26 | 27 | // update point position to mouse coordinates 28 | px = mouseX; 29 | py = mouseY; 30 | 31 | // check for collision! 32 | boolean hit = pointCircle(px,py, cx,cy, radius); 33 | 34 | // draw circle 35 | // change fill color if hit 36 | if (hit) { 37 | fill(255,150,0); 38 | } 39 | else { 40 | fill(0,150,255); 41 | } 42 | noStroke(); 43 | ellipse(cx,cy, radius*2,radius*2); 44 | 45 | // draw the point 46 | stroke(0); 47 | point(px, py); 48 | } 49 | 50 | 51 | // POINT/CIRCLE 52 | boolean pointCircle(float px, float py, float cx, float cy, float r) { 53 | 54 | // get distance between the point and circle's center 55 | // using the Pythagorean Theorem 56 | float distX = px - cx; 57 | float distY = py - cy; 58 | float distance = sqrt( (distX*distX) + (distY*distY) ); 59 | 60 | // if the distance is less than the circle's 61 | // radius the point is inside! 62 | if (distance <= r) { 63 | return true; 64 | } 65 | return false; 66 | } 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /CodeExamples/RectRect/web-export/RectRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | RECTANGLE/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float s1x = 0; // square position (move with mouse) 9 | float s1y = 0; 10 | float s1w = 30; // and dimensions 11 | float s1h = 30; 12 | 13 | float s2x, s2y; // same for second square 14 | float s2w = 200; 15 | float s2h = 200; 16 | 17 | 18 | void setup() { 19 | size($("#wrapper").width(), 400); 20 | noStroke(); 21 | 22 | s2x = width/2-s2w/2; 23 | s2y = height/2-s2h/2; 24 | } 25 | 26 | 27 | void draw() { 28 | background(255); 29 | 30 | // update square to mouse coordinates 31 | s1x = mouseX; 32 | s1y = mouseY; 33 | 34 | // check for collision 35 | // if hit, change rectangle color 36 | boolean hit = rectRect(s1x,s1y,s1w,s1h, s2x,s2y,s2w,s2h); 37 | if (hit) { 38 | fill(255,150,0); 39 | } 40 | else { 41 | fill(0,150,255); 42 | } 43 | rect(s2x,s2y, s2w,s2h); 44 | 45 | // draw the other square 46 | fill(0, 150); 47 | rect(s1x,s1y, s1w,s1h); 48 | } 49 | 50 | 51 | // RECTANGLE/RECTANGLE 52 | boolean rectRect(float r1x, float r1y, float r1w, float r1h, float r2x, float r2y, float r2w, float r2h) { 53 | 54 | // are the sides of one rectangle touching the other? 55 | 56 | if (r1x + r1w >= r2x && // r1 right edge past r2 left 57 | r1x <= r2x + r2w && // r1 left edge past r2 right 58 | r1y + r1h >= r2y && // r1 top edge past r2 bottom 59 | r1y <= r2y + r2h) { // r1 bottom edge past r2 top 60 | return true; 61 | } 62 | return false; 63 | } 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /CodeExamples/PointCircle/web-export/PointCircle.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POINT/CIRCLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px = 0; // point position 9 | float py = 0; 10 | 11 | float cx, cy; // circle center position 12 | float radius = 100; // circle's radius 13 | 14 | 15 | void setup() { 16 | size($("#wrapper").width(), 400); 17 | noCursor(); 18 | 19 | strokeWeight(15); // thicker stroke so points are easier to see 20 | 21 | cx = width/2; 22 | cy = height/2; 23 | } 24 | 25 | 26 | void draw() { 27 | background(255); 28 | 29 | // update point position to mouse coordinates 30 | px = mouseX; 31 | py = mouseY; 32 | 33 | // check for collision! 34 | boolean hit = pointCircle(px,py, cx,cy, radius); 35 | 36 | // draw circle 37 | // change fill color if hit 38 | if (hit) { 39 | fill(255,150,0); 40 | } 41 | else { 42 | fill(0,150,255); 43 | } 44 | noStroke(); 45 | ellipse(cx,cy, radius*2,radius*2); 46 | 47 | // draw the point 48 | stroke(0); 49 | point(px, py); 50 | } 51 | 52 | 53 | // POINT/CIRCLE 54 | boolean pointCircle(float px, float py, float cx, float cy, float r) { 55 | 56 | // get distance between the point and circle's center 57 | // using the Pythagorean Theorem 58 | float distX = px - cx; 59 | float distY = py - cy; 60 | float distance = sqrt( (distX*distX) + (distY*distY) ); 61 | 62 | // if the distance is less than the circle's 63 | // radius the point is inside! 64 | if (distance <= r) { 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /Website/license.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Share and remix

4 | 5 |

LICENSE

6 | 7 |

This book's entire contents, including the code examples and this text, is released under a Creative Commons Attribution, Non-Commercial, Share-Alike license. This means:

8 | 9 |
    10 |
  1. You're welcome to use this book and the examples to make great stuff, but please cite this book somewhere in your project or its documentation.
  2. 11 |
  3. You can only use the book's text and/or code vertbatim for non-commercial projects. That means you can remix or make your own version of the book, and you can fork and create new libraries based on the code here, so long as they are not commercial projects (ie publishing a book with a publisher). I'm very happy to talk options if you have a paid gig and you'd like to use some of the materials.
  4. 12 |
  5. If you do make a project that forks or remixes this book or code-base, it must be released under this same license or a looser one. Pay it forward!
  6. 13 |
14 | 15 |

But you're free to use the ideas and code from this book inside your projects, so long as this is just a part of what you're working on. In other words: you're welcome to build a super cool game that relies on the code you've seen here, even if you're going to sell it!

16 | 17 |

If you have any questions about what you can and can't do with these examples, please get in touch.

18 | 19 | 20 | -------------------------------------------------------------------------------- /CodeExamples/TriPoint/TriPoint.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | TRIANGLE/POINT 4 | Jeff Thompson | 2015 | ww.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px = 0; // point (set by mouse) 9 | float py = 0; 10 | 11 | float x1 = 300; // three points of the triangle 12 | float y1 = 100; 13 | float x2 = 450; 14 | float y2 = 300; 15 | float x3 = 150; 16 | float y3 = 300; 17 | 18 | 19 | void setup() { 20 | size(600,400); 21 | noCursor(); 22 | 23 | strokeWeight(5); // make point easier to see 24 | } 25 | 26 | 27 | void draw() { 28 | background(255); 29 | 30 | // mouse point to mouse coordinates 31 | px = mouseX; 32 | py = mouseY; 33 | 34 | // check for collision 35 | // if hit, change fill color 36 | boolean hit = triPoint(x1,y1, x2,y2, x3,y3, px,py); 37 | if (hit) fill(255,150,0); 38 | else fill(0,150,255); 39 | noStroke(); 40 | triangle(x1,y1, x2,y2, x3,y3); 41 | 42 | // draw the point 43 | stroke(0, 150); 44 | point(px,py); 45 | } 46 | 47 | 48 | // TRIANGLE/POINT 49 | boolean triPoint(float x1, float y1, float x2, float y2, float x3, float y3, float px, float py) { 50 | 51 | // get the area of the triangle 52 | float areaOrig = abs( (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1) ); 53 | 54 | // get the area of 3 triangles made between the point 55 | // and the corners of the triangle 56 | float area1 = abs( (x1-px)*(y2-py) - (x2-px)*(y1-py) ); 57 | float area2 = abs( (x2-px)*(y3-py) - (x3-px)*(y2-py) ); 58 | float area3 = abs( (x3-px)*(y1-py) - (x1-px)*(y3-py) ); 59 | 60 | // if the sum of the three areas equals the original, 61 | // we're inside the triangle! 62 | if (area1 + area2 + area3 == areaOrig) { 63 | return true; 64 | } 65 | return false; 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /CodeExamples/PointPoint/web-export/PointPoint.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POINT POINT 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px, py; // point controlled by the mouse 9 | float targetX, targetY; // target point coordinates 10 | 11 | 12 | void setup() { 13 | size($("#wrapper").width(), 400); 14 | noCursor(); 15 | 16 | strokeWeight(15); // thicker stroke so points are easier to see 17 | 18 | targetX = width/2; 19 | targetY = height/2; 20 | } 21 | 22 | 23 | void draw() { 24 | 25 | // update point position to mouse coordinates 26 | px = mouseX; 27 | py = mouseY; 28 | 29 | // check for collision! 30 | // if hit, make background orange; if not, make it white 31 | boolean colliding = pointPoint(px, py, targetX, targetY); 32 | if (colliding) { 33 | background(255, 150, 0); 34 | } 35 | else { 36 | background(255); 37 | } 38 | 39 | // draw the two points 40 | stroke(0,150,255); 41 | point(targetX, targetY); 42 | 43 | stroke(0,150); 44 | point(px, py); 45 | } 46 | 47 | 48 | // POINT/POINT 49 | // (note this version includes a "buffer" zone around the points to 50 | // make collision feel more natural instead of being just a 1px zone) 51 | boolean pointPoint(float x1, float y1, float x2, float y2) { 52 | 53 | // treat the points like circles 54 | float buffer = 3; 55 | float distX = x1-x2; 56 | float distY = y1-y2; 57 | float distance = sqrt( (distX*distX) + (distY*distY) ); 58 | if (distance <= buffer*2) { 59 | return true; 60 | } 61 | return false; 62 | 63 | // original code 64 | // are the two points in the same location? 65 | //if (x1 == x2 && y1 == y2) { 66 | // return true; 67 | //} 68 | //return false; 69 | } 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /CodeExamples/LinePoint/LinePoint.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | LINE/POINT 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | Via: http://stackoverflow.com/a/17693146/1167783 7 | 8 | */ 9 | 10 | float px = 0; // point position (set by mouse) 11 | float py = 0; 12 | 13 | float x1 = 100; // line defined by two points 14 | float y1 = 300; 15 | float x2 = 500; 16 | float y2 = 100; 17 | 18 | 19 | void setup() { 20 | size(600,400); 21 | noCursor(); 22 | 23 | strokeWeight(5); // make things a little easier to see 24 | } 25 | 26 | 27 | void draw() { 28 | background(255); 29 | 30 | // set point to mouse coordinates 31 | px = mouseX; 32 | py = mouseY; 33 | 34 | // check for collision 35 | // if hit, change the color of the line 36 | boolean hit = linePoint(x1,y1, x2,y2, px,py); 37 | if (hit) stroke(255,150,0, 150); 38 | else stroke(0,150,255, 150); 39 | line(x1,y1, x2,y2); 40 | 41 | // draw the point 42 | stroke(0, 150); 43 | point(px,py); 44 | } 45 | 46 | 47 | // LINE/POINT 48 | boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) { 49 | 50 | // get distance from the point to the two ends of the line 51 | float d1 = dist(px,py, x1,y1); 52 | float d2 = dist(px,py, x2,y2); 53 | 54 | // get the length of the line 55 | float lineLen = dist(x1,y1, x2,y2); 56 | 57 | // since floats are so minutely accurate, add 58 | // a little buffer zone that will give collision 59 | float buffer = 0.1; // higher # = less accurate 60 | 61 | // if the two distances are equal to the line's length, the 62 | // point is on the line! 63 | // note we use the buffer here to give a range, rather than one # 64 | if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) { 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /CodeExamples/TriPoint/web-export/TriPoint.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POINT/TRIANGLE 4 | Jeff Thompson | 2015 | ww.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px = 0; // point (set by mouse) 9 | float py = 0; 10 | 11 | float x1, y1; // three points of the triangle 12 | float x2, y2; 13 | float x3, y3; 14 | 15 | 16 | void setup() { 17 | size($("#wrapper").width(), 400); 18 | noCursor(); 19 | 20 | strokeWeight(15); // make point easier to see 21 | 22 | x1 = width/2; 23 | y1 = 100; 24 | x2 = width/2+150; 25 | y2 = height-100; 26 | x3 = width/2-150; 27 | y3 = height-100; 28 | } 29 | 30 | 31 | void draw() { 32 | background(255); 33 | 34 | // mouse point to mouse coordinates 35 | px = mouseX; 36 | py = mouseY; 37 | 38 | // check for collision 39 | // if hit, change fill color 40 | boolean hit = triPoint(px,py, x1,y1, x2,y2, x3,y3); 41 | if (hit) fill(255,150,0); 42 | else fill(0,150,255); 43 | noStroke(); 44 | triangle(x1,y1, x2,y2, x3,y3); 45 | 46 | // draw the point 47 | stroke(0, 150); 48 | point(px,py); 49 | } 50 | 51 | 52 | boolean triPoint(float px, float py, float x1, float y1, float x2, float y2, float x3, float y3) { 53 | 54 | // get the area of the triangle 55 | float areaOrig = abs( (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1) ); 56 | 57 | // get the area of 3 triangles made between the point 58 | // and the corners of the triangle 59 | float area1 = abs( (x1-px)*(y2-py) - (x2-px)*(y1-py) ); 60 | float area2 = abs( (x2-px)*(y3-py) - (x3-px)*(y2-py) ); 61 | float area3 = abs( (x3-px)*(y1-py) - (x1-px)*(y3-py) ); 62 | 63 | // if the sum of the three areas equals the original, 64 | // we're inside the triangle! 65 | if (area1 + area2 + area3 == areaOrig) { 66 | return true; 67 | } 68 | return false; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /CodeExamples/LinePoint/web-export/LinePoint.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | LINE/POINT 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | Via: http://stackoverflow.com/a/17693146/1167783 7 | 8 | */ 9 | 10 | float px = 0; // point position (set by mouse) 11 | float py = 0; 12 | 13 | float x1,y1, x2,y2; // line defined by two points 14 | 15 | 16 | void setup() { 17 | size($("#wrapper").width(), 400); 18 | noCursor(); 19 | 20 | strokeWeight(15); // make things a little easier to see 21 | 22 | x1 = 100; 23 | y1 = height-100; 24 | x2 = width-100; 25 | y2 = 100; 26 | } 27 | 28 | 29 | void draw() { 30 | background(255); 31 | 32 | // set point to mouse coordinates 33 | px = mouseX; 34 | py = mouseY; 35 | 36 | // check for collision 37 | // if hit, change the color of the line 38 | boolean hit = linePoint(x1,y1, x2,y2, px,py); 39 | if (hit) stroke(255,150,0, 150); 40 | else stroke(0,150,255, 150); 41 | line(x1,y1, x2,y2); 42 | 43 | // draw the point 44 | stroke(0, 150); 45 | point(px,py); 46 | } 47 | 48 | 49 | // LINE/POINT 50 | boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) { 51 | 52 | // get distance from the point to the two ends of the line 53 | float d1 = dist(px,py, x1,y1); 54 | float d2 = dist(px,py, x2,y2); 55 | 56 | // get the length of the line 57 | float lineLen = dist(x1,y1, x2,y2); 58 | 59 | // since floats are so minutely accurate, add 60 | // a little buffer zone that will give collision 61 | float buffer = 0.1; // higher # = less accurate 62 | 63 | // if the two distances are equal to the line's length, the 64 | // point is on the line! 65 | // note we use the buffer here to give a range, rather than one # 66 | if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) { 67 | return true; 68 | } 69 | return false; 70 | } 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /CodeExamples/CircleRect/CircleRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | CIRCLE/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | Via this example by Matt Worden: 7 | http://vband3d.tripod.com/visualbasic/tut_mixedcollisions.htm 8 | 9 | */ 10 | 11 | float cx = 0; // circle position (set with mouse) 12 | float cy = 0; 13 | float r = 30; // circle radius 14 | 15 | float sx = 200; // square position 16 | float sy = 100; 17 | float sw = 200; // and dimensions 18 | float sh = 200; 19 | 20 | 21 | void setup() { 22 | size(600,400); 23 | noStroke(); 24 | } 25 | 26 | 27 | void draw() { 28 | background(255); 29 | 30 | // update square to mouse coordinates 31 | cx = mouseX; 32 | cy = mouseY; 33 | 34 | // check for collision 35 | // if hit, change rectangle color 36 | boolean hit = circleRect(cx,cy,r, sx,sy,sw,sh); 37 | if (hit) { 38 | fill(255,150,0); 39 | } 40 | else { 41 | fill(0,150,255); 42 | } 43 | rect(sx,sy, sw,sh); 44 | 45 | // draw the circle 46 | fill(0, 150); 47 | ellipse(cx,cy, r*2,r*2); 48 | } 49 | 50 | 51 | // CIRCLE/RECTANGLE 52 | boolean circleRect(float cx, float cy, float radius, float rx, float ry, float rw, float rh) { 53 | 54 | // temporary variables to set edges for testing 55 | float testX = cx; 56 | float testY = cy; 57 | 58 | // which edge is closest? 59 | if (cx < rx) testX = rx; // compare to left edge 60 | else if (cx > rx+rw) testX = rx+rw; // right edge 61 | if (cy < ry) testY = ry; // top edge 62 | else if (cy > ry+rh) testY = ry+rh; // bottom edge 63 | 64 | // get distance from closest edges 65 | float distX = cx-testX; 66 | float distY = cy-testY; 67 | float distance = sqrt( (distX*distX) + (distY*distY) ); 68 | 69 | // if the distance is less than the radius, collision! 70 | if (distance <= radius) { 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | 77 | -------------------------------------------------------------------------------- /CodeExamples/CircleRect/web-export/CircleRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | CIRCLE/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | Via this example by Matt Worden: 7 | http://vband3d.tripod.com/visualbasic/tut_mixedcollisions.htm 8 | 9 | */ 10 | 11 | float cx = 0; // circle position (set with mouse) 12 | float cy = 0; 13 | float r = 30; // circle radius 14 | 15 | float sx, sy; // square position 16 | float sw = 200; // and dimensions 17 | float sh = 200; 18 | 19 | 20 | void setup() { 21 | size($("#wrapper").width(), 400); 22 | noStroke(); 23 | 24 | sx = width/2-sw/2; 25 | sy = height/2-sh/2; 26 | } 27 | 28 | 29 | void draw() { 30 | background(255); 31 | 32 | // update square to mouse coordinates 33 | cx = mouseX; 34 | cy = mouseY; 35 | 36 | // check for collision 37 | // if hit, change rectangle color 38 | boolean hit = circleRect(cx,cy,r, sx,sy,sw,sh); 39 | if (hit) { 40 | fill(255,150,0); 41 | } 42 | else { 43 | fill(0,150,255); 44 | } 45 | rect(sx,sy, sw,sh); 46 | 47 | // draw the circle 48 | fill(0, 150); 49 | ellipse(cx,cy, r*2,r*2); 50 | } 51 | 52 | 53 | // CIRCLE/RECTANGLE 54 | boolean circleRect(float cx, float cy, float radius, float rx, float ry, float rw, float rh) { 55 | 56 | // temporary variables to set edges for testing 57 | float testX = cx; 58 | float testY = cy; 59 | 60 | // which edge is closest? 61 | if (cx < rx) testX = rx; // compare to left edge 62 | else if (cx > rx+rw) testX = rx+rw; // right edge 63 | if (cy < ry) testY = ry; // top edge 64 | else if (cy > ry+rh) testY = ry+rh; // bottom edge 65 | 66 | // get distance from closest edges 67 | float distX = cx-testX; 68 | float distY = cy-testY; 69 | float distance = sqrt( (distX*distX) + (distY*distY) ); 70 | 71 | // if the distance is less than the radius, collision! 72 | if (distance <= radius) { 73 | return true; 74 | } 75 | return false; 76 | } 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /CodeExamples/LineLine/web-export/LineLine.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | LINE/LINE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | Via examples by Paul Bourke: 7 | http://paulbourke.net/geometry/pointlineplane 8 | 9 | And Ibackstrom: 10 | http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2 11 | 12 | */ 13 | 14 | float x1 = 0; // line controlled by mouse 15 | float y1 = 0; 16 | float x2 = 20; // fixed end 17 | float y2 = 20; 18 | 19 | float x3,y3, x4,y4; // static line 20 | 21 | 22 | void setup() { 23 | size($("#wrapper").width(), 400); 24 | 25 | strokeWeight(15); // make lines easier to see 26 | 27 | x3 = 100; 28 | y3 = height-100; 29 | x4 = width-100; 30 | y4 = 100; 31 | } 32 | 33 | 34 | void draw() { 35 | background(255); 36 | 37 | // set line's end to mouse coordinates 38 | x1 = mouseX; 39 | y1 = mouseY; 40 | 41 | // check for collision 42 | // if hit, change color of line 43 | boolean hit = lineLine(x1,y1,x2,y2, x3,y3,x4,y4); 44 | if (hit) stroke(255,150,0, 150); 45 | else stroke(0,150,255, 150); 46 | line(x3,y3, x4,y4); 47 | 48 | // draw user-controlled line 49 | stroke(0, 150); 50 | line(x1,y1, x2,y2); 51 | } 52 | 53 | 54 | // LINE/LINE 55 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { 56 | 57 | // calculate the direction of the lines 58 | float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); 59 | float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); 60 | 61 | // if uA and uB are between 0-1, lines are colliding 62 | if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 63 | 64 | // optionally, draw a circle where the lines meet 65 | float intersectionX = x1 + (uA * (x2-x1)); 66 | float intersectionY = y1 + (uA * (y2-y1)); 67 | fill(255,0,0); 68 | noStroke(); 69 | ellipse(intersectionX,intersectionY, 20,20); 70 | 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | 77 | -------------------------------------------------------------------------------- /CodeExamples/LineLine/LineLine.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | LINE/LINE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | Via examples by Paul Bourke: 7 | http://paulbourke.net/geometry/pointlineplane 8 | 9 | And Ibackstrom: 10 | http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2 11 | 12 | And help from aperson1792 on Reddit: 13 | http://www.reddit.com/r/math/comments/36dt75/what_does_this_equation_solve_for/crd5mcc 14 | 15 | */ 16 | 17 | float x1 = 0; // line controlled by mouse 18 | float y1 = 0; 19 | float x2 = 20; // fixed end 20 | float y2 = 20; 21 | 22 | float x3 = 100; // static line 23 | float y3 = 300; 24 | float x4 = 500; 25 | float y4 = 100; 26 | 27 | 28 | void setup() { 29 | size(600,400); 30 | 31 | strokeWeight(5); // make lines easier to see 32 | } 33 | 34 | 35 | void draw() { 36 | background(255); 37 | 38 | // set line's end to mouse coordinates 39 | x1 = mouseX; 40 | y1 = mouseY; 41 | 42 | // check for collision 43 | // if hit, change color of line 44 | boolean hit = lineLine(x1,y1,x2,y2, x3,y3,x4,y4); 45 | if (hit) stroke(255,150,0, 150); 46 | else stroke(0,150,255, 150); 47 | line(x3,y3, x4,y4); 48 | 49 | // draw user-controlled line 50 | stroke(0, 150); 51 | line(x1,y1, x2,y2); 52 | } 53 | 54 | 55 | // LINE/LINE 56 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { 57 | 58 | // calculate the distance to intersection point 59 | float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); 60 | float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); 61 | 62 | // if uA and uB are between 0-1, lines are colliding 63 | if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 64 | 65 | // optionally, draw a circle where the lines meet 66 | float intersectionX = x1 + (uA * (x2-x1)); 67 | float intersectionY = y1 + (uA * (y2-y1)); 68 | fill(255,0,0); 69 | noStroke(); 70 | ellipse(intersectionX,intersectionY, 20,20); 71 | 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | -------------------------------------------------------------------------------- /Website/thanks.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

THANKS

4 | 5 |

All of this code is thanks to lots of help online and off. As much as possible, I've tried to keep track of specific tutorials, forum threads, and posts that have contributed to specific examples. This would not have been possible without a ton of people sharing their great work.

6 | 7 |

Examples built from:

8 | 9 | 16 | 17 |

Also a big "thank you" to:

18 | 19 | 26 | 27 |

That's it! If you found this book helpful or built something cool with these examples, please let me know!

28 | 29 | 30 | -------------------------------------------------------------------------------- /CodeExamples/PolyPoint/PolyPoint.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/POINT 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px = 0; // point position 9 | float py = 0; 10 | 11 | // array of PVectors, one for each vertex in the polygon 12 | PVector[] vertices = new PVector[4]; 13 | 14 | 15 | void setup() { 16 | size(600,400); 17 | noCursor(); 18 | 19 | strokeWeight(5); // make the point easier to see 20 | 21 | // set position of the vertices 22 | // here we draw a distorted trapezoid, but you could 23 | // make much more complex shapes, or even randomize the points! 24 | vertices[0] = new PVector(200,100); 25 | vertices[1] = new PVector(400,130); 26 | vertices[2] = new PVector(350,300); 27 | vertices[3] = new PVector(250,300); 28 | } 29 | 30 | 31 | void draw() { 32 | background(255); 33 | 34 | // update point to mouse coordinates 35 | px = mouseX; 36 | py = mouseY; 37 | 38 | // check for collision 39 | // if hit, change fill color 40 | boolean hit = polyPoint(vertices, px,py); 41 | if (hit) fill(255,150,0); 42 | else fill(0,150,255); 43 | 44 | // draw the polygon using beginShape() 45 | noStroke(); 46 | beginShape(); 47 | for (PVector v : vertices) { 48 | vertex(v.x, v.y); 49 | } 50 | endShape(); 51 | 52 | // draw the point 53 | stroke(0, 150); 54 | point(px,py); 55 | } 56 | 57 | 58 | // POLYGON/POINT 59 | boolean polyPoint(PVector[] vertices, float px, float py) { 60 | boolean collision = false; 61 | 62 | // go through each of the vertices, plus the next vertex in the list 63 | int next = 0; 64 | for (int current=0; current= py && vn.y < py) || (vc.y < py && vn.y >= py)) && 78 | (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) { 79 | collision = !collision; 80 | } 81 | } 82 | return collision; 83 | } -------------------------------------------------------------------------------- /CodeExamples/LineRect/LineRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | LINE/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float x1 = 0; // points for line (controlled by mouse) 9 | float y1 = 0; 10 | float x2 = 20; // static point 11 | float y2 = 20; 12 | 13 | float sx = 200; // square position 14 | float sy = 100; 15 | float sw = 200; // and size 16 | float sh = 200; 17 | 18 | 19 | void setup() { 20 | size(600, 400); 21 | 22 | strokeWeight(5); // make the line easier to see 23 | } 24 | 25 | 26 | void draw() { 27 | background(255); 28 | 29 | // set end of line to mouse coordinates 30 | x1 = mouseX; 31 | y1 = mouseY; 32 | 33 | // check if line has hit the square 34 | // if so, change the fill color 35 | boolean hit = lineRect(x1,y1,x2,y2, sx,sy,sw,sh); 36 | if (hit) fill(255,150,0); 37 | else fill(0,150,255); 38 | noStroke(); 39 | rect(sx, sy, sw, sh); 40 | 41 | // draw the line 42 | stroke(0, 150); 43 | line(x1, y1, x2, y2); 44 | } 45 | 46 | 47 | // LINE/RECTANGLE 48 | boolean lineRect(float x1, float y1, float x2, float y2, float rx, float ry, float rw, float rh) { 49 | 50 | // check if the line has hit any of the rectangle's sides 51 | // uses the Line/Line function below 52 | boolean left = lineLine(x1,y1,x2,y2, rx,ry,rx, ry+rh); 53 | boolean right = lineLine(x1,y1,x2,y2, rx+rw,ry, rx+rw,ry+rh); 54 | boolean top = lineLine(x1,y1,x2,y2, rx,ry, rx+rw,ry); 55 | boolean bottom = lineLine(x1,y1,x2,y2, rx,ry+rh, rx+rw,ry+rh); 56 | 57 | // if ANY of the above are true, the line has hit the rectangle 58 | if (left || right || top || bottom) { 59 | return true; 60 | } 61 | return false; 62 | } 63 | 64 | 65 | // LINE/LINE 66 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { 67 | 68 | // calculate the direction of the lines 69 | float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); 70 | float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); 71 | 72 | // if uA and uB are between 0-1, lines are colliding 73 | if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 74 | 75 | // optionally, draw a circle where the lines meet 76 | float intersectionX = x1 + (uA * (x2-x1)); 77 | float intersectionY = y1 + (uA * (y2-y1)); 78 | fill(255,0,0); 79 | noStroke(); 80 | ellipse(intersectionX, intersectionY, 20, 20); 81 | 82 | return true; 83 | } 84 | return false; 85 | } 86 | 87 | 88 | -------------------------------------------------------------------------------- /CodeExamples/PolyPoint/web-export/PolyPoint.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/POINT 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float px = 0; // point position 9 | float py = 0; 10 | 11 | // array of PVectors, one for each vertex in the polygon 12 | PVector[] vertices = new PVector[4]; 13 | 14 | 15 | void setup() { 16 | size($("#wrapper").width(), 400); 17 | noCursor(); 18 | 19 | strokeWeight(15); // make the point easier to see 20 | 21 | // set position of the vertices 22 | // here we draw a distorted trapezoid, but you could 23 | // make much more complex shapes, or even randomize the points! 24 | vertices[0] = new PVector(width/2-100, height/2-100); 25 | vertices[1] = new PVector(width/2+100, height/2-100); 26 | vertices[2] = new PVector(width/2+50, height/2+100); 27 | vertices[3] = new PVector(width/2-50, height/2+100); 28 | } 29 | 30 | 31 | void draw() { 32 | background(255); 33 | 34 | // update point to mouse coordinates 35 | px = mouseX; 36 | py = mouseY; 37 | 38 | // check for collision 39 | // if hit, change fill color 40 | boolean hit = polyPoint(vertices, px,py); 41 | if (hit) fill(255,150,0); 42 | else fill(0,150,255); 43 | 44 | // draw the polygon using beginShape() 45 | noStroke(); 46 | beginShape(); 47 | for (PVector v : vertices) { 48 | vertex(v.x, v.y); 49 | } 50 | endShape(); 51 | 52 | // draw the point 53 | stroke(0, 150); 54 | point(px,py); 55 | } 56 | 57 | 58 | // POLYGON/POINT 59 | boolean polyPoint(PVector[] vertices, float px, float py) { 60 | boolean collision = false; 61 | 62 | // go through each of the vertices, plus the next vertex in the list 63 | int next = 0; 64 | for (int current=0; current py) != (vn.y > py)) && 78 | if ( ((vc.y >= py && vn.y < py) || (vc.y < py && vn.y >= py)) && 79 | (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) { 80 | collision = !collision; 81 | } 82 | } 83 | return collision; 84 | } 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /CodeExamples/LineRect/web-export/LineRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | LINE/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float x1 = 0; // points for line (controlled by mouse) 9 | float y1 = 0; 10 | float x2 = 20; // static point 11 | float y2 = 20; 12 | 13 | float sx, sy; // square position 14 | float sw = 200; // and size 15 | float sh = 200; 16 | 17 | 18 | void setup() { 19 | size($("#wrapper").width(), 400); 20 | 21 | strokeWeight(5); // make the line easier to see 22 | 23 | sx = width/2-sw/2; 24 | sy = height/2-sh/2; 25 | } 26 | 27 | 28 | void draw() { 29 | background(255); 30 | 31 | // set end of line to mouse coordinates 32 | x1 = mouseX; 33 | y1 = mouseY; 34 | 35 | // check if line has hit the square 36 | // if so, change the fill color 37 | boolean hit = lineRect(x1,y1,x2,y2, sx,sy,sw,sh); 38 | if (hit) fill(255,150,0); 39 | else fill(0,150,255); 40 | noStroke(); 41 | rect(sx, sy, sw, sh); 42 | 43 | // draw the line 44 | stroke(0, 150); 45 | line(x1, y1, x2, y2); 46 | } 47 | 48 | 49 | // LINE/RECTANGLE 50 | boolean lineRect(float x1, float y1, float x2, float y2, float rx, float ry, float rw, float rh) { 51 | 52 | // check if the line has hit any of the rectangle's sides 53 | // uses the Line/Line function below 54 | boolean left = lineLine(x1,y1,x2,y2, rx,ry,rx, ry+rh); 55 | boolean right = lineLine(x1,y1,x2,y2, rx+rw,ry, rx+rw,ry+rh); 56 | boolean top = lineLine(x1,y1,x2,y2, rx,ry, rx+rw,ry); 57 | boolean bottom = lineLine(x1,y1,x2,y2, rx,ry+rh, rx+rw,ry+rh); 58 | 59 | // if ANY of the above are true, the line has hit the rectangle 60 | if (left || right || top || bottom) { 61 | return true; 62 | } 63 | return false; 64 | } 65 | 66 | 67 | // LINE/LINE 68 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { 69 | 70 | // calculate the direction of the lines 71 | float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); 72 | float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); 73 | 74 | // if uA and uB are between 0-1, lines are colliding 75 | if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 76 | 77 | // optionally, draw a circle where the lines meet 78 | float intersectionX = x1 + (uA * (x2-x1)); 79 | float intersectionY = y1 + (uA * (y2-y1)); 80 | fill(255,0,0); 81 | noStroke(); 82 | ellipse(intersectionX, intersectionY, 20, 20); 83 | 84 | return true; 85 | } 86 | return false; 87 | } 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /CodeExamples/Introduction/Introduction.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | COLLISION DETECTION: INTRODUCTION 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | int numEach = 50; 9 | 10 | // circle, controlled by the mouse 11 | float cx = 0; 12 | float cy = 0; 13 | float cr = 30; 14 | 15 | // lots of other objects! 16 | ArrayList circles = new ArrayList(); 17 | ArrayList rectangles = new ArrayList(); 18 | ArrayList lines = new ArrayList(); 19 | //ArrayList polygons = new ArrayList(); 20 | 21 | 22 | void setup() { 23 | size(600,400); 24 | 25 | // make some cirlces 26 | for (int i=0; i=0; i-=1) { 66 | Circle c = circles.get(i); 67 | c.update(); 68 | c.display(); 69 | if (c.y > height+50) { 70 | circles.remove(c); 71 | circles.add(new Circle(random(width), random(-height,-50))); 72 | } 73 | } 74 | 75 | for (int i=rectangles.size()-1; i>=0; i-=1) { 76 | Rectangle r = rectangles.get(i); 77 | r.update(); 78 | r.display(); 79 | if (r.y > height+50) { 80 | rectangles.remove(r); 81 | rectangles.add(new Rectangle(random(width), random(-height,-50))); 82 | } 83 | } 84 | 85 | for (int i=lines.size()-1; i>=0; i-=1) { 86 | Line l = lines.get(i); 87 | l.update(); 88 | l.display(); 89 | if (l.y1 > height+50 && l.y2 > height+50) { 90 | lines.remove(l); 91 | float x = random(width); 92 | float y = random(-height,-50); 93 | lines.add(new Line(x,y, x+random(-20,20), y+random(-20,20))); 94 | } 95 | } 96 | } 97 | 98 | 99 | -------------------------------------------------------------------------------- /Website/table_of_contents.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Point, rectangle, circle, line, triangle, polygon

4 | 5 |

TABLE OF CONTENTS

6 |
You can get back to this page any time by clicking on the Collision Detection text at the top!
7 | 8 |
9 |

PREAMBLE

10 | 15 | 16 |

SECTION 1

17 | 23 | 24 |

SECTION 2

25 | 31 | 32 |

SECTION 3

33 | 40 | 41 |

SECTION 4

42 | 50 | 51 |

SECTION 5

52 | 57 | 58 |

SECTION 6

59 | 62 | 63 |

THANKS

64 | 67 | 68 |
69 | 70 | 71 | -------------------------------------------------------------------------------- /CodeExamples/PolyLine/PolyLine.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/LINE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float x1 = 0; // line position (set by mouse) 9 | float y1 = 0; 10 | float x2 = 20; // fixed end 11 | float y2 = 20; 12 | 13 | // array of PVectors, one for each vertex in the polygon 14 | PVector[] vertices = new PVector[16]; 15 | 16 | 17 | void setup() { 18 | size(600, 400); 19 | noCursor(); 20 | 21 | strokeWeight(5); // make the line easier to see 22 | 23 | // set position of the vertices - a regular polygon! 24 | // based on this example: 25 | // https://processing.org/examples/regularpolygon.html 26 | float angle = TWO_PI / vertices.length; 27 | for (int i=0; i= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /CodeExamples/PolyLine/web-export/PolyLine.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/LINE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float x1 = 0; // line position (set by mouse) 9 | float y1 = 0; 10 | float x2 = 20; // fixed end 11 | float y2 = 20; 12 | 13 | // array of PVectors, one for each vertex in the polygon 14 | PVector[] vertices = new PVector[16]; 15 | 16 | 17 | void setup() { 18 | size($("#wrapper").width(), 400); 19 | noCursor(); 20 | 21 | strokeWeight(15); // make the line easier to see 22 | 23 | // set position of the vertices - a regular polygon! 24 | // based on this example: 25 | // https://processing.org/examples/regularpolygon.html 26 | float angle = TWO_PI / vertices.length; 27 | for (int i=0; i= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Website/point-point.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Use your mouse to collide the two points!
4 | 5 |

POINT/POINT

6 | 7 |

The easiest collision to test is between two points. To test if they are touching, we simply check to see if their X and Y coordinates are the same!

8 | 9 |
if (x1 == x2 && y1 == y2) {
10 |     // points are in the same place: collision!
11 | }
12 | else {
13 |     // not colliding
14 | }
15 | 
16 | 17 |

We can then wrap this code up in a function to make the it more usable. As arguments, we pass the X/Y coordinates for both points. The function returns a boolean value of true or false, depending on whether there is a collision or not.

18 | 19 |
boolean pointPoint(float x1, float y1, float x2, float y2) {
20 |     if (x1 == x2 && y1 == y2) {
21 |         return true;
22 |     }
23 |     return false;
24 | }
25 | 
26 | 27 |

Note the bit of shorthand above: we could specify else { return false; }, but our code does the same thing! Our version is a little easier to read, once you get used to it. Think of the return false; as the default value to be sent back, unless certain conditions are met.

28 | 29 |

With our very first collision detection function in hand, we can build something useful with it. Try the interactive example bumping into the other point and watch the background change color!

30 | 31 |

You'll notice it's a bit hard to get a collision to trigger (can you guess why?) — a problem we'll try to fix in the first challenges section.

32 | 33 |

The code for the example above can be seen here. Try pasting it into Processing, running it, and making changes to see how the behavior changes.

34 | 35 |
float px, py;           // point controlled by the mouse
36 | float targetX = 300;    // target point coordinates
37 | float targetY = 200;
38 | 
39 | 
40 | void setup() {
41 |   size(600, 400);
42 |   noCursor();
43 | 
44 |   strokeWeight(5);    // thicker stroke = easier to see
45 | }
46 | 
47 | 
48 | void draw() {
49 | 
50 |   // update point position to mouse coordinates
51 |   px = mouseX;
52 |   py = mouseY;
53 | 
54 |   // check for collision!
55 |   // if hit, make background orange; if not, make it white
56 |   boolean colliding = pointPoint(px, py, targetX, targetY);
57 |   if (colliding) {
58 |     background(255, 150, 0);
59 |   }
60 |   else {
61 |     background(255);
62 |   }
63 | 
64 |   // draw the two points
65 |   stroke(0,150,255);
66 |   point(targetX, targetY);
67 | 
68 |   stroke(0,150);
69 |   point(px, py);
70 | }
71 | 
72 | 
73 | // POINT/POINT
74 | boolean pointPoint(float x1, float y1, float x2, float y2) {
75 | 
76 |   // are the two points in the same location?
77 |   if (x1 == x2 && y1 == y2) {
78 |     return true;
79 |   }
80 |   return false;
81 | }
82 | 
83 | 84 |

Congrats, you've written your first program using collision! This basic structure (an interactive example, an explanation, some code, and the code from the example) will be the same in all the chapters.

85 | 86 | 87 | -------------------------------------------------------------------------------- /Website/circle-circle.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

CIRCLE/CIRCLE

4 | 5 |

Collision with points is fine, but rarely do objects actually occupy a single point in space. Next, we can use the same application of the Pythagorean Theorem from the Point/Circle example to test if two circles are colliding.

6 | 7 |

First, calculate the distance between the two circle's centers:

8 | 9 |
float distX = c1x - c2x;
10 | float distY = c1y - c2y;
11 | float distance = sqrt( (distX*distX) + (distY*distY) );
12 | 
13 | 14 |

To check if they are colliding, we see if the distance between them is less than the sum of their radii.

15 | 16 |
if (distance <= c1r+c2r) {
17 |     return true;
18 | }
19 | return false;
20 | 
21 | 22 |

Built into a full example, it looks like this:

23 | 24 |
float c1x = 0;      // circle 1 position
25 | float c1y = 0;      // (controlled by mouse)
26 | float c1r = 30;     // radius
27 | 
28 | float c2x = 300;    // circle 2 position
29 | float c2y = 200;
30 | float c2r = 100;
31 | 
32 | 
33 | void setup() {
34 |   size(600,400);
35 |   noStroke();
36 | }
37 | 
38 | 
39 | void draw() {
40 |   background(255);
41 | 
42 |   // update position to mouse coordinates
43 |   c1x = mouseX;
44 |   c1y = mouseY;
45 | 
46 |   // check for collision
47 |   // if hit, change color
48 |   boolean hit = circleCircle(c1x,c1y,c1r, c2x,c2y,c2r);
49 |   if (hit) {
50 |     fill(255,150,0);
51 |   }
52 |   else {
53 |     fill(0,150,255);
54 |   }
55 |   ellipse(c2x,c2y, c2r*2,c2r*2);
56 | 
57 |   // other circle, controlled by mouse
58 |   fill(0, 150);
59 |   ellipse(c1x,c1y, c1r*2,c1r*2);
60 | }
61 | 
62 | 
63 | // CIRCLE/CIRCLE
64 | boolean circleCircle(float c1x, float c1y, float c1r, float c2x, float c2y, float c2r) {
65 | 
66 |   // get distance between the circle's centers
67 |   // use the Pythagorean Theorem to compute the distance
68 |   float distX = c1x - c2x;
69 |   float distY = c1y - c2y;
70 |   float distance = sqrt( (distX*distX) + (distY*distY) );
71 | 
72 |   // if the distance is less than the sum of the circle's
73 |   // radii, the circles are touching!
74 |   if (distance <= c1r+c2r) {
75 |     return true;
76 |   }
77 |   return false;
78 | }
79 | 
80 | 81 |

Circle/Circle collision can be used to create "bounding circles" around more complex objects. While sacrificing accuracy, this kind of collision detection is very fast and can be a good approximation.

82 | 83 |

An example of a bounding circle

84 | 85 |
While it includes some areas that aren't part of the shape, a circle is a good approximation of this dodecagon.
86 | 87 |

You may be wondering why we are only talking about circles and not ellipses. It might seem fairly similar, but the math for ellipse collision is actually quite complicated. Consider it a great challenge once you master the other collision examples!

88 | 89 | 90 | -------------------------------------------------------------------------------- /CodeExamples/ObjectOrientedCollision/web-export/ObjectOrientedCollision.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | OBJECT-ORIENTED COLLISION 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | // a single Circle object, controlled by the mouse 9 | Circle circle; 10 | 11 | // a list of rectangles 12 | Rectangle[] rects = new Rectangle[8]; 13 | 14 | 15 | void setup() { 16 | size($("#wrapper").width(), 400); 17 | 18 | // create a new Circle with 30px radius 19 | circle = new Circle(0,0, 30); 20 | 21 | // generate rectangles in random locations 22 | for (int i=0; i rx+rw) testX = rx+rw; // right edge 82 | if (cy < ry) testY = ry; // top edge 83 | else if (cy > ry+rh) testY = ry+rh; // bottom edge 84 | 85 | // get distance from closest edges 86 | float distX = cx-testX; 87 | float distY = cy-testY; 88 | float distance = sqrt( (distX*distX) + (distY*distY) ); 89 | 90 | // if the distance is less than the radius, collision! 91 | if (distance <= radius) { 92 | return true; 93 | } 94 | return false; 95 | } 96 | 97 | 98 | 99 | class Rectangle { 100 | float x, y; // position 101 | float w, h; // size 102 | boolean hit = false; // is it hit? 103 | 104 | Rectangle (float _x, float _y, float _w, float _h) { 105 | x = _x; 106 | y = _y; 107 | w = _w; 108 | h = _h; 109 | } 110 | 111 | // check for collision with the circle using the 112 | // Circle/Rect function we made in the beginning 113 | void checkCollision(Circle c) { 114 | hit = circleRect(c.x,c.y,c.r, x,y,w,h); 115 | } 116 | 117 | // draw the rectangle 118 | // if hit, change the fill color 119 | void display() { 120 | if (hit) fill(255,150,0); 121 | else fill(0,150,255); 122 | noStroke(); 123 | rect(x,y, w,h); 124 | } 125 | } 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /Website/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

COLLISION DETECTION

4 | 5 |

Jeff Thompson

6 | 7 |

The collision of objects underlies most game experiences and user-interfaces. Baseball bats collide with balls, zombies bump into walls, and Mario lands on platforms and stomps turtles. Even something as simple as clicking a button (a rectangle) with your mouse (a point) is a collision.

8 | 9 |

This book explains the algorithms behind those collisions using basic shapes like circles, rectangles, and lines so you can implement them into your own projects.

10 | 11 |

UPDATE! This site has been getting lots of visits which is awesome. I've made some changes so it will look better on mobile devices, make navigation easier, and tried to look over everything for mistakes. If you have any problems or suggestions, please post an issue on the project repository. Thanks!

12 | 13 |

Ready to get started? Skip ahead to the Table of Contents...

14 | 15 | 16 |

WHAT'S COVERED HERE?

17 |

This book covers collisions between points, circles, rectangles, lines, polygons, and triangles. These examples are meant to be as readable and easily understood as possible. There are definitely faster, more efficient ways to detect these collisions, but this book is intended to be friendly and teach the principles with minimal math.

18 | 19 |

Each section include a description of the collision algorithm and an interactive example built using processing.js. You can view the source code for all the examples (and this book!) on GitHub.

20 | 21 |

NOTE! If you're on a mobile device, the examples might not work super well for you. They are designed for mouse input, so if you're getting frustrated or your finger is in the way, try the site on a computer.

22 | 23 |

WHAT'S NOT?

24 |

As with any book, there's a lot more useful material than could be covered here. Things that aren't discussed are mostly left out because the math gets too complicated. Three-dimensional space isn't touched on. Ellipses, which seem like they should be pretty easy, are actually very difficult.

25 | 26 |

If there's a specific collision not covered that would be helpful, please please submit an issue with a request or, better yet, submit a working example that you've built!

27 | 28 |

ISSUES?

29 |

If you find code that doesn't run correctly, an algorithm that isn't explained quite right, or a typo, please report them at this project's GitHub repository. Thanks for your help!

30 | 31 |

NAVIGATION

32 |

OK, let's write some code! Click the link at the bottom the page, or the arrows at the top, to move to the next chapter. The Collision Detection link at the top will take you back to the Table of Contents.

33 | 34 | 35 | -------------------------------------------------------------------------------- /Website/tri-point.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

TRIANGLE/POINT

4 | 5 |

To test if a point is inside a triangle, we compare the area of the original triangle with the sum of the area of three triangles made between the point and the corners of the triangle.

6 | 7 |

Here's a diagram demonstrating the triangles created for a point outside and inside the triangle:

8 | 9 |

Points outside and inside a triangle, forming three smaller triangles

10 | 11 |

To get the area, we use Heron's Forumula:

12 | 13 |
float areaOrig = abs( (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1) );
14 | 
15 | 16 |

We need to calculate the area of the three triangles made from the point as well:

17 | 18 |
float area1 =    abs( (x1-px)*(y2-py) - (x2-px)*(y1-py) );
19 | float area2 =    abs( (x2-px)*(y3-py) - (x3-px)*(y2-py) );
20 | float area3 =    abs( (x3-px)*(y1-py) - (x1-px)*(y3-py) );
21 | 
22 | 23 |

If we add the three areas together and they equal the original, we know we're inside the triangle! Using this, we can test for collision:

24 | 25 |
if (area1 + area2 + area3 == areaOrig) {
26 |   return true;
27 | }
28 | return false;
29 | 
30 | 31 |

Here's a full example:

32 | 33 |
float px = 0;        // point (set by mouse)
34 | float py = 0;
35 | 
36 | float x1 = 300;      // three points of the triangle
37 | float y1 = 100;
38 | float x2 = 450;
39 | float y2 = 300;
40 | float x3 = 150;
41 | float y3 = 300;
42 | 
43 | 
44 | void setup() {
45 |   size(600,400);
46 |   noCursor();
47 | 
48 |   strokeWeight(5);   // make point easier to see
49 | }
50 | 
51 | 
52 | void draw() {
53 |   background(255);
54 | 
55 |   // mouse point to mouse coordinates
56 |   px = mouseX;
57 |   py = mouseY;
58 | 
59 |   // check for collision
60 |   // if hit, change fill color
61 |   boolean hit = triPoint(x1,y1, x2,y2, x3,y3, px,py);
62 |   if (hit) fill(255,150,0);
63 |   else fill(0,150,255);
64 |   noStroke();
65 |   triangle(x1,y1, x2,y2, x3,y3);
66 | 
67 |   // draw the point
68 |   stroke(0, 150);
69 |   point(px,py);
70 | }
71 | 
72 | 
73 | // TRIANGLE/POINT
74 | boolean triPoint(float x1, float y1, float x2, float y2, float x3, float y3, float px, float py) {
75 | 
76 |   // get the area of the triangle
77 |   float areaOrig = abs( (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1) );
78 | 
79 |   // get the area of 3 triangles made between the point
80 |   // and the corners of the triangle
81 |   float area1 =    abs( (x1-px)*(y2-py) - (x2-px)*(y1-py) );
82 |   float area2 =    abs( (x2-px)*(y3-py) - (x3-px)*(y2-py) );
83 |   float area3 =    abs( (x3-px)*(y1-py) - (x1-px)*(y3-py) );
84 | 
85 |   // if the sum of the three areas equals the original,
86 |   // we're inside the triangle!
87 |   if (area1 + area2 + area3 == areaOrig) {
88 |     return true;
89 |   }
90 |   return false;
91 | }
92 | 
93 | 94 |

This example was built on a modified version of a post on YoYo Games. If you would like to read a lengthy discussion on the merits and problems with this method, and many other suggestions, see this thread on GameDev.net.

95 | 96 | 97 | -------------------------------------------------------------------------------- /Website/line-line.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

LINE/LINE

4 | 5 |

With this example, you'll be able to build a super-sweet sword fighting game! (Or reboot one that never got finished?)

6 | 7 |

To check if two lines are touching, we have to calculate the distance to the point of intersection:

8 | 9 |
float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
10 | 
11 | float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
12 | 
13 | 14 |

If there is a collision, uA and uB should both be in the range of 0-1. We test for that like this:

15 | 16 |
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
17 |     return true;
18 | }
19 | return false;
20 | 
21 | 22 |

That's it! We can add one more feature, if desired, that will tell us the intersection point of the two lines. This might be useful if, for example, you're making a sword-fighting game and want sparks to fly where the two blades hit.

23 | 24 |
float intersectionX = x1 + (uA * (x2-x1));
25 | float intersectionY = y1 + (uA * (y2-y1));
26 | 
27 | 28 |

Here's the full example:

29 | 30 |
float x1 = 0;    // line controlled by mouse
31 | float y1 = 0;
32 | float x2 = 10;   // fixed end
33 | float y2 = 10;
34 | 
35 | float x3 = 100;  // static line
36 | float y3 = 300;
37 | float x4 = 500;
38 | float y4 = 100;
39 | 
40 | 
41 | void setup() {
42 |   size(600,400);
43 | 
44 |   strokeWeight(5);  // make lines easier to see
45 | }
46 | 
47 | 
48 | void draw() {
49 |   background(255);
50 | 
51 |   // set line's end to mouse coordinates
52 |   x1 = mouseX;
53 |   y1 = mouseY;
54 | 
55 |   // check for collision
56 |   // if hit, change color of line
57 |   boolean hit = lineLine(x1,y1,x2,y2, x3,y3,x4,y4);
58 |   if (hit) stroke(255,150,0, 150);
59 |   else stroke(0,150,255, 150);
60 |   line(x3,y3, x4,y4);
61 | 
62 |   // draw user-controlled line
63 |   stroke(0, 150);
64 |   line(x1,y1, x2,y2);
65 | }
66 | 
67 | 
68 | // LINE/LINE
69 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
70 | 
71 |   // calculate the distance to intersection point
72 |   float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
73 |   float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
74 | 
75 |   // if uA and uB are between 0-1, lines are colliding
76 |   if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
77 | 
78 |     // optionally, draw a circle where the lines meet
79 |     float intersectionX = x1 + (uA * (x2-x1));
80 |     float intersectionY = y1 + (uA * (y2-y1));
81 |     fill(255,0,0);
82 |     noStroke();
83 |     ellipse(intersectionX,intersectionY, 20,20);
84 | 
85 |     return true;
86 |   }
87 |   return false;
88 | }
89 | 
90 | 91 |

Based on a tutorial by Paul Bourke, who includes code to test if the lines are parallel and coincident. Also based on this post by Ibackstrom and help from Reddit.

92 | 93 | 94 | -------------------------------------------------------------------------------- /Website/point-rect.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

POINT/RECTANGLE

4 | 5 |

Checking for collision with circular objects is fairly easy, since it is the same distance from the center to their edge in every direction. Rectangles require a bit more complex algorithm.

6 | 7 |

Let's say we have a square:

8 | 9 |
float rx = 10;  // x position
 10 | float ry = 10;  // y position
 11 | float rw = 30;  // width
 12 | float rh = 30;  // height
 13 | 
14 | 15 |

To see if a point is inside the square, we have to test:

16 | 17 |
Is the X position of the point to the RIGHT of the LEFT EDGE?
 18 | Is the X position of the point to the LEFT of the RIGHT EDGE?
 19 | Is the Y position of the point BELOW the TOP EDGE?
 20 | Is the Y position of the point ABOVE the BOTTOM EDGE?
 21 | 
22 | 23 |

If all of these are true, then the point is inside. Let's look at testing the left edge first. Since the default mode for the rect() command draws from the upper-left corner, the left edge is at rx:

24 | 25 |
if (px >= rx) {
 26 |     // to the right of the left edge
 27 | }
 28 | 
29 | 30 |

Pretty easy, but maybe not so intuitive. Here's a diagram showing the left edge of the rectangle:

31 | 32 |

Left edge of a rectangle

33 | 34 |

If we want to check the right edge, we need to get its X position, which is the left edge plus the width:

35 | 36 |
float rightEdge = rx + rw;
 37 | if (px <= rightEdge) {
 38 |     // to the left of the right edge
 39 | }
 40 | 
41 | 42 |

Here's the full if statement:

43 | 44 |
if (px >= rx &&         // right of the left edge AND
 45 |     px <= rx + rw &&    // left of the right edge AND
 46 |     py >= ry &&         // below the top AND
 47 |     py <= ry + rh) {    // above the bottom
 48 |         return true;
 49 | }
 50 | return false;
 51 | 
52 | 53 |

If all the statements are true, then the point is inside the square. Note we can break our if statement into multiple lines, which makes it a little easier to read. This is personal preference, but we'll keep doing that here for the sake of clarity.

54 | 55 |

Here's a full example:

56 | 57 |
float px = 0;      // point position (move with mouse)
 58 | float py = 0;
 59 | 
 60 | float sx = 200;    // square position
 61 | float sy = 100;
 62 | float sw = 200;    // and dimensions
 63 | float sh = 200;
 64 | 
 65 | 
 66 | void setup() {
 67 |   size(600,400);
 68 |   noCursor();
 69 | 
 70 |   strokeWeight(5);    // thicker stroke = easier to see
 71 | }
 72 | 
 73 | 
 74 | void draw() {
 75 |   background(255);
 76 | 
 77 |   // update point to mouse coordinates
 78 |   px = mouseX;
 79 |   py = mouseY;
 80 | 
 81 |   // check for collision
 82 |   // if hit, change rectangle color
 83 |   boolean hit = pointRect(px,py, sx,sy,sw,sh);
 84 |   if (hit) {
 85 |     fill(255,150,0);
 86 |   }
 87 |   else {
 88 |     fill(0,150,255);
 89 |   }
 90 |   noStroke();
 91 |   rect(sx,sy, sw,sh);
 92 | 
 93 |   // draw the point
 94 |   stroke(0);
 95 |   point(px,py);
 96 | }
 97 | 
 98 | 
 99 | // POINT/RECTANGLE
100 | boolean pointRect(float px, float py, float rx, float ry, float rw, float rh) {
101 | 
102 |   // is the point inside the rectangle's bounds?
103 |   if (px >= rx &&        // right of the left edge AND
104 |       px <= rx + rw &&   // left of the right edge AND
105 |       py >= ry &&        // below the top AND
106 |       py <= ry + rh) {   // above the bottom
107 |         return true;
108 |   }
109 |   return false;
110 | }
111 | 
112 | 113 | 114 | -------------------------------------------------------------------------------- /CodeExamples/LineCircle/LineCircle.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | LINE/CIRCLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | Via this example by Philip Nicoletti: 7 | http://www.codeguru.com/forum/showthread.php?threadid=194400 8 | 9 | */ 10 | 11 | float cx = 0; // circle position (set by mouse) 12 | float cy = 0; 13 | float r = 30; // circle radius 14 | 15 | float x1, y1; // coordinates of line 16 | float x2, y2; 17 | 18 | 19 | void setup() { 20 | size($("#wrapper").width(), 400); 21 | 22 | strokeWeight(5); // make it a little easier to see the line 23 | 24 | x1 = 100; 25 | y1 = height-100; 26 | x2 = width-100; 27 | y2 = 100; 28 | } 29 | 30 | 31 | void draw() { 32 | background(255); 33 | 34 | // update circle to mouse position 35 | cx = mouseX; 36 | cy = mouseY; 37 | 38 | // check for collision 39 | // if hit, change line's stroke color 40 | boolean hit = lineCircle(x1,y1, x2,y2, cx,cy,r); 41 | if (hit) stroke(255,150,0, 150); 42 | else stroke(0,150,255, 150); 43 | line(x1,y1, x2,y2); 44 | 45 | // draw the circle 46 | fill(0, 150); 47 | noStroke(); 48 | ellipse(cx,cy, r*2,r*2); 49 | } 50 | 51 | 52 | // LINE/CIRCLE 53 | boolean lineCircle(float x1, float y1, float x2, float y2, float cx, float cy, float r) { 54 | 55 | // is either end INSIDE the circle? 56 | // if so, return true immediately 57 | boolean inside1 = pointCircle(x1,y1, cx,cy,r); 58 | boolean inside2 = pointCircle(x2,y2, cx,cy,r); 59 | if (inside1 || inside2) return true; 60 | 61 | // get length of the line 62 | float distX = x1 - x2; 63 | float distY = y1 - y2; 64 | float len = sqrt( (distX*distX) + (distY*distY) ); 65 | 66 | // get dot product of the line and circle 67 | float dot = ( ((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) ) / pow(len,2); 68 | 69 | // find the closest point on the line 70 | float closestX = x1 + (dot * (x2-x1)); 71 | float closestY = y1 + (dot * (y2-y1)); 72 | 73 | // is this point actually on the line segment? 74 | // if so keep going, but if not, return false 75 | boolean onSegment = linePoint(x1,y1,x2,y2, closestX,closestY); 76 | if (!onSegment) return false; 77 | 78 | // optionally, draw a circle at the closest point on the line 79 | fill(255,0,0); 80 | noStroke(); 81 | ellipse(closestX, closestY, 20, 20); 82 | 83 | // get distance to closest point 84 | distX = closestX - cx; 85 | distY = closestY - cy; 86 | float distance = sqrt( (distX*distX) + (distY*distY) ); 87 | 88 | if (distance <= r) { 89 | return true; 90 | } 91 | return false; 92 | } 93 | 94 | 95 | // LINE/POINT 96 | boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) { 97 | 98 | // get distance from the point to the two ends of the line 99 | float d1 = dist(px,py, x1,y1); 100 | float d2 = dist(px,py, x2,y2); 101 | 102 | // get the length of the line 103 | float lineLen = dist(x1,y1, x2,y2); 104 | 105 | // since floats are so minutely accurate, add 106 | // a little buffer zone that will give collision 107 | float buffer = 0.1; // higher # = less accurate 108 | 109 | // if the two distances are equal to the line's length, the 110 | // point is on the line! 111 | // note we use the buffer here to give a range, rather than one # 112 | if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) { 113 | return true; 114 | } 115 | return false; 116 | } 117 | 118 | 119 | // POINT/CIRCLE 120 | boolean pointCircle(float px, float py, float cx, float cy, float r) { 121 | 122 | // get distance between the point and circle's center 123 | // using the Pythagorean Theorem 124 | float distX = px - cx; 125 | float distY = py - cy; 126 | float distance = sqrt( (distX*distX) + (distY*distY) ); 127 | 128 | // if the distance is less than the circle's 129 | // radius the point is inside! 130 | if (distance <= r) { 131 | return true; 132 | } 133 | return false; 134 | } 135 | 136 | 137 | -------------------------------------------------------------------------------- /CodeExamples/LineCircle/web-export/LineCircle.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | LINE/CIRCLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | Via this example by Philip Nicoletti: 7 | http://www.codeguru.com/forum/showthread.php?threadid=194400 8 | 9 | */ 10 | 11 | float cx = 0; // circle position (set by mouse) 12 | float cy = 0; 13 | float r = 30; // circle radius 14 | 15 | float x1,y1, x2,y2; // coordinates of line 16 | 17 | 18 | void setup() { 19 | size($("#wrapper").width(), 400); 20 | 21 | strokeWeight(15); // make it a little easier to see the line 22 | 23 | x1 = 100; 24 | y1 = height-100; 25 | x2 = width-100; 26 | y2 = 100; 27 | } 28 | 29 | 30 | void draw() { 31 | background(255); 32 | 33 | // update circle to mouse position 34 | cx = mouseX; 35 | cy = mouseY; 36 | 37 | // check for collision 38 | // if hit, change line's stroke color 39 | boolean hit = lineCircle(x1,y1, x2,y2, cx,cy,r); 40 | if (hit) stroke(255,150,0, 150); 41 | else stroke(0,150,255, 150); 42 | line(x1,y1, x2,y2); 43 | 44 | // draw the circle 45 | fill(0, 150); 46 | noStroke(); 47 | ellipse(cx,cy, r*2,r*2); 48 | } 49 | 50 | 51 | // LINE/CIRCLE 52 | boolean lineCircle(float x1, float y1, float x2, float y2, float cx, float cy, float r) { 53 | 54 | // is either end INSIDE the circle? 55 | // if so, return true immediately 56 | boolean inside1 = pointCircle(x1,y1, cx,cy,r); 57 | boolean inside2 = pointCircle(x2,y2, cx,cy,r); 58 | if (inside1 || inside2) return true; 59 | 60 | // get length of the line 61 | float distX = x1 - x2; 62 | float distY = y1 - y2; 63 | float len = sqrt( (distX*distX) + (distY*distY) ); 64 | 65 | // get dot product of the line and circle 66 | float dot = ( ((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) ) / pow(len,2); 67 | 68 | // find the closest point on the line 69 | float closestX = x1 + (dot * (x2-x1)); 70 | float closestY = y1 + (dot * (y2-y1)); 71 | 72 | // is this point actually on the line segment? 73 | // if so keep going, but if not, return false 74 | boolean onSegment = linePoint(x1,y1,x2,y2, closestX,closestY); 75 | if (!onSegment) return false; 76 | 77 | // optionally, draw a circle at the closest point on the line 78 | fill(255,0,0); 79 | noStroke(); 80 | ellipse(closestX, closestY, 20, 20); 81 | 82 | // get distance to closest point 83 | distX = closestX - cx; 84 | distY = closestY - cy; 85 | float distance = sqrt( (distX*distX) + (distY*distY) ); 86 | 87 | if (distance <= r) { 88 | return true; 89 | } 90 | return false; 91 | } 92 | 93 | 94 | // LINE/POINT 95 | boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) { 96 | 97 | // get distance from the point to the two ends of the line 98 | float d1 = dist(px,py, x1,y1); 99 | float d2 = dist(px,py, x2,y2); 100 | 101 | // get the length of the line 102 | float lineLen = dist(x1,y1, x2,y2); 103 | 104 | // since floats are so minutely accurate, add 105 | // a little buffer zone that will give collision 106 | float buffer = 0.1; // higher # = less accurate 107 | 108 | // if the two distances are equal to the line's length, the 109 | // point is on the line! 110 | // note we use the buffer here to give a range, rather than one # 111 | if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) { 112 | return true; 113 | } 114 | return false; 115 | } 116 | 117 | 118 | // POINT/CIRCLE 119 | boolean pointCircle(float px, float py, float cx, float cy, float r) { 120 | 121 | // get distance between the point and circle's center 122 | // using the Pythagorean Theorem 123 | float distX = px - cx; 124 | float distY = py - cy; 125 | float distance = sqrt( (distX*distX) + (distY*distY) ); 126 | 127 | // if the distance is less than the circle's 128 | // radius the point is inside! 129 | if (distance <= r) { 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /CodeExamples/Introduction/CollisionFunctions.pde: -------------------------------------------------------------------------------- 1 | 2 | // CIRCLE/CIRCLE 3 | boolean circleCircle(float c1x, float c1y, float c1r, float c2x, float c2y, float c2r) { 4 | 5 | // get distance between the circle's centers 6 | // use the Pythagorean Theorem to compute the distance 7 | float distX = c1x - c2x; 8 | float distY = c1y - c2y; 9 | float distance = sqrt( (distX*distX) + (distY*distY) ); 10 | 11 | // if the distance is less than the sum of the circle's 12 | // radii, the circles are touching! 13 | if (distance <= c1r+c2r) { 14 | return true; 15 | } 16 | return false; 17 | } 18 | 19 | 20 | // LINE/CIRCLE 21 | boolean lineCircle(float x1, float y1, float x2, float y2, float cx, float cy, float r) { 22 | 23 | // either end inside the circle? 24 | boolean inside = pointCircle(x1,y1, cx,cy,r); 25 | if (inside) return true; 26 | inside = pointCircle(x2,y2, cx,cy,r); 27 | if (inside) return true; 28 | 29 | // get length of the line 30 | float distX = x1 - x2; 31 | float distY = y1 - y2; 32 | float len = sqrt( (distX*distX) + (distY*distY) ); 33 | 34 | // get dot product of the line and circle 35 | float dot = ( ((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) ) / pow(len,2); 36 | 37 | // find the closest point on the line 38 | float closestX = x1 + (dot * (x2-x1)); 39 | float closestY = y1 + (dot * (y2-y1)); 40 | 41 | // is this point actually on the line segment? 42 | // if so keep going, but if not, return false 43 | boolean onSegment = linePoint(x1,y1,x2,y2, closestX,closestY); 44 | if (!onSegment) return false; 45 | 46 | // get distance to closest point 47 | distX = closestX - cx; 48 | distY = closestY - cy; 49 | float distance = sqrt( (distX*distX) + (distY*distY) ); 50 | 51 | if (distance <= r) { 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | 58 | // POINT/CIRCLE 59 | boolean pointCircle(float px, float py, float cx, float cy, float r) { 60 | 61 | // get distance between the point and circle's center 62 | // using the Pythagorean Theorem 63 | float distX = px - cx; 64 | float distY = py - cy; 65 | float distance = sqrt( (distX*distX) + (distY*distY) ); 66 | 67 | // if the distance is less than the circle's 68 | // radius the point is inside! 69 | if (distance <= r) { 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | 76 | // LINE/POINT 77 | boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) { 78 | 79 | // get distance from the point to the two ends of the line 80 | float d1 = dist(px,py, x1,y1); 81 | float d2 = dist(px,py, x2,y2); 82 | 83 | // get the length of the line 84 | float lineLen = dist(x1,y1, x2,y2); 85 | 86 | // since floats are so minutely accurate, add 87 | // a little buffer zone that will give collision 88 | float buffer = 5; // higher # = less accurate 89 | 90 | // if the two distances are equal to the line's length, the 91 | // point is on the line! 92 | // note we use the buffer here to give a range, rather than one # 93 | if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) { 94 | return true; 95 | } 96 | return false; 97 | } 98 | 99 | 100 | // CIRCLE/RECTANGLE 101 | boolean circleRect(float cx, float cy, float radius, float rx, float ry, float rw, float rh) { 102 | 103 | // temporary variables to set edges for testing 104 | float testX = cx; 105 | float testY = cy; 106 | 107 | // which edge is closest? 108 | if (cx < rx) testX = rx; // compare to left edge 109 | else if (cx > rx+rw) testX = rx+rw; // right edge 110 | if (cy < ry) testY = ry; // top edge 111 | else if (cy > ry+rh) testY = ry+rh; // bottom edge 112 | 113 | // get distance from closest edges 114 | float distX = cx-testX; 115 | float distY = cy-testY; 116 | float distance = sqrt( (distX*distX) + (distY*distY) ); 117 | 118 | // if the distance is less than the radius, collision! 119 | if (distance <= radius) { 120 | return true; 121 | } 122 | return false; 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /Website/circle-rect.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

CIRCLE/RECTANGLE

4 | 5 |

The last example of this section combines the circle and rectangle code together. We have a circle with the position (cx,cy) with a radius r and a square at (rx,ry) with a width/height (rw,rh).

6 | 7 |

Our code will first test which edge of the rectangle is closest to the circle, then see if there is a collision using the Pythagorean Theorem. Let's create a temporary variable for the square's closest X/Y edges. We'll set them as the circle's position to start:

8 | 9 |
float testX = cx;
 10 | float testY = cy;
 11 | 
12 | 13 |

Then we do the following tests:

14 | 15 |
If the circle is to the RIGHT of the square, check against the RIGHT edge.
 16 | 
 17 | If the circle is to the LEFT of the square, check against the LEFT edge.
 18 | 
 19 | If the circle is ABOVE the square, check against the TOP edge.
 20 | 
 21 | If the circle is to the BELOW the square, check against the BOTTOM edge.
 22 | 
23 | 24 |

Here's how that works as an if statement:

25 | 26 |
if (cx < rx)         testX = rx;        // left edge
 27 | else if (cx > rx+rw) testX = rx+rw;     // right edge
 28 | 
 29 | if (cy < ry)         testY = ry;        // top edge
 30 | else if (cy > ry+rh) testY = ry+rh;     // bottom edge
 31 | 
32 | 33 |

Notice the slightly different if/else layout. You don't need the curly brackets if your statement is all on one line. This can tidy things up, but be careful you're not saving space at the expense of readable code!

34 | 35 |

Now that we know which edges to check, we run the Pythagorean Theorem code using the circle's center and the two edges we found above:

36 | 37 |
float distX = cx-testX;
 38 | float distY = cy-testY;
 39 | float distance = sqrt( (distX*distX) + (distY*distY) );
 40 | 
41 | 42 |

Finally, we compare this distance to the circle's radius:

43 | 44 |
if (distance <= radius) {
 45 |     return true;
 46 | }
 47 | return false;
 48 | 
49 | 50 |

Here's a full example:

51 | 52 |
float cx = 0;      // circle position (set with mouse)
 53 | float cy = 0;
 54 | float r = 30;      // circle radius
 55 | 
 56 | float sx = 200;    // square position
 57 | float sy = 100;
 58 | float sw = 200;    // and dimensions
 59 | float sh = 200;
 60 | 
 61 | 
 62 | void setup() {
 63 |   size(600,400);
 64 |   noStroke();
 65 | }
 66 | 
 67 | 
 68 | void draw() {
 69 |   background(255);
 70 | 
 71 |   // update square to mouse coordinates
 72 |   cx = mouseX;
 73 |   cy = mouseY;
 74 | 
 75 |   // check for collision
 76 |   // if hit, change rectangle color
 77 |   boolean hit = circleRect(cx,cy,r, sx,sy,sw,sh);
 78 |   if (hit) {
 79 |     fill(255,150,0);
 80 |   }
 81 |   else {
 82 |     fill(0,150,255);
 83 |   }
 84 |   rect(sx,sy, sw,sh);
 85 | 
 86 |   // draw the circle
 87 |   fill(0, 150);
 88 |   ellipse(cx,cy, r*2,r*2);
 89 | }
 90 | 
 91 | 
 92 | // CIRCLE/RECTANGLE
 93 | boolean circleRect(float cx, float cy, float radius, float rx, float ry, float rw, float rh) {
 94 | 
 95 |   // temporary variables to set edges for testing
 96 |   float testX = cx;
 97 |   float testY = cy;
 98 | 
 99 |   // which edge is closest?
100 |   if (cx < rx)         testX = rx;      // test left edge
101 |   else if (cx > rx+rw) testX = rx+rw;   // right edge
102 |   if (cy < ry)         testY = ry;      // top edge
103 |   else if (cy > ry+rh) testY = ry+rh;   // bottom edge
104 | 
105 |   // get distance from closest edges
106 |   float distX = cx-testX;
107 |   float distY = cy-testY;
108 |   float distance = sqrt( (distX*distX) + (distY*distY) );
109 | 
110 |   // if the distance is less than the radius, collision!
111 |   if (distance <= radius) {
112 |     return true;
113 |   }
114 |   return false;
115 | }
116 | 
117 | 118 |

This example is built on code by Matt Worden (thanks!).

119 | 120 | 121 | -------------------------------------------------------------------------------- /Website/line-point.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

LINE/POINT

4 | 5 |

So far, our collisions have mostly been logic and a little bit of addition. Line collision is a little trickier, unless your high school geometry class is still fresh.

6 | 7 |

A line (see note) is defined by two sets of X/Y coordinates. We can find the length of the line using our old standby the Pythagorean Theorem, but since we'll need to use it three times in this example, let's cheat and use Processing's built-in dist() function:

8 | 9 |
float lineLen = dist(x1,y1, x2,y2);
 10 | 
11 | 12 |

We also need to figure out the distance between the point and the two ends of the line:

13 | 14 |
float d1 = dist(px,py, x1,y1);
 15 | float d2 = dist(px,py, x2,y2);
 16 | 
17 | 18 |

If d1+d2 is equal to the length of the line, then we're on the line! This doesn't make intuitive sense, but look at this diagram:

19 | 20 |

Forming triangles between a point and line

21 | 22 |

If we collapse the distances, they are longer than the line!

23 | 24 |

There's a bit of an issue here, though. Since floating-point numbers are so minutely accurate, the collision only occurs if the point is exactly on the line, which means we're not going to get a natural-feeling collision. This is very similar to our first example, Point/Point. To fix this, let's create a small buffer and check if d1+d2 is +/- that range.

25 | 26 |
float buffer = 0.1;     // higher # = less accurate collision
 27 | 
28 | 29 |

Try playing with this value until you get something that feels right. Using this buffer value, we'll check for a collision:

30 | 31 |
if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) {
 32 |     return true;
 33 | }
 34 | return false;
 35 | 
36 | 37 |

Here's a full example, combining everything above:

38 | 39 |
float px = 0;     // point position (set by mouse)
 40 | float py = 0;
 41 | 
 42 | float x1 = 100;   // line defined by two points
 43 | float y1 = 300;
 44 | float x2 = 500;
 45 | float y2 = 100;
 46 | 
 47 | 
 48 | void setup() {
 49 |   size(600,400);
 50 |   noCursor();
 51 | 
 52 |   strokeWeight(5);  // make things a little easier to see
 53 | }
 54 | 
 55 | 
 56 | void draw() {
 57 |   background(255);
 58 | 
 59 |   // set point to mouse coordinates
 60 |   px = mouseX;
 61 |   py = mouseY;
 62 | 
 63 |   // check for collision
 64 |   // if hit, change the color of the line
 65 |   boolean hit = linePoint(x1,y1, x2,y2, px,py);
 66 |   if (hit) stroke(255,150,0, 150);
 67 |   else stroke(0,150,255, 150);
 68 |   line(x1,y1, x2,y2);
 69 | 
 70 |   // draw the point
 71 |   stroke(0, 150);
 72 |   point(px,py);
 73 | }
 74 | 
 75 | 
 76 | // LINE/POINT
 77 | boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) {
 78 | 
 79 |   // get distance from the point to the two ends of the line
 80 |   float d1 = dist(px,py, x1,y1);
 81 |   float d2 = dist(px,py, x2,y2);
 82 | 
 83 |   // get the length of the line
 84 |   float lineLen = dist(x1,y1, x2,y2);
 85 | 
 86 |   // since floats are so minutely accurate, add
 87 |   // a little buffer zone that will give collision
 88 |   float buffer = 0.1;    // higher # = less accurate
 89 | 
 90 |   // if the two distances are equal to the line's 
 91 |   // length, the point is on the line!
 92 |   // note we use the buffer here to give a range, 
 93 |   // rather than one #
 94 |   if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) {
 95 |     return true;
 96 |   }
 97 |   return false;
 98 | }
 99 | 
100 | 101 | 102 |

OK technically this would be called a line segment. But for the sake of simplicity, we'll be referring to these as the generic term line. Haters to the left.

103 | 104 |

This algorithm is thanks to help from this answer by MrRoy on StackOverflow.

105 | 106 | 107 | -------------------------------------------------------------------------------- /Website/rect-rect.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

RECTANGLE/RECTANGLE

4 | 5 |

Moving from Point/Rectangle to two rectangles is easy, but the if statements start to get pretty long. Let's say we have two squares, r1 and r2, with positions and sizes set like the last example. We now have to check:

6 | 7 |
Is the RIGHT edge of r1 to the RIGHT of the LEFT edge of r2?
  8 | Is the LEFT edge of r1 to the LEFT of the RIGHT edge of r2?
  9 | Is the BOTTOM edge of r1 BELOW the TOP edge of r2?
 10 | Is the TOP edge of r1 ABOVE the BOTTOM edge of r2?
 11 | 
12 | 13 |

Yeah, not so intuitive 😖. A picture will probably help:

14 | 15 |

Testing rectangle overlap

16 | 17 |

To start, let's test the right edge of r1 with the left edge of r2:

18 | 19 |
float r1RightEdge = r1x + r1w;
 20 | if (r1RightEdge >= r2x) {
 21 |     // right edge of r1 is past left edge of r2
 22 | }
 23 | 
24 | 25 |

We can expand this idea, checking all four edges:

26 | 27 |
if (r1x + r1w >= r2x &&     // r1 right edge past r2 left
 28 |   r1x <= r2x + r2w &&       // r1 left edge past r2 right
 29 |   r1y + r1h >= r2y &&       // r1 top edge past r2 bottom
 30 |   r1y <= r2y + r2h) {       // r1 bottom edge past r2 top
 31 |     return true;
 32 | }
 33 | return false;
 34 | 
35 | 36 |

While the math here is simple addition, this is the trickiest collision for most people to get used to. With practice, you'll be able to picture this in your head. Of course, building a re-usable function makes checking for collisions much easier! In the meantime, it may help to map things out on a piece of paper when you're writing your code.

37 | 38 |

Here's a full example:

39 | 40 |
float s1x = 0;      // square position (move with mouse)
 41 | float s1y = 0;
 42 | float s1w = 30;     // and dimensions
 43 | float s1h = 30;
 44 | 
 45 | float s2x = 200;    // same for second square
 46 | float s2y = 100;
 47 | float s2w = 200;
 48 | float s2h = 200;
 49 | 
 50 | 
 51 | void setup() {
 52 |   size(600,400);
 53 |   noStroke();
 54 | }
 55 | 
 56 | 
 57 | void draw() {
 58 |   background(255);
 59 | 
 60 |   // update square to mouse coordinates
 61 |   s1x = mouseX;
 62 |   s1y = mouseY;
 63 | 
 64 |   // check for collision
 65 |   // if hit, change rectangle color
 66 |   boolean hit = rectRect(s1x,s1y,s1w,s1h, s2x,s2y,s2w,s2h);
 67 |   if (hit) {
 68 |     fill(255,150,0);
 69 |   }
 70 |   else {
 71 |     fill(0,150,255);
 72 |   }
 73 |   rect(s2x,s2y, s2w,s2h);
 74 | 
 75 |   // draw the other square
 76 |   fill(0, 150);
 77 |   rect(s1x,s1y, s1w,s1h);
 78 | }
 79 | 
 80 | 
 81 | // RECTANGLE/RECTANGLE
 82 | boolean rectRect(float r1x, float r1y, float r1w, float r1h, float r2x, float r2y, float r2w, float r2h) {
 83 | 
 84 |   // are the sides of one rectangle touching the other?
 85 | 
 86 |   if (r1x + r1w >= r2x &&    // r1 right edge past r2 left
 87 |       r1x <= r2x + r2w &&    // r1 left edge past r2 right
 88 |       r1y + r1h >= r2y &&    // r1 top edge past r2 bottom
 89 |       r1y <= r2y + r2h) {    // r1 bottom edge past r2 top
 90 |         return true;
 91 |   }
 92 |   return false;
 93 | }
 94 | 
95 | 96 |

It's worth noting two important things here. First, the last two examples use squares, but any rectangle will work with this code. Second, this algorithm assumes you're using the default rectMode(CORNER), which draws rectangles from the top corner and specifies width/height. If you want to use rectMode(CENTER), you'll need to modify this algorithm (see the Challenge Questions at the end of this section).

97 | 98 |

99 | 100 |

Similar to the Circle/Circle example, Rectangle/Rectangle collision can be used to draw "bounding boxes" around more complex shapes. However, what you gain in performance you lose in accuracy. If you've ever played a game and frustratedly shouted "I totally hit you!" you have probably experienced bounding boxes that don't quite line up with their objects. Find the right balance between actually being correct and what feels right for the user.

101 | 102 | 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collision Detection 2 | 3 | **View the book, including interactive examples: [jeffreythompson.org/collision-detection](http://www.jeffreythompson.org/collision-detection)** 4 | 5 | > **UPDATE!** This site has been getting lots of visits which is awesome. I've made some changes so it will look better on mobile devices, make navigation easier, and tried to look over everything for mistakes. If you have any problems or suggestions, please [post an issue on the project repository](https://github.com/jeffThompson/CollisionDetection/issues). Thanks! 6 | 7 | The collision of objects underlies most game experiences and user-interfaces. Baseball bats collide with balls, zombies bump into walls, and Mario lands on platforms and stomps turtles. Even something as simple as clicking a button (a rectangle) with your mouse (a point) is a collision. 8 | 9 | This book explains the algorithms behind those collisions using basic shapes like circles, rectangles, and lines so you can implement them into your own projects. 10 | 11 | --- 12 | 13 | #### WHAT'S COVERED HERE? 14 | This book covers collisions between points, circles, rectangles, lines, polygons, and triangles. These examples are meant to be as readable and easily understood as possible. There are definitely faster, more efficient ways to detect these collisions, but this book is intended to be friendly and teach the principles with minimal math. 15 | 16 | Each section includes a description of the collision algorithm and an interactive example built using `processing.js`. 17 | 18 | --- 19 | 20 | #### WHAT'S NOT? 21 | As with any book, there's a lot more useful material than could be covered here. Things that aren't discussed are mostly left out because the math gets too complicated. Three-dimensional space isn't touched on. Ellipses, which seem like they should be pretty easy, are actually very difficult. 22 | 23 | If there's a specific collision not covered that would be helpful, please please [submit an issue with a request](https://github.com/jeffThompson/CollisionDetection/issues) or, better yet, submit a working example that you've built! 24 | 25 | --- 26 | 27 | #### ISSUES? 28 | If you find code that doesn't run correctly, an algorithm that isn't explained quite right, or a typo, please [report them](https://github.com/jeffThompson/CollisionDetection/issues). Thanks for your help! 29 | 30 | --- 31 | 32 | #### LICENSE 33 | This book's entire contents, including the code examples and this text, is released under a [Creative Commons Attribution, Non-Commercial, Share-Alike license](http://creativecommons.org/licenses/by-nc-sa/3.0/). This means: 34 | 35 | 1. You're welcome to use this book and the examples to make great stuff, but please cite this book somewhere in your project or its documentation. 36 | 2. You can only use the book's text and/or code vertbatim for non-commercial projects. That means you can remix or make your own version of the book, and you can fork and create new libraries based on the code here, so long as they are not commercial projects (ie publishing a book with a publisher). I'm very happy to talk options if you have a paid gig and you'd like to use some of the materials. 37 | 3. If you do make a project that forks or remixes this book or code-base, it must be released under this same license or a looser one. Pay it forward! 38 | 39 | **But you're free to use the ideas and code from this book *inside* your projects,** so long as this is just a part of what you're working on. In other words: you're welcome to build a super cool game that relies on the code you've seen here, even if you're going to sell it! 40 | 41 | If you have any questions about what you can and can't do with these examples, please [get in touch](mailto:mail@jeffreythompson.org). 42 | 43 | --- 44 | 45 | #### WHAT YOU SHOULD ALREADY KNOW 46 | This book's examples are written in [Processing](http://www.processing.org), a wrapper for the Java programming language. While you need only a little programming experience, you should understand how a basic Processing sketch is structured, how to use variables, how to draw shapes and get input from the mouse, and how `if/else` statements work. It may be helpful to understand using `PVector` objects to store positions, but we'll cover the basics if you haven't used them before. 47 | 48 | At the end, we will talk about using collision in object-oriented code. Understanding object-oriented programming will not be required to use this book, but it will be helpful for using these topics in larger projects with lots of objects hitting each other. 49 | -------------------------------------------------------------------------------- /Website/what_you_should_already_know.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

WHAT YOU SHOULD ALREADY KNOW

4 | 5 |

This book's examples are written in Processing, a wrapper for the Java programming language. While you need only a little programming experience, you should understand how a basic Processing sketch is structured, how to use variables, how to draw shapes and get input from the mouse, and how if/else statements work.

6 | 7 |

It may be helpful to understand using PVector objects to store positions, but we'll cover the basics if you haven't used them before.

8 | 9 |

At the end, we will talk about using collision in object-oriented code. Understanding object-oriented programming will not be required to use this book, but it will be helpful for using these topics in larger projects with lots of objects hitting each other.

10 | 11 |

Haven't used Processing before? That's ok! If you've used another programming language before it should be easy to understand these examples and port the code to the language of your choice.

12 | 13 |

FUNCTIONS

14 | 15 |

The core of the collision examples are functions, so you will need to be familiar with them to use this book. If you have never created a function, please read this section carefully; if you already understand this topic, you can skip ahead.

16 | 17 |

A function is a re-usable, self-contained piece of code. Functions are used for operations that you want to perform more than once: like checking for collision between two objects.

18 | 19 |

A function returns (sends back) a variable type (like int or boolean). For example, here's a function that returns the string "Hello!"

20 | 21 |
String sayHi() {
22 |     return "Hello!";
23 | }
24 | 
25 | 26 |

Note that when we declare the function, we list the variable type to be returned. The function does something useful, then sends data back using the return command. If the function doesn't need to return anything (for example, if all it does is draw a rectangle), the type is void. Sound familiar? The setup() and draw() sections of Processing are actually functions!

27 | 28 |

Functions can also receive arguments, or parameters that are fed into them. An argument is given a type and name (which exists only inside the function); multiple arguments are separated by commas. Here's a simple function that adds two numbers:

29 | 30 |
int sum(int a, int b) {
31 |     return a + b;
32 | }
33 | 
34 | 35 |

Once finished, you can use the function elsewhere in your code. For example, our sum() function above can be used like this:

36 | 37 |
int result = sum(2, 2);
38 | println(result);
39 | >> 4
40 | 
41 | 42 |

All of the examples in this book are functions. They are fed parameters of the objects to be tested (such as position or size) and return a boolean value whether or not a collision is happening. They could also be modified to return the position of the collision, such as in the Line/Line example. Be sure to look at the full code at the end of each example to see how the function is structured and called.

43 | 44 |

FLOATS

45 | 46 |

You'll notice throughout this book that we use floating-point variables. This is for a few reasons.

47 | 48 |

First, it allows more flexibility for later use. Integers (whole numbers) can be passed into the functions without an error, but the opposite would not be true.

49 | 50 |
// int-to-float argument ok
51 | int a = 1;
52 | floatFunction(a);
53 | 
54 | void floatFunction(float f) {
55 |     // Processing automatically converts to a float
56 | }
57 | 
58 | 
59 | // float-to-int argument not ok!
60 | float b = 1;
61 | intFunction(b);
62 | 
63 | void intFunction(int i) {
64 |     // this will cause an error
65 | }
66 | 
67 | 68 |

Second, floats give us the ability to measure more precisely and to move objects more smoothly across the screen, making your interactive projects feel more natural.

69 | 70 |

Finally, using floats makes it much easier to transition from separate X/Y positions to using using the PVector class, as the more advanced examples will do. The numbers inside a PVectors are floats, so this ensures our functions can be easily tweaked to work with vectors.

71 | 72 | 73 | -------------------------------------------------------------------------------- /Website/point-circle.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

POINT/CIRCLE

4 | 5 |

Point/Point collision was very easy, but from here on out we'll need some basic math to check if objects are touching each other. Testing if a point is inside a circle requires us to remember back to middle school math class and the Pythagorean Theorem:

6 | 7 |
a2 + b2 = c2
  8 | 
9 | 10 |

We can get the length of the long edge of a triangle c given the length of the other two sides. Translated to code, it looks like this:

11 | 12 |
c = sqrt( (a*a) + (b*b) );
 13 | 
14 | 15 |

Multiply a by itself, same for b, add the two together, and get the square root of the result.

16 | 17 |

Why do we need this? We can use the Pythagorean Theorem to get the distance between two objects in 2D space! In this context, a and b are the horizontal and vertical distances between the point and the center of the circle.

18 | 19 |

A triangle formed between a point and a circle

20 | 21 |

We can calculate the X and Y distance like this:

22 | 23 |
float distX = px - cx;
 24 | float distY = py - cy;
 25 | 
26 | 27 |

Then we can find the distance between the two points using the Pythagorean Theorem:

28 | 29 |
float distance = sqrt( (distX*distX) + (distY*distY) );
 30 | 
31 | 32 |

So if the point is at (10,10) and the circle's center is at (40,50), we get a distance of 50. You might be thinking, "What if the distance comes out negative?" Not to worry: since we multiply the distance values by themselves, even if they are negative the result will be positive.

33 | 34 |

OK, but how do we use this to test for collision? If the distance between the point and the center of the circle is less than the radius of the circle, we're colliding!

35 | 36 |
if (distance <= r) {
 37 |     return true;
 38 | }
 39 | return false;
 40 | 
41 | 42 |

Used in a full example, we can change the color of the circle if the point is inside it.

43 | 44 |
float px =     0;      // point position
 45 | float py =     0;
 46 | 
 47 | float cx =     300;    // circle center position
 48 | float cy =     200;
 49 | float radius = 100;    // circle's radius
 50 | 
 51 | 
 52 | void setup() {
 53 |   size(600,400);
 54 |   noCursor();
 55 | 
 56 |   strokeWeight(5);   // thicker stroke = easier to see
 57 | }
 58 | 
 59 | 
 60 | void draw() {
 61 |   background(255);
 62 | 
 63 |   // update point position to mouse coordinates
 64 |   px = mouseX;
 65 |   py = mouseY;
 66 | 
 67 |   // check for collision!
 68 |   boolean hit = pointCircle(px,py, cx,cy, radius);
 69 | 
 70 |   // draw circle
 71 |   // change fill color if hit
 72 |   if (hit) {
 73 |     fill(255,150,0);
 74 |   }
 75 |   else {
 76 |     fill(0,150,255);
 77 |   }
 78 |   noStroke();
 79 |   ellipse(cx,cy, radius*2,radius*2);
 80 | 
 81 |   // draw the point
 82 |   stroke(0);
 83 |   point(px, py);
 84 | }
 85 | 
 86 | 
 87 | // POINT/CIRCLE
 88 | boolean pointCircle(float px, float py, float cx, float cy, float r) {
 89 | 
 90 |   // get distance between the point and circle's center
 91 |   // using the Pythagorean Theorem
 92 |   float distX = px - cx;
 93 |   float distY = py - cy;
 94 |   float distance = sqrt( (distX*distX) + (distY*distY) );
 95 | 
 96 |   // if the distance is less than the circle's
 97 |   // radius the point is inside!
 98 |   if (distance <= r) {
 99 |     return true;
100 |   }
101 |   return false;
102 | }
103 | 
104 | 105 |

This method using the Pythagorean Theorem will come back many times. Processing has a built-in dist() function, if you prefer, though we'll keep the math in place as a reference.

106 | 107 |

One caveat: if you have a very fast-moving object, it can sometimes go right through its target without a collision being triggered! This is sometimes referred to as the "bullet through paper" problem. There are lots of solutions, but a good place to start would be this GameDev.net post. A standard way for detecting this is called "Continuous Collision Detection" or CCD.

108 | 109 | 110 | -------------------------------------------------------------------------------- /Website/line-rect.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

LINE/RECTANGLE

4 | 5 |

We've actually already covered how to check if a line has hit a rectangle: it's really just four Line/Line collisions, one for each side!

6 | 7 |

For example, the left edge of the square starts at (rx,ry) and extends down to ry+rh. We can treat that as a line, using the algorithm we made in the last section:

8 | 9 |
boolean left =   lineLine(x1,y1,x2,y2, rx,ry, rx,ry+rh);
 10 | 
11 | 12 |

This can be more easily visualized like this:

13 | 14 |

Dividing a rectangle into four lines

15 | 16 |

We do the same for the other three sides:

17 | 18 |
boolean left =   lineLine(x1,y1,x2,y2, rx,ry,rx, ry+rh);
 19 | boolean right =  lineLine(x1,y1,x2,y2, rx+rw,ry, rx+rw,ry+rh);
 20 | boolean top =    lineLine(x1,y1,x2,y2, rx,ry, rx+rw,ry);
 21 | boolean bottom = lineLine(x1,y1,x2,y2, rx,ry+rh, rx+rw,ry+rh);
 22 | 
23 | 24 |

If any of the above statements are true, the line has hit the rectangle.

25 | 26 |
if (left || right || top || bottom) {
 27 |     return true;
 28 | }
 29 | return false;
 30 | 
31 | 32 |

A full example is below. Note that the red dots are drawn in the Line/Line function, showing where the line intersects the rectangle. You can delete them from the function if you don't want them in your finished project.

33 | 34 |
float x1 = 0;      // points for line (controlled by mouse)
 35 | float y1 = 0;
 36 | float x2 = 0;      // static point
 37 | float y2 = 0;
 38 | 
 39 | float sx = 200;    // square position
 40 | float sy = 100;
 41 | float sw = 200;    // and size
 42 | float sh = 200;
 43 | 
 44 | 
 45 | void setup() {
 46 |   size(600, 400);
 47 | 
 48 |   strokeWeight(5);  // make the line easier to see
 49 | }
 50 | 
 51 | 
 52 | void draw() {
 53 |   background(255);
 54 | 
 55 |   // set end of line to mouse coordinates
 56 |   x1 = mouseX;
 57 |   y1 = mouseY;
 58 | 
 59 |   // check if line has hit the square
 60 |   // if so, change the fill color
 61 |   boolean hit = lineRect(x1,y1,x2,y2, sx,sy,sw,sh);
 62 |   if (hit) fill(255,150,0);
 63 |   else fill(0,150,255);
 64 |   noStroke();
 65 |   rect(sx, sy, sw, sh);
 66 | 
 67 |   // draw the line
 68 |   stroke(0, 150);
 69 |   line(x1, y1, x2, y2);
 70 | }
 71 | 
 72 | 
 73 | // LINE/RECTANGLE
 74 | boolean lineRect(float x1, float y1, float x2, float y2, float rx, float ry, float rw, float rh) {
 75 | 
 76 |   // check if the line has hit any of the rectangle's sides
 77 |   // uses the Line/Line function below
 78 |   boolean left =   lineLine(x1,y1,x2,y2, rx,ry,rx, ry+rh);
 79 |   boolean right =  lineLine(x1,y1,x2,y2, rx+rw,ry, rx+rw,ry+rh);
 80 |   boolean top =    lineLine(x1,y1,x2,y2, rx,ry, rx+rw,ry);
 81 |   boolean bottom = lineLine(x1,y1,x2,y2, rx,ry+rh, rx+rw,ry+rh);
 82 | 
 83 |   // if ANY of the above are true, the line
 84 |   // has hit the rectangle
 85 |   if (left || right || top || bottom) {
 86 |     return true;
 87 |   }
 88 |   return false;
 89 | }
 90 | 
 91 | 
 92 | // LINE/LINE
 93 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
 94 | 
 95 |   // calculate the direction of the lines
 96 |   float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
 97 |   float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
 98 | 
 99 |   // if uA and uB are between 0-1, lines are colliding
100 |   if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
101 | 
102 |     // optionally, draw a circle where the lines meet
103 |     float intersectionX = x1 + (uA * (x2-x1));
104 |     float intersectionY = y1 + (uA * (y2-y1));
105 |     fill(255,0,0);
106 |     noStroke();
107 |     ellipse(intersectionX, intersectionY, 20, 20);
108 | 
109 |     return true;
110 |   }
111 |   return false;
112 | }
113 | 
114 | 115 |

This algorithm can also be used to test line-of-sight. Let's say you have two objects and a rectangular obstacle: if you draw a line between one object and another, then check if it has hit the rectangle, you can tell if the objects can "see" each other or if they are hidden behind the obstacle.

116 | 117 |

An example of line of sight

118 | 119 |

For an example of this in code, see the Line Of Sight example in my Processing teaching repository.

120 | 121 | 122 | -------------------------------------------------------------------------------- /CodeExamples/PolyRect/PolyRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float sx = 0; // a square, controlled by the mouse 9 | float sy = 0; 10 | float sw = 30; // width and height 11 | float sh = 30; 12 | 13 | // array of PVectors, one for each vertex in the polygon 14 | PVector[] vertices = new PVector[4]; 15 | 16 | 17 | void setup() { 18 | size(600,400); 19 | noStroke(); 20 | 21 | // set position of the vertices (here a parallelogram) 22 | vertices[0] = new PVector(100,100); 23 | vertices[1] = new PVector(400,100); 24 | vertices[2] = new PVector(500,300); 25 | vertices[3] = new PVector(200,300); 26 | } 27 | 28 | 29 | void draw() { 30 | background(255); 31 | 32 | // update circle to mouse coordinates 33 | sx = mouseX; 34 | sy = mouseY; 35 | 36 | // check for collision 37 | // if hit, change fill color 38 | boolean hit = polyRect(vertices, sx,sy,sw,sh); 39 | if (hit) fill(255,150,0); 40 | else fill(0,150,255); 41 | 42 | // draw the polygon using beginShape() 43 | noStroke(); 44 | beginShape(); 45 | for (PVector v : vertices) { 46 | vertex(v.x, v.y); 47 | } 48 | endShape(); 49 | 50 | // draw the rectangle 51 | fill(0, 150); 52 | rect(sx,sy, sw,sh); 53 | } 54 | 55 | 56 | // POLYGON/RECTANGLE 57 | boolean polyRect(PVector[] vertices, float rx, float ry, float rw, float rh) { 58 | 59 | // go through each of the vertices, plus the next vertex in the list 60 | int next = 0; 61 | for (int current=0; current= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 115 | return true; 116 | } 117 | return false; 118 | } 119 | 120 | 121 | // POLYGON/POINT 122 | // only needed if you're going to check if the rectangle is INSIDE the polygon 123 | boolean polygonPoint(PVector[] vertices, float px, float py) { 124 | boolean collision = false; 125 | 126 | // go through each of the vertices, plus the next vertex in the list 127 | int next = 0; 128 | for (int current=0; current py && vn.y < py) || (vc.y < py && vn.y > py)) && 142 | (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) { 143 | collision = !collision; 144 | } 145 | } 146 | return collision; 147 | } 148 | 149 | 150 | -------------------------------------------------------------------------------- /CodeExamples/PolyRect/web-export/PolyRect.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/RECTANGLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float sx = 0; // a square, controlled by the mouse 9 | float sy = 0; 10 | float sw = 30; // width and height 11 | float sh = 30; 12 | 13 | // array of PVectors, one for each vertex in the polygon 14 | PVector[] vertices = new PVector[4]; 15 | 16 | 17 | void setup() { 18 | size($("#wrapper").width(), 400); 19 | noStroke(); 20 | 21 | // set position of the vertices (here a parallelogram) 22 | vertices[0] = new PVector(width/2-100, height/2-100); 23 | vertices[1] = new PVector(width/2+100, height/2-100); 24 | vertices[2] = new PVector(width/2+50, height/2+100); 25 | vertices[3] = new PVector(width/2-50, height/2+100); 26 | } 27 | 28 | 29 | void draw() { 30 | background(255); 31 | 32 | // update circle to mouse coordinates 33 | sx = mouseX; 34 | sy = mouseY; 35 | 36 | // check for collision 37 | // if hit, change fill color 38 | boolean hit = polyRect(sx,sy,sw,sh, vertices); 39 | if (hit) fill(255,150,0); 40 | else fill(0,150,255); 41 | 42 | // draw the polygon using beginShape() 43 | noStroke(); 44 | beginShape(); 45 | for (PVector v : vertices) { 46 | vertex(v.x, v.y); 47 | } 48 | endShape(); 49 | 50 | // draw the rectangle 51 | fill(0, 150); 52 | rect(sx,sy, sw,sh); 53 | } 54 | 55 | 56 | // POLYGON/RECTANGLE 57 | boolean polyRect(float rx, float ry, float rw, float rh, PVector[] vertices) { 58 | 59 | // go through each of the vertices, plus the next vertex in the list 60 | int next = 0; 61 | for (int current=0; current= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 115 | return true; 116 | } 117 | return false; 118 | } 119 | 120 | 121 | // POLYGON/POINT 122 | // only needed if you're going to check if the rectangle is INSIDE the polygon 123 | boolean polygonPoint(float px, float py, PVector[] vertices) { 124 | boolean collision = false; 125 | 126 | // go through each of the vertices, plus the next vertex in the list 127 | int next = 0; 128 | for (int current=0; current py) != (vn.y > py)) && (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) { 142 | collision = !collision; 143 | } 144 | } 145 | return collision; 146 | } 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /Website/poly-line.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

POLYGON/LINE

4 | 5 |

Checking if a line is hitting a polygon is very much like the Rectangle/Line example. We go through each side of the polygon and do a Line/Line check.

6 | 7 |

In this example, we make a nice regular polygon with 16 sides (a hexadecagon). The points are stored in an array of PVectors again:

8 | 9 |
PVector[] vertices = new PVector[16];
 10 | 
 11 | // generate a nice, even polygon
 12 | float angle = TWO_PI / vertices.length;
 13 | for (int i=0; i<vertices.length; i++) {
 14 |   float a = angle * i;
 15 |   float x = 300 + cos(a) * 100;
 16 |   float y = 200 + sin(a) * 100;
 17 |   vertices[i] = new PVector(x,y);
 18 | }
 19 | 
20 | 21 |

We do the same for loop that walks through the vertices and gets the current point, as well as the point one step ahead in the array.

22 | 23 |
int next = 0;
 24 | for (int current=0; current<vertices.length; current++) {
 25 | 
 26 |     // get next vertex in list
 27 |     // if we've hit the end, wrap around to 0
 28 |     next = current+1;
 29 |     if (next == vertices.length) next = 0;
 30 | }
 31 | 
32 | 33 |

Now we can get the X/Y coordinates of those two points, which form a line:

34 | 35 |
float x3 = vertices[current].x;
 36 | float y3 = vertices[current].y;
 37 | float x4 = vertices[next].x;
 38 | float y4 = vertices[next].y;
 39 | 
40 | 41 |

And we can pass that to a Line/Line collision. If any of the lines hit, we can immediately send back true. This saves processing, since we can skip computing the remaining sides. If we get to the end and haven't had a hit, we return false.

42 | 43 |
boolean hit = lineLine(x1, y1, x2, y2, x3, y3, x4, y4);
 44 | if (hit) {
 45 |   return true;
 46 | }
 47 | 
48 | 49 |

Here's a full example:

50 | 51 |
float x1 = 0;    // line position (set by mouse)
 52 | float y1 = 0;
 53 | float x2 = 20;   // fixed end
 54 | float y2 = 20;
 55 | 
 56 | // array of PVectors, one for each vertex in the polygon
 57 | PVector[] vertices = new PVector[16];
 58 | 
 59 | 
 60 | void setup() {
 61 |   size(600, 400);
 62 |   noCursor();
 63 | 
 64 |   strokeWeight(5);  // make the line easier to see
 65 | 
 66 |   // set position of the vertices - a regular polygon!
 67 |   // based on this example:
 68 |   // https://processing.org/examples/regularpolygon.html
 69 |   float angle = TWO_PI / vertices.length;
 70 |   for (int i=0; i<vertices.length; i++) {
 71 |     float a = angle * i;
 72 |     float x = 300 + cos(a) * 100;
 73 |     float y = 200 + sin(a) * 100;
 74 |     vertices[i] = new PVector(x,y);
 75 |   }
 76 | }
 77 | 
 78 | 
 79 | void draw() {
 80 |   background(255);
 81 | 
 82 |   // update line to mouse coordinates
 83 |   x1 = mouseX;
 84 |   y1 = mouseY;
 85 | 
 86 |   // check for collision
 87 |   // if hit, change fill color
 88 |   boolean hit = polyLine(vertices, x1, y1, x2, y2);
 89 |   if (hit) fill(255, 150, 0);
 90 |   else fill(0, 150, 255);
 91 | 
 92 |   // draw the polygon using beginShape()
 93 |   noStroke();
 94 |   beginShape();
 95 |   for (PVector v : vertices) {
 96 |     vertex(v.x, v.y);
 97 |   }
 98 |   endShape(CLOSE);
 99 | 
100 |   // draw line
101 |   stroke(0, 150);
102 |   line(x1, y1, x2, y2);
103 | }
104 | 
105 | 
106 | // POLYGON/LINE
107 | boolean polyLine(PVector[] vertices, float x1, float y1, float x2, float y2) {
108 | 
109 |   // go through each of the vertices, plus the next
110 |   // vertex in the list
111 |   int next = 0;
112 |   for (int current=0; current<vertices.length; current++) {
113 | 
114 |     // get next vertex in list
115 |     // if we've hit the end, wrap around to 0
116 |     next = current+1;
117 |     if (next == vertices.length) next = 0;
118 | 
119 |     // get the PVectors at our current position
120 |     // extract X/Y coordinates from each
121 |     float x3 = vertices[current].x;
122 |     float y3 = vertices[current].y;
123 |     float x4 = vertices[next].x;
124 |     float y4 = vertices[next].y;
125 | 
126 |     // do a Line/Line comparison
127 |     // if true, return 'true' immediately and
128 |     // stop testing (faster)
129 |     boolean hit = lineLine(x1, y1, x2, y2, x3, y3, x4, y4);
130 |     if (hit) {
131 |       return true;
132 |     }
133 |   }
134 | 
135 |   // never got a hit
136 |   return false;
137 | }
138 | 
139 | 
140 | // LINE/LINE
141 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
142 | 
143 |   // calculate the direction of the lines
144 |   float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
145 |   float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
146 | 
147 |   // if uA and uB are between 0-1, lines are colliding
148 |   if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
149 |     return true;
150 |   }
151 |   return false;
152 | }
153 | 
154 | 155 | 156 | -------------------------------------------------------------------------------- /CodeExamples/PolyPoly/PolyPoly.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/POLYGON 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | // array of PVectors for each shape 9 | PVector[] pentagon = new PVector[5]; 10 | PVector[] randomPoly = new PVector[8]; 11 | 12 | 13 | void setup() { 14 | size(600,400); 15 | noStroke(); 16 | 17 | // set position of the pentagon's vertices 18 | float angle = TWO_PI / pentagon.length; 19 | for (int i=0; i= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 145 | return true; 146 | } 147 | return false; 148 | } 149 | 150 | 151 | // POLYGON/POINT 152 | // used only to check if the second polygon is INSIDE the first 153 | boolean polyPoint(PVector[] vertices, float px, float py) { 154 | boolean collision = false; 155 | 156 | // go through each of the vertices, plus the next vertex in the list 157 | int next = 0; 158 | for (int current=0; current py && vn.y < py) || (vc.y < py && vn.y > py)) && 172 | (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) { 173 | collision = !collision; 174 | } 175 | } 176 | return collision; 177 | } 178 | 179 | 180 | -------------------------------------------------------------------------------- /CodeExamples/PolyPoly/web-export/PolyPoly.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/POLYGON 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | // array of PVectors for each shape 9 | PVector[] pentagon = new PVector[5]; 10 | PVector[] randomPoly = new PVector[8]; 11 | 12 | 13 | void setup() { 14 | size($("#wrapper").width(), 400); 15 | noStroke(); 16 | 17 | // set position of the pentagon's vertices 18 | float angle = TWO_PI / pentagon.length; 19 | for (int i=0; i= 0 && uA <= 1 && uB >= 0 && uB <= 1) { 145 | return true; 146 | } 147 | return false; 148 | } 149 | 150 | 151 | // POLYGON/POINT 152 | // used only to check if the second polygon is INSIDE the first 153 | boolean polyPoint(PVector[] vertices, float px, float py) { 154 | boolean collision = false; 155 | 156 | // go through each of the vertices, plus the next vertex in the list 157 | int next = 0; 158 | for (int current=0; current py && vn.y < py) || (vc.y < py && vn.y > py)) && 172 | (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) { 173 | collision = !collision; 174 | } 175 | } 176 | return collision; 177 | } 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /Website/poly-rect.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

POLYGON/RECTANGLE

4 | 5 |

Like the previous example, collision between a polygon and a rectangle really just requires us to extend existing functions. In this case, we can test if any of the edges of the rectangle are hitting any of the edges of the polygon.

6 | 7 |

To do this, we test Line/Rectangle collision for each side of the polygon. Like our previous exmaples, vc and vn are the two PVectors forming a side:

8 | 9 |
boolean collision = lineRect(vc.x,vc.y,vn.x,vn.y, rx,ry,rw,rh);
 10 | if (collision) return true;
 11 | 
12 | 13 |

Also like the last example, we can catch the edge case where the rectangle is inside the polygon by testing if its X/Y position (a point) is inside the polygon. This should be left off unless necessary, since like our previous example it requires going through all the vertices of the polygon again, slowing down your program.

14 | 15 |
boolean inside = polygonPoint(vertices, rx,ry);
 16 | if (inside) return true;
 17 | 
18 | 19 |

Here's a full example:

20 | 21 |
float sx = 0;    // a square, controlled by the mouse
 22 | float sy = 0;
 23 | float sw = 30;   // width and height
 24 | float sh = 30;
 25 | 
 26 | // array of PVectors, one for each vertex in the polygon
 27 | PVector[] vertices = new PVector[4];
 28 | 
 29 | 
 30 | void setup() {
 31 |   size(600,400);
 32 |   noStroke();
 33 | 
 34 |   // set position of the vertices (here a parallelogram)
 35 |   vertices[0] = new PVector(100,100);
 36 |   vertices[1] = new PVector(400,100);
 37 |   vertices[2] = new PVector(500,300);
 38 |   vertices[3] = new PVector(200,300);
 39 | }
 40 | 
 41 | 
 42 | void draw() {
 43 |   background(255);
 44 | 
 45 |   // update circle to mouse coordinates
 46 |   sx = mouseX;
 47 |   sy = mouseY;
 48 | 
 49 |   // check for collision
 50 |   // if hit, change fill color
 51 |   boolean hit = polyRect(vertices, sx,sy,sw,sh);
 52 |   if (hit) fill(255,150,0);
 53 |   else fill(0,150,255);
 54 | 
 55 |   // draw the polygon using beginShape()
 56 |   noStroke();
 57 |   beginShape();
 58 |   for (PVector v : vertices) {
 59 |     vertex(v.x, v.y);
 60 |   }
 61 |   endShape();
 62 | 
 63 |   // draw the rectangle
 64 |   fill(0, 150);
 65 |   rect(sx,sy, sw,sh);
 66 | }
 67 | 
 68 | 
 69 | // POLYGON/RECTANGLE
 70 | boolean polyRect(PVector[] vertices, float rx, float ry, float rw, float rh) {
 71 | 
 72 |   // go through each of the vertices, plus the next
 73 |   // vertex in the list
 74 |   int next = 0;
 75 |   for (int current=0; current<vertices.length; current++) {
 76 | 
 77 |     // get next vertex in list
 78 |     // if we've hit the end, wrap around to 0
 79 |     next = current+1;
 80 |     if (next == vertices.length) next = 0;
 81 | 
 82 |     // get the PVectors at our current position
 83 |     // this makes our if statement a little cleaner
 84 |     PVector vc = vertices[current];    // c for "current"
 85 |     PVector vn = vertices[next];       // n for "next"
 86 | 
 87 |     // check against all four sides of the rectangle
 88 |     boolean collision = lineRect(vc.x,vc.y,vn.x,vn.y, rx,ry,rw,rh);
 89 |     if (collision) return true;
 90 | 
 91 |     // optional: test if the rectangle is INSIDE the polygon
 92 |     // note that this iterates all sides of the polygon
 93 |     // again, so only use this if you need to
 94 |     boolean inside = polygonPoint(vertices, rx,ry);
 95 |     if (inside) return true;
 96 |   }
 97 | 
 98 |   return false;
 99 | }
100 | 
101 | 
102 | // LINE/RECTANGLE
103 | boolean lineRect(float x1, float y1, float x2, float y2, float rx, float ry, float rw, float rh) {
104 | 
105 |   // check if the line has hit any of the rectangle's sides
106 |   // uses the Line/Line function below
107 |   boolean left =   lineLine(x1,y1,x2,y2, rx,ry,rx, ry+rh);
108 |   boolean right =  lineLine(x1,y1,x2,y2, rx+rw,ry, rx+rw,ry+rh);
109 |   boolean top =    lineLine(x1,y1,x2,y2, rx,ry, rx+rw,ry);
110 |   boolean bottom = lineLine(x1,y1,x2,y2, rx,ry+rh, rx+rw,ry+rh);
111 | 
112 |   // if ANY of the above are true,
113 |   // the line has hit the rectangle
114 |   if (left || right || top || bottom) {
115 |     return true;
116 |   }
117 |   return false;
118 | }
119 | 
120 | 
121 | // LINE/LINE
122 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
123 | 
124 |   // calculate the direction of the lines
125 |   float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
126 |   float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
127 | 
128 |   // if uA and uB are between 0-1, lines are colliding
129 |   if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
130 |     return true;
131 |   }
132 |   return false;
133 | }
134 | 
135 | 
136 | // POLYGON/POINT
137 | // only needed if you're going to check if the rectangle
138 | // is INSIDE the polygon
139 | boolean polygonPoint(PVector[] vertices, float px, float py) {
140 |   boolean collision = false;
141 | 
142 |   // go through each of the vertices, plus the next
143 |   // vertex in the list
144 |   int next = 0;
145 |   for (int current=0; current<vertices.length; current++) {
146 | 
147 |     // get next vertex in list
148 |     // if we've hit the end, wrap around to 0
149 |     next = current+1;
150 |     if (next == vertices.length) next = 0;
151 | 
152 |     // get the PVectors at our current position
153 |     // this makes our if statement a little cleaner
154 |     PVector vc = vertices[current];    // c for "current"
155 |     PVector vn = vertices[next];       // n for "next"
156 | 
157 |     // compare position, flip 'collision' variable
158 |     // back and forth
159 |     if (((vc.y > py && vn.y < py) || (vc.y < py && vn.y > py)) &&
160 |          (px < (vn.x-vc.x)*(py-vc.y) / (vn.y-vc.y)+vc.x)) {
161 |             collision = !collision;
162 |     }
163 |   }
164 |   return collision;
165 | }
166 | 
167 | 168 | 169 | -------------------------------------------------------------------------------- /Website/poly-poly.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Refresh your browser for a new random polygon!
4 | 5 |

POLYGON/POLYGON

6 | 7 |

Our final example in this section checks for the collision of two polygons. Since we really just need to check if any of the sides of one polygon are hitting any of the sides of the other, this is pretty straight-forward! As before, we also test if the one polygon is fully inside the other one.

8 | 9 |

Here's the full example:

10 | 11 |
// array of PVectors for each shape
 12 | PVector[] pentagon = new PVector[5];
 13 | PVector[] randomPoly = new PVector[8];
 14 | 
 15 | 
 16 | void setup() {
 17 |   size(600,400);
 18 |   noStroke();
 19 | 
 20 |   // set position of the pentagon's vertices
 21 |   float angle = TWO_PI / pentagon.length;
 22 |   for (int i=0; i<pentagon.length; i++) {
 23 |     float a = angle * i;
 24 |     float x = 300 + cos(a) * 100;
 25 |     float y = 200 + sin(a) * 100;
 26 |     pentagon[i] = new PVector(x,y);
 27 |   }
 28 | 
 29 |   // and create the random polygon
 30 |   float a = 0;
 31 |   int i = 0;
 32 |   while (a < 360) {
 33 |     float x = cos(radians(a)) * random(30,50);
 34 |     float y = sin(radians(a)) * random(30,50);
 35 |     randomPoly[i] = new PVector(x,y);
 36 |     a += random(15, 40);
 37 |     i += 1;
 38 |   }
 39 | }
 40 | 
 41 | 
 42 | void draw() {
 43 |   background(255);
 44 | 
 45 |   // update random polygon to mouse position
 46 |   PVector mouse = new PVector(mouseX, mouseY);
 47 |   PVector diff = PVector.sub(mouse, randomPoly[0]);
 48 |   for (PVector v : randomPoly) {
 49 |     v.add(diff);
 50 |   }
 51 | 
 52 |   // check for collision
 53 |   // if hit, change fill color
 54 |   boolean hit = polyPoly(pentagon, randomPoly);
 55 |   if (hit) fill(255,150,0);
 56 |   else fill(0,150,255);
 57 | 
 58 |   // draw the pentagon
 59 |   noStroke();
 60 |   beginShape();
 61 |   for (PVector v : pentagon) {
 62 |     vertex(v.x, v.y);
 63 |   }
 64 |   endShape();
 65 | 
 66 |   // draw the random polygon
 67 |   fill(0, 150);
 68 |   beginShape();
 69 |   for (PVector v : randomPoly) {
 70 |     vertex(v.x, v.y);
 71 |   }
 72 |   endShape();
 73 | }
 74 | 
 75 | 
 76 | // POLYGON/POLYGON
 77 | boolean polyPoly(PVector[] p1, PVector[] p2) {
 78 | 
 79 |   // go through each of the vertices, plus the next
 80 |   // vertex in the list
 81 |   int next = 0;
 82 |   for (int current=0; current<p1.length; current++) {
 83 | 
 84 |     // get next vertex in list
 85 |     // if we've hit the end, wrap around to 0
 86 |     next = current+1;
 87 |     if (next == p1.length) next = 0;
 88 | 
 89 |     // get the PVectors at our current position
 90 |     // this makes our if statement a little cleaner
 91 |     PVector vc = p1[current];    // c for "current"
 92 |     PVector vn = p1[next];       // n for "next"
 93 | 
 94 |     // now we can use these two points (a line) to compare
 95 |     // to the other polygon's vertices using polyLine()
 96 |     boolean collision = polyLine(p2, vc.x,vc.y,vn.x,vn.y);
 97 |     if (collision) return true;
 98 | 
 99 |     // optional: check if the 2nd polygon is INSIDE the first
100 |     collision = polyPoint(p1, p2[0].x, p2[0].y);
101 |     if (collision) return true;
102 |   }
103 | 
104 |   return false;
105 | }
106 | 
107 | 
108 | // POLYGON/LINE
109 | boolean polyLine(PVector[] vertices, float x1, float y1, float x2, float y2) {
110 | 
111 |   // go through each of the vertices, plus the next
112 |   // vertex in the list
113 |   int next = 0;
114 |   for (int current=0; current<vertices.length; current++) {
115 | 
116 |     // get next vertex in list
117 |     // if we've hit the end, wrap around to 0
118 |     next = current+1;
119 |     if (next == vertices.length) next = 0;
120 | 
121 |     // get the PVectors at our current position
122 |     // extract X/Y coordinates from each
123 |     float x3 = vertices[current].x;
124 |     float y3 = vertices[current].y;
125 |     float x4 = vertices[next].x;
126 |     float y4 = vertices[next].y;
127 | 
128 |     // do a Line/Line comparison
129 |     // if true, return 'true' immediately and
130 |     // stop testing (faster)
131 |     boolean hit = lineLine(x1, y1, x2, y2, x3, y3, x4, y4);
132 |     if (hit) {
133 |       return true;
134 |     }
135 |   }
136 | 
137 |   // never got a hit
138 |   return false;
139 | }
140 | 
141 | 
142 | // LINE/LINE
143 | boolean lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
144 | 
145 |   // calculate the direction of the lines
146 |   float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
147 |   float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
148 | 
149 |   // if uA and uB are between 0-1, lines are colliding
150 |   if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
151 |     return true;
152 |   }
153 |   return false;
154 | }
155 | 
156 | 
157 | // POLYGON/POINT
158 | // used only to check if the second polygon is
159 | // INSIDE the first
160 | boolean polyPoint(PVector[] vertices, float px, float py) {
161 |   boolean collision = false;
162 | 
163 |   // go through each of the vertices, plus the next
164 |   // vertex in the list
165 |   int next = 0;
166 |   for (int current=0; current<vertices.length; current++) {
167 | 
168 |     // get next vertex in list
169 |     // if we've hit the end, wrap around to 0
170 |     next = current+1;
171 |     if (next == vertices.length) next = 0;
172 | 
173 |     // get the PVectors at our current position
174 |     // this makes our if statement a little cleaner
175 |     PVector vc = vertices[current];    // c for "current"
176 |     PVector vn = vertices[next];       // n for "next"
177 | 
178 |     // compare position, flip 'collision' variable
179 |     // back and forth
180 |     if (((vc.y > py && vn.y < py) || (vc.y < py && vn.y > py)) &&
181 |          (px < (vn.x-vc.x)*(py-vc.y) / (vn.y-vc.y)+vc.x)) {
182 |             collision = !collision;
183 |     }
184 |   }
185 |   return collision;
186 | }
187 | 
188 | 189 | 190 | -------------------------------------------------------------------------------- /CodeExamples/PolyCircle/PolyCircle.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/CIRCLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float cx = 0; // position of the circle 9 | float cy = 0; 10 | float r = 30; // circle's radius 11 | 12 | // array of PVectors, one for each vertex in the polygon 13 | PVector[] vertices = new PVector[4]; 14 | 15 | 16 | void setup() { 17 | size(600,400); 18 | noStroke(); 19 | 20 | // set position of the vertices (here a trapezoid) 21 | vertices[0] = new PVector(200,100); 22 | vertices[1] = new PVector(400,100); 23 | vertices[2] = new PVector(350,300); 24 | vertices[3] = new PVector(250,300); 25 | } 26 | 27 | 28 | void draw() { 29 | background(255); 30 | 31 | // update circle to mouse coordinates 32 | cx = mouseX; 33 | cy = mouseY; 34 | 35 | // check for collision 36 | // if hit, change fill color 37 | boolean hit = polyCircle(vertices, cx,cy,r); 38 | if (hit) fill(255,150,0); 39 | else fill(0,150,255); 40 | 41 | // draw the polygon using beginShape() 42 | noStroke(); 43 | beginShape(); 44 | for (PVector v : vertices) { 45 | vertex(v.x, v.y); 46 | } 47 | endShape(); 48 | 49 | // draw the circle 50 | fill(0, 150); 51 | ellipse(cx,cy, r*2,r*2); 52 | } 53 | 54 | 55 | // POLYGON/CIRCLE 56 | boolean polyCircle(PVector[] vertices, float cx, float cy, float r) { 57 | 58 | // go through each of the vertices, plus the next vertex in the list 59 | int next = 0; 60 | for (int current=0; current= lineLen-buffer && d1+d2 <= lineLen+buffer) { 154 | return true; 155 | } 156 | return false; 157 | } 158 | 159 | 160 | // POINT/CIRCLE 161 | boolean pointCircle(float px, float py, float cx, float cy, float r) { 162 | 163 | // get distance between the point and circle's center 164 | // using the Pythagorean Theorem 165 | float distX = px - cx; 166 | float distY = py - cy; 167 | float distance = sqrt( (distX*distX) + (distY*distY) ); 168 | 169 | // if the distance is less than the circle's 170 | // radius the point is inside! 171 | if (distance <= r) { 172 | return true; 173 | } 174 | return false; 175 | } 176 | 177 | 178 | // POLYGON/POINT 179 | // only needed if you're going to check if the circle is INSIDE the polygon 180 | boolean polygonPoint(PVector[] vertices, float px, float py) { 181 | boolean collision = false; 182 | 183 | // go through each of the vertices, plus the next vertex in the list 184 | int next = 0; 185 | for (int current=0; current py && vn.y < py) || (vc.y < py && vn.y > py)) && 199 | (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) { 200 | collision = !collision; 201 | } 202 | } 203 | return collision; 204 | } -------------------------------------------------------------------------------- /CodeExamples/PolyCircle/web-export/PolyCircle.pde: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | POLYGON/CIRCLE 4 | Jeff Thompson | 2015 | www.jeffreythompson.org 5 | 6 | */ 7 | 8 | float cx = 0; // position of the circle 9 | float cy = 0; 10 | float r = 30; // circle's radius 11 | 12 | // array of PVectors, one for each vertex in the polygon 13 | PVector[] vertices = new PVector[4]; 14 | 15 | 16 | void setup() { 17 | size($("#wrapper").width(), 400); 18 | noStroke(); 19 | 20 | // set position of the vertices (here a trapezoid) 21 | vertices[0] = new PVector(width/2-100, height/2-100); 22 | vertices[1] = new PVector(width/2+100, height/2-100); 23 | vertices[2] = new PVector(width/2+50, height/2+100); 24 | vertices[3] = new PVector(width/2-50, height/2+100); 25 | } 26 | 27 | 28 | void draw() { 29 | background(255); 30 | 31 | // update circle to mouse coordinates 32 | cx = mouseX; 33 | cy = mouseY; 34 | 35 | // check for collision 36 | // if hit, change fill color 37 | boolean hit = polyCircle(vertices, cx,cy,r); 38 | if (hit) fill(255,150,0); 39 | else fill(0,150,255); 40 | 41 | // draw the polygon using beginShape() 42 | noStroke(); 43 | beginShape(); 44 | for (PVector v : vertices) { 45 | vertex(v.x, v.y); 46 | } 47 | endShape(); 48 | 49 | // draw the circle 50 | fill(0, 150); 51 | ellipse(cx,cy, r*2,r*2); 52 | } 53 | 54 | 55 | // POLYGON/CIRCLE 56 | boolean polyCircle(PVector[] vertices, float cx, float cy, float r) { 57 | 58 | // go through each of the vertices, plus the next vertex in the list 59 | int next = 0; 60 | for (int current=0; current= lineLen-buffer && d1+d2 <= lineLen+buffer) { 154 | return true; 155 | } 156 | return false; 157 | } 158 | 159 | 160 | // POINT/CIRCLE 161 | boolean pointCircle(float px, float py, float cx, float cy, float r) { 162 | 163 | // get distance between the point and circle's center 164 | // using the Pythagorean Theorem 165 | float distX = px - cx; 166 | float distY = py - cy; 167 | float distance = sqrt( (distX*distX) + (distY*distY) ); 168 | 169 | // if the distance is less than the circle's 170 | // radius the point is inside! 171 | if (distance <= r) { 172 | return true; 173 | } 174 | return false; 175 | } 176 | 177 | 178 | // POLYGON/POINT 179 | // only needed if you're going to check if the circle is INSIDE the polygon 180 | boolean polygonPoint(PVector[] vertices, float px, float py) { 181 | boolean collision = false; 182 | 183 | // go through each of the vertices, plus the next vertex in the list 184 | int next = 0; 185 | for (int current=0; current py) != (vn.y > py)) && (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) { 199 | collision = !collision; 200 | } 201 | } 202 | return collision; 203 | } 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /Website/object_oriented_collision.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Refresh your browser for random squares!
4 | 5 |

MOVING TO OBJECT-ORIENTED COLLISION

6 | 7 |

Congrats! You've made it through a lot of collision-detection code. But these examples are meant as simple demonstrations of how the algorithms work. Combining them into bigger projects probably means moving your code to an object-oriented approach. (For an excellent introduction to object-oriented programming, see Daniel Shiffman's book Nature Of Code.)

8 | 9 |

Why? Let's say we have a circle and a bunch of rectangles (like above). We could store separate positions, sizes, and collisions for each, but that would quickly get messy. Instead, a Circle and Rectangle class will give our code a lot more power and flexibility.

10 | 11 |

Let's start with our Circle class:

12 | 13 |
class Circle {
 14 |   float x, y;    // position
 15 |   float r;       // radius
 16 | 
 17 |   Circle (float _x, float _y, float _r) {
 18 |     x = _x;
 19 |     y = _y;
 20 |     r = _r;
 21 |   }
 22 | 
 23 |   // move into mouse position
 24 |   void update() {
 25 |     x = mouseX;
 26 |     y = mouseY;
 27 |   }
 28 | 
 29 |   // draw
 30 |   void display() {
 31 |     fill(0, 150);
 32 |     noStroke();
 33 |     ellipse(x,y, r*2, r*2);
 34 |   }
 35 | }
 36 | 
37 | 38 |

Pretty straightforward. We can also make a basic Rectangle class:

39 | 40 |
class Rectangle {
 41 |   float x, y;            // position
 42 |   float w, h;            // size
 43 |   boolean hit = false;   // is it hit?
 44 | 
 45 |   Rectangle (float _x, float _y, float _w, float _h) {
 46 |     x = _x;
 47 |     y = _y;
 48 |     w = _w;
 49 |     h = _h;
 50 |   }
 51 | 
 52 |   // draw the rectangle
 53 |   // if hit, change the fill color
 54 |   void display() {
 55 |     if (hit) fill(255,150,0);
 56 |     else fill(0,150,255);
 57 |     noStroke();
 58 |     rect(x,y, w,h);
 59 |   }
 60 | }
 61 | 
62 | 63 |

Notice we have a variable for the Rectangle called hit. This way we can keep track of whether or not the circle has hit a particular rectangle and change its fill color accordingly. By default, the value is set to false.

64 | 65 |

We have just one Circle, but we create an ArrayList of Rectangle objects. To run everything, here's what our main draw() loop looks like:

66 | 67 |
void draw() {
 68 |   background(255);
 69 | 
 70 |   // go through all rectangles
 71 |   // and draw them onscreen
 72 |   for (Rectangle r : rects) {
 73 |     r.display();
 74 |   }
 75 | 
 76 |   // update circle's position and draw
 77 |   circle.update();
 78 |   circle.display();
 79 | }
 80 | 
81 | 82 |

So how do we test if the circle has hit something? Let's create a method (an internal function) of the Rectangle class called checkCollision(). We'll pass the Circle object as an argument, then do a basic Circle/Rectangle collision test.

83 | 84 |
void checkCollision(Circle c) {
 85 |   hit = circleRect(c.x,c.y,c.r, x,y,w,h);
 86 | }
 87 | 
88 | 89 |

The result of circleRect() sets hit to be true or false, which in turn changes the fill color. Now we just add the test to the draw() loop:

90 | 91 |
for (Rectangle r : rects) {
 92 |   r.checkCollision(circle);  // check for collision
 93 |   r.display();               // and draw
 94 | }
 95 | 
96 | 97 |

Pretty cool! Here's the full code:

98 | 99 |
// a single Circle object, controlled by the mouse
100 | Circle circle;
101 | 
102 | // a list of rectangles
103 | Rectangle[] rects = new Rectangle[8];
104 | 
105 | 
106 | void setup() {
107 |   size(600,400);
108 | 
109 |   // create a new Circle with 30px radius
110 |   circle = new Circle(0,0, 30);
111 | 
112 |   // generate rectangles in random locations
113 |   // but snap to grid!
114 |   for (int i=0; i<rects.length; i++) {
115 |     float x = int(random(50,width-50)/50) * 50;
116 |     float y = int(random(50,height-50)/50) * 50;
117 |     rects[i] = new Rectangle(x,y, 50,50);
118 |   }
119 | }
120 | 
121 | 
122 | void draw() {
123 |   background(255);
124 | 
125 |   // go through all rectangles...
126 |   for (Rectangle r : rects) {
127 |     r.checkCollision(circle);  // check for collision
128 |     r.display();               // and draw
129 |   }
130 | 
131 |   // update circle's position and draw
132 |   circle.update();
133 |   circle.display();
134 | }
135 | 
136 | 
137 | class Circle {
138 |   float x, y;    // position
139 |   float r;       // radius
140 | 
141 |   Circle (float _x, float _y, float _r) {
142 |     x = _x;
143 |     y = _y;
144 |     r = _r;
145 |   }
146 | 
147 |   // move into mouse position
148 |   void update() {
149 |     x = mouseX;
150 |     y = mouseY;
151 |   }
152 | 
153 |   // draw
154 |   void display() {
155 |     fill(0, 150);
156 |     noStroke();
157 |     ellipse(x,y, r*2, r*2);
158 |   }
159 | }
160 | 
161 | 
162 | class Rectangle {
163 |   float x, y;            // position
164 |   float w, h;            // size
165 |   boolean hit = false;   // is it hit?
166 | 
167 |   Rectangle (float _x, float _y, float _w, float _h) {
168 |     x = _x;
169 |     y = _y;
170 |     w = _w;
171 |     h = _h;
172 |   }
173 | 
174 |   // check for collision with the circle using the
175 |   // Circle/Rect function we made in the beginning
176 |   void checkCollision(Circle c) {
177 |     hit = circleRect(c.x,c.y,c.r, x,y,w,h);
178 |   }
179 | 
180 |   // draw the rectangle
181 |   // if hit, change the fill color
182 |   void display() {
183 |     if (hit) fill(255,150,0);
184 |     else fill(0,150,255);
185 |     noStroke();
186 |     rect(x,y, w,h);
187 |   }
188 | }
189 | 
190 | 
191 | // CIRCLE/RECTANGLE
192 | boolean circleRect(float cx, float cy, float radius, float rx, float ry, float rw, float rh) {
193 | 
194 |   // temporary variables to set edges for testing
195 |   float testX = cx;
196 |   float testY = cy;
197 | 
198 |   // which edge is closest?
199 |   if (cx < rx)         testX = rx;      // compare left edge
200 |   else if (cx > rx+rw) testX = rx+rw;   // right edge
201 |   if (cy < ry)         testY = ry;      // top edge
202 |   else if (cy > ry+rh) testY = ry+rh;   // bottom edge
203 | 
204 |   // get distance from closest edges
205 |   float distX = cx-testX;
206 |   float distY = cy-testY;
207 |   float distance = sqrt( (distX*distX) + (distY*distY) );
208 | 
209 |   // if the distance is less than the radius, collision!
210 |   if (distance <= radius) {
211 |     return true;
212 |   }
213 |   return false;
214 | }
215 | 
216 | 217 |

Note that our code is a bit long with all the classes, so the actual Processing file is broken up into separate tabs. This would be a good idea for projects that require several collision functions. You could name the tab CollisionFunctions and keep all the code there.

218 | 219 |

You can see another, more complex example of object-oriented collision in the Introduction. It uses a class for circles, rectangles, and lines.

220 | 221 | 222 | -------------------------------------------------------------------------------- /Website/line-circle.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

LINE/CIRCLE

4 | 5 |

To check if a circle is hitting a line, we use code from previous examples — a practice that we'll use through the rest of the book. The resulting math behind this gets a little hairy, but we'll simplify the harder parts.

6 | 7 |

First, let's test if either of the ends of the line are inside the circle. This is likely to happen if the line is much smaller than the circle. To do this, we can use Point/Circle from the beginning of the book. If either end is inside, return true immediately and skip the rest.

8 | 9 |
boolean inside1 = pointCircle(x1,y1, cx,cy,r);
 10 | boolean inside2 = pointCircle(x2,y2, cx,cy,r);
 11 | if (inside1 || inside2) return true;
 12 | 
13 | 14 |

Next, we need to get closest point on the line. To start, let's get the length of the line using the Pythagorean Theorem:

15 | 16 |
float distX = x1 - x2;
 17 | float distY = y1 - y2;
 18 | float len = sqrt( (distX*distX) + (distY*distY) );
 19 | 
20 | 21 |

Then, we get a value we're calling dot. If you've done vector math before, this is the same as doing the dot product of two vectors. If this isn't familiar, no worry! Consider this step a lot of math you can be glad not to have to solve by hand:

22 | 23 |
float dot = ( ((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) ) / pow(len,2);
 24 | 
25 | 26 |

Finally, we can use this equation to find the closest point on the line:

27 | 28 |
float closestX = x1 + (dot * (x2-x1));
 29 | float closestY = y1 + (dot * (y2-y1));
 30 | 
31 | 32 |

However, this returns a point anywhere on the line as it extends to infinity in both directions. In other words, it could give us a point off the end of the line! So let's check if that closest point is actually on the line using the Line/Point algorithm we just made. This is the first of many times we'll nest previous functions when working on more complex collisions.

33 | 34 |

If the point is on the line, we can keep going. If not, we can immediately return false, since that means the closest point is off one of the ends:

35 | 36 |
boolean onSegment = linePoint(x1,y1,x2,y2, closestX,closestY);
 37 | if (!onSegment) return false;
 38 | 
39 | 40 |

Finally, we get the distance from the circle to the closest point on the line, once again using the Pythagorean Theorem:

41 | 42 |
distX = closestX - cx;
 43 | distY = closestY - cy;
 44 | float distance = sqrt( (distX*distX) + (distY*distY) );
 45 | 
46 | 47 |

If that distance is less than the radius, we have a collision (same as Point/Circle).

48 | 49 |
if (distance <= r) {
 50 |     return true;
 51 | }
 52 | return false;
 53 | 
54 | 55 |

Here's a full example putting everything together. Notice that we have three functions at the bottom: the one we just built and two previous functions.

56 | 57 |
float cx = 0;      // circle position (set by mouse)
 58 | float cy = 0;
 59 | float r =  30;     // circle radius
 60 | 
 61 | float x1 = 100;    // coordinates of line
 62 | float y1 = 300;
 63 | float x2 = 500;
 64 | float y2 = 100;
 65 | 
 66 | 
 67 | void setup() {
 68 |   size(600,400);
 69 | 
 70 |   strokeWeight(5);    // make it a little easier to see
 71 | }
 72 | 
 73 | 
 74 | void draw() {
 75 |   background(255);
 76 | 
 77 |   // update circle to mouse position
 78 |   cx = mouseX;
 79 |   cy = mouseY;
 80 | 
 81 |   // check for collision
 82 |   // if hit, change line's stroke color
 83 |   boolean hit = lineCircle(x1,y1, x2,y2, cx,cy,r);
 84 |   if (hit) stroke(255,150,0, 150);
 85 |   else stroke(0,150,255, 150);
 86 |   line(x1,y1, x2,y2);
 87 | 
 88 |   // draw the circle
 89 |   fill(0,150,255, 150);
 90 |   noStroke();
 91 |   ellipse(cx,cy, r*2,r*2);
 92 | }
 93 | 
 94 | 
 95 | // LINE/CIRCLE
 96 | boolean lineCircle(float x1, float y1, float x2, float y2, float cx, float cy, float r) {
 97 | 
 98 |   // is either end INSIDE the circle?
 99 |   // if so, return true immediately
100 |   boolean inside1 = pointCircle(x1,y1, cx,cy,r);
101 |   boolean inside2 = pointCircle(x2,y2, cx,cy,r);
102 |   if (inside1 || inside2) return true;
103 | 
104 |   // get length of the line
105 |   float distX = x1 - x2;
106 |   float distY = y1 - y2;
107 |   float len = sqrt( (distX*distX) + (distY*distY) );
108 | 
109 |   // get dot product of the line and circle
110 |   float dot = ( ((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) ) / pow(len,2);
111 | 
112 |   // find the closest point on the line
113 |   float closestX = x1 + (dot * (x2-x1));
114 |   float closestY = y1 + (dot * (y2-y1));
115 | 
116 |   // is this point actually on the line segment?
117 |   // if so keep going, but if not, return false
118 |   boolean onSegment = linePoint(x1,y1,x2,y2, closestX,closestY);
119 |   if (!onSegment) return false;
120 | 
121 |   // optionally, draw a circle at the closest
122 |   // point on the line
123 |   fill(255,0,0);
124 |   noStroke();
125 |   ellipse(closestX, closestY, 20, 20);
126 | 
127 |   // get distance to closest point
128 |   distX = closestX - cx;
129 |   distY = closestY - cy;
130 |   float distance = sqrt( (distX*distX) + (distY*distY) );
131 | 
132 |   if (distance <= r) {
133 |     return true;
134 |   }
135 |   return false;
136 | }
137 | 
138 | 
139 | // POINT/CIRCLE
140 | boolean pointCircle(float px, float py, float cx, float cy, float r) {
141 | 
142 |   // get distance between the point and circle's center
143 |   // using the Pythagorean Theorem
144 |   float distX = px - cx;
145 |   float distY = py - cy;
146 |   float distance = sqrt( (distX*distX) + (distY*distY) );
147 | 
148 |   // if the distance is less than the circle's
149 |   // radius the point is inside!
150 |   if (distance <= r) {
151 |     return true;
152 |   }
153 |   return false;
154 | }
155 | 
156 | 
157 | // LINE/POINT
158 | boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) {
159 | 
160 |   // get distance from the point to the two ends of the line
161 |   float d1 = dist(px,py, x1,y1);
162 |   float d2 = dist(px,py, x2,y2);
163 | 
164 |   // get the length of the line
165 |   float lineLen = dist(x1,y1, x2,y2);
166 | 
167 |   // since floats are so minutely accurate, add
168 |   // a little buffer zone that will give collision
169 |   float buffer = 0.1;    // higher # = less accurate
170 | 
171 |   // if the two distances are equal to the line's
172 |   // length, the point is on the line!
173 |   // note we use the buffer here to give a range,
174 |   // rather than one #
175 |   if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) {
176 |     return true;
177 |   }
178 |   return false;
179 | }
180 | 
181 | 182 |

Math using lines can benefit from some of the built-in functionality of the PVector class. If you haven't used PVectors before, it may be worth some time to get familiar with them. The Processing website has a good tutorial. Daniel Shiffman's excellent Nature of Code book deals with vectors quite a bit and is a very friendly introduction. We'll cover PVectors a little bit when we start working with polygons, if you want a very short introduction.

183 | 184 |

This example was based on code by Philip Nicoletti. This CodeGuru post inclues a lot more discussion of how this algorithm works and the math behind it, if you're so inclined.

185 | 186 | 187 | -------------------------------------------------------------------------------- /Website/poly-point.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

POLYGON/POINT

4 | 5 |

Circle and rectangle collisions are great, and oftentimes simplifying the collision of complex shapes using bounding boxes and circles makes sense. But there are applications when we want more accuracy. Fortunately, most of the remaining examples use ideas we've already covered, even if how we apply them gets more complicated.

6 | 7 |

In this first example, we'll check if a point is inside a complex polygon. We define our polygon using a set of X/Y points called vertices. To store these points, we'll use an array of PVector objects. PVectors simply store X/Y (or X/Y/Z) coordinates. This makes storing points a little easier, and Processing gives us some fancy math for PVectors that would be tricky otherwise. If you haven't used PVectors before, at least read the first part of this tutorial on the Processing site before continuing.

8 | 9 |

First, we create an array of four PVectors, one for each corner of our polygon:

10 | 11 |
PVector[] vertices = new PVector[4];
 12 | 
13 | 14 |

Then, we set their X/Y positions. Here we're drawing a distorted trapezoid (like above), but you could make more complicated shapes, or even randomize the points!

15 | 16 |
vertices[0] = new PVector(200,100);     // set X/Y position
 17 | vertices[1] = new PVector(400,130);
 18 | vertices[2] = new PVector(350,300);
 19 | vertices[3] = new PVector(250,300);
 20 | 
21 | 22 |

To check for collision, we're going to use a separate boolean variable. This will be inside our function later, so if this gets confusing, jump to the full example at the bottom.

23 | 24 |
boolean collision = false;
 25 | 
26 | 27 |

Then we need to go through the vertices one-by-one. To do this we use a for loop with the variable current. But also want the next vertex in the list so we can form a line (a side of the polygon). To do this, we use a second variable called next. Here's what the loop looks like:

28 | 29 |
int next = 0;
 30 | for (int current=0; current<vertices.length; current++) {
 31 | 
 32 |     // get next vertex in list
 33 |     // if we've hit the end, wrap around to 0
 34 |     next = current+1;
 35 |     if (next == vertices.length) next = 0;
 36 | 
 37 | }
 38 | 
39 | 40 |

Then we can use current and next to retrieve the PVectors from our array:

41 | 42 |
PVector vc = vertices[current];    // c for "current"
 43 | PVector vn = vertices[next];       // n for "next"
 44 | 
45 | 46 |

Now for the if statement. We can access the X/Y coordinates of each vertex using the syntax vc.x and vc.y. This statement is pretty tricky, so here's the whole thing, then we'll break it down into its parts:

47 | 48 |
if ( ((vc.y > py) != (vn.y > py)) && (px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x) ) {
 49 |   collision = !collision;
 50 | }
 51 | 
52 | 53 |

There are two tests happening here. The first checks if the point is between the two vertices in the Y direction:

54 | 55 |
(vc.y >= py && vn.y < py) || (vc.y < py && vn.y >= py)
 56 | 
57 | 58 |

We test if the point is either above vc.y and below vn.y, or below vc.y and above vn.y. Here's what this looks like visually:

59 | 60 |

Diagram of a point above/below the Y coordinates of a polygon

61 | 62 |

Note: There's a fancier, more concise way of writing this if statement, if you prefer:

63 | 64 |
(vc.y > py) != (vn.y > py)
 65 | 
66 | 67 |

That's a little confusing: it does the same test, but only evaluates true if both tests are not the same as each other!

68 | 69 |

Next up is a more complicated test. This is based on the Jordan Curve Theorem, which is pretty intense math so we'll skip explaining it. (If you understand how this algorithm works, please do let me know!)

70 | 71 |
px < (vn.x-vc.x) * (py-vc.y) / (vn.y-vc.y) + vc.x
 72 | 
73 | 74 |

If both checks are true, we switch collision to its opposite value. This is different than our previous tests, where we set the collision to simply true or false. After we've gone through all the vertices, whatever the final state of collision is is the result.

75 | 76 |
// set collision to the opposite of its current state
 77 | collision = !collision;
 78 | 
79 | 80 |

Here's a full example with everything together:

81 | 82 |
float px = 0;    // point position
 83 | float py = 0;
 84 | 
 85 | // array of PVectors, one for each vertex in the polygon
 86 | PVector[] vertices = new PVector[4];
 87 | 
 88 | 
 89 | void setup() {
 90 |   size(600,400);
 91 |   noCursor();
 92 | 
 93 |   strokeWeight(5);  // make the point easier to see
 94 | 
 95 |   // set position of the vertices
 96 |   // here we draw a distorted trapezoid, but
 97 |   // you could make much more complex shapes
 98 |   // or even randomize the points!
 99 |   vertices[0] = new PVector(200,100);
100 |   vertices[1] = new PVector(400,130);
101 |   vertices[2] = new PVector(350,300);
102 |   vertices[3] = new PVector(250,300);
103 | }
104 | 
105 | 
106 | void draw() {
107 |   background(255);
108 | 
109 |   // update point to mouse coordinates
110 |   px = mouseX;
111 |   py = mouseY;
112 | 
113 |   // check for collision
114 |   // if hit, change fill color
115 |   boolean hit = polyPoint(vertices, px,py);
116 |   if (hit) fill(255,150,0);
117 |   else fill(0,150,255);
118 | 
119 |   // draw the polygon using beginShape()
120 |   noStroke();
121 |   beginShape();
122 |   for (PVector v : vertices) {
123 |     vertex(v.x, v.y);
124 |   }
125 |   endShape();
126 | 
127 |   // draw the point
128 |   stroke(0, 150);
129 |   point(px,py);
130 | }
131 | 
132 | 
133 | // POLYGON/POINT
134 | boolean polyPoint(PVector[] vertices, float px, float py) {
135 |   boolean collision = false;
136 | 
137 |   // go through each of the vertices, plus
138 |   // the next vertex in the list
139 |   int next = 0;
140 |   for (int current=0; current<vertices.length; current++) {
141 | 
142 |     // get next vertex in list
143 |     // if we've hit the end, wrap around to 0
144 |     next = current+1;
145 |     if (next == vertices.length) next = 0;
146 | 
147 |     // get the PVectors at our current position
148 |     // this makes our if statement a little cleaner
149 |     PVector vc = vertices[current];    // c for "current"
150 |     PVector vn = vertices[next];       // n for "next"
151 | 
152 |     // compare position, flip 'collision' variable
153 |     // back and forth
154 |     if (((vc.y >= py && vn.y < py) || (vc.y < py && vn.y >= py)) &&
155 |          (px < (vn.x-vc.x)*(py-vc.y) / (vn.y-vc.y)+vc.x)) {
156 |             collision = !collision;
157 |     }
158 |   }
159 |   return collision;
160 | }
161 | 
162 | 163 |

This function is designed to take any number of vertices, so it can handle very complex shapes! However, the more vertices you check, the slower the function will be. If you wanted to do this in a full game, even just a few of these tests on complex shapes could slow your game to a crawl. Balance the need for accuracy with speed: whatever feels intuitive is probably the right way to go.

164 | 165 |

This example is based on this answer by nirg and Pranav from StackOverflow.

166 | 167 | 168 | -------------------------------------------------------------------------------- /Website/css/stylesheet.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | 4 | /* CSS RESET */ 5 | /* http://meyerweb.com/eric/tools/css/reset/ */ 6 | a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}table{border-collapse:collapse;border-spacing:0} 7 | 8 | 9 | /* KEEP SIZES THE SAME, EVEN WITH BORDERS/PADDING */ 10 | *, *:after, *:before { 11 | -webkit-box-sizing: border-box; 12 | -moz-box-sizing: border-box; 13 | box-sizing: border-box; 14 | } 15 | 16 | 17 | /* BASIC SETUP */ 18 | html, body { 19 | background: white; 20 | 21 | /* fonts */ 22 | font-family: 'Lora', serif; 23 | font-size: 20px; 24 | color: black; 25 | } 26 | 27 | /* HEADLINES */ 28 | h1, h2, h3, h4, h5, h6 { 29 | font-family: 'Raleway', 'Helvetica', 'Arial', sans-serif; 30 | font-weight: 600; 31 | text-align: center; 32 | } 33 | h1 { 34 | font-size: 2em; 35 | line-height: 1.15em; 36 | margin: 0em 0 0.2em 0; 37 | text-align: center; 38 | } 39 | h2 { 40 | font-size: 1.6em; 41 | line-height: 1.15em; 42 | margin-top: 1.1em; 43 | margin-bottom: 0.3em; 44 | } 45 | h3 { 46 | font-size: 1.3em; 47 | margin: 1.5em 0 0.3em 0; 48 | } 49 | h4 { 50 | font-size: 1.3em; 51 | } 52 | img + h4 { 53 | margin-top: 1.3em; 54 | } 55 | 56 | /* re-apply superscript formatting */ 57 | sup { 58 | vertical-align: super; 59 | font-size: smaller; 60 | } 61 | 62 | /* PARAGRAPHS */ 63 | p, figcaption { 64 | font-size: 1em; 65 | line-height: 1.4em; 66 | padding-bottom: 1.5em; 67 | } 68 | h4 + p { 69 | margin-top: 1.6em; 70 | } 71 | strong { 72 | font-weight: 600; 73 | } 74 | em { 75 | font-style: italic; 76 | font-size: 1.01em; 77 | } 78 | 79 | figcaption { 80 | font-style: italic; 81 | text-align: center; 82 | } 83 | 84 | /* LISTS */ 85 | ul, ol { 86 | font-size: 1em; 87 | line-height: 1.4em; 88 | } 89 | ul { 90 | margin: -1.3em 0 1.3em 0; 91 | } 92 | ul li { 93 | padding-left: 1.3em; 94 | text-indent: -1.3em; 95 | } 96 | ul li:before { 97 | padding-right: 0.8em; 98 | content: '+'; 99 | } 100 | h3 + ul { 101 | margin-top: 0em; 102 | } 103 | 104 | ol { 105 | list-style-type: decimal; 106 | margin-left: 1em; 107 | } 108 | ol li { 109 | padding-left: 0.3em; 110 | margin-bottom: 0.3em; 111 | } 112 | 113 | /* LINKS */ 114 | a, a:link, a:visited { 115 | color: black; 116 | font-weight: 600; 117 | text-decoration: none; 118 | background-color: transparent; 119 | } 120 | a:active, a:hover { 121 | color: black; 122 | background-color: rgb(255,255,0); 123 | } 124 | a:hover .fa, .license a:hover { 125 | color: rgb(255,150,0); 126 | background-color: transparent !important; 127 | } 128 | .url-link { 129 | word-wrap: break-word; /* break up long URLs shown onscreen */ 130 | -ms-word-break: break-all; 131 | word-break: break-all; 132 | word-break: break-word; 133 | } 134 | 135 | /* CODE */ 136 | pre, code, .prettyprint { 137 | font-family: Consolas, Menlo, Courier, monospace; 138 | line-height: 1.1em; 139 | 140 | /* wrap code */ 141 | white-space: pre-wrap; /* css-3 */ 142 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ 143 | white-space: -pre-wrap; /* Opera 4-6 */ 144 | white-space: -o-pre-wrap; /* Opera 7 */ 145 | word-wrap: break-word; /* Internet Explorer 5.5+ */ 146 | } 147 | 148 | /* large code blocks */ 149 | .prettyprint { 150 | font-size: 0.8em; 151 | background: rgb(245,245,245); 152 | border: 1px solid rgb(200,200,200) !important; 153 | padding: 1.2em !important; 154 | margin-bottom: 2em; 155 | } 156 | 157 | /* inline code */ 158 | code { 159 | background: rgb(245,245,245); 160 | padding: 0.2em 0; 161 | font-size: 85%; 162 | color: rgb(70,70,70); 163 | } 164 | .prettyprint .com { 165 | color: rgb(100,100,100); /* comments */ 166 | } 167 | code .kwd, code .tag, code .atn { 168 | /*font-weight: bold;*/ 169 | } 170 | 171 | /* IMAGES */ 172 | img { 173 | width: 100%; 174 | height: auto; 175 | border: 1px solid rgb(200,200,200); 176 | } 177 | 178 | /* LAYOUT ELEMENTS */ 179 | #wrapper { 180 | width: 100%; 181 | padding: 1em; 182 | margin-top: -1em; 183 | } 184 | 185 | #tableOfContents { 186 | text-align: center; 187 | margin-bottom: 100px; 188 | } 189 | #tableOfContents ul { 190 | list-style: none; 191 | font-size: 1.1em; 192 | } 193 | 194 | /* undo other list formatting */ 195 | #tableOfContents ul li { 196 | padding: 0; 197 | text-indent: 0; 198 | } 199 | #tableOfContents ul li:before { 200 | content: none; 201 | } 202 | #tableOfContents li a, 203 | #tableOfContents li a:link, 204 | #tableOfContents li a:visited, 205 | #tableOfContents li a:active, 206 | #tableOfContents li a:hover { 207 | font-weight: normal !important; 208 | } 209 | 210 | /* HEADER */ 211 | header { 212 | position: relative; 213 | width: 100%; 214 | margin: 0 auto 0 auto; 215 | padding: 1em 0.5em 0 0.5em; 216 | } 217 | #prev { 218 | width: 10%; 219 | float: left; 220 | } 221 | #title { 222 | width: 80%; 223 | float: left; 224 | text-align: center; 225 | } 226 | #next { 227 | width: 10%; 228 | float: right; 229 | text-align: right; 230 | } 231 | #next:after { 232 | clear: both; 233 | } 234 | 235 | /* next page link in footer */ 236 | .nextPage { 237 | text-align: center; 238 | background-color: rgb(245,245,245); 239 | border: 1px solid rgb(200,200,200); 240 | padding: 1em; 241 | margin: 1.6em 0 1.3em 0; 242 | text-align: center; 243 | } 244 | .nextPage:hover { 245 | background-color: rgb(255,255,0); 246 | } 247 | 248 | 249 | /* FOOTER */ 250 | footer { 251 | margin: 30px 0 60px 0; 252 | text-align: center; 253 | } 254 | #license { 255 | width: 88px; 256 | height: 31px; 257 | border: none; 258 | } 259 | 260 | /* INTERACTIVE DEMO */ 261 | canvas { 262 | display: block; 263 | width: 100%; 264 | 265 | border: 1px solid rgb(200,200,200); 266 | margin-bottom: 1.5em; 267 | } 268 | #Introduction { 269 | margin-top: 1em !important; 270 | } 271 | 272 | 273 | /* UPDATE CALLOUT BOX */ 274 | .callout { 275 | background-color: rgb(217,239,255); 276 | border: 1px solid rgb(200,200,200); 277 | padding: 1em; 278 | margin-bottom: 1.3em; 279 | text-align: center; 280 | visibility: visible !important; /* ios hack? */ 281 | } 282 | 283 | 284 | /* UTILITY */ 285 | .clear { 286 | clear: both; 287 | } 288 | 289 | 290 | /* SMALL SCREENS */ 291 | @media only screen and (min-width: 800px) { 292 | #wrapper { 293 | width: 600px; 294 | font-size: 19px; 295 | margin: 4em auto 60px auto; 296 | padding: 0; 297 | } 298 | 299 | /* slightly larger headline font */ 300 | h1 { 301 | font-size: 4em; 302 | } 303 | h2 { 304 | font-size: 2em; 305 | } 306 | h3 { 307 | font-size: 1.8em; 308 | } 309 | 310 | .prettyprint { 311 | font-size: 1em; 312 | } 313 | 314 | ol li { 315 | padding-left: 1.3em; 316 | margin-bottom: 1.3em; 317 | } 318 | 319 | header { 320 | font-size: 1em; 321 | position: fixed; 322 | width: 600px; 323 | height: 3.5em; 324 | top: 0; 325 | margin: 0 auto 0 auto; 326 | padding: 1em 0.5em 0 0.5em; 327 | background: rgba(255,255,255, 0.9); 328 | } 329 | } 330 | 331 | 332 | 333 | 334 | 335 | 336 | --------------------------------------------------------------------------------