packages feed

hps-0.11: Help/fractals.hs

module Main where

import Data.Maybe
import Graphics.PS hiding (a0, a1)
import Graphics.PS.Path.Graphs
import System.Directory
import System.Environment
import System.FilePath.Posix
import System.Random

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

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

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

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

-- | Thin grey stroke operator.
gstroke' :: Double -> Path -> Image
gstroke' g = Stroke (GS (RGB g g g) 0.01 RoundCap RoundJoin ([],0) 10.0)

-- | Bold blue stroke operator.
bstroke :: Path -> Image
bstroke = Stroke (GS (RGB 0.0 0.0 1.0) 16.0 RoundCap RoundJoin ([],0) 10.0)

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

-- | 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 =
    let t = Text (Font "Times" 72) "SimpleText"
    in gstroke 0.25 (MoveTo (Pt 100 400) +++ t)

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

-- | A random scattering of filled grey rectangles.
rectangles :: Image
rectangles = (foldl1 over . map rectangle_ . clump 4) (randn 1 240 0 1)

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

-- A Path must begin with a MoveTo operator.

-- | 'arc' makes a path composed of bezier curves.
semiarc :: [Double] -> Image
semiarc [g,x,y,z] =
    let x' = x * 500
        y' = y * 500
        z' = z * 64
        shift = translate x' y' . scale z' z'
    in (gstroke' g . shift . mkValid) (arc origin 1 0 (1.5 * pi))
semiarc _ = error "illegal semiarc"

-- | A random scattering of stroked arcs.
semiarcs :: Image
semiarcs = (foldl1 over . map semiarc . clump 4) (randn 1 120 0 1)

-- | '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] =
    let 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
    in f g $ shift $ mkValid $ annular origin ir' xr' sa' a'
semiann _ _ = error "illegal semiann"

-- | A random set of annular sections.
semiannsS :: Image
semiannsS = foldl1 over $ map (semiann gstroke') $ clump 5 $ randn 1 50 0 1

semiannsF :: Image
semiannsF = foldl1 over $ map (semiann gfill) $ clump 5 $ randn 1 50 0 1

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

curve_ex :: Image
curve_ex =
    let 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
    in s 0 c `over` s 0.5 l

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 =
    let 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
    in s 0 e0 `over` f 0.9 e1 `over` s 0.5 e2

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)

hps :: [Image]
hps =
    let arrows = fractalArrow 200 9
        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 = let e = close path_ex'
                in s 0 e `over` f 0.9 e
        s n = gstroke' n . normalize
        f n = gfill n . normalize
    in [ gstroke 0 fractal_sqr
       , gstroke 0 fractal_sqr'
       , 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
       ]

heps :: Image
heps = bstroke (erat (Pt 48 32) 120)

bb :: BBox
bb = HRBBox 35 19 180 164 40.0 24.0 176.0 160.0

writing :: String -> IO ()
writing fn = print ("writing output to: " ++ fn)

-- | Usage: main [ps_file_path] [eps_file_path]
--   Defaults are used for unsupplied args
main :: IO ()
main = do
  a <- getArgs
  u <- getUserDocumentsDirectory
  let ofn = case a of
              x:_ -> x
              _ -> u </> "hps.ps"
      epsfn = case a of
                [_,x] -> x
                _ -> u </> "heps.eps"
  writing ofn
  ps ofn a4 hps
  writing epsfn
  eps epsfn bb heps