Skip to content

Instantly share code, notes, and snippets.

@astro
Forked from anonymous/inkscape-tree.hs
Created October 21, 2009 00:01
Show Gist options
  • Save astro/214727 to your computer and use it in GitHub Desktop.
Save astro/214727 to your computer and use it in GitHub Desktop.
-- not ready !!!1!
import Random
data Point
-- X, Y
= Float Float
deriving (Show, Eq)
data Values = None
-- Point1, Angle1, Angle2, Point2
| Point Point Point Point
deriving (Show, Eq)
data Tree = Nil
| Trunk Values Tree Tree Tree
| Branch Values Tree Tree
deriving (Show, Eq)
data TreeType = NilType
| TrunkType
| BranchType
rollDice :: IO Int
rollDice = randomRIO (1,6)
rollType :: TreeType -> TreeType -> TreeType
rollType NilType expected = NilType
rollType BranchType expected
| r < 4 = BranchType
| r < 6 = expected
| otherwise = NilType
where r = rollDice
rollType TrunkType expected
| r < 3 = BranchType
| r < 6 = expected
| otherwise = TrunkType
where r = rollDice
--parent, start-size, end-size -> tree
grow :: TreeType -> Float -> Float -> Tree
grow NilType s e = Nil
grow TrunkType start end = Trunk None left middle right
where left = grow (rollType(TrunkType BranchType) start end)
middle = grow (rollType(TrunkType TrunkType) start end)
right = grow (rollType(TrunkType BranchType) start end)
grow BranchType start end = Branch None left right
where left = grow (rollType(BranchType NilType) start end)
right = grow (rollType(BranchType NilType) start end)
--start-size, end-size -> tree
makeTree :: Float -> Float -> Float -> Float -> Float -> Tree
makeTree x y size start end
| start < 0 = Nil
| end < 0 = Nil
| start <= end = Nil
| otherwise = Trunk None Nil grow(TrunkType start end) Nil
main = putStrLn $ makeTree 520 100 30 90 50
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
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
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 { branchBegin :: Point,
branchEnd :: Point,
branchAnchor1 :: Point,
branchAnchor2 :: Point,
branchLength :: Double,
branchSize :: Double,
branchAngle :: Double }
makeBranch :: Branch -- ^parent
-> Double -- ^length
-> Double -- ^angle
-> Branch
makeBranch parent length angle
= Branch { branchBegin = point1,
branchEnd = point2,
branchAnchor1 = anchor1,
branchAnchor2 = anchor2,
branchLength = length',
branchSize = branchSize parent - length' / 50,
branchAngle = angle }
where length' | length <= 80 = length
| otherwise = length * 0.9
point1 = branchEnd parent
point2 = point1 `pointPlus` (polarToPoint angle length')
anchor1 = point1 `pointPlus` (polarToPoint (-branchAngle parent) (length' * 0.6))
anchor2 = point2 `pointPlus` (polarToPoint (-angle) (-length' * 0.4))
branches :: Branch -> Grow [Branch]
branches b | branchLength b <= 50 = return []
| otherwise = do angle1 <- (branchAngle b +) `liftM`
genFromRange (-40 * pi / 180) (-20 * pi / 180)
angle2 <- (branchAngle b +) `liftM`
genFromRange (-20 * pi / 180) (20 * pi / 180)
angle3 <- (branchAngle b +) `liftM`
genFromRange (20 * pi / 180) (40 * pi / 180)
length' <- (branchLength b -) `liftM`
genFromRange 5 15
let left = makeBranch b length' angle1
middle = makeBranch b length' angle2
right = makeBranch b length' angle3
lefts <- branches left
rights <- branches right
case () of
_ | branchLength b <= 80 ->
return $ [left, right] ++ lefts ++ rights
_ ->
do middles <- branches middle
return $ [left, middle, right] ++
lefts ++
middles ++
rights
makeTrunk origin size start
= Branch 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 | branchLength b <= 80 = "green"
| otherwise = "brown"
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'
from random import random, choice
from math import sin, cos, sqrt, pi, radians as rad
#data:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
class Tree:
color = "000000"
def __init__(self, value):
self.value = value
def __repr__(self):
return self.__class__.__name__+hex(id(self))[2:]
class Branch(Tree):
color = "2D5016"
def __init__(self, value, left=None, right=None):
super().__init__(value)
self.left = left
self.right = right
def __str__(self):
return "<{2} left:{0} right:{1}>".format(self.left,self.right,repr(self))
class Trunk(Branch):
color = "552200"
def __init__(self, value, left=None, middle=None, right=None):
super().__init__(value, left, right)
self.middle = middle
def __str__(self):
return "<{3} left:{0} middle:{1} right:{2}>".format(self.left,self.middle,self.right,repr(self))
class Value:
def __init__(self, parent, length, angle, size=None):
self.angle = angle
if parent:
pvalue = parent.value
if pvalue:
if size is None: size = pvalue.size - length/50
self.angle += pvalue.angle
self.point1 = pvalue.point2
self.point2 = Point(pvalue.point2.x+length*cos(rad(self.angle)),
pvalue.point2.y+length*sin(rad(self.angle)))
if size is None: size = 1
if size < 0: size = 0.2
self.size = size
#radom:
def rollType(previous, expected):
if type(previous) is not type: previous = type(previous)
types = [None, Branch, Branch, expected]
if previous == Branch: types += [Branch, None]
elif previous == Trunk: types += [Branch, Trunk, expected, Trunk]
return choice(types)
def rollLength(length):
return length - random()*10 - 1
def rollAngle(middle,range):
return middle - random() * choice([range, -range])
#code
def grow(parent, middle, length, stop):
if length < stop: return None
klass = rollType(parent, Trunk if isinstance(parent, Trunk) else Branch)
if not klass: return None
l = length*2 if klass == Trunk else length
result = klass(Value(parent, l,rollAngle(middle, 20)))
result.left = grow(result,-40, rollLength(length), stop)
if isinstance(result, Trunk):
result.middle = grow(result,0, rollLength(length), stop)
result.right = grow(result,40, rollLength(length), stop)
x = parent.value.point2.x - parent.value.point1.x
y = parent.value.point2.y - parent.value.point1.y
r = sqrt(x*x+y*y)+0.000000000001
v = length*0.9; x *= v/r; y *= v/r
x += result.value.point1.x
y += result.value.point1.y
result.value.angle1 = Point(x,y)
x, y = 0, 0
if isinstance(result, Branch):
if result.left:
x += result.left.value.point1.x - result.left.value.point2.x
y += result.left.value.point1.y - result.left.value.point2.y
if result.right:
x += result.right.value.point1.x - result.right.value.point2.x
y += result.right.value.point1.y - result.right.value.point2.y
if isinstance(result, Trunk):
if result.middle:
x += result.middle.value.point1.x - result.middle.value.point2.x
y += result.middle.value.point1.y - result.middle.value.point2.y
r = sqrt(x*x+y*y)+0.000000000001
v = length*0.8; x *= v/r; y *= v/r
x += result.value.point2.x
y += result.value.point2.y
result.value.angle2 = Point(x,y)
return result
def makeTree(x, y, size, start, stop):
result = Trunk(Value(None, start,-90,size))
result.value.point1 = result.value.angle1 = Point(x,y)
result.value.point2 = result.value.angle2 = Point(x,y-start)
result.middle = grow(result,0, start, stop)
return result
#output
def get_list(node):
result = []
if isinstance(node, Tree):
result += [(repr(node),node.value.point1.x,node.value.point1.y,
node.value.angle1.x,node.value.angle1.y,
node.value.angle2.x,node.value.angle2.y,
node.value.point2.x,node.value.point2.y,node.value.size,node.color)]
if isinstance(node, Branch):
result += get_list(node.left) + get_list(node.right)
if isinstance(node, Trunk):
result += get_list(node.middle)
return result
header = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<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" />
"""
def print_it(tree):
l = get_list(tree)
svg = '<g id="{0}">'.format(repr(Tree(None)))
style = 'style="fill:none;fill-rule:evenodd;stroke:#{1};stroke-width:{0}px;'+\
'stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"'
for node in l:
s = style.format(*node[9:])
svg += '<path id="{1}" d="M {2:n},{3:n} C {4:n},{5:n} {6:n},{7:n} {8:n},{9:n}" {0} />'.format(s,*node)
svg += "</g>\n"
print(header+svg+"</svg>")
def main():
tree = makeTree(520,1000,30,90,50)
print_it( tree )
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment