Eji-Sensei14's picture
Upload folder using huggingface_hub
c6535db verified
export function isInRectangle(
x,
y,
left,
top,
width,
height,
) {
return x >= left &&
x < left + width &&
y >= top &&
y < top + height
}
/**
* A rectangle, represented as a float64 array of 4 numbers: [x, y, width, height].
*
* This class is a subclass of Float64Array, and so has all the methods of that class. Notably,
* {@link Rectangle.from} can be used to convert a {@link ReadOnlyRect}.
*
* Sub-array properties ({@link Float64Array.subarray}):
* - {@link pos}: The position of the top-left corner of the rectangle.
* - {@link size}: The size of the rectangle.
*/
export class Rectangle extends Float64Array {
#pos
#size
constructor(x = 0, y = 0, width = 0, height = 0) {
super(4)
this[0] = x
this[1] = y
this[2] = width
this[3] = height
}
subarray(begin = 0, end) {
const byteOffset = begin << 3
const length = end === undefined ? end : end - begin
return new Float64Array(this.buffer, byteOffset, length)
}
/**
* A reference to the position of the top-left corner of this rectangle.
*
* Updating the values of the returned object will update this rectangle.
*/
get pos() {
this.#pos ??= this.subarray(0, 2)
return this.#pos
}
set pos(value) {
this[0] = value[0]
this[1] = value[1]
}
/**
* A reference to the size of this rectangle.
*
* Updating the values of the returned object will update this rectangle.
*/
get size() {
this.#size ??= this.subarray(2, 4)
return this.#size
}
set size(value) {
this[2] = value[0]
this[3] = value[1]
}
// #region Property accessors
/** The x co-ordinate of the top-left corner of this rectangle. */
get x() {
return this[0]
}
set x(value) {
this[0] = value
}
/** The y co-ordinate of the top-left corner of this rectangle. */
get y() {
return this[1]
}
set y(value) {
this[1] = value
}
/** The width of this rectangle. */
get width() {
return this[2]
}
set width(value) {
this[2] = value
}
/** The height of this rectangle. */
get height() {
return this[3]
}
set height(value) {
this[3] = value
}
/** The x co-ordinate of the left edge of this rectangle. */
get left() {
return this[0]
}
set left(value) {
this[0] = value
}
/** The y co-ordinate of the top edge of this rectangle. */
get top() {
return this[1]
}
set top(value) {
this[1] = value
}
/** The x co-ordinate of the right edge of this rectangle. */
get right() {
return this[0] + this[2]
}
set right(value) {
this[0] = value - this[2]
}
/** The y co-ordinate of the bottom edge of this rectangle. */
get bottom() {
return this[1] + this[3]
}
set bottom(value) {
this[1] = value - this[3]
}
/** The x co-ordinate of the centre of this rectangle. */
get centreX() {
return this[0] + (this[2] * 0.5)
}
/** The y co-ordinate of the centre of this rectangle. */
get centreY() {
return this[1] + (this[3] * 0.5)
}
// #endregion Property accessors
/**
* Updates the rectangle to the values of {@link rect}.
* @param rect The rectangle to update to.
*/
updateTo(rect) {
this[0] = rect[0]
this[1] = rect[1]
this[2] = rect[2]
this[3] = rect[3]
}
/**
* Checks if the point [{@link x}, {@link y}] is inside this rectangle.
* @param x The x-coordinate to check
* @param y The y-coordinate to check
* @returns `true` if the point is inside this rectangle, otherwise `false`.
*/
containsXy(x, y) {
const {x: left, y: top, width, height} = this
return x >= left &&
x < left + width &&
y >= top &&
y < top + height
}
/**
* Checks if {@link point} is inside this rectangle.
* @param point The point to check
* @returns `true` if {@link point} is inside this rectangle, otherwise `false`.
*/
containsPoint(point) {
return this.x <= point[0] &&
this.y <= point[1] &&
this.x + this.width >= point[0] &&
this.y + this.height >= point[1]
}
/**
* Checks if {@link rect} is inside this rectangle.
* @param rect The rectangle to check
* @returns `true` if {@link rect} is inside this rectangle, otherwise `false`.
*/
containsRect(rect) {
return this.x <= rect[0] &&
this.y <= rect[1] &&
this.x + this.width >= rect[0] + rect[2] &&
this.y + this.height >= rect[1] + rect[3]
}
/**
* Checks if {@link rect} overlaps with this rectangle.
* @param rect The rectangle to check
* @returns `true` if {@link rect} overlaps with this rectangle, otherwise `false`.
*/
overlaps(rect) {
return this.x < rect[0] + rect[2] &&
this.y < rect[1] + rect[3] &&
this.x + this.width > rect[0] &&
this.y + this.height > rect[1]
}
/**
* Finds the corner (if any) of this rectangle that contains the point [{@link x}, {@link y}].
* @param x The x-coordinate to check
* @param y The y-coordinate to check
* @param cornerSize Each corner is treated as an inset square with this width and height.
* @returns The compass direction of the corner that contains the point, or `undefined` if the point is not in any corner.
*/
findContainingCorner(x, y, cornerSize) {
if (this.isInTopLeftCorner(x, y, cornerSize)) return "NW"
if (this.isInTopRightCorner(x, y, cornerSize)) return "NE"
if (this.isInBottomLeftCorner(x, y, cornerSize)) return "SW"
if (this.isInBottomRightCorner(x, y, cornerSize)) return "SE"
}
/** @returns `true` if the point [{@link x}, {@link y}] is in the top-left corner of this rectangle, otherwise `false`. */
isInTopLeftCorner(x, y, cornerSize) {
return isInRectangle(x, y, this.x, this.y, cornerSize, cornerSize)
}
/** @returns `true` if the point [{@link x}, {@link y}] is in the top-right corner of this rectangle, otherwise `false`. */
isInTopRightCorner(x, y, cornerSize) {
return isInRectangle(x, y, this.right - cornerSize, this.y, cornerSize, cornerSize)
}
/** @returns `true` if the point [{@link x}, {@link y}] is in the bottom-left corner of this rectangle, otherwise `false`. */
isInBottomLeftCorner(x, y, cornerSize) {
return isInRectangle(x, y, this.x, this.bottom - cornerSize, cornerSize, cornerSize)
}
/** @returns `true` if the point [{@link x}, {@link y}] is in the bottom-right corner of this rectangle, otherwise `false`. */
isInBottomRightCorner(x, y, cornerSize) {
return isInRectangle(x, y, this.right - cornerSize, this.bottom - cornerSize, cornerSize, cornerSize)
}
/** @returns `true` if the point [{@link x}, {@link y}] is in the top edge of this rectangle, otherwise `false`. */
isInTopEdge(x, y, edgeSize) {
return isInRectangle(x, y, this.x, this.y, this.width, edgeSize)
}
/** @returns `true` if the point [{@link x}, {@link y}] is in the bottom edge of this rectangle, otherwise `false`. */
isInBottomEdge(x, y, edgeSize) {
return isInRectangle(x, y, this.x, this.bottom - edgeSize, this.width, edgeSize)
}
/** @returns `true` if the point [{@link x}, {@link y}] is in the left edge of this rectangle, otherwise `false`. */
isInLeftEdge(x, y, edgeSize) {
return isInRectangle(x, y, this.x, this.y, edgeSize, this.height)
}
/** @returns `true` if the point [{@link x}, {@link y}] is in the right edge of this rectangle, otherwise `false`. */
isInRightEdge(x, y, edgeSize) {
return isInRectangle(x, y, this.right - edgeSize, this.y, edgeSize, this.height)
}
/** @returns The centre point of this rectangle, as a new array. */
getCentre() {
return [this.centreX, this.centreY]
}
/** @returns The area of this rectangle. */
getArea() {
return this.width * this.height
}
/** @returns The perimeter of this rectangle. */
getPerimeter() {
return 2 * (this.width + this.height)
}
/** @returns The top-left corner of this rectangle, as a new array. */
getTopLeft() {
return [this[0], this[1]]
}
/** @returns The bottom-right corner of this rectangle, as a new array. */
getBottomRight() {
return [this.right, this.bottom]
}
/** @returns The width and height of this rectangle, as a new array. */
getSize() {
return [this[2], this[3]]
}
/** @returns The offset from the top-left of this rectangle to the point, as a new array. */
getOffsetTo([x, y]) {
return [x - this[0], y - this[1]]
}
/** @returns The offset from the point to the top-left of this rectangle, as a new array. */
getOffsetFrom([x, y]) {
return [this[0] - x, this[1] - y]
}
/** Resizes the rectangle without moving it, setting its top-left corner to [{@link x}, {@link y}]. */
resizeTopLeft(x1, y1) {
this[2] += this[0] - x1
this[3] += this[1] - y1
this[0] = x1
this[1] = y1
}
/** Resizes the rectangle without moving it, setting its bottom-left corner to [{@link x}, {@link y}]. */
resizeBottomLeft(x1, y2) {
this[2] += this[0] - x1
this[3] = y2 - this[1]
this[0] = x1
}
/** Resizes the rectangle without moving it, setting its top-right corner to [{@link x}, {@link y}]. */
resizeTopRight(x2, y1) {
this[2] = x2 - this[0]
this[3] += this[1] - y1
this[1] = y1
}
/** Resizes the rectangle without moving it, setting its bottom-right corner to [{@link x}, {@link y}]. */
resizeBottomRight(x2, y2) {
this[2] = x2 - this[0]
this[3] = y2 - this[1]
}
/** Sets the width without moving the right edge (changes position) */
setWidthRightAnchored(width) {
const currentWidth = this[2]
this[2] = width
this[0] += currentWidth - width
}
/** Sets the height without moving the bottom edge (changes position) */
setHeightBottomAnchored(height) {
const currentHeight = this[3]
this[3] = height
this[1] += currentHeight - height
}
/** Alias of {@link export}. */
toArray() {
return this.export()
}
/** @returns A new, untyped array (serializable) containing the values of this rectangle. */
export() {
return [this[0], this[1], this[2], this[3]]
}
/** Draws a debug outline of this rectangle. */
_drawDebug(ctx, colour = "red") {
const {strokeStyle, lineWidth} = ctx
try {
ctx.strokeStyle = colour
ctx.lineWidth = 0.5
ctx.beginPath()
ctx.strokeRect(this[0], this[1], this[2], this[3])
} finally {
ctx.strokeStyle = strokeStyle
ctx.lineWidth = lineWidth
}
}
}