Beispiel #1
0
            /// <summary>
            /// Merge two crossings into one.
            /// </summary>
            /// <param name="crossing"></param>
            /// <returns>Return true if these two crossings are actually overlapping and merged; false otherwise</returns>
            public bool Merge(LassoCrossing crossing)
            {
                if (crossing.IsEmpty)
                {
                    return(false);
                }

                if (FIndices.IsEmpty && !crossing.IsEmpty)
                {
                    FIndices  = crossing.FIndices;
                    StartNode = crossing.StartNode;
                    EndNode   = crossing.EndNode;
                    return(true);
                }

                if (DoubleUtil.GreaterThanOrClose(crossing.FIndices.EndFIndex, FIndices.BeginFIndex) &&
                    DoubleUtil.GreaterThanOrClose(FIndices.EndFIndex, crossing.FIndices.BeginFIndex))
                {
                    if (DoubleUtil.LessThan(crossing.FIndices.BeginFIndex, FIndices.BeginFIndex))
                    {
                        FIndices.BeginFIndex = crossing.FIndices.BeginFIndex;
                        StartNode            = crossing.StartNode;
                    }

                    if (DoubleUtil.GreaterThan(crossing.FIndices.EndFIndex, FIndices.EndFIndex))
                    {
                        FIndices.EndFIndex = crossing.FIndices.EndFIndex;
                        EndNode            = crossing.EndNode;
                    }
                    return(true);
                }

                return(false);
            }
Beispiel #2
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="newFIndices"></param>
 /// <param name="strokeNode"></param>
 public LassoCrossing(StrokeFIndices newFIndices, StrokeNode strokeNode)
 {
     System.Diagnostics.Debug.Assert(!newFIndices.IsEmpty);
     System.Diagnostics.Debug.Assert(strokeNode.IsValid);
     FIndices  = newFIndices;
     StartNode = EndNode = strokeNode;
 }
        /// <summary>
        /// Finds out if a given node intersects with this one,
        /// and returns findices of the intersection.
        /// </summary>
        /// <param name="hitNode"></param>
        /// <returns></returns>
        internal StrokeFIndices CutTest(StrokeNode hitNode)
        {
            if ((IsValid == false) || (hitNode.IsValid == false))
            {
                return(StrokeFIndices.Empty);
            }

            IEnumerable <ContourSegment> hittingContour = hitNode.GetContourSegments();

            // If the node contours intersect, the result is a pair of findices
            // this segment should be cut at to let the hitNode's contour through it.
            StrokeFIndices cutAt = _operations.CutTest(_lastNode, _thisNode, ConnectingQuad, hittingContour);

            return((_index == 0) ? cutAt : BindFIndices(cutAt));
        }
        /// <summary>
        /// Finds out if a given linear segment intersects with the contour of this node
        /// (including connecting quadrangle), and returns findices of the intersection.
        /// </summary>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        internal StrokeFIndices CutTest(Point begin, Point end)
        {
            if (IsValid == false)
            {
                return(StrokeFIndices.Empty);
            }

            // If the node contours intersect, the result is a pair of findices
            // this segment should be cut at to let the hitNode's contour through it.
            StrokeFIndices cutAt = _operations.CutTest(_lastNode, _thisNode, ConnectingQuad, begin, end);

            System.Diagnostics.Debug.Assert(!double.IsNaN(cutAt.BeginFIndex) && !double.IsNaN(cutAt.EndFIndex));

            // Bind the found findices to the node and return the result
            return(BindFIndicesForLassoHitTest(cutAt));
        }
        /// <summary>
        /// Binds a local fragment to this node by setting the integer part of the
        /// fragment findices equal to the index of the previous node
        /// </summary>
        /// <param name="fragment"></param>
        /// <returns></returns>
        private StrokeFIndices BindFIndices(StrokeFIndices fragment)
        {
            System.Diagnostics.Debug.Assert(IsValid && (_index >= 0));

            if (fragment.IsEmpty == false)
            {
                // Adjust only findices which are on this segment of thew spine (i.e. between 0 and 1)
                if (!DoubleUtil.AreClose(fragment.BeginFIndex, StrokeFIndices.BeforeFirst))
                {
                    System.Diagnostics.Debug.Assert(fragment.BeginFIndex >= 0 && fragment.BeginFIndex <= 1);
                    fragment.BeginFIndex += _index - 1;
                }
                if (!DoubleUtil.AreClose(fragment.EndFIndex, StrokeFIndices.AfterLast))
                {
                    System.Diagnostics.Debug.Assert(fragment.EndFIndex >= 0 && fragment.EndFIndex <= 1);
                    fragment.EndFIndex += _index - 1;
                }
            }
            return(fragment);
        }
        /// <summary>
        /// Bind the StrokeFIndices for lasso hit test results.
        /// </summary>
        /// <param name="fragment"></param>
        /// <returns></returns>
        private StrokeFIndices BindFIndicesForLassoHitTest(StrokeFIndices fragment)
        {
            System.Diagnostics.Debug.Assert(IsValid);
            if (!fragment.IsEmpty)
            {
                // Adjust BeginFIndex
                if (DoubleUtil.AreClose(fragment.BeginFIndex, StrokeFIndices.BeforeFirst))
                {
                    // set it to be the index of the previous node, indicating intersection start from previous node
                    fragment.BeginFIndex = (_index == 0 ? StrokeFIndices.BeforeFirst:_index - 1);
                }
                else
                {
                    // Adjust findices which are on this segment of the spine (i.e. between 0 and 1)
                    System.Diagnostics.Debug.Assert(DoubleUtil.GreaterThanOrClose(fragment.BeginFIndex, 0f));

                    System.Diagnostics.Debug.Assert(DoubleUtil.LessThanOrClose(fragment.BeginFIndex, 1f));

                    // Adjust the value to consider index, say from 0.75 to 3.75 (for _index = 4)
                    fragment.BeginFIndex += _index - 1;
                }

                //Adjust EndFIndex
                if (DoubleUtil.AreClose(fragment.EndFIndex, StrokeFIndices.AfterLast))
                {
                    // set it to be the index of the current node, indicating the intersection cover the end of the node
                    fragment.EndFIndex = (_isLastNode ? StrokeFIndices.AfterLast:_index);
                }
                else
                {
                    System.Diagnostics.Debug.Assert(DoubleUtil.GreaterThanOrClose(fragment.EndFIndex, 0f));

                    System.Diagnostics.Debug.Assert(DoubleUtil.LessThanOrClose(fragment.EndFIndex, 1f));
                    // Ajust the value to consider the index
                    fragment.EndFIndex += _index - 1;
                }
            }
            return(fragment);
        }
