/// <summary> /// Sort and merge the crossing list /// </summary> /// <param name="crossingList">The crossing list to sort/merge</param> private static void SortAndMerge(ref List <LassoCrossing> crossingList) { // Sort the crossings based on the BeginFIndex values crossingList.Sort(); List <LassoCrossing> mergedList = new List <LassoCrossing>(); LassoCrossing mcrossing = LassoCrossing.EmptyCrossing; foreach (LassoCrossing crossing in crossingList) { System.Diagnostics.Debug.Assert(!crossing.IsEmpty && crossing.StartNode.IsValid && crossing.EndNode.IsValid); if (!mcrossing.Merge(crossing)) { System.Diagnostics.Debug.Assert(!mcrossing.IsEmpty && mcrossing.StartNode.IsValid && mcrossing.EndNode.IsValid); mergedList.Add(mcrossing); mcrossing = crossing; } } if (!mcrossing.IsEmpty) { System.Diagnostics.Debug.Assert(!mcrossing.IsEmpty && mcrossing.StartNode.IsValid && mcrossing.EndNode.IsValid); mergedList.Add(mcrossing); } crossingList = mergedList; }
internal StrokeIntersection[] HitTest(StrokeNodeIterator iterator) { System.Diagnostics.Debug.Assert(_points != null); System.Diagnostics.Debug.Assert(iterator != null); if (_points.Count < 3) { // // it takes at least 3 points to create a lasso // return(Array.Empty <StrokeIntersection>()); } // // We're about to perform hit testing with a lasso. // To do so we need to iterate through each StrokeNode. // As we do, we calculate the bounding rect between it // and the previous StrokeNode and store this in 'currentStrokeSegmentBounds' // // Next, we check to see if that StrokeNode pair's bounding box intersects // with the bounding box of the Lasso points. If not, we continue iterating through // StrokeNode pairs. // // If it does, we do a more granular hit test by pairing points in the Lasso, getting // their bounding box and seeing if that bounding box intersects our current StrokeNode // pair // Point lastNodePosition = new Point(); Point lassoLastPoint = _points[_points.Count - 1]; Rect currentStrokeSegmentBounds = Rect.Empty; // Initilize the current crossing to be an empty one LassoCrossing currentCrossing = LassoCrossing.EmptyCrossing; // Creat a list to hold all the crossings List <LassoCrossing> crossingList = new List <LassoCrossing>(); for (int i = 0; i < iterator.Count; i++) { StrokeNode strokeNode = iterator[i]; Rect nodeBounds = strokeNode.GetBounds(); currentStrokeSegmentBounds.Union(nodeBounds); // Skip the node if it's outside of the lasso's bounds if (currentStrokeSegmentBounds.IntersectsWith(_bounds) == true) { // currentStrokeSegmentBounds, made up of the bounding box of // this StrokeNode unioned with the last StrokeNode, // intersects the lasso bounding box. // // Now we need to iterate through the lasso points and find out where they cross // Point lastPoint = lassoLastPoint; foreach (Point point in _points) { // // calculate a segment of the lasso from the last point // to the current point // Rect lassoSegmentBounds = new Rect(lastPoint, point); // // see if this lasso segment intersects with the current stroke segment // if (!currentStrokeSegmentBounds.IntersectsWith(lassoSegmentBounds)) { lastPoint = point; continue; } // // the lasso segment DOES intersect with the current stroke segment // find out precisely where // StrokeFIndices strokeFIndices = strokeNode.CutTest(lastPoint, point); lastPoint = point; if (strokeFIndices.IsEmpty) { // current lasso segment does not hit the stroke segment, continue with the next lasso point continue; } // Create a potentially new crossing for the current hit testing result. LassoCrossing potentialNewCrossing = new LassoCrossing(strokeFIndices, strokeNode); // Try to merge with the current crossing. If the merge is succussful (return true), the new crossing is actually // continueing the current crossing, so do not start a new crossing. Otherwise, start a new one and add the existing // one to the list. if (!currentCrossing.Merge(potentialNewCrossing)) { // start a new crossing and add the existing on to the list crossingList.Add(currentCrossing); currentCrossing = potentialNewCrossing; } } } // Continue with the next node currentStrokeSegmentBounds = nodeBounds; lastNodePosition = strokeNode.Position; } // Adding the last crossing to the list, if valid if (!currentCrossing.IsEmpty) { crossingList.Add(currentCrossing); } // Handle the special case of no intersection at all if (crossingList.Count == 0) { // the stroke was either completely inside the lasso // or outside the lasso if (this.Contains(lastNodePosition)) { StrokeIntersection[] strokeIntersections = new StrokeIntersection[1]; strokeIntersections[0] = StrokeIntersection.Full; return(strokeIntersections); } else { return(Array.Empty <StrokeIntersection>()); } } // It is still possible that the current crossing list is not sorted or overlapping. // Sort the list and merge the overlapping ones. SortAndMerge(ref crossingList); // Produce the hit test results and store them in a list List <StrokeIntersection> strokeIntersectionList = new List <StrokeIntersection>(); ProduceHitTestResults(crossingList, strokeIntersectionList); return(strokeIntersectionList.ToArray()); }