/** * Class containing two numbers. * Usually they represent coordinates, * but they also might serve as length and width * as this class provides several convenience methods. */ class TwoDimPoint { /** * * @param {number} x * @param {number} y */ constructor(x = -1, y = -1) { this.x = x; this.y = y; } /** * * @returns {string} */ toString() { return `P(${this.x},${this.y})`; } /** * Loads the width and height of an element into the x and y of this TDP. * @param {HTMLElement} element * @returns {TwoDimPoint} */ loadFromElementDimensions(element) { let bcr = element.getBoundingClientRect(); this.x = bcr.width; this.y = bcr.height; return this; } /** * Loads the coordinates of the top-left corner of an element, * into the x and y of this TDP. * @param {HTMLElement} element * @returns {TwoDimPoint} */ loadFromElementZero(element) { let bcr = element.getBoundingClientRect(); this.x = bcr.left; this.y = bcr.top; return this; } /** * Loads the coordinates of the bottom-right corner of an element, * into the x and y of this TDP. * @param {HTMLElement} element * @returns {TwoDimPoint} */ loadFromElementMax(element) { let bcr = element.getBoundingClientRect(); this.x = bcr.left; this.y = bcr.top; return this; } /** * If onConditionMet is true (default) and the value of x is positive * the x of this TDP will be multiplied with -1, * otherwise nothing will be done. * @param {boolean} [onConditionMet = true] onConditionMet * @returns {TwoDimPoint} this TDP Object */ setXAsNegative(onConditionMet = true) { if (onConditionMet && this.x > 0) { this.x = this.x * -1; } return this; } /** * If onConditionMet is true (default) and the value of y is positive * the y of this TDP will be multiplied with -1, * otherwise nothing will be done. * @param {boolean} [onConditionMet = true] onConditionMet * @returns {TwoDimPoint} */ setYAsNegative(onConditionMet = true) { if (onConditionMet && this.y > 0) { this.y = this.y * -1; } return this; } /** * * @param {boolean} onConditionMetX * @param {boolean} onConditionMetY * @returns {TwoDimPoint} */ setAsNegative(onConditionMetX, onConditionMetY) { this.setXAsNegative(onConditionMetX); this.setYAsNegative(onConditionMetY); return this; } /** * Subtracts the given Delta from the x-value * @param {number} delta * @returns {TwoDimPoint} */ setXMinus(delta) { this.x = this.x - delta; return this; } /** * Adds the given Delta from the x-value * @param {number} delta * @returns {TwoDimPoint} */ setXPlus(delta) { this.x = this.x + delta; return this; } /** * Subtracts the given Delta from the y-value * @param {number} delta * @returns {TwoDimPoint} */ setYMinus(delta) { this.y = this.y - delta; return this; } /** * Adds the given Delta from the y-value * @param {number} delta * @returns {TwoDimPoint} */ setYPlus(delta) { this.y = this.y + delta; return this; } /** * Returns the absolute delta between this x and given ref * @param {number} refnumber * @returns {number} */ getXDeltaTo(refnumber) { return Math.abs(this.x - refnumber); } /** * Returns the absolute delta between this y and given ref * @param {number} refnumber * @returns {number} */ getYDeltaTo(refnumber) { return Math.abs(this.y - refnumber); } /** * * @param {number} times * @returns {TwoDimPoint} */ setXMultiplied(times) { this.x = this.x * times; return this; } /** * * @param {number} times * @returns {TwoDimPoint} */ setYMultiplied(times) { this.y = this.y * times; return this; } /** * * @param {number} times * @returns {TwoDimPoint} */ setXDivided(times) { if (times === 0) { throw new Error("Dividing by 0 is not defined"); } this.x = this.x / times; return this; } /** * * @param {number} times * @returns {TwoDimPoint} */ setYDivided(times) { if (times === 0) { throw new Error("Dividing by 0 is not defined"); } this.y = this.y / times; return this; } /** * Subtracts the given Delta {TwoDimPoint} TDP from this TDP * @param {TwoDimPoint} delta * @returns {TwoDimPoint} */ setThisByMinusTDP(delta) { this.x = this.x - delta.x; this.y = this.y - delta.y; return this; } /** * Adds the given Delta {TwoDimPoint} TDP to this TDP * @param {TwoDimPoint} delta * @returns {TwoDimPoint} */ setThisByPlusTDP(delta) { this.x = this.x + delta.x; this.y = this.y + delta.y; return this; } /** * * @param {nr} tdp * @returns {TwoDimPoint} */ setByMultipliedByNumber(nr) { this.x = this.x * nr; this.y = this.y * nr; return this; } /** * * @param {TwoDimPoint} tdp * @returns {TwoDimPoint} */ setByMultipliedByTDP(tdp) { this.x = this.x * tdp.x; this.y = this.y * tdp.y; return this; } /** * @param {nr} tdp * @returns {TwoDimPoint} */ setByDividedByNumber(nr) { if (nr === 0) { throw new Error("Dividing by 0 is not defined"); } this.x = this.x / nr; this.y = this.y / nr; return this; } /** * * @param {TwoDimPoint} tdp * @returns {TwoDimPoint} */ setByDividedByTDP(tdp) { if (tdp.x === 0 | tdp.y === 0) { throw new Error("Dividing by 0 is not defined"); } this.x = this.x / tdp.x; this.y = this.y / tdp.y; return this; } /** * Creates and returns a new TDP * consisting of the absolute deltas of the x/y coordinates of this and the reference TDP. * @param {TwoDimPoint} refTDP * @returns {TwoDimPoint} */ getNewDeltaTDP(refTDP) { return new TwoDimPoint( this.getXDeltaTo(refTDP.x), this.getYDeltaTo(refTDP.y) ); } /** * Returns the absolute Distance between two points. * @param {TwoDimPoint} refTDP * @returns {number} */ getDistanceBetween(refTDP) { return Math.hypot( this.getXDeltaTo(refTDP.x), this.getYDeltaTo(refTDP.y) ); } /** * Returns a new TDP where x and y are calculated, * based on the given distance and angle (degree). * @param {number} distance * @param {number} degrees * @returns {TwoDimPoint} */ getNewTDPByDistance( distance, degrees ) { /** angle in radians */ let angle = ((parseFloat(degrees) * Math.PI) / 180); return new TwoDimPoint( this.x + distance * Math.cos(angle), this.y + distance * Math.sin(angle), ); } /** * Multiplies both (x,y) with -1 to inverse them. * @returns {TwoDimPoint} */ inverse() { this.x = this.x * -1 this.y = this.y * -1 return this; } /** * Flips/changes x and y with each other. * @returns {TwoDimPoint} */ flip() { let oldX = this.x; this.x = this.y; this.y = oldX; return this; } } /** * * @param {number} start * @param {number} end * @param {number} value * @param {number} tolerance * @param {boolean} usePercentage * @returns {boolean} */ function isValueInBounds(start, end, value, tolerance = 0, usePercentage = false) { if (tolerance !== 0) { if (usePercentage) { start = start * (1 - tolerance / 100); end = end * (1 + tolerance / 100); } else { start = start - tolerance; end = end + tolerance; } } return value >= start && value <= end; } /** * @param {number} x * @param {number} y * @param {Map} area * @param {number} tolerance * @param {boolean} usePercentage if tolerance is given and this is set to true, * the tolerance will be calculated by percentage, * otherwise it will be subtracted/added * @returns {boolean} */ function areXYInArea(x, y, area, tolerance = 0, usePercentage = false) { return isValueInBounds( area.get(SideDirections.LEFT), area.get(SideDirections.RIGHT), x, tolerance, usePercentage ) && isValueInBounds( area.get(SideDirections.TOP), area.get(SideDirections.BOTTOM), y, tolerance, usePercentage ); } /** * * @param {TwoDimPoint} point * @param {Map} area * @param {number} tolerance * @param {boolean} usePercentage if tolerance is given and this is set to true, * the tolerance will be calculated by percentage, * otherwise it will be subtracted/added */ function isPointInArea(point, area, tolerance = 0, usePercentage = false) { return areXYInArea( point.x, point.y, area, tolerance, usePercentage ); } /** * * @param {HTMLElement} element * @returns {Object