module Reanimate.Morph.Linear
  ( linear, rawLinear
  , linearCorrespondence
  , closestLinearCorrespondence
  , linearTrajectory
  ) where

import           Data.Hashable
import qualified Data.Vector            as V
import           Linear.Vector
import           Reanimate.ColorComponents
import           Reanimate.Math.Common
import           Reanimate.Math.Polygon
import           Reanimate.Morph.Cache
import           Reanimate.Morph.Common

linear :: Morph
linear = rawLinear
  { morphPointCorrespondence  =
      cachePointCorrespondence (hash ("closest"::String))
        closestLinearCorrespondence }

rawLinear :: Morph
rawLinear = Morph
  { morphTolerance            = 0.001
  , morphColorComponents      = labComponents
  , morphPointCorrespondence  = linearCorrespondence
  , morphTrajectory           = linearTrajectory
  , morphObjectCorrespondence = splitObjectCorrespondence }

linearCorrespondence :: PointCorrespondence
linearCorrespondence = normalizePolygons

closestLinearCorrespondence :: PointCorrespondence
closestLinearCorrespondence src' dst' =
    (src, worker dst (score dst) options)
  where
    (src, dst) = normalizePolygons src' dst'
    worker bestP _bestPScore [] = bestP
    worker bestP bestPScore (x:xs) =
      let newScore = score x in
      if newScore < bestPScore
        then worker x newScore xs
        else worker bestP bestPScore xs
    options = pCycles dst
    score p = sum
      [ approxDist (pAccess src n) (pAccess p n)
      | n <- [0 .. pSize src-1] ]

linearTrajectory :: Trajectory
linearTrajectory (src,dst)
  | pSize src == pSize dst = \t -> mkPolygon $
    V.zipWith (lerp $ realToFrac t) (polygonPoints dst) (polygonPoints src)
  | otherwise = error $ "Invalid lengths: " ++ show (pSize src, pSize dst)