HitTestWithGeometryDrawingContextWalker.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 / System / Windows / Media / HitTestWithGeometryDrawingContextWalker.cs / 1305600 / HitTestWithGeometryDrawingContextWalker.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// File: HitTestWithGeometryDrawingContextWalker.cs 
//
// Description: The implementation of HitTestWithGeometryDrawingContextWalker, 
//              used to perform hit tests geometry on renderdata. 
//
// History: 
//  06/20/2005 : michka - Carved it out of HitTestDrawingWalker.cs
//
//---------------------------------------------------------------------------
 
using MS.Internal;
using System; 
using System.ComponentModel; 
using System.Collections;
using System.Collections.Generic; 
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows; 
using System.Windows.Media;
using System.Windows.Media.Media3D; 
using System.Windows.Media.Animation; 
using System.Windows.Media.Composition;
using System.Windows.Media.Imaging; 

namespace System.Windows.Media
{
    ///  
    /// HitTestDrawingContextWalker - a DrawingContextWalker which will perform a point or
    /// geometry based hit test on the contents of a render data. 
    ///  
    internal class HitTestWithGeometryDrawingContextWalker : HitTestDrawingContextWalker
    { 
        /// 
        /// Constructor
        /// 
        ///  Geometry - the geometry to hit test, in local coordinates.  
        internal HitTestWithGeometryDrawingContextWalker(PathGeometry geometry)
        { 
            // The caller should pre-cull if the geometry is null. 
            Debug.Assert(geometry != null);
 
            _geometry = geometry;
            _currentTransform = null;
            _currentClip = null;
            _intersectionDetail = IntersectionDetail.NotCalculated; 
        }
 
 
        /// 
        /// IsHit Property - Returns true if geometry intersected the drawing instructions. 
        /// 
        internal override bool IsHit
        {
            get 
            {
                return (_intersectionDetail != IntersectionDetail.Empty && 
                        _intersectionDetail != IntersectionDetail.NotCalculated); 
            }
        } 

        internal override IntersectionDetail IntersectionDetail
        {
            get 
            {
                if (_intersectionDetail == IntersectionDetail.NotCalculated) 
                { 
                    return IntersectionDetail.Empty;
                } 
                else
                {
                    return _intersectionDetail;
                } 
            }
        } 
 
        #region Private helper classes
 
        private class ModifierNode
        {
        }
 
        private class TransformModifierNode : ModifierNode
        { 
            public TransformModifierNode(Transform transform) {_transform = transform;} 
            public Transform _transform;
        } 

        private class ClipModifierNode : ModifierNode
        {
            public ClipModifierNode(Geometry clip) {_clip = clip;} 
            public Geometry _clip;
        } 
 
        #endregion Private helper classes
 
        #region Static Drawing Context Methods

        /// 
        ///     DrawGeometry - 
        ///     Draw a Geometry with the provided Brush and/or Pen.
        ///     If both the Brush and Pen are null this call is a no-op. 
        ///  
        /// 
        ///     The Brush with which to fill the Geometry. 
        ///     This is optional, and can be null, in which case no fill is performed.
        /// 
        /// 
        ///     The Pen with which to stroke the Geometry. 
        ///     This is optional, and can be null, in which case no stroke is performed.
        ///  
        ///  The Geometry to fill and/or stroke.  
        public override void DrawGeometry(
            Brush brush, 
            Pen pen,
            Geometry geometry)
        {
            if ((geometry == null) || geometry.IsEmpty()) 
            {
                return; 
            } 

            Geometry testedGeometry; 

            // Transform if so prescribed
            if ((_currentTransform != null) && !_currentTransform.IsIdentity)
            { 
                testedGeometry = geometry.GetTransformedCopy(_currentTransform);
            } 
            else 
            {
                testedGeometry = geometry; 
            }

            // Clip, if so prescribed
            if (_currentClip != null) 
            {
                testedGeometry = Geometry.Combine( 
                    testedGeometry, 
                    _currentClip,
                    GeometryCombineMode.Intersect, 
                    null);  // transform
            }

            if (brush != null) 
            {
                AccumulateIntersectionDetail(testedGeometry.FillContainsWithDetail(_geometry)); 
            } 

            // If we have a pen and we haven't yet hit, try the widened geometry. 
            if ((pen != null) && !_contains)
            {
                AccumulateIntersectionDetail(testedGeometry.StrokeContainsWithDetail(pen, _geometry));
            } 

            // If we've hit, stop walking. 
            if (_contains) 
            {
                StopWalking(); 
            }
        }

 
        /// 
        /// Draw a GlyphRun. 
        ///  
        /// Foreground brush to draw GlyphRun with. 
        ///  The GlyphRun to draw.  
        /// 
        /// This call is illegal if this object has already been closed or disposed.
        /// 
        public override void DrawGlyphRun(Brush foregroundBrush, GlyphRun glyphRun) 
        {
            if (glyphRun != null) 
            { 
                // The InkBoundingBox + the Origin produce the true InkBoundingBox.
                Rect rectangle = glyphRun.ComputeInkBoundingBox(); 

                if (!rectangle.IsEmpty)
                {
                    rectangle.Offset((Vector)glyphRun.BaselineOrigin); 
                    DrawGeometry(Brushes.Black, null /* pen */, new RectangleGeometry(rectangle));
                } 
            } 
        }
 
