--{-# LANGUAGE TupleSections #-} module Geometry.Zone ( ddaExt ) where import Geometry.Data import Data.Foldable import qualified Data.IntMap.Strict as IM import qualified Data.IntSet as IS --foldl2' -- :: (b -> a -> a -> b) -- -> b -- -> [a] -- -> b --foldl2' f s (t:ts) = fst $ foldl' g (s, t) ts -- where -- g (r,x) y = (f r x y,y) --foldl2' _ s _ = s --sortArguments -- :: Ord a -- => (a -> a -> b) -- -> a -> a -> b --sortArguments f x y -- | x < y = f x y -- | otherwise = f y x --sortArgumentsReverse -- :: Ord a -- => (a -> a -> [b]) -- -> a -> a -> [b] --sortArgumentsReverse f x y -- | x < y = f x y -- | otherwise = reverse $ f y x -- --intervalBounds -- :: Float -- ^ interval threshold -- -> Float -- ^ First endpoint -- -> Float -- ^ Second endpoint -- -> [Float] --intervalBounds = sortArgumentsReverse . f -- where -- f r a b -- | x > b = [a] -- | otherwise = (a : [x,x+r..b]) -- where -- x = floorTo r a + r --floorTo :: Float -> Float -> Float --floorTo r x = r * (fromIntegral ((floor $ x / r) :: Int)) --ceilingTo :: Float -> Float -> Float --ceilingTo r x = r * (fromIntegral ((ceiling $ x / r) :: Int)) divTo :: Float -> Float -> Int {-# INLINE divTo #-} divTo s = floor . (/s) --flipV :: Point2 -> Point2 --{-# INLINE flipV #-} --flipV (V2 a b) = V2 b a --applyInverted -- :: (Point2 -> Point2 -> [Point2]) -- -> Point2 -> Point2 -> [Point2] --applyInverted f sp@(V2 sx sy) ep@(V2 ex ey) -- | abs (sx-ex) > abs (sy-ey) = f sp ep -- | otherwise = map flipV $ f (flipV sp) (flipV ep) sizeZoneOfPoint' :: Float -> Point2 -> V2 Int {-# INLINE sizeZoneOfPoint' #-} sizeZoneOfPoint' s = fmap (divTo s) --increasingInterval :: Int -> Int -> [Int] --increasingInterval x y -- | y > x = [x .. y] -- | otherwise = [y .. x] -- | Determines which horizontal and vertical lines on a grid are crossed by a -- line. For each adds the x-y index of the square to the right or above the -- crossed grid line. Also adds the index of the square containing the start -- point. ddaExt :: Float -> V2 Float -> V2 Float -> IM.IntMap IS.IntSet ddaExt s sp@(V2 sx sy) ep@(V2 ex ey) | x1 <= x2 = addsp . addys . IM.fromDistinctAscList $ zip [x1 .. x2] $ map (IS.singleton . divTo s) [x1y,x1y+ydx..] | otherwise = addsp . addys . IM.fromDistinctAscList $ zip [x2-1 .. x1-1] $ map (IS.singleton . divTo s) [x2y,x2y+ydx..] where addsp im = let V2 x y = sizeZoneOfPoint' s sp in insertXY im (x,y) x1 = divTo s sx x2 = divTo s ex x1y = fx' sp ep $ s * fromIntegral x1 x2y = fx' sp ep $ s * fromIntegral x2 ydx = s * ydx' sp ep addys m = add2s m ypairs y1 = divTo s sy y2 = divTo s ey y1x = fy' sp ep $ s * fromIntegral y1 y2x = fy' sp ep $ s * fromIntegral y2 xdy = s * xdy' sp ep ypairs | y1 <= y2 = zip (map (divTo s) [y1x,y1x+xdy..]) [y1 .. y2] | otherwise = zip (map (divTo s) [y2x,y2x+xdy..]) [y2-1 .. y1-1] ydx' :: Point2 -> Point2 -> Float {-# INLINE ydx' #-} ydx' (V2 sx sy) (V2 ex ey) | sx == ex = 0 | otherwise = (ey - sy) / (ex - sx) fx' :: Point2 -> Point2 -> Float -> Float {-# INLINE fx' #-} fx' sp@(V2 sx sy) ep@(V2 _ ey) x | sy == ey = sy | otherwise = sy + ydx' sp ep * (x - sx) xdy' :: Point2 -> Point2 -> Float {-# INLINE xdy' #-} xdy' (V2 sx sy) (V2 ex ey) | sy == ey = 0 | otherwise = (ex - sx) / (ey - sy) fy' :: Point2 -> Point2 -> Float -> Float {-# INLINE fy' #-} fy' sp@(V2 sx sy) ep@(V2 ex _) y | sx == ex = sx | otherwise = sx + xdy' sp ep * (y - sy) add2s :: IM.IntMap IS.IntSet -> [(Int,Int)] -> IM.IntMap IS.IntSet {-# INLINE add2s #-} add2s = foldl' (\m (k,x) -> IM.insertWith (\_ old -> IS.insert x old) k (IS.singleton x) m) insertXY :: IM.IntMap IS.IntSet -> (Int,Int) -> IM.IntMap IS.IntSet {-# INLINE insertXY #-} insertXY m (k,x) = IM.insertWith (\_ old -> IS.insert x old) k (IS.singleton x) m --addV2s :: IM.IntMap IS.IntSet -> [V2 Int] -> IM.IntMap IS.IntSet --{-# INLINE addV2s #-} --addV2s imis = foldl' -- (\m (V2 k x) -> IM.insertWith (\_ old -> IS.insert x old) k (IS.singleton x) m) -- imis --pairsToIntMapSet :: [V2 Int] -> IM.IntMap IS.IntSet --pairsToIntMapSet = foldl' -- (\m (V2 k x) -> IM.insertWith (\_ old -> IS.insert x old) k (IS.singleton x) m) -- IM.empty