保持 MultiScaleImage 上的相对位置

发布于 2024-09-27 20:56:32 字数 5724 浏览 6 评论 0原文

在看到此说明后,我实现了以下 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

主要问题是,如何才能当用户缩放和平移时,我将点保留在图像上的相对位置。

I implemented the following Silverlight app after seeing this instructions, here's my code:

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"; }
    }
}

With this I can zoom and pan on a MultiScaleImage and add my custom Dot object to the DotCanvas canvas. Here's the 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>

Now, the problem is that, since the Dots are placed in a canvas that's over the MultiScateImage (ZoomImage object) when I pan and zoom the Dots will stay in their respective place over the canvas. This code has some missed tries trying to keep the Dots on place while the image is panning and zooming.

Here's an image of the app, the blue dots around are my custom Dot object:

alt text

The main question is, how can I keep the Dots in their relative place over the image while the user zooms and pans.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

千紇 2024-10-04 20:56:32

这很棘手,但绝对可以做到,我最近对类似的应用程序做了同样的事情。我不会撒谎,我花了几个小时才让它工作,所以准备好一些头痛吧。

基本上涉及两件事:

1。将点放置在正确的位置

数学是你的朋友。您必须创建一些方法将基于 multiScaleImage 的坐标转置为画布(即视口)坐标。

首先,您必须深入了解 ViewPortOrigin 和 ViewPortWidth (这是一个非常好的开始)。他们有一些警告(例如,我似乎记得 viewPortHeight 必须乘以图像比率才能获得实际值 - 或类似的值)。

为了向您指出解决方案:您必须减去 viewPortOrigin 并乘/除以 viewPortWidth。如果您有耐心(并且幸运;-))今晚我将查看我的项目并发布一些代码,但如果您真正理解这些参数,那就太好了 - 否则调试和故障排除将会很棘手。

帮助我理解正在发生的事情的方法是放置一些文本块并显示视口宽度/原点/等。在浏览多尺度图像时始终如此。

编辑:你很幸运,我记得这一点 - 所以这里有一些应该有所帮助的代码。再次强调,我建议您不要只是复制和复制。不理解就粘贴,因为你不会走那么远。

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 秒) 。
该技术解释得很好 这里。在该博客中,您还可以找到针对您的特定问题的其他有用资源。

It is tricky but definitely can be done, I recently did the same for a similar application. I won't lie, it took me several hours to get it working, so prepare for some headscratching.

There are basically two things involved:

1. positioning the dots in the right place

Math is your friend here. You will have to create some methods that transpose the multiScaleImage-based coordinates to your canvas (i.e. the viewport) coordinates.

First, you'll have to understand in depth ViewPortOrigin and ViewPortWidth (this is a very good start). They have a couple of caveats (for ex. I seem to recall that viewPortHeight has to be multiplied by the image ratio to get the actual value -or something similar).

To point you towards the solution: you will have to subtract viewPortOrigin and multiply/divide by viewPortWidth. If you are patient (and lucky ;-) ) tonight I'll look at my project and post some code, but it's good if you really understand those parameters -otherwise it will be tricky to debug and troubleshoot.

Something that helped me a lot understanding what was going on was to put some textblocks around and display viewportWidth/Origin/etc. all the time while navigating the multiscaleImage.

edit: you are lucky, I remembered this -so here is some code that should help. Again, I suggest you don't just copy & paste without understanding as you won't get that far.

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. keeping the dots in sync during zoom and pan animation.

The basic idea is to loop a 0 seconds-long animation that keeps updating your points position for the whole duration of the zoom/pan (1.5 seconds if I remember correctly).
the technique is explained very well here. In that blog you will also find other useful resources for your particular problem.

如日中天 2024-10-04 20:56:32

如果您看到 Deep Zoom Composer,它还允许您设置超链接区域,并且还具有用于生成源代码项目的模板。因此,这些点可以是图像本身(使用图像集合,以便您甚至可以以编程方式打开/关闭子图像 [SubMultiScaleImage,如果我记得很好的话是类]),并在它们上有超链接,让 MultiScaleImage 处理集合并让您知道何时单击超链接(查看生成的代码)

If you see Deep Zoom Composer, it also allow you to set hyperlink areas and has templates to generate sourcecode projects too. So the dots could be images themselves (using an image collection so that you can even turn on/off subimages [SubMultiScaleImage if I remember well is the class] programmatically) and have hyperlinks on them and let MultiScaleImage handle the collection and let you know when hyperlinks are clicked (see the generated code)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文