2. Design grammars are sets of very simple non-
deterministic rules to produce images.
Context Free is a graphical environment for editing,
rendering, and exploring CFDG.
John HoriganChris Coyne Mark Lentczner
3. startshape startclouds
background { b -0.1 h 220 sat 0.75 }
rule startclouds {
3* { x 1 } startcloud { a -1 }
}
rule startcloud {
side { y -0.5 }
}
rule startcloud {
side { y 0.5 }
}
rule cloud {
step { }
horiz { s 0.7 }
side { }
}
rule side {
step { }
cloud { x -1 a 0.0005 s 0.99 }
}
rule side {
step { }
cloud { x 1 a 0.001 s 0.99 }
}
rule horiz {
step { }
rotatecloud { y -1 }
}
rule side {}
rule horiz {
step { }
rotatecloud { y 1 }
}
rule rotatecloud {
cloud { r 15 }
}
rule rotatecloud {
cloud { r -15 }
}
rule step {
SQUARE { b 1 }
}
rule step {
CIRCLE { b 1 }
}
4. Start with a root symbol/shape (the startshape) and use grammar
rules to elaborate this into a sentence of symbols/shapes.
When Context Free draws a shape it remembers some state
information for the shape (colour, xyz position, size, rotation). To
draw the shape Context Free evaluates a rule for the shape and
replaces the shape with the shapes listed in the rule. The state for
these replacement shapes is relative to the state of the parent
shape.
22. Start with a simple example
Copy and Paste into Context Free Art and hit Ctrl + R (Render)
23. startshape tree
shape tree
rule {
SQUARE []
tree [ s .99 r 2 y .995]
}
rule 33 {
SQUARE []
tree [ s .99 r 2 y .995 flip 90]
}
rule {
SQUARE []
tree [[ s .5 r 10 y .995 b .07 z -.07 ]]
tree [ s .99 y .995 flip 90 ]
tree [[ s .5 flip 90 r 10 y .995 b .07 z -.07]]
}
rule 3 {
SQUARE []
tree [[ sat 0.5 b 0.1 s .8 r 10 y .995 b .01 z -.001]]
tree [[ sat 0.5 b 0.1 s .8 flip 90 r 10 y .995 b .01 z -.001]]
}
24.
25. startshape tree
shape tree
rule {
SQUARE []
tree [ s .99 r 2 y .995]
}
rule 33 {
SQUARE []
tree [ s .99 r 2 y .995 flip 90]
}
rule {
SQUARE []
tree [[ s .5 r 10 y .995 b .07 z -.07 ]]
tree [ s .99 y .995 flip 90 ]
tree [[ s .5 flip 90 r 10 y .995 b .07 z -.07]]
}
rule 3 {
SQUARE []
tree [[ hue 30 sat 0.5 b 0.1 s .8 r 10 y .995 b .01 z -.001]]
tree [[ hue 30 sat 0.5 b 0.1 s .8 flip 90 r 10 y .995 b .01 z -.001]]
}
Using colour to figure out what rules generate trunk,
main and secondary branches
26. startshape tree
shape tree
rule {
SQUARE []
tree [ hue 30 sat 0.5 b 0.1 s .99 r 2 y .995]
}
rule 33 {
SQUARE []
tree [ hue 30 sat 0.5 b 0.1 s .99 r 2 y .995 flip 90]
}
rule {
SQUARE []
tree [[ s .5 r 10 y .995 b .07 z -.07 ]]
tree [ s .99 y .995 flip 90 ]
tree [[ s .5 flip 90 r 10 y .995 b .07 z -.07]]
}
rule 3 {
SQUARE []
tree [[ s .8 r 10 y .995 b .01 z -.001]]
tree [[ s .8 flip 90 r 10 y .995 b .01 z -.001]]
}
Using colour to figure out what rules generate trunk,
main and secondary branches
27. startshape tree
shape tree
rule {
SQUARE []
tree [ s .99 r 2 y .995]
}
rule 33 {
SQUARE []
tree [ s .99 r 2 y .995 flip 90]
}
First two rules generate thick, mainly vertical
arrangement of squares: ‘trunk’
28. startshape tree
shape tree
rule {
SQUARE []
tree [ s .99 r 2 y .995]
}
rule {
SQUARE []
tree [ s .99 r 2 y .995 flip 90]
}
Change the odds for each rule to trigger to see effects
in the type of trunks generated
29. startshape tree
shape tree
rule {
SQUARE []
tree [ s .99 r 2 y .995]
}
rule 33 {
SQUARE []
tree [ s .99 r 2 y .995 flip 90]
}
Second rule is called 33x more than the first rule
30. startshape tree
shape tree
rule {
SQUARE []
tree [ s .99 r 2 y .995]
}
rule 33 {
SQUARE []
tree [ s .99 r 2 y .995 flip 90]
}
rule {
SQUARE []
tree [[ s .5 r 10 y .995 b .07 z -.07 ]]
tree [ s .99 y .995 flip 90 ]
tree [[ s .5 flip 90 r 10 y .995 b .07 z -.07]]
}
rule 3 {
SQUARE []
tree [[ sat 0.5 b 0.1 s .8 r 10 y .995 b .01 z -.001]]
tree [[ sat 0.5 b 0.1 s .8 flip 90 r 10 y .995 b .01 z -.001]]
}
Play with the third and fourth rules to see branching
effects. Remember to “Render” a few times to see
variations due to randomness
32. startshape PLANT
shape PLANT {
CIRCLE [ ]
}
Starting point:
This code draws a simple black circle,
we arbitrarily call the function “PLANT”:
33. startshape PLANT
shape PLANT {
BL [ h 10 sat 1 b 1 ]
}
shape BL {
CIRCLE [ ]
}
Two changes:
1. The “CIRCLE” definition moved to a function that we call “BL”
2. Shape parameters are sent when invoking BL:
a) hue 10
b) saturation 1
c) brightness 1
34. startshape PLANT
shape PLANT {
BL [ h 10 sat 1 b 1 ]
}
shape BL {
CIRCLE [ ]
BL [size (0.95) y 1 ]
}
Recursion:
We introduce an operation after the CIRCLE, which
the program applies in a loop (until the shapes are
too small to be rendered, then it stops).
The new line is effectively, updating itself (shape BL)
scaling down its size and moving in the y axis 1 unit
up
35. startshape PLANT
shape PLANT {
BL [ h 10 sat 1 b 1 ]
}
shape BL {
CIRCLE [ ]
BL [size (0.95) y 1 x 1 ]
}
Recursion:
Now each new shape is moved one unit up and one
unit right on every step
36. startshape PLANT
shape PLANT {
BL [ h 10 sat 1 b 1 ]
}
shape BL
rule {
CIRCLE [ ]
BL [size (0.95) y 1 x 1]
}
Randomness:
This is exactly the same code as before, it just shows
that shapes can have more than one operations, i.e.,
“rules”. Here BL has a single rule, the same as the
previous step
37. startshape PLANT
shape PLANT {
BL [ h 10 sat 1 b 1 ]
}
shape BL
rule {
CIRCLE [ ]
BL [size (0.95) y 1 ]
}
rule {
CIRCLE [ ]
BL [size (0.95) x 1 ]
}
Randomness: Now the fun begins: randomness. When two rules
are defined for a shape, then the software chooses
randomly only one of them to execute. The results
start being a bit unpredictable…
38. startshape PLANT
shape PLANT {
BL [ h 10 sat 1 b 1 ]
}
shape BL
rule {
CIRCLE [ ]
BL [ rotate randint(0, 8) size (0.95) y 1 ]
}
rule {
CIRCLE [ ]
BL [ rotate randint(0, 8) size (0.95) x 1 ]
}
More randomness: Now we also add a random rotation (an integer
value between 0 and 8), so every time that the code
is executed, it renders a slightly different image:
overall the same logic, but with the specific details
different every time.
39. startshape PLANT
shape PLANT {
BL [ sat 1 b 1 ]
}
shape BL
rule {
CIRCLE [ ]
BL [ h randint(0, 360) rotate randint(0, 8) size (0.95) y 1 ]
}
rule {
CIRCLE [ ]
BL [ h randint(0, 360) rotate randint(0, 8) size (0.95) x 1 ]
}
Even more randomness:
We take the hue parameter out from the BL
definition in “shape PLANT” and make it random too
every time BL is called in a rule.
But now the code is getting “messy” and things are
duplicated… let’s do some cleaning next
40. startshape PLANT
shape PLANT {
BL [ sat 1 b 1 ]
}
shape BL {
CIRCLE [ ]
WL [ size (0.985) y 1 ]
}
shape WL
rule { BL [ h randint(0, 360) rotate randint(0, 8)] }
Recursion between shapes:
Compare the code with the previous version. Shape
BL now declares a circle and calls shape WL with a
change in size and location in y axis
And shape WL has one rule where it calls shape BL
with a change in hue and rotation. The program
continues with this recursive loop until it can’t
render new shapes because they are too small
41. startshape PLANT
shape PLANT {
BL [ sat 1 b 1 ]
}
shape BL {
CIRCLE [ ]
WL [ size (0.985) y 1 ]
}
shape WL
rule 50 { BL [ h randint(0, 360) rotate randint(0, 8)] }
rule { BRANCH [] }
shape BRANCH {
BL [ rotate 1 flip 90]
BL [ rotate -1 flip 90]
}
Recursion between shapes: The advantage of separating and calling shapes
recursively, is that we can create routines with
several rules, each with a “weight” or probability of
being chosen and executed.
This is how the shape BRANCH looks, and we add a
weight of 50 to the rule that calls shape BL.
Now every instance looks quite different! (within a
shared generative grammar)
42. Download and install: https://www.contextfreeart.org
To Do:
- Play with the code
- Document and share your results in your blog
Credits:
Chris Coyne designed the CFDG grammar and wrote the original command-line version.
Mark Lentczner saw Chris' site and decided he had to have an interactive version. He is responsible for the Macintosh version.
John Horigan got sucked into this project and it immediately ate all of his spare time. He wrote the Windows version. Mark and John
jointly maintain the common code base and the Posix/Unix version.