Patterning In 15 Minutes

Some examples to get you started with Patterning.

Please make sure you have the most up-to-date version of Patterning from the repository. The library is changing very rapidly and the API isn't at all stable yet. I'm making an effort to keep this tutorial up-to-date with the latest code-base so you may find these examples break with older versions of the code.

To run these examples, make sure tutorial.clj is included in core.clj

(:require [patterning.examples.tutorial :refer :all])

Then in core.clj, assign one of the patterns below to "final-pattern".

For example :

(def final-pattern tutorial/triangles)

and type lein run at the command line, to see the effect.

Five triangles in a ring

Start by defining a style. A style is represented as a mapping with keyword keys. For example :

(def red-style {:stroke (p-color 255 100 100) :stroke-weight 2 })

Commonly used entries are :stroke, :stroke-weight and :fill. :stroke and :fill are colours.

See more about colours below.

Now let's make a simple pattern.

(def triangles (clock-rotate 5 (poly 0.5 0.5 0.3 3 red-style) ) )

poly creates a regular polygon. Its arguments are x-centre, y-centre, radius, number-of-sides and, optionally, style.

clock-rotate is a Layout, a function which takes an existing pattern and returns a new one.

In this case, clock-rotate takes a number, n, and a pattern, and makes the new pattern by rotating n copies of the input around the centre point.

Stack the five triangles on a blue pentagon

stack is a Layout which can overlay a number of patterns together.

(def blue-style {:color (p-color 200 200 255)  :stroke-weight 3})
(def pentagon (poly 0 0 0.7 5 blue-style))
(def pen-tri (stack pentagon triangles))

Let's make some grids of these

Note that grid-layout takes a list of the patterns we want to lay out on it.

Here we just use (repeat pen-tri) to make an infinite lazy list of them.

(def pt-grid (grid-layout 8 (repeat pen-tri)) )

Not massively exciting, instead let's do a chequered pattern.

The checked-layout takes two streams of patterns and interpolates between them when laying on a grid

(def pt-checks (checked-layout 8 (repeat pentagon) (repeat triangles)))

A Drunk Line

OK. change of direction, a "drunkards walk" is a series of points each of which is a move in a random direction from the previous one.

Patterning has a function for that, dline, which takes a number of steps, a step-length and an option style

(def dline (drunk-line 10 0.1 {:stroke (p-color 100 255 100) :stroke-weight 3}) )

Why do we want a random wiggle? Well, they look a lot cooler when we do some more things to them

Like clock-rotate them

(def dl-clock (clock-rotate 12 dline))

Or mirror them

(def dl-mirror (four-mirror dline))

Or both

(def clock-mirror (clock-rotate 5 (four-mirror dline)))

And did you want that mixed with our other shapes?

(def m2 (clock-rotate 5 (four-mirror (stack dline (scale 0.4  triangles) ))))

And perhaps on a staggered grid? The half-drop-grid-layout gives us that.

(def m3 (half-drop-grid-layout 3 (repeat m2)))

Maybe bring back a bit of blue, every other.

(def m4 (half-drop-grid-layout 6 (cycle [m2 pentagon])))

And we can keep adding adding more transforms, like four-mirroring this :

(def m5 (four-mirror m4))

Which is getting way too dense to see on the screen. But have a look at the vector output which is automatically generated (out.svg). You can use Inkscape or the browser.

Bezier Curves

OK. So that's line for you, but what about something smoother?

Bezier curves? We got 'em

(def orange-style  {:color (p-color 255 128 64) :stroke-weight 4})
(def bez1 (bez-curve [[-0.9 0] [0.8 0.8] [-0.5 -0.8] [0.6 -0.5]] orange-style )  )

Note here the introduction of the group function. Technically, primitives in Patterning are stored in a SShape (styled shape), a record containing a style field and a list of points. But patterns are represented as lists of SShapes, known as groups. This list of SShapes is the standard representation for all patterns. Functions like poly and drunk-line already output a SShape wrapped as a group. But bez-curve doesn't. So it needs a call to the group function to wrap it.

Perhaps that would make the corners of a nice frame

(def frame-it 
    (framed 6 (repeat bez1)
               (repeat
               (stack (group (->SShape orange-style [[-0.5 -1] [-0.5 1]]))
                      (poly -0.8 0 0.1 12 {:fill (p-color 100 100 255) :stroke (p-color 255 255 200)})) )
               dl-clock ))

framed is a Layout that takes three arguments :

  • a list of corner pieces (which it reflects appropriately),
  • a list of edge pieces (which it rotates appropriately)
  • and a single centre (NOT a list, just a single group which it fills the middle.)

You can, of course, have frames inside frames :

(def multi-frame
  (let [edge
        (stack (group (->SShape orange-style [[-0.5 -1] [-0.5 1]]))
               (poly -0.8 0 0.1 12 {:fill (p-color 100 100 255) :stroke (p-color 255 255 200)})) ]
    (framed 6 (repeat bez1)
            (repeat edge)
            frame-it)))

A word about colour.

Colours can be defined with the function p-color which takes 1, 3 or 4 arguments. A single argument will give you a shade of grey between black (0) and white (255). Three arguments will get mapped to red, green and blue components. Four arguments will get mapped to red, green, bl ue and alpha (transparency) components.

A transparent green :

(def trans-green (p-color 150 255 150 150) )

We can over-ride or supplement the styling of an existing group using the over-style function. Here we're going to supplement the already bright green dl-clock with a transparent green fill. Note that the drunk-line is not closed but can still be "filled"

(def fill-clock (over-style {:fill trans-green} dl-clock) )

And to see the transparency we can stack it on top of an earlier pattern.

(def m6 (stack pt-checks fill-clock ))