```{-# LANGUAGE UnicodeSyntax #-}
module GraphRewriting.Layout.Rotation
(module GraphRewriting.Layout.Rotation,
module Data.View)
where

import Prelude.Unicode
import Data.Vector.Class
import Data.Vector.V2
import Data.View
import GraphRewriting.Layout.Geometry

newtype Rotation = Rotation {rotation ∷ Angle}

type Angle = Double -- ∊ [-π,π]
type Impulse = Angle → Angle
type Momentum = Impulse → Rotation → Rotation

v01 = Vector2 0 1

meanAngle ∷ [Angle] → Angle
meanAngle as = angle v01 \$ focalPoint [rotate a \$ v01 | a ← as]

momSum ∷ [Momentum] → Momentum
momSum [] impulse r = r
momSum as impulse r = Rotation \$ meanAngle [rotation (a impulse r) | a ← as]

approach ∷ Rotation → Momentum
approach target impulse current = momentum (angle currentV targetV) impulse current where
targetV = rotate (rotation target) v01
currentV = rotate (rotation current) v01

momentum ∷ Angle → Momentum
momentum a impulse = Rotation . (+) (signum a * impulse (abs a)) . rotation

angle ∷ Vector2 → Vector2 → Angle
angle u v = sign ⋅ (acos \$ bound \$ vdot a b) where
sign = signum \$ v2y b ⋅ v2x a - v2y a ⋅ v2x b
a = vnormalise u
b = vnormalise v
bound x = max (-1) (min 1 x) -- due to a rounding error in (vdot a b), acos might yield NaN

rotate ∷ Angle → Vector2 → Vector2
rotate a (Vector2 x y) = Vector2 (x ⋅ cos a - y ⋅ sin a) (x ⋅ sin a + y ⋅ cos a)
```