Created
October 21, 2009 15:09
-
-
Save anonymous/215172 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import System.Random | |
import Control.Monad.State | |
type Point = (Double, Double) | |
pointPlus :: Point -> Point -> Point | |
pointPlus (x1, y1) (x2, y2) = (x1 + x2, y1 + y2) | |
polarToPoint :: Double -> Double -> Point | |
polarToPoint angle length | |
= let x = length * sin angle | |
y = length * (- cos angle) | |
in (x, y) | |
data GrowState = GrowState { stRandoms :: [Double] -- ^ (0..1) | |
} | |
type Grow a = State GrowState a | |
data Type = BranchType | TrunkType | |
deriving Eq | |
genFromRange :: (Double, Double) -> Grow Double | |
genFromRange (min, max) | |
= do st <- get | |
let r:randoms = stRandoms st | |
put st { stRandoms = randoms } | |
return $ r * (max - min) + min | |
genAngleMod :: Type -> Double -> Double -> (Double, Double) | |
genAngleMod typ min max | |
| typ == BranchType = (min, max) | |
| otherwise = (min * 0.5, max * 0.5) | |
runGrow :: Grow a -> IO a | |
runGrow f = do gen <- getStdGen | |
let st = GrowState { stRandoms = randomRs (0, 1) gen } | |
return $ evalState f st | |
data Branch = Branch { branchType :: Type, | |
branchBegin :: Point, | |
branchEnd :: Point, | |
branchAnchor1 :: Point, | |
branchAnchor2 :: Point, | |
branchLength :: Double, | |
branchSize :: Double, | |
branchAngle :: Double } | |
makeBranch :: Branch -- ^parent | |
-> Double -- ^rand | |
-> Double -- ^length | |
-> Double -- ^angle | |
-> Branch | |
makeBranch parent rand length angle | |
= Branch { branchType = typ, | |
branchBegin = point1, | |
branchEnd = point2, | |
branchAnchor1 = anchor1, | |
branchAnchor2 = anchor2, | |
branchLength = length', | |
branchSize = branchSize parent - length' / 24, | |
branchAngle = angle } | |
where length' | branchType parent == TrunkType = length * 0.95 | |
| otherwise = length * 0.75 | |
typ | rand < length / branchLength parent - 0.5 = branchType parent | |
| otherwise = BranchType | |
point1 = branchEnd parent | |
point2 = point1 `pointPlus` (polarToPoint angle length') | |
anchor1 = point1 `pointPlus` (polarToPoint (branchAngle parent) (length' * 0.4)) | |
anchor2 = point2 `pointPlus` (polarToPoint (angle) (-length' * 0.3)) | |
branches :: Branch -> Grow [Branch] | |
branches b | branchLength b <= 50 = return [] | |
| otherwise = do angle1 <- (branchAngle b +) `liftM` | |
genFromRange ( genAngleMod (branchType b) (-40 * pi / 180) (-20 * pi / 180)) | |
angle2 <- (branchAngle b +) `liftM` | |
genFromRange ( genAngleMod (branchType b) (-20 * pi / 180) (20 * pi / 180)) | |
angle3 <- (branchAngle b +) `liftM` | |
genFromRange ( genAngleMod (branchType b) (20 * pi / 180) (40 * pi / 180)) | |
length' <- (branchLength b -) `liftM` | |
genFromRange (5, 15) | |
leftrand <- (0 +) `liftM` | |
genFromRange (0, 1) | |
middlerand <- (0 +) `liftM` | |
genFromRange (0, 1) | |
rightrand <- (0 +) `liftM` | |
genFromRange (0, 1) | |
let left = makeBranch b leftrand length' angle1 | |
middle = makeBranch b middlerand length' angle2 | |
right = makeBranch b rightrand length' angle3 | |
lefts <- branches left | |
rights <- branches right | |
case () of | |
_ | branchType b == BranchType -> | |
return $ [left, right] ++ lefts ++ rights | |
_ -> | |
do middles <- branches middle | |
return $ [left, middle, right] ++ | |
lefts ++ | |
middles ++ | |
rights | |
makeTrunk origin size start | |
= Branch TrunkType origin stem origin stem start size 0 | |
where stem = origin `pointPlus` (polarToPoint 0 start) | |
{- | |
branch :: Value -> Double -> Double -> [Value] | |
branch parent stop angle | |
= leftBranches ++ rightBranches | |
where left = makeValue (angle - 20 * pi / 180) | |
leftBranches = [left] ++ (branch left stop | |
-} | |
--class Growable d where | |
-- drawableNodes :: d -> [Node] | |
branchesToSVG bs = "<?xml version='1.0' encoding='UTF-8' standalone='no'?>" ++ | |
"<svg " ++ | |
"xmlns:svg='http://www.w3.org/2000/svg' " ++ | |
"xmlns='http://www.w3.org/2000/svg' " ++ | |
"version='1.0' " ++ | |
"width='210mm' " ++ | |
"height='297mm' " ++ | |
"id='svg2'> " ++ | |
"<defs id='defs4' /> " ++ | |
"<g>" ++ | |
(concat $ map branchToSVGpath bs) ++ | |
"</g>" ++ | |
"</svg>" | |
where branchToSVGpath b | |
= let size = branchSize b | |
color | branchType b == BranchType = "#2D5016" | |
| otherwise = "#552200" | |
style = "fill:none;fill-rule:evenodd;stroke:" ++ color ++ | |
";stroke-width:" ++ | |
(show size) ++ "px;" ++ | |
"stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" | |
in "<path d='M " ++ (s $ branchBegin b) ++ | |
" C " ++ (s $ branchAnchor1 b) ++ | |
" " ++ (s $ branchAnchor2 b) ++ | |
" " ++ (s $ branchEnd b) ++ | |
"' style='" ++ style ++ "'/>" | |
s (x, y) = (show $ truncate x) ++ "," ++ (show $ truncate y) | |
main = do let trunk = makeTrunk (520, 1000) 30 150 | |
branches' <- ([trunk] ++) `liftM` (runGrow $ branches trunk) | |
putStrLn $ branchesToSVG branches' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment