Mandelbrot Zoomer in WPF
Mandelbrot Zoomer in WPF
Written by Mike James   
Thursday, 23 July 2015
Article Index
Mandelbrot Zoomer in WPF
Plotting
Zooming
Where Next

Where next?

With the program as described above you can go exploring the Mandelbrot set to your heart’s content.

There’s a lot to see, but be warned areas that are mostly blue take a long time to plot because these take the iteration loop a full 1000 times around!

The fact that the computation takes so long makes it an ideal candidate for running on a thread other than the UI thread. You may notice that the UI freezes when the set is being computed and the only way to keep the application responsive is to use a worker thread.

When you are just trying to get it all working it is worth making the Image control small so that plots occur quickly.

You can add some additional features to make the program more usable – a plot interrupt button, for example, a pan, and so on..

 

mand2

There are lots of interesting areas to explore.

 

There is one small feature that I should warn you about before it wastes a lot of time when you try to “debug” the program. There are no limits on how far you can zoom in on any given region of the Mandelbrot set. You will discover that you can indeed use very high zooms, but sooner or later you will select a very small region and the result will not look fractal but “blocky”. No you haven’t discovered that there is a limit to the structure of the Mandelbrot set!

What is happening is that you have zoomed to the point where double precision variables aren’t accurate enough to represent the position of each point. The result is that you get the same answer for whole rectangular regions for the iteration and hence the blocky structure in the final plot. The solution to the problem is fairly simple – increase the precision - but this is easier said than done and you just hit another limit sooner or later. .

 

mand3

No not the end of fractals – you’ve just zoomed too far

 

Listing

using System;
using System.Collections.Generic;
using System.Linq;using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Numerics;

namespace MandelZoomer{
 public partial class MainWindow : Window    {       
  private Rect area = new Rect(
                 new Point(-2.4, -1.5),
                 new Point(0.8, 1.5));

  private Rectangle selection =
            new Rectangle()
           {
             Stroke = Brushes.Black,
             StrokeThickness = 1,
             Visibility = Visibility.Collapsed
           };

  private bool mousedown = false;
  private Point mousedownpos;

  public MainWindow()
  {
   InitializeComponent();
   image1.Source = drawSet(area);
   Canvas1.Children.Add(selection);
  }

  Int32 mandelbrot(Complex c)
  {
   Int32 count = 0;
   Complex z = Complex.Zero;
   while (count < 1000 && z.Magnitude < 4)
   {
    z = z * z + c;
    count++;
   }
   return count;
  }

  WriteableBitmap drawSet(Rect area)
  {
   int PixelHeight = (int)image1.Height;
   int PixelWidth = (int)image1.Width;
   WriteableBitmap wbmap = new WriteableBitmap(
           PixelWidth,
           PixelHeight,
           96, 96,
           PixelFormats.Bgra32,
           null);

   int BytesPerPixel = wbmap.Format.BitsPerPixel / 8;
   byte[] pixels = new byte[PixelHeight *
                            PixelWidth *
                             BytesPerPixel];
   int s = PixelWidth * BytesPerPixel;
   double xscale = (area.Right - area.Left)
                               / PixelWidth;
   double yscale = (area.Top - area.Bottom)
                              / PixelHeight;
   for (int i = 0; i < pixels.Length;
                        i += BytesPerPixel)
   {
    int py = i / s;
    int px = i % s / BytesPerPixel;
    double x = area.Left + px * xscale;
    double y = area.Top - py * yscale;
    Complex c = new Complex(x, y);
    int count = mandelbrot(c);
    Color C = colorMap(count);
    pixels[i] = C.B;
    pixels[i + 1] = C.G;
    pixels[i + 2] = C.R;
    pixels[i + 3] = C.A;
   }
   wbmap.WritePixels(new Int32Rect(0, 0,
      PixelWidth, PixelHeight), pixels, s, 0);
   return wbmap;
  }

  Color colorMap(int count)
  {
   Color C = new Color();
   C.B = (byte)(count / 100 * 25);
   count = count % 100;
   C.G = (byte)(count / 10 * 25);
   C.R = (byte)(count % 10 * 25);
   C.A = 255;
   return C;
  }

  private void button1_Click(object sender,
                            RoutedEventArgs e)
  {
   area = new Rect(new Point(-2.4, -1.5),
                       new Point(0.8, 1.5));
   image1.Source = drawSet(area);
  }


  private void Canvas1_MouseLeftButtonDown(
        object sender, MouseButtonEventArgs e)
  {
   mousedown = true;
   mousedownpos = e.GetPosition(Canvas1);
   Canvas.SetLeft(selection, mousedownpos.X);
   Canvas.SetTop(selection, mousedownpos.Y);
   selection.Width = 0;
   selection.Height = 0;
   selection.Visibility = Visibility.Visible;
  }

  private void Canvas1_MouseMove(
           object sender, MouseEventArgs e)
  {
   if (mousedown)
   {
    Point mousepos = e.GetPosition(Canvas1);
    Vector diff = mousepos - mousedownpos;
    Point TopLeft = mousedownpos;
    if (diff.X < 0)
    {
     TopLeft.X = mousepos.X;
     diff.X = -diff.X;
    }
    if (diff.Y < 0)
     TopLeft.Y = mousepos.Y;
     diff.Y = -diff.Y;
    }
    selection.Width = diff.X;
    selection.Height = diff.Y;
    Canvas.SetLeft(selection, TopLeft.X);
    Canvas.SetTop(selection, TopLeft.Y);
   }
  }

  private void Canvas1_MouseLeftButtonUp(
         object sender, MouseButtonEventArgs e)
  {
   mousedown = false;
   selection.Visibility = Visibility.Collapsed;
   double xscale = (area.Right - area.Left) /
                                 image1.Width;
   double yscale = (area.Top - area.Bottom) /
                                 image1.Height;
   Point TopLeft = new Point(area.Left +
    Canvas.GetLeft(selection) * xscale,
    area.Top - Canvas.GetTop(selection)
                                * yscale);
   Point BottomRight = TopLeft + new Vector(
     selection.Width * xscale,
            -selection.Height * yscale);
   area = new Rect(TopLeft, BottomRight);
   image1.Source = drawSet(area);
  }
 }
}

 

 

To access the code for this project, once you have registered,  click on CodeBin.

 mand2

 

Banner


BitmapSource: WPF Bitmaps

Although bitmap graphics are not a main focus of WPF it does offer some useful facilities



WPF The Easy 3D Way

WPF provides a full 3D graphics experience without the need to master DirectX. It really is the easy 3D approach.



Bitmap Coding and Metatdata in WPF


Having looked at working with raw pixel data we turn our attention to formattted image files and how to code and decode both the pixel data and the meta data they contain.

 



Routed Events

Routed events were new in .NET 3.5 but there is still plenty to discover about using them. We look at bubbling and tunneling and how to create your own routed even and how WPF changes the underlying m [ ... ]



Drawing Bitmaps – DrawingImage and DrawingVisual

 

WPF provides multiple ways to convert vector drawings to bitmaps. Find out how DrawingImage and DrawingVisual work and when to use which. On the way we look at how to create 2D vector drawings.


Other Articles

 

blog comments powered by Disqus

 

<ASIN:1507707614>

<ASIN:1449320104>

<ASIN:161729134X>

<ASIN:1449343503>

<ASIN:1430224819>

al

l the code



Last Updated ( Thursday, 23 July 2015 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2015 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.