reanimate-0.4.0.0: src/Reanimate/Math/SSSP.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Reanimate.Math.SSSP
( -- * Single-Source-Shortest-Path
SSSP
, sssp -- :: (Fractional a, Ord a) => Ring a -> Dual -> SSSP
, dual -- :: Int -> Triangulation -> Dual
, Dual(..)
, DualTree(..)
, PDual
, toPDual -- :: Ring Rational -> Dual -> PDual
, pdualRings -- :: Ring Rational -> PDual -> [Ring Rational]
-- * Misc
, dualToTriangulation -- :: Ring Rational -> Dual -> Triangulation
, pdualReduce -- :: Ring Rational -> PDual -> Int -> PDual
, visibilityArray -- :: Ring Rational -> V.Vector [Int]
, naive -- :: Ring Rational -> SSSP
, naive2 -- :: Ring Rational -> SSSP
, drawDual -- :: Dual -> String
) where
import Control.Monad
-- import Control.Exception
import Control.Monad.ST
-- import Data.FingerTree (SearchResult (..), (|>))
-- import qualified Data.FingerTree as F
import Data.Foldable
import Data.List
import qualified Data.Map as Map
import Data.Maybe
import Data.Ord
import Data.STRef
import Data.Tree
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as MV
import Reanimate.Math.Common
import Reanimate.Math.Triangulate
-- import Debug.Trace
type SSSP = V.Vector Int
-- ssspParent :: Polygon -> SSSP -> Int -> Int
-- ssspParent p sTree x =
-- (sTree V.! ((x - polygonOffset p) `mod` n) + polygonOffset p) `mod` n
-- where
-- n = polygonSize p
visibilityArray :: Ring Rational -> V.Vector [Int]
visibilityArray p = arr
where
n = ringSize p
arr = V.fromList
[ visibility y
| y <- [0..n-1]
]
visibility y =
[ i
| i <- [0..y-1]
, y `elem` arr V.! i ] ++
[ i
| i <- [y+1 .. n-1]
, let pI = ringAccess p i
isOpen = isRightTurn pYp pY pYn
, ringClamp p (y+1) == i || ringClamp p (y-1) == i || if isOpen
then isLeftTurnOrLinear pY pYn pI ||
isLeftTurnOrLinear pYp pY pI
else not $ isRightTurn pY pYn pI ||
isRightTurn pYp pY pI
, let myEdges = [(e1,e2) | (e1,e2) <- edges, e1/=y, e1/=i, e2/=y,e2/=i]
, all (isNothing . lineIntersect (pY,pI))
[ (ringAccess p e1, ringAccess p e2) | (e1,e2) <- myEdges ]]
where
pY = ringAccess p y
pYn = ringAccess p $ y+1
pYp = ringAccess p $ y-1
edges = zip [0..n-1] (tail [0..n-1] ++ [0])
-- Iterative Single Source Shortest Path solver. Quite slow.
naive :: Ring Rational -> SSSP
naive p =
V.fromList $ Map.elems $
Map.map snd $
worker initial
where
initial = Map.singleton 0 (0,0)
visibility = visibilityArray p
worker :: Map.Map Int (Rational, Int) -> Map.Map Int (Rational, Int)
worker m
| m==newM = newM
| otherwise = worker newM
where
ms' = [ Map.fromList
[ case Map.lookup v m of
Nothing -> (v, (distThroughI, i))
Just (otherDist,parent)
| otherDist > distThroughI -> (v, (distThroughI, i))
| otherwise -> (v, (otherDist, parent))
| v <- visibility V.! i
, let distThroughI = dist + approxDist (ringAccess p i) (ringAccess p v) ]
| (i,(dist,_)) <- Map.toList m
]
newM = Map.unionsWith g (m:ms') :: Map.Map Int (Rational,Int)
g a b = if fst a < fst b then a else b
naive2 :: Ring Rational -> SSSP
naive2 p = runST $ do
parents <- MV.replicate (ringSize p) (-1)
costs <- MV.replicate (ringSize p) (-1)
MV.write parents 0 0
MV.write costs 0 0
changedRef <- newSTRef False
let loop i
| i == ringSize p = do
changed <- readSTRef changedRef
when changed $ do
writeSTRef changedRef False
loop 0
| otherwise = do
myCost <- MV.read costs i
unless (myCost < 0) $
forM_ (visibility V.! i) $ \n -> do
-- n is visible from i.
theirCost <- MV.read costs n
let throughCost = myCost + approxDist (ringAccess p i) (ringAccess p n)
when (throughCost < theirCost || theirCost < 0) $ do
MV.write parents n i
MV.write costs n throughCost
writeSTRef changedRef True
loop (i+1)
loop 0
V.unsafeFreeze parents
where
visibility = visibilityArray p
data PDual = PDual (V.Vector Int) Rational [PDual]
deriving (Show)
toPDual :: Ring Rational -> Dual -> PDual
toPDual p d =
case d of
Dual (a,b,c) l r ->
PDual (V.fromList [a,b,c])
(area2X (ringAccess p a) (ringAccess p b) (ringAccess p c))
(catMaybes [ worker c a l, worker b c r])
where
worker _ _ EmptyDual = Nothing
worker a b (NodeDual x l r) = Just $
PDual (V.fromList [a,x,b])
(area2X (ringAccess p a) (ringAccess p x) (ringAccess p b))
(catMaybes [ worker x b l, worker a x r])
pdualSize :: PDual -> Int
pdualSize (PDual _ _ children) = 1 + sum (map pdualSize children)
pdualArea :: PDual -> Rational
pdualArea (PDual _ faceArea _) = faceArea
-- FIXME: 'origin' isn't used. Remove.
pdualReduce :: Ring Rational -> PDual -> Int -> PDual
pdualReduce origin pdual n
| pdualSize pdual <= n = pdual
| otherwise =
let smallest = minimum $ pAreas pdual
in pdualReduce origin (merge smallest pdual) n
where
merge _s (PDual p faceArea []) = PDual p faceArea []
merge s (PDual p faceArea children)
| faceArea == s =
let (PDual p2 area2 children2:xs) = sortBy (comparing pdualArea) children
in PDual (joinP p p2) (faceArea+area2) (children2++xs)
| otherwise =
let (PDual p2 area2 children2:xs) = sortBy (comparing pdualArea) children
in if area2 == s
then PDual (joinP p p2) (faceArea+area2) (children2++xs)
else PDual p faceArea (map (merge s) children)
pAreas (PDual _ faceArea children) = faceArea : concatMap pAreas children
joinP a b = V.fromList (sort (V.toList a ++ V.toList b))
pdualRings :: Ring Rational -> PDual -> [Ring Rational]
pdualRings p (PDual pts _area children) =
ringPack (V.map (ringAccess p) pts) : concatMap (pdualRings p) children
-- Dual of triangulated polygon
data Dual = Dual (Int,Int,Int) -- (a,b,c)
DualTree -- borders ca
DualTree -- borders bc
deriving (Show)
data DualTree
= EmptyDual
| NodeDual Int -- axb triangle, a and b are from parent.
DualTree -- borders xb
DualTree -- borders ax
deriving (Show)
drawDual :: Dual -> String
drawDual d = drawTree $
case d of
Dual (a,b,c) l r -> Node (show (a,b,c)) [worker c a l, worker b c r]
where
worker _a _b EmptyDual = Node "Leaf" []
worker a b (NodeDual x l r) =
Node (show (b,a,x)) [worker x b l, worker a x r]
dualToTriangulation :: Ring Rational -> Dual -> Triangulation
dualToTriangulation p d = edgesToTriangulation (ringSize p) $ filter goodEdge $
case d of
Dual (a,b,c) l r ->
(a,b):(a,c):(b,c):worker c a l ++ worker b c r
where
goodEdge (a,b)
= a /= ringClamp p (b+1) && a /= ringClamp p (b-1)
worker _a _b EmptyDual = []
worker a b (NodeDual x l r) =
(a,x) : (x, b) : worker x b l ++ worker a x r
-- Dual path:
-- (Int,Int,Int) + V.Vector Int + V.Vector LeftOrRight
-- simplifyDual :: DualTree -> DualTree
-- -- simplifyDual (NodeDual x EmptyDual EmptyDual) = NodeLeaf x
-- -- simplifyDual (NodeDual x l EmptyDual) = NodeDualL x l
-- -- simplifyDual (NodeDual x EmptyDual r) = NodeDualR x r
-- simplifyDual d = d
dual :: Int -> Triangulation -> Dual
dual root t =
case hasTriangle of
[] -> error "weird triangulation"
-- [] -> Dual (0,1,V.length t-1) EmptyDual (dualTree t (1, (V.length t-1)) 0)
(x:_) -> Dual (root,rootNext,x) (dualTree t (x,root) rootNext) (dualTree t (rootNext,x) root)
where
rootNext = idx (root+1)
rootPrev = idx (root-1)
rootNNext = idx (root+2)
idx i = i `mod` n
hasTriangle = (rootPrev : t V.! root) `intersect` (rootNNext : t V.! rootNext)
n = V.length t
-- a=6, b=0, e=1
dualTree :: Triangulation -> (Int,Int) -> Int -> DualTree
dualTree t (a,b) e = -- simplifyDual $
case hasTriangle of
[] -> EmptyDual
[(ab)] ->
NodeDual ab
(dualTree t (ab,b) a)
(dualTree t (a,ab) b)
_ -> error $ "Invalid triangulation: " ++ show (a,b,e,hasTriangle)
where
hasTriangle = (prev a : next a : t V.! a) `intersect` (prev b : next b : t V.! b)
\\ [e]
n = V.length t
next x = (x+1) `mod` n
prev x = (x-1) `mod` n
-- data MinMax = MinMax Int Int | MinMaxEmpty deriving (Show)
-- instance Semigroup MinMax where
-- MinMaxEmpty <> b = b
-- a <> MinMaxEmpty = a
-- MinMax a b <> MinMax c d
-- = MinMax (min a c) (max b d)
-- -- = MinMax c b
-- instance Monoid MinMax where
-- mempty = MinMaxEmpty
--
-- instance F.Measured MinMax Int where
-- measure i = MinMax i i
-- dualRoot :: Dual -> Int
-- dualRoot (Dual (a,_,_) _ _) = a
-- O(n*ln n), could be O(n) if I could figure out how to use fingertrees...
sssp :: (Fractional a, Ord a) => Ring a -> Dual -> SSSP
sssp p d = toSSSP $
case d of
Dual (a,b,c) l r ->
(a, a) :
(b, a) :
(c, a) :
worker [c] [b] a r ++
loopLeft a c l
where
toSSSP edges =
(V.fromList . map snd . sortOn fst) edges
loopLeft a outer l =
case l of
EmptyDual -> []
NodeDual x l' r' ->
(x,a) :
worker [x] [outer] a r' ++
loopLeft a x l'
searchFn _checkStep _cusp _x [] = Nothing
searchFn checkStep cusp x (y:ys)
| not (checkStep (ringAccess p cusp) (ringAccess p y) (ringAccess p x))
= Just $ helper [] y ys
| otherwise = Nothing
where
helper acc v [] = (v, [], reverse acc)
helper acc v1 (v2:vs)
| checkStep (ringAccess p v1) (ringAccess p v2) (ringAccess p x) =
(v1, v2:vs, reverse acc)
| otherwise = helper (v1:acc) v2 vs
searchRight = searchFn isLeftTurn
searchLeft = searchFn isRightTurn
-- adj x = x -- ringClamp p (x-dualRoot d)
-- optTrace msg =
-- if False -- dualRoot d == 1 || dualRoot d == 0
-- then trace msg
-- else id
worker _ _ _ EmptyDual = []
worker f1 f2 cusp (NodeDual x l r) =
-- (optTrace ("Funnel: " ++ show
-- (map adj $ toList f1
-- ,adj cusp
-- ,map adj $ toList f2
-- ,adj x
-- , dualRoot d))
-- ) $
case searchLeft cusp x (toList f1) of
Just (v, f1Hi, f1Lo) ->
-- optTrace (" Visble from left: " ++ show (adj x,adj v)) $
(x, v::Int) :
worker f1Hi [x] v l ++
worker (f1Lo ++ [v, x]) f2 cusp r
Nothing ->
case searchRight cusp x (toList f2) of
Just (v, f2Hi, f2Lo) ->
-- optTrace (" Visble from right: " ++ show (adj x,adj v)) $
(x, v::Int) :
worker f1 (f2Lo ++ [v, x]) cusp l ++
worker [x] f2Hi v r
Nothing ->
-- optTrace (" Visble from cusp: " ++ show (adj x,adj cusp)) $
(x, cusp::Int) :
worker f1 [x] cusp l ++
worker [x] f2 cusp r