[A] Primitive zeichnen



  • Schreib einfach mal einen Teil und skizziere den Rest, also Überschrift und ein paar Stichwörter und poste es mal. Dann lässt sich besser darüber reden. Falls es sich als zu lang rausstellen würde lässt sich in dem Fall noch einfach splitten und nicht viel Arbeit war um sonst.



  • Es ist doch auch sonst nichts umsonst, umstellen kann man immer noch - nur das macht zusätzliche Arbeit. 🙂



  • estartu schrieb:

    Es ist doch auch sonst nichts umsonst, umstellen kann man immer noch - nur das macht zusätzliche Arbeit. 🙂

    Einleitung, Überleitung, Schlussfolgerung, "roter Faden" es gibt schon einige Sachen die man bei einer Umstellung neu machen muss.



  • ich werde zunächst mal das Rastern von Linien abhandeln - dazu werde ich einen erweiteten Bresenham Algo verwenden



  • #include "CImg-1.2.2.1\CImg.h"
    #include <iostream>
    using namespace cimg_library;
    
    ///////////////////////////////////////////////////////////////////////////////////////////
    // Setzt ein bestimmtes Pixel in einer Rastergrafik.
    template <class T>
    void draw_pixel(CImg<T>& img, int x, int y, T red = 255, T green = 0, T blue = 0)
    {
    	if(x < 0 ||
    	   y < 0 ||
    	   img.height < y ||
    	   img.width < x)
    	   return;
    
    	unsigned char color[3]={red,green,blue};	// Define a purple color
    	y = img.height-y-1;
    	img.draw_line(x,y,x,y,color);
    
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////////////////////////
    // Vertauscht die Werte zweier Variablen.
    template <typename T>
    void swap(T& x, T& y)
    {
    	T tmp = x;
    	x = y;
    	y = tmp;
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////////////////////////
    // Vom linken zum rechten Punkt laufen.
    template <class T>
    void draw_line1(CImg<T>& img, int x1, int y1, int x2, int y2)
    {
    	// (x1,y1) = Koordinaten des linken Punktes
    	// (x2,y2) = Koordinaten des rechten Punktes
    	if(x1 > x2)
    	{
    		swap(x1,x2);
    	    swap(y1,y2);
    	}
    
    	// Steigung berechnen
    	float m = float(y2 - y1) / (x2 - x1);
    
    	// y-Achsen Abschnitt berechnen
    	float t = y1 - m * x1;
    
    	for(int x = x1; x <= x2; x++)
    	{
    		float y = m*x+t;
    
    		draw_pixel(img, x,y+0.5); // ab 0.5 wird aufgerundet
    	}
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////////////////////////
    // Vom unten nach oben laufen.
    template <class T>
    void draw_line2(CImg<T>& img, int x1, int y1, int x2, int y2)
    {
    	// (x1,y1) = Koordinaten des unteren Punktes
    	// (x2,y2) = Koordinaten des oberen Punktes
    	if(y1 > y2)
    	{
    		swap(x1,x2);
    	    swap(y1,y2);
    	}
    
    	// Steigung berechnen
    	float m = float(y2 - y1) / (x2 - x1);
    
    	// y-Achsen Abschnitt berechnen
    	float t = y1 - m * x1;
    
    	for(int y = y1; y <= y2; y++)
    	{
    		float x = (y-t)/m;
    
    		draw_pixel(img, x+0.5,y); // ab 0.5 wird aufgerundet
    	}
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////////
    /// Findet heraus entlang welche Achse (x oder y) die Strecke am längsten ist und wählt 
    /// dann den geeigenten Algorihtmus
    template <class T>
    void draw_line3(CImg<T>& img, int x1, int y1, int x2, int y2)
    {
    	int x_axis_length = abs(x1-x2);
    	int y_axis_length = abs(y1-y2);
    
    	if(x_axis_length > y_axis_length)
    	{
    		draw_line1(img, x1, y1, x2, y2);
    	}
    	else
    	{
    		draw_line2(img, x1, y1, x2, y2);
    	}
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////////////////////////
    // Symmetrie Eigenschaften ausnutzen. Ein Punkt im Oktant 1 mit (x,y) kann auf Oktant 2 
    // abgebildet werden indem man die x und y Werte einfach vertauscht.
    template <class T>
    void draw_line4(CImg<T>& img, int x1, int y1, int x2, int y2)
    {
    	int x_axis_length = abs(x1-x2);
    	int y_axis_length = abs(y1-y2);
    
    	// x- und y-Werte vertauschen wenn y-Achse Hauptachse ist
    	if(x_axis_length <= y_axis_length)
    	{
    		swap(x1,y1);
    		swap(x2,y2);
    	}
    
    	// (x1,y1) = Koordinaten des linken Punktes
    	// (x2,y2) = Koordinaten des rechten Punktes
    	if(x1 > x2)
    	{
    		swap(x1,x2);
    		swap(y1,y2);
    	}
    
    	// Steigung berechnen
    	float m = float(y2 - y1) / (x2 - x1);
    
    	// y-Achsen Abschnitt berechnen
    	float t = y1 - m * x1;
    
    	for(int x = x1; x <= x2; x++)
    	{
    		float y = m*x+t;
    
    		if(x_axis_length > y_axis_length)
    			draw_pixel(img, x,y+0.5); // ab 0.5 wird aufgerundet
    		else
    			draw_pixel(img, y+0.5,x); // Hier werden die zuvor vertauschten Werte wieder zurückgetäuscht
    	}
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////////////////////////
    // Experiment
    template <class T>
    void draw_horizontal_line(CImg<T>& img, int x1, int x2, int y)
    {
    	for(int x = x1; x <= x2; x++)
    	{
    		draw_pixel(img, x, y);
    	}
    }
    
    template <class T>
    void draw_line5(CImg<T>& img, int x1, int y1, int x2, int y2)
    {
    	// vorerst nur 1. Oktant mit steigung   m € ]0;1[
    	// und x1 < x2
    	// und y1 < y2
    
    	int a = abs(x1-x2);
    	int b = abs(y1-y2);
    	int r = a % b;
    	int e = a % (2*b);
    
    	int outer_run_length_short = a / (2*b);
    	int inner_run_length_short = a / b;
    
    	if(e == 0)
    	{
    		draw_horizontal_line(img, x1, x1+outer_run_length_short+1,y1);
    		x1+= outer_run_length_short + 1 +1;
    	}
    	else
    	{
    		draw_horizontal_line(img, x1, x1+outer_run_length_short,y1);
    		x1+= outer_run_length_short + 1;
    	}
    
    	e += r;
    
    	if(e > b)
    		e -= b;
    
    	for(int y = y1+1; y < y2; y++)   // von unten nach oben zeichnen
    	{
    		if(e + r >= b)
    		{
    			draw_horizontal_line(img, x1, x1+ inner_run_length_short + 1, y);
    			x1+= inner_run_length_short + 1 +1;
    		}
    		else
    		{
    			draw_horizontal_line(img, x1, x1+ inner_run_length_short, y);
    			x1+= inner_run_length_short + 1;
    		}	
    
    		e += r;
    
    		if(e > b)
    			e -= b;
    	}
    
    	draw_horizontal_line(img, x1, x1+outer_run_length_short,y2);
    }
    
    template <typename T>
    class Vector2D
    {
    public:
    	Vector2D(T x, T y)
    	{
    		m_X = x;
    		m_Y = y;
    	}
    
    	T getX()
    	{
    		return m_X;
    	}
    
    	T getY()
    	{
    		return m_Y;
    	}
    private:
    	T m_X;
    	T m_Y;
    };
    
    template <typename T>
    class StraightLine2D
    {
    public:
    	StraightLine2D(int x1, int y1, int x2, int y2)
    	{
    		m_Slope = T(y2 - y1) / T(x2 - x1);
    
    		// y=mx+t
    		// t=y-mx
    		m_Intercept = y1-m_Slope*x1;
    	}
    
    	StraightLine2D(T slope, T intercept)
    	{
    		m_Slope = slope;
    		m_Intercept = intercept;
    	}
    
    	StraightLine2D(T intercept)
    	{
    		m_Intercept = intercept;
    	}
    
    	T getIntercept() const
    	{
    		return m_Intercept;
    	}
    
    	T getSlope() const
    	{
    		return m_Slope;
    	}
    private:
    	T m_Slope;
    	T m_Intercept;
    };
    
    template <typename T>
    Vector2D<T> intersect(const StraightLine2D<T> &a, const StraightLine2D<T> &b)
    {
    	Vector2D<T> intersectionPoint((b.getIntercept()-a.getIntercept())/a.getSlope(), b.getIntercept());
    	return intersectionPoint;
    }
    
    template <class T>
    void experiment(CImg<T>& img, int x1, int y1, int x2, int y2)
    {
    	using namespace std;
    
    	// vorerst nur 1. Oktant mit steigung   m € ]0;1[
    	// und y1 < y2
    
    	StraightLine2D<double> line(x1,y1,x2,y2);
    
    	double last = y1;
    
    	int sum_of_run_lengths = 0;
    
    	for(int y = y1; y <= y2; y++)
    	{
    		StraightLine2D<double> horizontal = 0.5 + y;
    
    		Vector2D<double> intersectionPoint = intersect(line, horizontal);
    
    		int run_length = floor(intersectionPoint.getX())-floor(last); 
    		sum_of_run_lengths += run_length;
    
    		cout<<"run length "<<y-y1<<": "<<"("<<intersectionPoint.getX()<<","<<intersectionPoint.getY()<<") "
    <<last<<" bis "<<intersectionPoint.getX()<<" length="<<run_length<<endl;
    
    		last = intersectionPoint.getX();
    	}
    
    	cout<<"sum_of_run_lengths: "<<sum_of_run_lengths<<" (Check: "<<x2-x1<<")"<<endl;
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    
    int main() {
    
    	CImg<unsigned char> img(640,400,1,3);	// Define a 640x400 color image with 8 bits per color component.
    	img.fill(255, 255, 255);		
    
    	draw_line1(img, 10, 10, 200, 57);
    	draw_line5(img, 10, 10, 200, 57);
    
    	experiment(img, 10, 10, 200, 57);
    	//draw_line5(img, 200, 57, 10, 10);
    	//draw_line5(img, 100, 100, 200, 200);
    	//draw_line5(img, 100, 100, 200, 100);
    	//draw_line5(img, 100, 100, 140, 200);
    	//draw_line5(img, 100, 100, 40, 200);
    
    	system("pause");
    
    //	img.save_png("test.png");
    //	img.display("Line Drawing");			// Display the image in a display window.
    	return 0;
    }
    
    run length 0: (12.0213,10.5) 10 bis 12.0213 length=2
    run length 1: (16.0638,11.5) 12.0213 bis 16.0638 length=4
    run length 2: (20.1064,12.5) 16.0638 bis 20.1064 length=4
    run length 3: (24.1489,13.5) 20.1064 bis 24.1489 length=4
    run length 4: (28.1915,14.5) 24.1489 bis 28.1915 length=4
    run length 5: (32.234,15.5) 28.1915 bis 32.234 length=4
    run length 6: (36.2766,16.5) 32.234 bis 36.2766 length=4
    run length 7: (40.3191,17.5) 36.2766 bis 40.3191 length=4
    run length 8: (44.3617,18.5) 40.3191 bis 44.3617 length=4
    run length 9: (48.4043,19.5) 44.3617 bis 48.4043 length=4
    run length 10: (52.4468,20.5) 48.4043 bis 52.4468 length=4
    run length 11: (56.4894,21.5) 52.4468 bis 56.4894 length=4
    run length 12: (60.5319,22.5) 56.4894 bis 60.5319 length=4
    run length 13: (64.5745,23.5) 60.5319 bis 64.5745 length=4
    run length 14: (68.617,24.5) 64.5745 bis 68.617 length=4
    run length 15: (72.6596,25.5) 68.617 bis 72.6596 length=4
    run length 16: (76.7021,26.5) 72.6596 bis 76.7021 length=4
    run length 17: (80.7447,27.5) 76.7021 bis 80.7447 length=4
    run length 18: (84.7872,28.5) 80.7447 bis 84.7872 length=4
    run length 19: (88.8298,29.5) 84.7872 bis 88.8298 length=4
    run length 20: (92.8723,30.5) 88.8298 bis 92.8723 length=4
    run length 21: (96.9149,31.5) 92.8723 bis 96.9149 length=4
    run length 22: (100.957,32.5) 96.9149 bis 100.957 length=4
    run length 23: (105,33.5) 100.957 bis 105 length=5
    run length 24: (109.043,34.5) 105 bis 109.043 length=4
    run length 25: (113.085,35.5) 109.043 bis 113.085 length=4
    run length 26: (117.128,36.5) 113.085 bis 117.128 length=4
    run length 27: (121.17,37.5) 117.128 bis 121.17 length=4
    run length 28: (125.213,38.5) 121.17 bis 125.213 length=4
    run length 29: (129.255,39.5) 125.213 bis 129.255 length=4
    run length 30: (133.298,40.5) 129.255 bis 133.298 length=4
    run length 31: (137.34,41.5) 133.298 bis 137.34 length=4
    run length 32: (141.383,42.5) 137.34 bis 141.383 length=4
    run length 33: (145.426,43.5) 141.383 bis 145.426 length=4
    run length 34: (149.468,44.5) 145.426 bis 149.468 length=4
    run length 35: (153.511,45.5) 149.468 bis 153.511 length=4
    run length 36: (157.553,46.5) 153.511 bis 157.553 length=4
    run length 37: (161.596,47.5) 157.553 bis 161.596 length=4
    run length 38: (165.638,48.5) 161.596 bis 165.638 length=4
    run length 39: (169.681,49.5) 165.638 bis 169.681 length=4
    run length 40: (173.723,50.5) 169.681 bis 173.723 length=4
    run length 41: (177.766,51.5) 173.723 bis 177.766 length=4
    run length 42: (181.809,52.5) 177.766 bis 181.809 length=4
    run length 43: (185.851,53.5) 181.809 bis 185.851 length=4
    run length 44: (189.894,54.5) 185.851 bis 189.894 length=4
    run length 45: (193.936,55.5) 189.894 bis 193.936 length=4
    run length 46: (197.979,56.5) 193.936 bis 197.979 length=4
    run length 47: (202.021,57.5) 197.979 bis 202.021 length=5
    sum_of_run_lengths: 192 (Check: 190)
    


  • Hier kann man schon ein bischen was von dem Artikel lesen (schreibe ihn mit Word daher nur mal als PDF)
    http://loop.servehttp.com/~vertexwahn/uploads/LineDrawing.pdf



  • *gelöscht*



  • Status: Warte darauf, dass das pic-Tag wieder funktioniert - Artikel wäre eigentlich woweit fertig...



  • Poste ihn trotzdem mal. Auch wenn es gerade Problem mit der Forensoftware gibt kann man trotzdem den Text überlesen. An die Bilder kommt man ja auch, wenn auch nur etwas umständlich.



  • ok ich hab ihn gepostet (siehe 1ter Beitrag) - bisher verwende ich ca. 80 Bilder, da ich auch die Formeln als Bilder abgespeichert habe - dazu könnte ich natürlich auch Latex verwenden...





  • Ich finde, dass deinen Weg diesen Algorithmus zu erklären sich viel leichter geometrisch erklären lässt als mit mathematischen Formeln. Hier ist mein Ansatz. Vielleicht kannst du ja irgendetwas gebrauchen.

    Als erstes würde ich das Segment nicht in der Mitte eines Pixels starten lassen sondern an einer Kreuzung des Rasters. Hierdurch gibt es nur noch 2 verschiedene Teilsegmente, deren Länge sich auch leicht herausfinden lässt.

    Man beschränkt sich auf das erste Oktant und auf Segmente welche vom Nullpunkt aus auf einen Punkt (x, y) gehen (also an sich zeichnet man einen Vektor). Man stellt sich das Segment zum Punkt (y, y) vor. Dieser Spezialfall ist einfach und besteht aus y 1-Pixel Teilsegmenten.

    Diese zieht man entlang der X-Axe auseinander bis man die gewünschte Länge erreicht hat (ohne auf das Raster zu achten, also die Pixels werden zu Rechtecken mit rationaler Länge) und man erhält ein Segment das immer noch aus y Teilen besteht. Da wir nichts entlang der Y-Axe verändert haben sind diese Teile immer noch 1 Pixel hoch. Die Länge lässt sich einfach mittels Thales bestimmen da die Höhe bekannt ist und sie ist y/x.

    Da diese verzogenen Pixel aber nicht mehr rastergerecht sind müssen wir sie etwas anpassen damit sie wieder in das Gitter passen. Da die Höhe passt müssen wir nur die Länge anpassen. Da gibt es zwei Möglichkeiten : Auf- oder Abrunden. Also dein Q oder Q+1.

    Wie man ein Teilsegment raster dürfte klar sein. Bleibt also nur noch zu bestimmen wann man welches Teilsegment rastert. Hier kann ich dir nicht mehr ohne weiteres folgen in deinem Artikel allerdings ist es glaub ich ist es der gleiche Ansatz wie im normalen Bresenham Algo.

    Man verhält sich die reale Länge des Segments entlang der X-Axe und die des Rasterstrichs. Dies läuft auf banales Aufsummen heraus da man die Längen aller Teilstücke kennt. Ist der Betrag der Differenz (also die Abweichung vom optimalen Strich) kleiner als 0,5 gibt es ein kleines Stück und falls sie größer ist gibt es ein großes (da der Rasterstrich hinterher hinkt).

    Durch genauere Betrachtung stellt man fest, dass die reale Länge immer eine rationale Zahl ist mit Teiler y. Durch ein wenig ausmultiplizieren kann man also auch alles mit Ganzzahlen machen. (Man sollte hier aber über den möglichen Überlauf sprechen und wieso dieser hier keine Rolle spielt.)

    Um die Endpunkte in der Mitte der Pixel zu haben schneidet man die Anfangs- und Endteilstücke einfach in zwei und rundest wie man es gerade braucht.

    Du geht ausgiebig auf das Problem des Abrundens bei 0,5 ein. Wieso ist das so wichtig? Mich stört es nicht wenn da abgerundet wird. Ich habe deine Funktion welche alle Oktanten beherrscht jetzt nicht im Detail angeschaut allerdings wird da auch richtig gerundet wenn es sich nicht um das 1. Oktant handelt? Darauf müsstest du eigentlich eingehen um konsequent zu sein. Dadurch, dass man dein Segment nämlich auf den Kopf stellt wird das Runden verändert.

    Du wirfst den Leuten an den Kopf, dass es 4 verschiedene Teilsegmente gibt ohne wirkliche Begründung. Nicht sonderlich schön allerdings kann man das durchaus in einem Artikel machen der sich mehr mit der Implementierung beschäftigt. Dann gehst du aber ausgiebig auf das Problem der Länge dieser Teilsegmente ein. Diese könntest du konsequenterweise in diesem Fall auch den Leuten einfach an den Kopf werfen.

    Wer sich für eine Begründung interessiert, der ließt bis er dies an den Kopf geworfen kriegt. Wer sich nur für einen funktionierenden Algo interessiert der hört bei den Formeln auf zu lesen. Du wechselst in der Mitte des Artikels die Zielgruppe.

    Die Implementierung könntest viel lesbarer machen indem du die horizontalen Striche in eine eigene Funktion auslagerst.

    Ist CImg<T> ein realer MS Type oder etwas was du dir ausgedacht hast?

    Die Einleitung finde ich gut gemacht.

    Soll nur konstruktive Kritik sein. Ist nicht schlecht was bis jetzt hast. 🙂



  • Ben04 schrieb:

    Als erstes würde ich das Segment nicht in der Mitte eines Pixels starten lassen sondern an einer Kreuzung des Rasters. Hierdurch gibt es nur noch 2 verschiedene Teilsegmente, deren Länge sich auch leicht herausfinden lässt.

    Das sehe ich gerade nicht. Welchen der jeweils vier in Frage kommenden Gitterpunkte wählst Du? Und entspricht das nicht gerade der Verschiebung des Gitters um eine halbe Gitterzelle in beiden Richtungen, so wie's auch in den Zeichnungen angedeutet ist?

    Du wirfst den Leuten an den Kopf, dass es 4 verschiedene Teilsegmente gibt ohne wirkliche Begründung. Nicht sonderlich schön allerdings kann man das durchaus in einem Artikel machen der sich mehr mit der Implementierung beschäftigt. Dann gehst du aber ausgiebig auf das Problem der Länge dieser Teilsegmente ein. Diese könntest du konsequenterweise in diesem Fall auch den Leuten einfach an den Kopf werfen.

    Wer sich für eine Begründung interessiert, der ließt bis er dies an den Kopf geworfen kriegt. Wer sich nur für einen funktionierenden Algo interessiert der hört bei den Formeln auf zu lesen. Du wechselst in der Mitte des Artikels die Zielgruppe.

    Das sehe ich ähnlich. Ich hab mir die Rechnung angesehen. Die wird sich aber wohl sonst kaum jemand antun. Schon der Anfang "Klar ist:" und dann eine Formel mit Variablen, die irgendwo 2 Seiten vorher mal definiert wurden und dann eine völlig unmotivierte Berechnung wo in der Mitte noch lauter neue Variablen eingeführt werden. Sowas ist extrem anstrengend zu lesen. (und ich hab einiges an übung mit sowas). Vielleicht hilft eine Zeichnung in der die wichtigsten Variablen eingezeichnet sind? Und bitte mehr Motivation... was willst Du rauskriegen? Warum? Was ist die Idee, die hinter dem Beweis/der Rechnung steckt? So sieht das alles so aus als würde es vom Himmel fallen.

    Ich finde es schön, dass Du auch die Theorie mit darstellen möchtest. Damit das aber von vielen Lesern genutzt wird, solltest Du versuchen es dem Leser so angenehm wie möglich zu machen. Eventuell ist es auch interessant dem leser die Möglichkeit zu geben nur das Ergebnis zu lesen und die Rechnungen zu überspringen, indem Du irgendwo genau sagt was Aussage ist und was zur Rechnung gehört.



  • Jester schrieb:

    Ben04 schrieb:

    Als erstes würde ich das Segment nicht in der Mitte eines Pixels starten lassen sondern an einer Kreuzung des Rasters. Hierdurch gibt es nur noch 2 verschiedene Teilsegmente, deren Länge sich auch leicht herausfinden lässt.

    Das sehe ich gerade nicht. Welchen der jeweils vier in Frage kommenden Gitterpunkte wählst Du? Und entspricht das nicht gerade der Verschiebung des Gitters um eine halbe Gitterzelle in beiden Richtungen, so wie's auch in den Zeichnungen angedeutet ist?

    Ich wollte nicht sagen, dass der fertige Algo in der Mitte starten soll, sondern dass sich der Algo einfacher entwickeln lässt wenn man davon ausgeht. Am Ende geht man halt dann das erste und das letzte Segment in der Mitte durchschneiden und schon kann man wieder die Mitten der Pixel als Start und Ende angeben. An sich macht Vertexwahn ja auch nichts anderes in dem er sagt, dass M = Q/2. Du definierst es halt so wie es dir gerade am besten passt.



  • Ben04 schrieb:

    Ist CImg<T> ein realer MS Type oder etwas was du dir ausgedacht hast?:)

    Nö das ist eine Klasse von dieser Bibliothek:
    http://cimg.sourceforge.net/

    Kleiens Beispiel:

    int main() {
        CImg<unsigned char> img(640,400,1,3);    // Define a 640x400 color image with 8 bits per color component.
    
        img.save_png("test.png");
        img.display("Test");  // Display the image in a display window.
        return 0;
    }
    

    ich wollte dem Leser nur einen Weg aufzeigen, wie man mit ganz einfachen mitteln ein paar Pixel auf dem Bildschirm einfärben kann. Jedoch bin ich mir jetzt nicht sicher ob ich im Artikel auf die Bibliothek kurz eingehen soll oder ob ich einfach nur Pseudocodemäßig einfach DrawPixel schreiben soll, wenn ein Pixel gepolottet wird...



  • An sich brauchst du ja nur einen Weg eine Punktmenge auszugeben. Da hast du sehr viele Möglichkeiten : Array, Funktor. DrawPixel ist ja an sich der Funktorweg. Ich würde schon C++ verwenden und eine hypothetische Funktion PutPixel(x, y) verwenden. Die kann jeder dann seinen Bedürfnissen anpassen.





  • Ich hab jetzt keine Zeit mir das im Detail durch zu lesen. Es sieht aber schon viel besser aus als die Vorgängerversion. 🙂





  • Hi,

    ich lese es mal durch und schreib nebenbei gleich meine Meinung dazu hier rein, alles IMHO(!). Zunächst fällt mir auf, dass der Artikel unheimlich lang ist. Zu lang imho. Ich denke, da solltest Du imho was kürzen, wenn Du darauf hoffen möchtest, dass sich jemand komplett durchbeißt.

    Der erste Teil über Koordinatensysteme lässt sich denke ich ganz gut kürzen. Ist das Rastern von Punkten und deren Abweichung vom echten Punkt für den restlichen Artikel überhaupt von Bedeutung? Ich hab das Gefühl, dass das nicht wirklich der Fall ist. Der Inhalt ist auch wenig überraschend. Die Bilder würde ich etwas kleiner machen und nebeneinander setzen. Das spart Platz.

    Warum benötigst du einen eigenen abschnitt, um dich auf ganzzahlige werte einzuschränken? Ich denke, die Mehrheit wird mit ihrer Vorstellung bereits an dieser Stelle einsteigen: Pixelgrafik und ganzzahlige Koordinaten.

    Der Teil in dem Du die Bibliotheken kurz einführst ist zwar nett, aber für den Artikel nicht zwingend nötig. Setze doch einfach eine drawPixel-Funktion als gegeben voraus. Die Beschreibung der Bibliotheken würde ich dann in einen Anhang verschieben und auf den Verweisen. Wer das direkt sehen will springt nach hinten und schaut. Aber die Mehrheit glaubt, dass es sowas wie drawPixel gibt und liest lieber was über das Thema des Artikels.

    Das Kapitel "der triviale Ansatz" gefällt mir sehr gut. Das sollte aber imho viel viel früher im Artikel kommen. Eventuell geht das Bild, das die Problematik bei Strecken mit starker Steigung zeigt auch etwas kleiner?

    Den kurzen Abstecher zu Bresenham würde ich einfach mit in den vorigen Abschnitt aufnehmen. Eine richtige Quellenangabe fände ich besser.

    Die Idee zum Run-Length-Slice-Algorithmus kommt jetzt viel besser rüber! (den teil der unter "die beobachtung" steht).

    Der Teil unter "Die idee" ist nicht wirklich neu. Das machten wir doch schon bei "der triviale ansatz", oder? Bring es doch dort schon. 🙂 Das einzige was neu ist, ist die idee die segmentlängen vorher zu berechnen. Das passt auch in den Teil "Die Beobachtung" noch mit rein. Vielleicht kannste da dann ja da dann "Idee" drüber schreiben?

    Die Überschrift des Teils "Schafe und Mathematiker" hat imho wenig mit dem Inhalt des Abschnitts zu tun. Ich würde die Geschichte ganz weglassen. Deine Interpretation ist imho ziemlich einseitig und macht auch wenig lust darauf den rest des abschnitts zu lesen (ist schließlich nur unpraktisches zeug über dessen betrachtung man das ziel aus den augen verliert). Kennt jeder die Interfallschreibweise? vielleicht wäre 0 < m < 1 sicherer. Auch eine Erklärung, was das für die Steigung bedeutet könnte helfen (Steigung kleiner als 45° und man kann daher entlang der x-achse rastern). Außerdem vereinfachst Du zu Ursprung (0,0). Das geht doch generell und immer für alle Linienzeichenalgorithmen, oder? Dann würde ich mir überlegen, das als warm-up direkt zu Anfang zu bringen. Dann kannst du im ganzen restlichen artikel darauf zurückgreifen.

    Die Definition von A_i geht ein bißchen unter, weil es ganz verschämt am Ende eines Satzes steht. Auch eine sprachliche Definition hilft sicher die Bedeutung zu erfassen.

    Bei der Berechnung von A_i:
    Du schreibst "Durch Umformen ergibt sich"... umgeformt ist schon. Das ist eher Einsetzen.

    Das Q würde ich ganz zu Anfang definieren und dort auch den Sinn des Restes direkt nennen. Nämlich eine Formel für die Segmentlängen zu erhalten... die enthält dann ja auch das Q.

    Vielleicht hilft es auch, wenn Du Definitionen durch die Verwendung von := deutlich machst. Ich muss oft nach oben scrollen um zu sehen, ob der ausdruck nun gerade ne gleichung ist, oder eine neue variable definiert wird.

    Der Teil ist sehr anstrengend zu lesen. Mir fehlt ein bißchen das Ziel, es fallen ständig neue Variablen vom Himmel und es wird mehr oder weniger unmotiviert umgerechnet. Ich würde versuchen genauer zu formulieren, was rauskommen soll (kann man die formeln vielleicht einfach direkt angeben?) und auch darauf eingehen, wie man diese nun implementieren kann. die rechnung würde ich evtl. dann weglassen oder in nen anhang packen. ich kann mir kaum vorstellen, dass sich das jemand komplett durchliest.

    Was hilft es try1 zuerst vorzustellen? Würde ich entweder weglassen, oder nur die nötigen Modifikationen für try2 erklären, nicht nochmal komplett abdrucken.

    Du zeigst zwei Varianten, die für alle Steigungen funktionieren. Kannst Du was zum Unterschied sagen? Mir persönlich ist das etwas zu viel Code.


Anmelden zum Antworten