locked
Optimized Zoom RRS feed

  • Question

  • Did anyone ever translate this code into VB.NET Code? There are a lot of very interesting functions for Silverlight multiscale images, like the "optimized zoom animation" used in the preview output of the new Deep Zoom Composer.

    I tried to translate it, but it's a lot of work and I didn't get far the first try.

    Found it here: http://www.java2s.com/Open-Source/ASP.NET/WPF/slaf/Microsoft/LiveLabs/DeepZoom/MultiScaleImage.Extensions.cs.htm

     

    // <copyright file="MultiScaleImage.Extensions.cs" company="Microsoft Corporation">
    // Copyright (c) 2010 Microsoft Corporation All Rights Reserved
    // </copyright>
    // <author>Microsoft Live Labs</author>
    // <email></email>
    // <date>2010-05-15</date>
    // <summary>MultiScaleImage extension methods</summary>
    
    namespace Microsoft.LiveLabs.DeepZoom
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Media.Animation;
    
        /// <summary>
        /// Deep Zoom arange mode
        /// </summary>
        public enum ArrangeMode
        {
            /// <summary>
            /// Square arrangement
            /// </summary>
            Square,
    
            /// <summary>
            /// Tight arrangement
            /// </summary>
            Tight,
    
            /// <summary>
            /// Spiral arrangement
            /// </summary>
            Spiral,
    
            /// <summary>
            /// Filmstrip arrangement
            /// </summary>
            Filmstrip,
        }
    
        /// <summary>
        /// Neighbors for the sub image
        /// </summary>
        public class Neighbors
        {
            /// <summary>
            /// Gets or sets the neighbor above
            /// </summary>
            public MultiScaleSubImage Up { get; set; }
    
            /// <summary>
            /// Gets or sets the neighbor below
            /// </summary>
            public MultiScaleSubImage Down { get; set; }
    
            /// <summary>
            /// Gets or sets the left neighbor
            /// </summary>
            public MultiScaleSubImage Left { get; set; }
    
            /// <summary>
            /// Gets or sets teh right neighbor
            /// </summary>
            public MultiScaleSubImage Right { get; set; }
        }
    
        /// <summary>
        /// MultiScale image extensions
        /// </summary>
        public static class MultiScaleImageExtensions
        {
            #region Fields
            /// <summary>
            /// Clamp Margin
            /// </summary>
            private const double ClampMargin = .1;
    
            private const int SquareMargin = 15;
            private const int SquareItemSize = 128;
    
            private const int TightMargin = 25;
            private const int TightItemSize = 128;
    
            private const int SpiralItemSize = 128;
    
            private const int FilmstripMargin = 15;
            private const int FilmstripItemSize = 128;
            #endregion
    
            #region Behaviors
    
            ////public static void ApplyDefaultBehaviors( this MultiScaleImage msi )
            ////{
            ////    new DeepZoomBehaviors( msi, false );
            ////    _multiScaleImageAspectRatio = GetAspectRatio(msi);
            ////}
    
            ////public static void ApplyMouseWheelBehavior( this MultiScaleImage msi )
            ////{
            ////    new DeepZoomBehaviors( msi, true );
            ////}
    
            #endregion
    
            #region Scene
    
            /// <summary>
            /// Get the viewport bounds
            /// </summary>
            /// <param name="msi">the multiscale image</param>
            /// <returns>the bounding rectangle</returns>
            public static Rect GetViewportBounds(this MultiScaleImage msi)
            {
                Point tl = msi.ElementToLogicalPoint(new Point(0, 0));
                Point br = msi.ElementToLogicalPoint(new Point(msi.ActualWidth, msi.ActualHeight));
                return new Rect(tl.X, tl.Y, br.X - tl.X, br.Y - tl.Y);
            }
    
            /// <summary>
            /// Set the viewport bounds
            /// </summary>
            /// <param name="msi"></param>
            /// <param name="newBounds"></param>
            public static void SetViewportBounds(this MultiScaleImage msi, Rect newBounds)
            {
                msi.ViewportOrigin = new Point(newBounds.Left, newBounds.Top);
                msi.ViewportWidth = newBounds.Width;
            }
    
           [...]
    
    
            #endregion
    
            /// <summary>
            /// Implements an "optimal" animated zoom/pan path between two view rectangles.
            /// </summary>
            /// <remarks>
            /// Based on the paper "Smooth and efficient zooming and panning" by Jarke j. van Wijk and Wim A.A. Nuij
            /// </remarks>
            private class ViewportAnimation : DependencyObject
            {
                #region Fields
                public static readonly DependencyProperty ProgressProperty = DependencyProperty.RegisterAttached("Progress", typeof(double), typeof(ViewportAnimation), new PropertyMetadata(0.0, new PropertyChangedCallback(delegate(DependencyObject o, DependencyPropertyChangedEventArgs e) { ((ViewportAnimation)o).ProgressChanged(e); })));
    
                // Rho controls the amount of zooming used.  sqrt(2) is an empirically determined value suggested in the paper.
                private static readonly double p = Math.Sqrt(2);
    
                // average speed of motion in units of (size of viewport)/second.
                private static readonly double velocity = 1;
    
                // c0 and c1 are the positions of the center of the viewport at the beginning and end of the animation, respectively.
                // direction = c1 - c0.
                private Point c0, c1, direction;
    
                // w is the width of the viewport.
                // u is the distance the center of the viewport has traveled.
                // b and r are constants used in the paper's equations.
                // S is the perceptual distance traveled in units of (size of viewport), including the effects of zooming.
                private double w0, w1, u0, u1, b0, b1, r0, r1, S;
                private MultiScaleImage multiScaleImage;
                #endregion
    
                #region Constructors
                /// <summary>
                /// Initializes a new instance of the ViewportAnimation class.
                /// </summary>
                /// <param name="multiScaleImage">the multiscale image</param>
                /// <param name="newViewportBounds">the new viewport bounds</param>
                public ViewportAnimation(MultiScaleImage multiScaleImage, Rect newViewportBounds)
                {
                    this.multiScaleImage = multiScaleImage;
    
                    // Calculate all the constants for the equations given in the paper.
                    newViewportBounds = multiScaleImage.CenterRectInViewport(newViewportBounds);
                    Rect initialViewportBounds = new Rect(multiScaleImage.ViewportOrigin, new Point(multiScaleImage.ViewportOrigin.X + multiScaleImage.ViewportWidth, multiScaleImage.ViewportOrigin.Y + multiScaleImage.ViewportWidth * multiScaleImage.ActualHeight / multiScaleImage.ActualWidth));
    
                    this.c0 = new Point(
                        initialViewportBounds.Left + initialViewportBounds.Width / 2,
                        initialViewportBounds.Top + initialViewportBounds.Height / 2);
    
                    this.c1 = new Point(
                        newViewportBounds.Left + newViewportBounds.Width / 2,
                        newViewportBounds.Top + newViewportBounds.Height / 2);
    
                    this.direction = new Point(this.c1.X - this.c0.X, this.c1.Y - this.c0.Y);
    
                    this.w0 = initialViewportBounds.Width;
                    this.w1 = newViewportBounds.Width;
    
                    this.u0 = 0;
                    this.u1 = Math.Sqrt(this.direction.X * this.direction.X + this.direction.Y * this.direction.Y);
    
                    // See page 5 of the paper for these equations.
                    this.b0 = (this.w1 * this.w1 - this.w0 * this.w0 + p * p * p * p * this.u1 * this.u1) / (2 * this.w0 * p * p * this.u1);
                    this.b1 = (this.w1 * this.w1 - this.w0 * this.w0 - p * p * p * p * this.u1 * this.u1) / (2 * this.w1 * p * p * this.u1);
    
                    this.r0 = Math.Log(-this.b0 + Math.Sqrt(this.b0 * this.b0 + 1));
                    this.r1 = Math.Log(-this.b1 + Math.Sqrt(this.b1 * this.b1 + 1));
    
                    this.S = (this.r1 - this.r0) / p;
                    if (Double.IsNaN(this.S))
                    {
                        return;
                    }
    
                    // Construct a Silverlight storyboard to drive the animation.
                    // The storyboard will drive the Progress property of this object from 0 to 1,
                    // which will in turn set ViewportWidth and ViewportOrigin appropriately.
                    // Unlike the paper, we use a spline to accelerate and decelerate instead of 
                    // maintaining a constant velocity.
                    TimeSpan duration = TimeSpan.FromSeconds(this.S / velocity);
                    DoubleAnimationUsingKeyFrames da = new DoubleAnimationUsingKeyFrames();
                    ////da.Duration = new Duration(duration);
                    SplineDoubleKeyFrame kf = new SplineDoubleKeyFrame();
                    kf.KeySpline.ControlPoint1 = new Point(.7, 1);
                    kf.KeySpline.ControlPoint2 = new Point(.7, 1);
                    kf.Value = 1;
                    kf.KeyTime = KeyTime.FromTimeSpan(duration);
                    da.KeyFrames.Add(kf);
                    Storyboard.SetTarget(da, this);
                    Storyboard.SetTargetProperty(da, new PropertyPath(ProgressProperty));
                    Storyboard sb = new Storyboard();
                    sb.Children.Add(da);
                    sb.Completed += delegate(object o, EventArgs e) { multiScaleImage.UseSprings = true; };
                    multiScaleImage.UseSprings = false;
                    sb.Begin();
                }
                #endregion
    
                #region Properties
                /// <summary>
                /// Gets or sets the progress
                /// </summary>
                public double Progress
                {
                    get
                    {
                        return (double)GetValue(ProgressProperty);
                    }
    
                    set
                    {
                        SetValue(ProgressProperty, value);
                    }
                }
                #endregion
    
                #region Methods
    
                /// <summary>
                /// Progress changed event handler
                /// </summary>
                /// <param name="e"></param>
                public void ProgressChanged(DependencyPropertyChangedEventArgs e)
                {
                    // See page 5 of the paper for these equations.
                    // s goes from 0 to S as Progress goes from 0 to 1
                    double s = ((double)e.NewValue) * this.S;
    
                    // Now compute u(s) and w(s)
                    double u = this.w0 / (p * p) * Math.Cosh(this.r0) * Math.Tanh(p * s + this.r0) - this.w0 / (p * p) * Math.Sinh(this.r0) + this.u0;
                    double w = this.w0 * Math.Cosh(this.r0) / Math.Cosh(p * s + this.r0);
    
                    this.multiScaleImage.ViewportWidth = w;
                    this.multiScaleImage.SetViewportCenter(new Point(this.c0.X + this.direction.X * u / this.u1, this.c0.Y + this.direction.Y * u / this.u1));
                }
    
                #endregion
            }
        }
    }
    


     

    Thursday, January 19, 2012 6:51 PM