Пример #1
0
        /// <summary>
        /// Calculate the after-clipping Strokes. Only the "in-segments" are left after this operation.
        /// </summary>
        /// <param name="cutAt">Array of intersections indicating the clipping locations</param>
        /// <returns>The resulting StrokeCollection</returns>
        internal StrokeCollection Clip(StrokeIntersection[] cutAt)
        {
            System.Diagnostics.Debug.Assert(cutAt != null);

            // Nothing is inside
            if (cutAt.Length == 0)
            {
                return(new StrokeCollection());
            }


            // Get the "in-segments"
            StrokeFIndices[] inSegments = StrokeIntersection.GetInSegments(cutAt);

            // For special case like cutAt is {BF, AL, BF, 0.67}, the inSegments are empty
            if (inSegments.Length == 0)
            {
                return(new StrokeCollection());
            }

            // Two other validations are deferred to the private clip function to avoid duplicate code.
            // 1. ValidateSortedNoOverlap
            // 2. Check whether the insegments are out of range with the packets
            return(this.Clip(inSegments));
        }
Пример #2
0
        /// <summary>
        /// Calculate the after-erasing Strokes. Only the "out-segments" are left after this operation.
        /// </summary>
        /// <param name="cutAt">Array of intersections indicating the erasing locations</param>
        /// <returns></returns>
        internal StrokeCollection Erase(StrokeIntersection[] cutAt)
        {
            System.Diagnostics.Debug.Assert(cutAt != null);

            // Nothing needs to be erased
            if (cutAt.Length == 0)
            {
                StrokeCollection strokes = new StrokeCollection();
                strokes.Add(this.Clone()); //clip and erase always return clones for this condition
                return(strokes);
            }

            // Two assertions are deferred to the private erase function to avoid duplicate code.
            // 1. AssertSortedNoOverlap
            // 2. Check whether the insegments are out of range with the packets
            StrokeFIndices[] hitSegments = StrokeIntersection.GetHitSegments(cutAt);
            return(this.Erase(hitSegments));
        }
Пример #3
0
        /// <summary>
        /// Get the "hit-segments"
        /// </summary> 
        internal static StrokeFIndices[] GetHitSegments(StrokeIntersection[] intersections)
        { 
            System.Diagnostics.Debug.Assert(intersections != null); 
            System.Diagnostics.Debug.Assert(intersections.Length > 0);
 
            List<StrokeFIndices> hitFIndices = new List<StrokeFIndices>(intersections.Length);
            for (int j = 0; j < intersections.Length; j++)
            {
                System.Diagnostics.Debug.Assert(!intersections[j].IsEmpty); 
                if (!intersections[j].HitSegment.IsEmpty)
                { 
                    if (hitFIndices.Count > 0 && 
                        hitFIndices[hitFIndices.Count - 1].EndFIndex >=
                        intersections[j].HitSegment.BeginFIndex) 
                    {
                        //merge
                        StrokeFIndices sfiPrevious = hitFIndices[hitFIndices.Count - 1];
                        sfiPrevious.EndFIndex = intersections[j].HitSegment.EndFIndex; 
                        hitFIndices[hitFIndices.Count - 1] = sfiPrevious;
                    } 
                    else 
                    {
                        hitFIndices.Add(intersections[j].HitSegment); 
                    }
                }
            }
            return hitFIndices.ToArray(); 
        }
Пример #4
0
 /// <summary>
 /// C-tor
 /// </summary>
 internal StrokeHitEventArgs(Stroke stroke, StrokeIntersection[] hitFragments)
 {
     System.Diagnostics.Debug.Assert(stroke != null && hitFragments != null && hitFragments.Length > 0);
     _stroke = stroke;
     _hitFragments = hitFragments;
 }
