Armed with a text editor

mu's views on program and recipe! design

#! /usr/bin/env python
import cairo
from math import pi
import sys

class Diagram(object):
    def __init__(self, filename, width, height, alpha, separate=True):
        iwidth = width
        if separate:
            iwidth = int(width * 4.0 / 3 + 0.99)
        iheight = height
        #self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, iwidth, iheight)
        self.surface = cairo.SVGSurface(filename + '.svg', iwidth, iheight)
        cr = self.cr = cairo.Context(self.surface)

        cr.scale(width, height)
        cr.set_line_width(0.01)

        cr.save()
        cr.transform(cairo.Matrix(0.6, 0, 1.0/3, 0.5, 0.02, 0.45))
        cr.push_group()
        cr.rectangle(0, 0, 1, 1); cr.clip()
        self.draw_dest(self.cr)
        cr.set_source_rgb(0, 0, 0)
        cr.set_line_width( max(cr.device_to_user_distance(2, 2)) )
        cr.rectangle(0, 0, 1, 1)
        cr.stroke()
        cr.pop_group_to_source()
        cr.paint_with_alpha(alpha[0])
        cr.restore()

        cr.save()
        cr.transform(cairo.Matrix(0.6, 0, 1.0/3, 0.5, 0.04, 0.25))
        cr.push_group()
        cr.rectangle(0, 0, 1, 1); cr.clip()
        self.draw_mask(self.cr)
        cr.pop_group_to_source()
        cr.paint_with_alpha(alpha[1])
        cr.restore()

        cr.save()
        cr.transform(cairo.Matrix(0.6, 0, 1.0/3, 0.5, 0.06, 0.05))
        cr.push_group()
        cr.rectangle(0, 0, 1, 1); cr.clip()
        self.draw_src(self.cr)
        cr.pop_group_to_source()
        cr.paint_with_alpha(alpha[2])
        cr.restore()

        if separate:
            cr.save()
            cr.translate(1, 0)
            cr.scale(1.0 / 3, 1.0 / 3)
            cr.push_group()
            cr.rectangle(0, 0, 1, 1); cr.clip()
            self.draw_src(self.cr)
            cr.pop_group_to_source()
            cr.paint()
            cr.restore()

            cr.save()
            cr.translate(1, 1.0 / 3)
            cr.scale(1.0 / 3, 1.0 / 3)
            cr.push_group()
            cr.rectangle(0, 0, 1, 1); cr.clip()
            self.draw_mask(self.cr)
            cr.pop_group_to_source()
            cr.paint()
            cr.restore()

            cr.save()
            cr.translate(1, 2.0 / 3)
            cr.scale(1.0 / 3, 1.0 / 3)
            cr.push_group()
            cr.rectangle(0, 0, 1, 1); cr.clip()
            self.draw_dest(self.cr)
            cr.pop_group_to_source()
            cr.paint()
            cr.restore()

            cr.set_line_width( max(cr.device_to_user_distance(2, 2)) )
            cr.rectangle(1, 0, 1.0/3, 1)
            cr.clip_preserve()
            cr.stroke()
            cr.rectangle(1, 1.0/3, 1.0/3, 1.0/3)
            cr.stroke()

        self.surface.write_to_png(filename + '.png')
        cr.show_page()
        self.surface.finish()

    def draw_dest(self, cr):
        cr.set_source_rgb(1, 1, 1)
        cr.rectangle(0, 0, 1, 1)
        cr.fill()

    def draw_mask(self, cr, mask=None):
        cr.set_source_rgb(1, 0.9, 0.6)
        if mask:
            cr.mask(mask)
        else:
            cr.rectangle(0, 0, 1, 1)
            cr.fill()

    def draw_src(self, cr):
        cr.set_source_rgb(0, 0, 0)
        cr.rectangle(0, 0, 1, 1)
        cr.fill()

class Stroke(Diagram):
    def draw_mask(self, cr):
        cr.push_group()
        cr.rectangle(0, 0, 1, 1)
        cr.rectangle(0.20, 0.20, 0.6, 0.6)
        cr.rectangle(0.30, 0.30, 0.4, 0.4)
        cr.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
        cr.fill()
        cr.set_fill_rule(cairo.FILL_RULE_WINDING)

        Diagram.draw_mask(self, cr, cr.pop_group())

        cr.rectangle(0.25, 0.25, 0.5, 0.5)
        cr.set_source_rgb(0, 0.6, 0)
        cr.set_line_width( max(cr.device_to_user_distance(1, 1)) )
        cr.stroke()


    def draw_dest(self, cr):
        Diagram.draw_dest(self, cr)

        cr.set_line_width(0.1)              #stroke
        cr.set_source_rgb(0, 0, 0)          #stroke
        cr.rectangle(0.25, 0.25, 0.5, 0.5)  #stroke
        cr.stroke()                         #stroke

