private void BuildOrderedRowCache(int currentRowIndex, int lastToCacheRowIndex) { if (_orderedRowCache == null || _rebuild) { _orderedRowCache = new List <RevisionGraphRow>(currentRowIndex); _rebuild = false; } int nextIndex = _orderedRowCache.Count; if (nextIndex > lastToCacheRowIndex) { return; } int cacheCount = _orderedNodesCache.Count; while (nextIndex <= lastToCacheRowIndex && cacheCount > nextIndex) { bool startSegmentsAdded = false; RevisionGraphRevision revision = _orderedNodesCache[nextIndex]; // The list containing the segments is created later. We can set the correct capacity then, to prevent resizing List <RevisionGraphSegment> segments; if (nextIndex > 0) { // Copy lanes from last row RevisionGraphRow previousRevisionGraphRow = _orderedRowCache[nextIndex - 1]; // Create segments list with te correct capacity segments = new List <RevisionGraphSegment>(previousRevisionGraphRow.Segments.Count + revision.StartSegments.Count); // Loop through all segments that do not end in this row foreach (var segment in previousRevisionGraphRow.Segments.Where(s => s.Parent != previousRevisionGraphRow.Revision)) { segments.Add(segment); // This segments continues in the next row. Copy all other segments that start from this revision to this lane. if (revision == segment.Parent && !startSegmentsAdded) { startSegmentsAdded = true; segments.AddRange(revision.StartSegments); } } } else { // Create segments list with te correct capacity segments = new List <RevisionGraphSegment>(revision.StartSegments.Count); } if (!startSegmentsAdded) { // Add new segments started by this revision to the end segments.AddRange(revision.StartSegments); } _orderedRowCache.Add(new RevisionGraphRow(revision, segments)); _buildUntilScore = revision.Score; nextIndex++; } Updated?.Invoke(); }
// Build the revision graph. There are two caches that are build in this method. // cache 1: an ordered list of the revisions. This is very cheap to build. (_orderedNodesCache) // cache 2: an ordered list of all prepared graphrows. This is expensive to build. (_orderedRowCache) // untillRow: the row that needs to be displayed. This ensures the orded revisions are available untill this index. // graphUntillRow: the graph can be build per x rows. This defines the row index that the graph will be cached until. public void CacheTo(int untillRow, int graphUntillRow) { if (_orderedNodesCache == null || _reorder || _orderedNodesCache.Count < untillRow) { _orderedNodesCache = _nodes.OrderBy(n => n.Score).ToList(); if (_orderedNodesCache.Count > 0) { _cachedUntillScore = _orderedNodesCache.Last().Score; } if (_reorder || _orderedRowCache == null) { _orderedRowCache = new List <RevisionGraphRow>(untillRow); } _reorder = false; Updated?.Invoke(); } int nextIndex = _orderedRowCache.Count; if (nextIndex <= graphUntillRow) { int cacheCount = _orderedNodesCache.Count; while (nextIndex <= graphUntillRow && cacheCount > nextIndex) { bool startSegmentsAdded = false; RevisionGraphRevision revision = _orderedNodesCache[nextIndex]; // The list containing the segments is created later. We can set the correct capacity then, to prevent resizing List <RevisionGraphSegment> segments; if (nextIndex > 0) { // Copy lanes from last row RevisionGraphRow previousRevisionGraphRow = _orderedRowCache[nextIndex - 1]; // Create segments list with te correct capacity segments = new List <RevisionGraphSegment>(previousRevisionGraphRow.Segments.Count + revision.StartSegments.Count); // Loop through all segments that do not end in this row foreach (var segment in previousRevisionGraphRow.Segments.Where(s => s.Parent != previousRevisionGraphRow.Revision)) { segments.Add(segment); // This segments continues in the next row. Copy all other segments that start from this revision to this lane. if (revision == segment.Parent && !startSegmentsAdded) { startSegmentsAdded = true; segments.AddRange(revision.StartSegments); } } } else { // Create segments list with te correct capacity segments = new List <RevisionGraphSegment>(revision.StartSegments.Count); } if (!startSegmentsAdded) { // Add new segments started by this revision to the end segments.AddRange(revision.StartSegments); } _orderedRowCache.Add(new RevisionGraphRow(_orderedNodesCache[nextIndex], segments)); nextIndex++; } Updated?.Invoke(); } }
private void BuildOrderedRowCache(IList <RevisionGraphRevision> orderedNodesCache, int currentRowIndex, int lastToCacheRowIndex) { // Ensure we keep using the same instance of the rowcache from here on var localOrderedRowCache = _orderedRowCache; if (localOrderedRowCache is null || CheckRowCacheIsDirty(localOrderedRowCache, orderedNodesCache)) { localOrderedRowCache = new List <RevisionGraphRow>(currentRowIndex); } int nextIndex = localOrderedRowCache.Count; if (nextIndex > lastToCacheRowIndex) { return; } int cacheCount = orderedNodesCache.Count; while (nextIndex <= lastToCacheRowIndex && cacheCount > nextIndex) { bool startSegmentsAdded = false; RevisionGraphRevision revision = orderedNodesCache[nextIndex]; // The list containing the segments is created later. We can set the correct capacity then, to prevent resizing List <RevisionGraphSegment> segments; if (nextIndex == 0) { // This is the first row. Start with only the startsegments of this row segments = new List <RevisionGraphSegment>(revision.StartSegments); } else { // Copy lanes from last row RevisionGraphRow previousRevisionGraphRow = localOrderedRowCache[nextIndex - 1]; // Create segments list with te correct capacity segments = new List <RevisionGraphSegment>(previousRevisionGraphRow.Segments.Count + revision.StartSegments.Count); // Loop through all segments that do not end in the previous row foreach (var segment in previousRevisionGraphRow.Segments.Where(s => s.Parent != previousRevisionGraphRow.Revision)) { segments.Add(segment); // This segment that is copied from the previous row, connects to the node in this row. // Copy all new segments that start from this node (revision) to this lane. if (revision == segment.Parent && !startSegmentsAdded) { startSegmentsAdded = true; segments.AddRange(revision.StartSegments); } } // The startsegments do not connect to any previous row. This means that this is a new branch. if (!startSegmentsAdded) { // Add new segments started by this revision to the end segments.AddRange(revision.StartSegments); } } localOrderedRowCache.Add(new RevisionGraphRow(revision, segments)); nextIndex++; } // Overwrite the global instance at the end, to prevent flickering _orderedRowCache = localOrderedRowCache; Updated?.Invoke(); }
private void BuildOrderedRowCache(RevisionGraphRevision[] orderedNodesCache, int currentRowIndex, int lastToCacheRowIndex) { // Ensure we keep using the same instance of the rowcache from here on IList <RevisionGraphRow>?localOrderedRowCache = _orderedRowCache; if (localOrderedRowCache is null || CheckRowCacheIsDirty(localOrderedRowCache, orderedNodesCache)) { localOrderedRowCache = new List <RevisionGraphRow>(currentRowIndex); } lastToCacheRowIndex = Math.Min(lastToCacheRowIndex, orderedNodesCache.Length - 1); int startIndex = localOrderedRowCache.Count; if (startIndex > lastToCacheRowIndex) { return; } for (int nextIndex = startIndex; nextIndex <= lastToCacheRowIndex; ++nextIndex) { bool startSegmentsAdded = false; RevisionGraphRevision revision = orderedNodesCache[nextIndex]; // The list containing the segments is created later. We can set the correct capacity then, to prevent resizing List <RevisionGraphSegment> segments; if (nextIndex == 0) { // This is the first row. Start with only the startsegments of this row segments = new List <RevisionGraphSegment>(revision.StartSegments); foreach (var startSegment in revision.StartSegments) { startSegment.LaneInfo = new LaneInfo(startSegment, derivedFrom: null); } } else { // Copy lanes from last row RevisionGraphRow previousRevisionGraphRow = localOrderedRowCache[nextIndex - 1]; // Create segments list with te correct capacity segments = new List <RevisionGraphSegment>(previousRevisionGraphRow.Segments.Count + revision.StartSegments.Count); // Loop through all segments that do not end in the previous row foreach (var segment in previousRevisionGraphRow.Segments.Where(s => s.Parent != previousRevisionGraphRow.Revision)) { segments.Add(segment); // This segment that is copied from the previous row, connects to the node in this row. // Copy all new segments that start from this node (revision) to this lane. if (revision == segment.Parent) { if (!startSegmentsAdded) { startSegmentsAdded = true; segments.AddRange(revision.StartSegments); } foreach (var startSegment in revision.StartSegments) { if (startSegment == revision.StartSegments.First()) { if (startSegment.LaneInfo is null || startSegment.LaneInfo.StartScore > segment.LaneInfo?.StartScore) { startSegment.LaneInfo = segment.LaneInfo; } } else { if (startSegment.LaneInfo is null) { startSegment.LaneInfo = new LaneInfo(startSegment, derivedFrom: segment.LaneInfo); } } } } } // The startsegments do not connect to any previous row. This means that this is a new branch. if (!startSegmentsAdded) { // Add new segments started by this revision to the end segments.AddRange(revision.StartSegments); foreach (var startSegment in revision.StartSegments) { startSegment.LaneInfo = new LaneInfo(startSegment, derivedFrom: null); } } } localOrderedRowCache.Add(new RevisionGraphRow(revision, segments)); } StraightenLanes(startIndex - _straightenLanesLookAhead, lastToCacheRowIndex, localOrderedRowCache); // Overwrite the global instance at the end, to prevent flickering _orderedRowCache = localOrderedRowCache; Updated?.Invoke(); return;