>
     Display Functions for Sequences of Cartesian Coordinates

> module DrawingUtilities
>   (makeChart, displayChart)
>   where

>   import SequenceUtilities(reps)
>   import IOutilities(spaces, rightJustify) 
>   import NumericUtilities(digitize)

deliver picture and associated scale figures
given character to use to mark points, picture dimensions, and points

>   makeChart ::
>    RealFrac num => Char -> Int -> Int -> [[num]] -> ([num], [String])
>   makeChart dot width height points =
>     ([minHorizontal,maxHorizontal,minVertical,maxVertical], picture)
>     where
>     minHorizontal = minimum[x | [x, y] <- points]
>     maxHorizontal = maximum[x | [x, y] <- points]
>     minVertical   = minimum[y | [x, y] <- points]
>     maxVertical   = maximum[y | [x, y] <- points]
>     picture = sequenceOfUpdates (blankPicture width height)
>     sequenceOfUpdates =
>       foldr (.) id [updatePicture dot digitalPoint |
>                                  digitalPoint <- digitalPoints]
>     digitalPoints =
>      [[digitize width minHorizontal maxHorizontal x,
>        digitize height minVertical maxVertical y] | [x, y] <- points]


make blank picture

>   blankPicture :: Int -> Int -> [String]
>   blankPicture width height = reps height (spaces width)


convert chart (from makeChart function) to displayable string

>   displayChart ::
>     RealFrac num => [String] -> ([num], [String]) -> String
>   displayChart
>     [horizontalAxisName, verticalAxisName]
>     ([minHorizontal,maxHorizontal,minVertical,maxVertical], picture)=
>     unlines(
>       [topBorder] ++
>       [addSideBorders verticalBarOnLeft row | row <- chartAbove]   ++
>       [addSideBorders barWithAxisName row | row <- chartAtAxisName]++
>       [addSideBorders verticalBarOnLeft row | row <- chartBelow]   ++
>       [bottomBorder] ++
>       [horizontalLabels])
>     where
>     topBorder    = maxVerticalLabelPadded ++ horizontalBorder
>     bottomBorder = minVerticalLabelPadded ++ horizontalBorder
>     maxVerticalLabel   = show maxVertical
>     minVerticalLabel   = show minVertical
>     maxHorizontalLabel = show maxHorizontal
>     minHorizontalLabel = show minHorizontal
>     pictureWithMaxAtTop = reverse picture
>     (chartAbove, chartAtAndBelow) =
>       splitAt (pictureHeight `div` 2) pictureWithMaxAtTop
>     (chartAtAxisName, chartBelow) = splitAt 1 chartAtAndBelow
>     verticalBarOnLeft = rightJustify verticalLabelWidth "|"
>     barWithAxisName =
>       rightJustify verticalLabelWidth (verticalAxisName ++ "|")
>     verticalLabelWidth =
>       1 + maximum[length label | label <- verticalAxisLabels]
>     verticalAxisLabels =
>       [maxVerticalLabel, minVerticalLabel, verticalAxisName]
>     maxVerticalLabelPadded =
>       rightJustify verticalLabelWidth (maxVerticalLabel ++ "|")
>     minVerticalLabelPadded =
>       rightJustify verticalLabelWidth (minVerticalLabel ++ "|")
>     horizontalBorder = reps pictureWidth '-'  ++ "|"
>     addSideBorders label row = label ++  row  ++ "|"
>     horizontalLabels =
>       verticalBarOnLeft ++ minHorizontalLabel ++
>       spaces before ++ horizontalAxisName ++ spaces after ++ "|" ++
>       maxHorizontalLabel
>     spaceRemaining =
>       max 0 (pictureWidth -
>              (length minHorizontalLabel + length horizontalAxisName))
>     before = spaceRemaining `div` 2
>     after  = spaceRemaining - before
>     pictureWidth  = (length . head) picture
>     pictureHeight = length picture


function: updatePicture
  inserts a dot in a picture at a specified point;
  a point's position in the picture is represented by
  a sequence of two integers [i, j], where i is the
  distance from the left edge and j is the distance
  from the top;
  a picture is represented as a sequence of strings,
  each of which has the same length; the top of the
  picture is the first string and the left edge is
  the first character of each string; indexing starts
  at zero in both cases

>   updatePicture :: a -> [Int] -> [[a]] -> [[a]]
>   updatePicture dot [distanceFromLeft, distanceFromTop] =
>     update (update dotSetter distanceFromLeft) distanceFromTop
>     where
>     dotSetter anything = dot
  

function: update
  uses a function supplied as an argument to make a change
  in an element of a sequence; the element to be changed
  is specified in the second argument as a zero-based index

>   update :: (a -> a) -> Int  -> [a] -> [a]
>   update modifier zeroBasedIndex xs =
>     before ++ [modifier x] ++ after
>     where
>     (before, atAndBeyond) = splitAt zeroBasedIndex xs
>     ([x], after) = splitAt 1 atAndBeyond
Last Modified: