ErasingStroke.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / MS / Internal / Ink / ErasingStroke.cs / 1305600 / ErasingStroke.cs

                            //------------------------------------------------------------------------ 
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
// 
//----------------------------------------------------------------------- 
//#define POINTS_FILTER_TRACE
using System; 
using System.Windows; 
using System.Windows.Ink;
using System.Windows.Media; 
using System.Collections.Generic;


namespace MS.Internal.Ink 
{
    #region ErasingStroke 
 
    /// 
    /// This class represents a contour of an erasing stroke, and provides 
    /// internal API for static and incremental stroke_contour vs stroke_contour
    /// hit-testing.
    /// 
    internal class ErasingStroke 
    {
        #region Constructors 
 
        /// 
        /// Constructor for incremental erasing 
        /// 
        /// The shape of the eraser's tip
        internal ErasingStroke(StylusShape erasingShape)
        { 
            System.Diagnostics.Debug.Assert(erasingShape != null);
            _nodeIterator = new StrokeNodeIterator(erasingShape); 
        } 

        ///  
        /// Constructor for static (atomic) erasing
        /// 
        /// The shape of the eraser's tip
        /// the spine of the erasing stroke 
        internal ErasingStroke(StylusShape erasingShape, IEnumerable path)
            : this(erasingShape) 
        { 
            MoveTo(path);
        } 

        #endregion

        #region API 

        ///  
        /// Generates stroke nodes along a given path. 
        /// Drops any previously genererated nodes.
        ///  
        /// 
        internal void MoveTo(IEnumerable path)
        {
            System.Diagnostics.Debug.Assert((path != null) && (IEnumerablePointHelper.GetCount(path) != 0)); 
            Point[] points = IEnumerablePointHelper.GetPointArray(path);
 
            if (_erasingStrokeNodes == null) 
            {
                _erasingStrokeNodes = new List(points.Length); 
            }
            else
            {
                _erasingStrokeNodes.Clear(); 
            }
 
 
            _bounds = Rect.Empty;
            _nodeIterator = _nodeIterator.GetIteratorForNextSegment(points.Length > 1 ? FilterPoints(points) : points); 
            for (int i = 0; i < _nodeIterator.Count; i++)
            {
                StrokeNode strokeNode = _nodeIterator[i];
                _bounds.Union(strokeNode.GetBoundsConnected()); 
                _erasingStrokeNodes.Add(strokeNode);
            } 
#if POINTS_FILTER_TRACE 
            _totalPointsAdded += path.Length;
            System.Diagnostics.Debug.WriteLine(String.Format("Total Points added: {0} screened: {1} collinear screened: {2}", _totalPointsAdded, _totalPointsScreened, _collinearPointsScreened)); 
#endif

        }
 
        /// 
        /// Returns the bounds of the eraser's last move. 
        ///  
        /// 
        internal Rect Bounds { get { return _bounds; } } 

        /// 
        /// Hit-testing for stroke erase scenario.
        ///  
        /// the stroke nodes to iterate
        /// true if the strokes intersect, false otherwise 
        internal bool HitTest(StrokeNodeIterator iterator) 
        {
            System.Diagnostics.Debug.Assert(iterator != null); 

            if ((_erasingStrokeNodes == null) || (_erasingStrokeNodes.Count == 0))
            {
                return false; 
            }
 
            Rect inkSegmentBounds = Rect.Empty; 
            for (int i = 0; i < iterator.Count; i++)
            { 
                StrokeNode inkStrokeNode = iterator[i];
                Rect inkNodeBounds = inkStrokeNode.GetBounds();
                inkSegmentBounds.Union(inkNodeBounds);
 
                if (inkSegmentBounds.IntersectsWith(_bounds))
                { 
                    // 

                    foreach (StrokeNode erasingStrokeNode in _erasingStrokeNodes) 
                    {
                        if (inkSegmentBounds.IntersectsWith(erasingStrokeNode.GetBoundsConnected())
                            && erasingStrokeNode.HitTest(inkStrokeNode))
                        { 
                            return true;
                        } 
                    } 
                }
            } 
            return false;
        }

        ///  
        /// Hit-testing for point erase.
        ///  
        ///  
        /// 
        ///  
        internal bool EraseTest(StrokeNodeIterator iterator, List intersections)
        {
            System.Diagnostics.Debug.Assert(iterator != null);
            System.Diagnostics.Debug.Assert(intersections != null); 
            intersections.Clear();
 
            List eraseAt = new List(); 

            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);
        }

        #endregion 

