StrokeNode.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

                            //#define DEBUG_RENDERING_FEEDBACK 
//------------------------------------------------------------------------
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
//  
//-----------------------------------------------------------------------
using System; 
using System.Collections.Generic; 
using System.Windows;
using System.Windows.Ink; 
using System.Windows.Media;
using System.Windows.Input;
using System.Diagnostics;
 
namespace MS.Internal.Ink
{ 
    #region StrokeNode 

    ///  
    /// StrokeNode represents a single segment on a stroke spine.
    /// It's used in enumerating through basic geometries making a stroke contour.
    /// 
    internal struct StrokeNode 
    {
        #region Constructors 
 
        /// 
        /// Constructor. 
        /// 
        /// StrokeNodeOperations object created for particular rendering
        /// Index of the node on the stroke spine
        /// StrokeNodeData for this node 
        /// StrokeNodeData for the precedeng node
        /// Whether the current node is the last node 
        internal StrokeNode( 
            StrokeNodeOperations operations,
            int index, 
            StrokeNodeData nodeData,
            StrokeNodeData lastNodeData,
            bool isLastNode)
        { 
            System.Diagnostics.Debug.Assert(operations != null);
            System.Diagnostics.Debug.Assert((nodeData.IsEmpty == false) && (index >= 0)); 
 

            _operations = operations; 
            _index = index;
            _thisNode = nodeData;
            _lastNode = lastNodeData;
            _isQuadCached = lastNodeData.IsEmpty; 
            _connectingQuad = Quad.Empty;
            _isLastNode = isLastNode; 
        } 

        #endregion 

        #region Public API

        ///  
        /// Position of the node on the stroke spine.
        ///  
        ///  
        internal Point Position { get { return _thisNode.Position; } }
 
        /// 
        /// Position of the previous StrokeNode
        /// 
        ///  
        internal Point PreviousPosition { get { return _lastNode.Position; } }
 
        ///  
        /// PressureFactor of the node on the stroke spine.
        ///  
        /// 
        internal float PressureFactor { get { return _thisNode.PressureFactor; } }

        ///  
        /// PressureFactor of the previous StrokeNode
        ///  
        ///  
        internal float PreviousPressureFactor { get { return _lastNode.PressureFactor; } }
 
        /// 
        /// Tells whether the node shape (the stylus shape used in the rendering)
        /// is elliptical or polygonal. If the shape is an ellipse, GetContourPoints
        /// returns the control points for the quadratic Bezier that defines the ellipse. 
        /// 
        /// true if the shape is ellipse, false otherwise 
        internal bool IsEllipse { get { return IsValid && _operations.IsNodeShapeEllipse; } } 

        ///  
        /// Returns true if this is the last node in the enumerator
        /// 
        internal bool IsLastNode { get { return _isLastNode; } }
 
        /// 
        /// Returns the bounds of the node shape w/o connecting quadrangle 
        ///  
        /// 
        internal Rect GetBounds() 
        {
            return IsValid ? _operations.GetNodeBounds(_thisNode) : Rect.Empty;
        }
 
        /// 
        /// Returns the bounds of the node shape and connecting quadrangle 
        ///  
        /// 
        internal Rect GetBoundsConnected() 
        {
            return IsValid ? Rect.Union(_operations.GetNodeBounds(_thisNode), ConnectingQuad.Bounds) : Rect.Empty;
        }
 
        /// 
        /// Returns the points that make up the stroke node shape (minus the connecting quad) 
        ///  
        internal void GetContourPoints(List pointBuffer)
        { 
            if (IsValid)
            {
                _operations.GetNodeContourPoints(_thisNode, pointBuffer);
            } 
        }
 
        ///  
        /// Returns the points that make up the stroke node shape (minus the connecting quad)
        ///  
        internal void GetPreviousContourPoints(List pointBuffer)
        {
            if (IsValid)
            { 
                _operations.GetNodeContourPoints(_lastNode, pointBuffer);
            } 
        } 

        ///  
        /// Returns the connecting quad
        /// 
        internal Quad GetConnectingQuad()
        { 
            if (IsValid)
            { 
                return ConnectingQuad; 
            }
            return Quad.Empty; 
        }