        /// 
        ///     PushClip -
        ///     Push a clip region, which will apply to all drawing primitives until the
        ///     corresponding Pop call. 
        /// 
        ///  The Geometry to which we will clip.  
        public override void PushClip( 
            Geometry clipGeometry)
        { 
            // Intersect the new clip with the current one

            // 1st case:- No change, keep the old clip
            // 2nd case:- No matter what, the intersection will also be empty 
            if ((clipGeometry == null)
                || 
                ((_currentClip!=null) && (_currentClip.IsEmpty()))) 
            {
                clipGeometry = _currentClip; 
            }
            else
            {
                // Transform the clip new if so prescribed 
                if ((_currentTransform != null) && !_currentTransform.IsIdentity)
                { 
                    clipGeometry = clipGeometry.GetTransformedCopy(_currentTransform); 
                }
 
                // Intersect it with the current clip
                if (_currentClip != null)
                {
                    clipGeometry = Geometry.Combine( 
                        _currentClip,
                        clipGeometry, 
                        GeometryCombineMode.Intersect, 
                        null);  // Transform
                } 
            }

            // Push the previous clip on the stack
            PushModifierStack(new ClipModifierNode(_currentClip)); 

            _currentClip = clipGeometry; 
        } 

        ///  
        ///     PushOpacityMask -
        ///     Push an opacity mask
        /// 
        ///  
        ///     The opacity mask brush
        ///  
        public override void PushOpacityMask(Brush brush) 
        {
            // Opacity mask does not affect hit testing, but requires a place-holder on the stack 
            PushModifierStack(null);
        }

        ///  
        ///     PushOpacity -
        ///     Push an opacity which will blend the composite of all drawing primitives added 
        ///     until the corresponding Pop call. 
        /// 
        ///  
        ///     The opacity with which to blend - 0 is transparent, 1 is opaque.
        /// 
        public override void PushOpacity(
            Double opacity) 
        {
            // Opacity does not affect hit testing, but requires a place-holder on the stack 
            PushModifierStack(null); 
        }
 
        /// 
        ///     PushTransform -
        ///     Push a Transform which will apply to all drawing operations until the corresponding
        ///     Pop. 
        /// 
        ///  The Transform to push.  
        public override void PushTransform( 
            Transform transform)
        { 
            // Combine the new transform with the current one
            if ((transform == null) || transform.IsIdentity)
            {
                // The new transform does not change the existing one 
                transform = _currentTransform;
            } 
            else if ((_currentTransform != null) && !_currentTransform.IsIdentity) 
            {
                // Both the current transform and the new one are nontrivial, combine them 
                Matrix combined = _currentTransform.Value * transform.Value;
                transform = new MatrixTransform(combined);
            }
 
            // Push the previous transform on the stack
            PushModifierStack(new TransformModifierNode(_currentTransform)); 
 
            _currentTransform = transform;
        } 


        /// 
        ///     PushGuidelineSet - 
        ///     Push a set of guidelines which should be applied
        ///     to all drawing operations until the 
        ///     corresponding Pop. 
        /// 
        ///  The GuidelineSet to push.  
        public override void PushGuidelineSet(
            GuidelineSet guidelines)
        {
            // GuidelineSet does not affect hit testing, but requires a place-holder on the stack 
            PushModifierStack(null);
        } 
 

        ///  
        ///     PushGuidelineY1 -
        ///     Explicitly push one horizontal guideline.
        /// 
        ///  The coordinate of leading guideline.  
        internal override void PushGuidelineY1(
            Double coordinate) 
        { 
            // GuidelineSet does not affect hit testing, but requires a place-holder on the stack
            PushModifierStack(null); 
        }


