diff --git a/src/math/Vec2.js b/src/math/Vec2.js index ce922a82..de8a87b0 100644 --- a/src/math/Vec2.js +++ b/src/math/Vec2.js @@ -474,7 +474,7 @@ export class Vec2 { } /** - * Computes the cross product between this and another vector and changes the value of this vector. + * Computes the cross product between this and another vector. * * [Cross product visualisation](https://www.geogebra.org/m/psMTGDgc) (in 3d) * @@ -525,6 +525,36 @@ export class Vec2 { return this.set(other); } + /** + * Rotates the vector (around 0, 0) and modifies it. + * Positive angles rotate the vector clockwise. (Positive x is right, positive y is down) + * + * For instance, say you have a `Vec2<1, 1>`: + * ```none + * o + * \ + * \ + * V + * ``` + * then calling `rotate(Math.PI * 0.5)` results in a `Vec2<-1, 1>`: + * ```none + * o + * / + * / + * V + * ``` + * @param {number} angle Angle in radians. + */ + rotate(angle) { + const cos = Math.cos(angle); + const sin = Math.sin(angle); + + return this.set( + this._x * cos - this._y * sin, + this._x * sin + this._y * cos, + ); + } + /** * @returns {[x: number, y: number]} */ diff --git a/test/unit/src/math/Vec2.test.js b/test/unit/src/math/Vec2.test.js index dca7ea4d..2f20cd80 100644 --- a/test/unit/src/math/Vec2.test.js +++ b/test/unit/src/math/Vec2.test.js @@ -736,6 +736,48 @@ Deno.test({ }, }); +Deno.test({ + name: "rotate()", + fn() { + const tests = [ + { vec: [0, 1], angle: 0, result: [0, 1] }, + { vec: [0, 1], angle: Math.PI, result: [0, -1] }, + { vec: [1, 0], angle: Math.PI * 0.5, result: [0, 1] }, + { vec: [1, 1], angle: Math.PI * 0.5, result: [-1, 1] }, + { vec: [0, 0], angle: 100, result: [0, 0] }, + { vec: [12, 34], angle: Math.PI * 2, result: [12, 34] }, + { vec: [12, 34], angle: 0.1, result: [8.54571, 35.02814] }, + ]; + + for (const { vec, angle, result } of tests) { + const actual = new Vec2(vec).rotate(angle); + + assertVecAlmostEquals(actual, result); + } + }, +}); + +Deno.test({ + name: "rotate() angle matches the direction of clockwiseAngleTo", + fn() { + const tests = [ + [0.123, -0.456], + [0.5, 2], + [-10, 20], + [-10, -20], + ]; + + for (const test of tests) { + const vec = new Vec2(test); + const newVec = new Vec2(0, 1); + const angle = newVec.clockwiseAngleTo(vec); + newVec.rotate(angle); + newVec.magnitude = vec.magnitude; + assertVecAlmostEquals(newVec, test); + } + }, +}); + // ======== onChange Callbacks ======== Deno.test({ @@ -894,6 +936,17 @@ Deno.test({ fireResults.push(-1); expectedResult.push(-1); + vec.set(0, 1); + expectedResult.push(0x11); + + vec.rotate(0); + // rotating 0 radians shouldn't fire the callback + fireResults.push(-1); + expectedResult.push(-1); + + vec.rotate(0.5); + expectedResult.push(0x11); + assertEquals(fireResults, expectedResult); }, });