A Mandelbrot Explorer/Zoom with Julia Walkabout






4.33/5 (5 votes)
A Mandelbrot Explorer/Zoom with Julia walkabout
- Download source - 10.25 KB (Visual C++ 6.0 project files)

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