/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
1
# -*- coding: UTF-8 -*-
2
"""Directed graph production.
3
4
This module contains the code to produce an ordered directed graph of a
5
bzr branch, such as we display in the tree view at the top of the bzrk
6
window.
7
"""
8
9
__copyright__ = "Copyright © 2005 Canonical Ltd."
10
__author__    = "Scott James Remnant <scott@ubuntu.com>"
11
256.2.25 by Gary van der Merwe
Use Merge sort
12
from bzrlib.tsort import merge_sort
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
13
14
def linegraph(branch, start, maxnum):
15
    """Produce a directed graph of a bzr branch.
16
256.4.1 by Gary van der Merwe
* Set a width and use ellips on Revision No column.
17
    Returns a tuple of (line_graph, revid_index, columns_len) where
18
    * line_graph is a list of tuples of (revid,
19
                                         node,
20
                                         lines,
21
                                         parents,
22
                                         children,
23
                                         revno_sequence),
24
    * revid_index is a dict of each revision with the key being the revid, and
25
      the value the row index, and
26
    * columns_len is the number of columns need to draw the line graph.
27
    
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
28
29
    Node is a tuple of (column, colour) with column being a zero-indexed
30
    column number of the graph that this revision represents and colour
31
    being a zero-indexed colour (which doesn't specify any actual colour
32
    in particular) to draw the node in.
33
34
    Lines is a list of tuples which represent lines you should draw away
35
    from the revision, if you also need to draw lines into the revision
36
    you should use the lines list from the previous iteration.  Each
37
    typle in the list is in the form (start, end, colour) with start and
38
    end being zero-indexed column numbers and colour as in node.
39
40
    It's up to you how to actually draw the nodes and lines (straight,
41
    curved, kinked, etc.) and to pick the actual colours for each index.
42
    """
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
43
    
44
    # We get the mainline so we can pass it to merge_sort to make merge_sort
45
    # run faster.
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
46
    mainline = branch.revision_history()
47
    graph_parents = branch.repository.get_revision_graph(start)
48
    graph_children = {}
256.2.42 by Gary van der Merwe
Performance improvements.
49
    for revid in graph_parents.iterkeys():
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
50
        graph_children[revid] = []
256.2.28 by Gary van der Merwe
Better line drawing with info from merge_sort
51
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
52
    merge_sorted_revisions = merge_sort(
53
        graph_parents,
54
        start,
55
        mainline,
56
        generate_revno=True)
57
    
256.2.28 by Gary van der Merwe
Better line drawing with info from merge_sort
58
    revid_index = {}
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
59
    revno_index = {}
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
60
    
61
    # This will hold an item for each "branch". For a revisions, the revsion
62
    # number less the least significant digit is the branch_id, and used as the
63
    # key for the dict. Hence revision with the same revsion number less the
64
    # least significant digit are considered to be in the same branch line.
65
    # e.g.: for revisions 290.12.1 and 290.12.2, the branch_id would be 290.12,
66
    # and these two revisions will be in the same branch line. Each value is
67
    # a list of [rev_indexes, min_index, max_index, col_index].
256.2.28 by Gary van der Merwe
Better line drawing with info from merge_sort
68
    branch_lines = {}
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
69
    BL_REV_INDEXES = 0
70
    BL_MIN_INDEX = 1
71
    BL_MAX_INDEX = 2
72
    BL_COL_INDEX = 3
73
    
256.2.28 by Gary van der Merwe
Better line drawing with info from merge_sort
74
    linegraph = []    
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
75
    
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
76
    for (rev_index, (sequence_number,
77
                     revid,
78
                     merge_depth,
79
                     revno_sequence,
80
                     end_of_merge)) in enumerate(merge_sorted_revisions):
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
81
        
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
82
        revid_index[revid] = rev_index
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
83
        revno_index[revno_sequence] = rev_index
256.2.28 by Gary van der Merwe
Better line drawing with info from merge_sort
84
        
85
        branch_id = revno_sequence[0:-1]
86
        
256.2.33 by Gary van der Merwe
Revert fast test
87
        branch_line = None
88
        if branch_id not in branch_lines:
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
89
            branch_line = [[],        # BL_REV_INDEXES
90
                           rev_index, # BL_MIN_INDEX
91
                           0,         # BL_MAX_INDEX
92
                           None]      # BL_COL_INDEX
256.2.33 by Gary van der Merwe
Revert fast test
93
            branch_lines[branch_id] = branch_line
94
        else:
95
            branch_line = branch_lines[branch_id]
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
96
        
97
        branch_line[BL_REV_INDEXES].append(rev_index)
98
        if rev_index > branch_line[BL_MAX_INDEX]:
99
            branch_line[BL_MAX_INDEX] = rev_index
256.2.33 by Gary van der Merwe
Revert fast test
100
        
256.2.28 by Gary van der Merwe
Better line drawing with info from merge_sort
101
        parents = graph_parents[revid]
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
102
        for parent_revid in parents:
103
            graph_children[parent_revid].append(revid)
104
        
