Pycairo: "paint" with transparency/alpha

January 18, 2019 ยท 2 minute read

After some creative brainstorming, we went for “engineered joy” as a good name for what we want to do together outside of business. And, of course, we wanted a logo. The imagery came pretty naturally: a heart for joy, and a nut for engineered. Of course, I figured the logo should be generated. In the process, I discovered something that might be usefull: how to remove color from a Context.fill() surface

The easy part

A nut is pretty easy to represent:

def create_nut(d, col=pink,lin_col = (1,1,1), lin_w = 5 , c=c):
    """
    hexagon of 1.5 d width between paralel sides so r(ish) 1.5*d/sin(pi/6)
    :param d: float inner dia
    :param col: tuple (r,g,b) 
    :param c: Context
    :return:
    """

    c.save()
    r = (1.5 * d) /(2 * cos(pi/6))
    print("r is {}".format(r))
    ang = pi/6
    pts = []
    while ang < 2*pi:
        pts.append([r*sin(ang),r*cos(ang)])
        ang += pi/3
    print("{} pts are {}".format(len(pts), pts))
    pts.append(pts[0])
    c.move_to(pts[0][0],pts[0][1])
    for pt in pts:
        c.line_to(pt[0],pt[1])
    #clipping
    # c.clip()
    c.set_source_rgb(*col)
    c.fill()

The international standard for drawing a nut is roughly this:

internation nut standard (source: wikipedia)

S is normally 1.5 d, and so the next logical step was to add something like this to the function:

c.set_source_rgb(1,1,1)
c.arc(0,0, d, 0, 2*pi)
c.stroke

The problem, however, is that I would have a white overlay, not alpha values. Sure, I could use gimp’s excellent color-to-alpha, but I finally got something better.

Solution: OPERATOR_CLEAR

I’ve yet to figure clip() and mask()and would we grateful if anyone can point me to good resources. Because I couldn’t convert a path to a pattern, I’ve looked into operators. The pycairo relevant doc is not particularly usefull, but the cairo version is.

In the end the full function was:


def create_nut(d, col=pink,lin_col = (1,1,1), lin_w = 5 , c=c):

    c.save()
    r = (1.5 * d) /(2 * cos(pi/6))
    print("r is {}".format(r))
    ang = pi/6
    pts = []
    while ang < 2*pi:
        pts.append([r*sin(ang),r*cos(ang)])
        ang += pi/3
    print("{} pts are {}".format(len(pts), pts))
    pts.append(pts[0])
    c.move_to(pts[0][0],pts[0][1])
    for pt in pts:
        c.line_to(pt[0],pt[1])
    c.set_source_rgb(*col)
    c.fill()
    #then we remove some pink
    c.set_line_width(lin_w)
    c.arc(0, 0, d / 2, 0, 2 * pi)
    c.set_source_rgb(1,1,1)
    c.set_operator(OPERATOR_CLEAR)
    c.stroke()
    c.restore()

Suggested background music

Be Svendsen is a cool name, and this set at Robot Heart is solid but not overwhelming.