Beispiel für ein erstes Mandelbrot?



  • Hallo,
    Als ich mein erstes Mandelbrot schreiben wollte, habe ich mir unter anderem andere Codes zu dem Thema angeschaut. Da ich das recht hilfreich fand, stelle ich mal meinen Code hier rein.
    Vorteil ist, wenn irgendwas falsch oder schlecht, kann er sofort korrigiert werden.

    Das schwierigste war für mich, passende Kommentare zu finden, deshalb sind die sehr spärlich verteilt. Wenn irgendwo was unklar ist, kann ich ja nachträglich noch kommentieren.

    Im Moment ist es noch eine Konsolenanwendung, es sind aber nur vier Methoden die dafür notwendig sind, die habe ich zur Unterscheidung in deutsch kommentiert.

    #pragma once
    #include "Console.h"
    
    using Color = Char; // Console:: Char ist ein eigenes CHAR_INFO, was hier als eigenständige Farbe steht
    
    namespace mandelbrot
    {
    	struct Area // range of values (soll englisch für 'Wertebereich' sein)
    	{
    		double x0 = 0;
    		double x = 0;
    		double y0 = 0;
    		double y = 0;
    	};
    
    	template<typename T>
    	void plotMandelbrotSet(const T& mb)
    	{
    		for (const auto& dot : mb.mandelbrotSet())
    			Console::putChar(dot.x, dot.y, dot.color); // Console:: sowas wie pset(x, y, color) in Pseudocode
    	}                                                  // wird in einen eigenen Buffer geschrieben   
    }                                                      // erst mit ::writeBuffer() erfolgt die Ausgabe  
    
    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;
    		Color color;
    	};
    
    public:
    	Mandelbrot(const int width, const int height, const std::vector<Color>& colors) : 
    		size{ width, height }, 
    		mandelbrotSet_(width * height),
    		givenColors{ colors },
    		iterations_(100),
    		maxIterations_(300)
    	{
    		fitColorsTo(maxIterations_);
    		setArea(-2., 1., -1., 1.);
    	}
    
    	int width() const { return size.width; }
    	int height() const { return size.height; }
    	mandelbrot::Area area() const { return area_; }
    	SizeFactor sizeFactor() const { return sizeFactor_; }
    	std::size_t iterations() const { return iterations_; }
    	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 mandelbrot::Area& area)
    	{
    		area_ = area;
    		getSizeFactor();
    		getMandelbrotSet();
    	}
    
    	void setIterations(const std::size_t iterations)
    	{
    		if (iterations > maxIterations_)
    			iterations_ = maxIterations_;
    		else
    			iterations_ = iterations;
    
    		getMandelbrotSet();
    	}
    
    private:
    	Size size;
    	mandelbrot::Area area_;
    	SizeFactor sizeFactor_;
    	Dot dot;
    	std::vector<Dot> mandelbrotSet_;
    
    	const std::size_t maxIterations_ = 0;
    	std::size_t iterations_ = 0;
    	std::vector<Color> givenColors;
    	std::vector<Color> colors;
    
    	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;
    	}
    
    	Color getColor(const double real, const double imag) const
    	{
    		const std::size_t colValue = mandelbrot(real, imag, iterations_);
    
    		if (colValue == iterations_) return Console::windowColor(); // Console:: gibt die Hintergrundfarbe des Konsolenfensters zurück
    		else return colors[colValue];
    	}
    
    	void getMandelbrotSet()
    	{
    		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, getColor(real, imag) };
    			}
    		}
    	}
    
    	void getSizeFactor()
    	{
    		sizeFactor_.x = (area_.x - area_.x0) / (size.width - 1.);
    		sizeFactor_.y = (area_.y - area_.y0) / (size.height - 1.);
    	}
    
    	void fitColorsTo(const std::size_t limit)
    	{
    		if (givenColors.size() >= limit)
    		{
    			colors = givenColors;
    			return;
    		}
    
    		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)
    					colors[n++] = givenColors[i];
    
    				hop_counter += hop_step; 
    			}
    			else  
    			{
    				for (std::size_t j = 0; j < sub_interval_length; ++j)
    					colors[n++] = givenColors[i];
    			}
    		}
    	}
    };
    

    Damit man was damit anfangen kann, habe ich noch einen namespace namens 'areaHandling' 🙄

    #pragma once
    #include "Mandelbrot.h"
    
    using namespace mandelbrot;
    
    namespace areaHandling
    {
    	class Cursor
    	{
    		struct Size
    		{
    			int x = 0;
    			int y = 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:
    		Color dot;
    		std::vector<bool> image;
    		Size size;
    		Position pos;
    
    		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 } {}
    	};
    
    	void moveCursor(const Mandelbrot& mb, Cursor& cursor, 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, Cursor& cursor)
    	{
    		const double fx0 = 1. / mb.sizeFactor().x * 2.;
    		const double fy0 = 1. / mb.sizeFactor().y;
    		moveCursor(mb, cursor, (int)std::round(fx0), (int)std::round(fy0));
    	}
    
    	void plotCursor(const Cursor& cursor)
    	{
    		std::size_t n = 0;
    		for (int i = 0; i < cursor.size.y; ++i)
    			for (int j = 0; j < cursor.size.x; ++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); // Console:: 
    			}
    	}
    
    	void updateWindowTitle(const Mandelbrot& mb, const Cursor& cursor)
    	{
    		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); // Console:: 
    	}
    
    	void rePlot(const Mandelbrot& mb, const Cursor& cursor, const bool showCursor = true)
    	{
    		plotMandelbrotSet(mb);
    		if (showCursor) plotCursor(cursor);
    		updateWindowTitle(mb, cursor);
    		Console::writeBuffer(); // Console:: 	
    	}
    
    	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, Cursor& cursor, const int cursorStep)
    	{
    		moveCursor(mb, cursor, -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, cursor, cursorStep, 0);
    		}
    	}
    	void cursorRight(Mandelbrot& mb, Cursor& cursor, const int cursorStep)
    	{
    		moveCursor(mb, cursor, 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, cursor, -cursorStep, 0);
    		}
    	}
    	void cursorUp(Mandelbrot& mb, Cursor& cursor, const int cursorStep)
    	{
    		moveCursor(mb, cursor, 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, cursor, 0, cursorStep);
    		}
    	}
    	void cursorDown(Mandelbrot& mb, Cursor& cursor, const int cursorStep)
    	{
    		moveCursor(mb, cursor, 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, cursor, 0, -cursorStep);
    		}
    	}
    	void zoomToArea(Mandelbrot& mb, const Cursor& cursor, 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, cursor, false);
    		}
    	}
    	void zoomToCursor(Mandelbrot& mb, const Cursor& cursor, const int zoomSteps, const int zoomIterStep)
    	{
    		Area area = {
    			cursor.pos.fx,
    			cursor.pos.fx,
    			cursor.pos.fy,
    			cursor.pos.fy };
    
    		zoomToArea(mb, cursor, area, zoomSteps);
    		mb.setIterations(mb.iterations() + zoomIterStep);
    	}
    	void zoomIn(Mandelbrot& mb, const Cursor& cursor, const int zoomSteps, int zoomStep)
    	{
    
    	}
    	void zoomOut(Mandelbrot& mb, const Cursor& cursor, const int zoomSteps, int zoomStep)
    	{
    
    	}
    
    	void exploreMandelbrot(Mandelbrot& mb, bool& exploring)
    	{
    		Cursor cursor;
    		setCursorToZero(mb, cursor);
    		rePlot(mb, cursor);
    		
    		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 curStep = 1, zoomStep = 1;
    
    		bool shiftKey = false;
    		bool getKey = true;
    		while (getKey)
    		{
    			switch (Console::getKeyCode()) // Console::
    			{
    			case VK_ADD:
    				addIterations(mb, iterStep);
    				rePlot(mb, cursor);
    				break;
    
    			case VK_SUBTRACT:
    				subIterations(mb, iterStep);
    				rePlot(mb, cursor);
    				break;
    
    			case VK_LEFT:
    				cursorLeft(mb, cursor, curStep);
    				rePlot(mb, cursor);
    				break;
    
    			case VK_RIGHT:
    				cursorRight(mb, cursor, curStep);
    				rePlot(mb, cursor);
    				break;
    
    			case VK_UP:
    				cursorUp(mb, cursor, curStep);
    				rePlot(mb, cursor);
    				break;
    
    			case VK_DOWN:
    				cursorDown(mb, cursor, curStep);
    				rePlot(mb, cursor);
    				break;
    
    			case VK_SHIFT: // change cursorStep
    				if (!shiftKey) 
    				{
    					curStep = 10;
    					shiftKey = true;
    				}
    				else
    				{
    					curStep = 1;
    					shiftKey = false;
    				}
    				break;
    
    			case 'Z':
    				zoomToCursor(mb, cursor, zoomSteps, zoomIterStep);
    				rePlot(mb, cursor);
    				break;
    
    			case 'A': // not yet implemented
    				zoomIn(mb, cursor, zoomSteps, zoomStep);
    				rePlot(mb, cursor);
    				break;
    
    			case 'Y': // not yet implemented
    				zoomOut(mb, cursor, zoomSteps, zoomStep);
    				rePlot(mb, cursor);
    				break;
    
    			case VK_ESCAPE:
    				getKey = false; //return to MandelBrot
    				exploring = false; //return to EXIT
    				break;
    
    			default:
    				break;
    			}
    		}
    	}
    
    	void explore(Mandelbrot& mb)
    	{
    		bool running = true;
    		while (running)
    		{
    			exploreMandelbrot(mb, running);
    		}
    	}
    }
    


  • Man kann das wirklich von mehreren Seiten betrachten, und dementsprechend erhält man andere Fragestellungen, Beispiele:

    1. Wie erkläre ich Normalsterblichen das in Zeile 108 die Fläche des Hypothenusenquadrat kleiner 4 bleiben soll - oder versteht das jeden auf den ersten Blick wie ich?
    2. Wieviel Zeit und Speicher kostet es wohl extra wenn x und y erstmal an eine Funktion weitergegeben und dann unnötigerweise noch in einem vector abgelegt werden.
    3. Wer zum Teufel lädt ein Bild nicht aus einer Datei sondern hardcoded das?


  • Das sind Fragen bzw Punkte, an die ich jetzt gar nicht gedacht hatte. Muss ich mal schauen.



  • @TGGC sagte in Beispiel für ein erstes Mandelbrot?:

    Man kann das wirklich von mehreren Seiten betrachten, und dementsprechend erhält man andere Fragestellungen, Beispiele:

    1. Wie erkläre ich Normalsterblichen das in Zeile 108 die Fläche des Hypothenusenquadrat kleiner 4 bleiben soll - oder versteht das jeden auf den ersten Blick wie ich?

    Ich denke das versteht jeder der auch ansonsten keine Probleme damit hat zu verstehen was die Funktion berechnet.

    1. Wieviel Zeit und Speicher kostet es wohl extra wenn x und y erstmal an eine Funktion weitergegeben und dann unnötigerweise noch in einem vector abgelegt werden.

    Davon sollte nach Inlining nichts mehr übrig bleiben. Davon abgesehen ist wohl auch ein echter Funktionsaufruf pro Pixel ziemlich billig im Vergleich zur durchschnittlichen Laufzeit der Berechnung die pro Pixel durchgeführt werden muss.

    1. Wer zum Teufel lädt ein Bild nicht aus einer Datei sondern hardcoded das?

    Falls du das Mini-Bild für den Cursor meinst: ich!



  • @TGGC sagte in Beispiel für ein erstes Mandelbrot?:

    1. Wieviel Zeit und Speicher kostet es wohl extra wenn x und y erstmal an eine Funktion weitergegeben und dann unnötigerweise noch in einem vector abgelegt werden.

    Müsste ich mal nachmessen. Gefühlt macht es keinen Unterschied, ob erst in den vector oder gleich plotten. Wenn das gemeint ist?



  • @hustbaer sagte in Beispiel für ein erstes Mandelbrot?:

    @TGGC sagte in Beispiel für ein erstes Mandelbrot?:

    1. Wer zum Teufel lädt ein Bild nicht aus einer Datei sondern hardcoded das?

    Falls du das Mini-Bild für den Cursor meinst: ich!

    Da schliesse ich mich an, das habe ich auch schon so gemacht. Im Besonderen z.B. auch eine hardcodierte Schachbretttextur, die immer dann verwendet wurde, wenn die Texturdatei nicht geladen werden konnte.



  • Was mich an dem Code stört, es wird mit komplexen Zahlen gerechnet, aber es wird kein Datentyp std::complex<T> verwendet. Das macht auch die Formeln und den Code komplexer als es sein müsste.

    Beispiel wie man es mit std::complex sehr viel näher am Lehrbuch formulieren kann.

    #include <complex>
      
    std::size_t mandelbrot(const std::complex<double>& value, const std::size_t N) {
        // c ist das was Du als imag und real bezeichnet hast.
        std::complex<double>    z = value;
    
        for (std::size_t n = 0; n < N; ++n) {
            if (abs(z) > 4.0) return n;
    
            z = c + (z*z);
        }
    
        return N;
    }
    


    • inkonstistente Namensgebung, manche Member enden mit einem Underscore, andere nicht.
    • x und y als Member von Size sind ok, aber width and height hätten mir besser gefallen.
    • getMandelbrotSet() liest sich wie ein getter, gibt aber nichts zurück
    • rePlot() braucht zwei Parameter, um zu unterscheiden, ob der Cursor angezeigt werden soll oder nicht. Hier täte es vllt auch ein Cursor const* Obwohl der Cursor auch noch für updateWindowTitle gebraucht wird. Hm....
    • west const ;). Is aber wohl Geschmacksfrage.


  • Was sollen eigentlich die Kommentare

    // Console:: 
    

    ?

    Außerdem solltest du die Berechnungsfunktionen und UI (Console) strikt trennen.

    Edit: Für reine Berechnungsfunktionen benutze ich calc(ulate) als Namenspräfix.



  • @john-0 sagte in Beispiel für ein erstes Mandelbrot?:

    Was mich an dem Code stört, es wird mit komplexen Zahlen gerechnet, aber es wird kein Datentyp std::complex<T> verwendet. Das macht auch die Formeln und den Code komplexer als es sein müsste.

    Beispiel wie man es mit std::complex sehr viel näher am Lehrbuch formulieren kann.

    Je nachdem, wen man fragt. In einem Beitrag in stackoverflow wurde genau andersrum argumentiert. Da kam einer mit std::complex, wurde dann aber wie bei mir aufgedröselt. Hatte auch viel Zustimmung. Aber eigentlich würde mir es mit complex besser gefallen. Dachte aber, das wäre nicht klug 😉

    @DocShoe sagte in Beispiel für ein erstes Mandelbrot?:

    • inkonstistente Namensgebung, manche Member enden mit einem Underscore, andere nicht.

    Stimmt. Habe ich auch schon dran gedacht.

    • x und y als Member von Size sind ok, aber width and height hätten mir besser gefallen.

    Ist doch so?

    struct Size
    	{
    		int width = 0;
    		int height = 0;
    	};
    

    Oder meinst Du den Cursor?

    • getMandelbrotSet() liest sich wie ein getter, gibt aber nichts zurück

    Was schlägst Du vor? calculateMandelbrotSet() oder ähnliches?

    • rePlot() braucht zwei Parameter, um zu unterscheiden, ob der Cursor angezeigt werden soll oder nicht. Hier täte es vllt auch ein Cursor const* Obwohl der Cursor auch noch für updateWindowTitle gebraucht wird. Hm....

    Hier verstehe ich nicht ganz. Wenn der Cursor gezeichnet oder seine Werte angezeigt werden soll, muss ich ihn doch der Funktion übergeben? Und ob er gezeichnet werden soll, ist doch nur ein vordefinierter Parameter? Soll er gezeichnet werden, muss man gar nichts übergeben?

    @Th69 sagte in Beispiel für ein erstes Mandelbrot?:

    Was sollen eigentlich die Kommentare

    // Console:: 
    

    ?

    Ja, ist ein bißchen blöd. Ich hatte oben versprochen, alle Console-Funktionen zur Unterscheidung zu kommentieren. Was soll man aber kommentieren, was schon dasteht? War also nur der Ordnung halber. Naja.

    Außerdem solltest du die Berechnungsfunktionen und UI (Console) strikt trennen.

    Habe ich nicht?

    Edit: Für reine Berechnungsfunktionen benutze ich calc(ulate) als Namenspräfix.

    Jepp, bezieht sich auf den Hinweis von DocShoe wg getMandelbrotSet()?



  • @zeropage sagte in Beispiel für ein erstes Mandelbrot?:

    In einem Beitrag in stackoverflow wurde genau andersrum argumentiert. Da kam einer mit std::complex, wurde dann aber wie bei mir aufgedröselt. Hatte auch viel Zustimmung.

    Was waren denn die Gegenargumente? Link?



  • Find ich jetzt nicht mehr. Aber ein Argument war Optimierung. Betrachten wir es als erledigt 😉 Gefällt mir ja selbst nicht wirklich, wie ich es gemacht habe.



  • @Swordfish sagte in Beispiel für ein erstes Mandelbrot?:

    Was waren denn die Gegenargumente? Link?

    Vermutlich genau die selbe Art unterschiedlicher Denkansätze, wie ich schon beispielhaft versucht habe, zu demonstrieren. Soll man die Formel möglichst weit auflösen, so das man etwa fast kostenlos das Divergenzkriterium testen kann? Oder benutzt man einen möglichst flexiblen aber selbsterklärenden Code, den der Compiler aber höchstwahrscheinlich nicht bis zu diesem Grad optimieren wird. Es gibt keine übergreifende Antwort dazu.



  • @zeropage Vielleicht interessant wenn man es auf Geschwindigkeit anlegt: Mandelbrot set. Crunching numbers with SSE, AVX and OpenCL



  • Aha, danke 😌



  • @zeropage Ich interpretiere das als "Nein". In dem Fall würd' ich es "schön" schreiben 🙂



  • Liest sich aber interessant. Was man so alles machen kann...



  • @zeropage sagte in Beispiel für ein erstes Mandelbrot?:

    Außerdem solltest du die Berechnungsfunktionen und UI (Console) strikt trennen.

    Habe ich nicht?

    Edit: Für reine Berechnungsfunktionen benutze ich calc(ulate) als Namenspräfix.

    Jepp, bezieht sich auf den Hinweis von DocShoe wg getMandelbrotSet()?

    Du hast noch "Console.h" in deiner mandelbrot-Headerdatei eingebunden (und verwendest es auch). Dadurch kann die Klasse nicht einfach in anderen Projekten verwendet werden.
    Du solltest die größeren (privaten) Funktionen auch in eine CPP-Datei auslagern (so werden sie ja bisher alle als inline kompiliert).

    Und ja, ich meinte calc(ulate) als Hinweis wegen getMandelbrotSet().



  • @Th69 sagte in Beispiel für ein erstes Mandelbrot?:

    Du hast noch "Console.h" in deiner mandelbrot-Headerdatei eingebunden (und verwendest es auch). Dadurch kann die Klasse nicht einfach in anderen Projekten verwendet werden.

    Ah, verstehe.
    Console.h brauche ich ja für die "Farbe". Sollte ich in diesem Beispiel dann ein Template für nehmen? Und die Funktion plotMandelbrotSet()aus dem namespace nehmen und woanders hin setzen?



  • Du könntest eine eigene spezialisierte Mandelbrot_Console-Datei erstellen.
    Und als Farbe (Color) in deiner Mandelbrot-Klasse könntest du einen eigenen Datentyp benutzen (oder als Template-Parameter [falls du sowohl Farbpaletten als auch z.B. RGB unterstützen möchtest]?).
    Mathematisch /Technisch würde aber ein reiner Indexwert reichen (und nur die Anzeige sorgt dann für die passende Farbumwandlung - dies wäre dann auch wieder strikte Trennung von Logik und UI ;-).


Log in to reply