UNPKG

@dominicstop/utils

Version:

Yet another event emitter written in typescript.

202 lines (201 loc) 6.09 kB
import { InterpolationHelpers } from "../helpers"; import { Line } from "./Line"; import { Rect } from "./Rect"; import { Vector2D } from "./Vector2D"; export class Point { constructor(args) { this.epsilon = 1e-10; this.x = args.x; this.y = args.y; } ; get asValue() { return { x: this.x, y: this.y, }; } ; get asVector() { return new Vector2D({ dx: this.x, dy: this.y, }); } ; get magnitude() { return this.asVector.magnitude; } get normalized() { return this.asVector.normalized; } ; // MARK: - Methods // --------------- clone() { return new Point({ x: this.x, y: this.y }); } isEqualToOtherPoint(otherPoint) { return (Math.abs(this.x - otherPoint.x) < this.epsilon && Math.abs(this.y - otherPoint.y) < this.epsilon); } get isZero() { return (Math.abs(this.x) < this.epsilon && Math.abs(this.y) < this.epsilon); } ; get isNaN() { return (Number.isNaN(this.x) || Number.isNaN(this.y)); } ; toString() { return `Point(${this.x}, ${this.y})`; } createLine(otherPoint) { return new Line({ startPoint: this, endPoint: otherPoint }); } ; getDistance(otherPoint) { return Point.getDistanceBetweenTwoPoints(this, otherPoint); } ; getDelta(otherPoint) { return new Point({ x: this.x - otherPoint.x, y: this.y - otherPoint.y, }); } ; getSum(...otherPoints) { return Point.sumOfAllPoints(this, ...otherPoints); } ; getMidpointBetweenOtherPoint(otherPoint) { const line = this.createLine(otherPoint); return line.midPoint; } ; rotateRelativeToCenterPoint(args) { const angleRad = args.angle.radians; const translatedX = this.x - args.center.x; const translatedY = this.y - args.center.y; // 2d rotation matrix: // * sin wave and cos wave can be used to modulate x and y axis // * as they wobble back and forth, combined they can be used trace a circlular path // * these two equations trace a circular path as the angle increases // // cos and sin on the unit: // * the unit circle is a circle with radius 1, centered at the // origin (x: 0, y: 0). // // * for any angle theta (in radians), a point on the unit circle is: // `(x: cos(theta), y: sin(theta))`. // // * innother words, if you plot `(cos(angle), sin(angle))`, // you get points around a circle. // // * the resulting point is a direction vector (normalized vector), i.e. // a unit-length arrow pointing in the direction of angle. // // * note: for this function, we define a specific center; as such // the point is first translated to be relative to the center. // const rotatedX = translatedX * Math.cos(angleRad) - translatedY * Math.sin(angleRad); const rotatedY = translatedX * Math.sin(angleRad) + translatedY * Math.cos(angleRad); this.x = rotatedX + args.center.x; this.y = rotatedY + args.center.y; } ; rotatedRelativeToCenterPoint(args) { const copy = this.clone(); copy.rotateRelativeToCenterPoint(args); return copy; } ; // MARK: - Static Alias // -------------------- static get zero() { return new Point({ x: 0, y: 0 }); } ; // MARK: - Static Methods // ---------------------- static lerp(valueStart, valueEnd, percent) { const nextX = InterpolationHelpers.lerp(valueStart.x, valueEnd.x, percent); const nextY = InterpolationHelpers.lerp(valueStart.y, valueEnd.y, percent); return new Point({ x: nextX, y: nextY }); } ; static getBoundingBoxForPoints(points) { const valuesX = points.map(point => point.x); const valuesY = points.map(point => point.y); const sortedValuesX = valuesX.sort((a, b) => a - b); const sortedValuesY = valuesY.sort((a, b) => a - b); const minX = sortedValuesX[0] ?? 0; const maxX = sortedValuesX[valuesX.length - 1] ?? 0; const minY = sortedValuesY[0] ?? 0; const maxY = sortedValuesY[valuesY.length - 1] ?? 0; return new Rect({ mode: 'corners', minX, maxX, minY, maxY, }); } ; static translatePoints(args) { const boundingBox = this.getBoundingBoxForPoints(args.points); // calc the translation for the derived bounding box const translatedOrigin = new Point({ x: boundingBox.origin.x + args.dx, y: boundingBox.origin.y + args.dy }); // adj each point by translation return args.points.map(point => { const adjX = translatedOrigin.x - boundingBox.origin.x; const adjY = translatedOrigin.y - boundingBox.origin.y; return new Point({ x: point.x + adjX, y: point.y + adjY }); }); } ; static sumOfAllPoints(...points) { let sumX = 0; let sumY = 0; for (const point of points) { sumX += point.x; sumY += point.y; } ; return new Point({ x: sumX, y: sumY }); } ; static getDistanceBetweenTwoPoints(pointA, pointB) { const line = pointA.createLine(pointB); return line.distance; } ; static rotatePointsRelativeToCenter(args) { if (args.points.length === 0) return []; return args.points.map(point => point.rotatedRelativeToCenterPoint({ angle: args.rotationAmount, center: args.center })); } ; } ;