UNPKG

@eclipse-scout/core

Version:
236 lines (214 loc) 6.66 kB
/* * Copyright (c) 2010, 2024 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import {Dimension, Insets, Point} from '../index'; export class Rectangle { x: number; y: number; width: number; height: number; /** * If no parameters are passed, all members are initialized with 0. */ constructor(xOrRectangle?: number | Rectangle, y?: number, width?: number, height?: number) { if (xOrRectangle instanceof Rectangle) { this.x = xOrRectangle.x; this.y = xOrRectangle.y; this.width = xOrRectangle.width; this.height = xOrRectangle.height; } else { this.x = xOrRectangle || 0; this.y = y || 0; this.width = width || 0; this.height = height || 0; } } toString(): string { return 'Rectangle[x=' + this.x + ' y=' + this.y + ' width=' + this.width + ' height=' + this.height + ']'; } equals(o: Rectangle): boolean { if (!o) { return false; } return (this.x === o.x && this.y === o.y && this.width === o.width && this.height === o.height); } clone(): Rectangle { return new Rectangle(this.x, this.y, this.width, this.height); } center(): Point { return new Point(this.x + this.width / 2, this.y + this.height / 2); } right(): number { return this.x + this.width; } bottom(): number { return this.y + this.height; } contains(xOrPoint: number | Point, y?: number): boolean { let point = new Point(xOrPoint, y); return point.y >= this.y && point.y < this.bottom() && point.x >= this.x && point.x < this.right(); } /** * Tests whether the specified rectangle intersects this rectangle. * This means the two rectangles share at least one internal point. * * @param r the rectangle to test against * @returns true if the specified rectangle intersects this one */ intersects(r: Rectangle): boolean { if (!r) { return false; } return r.width > 0 && r.height > 0 && this.width > 0 && this.height > 0 && r.x < this.right() && r.right() > this.x && r.y < this.bottom() && r.bottom() > this.y; } /** * Determines the intersection with the given rectangle. * * @returns the rectangle that represents the intersection, or an empty rectangle, if the rectangles don't intersect. */ intersect(rect: Rectangle): Rectangle { if (!this.intersects(rect)) { return new Rectangle(); } let newRect = new Rectangle(Math.max(this.x, rect.x), Math.max(this.y, rect.y)); newRect.width = Math.min(this.right(), rect.right()) - newRect.x; newRect.height = Math.min(this.bottom(), rect.bottom()) - newRect.y; return newRect; } /** * Subtracts the given insets from the rectangle. */ subtract(insets: Insets): Rectangle { return new Rectangle( this.x + insets.left, this.y + insets.top, this.width - insets.horizontal(), this.height - insets.vertical()); } /** * Subtracts the insets only from the dimension properties (width and height) */ subtractFromDimension(insets: Insets): Rectangle { return new Rectangle( this.x, this.y, this.width - insets.horizontal(), this.height - insets.vertical()); } /** * Adds the given insets to the rectangle. */ add(insets: Insets): Rectangle { return new Rectangle( this.x - insets.left, this.y - insets.top, this.width + insets.horizontal(), this.height + insets.vertical()); } /** * Moves the rectangle the given distance. * * @param dxOrPoint the distance to move the rectangle along the x-axis, or a point object containing the diff on the x and y-axis. * @param dy the distance to move the rectangle along the y-axis. */ translate(dxOrPoint: number | Point, dy?: number): Rectangle { let point = this.point().add(new Point(dxOrPoint, dy)); return this.moveTo(point); } /** * Moves the rectangle to the new point. * * @param xOrPoint the new position on the x-axis or a point object containing the new position. * @param y the new position on the y-axis. */ moveTo(xOrPoint: number | Point, y?: number): Rectangle { let point = new Point(xOrPoint, y); return new Rectangle( point.x, point.y, this.width, this.height); } /** * @returns property x and y of this instance as new {@link Point} instance. */ point(): Point { return new Point(this.x, this.y); } /** * @returns property width and height of this instance as new {@link Dimension} instance. */ dimension(): Dimension { return new Dimension(this.width, this.height); } union(r: Rectangle): Rectangle { let tx2 = this.width; let ty2 = this.height; if (tx2 < 0 || ty2 < 0) { // This rectangle has negative dimensions... // If r has non-negative dimensions then it is the answer. // If r is non-existent (has a negative dimension), then both // are non-existent, and we can return any non-existent rectangle // as an answer. Thus, returning r meets that criterion. // Either way, r is our answer. return new Rectangle(r.x, r.y, r.width, r.height); } let rx2 = r.width; let ry2 = r.height; if (rx2 < 0 || ry2 < 0) { return new Rectangle(this.x, this.y, this.width, this.height); } let tx1 = this.x; let ty1 = this.y; tx2 += tx1; ty2 += ty1; let rx1 = r.x; let ry1 = r.y; rx2 += rx1; ry2 += ry1; if (tx1 > rx1) { tx1 = rx1; } if (ty1 > ry1) { ty1 = ry1; } if (tx2 < rx2) { tx2 = rx2; } if (ty2 < ry2) { ty2 = ry2; } tx2 -= tx1; ty2 -= ty1; // tx2,ty2 will never underflow since both original rectangles // were already proven to be non-empty // they might overflow, though... if (tx2 > Number.MAX_VALUE) { tx2 = Number.MAX_VALUE; } if (ty2 > Number.MAX_VALUE) { ty2 = Number.MAX_VALUE; } return new Rectangle(tx1, ty1, tx2, ty2); } /** * Creates a copy and calls Math.floor() on each property. */ floor(): Rectangle { return new Rectangle(Math.floor(this.x), Math.floor(this.y), Math.floor(this.width), Math.floor(this.height)); } /** * Creates a copy and calls Math.ceil() on each property. */ ceil(): Rectangle { return new Rectangle(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.width), Math.ceil(this.height)); } }