Сохранять относительные позиции над MultiScaleImage - PullRequest
2 голосов
/ 16 октября 2010

Я реализовал следующее приложение Silverlight после просмотра этой инструкции , вот мой код:

public partial class MainPage : UserControl
{
    private Point lastMousePos = new Point();
    private double zoom = 1;
    private Point lastMouseLogicaPos = new Point();
    private Point lastMouseViewPort = new Point();
    private bool duringDrag = false;
    private bool duringOpen = false;
    private List<Dot> dots = new List<Dot>();
    private bool addDot = false;

    public MainPage()
    {
        InitializeComponent();

        this.MouseMove += delegate(object sender, MouseEventArgs e)
        { this.lastMousePos = e.GetPosition(this.ZoomImage); };

        ZoomImage.MouseWheel += new MouseWheelEventHandler(ZoomImage_MouseWheel);
        this.ZoomImage.UseSprings = false;
    }

    private void ZoomImage_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        double newzoom = zoom;

        if (e.Delta > 0)
        { newzoom /= 1.3; }
        else
        { newzoom *= 1.3; }

        Point logicalPoint = this.ZoomImage.ElementToLogicalPoint(this.lastMousePos);
        this.ZoomImage.ZoomAboutLogicalPoint(zoom / newzoom, logicalPoint.X, logicalPoint.Y);

        zoom = newzoom;
        e.Handled = true;
    }

    private void ZoomImage_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        lastMouseLogicaPos = e.GetPosition(LayoutRoot);
        lastMouseViewPort = this.ZoomImage.ViewportOrigin;

        foreach (var dot in this.dots)
        { dot.LastMouseLogicPos = e.GetPosition(LayoutRoot); }

        if (!this.addDot)
        { duringDrag = true; }
    }

    private void ZoomImage_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        if (this.addDot)
        {
            Dot dot = new Dot(this.lastMouseLogicaPos.X, this.lastMouseLogicaPos.Y) 
                                { Name = "Dot" + (this.dots.Count + 1).ToString() };

            this.dots.Add(dot);
            this.DotCanvas.Children.Add(dot);
        }
        else
        { duringDrag = false; }
    }

    private void ZoomImage_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
    {
        if (duringDrag)
        {
            double zoomFactor = 1 / this.ZoomImage.ViewportWidth;
            Point newPoint = lastMouseViewPort;
            Point thisMouseLogicalPos = e.GetPosition(LayoutRoot);
            newPoint.X += (lastMouseLogicaPos.X - thisMouseLogicalPos.X) / (this.ZoomImage.ActualWidth * zoomFactor);
            newPoint.Y += (lastMouseLogicaPos.Y - thisMouseLogicalPos.Y) / (this.ZoomImage.ActualWidth * zoomFactor);
            this.ZoomImage.ViewportOrigin = newPoint;

            foreach (var dot in this.dots)
            {
                Point dotLogicPoint = this.ZoomImage.ElementToLogicalPoint(new Point(dot.X, dot.Y));
                thisMouseLogicalPos = e.GetPosition(LayoutRoot);

                dotLogicPoint.X -= (dot.LastMouseLogicPos.X - thisMouseLogicalPos.X) / ((1 / 1.8) * this.ZoomImage.ViewportWidth);
                dotLogicPoint.Y -= (dot.LastMouseLogicPos.Y - thisMouseLogicalPos.Y) / (this.ZoomImage.ActualWidth * this.ZoomImage.ViewportWidth);

                dot.X = (this.ZoomImage.LogicalToElementPoint(locLogicPoint).X);
                dot.Y = (this.ZoomImage.LogicalToElementPoint(locLogicPoint).Y);
            }
        }
    }

    private void ZoomImage_ImageOpenSucceeded(object sender, System.Windows.RoutedEventArgs e)
    { duringOpen = true; }

    private void ZoomImage_MotionFinished(object sender, System.Windows.RoutedEventArgs e)
    {
        if (duringOpen)
        { duringOpen = false; }
    }

    private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        this.addDot = !this.addDot;

        if (this.addDot)
        { this.btnAddDot.Content = "Click on Image"; }
        else
        { this.btnAddDot.Content = "Add Dot"; }
    }
}

С этим я могу масштабировать и панорамировать MultiScaleImage и добавлять свой собственный объект Dot вхолст DotCanvas.Вот XAML:

<UserControl x:Class="DeepZoomSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" Width="800" Height="600">

<Grid x:Name="LayoutRoot" Background="Black" Margin="0,0,-98,-86">

    <MultiScaleImage x:Name="ZoomImage" Source="GeneratedImages/dzc_output.xml" 
                     Margin="8,8,0,0" MouseLeftButtonDown="ZoomImage_MouseLeftButtonDown" 
                     MouseLeftButtonUp="ZoomImage_MouseLeftButtonUp" MouseMove="ZoomImage_MouseMove" ImageOpenSucceeded="ZoomImage_ImageOpenSucceeded" 
                     MotionFinished="ZoomImage_MotionFinished" Height="584" VerticalAlignment="Top" HorizontalAlignment="Left" Width="784"/>

    <Canvas x:Name="DotCanvas" HorizontalAlignment="Left" Height="584" Margin="8" VerticalAlignment="Top" Width="784" MouseLeftButtonUp="LocationCanvas_MouseLeftButtonUp"/>
    <Button x:Name="btnAddDot" Content="Add Location" HorizontalAlignment="Right" Height="44" Margin="0,0,24,24" VerticalAlignment="Bottom" Width="112" Click="Button_Click"/>

