import type { Bounds, Vec2 } from "./index.js"; const TWO_PI = Math.PI * 2; function finiteOrDefault(value: unknown, fallback: number): number { return typeof value === "number" && Number.isFinite(value) ? value : fallback; } export function rotationForProperties( properties: Readonly> = {}, ): number { return finiteOrDefault(properties.rotation, 0); } export function rotatePoint(point: Vec2, center: Vec2, angle: number): Vec2 { const cosine = Math.cos(angle); const sine = Math.sin(angle); const offsetX = point.x - center.x; const offsetY = point.y - center.y; return { x: center.x + offsetX * cosine - offsetY * sine, y: center.y + offsetX * sine + offsetY * cosine, }; } export function inverseRotatePoint( point: Vec2, center: Vec2, angle: number, ): Vec2 { return rotatePoint(point, center, -angle); } export function rotatedBounds(bounds: Bounds, angle: number): Bounds { const center = { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2, }; const corners = [ rotatePoint({ x: bounds.x, y: bounds.y }, center, angle), rotatePoint( { x: bounds.x + bounds.width, y: bounds.y }, center, angle, ), rotatePoint( { x: bounds.x + bounds.width, y: bounds.y + bounds.height }, center, angle, ), rotatePoint( { x: bounds.x, y: bounds.y + bounds.height }, center, angle, ), ]; const x = Math.min(...corners.map((corner) => corner.x)); const y = Math.min(...corners.map((corner) => corner.y)); const right = Math.max(...corners.map((corner) => corner.x)); const bottom = Math.max(...corners.map((corner) => corner.y)); return { x, y, width: right - x, height: bottom - y }; } export function rotationHandlePoint( bounds: Bounds, angle: number, offset = 28, ): Vec2 { const center = { x: bounds.x + bounds.width / 2, y: bounds.y + bounds.height / 2, }; const safeOffset = Math.max(0, finiteOrDefault(offset, 28)); return rotatePoint( { x: center.x, y: bounds.y - safeOffset }, center, angle, ); } export function normalizeRotation(angle: number): number { const finiteAngle = finiteOrDefault(angle, 0); const normalized = ((finiteAngle + Math.PI) % TWO_PI + TWO_PI) % TWO_PI - Math.PI; return normalized === -Math.PI ? Math.PI : normalized; }