Beispiel #7
0
        /// <summary>
        /// Helper method to calculate the exact location to cut
        /// </summary>
        /// <param name="spineVector">Vector the relative location of the two inking nodes</param>
        /// <param name="hitBegin">the begin point of the hitting segment</param>
        /// <param name="hitEnd">the end point of the hitting segment</param>
        /// <param name="endRadius">endNode radius</param>
        /// <param name="beginRadius">beginNode radius</param>
        /// <param name="result">StrokeFIndices representing the location for cutting</param>
        private void CalculateCutLocations(
            Vector spineVector, Vector hitBegin, Vector hitEnd, double endRadius, double beginRadius, ref StrokeFIndices result)
        {
            // Find out whether the {hitBegin, hitEnd} segment intersects with the contour
            // of the stroke segment, and find the lower index of the fragment to cut out.
            if (!DoubleUtil.AreClose(result.EndFIndex, StrokeFIndices.AfterLast))
            {
                if (WhereIsNodeAboutSegment(spineVector, hitBegin, hitEnd) == HitResult.Left)
                {
                    double findex = 1 - ClipTest(spineVector, endRadius, beginRadius, hitBegin, hitEnd);
                    if (findex > result.EndFIndex)
                    {
                        result.EndFIndex = findex;
                    }
                }
            }

            // Find out whether the {hitBegin, hitEnd} segment intersects with the contour
            // of the stroke segment, and find the higher index of the fragment to cut out.
            if (!DoubleUtil.AreClose(result.BeginFIndex, StrokeFIndices.BeforeFirst))
            {
                hitBegin -= spineVector;
                hitEnd   -= spineVector;
                if (WhereIsNodeAboutSegment(-spineVector, hitBegin, hitEnd) == HitResult.Left)
                {
                    double findex = ClipTest(-spineVector, beginRadius, endRadius, hitBegin, hitEnd);
                    if (findex < result.BeginFIndex)
                    {
                        result.BeginFIndex = findex;
                    }
                }
            }
        }
