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

/**
* 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)
}
}