Elliot
Elliot

Reputation: 5541

Creating an arc with a given thickness using PIL's Imagedraw

I am trying to create a segmented arc using PIL and Imagedraw. The arc function allows me to draw an arc easily, but it is just a line. I need to be able to place an arc of given radius and thickness(ID to OD), but AI cannot find any type of thickness or width setting. Is there a way to do this? If not, is there some other way to do this using PIL?

Snippet:

import Image
import ImageDraw

conv = 0.1
ID = 15
OD = 20
image = Image.new('1',(int(ceil(OD/conv))+2,int(ceil(OD/conv))+1), 1)
draw = ImageDraw.Draw(image)
diam = OD-ID
box=(1, 1, int(ceil(diam/conv)), int(ceil(diam/conv))) #create bounding box
draw.arc(box, 0, 90, 0) #draw circle in black

Upvotes: 10

Views: 5320

Answers (4)

Pieter Ennes
Pieter Ennes

Reputation: 2409

I created the following arc replacement function based on Mark's suggestion:

https://gist.github.com/skion/9259926

Probably not pixel perfect (nor fast), but seems to come close for what I need it for. If you have a better version please comment in the Gist.

def arc(draw, bbox, start, end, fill, width=1, segments=100):
    """
    Hack that looks similar to PIL's draw.arc(), but can specify a line width.
    """
    # radians
    start *= math.pi / 180
    end *= math.pi / 180

    # angle step
    da = (end - start) / segments

    # shift end points with half a segment angle
    start -= da / 2
    end -= da / 2

    # ellips radii
    rx = (bbox[2] - bbox[0]) / 2
    ry = (bbox[3] - bbox[1]) / 2

    # box centre
    cx = bbox[0] + rx
    cy = bbox[1] + ry

    # segment length
    l = (rx+ry) * da / 2.0

    for i in range(segments):

        # angle centre
        a = start + (i+0.5) * da

        # x,y centre
        x = cx + math.cos(a) * rx
        y = cy + math.sin(a) * ry

        # derivatives
        dx = -math.sin(a) * rx / (rx+ry)
        dy = math.cos(a) * ry / (rx+ry)

        draw.line([(x-dx*l,y-dy*l), (x+dx*l, y+dy*l)], fill=fill, width=width)

Upvotes: 4

Elliot
Elliot

Reputation: 5541

A trick I found that can be pulled is to make a white circle inside the black circle. You can use the pieslice method to break it up as needed. The rendering is sequential, so you just have to get the ordering correct. The hard part is getting the positioning correct, due to Imagedraw's use of bounding boxes as opposed to center and radius coordinates. You have to make sure that the centers of everything end up exactly on each other.

THIS SOLUTION IS GOOD ONLY IN A LIMITED CASE, see comment.

Upvotes: 0

Mark Ransom
Mark Ransom

Reputation: 308120

Simulate the arc using straight line segments and put the coordinates of those segments into a list. Use draw.line with a width option to draw the arc.

Upvotes: 0

Ned Batchelder
Ned Batchelder

Reputation: 375514

PIL can't draw wide arcs, but Aggdraw can, and works well with PIL (same author).

Upvotes: 1

Related Questions