Brownian tree/C++ animated

From Rosetta Code

BrownianTreeAnim cpp.png

The green dots you can see in this picture are what I call movers. They are particles that still do not belong to the tree.

#include <windows.h>
#include <string>

//--------------------------------------------------------------------------------------------------
using namespace std;

//--------------------------------------------------------------------------------------------------
enum states { SEED, GROWING, MOVING, REST };
enum treeStates { NONE, MOVER, TREE };
const int MAX_SIDE = 500, MAX_MOVERS = 511, MAX_CELLS = 15137;

//--------------------------------------------------------------------------------------------------
struct point
{
    point() : x(0), y(0)                 { }
    point( int a, int b ) : x(a), y(b)   { }
    void set( int a, int b ) { x = a; y = b; }
    int x, y;
};
//--------------------------------------------------------------------------------------------------
struct movers
{
    point pos;
    bool moving;
    movers() : moving( false ){}
};
//--------------------------------------------------------------------------------------------------
class myBitmap
{
public:
    myBitmap() : pen( NULL ) {}
    ~myBitmap()
    {
	DeleteObject( pen );
	DeleteDC( hdc );
	DeleteObject( bmp );
    }

    bool create( int w, int h )
    {
	BITMAPINFO	bi;
	ZeroMemory( &bi, sizeof( bi ) );

	bi.bmiHeader.biSize	   = sizeof( bi.bmiHeader );
	bi.bmiHeader.biBitCount	   = sizeof( DWORD ) * 8;
	bi.bmiHeader.biCompression = BI_RGB;
	bi.bmiHeader.biPlanes	   = 1;
	bi.bmiHeader.biWidth	   =  w;
	bi.bmiHeader.biHeight	   = -h;

	HDC dc = GetDC( GetConsoleWindow() );
	bmp = CreateDIBSection( dc, &bi, DIB_RGB_COLORS, &pBits, NULL, 0 );
	if( !bmp ) return false;

	hdc = CreateCompatibleDC( dc );
	SelectObject( hdc, bmp );
	ReleaseDC( GetConsoleWindow(), dc ); 

	width = w; height = h;

	return true;
    }

    void clear()
    {
	ZeroMemory( pBits, width * height * sizeof( DWORD ) );
    }

    void setPenColor( DWORD clr )
    {
	if( pen ) DeleteObject( pen );
	pen = CreatePen( PS_SOLID, 1, clr );
	SelectObject( hdc, pen );
    }

    void saveBitmap( string path )
    {
	BITMAPFILEHEADER fileheader;
	BITMAPINFO	 infoheader;
	BITMAP		 bitmap;
	DWORD		 wb;

	GetObject( bmp, sizeof( bitmap ), &bitmap );

	DWORD* dwpBits = new DWORD[bitmap.bmWidth * bitmap.bmHeight];
	ZeroMemory( dwpBits, bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD ) );
	ZeroMemory( &infoheader, sizeof( BITMAPINFO ) );
	ZeroMemory( &fileheader, sizeof( BITMAPFILEHEADER ) );

	infoheader.bmiHeader.biBitCount = sizeof( DWORD ) * 8;
	infoheader.bmiHeader.biCompression = BI_RGB;
	infoheader.bmiHeader.biPlanes = 1;
	infoheader.bmiHeader.biSize = sizeof( infoheader.bmiHeader );
	infoheader.bmiHeader.biHeight = bitmap.bmHeight;
	infoheader.bmiHeader.biWidth = bitmap.bmWidth;
	infoheader.bmiHeader.biSizeImage = bitmap.bmWidth * bitmap.bmHeight * sizeof( DWORD );

	fileheader.bfType    = 0x4D42;
	fileheader.bfOffBits = sizeof( infoheader.bmiHeader ) + sizeof( BITMAPFILEHEADER );
	fileheader.bfSize    = fileheader.bfOffBits + infoheader.bmiHeader.biSizeImage;

	GetDIBits( hdc, bmp, 0, height, ( LPVOID )dwpBits, &infoheader, DIB_RGB_COLORS );

	HANDLE file = CreateFile( path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	WriteFile( file, &fileheader, sizeof( BITMAPFILEHEADER ), &wb, NULL );
	WriteFile( file, &infoheader.bmiHeader, sizeof( infoheader.bmiHeader ), &wb, NULL );
	WriteFile( file, dwpBits, bitmap.bmWidth * bitmap.bmHeight * 4, &wb, NULL );
	CloseHandle( file );

	delete [] dwpBits;
    }

    HDC getDC() const     { return hdc; }
    int getWidth() const  { return width; }
    int getHeight() const { return height; }

private:
    HBITMAP bmp;
    HDC	    hdc;
    HPEN    pen;
    void	*pBits;
    int	    width, height;
};
//--------------------------------------------------------------------------------------------------
class brownianTree
{
public:
    brownianTree()         
    { 
	_bmp.create( MAX_SIDE, MAX_SIDE );
	init(); 
    }

    void init()
    {
	_state = SEED;
	_cellCount = 0;
	for( int x = 0; x < 20; x++ )
	    _color[x] = RGB( rand() % 150 + 75, rand() % 200 + 50, rand() % 180 + 45 );
	ZeroMemory( _grid, sizeof( _grid ) );
    }

    void mainLoop()
    {
	switch( _state )
	{
	    case REST: break;
	    case SEED: doSeed(); break;
	    case GROWING: startMovers(); break;
	    case MOVING: moveMovers();
	}
	drawGrid();
    }

