GDI Mandelbrot Set generator not working...

Pages: 12
I'm trying to create a Mandelbrot Set generator based on the pseudocode from the Wikipedia article. All it's doing is drawing black pixels in the whole client area (save for a few rows at the top).

Here's the relevant portion of the drawing code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
for(int vPos= cr.top; vPos < cr.bottom; vPos++)
{
	for(int hPos= cr.left; hPos < cr.right; hPos++)
	{
		COLORREF color= NULL;
					
		int x0= hPos,y0= vPos;
		int x= x0,y= y0;
		
		int iteration= 0;
		int maxIterations= 10;

		while((x*x) + (y*y) <= 4 && iteration < maxIterations)
		{
			double xTemp= x*x - y*y + x0;
			vPos= 2*x*y + y0;

			x= xTemp;

			iteration++;
		}

		if(iteration == maxIterations)
			color= RGB(0,0,0);
		else
			color= RGB(iteration,0,0);

		SetPixel(hdc,hPos,vPos,color);
	}
}


Yes, I know, I'm being bad and using SetPixel(), which is very slow, but at this point my brain would probably explode if I tried to do it using BitBlt(), ;). In fact, I'm not sure if that's even possible...

I have yet to understand what the simplest form of the Mandelbrot formula is (Wikipedia slams you with loads upon loads of Greek symbols and *shudder* calculus... DX ), so if someone could help me with the math, that would be cool, too.

I'm thinking it might be because I'm putting the procedure in the wrong place, but I don't know where else to put it!

Just for clarification, here's Wikipedia's pseudocode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
For each pixel on the screen do:
{
  x = x0 = x co-ordinate of pixel
  y = y0 = y co-ordinate of pixel

  iteration = 0
  max_iteration = 1000
 
  while ( x*x + y*y <= (2*2)  AND  iteration < max_iteration ) 
  {
    xtemp = x*x - y*y + x0
    y = 2*x*y + y0

    x = xtemp

    iteration = iteration + 1
  }
 
  if ( iteration == max_iteration ) 
  then 
    color = black
  else 
    color = iteration

  plot(x0,y0,color)
}
Last edited on
You have:
 
vPos= 2*x*y + y0;


You mean:
 
y= 2*x*y + y0;


I think SetPixelV is a little faster.
Last edited on
Oops... I guess I missed that little detail; thanks. Fixing it hasn't changed it much, though. All it did was fill in the missing rows at the top of the client rectangle.

As for SetPixelV() being faster than SetPixel(), according to MSDN, it's faster because "it does not need to return the color value of the point actually painted.", although I can't really see any visibly significant speed difference. Thanks for the suggestion, though; I might start using that more often (I don't think I'll really need the color from SetPixel's return value anytime soon, anyway, unless it's for some complex (and slow) graphics operation).

I think the problem is in my math (ugh!). Wikipedia's pseudocode is really vague about the variable declarations. I know it's supposed to be pseudocode, not a full example in C, but I'm talking about the way variables are supposed to be associated with each other. When they say "x = x0 = x coordinate of pixel", do they mean that you should declare a variable x0 that is equal to the x coordinate of the pixel, then declare one called x, and have it equal to x0, which is equal to the x coord of the pixel? Seems kinda redundant if you ask me.

Oh well. In the meantime, I'll try Googling around for some *simplified* equation for the Mandelbrot Set.

Although if anyone wants to pitch in with the math, that'd be very much appreciated.

EDIT: Found a pdf on the set, but it just gives me the same eq'n as what I got (in essence) from the pseudocode. Now I'm really stumped...
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/////////////////////////////////////////////////////////////////////////////
// CMandelbrotView message handlers
// --------------------------------------------------------------------------------------------------------
// CDrawItView::mandel_iterC
// --------------------------------------------------------------------------------------------------------
// Parameter : 	double x, y = Position in Gauss'schen Plain
//				int maxiter = maximum number of iterartions
// --------------------------------------------------------------------------------------------------------
// mandel_iterC() does the underlying/actual Mandelbrot-Calculation and returns the number of 
//  executed iterations
// --------------------------------------------------------------------------------------------------------
// Return-Value : int i = Number of Iteration Steps
// --------------------------------------------------------------------------------------------------------