        ///// 
        ///// IsPointWithinRectOrEllipse 
        ///// 
        //internal bool IsPointWithinRectOrEllipse(Point point, double xRadiusOrHalfWidth, double yRadiusOrHalfHeight, Point center, bool isEllipse) 
        //{ 
        //    if (isEllipse)
        //    { 
        //        //determine what delta is required to move the rect to be
        //        //centered at 0,0
        //        double xDelta = center.X + xRadiusOrHalfWidth;
        //        double yDelta = center.Y + yRadiusOrHalfHeight; 

        //        //offset the point by that delta 
        //        point.X -= xDelta; 
        //        point.Y -= yDelta;
 
        //        //formula for ellipse is x^2/a^2 + y^2/b^2 = 1
        //        double a = xRadiusOrHalfWidth;
        //        double b = yRadiusOrHalfHeight;
        //        double res = (((point.X * point.X) / (a * a)) + 
        //                     ((point.Y * point.Y) / (b * b)));
 
        //        if (res <= 1) 
        //        {
        //            return true; 
        //        }
        //        return false;
        //    }
        //    else 
        //    {
        //        if (point.X >= (center.X - xRadiusOrHalfWidth) && 
        //            point.X <= (center.X + xRadiusOrHalfWidth) && 
        //            point.Y >= (center.Y - yRadiusOrHalfHeight) &&
        //            point.Y <= (center.Y + yRadiusOrHalfHeight)) 
        //        {
        //            return true;
        //        }
        //        return false; 
        //    }
        //} 
 
