2017-06-21 2 views
-1

J'essaie de trouver un moyen de zoomer sur mon jeu Mandelbrot sur le clic. Je l'ai donc quand je clique légèrement, mais il ne bouge pas le Mandelbrot en conséquence.Zoom sur Mandelbrot

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 
using System.Numerics; 

namespace Project_V2 
{ 
    public partial class FractalGen : Form 
    { 
     public double zoom = 2.4; 
     public FractalGen() 
     { 
      InitializeComponent(); 
     } 

     private void pictureBox1_Click(object sender, EventArgs e) 
     { 
      zoom -= 0.3; 
      Mandelbrot(); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      Mandelbrot(); 
     } 

     private void timer1_Tick(object sender, EventArgs e) 
     { 

     } 

     private void Mandelbrot() 
     { 
      Bitmap bm = new Bitmap(pictureBox1.Width, pictureBox1.Height); 
      DateTime StartT = DateTime.Now; 
      for (int x = 0; x < pictureBox1.Width; x++) 
      { 
       for (int y = 0; y < pictureBox1.Height; y++) 
       { 
        double a = (double)(x - (pictureBox1.Width/1.25))/(double)(pictureBox1.Width/zoom); 
        double b = (double)(y - (pictureBox1.Height/2))/(double)(pictureBox1.Height/zoom); 
        Complex C = new Complex(a, b); 
        Complex Z = new Complex(0, 0); 
        int u = 0; 
        do 
        { 
         u++; 
         Z = Z * Z; 
         Z = Z + C; 
         double Mag = Complex.Abs(Z); 
         if (Mag > 2.0) break; 
        } while (u < 255); 
        Color rgbInside = Color.FromArgb(0, 0, 0); 
        Color rgbOutside = Color.FromArgb(u >= 127 ? 255 : 2 * u, u >= 127 ? (u - 127) : 0, 0); 
        bm.SetPixel(x, y, u < 255 ? rgbOutside : rgbInside); 
       } 
      } 
      pictureBox1.Image = bm; 
      DateTime EndT = DateTime.Now; 
      string Time = Convert.ToString((EndT - StartT).TotalSeconds); 
      textBox1.Text = "Time Taken: " + Time + " Seconds"; 
     } 

     private void button1_Click_1(object sender, EventArgs e) 
     { 
      zoom = 2.4; 
      Mandelbrot(); 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      saveFileDialog1.ShowDialog(); 
     } 

     private void saveFileDialog1_FileOk(object sender, CancelEventArgs e) 
     { 
      string name = saveFileDialog1.FileName; 
      pictureBox1.Image.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Png); 
     } 
    } 
} 

Le code actuel divise la largeur et la hauteur de picturebox par une valeur, mais je veux qu'il en soit ainsi, il fait un zoom sur l'endroit où je clique. Comment puis-je redimensionner l'image par rapport à l'endroit où je clique?

+1

Vous devez récupérer et travailler avec le MousePosition. Cette réponse a la solution, même prêt pour une boîte d'image https://stackoverflow.com/questions/7055211/how-to-get-the-position-of-a-click –

+0

Oubliez la zone d'image et concentrez-vous sur les données! - Oh, je te l'ai déjà dit et tu l'as ignoré. Eh bien, bonne chance alors .. – TaW

+0

@TaW: _ "Je vous l'ai dit avant" _ - dans quel contexte? Y a-t-il une question supprimée où vous avez fourni cette information? Il serait utile de savoir quels conseils ont déjà été donnés au PO, de mieux comprendre ce qu'ils ont déjà entendu et ce qu'ils pourraient avoir de la difficulté à comprendre. –

Répondre

2

Il serait utile de comprendre les conseils qui vous ont déjà été prodigués. Je déduis de this comment qu'il existe probablement une question existante que vous avez posée puis supprimée, dans laquelle ce problème a été discuté. Sans savoir quelle aide a déjà été offerte et ce que vous étiez précisément incapable de comprendre, il est difficile de savoir comment présenter une nouvelle réponse.

