UNPKG

pxt-common-packages

Version:
194 lines (161 loc) 6.11 kB
namespace sprites { let aabbPoints: number[]; export class RotatedBoundingBox { protected _rotation: number; protected _width: number; protected _height: number; protected points: number[]; protected cornerDistance: number; protected cornerAngle: number; public get x0(): number { return this.points[0]; } public get y0(): number { return this.points[1]; } public get x1(): number { return this.points[2]; } public get y1(): number { return this.points[3]; } public get x2(): number { return this.points[4]; } public get y2(): number { return this.points[5]; } public get x3(): number { return this.points[6]; } public get y3(): number { return this.points[7]; } public get rotation() { return this._rotation; } public set rotation(value: number) { this.setRotation(value); } public get width() { return this._width; } public get height() { return this._height; } constructor( public anchor: Sprite, width: number, height: number ) { this.points = []; this._rotation = 0; this.setDimensions(width, height); } setDimensions(width: number, height: number) { width /= 2; height /= 2; this.cornerDistance = Math.sqrt( width * width + height * height ); this.cornerAngle = Math.atan2(height, width); this.setRotation(this._rotation); } setRotation(angle: number) { this._rotation = angle; this.points[0] = Math.cos(this.cornerAngle + angle) * this.cornerDistance; this.points[1] = Math.sin(this.cornerAngle + angle) * this.cornerDistance; this.points[2] = Math.cos(Math.PI - this.cornerAngle + angle) * this.cornerDistance; this.points[3] = Math.sin(Math.PI - this.cornerAngle + angle) * this.cornerDistance; this.points[4] = Math.cos(Math.PI + this.cornerAngle + angle) * this.cornerDistance; this.points[5] = Math.sin(Math.PI + this.cornerAngle + angle) * this.cornerDistance; this.points[6] = Math.cos(angle - this.cornerAngle) * this.cornerDistance; this.points[7] = Math.sin(angle - this.cornerAngle) * this.cornerDistance; this.updateWidthHeight(); } overlaps(other: RotatedBoundingBox): boolean { return doRectanglesIntersect( this.points, this.anchor.x, this.anchor.y, other.points, other.anchor.x, other.anchor.y ); } overlapsAABB(left: number, top: number, right: number, bottom: number) { if (!aabbPoints) { aabbPoints = []; } aabbPoints[0] = left; aabbPoints[1] = top; aabbPoints[2] = right; aabbPoints[3] = top; aabbPoints[4] = right; aabbPoints[5] = bottom; aabbPoints[6] = left; aabbPoints[7] = bottom; return doRectanglesIntersect( this.points, this.anchor.x, this.anchor.y, aabbPoints, 0, 0 ); } protected updateWidthHeight() { let minX = this.points[0]; let maxX = minX; let minY = this.points[1]; let maxY = minY; for (let i = 2; i < 8; i += 2) { minX = Math.min(minX, this.points[i]); maxX = Math.max(maxX, this.points[i]); minY = Math.min(minY, this.points[i + 1]); maxY = Math.max(maxY, this.points[i + 1]); } this._width = (maxX - minX) | 0; this._height = (maxY - minY) | 0; } } // adapted from https://stackoverflow.com/questions/10962379/how-to-check-intersection-between-2-rotated-rectangles // but optimized for rectangles function doRectanglesIntersect(a: number[], ax: number, ay: number, b: number[], bx: number, by: number) { return !(checkForNonIntersection(a, ax, ay, b, bx, by) || checkForNonIntersection(b, bx, by, a, ax, ay)); } function checkForNonIntersection(a: number[], ax: number, ay: number, b: number[], bx: number, by: number) { // we only need to check the first two sides because the // normals are the same for the other two for (let pointIndex = 0; pointIndex < 4; pointIndex += 2) { const normalX = a[pointIndex + 3] - a[pointIndex + 1]; const normalY = a[pointIndex] - a[pointIndex + 2]; let minA: number = undefined; let maxA: number = undefined; let minB: number = undefined; let maxB: number = undefined; for (let i = 0; i < 8; i += 2) { const projected = normalX * (a[i] + ax) + normalY * (a[i + 1] + ay); if (minA === undefined || projected < minA) { minA = projected; } if (maxA == undefined || projected > maxA) { maxA = projected; } } for (let i = 0; i < 8; i += 2) { const projected = normalX * (b[i] + bx) + normalY * (b[i + 1] + by); if (minB === undefined || projected < minB) { minB = projected; } if (maxB == undefined || projected > maxB) { maxB = projected; } } if (maxA < minB || maxB < minA) { return true; } } return false; } }