Tutorial

let's import pycontextfree:

In [11]:
import contextfree as cf

before you draw something you have to initialize the drawing surface. This is dones with init command

In [12]:
cf.init(canvas_size=(200,200), background_color="#ffffff")

Let's draw someting, say a cricle:

In [13]:
cf.circle()

and render it as IPython image

In [14]:
cf.display_ipython()
Out[14]:

By the way, with init() you can set background and foreground color as well:

In [15]:
cf.init(canvas_size=(200, 200), background_color="#123456", face_color="#11AA89")
cf.circle()
cf.display_ipython()
Out[15]:

Other primitive shapes in contextfree including box

In [16]:
cf.init(canvas_size=(200,200), background_color="#ffffff")
cf.box()
cf.display_ipython()
Out[16]:

and trianlge etc

In [17]:
cf.init(canvas_size=(200,200), background_color="#ffffff")
cf.triangle()
cf.display_ipython()
Out[17]:

Pycontextfree comes with a set of geometric and photometric transformations. For instance, to move to the 1 unit to the right:

In [18]:
cf.init(canvas_size=(200, 200), background_color="#ffffff")
cf.circle()
with cf.transform(x=2):
    cf.box()
cf.display_ipython()
Out[18]:

Did you notice that each elements got smaller? That is because pycontextfree is tracking the positions of the elements and resized your drawing so that everything fits into the target canvas size.

Transformations can be combined or nested. Here is an example of another usefull transformation called scale - as the name implies, it scales the image.

In [19]:
cf.init(canvas_size=(200, 200), background_color="#ffffff")
cf.circle()
with cf.transform(x=1, scale_x=0.5):
    cf.circle()
cf.display_ipython()
Out[19]:
In [20]:
cf.init(canvas_size=(200, 200), background_color="#ffffff")
with cf.scale(2):
    cf.circle()
    with cf.transform(x=0.5, hue=0.5, lightness=0.5, saturation=0.5, alpha=0.6):
        cf.circle()
cf.display_ipython()
Out[20]:

Now we can write recursive rules to describe more interesting shapes. To avoid infinite recursion, pycontextfree provides check_limits decorator. It will stop iteration when the element becomes smaller than one pixel in size.

In [21]:
cf.init(canvas_size=(400, 200), background_color="#ffffff")
@cf.check_limits
def infinite_circles():
    cf.circle()
    with cf.transform(x=1, scale_x=0.7):
        infinite_circles()
infinite_circles()
cf.display_ipython()
Out[21]:

It is also possible to explicitly specify the maximal recursion depth

Pycontextfree allows to define several versions of a rule with same name and to assign probability to each version. Probabilities do not have to sum to 1, the values will be normalized by the runtime:

In [22]:
cf.init(canvas_size=(400, 400), background_color="#ffffff", max_depth=40)
@cf.rule(1)
def random_walk():
    cf.circle()
    with cf.transform(scale_x=0.9, x=1):
        random_walk()

@cf.rule(1)
def random_walk():
    cf.circle()
    with cf.transform(scale_x=0.9, y=1):
            random_walk()
random_walk()
cf.display_ipython()
Out[22]:

Well, this should have covered the basics.

Please look at the gallery for more inspiration or at the API reference for more technical details :)

Cheers!