#Copyright (c) 2012 Elf M. Sternberg # # Much of the code here I would never have understood if it hadn't # been for the patient work of Caleb Helbling # (http://www.propulsionjs.com/), as well as the Wikipedia pages for # the Separating Axis Theorem. It took me a week to wrap my head # around these ideas. # #Permission is hereby granted, free of charge, to any person obtaining a copy #of this software and associated documentation files (the "Software"), to deal #in the Software without restriction, including without limitation the rights #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #copies of the Software, and to permit persons to whom the Software is #furnished to do so, subject to the following conditions: # #The above copyright notice and this permission notice shall be included in #all copies or substantial portions of the Software. # #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN #THE SOFTWARE. Math.vector = add: (v1, v2) -> {x: (v1.x + v2.x), y: (v1.y + v2.y)} # Scale a given vector. scalar: (v, s) -> {x: (v.x * s), y: (v.y * s)} dot: (v1, v2) -> v1.x * v2.x + v1.y * v2.y magnitude2: (v) -> x = v.x y = v.y x * x + y * y magnitude: (v) -> Math.sqrt(Math.vector.magnitude2(v)) normalize: (v) -> mag = Math.vector.magnitude(v) {x: (v.x / mag), y: (v.y / mag)} leftNormal: (v) -> {x: -v.y, y: v.x} this.colliding = (shape1, shape2) -> # Return the axes of a shape. In a polygon, each potential # separating axis is the normal to each edge. For our purposes, a # "shape" is an array of points with the structure [{x: 0, y: 0}, .. ] # We assume that the final edge is from the last point back to the # first. genAxes = (shape) -> throw "Cannot handle non-polygons" if shape.length < 3 # Calculate the normal of a single pair of points in the # shape. axis = (shape, pi) -> p1 = shape[pi] p2 = shape[if pi == (shape.length - 1) then 0 else pi + 1] edge = {x: p1.x - p2.x, y: p1.y - p2.y} Math.vector.normalize(Math.vector.leftNormal(edge)) (axis(shape, i) for i in [0...shape.length]) # Calculate the extremis of the shape "above" a given axis genProjection = (shape, axis) -> min = Math.vector.dot(axis, shape[0]) max = min for i in [1...shape.length] p = Math.vector.dot(axis, shape[i]) min = p if p < min max = p if p > max {min: min, max: max} axes1 = genAxes(shape1) axes2 = genAxes(shape2) axes = axes1.concat axes2 for axis in axes proj1 = genProjection(shape1, axis) proj2 = genProjection(shape2, axis) if not ( \ (proj1.min >= proj2.min and proj1.min <= proj2.max) or \ (proj1.max >= proj2.min and proj1.max <= proj2.max) or \ (proj2.min >= proj1.min and proj2.min <= proj1.max) or \ (proj2.max >= proj1.min and proj2.max <= proj1.max)) return false return true