2016-04-19 20 views
0

Ich kann auf das Mandelbrot-Set zoomen, solange sich die Maus nicht bewegt, nachdem das Zoomen begonnen hat. Ich habe versucht, ein normalisiertes Delta (neue Koordinate - alte Koordinate) * (oldzoom) zu berechnen, aber was passiert, ist, dass das Bild an einen neuen Ort springt. Ich habe dieses Problem schon einmal gesehen. Ich kämpfe hier mehr, weil ich das Delta der Mausposition irgendwie in den -2,2-Koordinatenraum des Mandelbrot-Sets umwandeln muss.Punkt-Zoom auf Mandelbrot-Set in C# - Es funktioniert, außer wenn die Maus bewegt wurde

Hier ist mein Code. Wichtig ist die GetZoomPoint-Methode und dann die Codezeilen, die x0 und y0 definieren. Außerdem verwende ich die Range-Klasse, um Werte von einem Bereich zum anderen zu skalieren. I WAS mit deltaTrans (das ist die Sache, über die ich früher gesprochen habe, wo ich das Maus-Delta mit der alten Skala normalisiere).

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; 
     } 
    } 
} 
+0

Was ist 'GetZoomPoint'? Googeln zeigt diese Frage an der Spitze. –

+0

HAHA, ok ja, ich habe das ziemlich schlecht geplant. Das mache ich manchmal, um die Entwicklung kurzfristig zu beschleunigen. Um dies zu vereinfachen, gibt es globale Transformationen (Skalieren, Übersetzen usw.), auf die diese zugreifen können. Außerhalb dieser Klasse werden der globale Maßstab und die Übersetzung über das Mausrad aktualisiert. GetZoomPoint versucht, die globale Skalierung und die Übersetzung zu verwenden, um herauszufinden, wo im Mandelbrot-Set der Fokus gesetzt werden sollte. Die generierte Bitmap wird im Wesentlichen als GPU-Textur behandelt, aber da ich die Basis Draw überschreibe, gelten die Transformationen nicht. Ist das klar, was GetZoomPoint macht? – applejacks01

+0

Vielleicht eine bessere Frage, die ich stellen könnte, ist, angesichts einer gewünschten Fenstergröße, einer Mauskoordinate (die sich jederzeit bewegen kann) und eines gewünschten Maßstabs, wie kann ich in die Mandelbrotmenge (oder irgendeinen beliebigen zweidimensionalen Begrenzungsraum) hineinzoomen – applejacks01

Antwort

0

Ich refaktorierte meinen Code und fand auch eine Lösung für dieses Problem. 2 große Gewinne in einem. Ok, also habe ich eine Lösung in CodeProject gefunden, die in C# geschrieben war und die ich problemlos an mein Projekt anpassen konnte. Ich bin nicht sicher, warum ich das nicht bemerkt habe, als ich die Frage gestellt habe, aber was ich brauchte, um dieses Problem zu lösen, war, ein "Fenster" des Zooms zu erstellen und nicht in Form eines "Punktzooms" zu denken. Ja, selbst wenn ich direkt in einen Punkt zoomen möchte, ist dieser Punkt nur das Zentrum einer Art Fenster.

Hier ist die Methode, die ich erwarte, die Start-und End-mousedown Koordinaten (Bildschirmplatz) erwartet, und konvertiert die Mandelbrot-Fenstergröße entsprechend.

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 

    } 

Grundsätzlich was passiert ist, dass wir das Verhältnis zwischen der mandelFensterGröße in Abhängigkeit von der Bildschirmgröße berechnen wir zu zeichnen. Mit dieser Skala konvertieren wir im Grunde unsere Mousedown-Koordinaten in mandelbrot-Koordinaten (x1 * scaleX, usw.) und manipulieren die aktuellen Min- und Max-Koordinaten mit ihnen, wobei die Min-Werte als Drehpunkt verwendet werden.

Hier ist der Link zu dem Codeproject ich als Referenz verwendet: CodeProject link