Source

grrdrr / draw.py

from model import (ensure, WithGraphStyle, WithNodeLabel, WithNodeDimensions, WithGraphMargins,
                   WithGraphSize, WithNodePosition, WithArcVertices)
import cairo


def measure(graph):
    ensure(graph.readonly(WithGraphStyle, WithNodeLabel), "without graph style or node labels", graph)
    ensure(not graph.readable(WithNodeDimensions), "already with node dimensions", graph)
    ensure(not graph.readable(WithGraphMargins), "already with graph margins", graph)
    graph.upgrade(WithNodeDimensions)
    graph.upgrade(WithGraphMargins)

    dummy_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1)
    dummy_context = cairo.Context(dummy_surface)
    dummy_context.set_font_size(graph.style.font_size)

    extents = dummy_context.text_extents('M')
    x_bearing, y_bearing, width, height, x_advance, y_advance = extents
    m_width = width
    m_height = height
    graph.set_margins(int(3*m_width), int(3*m_height))

    for node in graph.nodes:
        extents = dummy_context.text_extents(node.label.text)
        x_bearing, y_bearing, width, height, x_advance, y_advance = extents
        node_width = int(width+m_width*2)
        node_height = int(height+m_height*2)
        node.set_dimensions(node_width, node_height)

    graph.freeze(WithNodeDimensions)
    graph.freeze(WithGraphMargins)

    return graph


def draw(graph, stream):
    ensure(graph.readonly(WithGraphStyle), "without graph style", graph)
    ensure(graph.readonly(WithGraphSize), "without graph size", graph)
    ensure(graph.readonly(WithNodePosition), "without node positions", graph)
    ensure(graph.readonly(WithArcVertices), "without arc vertices", graph)

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, graph.size.width, graph.size.height)
    context = cairo.Context(surface)
    context.set_font_size(graph.style.font_size)

    context.set_source_rgb(*graph.style.background_color)
    context.rectangle(0, 0, graph.size.width, graph.size.height)
    context.fill()

    context.set_source_rgb(*graph.style.arc_color)
    context.set_line_width(graph.style.arc_width)
    for arc in graph.arcs:
        context.new_path()
        vertex = arc.vertices[0]
        context.move_to(vertex.x, vertex.y)
        for vertex in arc.vertices[1:]:
            context.line_to(vertex.x, vertex.y)
        context.stroke()

    context.set_source_rgb(*graph.style.node_color)
    for node in graph.nodes:
        context.new_path()
        context.rectangle(node.position.x, node.position.y, node.position.w, node.position.h)
        context.fill()

    context.set_source_rgb(*graph.style.border_color)
    context.set_line_width(graph.style.border_width)
    for node in graph.nodes:
        context.new_path()
        context.rectangle(node.position.x, node.position.y, node.position.w, node.position.h)
        context.stroke()

    if graph.readonly(WithNodeLabel):
        context.set_source_rgb(*graph.style.text_color)
        for node in graph.nodes:
            if node.label is not None:
                cx = node.position.x + node.position.w/2
                cy = node.position.y + node.position.h/2
                extents = context.text_extents(node.label.text)
                x_bearing, y_bearing, width, height, x_advance, y_advance = extents
                x = cx - (width/2 + x_bearing)
                y = cy - (height/2 + y_bearing)
                context.move_to(x, y)
                context.show_text(node.label.text)

    surface.write_to_png(stream)

    return graph


def draw_smooth(graph, stream):
    ensure(graph.readonly(WithGraphStyle), "without graph style", graph)
    ensure(graph.readonly(WithGraphSize), "without graph size", graph)
    ensure(graph.readonly(WithNodePosition), "without node positions", graph)
    ensure(graph.readonly(WithArcVertices), "without arc vertices", graph)

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, graph.size.width, graph.size.height)
    context = cairo.Context(surface)
    context.set_font_size(graph.style.font_size)

    context.set_source_rgb(*graph.style.background_color)
    context.rectangle(0, 0, graph.size.width, graph.size.height)
    context.fill()

    context.set_source_rgb(*graph.style.arc_color)
    context.set_line_width(graph.style.arc_width)
    for arc in graph.arcs:
        context.new_path()
        if len(arc.vertices) > 2:
            vertex = arc.vertices[0]
            context.move_to(vertex.x, vertex.y)
            for idx in range(1, len(arc.vertices)-1):
                if idx == 1:
                    vertex = arc.vertices[0]
                    s_x, s_y = vertex.x, vertex.y
                else:
                    vertex1 = arc.vertices[idx-1]
                    vertex2 = arc.vertices[idx]
                    s_x, s_y = (vertex1.x+vertex2.x)/2, (vertex1.y+vertex2.y)/2
                if idx == len(arc.vertices)-2:
                    vertex = arc.vertices[-1]
                    e_x, e_y = vertex.x, vertex.y
                else:
                    vertex1 = arc.vertices[idx]
                    vertex2 = arc.vertices[idx+1]
                    e_x, e_y = (vertex1.x+vertex2.x)/2, (vertex1.y+vertex2.y)/2
                vertex = arc.vertices[idx]
                q_x, q_y = vertex.x, vertex.y
                x1, y1 = 1.0/3.0*s_x+2.0/3.0*q_x, 1.0/3.0*s_y+2.0/3.0*q_y
                x2, y2 = 1.0/3.0*e_x+2.0/3.0*q_x, 1.0/3.0*e_y+2.0/3.0*q_y
                x3, y3 = e_x, e_y
                context.curve_to(x1, y1, x2, y2, x3, y3)
        else:
            vertex = arc.vertices[0]
            context.move_to(vertex.x, vertex.y)
            for vertex in arc.vertices[1:]:
                context.line_to(vertex.x, vertex.y)
        context.stroke()

    context.set_source_rgb(*graph.style.node_color)
    for node in graph.nodes:
        context.new_path()
        context.rectangle(node.position.x, node.position.y, node.position.w, node.position.h)
        context.fill()

    context.set_source_rgb(*graph.style.border_color)
    context.set_line_width(graph.style.border_width)
    for node in graph.nodes:
        context.new_path()
        context.rectangle(node.position.x, node.position.y, node.position.w, node.position.h)
        context.stroke()

    if graph.readonly(WithNodeLabel):
        context.set_source_rgb(*graph.style.text_color)
        for node in graph.nodes:
            if node.label is not None:
                cx = node.position.x + node.position.w/2
                cy = node.position.y + node.position.h/2
                extents = context.text_extents(node.label.text)
                x_bearing, y_bearing, width, height, x_advance, y_advance = extents
                x = cx - (width/2 + x_bearing)
                y = cy - (height/2 + y_bearing)
                context.move_to(x, y)
                context.show_text(node.label.text)

    surface.write_to_png(stream)

    return graph
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.