-- | Calculate 2D distance fields via the Grevera improved 8SED (Danielsson)
-- using small kernel sweeps across the data stored as a 2D array.

module Grevera8SEDFast1
  (
    grevera8SED
  )
  where

import           Prelude             as P

import           Data.Vector.Unboxed as U

dX, dY, dXY :: Double
dX = 1
dY = 1
dXY = sqrt (dX * dX + dY * dY)

minDist :: Double -> Double -> Double -> Double
minDist offset y1 y = min (y1 + offset) y

-- | check(data, x, y, x, y[+|-]1, dy )
-- y1 is y +|- 1 and y is the current y
pass1 :: U.Vector Double -> U.Vector Double -> U.Vector Double
pass1 y1 y = U.zipWith (minDist dY) y1 y

-- | check(data, x, y, x-1, y, dx ), check(data, x, y, x+1, y, dx );
pass2s1 :: U.Vector Double -> U.Vector Double
pass2s1 y = U.scanl (minDist dX) (U.head y) (U.tail y)

-- | check(data, x, y, x-1, y[+|-]1, dxy ), check(data, x, y, x+1, y[+|-]1, dxy );
-- y1 is y +|- 1 and y is the current y
pass2s2 :: U.Vector Double -> U.Vector Double -> U.Vector Double
pass2s2 y1 y = U.head y `U.cons` U.zipWith (minDist dXY) y1 (U.tail y)

-- | pass y-1 y, pass y+1 y
-- y1 is y +|- 1 and y is the rest
grevera8SEDpass :: U.Vector Double -> U.Vector Double -> U.Vector Double
grevera8SEDpass y1 = U.reverse . pass2s2 (U.reverse y1) . pass2s1 . U.reverse .
                    pass2s2 y1 . pass2s1 . pass1 y1

-- | Return the distance field, same array size as the incoming array
grevera8SED :: [U.Vector Double] -> [U.Vector Double]
grevera8SED x =
  let y = P.reverse $
          P.scanl grevera8SEDpass (P.head x) (P.tail x) -- | pass y-1 y
  in P.scanl grevera8SEDpass (P.head y) (P.tail y) -- | pass y+1 y
