Fossil SCM

Do not draw risers and descenders all the way to the top and bottom of the graph if that is not necessary.

drh 2019-05-16 19:00 graph-improvements
Commit 9978f8120d422d0a7e4d6b7a24af271d4a15471eec1f954bce98ce4c26b1c533
2 files changed +57 -22 +20 -4
+57 -22
--- src/graph.c
+++ src/graph.c
@@ -18,10 +18,30 @@
1818
** This file contains code to compute a revision history graph.
1919
*/
2020
#include "config.h"
2121
#include "graph.h"
2222
#include <assert.h>
23
+
24
+/* Notes:
25
+**
26
+** The graph is laid out in 1 or more "rails". A "rail" is a vertical
27
+** band in the graph in which one can place nodes or arrows connecting
28
+** nodes. There can be between 1 and GR_MAX_RAIL rails. If the graph
29
+** is to complex to be displayed in GR_MAX_RAIL rails, it is omitted.
30
+**
31
+** A "riser" is the thick line that comes out of the top of a node and
32
+** goes up to the next node on the branch, or to the top of the screen.
33
+** A "descender" is a thick line that comes out of the bottom of a node
34
+** and proceeds down to the bottom of the page.
35
+**
36
+** Invoke graph_init() to create a new GraphContext object. Then
37
+** call graph_add_row() to add nodes, one by one, to the graph.
38
+** Nodes must be added in display order, from top to bottom.
39
+** Then invoke graph_render() to run the layout algorithm. The
40
+** layout algorithm computes which rails all of the nodes sit on, and
41
+** the rails used for merge arrows.
42
+*/
2343
2444
#if INTERFACE
2545
2646
#define GR_MAX_RAIL 40 /* Max number of "rails" to display */
2747
@@ -33,11 +53,11 @@
3353
** The nParent field is -1 for entires that do not participate in the graph
3454
** but which are included just so that we can capture their background color.
3555
*/
3656
struct GraphRow {
3757
int rid; /* The rid for the check-in */
38
- i8 nParent; /* Number of parents. -1 for technote lines */
58
+ i8 nParent; /* Number of parents. */
3959
i8 nCherrypick; /* Subset of aParent that are cherrypicks */
4060
i8 nNonCherrypick; /* Number of non-cherrypick parents */
4161
int *aParent; /* Array of parents. 0 element is primary .*/
4262
char *zBranch; /* Branch name */
4363
char *zBgClr; /* Background Color */
@@ -44,11 +64,11 @@
4464
char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
4565
4666
GraphRow *pNext; /* Next row down in the list of all rows */
4767
GraphRow *pPrev; /* Previous row */
4868
49
- int idx; /* Row index. First is 1. 0 used for "none" */
69
+ int idx; /* Row index. Top row is smallest. */
5070
int idxTop; /* Direct descendent highest up on the graph */
5171
GraphRow *pChild; /* Child immediately above this node */
5272
u8 isDup; /* True if this is duplicate of a prior entry */
5373
u8 isLeaf; /* True if this is a leaf node */
5474
u8 isStepParent; /* pChild is actually a step-child */
@@ -61,11 +81,10 @@
6181
int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
6282
int mergeUpto; /* Draw the mergeOut rail up to this level */
6383
int cherrypickUpto; /* Continue the mergeOut rail up to here */
6484
u64 mergeDown; /* Draw merge lines up from bottom of graph */
6585
u64 cherrypickDown; /* Draw cherrypick lines up from bottom */
66
-
6786
u64 railInUse; /* Mask of occupied rails at this row */
6887
};
6988
7089
/* Context while building a graph
7190
*/
@@ -83,10 +102,17 @@
83102
84103
#endif
85104
86105
/* The N-th bit */
87106
#define BIT(N) (((u64)1)<<(N))
107
+
108
+/*
109
+** Number of rows before and answer a node with a riser or descender
110
+** that goes off-screen before we can reuse that rail.
111
+*/
112
+#define RISER_MARGIN 4
113
+
88114
89115
/*
90116
** Malloc for zeroed space. Panic if unable to provide the
91117
** requested space.
92118
*/
@@ -246,11 +272,11 @@
246272
for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){}
247273
while( pRow && pRow->idx<=btm ){
248274
inUseMask |= pRow->railInUse;
249275
pRow = pRow->pNext;
250276
}
251
- for(i=0; i<32; i++){
277
+ for(i=0; i<GR_MAX_RAIL; i++){
252278
if( (inUseMask & BIT(i))==0 ){
253279
int dist;
254280
if( iNearto<=0 ){
255281
return i;
256282
}
@@ -268,11 +294,11 @@
268294
}
269295
270296
/*
271297
** Assign all children of node pBottom to the same rail as pBottom.
272298
*/
273
-static void assignChildrenToRail(GraphRow *pBottom){
299
+static void assignChildrenToRail(GraphRow *pBottom, u32 tmFlags){
274300
int iRail = pBottom->iRail;
275301
GraphRow *pCurrent;
276302
GraphRow *pPrior;
277303
u64 mask = ((u64)1)<<iRail;
278304
@@ -287,10 +313,18 @@
287313
while( pPrior->idx > pCurrent->idx ){
288314
pPrior->railInUse |= mask;
289315
pPrior = pPrior->pPrev;
290316
assert( pPrior!=0 );
291317
}
318
+ }
319
+ /* Mask of additional rows for the riser to infinity */
320
+ if( !pPrior->isLeaf && (tmFlags & TIMELINE_DISJOINT)==0 ){
321
+ int n = RISER_MARGIN;
322
+ GraphRow *p;
323
+ for(p=pPrior; p && (n--)>0; p=p->pPrev){
324
+ p->railInUse |= mask;
325
+ }
292326
}
293327
}
294328
295329
/*
296330
** Create a merge-arrow riser going from pParent up to pChild.
@@ -305,20 +339,21 @@
305339
u64 mask;
306340
GraphRow *pLoop;
307341
308342
if( pParent->mergeOut<0 ){
309343
u = pParent->aiRiser[pParent->iRail];
310
- if( u>=0 && u<pChild->idx ){
344
+ if( u>0 && u<pChild->idx ){
311345
/* The thick arrow up to the next primary child of pDesc goes
312346
** further up than the thin merge arrow riser, so draw them both
313347
** on the same rail. */
314348
pParent->mergeOut = pParent->iRail;
315
- }else{
349
+ }else{
316350
/* The thin merge arrow riser is taller than the thick primary
317351
** child riser, so use separate rails. */
318352
int iTarget = pParent->iRail;
319
- pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, iTarget);
353
+ int iBtm = pParent->idx - (u==0 ? RISER_MARGIN : 1);
354
+ pParent->mergeOut = findFreeRail(p, pChild->idx, iBtm, iTarget);
320355
mask = BIT(pParent->mergeOut);
321356
for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid;
322357
pLoop=pLoop->pNext){
323358
pLoop->railInUse |= mask;
324359
}
@@ -353,16 +388,18 @@
353388
}
354389
}
355390
}
356391
357392
/*
358
-** Draw a riser from pRow to the top of the graph
393
+** Draw a riser from pRow upward to indicate that it is going
394
+** to a node that is off the graph to the top.
359395
*/
360396
static void riser_to_top(GraphRow *pRow){
361397
u64 mask = BIT(pRow->iRail);
398
+ int n = RISER_MARGIN;
362399
pRow->aiRiser[pRow->iRail] = 0;
363
- while( pRow ){
400
+ while( pRow && (n--)>0 ){
364401
pRow->railInUse |= mask;
365402
pRow = pRow->pPrev;
366403
}
367404
}
368405
@@ -488,13 +525,14 @@
488525
pParent->idxTop = pRow->idxTop;
489526
}
490527
}
491528
492529
if( tmFlags & TIMELINE_FILLGAPS ){
493
- /* If a node has no pChild, and there is a later node (a node higher
494
- ** up on the graph) in the same branch that has no parent, then make
495
- ** the lower node a step-child of the upper node.
530
+ /* If a node has no pChild but there is a node higher up in the graph
531
+ ** that is in the same branch and that other node has no parent in
532
+ ** the graph, the lower node a step-child of the upper node. This will
533
+ ** be represented on the graph by a thick dotted line without an arrowhead.
496534
*/
497535
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
498536
if( pRow->pChild ) continue;
499537
for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){
500538
if( pLoop->nParent>0
@@ -510,11 +548,11 @@
510548
}
511549
}
512550
}
513551
514552
/* Identify rows where the primary parent is off screen. Assign
515
- ** each to a rail and draw descenders to the bottom of the screen.
553
+ ** each to a rail and draw descenders downward.
516554
**
517555
** Strive to put the "trunk" branch on far left.
518556
*/
519557
zTrunk = persistBranchName(p, "trunk");
520558
for(i=0; i<2; i++){
@@ -525,24 +563,21 @@
525563
if( pRow->zBranch!=zTrunk ) continue;
526564
}else {
527565
if( pRow->iRail>=0 ) continue;
528566
}
529567
if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
530
- if( omitDescenders ){
531
- pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx, 0);
532
- }else{
533
- pRow->iRail = ++p->mxRail;
534
- }
568
+ pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+RISER_MARGIN, 0);
535569
if( p->mxRail>=GR_MAX_RAIL ) return;
536570
mask = BIT(pRow->iRail);
537571
if( !omitDescenders ){
572
+ int n = RISER_MARGIN;
538573
pRow->bDescender = pRow->nParent>0;
539
- for(pLoop=pRow; pLoop; pLoop=pLoop->pNext){
574
+ for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
540575
pLoop->railInUse |= mask;
541576
}
542577
}
543
- assignChildrenToRail(pRow);
578
+ assignChildrenToRail(pRow, tmFlags);
544579
}
545580
}
546581
}
547582
548583
/* Assign rails to all rows that are still unassigned.
@@ -592,11 +627,11 @@
592627
}
593628
}
594629
mask = BIT(pRow->iRail);
595630
pRow->railInUse |= mask;
596631
if( pRow->pChild ){
597
- assignChildrenToRail(pRow);
632
+ assignChildrenToRail(pRow, tmFlags);
598633
}else if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){
599634
if( !pRow->timeWarp ) riser_to_top(pRow);
600635
}
601636
if( pParent ){
602637
for(pLoop=pParent->pPrev; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
603638
--- src/graph.c
+++ src/graph.c
@@ -18,10 +18,30 @@
18 ** This file contains code to compute a revision history graph.
19 */
20 #include "config.h"
21 #include "graph.h"
22 #include <assert.h>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24 #if INTERFACE
25
26 #define GR_MAX_RAIL 40 /* Max number of "rails" to display */
27
@@ -33,11 +53,11 @@
33 ** The nParent field is -1 for entires that do not participate in the graph
34 ** but which are included just so that we can capture their background color.
35 */
36 struct GraphRow {
37 int rid; /* The rid for the check-in */
38 i8 nParent; /* Number of parents. -1 for technote lines */
39 i8 nCherrypick; /* Subset of aParent that are cherrypicks */
40 i8 nNonCherrypick; /* Number of non-cherrypick parents */
41 int *aParent; /* Array of parents. 0 element is primary .*/
42 char *zBranch; /* Branch name */
43 char *zBgClr; /* Background Color */
@@ -44,11 +64,11 @@
44 char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
45
46 GraphRow *pNext; /* Next row down in the list of all rows */
47 GraphRow *pPrev; /* Previous row */
48
49 int idx; /* Row index. First is 1. 0 used for "none" */
50 int idxTop; /* Direct descendent highest up on the graph */
51 GraphRow *pChild; /* Child immediately above this node */
52 u8 isDup; /* True if this is duplicate of a prior entry */
53 u8 isLeaf; /* True if this is a leaf node */
54 u8 isStepParent; /* pChild is actually a step-child */
@@ -61,11 +81,10 @@
61 int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
62 int mergeUpto; /* Draw the mergeOut rail up to this level */
63 int cherrypickUpto; /* Continue the mergeOut rail up to here */
64 u64 mergeDown; /* Draw merge lines up from bottom of graph */
65 u64 cherrypickDown; /* Draw cherrypick lines up from bottom */
66
67 u64 railInUse; /* Mask of occupied rails at this row */
68 };
69
70 /* Context while building a graph
71 */
@@ -83,10 +102,17 @@
83
84 #endif
85
86 /* The N-th bit */
87 #define BIT(N) (((u64)1)<<(N))
 
 
 
 
 
 
 