Cela dit, il y a deux problèmes fondamentaux que vous devez résoudre pour obtenir le comportement que vous recherchez:

  1. RECENSER la souris est cliqué. Actuellement, vous utilisez l'événement Click, qui signale uniquement à votre code que le contrôle a été cliqué, pas il a été cliqué.
  2. Fixez votre code de rendu pour tenir compte des changements dans les limites de ce qui doit être dessiné. Actuellement vous associez toujours la gamme de pixels dans votre bitmap à la plage dans le plan complexe centré sur (-0,72, 0), en ajustant seulement la distance de ce centre est rendu (la
  3. variables zoom.

Pour répondre # 1, vous devez vous abonner à l'événement MouseClick.Cela va passer un objet MouseEventArgs à votre gestionnaire, qui a une propriété Location qui indique le point dans la zone cliente du contrôle qui a été effectivement cliqué (ie les coordonnées rapportées sont relatives à un origine en haut à gauche du contrôle)

Pour l'adresse 2, vous devez ajouter des variables à votre classe pour garder une trace de la position relative vouloir dessiner. Il y a beaucoup de façons de le faire, mais en restant avec votre convention actuelle de représenter le facteur "zoom" actuel comme la largeur et la hauteur de la zone rendue, il semble logique de suivre le centre de l'espace rendu, c.-à-d. emplacement de la souris a été cliqué, en termes de l'emplacement sur le plan complexe qui a été dessiné.

J'ai une forte objection aux programmes qui bloquent l'interface utilisateur pendant qu'ils font des tâches de longue durée. Donc, dans le processus de révision de votre exemple de code, je l'ai modifié afin qu'il rende l'image dans un thread de travail, et signale la progression du rendu en fournissant un message d'état sur un Label que j'ai ajouté au formulaire. (Cela impliquait de faire votre méthode Mandelbrot() une méthode async & hellip;. Je suis allé de l'avant et utilisé await lors de l'appel à supprimer les avertissements du compilateur, mais bien sûr, dans ce contexte, les await s ne sont pas strictement nécessaire)

Une chose que je ne pas était d'ajouter d'autres optimisations au code. Ceux-ci ont tendance à masquer les changements qui sont directement liés à votre question. Vous pouvez faire un certain nombre de choses à votre code pour améliorer les performances, en supprimant les calculs redondants ou en utilisant des relations mathématiques pour effectuer des calculs moins chers que la traduction littérale de l'algorithme de Mandelbrot.

Il y a beaucoup de choses sur Internet sur la façon de rendre des images Mandelbrot, donc vous pouvez regarder autour de vous pour trouver ce genre de conseil, une fois que vous avez compris l'idée de base. En outre, pour l'instant, avec seulement 255 itérations par pixel et l'image bitmap relativement petite, la performance n'est pas une considération sérieuse.

Voici la version de votre code, je suis venu avec:

public partial class Form1 : Form 
{ 
    double zoom = 2.4; 
    double centerX = -0.72, centerY = 0; 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private async void pictureBox1_MouseClick(object sender, MouseEventArgs e) 
    { 
     double minX = centerX - zoom/2, minY = centerY - zoom/2; 
     centerX = minX + (double)e.Location.X/pictureBox1.Width * zoom; 
     centerY = minY + (double)e.Location.Y/pictureBox1.Height * zoom; 
     zoom -= 0.3; 
     await Mandelbrot(); 
    } 

    private async void Form1_Load(object sender, EventArgs e) 
    { 
     await Mandelbrot(); 
    } 

    private async Task Mandelbrot() 
    { 
     IProgress<double> progress = new Progress<double>(x => label1.Text = $"{x * 100:0}% done"); 

     DateTime StartT = DateTime.UtcNow; 
     pictureBox1.Image = await Task.Run(() => _GenerateBitmap(progress)); 
     DateTime EndT = DateTime.UtcNow; 
     string Time = Convert.ToString((EndT - StartT).TotalSeconds); 
     textBox1.Text = "Time Taken: " + Time + " Seconds"; 
    } 

    private Bitmap _GenerateBitmap(IProgress<double> progress) 
    { 
     Bitmap bm = new Bitmap(pictureBox1.Width, pictureBox1.Height); 
     double minX = centerX - zoom/2, minY = centerY - zoom/2; 

     for (int x = 0; x < pictureBox1.Width; x++) 
     { 
      for (int y = 0; y < pictureBox1.Height; y++) 
      { 
       double a = minX + (double)x/pictureBox1.Width * zoom; 
       double b = minY + (double)y/pictureBox1.Height * zoom; 
       Complex C = new Complex(a, b); 
       Complex Z = new Complex(0, 0); 
       int u = 0; 
       do 
       { 
        u++; 
        Z = Z * Z; 
        Z = Z + C; 
        double Mag = Complex.Abs(Z); 
        if (Mag > 2.0) break; 
       } while (u < 255); 
       Color rgbInside = Color.FromArgb(0, 0, 0); 
       Color rgbOutside = Color.FromArgb(u >= 127 ? 255 : 2 * u, u >= 127 ? (u - 127) : 0, 0); 
       bm.SetPixel(x, y, u < 255 ? rgbOutside : rgbInside); 
      } 

      progress.Report((double)x/pictureBox1.Width); 
     } 

     return bm; 
    } 

    private async void button1_Click_1(object sender, EventArgs e) 
    { 
     zoom = 2.4; 
     await Mandelbrot(); 
    } 
} 
+0

Pourriez-vous expliquer ce que fait cette ligne de code? IProgress progress = new Progression (x => label1.Text = $ ("{x * 100: 0}% done"); –

+0

Cela crée une instance de 'Progress ', une classe générique utilisant 'double' dans ce Dans ce cas, le gestionnaire exécute automatiquement le gestionnaire qui lui est transmis sur ce thread UI Le gestionnaire dans ce cas est l'expression lambda qui reçoit la valeur d'état 'x' et la formate pour la présenter à l'écran en affectant En passant cette instance 'Progress ' (appelée 'IProgress ', l'interface de rapport de progression que 'Progress 'implémente) à la méthode qui fonctionne, elle peut indiquer la progression comme ça va. –