Ollin

Kolam and sona

A kolam or a sona is one line that travels between a field of dots at 45 degrees. The line runs straight until it meets the edge of the field, then it turns and carries on. It always comes back to where it started, so the drawing is the path it took.

The tradition belongs to more than one place. In south India a kolam is chalked on the doorstep at dawn around a grid of pulli (dots). In Angola a sona is drawn in the sand with one finger while the story that goes with it is told. The mathematics under both is the same, and it is exact. Over a plain field the line closes into gcd(rows, columns) loops. A 7 by 5 field is one unbroken line, and a 6 by 4 field is two.

A wall placed between two neighboring dots turns the line there as well. Each wall inside the field either cuts one loop in two or joins two loops into one, and it never does more than that. You steer a drawing toward a single line by placing walls, and that is how the figures of a sona are built.

Three dark panels of chalk-colored looping line work around small dots. One continuous line over a field of seven by five dots; two interleaved loops in cream and orange over six by four; and the same seven by five field cut into three loops by two short walls

The line-work is geometry, not a picture. loops hands back closed Contours. You can stroke them, feed them to the shape booleans, hatch them, and export them to SVG for a pen plotter. The walk draws a kolam with square corners, and Contour.smoothed(iterations:) rounds those corners into the drawn form.

Contents

kolam

kolam(in bounds: Rectangle? = nil,
      columns: Int, rows: Int,
      mirrors: [Kolam.Mirror] = []) -> Kolam

A kolam over a columns × rows field of dots that fills bounds, which is the whole canvas by default. Nothing here is random, so there is no seed, and the same field always draws the same line.

let design = kolam(columns: 7, rows: 5)
noFill(); stroke(.white); strokeWeight(6); strokeJoin(.round)
for loop in design.loops { drawPolyline(loop.smoothed(iterations: 3).points, closed: true) }

drawKolam

drawKolam(in bounds: Rectangle? = nil,
          columns: Int, rows: Int,
          mirrors: [Kolam.Mirror] = [],
          rounding: Int = 2)

Draw the line-work with the current stroke, rounded by rounding passes of corner cutting. Use 2 for the drawn look and 0 for the square lattice path. The call does not draw the dots, because a kolam shows them and a sona does not, so that choice stays yours.

The two faces

  • loops one closed Contour per loop, in canvas coordinates. Wherever the walls are placed, the loops always hold 4 × rows × columns segments between them, four in every cell, one against each of its four sides.
  • dots the field itself, row-major, for marking the pulli a kolam is drawn around.
  • loopCount how many closed loops the line makes. A count of one is the single unbroken line, which is what most traditional drawings aim for.

The design keeps the grid it was built on. That means design.grid.cells and design.grid.bounds are there when a sketch needs to place something else against the same field. The grid's gutter does not apply, because the line needs an even field to bounce in.

Walls

A Kolam.Mirror is a short wall between two neighboring dots. You name it after the dot it sits against, counting from the top-left dot, and isUpright says which of the two walls it is: true for one standing between the dot and its right neighbor, false for one lying between the dot and the one below.

Kolam.Mirror.rightOf(column: 2, row: 1)   // standing between this dot and its right neighbor
Kolam.Mirror.below(column: 3, row: 2)     // lying between this dot and the one below
let design = kolam(columns: 7, rows: 5,
                   mirrors: [.rightOf(column: 2, row: 1), .below(column: 3, row: 2)])

A wall you ask for on the outside edge is dropped rather than counted, because the edge already turns the line. Every other wall moves loopCount by exactly one, up or down.


Related: Hitomezashi stitching is the other grid pattern whose whole design is a handful of bits. See Geometry for the Grid, Contour, and shape booleans the line-work is built on. See Curves for the corner cutting that rounds it, and Fabrication for taking it to a pen plotter.

↑↓ move ↩ open