Beispiel für ein erstes Mandelbrot?



  • Gut, danke schon mal an alle 🙂

    Ich habe versucht, alle(?) Hinweise in diesem Thread umzusetzen, nur mit std::complex hakt es noch. Dazu später mehr.

    Die Klasse 'Mandelbrot' sieht jetzt so aus:

    #pragma once
    #include <vector>
    #include <complex>
    
    namespace param // damit ich 'Area' überall benutzen kann und Mandelbrot es nehmen kann
    {
    	struct Area
    	{
    		double x0 = 0;
    		double x = 0;
    		double y0 = 0;
    		double y = 0;
    	};
    }
    
    class Mandelbrot
    {
    	struct Size
    	{
    		int width = 0;
    		int height = 0;
    	};
    
    	struct SizeFactor
    	{
    		double x = 0;
    		double y = 0;
    	};
    
    	struct Dot
    	{
    		int x = 0;
    		int y = 0;
    		std::size_t index = 0;
    	};
    
    public:
    	Mandelbrot(const int width, const int height) :
    		size_{ width, height },
    		mandelbrotSet_(width * height),
    		maxIterations_(300),
    		iterations_(100)
    	{
    		setArea(-2., 1., -1., 1.);
    	}
    
    	int width() const { return size_.width; }
    	int height() const { return size_.height; }
    	param::Area area() const { return area_; }
    	SizeFactor sizeFactor() const { return sizeFactor_; }
    	std::size_t iterations() const { return iterations_; }
    	std::size_t maxIterations() const { return maxIterations_; }
    	std::vector<Dot> mandelbrotSet() const { return mandelbrotSet_; }
    
    	void setArea(const double x0, const double x, const double y0, const double y)
    	{
    		setArea({ x0, x, y0, y });
    	}
    
    	void setArea(const param::Area& area)
    	{
    		area_ = area;
    		sizeFactor_.x = (area_.x - area_.x0) / (size_.width - 1.);
    		sizeFactor_.y = (area_.y - area_.y0) / (size_.height - 1.);
    		calcMandelbrotSet();
    	}
    
    	void setIterations(const std::size_t iterations)
    	{
    		if (iterations > maxIterations_)
    			iterations_ = maxIterations_;
    		else
    			iterations_ = iterations;
    
    		calcMandelbrotSet();
    	}
    
    private:
    	Size size_;
    	SizeFactor sizeFactor_;
    	Dot dot_;
    	param::Area area_;
    	std::size_t iterations_ = 0;
    	const std::size_t maxIterations_ = 0;
    	std::vector<Dot> mandelbrotSet_;
    
    	std::size_t mandelbrot(const std::complex<double>& c, const std::size_t N)
    	{
    		std::complex<double> z = c;
    		for (std::size_t n = 0; n < N; ++n)
    		{
    			if (std::abs(z) > 4.)  return n;
    			z = (z * z) + c;
    		}
    		return N;
    	}
    
    	void calcMandelbrotSet()
    	{
    		std::size_t n = 0;
    		for (int y = 0; y < size_.height; y++)
    		{
    			for (int x = 0; x < size_.width; x++)
    			{
    				const double real = area_.x0 + x * sizeFactor_.x; // current real value
    				const double imag = area_.y0 + y * sizeFactor_.y; // current imaginary value
    				const std::complex<double> c(real, imag);
    
    				mandelbrotSet_[n++] = { x, y, mandelbrot(c, iterations_) };
    			}
    		}
    	}
    
    	/*std::size_t mandelbrot(const double real, const double imag, const std::size_t N) const
    	{
    		double zReal = real; // real value
    		double zImag = imag; // imaginary value
    
    		for (std::size_t n = 0; n < N; ++n)
    		{
    			const double r2 = zReal * zReal;
    			const double i2 = zImag * zImag;
    			const double v = r2 + i2;
    
    			if (v > 4.) return n;
    
    			zImag = 2. * zReal * zImag + imag;
    			zReal = r2 - i2 + real;
    		}
    		return N;
    	}
    		void calcMandelbrotSet()
    	{
    		std::size_t n = 0;
    		for (int y = 0; y < size_.height; y++)
    		{
    			for (int x = 0; x < size_.width; x++)
    			{
    				const double real = area_.x0 + x * sizeFactor_.x; // current real value
    				const double imag = area_.y0 + y * sizeFactor_.y; // current imaginary value
    
    				mandelbrotSet_[n++] = { x, y, mandelbrot(real, imag, iterations_) };
    			}
    		}
    	}*/
    };
    

    Der namespace 'areaHandling' 🙄 heißt jetzt 'mandelbrot' 🙄 und sieht so aus:

    #pragma once
    #include "Console.h"
    #include "Mandelbrot.h"
    
    using Color = Char;
    using namespace param;
    
    namespace mandelbrot
    {
    	class Cursor
    	{
    		struct Size
    		{
    			int width = 0;
    			int height = 0;
    			struct Center
    			{
    				int x = 0;
    				int y = 0;
    			} center;
    		};
    
    		struct Position
    		{
    			int x = 0;
    			int y = 0;
    			double fx = 0;
    			double fy = 0;
    		};
    
    	public:	
    		Size size;
    		Position pos;
    		Color dot;
    		std::vector<bool> image;
    
    		Cursor() :
    			dot{ '#', console::DGREY, console::GREY },
    			size{ 7, 7, { 3, 3 } },
    			image{
    			0, 0, 0, 1, 0, 0, 0,
    			0, 0, 0, 1, 0, 0, 0,
    			0, 0, 0, 0, 0, 0, 0,
    			1, 1, 0, 0, 0, 1, 1,
    			0, 0, 0, 0, 0, 0, 0,
    			0, 0, 0, 1, 0, 0, 0,
    			0, 0, 0, 1, 0, 0, 0 } {}
    	};
    
    	Cursor cursor;                // ist das so in Ordnung als
    	std::vector<Color> colors;    // namespace-globale Variablen?
    
    	void plotMandelbrotSet(const Mandelbrot& mb)
    	{
    		for (const auto& dot : mb.mandelbrotSet())
    		{
    			Color color;
    			if (dot.index == mb.iterations())
    				color = Console::windowColor();
    			else
    				color = mandelbrot::colors[dot.index];
    
    			Console::putChar(dot.x, dot.y, color);
    		}
    	}
    
    	void plotCursor()
    	{
    		std::size_t n = 0;
    		for (int i = 0; i < cursor.size.height; ++i)
    			for (int j = 0; j < cursor.size.width; ++j)
    			{
    				const int x = cursor.pos.x + j - cursor.size.center.x;
    				const int y = cursor.pos.y + i - cursor.size.center.y;
    				if (cursor.image[n++])
    					Console::putChar(x, y, cursor.dot);
    			}
    	}
    	
    	void updateWindowTitle(const Mandelbrot& mb)
    	{
    		const std::string x0 = " area: xMin: " + std::to_string(mb.area().x0);
    		const std::string x = "   xMax: " + std::to_string(mb.area().x);
    		const std::string y0 = "   yMin: " + std::to_string(mb.area().y0);
    		const std::string y = "   yMax: " + std::to_string(mb.area().y);
    		const std::string iter = "     iterations: " + std::to_string(mb.iterations());
    		const std::string cx = "     cursor: x: " + std::to_string(cursor.pos.fx);
    		const std::string cy = "   y: " + std::to_string(cursor.pos.fy);
    
    		Console::setTitle(x0 + x + y0 + y + iter + cx + cy);
    	}
    
    	void rePlot(const Mandelbrot& mb, const bool showCursor = true)
    	{
    		plotMandelbrotSet(mb);
    		if (showCursor) plotCursor();
    		updateWindowTitle(mb);
    		Console::writeBuffer();  
    	}
    
    	void moveCursor(const Mandelbrot& mb, const int x, const int y)
    	{
    		cursor.pos.x += x;
    		cursor.pos.y += y;
    		cursor.pos.fx = cursor.pos.x * mb.sizeFactor().x + mb.area().x0;
    		cursor.pos.fy = cursor.pos.y * mb.sizeFactor().y + mb.area().y0;
    	}
    
    	void setCursorToZero(const Mandelbrot& mb)
    	{
    		const double fx0 = 1. / mb.sizeFactor().x * 2.;
    		const double fy0 = 1. / mb.sizeFactor().y;
    		moveCursor(mb, (int)std::round(fx0), (int)std::round(fy0));
    	}
    
    	void addIterations(Mandelbrot& mb, const std::size_t iterStep)
    	{
    		mb.setIterations(mb.iterations() + iterStep);
    	}
    
    	void subIterations(Mandelbrot& mb, const std::size_t iterStep)
    	{
    		mb.setIterations(mb.iterations() - iterStep);
    	}
    
    	void cursorLeft(Mandelbrot& mb, const int cursorStep)
    	{
    		moveCursor(mb, -cursorStep, 0);
    		if (cursor.pos.x < 0)
    		{
    			Area area = {
    				mb.area().x0 - mb.sizeFactor().x * cursorStep,
    				mb.area().x  - mb.sizeFactor().x * cursorStep,
    				mb.area().y0,
    				mb.area().y };
    
    			mb.setArea(area);
    			moveCursor(mb, cursorStep, 0);
    		}
    	}
    
    	void cursorRight(Mandelbrot& mb, const int cursorStep)
    	{
    		moveCursor(mb, cursorStep, 0);
    		if (cursor.pos.x >= mb.width())
    		{
    			Area area = {
    				mb.area().x0 + mb.sizeFactor().x * cursorStep,
    				mb.area().x  + mb.sizeFactor().x * cursorStep,
    				mb.area().y0,
    				mb.area().y };
    
    			mb.setArea(area);
    			moveCursor(mb, -cursorStep, 0);
    		}
    	}
    
    	void cursorUp(Mandelbrot& mb, const int cursorStep)
    	{
    		moveCursor(mb, 0, -cursorStep);
    		if (cursor.pos.y < 0)
    		{
    			Area area = {
    				mb.area().x0,
    				mb.area().x,
    				mb.area().y0 - mb.sizeFactor().y * cursorStep,
    				mb.area().y  - mb.sizeFactor().y * cursorStep };
    
    			mb.setArea(area);
    			moveCursor(mb, 0, cursorStep);
    		}
    	}
    
    	void cursorDown(Mandelbrot& mb, const int cursorStep)
    	{
    		moveCursor(mb, 0, cursorStep);
    		if (cursor.pos.y >= mb.height())
    		{
    			Area area = {
    				mb.area().x0,
    				mb.area().x,
    				mb.area().y0 + mb.sizeFactor().y * cursorStep,
    				mb.area().y  + mb.sizeFactor().y * cursorStep };
    
    			mb.setArea(area);
    			moveCursor(mb, 0, -cursorStep);
    		}
    	}
    
    	void zoomToArea(Mandelbrot& mb, const Area& lim, const int zoomSteps)
    	{
    		Area area = mb.area();
    		const double dx0 = (area.x0 - lim.x0) / zoomSteps;
    		const double dx = (area.x - lim.x) / zoomSteps;
    		const double dy0 = (area.y0 - lim.y0) / zoomSteps;
    		const double dy = (area.y - lim.y) / zoomSteps;
    
    		const int zoomLimit = 5; 
    		for (int i = 0; i < zoomSteps - zoomLimit; ++i)
    		{
    			area.x0 -= dx0,	
    			area.x -= dx,
    			area.y0 -= dy0,
    			area.y -= dy;
    			mb.setArea(area);
    			rePlot(mb, false);
    		}
    	}
    
    	void zoomToCursor(Mandelbrot& mb, const int zoomSteps, const int zoomIterStep)
    	{
    		Area area = {
    			cursor.pos.fx,
    			cursor.pos.fx,
    			cursor.pos.fy,
    			cursor.pos.fy };
    
    		zoomToArea(mb, area, zoomSteps);
    		mb.setIterations(mb.iterations() + zoomIterStep);
    	}
    
    	void zoomIn(Mandelbrot& mb, const int zoomSteps, int zoomStep)
    	{
    
    	}
    
    	void zoomOut(Mandelbrot& mb, const int zoomSteps, int zoomStep)
    	{
    
    	}
    
    	void explore(Mandelbrot& mb, bool& exploring)
    	{
    		setCursorToZero(mb);
    		rePlot(mb);
    		
    		const std::size_t iterStep = 10;	  // add this to iterations in addIterations
    		const std::size_t zoomIterStep = 50;  // add this to iterations in zoomToCursor
    		const int zoomSteps = 30;             // amount of zoomSteps in zoomToCursor
    		int cursorStep = 1, zoomStep = 1;
    
    		bool shiftKey = false;
    		bool getKey = true;
    		while (getKey)
    		{
    			switch (Console::getKeyCode()) // Console::
    			{
    			case VK_ADD:
    				addIterations(mb, iterStep);
    				rePlot(mb);
    				break;
    
    			case VK_SUBTRACT:
    				subIterations(mb, iterStep);
    				rePlot(mb);
    				break;
    
    			case VK_LEFT:
    				cursorLeft(mb, cursorStep);
    				rePlot(mb);
    				break;
    
    			case VK_RIGHT:
    				cursorRight(mb, cursorStep);
    				rePlot(mb);
    				break;
    
    			case VK_UP:
    				cursorUp(mb, cursorStep);
    				rePlot(mb);
    				break;
    
    			case VK_DOWN:
    				cursorDown(mb, cursorStep);
    				rePlot(mb);
    				break;
    
    			case VK_SHIFT: // change cursorStep
    				if (!shiftKey) 
    				{
    					cursorStep = 10;
    					shiftKey = true;
    				}
    				else
    				{
    					cursorStep = 1;
    					shiftKey = false;
    				}
    				break;
    
    			case 'Z':
    				zoomToCursor(mb, zoomSteps, zoomIterStep);
    				rePlot(mb);
    				break;
    
    			case 'A': // not yet implemented
    				zoomIn(mb, zoomSteps, zoomStep);
    				rePlot(mb);
    				break;
    
    			case 'Y': // not yet implemented
    				zoomOut(mb, zoomSteps, zoomStep);
    				rePlot(mb);
    				break;
    
    			case VK_ESCAPE:
    				getKey = false; //return to MandelBrot
    				exploring = false; //return to EXIT
    				break;
    
    			default:
    				break;
    			}
    		}
    	}
    
    	void createColorsForMandelbrot(const Mandelbrot& mb, const std::vector<Color>& givenColors)
    	{
    		const std::size_t limit = mb.maxIterations();
    
    		if (givenColors.size() >= limit)
    		{
    			mandelbrot::colors = givenColors;
    			return;
    		}
    
    		mandelbrot::colors.resize(limit);
    		std::size_t n = 0;
    		const std::size_t num_intervals = givenColors.size() - 1;
    		const std::size_t sub_interval_length = limit / num_intervals;
    		const std::size_t num_long_intervals = limit % num_intervals;
    		const float hop_step = 1.f * num_intervals / num_long_intervals;
    
    		float hop_counter = 0;
    		for (std::size_t i = 0; i < num_intervals; ++i)
    		{
    			if (i >= hop_counter)
    			{
    				for (std::size_t j = 0; j < sub_interval_length + 1; ++j)
    					mandelbrot::colors[n++] = givenColors[i];
    
    				hop_counter += hop_step;
    			}
    			else
    			{
    				for (std::size_t j = 0; j < sub_interval_length; ++j)
    					mandelbrot::colors[n++] = givenColors[i];
    			}
    		}
    	}
    
    	void exploreMandelbrot(Mandelbrot& mb, const std::vector<Color>& givenColors)
    	{
    		createColorsForMandelbrot(mb, givenColors);
    
    		bool running = true;
    		while (running)
    			explore(mb, running);
    	}
    }
    
    

    Jetzt dauert aber die Berechnung mit direkt komplexen Zahlen wie eine Ewigkeit gegenüber den aufgedröselten im auskommentierten Bereich, die sofort, augenblicklich geschehen ist.

    Es ist bestimmt irgendwas albernes, wofür ich mich wieder ein halbes Jahr schämen werde, aber was ist da falsch?

    Werde jetzt auch Getränke zu mir nehmen, also nicht wundern, wenn ich nicht gleich antworte 😉


Anmelden zum Antworten