int CMandelbrotView::mandel_iterC (double x, double y, int maxiter)
{
	// initialisiere temporäre Variablen
	double sx, sy, zx, zy;
	int i;

	zx = x;
	zy = y;
	sx = zx * zx;
	sy = zy * zy;
	i = 0;

	// calculate iterations
	while ((i <= maxiter) && (sx+sy<=4.0))
	{
		zy = (zx+zx)*zy + y;
		zx = sx - sy + x;
		sx = zx * zx;
		sy = zy * zy;
		i = i+1;
	}	

	// Gebe Anzahl der Iterationsschritte zurück
	return (i);
}


/////////////////////////////////////////////////////////////////////////////
// CMandelbrotView drawing

void CMandelbrotView::OnDraw(CDC* pDC)
{
	// Zugriff auf das Dokument erlangen
	CMandelbrotDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	// TODO: add draw code for native data here

	// Erzeuge Stift und speichere den bisher aktuellen
	CPen plotPen(PS_SOLID,1, RGB(0,0,0));
	CPen *pOldPen = pDC->SelectObject(&plotPen);

	// initialisiere temporäre Variablen
	long double dx, dy, x, y;
	int anzahl_gitterpunkte, iterationswert, aufloesung;
	int farbvorberechnung = (255 / pDoc->getMaxIter());
	POINT bildposition;
	CRect clientRect;
	GetClientRect(clientRect);

	// get resolution to be used
	aufloesung = (clientRect.Width() > clientRect.Height()) ? clientRect.Width() 
															: clientRect.Height();

	// number of of points to be calculated is resolution(aufloesung) + 1
	anzahl_gitterpunkte = aufloesung+1;

	// calculate value range of pixel
	dx = (pDoc->getXEndPosition()-pDoc->getXStartPosition()) / long double(aufloesung);
	dy = (pDoc->getYEndPosition()-pDoc->getYStartPosition()) / long double(aufloesung);

	// setze die Y-Werte auf START
	y = pDoc->getYStartPosition();
	bildposition.y = 1;

	// machine all rows
	while (bildposition.y <= anzahl_gitterpunkte)
	{
		// setze die X-Werte auf START
		x = pDoc->getXStartPosition();
		bildposition.x = 1;
  
	  // machine every pixel
      while (bildposition.x <= anzahl_gitterpunkte)
	  {
		  // calculate iteration-value
          iterationswert = mandel_iterC(x,y,pDoc->getMaxIter());

		  // wurde die Iterationsgrenze überschritten?
          if (iterationswert >= pDoc->getMaxIter()) 
		  {
			  // ja, zeichne einen schwarzen Punkt
			  pDC->SetPixel(bildposition, RGB(0,0,0));
		  }
		  else
		  {
			  // befinden wir uns im Farbmodus?
			  if (pDoc->inColorMode())
			  {
				  // ja, zeichne einen passenden farbigen Punkt
					int farbe = 255 - (farbvorberechnung * iterationswert);
					pDC->SetPixel(bildposition, RGB(255,farbe,farbe));
			  }
		  }

		  // rücke zum nächsten Pixel vor
          x += dx;
          bildposition.x += 1;
      }

	  // rücke zur nächsten Zeile vor
      y += dy;
      bildposition.y += 1;
	}	

	// setze den Stift auf den Ausgangsstift zurück
	pDC->SelectObject(&pOldPen);
}


I hope this helps a bit... im sorry if u there is not everything translated^^...

well... but i have to say that its slow, too - at least when u increase the max_iter^^...


(and pssst: it uses MFC:P - but that should not bother u^^)
Last edited on
This color would be very dark (almost black) color= RGB(iteration,0,0);
Try changing it (for now at least) to RGB(255,255,255).
Well, sorry to seem picky, but I really would rather not re-write my program to use MFC (I don't even know MFC anyways; I got what I know from theForger's Win API tutorial ( http://www.winprog.org/tutorial/ ) and MSDN). I'm looking at the code, though to see if I can adapt it to API.

Also, regarding Hammurabi's post, changing the iteration color to white just gives me all white pixels... I think I'm doing something wrong procedure-wise, but I don't know what. Still looking into it.

EDIT: trying to adapt the MFC code is turning out to be way harder than I expected (and frankly I'm starting to get frustrated)... maybe clarification on some of the functions would help? For instance, could I just use the client rect's right and bottom members for pDoc->getX/YEndPosition when initializing dx/y? And what's up with the "?" operator... isn't that bad usage? (it doesn't have the " x (condition) y ? a:b" format...)

I apologize for sounding like a n00b... I'm really not all that experienced...
Last edited on
Your x and y positions need to be doubles. They also must have values within the range of the Mandelbrot set, which is between -2 and .5 for x and -1.25 and 1.25 for y. So for instance if vPos is a pixel offset (an integer between 0 and whatever) then you cannot simply assign it to y. You must translate it to the Mandelbrot coords.

You should post your entire program, as one file preferably.
Ok, so I've tried revising my code, and I think I might be starting to make a little headway with it, but now I'm seeing all white pixels. I've changed the x and y to long doubles, just to be on the safe side. Now the thing is, I can't figure out how to get the range of -2 to 0.5 for x and -1.25 and 1.25 for y. There are a couple of functions in Incubbus's code that seem to be specific to the CMandelbrotView class pointer he's declared (pDoc->getMaxIter() and pDoc->inColorMode()) that apparently get the maximum number of iterations and set the x and y ranges(?).

In any case, here's my revised (albeit still non-functioning) code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for(int vPos= cr.top; vPos < cr.bottom; vPos++) //for each line
{
	for(int hPos= cr.left; hPos < cr.right; hPos++) //for each pixel
	{
		COLORREF color= NULL;
		
		long double x= hPos,y= vPos;

		int maxIterations= 1000;
		float colorCalc= 255 / (float)maxIterations;
		int iterations= CalcPixel(x,y,maxIterations);

		if(iterations >= maxIterations)
		SetPixel(hdc,hPos,vPos,RGB(0,0,0));
		else
		{
			float colorRange= 255 - (colorCalc * iterations);
			SetPixel(hdc,hPos,vPos,RGB(255,(int)colorRange,(int)colorRange));
		}
	}
}


Here's a link to the whole program code: http://www.mediafire.com/?sharekey=c828b75e00505b6e7f7ec40ada4772a6e04e75f6e8ebb871

I apologize for the long address URL, I can't get [url] tags to work in the UBBcode.

The implementation of CalcPixel is the same as Incubbus's CMandelbrotView::mandel_iterC() function, just renamed, so I'll give him the credit for it... I really don't feel like re-writing a function that is basically the same thing, just with different variable names and such just to avoid copying issues (because it's really still outright copying anyway, even if you change the variable names, am I right?)

Also note: the typecasting was done to stop VC++ nagging me about "possible loss of data". I know I should be compiling it as C rather than C++, but I was being lazy when I first created the project (I let VC++ do the extension appendage), so now I have to do typecasting manually. I don't think that's going to cause any major problems, other than it being a little less accurate than it could be...
Last edited on
There's no reason to use long doubles, regular doubles are fine. You just can't use int. But you're still just using pixel numbers for x and y. That won't work. The Mandelbrot set is a set of numbers in the complex plane. You need to translate your pixel numbers to complex numbers. Something like this (just whipped up, untested code, probably has errors):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct DRect { double left, top, right, bottom; };

void drawMandelbrot(
    HDC hdc,          // HDC of window to draw to.
    RECT& cr,         // Client rect.
    DRect& disp       // Area of the complex plane to display.
){
    const int MaxCount = 200;
    double stepX = (disp.right - disp.left) / cr.right;
    double stepY = (disp.botom - disp.top)  / cr.bottom;
    double cx = disp.left;
    double cy = disp.top;

    for( int y = 0; y < cr.right; ++y ){
        for( int x = 0; x < cr.bottom; ++x ){

            double zx = 0, zy = 0;
            int count;
            for( count = 0; count < MaxCount; ++count ){
                double oldX = zx;
                zx = oldX * oldX - zy * zy + cx;
                zy = 2 * oldX * zy + cy;
                if( zx * zx + zy * zy > 4.0 ) break;
            }

            // Display in grayscale
            int n = 0;
            if( count < MaxCount ) n = count * 256 / MaxCount;
            SetPixelV( hdc, x, y, RGB( n, n, n ));

            cx += stepX;
        }
        cx = disp.left;
        cy += stepY;
    }
}

// wherever
    RECT cr;
    GetClientRect( hwnd, &cr );
    DRect disp = { -2, -1.25, .5, 1.25 };
    drawMandelbrot( hdc, cr, disp );


Note that in C++ you could use the complex type (might be slower, though):

1
2
3
4
            for( count = 0; count < MaxCount; ++count ){
                z = z * z + c;
                if( abs( z ) > 2.0 ) break;
            }

Last edited on
Here's a runnable example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <windows.h>

#define CLIENT_SIZE_X  400
#define CLIENT_SIZE_Y  400

struct DRect { double left, top, right, bottom; };
struct WndSize { int horz, vert; };

void drawMandelbrot( HDC hdc, const DRect& loc, const WndSize& pixels ){

    const int MaxCount = 256;
    double xstep = (loc.right - loc.left) / pixels.horz;
    double ystep = (loc.bottom - loc.top) / pixels.vert;

    double cx, cy;
    int x, y;
    for( y = 0, cy = loc.top;
         y < pixels.vert;
         ++y, cy += ystep )
        for( x = 0, cx = loc.left;
             x < pixels.horz;
             ++x, cx += xstep ){

            double zx = 0, zy = 0;
            double zx2 = 0, zy2 = 0; // zx and zy squared

            int count;
            for( count = 0; count < MaxCount; ++count ){
                zy = 2 * zx * zy + cy; // must be before zx is changed
                zx = zx2 - zy2 + cx;
                zx2 = zx * zx;
                zy2 = zy * zy;
                if( zx2 + zy2 > 4.0 ) break;
            }

            int n = 0;
            if( count < MaxCount ) n = 255 - count * 256 / MaxCount;

            SetPixelV( hdc, x, y, RGB( n, n, n ));
        }
}

void wmPaint( HWND hwnd, WPARAM wp, LPARAM lp ){
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint( hwnd, &ps );
    DRect loc = { -2.0, 1.25, 0.5, -1.25 };
    WndSize wndSize = { CLIENT_SIZE_X, CLIENT_SIZE_Y };
    drawMandelbrot( hdc, loc, wndSize );
    EndPaint( hwnd, &ps );
}

LRESULT CALLBACK mainWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ){
    switch( msg ){
    case WM_PAINT:    wmPaint       (hwnd, wp, lp);  break;
    case WM_DESTROY:  PostQuitMessage( 0 );          break;
    default:   return DefWindowProc (hwnd, msg, wp, lp);
    }
    return 0;
}

void registerWindow( HINSTANCE hinst, char *appname, WNDPROC wp ){
    WNDCLASSEX wc = {sizeof(wc)}; // set first member, zero the rest
    wc.style         = CS_HREDRAW|CS_VREDRAW;
    wc.lpfnWndProc   = wp;
    wc.hInstance     = hinst;
    wc.hIcon         = LoadIcon( hinst, appname );
    wc.hCursor       = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH );
    wc.lpszMenuName  = appname;
    wc.lpszClassName = appname;
    wc.hIconSm       = LoadIcon( hinst, appname );
    RegisterClassEx( &wc );
}

int WINAPI WinMain( HINSTANCE hinst, HINSTANCE unused,
                    PSTR cmd, int show ){
    char *appName = "Mandelbrot";
    MSG msg;
    DWORD style = WS_POPUPWINDOW|WS_CAPTION;
    RECT rect = {0, 0, CLIENT_SIZE_X, CLIENT_SIZE_Y};

    registerWindow( hinst, appName, mainWndProc );
    AdjustWindowRect( &rect, style, FALSE );
    CreateWindowEx( 0, appName, appName, style | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT,
        rect.right - rect.left, rect.bottom - rect.top,
        NULL, NULL, hinst, NULL );

    while( GetMessage( &msg, NULL, 0, 0 )){
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    return msg.wParam;
}

*sigh*, I can't get it to work, and I probably never will... at least until I get a good understanding of the math behind it. I couldn't get the first example to work (apparently it does have errors, which I won't be able to figure out anytime soon), and the second one, being a full program, would require me to completely re-think my WndProc structure. My math really isn't that good; I don't know why I'm even attempting this, considering I really don't know the (apparently) intricate details of complex numbers required to pull this off (all I really know is that i is just an abstract representation of sqrt(-1); everything else is just guesswork for me). As far as recursive graphical routines, I think I'm better suited to LOGO or just very basic GDI fractals (i.e. ones that involve boolean logic, rather than "numbers on the complex plane").

I guess I just got too enthusiastic about wanting to do this and took a bigger bite than I could chew. I tried to dive straight into complex mathematics without having a proper knowledge of the subject. I apologize if I've wasted anyone's time.

I think I'll just go ahead and put this off for now, until I can really get a better grasp on the math...
[Edit]: I found your mistake. You are not scaling your pixels! [Edit:] Just saw Hammurabi pointed that out too... Well below is how to modify your original post's code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int middleX = cr.right- cr.left;
int middleY = cr.bottom- cr.top;
for(int vPos= cr.top; vPos < cr.bottom; vPos++)
{
  for(int hPos= cr.left; hPos < cr.right; hPos++)
  { 
    COLORREF color= NULL;
    double x0= (hPos-middleX)/200,y0= (vPos- middleY)/200;
    double x= x0,y= y0;
    int iteration= 0;
    int maxIterations= 10;
    while((x*x) + (y*y) <= 4 && iteration < maxIterations)
    {
      double xTemp= x*x - y*y + x0;
      y= 2*x*y + y0;//originally it was wrong: hPos= 2*x*y + y0.
      x= xTemp;
      iteration++;
    }
    if(iteration == maxIterations)
      color= RGB(0,0,0);
    else
      color= RGB(255,255,255);//you can simply make it black and white to begin with. 
    //as soon as you get something that looks like the Mandelbrot set you can play with the colors:)
    
    SetPixel(hdc,hPos,vPos,color);
  }
}


A math question to you: what will happen when you change that number 200 I put in there?
Last edited on
@ Hammurabi: Tried your program, looks beautiful :)
wow... impressive.
Here's an updated version. It displays in color and allows you to zoom in. Hold the left mouse button down to draw a selection rectangle. If you press the right button before you let the left button up, it will cancel the selection rectangle. Double click to reset to the original view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include <windows.h>

const int CLIENT_SIZE_X = 400;
const int CLIENT_SIZE_Y = 400;
const int SELECTION_TOO_SMALL = 4;

struct DRect { double left, top, right, bottom; };
struct WndSize { int horz, vert; };
const DRect INIT_LOCATION = { -2.0, 1.25, 0.5, -1.25 };

DRect g_location = INIT_LOCATION;
RECT  g_selection;


void drawMandelbrot( HDC hdc, const DRect& loc, const WndSize& pixels ){

    const double saturation = 0.5;
    const double value = 0.7;
    const double rotation = 20.0; // adjusts hue
    const int MaxCount = 100;

    COLORREF color;
    double hue, f;
    int p, q, t;

    double xstep = (loc.right - loc.left) / pixels.horz;
    double ystep = (loc.bottom - loc.top) / pixels.vert;
    double cx, cy;
    int x, y;

    for( y = 0, cy = loc.top;
         y < pixels.vert;
         ++y, cy += ystep )
        for( x = 0, cx = loc.left;
             x < pixels.horz;
             ++x, cx += xstep ){

            double zx = 0, zy = 0;
            double zx2 = 0, zy2 = 0; // zx and zy squared

            int count;
            for( count = 0; count < MaxCount; ++count ){
                zy = 2 * zx * zy + cy; // must be before zx is changed
                zx = zx2 - zy2 + cx;
                zx2 = zx * zx;
                zy2 = zy * zy;
                if( zx2 + zy2 > 4.0 ) break;
            }

            color = RGB( 0, 0, 0 ); // Points in the set are black
            if( count < MaxCount ){ // Points outside the set are colored

                hue = count * 360.0 / MaxCount + rotation;
                while( hue >= 360.0 ) hue -= 360.0;

                // Convert HSV to RGB
                f = hue / 60 - (int)(hue / 60);
                p = (int) (value * (1 - saturation) * 255);
                q = (int) (value * (1 - f * saturation) * 255);
                t = (int) (value * (1 - (1 - f) * saturation) * 255);
                switch( (int)(hue / 60) % 6 )
                {
                case 0: color = RGB( value, t, p );  break;
                case 1: color = RGB( q, value, p );  break;
                case 2: color = RGB( p, value, t );  break;
                case 3: color = RGB( p, q, value );  break;
                case 4: color = RGB( t, p, value );  break;
                case 5: color = RGB( value, p, q );  break;
                }
            }

            SetPixelV( hdc, x, y, color );
        }
}

void normalizeRect( RECT *rect ){
    LONG temp;
    if( rect->top > rect->bottom ){
        temp = rect->top;
        rect->top = rect->bottom;
        rect->bottom = temp;
    }
    if( rect->left > rect->right ){
        temp = rect->left;
        rect->left = rect->right;
        rect->right = temp;
    }
}

void setSelectRect( LONG l, LONG t, LONG r, LONG b ){
    g_selection.left  = l;  g_selection.top    = t;
    g_selection.right = r;  g_selection.bottom = b;
}

void drawSelectRect( HWND hwnd ){
    HDC hdc = GetDC( hwnd );
    RECT rect = g_selection;
    normalizeRect( &rect );
    DrawFocusRect( hdc, &rect );
    ReleaseDC( hwnd, hdc );
}

void endSelection( HWND hwnd ){
    HDC hdc = GetDC( hwnd );
    normalizeRect( &g_selection );
    DrawFocusRect( hdc, &g_selection );
    ReleaseDC( hwnd, hdc );
    ReleaseCapture();
}

BOOL isSelectionTooSmall( void ){
    return( g_selection.right  - g_selection.left < SELECTION_TOO_SMALL ||
            g_selection.bottom - g_selection.top  < SELECTION_TOO_SMALL );
}

void updateLocation( void ){
    double size = g_location.right - g_location.left;
    double temp = g_location.left + size * g_selection.left / CLIENT_SIZE_X;
    g_location.right = g_location.left
                       + size * g_selection.right / CLIENT_SIZE_X;
    g_location.left = temp;
    size = g_location.bottom - g_location.top;
    temp = g_location.top + size * g_selection.top / CLIENT_SIZE_Y;
    g_location.bottom = g_location.top +
                        size * g_selection.bottom / CLIENT_SIZE_Y;
    g_location.top = temp;
}


void wmLButtonDown( HWND hwnd, WPARAM wp, LPARAM lp ){
    SetCapture( hwnd );
    setSelectRect( LOWORD( lp ), HIWORD( lp ),
                     LOWORD( lp ), HIWORD( lp ));
    drawSelectRect( hwnd );
}

void wmMouseMove( HWND hwnd, WPARAM wp, LPARAM lp ){
    if( GetCapture() != hwnd ) return;
    drawSelectRect( hwnd );
    g_selection.right = LOWORD( lp );
    g_selection.bottom = HIWORD( lp );
    drawSelectRect( hwnd );
}

void wmLButtonUp( HWND hwnd, WPARAM wp, LPARAM lp ){
    if( GetCapture() != hwnd ) return;
    endSelection( hwnd );
    if( isSelectionTooSmall() ) return;
    updateLocation();
    InvalidateRect( hwnd, 0, 1 );
}

void wmRButtonDown( HWND hwnd, WPARAM wp, LPARAM lp ){
    if( GetCapture() == hwnd ) // If in capture mode
        endSelection( hwnd );  // cancel selection rect
}

void wmLButtonDblClk( HWND hwnd, WPARAM wp, LPARAM lp ){
    g_location = INIT_LOCATION;
    InvalidateRect( hwnd, 0, TRUE );
}

void wmPaint( HWND hwnd, WPARAM wp, LPARAM lp ){
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint( hwnd, &ps );
    WndSize wndSize = { CLIENT_SIZE_X, CLIENT_SIZE_Y };
    drawMandelbrot( hdc, g_location, wndSize );
    EndPaint( hwnd, &ps );
}


LRESULT CALLBACK mainWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ){
    switch( msg ){
    case WM_PAINT:         wmPaint        (hwnd, wp, lp);  break;
    case WM_LBUTTONDOWN:   wmLButtonDown  (hwnd, wp, lp);  break;
    case WM_MOUSEMOVE:     wmMouseMove    (hwnd, wp, lp);  break;
    case WM_LBUTTONUP:     wmLButtonUp    (hwnd, wp, lp);  break;
    case WM_RBUTTONDOWN:   wmRButtonDown  (hwnd, wp, lp);  break;
    case WM_LBUTTONDBLCLK: wmLButtonDblClk(hwnd, wp, lp);  break;
    case WM_DESTROY:       PostQuitMessage( 0 );           break;
    default:        return DefWindowProc  (hwnd, msg, wp, lp);
    }
    return 0;
}

void registerWindow( HINSTANCE hinst, char *appname, WNDPROC wp ){
    WNDCLASSEX wc = {sizeof(wc)}; // set first member, zero the rest
    wc.style         = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
    wc.lpfnWndProc   = wp;
    wc.hInstance     = hinst;
    wc.hIcon         = LoadIcon( hinst, appname );
    wc.hCursor       = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH );
    wc.lpszMenuName  = appname;
    wc.lpszClassName = appname;
    wc.hIconSm       = LoadIcon( hinst, appname );
    RegisterClassEx( &wc );
}

int WINAPI WinMain( HINSTANCE hinst, HINSTANCE unused,
                    PSTR cmd, int show ){
    char *appName = "Mandelbrot";
    MSG msg;
    DWORD style = WS_POPUPWINDOW|WS_CAPTION;
    RECT rect = {0, 0, CLIENT_SIZE_X, CLIENT_SIZE_Y};

    registerWindow( hinst, appName, mainWndProc );
    AdjustWindowRect( &rect, style, FALSE );
    CreateWindowEx( 0, appName, appName, style | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT,
        rect.right - rect.left, rect.bottom - rect.top,
        NULL, NULL, hinst, NULL );

    while( GetMessage( &msg, NULL, 0, 0 )){
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    return msg.wParam;
}

That's awesome. I need to learn how to do this. What would be interesting is if you could zoom in with the mouse scroll bar wherever the pointer was located. :)
Ultra cool :))))
You should post this in the code section!
Last edited on
You should post this in the code section!

I wouldn't mind. I'd have to add comments, though.
Thanks titon! It works now! I've also used the coloration code from Hammurabi's post.

The next step for me now is to add zooming capabilities. I'd like to do it (if possible) with the mouse wheel, and set the center of the next frame to the position of the cursor from the previous frame (same idea for zooming both in and out). The only thing I really should need to know how to do in order to implement this is how to translate the view. I have a hard time intuiting what to change from someone else's code, especially since it's almost completely different from mine.

Could anyone help me with what needs to be made variable?
I would really advise you to try to do this on your own. It requires no extra knowledge (only what you know from high school), and it would be cheating yourself not to try it on your own.

Of course, if you post a failed attempt, we can help you find your mistake.
Pages: 12