        /// 
        /// GetPointsAtStartOfSegment 
        /// 
        internal void GetPointsAtStartOfSegment(List abPoints,
                                                List dcPoints
#if DEBUG_RENDERING_FEEDBACK 
                                                , DrawingContext debugDC, double feedbackSize, bool showFeedback
#endif 
                                                ) 
        {
            if (IsValid) 
            {
                Quad quad = ConnectingQuad;
                if (IsEllipse)
                { 
                    Rect startNodeBounds = _operations.GetNodeBounds(_lastNode);
 
                    //add instructions to arc from D to A 
                    abPoints.Add(quad.D);
                    abPoints.Add(StrokeRenderer.ArcToMarker); 
                    abPoints.Add(new Point(startNodeBounds.Width, startNodeBounds.Height));
                    abPoints.Add(quad.A);

                    //simply start at D 
                    dcPoints.Add(quad.D);
 
#if DEBUG_RENDERING_FEEDBACK 
                    if (showFeedback)
                    { 
                        debugDC.DrawEllipse(null, new Pen(Brushes.Pink, feedbackSize / 2), _lastNode.Position, startNodeBounds.Width / 2, startNodeBounds.Height / 2);
                        debugDC.DrawEllipse(Brushes.Red, null, quad.A, feedbackSize, feedbackSize);
                        debugDC.DrawEllipse(Brushes.Blue, null, quad.D, feedbackSize, feedbackSize);
                    } 
#endif
                } 
                else 
                {
                    //we're interested in the A, D points as well as the 
                    //nodecountour points between them
                    Rect endNodeRect = _operations.GetNodeBounds(_thisNode);

#if DEBUG_RENDERING_FEEDBACK 
                    if (showFeedback)
                    { 
                        debugDC.DrawRectangle(null, new Pen(Brushes.Pink, feedbackSize / 2), _operations.GetNodeBounds(_lastNode)); 
                    }
#endif 
                    Vector[] vertices = _operations.GetVertices();
                    double pressureFactor = _lastNode.PressureFactor;
                    int maxCount = vertices.Length * 2;
                    int i = 0; 
                    bool dIsInEndNode = true;
                    for (; i < maxCount; i++) 
                    { 
                        //look for the d point first
                        Point point = _lastNode.Position + (vertices[i % vertices.Length] * pressureFactor); 
                        if (point == quad.D)
                        {
                            //ab always starts with the D position (only add if it's not in endNode's bounds)
                            if (!endNodeRect.Contains(quad.D)) 
                            {
                                dIsInEndNode = false; 
                                abPoints.Add(quad.D); 
                                dcPoints.Add(quad.D);
                            } 
#if DEBUG_RENDERING_FEEDBACK
                            if (showFeedback)
                            {
                                debugDC.DrawEllipse(Brushes.Blue, null, quad.D, feedbackSize, feedbackSize); 
                            }
#endif 
                            break; 
                        }
                    } 

                    if (i == maxCount)
                    {
                        Debug.Assert(false, "StrokeNodeOperations.GetPointsAtStartOfSegment failed to find the D position"); 
                        //we didn't find the d point, return
                        return; 
                    } 

 
                    //now look for the A position
                    //advance i
                    i++;
                    for (int j = 0; i < maxCount && j < vertices.Length; i++, j++) 
                    {
                        //look for the A point now 
                        Point point = _lastNode.Position + (vertices[i % vertices.Length] * pressureFactor); 
                        //add everything in between to ab as long as it's not already in endNode's bounds
                        if (!endNodeRect.Contains(point)) 
                        {
                            abPoints.Add(point);
#if DEBUG_RENDERING_FEEDBACK
                        if (showFeedback) 
                        {
                            debugDC.DrawEllipse(Brushes.Wheat, null, point, feedbackSize, feedbackSize); 
                        } 
#endif
                        } 
                        if (dIsInEndNode)
                        {
                            Debug.Assert(!endNodeRect.Contains(point));
 
                            //add the first point after d, clockwise
                            dIsInEndNode = false; 
                            dcPoints.Add(point); 
                        }
                        if (point == quad.A) 
                        {
#if DEBUG_RENDERING_FEEDBACK
                            if (showFeedback)
                            { 
                                debugDC.DrawEllipse(Brushes.Red, null, point, feedbackSize, feedbackSize);
                            } 
#endif 
                            break;
                        } 
                    }
                }
            }
        } 

 
        ///  
        /// GetPointsAtEndOfSegment
        ///  
        internal void GetPointsAtEndOfSegment(  List abPoints,
                                                List dcPoints
#if DEBUG_RENDERING_FEEDBACK
                                                , DrawingContext debugDC, double feedbackSize, bool showFeedback 
#endif
                                                ) 
        { 
            if (IsValid)
            { 
                Quad quad = ConnectingQuad;
                if (IsEllipse)
                {
                    Rect bounds = GetBounds(); 
                    //add instructions to arc from D to A
                    abPoints.Add(quad.B); 
                    abPoints.Add(StrokeRenderer.ArcToMarker); 
                    abPoints.Add(new Point(bounds.Width, bounds.Height));
                    abPoints.Add(quad.C); 

                    //don't add to the dc points
#if DEBUG_RENDERING_FEEDBACK
                    if (showFeedback) 
                    {
                        debugDC.DrawEllipse(null, new Pen(Brushes.Pink, feedbackSize / 2), _thisNode.Position, bounds.Width / 2, bounds.Height / 2); 
                        debugDC.DrawEllipse(Brushes.Green, null, quad.B, feedbackSize, feedbackSize); 
                        debugDC.DrawEllipse(Brushes.Yellow, null, quad.C, feedbackSize, feedbackSize);
                    } 
#endif
                }
                else
                { 
#if DEBUG_RENDERING_FEEDBACK
                    if (showFeedback) 
                    { 
                        debugDC.DrawRectangle(null, new Pen(Brushes.Pink, feedbackSize / 2), GetBounds());
                    } 
#endif
                    //we're interested in the B, C points as well as the
                    //nodecountour points between them
                    double pressureFactor = _thisNode.PressureFactor; 
                    Vector[] vertices = _operations.GetVertices();
                    int maxCount = vertices.Length * 2; 
                    int i = 0; 
                    for (; i < maxCount; i++)
                    { 
                        //look for the d point first
                        Point point = _thisNode.Position + (vertices[i % vertices.Length] * pressureFactor);
                        if (point == quad.B)
                        { 
                            abPoints.Add(quad.B);
#if DEBUG_RENDERING_FEEDBACK 
                            if (showFeedback) 
                            {
                                debugDC.DrawEllipse(Brushes.Green, null, point, feedbackSize, feedbackSize); 
                            }
#endif
                            break;
                        } 
                    }
 
                    if (i == maxCount) 
                    {
                        Debug.Assert(false, "StrokeNodeOperations.GetPointsAtEndOfSegment failed to find the B position"); 
                        //we didn't find the d point, return
                        return;
                    }
 
                    //now look for the C position
                    //advance i 
                    i++; 
                    for (int j = 0; i < maxCount && j < vertices.Length; i++, j++)
                    { 
                        //look for the c point last
                        Point point = _thisNode.Position + (vertices[i % vertices.Length] * pressureFactor);
                        if (point == quad.C)
                        { 
                            break;
                        } 
                        //only add to ab if we didn't find C 
                        abPoints.Add(point);
 
#if DEBUG_RENDERING_FEEDBACK
                        if (showFeedback)
                        {
                            debugDC.DrawEllipse(Brushes.Wheat, null, quad.C, feedbackSize, feedbackSize); 
                        }
#endif 
                    } 
                    //finally, add the D point
                    dcPoints.Add(quad.C); 

#if DEBUG_RENDERING_FEEDBACK
                    if (showFeedback)
                    { 
                        debugDC.DrawEllipse(Brushes.Yellow, null, quad.C, feedbackSize, feedbackSize);
                    } 
#endif 
                }
            } 
        }

