| @@ -33,5 +33,6 @@ | |||
| \input{analysis16.tex} | |||
| \input{analysis17.tex} | |||
| \input{analysis18.tex} | |||
| \input{analysis19.tex} | |||
| \end{document} | |||
| @@ -0,0 +1,138 @@ | |||
| #include <string> | |||
| struct BoundaryCondition | |||
| { | |||
| virtual bool boundary(Board& board, int i, int j) = 0; | |||
| }; | |||
| class Board { | |||
| public : | |||
| // Konstruktor, erzeuge bool* _fields | |||
| Board(int width, int height, BoundaryCondition cond); | |||
| // Desktruktor, raeume bool* _fields auf | |||
| ~Board(); | |||
| // copy constructor, intializes by copying from 'other' | |||
| Board(Board& other); | |||
| // assignment operator | |||
| Board& operator=(Board& other); | |||
| // gibt die Breite des Bretts zurueck | |||
| double width(); | |||
| // gibt die Hoehe des Bretts zurueck | |||
| double height(); | |||
| // schreibe value an den Pixel (i,j) | |||
| // Ueberlegen Sie wie aus (i,j) den flachen Index bei row-major bekommen | |||
| void updateField(int i, int j, bool value); | |||
| // Zugang zum Pixel (i,j) im 1D Array | |||
| // Ueberlegen Sie wie aus (i,j) den flachen Index bei row-major bekommen | |||
| int operator()(int i, int j); | |||
| // Gibt zurueck ob Feld am Rand liegt | |||
| bool touchesBoundary(int i, int j); | |||
| // Gibt Zahl der lebenden Zellen zurueck, die das Feld umgeben | |||
| int livingCells(int i, int j); | |||
| // Prueft ob Zelle lebt oder nicht | |||
| bool isLiving(int i, int j); | |||
| // Gibt die BoundaryCondition zurueck | |||
| BoundaryCondition boundaryCondition(); | |||
| private : | |||
| int _width; | |||
| int _height; | |||
| bool* _fields; | |||
| void copy(Board& other); | |||
| BoundaryCondition _boundCond; | |||
| }; | |||
| Board::Board(int width, int height, BoundaryCondition cond) { | |||
| // initialize all values | |||
| _width = width; | |||
| _height = height; | |||
| _boundCond = cond; | |||
| _fields = new bool[width * height]; | |||
| } | |||
| Board::~Board() { | |||
| _fields = 0; | |||
| } | |||
| void Board::copy(Board& other) { | |||
| _width = other.width(); | |||
| _height = other.height(); | |||
| _boundCond = other.boundaryCondition(); | |||
| _fields = new bool[_width * _height]; | |||
| for (int i = 0; i < _height; i++) { | |||
| for (int j = 0; j < _width; j++) { | |||
| updateField(i, j, other.isLiving(i, j)); | |||
| } | |||
| } | |||
| } | |||
| Board::Board(Board& other) { | |||
| copy(other); | |||
| } | |||
| Board& Board::operator=(Board& other) { | |||
| if (this == &other) { | |||
| return *this; | |||
| } | |||
| copy(other); | |||
| return *this; | |||
| } | |||
| double Board::width() { | |||
| return _width; | |||
| } | |||
| double Board::height() { | |||
| return _height; | |||
| } | |||
| BoundaryCondition Board::BoundaryCondition() { | |||
| return _boundCond; | |||
| } | |||
| void Board::updateField(int i, int j, bool value) { | |||
| // i = Zeile und wegen row-major Konvention muss i mit der Breite | |||
| // multipliziert werden | |||
| _fields[i * _width + j] = value; | |||
| } | |||
| bool Board::touchesBoundary(int i, int j) { | |||
| return i == _height - 1 || i == -1 || j == _width - 1 || j == -1; | |||
| } | |||
| int Board::livingCells(int i, int j) { | |||
| bool surround[8] = { isLiving(i+1, j+1), isLiving(i, j+1), isLiving(i-1, j+1) | |||
| , isLiving(i+1, j), isLiving(i-1, j) | |||
| , isLiving(i+1, j-1), isLiving(i, j-1), isLiving(i-1, j-1)}; | |||
| int numLiving = 0; | |||
| for (int i = 0; i < 8; i++) { | |||
| if (surround[i] == true) { | |||
| numLiving += 1; | |||
| } | |||
| } | |||
| } | |||
| bool Board::isLiving(int i, int j) { | |||
| if (touchesBoundary(i, j)) { | |||
| return _boundCond.boundary(i, j); | |||
| } else { | |||
| return _fields[i * _width + j]; | |||
| } | |||
| } | |||
| int Board::operator()(int i, int j) { | |||
| // i = Zeile und wegen row-major Konvention muss i mit der Breite | |||
| // Pixel multipliziert werden | |||
| return _fields[i * _width+ j]; | |||
| } | |||
| @@ -0,0 +1,116 @@ | |||
| #include <string> | |||
| #include "pgm.hh" | |||
| // Komplexe Zahl | |||
| struct Complex | |||
| { | |||
| double real; | |||
| double imag; | |||
| }; | |||
| class Canvas | |||
| { | |||
| public : | |||
| // Konstruktor, erzeuge int* _pixels | |||
| Canvas(double center_x, double center_y, | |||
| double width, double height, | |||
| int horPixels, int vertPixels); | |||
| // Desktruktor, raeume int* _pixels auf | |||
| ~Canvas(); | |||
| // gibt die Breite des Bildes zurueck | |||
| double width(); | |||
| // gibt die Hoehe des Bildes zurueck | |||
| double height(); | |||
| // gibt die Anzahl an horizontalen Pixeln | |||
| int horPixels(); | |||
| // gibt die Anzahl an vertikalen Pixeln | |||
| int vertPixels(); | |||
| // gebe die Koordinaten des Pixels (i,j) als Complex zurueck | |||
| Complex coord(int i, int j); | |||
| // schreibe value an den Pixel (i,j) | |||
| // Ueberlegen Sie wie aus (i,j) den flachen Index bei row-major bekommen | |||
| void writePixel(int i, int j, int value); | |||
| // Zugang zum Pixel (i,j) im 1D Array | |||
| // Ueberlegen Sie wie aus (i,j) den flachen Index bei row-major bekommen | |||
| int operator()(int i, int j); | |||
| // schreibe Bild mit Dateinamen filename | |||
| void write(std::string filename); | |||
| private : | |||
| double _center_x; | |||
| double _center_y; | |||
| double _width; | |||
| double _height; | |||
| int _horPixels; | |||
| int _vertPixels; | |||
| int* _pixels; | |||
| }; | |||
| // diese Methode ist bereits implementiert | |||
| void Canvas::write(std::string filename) | |||
| { | |||
| write_pgm(_pixels,_horPixels,_vertPixels,filename); | |||
| } | |||
| Canvas::Canvas(double center_x, double center_y, double width, double height, int horPixels, | |||
| int vertPixels) { | |||
| // initialize all values | |||
| _center_x = center_x; | |||
| _center_y = center_y; | |||
| _width = width; | |||
| _height = height; | |||
| _horPixels = horPixels; | |||
| _vertPixels = vertPixels; | |||
| _pixels = new int[horPixels * vertPixels]; | |||
| } | |||
| Canvas::~Canvas() { | |||
| _pixels = 0; | |||
| } | |||
| double Canvas::width() { | |||
| return _width; | |||
| } | |||
| double Canvas::height() { | |||
| return _height; | |||
| } | |||
| int Canvas::horPixels() { | |||
| return _horPixels; | |||
| } | |||
| int Canvas::vertPixels() { | |||
| return _vertPixels; | |||
| } | |||
| Complex Canvas::coord(int i, int j) { | |||
| Complex c; | |||
| // Bringe die Pixel mit den echten Laengen ins Verhaeltnis | |||
| double y = i * (_height / _vertPixels) - _height / 2; | |||
| double x = j * (_width / _horPixels) - _width / 2; | |||
| // Gebe die Koordinaten in Relation zum gewuenschten Zentrum aus | |||
| c.imag = y + _center_y; | |||
| c.real = x + _center_x; | |||
| return c; | |||
| } | |||
| void Canvas::writePixel(int i, int j, int value) { | |||
| // i = Zeile und wegen row-major Konvention muss i mit der Zahl der horizontalen | |||
| // Pixel multipliziert werden | |||
| _pixels[i*_horPixels + j] = value; | |||
| } | |||
| int Canvas::operator()(int i, int j) { | |||
| // i = Zeile und wegen row-major Konvention muss i mit der Zahl der horizontalen | |||
| // Pixel multipliziert werden | |||
| return _pixels[i*_horPixels + j]; | |||
| } | |||
| @@ -0,0 +1,125 @@ | |||
| #include <iostream> | |||
| #include <unistd.h> // needed for usleep | |||
| #include <string> | |||
| #include <fstream> | |||
| #include "board.hh" | |||
| // Basisklassen | |||
| struct Regel | |||
| { | |||
| virtual void anwenden(Board& board) = 0; | |||
| }; | |||
| class TorusCondition : public BoundaryCondition | |||
| { | |||
| bool boundary(Board& board, int i, int j) { | |||
| if (i == board.height() - 1) { | |||
| return board.isLiving(0, j); | |||
| } else if (i == -1) { | |||
| return board.isLiving(board.height() - 1, j); | |||
| } else if (j == board.width() - 1) { | |||
| return board.isLiving(i, 0); | |||
| } else if (j == -1) { | |||
| return board.isLiving(i, board.width() - 1); | |||
| } | |||
| } | |||
| }; | |||
| class AliveCondition : public BoundaryCondition | |||
| { | |||
| bool boundary(Board& board, int i, int j) { | |||
| return true; | |||
| } | |||
| }; | |||
| class DeadCondition : public BoundaryCondition | |||
| { | |||
| bool boundary(Board& board, int i, int j) { | |||
| return false; | |||
| } | |||
| }; | |||
| class GameOfLifeRules : public Regel | |||
| { | |||
| public : | |||
| //void anwenden(Board& board) { | |||
| // // copy new board from old board | |||
| // Board updated = Board(board.width(), board.height()); | |||
| // for (int i = 0; i < board.height(); i++) { | |||
| // for (int j = 0; i < board.width(); i++) { | |||
| // int n = board.livingCells(i, j); | |||
| // int self = board.isLiving(i, j); | |||
| // if (self && n < 2) { | |||
| // updated.updateField(i, j, false); | |||
| // } else if (!self && n == 3) { | |||
| // updated.updateField(i, j, true); | |||
| // } else if (self && n > 3) { | |||
| // updated.updateField(i, j, false); | |||
| // } else { | |||
| // updated.updateField(i, j, self); | |||
| // } | |||
| // } | |||
| // } | |||
| // board = updated; | |||
| //} | |||
| }; | |||
| // Ein zellulaerer Automat, der Regeln und Datenstrukturen von aussen bekommt | |||
| class Automat | |||
| { | |||
| public : | |||
| // der Datentyp fuer das 2D Bool Array | |||
| Automat(Board& board, Regel& regel) | |||
| : _board(board) | |||
| , _regel(regel) | |||
| {} | |||
| // mache n Schritte | |||
| void doSteps(int n=1) | |||
| { | |||
| for (int i=0; i<n; ++i) | |||
| { | |||
| // Linux-spezifische Art und Weise den Inhalt der Konsole zu loeschen | |||
| // und den Cursor nach oben links zu setzen. | |||
| //std::cout << "\x1B[2J\x1B[H" << "Step " << i << std::endl << _board; | |||
| // Das Wiedergeben der Loesung soll immer 10 Sekunden (=1e7 Mikrosekunden) | |||
| // dauern. Sie koennen diesen Wert auch aendern. | |||
| usleep(1.e7/n); | |||
| //_regel.anwenden(_board); | |||
| } | |||
| } | |||
| private : | |||
| Board& _board; | |||
| Regel& _regel; | |||
| }; | |||
| int main(int argc, char** argv) | |||
| { | |||
| if (argc != 2) | |||
| { | |||
| std::cout << "Usage: ./<progname> <txt-file>" << std::endl; | |||
| return 1; | |||
| } | |||
| Board board = Board(2, 2, AliveCondition); | |||
| board.updateField(1,1, true); | |||
| board.livingCells(1,1); | |||
| // Initialisiere Datenstruktur des 2D Bool Array | |||
| // TODO | |||
| // Ueberlegen Sie sich wie Sie einen Startzustand in das 2D Bool Array bekommen | |||
| // Lesen Sie ggf. | |||
| // (1) eine solche Textdatei mittels Filestream ein | |||
| // (2) ueberladen Sie Operatoren operator<< oder operator>> | |||
| // TODO | |||
| // - legen Sie eine Instanz der Randbedingung an | |||
| // - legen Sie eine Instanz des Regelsystems an | |||
| // - Initialisieren Sie den zellulaeren Automaten | |||
| // Experimentieren Sie hier mit Ihrem Automaten | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,44 @@ | |||
| \documentclass[uebung]{../../../lecture} | |||
| \title{IPI: Übungsblatt 9} | |||
| \author{Samuel Weidemaier, Christian Merten} | |||
| \usepackage[]{listings} | |||
| \usepackage{xcolor} | |||
| \lstdefinestyle{mystyle}{ | |||
| commentstyle=\color{gray}, | |||
| language=C++, | |||
| keywordstyle=\color{blue}, | |||
| numberstyle=\tiny\color{gray}, | |||
| stringstyle=\color{black}, | |||
| basicstyle=\ttfamily\footnotesize, | |||
| breakatwhitespace=false, | |||
| breaklines=true, | |||
| captionpos=b, | |||
| keepspaces=true, | |||
| numbers=left, | |||
| numbersep=5pt, | |||
| showspaces=false, | |||
| showstringspaces=false, | |||
| showtabs=false, | |||
| tabsize=2 | |||
| } | |||
| \lstset{style=mystyle} | |||
| \begin{document} | |||
| \begin{aufgabe}[Mandelbrot-Menge] | |||
| (a) und (b) siehe \textit{mandelbrot.cc} und \textit{canvas.hh}. | |||
| (c) Je höher \lstinline{threshold}, desto heller wird das Bild, allerdings ist bereits zwischen 100 und 1000 | |||
| kein großer Unterschied mehr zu erkennen. Bei sehr kleinen \lstinline{threshold}s werden die Randbereiche | |||
| wieder schwarz. | |||
| Bei geringen \lstinline{maxIt}s ist die Genauigkeit sehr gering, je höher desto feiner wird das Bild. | |||
| Allerdings ist auch hier zwischen 100 und 1000 bereits kein Unterschied mehr zu erkennen. | |||
| \end{aufgabe} | |||
| \end{document} | |||
| @@ -0,0 +1,61 @@ | |||
| #include <iostream> | |||
| #include <cmath> | |||
| #include <string> | |||
| // Datentyp Complex in der Datei canvas.hh | |||
| #include "canvas.hh" | |||
| // Summiert zwei komplexe Zahlen z und c und schreibt das Ergebnis in z | |||
| void add_complex(Complex& z, Complex& c) { | |||
| z.real += c.real; | |||
| z.imag += c.imag; | |||
| } | |||
| // Multipliziert zwei komplexe Zahlen z und c und schreibt das Ergebnis in z | |||
| void multiply_complex(Complex& z, Complex& c) { | |||
| double re = z.real*c.real - z.imag*c.imag; | |||
| double im = z.real*c.imag + z.imag*c.real; | |||
| z.real = re; | |||
| z.imag = im; | |||
| } | |||
| // Betrag einer komplexen Zahl | |||
| double betrag(Complex z) | |||
| { | |||
| return std::sqrt(std::pow(z.real, 2) + std::pow(z.imag, 2)); | |||
| } | |||
| // Generiert die Mandelbrot Menge und speichert sie in canvas mit threshold als Entfernung, ab dem | |||
| // ein Punkt ,,entkommt'', mit maxIt als maximale Anzahl an Iterationen, die für | |||
| // die Folge durchgeführt werden soll. Wird als PGM Bild unter filename gespeichert | |||
| void mandelbrot(Canvas& canvas, double threshold, int maxIt, std::string filename) { | |||
| for (int i = 0; i < canvas.vertPixels(); i++) { | |||
| for (int j = 0; j < canvas.horPixels(); j++) { | |||
| Complex c = canvas.coord(i, j); | |||
| Complex z; | |||
| z.real = 0; | |||
| z.imag = 0; | |||
| int neededIterations = -1; | |||
| for (int k = 1; k <= maxIt; k++) { | |||
| multiply_complex(z, z); | |||
| add_complex(z, c); | |||
| if (betrag(z) > threshold) { | |||
| neededIterations = k; | |||
| break; | |||
| } | |||
| } | |||
| if (neededIterations == -1) { | |||
| canvas.writePixel(i, j, 0); | |||
| } else { | |||
| canvas.writePixel(i, j, std::log(neededIterations)*100); | |||
| } | |||
| } | |||
| } | |||
| canvas.write(filename); | |||
| } | |||
| int main() | |||
| { | |||
| Canvas canvas = Canvas(-1, 0, 4, 3, 4000, 3000); | |||
| mandelbrot(canvas, 1000, 10000, "mandelbrot.pgm"); | |||
| } | |||
| @@ -0,0 +1,64 @@ | |||
| #include <string> | |||
| #include <fstream> | |||
| #include <cmath> | |||
| void write_pgm(int* pixels, int horPixels, int vertPixels, std::string filename) | |||
| { | |||
| if(horPixels == 0 || vertPixels == 0) | |||
| { | |||
| std::cerr << "Leeres Pixel Array" << std::endl; | |||
| return; | |||
| } | |||
| // write file | |||
| // unfortunately we cannot use C++-11 which would simplify our life in that case | |||
| std::ofstream outfile; | |||
| outfile.open(filename.c_str()); | |||
| outfile << "P2" << std::endl | |||
| << horPixels << " " << vertPixels << std::endl; | |||
| // renormalize if necessary | |||
| int maxVal = 0; | |||
| for(int entry=0; entry<horPixels*vertPixels; entry++) | |||
| maxVal = std::max(pixels[entry],maxVal); | |||
| // renormalizing output | |||
| if(maxVal > 65535) | |||
| { | |||
| outfile << 65535 << std::endl; | |||
| for(int j=0, entry=0; j<vertPixels; j++) | |||
| { | |||
| for(int i=0; i<horPixels; i++, entry++) | |||
| { | |||
| if(pixels[entry] < 0) | |||
| { | |||
| std::cerr << "Negativer Eintrag bei (" << i << ", " << j << ")" << std::endl; | |||
| return; | |||
| } | |||
| outfile << std::floor(pixels[entry] * (65535./maxVal)) << " "; | |||
| } | |||
| outfile << std::endl; | |||
| } | |||
| } | |||
| // normal output | |||
| else | |||
| { | |||
| outfile << maxVal << std::endl; | |||
| for(int j=0; j<vertPixels; j++) | |||
| { | |||
| for(int i=0; i<horPixels; i++) | |||
| { | |||
| int entry = i + horPixels*(vertPixels-1-j); | |||
| if(pixels[entry] < 0) | |||
| { | |||
| std::cerr << "Negativer Eintrag bei (" << i << ", " << j << ")" << std::endl; | |||
| return; | |||
| } | |||
| outfile << pixels[entry] << " "; | |||
| } | |||
| outfile << std::endl; | |||
| } | |||
| } | |||
| outfile.close(); | |||
| } | |||