import { minMax } from "./utilities"; /** * Rotate `bboxCoords` by `rotation` degrees around `pivot` * `whRatio` is used to do rotation result in internal metric 100x100: * scaleX = stageWidth / 100 * 1. shift coords to rotate zero-based vectors: x - pivot | y - pivot * 2. convert internal to absolute: x * scaleX | y * scaleY * 3. rotate vector: x * cosA - y * sinA | x * sinA + y * cosA * 4. scale back: x / scale | y / scale * 5. shift back: x + pivot | y + pivot * 2+3+4 are simplified for x: * ((x * scaleX) * cosA - (y * scaleY) * sinA) / scaleX * x * cosA - y * sinA * scaleY / scaleX * similar for y * and scaleX / scaleY = whRatio * @typedef {{ left: number, top: number, right: number, bottom: number }} BBox * @param {BBox} bboxCoords * @param {number} rotation degrees * @param {{ x: number, y: number }} pivot * @param {number} whRatio * @returns {BBox} */ export function rotateBboxCoords(bboxCoords, rotation, pivot = { x: bboxCoords.left, y: bboxCoords.top }, whRatio = 1) { if (!bboxCoords) return bboxCoords; const a = (rotation * Math.PI) / 180; const cosA = Math.cos(a); const sinA = Math.sin(a); const points = [ { x: bboxCoords.left - pivot.x, y: bboxCoords.top - pivot.y, }, { x: bboxCoords.right - pivot.x, y: bboxCoords.top - pivot.y, }, { x: bboxCoords.left - pivot.x, y: bboxCoords.bottom - pivot.y, }, { x: bboxCoords.right - pivot.x, y: bboxCoords.bottom - pivot.y, }, ].map((p) => ({ x: p.x * cosA - (p.y * sinA) / whRatio, y: p.x * sinA * whRatio + p.y * cosA, })); const [left, right] = minMax(points.map((p) => p.x)); const [top, bottom] = minMax(points.map((p) => p.y)); return { left: left + pivot.x, right: right + pivot.x, top: top + pivot.y, bottom: bottom + pivot.y, }; }