        #region private API 
        private Point[] FilterPoints(Point[] path) 
        {
            System.Diagnostics.Debug.Assert(path.Length > 1); 
            Point back2, back1;
            int i;
            List newPath = new List();
            if (_nodeIterator.Count == 0) 
            {
                newPath.Add(path[0]); 
                newPath.Add(path[1]); 
                back2 = path[0];
                back1 = path[1]; 
                i = 2;
            }
            else
            { 
                newPath.Add(path[0]);
                back2 = _nodeIterator[_nodeIterator.Count - 1].Position; 
                back1 = path[0]; 
                i = 1;
            } 

            while (i < path.Length)
            {
                if (DoubleUtil.AreClose(back1, path[i])) 
                {
                    // Filter out duplicate points 
                    i++; 
                    continue;
                } 

                Vector begin = back2 - back1;
                Vector end = path[i] - back1;
                //On a line defined by begin & end,  finds the findex of the point nearest to the origin (0,0). 
                double findex = StrokeNodeOperations.GetProjectionFIndex(begin, end);
 
                if (DoubleUtil.IsBetweenZeroAndOne(findex)) 
                {
                    Vector v = (begin + (end - begin) * findex); 
                    if (v.LengthSquared < CollinearTolerance)
                    {
                        // The point back1 can be considered as on the line from back2 to the toTest StrokeNode.
                        // Modify the previous point. 
                        newPath[newPath.Count - 1] = path[i];
                        back1 = path[i]; 
                        i++; 
#if POINTS_FILTER_TRACE
                        _collinearPointsScreened ++; 
#endif
                        continue;
                    }
                } 

                // Add the surviving point into the list. 
                newPath.Add(path[i]); 
                back2 = back1;
                back1 = path[i]; 
                i++;
            }
#if POINTS_FILTER_TRACE
            _totalPointsScreened += path.Length - newPath.Count; 
#endif
            return newPath.ToArray(); 
        } 

        #endregion 

        #region Fields

        private StrokeNodeIterator      _nodeIterator; 
        private List        _erasingStrokeNodes = null;
        private Rect                    _bounds = Rect.Empty; 
 
#if POINTS_FILTER_TRACE
        private int                     _totalPointsAdded = 0; 
        private int                     _totalPointsScreened = 0;
        private int                     _collinearPointsScreened = 0;
#endif
 
        // The collinear tolerance used in points filtering algorithm. The valie
        // should be further tuned considering trade-off of performance and accuracy. 
        // In general, the larger the value, more points are filtered but less accurate. 
        // For a value of 0.5, typically 70% - 80% percent of the points are filtered out.
        private static readonly double CollinearTolerance = 0.1f; 

        #endregion
    }
 
    #endregion
} 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------ 
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
// 
//----------------------------------------------------------------------- 
//#define POINTS_FILTER_TRACE
using System; 
using System.Windows; 
using System.Windows.Ink;
using System.Windows.Media; 
using System.Collections.Generic;


namespace MS.Internal.Ink 
{
    #region ErasingStroke 
 
    /// 
    /// This class represents a contour of an erasing stroke, and provides 
    /// internal API for static and incremental stroke_contour vs stroke_contour
    /// hit-testing.
    /// 
    internal class ErasingStroke 
    {
        #region Constructors 
 
        /// 
        /// Constructor for incremental erasing 
        /// 
        /// The shape of the eraser's tip
        internal ErasingStroke(StylusShape erasingShape)
        { 
            System.Diagnostics.Debug.Assert(erasingShape != null);
            _nodeIterator = new StrokeNodeIterator(erasingShape); 
        } 

        ///  
        /// Constructor for static (atomic) erasing
        /// 
        /// The shape of the eraser's tip
        /// the spine of the erasing stroke 
        internal ErasingStroke(StylusShape erasingShape, IEnumerable path)
            : this(erasingShape) 
        { 
            MoveTo(path);
        } 

        #endregion

        #region API 

        ///  
        /// Generates stroke nodes along a given path. 
        /// Drops any previously genererated nodes.
        ///  
        /// 
        internal void MoveTo(IEnumerable path)
        {
            System.Diagnostics.Debug.Assert((path != null) && (IEnumerablePointHelper.GetCount(path) != 0)); 
            Point[] points = IEnumerablePointHelper.GetPointArray(path);
 
            if (_erasingStrokeNodes == null) 
            {
                _erasingStrokeNodes = new List(points.Length); 
            }
            else
            {
                _erasingStrokeNodes.Clear(); 
            }
 
 
            _bounds = Rect.Empty;
            _nodeIterator = _nodeIterator.GetIteratorForNextSegment(points.Length > 1 ? FilterPoints(points) : points); 
            for (int i = 0; i < _nodeIterator.Count; i++)
            {
                StrokeNode strokeNode = _nodeIterator[i];
                _bounds.Union(strokeNode.GetBoundsConnected()); 
                _erasingStrokeNodes.Add(strokeNode);
            } 
#if POINTS_FILTER_TRACE 
            _totalPointsAdded += path.Length;
            System.Diagnostics.Debug.WriteLine(String.Format("Total Points added: {0} screened: {1} collinear screened: {2}", _totalPointsAdded, _totalPointsScreened, _collinearPointsScreened)); 
#endif

        }
 
