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