        /// 
        /// GetPointsAtMiddleSegment 
        /// 
        internal void GetPointsAtMiddleSegment( StrokeNode previous, 
                                                double angleBetweenNodes, 
                                                List abPoints,
                                                List dcPoints, 
                                                out bool missingIntersection
#if DEBUG_RENDERING_FEEDBACK
                                                , DrawingContext debugDC, double feedbackSize, bool showFeedback
#endif 
                                                )
        { 
            missingIntersection = false; 
            if (IsValid && previous.IsValid)
            { 
                Quad quad1 = previous.ConnectingQuad;
                if (!quad1.IsEmpty)
                {
                    Quad quad2 = ConnectingQuad; 
                    if (!quad2.IsEmpty)
                    { 
                        if (IsEllipse) 
                        {
                            Rect node1Bounds = _operations.GetNodeBounds(previous._lastNode); 
                            Rect node2Bounds = _operations.GetNodeBounds(_lastNode);
                            Rect node3Bounds = _operations.GetNodeBounds(_thisNode);
#if DEBUG_RENDERING_FEEDBACK
                            if (showFeedback) 
                            {
                                debugDC.DrawEllipse(null, new Pen(Brushes.Pink, feedbackSize / 2), _lastNode.Position, node2Bounds.Width / 2, node2Bounds.Height / 2); 
                            } 
#endif
                            if (angleBetweenNodes == 0.0d || ((quad1.B == quad2.A) && (quad1.C == quad2.D))) 
                            {
                                //quads connections are the same, just add them
                                abPoints.Add(quad1.B);
                                dcPoints.Add(quad1.C); 
#if DEBUG_RENDERING_FEEDBACK
                                if (showFeedback) 
                                { 
                                    debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                    debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize); 
                                }
#endif
                            }
                            else if (angleBetweenNodes > 0.0) 
                            {
                                //the stroke angled towards the AB side 
                                //this part is easy 
                                if (quad1.B == quad2.A)
                                { 
                                    abPoints.Add(quad1.B);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    { 
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                    } 
#endif 
                                }
                                else 
                                {
                                    Point intersection = GetIntersection(quad1.A, quad1.B, quad2.A, quad2.B);
                                    Rect union = Rect.Union(node1Bounds, node2Bounds);
                                    union.Inflate(1.0, 1.0); 
                                    //make sure we're not off in space
 
#if DEBUG_RENDERING_FEEDBACK 
                                    if (showFeedback)
                                    { 
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize * 1.5, feedbackSize * 1.5);
                                        debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize);
                                    }
#endif 

                                    if (union.Contains(intersection)) 
                                    { 
                                        abPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK 
                                        if (showFeedback)
                                        {
                                            debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize);
                                        } 
#endif
                                    } 
                                    else 
                                    {
                                        //if we missed the intersection we'll need to close the stroke segment 
                                        //this work is done in StrokeRenderer
                                        missingIntersection = true;
                                        return; //we're done.
                                    } 
                                }
 
                                if (quad1.C == quad2.D) 
                                {
                                    dcPoints.Add(quad1.C); 
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize); 
                                    }
#endif 
                                } 
                                else
                                { 
                                    //add instructions to arc from quad1.C to quad2.D in reverse order (since we walk this array backwards to render)
                                    dcPoints.Add(quad1.C);
                                    dcPoints.Add(new Point(node2Bounds.Width, node2Bounds.Height));
                                    dcPoints.Add(StrokeRenderer.ArcToMarker); 
                                    dcPoints.Add(quad2.D);
#if DEBUG_RENDERING_FEEDBACK 
                                    if (showFeedback) 
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize); 
                                        debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize);
                                    }
