packages feed

hpc-strobe-0.1: demonstration/StrobeClock.hs

-- StrobeClock.hs: Demonstrate hpc-strobe by rendering a crude analog
-- clock in the marked-up code
-- Copyright (c) 2009, Thorkil Naur
--
-- Usage: ./StrobeClock tixfile-directory
--
-- Note: The indicated tixfile-directory must exist.
--

module Main where

  import System
  import IO
  import Time
  import Control.Concurrent
  import List

  import Trace.Hpc.Strobe

  progName = "StrobeClock"
  progStamp = "2009-May-08 17.26"

  -- The clock will be rendered in the following part of the code. The
  -- number of lines and their width may be adjusted; the width of the
  -- shortest line will determine the width of the clock.

  canvas x
    = [
          [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
        , [x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x]
      ]

  -- Canvas dimensions:

  pixelHeight = (length $ canvas 0) - 1
  pixelWidth = minimum $ map ((subtract 1) . length) $ canvas 0

  -- Defines the shape of pixels: To calibrate, measure the height and
  -- width of a square part of the canvas as rendered and enter the
  -- ratio here:

  pixelHeightDividedByWidth = 16.2/17.4

  -- World coordinate system is such that a 2x2 square with (0,0) in its
  -- center fits exactly inside the canvas rectangle:

  worldDimensions
    = let
        rawDimensions = [1.0,pixelHeightDividedByWidth]
      in
        map ((*2.0) . (/(minimum rawDimensions))) rawDimensions

  worldLowerLeftCorner = map (negate . (/2.0)) worldDimensions

  -- Converting between coordinate systems:

  canvasToWorld [row,col]
    = zipWith (+) worldLowerLeftCorner $ zipWith (*) worldDimensions
        [fromIntegral col / fromIntegral pixelWidth,
          1.0 - fromIntegral row / fromIntegral pixelHeight]

  worldToCanvas wcs
    = let
        [c0,r0]
          = zipWith (/) (zipWith (-) wcs worldLowerLeftCorner)
              worldDimensions
      in
        [round $ (1.0 - r0) * fromIntegral pixelHeight,
          round $ c0 * fromIntegral pixelWidth]

  -- Shade outside unit circle:

  circleShade
    = filter ((>1.0) . sum . map (^2) . canvasToWorld)
        [ [row,col] | row <- [0..pixelHeight], col <- [0..pixelWidth] ]

  -- Line:

  canvasLine [r1,c1] [r2,c2]
    = if abs (r2 - r1) > abs (c2 - c1) then
        map reverse $ canvasLine [c1,r1] [c2,r2]
      else
        if abs (c2 - c1) > 0 then
          let
            [(c1',r1'),(c2',r2')] = sort [(c1,r1),(c2,r2)]
          in
            [ [r,c] | c <- [c1'..c2'],
              let r = r1' + (round $ fromIntegral (r2' - r1')
                              * fromIntegral (c - c1')
                              / fromIntegral (c2' - c1')) ]
        else
          [[r1,c1]]

  worldLine [x1,y1] [x2,y2]
    = canvasLine (worldToCanvas [x1,y1]) (worldToCanvas [x2,y2])

  -- Piece of radial line, angle measured in degrees from vertical,
  -- clockwise:

  worldRadial angle from to
    = let
        radians = (90.0 - angle) / 180.0 * pi
        [p1,p2]
          = map (\t -> map (*t) [cos radians,sin radians]) [from,to]
      in
        worldLine p1 p2

  -- Clock:

  worldClockFixed
    = concat [ worldRadial (fromIntegral a) 0.9 1.0
               | a <- [30,60..360] ]
      ++ concat [ worldRadial (fromIntegral a) 0.8 1.0
                  | a <- [90,180..360] ]
      ++ circleShade

  worldClockVariable h m s
    = concat [ worldRadial ah 0.0 0.55 ]
      ++ concat [ worldRadial (ah+4.0) 0.0 0.45 ]
      ++ concat [ worldRadial (ah-4.0) 0.0 0.45 ]
      ++ concat [ worldRadial am 0.0 0.9 ]
      ++ concat [ worldRadial (am-2.5) 0.0 0.8 ]
      ++ concat [ worldRadial (am+2.5) 0.0 0.8 ]
      ++ concat [ worldRadial as (-0.15) 1.0 ]
      where
      as = fromIntegral s * (360.0/60.0)
      m' = fromIntegral m + fromIntegral s / 60.0
      am = m'*(360/60.0)
      ah = (fromIntegral h + m'/60.0)*(360.0/12.0)

  main'
    = do
        putStrLn $
          progName ++ "(" ++ progStamp ++ "): Canvas pixel height "
            ++ show pixelHeight ++ ", pixel width " ++ show pixelWidth
        mapM_
          (\n ->
            do
              clTime <- getClockTime
              localTime@(CalendarTime{ctHour = hr,
                ctMin = mn, ctSec = sc}) <- toCalendarTime clTime
              let
                timeStamp = calendarTimeToString localTime
                in
                  do
                    putStrLn $ progName ++ "(" ++ progStamp ++ "): "
                      ++ (show $
                            sum [ ((canvas n)!!i)!!j
                                  | [i,j] <- worldClockFixed
                                     ++ worldClockVariable hr mn sc ])
              threadDelay 950000
          ) [1..]

  mainArgsInterpret [tixfileDirectory]
    = withStrobesWrittenRegularly tixfileDirectory progName 1000000
        main'

  mainArgsInterpret args
    = error $ "Usage: \"./" ++ progName ++ " tixfile-directory\""

  main
    = do
        hSetBuffering stdout NoBuffering
        args <- getArgs
        mainArgsInterpret args