256.2.33 by Gary van der Merwe
Revert fast test
105
        linegraph.append([revid,
106
                          None,
256.2.27 by Gary van der Merwe
Show Revision No in tree
107
                          [],
108
                          parents,
256.2.33 by Gary van der Merwe
Revert fast test
109
                          None,
110
                          revno_sequence])
111
112
    branch_ids = branch_lines.keys()
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
113
    
256.2.39 by Gary van der Merwe
Tweek order that branch lines are processed for more understandable results.
114
    def branch_id_cmp(x, y):
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
115
        """Compaire branch_id's first by the number of digits, then reversed
116
        by their value"""
256.2.39 by Gary van der Merwe
Tweek order that branch lines are processed for more understandable results.
117
        len_x = len(x)
118
        len_y = len(y)
119
        if len_x == len_y:
120
            return -cmp(x, y)
121
        return cmp(len_x, len_y)
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
122
    
256.2.39 by Gary van der Merwe
Tweek order that branch lines are processed for more understandable results.
123
    branch_ids.sort(branch_id_cmp)
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
124
    # This will hold a tuple of (child_index, parent_index, col_index) for each
125
    # line that needs to be drawn. If col_index is not none, then the line is
126
    # drawn along that column, else the the line can be drawn directly between
127
    # the child and parent because either the child and parent are in the same
128
    # branch line, or the child and parent are 1 row apart.
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
129
    lines = []
256.2.42 by Gary van der Merwe
Performance improvements.
130
    empty_column = [False for i in range(len(graph_parents))]
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
131
    # This will hold a bit map for each cell. If the cell is true, then the
132
    # cell allready contains a node or line. This use when deciding what column
133
    # to place a branch line or line in, without it overlaping something else.
256.2.44 by Gary van der Merwe
Chose what column to place nodes and lines in better.
134
    columns = [list(empty_column)]
256.2.42 by Gary van der Merwe
Performance improvements.
135
    
256.2.38 by Gary van der Merwe
Add support for lines that go between branches.
136
    
137
    for branch_id in branch_ids:
138
        branch_line = branch_lines[branch_id]
139
        
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
140
        # Find the col_index for the direct parent branch. This will be the
141
        # starting point when looking for a free column.
256.2.44 by Gary van der Merwe
Chose what column to place nodes and lines in better.
142
        parent_col_index = 0
143
        if len(branch_id) > 1:
144
            parent_branch_id = branch_id[0:-2]
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
145
            parent_col_index = branch_lines[parent_branch_id][BL_COL_INDEX]
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
146
            parent_revno = branch_id[0:-1]
147
            if parent_revno in revno_index:
148
                parent_index = revno_index[parent_revno]
149
                branch_line[BL_MAX_INDEX] = parent_index - 1
150
        
151
        col_search_order = _branch_line_col_search_order(columns,
152
                                                         parent_col_index)
153
        branch_line[BL_COL_INDEX] = _append_line(columns,
154
                                                (branch_line[BL_MIN_INDEX],
155
                                                 branch_line[BL_MAX_INDEX]),
156
                                                empty_column,
157
                                                col_search_order)
256.2.33 by Gary van der Merwe
Revert fast test
158
        color = reduce(lambda x, y: x+y, branch_id, 0)
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
159
        col_index = branch_line[BL_COL_INDEX]
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
160
        node = (col_index, color)        
256.2.33 by Gary van der Merwe
Revert fast test
161
        
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
162
        for rev_index in branch_line[BL_REV_INDEXES]:
256.2.33 by Gary van der Merwe
Revert fast test
163
            (sequence_number,
164
                 revid,
165
                 merge_depth,
166
                 revno_sequence,
167
                 end_of_merge) = merge_sorted_revisions[rev_index]
168
            
169
            linegraph[rev_index][1] = node
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
170
            linegraph[rev_index][4] = graph_children[revid]
171
            
172
            for parent_revid in graph_parents[revid]:
173
                if parent_revid in revid_index:
174
                    parent_index = revid_index[parent_revid]
175
                    parent_revno = merge_sorted_revisions[parent_index][3]
176
                    parent_branch_id = parent_revno[0:-1]
256.2.42 by Gary van der Merwe
Performance improvements.
177
                    col_index = None
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
178
                    is_direct_parent_line = False
179
                    if len(branch_id) > 1:
180
                        if parent_revno == branch_id[0:-1]:
181
                            is_direct_parent_line = True
182
                    
183
                    # A line only needs it's own column if it is going from
184
                    # one branch line to another, it's not the line to the
185
                    # direct parent, and if it is longer than one row.
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
186
                    if branch_id != parent_branch_id and \
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
187
                       parent_index - rev_index > 1 and \
188
                       not is_direct_parent_line:
256.2.47 by Gary van der Merwe
Tweek the search order of columns.
189
                        parent_node = linegraph[parent_index][1]
190
                        if parent_node:
191
                            parent_col_index = parent_node[0]
192
                        else:
193
                            parent_col_index = None
194
                        col_search_order = \
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
195
                                _line_col_search_order(columns,
196
                                                       parent_col_index,
197
                                                       branch_line[BL_COL_INDEX])