#endif
                                } 
                            }
                            else 
                            { 
                                //the stroke angled towards the CD side
                                //this part is easy 
                                if (quad1.C == quad2.D)
                                {
                                    dcPoints.Add(quad1.C);
#if DEBUG_RENDERING_FEEDBACK 
                                    if (showFeedback)
                                    { 
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize); 
                                    }
#endif 
                                }
                                else
                                {
                                    Point intersection = GetIntersection(quad1.D, quad1.C, quad2.D, quad2.C); 
                                    Rect union = Rect.Union(node1Bounds, node2Bounds);
                                    union.Inflate(1.0, 1.0); 
                                    //make sure we're not off in space 

#if DEBUG_RENDERING_FEEDBACK 
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize * 1.5, feedbackSize * 1.5);
                                        debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize); 
                                    }
#endif 
 
                                    if (union.Contains(intersection))
                                    { 
                                        dcPoints.Add(intersection);
#if DEBUG_RENDERING_FEEDBACK
                                        if (showFeedback)
                                        { 
                                            debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize);
                                        } 
#endif 
                                    }
                                    else 
                                    {
                                        //if we missed the intersection we'll need to close the stroke segment
                                        //this work is done in StrokeRenderer
                                        missingIntersection = true; 
                                        return; //we're done.
                                    } 
                                } 

                                if (quad1.B == quad2.A) 
                                {
                                    abPoints.Add(quad1.B);
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback) 
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize); 
                                    } 
#endif
 
                                }
                                else
                                {
                                    //we need to arc between quad1.B and quad2.A along node2 
                                    abPoints.Add(quad1.B);
                                    abPoints.Add(StrokeRenderer.ArcToMarker); 
                                    abPoints.Add(new Point(node2Bounds.Width, node2Bounds.Height)); 
                                    abPoints.Add(quad2.A);
#if DEBUG_RENDERING_FEEDBACK 
                                    if (showFeedback)
                                    {
                                        debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                        debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize); 
                                    }
