I’m putting together a short demo on two things:
- Finding the angle between two line segments and direction (right or left) as you travel counterclockwise around a shape
- Using newer ecmascript syntax (e.g. .map, classes, const, let (no vars) backticks in console logging)
I’d like some feedback on whether the approach I’ve chosen for finding angles and directions is sound mathematically (I dislike demonstrating something that isn’t decent logic). I know I could do without the Vector usage but am trying to explain to my audience relative vs. absolute coordinates, particularly when using atan2.
And feedback on the syntax/methods. Please bear in mind that since this is a demo, the code is verbose in output. (I purposely use console.warn for each angle just to make them easier to spot, so no comments on that are necessary :-)).
For this set of points, starting from the upper left-hand corner and working counterclockwise, the expected output is 45°, 45°, -90°, 45°, 45°, -90°.
There are two classes, a constant to convert between degrees/radians and a small dataset:
class Vector { constructor(x, y, z) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } } class Point { constructor(x, y) { this.x = x || 0; this.y = y || 0; } } const degRad = 180 / Math.PI; const testCoordSet = [ [-5, 15], [- 5, 0], [0, 5], [5, 0], [5, 15], [0, 10] ];
Then:
// Generate our test set of points const testPts = testCoordSet.map(coord => new Point(coord[0], coord[1])); console.log(testPts); // using map testPts.map((pt, index, array) => { console.log(`\r\n$ {index}`); let pointA, pointB, pointC; // Determine which points will make up left, center and right points if (index === 0) { // If at beginning, left is the last point in the array pointA = array[array.length - 1]; pointB = array[0]; pointC = array[index + 1]; } else if (index === array.length - 1) { // If at end, right is the first point in the array pointA = array[index - 1]; pointB = array[index]; pointC = array[0]; } else { pointA = array[index - 1]; pointB = array[index]; pointC = array[index + 1]; } const vectorAB = new Vector(pointA.x - pointB.x, pointA.y - pointB.y); const vectorCB = new Vector(pointC.x - pointB.x, pointC.y - pointB.y); console.log("vectorAB:", vectorAB); console.log("vectorCB:", vectorCB); // Stay in radians until the final result is calculated const absAngleABRad = Math.atan2(vectorAB.y, vectorAB.x); console.log(`absAngleABRad: $ {absAngleABRad}`); const absAngleCBRad = Math.atan2(vectorCB.y, vectorCB.x); console.log(`absAngleCBRad: $ {absAngleCBRad}`); const angularDiffRad = absAngleABRad - absAngleCBRad; console.log(`angularDiffRad: $ {angularDiffRad}`); const angleDeg = angularDiffRad * degRad; console.warn(`angleDeg: $ {angleDeg}`); });
For comparison, I’ve thrown in a traditional for-next loop:
// old school for (let i = 0; i < testPts.length; i++) { console.log(`\r\n$ {i}`); let pointA, pointB, pointC; // Determine which points will make up left, center and right points if (i === 0) { // If at beginning, left is the last point in the array pointA = testPts[testPts.length - 1]; pointB = testPts[0]; pointC = testPts[i + 1]; } else if (i === testPts.length - 1) { // If at end, right is the first point in the array pointA = testPts[i - 1]; pointB = testPts[i]; pointC = testPts[0]; } else { pointA = testPts[i - 1]; pointB = testPts[i]; pointC = testPts[i + 1]; } const vectorAB = new Vector(pointA.x - pointB.x, pointA.y - pointB.y); const vectorCB = new Vector(pointC.x - pointB.x, pointC.y - pointB.y); console.log("vectorAB:", vectorAB); console.log("vectorCB:", vectorCB); // Stay in radians until the final result is calculated const absAngleABRad = Math.atan2(vectorAB.y, vectorAB.x); console.log(`absAngleABRad: $ {absAngleABRad}`); const absAngleCBRad = Math.atan2(vectorCB.y, vectorCB.x); console.log(`absAngleCBRad: $ {absAngleCBRad}`); const angularDiffRad = absAngleABRad - absAngleCBRad; console.log(`angularDiffRad: $ {angularDiffRad}`); const angleDeg = angularDiffRad * degRad; console.warn(`angleDeg: $ {angleDeg}`); }
The complete code may be found here: https://gist.github.com/stonetip/4ba313fa72c95bee6b59ceb5a64a6108