Pt = Point = class Point { constructor(x, y, z) { this.is3D = z != undefined this.x = x this.y = y this.z = z } equals(pt) { return this.x == pt.x && this.y == pt.y && (!this.is3D || this.z == pt.z) } isIn(arr) { return this.indexIn(arr) != -1 } indexIn(arr) { return arr.findIndex((pt) => this.equals(pt)) } lastIndexIn(arr) { return arr.findLastIndex((pt) => this.equals(pt)) } up() { return new Point(this.x, this.y - 1) } down() { return new Point(this.x, this.y + 1) } left() { return new Point(this.x - 1, this.y) } right() { return new Point(this.x + 1, this.y) } upleft() { return new Point(this.x - 1, this.y - 1) } upright() { return new Point(this.x + 1, this.y - 1) } downleft() { return new Point(this.x - 1, this.y + 1) } downright() { return new Point(this.x + 1, this.y + 1) } above() { return new Point(this.x, this.y, this.z - 1) } below() { return new Point(this.x, this.y, this.z + 1) } u() { return this.up() } d() { return this.down() } l() { return this.left() } r() { return this.right() } ul() { return this.upleft() } ur() { return this.upright() } dl() { return this.downleft() } dr() { return this.downright() } getUnfilteredAdjNeighborsIncSelf() { if (!this.is3D) { return new PointArray( this.u(), this.l(), this.copy(), this.r(), this.d()) } else { return new PointArray( this.above(), this.u(), this.l(), this.copy(), this.r(), this.d(), this.below()) } } getUnfilteredWingNeighborsIncSelf() { if (!this.is3D) { console.error("Can't get wing neighbors of 2D point") } return new PointArray( this.u().above(), this.l().above(), this.r().above(), this.d().above(), this.ul(), this.ur(), this.copy(), this.dl(), this.dr(), this.u().below(), this.l().below(), this.r().below(), this.d().below()) } getUnfilteredDiagNeighborsIncSelf() { if (!this.is3D) { return new PointArray( this.ul(), this.ur(), this.copy(), this.dl(), this.dr()) } else { return new PointArray( this.ul().above(), this.ur().above(), this.dl().above(), this.dr().above(), this.copy(), this.ul().below(), this.ur().below(), this.dl().below(), this.dr().below()) } } getUnfilteredAllNeighborsIncSelf() { if (!this.is3D) { return new PointArray( this.ul(), this.u(), this.ur(), this.l(), this.copy(), this.r(), this.dl(), this.d(), this.dr()) } else { return new PointArray( this.ul().above(), this.u().above(), this.ur().above(), this.l().above(), this.above(), this.r().above(), this.dl().above(), this.d().above(), this.dr().above(), this.ul(), this.u(), this.ur(), this.l(), this.copy(), this.r(), this.dl(), this.d(), this.dr(), this.ul().below(), this.u().below(), this.ur().below(), this.l().below(), this.below(), this.r().below(), this.dl().below(), this.d().below(), this.dr().below()) } } getUnfilteredAdjNeighbors() { return this.getUnfilteredAdjNeighborsIncSelf().filter((pt) => !this.equals(pt)) } getUnfilteredDiagNeighbors() { return this.getUnfilteredDiagNeighborsIncSelf().filter((pt) => !this.equals(pt)) } getUnfilteredAllNeighbors() { return this.getUnfilteredAllNeighborsIncSelf().filter((pt) => !this.equals(pt)) } add(pt) { return new Point(this.x + pt.x, this.y + pt.y, this.is3D ? this.z + pt.z : undefined) } addMut(pt) { this.x += pt.x this.y += pt.y if (this.is3D) { this.z += pt.z } return this } sub(pt) { return new Point(this.x - pt.x, this.y - pt.y, this.is3D ? this.z - pt.z : undefined) } subMut(pt) { this.x -= pt.x this.y -= pt.y if (this.is3D) { this.z -= pt.z } return this } mult(n) { return new Point(this.x * n, this.y * n, this.is3D ? this.z * n : undefined) } multMut(n) { this.x *= n this.y *= n if (this.is3D) { this.z *= n } return this } neg(n) { return this.mult(-1) } negMut(n) { return this.multMut(-1) } squaredMag() { return this.x * this.x + this.y * this.y + (this.is3D ? this.z * this.z : 0) } mag() { return Math.sqrt(this.squaredMag()) } squaredDist(pt) { return this.sub(pt).squaredMag() } dist(pt) { return this.sub(pt).mag() } readingOrderCompare(pt) { if (this.is3D && this.z < pt.z) { return -1 } else if (this.is3D && this.z > pt.z) { return 1 } else if (this.y < pt.y) { return -1 } else if (this.y > pt.y) { return 1 } else if (this.x < pt.x) { return -1 } else if (this.x > pt.x) { return 1 } else { return 0 } } copy() { return new Point(this.x, this.y, this.z) } toString() { return this.x + "," + this.y + (this.is3D ? "," + this.z : "") } } Point.NONE = new Point(null, null) P = function P(...args) { return new Point(...args) }