May 2021

Quil is a great library to make generative art with. As might be expected for a library that allows for points to be graphed, many functions expect one or more points to be passed to it.

For example, point, vertex, and triangle all take these `x`

and `y`

values. Quad even takes a whopping *eight* values, representing four points! Working this way, it's easy to do simple things.

(q/point 0 100)

In fact, most example code I've seen works this way. However, it becomes harder to do more complicated things.

(let [original-x 0 original-y 100 change-in-x 50 change-in-y 25] (q/line original-x original-y (+ original-x change-in-x) (+ original-y change-in-y)))

It's easy to lose track of which argument is the x value, and which the y. What if instead, we work with *vectors*?

(defn vector+ "Add together any nonzero number of vectors." [& vectors] (reduce #(mapv + %1 %2) vectors)) (let [original-point [0 100] change [50 25]] (q/line original-point (vector+ original-point change)))

This is easier to understand. There is some complexity in `vector+`

, but this complexity is *hidden* from the user of the function. The version that requires points as separate `x`

and `y`

values pushes all the complexity to the top level.

Note that this relies on the `line`

function accepting arguments as points. `Line`

is a rare Quil function – it not only accepts its arguments as separate `x`

, `y`

arguments, but *also* accepts the points as vectors.

If we want to use Quil functions that *don't* already take arguments this way, we can wrap them. For example, `vertex`

can be wrapped as follows:

(defn vertex "A version of quil's vertex that takes a point as a single argument." [point] (apply q/vertex point))

So far we've only discussed simple uses – ones that are easy enough to work with even without using points. Let's look at a more complex algorithm that takes advantage of passing around points: Chaikin curves. This algorithm is a method of taking a series of points and turning it into a smoother curve. You can see the effect below, starting with the algorithm not applied at all on the left shape, one more time on each shape to the right than the previous shape.

The code to generate this picture is below. The important part is in the function `chaikin`

, which takes a seq of points.

(defn chaikin "Generate the ORDER-th level Chaikin curve for the control polygon formed by POINTS. Note that POINTS is treated as an open polygon; we do not wrap around from the front to back. Also note that this function is NOT LAZY. Only pass to it points you will actually graph. More information is here: http://graphics.cs.ucdavis.edu/education/CAGDNotes/Chaikins-Algorithm/Chaikins-Algorithm.html" [points order] (loop [points points order order] (if (< order 1) points (recur (mapcat (fn [p1 p2] (list (vector-scale (vector+ (vector-scale p1 3) p2) 0.25) (vector-scale (vector+ p1 (vector-scale p2 3)) 0.25))) (butlast points) (rest points)) (dec order))))) (defn chaikin-blogpost-draw [] (let [control-points [[50 100] [100 400] [200 350] [200 50] [100 200]] number-of-offsets 4 offset-vector [250 0]] (q/fill nil) (dotimes [offset-number number-of-offsets] (q/begin-shape) (let [offset (v/scale offset-vector offset-number)] (doseq [point (v/chaikin (map (partial v/v+ offset) control-points) offset-number)] (h/vertex point))) (q/end-shape)))) (q/sketch :size [1000 500] :draw chaikin-blogpost-draw)

Imagine what this code would have to look like if we dealt with x and y coordinates separately! Operating on points lets us do operations that would be quite painful to do if we passed coordinates around separately. So use vectors.

## Postscript

It helps that Clojure works very nicely with vectors – we don't have to make a separate class to handle points. We can use a vector, and it's easy to create, introspect, and pass around. Thanks, Clojure!