Fossil SCM
Timeline graph layout changes that strive to do better a communicating the merging and branching activity between multiple branches.
Commit
d1d7fce64eefa15c8d9c029c05b09b27336176370cc2d241f8339d11769316cd
Parent
632d07c6a90810c…
1 file changed
+88
-37
+88
-37
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -109,10 +109,11 @@ | ||
| 109 | 109 | GraphRow *pLast; /* Last row in the list. Bottom row of graph. */ |
| 110 | 110 | int nBranch; /* Number of distinct branches */ |
| 111 | 111 | char **azBranch; /* Names of the branches */ |
| 112 | 112 | int nRow; /* Number of rows */ |
| 113 | 113 | int nHash; /* Number of slots in apHash[] */ |
| 114 | + u64 mergeRail; /* Rails used for merge lines */ | |
| 114 | 115 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 115 | 116 | u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ |
| 116 | 117 | }; |
| 117 | 118 | |
| 118 | 119 | #endif |
| @@ -119,11 +120,11 @@ | ||
| 119 | 120 | |
| 120 | 121 | /* The N-th bit */ |
| 121 | 122 | #define BIT(N) (((u64)1)<<(N)) |
| 122 | 123 | |
| 123 | 124 | /* |
| 124 | -** Number of rows before and answer a node with a riser or descender | |
| 125 | +** Number of rows before and after a node with a riser or descender | |
| 125 | 126 | ** that goes off-screen before we can reuse that rail. |
| 126 | 127 | */ |
| 127 | 128 | #define RISER_MARGIN 4 |
| 128 | 129 | |
| 129 | 130 | |
| @@ -275,11 +276,12 @@ | ||
| 275 | 276 | ** top and bottom, inclusive. |
| 276 | 277 | */ |
| 277 | 278 | static int findFreeRail( |
| 278 | 279 | GraphContext *p, /* The graph context */ |
| 279 | 280 | int top, int btm, /* Span of rows for which the rail is needed */ |
| 280 | - int iNearto /* Find rail nearest to this rail */ | |
| 281 | + int iNearto, /* Find rail nearest to this rail */ | |
| 282 | + int bMergeRail /* This rail will be used for a merge line */ | |
| 281 | 283 | ){ |
| 282 | 284 | GraphRow *pRow; |
| 283 | 285 | int i; |
| 284 | 286 | int iBest = 0; |
| 285 | 287 | int iBestDist = 9999; |
| @@ -287,15 +289,38 @@ | ||
| 287 | 289 | for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){} |
| 288 | 290 | while( pRow && pRow->idx<=btm ){ |
| 289 | 291 | inUseMask |= pRow->railInUse; |
| 290 | 292 | pRow = pRow->pNext; |
| 291 | 293 | } |
| 292 | - for(i=0; i<GR_MAX_RAIL; i++){ | |
| 293 | - if( (inUseMask & BIT(i))==0 ){ | |
| 294 | + | |
| 295 | + /* First look for a match that honors bMergeRail */ | |
| 296 | + for(i=0; i<=p->mxRail; i++){ | |
| 297 | + u64 m = BIT(i); | |
| 298 | + int dist; | |
| 299 | + if( inUseMask & m ) continue; | |
| 300 | + if( (bMergeRail!=0) != ((p->mergeRail & m)!=0) ) continue; | |
| 301 | + if( iNearto<=0 ){ | |
| 302 | + iBest = i; | |
| 303 | + iBestDist = 1; | |
| 304 | + break; | |
| 305 | + } | |
| 306 | + dist = i - iNearto; | |
| 307 | + if( dist<0 ) dist = -dist; | |
| 308 | + if( dist<iBestDist ){ | |
| 309 | + iBestDist = dist; | |
| 310 | + iBest = i; | |
| 311 | + } | |
| 312 | + } | |
| 313 | + | |
| 314 | + /* If no match, consider all possible rails */ | |
| 315 | + if( iBestDist>1000 ){ | |
| 316 | + for(i=0; i<=p->mxRail+1; i++){ | |
| 294 | 317 | int dist; |
| 318 | + if( inUseMask & BIT(i) ) continue; | |
| 295 | 319 | if( iNearto<=0 ){ |
| 296 | 320 | iBest = i; |
| 321 | + iBestDist = 1; | |
| 297 | 322 | break; |
| 298 | 323 | } |
| 299 | 324 | dist = i - iNearto; |
| 300 | 325 | if( dist<0 ) dist = -dist; |
| 301 | 326 | if( dist<iBestDist ){ |
| @@ -304,10 +329,11 @@ | ||
| 304 | 329 | } |
| 305 | 330 | } |
| 306 | 331 | } |
| 307 | 332 | if( iBestDist>1000 ) p->nErr++; |
| 308 | 333 | if( iBest>p->mxRail ) p->mxRail = iBest; |
| 334 | + if( bMergeRail ) p->mergeRail |= BIT(iBest); | |
| 309 | 335 | return iBest; |
| 310 | 336 | } |
| 311 | 337 | |
| 312 | 338 | /* |
| 313 | 339 | ** Assign all children of node pBottom to the same rail as pBottom. |
| @@ -369,11 +395,12 @@ | ||
| 369 | 395 | pParent->mergeOut = pParent->iRail; |
| 370 | 396 | }else{ |
| 371 | 397 | /* The thin merge arrow riser is taller than the thick primary |
| 372 | 398 | ** child riser, so use separate rails. */ |
| 373 | 399 | int iTarget = pParent->iRail; |
| 374 | - pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, iTarget); | |
| 400 | + pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, | |
| 401 | + iTarget, 1); | |
| 375 | 402 | mask = BIT(pParent->mergeOut); |
| 376 | 403 | for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid; |
| 377 | 404 | pLoop=pLoop->pNext){ |
| 378 | 405 | pLoop->railInUse |= mask; |
| 379 | 406 | } |
| @@ -646,11 +673,11 @@ | ||
| 646 | 673 | if( i==0 && pRow->zBranch!=zTrunk ) continue; |
| 647 | 674 | if( pRow->iRail>=0 ) continue; |
| 648 | 675 | if( pRow->isDup ) continue; |
| 649 | 676 | if( pRow->nParent<0 ) continue; |
| 650 | 677 | if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ |
| 651 | - pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin, 0); | |
| 678 | + pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0); | |
| 652 | 679 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 653 | 680 | mask = BIT(pRow->iRail); |
| 654 | 681 | if( !omitDescenders ){ |
| 655 | 682 | int n = RISER_MARGIN; |
| 656 | 683 | pRow->bDescender = pRow->nParent>0; |
| @@ -690,11 +717,11 @@ | ||
| 690 | 717 | } |
| 691 | 718 | if( pParent->idx>pRow->idx ){ |
| 692 | 719 | /* Common case: Child occurs after parent and is above the |
| 693 | 720 | ** parent in the timeline */ |
| 694 | 721 | pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, |
| 695 | - pParent->iRail); | |
| 722 | + pParent->iRail, 0); | |
| 696 | 723 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 697 | 724 | pParent->aiRiser[pRow->iRail] = pRow->idx; |
| 698 | 725 | }else{ |
| 699 | 726 | /* Timewarp case: Child occurs earlier in time than parent and |
| 700 | 727 | ** appears below the parent in the timeline. */ |
| @@ -741,11 +768,11 @@ | ||
| 741 | 768 | int isCherrypick = 0; |
| 742 | 769 | for(i=1; i<pRow->nParent; i++){ |
| 743 | 770 | GraphRowId parentRid = pRow->aParent[i]; |
| 744 | 771 | if( i==pRow->nNonCherrypick ){ |
| 745 | 772 | /* Because full merges are laid out before cherrypicks, |
| 746 | - ** it is ok to use a full-merge raise for a cherrypick. | |
| 773 | + ** it is ok to use a full-merge raiser for a cherrypick. | |
| 747 | 774 | ** See the graph on check-in 8ac66ef33b464d28 for example |
| 748 | 775 | ** iReuseIdx = -1; |
| 749 | 776 | ** iReuseRail = -1; */ |
| 750 | 777 | isCherrypick = 1; |
| 751 | 778 | } |
| @@ -761,11 +788,11 @@ | ||
| 761 | 788 | iMrail = j; |
| 762 | 789 | break; |
| 763 | 790 | } |
| 764 | 791 | } |
| 765 | 792 | if( iMrail==-1 ){ |
| 766 | - iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0); | |
| 793 | + iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1); | |
| 767 | 794 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 768 | 795 | mergeRiserFrom[iMrail] = parentRid; |
| 769 | 796 | } |
| 770 | 797 | iReuseIdx = p->nRow+1; |
| 771 | 798 | iReuseRail = iMrail; |
| @@ -847,40 +874,64 @@ | ||
| 847 | 874 | ** |
| 848 | 875 | ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail. |
| 849 | 876 | */ |
| 850 | 877 | aMap = p->aiRailMap; |
| 851 | 878 | for(i=0; i<=p->mxRail; i++) aMap[i] = i; |
| 852 | - if( zLeftBranch && nTimewarp==0 ){ | |
| 853 | - char *zLeft = persistBranchName(p, zLeftBranch); | |
| 854 | - u64 pureMergeRail = (BIT(p->mxRail)-1)<<1|1; | |
| 855 | - u64 toLeft = 0; | |
| 856 | - j = 0; | |
| 857 | - for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ | |
| 858 | - pureMergeRail &= ~BIT(pRow->iRail); | |
| 859 | - if( pRow->zBranch==zLeft ){ | |
| 860 | - for(i=0; i<=p->mxRail; i++){ | |
| 861 | - if( pRow->mergeIn[i] ) toLeft |= BIT(i); | |
| 862 | - } | |
| 863 | - if( aMap[pRow->iRail]>=j ){ | |
| 864 | - for(i=0; i<=p->mxRail; i++){ | |
| 865 | - if( aMap[i]>=j && aMap[i]<=pRow->iRail ) aMap[i]++; | |
| 866 | - } | |
| 867 | - aMap[pRow->iRail] = j++; | |
| 868 | - } | |
| 869 | - } | |
| 870 | - } | |
| 871 | - toLeft &= pureMergeRail; | |
| 872 | - for(i=j; i<=p->mxRail; i++){ | |
| 873 | - int k; | |
| 874 | - if( (BIT(i) & toLeft)==0 ) continue; | |
| 875 | - for(k=0; k<=p->mxRail; k++){ | |
| 876 | - if( aMap[k]>=j && aMap[k]<=i ) aMap[k]++; | |
| 877 | - } | |
| 878 | - aMap[i] = j; | |
| 879 | - } | |
| 879 | + if( nTimewarp==0 ){ | |
| 880 | + u8 aPriority[GR_MAX_RAIL]; | |
| 881 | + memset(aPriority, 0, p->mxRail+1); | |
| 882 | + if( zLeftBranch ){ | |
| 883 | + char *zLeft = persistBranchName(p, zLeftBranch); | |
| 884 | + for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ | |
| 885 | + if( pRow->zBranch==zLeft ){ | |
| 886 | + aPriority[pRow->iRail] |= 4; | |
| 887 | + for(i=0; i<=p->mxRail; i++){ | |
| 888 | + if( pRow->mergeIn[i] ) aPriority[i] |= 1; | |
| 889 | + } | |
| 890 | + if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; | |
| 891 | + } | |
| 892 | + } | |
| 893 | + }else{ | |
| 894 | + j = 1; | |
| 895 | + aPriority[0] = 4; | |
| 896 | + for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ | |
| 897 | + if( pRow->iRail==0 ){ | |
| 898 | + for(i=0; i<=p->mxRail; i++){ | |
| 899 | + if( pRow->mergeIn[i] ) aPriority[i] |= 1; | |
| 900 | + } | |
| 901 | + if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; | |
| 902 | + } | |
| 903 | + } | |
| 904 | + } | |
| 905 | + j = 0; | |
| 906 | + for(i=0; i<=p->mxRail; i++){ | |
| 907 | + if( p->mergeRail & BIT(i) ){ | |
| 908 | + aPriority[i] |= 2; | |
| 909 | + } | |
| 910 | + } | |
| 911 | + | |
| 912 | +#if 0 | |
| 913 | + fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail); | |
| 914 | + fprintf(stderr,"Priority:"); | |
| 915 | + for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]); | |
| 916 | + fprintf(stderr,"\n"); | |
| 917 | +#endif | |
| 918 | + | |
| 919 | + for(i=0; i<=p->mxRail; i++){ | |
| 920 | + if( aPriority[i]>=4 ) aMap[i] = j++; | |
| 921 | + } | |
| 922 | + for(i=p->mxRail; i>=0; i--){ | |
| 923 | + if( aPriority[i]==3 ) aMap[i] = j++; | |
| 924 | + } | |
| 925 | + for(i=0; i<=p->mxRail; i++){ | |
| 926 | + if( aPriority[i]==1 || aPriority[i]==2 ) aMap[i] = j++; | |
| 927 | + } | |
| 928 | + for(i=0; i<=p->mxRail; i++){ | |
| 929 | + if( aPriority[i]==0 ) aMap[i] = j++; | |
| 930 | + } | |
| 880 | 931 | cgi_printf("<!-- aiRailMap ="); |
| 881 | 932 | for(i=0; i<=p->mxRail; i++) cgi_printf(" %d", aMap[i]); |
| 882 | 933 | cgi_printf(" -->\n"); |
| 883 | 934 | } |
| 884 | 935 | |
| 885 | 936 | p->nErr = 0; |
| 886 | 937 | } |
| 887 | 938 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -109,10 +109,11 @@ | |
| 109 | GraphRow *pLast; /* Last row in the list. Bottom row of graph. */ |
| 110 | int nBranch; /* Number of distinct branches */ |
| 111 | char **azBranch; /* Names of the branches */ |
| 112 | int nRow; /* Number of rows */ |
| 113 | int nHash; /* Number of slots in apHash[] */ |
| 114 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 115 | u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ |
| 116 | }; |
| 117 | |
| 118 | #endif |
| @@ -119,11 +120,11 @@ | |
| 119 | |
| 120 | /* The N-th bit */ |
| 121 | #define BIT(N) (((u64)1)<<(N)) |
| 122 | |
| 123 | /* |
| 124 | ** Number of rows before and answer a node with a riser or descender |
| 125 | ** that goes off-screen before we can reuse that rail. |
| 126 | */ |
| 127 | #define RISER_MARGIN 4 |
| 128 | |
| 129 | |
| @@ -275,11 +276,12 @@ | |
| 275 | ** top and bottom, inclusive. |
| 276 | */ |
| 277 | static int findFreeRail( |
| 278 | GraphContext *p, /* The graph context */ |
| 279 | int top, int btm, /* Span of rows for which the rail is needed */ |
| 280 | int iNearto /* Find rail nearest to this rail */ |
| 281 | ){ |
| 282 | GraphRow *pRow; |
| 283 | int i; |
| 284 | int iBest = 0; |
| 285 | int iBestDist = 9999; |
| @@ -287,15 +289,38 @@ | |
| 287 | for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){} |
| 288 | while( pRow && pRow->idx<=btm ){ |
| 289 | inUseMask |= pRow->railInUse; |
| 290 | pRow = pRow->pNext; |
| 291 | } |
| 292 | for(i=0; i<GR_MAX_RAIL; i++){ |
| 293 | if( (inUseMask & BIT(i))==0 ){ |
| 294 | int dist; |
| 295 | if( iNearto<=0 ){ |
| 296 | iBest = i; |
| 297 | break; |
| 298 | } |
| 299 | dist = i - iNearto; |
| 300 | if( dist<0 ) dist = -dist; |
| 301 | if( dist<iBestDist ){ |
| @@ -304,10 +329,11 @@ | |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | if( iBestDist>1000 ) p->nErr++; |
| 308 | if( iBest>p->mxRail ) p->mxRail = iBest; |
| 309 | return iBest; |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | ** Assign all children of node pBottom to the same rail as pBottom. |
| @@ -369,11 +395,12 @@ | |
| 369 | pParent->mergeOut = pParent->iRail; |
| 370 | }else{ |
| 371 | /* The thin merge arrow riser is taller than the thick primary |
| 372 | ** child riser, so use separate rails. */ |
| 373 | int iTarget = pParent->iRail; |
| 374 | pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, iTarget); |
| 375 | mask = BIT(pParent->mergeOut); |
| 376 | for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid; |
| 377 | pLoop=pLoop->pNext){ |
| 378 | pLoop->railInUse |= mask; |
| 379 | } |
| @@ -646,11 +673,11 @@ | |
| 646 | if( i==0 && pRow->zBranch!=zTrunk ) continue; |
| 647 | if( pRow->iRail>=0 ) continue; |
| 648 | if( pRow->isDup ) continue; |
| 649 | if( pRow->nParent<0 ) continue; |
| 650 | if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ |
| 651 | pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin, 0); |
| 652 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 653 | mask = BIT(pRow->iRail); |
| 654 | if( !omitDescenders ){ |
| 655 | int n = RISER_MARGIN; |
| 656 | pRow->bDescender = pRow->nParent>0; |
| @@ -690,11 +717,11 @@ | |
| 690 | } |
| 691 | if( pParent->idx>pRow->idx ){ |
| 692 | /* Common case: Child occurs after parent and is above the |
| 693 | ** parent in the timeline */ |
| 694 | pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, |
| 695 | pParent->iRail); |
| 696 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 697 | pParent->aiRiser[pRow->iRail] = pRow->idx; |
| 698 | }else{ |
| 699 | /* Timewarp case: Child occurs earlier in time than parent and |
| 700 | ** appears below the parent in the timeline. */ |
| @@ -741,11 +768,11 @@ | |
| 741 | int isCherrypick = 0; |
| 742 | for(i=1; i<pRow->nParent; i++){ |
| 743 | GraphRowId parentRid = pRow->aParent[i]; |
| 744 | if( i==pRow->nNonCherrypick ){ |
| 745 | /* Because full merges are laid out before cherrypicks, |
| 746 | ** it is ok to use a full-merge raise for a cherrypick. |
| 747 | ** See the graph on check-in 8ac66ef33b464d28 for example |
| 748 | ** iReuseIdx = -1; |
| 749 | ** iReuseRail = -1; */ |
| 750 | isCherrypick = 1; |
| 751 | } |
| @@ -761,11 +788,11 @@ | |
| 761 | iMrail = j; |
| 762 | break; |
| 763 | } |
| 764 | } |
| 765 | if( iMrail==-1 ){ |
| 766 | iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0); |
| 767 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 768 | mergeRiserFrom[iMrail] = parentRid; |
| 769 | } |
| 770 | iReuseIdx = p->nRow+1; |
| 771 | iReuseRail = iMrail; |
| @@ -847,40 +874,64 @@ | |
| 847 | ** |
| 848 | ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail. |
| 849 | */ |
| 850 | aMap = p->aiRailMap; |
| 851 | for(i=0; i<=p->mxRail; i++) aMap[i] = i; |
| 852 | if( zLeftBranch && nTimewarp==0 ){ |
| 853 | char *zLeft = persistBranchName(p, zLeftBranch); |
| 854 | u64 pureMergeRail = (BIT(p->mxRail)-1)<<1|1; |
| 855 | u64 toLeft = 0; |
| 856 | j = 0; |
| 857 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 858 | pureMergeRail &= ~BIT(pRow->iRail); |
| 859 | if( pRow->zBranch==zLeft ){ |
| 860 | for(i=0; i<=p->mxRail; i++){ |
| 861 | if( pRow->mergeIn[i] ) toLeft |= BIT(i); |
| 862 | } |
| 863 | if( aMap[pRow->iRail]>=j ){ |
| 864 | for(i=0; i<=p->mxRail; i++){ |
| 865 | if( aMap[i]>=j && aMap[i]<=pRow->iRail ) aMap[i]++; |
| 866 | } |
| 867 | aMap[pRow->iRail] = j++; |
| 868 | } |
| 869 | } |
| 870 | } |
| 871 | toLeft &= pureMergeRail; |
| 872 | for(i=j; i<=p->mxRail; i++){ |
| 873 | int k; |
| 874 | if( (BIT(i) & toLeft)==0 ) continue; |
| 875 | for(k=0; k<=p->mxRail; k++){ |
| 876 | if( aMap[k]>=j && aMap[k]<=i ) aMap[k]++; |
| 877 | } |
| 878 | aMap[i] = j; |
| 879 | } |
| 880 | cgi_printf("<!-- aiRailMap ="); |
| 881 | for(i=0; i<=p->mxRail; i++) cgi_printf(" %d", aMap[i]); |
| 882 | cgi_printf(" -->\n"); |
| 883 | } |
| 884 | |
| 885 | p->nErr = 0; |
| 886 | } |
| 887 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -109,10 +109,11 @@ | |
| 109 | GraphRow *pLast; /* Last row in the list. Bottom row of graph. */ |
| 110 | int nBranch; /* Number of distinct branches */ |
| 111 | char **azBranch; /* Names of the branches */ |
| 112 | int nRow; /* Number of rows */ |
| 113 | int nHash; /* Number of slots in apHash[] */ |
| 114 | u64 mergeRail; /* Rails used for merge lines */ |
| 115 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 116 | u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ |
| 117 | }; |
| 118 | |
| 119 | #endif |
| @@ -119,11 +120,11 @@ | |
| 120 | |
| 121 | /* The N-th bit */ |
| 122 | #define BIT(N) (((u64)1)<<(N)) |
| 123 | |
| 124 | /* |
| 125 | ** Number of rows before and after a node with a riser or descender |
| 126 | ** that goes off-screen before we can reuse that rail. |
| 127 | */ |
| 128 | #define RISER_MARGIN 4 |
| 129 | |
| 130 | |
| @@ -275,11 +276,12 @@ | |
| 276 | ** top and bottom, inclusive. |
| 277 | */ |
| 278 | static int findFreeRail( |
| 279 | GraphContext *p, /* The graph context */ |
| 280 | int top, int btm, /* Span of rows for which the rail is needed */ |
| 281 | int iNearto, /* Find rail nearest to this rail */ |
| 282 | int bMergeRail /* This rail will be used for a merge line */ |
| 283 | ){ |
| 284 | GraphRow *pRow; |
| 285 | int i; |
| 286 | int iBest = 0; |
| 287 | int iBestDist = 9999; |
| @@ -287,15 +289,38 @@ | |
| 289 | for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){} |
| 290 | while( pRow && pRow->idx<=btm ){ |
| 291 | inUseMask |= pRow->railInUse; |
| 292 | pRow = pRow->pNext; |
| 293 | } |
| 294 | |
| 295 | /* First look for a match that honors bMergeRail */ |
| 296 | for(i=0; i<=p->mxRail; i++){ |
| 297 | u64 m = BIT(i); |
| 298 | int dist; |
| 299 | if( inUseMask & m ) continue; |
| 300 | if( (bMergeRail!=0) != ((p->mergeRail & m)!=0) ) continue; |
| 301 | if( iNearto<=0 ){ |
| 302 | iBest = i; |
| 303 | iBestDist = 1; |
| 304 | break; |
| 305 | } |
| 306 | dist = i - iNearto; |
| 307 | if( dist<0 ) dist = -dist; |
| 308 | if( dist<iBestDist ){ |
| 309 | iBestDist = dist; |
| 310 | iBest = i; |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | /* If no match, consider all possible rails */ |
| 315 | if( iBestDist>1000 ){ |
| 316 | for(i=0; i<=p->mxRail+1; i++){ |
| 317 | int dist; |
| 318 | if( inUseMask & BIT(i) ) continue; |
| 319 | if( iNearto<=0 ){ |
| 320 | iBest = i; |
| 321 | iBestDist = 1; |
| 322 | break; |
| 323 | } |
| 324 | dist = i - iNearto; |
| 325 | if( dist<0 ) dist = -dist; |
| 326 | if( dist<iBestDist ){ |
| @@ -304,10 +329,11 @@ | |
| 329 | } |
| 330 | } |
| 331 | } |
| 332 | if( iBestDist>1000 ) p->nErr++; |
| 333 | if( iBest>p->mxRail ) p->mxRail = iBest; |
| 334 | if( bMergeRail ) p->mergeRail |= BIT(iBest); |
| 335 | return iBest; |
| 336 | } |
| 337 | |
| 338 | /* |
| 339 | ** Assign all children of node pBottom to the same rail as pBottom. |
| @@ -369,11 +395,12 @@ | |
| 395 | pParent->mergeOut = pParent->iRail; |
| 396 | }else{ |
| 397 | /* The thin merge arrow riser is taller than the thick primary |
| 398 | ** child riser, so use separate rails. */ |
| 399 | int iTarget = pParent->iRail; |
| 400 | pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, |
| 401 | iTarget, 1); |
| 402 | mask = BIT(pParent->mergeOut); |
| 403 | for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid; |
| 404 | pLoop=pLoop->pNext){ |
| 405 | pLoop->railInUse |= mask; |
| 406 | } |
| @@ -646,11 +673,11 @@ | |
| 673 | if( i==0 && pRow->zBranch!=zTrunk ) continue; |
| 674 | if( pRow->iRail>=0 ) continue; |
| 675 | if( pRow->isDup ) continue; |
| 676 | if( pRow->nParent<0 ) continue; |
| 677 | if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ |
| 678 | pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0); |
| 679 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 680 | mask = BIT(pRow->iRail); |
| 681 | if( !omitDescenders ){ |
| 682 | int n = RISER_MARGIN; |
| 683 | pRow->bDescender = pRow->nParent>0; |
| @@ -690,11 +717,11 @@ | |
| 717 | } |
| 718 | if( pParent->idx>pRow->idx ){ |
| 719 | /* Common case: Child occurs after parent and is above the |
| 720 | ** parent in the timeline */ |
| 721 | pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, |
| 722 | pParent->iRail, 0); |
| 723 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 724 | pParent->aiRiser[pRow->iRail] = pRow->idx; |
| 725 | }else{ |
| 726 | /* Timewarp case: Child occurs earlier in time than parent and |
| 727 | ** appears below the parent in the timeline. */ |
| @@ -741,11 +768,11 @@ | |
| 768 | int isCherrypick = 0; |
| 769 | for(i=1; i<pRow->nParent; i++){ |
| 770 | GraphRowId parentRid = pRow->aParent[i]; |
| 771 | if( i==pRow->nNonCherrypick ){ |
| 772 | /* Because full merges are laid out before cherrypicks, |
| 773 | ** it is ok to use a full-merge raiser for a cherrypick. |
| 774 | ** See the graph on check-in 8ac66ef33b464d28 for example |
| 775 | ** iReuseIdx = -1; |
| 776 | ** iReuseRail = -1; */ |
| 777 | isCherrypick = 1; |
| 778 | } |
| @@ -761,11 +788,11 @@ | |
| 788 | iMrail = j; |
| 789 | break; |
| 790 | } |
| 791 | } |
| 792 | if( iMrail==-1 ){ |
| 793 | iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1); |
| 794 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 795 | mergeRiserFrom[iMrail] = parentRid; |
| 796 | } |
| 797 | iReuseIdx = p->nRow+1; |
| 798 | iReuseRail = iMrail; |
| @@ -847,40 +874,64 @@ | |
| 874 | ** |
| 875 | ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail. |
| 876 | */ |
| 877 | aMap = p->aiRailMap; |
| 878 | for(i=0; i<=p->mxRail; i++) aMap[i] = i; |
| 879 | if( nTimewarp==0 ){ |
| 880 | u8 aPriority[GR_MAX_RAIL]; |
| 881 | memset(aPriority, 0, p->mxRail+1); |
| 882 | if( zLeftBranch ){ |
| 883 | char *zLeft = persistBranchName(p, zLeftBranch); |
| 884 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 885 | if( pRow->zBranch==zLeft ){ |
| 886 | aPriority[pRow->iRail] |= 4; |
| 887 | for(i=0; i<=p->mxRail; i++){ |
| 888 | if( pRow->mergeIn[i] ) aPriority[i] |= 1; |
| 889 | } |
| 890 | if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; |
| 891 | } |
| 892 | } |
| 893 | }else{ |
| 894 | j = 1; |
| 895 | aPriority[0] = 4; |
| 896 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 897 | if( pRow->iRail==0 ){ |
| 898 | for(i=0; i<=p->mxRail; i++){ |
| 899 | if( pRow->mergeIn[i] ) aPriority[i] |= 1; |
| 900 | } |
| 901 | if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; |
| 902 | } |
| 903 | } |
| 904 | } |
| 905 | j = 0; |
| 906 | for(i=0; i<=p->mxRail; i++){ |
| 907 | if( p->mergeRail & BIT(i) ){ |
| 908 | aPriority[i] |= 2; |
| 909 | } |
| 910 | } |
| 911 | |
| 912 | #if 0 |
| 913 | fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail); |
| 914 | fprintf(stderr,"Priority:"); |
| 915 | for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]); |
| 916 | fprintf(stderr,"\n"); |
| 917 | #endif |
| 918 | |
| 919 | for(i=0; i<=p->mxRail; i++){ |
| 920 | if( aPriority[i]>=4 ) aMap[i] = j++; |
| 921 | } |
| 922 | for(i=p->mxRail; i>=0; i--){ |
| 923 | if( aPriority[i]==3 ) aMap[i] = j++; |
| 924 | } |
| 925 | for(i=0; i<=p->mxRail; i++){ |
| 926 | if( aPriority[i]==1 || aPriority[i]==2 ) aMap[i] = j++; |
| 927 | } |
| 928 | for(i=0; i<=p->mxRail; i++){ |
| 929 | if( aPriority[i]==0 ) aMap[i] = j++; |
| 930 | } |
| 931 | cgi_printf("<!-- aiRailMap ="); |
| 932 | for(i=0; i<=p->mxRail; i++) cgi_printf(" %d", aMap[i]); |
| 933 | cgi_printf(" -->\n"); |
| 934 | } |
| 935 | |
| 936 | p->nErr = 0; |
| 937 | } |
| 938 |