You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
434 lines
10 KiB
434 lines
10 KiB
|
|
/**
|
|
* 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<SideDirections,number>} 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<SideDirections,number>} 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<SideDirections, number}
|
|
*/
|
|
function getEnclosingBounds(element) {
|
|
let area = element.getBoundingClientRect();
|
|
let parentArea = element.parentElement.getBoundingClientRect();
|
|
return {
|
|
[SideDirections.LEFT]: Math.min(area.left, parentArea.left),
|
|
[SideDirections.RIGHT]: Math.max(area.right, parentArea.right),
|
|
[SideDirections.TOP]: Math.min(area.top, parentArea.top),
|
|
[SideDirections.BOTTOM]: Math.max(area.bottom, parentArea.bottom)
|
|
}
|
|
}
|
|
|