        ///  
        ///     PushGuidelineY2 -
        ///     Explicitly push a pair of horizontal guidelines. 
        ///  
        /// 
        ///     The coordinate of leading guideline. 
        /// 
        /// 
        ///     The offset from leading guideline to driven guideline.
        ///  
        internal override void PushGuidelineY2(
            Double leadingCoordinate, 
            Double offsetToDrivenCoordinate) 
        {
            // GuidelineSet does not affect hit testing, but requires a place-holder on the stack 
            PushModifierStack(null);
        }

 
        /// 
        /// Pop 
        ///  
        public override void Pop()
        { 
            // We must have a modifier stack and it must not be empty.
            Debug.Assert(_modifierStack != null);
            Debug.Assert(_modifierStack.Count > 0);
 
            object currentModifier = _modifierStack.Pop();
 
            if (currentModifier is TransformModifierNode) 
            {
                _currentTransform = ((TransformModifierNode)currentModifier)._transform; 

                // Since the drawing context starts out with no transform and no clip,
                // the first element pushed on the stack will always be null.
                Debug.Assert((_modifierStack.Count > 0) || (_currentTransform == null)); 
            }
            else if (currentModifier is ClipModifierNode) 
            { 
                _currentClip = ((ClipModifierNode)currentModifier)._clip;
 
                // Since the drawing context starts out with no transform and no clip,
                // the first element pushed on the stack will always be null.
                Debug.Assert((_modifierStack.Count > 0) || (_currentClip == null));
            } 
            else
            { 
                Debug.Assert(currentModifier == null); 
            }
        } 

        #endregion Static Drawing Context Methods

        #region Private Methods 

 
        ///  
        /// AccumulateIntersectionDetail - accepts a new IntersectionDetail which is the result
        /// of "drawingCommandGeometry.FillContainsWithDetail(hitTestGeometry)" and updates 
        /// the current _intersectionDetail, setting _contains as appropriate.
        /// 
        /// 
        ///   The IntersectionDetail from hit-testing the current node. 
        /// 
        private void AccumulateIntersectionDetail(IntersectionDetail intersectionDetail) 
        { 
            // Note that:
            // * "FullyContains" means that the target node contains the hit test-geometry, 
            // * "FullyInside" means that the target node is fully inside the hit-test geometry

            // The old result cannot be FullyContain, because that would have
            // triggered a StopWalk and we wouldn't be here 

            Debug.Assert(_intersectionDetail != IntersectionDetail.FullyContains); 
 
            // The new result cannot be NotCalculated, because we just
            // calculated! 

            Debug.Assert(intersectionDetail != IntersectionDetail.NotCalculated);

            // The current _intersectionDetail is computed from its old value and the 
            // new result according the the following table:
 
            //     \ old   + 
            //  New \      + NotCalc     | Empty       | Intersects  | FullyInside      There
            // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++      is 
            // Empty       + Empty       | Empty       | Intersects  | Intersects        no
            // ------------+-------------------------------------------------------    Contains
            // Intersects  + Intersects  | Intersects  | Intersects  | Intersects      column
            // ------------+-------------------------------------------------------     (see 
            // Contains    + Contains    | Contains    | Contains    | Contains        assertion
            // ------------+--------------------------------------------------------     above) 
            // FullyInside + FullInside  | Intersects  | Intersects  | FullyInside 

            if (_intersectionDetail == IntersectionDetail.NotCalculated) 
                // This is the first node
            {
                _intersectionDetail = intersectionDetail;
                // Takes care of the first column. 
            }
            else if (intersectionDetail == IntersectionDetail.FullyInside 
                // This node is fully inside the hit geometry -- 
                &&
                _intersectionDetail != IntersectionDetail.FullyInside) 
                //  -- but we have already encountered a previous node that was not fully inside
            {
                _intersectionDetail = IntersectionDetail.Intersects;
 
                // Taking care of the second-to-left bottom cell
            } 
            else if (intersectionDetail == IntersectionDetail.Empty 
                // This node does not touch the hit geometry --
                && 
                _intersectionDetail != IntersectionDetail.Empty)
                //  -- but we have already encountered a previous node that was touched
            {
                _intersectionDetail = IntersectionDetail.Intersects; 

                // Taking care of the third and fourth cells in the first row 
            } 
            else
            { 
                // Accept the new result as is
                _intersectionDetail = intersectionDetail;

                // Taking care of the second and third row and the diagonal 
            }
 
            if (_intersectionDetail == IntersectionDetail.FullyContains) 
            {
                // The hit geometry is fully contained in the visual, so signal a StopWalk 
                _contains = true;
            }

        } 

        private void PushModifierStack(ModifierNode modifier) 
        { 
            // Push the old modifier on the stack
            if (_modifierStack == null) 
            {
                _modifierStack = new Stack();
            }
 
            _modifierStack.Push(modifier);
        } 
 
        #endregion Private Methods
 
        #region Private Fields

        // The geometry with which we are hit-testing
        private PathGeometry _geometry; 

        // The stack of previous values of transfrom/clip 
        private Stack _modifierStack; 

        // The current transform 
        private Transform _currentTransform;

        // The current clip
        private Geometry _currentClip; 

        // This keeps track of the details of a geometry hit test. 
        private IntersectionDetail _intersectionDetail; 

        #endregion Private Fields 
    }
}


// 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