198
                        col_index = _append_line(columns,
199
                                                 (rev_index+1, parent_index-1),
200
                                                 empty_column,
201
                                                 col_search_order)
256.2.42 by Gary van der Merwe
Performance improvements.
202
                    lines.append((rev_index, parent_index, col_index))
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
203
    
256.2.42 by Gary van der Merwe
Performance improvements.
204
    for (child_index, parent_index, line_col_index) in lines:
205
        child_col_index = linegraph[child_index][1][0]
206
        
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
207
        parent_node = linegraph[parent_index][1]
208
        parent_col_index = parent_node[0]
209
        color = parent_node[1]
210
        
256.2.42 by Gary van der Merwe
Performance improvements.
211
        if line_col_index:
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
212
            # line from the child's column to the lines column
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
213
            linegraph[child_index][2].append(
214
                (child_col_index,
215
                 line_col_index,
216
                 color))
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
217
            # lines down the line's column
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
218
            for line_part_index in range(child_index+1, parent_index-1):
219
                linegraph[line_part_index][2].append(
220
                    (line_col_index,   
221
                     line_col_index,
222
                     color))
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
223
            # line from the line's column to the parent's column
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
224
            linegraph[parent_index-1][2].append(
225
                (line_col_index,
226
                 parent_col_index,
227
                 color))
228
        else:
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
229
            # lines down the child's column
230
            for line_part_index in range(child_index, parent_index-1):
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
231
                linegraph[line_part_index][2].append(
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
232
                    (child_col_index,   
233
                     child_col_index,
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
234
                     color))
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
235
            # line from the child's column to the parent's column
236
            linegraph[parent_index-1][2].append(
237
                (child_col_index,
238
                 parent_col_index,
239
                 color))
256.2.28 by Gary van der Merwe
Better line drawing with info from merge_sort
240
    
256.4.1 by Gary van der Merwe
* Set a width and use ellips on Revision No column.
241
    return (linegraph, revid_index, len(columns))
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
242
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
243
def _branch_line_col_search_order(columns, parent_col_index):
256.2.47 by Gary van der Merwe
Tweek the search order of columns.
244
    return range(parent_col_index, len(columns)) + \
245
           range(parent_col_index-1, -1, -1)
246
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
247
def _line_col_search_order(columns, parent_col_index, child_col_index):
256.2.47 by Gary van der Merwe
Tweek the search order of columns.
248
    dest_col_indexes = []
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
249
    if parent_col_index is not None:
250
        dest_col_indexes.append(parent_col_index)
251
    else:
252
        dest_col_indexes.append(child_col_index)
253
    dest_col_indexes.append(child_col_index)
256.2.47 by Gary van der Merwe
Tweek the search order of columns.
254
    dest_col_indexes.sort()
255
    col_search_order = range(dest_col_indexes[1], dest_col_indexes[0] -1, -1) 
256
    i = 1
257
    while dest_col_indexes[1] + i < len(columns) or \
258
          dest_col_indexes[0] - i > -1:
259
        if dest_col_indexes[1] + i < len(columns):
260
            col_search_order.append(dest_col_indexes[1] + i)
261
        if dest_col_indexes[0] - i > -1:
262
            col_search_order.append(dest_col_indexes[0] - i)
263
        i += 1
264
    return col_search_order
265
256.2.48 by Gary van der Merwe
Make the line to a direct parent in the same column as the child branch line.
266
def _append_line(columns, line, empty_column, col_search_order):
256.2.42 by Gary van der Merwe
Performance improvements.
267
    line_range = range(line[0], line[1]+1)
256.2.46 by Gary van der Merwe
Store branch lines as list rather than dict.
268
    
256.2.47 by Gary van der Merwe
Tweek the search order of columns.
269
    for col_index in col_search_order:
256.2.44 by Gary van der Merwe
Chose what column to place nodes and lines in better.
270
        column = columns[col_index]
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
271
        has_overlaping_line = False
256.2.42 by Gary van der Merwe
Performance improvements.
272
        for row_index in line_range:
273
            if column[row_index]:
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
274
                has_overlaping_line = True
275
                break
276
        if not has_overlaping_line:
277
            break
278
    else:
279
        col_index = len(columns)
256.2.42 by Gary van der Merwe
Performance improvements.
280
        column = list(empty_column)
281
        columns.append(column)
256.2.40 by Gary van der Merwe
Take out a whole bunch of code for better performance.
282
    
256.2.42 by Gary van der Merwe
Performance improvements.
283
    for row_index in line_range:
284
        column[row_index] = True
285
    return col_index
256.2.26 by Gary van der Merwe
Use info from merge sort to draw lines. Needs work
286
287
def same_branch(a, b):
288
    """Return whether we think revisions a and b are on the same branch."""
289
    if len(a.parent_ids) == 1:
290
        # Defacto same branch if only parent
291
        return True
292
    elif a.committer == b.committer:
293
        # Same committer so may as well be
294
        return True
295
    else:
296
        return False