Beispiel #8
0
        /// <summary>
        /// CutTest an inking StrokeNode segment (two nodes and a connecting quadrangle) against a hitting contour
        /// (represented by an enumerator of Contoursegments).
        /// </summary>
        /// <param name="beginNode">The begin StrokeNodeData</param>
        /// <param name="endNode">The end StrokeNodeData</param>
        /// <param name="quad">Connecing quadrangle between the begin and end inking node</param>
        /// <param name="hitContour">The hitting ContourSegments</param>
        /// <returns>StrokeFIndices representing the location for cutting</returns>
        internal override StrokeFIndices CutTest(
            StrokeNodeData beginNode, StrokeNodeData endNode, Quad quad, IEnumerable <ContourSegment> hitContour)
        {
            // Compute the positions of the beginNode relative to the endNode.
            Vector spineVector = beginNode.IsEmpty ? new Vector(0, 0) : (beginNode.Position - endNode.Position);

            // If the node shape is an ellipse, transform the scene to turn the shape to a circle
            if (_nodeShapeToCircle.IsIdentity == false)
            {
                spineVector = _nodeShapeToCircle.Transform(spineVector);
            }

            double beginRadius = 0, endRadius;
            double beginRadiusSquared = 0, endRadiusSquared;

            endRadius        = _radius * endNode.PressureFactor;
            endRadiusSquared = endRadius * endRadius;
            if (beginNode.IsEmpty == false)
            {
                beginRadius        = _radius * beginNode.PressureFactor;
                beginRadiusSquared = beginRadius * beginRadius;
            }

            bool           isInside = true;
            StrokeFIndices result   = StrokeFIndices.Empty;

            foreach (ContourSegment hitSegment in hitContour)
            {
                if (hitSegment.IsArc)
                {
                    // ISSUE-2004/06/15-vsmirnov - ellipse vs arc hit-testing is not implemented
                    // and currently disabled in ErasingStroke
                }
                else
                {
                    Vector hitBegin = hitSegment.Begin - endNode.Position;
                    Vector hitEnd   = hitBegin + hitSegment.Vector;

                    // If the node shape is an ellipse, transform the scene to turn
                    // the shape into circle.
                    if (_nodeShapeToCircle.IsIdentity == false)
                    {
                        hitBegin = _nodeShapeToCircle.Transform(hitBegin);
                        hitEnd   = _nodeShapeToCircle.Transform(hitEnd);
                    }

                    bool isHit = false;

                    // Hit-test the end node
                    Vector nearest = GetNearest(hitBegin, hitEnd);
                    if (nearest.LengthSquared < endRadiusSquared)
                    {
                        isHit = true;
                        if (!DoubleUtil.AreClose(result.EndFIndex, StrokeFIndices.AfterLast))
                        {
                            result.EndFIndex = StrokeFIndices.AfterLast;
                            if (beginNode.IsEmpty)
                            {
                                result.BeginFIndex = StrokeFIndices.BeforeFirst;
                                break;
                            }
                            if (DoubleUtil.AreClose(result.BeginFIndex, StrokeFIndices.BeforeFirst))
                            {
                                break;
                            }
                        }
                    }

                    if ((beginNode.IsEmpty == false) && (!isHit || !DoubleUtil.AreClose(result.BeginFIndex, StrokeFIndices.BeforeFirst)))
                    {
                        // Hit-test the first node
                        nearest = GetNearest(hitBegin - spineVector, hitEnd - spineVector);
                        if (nearest.LengthSquared < beginRadiusSquared)
                        {
                            isHit = true;
                            if (!DoubleUtil.AreClose(result.BeginFIndex, StrokeFIndices.BeforeFirst))
                            {
                                result.BeginFIndex = StrokeFIndices.BeforeFirst;
                                if (DoubleUtil.AreClose(result.EndFIndex, StrokeFIndices.AfterLast))
                                {
                                    break;
                                }
                            }
                        }
                    }

                    // If both nodes are hit or nothing is hit at all, return.
                    if (beginNode.IsEmpty || (!isHit && (quad.IsEmpty ||
                                                         (HitTestQuadSegment(quad, hitSegment.Begin, hitSegment.End) == false))))
                    {
                        if (isInside && (WhereIsVectorAboutVector(
                                             endNode.Position - hitSegment.Begin, hitSegment.Vector) != HitResult.Right))
                        {
                            isInside = false;
                        }
                        continue;
                    }

                    isInside = false;

                    // NTRAID#Window OS bug-1029694-2004/10/18-xiaotu, refactor the code to make it a method
                    // to increase the maintainability of the program. FxCop bug.
                    // Calculate the exact locations to cut.
                    CalculateCutLocations(spineVector, hitBegin, hitEnd, endRadius, beginRadius, ref result);

                    if (result.IsFull)
                    {
                        break;
                    }
                }
            }

            //
            if (!result.IsFull)
            {
                if (isInside == true)
                {
                    System.Diagnostics.Debug.Assert(result.IsEmpty);
                    result = StrokeFIndices.Full;
                }
                else if ((DoubleUtil.AreClose(result.EndFIndex, StrokeFIndices.BeforeFirst)) && (!DoubleUtil.AreClose(result.BeginFIndex, StrokeFIndices.AfterLast)))
                {
                    result.EndFIndex = StrokeFIndices.AfterLast;
                }
                else if ((DoubleUtil.AreClose(result.BeginFIndex, StrokeFIndices.AfterLast)) && (!DoubleUtil.AreClose(result.EndFIndex, StrokeFIndices.BeforeFirst)))
                {
                    result.BeginFIndex = StrokeFIndices.BeforeFirst;
                }
            }

            if (IsInvalidCutTestResult(result))
            {
                return(StrokeFIndices.Empty);
            }

            return(result);
        }
