C# 中 Mandelbrot 集的点缩放 - 它有效,除非鼠标移动

Point-Zoom on Mandelbrot Set in C# - It works, except when the mouse has moved

只要在缩放开始后鼠标不移动,我就可以在 Mandelbrot 集上进行缩放。我已经尝试计算归一化增量(新坐标 - 旧坐标)*(oldzoom),但发生的事情是图像似乎跳到一个新位置。我以前见过这个问题。我在这里更加挣扎,因为我必须以某种方式将鼠标位置增量转换回 Mandelbrot 集的 -2,2 坐标 space。

这是我的代码。重要的是 GetZoomPoint 方法,然后是定义 x0 和 y0 的代码行。此外,我使用范围 class 将值从一个范围缩放到另一个范围。我 WAS 使用 deltaTrans(这就是我之前谈论的事情,我用旧比例标准化鼠标增量)。

using OpenTK.Graphics.OpenGL;
using SpriteSheetMaker;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace Fractal.Fractal
{
    public class Mandelbrot : BaseTexture
    {
        private static Transform GlobalTransform = SpriteSheetMaker.Global.Transform;
        private static Vector3 GlobalScale = GlobalTransform.Scale;
        private static Vector3 GlobalTrans = GlobalTransform.Translation;
        private static Vector3 LastWindowPoint = null;
        private static Vector3 ZoomFactor = Vector3.ONE * 1.2f;
        private static Vector3 Displacement = Vector3.ZERO;
        private static int WindowSize = 100;

        public static Vector3 GetZoomPoint()
        {


            var zP = OpenGLHelpers.LastZoomPoint.Clone();
            if (LastWindowPoint == null)
            {
                LastWindowPoint = zP.Clone();
            }
            var delta = zP - LastWindowPoint;
            var oldZoom = GlobalScale / ZoomFactor;
            var deltaTrans = delta.XY * oldZoom.XY;
            var factor = ZoomFactor.Clone();

            Range xR = new Range(0, WindowSize);
            Range yR = new Range(0, WindowSize);
            Range complexRange = new Range(-2, 2);

            // Calculate displacement of zooming position.
            var dx = (zP.X - Displacement.X) * (factor.X - 1f);
            var dy = (zP.Y - Displacement.Y) * (factor.Y - 1f);
            // Compensate for displacement.
            Displacement.X -= dx;
            Displacement.Y -= dy;

            zP -= Displacement;
            var x = complexRange.ScaleValue(zP.X, xR);
            var y = complexRange.ScaleValue(zP.Y, yR);

            var rtn = new Vector3(x, y);

            LastWindowPoint = zP.Clone();

            return rtn;
        }
        public static Mandelbrot Generate()
        {
            var size = new Size(WindowSize, WindowSize);
            var radius = new Size(size.Width / 2, size.Height / 2);

            Bitmap bmp = new Bitmap(size.Width, size.Height);
            LockBitmap.LockBitmapUnsafe lbm = new LockBitmap.LockBitmapUnsafe(bmp);
            lbm.LockBits();


            var pt = Mandelbrot.GetZoomPoint();
            Parallel.For(0, size.Width, i =>
            {
                //  float x0 = complexRangeX.ScaleValue(i, xRange);
                float x0 = ((i - radius.Width) / GlobalScale.X) + pt.X;

                Parallel.For(0, size.Height, j =>
                 {
                     // float y0 = complexRangeY.ScaleValue(j, yRange);
                     float y0 = ((j - radius.Height) / GlobalScale.Y) + pt.Y;
                     float value = 0f;
                     float x = 0.0f;
                     float y = 0.0f;
                     int iteration = 0;
                     int max_iteration = 100;
                     while (x * x + y * y <= 4.0 && iteration < max_iteration)
                     {
                         float xtemp = x * x - y * y + x0;
                         y = 2.0f * x * y + y0;
                         x = xtemp;
                         iteration += 1;
                         if (iteration == max_iteration)
                         {
                             value = 255;
                             break;
                         }
                         else
                         {
                             value = iteration * 50f % 255f;
                         }
                     }

                     int v = (int)value;
                     lbm.SetPixel(i, j, new ColorLibrary.HSL(v / 255f, 1.0, 0.5).ToDotNetColor());
                 });
            });
            lbm.UnlockBits();
            var tex = new BaseTextureImage(bmp);
            var rtn = new Mandelbrot(tex);
            return rtn;
        }

        public override void Draw()
        {
            base._draw();
        }
        private Mandelbrot(BaseTextureImage graphic)
        {
            var topLeft = new Vector3(0, 1);
            var bottomLeft = new Vector3(0, 0);
            var bottomRight = new Vector3(1, 0);
            var topRight = new Vector3(1, 1);
            this.Vertices = new List<Vector3>()
            {
                topLeft,bottomLeft,bottomRight,topRight
            };
            this.Size.X = WindowSize;
            this.Size.Y = WindowSize;
            this.Texture2D = graphic;
        }
    }
}

我重构了我的代码,也找到了解决这个问题的办法。 2场大胜合二为一。好的,所以我在 CodeProject 上找到了一个用 C# 编写的解决方案,我很容易适应我的项目。我不确定为什么我在发布问题时没有意识到这一点,但我需要解决这个问题的是创建一个 'window' 的缩放而不是根据 'point zoom' 来思考。是的,即使我试图直接放大一个点,那个点也只是某种 window 的中心。

这是我的方法,它需要开始和结束 mousedown 坐标(屏幕 space),并相应地转换 mandelbrot 集 window 大小。

    public void ApplyZoom(double x0, double y0, double x1, double y1)
    {
        if (x1 == x0 && y0 == y1)
        {
            //This was just a click, no movement occurred
            return;
        }

        /*
            * XMin, YMin and XMax, YMax are the current extent of the set
            * mx0,my0 and mx1,my1 are the part we selected
            * do the math to draw the selected rectangle
            * */
        double scaleX, scaleY;

        scaleX = (XMax - XMin) / (float)BitmapSize;
        scaleY = (YMax - YMin) / (float)BitmapSize;
        XMax = (float)x1 * scaleX + XMin;
        YMax = (float)y1 * scaleY + YMin;
        XMin = (float)x0 * scaleX + XMin;
        YMin = (float)y0 * scaleY + YMin;

        this.Refresh(); // force mandelbrot to redraw

    }

基本上,发生的事情是我们计算 mandelbrot window 大小与我们正在绘制的屏幕大小之间的比率。然后,使用该比例,我们基本上将 mousedown 坐标转换为 mandelbrot set 坐标(x1*scaleX 等)并使用它们操作当前的最小和最大坐标,使用最小值作为轴心点。

这是我用作参考的 CodeProject 的 link:CodeProject link