module Main where
import Data.Cluster.ROC
import Data.Monoid
import Graphics.Gloss.Interface.Pure.Game
import Options.Applicative
import qualified Data.Foldable as F
import qualified Data.Sequence as S
import Debug.Trace
data Vec2 = Vec2 !Float !Float
deriving (Eq, Show)
instance ClusterSpace Vec2 where
pointZero = Vec2 0 0
pointAdd (Vec2 x1 y1) (Vec2 x2 y2) = Vec2 (x1 + x2) (y1 + y2)
pointScale v (Vec2 x y) = Vec2 (realToFrac v * x) (realToFrac v * y)
-- gaus kernell with sigma = 1
pointKernel (Vec2 x1 y1) (Vec2 x2 y2) = realToFrac $ exp ( negate $ 0.5 * ((x2 - x1) ^ 2 + (y2 - y1) ^ 2) )
pointDistanceSquared x y = 2 - 2 * pointKernel x y
{-# INLINE pointZero #-}
{-# INLINE pointAdd #-}
{-# INLINE pointScale #-}
{-# INLINE pointKernel #-}
{-# INLINE pointDistanceSquared #-}
data World = World {
worldClusters :: ROCContext Vec2
, worldWidth :: Float
, worldHeight :: Float
, worldNewPoints :: S.Seq Vec2
, worldOldPoints :: S.Seq Vec2
}
runClustering :: ROCConfig -> IO ()
runClustering cfg = play display bg fps world render handleEvent update
where
width = 800
height = 800
display = InWindow "ROC demo" (width, height) (100, 100)
bg = greyN 0.9
fps = 60
world = World (emptyROCContext cfg) (fromIntegral width) (fromIntegral height) mempty mempty
render :: World -> Picture
render World{..} = scaleWorld $ drawOrigin <> drawNewPoints <> drawOldPoints <> drawClusters
where
scaleWorld = scale (worldWidth*0.5) (worldHeight*0.5)
drawOrigin =
line [(-0.9, 0), (0.9, 0)]
<> line [(0, -0.9), (0, 0.9)]
drawNewPoints = F.foldMap (\(Vec2 x y) -> translate x y $ color red $ circleSolid 0.005) worldNewPoints
drawOldPoints = F.foldMap (\(Vec2 x y) -> translate x y $ color blue $ circleSolid 0.005) worldOldPoints
drawClusters = F.foldMap drawCluster $ rocPrototypes worldClusters
drawCluster p = let
Vec2 x y = prototypeValue p
r = realToFrac $ 0.1 / (1 + exp (negate $ prototypeWeight p) )
in translate x y $ circle r <> line [(-0.005, 0), (0.005, 0)] <> line [(0, -0.005), (0, 0.005)]
handleEvent :: Event -> World -> World
handleEvent (EventResize (w, h)) world = world {
worldWidth = fromIntegral w
, worldHeight = fromIntegral h
}
handleEvent (EventKey (MouseButton LeftButton) Down _ (x, y)) world@World{..} = world {
worldNewPoints = worldNewPoints S.|> Vec2 (2 * x / worldWidth) (2 * y / worldHeight)
}
handleEvent (EventKey (MouseButton RightButton) Down _ _) world@World{..} = world {
worldNewPoints = mempty
, worldOldPoints = worldOldPoints <> worldNewPoints
, worldClusters = clusterize worldNewPoints worldClusters
}
handleEvent _ world = world
update :: Float -> World -> World
update = const id
data Options = Options {
threshold :: Double
, maxClusters :: Int
}
toROCConfig :: Options -> ROCConfig
toROCConfig Options{..} = defaultROCConfig {
rocThreshold = threshold
, rocMaxClusters = maxClusters
}
configParser :: Parser ROCConfig
configParser = fmap toROCConfig $ Options
<$> option auto (
long "threshold"
<> help "Cluster with lower weight will be deleted at post-process step."
<> value 0.1
)
<*> option auto (
long "maxclusters"
<> help "Maximum count of allowed clusters."
<> value 10
)
main :: IO ()
main = runClustering =<< execParser opts
where
opts = info (configParser <**> helper)
( fullDesc
<> progDesc "Run interactive demo for ROC clustering algorithm"
<> header "roc-cluster-demo - a demo for roc-cluster-demo" )