Kirill Simonov avatar Kirill Simonov committed d9d3222

Initial version.

Comments (0)

Files changed (3)

+syntax: glob
+.*.swp
+*.pyc
+*.png
+pycairo-*
+
+
+import cairo
+
+
+class Graph(object):
+
+    def __init__(self):
+        self.nodes = []
+        self.arcs = []
+        self.layers = []
+        self.style = None
+        self.margins = None
+        self.size = None
+
+    def add_node(self):
+        return Node(self)
+
+    def add_arc(self, origin, target, weight=1.0):
+        return Arc(self, origin, target, weight)
+
+    def add_layer(self):
+        return Layer(self)
+
+    def set_style(self,
+                background_color=(1.0, 1.0, 1.0),
+                node_color=(0.0, 0.0, 0.0),
+                arc_color=(0.0, 0.0, 0.0),
+                text_color=(1.0, 1.0, 1.0),
+                font_size=10):
+        return GraphStyle(self, background_color=background_color,
+                    node_color=node_color, arc_color=arc_color,
+                    text_color=text_color, font_size=font_size)
+
+    def set_margins(self, top, right, bottom, left):
+        return GraphMargins(self, top, right, bottom, left)
+
+    def set_size(self, width, height):
+        return GraphSize(self, width, height)
+
+    def measuring(self):
+        if self.style is None:
+            self.set_style()
+
+        dummy_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1)
+        dummy_context = cairo.Context(dummy_surface)
+        dummy_context.set_font_size(self.style.font_size)
+
+        extents = dummy_context.text_extents('M')
+        x_bearing, y_bearing, width, height, x_advance, y_advance = extents
+        m_width = int(width)
+        m_height = int(height)
+
+        if self.margins is None:
+            self.set_margins(m_height, m_width, m_height, m_width)
+
+        for node in self.nodes:
+            if node.dimensions is not None:
+                continue
+            if node.label is not None:
+                extents = dummy_context.text_extents(node.label.text)
+                x_bearing, y_bearing, width, height, x_advance, y_advance = extents
+                node_width = int(width*2)
+                node_height = int(height*2)
+            else:
+                node_width = m_width*2
+                node_height = m_height*2
+            node.set_dimensions(node_width, node_height)
+
+    def sorting(self):
+        existing_ranks = set(node.rank.value for node in self.nodes if node.rank is not None)
+        value = 0
+        for node in self.nodes:
+            if node.rank is not None:
+                continue
+            while value in existing_ranks:
+                value += 1
+            node.set_rank(value)
+            value += 1
+
+    def layering(self):
+        nodes_per_layer = int(len(self.nodes)**0.5)
+        layer = None
+        for node in sorted(self.nodes, key=(lambda node: node.rank.value)):
+            if node.layer_item is not None:
+                continue
+            if layer is None or len(layer.items) > nodes_per_layer:
+                layer = self.add_layer()
+            layer.add_node_item(node)
+        for arc in self.arcs:
+            origin_layer_index = arc.origin.layer_item.layer.index
+            target_layer_index = arc.target.layer_item.layer.index
+            min_index = min(origin_layer_index, target_layer_index) + 1
+            max_index = max(origin_layer_index, target_layer_index)
+            for index in range(min_index, max_index):
+                layer = self.layers[index]
+                layer.add_arc_item(arc)
+
+    def ordering(self):
+        for layer in self.layers:
+            node_items = [node.layer_item for node in self.nodes
+                        if node.layer_item.layer is layer]
+            arc_items = [layer_item for arc in self.arcs for layer_item in arc.layer_items
+                        if layer_item.layer is layer]
+            node_items_index = 0
+            arc_items_index = 0
+            value = 0
+
+            while node_items_index < len(node_items) and arc_items_index < len(arc_items):
+                if node_items_index <= arc_items_index:
+                    item = node_items[node_items_index]
+                    node_items_index += 1
+                else:
+                    item = arc_items[arc_items_index]
+                    arc_items_index += 1
+                item.set_order(value)
+                value += 1
+
+            while node_items_index < len(node_items):
+                item = node_items[node_items_index]
+                node_items_index += 1
+                item.set_order(value)
+                value += 1
+
+            while arc_items_index < len(arc_items):
+                item = arc_items[arc_items_index]
+                arc_items_index +=  1
+                item.set_order(value)
+                value += 1
+
+    def placing(self):
+
+        layer_top = self.margins.top
+        max_layer_bottom = layer_top
+        layer_left = self.margins.left
+        max_layer_right = layer_left
+        is_leading_line = True
+        for layer in self.layers:
+            items = sorted(layer.items, key=(lambda item: item.order.value))
+            if is_leading_line:
+                is_leading_line = False
+            else:
+                layer_top += max(self.margins.top, self.margins.bottom)
+            layer_bottom = layer_top
+            layer_right = layer_left
+            is_leading_column = True
+            for item in items:
+                if is_leading_column:
+                    is_leading_column = False
+                else:
+                    layer_right += max(self.margins.left, self.margins.right)
+                if item.is_node:
+                    layer_bottom = max(layer_bottom, layer_top+item.node.dimensions.height)
+                    layer_right += item.node.dimensions.width
+            
+            item_left = layer_left
+            is_leading_column = True
+            for item in items:
+                if is_leading_column:
+                    is_leading_column = False
+                else:
+                    item_left += max(self.margins.left, self.margins.right)
+                if item.is_node:
+                    node = item.node
+                    x1 = item_left
+                    y1 = (layer_top + layer_bottom - node.dimensions.height)/2
+                    x2 = x1 + node.dimensions.width
+                    y2 = y1 + node.dimensions.height
+                    node.set_position(x1, y1, x2, y2)
+                    cx = (x1 + x2)/2
+                    cy = (y1 + y2)/2
+                    for arc in node.incoming + node.outgoing:
+                        arc.add_vertex(cx, cy)
+                    item_left += node.dimensions.width
+                else:
+                    arc = item.arc
+                    x = item_left
+                    y = layer_top
+                    arc.add_vertex(x, y)
+                    x = item_left
+                    y = layer_bottom
+                    arc.add_vertex(x, y)
+
+            layer_top = layer_bottom
+
+            max_layer_bottom = layer_bottom
+            max_layer_right = max(max_layer_right, layer_right)
+
+        graph_width = max_layer_right + self.margins.right
+        graph_height = max_layer_bottom + self.margins.bottom
+
+        self.set_size(graph_width, graph_height)
+
+    def layout(self):
+        self.measuring()
+        self.sorting()
+        self.layering()
+        self.ordering()
+        self.placing()
+
+    def draw(self, stream):
+        self.layout()
+
+        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.size.width, self.size.height)
+        context = cairo.Context(surface)
+        context.set_font_size(self.style.font_size)
+
+        context.set_source_rgb(*self.style.arc_color)
+        for arc in self.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(*self.style.node_color)
+        for node in self.nodes:
+            position = node.position
+            context.new_path()
+            x = position.x1
+            y = position.y1
+            w = position.x2 - position.x1
+            h = position.y2 - position.y1
+            context.rectangle(x, y, w, h)
+            context.fill()
+
+        context.set_source_rgb(*self.style.text_color)
+        for node in self.nodes:
+            if node.label is not None:
+                cx = (node.position.x1 + node.position.x2)/2
+                cy = (node.position.y1 + node.position.y2)/2
+                text = node.label.text
+                extents = context.text_extents(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(text)
+
+        surface.write_to_png(stream)
+
+
+class GraphMargins(object):
+
+    def __init__(self, graph, top, right, bottom, left):
+        self.graph = graph
+        self.top = top
+        self.right = right
+        self.bottom = bottom
+        self.left = left
+        graph.margins = self
+
+
+class GraphStyle(object):
+
+    def __init__(self, graph, background_color, node_color,
+                arc_color, text_color, font_size):
+        self.graph = graph
+        self.background_color = background_color
+        self.node_color = node_color
+        self.arc_color = arc_color
+        self.text_color = text_color
+        self.font_size = font_size
+        graph.style = self
+
+
+class GraphSize(object):
+
+    def __init__(self, graph, width, height):
+        self.graph = graph
+        self.width = width
+        self.height = height
+        graph.size = self
+
+
+class Node(object):
+
+    def __init__(self, graph):
+        self.graph = graph
+        self.incoming = []
+        self.outgoing = []
+        self.label = None
+        self.dimensions = None
+        self.position = None
+        self.rank = None
+        self.order = None
+        self.layer_item = None
+        self.index = len(graph.nodes)
+        graph.nodes.append(self)
+
+    def set_label(self, text):
+        return NodeLabel(self, text)
+
+    def set_dimensions(self, width, height):
+        return NodeDimensions(self, width, height)
+
+    def set_position(self, x1, y1, x2, y2):
+        return NodePosition(self, x1, y1, x2, y2)
+
+    def set_rank(self, value):
+        return NodeRank(self, value)
+
+
+class NodeLabel(object):
+
+    def __init__(self, node, text):
+        self.node = node
+        self.text = text
+        node.label = self
+
+
+class NodeDimensions(object):
+
+    def __init__(self, node, width, height):
+        self.node = node
+        self.width = width
+        self.height = height
+        node.dimensions = self
+
+
+class NodePosition(object):
+
+    def __init__(self, node, x1, y1, x2, y2):
+        self.node = node
+        self.x1 = x1
+        self.y1 = y1
+        self.x2 = x2
+        self.y2 = y2
+        node.position = self
+
+
+class NodeRank(object):
+
+    def __init__(self, node, value):
+        self.node = node
+        self.value = value
+        node.rank = self
+
+
+class NodeOrder(object):
+
+    def __init__(self, node, value):
+        self.node = node
+        self.value = value
+        node.order = self
+
+
+class Arc(object):
+
+    def __init__(self, graph, origin, target, weight):
+        self.graph = graph
+        self.origin = origin
+        self.target = target
+        self.weight = weight
+        self.label = None
+        self.layer_items = []
+        self.vertices = []
+        self.index = len(graph.arcs)
+        graph.arcs.append(self)
+        origin.outgoing.append(self)
+        target.incoming.append(self)
+
+    def set_label(self, text):
+        return ArcLabel(self, text)
+
+    def set_order(self, values):
+        return ArcOrder(self, values)
+
+    def add_vertex(self, x, y):
+        return ArcVertex(self, x, y)
+
+
+class ArcLabel(object):
+
+    def __init__(self, arc, text):
+        self.arc = arc
+        self.text = text
+        arc.label = self
+
+
+class ArcOrder(object):
+
+    def __init__(self, arc, values):
+        self.arc = arc
+        self.values = values
+        arc.order = self
+
+
+class ArcVertex(object):
+
+    def __init__(self, arc, x, y):
+        self.arc = arc
+        self.x = x
+        self.y = y
+        arc.vertices.append(self)
+
+
+class Layer(object):
+
+    def __init__(self, graph):
+        self.graph = graph
+        self.items = []
+        self.index = len(graph.layers)
+        graph.layers.append(self)
+
+    def add_node_item(self, node):
+        return LayerNodeItem(self, node)
+
+    def add_arc_item(self, arc):
+        return LayerArcItem(self, arc)
+
+
+class LayerNodeItem(object):
+
+    def __init__(self, layer, node):
+        self.layer = layer
+        self.node = node
+        self.is_node = True
+        self.is_arc = False
+        self.order = None
+        self.index = len(layer.items)
+        layer.items.append(self)
+        node.layer_item = self
+
+    def set_order(self, value):
+        return LayerItemOrder(self, value)
+
+
+class LayerArcItem(object):
+
+    def __init__(self, layer, arc):
+        self.layer = layer
+        self.arc = arc
+        self.is_node = False
+        self.is_arc = True
+        self.order = None
+        self.index = len(layer.items)
+        layer.items.append(self)
+        arc.layer_items.append(self)
+
+    def set_order(self, value):
+        return LayerItemOrder(self, value)
+
+
+class LayerItemOrder(object):
+
+    def __init__(self, layer_item, value):
+        self.layer_item = layer_item
+        self.value = value
+        layer_item.order = self
+
+
+
+from gd import *
+
+graph = Graph()
+
+op_organization = graph.add_node()
+op_project = graph.add_node()
+op_person = graph.add_node()
+op_participation = graph.add_node()
+hr_private_info = graph.add_node()
+ws_workitem = graph.add_node()
+ws_worklist = graph.add_node()
+ws_dependency = graph.add_node()
+wsx_issue = graph.add_node()
+wsx_meeting = graph.add_node()
+wsx_meeting_topic = graph.add_node()
+wsx_standing_meeting = graph.add_node()
+tb_invoice = graph.add_node()
+tb_lineitem = graph.add_node()
+tb_timeslip = graph.add_node()
+
+organization_division_of_fk = graph.add_arc(op_organization, op_organization, 0.5)
+project_organization_fk = graph.add_arc(op_project, op_organization, 0.5)
+person_organization_fk = graph.add_arc(op_person, op_organization, 1.0)
+participation_project_fk = graph.add_arc(op_participation, op_project, 1.0)
+participation_person_fk = graph.add_arc(op_participation, op_person, 1.0)
+private_info_person_fk = graph.add_arc(hr_private_info, op_person, 1.0)
+workitem_project_fk = graph.add_arc(ws_workitem, op_project, 1.0)
+workitem_worklist_fk = graph.add_arc(ws_workitem, ws_worklist, 0.5)
+worklist_workitem_fk = graph.add_arc(ws_worklist, ws_workitem, 1.0)
+worklist_person_fk = graph.add_arc(ws_worklist, op_person, 0.5)
+dependency_of_workitem_fk = graph.add_arc(ws_dependency, ws_workitem, 1.0)
+dependency_on_workitem_fk = graph.add_arc(ws_dependency, ws_workitem, 1.0)
+issue_workitem_fk = graph.add_arc(wsx_issue, ws_workitem, 1.0)
+issue_person_fk = graph.add_arc(wsx_issue, op_person, 0.5)
+meeting_workitem_fk = graph.add_arc(wsx_meeting, ws_workitem, 1.0)
+meeting_topic_meeting_fk = graph.add_arc(wsx_meeting_topic, wsx_meeting, 1.0)
+standing_meeting_meeting_fk = graph.add_arc(wsx_standing_meeting, wsx_meeting, 1.0)
+invoice_organization_fk = graph.add_arc(tb_invoice, op_organization, 0.5)
+lineitem_invoice_fk = graph.add_arc(tb_lineitem, tb_invoice, 1.0)
+timeslip_participation_fk = graph.add_arc(tb_timeslip, op_participation, 1.0)
+timeslip_lineitem_fk = graph.add_arc(tb_timeslip, tb_lineitem, 0.5)
+timeslip_workitem_fk = graph.add_arc(tb_timeslip, ws_workitem, 0.5)
+
+op_organization.set_label("op.organization")
+op_project.set_label("op.project")
+op_person.set_label("op.person")
+op_participation.set_label("op.participation")
+hr_private_info.set_label("hr.private_info")
+ws_workitem.set_label("ws.workitem")
+ws_worklist.set_label("ws.worklist")
+ws_dependency.set_label("ws.dependency")
+wsx_issue.set_label("wsx.issue")
+wsx_meeting.set_label("wsx.meeting")
+wsx_meeting_topic.set_label("wsx.meeting_topic")
+wsx_standing_meeting.set_label("wsx.standing_meeting")
+tb_invoice.set_label("tb.invoice")
+tb_lineitem.set_label("tb.lineitem")
+tb_timeslip.set_label("tb.timeslip")
+
+organization_division_of_fk.set_label("division_of")
+project_organization_fk.set_label("organization")
+person_organization_fk.set_label("organization")
+participation_project_fk.set_label("project")
+participation_person_fk.set_label("person")
+private_info_person_fk.set_label("person")
+workitem_project_fk.set_label("project")
+workitem_worklist_fk.set_label("worklist")
+worklist_workitem_fk.set_label("workitem")
+worklist_person_fk.set_label("assigned_to")
+dependency_of_workitem_fk.set_label("of")
+dependency_on_workitem_fk.set_label("on")
+issue_workitem_fk.set_label("workitem")
+issue_person_fk.set_label("reported_by")
+meeting_workitem_fk.set_label("workitem")
+meeting_topic_meeting_fk.set_label("meeting")
+standing_meeting_meeting_fk.set_label("meeting")
+invoice_organization_fk.set_label("organization")
+lineitem_invoice_fk.set_label("invoice")
+timeslip_participation_fk.set_label("participation")
+timeslip_lineitem_fk.set_label("lineitem")
+timeslip_workitem_fk.set_label("workitem")
+
+graph.set_style(
+        background_color=(0.5, 0.5, 0.5),
+        node_color=(0.0, 0.0, 0.5),
+        arc_color=(0.5, 0.0, 0.0),
+        text_color=(1.0, 1.0, 1.0),
+        font_size=14.0)
+
+stream = open('gd_htsql_test.png', 'wb')
+graph.draw(stream)
+
+
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.