123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.Rectangle = void 0;
- const util_1 = require("./util");
- const angle_1 = require("./angle");
- const line_1 = require("./line");
- const point_1 = require("./point");
- const geometry_1 = require("./geometry");
- class Rectangle extends geometry_1.Geometry {
- get left() {
- return this.x;
- }
- get top() {
- return this.y;
- }
- get right() {
- return this.x + this.width;
- }
- get bottom() {
- return this.y + this.height;
- }
- get origin() {
- return new point_1.Point(this.x, this.y);
- }
- get topLeft() {
- return new point_1.Point(this.x, this.y);
- }
- get topCenter() {
- return new point_1.Point(this.x + this.width / 2, this.y);
- }
- get topRight() {
- return new point_1.Point(this.x + this.width, this.y);
- }
- get center() {
- return new point_1.Point(this.x + this.width / 2, this.y + this.height / 2);
- }
- get bottomLeft() {
- return new point_1.Point(this.x, this.y + this.height);
- }
- get bottomCenter() {
- return new point_1.Point(this.x + this.width / 2, this.y + this.height);
- }
- get bottomRight() {
- return new point_1.Point(this.x + this.width, this.y + this.height);
- }
- get corner() {
- return new point_1.Point(this.x + this.width, this.y + this.height);
- }
- get rightMiddle() {
- return new point_1.Point(this.x + this.width, this.y + this.height / 2);
- }
- get leftMiddle() {
- return new point_1.Point(this.x, this.y + this.height / 2);
- }
- get topLine() {
- return new line_1.Line(this.topLeft, this.topRight);
- }
- get rightLine() {
- return new line_1.Line(this.topRight, this.bottomRight);
- }
- get bottomLine() {
- return new line_1.Line(this.bottomLeft, this.bottomRight);
- }
- get leftLine() {
- return new line_1.Line(this.topLeft, this.bottomLeft);
- }
- constructor(x, y, width, height) {
- super();
- this.x = x == null ? 0 : x;
- this.y = y == null ? 0 : y;
- this.width = width == null ? 0 : width;
- this.height = height == null ? 0 : height;
- }
- getOrigin() {
- return this.origin;
- }
- getTopLeft() {
- return this.topLeft;
- }
- getTopCenter() {
- return this.topCenter;
- }
- getTopRight() {
- return this.topRight;
- }
- getCenter() {
- return this.center;
- }
- getCenterX() {
- return this.x + this.width / 2;
- }
- getCenterY() {
- return this.y + this.height / 2;
- }
- getBottomLeft() {
- return this.bottomLeft;
- }
- getBottomCenter() {
- return this.bottomCenter;
- }
- getBottomRight() {
- return this.bottomRight;
- }
- getCorner() {
- return this.corner;
- }
- getRightMiddle() {
- return this.rightMiddle;
- }
- getLeftMiddle() {
- return this.leftMiddle;
- }
- getTopLine() {
- return this.topLine;
- }
- getRightLine() {
- return this.rightLine;
- }
- getBottomLine() {
- return this.bottomLine;
- }
- getLeftLine() {
- return this.leftLine;
- }
- /**
- * Returns a rectangle that is the bounding box of the rectangle.
- *
- * If `angle` is specified, the bounding box calculation will take into
- * account the rotation of the rectangle by angle degrees around its center.
- */
- bbox(angle) {
- if (!angle) {
- return this.clone();
- }
- const rad = angle_1.Angle.toRad(angle);
- const st = Math.abs(Math.sin(rad));
- const ct = Math.abs(Math.cos(rad));
- const w = this.width * ct + this.height * st;
- const h = this.width * st + this.height * ct;
- return new Rectangle(this.x + (this.width - w) / 2, this.y + (this.height - h) / 2, w, h);
- }
- round(precision = 0) {
- this.x = util_1.GeometryUtil.round(this.x, precision);
- this.y = util_1.GeometryUtil.round(this.y, precision);
- this.width = util_1.GeometryUtil.round(this.width, precision);
- this.height = util_1.GeometryUtil.round(this.height, precision);
- return this;
- }
- add(x, y, width, height) {
- const rect = Rectangle.create(x, y, width, height);
- const minX = Math.min(this.x, rect.x);
- const minY = Math.min(this.y, rect.y);
- const maxX = Math.max(this.x + this.width, rect.x + rect.width);
- const maxY = Math.max(this.y + this.height, rect.y + rect.height);
- this.x = minX;
- this.y = minY;
- this.width = maxX - minX;
- this.height = maxY - minY;
- return this;
- }
- update(x, y, width, height) {
- const rect = Rectangle.create(x, y, width, height);
- this.x = rect.x;
- this.y = rect.y;
- this.width = rect.width;
- this.height = rect.height;
- return this;
- }
- inflate(dx, dy) {
- const w = dx;
- const h = dy != null ? dy : dx;
- this.x -= w;
- this.y -= h;
- this.width += 2 * w;
- this.height += 2 * h;
- return this;
- }
- snapToGrid(gx, gy) {
- const origin = this.origin.snapToGrid(gx, gy);
- const corner = this.corner.snapToGrid(gx, gy);
- this.x = origin.x;
- this.y = origin.y;
- this.width = corner.x - origin.x;
- this.height = corner.y - origin.y;
- return this;
- }
- translate(tx, ty) {
- const p = point_1.Point.create(tx, ty);
- this.x += p.x;
- this.y += p.y;
- return this;
- }
- scale(sx, sy, origin = new point_1.Point()) {
- const pos = this.origin.scale(sx, sy, origin);
- this.x = pos.x;
- this.y = pos.y;
- this.width *= sx;
- this.height *= sy;
- return this;
- }
- rotate(degree, center = this.getCenter()) {
- if (degree !== 0) {
- const rad = angle_1.Angle.toRad(degree);
- const cos = Math.cos(rad);
- const sin = Math.sin(rad);
- let p1 = this.getOrigin();
- let p2 = this.getTopRight();
- let p3 = this.getBottomRight();
- let p4 = this.getBottomLeft();
- p1 = point_1.Point.rotateEx(p1, cos, sin, center);
- p2 = point_1.Point.rotateEx(p2, cos, sin, center);
- p3 = point_1.Point.rotateEx(p3, cos, sin, center);
- p4 = point_1.Point.rotateEx(p4, cos, sin, center);
- const rect = new Rectangle(p1.x, p1.y, 0, 0);
- rect.add(p2.x, p2.y, 0, 0);
- rect.add(p3.x, p3.y, 0, 0);
- rect.add(p4.x, p4.y, 0, 0);
- this.update(rect);
- }
- return this;
- }
- rotate90() {
- const t = (this.width - this.height) / 2;
- this.x += t;
- this.y -= t;
- const tmp = this.width;
- this.width = this.height;
- this.height = tmp;
- return this;
- }
- /**
- * Translates the rectangle by `rect.x` and `rect.y` and expand it by
- * `rect.width` and `rect.height`.
- */
- moveAndExpand(rect) {
- const ref = Rectangle.clone(rect);
- this.x += ref.x || 0;
- this.y += ref.y || 0;
- this.width += ref.width || 0;
- this.height += ref.height || 0;
- return this;
- }
- /**
- * Returns an object where `sx` and `sy` give the maximum scaling that can be
- * applied to the rectangle so that it would still fit into `limit`. If
- * `origin` is specified, the rectangle is scaled around it; otherwise, it is
- * scaled around its center.
- */
- getMaxScaleToFit(limit, origin = this.center) {
- const rect = Rectangle.clone(limit);
- const ox = origin.x;
- const oy = origin.y;
- // Find the maximal possible scale for all corners, so when the scale
- // is applied the point is still inside the rectangle.
- let sx1 = Infinity;
- let sx2 = Infinity;
- let sx3 = Infinity;
- let sx4 = Infinity;
- let sy1 = Infinity;
- let sy2 = Infinity;
- let sy3 = Infinity;
- let sy4 = Infinity;
- // Top Left
- const p1 = rect.topLeft;
- if (p1.x < ox) {
- sx1 = (this.x - ox) / (p1.x - ox);
- }
- if (p1.y < oy) {
- sy1 = (this.y - oy) / (p1.y - oy);
- }
- // Bottom Right
- const p2 = rect.bottomRight;
- if (p2.x > ox) {
- sx2 = (this.x + this.width - ox) / (p2.x - ox);
- }
- if (p2.y > oy) {
- sy2 = (this.y + this.height - oy) / (p2.y - oy);
- }
- // Top Right
- const p3 = rect.topRight;
- if (p3.x > ox) {
- sx3 = (this.x + this.width - ox) / (p3.x - ox);
- }
- if (p3.y < oy) {
- sy3 = (this.y - oy) / (p3.y - oy);
- }
- // Bottom Left
- const p4 = rect.bottomLeft;
- if (p4.x < ox) {
- sx4 = (this.x - ox) / (p4.x - ox);
- }
- if (p4.y > oy) {
- sy4 = (this.y + this.height - oy) / (p4.y - oy);
- }
- return {
- sx: Math.min(sx1, sx2, sx3, sx4),
- sy: Math.min(sy1, sy2, sy3, sy4),
- };
- }
- /**
- * Returns a number that specifies the maximum scaling that can be applied to
- * the rectangle along both axes so that it would still fit into `limit`. If
- * `origin` is specified, the rectangle is scaled around it; otherwise, it is
- * scaled around its center.
- */
- getMaxUniformScaleToFit(limit, origin = this.center) {
- const scale = this.getMaxScaleToFit(limit, origin);
- return Math.min(scale.sx, scale.sy);
- }
- containsPoint(x, y) {
- return util_1.GeometryUtil.containsPoint(this, point_1.Point.create(x, y));
- }
- containsRect(x, y, width, height) {
- const b = Rectangle.create(x, y, width, height);
- const x1 = this.x;
- const y1 = this.y;
- const w1 = this.width;
- const h1 = this.height;
- const x2 = b.x;
- const y2 = b.y;
- const w2 = b.width;
- const h2 = b.height;
- // one of the dimensions is 0
- if (w1 === 0 || h1 === 0 || w2 === 0 || h2 === 0) {
- return false;
- }
- return x2 >= x1 && y2 >= y1 && x2 + w2 <= x1 + w1 && y2 + h2 <= y1 + h1;
- }
- /**
- * Returns an array of the intersection points of the rectangle and the line.
- * Return `null` if no intersection exists.
- */
- intersectsWithLine(line) {
- const rectLines = [
- this.topLine,
- this.rightLine,
- this.bottomLine,
- this.leftLine,
- ];
- const points = [];
- const dedupeArr = [];
- rectLines.forEach((l) => {
- const p = line.intersectsWithLine(l);
- if (p !== null && dedupeArr.indexOf(p.toString()) < 0) {
- points.push(p);
- dedupeArr.push(p.toString());
- }
- });
- return points.length > 0 ? points : null;
- }
- /**
- * Returns the point on the boundary of the rectangle that is the intersection
- * of the rectangle with a line starting in the center the rectangle ending in
- * the point `p`.
- *
- * If `angle` is specified, the intersection will take into account the
- * rotation of the rectangle by `angle` degrees around its center.
- */
- intersectsWithLineFromCenterToPoint(p, angle) {
- const ref = point_1.Point.clone(p);
- const center = this.center;
- let result = null;
- if (angle != null && angle !== 0) {
- ref.rotate(angle, center);
- }
- const sides = [this.topLine, this.rightLine, this.bottomLine, this.leftLine];
- const connector = new line_1.Line(center, ref);
- for (let i = sides.length - 1; i >= 0; i -= 1) {
- const intersection = sides[i].intersectsWithLine(connector);
- if (intersection !== null) {
- result = intersection;
- break;
- }
- }
- if (result && angle != null && angle !== 0) {
- result.rotate(-angle, center);
- }
- return result;
- }
- intersectsWithRect(x, y, width, height) {
- const ref = Rectangle.create(x, y, width, height);
- // no intersection
- if (!this.isIntersectWithRect(ref)) {
- return null;
- }
- const myOrigin = this.origin;
- const myCorner = this.corner;
- const rOrigin = ref.origin;
- const rCorner = ref.corner;
- const xx = Math.max(myOrigin.x, rOrigin.x);
- const yy = Math.max(myOrigin.y, rOrigin.y);
- return new Rectangle(xx, yy, Math.min(myCorner.x, rCorner.x) - xx, Math.min(myCorner.y, rCorner.y) - yy);
- }
- isIntersectWithRect(x, y, width, height) {
- const ref = Rectangle.create(x, y, width, height);
- const myOrigin = this.origin;
- const myCorner = this.corner;
- const rOrigin = ref.origin;
- const rCorner = ref.corner;
- if (rCorner.x <= myOrigin.x ||
- rCorner.y <= myOrigin.y ||
- rOrigin.x >= myCorner.x ||
- rOrigin.y >= myCorner.y) {
- return false;
- }
- return true;
- }
- /**
- * Normalize the rectangle, i.e. make it so that it has non-negative
- * width and height. If width is less than `0`, the function swaps left and
- * right corners and if height is less than `0`, the top and bottom corners
- * are swapped.
- */
- normalize() {
- let newx = this.x;
- let newy = this.y;
- let newwidth = this.width;
- let newheight = this.height;
- if (this.width < 0) {
- newx = this.x + this.width;
- newwidth = -this.width;
- }
- if (this.height < 0) {
- newy = this.y + this.height;
- newheight = -this.height;
- }
- this.x = newx;
- this.y = newy;
- this.width = newwidth;
- this.height = newheight;
- return this;
- }
- /**
- * Returns a rectangle that is a union of this rectangle and rectangle `rect`.
- */
- union(rect) {
- const ref = Rectangle.clone(rect);
- const myOrigin = this.origin;
- const myCorner = this.corner;
- const rOrigin = ref.origin;
- const rCorner = ref.corner;
- const originX = Math.min(myOrigin.x, rOrigin.x);
- const originY = Math.min(myOrigin.y, rOrigin.y);
- const cornerX = Math.max(myCorner.x, rCorner.x);
- const cornerY = Math.max(myCorner.y, rCorner.y);
- return new Rectangle(originX, originY, cornerX - originX, cornerY - originY);
- }
- /**
- * Returns a string ("top", "left", "right" or "bottom") denoting the side of
- * the rectangle which is nearest to the point `p`.
- */
- getNearestSideToPoint(p) {
- const ref = point_1.Point.clone(p);
- const distLeft = ref.x - this.x;
- const distRight = this.x + this.width - ref.x;
- const distTop = ref.y - this.y;
- const distBottom = this.y + this.height - ref.y;
- let closest = distLeft;
- let side = 'left';
- if (distRight < closest) {
- closest = distRight;
- side = 'right';
- }
- if (distTop < closest) {
- closest = distTop;
- side = 'top';
- }
- if (distBottom < closest) {
- side = 'bottom';
- }
- return side;
- }
- /**
- * Returns a point on the boundary of the rectangle nearest to the point `p`.
- */
- getNearestPointToPoint(p) {
- const ref = point_1.Point.clone(p);
- if (this.containsPoint(ref)) {
- const side = this.getNearestSideToPoint(ref);
- if (side === 'left') {
- return new point_1.Point(this.x, ref.y);
- }
- if (side === 'top') {
- return new point_1.Point(ref.x, this.y);
- }
- if (side === 'right') {
- return new point_1.Point(this.x + this.width, ref.y);
- }
- if (side === 'bottom') {
- return new point_1.Point(ref.x, this.y + this.height);
- }
- }
- return ref.adhereToRect(this);
- }
- equals(rect) {
- return (rect != null &&
- rect.x === this.x &&
- rect.y === this.y &&
- rect.width === this.width &&
- rect.height === this.height);
- }
- clone() {
- return new Rectangle(this.x, this.y, this.width, this.height);
- }
- toJSON() {
- return { x: this.x, y: this.y, width: this.width, height: this.height };
- }
- serialize() {
- return `${this.x} ${this.y} ${this.width} ${this.height}`;
- }
- }
- exports.Rectangle = Rectangle;
- (function (Rectangle) {
- function isRectangle(instance) {
- return instance != null && instance instanceof Rectangle;
- }
- Rectangle.isRectangle = isRectangle;
- })(Rectangle = exports.Rectangle || (exports.Rectangle = {}));
- (function (Rectangle) {
- function isRectangleLike(o) {
- return (o != null &&
- typeof o === 'object' &&
- typeof o.x === 'number' &&
- typeof o.y === 'number' &&
- typeof o.width === 'number' &&
- typeof o.height === 'number');
- }
- Rectangle.isRectangleLike = isRectangleLike;
- })(Rectangle = exports.Rectangle || (exports.Rectangle = {}));
- (function (Rectangle) {
- function create(x, y, width, height) {
- if (x == null || typeof x === 'number') {
- return new Rectangle(x, y, width, height);
- }
- return clone(x);
- }
- Rectangle.create = create;
- function clone(rect) {
- if (Rectangle.isRectangle(rect)) {
- return rect.clone();
- }
- if (Array.isArray(rect)) {
- return new Rectangle(rect[0], rect[1], rect[2], rect[3]);
- }
- return new Rectangle(rect.x, rect.y, rect.width, rect.height);
- }
- Rectangle.clone = clone;
- /**
- * Returns a new rectangle from the given ellipse.
- */
- function fromEllipse(ellipse) {
- return new Rectangle(ellipse.x - ellipse.a, ellipse.y - ellipse.b, 2 * ellipse.a, 2 * ellipse.b);
- }
- Rectangle.fromEllipse = fromEllipse;
- function fromSize(size) {
- return new Rectangle(0, 0, size.width, size.height);
- }
- Rectangle.fromSize = fromSize;
- function fromPositionAndSize(pos, size) {
- return new Rectangle(pos.x, pos.y, size.width, size.height);
- }
- Rectangle.fromPositionAndSize = fromPositionAndSize;
- })(Rectangle = exports.Rectangle || (exports.Rectangle = {}));
- //# sourceMappingURL=rectangle.js.map
|