123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.Point = void 0;
- const util_1 = require("./util");
- const angle_1 = require("./angle");
- const geometry_1 = require("./geometry");
- class Point extends geometry_1.Geometry {
- constructor(x, y) {
- super();
- this.x = x == null ? 0 : x;
- this.y = y == null ? 0 : y;
- }
- /**
- * Rounds the point to the given precision.
- */
- round(precision = 0) {
- this.x = util_1.GeometryUtil.round(this.x, precision);
- this.y = util_1.GeometryUtil.round(this.y, precision);
- return this;
- }
- add(x, y) {
- const p = Point.create(x, y);
- this.x += p.x;
- this.y += p.y;
- return this;
- }
- update(x, y) {
- const p = Point.create(x, y);
- this.x = p.x;
- this.y = p.y;
- return this;
- }
- translate(dx, dy) {
- const t = Point.create(dx, dy);
- this.x += t.x;
- this.y += t.y;
- return this;
- }
- /**
- * Rotate the point by `degree` around `center`.
- */
- rotate(degree, center) {
- const p = Point.rotate(this, degree, center);
- this.x = p.x;
- this.y = p.y;
- return this;
- }
- /**
- * Scale point by `sx` and `sy` around the given `origin`. If origin is
- * not specified, the point is scaled around `0, 0`.
- */
- scale(sx, sy, origin = new Point()) {
- const ref = Point.create(origin);
- this.x = ref.x + sx * (this.x - ref.x);
- this.y = ref.y + sy * (this.y - ref.y);
- return this;
- }
- /**
- * Chooses the point closest to this point from among `points`. If `points`
- * is an empty array, `null` is returned.
- */
- closest(points) {
- if (points.length === 1) {
- return Point.create(points[0]);
- }
- let ret = null;
- let min = Infinity;
- points.forEach((p) => {
- const dist = this.squaredDistance(p);
- if (dist < min) {
- ret = p;
- min = dist;
- }
- });
- return ret ? Point.create(ret) : null;
- }
- /**
- * Returns the distance between the point and another point `p`.
- */
- distance(p) {
- return Math.sqrt(this.squaredDistance(p));
- }
- /**
- * Returns the squared distance between the point and another point `p`.
- *
- * Useful for distance comparisons in which real distance is not necessary
- * (saves one `Math.sqrt()` operation).
- */
- squaredDistance(p) {
- const ref = Point.create(p);
- const dx = this.x - ref.x;
- const dy = this.y - ref.y;
- return dx * dx + dy * dy;
- }
- manhattanDistance(p) {
- const ref = Point.create(p);
- return Math.abs(ref.x - this.x) + Math.abs(ref.y - this.y);
- }
- /**
- * Returns the magnitude of the point vector.
- *
- * @see http://en.wikipedia.org/wiki/Magnitude_(mathematics)
- */
- magnitude() {
- return Math.sqrt(this.x * this.x + this.y * this.y) || 0.01;
- }
- /**
- * Returns the angle(in degrees) between vector from this point to `p` and
- * the x-axis.
- */
- theta(p = new Point()) {
- const ref = Point.create(p);
- const y = -(ref.y - this.y); // invert the y-axis.
- const x = ref.x - this.x;
- let rad = Math.atan2(y, x);
- // Correction for III. and IV. quadrant.
- if (rad < 0) {
- rad = 2 * Math.PI + rad;
- }
- return (180 * rad) / Math.PI;
- }
- /**
- * Returns the angle(in degrees) between vector from this point to `p1` and
- * the vector from this point to `p2`.
- *
- * The ordering of points `p1` and `p2` is important.
- *
- * The function returns a value between `0` and `180` when the angle (in the
- * direction from `p1` to `p2`) is clockwise, and a value between `180` and
- * `360` when the angle is counterclockwise.
- *
- * Returns `NaN` if either of the points `p1` and `p2` is equal with this point.
- */
- angleBetween(p1, p2) {
- if (this.equals(p1) || this.equals(p2)) {
- return NaN;
- }
- let angle = this.theta(p2) - this.theta(p1);
- if (angle < 0) {
- angle += 360;
- }
- return angle;
- }
- /**
- * Returns the angle(in degrees) between the line from `(0,0)` and this point
- * and the line from `(0,0)` to `p`.
- *
- * The function returns a value between `0` and `180` when the angle (in the
- * direction from this point to `p`) is clockwise, and a value between `180`
- * and `360` when the angle is counterclockwise. Returns `NaN` if called from
- * point `(0,0)` or if `p` is `(0,0)`.
- */
- vectorAngle(p) {
- const zero = new Point(0, 0);
- return zero.angleBetween(this, p);
- }
- /**
- * Converts rectangular to polar coordinates.
- */
- toPolar(origin) {
- this.update(Point.toPolar(this, origin));
- return this;
- }
- /**
- * Returns the change in angle(in degrees) that is the result of moving the
- * point from its previous position to its current position.
- *
- * More specifically, this function computes the angle between the line from
- * the ref point to the previous position of this point(i.e. current position
- * `-dx`, `-dy`) and the line from the `ref` point to the current position of
- * this point.
- *
- * The function returns a positive value between `0` and `180` when the angle
- * (in the direction from previous position of this point to its current
- * position) is clockwise, and a negative value between `0` and `-180` when
- * the angle is counterclockwise.
- *
- * The function returns `0` if the previous and current positions of this
- * point are the same (i.e. both `dx` and `dy` are `0`).
- */
- changeInAngle(dx, dy, ref = new Point()) {
- // Revert the translation and measure the change in angle around x-axis.
- return this.clone().translate(-dx, -dy).theta(ref) - this.theta(ref);
- }
- /**
- * If the point lies outside the rectangle `rect`, adjust the point so that
- * it becomes the nearest point on the boundary of `rect`.
- */
- adhereToRect(rect) {
- if (!util_1.GeometryUtil.containsPoint(rect, this)) {
- this.x = Math.min(Math.max(this.x, rect.x), rect.x + rect.width);
- this.y = Math.min(Math.max(this.y, rect.y), rect.y + rect.height);
- }
- return this;
- }
- /**
- * Returns the bearing(cardinal direction) between me and the given point.
- *
- * @see https://en.wikipedia.org/wiki/Cardinal_direction
- */
- bearing(p) {
- const ref = Point.create(p);
- const lat1 = angle_1.Angle.toRad(this.y);
- const lat2 = angle_1.Angle.toRad(ref.y);
- const lon1 = this.x;
- const lon2 = ref.x;
- const dLon = angle_1.Angle.toRad(lon2 - lon1);
- const y = Math.sin(dLon) * Math.cos(lat2);
- const x = Math.cos(lat1) * Math.sin(lat2) -
- Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
- const brng = angle_1.Angle.toDeg(Math.atan2(y, x));
- const bearings = ['NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N'];
- let index = brng - 22.5;
- if (index < 0) {
- index += 360;
- }
- index = parseInt((index / 45), 10);
- return bearings[index];
- }
- /**
- * Returns the cross product of the vector from me to `p1` and the vector
- * from me to `p2`.
- *
- * The left-hand rule is used because the coordinate system is left-handed.
- */
- cross(p1, p2) {
- if (p1 != null && p2 != null) {
- const a = Point.create(p1);
- const b = Point.create(p2);
- return (b.x - this.x) * (a.y - this.y) - (b.y - this.y) * (a.x - this.x);
- }
- return NaN;
- }
- /**
- * Returns the dot product of this point with given other point.
- */
- dot(p) {
- const ref = Point.create(p);
- return this.x * ref.x + this.y * ref.y;
- }
- diff(dx, dy) {
- if (typeof dx === 'number') {
- return new Point(this.x - dx, this.y - dy);
- }
- const p = Point.create(dx);
- return new Point(this.x - p.x, this.y - p.y);
- }
- /**
- * Returns an interpolation between me and point `p` for a parametert in
- * the closed interval `[0, 1]`.
- */
- lerp(p, t) {
- const ref = Point.create(p);
- return new Point((1 - t) * this.x + t * ref.x, (1 - t) * this.y + t * ref.y);
- }
- /**
- * Normalize the point vector, scale the line segment between `(0, 0)`
- * and the point in order for it to have the given length. If length is
- * not specified, it is considered to be `1`; in that case, a unit vector
- * is computed.
- */
- normalize(length = 1) {
- const scale = length / this.magnitude();
- return this.scale(scale, scale);
- }
- /**
- * Moves this point along the line starting from `ref` to this point by a
- * certain `distance`.
- */
- move(ref, distance) {
- const p = Point.create(ref);
- const rad = angle_1.Angle.toRad(p.theta(this));
- return this.translate(Math.cos(rad) * distance, -Math.sin(rad) * distance);
- }
- /**
- * Returns a point that is the reflection of me with the center of inversion
- * in `ref` point.
- */
- reflection(ref) {
- return Point.create(ref).move(this, this.distance(ref));
- }
- snapToGrid(gx, gy) {
- this.x = util_1.GeometryUtil.snapToGrid(this.x, gx);
- this.y = util_1.GeometryUtil.snapToGrid(this.y, gy == null ? gx : gy);
- return this;
- }
- equals(p) {
- const ref = Point.create(p);
- return ref != null && ref.x === this.x && ref.y === this.y;
- }
- clone() {
- return Point.clone(this);
- }
- /**
- * Returns the point as a simple JSON object. For example: `{ x: 0, y: 0 }`.
- */
- toJSON() {
- return Point.toJSON(this);
- }
- serialize() {
- return `${this.x} ${this.y}`;
- }
- }
- exports.Point = Point;
- (function (Point) {
- function isPoint(instance) {
- return instance != null && instance instanceof Point;
- }
- Point.isPoint = isPoint;
- })(Point = exports.Point || (exports.Point = {}));
- (function (Point) {
- function isPointLike(p) {
- return (p != null &&
- typeof p === 'object' &&
- typeof p.x === 'number' &&
- typeof p.y === 'number');
- }
- Point.isPointLike = isPointLike;
- function isPointData(p) {
- return (p != null &&
- Array.isArray(p) &&
- p.length === 2 &&
- typeof p[0] === 'number' &&
- typeof p[1] === 'number');
- }
- Point.isPointData = isPointData;
- })(Point = exports.Point || (exports.Point = {}));
- (function (Point) {
- function create(x, y) {
- if (x == null || typeof x === 'number') {
- return new Point(x, y);
- }
- return clone(x);
- }
- Point.create = create;
- function clone(p) {
- if (Point.isPoint(p)) {
- return new Point(p.x, p.y);
- }
- if (Array.isArray(p)) {
- return new Point(p[0], p[1]);
- }
- return new Point(p.x, p.y);
- }
- Point.clone = clone;
- function toJSON(p) {
- if (Point.isPoint(p)) {
- return { x: p.x, y: p.y };
- }
- if (Array.isArray(p)) {
- return { x: p[0], y: p[1] };
- }
- return { x: p.x, y: p.y };
- }
- Point.toJSON = toJSON;
- /**
- * Returns a new Point object from the given polar coordinates.
- * @see http://en.wikipedia.org/wiki/Polar_coordinate_system
- */
- function fromPolar(r, rad, origin = new Point()) {
- let x = Math.abs(r * Math.cos(rad));
- let y = Math.abs(r * Math.sin(rad));
- const org = clone(origin);
- const deg = angle_1.Angle.normalize(angle_1.Angle.toDeg(rad));
- if (deg < 90) {
- y = -y;
- }
- else if (deg < 180) {
- x = -x;
- y = -y;
- }
- else if (deg < 270) {
- x = -x;
- }
- return new Point(org.x + x, org.y + y);
- }
- Point.fromPolar = fromPolar;
- /**
- * Converts rectangular to polar coordinates.
- */
- function toPolar(point, origin = new Point()) {
- const p = clone(point);
- const o = clone(origin);
- const dx = p.x - o.x;
- const dy = p.y - o.y;
- return new Point(Math.sqrt(dx * dx + dy * dy), // r
- angle_1.Angle.toRad(o.theta(p)));
- }
- Point.toPolar = toPolar;
- function equals(p1, p2) {
- if (p1 === p2) {
- return true;
- }
- if (p1 != null && p2 != null) {
- return p1.x === p2.x && p1.y === p2.y;
- }
- return false;
- }
- Point.equals = equals;
- function equalPoints(p1, p2) {
- if ((p1 == null && p2 != null) ||
- (p1 != null && p2 == null) ||
- (p1 != null && p2 != null && p1.length !== p2.length)) {
- return false;
- }
- if (p1 != null && p2 != null) {
- for (let i = 0, ii = p1.length; i < ii; i += 1) {
- if (!equals(p1[i], p2[i])) {
- return false;
- }
- }
- }
- return true;
- }
- Point.equalPoints = equalPoints;
- /**
- * Returns a point with random coordinates that fall within the range
- * `[x1, x2]` and `[y1, y2]`.
- */
- function random(x1, x2, y1, y2) {
- return new Point(util_1.GeometryUtil.random(x1, x2), util_1.GeometryUtil.random(y1, y2));
- }
- Point.random = random;
- function rotate(point, angle, center) {
- const rad = angle_1.Angle.toRad(angle_1.Angle.normalize(-angle));
- const sin = Math.sin(rad);
- const cos = Math.cos(rad);
- return rotateEx(point, cos, sin, center);
- }
- Point.rotate = rotate;
- function rotateEx(point, cos, sin, center = new Point()) {
- const source = clone(point);
- const origin = clone(center);
- const dx = source.x - origin.x;
- const dy = source.y - origin.y;
- const x1 = dx * cos - dy * sin;
- const y1 = dy * cos + dx * sin;
- return new Point(x1 + origin.x, y1 + origin.y);
- }
- Point.rotateEx = rotateEx;
- })(Point = exports.Point || (exports.Point = {}));
- //# sourceMappingURL=point.js.map
|