88
89 /*
90 ** Malloc for zeroed space. Panic if unable to provide the
91 ** requested space.
92 */
@@ -246,11 +272,11 @@
246 for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){}
247 while( pRow && pRow->idx<=btm ){
248 inUseMask |= pRow->railInUse;
249 pRow = pRow->pNext;
250 }
251 for(i=0; i<32; i++){
252 if( (inUseMask & BIT(i))==0 ){
253 int dist;
254 if( iNearto<=0 ){
255 return i;
256 }
@@ -268,11 +294,11 @@
268 }
269
270 /*
271 ** Assign all children of node pBottom to the same rail as pBottom.
272 */
273 static void assignChildrenToRail(GraphRow *pBottom){
274 int iRail = pBottom->iRail;
275 GraphRow *pCurrent;
276 GraphRow *pPrior;
277 u64 mask = ((u64)1)<<iRail;
278
@@ -287,10 +313,18 @@
287 while( pPrior->idx > pCurrent->idx ){
288 pPrior->railInUse |= mask;
289 pPrior = pPrior->pPrev;
290 assert( pPrior!=0 );
291 }
 
 
 
 
 
 
 
 
292 }
293 }
294
295 /*
296 ** Create a merge-arrow riser going from pParent up to pChild.
@@ -305,20 +339,21 @@
305 u64 mask;
306 GraphRow *pLoop;
307
308 if( pParent->mergeOut<0 ){
309 u = pParent->aiRiser[pParent->iRail];
310 if( u>=0 && u<pChild->idx ){
311 /* The thick arrow up to the next primary child of pDesc goes
312 ** further up than the thin merge arrow riser, so draw them both
313 ** on the same rail. */
314 pParent->mergeOut = pParent->iRail;
315 }else{
316 /* The thin merge arrow riser is taller than the thick primary
317 ** child riser, so use separate rails. */
318 int iTarget = pParent->iRail;
319 pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, iTarget);
 
