Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Timeline graph layout changes that strive to do better a communicating the merging and branching activity between multiple branches. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
d1d7fce64eefa15c8d9c029c05b09b27 |
User & Date: | drh 2022-03-08 01:07:25 |
Context
2022-03-08
| ||
13:08 | Faster implementation of start_of_branch() using a CTE. ... (check-in: 8736de8b user: drh tags: trunk) | |
01:07 | Timeline graph layout changes that strive to do better a communicating the merging and branching activity between multiple branches. ... (check-in: d1d7fce6 user: drh tags: trunk) | |
2022-03-07
| ||
21:12 | Fix the display of cherrypick links that are on the same rail as their origin node but then go left. ... (check-in: 632d07c6 user: drh tags: trunk) | |
Changes
Changes to src/graph.c.
︙ | ︙ | |||
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | int mxRail; /* Number of rails required to render the graph */ GraphRow *pFirst; /* First row in the list. Top row of graph. */ GraphRow *pLast; /* Last row in the list. Bottom row of graph. */ int nBranch; /* Number of distinct branches */ char **azBranch; /* Names of the branches */ int nRow; /* Number of rows */ int nHash; /* Number of slots in apHash[] */ GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ }; #endif /* The N-th bit */ #define BIT(N) (((u64)1)<<(N)) /* | > | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | int mxRail; /* Number of rails required to render the graph */ GraphRow *pFirst; /* First row in the list. Top row of graph. */ GraphRow *pLast; /* Last row in the list. Bottom row of graph. */ int nBranch; /* Number of distinct branches */ char **azBranch; /* Names of the branches */ int nRow; /* Number of rows */ int nHash; /* Number of slots in apHash[] */ u64 mergeRail; /* Rails used for merge lines */ GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ }; #endif /* The N-th bit */ #define BIT(N) (((u64)1)<<(N)) /* ** Number of rows before and after a node with a riser or descender ** that goes off-screen before we can reuse that rail. */ #define RISER_MARGIN 4 /* ** Malloc for zeroed space. Panic if unable to provide the |
︙ | ︙ | |||
273 274 275 276 277 278 279 | /* ** Return the index of a rail currently not in use for any row between ** top and bottom, inclusive. */ static int findFreeRail( GraphContext *p, /* The graph context */ int top, int btm, /* Span of rows for which the rail is needed */ | | > > > | > > | > > > > > > > > > > > > > > > > > > > > | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | /* ** Return the index of a rail currently not in use for any row between ** top and bottom, inclusive. */ static int findFreeRail( GraphContext *p, /* The graph context */ int top, int btm, /* Span of rows for which the rail is needed */ int iNearto, /* Find rail nearest to this rail */ int bMergeRail /* This rail will be used for a merge line */ ){ GraphRow *pRow; int i; int iBest = 0; int iBestDist = 9999; u64 inUseMask = 0; for(pRow=p->pFirst; pRow && pRow->idx<top; pRow=pRow->pNext){} while( pRow && pRow->idx<=btm ){ inUseMask |= pRow->railInUse; pRow = pRow->pNext; } /* First look for a match that honors bMergeRail */ for(i=0; i<=p->mxRail; i++){ u64 m = BIT(i); int dist; if( inUseMask & m ) continue; if( (bMergeRail!=0) != ((p->mergeRail & m)!=0) ) continue; if( iNearto<=0 ){ iBest = i; iBestDist = 1; break; } dist = i - iNearto; if( dist<0 ) dist = -dist; if( dist<iBestDist ){ iBestDist = dist; iBest = i; } } /* If no match, consider all possible rails */ if( iBestDist>1000 ){ for(i=0; i<=p->mxRail+1; i++){ int dist; if( inUseMask & BIT(i) ) continue; if( iNearto<=0 ){ iBest = i; iBestDist = 1; break; } dist = i - iNearto; if( dist<0 ) dist = -dist; if( dist<iBestDist ){ iBestDist = dist; iBest = i; } } } if( iBestDist>1000 ) p->nErr++; if( iBest>p->mxRail ) p->mxRail = iBest; if( bMergeRail ) p->mergeRail |= BIT(iBest); return iBest; } /* ** Assign all children of node pBottom to the same rail as pBottom. */ static void assignChildrenToRail(GraphRow *pBottom, u32 tmFlags){ |
︙ | ︙ | |||
367 368 369 370 371 372 373 | pParent->mergeOut = pParent->iRail; }else if( pParent->idx - pChild->idx < pParent->selfUp ){ pParent->mergeOut = pParent->iRail; }else{ /* The thin merge arrow riser is taller than the thick primary ** child riser, so use separate rails. */ int iTarget = pParent->iRail; | | > | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | pParent->mergeOut = pParent->iRail; }else if( pParent->idx - pChild->idx < pParent->selfUp ){ pParent->mergeOut = pParent->iRail; }else{ /* The thin merge arrow riser is taller than the thick primary ** child riser, so use separate rails. */ int iTarget = pParent->iRail; pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, iTarget, 1); mask = BIT(pParent->mergeOut); for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; } } } |
︙ | ︙ | |||
644 645 646 647 648 649 650 | for(i=0; i<2; i++){ for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ if( i==0 && pRow->zBranch!=zTrunk ) continue; if( pRow->iRail>=0 ) continue; if( pRow->isDup ) continue; if( pRow->nParent<0 ) continue; if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ | | | 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 | for(i=0; i<2; i++){ for(pRow=p->pLast; pRow; pRow=pRow->pPrev){ if( i==0 && pRow->zBranch!=zTrunk ) continue; if( pRow->iRail>=0 ) continue; if( pRow->isDup ) continue; if( pRow->nParent<0 ) continue; if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0); if( p->mxRail>=GR_MAX_RAIL ) return; mask = BIT(pRow->iRail); if( !omitDescenders ){ int n = RISER_MARGIN; pRow->bDescender = pRow->nParent>0; for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){ pLoop->railInUse |= mask; |
︙ | ︙ | |||
688 689 690 691 692 693 694 | pRow->railInUse = BIT(pRow->iRail); continue; } if( pParent->idx>pRow->idx ){ /* Common case: Child occurs after parent and is above the ** parent in the timeline */ pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, | | | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 | pRow->railInUse = BIT(pRow->iRail); continue; } if( pParent->idx>pRow->idx ){ /* Common case: Child occurs after parent and is above the ** parent in the timeline */ pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, pParent->iRail, 0); if( p->mxRail>=GR_MAX_RAIL ) return; pParent->aiRiser[pRow->iRail] = pRow->idx; }else{ /* Timewarp case: Child occurs earlier in time than parent and ** appears below the parent in the timeline. */ int iDownRail = ++p->mxRail; if( iDownRail<1 ) iDownRail = ++p->mxRail; |
︙ | ︙ | |||
739 740 741 742 743 744 745 | int iReuseIdx = -1; int iReuseRail = -1; int isCherrypick = 0; for(i=1; i<pRow->nParent; i++){ GraphRowId parentRid = pRow->aParent[i]; if( i==pRow->nNonCherrypick ){ /* Because full merges are laid out before cherrypicks, | | | | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 | int iReuseIdx = -1; int iReuseRail = -1; int isCherrypick = 0; for(i=1; i<pRow->nParent; i++){ GraphRowId parentRid = pRow->aParent[i]; if( i==pRow->nNonCherrypick ){ /* Because full merges are laid out before cherrypicks, ** it is ok to use a full-merge raiser for a cherrypick. ** See the graph on check-in 8ac66ef33b464d28 for example ** iReuseIdx = -1; ** iReuseRail = -1; */ isCherrypick = 1; } pDesc = hashFind(p, parentRid); if( pDesc==0 ){ int iMrail = -1; /* Merge from a node that is off-screen */ if( iReuseIdx>=p->nRow+1 ){ continue; /* Suppress multiple off-screen merges */ } for(j=0; j<GR_MAX_RAIL; j++){ if( mergeRiserFrom[j]==parentRid ){ iMrail = j; break; } } if( iMrail==-1 ){ iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1); if( p->mxRail>=GR_MAX_RAIL ) return; mergeRiserFrom[iMrail] = parentRid; } iReuseIdx = p->nRow+1; iReuseRail = iMrail; mask = BIT(iMrail); if( i>=pRow->nNonCherrypick ){ |
︙ | ︙ | |||
845 846 847 848 849 850 851 | ** Compute the rail mapping that tries to put the branch named ** zLeftBranch at the left margin. ** ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail. */ aMap = p->aiRailMap; for(i=0; i<=p->mxRail; i++) aMap[i] = i; | > > > | | > > > | > > > > > > | > > > > > > > > > > < < < | | > | < < < | | > > > > > > | > > | > > < | < < < | | > | | | 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 | ** Compute the rail mapping that tries to put the branch named ** zLeftBranch at the left margin. ** ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail. */ aMap = p->aiRailMap; for(i=0; i<=p->mxRail; i++) aMap[i] = i; if( nTimewarp==0 ){ u8 aPriority[GR_MAX_RAIL]; memset(aPriority, 0, p->mxRail+1); if( zLeftBranch ){ char *zLeft = persistBranchName(p, zLeftBranch); for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->zBranch==zLeft ){ aPriority[pRow->iRail] |= 4; for(i=0; i<=p->mxRail; i++){ if( pRow->mergeIn[i] ) aPriority[i] |= 1; } if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; } } }else{ j = 1; aPriority[0] = 4; for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->iRail==0 ){ for(i=0; i<=p->mxRail; i++){ if( pRow->mergeIn[i] ) aPriority[i] |= 1; } if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; } } } j = 0; for(i=0; i<=p->mxRail; i++){ if( p->mergeRail & BIT(i) ){ aPriority[i] |= 2; } } #if 0 fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail); fprintf(stderr,"Priority:"); for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]); fprintf(stderr,"\n"); #endif for(i=0; i<=p->mxRail; i++){ if( aPriority[i]>=4 ) aMap[i] = j++; } for(i=p->mxRail; i>=0; i--){ if( aPriority[i]==3 ) aMap[i] = j++; } for(i=0; i<=p->mxRail; i++){ if( aPriority[i]==1 || aPriority[i]==2 ) aMap[i] = j++; } for(i=0; i<=p->mxRail; i++){ if( aPriority[i]==0 ) aMap[i] = j++; } cgi_printf("<!-- aiRailMap ="); for(i=0; i<=p->mxRail; i++) cgi_printf(" %d", aMap[i]); cgi_printf(" -->\n"); } p->nErr = 0; } |