packages feed

hgeometry-0.12.0.0: planargraph.hs

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
module Main where

import Data.PlanarGraph.Immutable
import qualified Data.PlanarGraph.Mutable as Mut

import           Control.Lens          ()
import           Control.Monad.ST
import           Data.Ext
import           Data.Foldable         as F
import           Data.Geometry.Point
import           Data.Geometry.Polygon
import           Data.STRef
import           Data.Hashable
import qualified Data.Text             as T
import qualified Data.Vector           as V
import qualified Data.Vector.Circular  as CV
import           Graphics.SvgTree      (Number (..))
import           Linear.Metric
import           Linear.V2
import           Linear.Vector
import           Reanimate
import           Reanimate.Animation
import           System.Directory
import           System.FilePath
import Debug.Trace

graphs :: [PlanarGraph]
graphs =
  [ pgFromFaces [[0..2]]
  , pgFromFaces [[1,2,3]]
  , pgFromFaces [[0..3]]
  , pgFromFaces [[0..3],[4,3,2,1]]
  , let pg = pgFromFaces [[0..3]]
    in pgMutate pg $ \pg' -> do
          let he0 = Mut.halfEdgeFromId 0 pg'
              he4 = Mut.halfEdgeFromId 4 pg'
          _newEdge <- Mut.pgConnectVertices he0 he4
          return ()
  , pgFromFaces [[0,4,1],[0,1,2],[4,3,1],[4,5,3],[3,5,2],[2,5,0]]
  ]

main :: IO ()
-- main = reanimate $ staticFrame (1/60) (fst test2)
main = do
  forM_ graphs savePlanarGraphSVG

-- test1 = renderPlanarGraph (pgFromFaces [[0..2]])
-- test2 = renderPlanarGraph (pgFromFaces [[0..3],[4,3,2,1]])

savePlanarGraphSVG :: PlanarGraph -> IO ()
savePlanarGraphSVG pg = do
    writeFile fileName svgOutput
    writeFile compactName compactOutput
  where
    svgOutput = renderSvg (Just $ Num 300) (Just $ Num 300) svg
    compactOutput = renderSvg (Just $ Num 300) (Just $ Num 300) compactSvg
    fileName = "planargraph-" ++ show (hash pg) <.> "svg"
    compactName = "planargraph-" ++ show (hash pg) <.> "compact" <.> "svg"
    defOpts = RenderOptions { disableHalfEdges = False }
    compactOpts = RenderOptions { disableHalfEdges = True }
    svg = renderPlanarGraph defOpts pg
    compactSvg = renderPlanarGraph compactOpts pg

data RenderOptions = RenderOptions
  { disableHalfEdges :: Bool }

renderPlanarGraph :: RenderOptions -> PlanarGraph -> SVG
renderPlanarGraph RenderOptions{..} pg = svg
  where
    vs = tutteEmbedding pg
    faces = pgFaces pg
    svg =
        withViewBox (screenBottom, screenBottom, screenHeight, screenHeight) $
        mkGroup
        [ mkBackground bgColor
        , mkGroup
          [ translate (x*scaleFactor) (y*scaleFactor) $
            mkGroup
            [ label
            , withFillOpacity 0 $ withStrokeColor "black" $ withStrokeWidth strokeWidth $
              -- mkRect (strokeWidth+svgWidth label*1.5) (strokeWidth+svgHeight label*1.5)
              translate 0 (-(svgHeight label+0.1)/2) $
              center $ mkLine (0,0) (svgWidth label*1.2,0)
            ]
          | face <- faces
          , let boundary = faceBoundary face pg
                poly = simpleFromPoints $ map ext
                        [ Point2 x y
                        | vId <- boundary
                        , let V2 x y = vs V.! vertexId vId
                        ]
                Point2 x y = centroid poly
                label = scale 0.5 $ center $ latex (T.pack $ show $ faceId face)
          ]
        , mkGroup
          [ mkGroup $
            [ withStrokeColor "black" $
              mkLine (tipX*scaleFactor, tipY*scaleFactor) (tailX*scaleFactor, tailY*scaleFactor)
            ] ++ if disableHalfEdges
              then []
              else 
                [ translate (halfX*scaleFactor + angY) (halfY*scaleFactor - angX) $
                  labelTwin
                , translate (halfX*scaleFactor - angY) (halfY*scaleFactor + angX) $
                  labelEdge
                ]
          | edge <- pgEdges pg
          , let (tip, tail) = edgeHalfEdges edge
                V2 tipX tipY = vs V.! vertexId (halfEdgeVertex tip pg)
                V2 tailX tailY = vs V.! vertexId (halfEdgeVertex tail pg)
                (halfX,halfY) = (tipX + (tailX-tipX)/2, tipY + (tailY-tipY)/2)
                labelEdge = scale 0.4 $ center $ latex (T.pack $ show $ halfEdgeId tip)
                labelTwin = scale 0.4 $ center $ latex (T.pack $ show $ halfEdgeId tail)
                V2 angX angY = signorm (V2 tipX tipY - V2 tailX tailY) ^* 0.3
          ]
        , mkGroup
          [ translate (x*scaleFactor) (y*scaleFactor) $
            mkGroup
            [ withFillOpacity 1 $ withFillColor "white" $ withStrokeColor "black" $
              mkCircle 0.3
            , label ]
          | v <- map vertexId $ pgVertices pg
          , let V2 x y = vs V.! v
                label = scaleToWidth 0.2 $ center $ latex (T.pack $ show v)
          ]
        ]

strokeWidth = defaultStrokeWidth*0.5

bgColor :: String
bgColor = "white"

scaleFactor :: Double
scaleFactor = screenTop*0.8