Ausgefüllten Kreis malen?



  • Hi,

    ich habe eine funktion "inline void drawPixel (int x, int y, unsigned char color);"

    und damit kann man einen Pixel malen 🙂

    Jetzt möchte ich gerne einen ausgefüllten Kreis malen und einen unausgefüllten kreis! Wie macht man das?



  • (unausgefüllten Kreis/Ellipse)
    Midpoint Circle/Ellipse Algorithm

    http://member.ctinets.com/~winnyefanho/research/MEA.pdf

    (ausgefüllter Kreis)
    unausgefüllten Kreis zeichnen und dann von jedem gezeichneten Punkt
    eine vertikale Linie ziehen.

    EDIT:
    Sogar noch ein Beispiel gefunden:
    http://www.vyomworld.com/source/code.asp?id=12&l=Graphics&t=Bresenham's Circle Drawing Algorithm.

    Viele Grüße
    Fischi



  • gagagogo schrieb:

    Jetzt möchte ich gerne einen ausgefüllten Kreis malen und einen unausgefüllten kreis! Wie macht man das?

    Erstmal den nicht ausgefüllten Kreis. Den ausgefüllten solltest du dann selber können...
    Schau dir zuerst mal die Funktion f(x)=r2x2f(x) = \sqrt{r^2 - x^2} für x\in [-r,r] an. Diese Funktion "malt" einen Halbkreis oberhalb der x-Achse mit dem Radius r und dem Mittelpunkt (0,0). Sagen wir, du willst einen Kreis mit dem Mittelpunkt (100,100) und dem Radius 50 malen. Dann setzt du in der Funktion f erstmal r = 50. Das ist dann ein Halbkreis um (0,0) mit dem Radius 50. Davon wirst du natürlich nur ein Viertel auf deinem Bildschirm sehen können. Wir müssen den Kreis also noch verschieben. Erstmal um 100 nach rechts. Das sieht dann so aus: f(x)=502(x100)2f(x) = \sqrt{50^2 - (x-100)^2}. Hier läuft x von 100 - r bis 100 + r, also x\in [50, 150]. Jetzt müssen wir den Kreis noch um 100 nach unten verschieben (auf dem Bildschirm ist es nach "unten", da die y-Werte nach unten hin größer werden - andersherum als im Koordinatensystem). Das macht man ganz einfach, indem man zu f(x) noch 100 dazuaddiert, also so:

    f(x)=100+502(x100)2f(x) = 100 + \sqrt{50^2 - (x-100)^2}.

    Jetzt lässt du x in einer Schleife einfach von 50 bis 150 laufen und malst in jedem Durchlauf bei (x, f(x)) einen Punkt:

    for(int x=50; x<=150; ++x)
    {
       drawPixel(x, UpperCircleFunction(x, 50, 100, 100), 0);
       drawPixel(x, LowerCircleFunction(x, 50, 100, 100), 0);
    }
    

    Die Funktionen UpperCircleFunction und LowerCircleFunction kannst du wie folgt implementieren:

    dubble UpperCircleFunction(int x, int radius, int mx, int my)
    {
       return(my - \sqrt(radius^2 - (x - mx)^2));
    }
    
    dubble LowerCircleFunction(int x, int radius, int mx, int my)
    {
       return(my + \sqrt(radius^2 - (x - mx)^2));
    }
    

    Dann hast du einen Kreis gemalt. Dieser sieht jetzt noch nicht so schön aus, da die Punkte doch recht weit auseinanderliegen können, und der Kreis so "gepunktet" aussehen kann. Es wäre hier hilfreich, wenn du noch eine Funktion DrawLine() oder so hättest, mit der du die einzelnen Punkte verbinden kannst.



  • Die Funktion f(x)=r2x2f(x) = \sqrt{r^2 - x^2} zu plotten ist meiner Meinung nach keine gute Idee, da es zum einen nicht sonderlich effektiv ist und
    zum anderen, wie von dir schon angesprochen, die Punkte sehr weit auseinanderliegen.
    Dann würde ich doch eher diese Punkte darzustellen:

    \left( {x,y} \right) = \left( {r \cdot \sin (\alpha ),r \cdot \cos (\alpha )} \right),\alpha \in \left[ {0,\pi /4} \right]

    Das liefert dir einen Achtelkreis, der Rest ist durch Ausnutzung von
    Symetrieeigenschaften darstellbar.
    Als Schrittweite für alpha ist 1/r ein guter Anfang.

    Viele Grüße
    Fischi



  • Das Problem bei deiner Methode ist tatsächlich die Schrittweite. Bevor ich meinen Beitrag hier geschrieben hatte, war ich auch zuerst auf dem Trichter. Nur hatte ich dann keine Idee, wie ich die Schrittweite wählen soll. Wenn man sie zu klein wählt, läuft man Gefahr, zu lange auf einem Punkt rumzuspringen. Das verbraucht unnötig Rechenleistung und Zeit. Wie kommst du denn auf die Schrittweite 1/r?



  • das problem mit der schrittweite umgehst du, wenn du gleicht mit pixeln arbeitest:

    void DrawCircle(const int mx, const int my, const int r)
    {
    	int r2 = r * r + r;
    	int x = 0, y = r, line = r * r;
    	do
    	{
    		DrawPoint( mx - x, my - y );
    		DrawPoint( mx + x, my - y );
    		DrawPoint( mx - y, my - x );
    		DrawPoint( mx + y, my - x );
    		DrawPoint( mx - y, my + x );
    		DrawPoint( mx + y, my + x );
    		DrawPoint( mx - x, my + y );
    		DrawPoint( mx + x, my + y );
    		if ( ( line += x + x + 1 ) > r2 )
    		{
    			line -= y + y - 1;
    			--y;
    		}
    	}
    	while( ++x < y );
    }
    

    das vermeidet auch unnötige operationen wie cos/sin oder multiplikationen.



  • Kleiner Verbesserungsvorschlag meinerseits:

    while( ++x <= y );
    


  • stimmt 🙂


Anmelden zum Antworten