320 mask = BIT(pParent->mergeOut);
321 for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid;
322 pLoop=pLoop->pNext){
323 pLoop->railInUse |= mask;
324 }
@@ -353,16 +388,18 @@
353 }
354 }
355 }
356
357 /*
358 ** Draw a riser from pRow to the top of the graph
 
359 */
360 static void riser_to_top(GraphRow *pRow){
361 u64 mask = BIT(pRow->iRail);
 
362 pRow->aiRiser[pRow->iRail] = 0;
363 while( pRow ){
364 pRow->railInUse |= mask;
365 pRow = pRow->pPrev;
366 }
367 }
368
@@ -488,13 +525,14 @@
488 pParent->idxTop = pRow->idxTop;
489 }
490 }
491
492 if( tmFlags & TIMELINE_FILLGAPS ){
493 /* If a node has no pChild, and there is a later node (a node higher
494 ** up on the graph) in the same branch that has no parent, then make
495 ** the lower node a step-child of the upper node.
 
496 */
497 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
498 if( pRow->pChild ) continue;
499 for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){
500 if( pLoop->nParent>0
@@ -510,11 +548,11 @@
510 }
511 }
512 }
513
514 /* Identify rows where the primary parent is off screen. Assign
515 ** each to a rail and draw descenders to the bottom of the screen.
516 **
517 ** Strive to put the "trunk" branch on far left.
518 */
519 zTrunk = persistBranchName(p, "trunk");
520 for(i=0; i<2; i++){
@@ -525,24 +563,21 @@
525 if( pRow->zBranch!=zTrunk ) continue;
526 }else {
527 if( pRow->iRail>=0 ) continue;
528 }
529 if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
530 if( omitDescenders ){
531 pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx, 0);
532 }else{
533 pRow->iRail = ++p->mxRail;
534 }
535 if( p->mxRail>=GR_MAX_RAIL ) return;
536 mask = BIT(pRow->iRail);
537 if( !omitDescenders ){
 
538 pRow->bDescender = pRow->nParent>0;
539 for(pLoop=pRow; pLoop; pLoop=pLoop->pNext){
540 pLoop->railInUse |= mask;
541 }
542 }
543 assignChildrenToRail(pRow);
544 }
545 }
546 }
547
548 /* Assign rails to all rows that are still unassigned.
@@ -592,11 +627,11 @@
592 }
593 }
594 mask = BIT(pRow->iRail);
595 pRow->railInUse |= mask;
596 if( pRow->pChild ){
597 assignChildrenToRail(pRow);
598 }else if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){
599 if( !pRow->timeWarp ) riser_to_top(pRow);
600 }
601 if( pParent ){
602 for(pLoop=pParent->pPrev; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
603
--- src/graph.c
+++ src/graph.c
@@ -18,10 +18,30 @@
18 ** This file contains code to compute a revision history graph.
19 */
20 #include "config.h"
21 #include "graph.h"
22 #include <assert.h>
23
24 /* Notes:
25 **
26 ** The graph is laid out in 1 or more "rails". A "rail" is a vertical
27 ** band in the graph in which one can place nodes or arrows connecting
28 ** nodes. There can be between 1 and GR_MAX_RAIL rails. If the graph
29 ** is to complex to be displayed in GR_MAX_RAIL rails, it is omitted.
30 **
31 ** A "riser" is the thick line that comes out of the top of a node and
32 ** goes up to the next node on the branch, or to the top of the screen.
33 ** A "descender" is a thick line that comes out of the bottom of a node
34 ** and proceeds down to the bottom of the page.
35 **
36 ** Invoke graph_init() to create a new GraphContext object. Then
37 ** call graph_add_row() to add nodes, one by one, to the graph.
38 ** Nodes must be added in display order, from top to bottom.
39 ** Then invoke graph_render() to run the layout algorithm. The
40 ** layout algorithm computes which rails all of the nodes sit on, and
41 ** the rails used for merge arrows.
42 */
43
44 #if INTERFACE
45
46 #define GR_MAX_RAIL 40 /* Max number of "rails" to display */
47
@@ -33,11 +53,11 @@
53 ** The nParent field is -1 for entires that do not participate in the graph
54 ** but which are included just so that we can capture their background color.
55 */
56 struct GraphRow {
57 int rid; /* The rid for the check-in */
58 i8 nParent; /* Number of parents. */
59 i8 nCherrypick; /* Subset of aParent that are cherrypicks */
60 i8 nNonCherrypick; /* Number of non-cherrypick parents */
61 int *aParent; /* Array of parents. 0 element is primary .*/
62 char *zBranch; /* Branch name */
63 char *zBgClr; /* Background Color */
@@ -44,11 +64,11 @@
64 char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
65
66 GraphRow *pNext; /* Next row down in the list of all rows */
67 GraphRow *pPrev; /* Previous row */
68
69 int idx; /* Row index. Top row is smallest. */
70 int idxTop; /* Direct descendent highest up on the graph */
71 GraphRow *pChild; /* Child immediately above this node */
72 u8 isDup; /* True if this is duplicate of a prior entry */
73 u8 isLeaf; /* True if this is a leaf node */
74 u8 isStepParent; /* pChild is actually a step-child */
@@ -61,11 +81,10 @@
81 int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
82 int mergeUpto; /* Draw the mergeOut rail up to this level */
83 int cherrypickUpto; /* Continue the mergeOut rail up to here */
84 u64 mergeDown; /* Draw merge lines up from bottom of graph */
85 u64 cherrypickDown; /* Draw cherrypick lines up from bottom */
 
86 u64 railInUse; /* Mask of occupied rails at this row */
87 };
88
89 /* Context while building a graph
90 */
@@ -83,10 +102,17 @@
102
103 #endif
104
105 /* The N-th bit */
106 #define BIT(N) (((u64)1)<<(N))
107
108 /*
109 ** Number of rows before and answer a node with a riser or descender
110 ** that goes off-screen before we can reuse that rail.
111 */
112 #define RISER_MARGIN 4
113
114
115 /*
116 ** Malloc for zeroed space. Panic if unable to provide the
117 ** requested space.
118 */
@@ -246,11 +272,11 @@
272 for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){}
273 while( pRow && pRow->idx<=btm ){
274 inUseMask |= pRow->railInUse;
275 pRow = pRow->pNext;
276 }
277 for(i=0; i<GR_MAX_RAIL; i++){
278 if( (inUseMask & BIT(i))==0 ){
279 int dist;
280 if( iNearto<=0 ){
281 return i;
282 }
@@ -268,11 +294,11 @@
294 }
295
296 /*
297 ** Assign all children of node pBottom to the same rail as pBottom.
298 */
299 static void assignChildrenToRail(GraphRow *pBottom, u32 tmFlags){
300 int iRail = pBottom->iRail;
301 GraphRow *pCurrent;
302 GraphRow *pPrior;
303 u64 mask = ((u64)1)<<iRail;
304
@@ -287,10 +313,18 @@
313 while( pPrior->idx > pCurrent->idx ){
314 pPrior->railInUse |= mask;
315 pPrior = pPrior->pPrev;
316 assert( pPrior!=0 );
317 }
318 }
319 /* Mask of additional rows for the riser to infinity */
320 if( !pPrior->isLeaf && (tmFlags & TIMELINE_DISJOINT)==0 ){
321 int n = RISER_MARGIN;
322 GraphRow *p;
323 for(p=pPrior; p && (n--)>0; p=p->pPrev){
324 p->railInUse |= mask;
325 }
326 }
327 }
328
329 /*
330 ** Create a merge-arrow riser going from pParent up to pChild.
@@ -305,20 +339,21 @@
339 u64 mask;
340 GraphRow *pLoop;
341
342 if( pParent->mergeOut<0 ){
343 u = pParent->aiRiser[pParent->iRail];
344 if( u>0 && u<pChild->idx ){
345 /* The thick arrow up to the next primary child of pDesc goes
346 ** further up than the thin merge arrow riser, so draw them both
347 ** on the same rail. */
348 pParent->mergeOut = pParent->iRail;
349 }else{
350 /* The thin merge arrow riser is taller than the thick primary
351 ** child riser, so use separate rails. */
352 int iTarget = pParent->iRail;
353 int iBtm = pParent->idx - (u==0 ? RISER_MARGIN : 1);
354 pParent->mergeOut = findFreeRail(p, pChild->idx, iBtm, iTarget);
355 mask = BIT(pParent->mergeOut);
356 for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid;
357 pLoop=pLoop->pNext){
358 pLoop->railInUse |= mask;
359 }
@@ -353,16 +388,18 @@
388 }
389 }
390 }
391
392 /*
393 ** Draw a riser from pRow upward to indicate that it is going
394 ** to a node that is off the graph to the top.
395 */
396 static void riser_to_top(GraphRow *pRow){
397 u64 mask = BIT(pRow->iRail);
398 int n = RISER_MARGIN;
399 pRow->aiRiser[pRow->iRail] = 0;
400 while( pRow && (n--)>0 ){
401 pRow->railInUse |= mask;
402 pRow = pRow->pPrev;
403 }
404 }
405
@@ -488,13 +525,14 @@
525 pParent->idxTop = pRow->idxTop;
526 }
527 }
528
529 if( tmFlags & TIMELINE_FILLGAPS ){
530 /* If a node has no pChild but there is a node higher up in the graph
531 ** that is in the same branch and that other node has no parent in
532 ** the graph, the lower node a step-child of the upper node. This will
533 ** be represented on the graph by a thick dotted line without an arrowhead.
534 */
535 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
536 if( pRow->pChild ) continue;
537 for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){
538 if( pLoop->nParent>0
@@ -510,11 +548,11 @@
548 }
549 }
550 }
551
552 /* Identify rows where the primary parent is off screen. Assign
553 ** each to a rail and draw descenders downward.
554 **
555 ** Strive to put the "trunk" branch on far left.
556 */
557 zTrunk = persistBranchName(p, "trunk");
558 for(i=0; i<2; i++){
@@ -525,24 +563,21 @@
563 if( pRow->zBranch!=zTrunk ) continue;
564 }else {
565 if( pRow->iRail>=0 ) continue;
566 }
567 if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
568 pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+RISER_MARGIN, 0);
 
 
 
 
569 if( p->mxRail>=GR_MAX_RAIL ) return;
570 mask = BIT(pRow->iRail);
571 if( !omitDescenders ){
572 int n = RISER_MARGIN;
573 pRow->bDescender = pRow->nParent>0;
574 for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
575 pLoop->railInUse |= mask;
576 }
577 }
578 assignChildrenToRail(pRow, tmFlags);
579 }
580 }
581 }
582
583 /* Assign rails to all rows that are still unassigned.
@@ -592,11 +627,11 @@
627 }
628 }
629 mask = BIT(pRow->iRail);
630 pRow->railInUse |= mask;
631 if( pRow->pChild ){
632 assignChildrenToRail(pRow, tmFlags);
633 }else if( !omitDescenders && count_nonbranch_children(pRow->rid)!=0 ){
634 if( !pRow->timeWarp ) riser_to_top(pRow);
635 }
636 if( pParent ){
637 for(pLoop=pParent->pPrev; pLoop && pLoop!=pRow; pLoop=pLoop->pPrev){
638
+20 -4
--- src/graph.js
+++ src/graph.js
@@ -197,11 +197,11 @@
197197
drawLine(line,color,x,y0,null,y1);
198198
x = to.x + (node.w-arw.w)/2;
199199
var n = drawBox(arw.cls,null,x,y);
200200
if(color) n.style.borderBottomColor = color;
201201
}
202
- function drawUpDotted(from,to,color){
202
+ function drawDotted(from,to,color){
203203
var x = to.x + (node.w-line.w)/2;
204204
var y0 = from.y + node.h/2;
205205
var y1 = Math.ceil(to.y + node.h);
206206
drawLine(dotLine,color,x,y0,null,y1);
207207
}
@@ -243,21 +243,37 @@
243243
e = document.getElementById("md"+p.id);
244244
if(e) e.style.backgroundColor = p.bg;
245245
}
246246
if( p.r<0 ) return;
247247
if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg);
248
- if( p.sb>0 ) drawUpDotted(p,tx.rowinfo[p.sb-tx.iTopRow],null);
248
+ if( p.sb>0 ) drawDotted(p,tx.rowinfo[p.sb-tx.iTopRow],null);
249249
var cls = node.cls;
250250
if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge";
251251
if( p.f&1 ) cls += " leaf";
252252
var n = drawBox(cls,p.bg,p.x,p.y);
253253
n.id = "tln"+p.id;
254254
n.onclick = clickOnNode;
255255
n.style.zIndex = 10;
256256
if( !tx.omitDescenders ){
257
- if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
258
- if( p.hasOwnProperty('d') ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
257
+ if( p.u==0 ){
258
+ if( p.hasOwnProperty('mo') && p.r==p.mo ){
259
+ var top = tx.rowinfo[p.mu-tx.iTopRow]
260
+ drawUpArrow(p,{x: p.x, y: top.y-node.h}, p.fg);
261
+ }else if( p.y>100 ){
262
+ drawUpArrow(p,{x: p.x, y: p.y-50}, p.fg);
263
+ }else{
264
+ drawUpArrow(p,{x: p.x, y: 0},p.fg);
265
+ }
266
+ }
267
+ if( p.hasOwnProperty('d') ){
268
+ if( p.y + 150 >= btm ){
269
+ drawUpArrow({x: p.x, y: btm - node.h/2},p,p.fg);
270
+ }else{
271
+ drawUpArrow({x: p.x, y: p.y+50},p,p.fg);
272
+ drawDotted({x: p.x, y: p.y+63},{x: p.x, y: p.y+50-node.h/2},null);
273
+ }
274
+ }
259275
}
260276
if( p.hasOwnProperty('mo') ){
261277
var x0 = p.x + node.w/2;
262278
var x1 = p.mo*railPitch + node.w/2;
263279
var u = tx.rowinfo[p.mu-tx.iTopRow];
264280
--- src/graph.js
+++ src/graph.js
@@ -197,11 +197,11 @@
197 drawLine(line,color,x,y0,null,y1);
198 x = to.x + (node.w-arw.w)/2;
199 var n = drawBox(arw.cls,null,x,y);
200 if(color) n.style.borderBottomColor = color;
201 }
202 function drawUpDotted(from,to,color){
203 var x = to.x + (node.w-line.w)/2;
204 var y0 = from.y + node.h/2;
205 var y1 = Math.ceil(to.y + node.h);
206 drawLine(dotLine,color,x,y0,null,y1);
207 }
@@ -243,21 +243,37 @@
243 e = document.getElementById("md"+p.id);
244 if(e) e.style.backgroundColor = p.bg;
245 }
246 if( p.r<0 ) return;
247 if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg);
248 if( p.sb>0 ) drawUpDotted(p,tx.rowinfo[p.sb-tx.iTopRow],null);
249 var cls = node.cls;
250 if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge";
251 if( p.f&1 ) cls += " leaf";
252 var n = drawBox(cls,p.bg,p.x,p.y);
253 n.id = "tln"+p.id;
254 n.onclick = clickOnNode;
255 n.style.zIndex = 10;
256 if( !tx.omitDescenders ){
257 if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
258 if( p.hasOwnProperty('d') ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259 }
260 if( p.hasOwnProperty('mo') ){
261 var x0 = p.x + node.w/2;
262 var x1 = p.mo*railPitch + node.w/2;
263 var u = tx.rowinfo[p.mu-tx.iTopRow];
264
--- src/graph.js
+++ src/graph.js
@@ -197,11 +197,11 @@
197 drawLine(line,color,x,y0,null,y1);
198 x = to.x + (node.w-arw.w)/2;
199 var n = drawBox(arw.cls,null,x,y);
200 if(color) n.style.borderBottomColor = color;
201 }
202 function drawDotted(from,to,color){
203 var x = to.x + (node.w-line.w)/2;
204 var y0 = from.y + node.h/2;
205 var y1 = Math.ceil(to.y + node.h);
206 drawLine(dotLine,color,x,y0,null,y1);
207 }
@@ -243,21 +243,37 @@
243 e = document.getElementById("md"+p.id);
244 if(e) e.style.backgroundColor = p.bg;
245 }
246 if( p.r<0 ) return;
247 if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg);
248 if( p.sb>0 ) drawDotted(p,tx.rowinfo[p.sb-tx.iTopRow],null);
249 var cls = node.cls;
250 if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge";
251 if( p.f&1 ) cls += " leaf";
252 var n = drawBox(cls,p.bg,p.x,p.y);
253 n.id = "tln"+p.id;
254 n.onclick = clickOnNode;
255 n.style.zIndex = 10;
256 if( !tx.omitDescenders ){
257 if( p.u==0 ){
258 if( p.hasOwnProperty('mo') && p.r==p.mo ){
259 var top = tx.rowinfo[p.mu-tx.iTopRow]
260 drawUpArrow(p,{x: p.x, y: top.y-node.h}, p.fg);
261 }else if( p.y>100 ){
262 drawUpArrow(p,{x: p.x, y: p.y-50}, p.fg);
263 }else{
264 drawUpArrow(p,{x: p.x, y: 0},p.fg);
265 }
266 }
267 if( p.hasOwnProperty('d') ){
268 if( p.y + 150 >= btm ){
269 drawUpArrow({x: p.x, y: btm - node.h/2},p,p.fg);
270 }else{
271 drawUpArrow({x: p.x, y: p.y+50},p,p.fg);
272 drawDotted({x: p.x, y: p.y+63},{x: p.x, y: p.y+50-node.h/2},null);
273 }
274 }
275 }
276 if( p.hasOwnProperty('mo') ){
277 var x0 = p.x + node.w/2;
278 var x1 = p.mo*railPitch + node.w/2;
279 var u = tx.rowinfo[p.mu-tx.iTopRow];
280

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button