    void setHwnd( HWND h ) { _hWnd = h; }

private:
    void drawGrid()
    {
	_bmp.clear();

	for( int y = 0; y < MAX_SIDE; y++ )
	    for( int x = 0; x < MAX_SIDE; x++ )
	    {
		BYTE g = _grid[x][y];
		if( g != NONE )
		{
		    DWORD clr = g > MOVER ? _color[g - 2] : RGB( 0, 255, 0 );
		    SetPixel( _bmp.getDC(), x, y, clr );
		}
	    }

	    HDC dc = GetDC( _hWnd );
	    BitBlt( dc, 0, 0, MAX_SIDE, MAX_SIDE, _bmp.getDC(), 0, 0, SRCCOPY );
	    ReleaseDC( _hWnd, dc );
    }

    void doSeed()
    {
	int x = MAX_SIDE - MAX_SIDE / 2, y = MAX_SIDE / 4;
	_grid[rand() % x + y][rand() % x + y] = TREE;
	_cellCount++;
	_state = GROWING;
    }

    void addMover( movers* m )
    {
	m->moving = true;
	int x = MAX_SIDE - MAX_SIDE / 2, y = MAX_SIDE / 4, a, b;
	do
	{
	    a = rand() % x + y; b = rand() % x + y;
	} while ( _grid[a][b] != NONE )

	m->pos.set( a, b );
	_grid[a][b] = MOVER;
    }

    void startMovers()
    {
	for( int c = 0; c < MAX_MOVERS; c++ )
	{
	    movers* m = &_movers[c];
	    addMover( m );
	}
	_state = MOVING;
    }

    void addToTree( movers* m )
    {
	m->moving = false;
	_grid[m->pos.x][m->pos.y] = ( BYTE )GetTickCount() % 20;
	++_cellCount;
    }

    bool moveIt( movers* m )
    {
	point f[8]; int ff = 0;
	for( int y = -1; y < 2; y++ )
	{
	    for( int x = -1; x < 2; x++ )
	    {
		if( !x && !y ) continue;
		int a = m->pos.x + x, b = m->pos.y + y;
		if( a < 0 || b < 0 || a >= MAX_SIDE || b >= MAX_SIDE )
		{
		    addToTree( m );
		    return true;
		}
				
		BYTE g = _grid[a][b];
		if( g > MOVER )
		{
		    addToTree( m );
		    return true;
		}
		else if( g == NONE ) f[ff++].set( a, b );
	    }
	}

	if( ff < 1 ) return false;
	_grid[m->pos.x][m->pos.y] = NONE;
	m->pos = f[rand() % ff];
	_grid[m->pos.x][m->pos.y] = MOVER;

	return false;
    }

    void moveMovers()
    {
	bool found = false;
	for( int m = 0; m < MAX_MOVERS; m++ )
	{
	    movers* mm = &_movers[m];
	    if( !mm->moving ) continue;
	    found = true;
	    if( moveIt( mm ) && _cellCount < MAX_CELLS ) addMover( mm );
	}

	if( !found ) _state = REST;
    }

    HWND     _hWnd;
    states   _state;
    BYTE     _grid[MAX_SIDE][MAX_SIDE];
    myBitmap _bmp;
    int      _cellCount;
    movers   _movers[MAX_MOVERS];
    DWORD    _color[20];
};
//--------------------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
	case WM_DESTROY: PostQuitMessage( 0 ); break;
	default:
	    return DefWindowProc( hWnd, msg, wParam, lParam );
    }
    return 0;
}
//--------------------------------------------------------------------------------------------------
HWND InitAll( HINSTANCE hInstance )
{
    WNDCLASSEX wcex;
    ZeroMemory( &wcex, sizeof( wcex ) );

    wcex.cbSize	       = sizeof( WNDCLASSEX );
    wcex.style	       = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc   = WndProc;
    wcex.hInstance     = hInstance;
    wcex.hCursor       = LoadCursor( NULL, IDC_ARROW );
    wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
    wcex.lpszClassName = "_BROWNIAN_";

    RegisterClassEx( &wcex ); 
    return CreateWindow( "_BROWNIAN_", ".: Brownian Tree -- PJorente :.", WS_SYSMENU, CW_USEDEFAULT, 0, MAX_SIDE, MAX_SIDE, NULL, NULL, hInstance, NULL );
}
//--------------------------------------------------------------------------------------------------
int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
{
    srand( GetTickCount() );

    HWND hwnd = InitAll( hInstance );
    if( !hwnd ) return -1;

    RECT rc = { 0, 0, MAX_SIDE, MAX_SIDE };

    AdjustWindowRectEx( &rc, WS_SYSMENU | WS_CAPTION, FALSE, 0 );
    int w = rc.right  - rc.left, 
	h = rc.bottom - rc.top;

    int posX = ( GetSystemMetrics( SM_CXSCREEN ) >> 1 ) - ( w >> 1 ),
        posY = ( GetSystemMetrics( SM_CYSCREEN ) >> 1 ) - ( h >> 1 );

    SetWindowPos( hwnd, HWND_TOP, posX, posY, w, h, SWP_NOZORDER );
    ShowWindow( hwnd, nCmdShow );
    UpdateWindow( hwnd );

    brownianTree tree;
    tree.setHwnd( hwnd );

    MSG msg;
    ZeroMemory( &msg, sizeof( msg ) );
    while( msg.message != WM_QUIT )
    {
	if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) != 0 )
	{
	    TranslateMessage( &msg );
	    DispatchMessage( &msg );
	}
	else
	{
	    tree.mainLoop();
	}
    }
    return UnregisterClass( "_BROWNIAN_", hInstance );
}
//--------------------------------------------------------------------------------------------------