/// <summary>
        /// Returns the intersection info for the given rayHitResult.  Intersection info
        /// only exists for an InteractiveModelVisual3D, so if an InteractiveModelVisual3D
        /// is not hit, then the return value is null.
        /// </summary>
        /// <param name="rayHitResult"></param>
        /// <returns>
        /// Returns ClosestIntersectionInfo if an InteractiveModelVisual3D is hit, otherwise
        /// returns null.
        /// </returns>
        private ClosestIntersectionInfo GetIntersectionInfo(RayHitTestResult rayHitResult)
        {
            ClosestIntersectionInfo isectInfo = null;

            // try to cast to a RaymeshGeometry3DHitTestResult
            RayMeshGeometry3DHitTestResult rayMeshResult = rayHitResult as RayMeshGeometry3DHitTestResult;

            if (rayMeshResult != null)
            {
                // see if we hit an InteractiveVisual3D
                InteractiveVisual3D imv3D = rayMeshResult.VisualHit as InteractiveVisual3D;
                if (imv3D != null)
                {
                    // we can now extract the mesh and visual for the object we hit
                    MeshGeometry3D geom   = rayMeshResult.MeshHit;
                    UIElement      uiElem = imv3D.InternalVisual;

                    if (uiElem != null)
                    {
                        // pull the barycentric coordinates of the intersection point
                        double vertexWeight1 = rayMeshResult.VertexWeight1;
                        double vertexWeight2 = rayMeshResult.VertexWeight2;
                        double vertexWeight3 = rayMeshResult.VertexWeight3;

                        // the indices in to where the actual intersection occurred
                        int index1 = rayMeshResult.VertexIndex1;
                        int index2 = rayMeshResult.VertexIndex2;
                        int index3 = rayMeshResult.VertexIndex3;

                        // texture coordinates of the three vertices hit
                        // in the case that no texture coordinates are supplied we will simply
                        // treat it as if no intersection occurred
                        if (geom.TextureCoordinates != null &&
                            index1 < geom.TextureCoordinates.Count &&
                            index2 < geom.TextureCoordinates.Count &&
                            index3 < geom.TextureCoordinates.Count)
                        {
                            Point texCoord1 = geom.TextureCoordinates[index1];
                            Point texCoord2 = geom.TextureCoordinates[index2];
                            Point texCoord3 = geom.TextureCoordinates[index3];

                            // get the final uv values based on the barycentric coordinates
                            Point finalPoint = new Point(texCoord1.X * vertexWeight1 +
                                                         texCoord2.X * vertexWeight2 +
                                                         texCoord3.X * vertexWeight3,
                                                         texCoord1.Y * vertexWeight1 +
                                                         texCoord2.Y * vertexWeight2 +
                                                         texCoord3.Y * vertexWeight3);

                            // create and return a valid intersection info
                            isectInfo = new ClosestIntersectionInfo(finalPoint,
                                                                    uiElem,
                                                                    imv3D);
                        }
                    }
                }
            }

            return(isectInfo);
        }
        /// <summary>
        /// This function sets the passed in uiElem as the hidden visual, and aligns
        /// it so that the point the uv coordinates map to on the visual are located
        /// at the same location as mousePos.
        /// </summary>
        /// <param name="uiElem">The UIElement that should be the hidden visual</param>
        /// <param name="uv">The uv coordinates on that UIElement that should be aligned with mousePos</param>
        /// <param name="mousePos">The mouse location</param>
        /// <param name="scaleHiddenVisual">Whether to scale the visual in addition to moving it</param>
        /// <returns></returns>
        private bool UpdateHiddenVisual(ClosestIntersectionInfo isectInfo, Point mousePos, bool scaleHiddenVisual)
        {
            bool   needsMouseReSync = false;
            double newOffsetX, newOffsetY;

            // compute positioning information
            if (isectInfo != null)
            {
                UIElement uiElem = isectInfo.UIElementHit;

                // set our UIElement to be the one passed in
                if (_hiddenVisual.Child != uiElem)
                {
                    // we need to replace the old one with this new one
                    UIElement prevVisual = _hiddenVisual.Child;

                    // clear out uiElem from any of our hidden visuals
                    if (_oldHiddenVisual.Child == uiElem)
                    {
                        _oldHiddenVisual.Child = null;
                    }
                    if (_oldKeyboardFocusVisual.Child == uiElem)
                    {
                        _oldKeyboardFocusVisual.Child = null;
                    }

                    // also clear out prevVisual
                    if (_oldHiddenVisual.Child == prevVisual)
                    {
                        _oldHiddenVisual.Child = null;
                    }
                    if (_oldKeyboardFocusVisual.Child == prevVisual)
                    {
                        _oldKeyboardFocusVisual.Child = null;
                    }

                    // depending on whether or not it has focus, do two different things, either
                    // use the _oldKeyboardFocusVisual or the _oldHiddenVisual
                    Decorator _oldVisToUse = null;
                    if (prevVisual != null && prevVisual.IsKeyboardFocusWithin)
                    {
                        _oldVisToUse = _oldKeyboardFocusVisual;
                    }
                    else
                    {
                        _oldVisToUse = _oldHiddenVisual;
                    }

                    // now safely link everything up
                    _hiddenVisual.Child = uiElem;
                    _oldVisToUse.Child  = prevVisual;

                    needsMouseReSync = true;
                }

                Point ptOnVisual = TextureCoordsToVisualCoords(isectInfo.PointHit, _hiddenVisual);
                newOffsetX = mousePos.X - ptOnVisual.X;
                newOffsetY = mousePos.Y - ptOnVisual.Y;
            }
            else
            {
                // because we didn't interesect with anything, we need to move the hidden visual off
                // screen so that it can no longer be interacted with
                newOffsetX = ActualWidth + 1;
                newOffsetY = ActualHeight + 1;
            }

            // compute the scale needed
            double newScale;

            if (scaleHiddenVisual)
            {
                newScale = Math.Max(Viewport3D.RenderSize.Width,
                                    Viewport3D.RenderSize.Height);
            }
            else
            {
                newScale = 1.0;
            }

            // do the actual positioning
            needsMouseReSync |= PositionHiddenVisual(newOffsetX, newOffsetY, newScale, mousePos);

            return(needsMouseReSync);
        }
        /// <summary>
        /// Returns the intersection info for the given rayHitResult.  Intersection info
        /// only exists for an InteractiveModelVisual3D, so if an InteractiveModelVisual3D
        /// is not hit, then the return value is null.
        /// </summary>
        /// <param name="rayHitResult"></param>
        /// <returns>
        /// Returns ClosestIntersectionInfo if an InteractiveModelVisual3D is hit, otherwise
        /// returns null.
        /// </returns>
        private ClosestIntersectionInfo GetIntersectionInfo(RayHitTestResult rayHitResult)
        {
            ClosestIntersectionInfo isectInfo = null;

            // try to cast to a RaymeshGeometry3DHitTestResult
            RayMeshGeometry3DHitTestResult rayMeshResult = rayHitResult as RayMeshGeometry3DHitTestResult;
            if (rayMeshResult != null)
            {
                // see if we hit an InteractiveVisual3D
                InteractiveVisual3D imv3D = rayMeshResult.VisualHit as InteractiveVisual3D;
                if (imv3D != null)
                {
                    // we can now extract the mesh and visual for the object we hit
                    MeshGeometry3D geom = rayMeshResult.MeshHit;
                    UIElement uiElem = imv3D.InternalVisual;
                
                    if (uiElem != null)
                    {
                        // pull the barycentric coordinates of the intersection point
                        double vertexWeight1 = rayMeshResult.VertexWeight1;
                        double vertexWeight2 = rayMeshResult.VertexWeight2;
                        double vertexWeight3 = rayMeshResult.VertexWeight3;

                        // the indices in to where the actual intersection occurred
                        int index1 = rayMeshResult.VertexIndex1;
                        int index2 = rayMeshResult.VertexIndex2;
                        int index3 = rayMeshResult.VertexIndex3;

                        // texture coordinates of the three vertices hit
                        // in the case that no texture coordinates are supplied we will simply
                        // treat it as if no intersection occurred
                        if (geom.TextureCoordinates != null &&
                            index1 < geom.TextureCoordinates.Count &&
                            index2 < geom.TextureCoordinates.Count &&
                            index3 < geom.TextureCoordinates.Count)
                        {
                            Point texCoord1 = geom.TextureCoordinates[index1];
                            Point texCoord2 = geom.TextureCoordinates[index2];
                            Point texCoord3 = geom.TextureCoordinates[index3];

                            // get the final uv values based on the barycentric coordinates
                            Point finalPoint = new Point(texCoord1.X * vertexWeight1 +
                                                         texCoord2.X * vertexWeight2 +
                                                         texCoord3.X * vertexWeight3,
                                                         texCoord1.Y * vertexWeight1 +
                                                         texCoord2.Y * vertexWeight2 +
                                                         texCoord3.Y * vertexWeight3);

                            // create and return a valid intersection info
                            isectInfo = new ClosestIntersectionInfo(finalPoint,
                                                                    uiElem,
                                                                    imv3D);
                        }
                    }
                }
            }

            return isectInfo;
        }
        /// <summary>
        /// Finds the point in edges that is closest ot the mouse position.  Updates closestIntersectionInfo
        /// with the results of this calculation
        /// </summary>
        /// <param name="mousePos">The mouse position</param>
        /// <param name="edges">The edges to test against</param>
        /// <param name="imv3DHit">The model that has the visual on it with capture</param>
        private void FindClosestIntersection(Point mousePos, List <HitTestEdge> edges, InteractiveVisual3D imv3DHit)
        {
            double closestDistance     = Double.MaxValue;
            Point  closestIntersection = new Point(); // the uv of the closest intersection

            // Find the closest point to the mouse position
            for (int i = 0; i < edges.Count; i++)
            {
                Vector v1 = mousePos - edges[i]._p1Transformed;
                Vector v2 = edges[i]._p2Transformed - edges[i]._p1Transformed;

                Point  currClosest;
                double distance;

                // calculate the distance from the mouse position to this edge
                // The closest distance can be computed by projecting v1 on to v2.  If the
                // projectiong occurs between _p1Transformed and _p2Transformed, then this is the
                // closest point.  Otherwise, depending on which side it lies, it is either _p1Transformed
                // or _p2Transformed.
                //
                // The projection equation is given as: (v1 DOT v2) / (v2 DOT v2) * v2.
                // v2 DOT v2 will always be positive.  Thus, if v1 DOT v2 is negative, we know the projection
                // will occur before _p1Transformed (and so it is the closest point).  If (v1 DOT v2) is greater
                // than (v2 DOT v2), then we have gone passed _p2Transformed and so it is the closest point.
                // Otherwise the projection gives us this value.
                //
                double denom = v2 * v2;
                if (denom == 0)
                {
                    currClosest = edges[i]._p1Transformed;
                    distance    = v1.Length;
                }
                else
                {
                    double numer = v2 * v1;
                    if (numer < 0)
                    {
                        currClosest = edges[i]._p1Transformed;
                    }
                    else
                    {
                        if (numer > denom)
                        {
                            currClosest = edges[i]._p2Transformed;
                        }
                        else
                        {
                            currClosest = edges[i]._p1Transformed + (numer / denom) * v2;
                        }
                    }

                    distance = (mousePos - currClosest).Length;
                }

                // see if we found a new closest distance
                if (distance < closestDistance)
                {
                    closestDistance = distance;

                    if (denom != 0)
                    {
                        closestIntersection = ((currClosest - edges[i]._p1Transformed).Length / Math.Sqrt(denom) *
                                               (edges[i]._uv2 - edges[i]._uv1)) + edges[i]._uv1;
                    }
                    else
                    {
                        closestIntersection = edges[i]._uv1;
                    }
                }
            }

            if (closestDistance != Double.MaxValue)
            {
                UIElement uiElemWCapture = (UIElement)Mouse.Captured;
                UIElement uiElemOnMesh   = imv3DHit.InternalVisual;

                Rect  contBounds     = VisualTreeHelper.GetDescendantBounds(uiElemWCapture);
                Point ptOnVisual     = TextureCoordsToVisualCoords(closestIntersection, uiElemOnMesh);
                Point ptRelToCapture = uiElemOnMesh.TransformToDescendant(uiElemWCapture).Transform(ptOnVisual);

                // we want to "ring" around the outside so things like buttons are not pressed
                // this code here does that - the +BUFFER_SIZE and -BUFFER_SIZE are to give a bit of a
                // buffer for any numerical issues
                if (ptRelToCapture.X <= contBounds.Left + 1)
                {
                    ptRelToCapture.X -= BUFFER_SIZE;
                }
                if (ptRelToCapture.Y <= contBounds.Top + 1)
                {
                    ptRelToCapture.Y -= BUFFER_SIZE;
                }
                if (ptRelToCapture.X >= contBounds.Right - 1)
                {
                    ptRelToCapture.X += BUFFER_SIZE;
                }
                if (ptRelToCapture.Y >= contBounds.Bottom - 1)
                {
                    ptRelToCapture.Y += BUFFER_SIZE;
                }

                Point finalVisualPoint = uiElemWCapture.TransformToAncestor(uiElemOnMesh).Transform(ptRelToCapture);

                _closestIntersectInfo = new ClosestIntersectionInfo(VisualCoordsToTextureCoords(finalVisualPoint, uiElemOnMesh),
                                                                    imv3DHit.InternalVisual,
                                                                    imv3DHit);
            }
        }
        /// <summary>
        /// This function sets the passed in uiElem as the hidden visual, and aligns
        /// it so that the point the uv coordinates map to on the visual are located
        /// at the same location as mousePos.
        /// </summary>
        /// <param name="uiElem">The UIElement that should be the hidden visual</param>
        /// <param name="uv">The uv coordinates on that UIElement that should be aligned with mousePos</param>
        /// <param name="mousePos">The mouse location</param>
        /// <param name="scaleHiddenVisual">Whether to scale the visual in addition to moving it</param>
        /// <returns></returns>
        private bool UpdateHiddenVisual(ClosestIntersectionInfo isectInfo, Point mousePos, bool scaleHiddenVisual)
        {
            bool needsMouseReSync = false;
            double newOffsetX, newOffsetY;

            // compute positioning information
            if (isectInfo != null)
            {
                UIElement uiElem = isectInfo.UIElementHit;

                // set our UIElement to be the one passed in
                if (_hiddenVisual.Child != uiElem)
                {
                    // we need to replace the old one with this new one
                    UIElement prevVisual = _hiddenVisual.Child;

                    // clear out uiElem from any of our hidden visuals
                    if (_oldHiddenVisual.Child == uiElem) _oldHiddenVisual.Child = null;
                    if (_oldKeyboardFocusVisual.Child == uiElem) _oldKeyboardFocusVisual.Child = null;

                    // also clear out prevVisual
                    if (_oldHiddenVisual.Child == prevVisual) _oldHiddenVisual.Child = null;
                    if (_oldKeyboardFocusVisual.Child == prevVisual) _oldKeyboardFocusVisual.Child = null;

                    // depending on whether or not it has focus, do two different things, either
                    // use the _oldKeyboardFocusVisual or the _oldHiddenVisual
                    Decorator _oldVisToUse = null;
                    if (prevVisual != null && prevVisual.IsKeyboardFocusWithin)
                    {
                        _oldVisToUse = _oldKeyboardFocusVisual;
                    }
                    else
                    {
                        _oldVisToUse = _oldHiddenVisual;
                    }

                    // now safely link everything up
                    _hiddenVisual.Child = uiElem;
                    _oldVisToUse.Child = prevVisual;

                    needsMouseReSync = true;
                }

                Point ptOnVisual = TextureCoordsToVisualCoords(isectInfo.PointHit, _hiddenVisual);
                newOffsetX = mousePos.X - ptOnVisual.X;
                newOffsetY = mousePos.Y - ptOnVisual.Y;
            }
            else
            {
                // because we didn't interesect with anything, we need to move the hidden visual off
                // screen so that it can no longer be interacted with
                newOffsetX = ActualWidth + 1;
                newOffsetY = ActualHeight + 1;                
            }

            // compute the scale needed
            double newScale;
            if (scaleHiddenVisual)
            {
                newScale = Math.Max(Viewport3D.RenderSize.Width,
                                    Viewport3D.RenderSize.Height);
            }
            else
            {
                newScale = 1.0;
            }
            
            // do the actual positioning
            needsMouseReSync |= PositionHiddenVisual(newOffsetX, newOffsetY, newScale, mousePos); 
                      
            return needsMouseReSync;
        }
        /// <summary>
        /// Finds the point in edges that is closest ot the mouse position.  Updates closestIntersectionInfo
        /// with the results of this calculation
        /// </summary>
        /// <param name="mousePos">The mouse position</param>
        /// <param name="edges">The edges to test against</param>
        /// <param name="imv3DHit">The model that has the visual on it with capture</param>
        private void FindClosestIntersection(Point mousePos, List<HitTestEdge> edges, InteractiveVisual3D imv3DHit)
        {
            double closestDistance = Double.MaxValue;
            Point closestIntersection = new Point();  // the uv of the closest intersection

            // Find the closest point to the mouse position            
            for (int i=0; i<edges.Count; i++)
            {
                Vector v1 = mousePos - edges[i]._p1Transformed;
                Vector v2 = edges[i]._p2Transformed - edges[i]._p1Transformed;
                
                Point currClosest;
                double distance;

                // calculate the distance from the mouse position to this edge
                // The closest distance can be computed by projecting v1 on to v2.  If the
                // projectiong occurs between _p1Transformed and _p2Transformed, then this is the
                // closest point.  Otherwise, depending on which side it lies, it is either _p1Transformed
                // or _p2Transformed.  
                //
                // The projection equation is given as: (v1 DOT v2) / (v2 DOT v2) * v2.
                // v2 DOT v2 will always be positive.  Thus, if v1 DOT v2 is negative, we know the projection
                // will occur before _p1Transformed (and so it is the closest point).  If (v1 DOT v2) is greater
                // than (v2 DOT v2), then we have gone passed _p2Transformed and so it is the closest point.
                // Otherwise the projection gives us this value.
                //
                double denom = v2 * v2;
                if (denom == 0)
                {
                    currClosest = edges[i]._p1Transformed;
                    distance = v1.Length;
                }
                else
                {
                    double numer = v2 * v1;
                    if (numer < 0)
                    {
                        currClosest = edges[i]._p1Transformed;
                    }
                    else
                    {
                        if (numer > denom)
                        {
                            currClosest = edges[i]._p2Transformed;
                        }
                        else
                        {
                            currClosest = edges[i]._p1Transformed + (numer / denom) * v2;
                        }
                    }

                    distance = (mousePos - currClosest).Length; 
                }

                // see if we found a new closest distance
                if (distance < closestDistance)
                {
                    closestDistance = distance;
                    
                    if (denom != 0)
                    {
                        closestIntersection = ((currClosest - edges[i]._p1Transformed).Length / Math.Sqrt(denom) * 
                                               (edges[i]._uv2 - edges[i]._uv1)) + edges[i]._uv1;
                    }
                    else
                    {
                        closestIntersection = edges[i]._uv1;
                    }
                }                
            }

            if (closestDistance != Double.MaxValue)
            {
                UIElement uiElemWCapture = (UIElement)Mouse.Captured;
                UIElement uiElemOnMesh = imv3DHit.InternalVisual;
                
                Rect contBounds = VisualTreeHelper.GetDescendantBounds(uiElemWCapture);                
                Point ptOnVisual = TextureCoordsToVisualCoords(closestIntersection, uiElemOnMesh);
                Point ptRelToCapture = uiElemOnMesh.TransformToDescendant(uiElemWCapture).Transform(ptOnVisual);

                // we want to "ring" around the outside so things like buttons are not pressed
                // this code here does that - the +BUFFER_SIZE and -BUFFER_SIZE are to give a bit of a 
                // buffer for any numerical issues
                if (ptRelToCapture.X <= contBounds.Left + 1) ptRelToCapture.X -= BUFFER_SIZE;
                if (ptRelToCapture.Y <= contBounds.Top + 1) ptRelToCapture.Y -= BUFFER_SIZE;
                if (ptRelToCapture.X >= contBounds.Right - 1) ptRelToCapture.X += BUFFER_SIZE;
                if (ptRelToCapture.Y >= contBounds.Bottom - 1) ptRelToCapture.Y += BUFFER_SIZE;

                Point finalVisualPoint = uiElemWCapture.TransformToAncestor(uiElemOnMesh).Transform(ptRelToCapture);                
                
                _closestIntersectInfo = new ClosestIntersectionInfo(VisualCoordsToTextureCoords(finalVisualPoint, uiElemOnMesh), 
                                                                    imv3DHit.InternalVisual, 
                                                                    imv3DHit);                 
            }
        }
        /// <summary>
        /// Constructs the InteractiveViewport3D
        /// </summary>
        public Interactive3DDecorator()
            : base()
        {
            // keep everything within our bounds so that the hidden visuals are only
            // accessable over the Viewport3D
            ClipToBounds = true;

            // the offset of the hidden visual and the transform associated with it
            _offsetX = _offsetY = 0.0;
            _scale = 1;

            // set up the hidden visual transforms
            _hiddenVisTranslate = new TranslateTransform(_offsetX, _offsetY);
            _hiddenVisScale = new ScaleTransform(_scale, _scale);
            _hiddenVisTransform = new TransformGroup();
            _hiddenVisTransform.Children.Add(_hiddenVisScale);
            _hiddenVisTransform.Children.Add(_hiddenVisTranslate);

            // the layer that contains our moving visual
            _hiddenVisual = new Decorator();
            _hiddenVisual.Opacity = 0.0;
            _hiddenVisual.RenderTransform = _hiddenVisTransform;

            // where we store the previous hidden visual so that it can be in the tree
            // after it is removed so any state (i.e. mouse over) can be updated.
            _oldHiddenVisual = new Decorator();
            _oldHiddenVisual.Opacity = 0.0;

            // the keyboard focus visual
            _oldKeyboardFocusVisual = new Decorator();
            _oldKeyboardFocusVisual.Opacity = 0.0;

            // add all of the hidden visuals
            PostViewportChildren.Add(_oldHiddenVisual);
            PostViewportChildren.Add(_oldKeyboardFocusVisual);
            PostViewportChildren.Add(_hiddenVisual);

            // initialize other member variables to null
            _closestIntersectInfo = null;
            _lastValidClosestIntersectInfo = null;

            AllowDrop = true;
        }
        /// <summary>
        /// The HTResult function simply takes the intersection closest to the origin and
        /// and stores the intersection info for that closest intersection point.
        /// </summary>
        /// <param name="rawresult"></param>
        /// <returns></returns>
        private HitTestResultBehavior HTResult(System.Windows.Media.HitTestResult rawresult)
        {
            RayHitTestResult rayResult = rawresult as RayHitTestResult;
            HitTestResultBehavior hitTestResultBehavior = HitTestResultBehavior.Continue;

            // since we're hit testing a viewport3D we should be getting the ray hit test result back
            if (rayResult != null) {
                _closestIntersectInfo = GetIntersectionInfo(rayResult);
                hitTestResultBehavior = HitTestResultBehavior.Stop;
            }

            return hitTestResultBehavior;
        }
        /// <summary>
        /// Arranges the hidden visuals so that interactivity is achieved.
        /// </summary>
        /// <param name="mouseposition">The location of the mouse</param>
        /// <returns>Whether a mouse resynch is necessary</returns>
        private bool ArrangeHiddenVisual(Point mouseposition, bool scaleHiddenVisual)
        {
            bool needMouseResync = false;

            // get the underlying viewport3D we're enhancing
            Viewport3D viewport3D = Viewport3D;

            // if the viewport3D exists - perform a hit test operation on the underlying visuals
            if (viewport3D != null) {
                // set up the hit test parameters
                PointHitTestParameters pointparams = new PointHitTestParameters(mouseposition);
                _closestIntersectInfo = null;
                _mouseCaptureInHiddenVisual = _hiddenVisual.IsMouseCaptureWithin;

                // first hit test - this one attempts to hit a visible mesh if possible
                VisualTreeHelper.HitTest(viewport3D, InteractiveMV3DFilter, HTResult, pointparams);

                // perform capture positioning if we didn't hit anything and something has capture
                if (_closestIntersectInfo == null &&
                    _mouseCaptureInHiddenVisual &&
                    _lastValidClosestIntersectInfo != null) {
                    HandleMouseCaptureButOffMesh(_lastValidClosestIntersectInfo.InteractiveModelVisual3DHit,
                                                 mouseposition);
                }
                else if (_closestIntersectInfo != null) {
                    // save it for if we walk off the mesh and something has capture
                    _lastValidClosestIntersectInfo = _closestIntersectInfo;
                }

                // update the location if we have positioning information
                needMouseResync = UpdateHiddenVisual(_closestIntersectInfo,
                                                     mouseposition,
                                                     scaleHiddenVisual);
            }

            return needMouseResync;
        }