Beispiel #9
0
        /// <summary>
        /// Cut-test ink segment defined by two nodes and a connecting quad against a linear segment
        /// </summary>
        /// <param name="beginNode">Begin node of the ink segment</param>
        /// <param name="endNode">End node of the ink segment</param>
        /// <param name="quad">Pre-computed quadrangle connecting the two ink nodes</param>
        /// <param name="hitBeginPoint">egin point of the hitting segment</param>
        /// <param name="hitEndPoint">End point of the hitting segment</param>
        /// <returns>Exact location to cut at represented by StrokeFIndices</returns>
        internal override StrokeFIndices CutTest(
            StrokeNodeData beginNode, StrokeNodeData endNode, Quad quad, Point hitBeginPoint, Point hitEndPoint)
        {
            // Compute the positions of the involved points relative to the endNode.
            Vector spineVector = beginNode.IsEmpty ? new Vector(0, 0) : (beginNode.Position - endNode.Position);
            Vector hitBegin    = hitBeginPoint - endNode.Position;
            Vector hitEnd      = hitEndPoint - endNode.Position;

            // If the node shape is an ellipse, transform the scene to turn the shape to a circle
            if (_nodeShapeToCircle.IsIdentity == false)
            {
                spineVector = _nodeShapeToCircle.Transform(spineVector);
                hitBegin    = _nodeShapeToCircle.Transform(hitBegin);
                hitEnd      = _nodeShapeToCircle.Transform(hitEnd);
            }

            StrokeFIndices result = StrokeFIndices.Empty;

            // Hit-test the end node
            double beginRadius = 0, endRadius = _radius * endNode.PressureFactor;
            Vector nearest = GetNearest(hitBegin, hitEnd);

            if (nearest.LengthSquared <= (endRadius * endRadius))
            {
                result.EndFIndex   = StrokeFIndices.AfterLast;
                result.BeginFIndex = beginNode.IsEmpty ? StrokeFIndices.BeforeFirst : 1;
            }
            if (beginNode.IsEmpty == false)
            {
                // Hit-test the first node
                beginRadius = _radius * beginNode.PressureFactor;
                nearest     = GetNearest(hitBegin - spineVector, hitEnd - spineVector);
                if (nearest.LengthSquared <= (beginRadius * beginRadius))
                {
                    result.BeginFIndex = StrokeFIndices.BeforeFirst;
                    if (!DoubleUtil.AreClose(result.EndFIndex, StrokeFIndices.AfterLast))
                    {
                        result.EndFIndex = 0;
                    }
                }
            }

            // If both nodes are hit or nothing is hit at all, return.
            if (result.IsFull || quad.IsEmpty ||
                (result.IsEmpty && (HitTestQuadSegment(quad, hitBeginPoint, hitEndPoint) == false)))
            {
                return(result);
            }

            // Find out whether the {begin, end} segment intersects with the contour
            // of the stroke segment {_lastNode, _thisNode}, and find the lower index
            // of the fragment to cut out.
            if (!DoubleUtil.AreClose(result.BeginFIndex, StrokeFIndices.BeforeFirst))
            {
                result.BeginFIndex = ClipTest(-spineVector, beginRadius, endRadius, hitBegin - spineVector, hitEnd - spineVector);
            }

            if (!DoubleUtil.AreClose(result.EndFIndex, StrokeFIndices.AfterLast))
            {
                result.EndFIndex = 1 - ClipTest(spineVector, endRadius, beginRadius, hitBegin, hitEnd);
            }

            if (IsInvalidCutTestResult(result))
            {
                return(StrokeFIndices.Empty);
            }

            return(result);
        }