#endif 
                                } 
                            }
                        } 
                        else
                        {
                            //rectangle
                            int indexA = -1; 
                            int indexB = -1;
                            int indexC = -1; 
                            int indexD = -1; 

                            Vector[] vertices = _operations.GetVertices(); 
                            double pressureFactor = _lastNode.PressureFactor;
                            for (int i = 0; i < vertices.Length; i++)
                            {
                                Point point = _lastNode.Position + (vertices[i % vertices.Length] * pressureFactor); 
                                if (point == quad2.A)
                                { 
                                    indexA = i; 
                                }
                                if (point == quad1.B) 
                                {
                                    indexB = i;
                                }
                                if (point == quad1.C) 
                                {
                                    indexC = i; 
                                } 
                                if (point == quad2.D)
                                { 
                                    indexD = i;
                                }
                            }
 
                            if (indexA == -1 || indexB == -1 || indexC == -1 || indexD == -1)
                            { 
                                Debug.Assert(false, "Couldn't find all 4 indexes in StrokeNodeOperations.GetPointsAtMiddleSegment"); 
                                return;
                            } 

#if DEBUG_RENDERING_FEEDBACK
                            if (showFeedback)
                            { 

                                debugDC.DrawRectangle(null, new Pen(Brushes.Pink, feedbackSize / 2), _operations.GetNodeBounds(_lastNode)); 
                                debugDC.DrawEllipse(Brushes.Red, null, quad2.A, feedbackSize, feedbackSize); 
                                debugDC.DrawEllipse(Brushes.Green, null, quad1.B, feedbackSize, feedbackSize);
                                debugDC.DrawEllipse(Brushes.Yellow, null, quad1.C, feedbackSize, feedbackSize); 
                                debugDC.DrawEllipse(Brushes.Blue, null, quad2.D, feedbackSize, feedbackSize);
                            }
#endif
 
                            Rect node3Rect = _operations.GetNodeBounds(_thisNode);
                            //take care of a-b first 
                            if (indexA == indexB) 
                            {
                                //quad connection is the same, just add it 
                                if (!node3Rect.Contains(quad1.B))
                                {
                                    abPoints.Add(quad1.B);
                                } 
                            }
                            else if ((indexA == 0 && indexB == 3) || ((indexA != 3 || indexB != 0) && (indexA > indexB))) 
                            { 
                                if (!node3Rect.Contains(quad1.B))
                                { 
                                    abPoints.Add(quad1.B);
                                }
                                if (!node3Rect.Contains(quad2.A))
                                { 
                                    abPoints.Add(quad2.A);
                                } 
                            } 
                            else
                            { 
                                Point intersection = GetIntersection(quad1.A, quad1.B, quad2.A, quad2.B);
                                Rect node12 = Rect.Union(_operations.GetNodeBounds(previous._lastNode), _operations.GetNodeBounds(_lastNode));
                                node12.Inflate(1.0, 1.0);
                                //make sure we're not off in space 
                                if (node12.Contains(intersection))
                                { 
                                    abPoints.Add(intersection); 
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback) 
                                    {
                                        debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize * 1.5);
                                    }
#endif 
                                }
                                else 
                                { 
                                    //if we missed the intersection we'll need to close the stroke segment
                                    //this work is done in StrokeRenderer. 
                                    missingIntersection = true;
                                    return; //we're done.
                                }
                            } 

                            // now take care of c-d. 
                            if (indexC == indexD) 
                            {
                                //quad connection is the same, just add it 
                                if (!node3Rect.Contains(quad1.C))
                                {
                                    dcPoints.Add(quad1.C);
                                } 
                            }
                            else if ((indexC == 0 && indexD == 3) || ((indexC != 3 || indexD != 0) && (indexC > indexD))) 
                            { 
                                if (!node3Rect.Contains(quad1.C))
                                { 
                                    dcPoints.Add(quad1.C);
                                }
                                if (!node3Rect.Contains(quad2.D))
                                { 
                                    dcPoints.Add(quad2.D);
                                } 
                            } 
                            else
                            { 
                                Point intersection = GetIntersection(quad1.D, quad1.C, quad2.D, quad2.C);
                                Rect node12 = Rect.Union(_operations.GetNodeBounds(previous._lastNode), _operations.GetNodeBounds(_lastNode));
                                node12.Inflate(1.0, 1.0);
                                //make sure we're not off in space 
                                if (node12.Contains(intersection))
                                { 
                                    dcPoints.Add(intersection); 
#if DEBUG_RENDERING_FEEDBACK
                                    if (showFeedback) 
                                    {
                                        debugDC.DrawEllipse(Brushes.Orange, null, intersection, feedbackSize, feedbackSize * 1.5);
                                    }
#endif 
                                }
                                else 
                                { 
                                    //if we missed the intersection we'll need to close the stroke segment
                                    //this work is done in StrokeRenderer. 
                                    missingIntersection = true;
                                    return; //we're done.
                                }
                            } 
                        }
                    } 
                } 
            }
        } 

        /// 
        /// Returns the intersection between two lines.  This code assumes there is an intersection
        /// and should only be called if that assumption is valid 
        /// 
        ///  
        internal static Point GetIntersection(Point line1Start, Point line1End, Point line2Start, Point line2End) 
        {
            double a1 = line1End.Y - line1Start.Y; 
            double b1 = line1Start.X - line1End.X;
            double c1 = (line1End.X * line1Start.Y) - (line1Start.X * line1End.Y);
            double a2 = line2End.Y - line2Start.Y;
            double b2 = line2Start.X - line2End.X; 
            double c2 = (line2End.X * line2Start.Y) - (line2Start.X * line2End.Y);
 
            double d = (a1 * b2) - (a2 * b1); 
            if (d != 0.0)
            { 
                double x = ((b1 * c2) - (b2 * c1)) / d;
                double y = ((a2 * c1) - (a1 * c2)) / d;

                //capture the min and max points 
                double line1XMin, line1XMax, line1YMin, line1YMax, line2XMin, line2XMax, line2YMin, line2YMax;
                if (line1Start.X < line1End.X) 
                { 
                    line1XMin = Math.Floor(line1Start.X);
                    line1XMax = Math.Ceiling(line1End.X); 
                }
                else
                {
                    line1XMin = Math.Floor(line1End.X); 
                    line1XMax = Math.Ceiling(line1Start.X);
                } 
 
                if (line2Start.X < line2End.X)
                { 
                    line2XMin = Math.Floor(line2Start.X);
                    line2XMax = Math.Ceiling(line2End.X);
                }
                else 
                {
                    line2XMin = Math.Floor(line2End.X); 
                    line2XMax = Math.Ceiling(line2Start.X); 
                }
 
                if (line1Start.Y < line1End.Y)
                {
                    line1YMin = Math.Floor(line1Start.Y);
                    line1YMax = Math.Ceiling(line1End.Y); 
                }
                else 
                { 
                    line1YMin = Math.Floor(line1End.Y);
                    line1YMax = Math.Ceiling(line1Start.Y); 
                }

                if (line2Start.Y < line2End.Y)
                { 
                    line2YMin = Math.Floor(line2Start.Y);
                    line2YMax = Math.Ceiling(line2End.Y); 
                } 
                else
                { 
                    line2YMin = Math.Floor(line2End.Y);
                    line2YMax = Math.Ceiling(line2Start.Y);
                }
 

                // now see if we have an intersection between the lines 
                // and not just the projection of the lines 
                if ((line1XMin <= x && x <= line1XMax) &&
                    (line1YMin <= y && y <= line1YMax) && 
                    (line2XMin <= x && x <= line2XMax) &&
                    (line2YMin <= y && y <= line2YMax))
                {
                    return new Point(x, y); 
                }
            } 
 
            if ((long)line1End.X == (long)line2Start.X &&
                (long)line1End.Y == (long)line2Start.Y) 
            {
                return new Point(line1End.X, line1End.Y);
            }
 
            return new Point(Double.NaN, Double.NaN);
        } 
 
        /// 
        /// This method tells whether the contour of a given stroke node 
        /// intersects with the contour of this node. The contours of both nodes
        /// include their connecting quadrangles.
        /// 
        ///  
        /// 
        internal bool HitTest(StrokeNode hitNode) 
        { 
            if (!IsValid || !hitNode.IsValid)
            { 
                return false;
            }

            IEnumerable hittingContour = hitNode.GetContourSegments(); 

            return _operations.HitTest(_lastNode, _thisNode, ConnectingQuad, hittingContour); 
        } 

        ///  
        /// Finds out if a given node intersects with this one,
        /// and returns findices of the intersection.
        /// 
        ///  
        /// 
        internal StrokeFIndices CutTest(StrokeNode hitNode) 
        { 
            if ((IsValid == false) || (hitNode.IsValid == false))
            { 
                return StrokeFIndices.Empty;
            }

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

        ///  
        /// Finds out if a given linear segment intersects with the contour of this node
        /// (including connecting quadrangle), and returns findices of the intersection. 
        ///  
        /// 
        ///  
        /// 
        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); 
        }
 
        #endregion

        #region Private helpers
 
        /// 
        /// Binds a local fragment to this node by setting the integer part of the 
        /// fragment findices equal to the index of the previous node 
        /// 
        ///  
        /// 
        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;
        }
 
        internal int Index
        { 
            get { return _index; } 
        }
 
        /// 
        /// Bind the StrokeFIndices for lasso hit test results.
        /// 
        ///  
        /// 
        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;
        } 

        /// 
        /// Tells whether the StrokeNode instance is valid or not (created via the default ctor)
        ///  
        internal bool IsValid { get { return _operations != null; } }
 
        ///  
        /// The quadrangle that connects this and the previous node.
        /// Can be empty if this node is the first one or if one of the nodes is 
        /// completely inside the other.
        /// The type Quad is supposed to be internal even if we surface StrokeNode.
        /// External users of StrokeNode should use GetConnectionPoints instead.
        ///  
        private Quad ConnectingQuad
        { 
            get 
            {
                System.Diagnostics.Debug.Assert(IsValid); 

                if (_isQuadCached == false)
                {
                    _connectingQuad = _operations.GetConnectingQuad(_lastNode, _thisNode); 
                    _isQuadCached = true;
                } 
                return _connectingQuad; 
            }
        } 

        /// 
        /// Returns an enumerator for edges of the contour comprised by the node
        /// and connecting quadrangle (_lastNode is excluded) 
        /// Used for hit-testing a stroke against an other stroke (stroke and point erasing)
        ///  
        private IEnumerable GetContourSegments() 
        {
            System.Diagnostics.Debug.Assert(IsValid); 

            // Calls thru to the StrokeNodeOperations object
            if (IsEllipse)
            { 
                // ISSUE-2004/06/15- temporary workaround to avoid hit-testing with ellipses
                return _operations.GetNonBezierContourSegments(_lastNode, _thisNode); 
            } 
            return  _operations.GetContourSegments(_thisNode, ConnectingQuad);
        } 

        /// 
        /// Returns the spine point that corresponds to the given findex.
        ///  
        /// A local findex between the previous index and this one (ex: between 2.0 and 3.0)
        /// Point on the spine 
        internal Point GetPointAt(double findex) 
        {
            System.Diagnostics.Debug.Assert(IsValid); 

            if (_lastNode.IsEmpty)
            {
                System.Diagnostics.Debug.Assert(findex == 0); 
                return _thisNode.Position;
            } 
 
            System.Diagnostics.Debug.Assert((findex >= _index - 1) && (findex <= _index));
 
            if (DoubleUtil.AreClose(findex, (double)_index))
            {
                //
                // we're being asked for this exact point 
                // if we don't return it here, our algorithm
                // below doesn't work 
                // 
                return _thisNode.Position;
            } 

            //
            // get the spare change to the left of the decimal point
            // eg turn 2.75 into .75 
            //
            double floor = Math.Floor(findex); 
            findex = findex - floor; 

            double xDiff = (_thisNode.Position.X - _lastNode.Position.X) * findex; 
            double yDiff = (_thisNode.Position.Y - _lastNode.Position.Y) * findex;

            //
            // return the previous point plus the delta's 
            //
            return new Point(   _lastNode.Position.X + xDiff, 
                                _lastNode.Position.Y + yDiff); 
        }
 
        #endregion

        #region Fields
 
        // Internal objects created for particular rendering
        private StrokeNodeOperations    _operations; 
 
        // Node's index on the stroke spine
        private int             _index; 

        // This and the previous node data that used by the StrokeNodeOperations object to build
        // and/or hit-test the contour of the node/segment
        private StrokeNodeData  _thisNode; 
        private StrokeNodeData  _lastNode;
 
        // Calculating of the connecting quadrangle is not a cheap operations, therefore, 
        // first, it's computed only by request, and second, once computed it's cached in the StrokeNode
        private bool            _isQuadCached; 
        private Quad            _connectingQuad;

        // Is the current stroke node the last node?
        private bool _isLastNode; 

        #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