HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Drawing an Archimedean spiral using Pillow

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
archimedeanpillowdrawingusingspiral

Problem

From Rosetta Code:


The Archimedean spiral is a spiral named after the Greek mathematician Archimedes. It can be described by the equation:
$$r=a+b\theta$$
with real numbers \$a\$ and \$b\$.

Here is my attempt to draw it in Python (using Pillow):

"""This module creates an Archimdean Spiral."""

from math import cos, sin, pi
from PIL import Image, ImageDraw

def translate(point, screen_size):
    """
    Takes a point and converts it to the appropriate coordinate system.
    Note that PIL uses upper left as 0, we want the center.
    Args:
        point (real, real): A point in space.
        screen_size (int): Size of an N x N screen.
    Returns:
        (real, real): Translated point for Pillow coordinate system.
    """
    return point[0] + screen_size / 2, point[1] + screen_size / 2

def draw_spiral(a, b, img, step=0.5, loops=5):
    """
    Draw the Archimdean spiral defined by:
    r = a + b*theta
    Args:
        a (real): First parameter
        b (real): Second parameter
        img (Image): Image to write spiral to.
        step (real): How much theta should increment by. (default: 0.5)
        loops (int): How many times theta should loop around. (default: 5)
    """
    draw = ImageDraw.Draw(img)
    theta = 0.0
    r = a
    prev_x = int(r*cos(theta))
    prev_y = int(r*sin(theta))
    while theta < 2 * loops * pi:
        theta += step
        r = a + b*theta
        # Draw pixels, but remember to convert to Cartesian:
        x = int(r*cos(theta))
        y = int(r*sin(theta))
        draw.line(translate((prev_x, prev_y), img.size[0]) +
                  translate((x, y), img.size[0]), fill=1)
        prev_x = x
        prev_y = y

if __name__ == '__main__':
    IMAGE_SIZE = 300, 300
    img = Image.new('1', IMAGE_SIZE)
    draw_spiral(1, 2, img)
    img.save('spiral.png')


The program outputs this image:

Solution

With the default values, the spiral does not look very smooth: it's easy to see that it's been drawn using a series of straight-line segments. That's because the step in \$\theta\$ is quite large — 0.5 radians is more than 28°. Setting step=0.1 and loops=10 is an improvement:

But you can still see deviations from smoothness in various places. The problem here is that the step in \$\theta\$ is constant, but as \$r\$ gets bigger this becomes a bigger step in actual distance. What we would like is a (roughly) constant step in terms of distance along the curve, which we get by replacing

theta += step


with

theta += step / r


This makes step into an approximate distance along the curve in pixels (rather than a change in angle, as in the original code). Now, with step=1 and loops=10, we get this spiral:

Code Snippets

theta += step
theta += step / r

Context

StackExchange Code Review Q#144073, answer score: 7

Revisions (0)

No revisions yet.