コンソールウィンドウでマンデルブロ集合

Windows のコンソールウィンドウにマンデルブロ集合を表示します。


マウスでぐりぐり動かして、[i] / [o] キーでズームイン / アウト。
暇があればマルチサンプリングで表示を改善したり、コードを整備したりする予定です。

Visual C++ 2010 / Visual Studio 2012 で動作します。実行ファイル (64kB)

//
//	Mandelbrot Console
//	Version : 1.0
//	Author : @Reputeless, @agehama_
//
# include <string>
# include <Windows.h>
# include <conio.h>

struct Vec2
{
	double x,y;
	Vec2(){}	
	Vec2( double _x, double _y ): x(_x),y(_y){}
};

int Mandelbrot( double a, double b )
{
	double x=0.0, y=0.0;

	for(int n=0; n<300; ++n)
	{
		const double x1 = x*x - y*y +a;
		const double y1= 2.0*x*y + b;
		
		if(x1*x1 + y1*y1 > 4.0)
		{
			return n; // 発散
		}

		x = x1;	
		y = y1;
	}

	return 0;
}

void CreateOutput( int width, int height, std::string& buffer, const Vec2& center, const Vec2& range )
{
	const Vec2 pixelSize(range.x/width,range.y/height);
	const Vec2 beginPos(center.x-(pixelSize.x*width/2),
						center.y-(pixelSize.y*height/2));

	for(int y=0; y<height; ++y)
	{
		const double py = beginPos.y + pixelSize.y*y;

		for(int x=0; x<width; ++x)
		{
			const double px = beginPos.x + pixelSize.x*x;
			buffer[y*width+x] = Mandelbrot(px,py) ? '*' : ' ';
		}
	}
}

// マウスカーソルの位置 [0.0,1.0] を返す
Vec2 GetMousePos()
{
	RECT rc;
	::GetWindowRect(::GetForegroundWindow(),&rc);

	POINT pos;
	::GetCursorPos(&pos);
	return Vec2(1.0*pos.x/(rc.right-rc.left),1.0*pos.y/(rc.bottom-rc.top));
}

class Console
{
public:

	Console()
		:	m_center(0.0,0.0),m_scale(4.0),m_aspect(1.0)
	{
		::SetConsoleTitleW(L"Mandelbrot Console / zoom [i]n / zoom [o]ut / [q]uit");

		m_console = ::GetStdHandle(STD_OUTPUT_HANDLE);

		COORD size = ::GetLargestConsoleWindowSize(m_console);
		::SetConsoleScreenBufferSize(m_console,size);

		SMALL_RECT dw = {0,0,size.X-2,size.Y-1};
		::SetConsoleWindowInfo(m_console,TRUE,&dw);
	
		m_width = size.X;
		m_height = size.Y-2;

		m_aspect = 1.0*m_width/m_height*0.4;

		::SetConsoleScreenBufferSize(m_console,size);

		m_output.resize(m_width*m_height,' ');
	}

	~Console()	{ ::CloseHandle(m_console); }

	void draw()
	{
		const Vec2 mousePos = GetMousePos();
		m_center.x += (mousePos.x-0.5)*m_scale*0.1*m_aspect;
		m_center.y += (mousePos.y-0.5)*m_scale*0.1;

		CreateOutput(m_width,m_height,m_output,m_center,Vec2(m_scale*m_aspect,m_scale));

		COORD cp = {0,0};
		DWORD n;		
		::WriteConsoleOutputCharacterA(m_console,m_output.c_str(),m_output.length(),cp,&n);
	}

	void zoomIn()	{ m_scale *= 0.75; }

	void zoomOut()	{ m_scale /= 0.75; }

private:

	HANDLE m_console;

	int m_width, m_height;

	std::string m_output;

	Vec2 m_center;

	double m_scale, m_aspect;
};

int main()
{
	Console console;

	for(;;)
	{
		console.draw();

		if(_kbhit())
		{
			switch(_getch())
			{
			case 'i': console.zoomIn(); break;
			case 'o': console.zoomOut(); break;
			case 'q': return 0;
			}
		}

		::Sleep(80);
	}
}