UNPKG

@aurigma/design-atoms-model

Version:

Design Atoms is a part of Customer's Canvas SDK which allows for manipulating individual design elements through your code.

314 lines 13.4 kB
import { RectangleF } from "./RectangleF"; import { PointF } from "./PointF"; import { EqualsOfFloatNumbers, normalizeAngle } from "./Common"; import { Transform } from "./Transform"; import { Matrix } from "./Matrix"; import { ArgumentException } from "../Exception"; export class RotatedRectangleF { constructor(centerX, centerY, width, height, angle) { this.centerX = (centerX) ? centerX : 0; this.centerY = (centerY) ? centerY : 0; this.width = (width || width === 0) ? width : 2; this.height = (height || height === 0) ? height : 2; this.angle = (angle) ? angle : 0; } getSimplifiedObject() { return { centerX: this.centerX, centerY: this.centerY, width: this.width, height: this.height, angle: this.angle }; } clone() { return new RotatedRectangleF(this.centerX, this.centerY, this.width, this.height, this.angle); } /** * Gets the bounds of this rotated rectangle taking into account its rotation. */ get bounds() { var center = new PointF(this.centerX, this.centerY); var w = this.width; var h = this.height; var points = [ new PointF(-w / 2, -h / 2), new PointF(-w / 2, h / 2), new PointF(w / 2, -h / 2), new PointF(w / 2, h / 2) ]; var minx = 0, miny = 0, maxx = 0, maxy = 0; for (var i = 0; i < 4; i++) { points[i].rotate(this.angle); if (points[i].x < minx || i === 0) minx = points[i].x; if (points[i].y < miny || i === 0) miny = points[i].y; if (points[i].x > maxx || i === 0) maxx = points[i].x; if (points[i].y > maxy || i === 0) maxy = points[i].y; } return new RectangleF(minx + center.x, miny + center.y, maxx - minx, maxy - miny); } equals(rect, tolerance) { return rect != null && EqualsOfFloatNumbers(this.centerX, rect.centerX, tolerance) && EqualsOfFloatNumbers(this.centerY, rect.centerY, tolerance) && EqualsOfFloatNumbers(this.width, rect.width, tolerance) && EqualsOfFloatNumbers(this.height, rect.height, tolerance) && EqualsOfFloatNumbers(this.angle, rect.angle, tolerance); } getUpperLeftCorner() { var p = new PointF(-this.width / 2, -this.height / 2); p = p.rotate(this.angle); p = p.translate(this.centerX, this.centerY); return p; } getUpperCenterPoint() { let p = new PointF(0, -this.height / 2); p = p.rotate(this.angle); p = p.translate(this.centerX, this.centerY); return p; } getLeftCenterPoint() { let p = new PointF(-this.width / 2, 0); p = p.rotate(this.angle); p = p.translate(this.centerX, this.centerY); return p; } getRightCenterPoint() { let p = new PointF(this.width / 2, 0); p = p.rotate(this.angle); p = p.translate(this.centerX, this.centerY); return p; } getUpperRightCorner() { var p = new PointF(this.width / 2, -this.height / 2); p = p.rotate(this.angle); p = p.translate(this.centerX, this.centerY); return p; } getBottomRightCorner() { var p = new PointF(this.width / 2, this.height / 2); p = p.rotate(this.angle); p = p.translate(this.centerX, this.centerY); return p; } getBottomLeftCorner() { var p = new PointF(-this.width / 2, this.height / 2); p = p.rotate(this.angle); p = p.translate(this.centerX, this.centerY); return p; } getBottomCenterPoint() { var p = new PointF(0, this.height / 2); p = p.rotate(this.angle); p = p.translate(this.centerX, this.centerY); return p; } _checkProjectionIntersection(points0, points1) { var p0 = this._getProjections(points0); var p1 = this._getProjections(points1); return p0.left <= p1.right && p0.right >= p1.left && p0.top <= p1.bottom && p0.bottom >= p1.top; } _getProjections(points) { let x0, x1, y0, y1; x0 = x1 = points[0].x; y0 = y1 = points[0].y; for (var i = 1; i < points.length; i++) { x0 = Math.min(x0, points[i].x); x1 = Math.max(x1, points[i].x); y0 = Math.min(y0, points[i].y); y1 = Math.max(y1, points[i].y); } return { left: x0, right: x1, top: y0, bottom: y1 }; } intersectsWith(rect) { var thisRect = this.clone(); var otherRect = rect.clone(); if (!EqualsOfFloatNumbers(this.angle, 0)) { const angle = thisRect.angle; thisRect.angle = 0; const center = otherRect.center; center.rotateAt(-angle, thisRect.center); otherRect.center = center; otherRect.angle -= angle; } var thisEdges = [thisRect.getUpperLeftCorner(), thisRect.getUpperRightCorner(), thisRect.getBottomRightCorner(), thisRect.getBottomLeftCorner()]; var otherEdges = [otherRect.getUpperLeftCorner(), otherRect.getUpperRightCorner(), otherRect.getBottomRightCorner(), otherRect.getBottomLeftCorner()]; var firstProjectionIntersection = this._checkProjectionIntersection(thisEdges, otherEdges); var secondProjectionIntersection = true; if (firstProjectionIntersection && !EqualsOfFloatNumbers(this.angle, rect.angle)) { thisRect = this.clone(); otherRect = rect.clone(); const angle = otherRect.angle; otherRect.angle = 0; const center = thisRect.center; center.rotateAt(-angle, otherRect.center); thisRect.center = center; thisRect.angle -= angle; thisEdges = [thisRect.getUpperLeftCorner(), thisRect.getUpperRightCorner(), thisRect.getBottomRightCorner(), thisRect.getBottomLeftCorner()]; otherEdges = [otherRect.getUpperLeftCorner(), otherRect.getUpperRightCorner(), otherRect.getBottomRightCorner(), otherRect.getBottomLeftCorner()]; secondProjectionIntersection = this._checkProjectionIntersection(thisEdges, otherEdges); } return firstProjectionIntersection && secondProjectionIntersection; } get location() { return this.getUpperLeftCorner(); } set location(location) { var p = this.getUpperLeftCorner(); this.centerX = this.centerX + (location.x - p.x); this.centerY = this.centerY + (location.y - p.y); } get center() { return new PointF(this.centerX, this.centerY); } set center(center) { this.centerX = center.x; this.centerY = center.y; } rotateAt(angle, center) { var rectCenter = this.center; rectCenter.rotateAt(angle, center); this.center = rectCenter; this.angle += angle; return this; } translate(x, y) { this.centerX += x; this.centerY += y; } setTransform(transform) { if (transform == null || transform.equals(new Transform())) return; this.width *= transform.scaleX; this.height *= transform.scaleY; this.angle += transform.angle; this.centerX += transform.translateX; this.centerY += transform.translateY; } getTransform(rectangle) { if (rectangle == null || this.equals(rectangle)) return new Transform(); var scaleX = rectangle.width === 0 ? 0 : this.width / rectangle.width; var scaleY = rectangle.height === 0 ? 0 : this.height / rectangle.height; var angle = this.angle - rectangle.angle; var center = rectangle.center; var translateX = this.centerX - center.x; var translateY = this.centerY - center.y; return new Transform(scaleX, scaleY, translateX, translateY, angle); } transform(transform, center) { this.rotateAt(transform.angle, center); transform.setAngle(0); this.setTransform(transform); } toString() { var r = [this.centerX, this.centerY, this.width, this.height, this.angle]; return r.join(","); } toRectangleF() { return new RectangleF(this.centerX - this.width / 2, this.centerY - this.height / 2, this.width, this.height); } contains(pt) { var rectangle = this.clone(); rectangle.angle = 0; var rotatedPoint = pt.clone(); rotatedPoint.rotateAt(-this.angle, this.center); return rectangle.bounds.contains(rotatedPoint); } //Starting from the top-left corner clockwise. getCornerByIndex(index) { switch (index) { case 0: return this.getUpperLeftCorner(); case 1: return this.getUpperRightCorner(); case 2: return this.getBottomRightCorner(); case 3: return this.getBottomLeftCorner(); default: return null; } } transformByMatrix(matrix, deltaAngle = null) { const newPoints = [ this.center.clone(), this.getUpperLeftCorner(), this.getUpperRightCorner(), this.getBottomRightCorner(), this.getBottomLeftCorner() ].map(p => matrix.transformPoint(p)); const angle = normalizeAngle(deltaAngle != null ? this.angle + deltaAngle : this.angle); const newCenter = newPoints[0]; const rotatedPoints = newPoints.slice(1, 5).map(p => p.rotateAt(-angle, newCenter)); if (!EqualsOfFloatNumbers(rotatedPoints[0].x, rotatedPoints[3].x, 0.01) || !EqualsOfFloatNumbers(rotatedPoints[0].y, rotatedPoints[1].y, 0.01)) { throw new ArgumentException("RotatedRectangle: Scew is not supported"); } const newWidth = Math.abs(rotatedPoints[1].x - rotatedPoints[0].x); const newHeight = Math.abs(rotatedPoints[2].y - rotatedPoints[1].y); const result = new RotatedRectangleF(newCenter.x, newCenter.y, newWidth, newHeight, angle); return result; } withoutAngle() { return new RotatedRectangleF(this.centerX, this.centerY, this.width, this.height, 0); } static union(a, b) { var aClone = a.clone(); var bClone = b.clone(); var angle = aClone.angle; var center = aClone.center; aClone.angle = 0; bClone.rotateAt(-angle, center); var bounds = RectangleF.union(aClone.bounds, bClone.bounds); var newCenter = new PointF(bounds.left + bounds.width / 2, bounds.top + bounds.height / 2); var result = new RotatedRectangleF(); result.center = newCenter; var widthSign = (Math.sign(a.width) === -1 || Math.sign(b.width) === -1) ? -1 : 1; var heightSign = (Math.sign(a.height) === -1 || Math.sign(b.height) === -1) ? -1 : 1; result.width = bounds.width * widthSign; result.height = bounds.height * heightSign; result.rotateAt(angle, center); return result; } ; static intersect(a, b) { const aClone = a.clone(); const bClone = b.clone(); const angle = aClone.angle; const center = aClone.center; aClone.angle = 0; bClone.rotateAt(-angle, center); const boundsA = aClone.bounds; const boundsB = bClone.bounds; if (!boundsA.intersectsWith(boundsB)) { return null; // Нет пересечения } const intersectionBounds = RectangleF.intersect(boundsA, boundsB); const newCenter = new PointF(intersectionBounds.left + intersectionBounds.width / 2, intersectionBounds.top + intersectionBounds.height / 2); const result = new RotatedRectangleF(); result.center = newCenter; const widthSign = (Math.sign(a.width) === -1 || Math.sign(b.width) === -1) ? -1 : 1; const heightSign = (Math.sign(a.height) === -1 || Math.sign(b.height) === -1) ? -1 : 1; result.width = intersectionBounds.width * widthSign; result.height = intersectionBounds.height * heightSign; result.rotateAt(angle, center); return result; } static fromRectangleF(rectangle, angle) { return new RotatedRectangleF(rectangle.left + rectangle.width / 2, rectangle.top + rectangle.height / 2, rectangle.width, rectangle.height, typeof angle == "number" ? angle : 0); } ; static FromLTRB(left, top, right, bottom) { return RotatedRectangleF.fromRectangleF(RectangleF.FromLTRB(left, top, right, bottom)); } ; static getRectWithOffset(rectangle, offset) { if (rectangle == null) throw new ArgumentException("RotatedRectangleF.getRectWithOffset: rectangle cannot be null!"); if (offset == null || offset.isOrigin()) return rectangle.clone(); return rectangle.transformByMatrix(new Matrix().translate(offset.x, offset.y)); } } //# sourceMappingURL=RotatedRectangleF.js.map