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 );
}
//--------------------------------------------------------------------------------------------------