File: AreaSpots.hs

package info (click to toggle)
haskell-chart 1.9.5-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 396 kB
  • sloc: haskell: 4,680; makefile: 3
file content (192 lines) | stat: -rw-r--r-- 8,074 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
-- |
-- Module     :  Graphics.Rendering.Chart.Plot.AreaSpots
-- Copyright  :  (c) Malcolm Wallace 2009
-- License    :  BSD-style (see COPYRIGHT file)
--
-- Area spots are a collection of unconnected filled circles,
-- with x,y position, and an independent z value to be represented
-- by the relative area of the spots.

{-# LANGUAGE TemplateHaskell #-}

module Graphics.Rendering.Chart.Plot.AreaSpots
  ( AreaSpots(..)

  , area_spots_title
  , area_spots_linethick
  , area_spots_linecolour
  , area_spots_fillcolour
  , area_spots_opacity
  , area_spots_max_radius
  , area_spots_values

  , AreaSpots4D(..)

  , area_spots_4d_title
  , area_spots_4d_linethick
  , area_spots_4d_palette
  , area_spots_4d_opacity
  , area_spots_4d_max_radius
  , area_spots_4d_values
  ) where

import Graphics.Rendering.Chart.Geometry hiding (scale, x0, y0)
import Graphics.Rendering.Chart.Drawing
import Graphics.Rendering.Chart.Plot.Types
import Graphics.Rendering.Chart.Axis
import Control.Lens
import Data.Colour hiding (over)
import Data.Colour.Names
import Data.Default.Class

import Control.Monad

-- | A collection of unconnected spots, with x,y position, and an
--   independent z value to be represented by the area of the spot.
data AreaSpots z x y = AreaSpots
  { _area_spots_title      :: String
  , _area_spots_linethick  :: Double
  , _area_spots_linecolour :: AlphaColour Double
  , _area_spots_fillcolour :: Colour Double
  , _area_spots_opacity    :: Double
  , _area_spots_max_radius :: Double   -- ^ the largest size of spot
  , _area_spots_values     :: [(x,y,z)]
  }

instance Default (AreaSpots z x y) where
  def = AreaSpots
    { _area_spots_title      = ""
    , _area_spots_linethick  = 0.1
    , _area_spots_linecolour = opaque blue
    , _area_spots_fillcolour = blue
    , _area_spots_opacity    = 0.2
    , _area_spots_max_radius = 20  -- in pixels
    , _area_spots_values     = []
    }

instance (PlotValue z) => ToPlot (AreaSpots z) where
    toPlot p = Plot { _plot_render = renderAreaSpots p
                    , _plot_legend = [(_area_spots_title p, renderSpotLegend p)]
                    , _plot_all_points = ( map (^._1) (_area_spots_values p)
                                         , map (^._2) (_area_spots_values p) )
                    }

renderAreaSpots  :: (PlotValue z) => AreaSpots z x y -> PointMapFn x y -> BackendProgram ()
renderAreaSpots p pmap = 
    forM_ (scaleMax (_area_spots_max_radius p^(2::Integer))
                    (_area_spots_values p))
          (\ (x,y,z)-> do
              let radius = sqrt z
              let psSpot = filledCircles radius $
                                                    flip withOpacity 
                                                      (_area_spots_opacity p) $
                                                    _area_spots_fillcolour p
              drawPoint psSpot (pmap (LValue x, LValue y))
              let psOutline = hollowCircles radius
                                                      (_area_spots_linethick p)
                                                      (_area_spots_linecolour p)
              drawPoint psOutline (pmap (LValue x, LValue y))
          )
  where
    scaleMax :: PlotValue z => Double -> [(x,y,z)] -> [(x,y,Double)]
    scaleMax n points = let largest  = maximum (map (^._3.to toValue) points)
                            scale v  = n * toValue v / largest
                        in over (mapped._3) scale points

renderSpotLegend :: AreaSpots z x y -> Rect -> BackendProgram ()
renderSpotLegend p (Rect p1 p2) = do
    let radius = min (abs (p_y p1 - p_y p2)) (abs (p_x p1 - p_x p2))
        centre = linearInterpolate p1 p2
        psSpot = filledCircles radius $ withOpacity 
                                        (_area_spots_fillcolour p)
                                        (_area_spots_opacity p)
        psOutline = hollowCircles radius (_area_spots_linethick p)
                                         (_area_spots_linecolour p)
    drawPoint psSpot centre
    drawPoint psOutline centre
  where
    linearInterpolate (Point x0 y0) (Point x1 y1) =
        Point (x0 + abs(x1-x0)/2) (y0 + abs(y1-y0)/2)

-- | A collection of unconnected spots, with x,y position, an
--   independent z value to be represented by the area of the spot,
--   and in addition, a fourth variable t to be represented by a colour
--   from a given palette.  (A linear transfer function from t to palette
--   is assumed.)
data AreaSpots4D z t x y = AreaSpots4D
  { _area_spots_4d_title      :: String
  , _area_spots_4d_linethick  :: Double
  , _area_spots_4d_palette    :: [Colour Double]
  , _area_spots_4d_opacity    :: Double
  , _area_spots_4d_max_radius :: Double        -- ^ the largest size of spot
  , _area_spots_4d_values     :: [(x,y,z,t)]
  }

instance Default (AreaSpots4D z t x y) where
  def = AreaSpots4D
    { _area_spots_4d_title      = ""
    , _area_spots_4d_linethick  = 0.1
    , _area_spots_4d_palette    = [ blue, green, yellow, orange, red ]
    , _area_spots_4d_opacity    = 0.2
    , _area_spots_4d_max_radius = 20  -- in pixels
    , _area_spots_4d_values     = []
    }

instance (PlotValue z, PlotValue t, Show t) => ToPlot (AreaSpots4D z t) where
    toPlot p = Plot { _plot_render = renderAreaSpots4D p
                    , _plot_legend = [ (_area_spots_4d_title p
                                       , renderSpotLegend4D p) ]
                    , _plot_all_points = ( map (^._1) (_area_spots_4d_values p)
                                         , map (^._2) (_area_spots_4d_values p) )
                    }

renderAreaSpots4D  :: (PlotValue z, PlotValue t, Show t) =>
                      AreaSpots4D z t x y -> PointMapFn x y -> BackendProgram ()
renderAreaSpots4D p pmap = 
    forM_ (scaleMax (_area_spots_4d_max_radius p^(2::Integer))
                    (length (_area_spots_4d_palette p))
                    (_area_spots_4d_values p))
          (\ (x,y,z,t)-> do
              let radius  = sqrt z
              let colour  = _area_spots_4d_palette p !! t 
              let psSpot
                    = filledCircles radius $
                          withOpacity colour (_area_spots_4d_opacity p)
              drawPoint psSpot (pmap (LValue x, LValue y))
              let psOutline
                    = hollowCircles radius (_area_spots_4d_linethick p)
                                           (opaque colour)
              drawPoint psOutline (pmap (LValue x, LValue y))
          )
  where
    scaleMax :: (PlotValue z, PlotValue t, Show t) =>
                Double -> Int -> [(x,y,z,t)] -> [(x,y,Double,Int)]
    scaleMax n c points = let largest  = maximum (map (^._3.to toValue) points)
                              scale v  = n * toValue v / largest
                              colVals  = map (^._4.to toValue) points
                              colMin   = minimum colVals
                              colMax   = maximum colVals
                              select t = min (c-1) $ 
                                         truncate ( fromIntegral c
                                                       * (toValue t-colMin)
                                                       / (colMax-colMin))
                          in map (\ (x,y,z,t) -> (x,y, scale z, select t))
                                 points

renderSpotLegend4D :: AreaSpots4D z t x y -> Rect -> BackendProgram ()
renderSpotLegend4D p (Rect p1 p2) = do
    let radius = min (abs (p_y p1 - p_y p2)) (abs (p_x p1 - p_x p2))
        centre = linearInterpolate p1 p2
        palCol = head $ _area_spots_4d_palette p
        psSpot = filledCircles radius $ withOpacity palCol
                                        (_area_spots_4d_opacity p)
        psOutline = hollowCircles radius (_area_spots_4d_linethick p)
                                         (opaque palCol)
    drawPoint psSpot centre
    drawPoint psOutline centre
  where
    linearInterpolate (Point x0 y0) (Point x1 y1) =
        Point (x0 + abs(x1-x0)/2) (y0 + abs(y1-y0)/2)

$( makeLenses ''AreaSpots )
$( makeLenses ''AreaSpots4D )