packages feed

hps-0.1: Help/HaskellPostScript.hs

import Fractal
import Graphics.PS hiding (a0, a1)
import Graphics.PS.Cairo
import System.Random
import Data.Maybe
import Data.List

-- | Group a list into a list of n element lists.
clump :: Int -> [a] -> [[a]]
clump _ [] = []
clump n l = i : clump n j where (i,j) = splitAt n l

-- | Generate a list of n random numbers in the range [l,r].
randn :: Int -> Int -> Double -> Double -> [Double]
randn s n l r = (map f . take n . randoms . mkStdGen) s
    where f i = i * (r - l) + l

-- | Grey fill operator.
gfill :: Double -> Path -> Image
gfill g = Fill (greyGS g)

-- | Grey stroke operator.
gstroke :: Double -> Path -> Image
gstroke g = Stroke (greyGS g)

-- | Crappy normalizer, ought to do bounds checks and take paper size.
normalize :: Path -> Path
normalize p = (translate s s . scale z z) p
    where s = 250
          z = 250

-- Text is a path constructor.  The first argument is a Font, the
-- second a list of Glyphs.  Ordinarly a Text path is filled, however
-- it can be stroked as below.

simpleText :: Image
simpleText = gstroke 0.25 t
    where t = MoveTo (Pt 100 400) +++ Text (Font "Times" 72) "SimpleText"

-- A random scattering of filled grey rectangles.

rectangle_ :: [Double] -> Image
rectangle_ [g,x,y,z] = gfill g (shift (rectangle origin 1 1))
    where  x'     = x * 500
           y'     = y * 500
           z'     = z * 64
           shift  = translate x' y' . scale z' z'
rectangle_ _ = error "illegal rectangle_"

-- Angles in Hps are in radians and counter-clockwise.

-- A Path must begin with a MoveTo operator.

-- A random scattering of stroked arcs.  arc makes a path composed of
-- bezier curves.

semiarc :: [Double] -> Image
semiarc [g,x,y,z] = (gstroke g . shift . mkValid) (arc origin 1 0 (1.5 * pi))
    where  x'    = x * 500
           y'    = y * 500
           z'    = z * 64
           shift = translate x' y' . scale z' z'
semiarc _ = error "illegal semiarc"

-- A random set of annular sections.  annular makes a path composed of
-- arcs and lines.  g = grey, ir = inner radius, xr = outer radius, sa
-- = start angle, a = angle.

semiann :: (Double -> Path -> Image) -> [Double] -> Image
semiann f [g,ir,xr,sa,a] = f g $ shift $ mkValid $ annular origin ir' xr' sa' a'
    where  x      = 250
           y      = 250
           z      = 250
           sa'    = sa * 2.0 * pi
           a'     = a * pi
           ir'    = min ir xr
           xr'    = max ir xr
           shift  = translate x y . scale z z
semiann _ _ = error "illegal semiann"

-- |flatten| applies all tranformations on a path generating a new
-- path.

curve_ex :: Image
curve_ex = s 0 c `over` s 0.5 l
    where p0 = Pt 0.1 0.5
          p1 = Pt 0.4 0.9
          p2 = Pt 0.6 0.1
          p3 = Pt 0.9 0.5
          c  = MoveTo p0 +++ CurveTo p1 p2 p3
          l  = MoveTo p0 +++ LineTo p1 +++ MoveTo p2 +++ LineTo p3
          s n = gstroke n . normalize

startPt_ :: Path -> Pt
startPt_  = fromJust . startPt

endPt_ :: Path -> Pt
endPt_  = fromJust . endPt

--arcd_ex :: Image
arcd_ex :: (Pt -> Double -> Double -> Double -> Path) -> Image
arcd_ex arcd = s 0 e0 `over` f 0.9 e1 `over` s 0.5 e2
    where c  = Pt 0.5 0.5
          r  = 0.4
          a0 = radians 45
          a1 = radians 180
          e0 = mkValid $ arcd c r a0 a1
          e1 = mkValid $ arc c 0.05 0 (2 * pi)
          e2 = MoveTo c +++ LineTo (startPt_ e0) +++ MoveTo c +++ LineTo (endPt_ e0)
          s n = gstroke n . normalize
          f n = gfill n . normalize

path_ex' :: Path
path_ex' = MoveTo (Pt 0.5 0.1)
           +++ LineTo (Pt 0.9 0.9)
           +++ LineTo (Pt 0.5 0.9)
           +++ CurveTo (Pt 0.2 0.9) (Pt 0.2 0.5) (Pt 0.5 0.5)


-- | Render each (p1, p2) as a distinct line.
renderLines :: [(Pt, Pt)] -> Path
renderLines = foldl f (MoveTo origin)
    where f pth (p1, p2) = pth +++ MoveTo p1 +++ LineTo p2

-- | Collapse line sequences into a single line.
renderLinesO :: [(Pt, Pt)] -> Path
renderLinesO = foldl f (MoveTo origin) . snd . mapAccumL g origin
    where g p (a,b) | p == a = (b, Right b)
                    | otherwise = (b, Left (a,b))
          f pth (Left (p1, p2)) = pth +++ MoveTo p1 +++ LineTo p2
          f pth (Right p2) = pth +++ LineTo p2

main :: IO ()
main = 
    let  rightAngles = renderLines (fractal (Pt 250 250) (Pt 175 175) 12)
         rightAnglesO = renderLinesO (fractal (Pt 250 250) (Pt 175 175) 12)
         arrows = fractalArrow 200 9
         rectangles = (foldl1 over . map rectangle_ . clump 4) (randn 1 240 0 1)
         semiarcs = (foldl1 over . map semiarc . clump 4) (randn 1 120 0 1)
         semiannsS = foldl1 over $ map (semiann gstroke) $ clump 5 $ randn 1 50 0 1
         semiannsF = foldl1 over $ map (semiann gfill) $ clump 5 $ randn 1 50 0 1
         arc_ex  = arcd_ex arc
         arcNegative_ex = arcd_ex arcNegative
         path_ex = s 0 path_ex'
         approx_ex n = s 0 (approx n path_ex')
         fs_ex = s 0 e `over` f 0.9 e where e = close path_ex'
         s n = gstroke n . normalize
         f n = gfill n . normalize
    in cg "test.ps" (Paper 900 600) [ gstroke 0 rightAngles
                                    , gstroke 0 rightAnglesO
                                    , gstroke 0 arrows
                                    , gstroke 0 (flatten arrows)
                                    , f 0 (sierpinski origin 0.5 0.01)
                                    , simpleText
                                    , rectangles
                                    , semiarcs
                                    , semiannsS
                                    , semiannsF
                                    , curve_ex
                                    , arc_ex
                                    , arcNegative_ex
                                    , path_ex
                                    , approx_ex 100
                                    , approx_ex 10 
                                    , fs_ex ]