grafische Minimalanwendung für Matrizenklasse



  • Hi,
    ich bin zwar noch nicht fertig mit der Matrizenklasse, vor allem fehlt noch das Invertieren, habe aber trotzdem schon eine Minimalanwendung gefertigt. Das ganze hat keinen tieferen Sinn, ich mag es nur, mich mit Grafik und Programmieren zu beschäftigen, auch wenn ich nicht soviel kapiere.

    Und vielleicht gibt es ja wieder arge Schnitzer oder Irrtümer.
    Das Console::Dings erst mal nicht beachten, vielleicht ein anderes Mal.

    #pragma once
    
    #include "Mat2D.h" //dort sind nur die namespace-Funktionen
    				   //darüber wird auch "Matrix.h" eingebunden
    
    template<typename T>
    class Point2D
    {
    public:
    	using val_t = T;
    	using size_t = std::size_t;
    
    	Point2D() = default;
    	Point2D(const val_t x, const val_t y)
    		: point{ Mat2D::Point2D<val_t>(x, y) } {}
    
    	Point2D(const Mat2D_T<val_t>& mT)
    		: point{ mT } {}
    
    	Point2D(const std::vector<val_t>& pVec)
    	{
    		if (pVec.size() != 2)
    			throw std::out_of_range("Point2D(vector): vector.size(): "
    				+ std::to_string(pVec.size()));
    
    		point(0, 0) = pVec[0];
    		point(0, 1) = pVec[1];
    		point(0, 2) = 1;
    	}
    
    	Point2D<val_t> operator*(const Mat2D_T<val_t>& mT)
    	{
    		return multiply((*this), mT);
    	}
    
    	val_t operator()(const size_t row, const size_t column) const
    	{
    		if (row > 1 || column > 2)
    			throw std::out_of_range("Point2D::operator(): row: "
    				+ std::to_string(row) + " column: "
    				+ std::to_string(column));
    
    		return point(row, column);
    	}
    
    	val_t& operator()(const size_t row, const size_t column)
    	{
    		if (row > 1 || column > 2)
    			throw std::out_of_range("Point2D::operator(): row: "
    				+ std::to_string(row) + " column: "
    				+ std::to_string(column));
    
    		return point(row, column);
    	}
    
    	val_t x() const { return (*this)(0, 0); }
    	val_t y() const { return (*this)(0, 1); }
    	val_t& x() { return (*this)(0, 0); }
    	val_t& y() { return (*this)(0, 1); }
    
    private:
    	Mat2D_T<val_t> point = Mat2D::Point2D<val_t>(0, 0);
    };
    
    template<typename val_t>
    Point2D<val_t> multiply(const Point2D<val_t>& p, const Mat2D_T<val_t>& mT)
    {
    	Mat2D_T<val_t> pMat = Mat2D::Point2D<val_t>(p(0, 0), p(0, 1)) * mT;
    	return pMat;
    }
    
    #pragma once
    
    #include "Console.h"
    #include "Point.h"
    
    class Plot2D
    {
    public:
    	Plot2D(const int width, const int height) :
    		width_(width), height_(height),
    		max_width(Console::width()), max_height(Console::height())
    	{
    		fillCh = Char(console::SOLID, console::BLUE, console::BLUE);
    		axisCh = Char('+', console::DBLUE);
    		gridCh = Char('o', console::DBLUE);
    		resize(width_, height_);
    	}
    
    	int width() const { return width_; }
    	int height() const { return height_; }
    	int centerX() const { return centerX_; }
    	int centerY() const { return centerY_; }
    
    	void resize(const int width, const int height)
    	{
    		width_ = width;
    		height_ = height;
    		const float xRight = width_  - centerX_;
    		const float xLeft = -centerX_;
    		const float yTop = height_ - centerY_ - 1;
    		const float yBottom = -centerY_ - 1;
    		windowMat(0, 0) = max_width / (xRight - xLeft);
    		windowMat(1, 1) = -max_height / (yTop - yBottom);
    		windowMat(2, 0) = -max_width * xLeft / (xRight - xLeft);
    		windowMat(2, 1) = max_height * yTop / (yTop - yBottom);
    
    		plotCOSystem();
    	}
    
    	void setCenter(const int x, const int y)
    	{
    		centerX_ = x;
    		centerY_ = y;
    		resize(width_, height_);
    	}
    
    	void scale(const float x, const float y)
    	{
    		Mat2D_T<float> scaleMat = Mat2D::ScaleMat<float>(std::abs(x), std::abs(y));
    		plotMat = plotMat * scaleMat;
    	}
    
    	void rotate(const int phi)
    	{
    		Mat2D_T<float> rotMat = Mat2D::RotMat<float>(phi);
    		plotMat = plotMat * rotMat;
    	}
    
    	void dot(const int X, const int Y, const Char& chr)
    	{
    		Coordinate p(X, Y, windowMat * plotMat);
    		put(p.x, p.y, chr);
    	}
    
    	void line(const int X0, const int Y0, const int X1, const int Y1, const Char& chr)
    	{
    		Coordinate p0(X0, Y0, windowMat * plotMat);
    		Coordinate p1(X1, Y1, windowMat * plotMat);
    		int x0 = p0.x;
    		int y0 = p0.y;
    		int x1 = p1.x;
    		int y1 = p1.y;
    		putLine(x0, y0, x1, y1, chr);
    	}
    
    	void rectangle(const int x0, const int y0, const int x4, const int y4, const Char& chr)
    	{
    		line(x0, y0, x4, y0, chr);
    		line(x4, y0, x4, y4, chr);
    		line(x4, y4, x0, y4, chr);
    		line(x0, y4, x0, y0, chr);
    	}
    
    	void polygon(const std::vector<Point2D<float>>& points, const Char& chr)
    	{
    		for (std::size_t i = 0; i < points.size() - 1; ++i)
    			line(points[i](0, 0), points[i](0, 1), points[i + 1](0, 0), points[i + 1](0, 1), chr);
    		
    		line(points.back()(0, 0), points.back()(0, 1), points.front()(0, 0), points.front()(0, 1), chr);
    	}
    
    	void plotCOSystem()
    	{
    		Console::fill(fillCh);
    		plotGrid(gridCh);
    		plotAxes(axisCh);
    	}
    
    	void plotAxes(const Char& ax)
    	{
    		Coordinate pw0(-max_width, 0, windowMat * plotMat);
    		Coordinate pw1(max_width, 0, windowMat * plotMat);
    		putLine(pw0.x, pw0.y, pw1.x, pw1.y, ax);
    		Coordinate ph0(0, -max_height, windowMat * plotMat);
    		Coordinate ph1(0, max_height, windowMat * plotMat);
    		putLine(ph0.x, ph0.y, ph1.x, ph1.y, ax);
    	}
    
    	void plotGrid(const Char& gd)
    	{
    		for (int y = 0; y < max_height; y += 10)
    		{
    			for (int x = 0; x < max_width; x += 10)
    			{
    				Coordinate p(x, y, windowMat * plotMat);
    				put(p.x, p.y, gd);
    				Coordinate pXM(x, y, Mat2D::mXMat<float>() * windowMat * plotMat);
    				put(pXM.x, pXM.y, gd);
    			}
    		}
    		for (int y = 0; y > -max_height; y -= 10)
    		{
    			for (int x = 0; x > -max_width; x -= 10)
    			{
    				Coordinate p(x, y, windowMat * plotMat);
    				put(p.x, p.y, gd);
    				Coordinate pXM(x, y, Mat2D::mXMat<float>() * windowMat * plotMat);
    				put(pXM.x, pXM.y, gd);
    			}
    		}
    	}
    
    	void clear() { plotCOSystem(); }
    	void display() { Console::writeBuffer(); }
    
    private:
    	int width_ = 0;
    	int height_ = 0;
    	int max_width = 0;
    	int max_height = 0;
    	int centerX_ = 0;
    	int centerY_ = 0;
    
    	Char fillCh = Char::Default();
    	Char axisCh = Char::Default();
    	Char gridCh = Char::Default();
    
    	Mat2D_T<float> windowMat = Mat2D::UnitMat<float>(3);
    	Mat2D_T<float> plotMat = Mat2D::UnitMat<float>(3);
    
    	class Coordinate
    	{
    	public:
    		Coordinate(const float xf, const float yf, const Mat2D_T<float>& mf)
    		{
    			Mat2D_T<float> p = Mat2D::Point2D<float>(xf, yf);
    			p = p * mf;
    			x = Math::toInt(p(0, 0));
    			y = Math::toInt(p(0, 1));
    		}
    		int x = 0;
    		int y = 0;
    	};
    
    	void put(const int x, const int y, const Char& chr)
    	{
    		Console::putChar(x, y, chr);
    	}
    
    	void putLine(int x0, int y0, int x1, int y1, const Char& chr)
    	{
    		int dx = std::abs(x1 - x0);
    		int dy = -std::abs(y1 - y0);
    		int sx = x0 < x1 ? 1 : -1;
    		int sy = y0 < y1 ? 1 : -1;
    		int ed = dx + dy;
    
    		for (;;)
    		{
    			put(x0, y0, chr);
    			if (x0 == x1 && y0 == y1) break;
    
    			int ed2 = 2 * ed;
    			if (ed2 > dy)
    			{
    				ed += dy;
    				x0 += sx;
    			}
    			if (ed2 < dx)
    			{
    				ed += dx;
    				y0 += sy;
    			}
    		}
    	}
    };
    

    Und ein Beispiel

    #include "Plot.h"
    
    void ready()
    {
    	std::cerr << "\nready_\n";
    	std::cin.sync();
    	std::cin.get();
    }
    
    std::vector<Point2D<float>> getPoints()
    {
    	std::initializer_list<std::initializer_list<float>> pList = { { 0., 0. }, { 16., 0. }, { 16., 9. }, { 8, 14. }, { 0., 9. } };
    	std::vector<Point2D<float>> pVec;
    	for (const auto& point : pList)
    	{
    		Point2D<float> p{ point };
    		pVec.push_back(p);
    	}
    	return pVec;
    }
    
    void movePoints(std::vector<Point2D<float>>& points, const float tx, const float ty)
    {
    	for (auto& p : points)
    		p = p * Mat2D::TransMat<float>(tx, ty);
    }
    
    void rotatePoints(std::vector<Point2D<float>>& points, const int phi)
    {
    	for (auto& p : points)
    		p = p * Mat2D::RotMat<float>(phi);
    }
    
    int main()
    {
    	try {
    
    		/*Vorbereitungen*/
    		std::vector<Point2D<float>> OrigPoints = getPoints();
    		std::vector<Point2D<float>> points = OrigPoints;
    		Char dot0(char(254), console::GREY);  //"Zeichenfarbe1"
    		Char dot1(char(254), console::BLACK); //Zeichenfarbe2
    	
    		/*Console und Plot erstellen*/
    		Console::create(180, 100, 8, 8); 
    		int cx = Console::width();
    		int cy = Console::height();
    		Plot2D plot(cx, cy); //selbes Verhältnis wie Console
    
    		/*Drehungen um einen beliebigen Punkt*/
    		float tx = 35; 
    		float ty = 25; //um diesen Punkt 
    
    		plot.setCenter(40, 35);			//Nullpunkt setzen
    		plot.rotate(15);				//COSystem um 15° kippen
    		plot.polygon(OrigPoints, dot0); //zeichne Originalposition
    		movePoints(points, tx, ty);     //bewegen zum Punkt
    		plot.polygon(points, dot1);     //zeichnen 
    		plot.display();				    //und darstellen
    
    		tx = tx + 8;
    		ty = ty + 7; //anpassen an Objektgröße
    
    		for (int i = 0; i < 1000; ++i)
    		{
    			Sleep(20);						
    			movePoints(points, -tx, -ty);   //bewege zum Nullpunkt
    			rotatePoints(points, 3);        //rotiere
    			movePoints(points, tx, ty);     //bewege zurück
    			plot.clear();				    //Fensterinhalt "löschen" bzw Plot neu zeichnen	
    			plot.polygon(OrigPoints, dot0); //zeichne Originalposition
    			plot.polygon(points, dot1);     //zeichnen
    			plot.display();                 //darstellen
    		}
    		
    		ready();
    
    	}
    	catch (const std::exception& err) {
    		std::cerr << "Error: " << err.what() << '\n';
    	}
    }
    


  • Eigentlich heißt es ja, keine Antworten sind gute Antworten. Aber wenn so gar nichts kommt, fängt man schon an zu grübeln woran das liegen könnte?



  • @zeropage Bei 280 Zeilen Code wäre halt eine genauere Frage als "vlt gibt es ja grobe Schnitzer" nicht schlecht.

    Funktioniert alles wie erwartet (hast du Unittests?) - ja, dann gut. Nein, dann hast noch eine offene Baustelle.

    Nach ganz kurzen drauf gucken vom Smartphone, du hast den Default Konstruktor auf Default gesetzt,was ist mit Copy, Move, Assignment?



  • @Schlangenmensch sagte in grafische Minimalanwendung für Matrizenklasse:

    @zeropage Bei 280 Zeilen Code wäre halt eine genauere Frage als "vlt gibt es ja grobe Schnitzer" nicht schlecht.

    Ahja, ok. Verstehe.

    Funktioniert alles wie erwartet (hast du Unittests?) - ja, dann gut. Nein, dann hast noch eine offene Baustelle.

    Funktionieren tut alles bestens. Wenn nichts ist, hilft es vielleicht der Nachwelt. Als ich mit den Matrizen angefangen habe, hätte cih sowas gebrauchen können.

    Nach ganz kurzen drauf gucken vom Smartphone, du hast den Default Konstruktor auf Default gesetzt,was ist mit Copy, Move, Assignment?

    Wozu brauche ich die? Mit denen gabs ja unter anderem viel Kritik. Oder war das jetzt nur ein schneller Allgemeinhinweis? Dann doch die Frage, wozu würde ich die gebrauchen können?



  • @zeropage ich muss immer überlegen, wann mir der Compiler das abnimmt, daher schreibe ich häufig alle =default hin, wenn ich einen Konstruktor selbst definiere. Aber in deinem Fall, besuchst du das nicht, wenn ich das grade richtig sehe.



  • Schön, danke für die Rückmeldung 🙂


Anmelden zum Antworten