Beispiel #10
0
        /// <summary>
        /// Hit-testing for point erase.
        /// </summary>
        /// <param name="iterator"></param>
        /// <param name="intersections"></param>
        /// <returns></returns>
        internal bool EraseTest(StrokeNodeIterator iterator, List <StrokeIntersection> intersections)
        {
            System.Diagnostics.Debug.Assert(iterator != null);
            System.Diagnostics.Debug.Assert(intersections != null);
            intersections.Clear();

            List <StrokeFIndices> eraseAt = new List <StrokeFIndices>();

            if ((_erasingStrokeNodes == null) || (_erasingStrokeNodes.Count == 0))
            {
                return(false);
            }

            Rect inkSegmentBounds = Rect.Empty;

            for (int x = 0; x < iterator.Count; x++)
            {
                StrokeNode inkStrokeNode = iterator[x];
                Rect       inkNodeBounds = inkStrokeNode.GetBounds();
                inkSegmentBounds.Union(inkNodeBounds);

                if (inkSegmentBounds.IntersectsWith(_bounds))
                {
                    //

                    int index = eraseAt.Count;
                    foreach (StrokeNode erasingStrokeNode in _erasingStrokeNodes)
                    {
                        if (false == inkSegmentBounds.IntersectsWith(erasingStrokeNode.GetBoundsConnected()))
                        {
                            continue;
                        }

                        StrokeFIndices fragment = inkStrokeNode.CutTest(erasingStrokeNode);
                        if (fragment.IsEmpty)
                        {
                            continue;
                        }

                        // Merge it with the other results for this ink segment
                        bool inserted = false;
                        for (int i = index; i < eraseAt.Count; i++)
                        {
                            StrokeFIndices lastFragment = eraseAt[i];
                            if (fragment.BeginFIndex < lastFragment.EndFIndex)
                            {
                                // If the fragments overlap, merge them
                                if (fragment.EndFIndex > lastFragment.BeginFIndex)
                                {
                                    fragment = new StrokeFIndices(
                                        Math.Min(lastFragment.BeginFIndex, fragment.BeginFIndex),
                                        Math.Max(lastFragment.EndFIndex, fragment.EndFIndex));

                                    // If the fragment doesn't go beyond lastFragment, break
                                    if ((fragment.EndFIndex <= lastFragment.EndFIndex) || ((i + 1) == eraseAt.Count))
                                    {
                                        inserted   = true;
                                        eraseAt[i] = fragment;
                                        break;
                                    }
                                    else
                                    {
                                        eraseAt.RemoveAt(i);
                                        i--;
                                    }
                                }
                                // insert otherwise
                                else
                                {
                                    eraseAt.Insert(i, fragment);
                                    inserted = true;
                                    break;
                                }
                            }
                        }

                        // If not merged nor inserted, add it to the end of the list
                        if (false == inserted)
                        {
                            eraseAt.Add(fragment);
                        }
                        // Break out if the entire ink segment is hit - {BeforeFirst, AfterLast}
                        if (eraseAt[eraseAt.Count - 1].IsFull)
                        {
                            break;
                        }
                    }
                    // Merge inter-segment overlapping fragments
                    if ((index > 0) && (index < eraseAt.Count))
                    {
                        StrokeFIndices lastFragment = eraseAt[index - 1];
                        if (DoubleUtil.AreClose(lastFragment.EndFIndex, StrokeFIndices.AfterLast))
                        {
                            if (DoubleUtil.AreClose(eraseAt[index].BeginFIndex, StrokeFIndices.BeforeFirst))
                            {
                                lastFragment.EndFIndex = eraseAt[index].EndFIndex;
                                eraseAt[index - 1]     = lastFragment;
                                eraseAt.RemoveAt(index);
                            }
                            else
                            {
                                lastFragment.EndFIndex = inkStrokeNode.Index;
                                eraseAt[index - 1]     = lastFragment;
                            }
                        }
                    }
                }
                // Start next ink segment
                inkSegmentBounds = inkNodeBounds;
            }
            if (eraseAt.Count != 0)
            {
                foreach (StrokeFIndices segment in eraseAt)
                {
                    intersections.Add(new StrokeIntersection(segment.BeginFIndex, StrokeFIndices.AfterLast,
                                                             StrokeFIndices.BeforeFirst, segment.EndFIndex));
                }
            }
            return(eraseAt.Count != 0);
        }
Beispiel #11
0
        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());
        }