Experiment 2: Collision Detection
Sections in this Article:
Circle To Circle Collision Detection
Following on from detecting when a circle is entirely within another, an extension to this method is to determine when two circles collide, e.g. when the begin to overlap. Examing the image below:
The explanation is as follows:
- It can be stated that when the sum of radius, r1 and r2 of cirlces c1 and c2 are less than the distance H between the centre points of cirlces c1 and c2, then the circles are overlapped or collided, or simply put:
- Circles c1 and c2 are overlapped when:
r1 + r2 < H
- H can again be calculated using Pythagoras theorem H2=a2+b2 with the right angled triangle being defined as sides a and b, where a = x1-x2 and b = y1-y2
- Which finally gives the following:
r1 + r2 < ((x1-x2)2 + (y1+y2)2)0.5
To see a live example of this in action, mouse over our touch (or in this example, drag your finger over) the image below to position the smaller circle. When the smaller circle overlaps or collides within the larger circle, the small circle will change colour to red.
Code - Circle in Circle
The full code for the Circle in Circle demonstration is listed below.
1(function() { 2 // create svg element in div element with name "demo" 3 var host = document.getElementById("demo"); 4 5 var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 6 svg.setAttribute("width","100%"); 7 svg.setAttribute("height","400px"); 8 svg.setAttribute("style", "background-color: silver;"); 9 host.appendChild(svg); 10 11 // create objects to keep track of where the circles are... 12 13 // add circle c1 14 var circle1 = { 15 "id": "circle1", 16 "x": 200, 17 "y": 200, 18 "r": 100, 19 "fill": "green" 20 }; 21 22 // create an SVG circle based on the definition of circle1 23 var c1 = createCircle(circle1, svg); 24 25 // add circle c2 26 var circle2 = { 27 "id": "circle2", 28 "x": 30, 29 "y": 30, 30 "r": 40, 31 "fill": "yellow" 32 }; 33 34 // create an SVG circle based on the definition of circle2 35 var c2 = createCircle(circle2, svg); 36 37 // add mouse move handler to svg 38 svg.addEventListener('mousemove',function(e) { 39 var loc = cursorPoint(e); 40 c2.setAttribute("cx", loc.x); 41 c2.setAttribute("cy", loc.y); 42 43 circle2.x = loc.x; 44 circle2.y = loc.y; 45 46 if( isCircleCollided(circle1, circle2)) { 47 c2.setAttribute("fill", "red"); 48 } else { 49 c2.setAttribute("fill", circle2.fill); 50 } 51 },false); 52 53 // on touch handler (for mobile devices) 54 svg.addEventListener('touchmove',function(e) { 55 var touchItem = e.touches[0]; 56 var loc = { 57 x: touchItem.clientX, 58 y: touchItem.clientY 59 } 60 61 //var loc = cursorPoint(e); 62 c2.setAttribute("cx", loc.x); 63 c2.setAttribute("cy", loc.y); 64 65 circle2.x = loc.x; 66 circle2.y = loc.y; 67 68 if( isCircleCollided(circle1, circle2)) { 69 c2.setAttribute("fill", "red"); 70 } else { 71 c2.setAttribute("fill", circle2.fill); 72 } 73 },false); 74 75 // need to translate the position to the SVG coordinate space 76 var svgPoint = svg.createSVGPoint(); 77 function cursorPoint(evt){ 78 svgPoint.x = evt.clientX; 79 svgPoint.y = evt.clientY; 80 return svgPoint.matrixTransform(svg.getScreenCTM().inverse()); 81 } 82 83 // here's where the maths is applied... 84 function isCircleCollided(parent, child) { 85 // triangle sides 86 var a = child.x - parent.x; 87 var b = child.y - parent.y; 88 89 // apply Pythagoras... 90 var h_sqr = Math.pow(a,2) + Math.pow(b,2); 91 92 // now the detection part (suqaring the radius' as more efficent than square roots!) 93 // see https://scicomp.stackexchange.com/questions/2168/what-is-the-computational-cost-of-sqrtx-in-standard-libraries 94 return h_sqr < Math.pow((parent.r + child.r), 2); 95 } 96 97 function createCircle(circle, svg) { 98 var element = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 99 element.setAttribute("id", circle.id); 100 element.setAttribute("cx", circle.x); 101 element.setAttribute("cy", circle.y); 102 element.setAttribute("r", circle.r); 103 element.setAttribute("fill",circle.fill); 104 element.setAttribute("fill-opacity","80%"); 105 svg.appendChild(element); 106 return element; 107 } 108})(); 109 110 111
Walk Through
If the code above isn't self-explanatory, here's some additional notes to guide through what's going on. Most of the code presented is more around getting the demo working and not the actual detection mechanism, which is only really a few lines of code!
- Line 1 Definition of a self-invoking function to get everything up and running when the JavaScript begins execution.
- Line 3 Locate the
demo
element in the main document - this is where the svg for the demo will be rendered. execution. - Lines 5-9 Create an SVG element and add it to the Web Component.
- Lines 13-20 Create an object literal for the main, larger circle.
- Line 23 Call to a helper function to create the circle that was defined on lines 13-20 and add it to the SVG.
- Lines 25-32 Create an object literal to represent the smaller circle that will be positioned on user input.
- Line 35 Call to a helper function to create the circle that was defined on lines 25-32 and add it to the SVG.
- Lines 38-51 Add an event listener to the SVG to track the mouse move position over the SVG. The positon of circle c2 will be adjusted to the mouse position and when positioned, a call to a helper method to detect if the circles C1 and C2 are collides. If they are, the smaller circle is filled red, if not, the smaller circle is returned to its original colour.
- Lines 54-73 An almost repeat of lines 38-51 [this could benefit from some refactoring] to position the smaller circle based on touch input; used to make the demonstration work on devices without a mouse, e.g. mobile phones!
- Lines 76-81 Helper method to translate the coordinate space in the browser, to the coordinate system of the SVG. This basically means converting the cursor position to the corresponding position on the SVG.
- Lines 84-95 This is simply a function that implements the collision detection algorithm.
Continue on reading to Conclusions...