From 9bd56da62da801e410a7a476efa9104f82ac2f7a Mon Sep 17 00:00:00 2001 From: "Elf M. Sternberg" Date: Fri, 22 Nov 2024 10:43:26 -0800 Subject: [PATCH] Added the axis code. --- src/sat.ts | 184 ++++++++++++++++++++++------------------------------- 1 file changed, 76 insertions(+), 108 deletions(-) diff --git a/src/sat.ts b/src/sat.ts index 9d6b03a..cdd8320 100644 --- a/src/sat.ts +++ b/src/sat.ts @@ -1,21 +1,21 @@ // 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 @@ -32,112 +32,80 @@ class Vector { this.x = x; this.y = y; } - - add: (v2: Vector) { + + add(v2: Vector) { return new Vector(this.x + v2.x, this.y + v2.y); } - - - - add: function (v1, v2) { - return { - x: v1.x + v2.x, - y: v1.y + v2.y, - }; - }, - scalar: function (v, s) { - return { - x: v.x * s, - y: v.y * s, - }; - }, - dot: function (v1, v2) { - return v1.x * v2.x + v1.y * v2.y; - }, - magnitude2: function (v) { - var x, y; - x = v.x; - y = v.y; - return x * x + y * y; - }, - magnitude: function (v) { - return Math.sqrt(Math.vector.magnitude2(v)); - }, - normalize: function (v) { - var mag; - mag = Math.vector.magnitude(v); - return { - x: v.x / mag, - y: v.y / mag, - }; - }, - leftNormal: function (v) { - return { - x: -v.y, - y: v.x, - }; - }, -}; - this.colliding = function (shape1, shape2) { - var axes, axes1, axes2, axis, genAxes, genProjection, j, len, proj1, proj2; - genAxes = function (shape) { - var axis, i, j, ref, results; - if (shape.length < 3) { - throw "Cannot handle non-polygons"; - } - axis = function (shape, pi) { - var edge, p1, p2; - p1 = shape[pi]; - p2 = shape[pi === shape.length - 1 ? 0 : pi + 1]; - edge = { - x: p1.x - p2.x, - y: p1.y - p2.y, - }; - return Math.vector.normalize(Math.vector.leftNormal(edge)); - }; - results = []; - for (i = j = 0, ref = shape.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) { - results.push(axis(shape, i)); - } - return results; - }; - genProjection = function (shape, axis) { - var i, j, max, min, p, ref; - min = Math.vector.dot(axis, shape[0]); - max = min; - for (i = j = 1, ref = shape.length; 1 <= ref ? j < ref : j > ref; i = 1 <= ref ? ++j : --j) { - p = Math.vector.dot(axis, shape[i]); - if (p < min) { - min = p; - } - if (p > max) { - max = p; - } - } - return { - min: min, - max: max, - }; - }; - axes1 = genAxes(shape1); - axes2 = genAxes(shape2); - axes = axes1.concat(axes2); - for (j = 0, len = axes.length; j < len; j++) { - axis = axes[j]; - proj1 = genProjection(shape1, axis); - proj2 = genProjection(shape2, axis); - if ( - !( - (proj1.min >= proj2.min && proj1.min <= proj2.max) || - (proj1.max >= proj2.min && proj1.max <= proj2.max) || - (proj2.min >= proj1.min && proj2.min <= proj1.max) || - (proj2.max >= proj1.min && proj2.max <= proj1.max) - ) - ) { - return false; - } + scalar(s: number) { + return new Vector(this.x * s, this.y * s); + } + + dot(v2: Vector) { + return this.x * v2.x + this.y * v2.y; + } + + magnitude2() { + return this.x * this.x + this.y * this.y; + } + + magnitude() { + return Math.sqrt(this.magnitude2()); + } + + normal() { + const mag = this.magnitude(); + return new Vector(this.x / mag, this.y / mag); + } + + leftNormal() { + return new Vector(-1 * this.y, this.x); + } +} + +const vec = (x: number, y: number) => new Vector(x, y); + +type Shape = Vector[]; + +type Projection = { min: number; max: number }; + +function colliding(shape1: Shape, shape2: Shape) { + const genAxes = (shape: Shape) => { + if (shape.length < 3) { + throw new Error("A Shape must be a polygon."); } - return true; + + const axis = (idx: number) => { + const p1 = shape[idx]; + const p2 = shape[idx === shape.length - 1 ? 0 : idx + 1]; + return vec(p1.x - p2.x, p1.y - p2.y) + .normal() + .leftNormal(); + }; + + return shape.map((_: Vector, idx: number) => axis(idx)); }; -}).call(this); + + const genProjection = (shape: Shape, axis: Vector) => + shape.reduce( + ({ min, max }: Projection, v: Vector) => { + const p = axis.dot(v); + return { min: p < min ? p : min, max: p > max ? p : max }; + }, + { min: axis.dot(shape[0]), max: axis.dot(shape[0]) } + ); + + const axes = [...genAxes(shape1), ...genAxes(shape2)]; + + // The logic here may be wrong. I may have to invert the return on the whole expression. + return axes.some((axis) => { + const proj1 = genProjection(shape1, axis); + const proj2 = genProjection(shape2, axis); + return !( + (proj1.min >= proj2.min && proj1.min <= proj2.max) || + (proj1.max >= proj2.min && proj1.max <= proj2.max) || + (proj2.min >= proj1.min && proj2.min <= proj1.max) || + (proj2.max >= proj1.min && proj2.max <= proj1.max) + ); + }); +}