class Fill(Diagram):
    def draw_mask(self, cr):
        cr.push_group()
        cr.rectangle(0, 0, 1, 1)
        cr.rectangle(0.25, 0.25, 0.5, 0.5)
        cr.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
        cr.fill()
        cr.set_fill_rule(cairo.FILL_RULE_WINDING)

        Diagram.draw_mask(self, cr, cr.pop_group())

        cr.rectangle(0.25, 0.25, 0.5, 0.5)
        cr.set_source_rgb(0, 0.6, 0)
        cr.set_line_width( max(cr.device_to_user_distance(1, 1)) )
        cr.stroke()

    def draw_dest(self, cr):
        Diagram.draw_dest(self, cr)

        cr.set_source_rgb(0, 0, 0)         #fill
        cr.rectangle(0.25, 0.25, 0.5, 0.5) #fill
        cr.fill()                          #fill

class ShowText(Diagram):
    def draw_mask(self, cr):
        cr.select_font_face("Georgia",
                cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
        cr.set_font_size(1.2)
        x_bearing, y_bearing, width, height = cr.text_extents("a")[:4]

        cr.push_group()
        cr.rectangle(0, 0, 1, 1)
        cr.move_to(0.5 - width / 2 - x_bearing, 0.5 - height / 2 - y_bearing)
        cr.text_path("a")
        cr.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
        cr.fill()
        cr.set_fill_rule(cairo.FILL_RULE_WINDING)
        Diagram.draw_mask(self, cr, cr.pop_group())

        cr.move_to(0.5 - width / 2 - x_bearing, 0.5 - height / 2 - y_bearing)
        cr.set_source_rgb(0, 0.6, 0)
        cr.set_line_width( max(cr.device_to_user_distance(1, 1)) )
        cr.text_path("a")
        cr.stroke()


    def draw_dest(self, cr):
        Diagram.draw_dest(self, cr)

        cr.set_source_rgb(0.0, 0.0, 0.0)                                      #text
        cr.select_font_face("Georgia",                                        #text
                cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)              #text
        cr.set_font_size(1.2)                                                 #text
        x_bearing, y_bearing, width, height = cr.text_extents("a")[:4]        #text
        cr.move_to(0.5 - width / 2 - x_bearing, 0.5 - height / 2 - y_bearing) #text
        cr.show_text("a")                                                     #text

class Paint(Diagram):
    def draw_mask(self, cr):
        pass

    def draw_dest(self, cr):
        Diagram.draw_dest(self, cr)
        cr.set_source_rgb(0.0, 0.0, 0.0) #paint
        cr.paint_with_alpha(0.5)         #paint

    def draw_src(self, cr):
        cr.set_source_rgb(0.0, 0.0, 0.0)
        cr.rectangle(0, 0, 1, 1)
        cr.fill()

class Mask(Diagram):
    def __init__(self, *args):

        self.linear = cairo.LinearGradient(0, 0, 1, 1)                    #mask
        self.linear.add_color_stop_rgb(0, 0, 0.3, 0.8)                    #mask
        self.linear.add_color_stop_rgb(1, 0, 0.8, 0.3)                    #mask
                                                                          #mask
        self.radial = cairo.RadialGradient(0.5, 0.5, 0.25, 0.5, 0.5, 0.75)#mask
        self.radial.add_color_stop_rgba(0, 0, 0, 0, 1)                    #mask
        self.radial.add_color_stop_rgba(0.5, 0, 0, 0, 0)                  #mask
                                                                          #mask
        self.radialinv = cairo.RadialGradient(0.5, 0.5, 0.25, 0.5, 0.5, 0.75)
        self.radialinv.add_color_stop_rgba(0, 0, 0, 0, 0)
        self.radialinv.add_color_stop_rgba(0.5, 0, 0, 0, 1)
        super(Mask, self).__init__(*args)

    def draw_mask(self, cr):
        cr.save()
        cr.rectangle(0, 0, 1, 1)
        cr.clip()
        Diagram.draw_mask(self, cr, self.radialinv)
        cr.restore()

    def draw_dest(self, cr):
        Diagram.draw_dest(self, cr)
        cr.save()
        cr.rectangle(0, 0, 1, 1)
        cr.clip()
        cr.set_source(self.linear)                                        #mask
        cr.mask(self.radial)                                              #mask
        cr.restore()

    def draw_src(self, cr):
        cr.set_source(self.linear)
        cr.rectangle(0, 0, 1, 1)
        cr.fill()

if __name__ == '__main__':
    size = 120
    Diagram('destination', size, size, [1, 0.15, 0.15])
    Diagram('the-mask', size, size, [0.15, 1, 0.15])
    Diagram('source', size, size, [0.15, 0.15, 1])
    Stroke('stroke', size, size, [1, 0.8, 0.4])
    Fill('fill', size, size, [1, 0.8, 0.4])
    ShowText('showtext', size, size, [1, 0.8, 0.4])
    Paint('paint', size, size, [1, 0.8, 0.4])
    Mask('mask', size, size, [1, 0.8, 0.4])