25 lines
1.1 KiB
TypeScript
25 lines
1.1 KiB
TypeScript
import { DEFAULT_ANGLE_STEP } from '@pascal-app/core'
|
|
|
|
/**
|
|
* Shared rotation delta for the 2D corner rotate-arrow affordances (column /
|
|
* elevator / roof-segment / shelf / spawn / stair — all structurally
|
|
* identical). Measures the pointer's angular sweep from the grab bearing
|
|
* around the node center, wrapped to [-π, π] so a drag crossing ±π keeps its
|
|
* sign, then snaps it to the 15° increment unless `free` (the held Shift the
|
|
* contextual HUD advertises). The 2D twin of the 3D gizmo's
|
|
* `snapDirectRotationDelta`, so rotating a node reads the same in both views.
|
|
*/
|
|
export function rotateAffordanceDelta(args: {
|
|
center: readonly [number, number]
|
|
initialAngle: number
|
|
planPoint: readonly [number, number]
|
|
free: boolean
|
|
}): number {
|
|
const { center, initialAngle, planPoint, free } = args
|
|
const currentAngle = Math.atan2(planPoint[1] - center[1], planPoint[0] - center[0])
|
|
let delta = currentAngle - initialAngle
|
|
while (delta > Math.PI) delta -= 2 * Math.PI
|
|
while (delta < -Math.PI) delta += 2 * Math.PI
|
|
return free ? delta : Math.round(delta / DEFAULT_ANGLE_STEP) * DEFAULT_ANGLE_STEP
|
|
}
|