UNPKG

basicprimitives

Version:

Basic Primitives Diagrams for JavaScript - data visualization components library that implements organizational chart and multi-parent dependency diagrams, contains implementations of JavaScript Controls and PDF rendering plugins.

492 lines (445 loc) 13.1 kB
import Point from './Point'; import Vector from './Vector'; import Interval from './Interval'; import { PlacementType } from '../../enums' /** * @class Rect * @classdesc Class describes the width, height and location of rectangle. * * @param {Rect} arg0 Rectangle to clone. * * @param {Point} arg0 The top left point. * @param {Point} arg1 The bottom right point. * * @param {number} arg0 The x coordinate of top left corner. * @param {number} arg1 The y coordinate of top left corner. * @param {number} arg2 Rect width. * @param {number} arg3 Rect height. */ export default function Rect(arg0, arg1, arg2, arg3) { /** * The location x coordinate * @type {number} */ this.x = null; /** * The location y coordinate * @type {number} */ this.y = null; /** * The width of rectangle. * @type {number} */ this.width = null; /** * The height of rectangle. * @type {number} */ this.height = null; /** * Reference to context object associated with this rectangle. * @type {object} */ this.context = null; switch (arguments.length) { case 1: this.x = arg0.x; this.y = arg0.y; this.width = arg0.width; this.height = arg0.height; break; case 2: this.x = Math.min(arg0.x, arg1.x); this.y = Math.min(arg0.y, arg1.y); this.width = Math.abs(arg1.x - arg0.x); this.height = Math.abs(arg1.y - arg0.y); break; case 4: this.x = arg0; this.y = arg1; this.width = arg2; this.height = arg3; break; default: break; } }; /** * Left * * @returns {number} Returns x coordinate of the rectangle */ Rect.prototype.left = function () { return this.x; }; /** * Top * * @returns {number} Returns y coordinate of the rectangle */ Rect.prototype.top = function () { return this.y; }; /** * Right * * @returns {number} Returns x-axis coordinate of the right side of the rectangle */ Rect.prototype.right = function () { return this.x + this.width; }; /** * Bottom * * @returns {number} Returns y-axis coordinate of the bottom side of the rectangle */ Rect.prototype.bottom = function () { return this.y + this.height; }; /** * Vertical center * * @returns {number} Returns y-axis coordinate of the center point of the rectangle. */ Rect.prototype.verticalCenter = function () { return this.y + this.height / 2.0; }; /** * Horizontal center * * @returns {number} Returns x-axis coordinate of the center point of the rectangle. */ Rect.prototype.horizontalCenter = function () { return this.x + this.width / 2.0; }; /** * Center point * * @returns {Point} Returns center point of the rectangle. */ Rect.prototype.centerPoint = function () { return new Point(this.horizontalCenter(), this.verticalCenter()); }; /** * Checks if rectangle is empty. Rectangle is empty if one of its sizes is undefined or less than zero. * * @returns {boolean} Returns true if rectangle is empty. */ Rect.prototype.isEmpty = function () { return this.x === null || this.y === null || this.width === null || this.height === null || this.width < 0 || this.height < 0; }; /** * Expands rectangle boundaries by using specified value in all directions. Value can be negative. * * @param {number} arg0 The amount by which to expand or shrink the sides of the rectangle. * @param {number} arg0 Left side * @param {number} arg1 Top side * @param {number} arg2 Right side * @param {number} arg3 Bottom side */ Rect.prototype.offset = function (arg0, arg1, arg2, arg3) { switch (arguments.length) { case 1: if (arg0 !== null && typeof arg0 == "object") { this.x = this.x - arg0.left; this.y = this.y - arg0.top; this.width = this.width + arg0.left + arg0.right; this.height = this.height + arg0.top + arg0.bottom; } else { this.x = this.x - arg0; this.y = this.y - arg0; this.width = this.width + arg0 * 2.0; this.height = this.height + arg0 * 2.0; } break; case 4: this.x = this.x - arg0; this.y = this.y - arg1; this.width = this.width + arg0 + arg2; this.height = this.height + arg1 + arg3; break; } return this; }; /** * Scales the rectangle by the specified value * * @param {number} scale * @returns {Rect} Returns reference to the current rectangle. */ Rect.prototype.scale = function (scale) { this.x = this.x * scale; this.y = this.y * scale; this.width = this.width * scale; this.height = this.height * scale; return this; }; /** * Moves the rectangle by the specified horizontal and vertical offsets. * * @param {number} x Horizontal offset * @param {number} y Vertical offset * * @returns {Rect} Returns reference to the current rectangle. */ Rect.prototype.translate = function (x, y) { this.x = this.x + x; this.y = this.y + y; return this; }; /** * Inverts rectangle coordinates * * @returns {Rect} Returns reference to the current rectangle. */ Rect.prototype.invert = function () { var width = this.width, x = this.x; this.width = this.height; this.height = width; this.x = this.y; this.y = x; return this; }; /** * Callback for iterating rectangle's sides * * @callback loopRectEdgesCallback * @param {Vector} vector Vector connecting two corners of the rectangle's side * @param {PlacementType} placementType The current side * @returns {boolean} Returns true to break iteration process */ /** * Loops edges of the rectangle in the clockwise order: Top, Right, Bottom, Left * * @param {loopRectEdgesCallback} callback A callback function to iterate over sides of the rectangle. * @returns {Rect} Returns reference to the current rectangle. */ Rect.prototype.loopEdges = function (callback) { // function(vector, placementType) {} var vertexes = [ new Point(this.left(), this.top()), new Point(this.right(), this.top()), new Point(this.right(), this.bottom()), new Point(this.left(), this.bottom()) ], placements = [ PlacementType.Top, PlacementType.Right, PlacementType.Bottom, PlacementType.Left ]; vertexes.push(vertexes[0]); if (callback != null) { for (var index = 1, len = vertexes.length; index < len; index += 1) { if (callback(new Vector(vertexes[index - 1], vertexes[index]), placements[index - 1])) { break; } } } return this; }; /** * Checks if the rectangle contains given point * * @param {Point} arg0 The point to check. * * @param {number} arg0 The x coordinate of the point to check. * @param {number} arg1 The y coordinate of the point to check. * @returns {boolean} Returns true if the rectangle contains the specified point; otherwise, false. */ Rect.prototype.contains = function (arg0, arg1) { switch (arguments.length) { case 1: return this.x <= arg0.x && arg0.x <= this.x + this.width && this.y <= arg0.y && arg0.y <= this.y + this.height; case 2: return this.x <= arg0 && arg0 <= this.x + this.width && this.y <= arg1 && arg1 <= this.y + this.height; default: return false; } }; /** * Crops the rectangle by the boundaries of the specified rectangle. * * @param {Rect} rect The rectangle that is used to crop boundaries by * @returns {Rect} Returns reference to the current rectangle. */ Rect.prototype.cropByRect = function (rect) { if (this.x < rect.x) { this.width -= (rect.x - this.x); this.x = rect.x; } if (this.right() > rect.right()) { this.width -= (this.right() - rect.right()); } if (this.y < rect.y) { this.height -= (rect.y - this.y); this.y = rect.y; } if (this.bottom() > rect.bottom()) { this.height -= this.bottom() - rect.bottom(); } if (this.isEmpty()) { this.x = null; this.y = null; this.width = null; this.height = null; } return this; }; /** * Checks if the rectangle overlaps the specified rectangle * * @param {Rect} rect The rectangle to check overlapping for. * @returns {boolean} Returns true if two rectangles overlap each other. */ Rect.prototype.overlaps = function (rect) { var result = true; if (this.x + this.width < rect.x || rect.x + rect.width < this.x || this.y + this.height < rect.y || rect.y + rect.height < this.y) { result = false; } return result; }; /** * Expands the rectangle boundaries to contain the specified rectangle. * * @param {Rect} arg0 The rectangle to contain. * * @param {number} arg0 The x coordinate of top left corner. * @param {number} arg1 The y coordinate of top left corner. * @param {number} [arg2=undefined] Width. * @param {number} [arg3=undefined] Height. * @returns {Rect} Returns reference to the current rectangle. */ Rect.prototype.addRect = function (arg0, arg1, arg2, arg3) { var right, bottom; switch (arguments.length) { case 1: if (!arg0.isEmpty()) { if (this.isEmpty()) { this.x = arg0.x; this.y = arg0.y; this.width = arg0.width; this.height = arg0.height; } else { right = Math.max(this.right(), arg0.right()); bottom = Math.max(this.bottom(), arg0.bottom()); this.x = Math.min(this.x, arg0.x); this.y = Math.min(this.y, arg0.y); this.width = right - this.x; this.height = bottom - this.y; } } break; case 2: if (this.isEmpty()) { this.x = arg0; this.y = arg1; this.width = 0; this.height = 0; } else { right = Math.max(this.right(), arg0); bottom = Math.max(this.bottom(), arg1); this.x = Math.min(this.x, arg0); this.y = Math.min(this.y, arg1); this.width = right - this.x; this.height = bottom - this.y; } break; case 4: if (this.isEmpty()) { this.x = arg0; this.y = arg1; this.width = arg2; this.height = arg3; } else { right = Math.max(this.right(), arg0 + arg2); bottom = Math.max(this.bottom(), arg1 + arg3); this.x = Math.min(this.x, arg0); this.y = Math.min(this.y, arg1); this.width = right - this.x; this.height = bottom - this.y; } break; } return this; }; /** * Returns rectangle location and size in form of CSS style object. * * @param {string} [units="px"] The string name of units. * @returns {object} CSS style object */ Rect.prototype.getCSS = function (units) { units = (units !== undefined) ? units : "px"; var result = { left: this.x + units, top: this.y + units, width: this.width + units, height: this.height + units }; return result; }; /** * Returns rectangle location and size in form of CSS style string. * * @param {string} [units="px"] The string name of units. * @returns {string} CSS style string. */ Rect.prototype.toString = function (units) { var result = ""; units = (units !== undefined) ? units : "px"; result += "left:" + this.x + units + ";"; result += "top:" + this.y + units + ";"; result += "width:" + this.width + units + ";"; result += "height:" + this.height + units + ";"; return result; }; /** * Validates rectangle properties * * @returns {boolean} Returns true if rectangle properties are valid. */ Rect.prototype.validate = function () { if (isNaN(this.x) || isNaN(this.y) || isNaN(this.width) || isNaN(this.height)) { throw "Invalid rect position."; } }; /** * Checks if rectangles are equal * * @param {Rect} rect Rectangle * @returns {boolean} Returns true if rectangles are equal. */ Rect.prototype.equalTo = function (rect) { return this.x == rect.x && this.y == rect.y && this.width == rect.width && this.height == rect.height; }; /** * Find intersection point between rectangle's perimeter and line connecting the given point and center of the rectangle * * @param {Point} point Point to project * @returns {Point} Returns point or null if point is inside rectangle. */ Rect.prototype.getProjectionPoint = function (point) { var result = null; if(!this.contains(point)) { var vector = new Vector(this.centerPoint(), point); this.loopEdges(function(edge) { result = vector.getIntersectionPoint(edge, true, 1.0); return (result != null); }); } return result; }; /** * Vertical Interval * * @returns {Interval} Returns vertical interval of the rectangle */ Rect.prototype.verticalInterval = function () { return new Interval( this.y, this.bottom() ); };