</Grid>

Теперь проблема в том, что, так как точки помещаются на холст над MultiScateImage (объект ZoomImage), когда я перемещаю и масштабирую, точки будутостаться на своем месте над холстом.В этом коде пропущено несколько попыток сохранить точки на месте, когда изображение панорамируется и масштабируется.

Вот изображение приложения, синие точки вокруг - мой пользовательский объект Dot:

alt text

Основной вопрос заключается в том, как я могу держать точки на их относительном месте над изображением, когда пользователь увеличивает и перемещает изображение.

Ответы [ 2 ]

1 голос
/ 20 октября 2010

Это сложно, но определенно можно сделать, я недавно сделал то же самое для аналогичного приложения.Я не буду лгать, мне потребовалось несколько часов, чтобы заставить его работать, поэтому приготовьтесь к тому, чтобы немного почесать голову.

В сущности, есть две вещи:

1.расположение точек в нужном месте

Математика - ваш друг здесь.Вам нужно будет создать несколько методов, которые будут транспонировать координаты на основе multiScaleImage в координаты вашего холста (т.е. области просмотра).

Во-первых, вы должны будете глубоко понять ViewPortOrigin и ViewPortWidth ( this * 1012)* это очень хорошее начало).У них есть пара предостережений (например, я помню, что viewPortHeight нужно умножить на коэффициент изображения, чтобы получить фактическое значение - или что-то похожее).

Чтобы указать вам на решение: вы будетедолжны вычесть viewPortOrigin и умножить / разделить на viewPortWidth.Если вы терпеливы (и удачливы ;-)) сегодня вечером, я посмотрю на свой проект и выложу некоторый код, но будет хорошо, если вы действительно поймете эти параметры - иначе будет сложно отладить и устранить неполадки.

Что-то, что помогло мне понять, что происходит, - это поместить несколько текстовых блоков и отобразить viewportWidth / Origin / etc.все время во время навигации по multiscaleImage.

edit: вам повезло, я запомнил это, так что вот код, который должен помочь.Опять же, я предлагаю вам не просто копировать и вставлять без понимания, поскольку вы не доберетесь до этого далеко.

private Point CanvasToDeepZoom(MultiScaleImage msi, Point absoluteInsideCanvas)
{
    // the only non-logical (to me) step: viewportOrigin.Y must be multiplied by the aspectRatio
    var ViewportHeight = msi.ViewportWidth * msi.AspectRatio * msi.ActualHeight / msi.ActualWidth;

    var relativeToCanvas = new Point(
        absoluteInsideCanvas.X / msi.ActualWidth,
        absoluteInsideCanvas.Y / msi.ActualHeight);

    return new Point(
        msi.ViewportOrigin.X + msi.ViewportWidth * relativeToCanvas.X,
        msi.ViewportOrigin.Y * msi.AspectRatio + ViewportHeight * relativeToCanvas.Y);
}


private Point DeepZoomToCanvas(MultiScaleImage msi, Point relativeInsideDeepZoom)
{
    var ViewportHeight = msi.ViewportWidth * msi.AspectRatio * msi.ActualHeight / msi.ActualWidth;

    var relativeToCanvas = new Point(
        (relativeInsideDeepZoom.X - msi.ViewportOrigin.X) / msi.ViewportWidth,
        (relativeInsideDeepZoom.Y - msi.ViewportOrigin.Y * msi.AspectRatio) / ViewportHeight);

    return new Point(
        relativeToCanvas.X * msi.ActualWidth,
        relativeToCanvas.Y * msi.ActualHeight);
 }

2.синхронизация точек во время анимации масштабирования и панорамирования.

Основная идея состоит в том, чтобы зациклить анимацию длиной 0 секунд, которая постоянно обновляет положение точек в течение всей продолжительности масштабирования / панорамирования (1,5 секунды).если я правильно помню).техника очень хорошо объяснена здесь .В этом блоге вы также найдете другие полезные ресурсы для вашей конкретной проблемы.

0 голосов
/ 24 апреля 2012

Если вы видите Deep Zoom Composer, он также позволяет вам устанавливать области гиперссылок и имеет шаблоны для генерации проектов с исходным кодом. Таким образом, точки могут быть самими изображениями (используя коллекцию изображений, так что вы даже можете включать / выключать субизображения [SubMultiScaleImage, если я хорошо помню, это класс] программно) и иметь гиперссылки на них, и MultiScaleImage может обрабатывать коллекцию и сообщать вам, когда гиперссылки нажимаются (см. сгенерированный код)

...