Skip to content

Project 2: Rangoli and mandala generator

A rangoli is a pattern of colour on the floor, made for a festival. A mandala is a round pattern that is the same all the way round. Both are good for code: you draw one part, and a loop turns it again and again.

How it works

The petal. Two overlapping circles make a lens, and & keeps only the part they share. A smaller lens is cut out with -, so each petal has a hole. The petal is built once for each ring, and drawn many times.

def lens(length, width):
    r = (length * length + width * width) / (4 * width)
    c = r - width / 2
    left = f.path().circle(-c, -length / 2, 2 * r)
    right = f.path().circle(c, -length / 2, 2 * r)
    return left & right

cut_petal = lens(length, width) - lens(length * 0.6, width * 0.5)   # a petal with a hole

The symmetry. A pattern that looks the same when it turns is one piece, drawn again and again. Move to the middle of the canvas once. Then loop. Each time round, f.push() saves the place, f.rotate() turns by one share of the circle, the petal is drawn, and f.pop() turns back.

f.translate(CX, CY)                                # the middle of the pattern
for i in range(count):
    f.push()
    f.rotate(360 * i / count)                      # 360 divided by the number of petals
    f.translate(0, -base)                          # out from the middle
    f.draw_path(cut_petal)
    f.pop()

The rings. Several rings are drawn, the outer ones first, so the inner ones sit on top. Rings next to each other turn in opposite directions (direction = 1 or -1), and each turns at its own speed. The turn comes from f.frame_count.

One seed, one design. f.random_seed(seed) comes before the random choices. The same seed always gives the same design, and "new design" adds one to the seed.

Stage 1: make it work

One petal, turned eight times. f.rotate() turns everything drawn after it. f.push() and f.pop() put the turn back each time.

import funground as f


def setup():
    f.size(480, 480)


def draw():
    f.background(40, 20, 60)
    f.no_stroke()
    f.translate(240, 240)
    for i in range(8):
        f.push()
        f.rotate(45 * i)
        f.fill(255, 150, 40)
        f.ellipse(0, -90, 50, 150)
        f.fill(255, 220, 90)
        f.circle(0, -170, 14)
        f.pop()
    f.fill(255, 80, 120)
    f.circle(0, 0, 50)


f.run()

Stage 2: make it yours

Add controls. A slider can set the number of petals. Make it once in setup(). Ask it for its value in draw(). The controls sit in a panel under the canvas.

import funground as f


def setup():
    global petals, spin
    f.size(480, 480)
    f.color_mode("hsb", 360, 100, 100)
    petals = f.create_slider(4, 16, 8, step=1, label="petals")
    spin = f.create_checkbox("spin")


def draw():
    f.background(285, 70, 22)
    f.no_stroke()
    f.translate(240, 240)
    if spin.checked():
        f.rotate(f.frame_count * 0.5)
    count = petals.value()
    for i in range(count):
        f.push()
        f.rotate(360 * i / count)
        f.fill(38 + i * 6, 90, 100)              # hue, saturation, brightness
        f.ellipse(0, -90, 50, 150)
        f.pop()


f.run()

In "hsb" colour mode a colour is a hue (the colour itself, 0 to 360), how strong it is, and how bright it is. It is easy to make a family of colours that go well together: keep the strength and brightness, and move the hue a little.

Cut a shape out of a shape. Two circles that overlap make a petal. & keeps only the part they share. - cuts one away. These are path booleans.

def lens(length, width):
    """A petal pointing up. Its base is at (0, 0) and its tip at (0, -length)."""
    r = (length * length + width * width) / (4 * width)
    c = r - width / 2
    return f.path().circle(-c, -length / 2, 2 * r) & f.path().circle(c, -length / 2, 2 * r)

petal = lens(150, 60)
hole = lens(90, 30).translate(0, -20)
f.draw_path(petal - hole)                      # a petal with a hole in it

Pick festival colours. Keep a list of palettes. Each is a background and some colours. A slider picks which one to use. Diwali is gold, orange and magenta on deep purple. Holi is bright colours on cream. Make your own.

Make a new design. f.random_seed(n) makes the random numbers the same each time, for the same n. A "new design" button can add one to n and pick fresh shapes. Every number always gives the same design, so you can find one you like again.

if new_design.clicked():
    seed += 1
    f.random_seed(seed)
    fatness = f.random(0.4, 0.7)

Draw with dots. f.text_to_points("Shubh", x, y, 5) gives a list of points along the outline of the letters, one every five pixels. Draw a small circle at each one and the letters are made of dots.

Save it. f.save("rangoli.svg") and f.save("rangoli.pdf") write the pattern as shapes. An SVG or a PDF can be printed as big as you like, or sent to a laser cutter. Call it from a button or a key.

def key_pressed():
    if f.key == "s":
        f.save("rangoli.svg")

Stage 3: make it shine

The full version is in the gallery: examples/gallery/projects/02_rangoli.py. It has sliders for the petals, the rings and the palette, a checkbox for the dots, a "new design" button and a "save" button. The petals have holes cut by booleans. The centre is a rosette of joined circles. The rings turn slowly, one way and then the other. The words at the bottom are made of dots. Press S or click "save" to write rangoli.svg and rangoli.pdf.

Challenge cards

  • Can you make the petals pointed at one end and round at the other?
  • Can you add a slider for how fast the rings turn?
  • Can you add a fifth palette from the colours of a festival you love?
  • Can you add a ring of dots between the petals?
  • Can you save every new design as its own file, rangoli_1.svg, rangoli_2.svg, and so on?

Credits and ideas

This project builds on the idea of rangoli, the Indian floor art of patterns that repeat round a centre (Rangoli). The code is written for funground (CC0). See CREDITS.md.

Back to: 18. Projects