// Returns true if we know at this point whether an intersection is possible between tic1 and tic2 // The fact of whether an intersection was found is stored in the ref parameter intersectionFound static private bool IntersectsHelperEqualCase(ref TimeIntervalCollection tic1, ref TimeIntervalCollection tic2, ref bool intersectionFound) { // If the nodes match exactly, check if the points are both included, or if the intervals are both included if ((tic1.CurrentNodeIsPoint && tic2.CurrentNodeIsPoint) || (tic1.CurrentNodeIsInterval && tic2.CurrentNodeIsInterval)) { intersectionFound = true; return true; } // We did not find an intersection, but advance whichever index has a closer next node else if (!tic1.CurrentIsAtLastNode && ( tic2.CurrentIsAtLastNode || (tic1.NextNodeTime < tic2.NextNodeTime))) { tic1.MoveNext(); } else if (!tic2.CurrentIsAtLastNode && ( tic1.CurrentIsAtLastNode || (tic2.NextNodeTime < tic1.NextNodeTime))) { tic2.MoveNext(); } else if (!tic1.CurrentIsAtLastNode && !tic2.CurrentIsAtLastNode) { // If both indices have room to advance, and we haven't yet advanced either one, it must be the next nodes are also exactly equal Debug.Assert(tic1.NextNodeTime == tic2.NextNodeTime); // It is necessary to advance both indices simultaneously, otherwise we break our invariant - one will be too far ahead tic1.MoveNext(); tic2.MoveNext(); } else // The only way we could get here is if both indices are pointing to the last nodes { Debug.Assert(tic1.CurrentIsAtLastNode && tic2.CurrentIsAtLastNode); // We have exhausted all the nodes and not found an intersection; bail intersectionFound = false; return true; } // Enforce our invariant: neither index gets too far ahead of the other. Debug.Assert(tic2.CurrentIsAtLastNode || (tic1.CurrentNodeTime < tic2.NextNodeTime)); Debug.Assert(tic1.CurrentIsAtLastNode || (tic2.CurrentNodeTime < tic1.NextNodeTime)); // Tell the main algorithm to continue working return false; }
// Make sure the indexers are starting next to each other static private void IntersectsHelperPrepareIndexers(ref TimeIntervalCollection tic1, ref TimeIntervalCollection tic2) { Debug.Assert(!tic1.IsEmptyOfRealPoints); // We shouldn't reach here if either TIC is empty Debug.Assert(!tic2.IsEmptyOfRealPoints); tic1.MoveFirst(); // Point _current to the first node in both TICs tic2.MoveFirst(); // First bring tic1._current and tic2._current within an interval of each other if (tic1.CurrentNodeTime < tic2.CurrentNodeTime) { // Keep advancing tic1._current as far as possible while keeping _nodeTime[tic1._current] < _nodeTime[tic2._current] while (!tic1.CurrentIsAtLastNode && (tic1.NextNodeTime <= tic2.CurrentNodeTime)) { tic1.MoveNext(); } } else if (tic2.CurrentNodeTime < tic1.CurrentNodeTime) { // Keep advancing tic2._current as far as possible while keeping _nodeTime[tic1._current] > _nodeTime[tic2._current] while (!tic2.CurrentIsAtLastNode && (tic2.NextNodeTime <= tic1.CurrentNodeTime)) { tic2.MoveNext(); } } }
// Returns true if we know at this point whether an intersection is possible between tic1 and tic2 // The fact of whether an intersection was found is stored in the ref parameter intersectionFound static private bool IntersectsHelperUnequalCase(ref TimeIntervalCollection tic1, ref TimeIntervalCollection tic2, ref bool intersectionFound) { Debug.Assert(!intersectionFound); // If an intersection was already found, we should not reach this far if (tic1.CurrentNodeIsInterval) // If we are within an interval in tic1, we immediately have an intersection { // If we have gotten into this method, tic1._current comes earlier than does tic2._current; // Suppose the following assert is false; then by Rule #2A, tic2's previous interval must be included; // If this was the case, then tic2's previous interval overlapped tic1's current interval. Since it's // included, we would have encountered an intersection before even reaching this method! Then you // should not even be here now. Else suppose we are at tic2's first node, then the below Assert // follows directly from Rule #3. Debug.Assert(tic2.CurrentNodeIsPoint || tic2.CurrentNodeIsInterval); intersectionFound = true; return true; } else if (tic1.CurrentIsAtLastNode) // // If we are already at the end of tic1, we ran out of nodes that may have an intersection { intersectionFound = false; return true; } else // Else we are inside a non-included interval in tic1, no intersection is possible, but keep advancing tic2._current { while (!tic2.CurrentIsAtLastNode && (tic2.NextNodeTime <= tic1.NextNodeTime)) { tic2.MoveNext(); } // If nextNodeTime1 is null, we should never get here because the IF statement would have caught it and quit Debug.Assert(!tic1.CurrentIsAtLastNode); // Thus tic1._current can be safely advanced now // Now tic1._current can be safely advanced forward tic1.MoveNext(); // If we broke out of Case I, its conditional should no longer hold true: Debug.Assert(tic1.CurrentNodeTime >= tic2.CurrentNodeTime); // Enforce our invariant: neither index gets too far ahead of the other. Debug.Assert(tic2.CurrentIsAtLastNode || (tic1.CurrentNodeTime < tic2.NextNodeTime)); Debug.Assert(tic1.CurrentIsAtLastNode || (tic2.CurrentNodeTime < tic1.NextNodeTime)); // Tell the main algorithm to continue working return false; } }
/// <summary> /// Performs the NORMALIZE operation, as described in the comments to the general projection function. /// Clip begin and end times, normalize by beginTime, scale by speedRatio. /// </summary> /// <param name="projection">The normalized collection to create.</param> /// <param name="beginTime">Begin time of the active period for clipping.</param> /// <param name="endTime">End time of the active period for clipping.</param> /// <param name="speedRatio">The ratio by which to scale begin and end time.</param> /// <param name="includeFillPeriod">Whether a non-zero fill period exists.</param> private void ProjectionNormalize(ref TimeIntervalCollection projection, TimeSpan beginTime, Nullable<TimeSpan> endTime, bool includeFillPeriod, double speedRatio) { Debug.Assert(!IsEmptyOfRealPoints); Debug.Assert(projection.IsEmpty); projection.EnsureAllocatedCapacity(this._nodeTime.Length); this.MoveFirst(); projection.MoveFirst(); // Get to the non-clipped zone; we must overlap the active zone, so we should terminate at some point. while (!CurrentIsAtLastNode && NextNodeTime <= beginTime) { MoveNext(); } if (CurrentNodeTime < beginTime) // This means we have an interval clipped by beginTime { if (CurrentNodeIsInterval) { projection._count++; projection.CurrentNodeTime = TimeSpan.Zero; projection.CurrentNodeIsPoint = true; projection.CurrentNodeIsInterval = true; projection.MoveNext(); } this.MoveNext(); } while(_current < _count && (!endTime.HasValue || CurrentNodeTime < endTime)) // Copy the main set of segments, transforming them { double timeOffset = (double)((this.CurrentNodeTime - beginTime).Ticks); projection._count++; projection.CurrentNodeTime = TimeSpan.FromTicks((long)(speedRatio * timeOffset)); projection.CurrentNodeIsPoint = this.CurrentNodeIsPoint; projection.CurrentNodeIsInterval = this.CurrentNodeIsInterval; projection.MoveNext(); this.MoveNext(); } Debug.Assert(_current > 0); // The only way _current could stay at zero is if the collection begins at (or past) the end of active period if (_current < _count // We have an interval reaching beyond the active zone, clip that interval && (_nodeIsInterval[_current - 1] || (CurrentNodeTime == endTime.Value && CurrentNodeIsPoint && includeFillPeriod))) { Debug.Assert(endTime.HasValue && CurrentNodeTime >= endTime.Value); double timeOffset = (double)((endTime.Value - beginTime).Ticks); projection._count++; projection.CurrentNodeTime = TimeSpan.FromTicks((long)(speedRatio * timeOffset)); projection.CurrentNodeIsPoint = includeFillPeriod && (CurrentNodeTime > endTime.Value || CurrentNodeIsPoint); projection.CurrentNodeIsInterval = false; } }