Пример #5
0
        /// <summary>
        /// Helper function to find out the hit test result
        /// </summary>
        private void ProduceHitTestResults(
                                List<LassoCrossing> crossingList, List<StrokeIntersection> strokeIntersections)
        {
            bool previousSegmentInsideLasso = false;
            for (int x = 0; x <= crossingList.Count; x++)
            {
                bool currentSegmentWithinLasso = false;
                bool canMerge = true;
                StrokeIntersection si = new StrokeIntersection();
                if (x == 0)
                {
                    si.HitBegin = StrokeFIndices.BeforeFirst;
                    si.InBegin = StrokeFIndices.BeforeFirst;
                }
                else
                {
                    si.InBegin = crossingList[x - 1].FIndices.EndFIndex;
                    si.HitBegin = crossingList[x - 1].FIndices.BeginFIndex;
                    currentSegmentWithinLasso = SegmentWithinLasso(crossingList[x - 1].EndNode, si.InBegin);
                }

                if (x == crossingList.Count)
                {
                    // NTRAID#WINOS-1132904-2005/04/27-XIAOTU: For a special case when the last intersection is something like (1.2, AL).
                    // As a result the last InSegment should be empty.
                    if (DoubleUtil.AreClose(si.InBegin, StrokeFIndices.AfterLast))
                    {
                        si.InEnd = StrokeFIndices.BeforeFirst;
                    }
                    else
                    {
                        si.InEnd = StrokeFIndices.AfterLast;
                    }
                    si.HitEnd = StrokeFIndices.AfterLast;
                }
                else
                {
                    si.InEnd = crossingList[x].FIndices.BeginFIndex;

                    // NTRAID#WINOS-1132904-2005/04/27-XIAOTU: For a speical case when the first intersection is something like (BF, 0.67).
                    // As a result the first InSegment should be empty
                    if (DoubleUtil.AreClose(si.InEnd, StrokeFIndices.BeforeFirst))
                    {
                        System.Diagnostics.Debug.Assert(DoubleUtil.AreClose(si.InBegin, StrokeFIndices.BeforeFirst));
                        si.InBegin = StrokeFIndices.AfterLast;
                    }

                    si.HitEnd = crossingList[x].FIndices.EndFIndex;
                    currentSegmentWithinLasso = SegmentWithinLasso(crossingList[x].StartNode, si.InEnd);

                    // NTRAID#WINOS-1141831-2005/04/27-XIAOTU: If both the start and end position of the current crossing is
                    // outside the lasso, the crossing is a hit-only intersection, i.e., the in-segment is empty.
                    if (!currentSegmentWithinLasso && !SegmentWithinLasso(crossingList[x].EndNode, si.HitEnd))
                    {
                        currentSegmentWithinLasso = true;
                        si.HitBegin = crossingList[x].FIndices.BeginFIndex;
                        si.InBegin = StrokeFIndices.AfterLast;
                        si.InEnd = StrokeFIndices.BeforeFirst;
                        canMerge = false;
                    }
                }

                if (currentSegmentWithinLasso)
                {
                    if (x > 0 && previousSegmentInsideLasso && canMerge)
                    {
                        // we need to consolidate with the previous segment
                        StrokeIntersection previousIntersection = strokeIntersections[strokeIntersections.Count - 1];

                        // For example: previousIntersection = [BF, AL, BF, 0.0027], si = [BF, 0.0027, 0.049, 0.063]
                        if (previousIntersection.InSegment.IsEmpty)
                        {
                            previousIntersection.InBegin = si.InBegin;
                        }
                        previousIntersection.InEnd = si.InEnd;
                        previousIntersection.HitEnd = si.HitEnd;
                        strokeIntersections[strokeIntersections.Count - 1] = previousIntersection;
                    }
                    else
                    {
                        strokeIntersections.Add(si);
                    }

                    if (DoubleUtil.AreClose(si.HitEnd, StrokeFIndices.AfterLast))
                    {
                        // The strokeIntersections already cover the end of the stroke. No need to continue.
                        return;
                    }
                }
                previousSegmentInsideLasso = currentSegmentWithinLasso;
            }
        }
Пример #6
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 new StrokeIntersection[0];
            }

            //
            // 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 new StrokeIntersection[0];
                }
            }

            // 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();
        }
Пример #7
0
        /// <summary>
        /// Calculate the after-clipping Strokes. Only the "in-segments" are left after this operation.
        /// </summary>
        /// <param name="cutAt">Array of intersections indicating the clipping locations</param>
        /// <returns>The resulting StrokeCollection</returns>
        internal StrokeCollection Clip(StrokeIntersection[] cutAt)
        {
            System.Diagnostics.Debug.Assert(cutAt != null);

            // Nothing is inside
            if (cutAt.Length == 0)
            {
                return new StrokeCollection();
            }


            // Get the "in-segments"
            StrokeFIndices[] inSegments = StrokeIntersection.GetInSegments(cutAt);

            // For special case like cutAt is {BF, AL, BF, 0.67}, the inSegments are empty
            if (inSegments.Length == 0)
            {
                return new StrokeCollection();
            }

            // Two other validations are deferred to the private clip function to avoid duplicate code.
            // 1. ValidateSortedNoOverlap
            // 2. Check whether the insegments are out of range with the packets
            return this.Clip(inSegments);
        }
Пример #8
0
        /// <summary>
        /// Calculate the after-erasing Strokes. Only the "out-segments" are left after this operation.
        /// </summary>
        /// <param name="cutAt">Array of intersections indicating the erasing locations</param>
        /// <returns></returns>
        internal StrokeCollection Erase(StrokeIntersection[] cutAt)
        {
            System.Diagnostics.Debug.Assert(cutAt != null);

            // Nothing needs to be erased
            if(cutAt.Length == 0)
            {
                StrokeCollection strokes = new StrokeCollection();
                strokes.Add(this.Clone()); //clip and erase always return clones for this condition
                return strokes;
            }

            // Two assertions are deferred to the private erase function to avoid duplicate code.
            // 1. AssertSortedNoOverlap
            // 2. Check whether the insegments are out of range with the packets
            StrokeFIndices[] hitSegments = StrokeIntersection.GetHitSegments(cutAt);
            return this.Erase(hitSegments);
        }