        /// 
        /// Returns the bounds of the eraser's last move. 
        ///  
        /// 
        internal Rect Bounds { get { return _bounds; } } 

        /// 
        /// Hit-testing for stroke erase scenario.
        ///  
        /// the stroke nodes to iterate
        /// true if the strokes intersect, false otherwise 
        internal bool HitTest(StrokeNodeIterator iterator) 
        {
            System.Diagnostics.Debug.Assert(iterator != null); 

            if ((_erasingStrokeNodes == null) || (_erasingStrokeNodes.Count == 0))
            {
                return false; 
            }
 
            Rect inkSegmentBounds = Rect.Empty; 
            for (int i = 0; i < iterator.Count; i++)
            { 
                StrokeNode inkStrokeNode = iterator[i];
                Rect inkNodeBounds = inkStrokeNode.GetBounds();
                inkSegmentBounds.Union(inkNodeBounds);
 
                if (inkSegmentBounds.IntersectsWith(_bounds))
                { 
                    // 

                    foreach (StrokeNode erasingStrokeNode in _erasingStrokeNodes) 
                    {
                        if (inkSegmentBounds.IntersectsWith(erasingStrokeNode.GetBoundsConnected())
                            && erasingStrokeNode.HitTest(inkStrokeNode))
                        { 
                            return true;
                        } 
                    } 
                }
            } 
            return false;
        }

        ///  
        /// Hit-testing for point erase.
        ///  
        ///  
        /// 
        ///  
        internal bool EraseTest(StrokeNodeIterator iterator, List intersections)
        {
            System.Diagnostics.Debug.Assert(iterator != null);
            System.Diagnostics.Debug.Assert(intersections != null); 
            intersections.Clear();
 
            List eraseAt = new List(); 

            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);
        }

        #endregion 

        #region private API 
        private Point[] FilterPoints(Point[] path) 
        {
            System.Diagnostics.Debug.Assert(path.Length > 1); 
            Point back2, back1;
            int i;
            List newPath = new List();
            if (_nodeIterator.Count == 0) 
            {
                newPath.Add(path[0]); 
                newPath.Add(path[1]); 
                back2 = path[0];
                back1 = path[1]; 
                i = 2;
            }
            else
            { 
                newPath.Add(path[0]);
                back2 = _nodeIterator[_nodeIterator.Count - 1].Position; 
                back1 = path[0]; 
                i = 1;
            } 

            while (i < path.Length)
            {
                if (DoubleUtil.AreClose(back1, path[i])) 
                {
                    // Filter out duplicate points 
                    i++; 
                    continue;
                } 

                Vector begin = back2 - back1;
                Vector end = path[i] - back1;
                //On a line defined by begin & end,  finds the findex of the point nearest to the origin (0,0). 
                double findex = StrokeNodeOperations.GetProjectionFIndex(begin, end);
 
                if (DoubleUtil.IsBetweenZeroAndOne(findex)) 
                {
                    Vector v = (begin + (end - begin) * findex); 
                    if (v.LengthSquared < CollinearTolerance)
                    {
                        // The point back1 can be considered as on the line from back2 to the toTest StrokeNode.
                        // Modify the previous point. 
                        newPath[newPath.Count - 1] = path[i];
                        back1 = path[i]; 
                        i++; 
#if POINTS_FILTER_TRACE
                        _collinearPointsScreened ++; 
#endif
                        continue;
                    }
                } 

                // Add the surviving point into the list. 
                newPath.Add(path[i]); 
                back2 = back1;
                back1 = path[i]; 
                i++;
            }
#if POINTS_FILTER_TRACE
            _totalPointsScreened += path.Length - newPath.Count; 
#endif
            return newPath.ToArray(); 
        } 

        #endregion 

        #region Fields

        private StrokeNodeIterator      _nodeIterator; 
        private List        _erasingStrokeNodes = null;
        private Rect                    _bounds = Rect.Empty; 
 
#if POINTS_FILTER_TRACE
        private int                     _totalPointsAdded = 0; 
        private int                     _totalPointsScreened = 0;
        private int                     _collinearPointsScreened = 0;
#endif
 
        // The collinear tolerance used in points filtering algorithm. The valie
        // should be further tuned considering trade-off of performance and accuracy. 
        // In general, the larger the value, more points are filtered but less accurate. 
        // For a value of 0.5, typically 70% - 80% percent of the points are filtered out.
        private static readonly double CollinearTolerance = 0.1f; 

        #endregion
    }
 
    #endregion
} 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK