Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Mandelbrot Explorer/Zoom with Julia Walkabout

0.00/5 (No votes)
30 May 2010 1  
A Mandelbrot Explorer/Zoom with Julia walkabout

Introduction

This simple app and article demonstrate Windows Message Handling and painting on a Win32 device context. A device context is a structure that defines a set of graphic objects and their associated attributes, as well as the graphic modes that affect output.

The graphics objects include a pen for line drawing, a brush for painting and filling, a bitmap for copying or scrolling parts of the screen, a palette for defining the set of available colors, a region for clipping and other operations.

We will start off by declaring our variables.

//*********************************************************************
//* Defines                                                           *
//*********************************************************************

#define  pi 3.1415926
#define  WIDTH 400
#define  HEIGHT 400
#define  WEXT 380
#define  HEXT 210
#define  MAGNITUDE_CUTOFF 100
#define  NUMCOLOURS 256
#define  ID_TIMER 1
#define  DIVFACTOR 1

#define  XMin   -1.4
#define  XMax    1.4
#define  YMin   -1.4
#define  YMax    1.4
#define  Iter   32
#define  Scal   32767
#define  ResX 150
#define  ResY 150

// **************************************************************************
// **                      Global Variables                                **
// **************************************************************************

bool xorDone=false;
char title[]="MandelZoom / Julia Walkabout      by Topcoder",buffer[80],String[40];
int max_iterations,mousex,mousey,xorEx=0,xorEy=0,MouseX,MouseY;
double xmin = -2.10, xmax = 0.75, ymin = -1.5 , ymax = 1.5;
double  width_fact, height_fact;
double midx,midy,dx,dy;
double dxx,dyy,px,py,zsx,zsy,zex,zey;
int xp,yp,random=0;
float jdx=0,jdy=0,r=0,theta=0,jx=0,jy=0,tempx,tempy;
rektangel rekt;
POINT start,end;
HDC hdc;
HWND Button[2],Edit,StatusBar;

HDC          hdcMem;
HBITMAP      hbmMem;
HANDLE       hOld;
RECT rect;
HBRUSH hbrush = CreateSolidBrush(RGB(0,0,128));

Then we register our window by using RegisterClassEx():

int WINAPI WinMain (HINSTANCE hinst, HINSTANCE hprevinst, LPSTR lpCmdLine,int cmdShow)
{
	if (hprevinst == NULL)
	{
		WNDCLASSEX wclass;
		wclass.cbSize = sizeof(WNDCLASSEX);
		wclass.hInstance = hinst;
		wclass.lpfnWndProc = (WNDPROC)WndProc;
		wclass.style = CS_HREDRAW | CS_VREDRAW;
		wclass.hIcon = LoadIcon(hinst, IDC_ARROW);
		wclass.hIconSm = NULL;
		wclass.hCursor = LoadCursor(NULL, IDC_ARROW);
		wclass.lpszMenuName = NULL;
		wclass.cbClsExtra = 0;
		wclass.cbWndExtra = 0;
		wclass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
		wclass.lpszClassName = "BASIC2";

		if(!RegisterClassEx(&wclass))   return 0;

Then we create the actual window and show it using CreateWindowEx() and ShowWindow().

HWND hwnd = CreateWindowEx(
	WS_EX_WINDOWEDGE,   // extended window style
	"BASIC2",           // registered class name
	title,              // window name
	WS_OVERLAPPEDWINDOW,// window style
	CW_USEDEFAULT,      // horizontal position of window
	CW_USEDEFAULT,      // vertical position of window
	WIDTH+WEXT,		    // window width
	HEIGHT+32,	        // window height
	NULL,				// handle to parent or owner window
	NULL,				// menu handle or child identifier
	hinst,				// handle to application instance
	NULL);				// window-creation data

	ShowWindow(hwnd, cmdShow);
	UpdateWindow(hwnd); 

Now, we start the Win32 message pump with the code below. A Windows application processes messages from the operating system, and most Win32 applications have to be able to translate and dispatch windows messages.

Now we can send and receive windows messages using TranslateMessage() and DispatchMessage().

while (GetMessage(&msg, NULL, 0, 0))
{
	TranslateMessage (&msg) ;
	DispatchMessage(&msg);	// send the message to the window proc
}

return msg.wParam;

}//  End int WINAPI WinMain ( 

The WM_CREATE Message

The WM_CREATE message is called upon when the application is being created and instantized. In this code section, controls such as buttons, listboxes, comboboxes are created using the CreateWindow() function.

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	 	switch(msg)
	 	{

case WM_CREATE:
	Button[0] = CreateWindow (
		"button",
		"reset",
		WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
		WIDTH+5,		//Button position x
		 HEIGHT-48,  	//Button position y
		 WIDTH/2, 	//Button Dimension x
		 30,  		//Button Dimension y
		 hwnd,
		 (HMENU) 5 ,
		 ((LPCREATESTRUCT) lparam)->hInstance,
		NULL);

	Button[1] = CreateWindow (
		"button",
		"Exit",
		WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
		WIDTH+(WIDTH/2)+5,	//Button position x
		 HEIGHT-48,  	//Button position y
		 (WIDTH/2)-48, 	//Button Dimension x
		 30,  		//Button Dimension y
		 hwnd,
		 (HMENU) 6,
		 ((LPCREATESTRUCT) lparam)->hInstance,
		NULL);

	   SetTimer(hwnd,ID_TIMER,1,NULL);
break;

The WM_PAINT Message

The WM_PAINT message is called upon if an area on the application needs to be updated.
We place our paint and update functions in this code section.

case WM_PAINT:
	Paint(hwnd);
	initWalk();
	putCursor();
	walkabout();
break;

The WM_LBUTTONDOWN Message

The WM_LBUTTONDOWN message is called upon when the left button is pressed down. When the user presses down the left mouse button, our application stores the mouse's current x and y coordinates in the variables start.x and start.y. As the user drags the mouse, a rectangle is drawn, it defines the size of the region of the mandelbrot on which to zoom in on.

case WM_LBUTTONDOWN:
{
	if (mouse_x>=0 && mouse_x<=WIDTH && mouse_y>=0 && mouse_y<=HEIGHT)
	{
		rekt.visa = true;
		rekt.sx = (float)mouse_x;
		rekt.sy = (float)mouse_y;
		rekt.ex = (float)mouse_x;
		rekt.ey = (float)mouse_y;

		xorEx=(int)rekt.sx+100;
		xorEy=(int)rekt.sy+100;
		end.x=0;
		end.y=0;
		start.x=mouse_x;
		start.y=mouse_y;
		MoveToEx (hdc, start.x, start.y, NULL) ;

		zsx=(xmin+(xmax-xmin)*((mousex)-px)/(WIDTH-1));
		zsy=(ymin+(ymax-ymin)*((mousey)-py)/(HEIGHT-1));
		zex=(xmin+(xmax-xmin)*((mousex)-px)/(WIDTH-1));
		zey=(ymin+(ymax-ymin)*((mousey)-py)/(HEIGHT-1));

		tempx=xmin;
		tempy=ymin;
		xmin=zsx;
		ymin=zsy;

		textout();
	}
} break; 

The WM_MOUSEMOVE Message

The WM_MOUSEMOVE message is called upon when the user drags the mouse. As the mouse is being dragged, a rectangular box is drawn to show where the next zoom region will be.

case WM_MOUSEMOVE:
{
	if (mouse_x>=0 && mouse_x<=WIDTH && mouse_y>=0 && mouse_y<=HEIGHT)
	{
		if (mouse_x>=rekt.sx && mouse_y >=rekt.sy )
		{
			rekt.ex = (float)mouse_x;
			rekt.ey = (float)mouse_y;
			dx=zex-zsx;
			dy=zey-zsy;
		}

		if (rekt.visa )
		{
			BitBlt(hdc, 0, 0, WIDTH, HEIGHT, hdcMem, 0, 0, SRCCOPY);
			SetROP2(hdc,R2_NOTXORPEN);
			int DrawMode = GetROP2(hdc);
			MoveToEx (hdc, (int)rekt.sx, (int)rekt.sy, NULL) ;
			LineTo (hdc, (int)rekt.ex ,(int)rekt.sy) ;
			LineTo (hdc, (int)rekt.ex ,(int)rekt.ey) ;
			LineTo (hdc, (int)rekt.sx ,(int)rekt.ey) ;
			LineTo (hdc, (int)rekt.sx ,(int)rekt.sy) ;
			SetROP2(hdc,R2_COPYPEN);

		}
	mousex=mouse_x;
	mousey=mouse_y;
	textout();
	}

} break; 

The WM_LBUTTONUP Message

The WM_LBUTTONUP message is called upon when the left button is released. Once the user releases the left mouse button, our application stores the mouse's current x and y coordinates in the variables end.x and end.y. Our application now calculates the new interpolants on which to calculate the mandelbrot from.

case WM_LBUTTONUP:
{
	if (mouse_x>=0 && mouse_x<=WIDTH && mouse_y>=0 && mouse_y<=HEIGHT)
	{

		rekt.visa = false;
		end.x=mouse_x;
		end.y=mouse_y;
		rekt.ex =(float)mousex;
		rekt.ey =(float)mousey;
		if (end.x >start.x+5 && end.y >start.y +5)
		{

			// Free-up the off-screen DC
			initWalk();
			MouseX=WIDTH/2;
			MouseY=HEIGHT/2;

			SelectObject(hdcMem, hOld);
			DeleteObject(hbmMem);
			DeleteDC(hdcMem);

			dx=zex-zsx;
			dy=zey-zsy;

			if (mouse_x>=rekt.sx && mouse_y >=rekt.sy )
			{
				zex=(xmin+(xmax-xmin)*((mousex)-px)/(WIDTH-1));
				zey=(ymin+(ymax-ymin)*((mousey)-py)/(HEIGHT-1));

				xmax=zex;
				ymax=zey;
				dx=zex-zsx;
				dy=zey-zsy;
				textout();
				//if (rekt.sx!=rekt.ex && rekt.sy!=rekt.ey)
				 fract(/*hdc*/);
				 putCursor();
				 walkabout();
			}

			}
			else
			{
				BitBlt(hdc, 0, 0, WIDTH, 
					HEIGHT, hdcMem, 0, 0, SRCCOPY);
				xmin=tempx;
				ymin=tempy;

		       }
	}
} break; 

The WM_KEYDOWN Message

The WM_KEYDOWN message is called upon when a key is pressed down.

case WM_KEYDOWN:
{
	int virtual_code = (int) wparam;
	int key_bits     = (int) lparam;

	switch (virtual_code)
	{
		case 27: {
					PostQuitMessage(0); //<esc> quits
				} break;

		case 13: {
					//<enter>
				resetFrac();
				fract(/*hdc*/);
				} break;
		case 32:{
				}break;

		case VK_RIGHT: { } break;
		case VK_LEFT: { } break;
	default: break;
}

} break;

The WM_QUIT and WM_DESTROY Message

The WM_QUIT and WM_DESTROY message is called upon when the application is signalling it wants to quit.

		WM_QUIT:
		case WM_DESTROY:
			KillTimer(hwnd,ID_TIMER);
			PostQuitMessage(0);
		break;

		return 0;

		default:
		return DefWindowProc(hwnd, msg, wparam, lparam);
		}

	ReleaseDC(hwnd, hdc);
	return 0;
} // end  LRESULT CALLBACK WndProc()

And that is all that is required to create a Win32 application that draws and zooms a mandelbrot on a Win32 device context.

Thanks for reading.

History

  • 2010-05-08 Updates made
  • 2002-06-22 Code complete

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here