Hi, Erstmal: ich war mir nicht sicher, ob ich hier posten soll, oder in "PC-Programmierung". Da ich aber auf einen µC programmieren will, habe ich mich jetzt mal für hier entschieden. Prinzpiell denke ich, habe ich den Bresenham für den Kreis verstanden. Die meisten Beispiele berechnen meistens nur einen Oktanten, dessen Startpunkt wird dann sogar manuell gesetzt. Und dann wird der Oktant einfach gespiegelt. Ich brauche jetzt aber eine Implementierung die wirklich den kompletten Kreis berechnet, einmal außenrum. Ganz frei nach Kathrin Passig: "Mache es nicht selbst, du machst nur die gleichen Fehler wie die anderen", traue ich mich jetzt auch nicht wirklich das selbst zu implementieren. Insbesondere da ich davon ausgehe, dass ich da nur die trölfhundertste Variante davon mache, und wahrscheinlich sogar noch eine schlechte. Leider bringen mir all meine Suchversuche (Bresenham Circle without mirroring; Bresenham Kreis ohne Spiegelung; ... plotter, usw. usw.) bei Google immer wieder nur Implementierungen mit Spiegelung, ist halt auch die einfachste Variante. Bevorzugte Sprache wäre C/C++, aber solange es eine einigermaßen verständliche Hochsprache oder Pseudocode ist, werde ich dass dann schon umsetzen können. Mit Assembler, Brainfuck & Co kann ich halt leider nix anfangen. Hintergrund: Ich will auf ein kleines OLED-Display ein Gauge, also eine analoge Anzeige, darstellen. Dazu möchte ich nur einen Teilkreis zeichnen, und brauche auch dessen Pixelanzahl um den Anzeigenwert passend darauf zu skalieren. Und dann muss ich natürlich auch noch den Punkt berechnen, auf dem die "Nadel" gerade sitzt. Wäre schön, wenn mir hier jemand hier mal in seinem Codearchiv nachgucken könnte oder so, und mir kurz unter die Arme greifen kann. VG da_user
Matthias S. schrieb: > Ich brauche jetzt aber eine Implementierung die wirklich > den kompletten Kreis berechnet, einmal außenrum. Dann brich nicht ab, sondern lass die Kiste einfach acht Mal so weit rechnen.
So wie ich das verstehe, willst du einen Kreisbogen zeichnen. So etwas habe ich schon gemacht, das geht so, dass man sich Anfangs und Endpunkt berechnet (also klassisch mit sin / cos o.ä.) und diese dann verwendet, um zu entscheiden, ob der Quadrant an sich und im Quadrant dann der Pixel gezeichnet werden muss. Das sind ein paar if-Abfragen die man hier meistern muss.
Kreisbogen habe ich schon herausen. Da Ende und Anfang für so eine Skala ja auf einer Linie liegen, geht das sogar relativ einfach: ich lass einfach nur Punkte Zeichnen die unter (0,0 liegt rechts oben) einem bestimmten Y-Wert liegen. Eine virtuelle Grenze auf der Y-Achse quasi. Für weiterer Anwendungen, also z.B. den Punkt auf den der Zeiger steht, oder auch die Beschriftung, brauche ich bestimmte Punkte auf dem Teilkreis. Und dazu bräuchte ich die Punkte in der richtigen Reihenfolge, darum eben sauber außenrum. Possetitjel schrieb: > Dann brich nicht ab, sondern lass die Kiste einfach > acht Mal so weit rechnen. Also in den Algorithmen die ich bis jetzt gesehen habe, ist keine Abbruchbedingung drinnen. Meine Idee wäre jetzt höchstens, die While-Schleife in 8 Varianten (für jeden Oktanten eine) zu schreiben. Aber das muss ja schöner gehen ;-)
Google: Da Silva "Raster Algorithms for 2D Primitives" evtl auch HAKMEM item 177, fuer ein low-level incremental path encoding(wegen uC).
Matthias S. schrieb: > Ich will auf ein kleines OLED-Display ein Gauge, also eine analoge > Anzeige, darstellen. Das ist länglich. Bei diesem Projekt ist ein Bresenham für beliebige Kreisabschnitte integriert: http://dl1dow.de/artikel/ftmctrl/index.htm -> glcd_gfxfun.c Läuft letztendlich auch wieder auf den Standard-Bresenham mit angepassten Abbruchbedingungen hinaus.
:
Bearbeitet durch User
Hi, Johann Klammer schrieb: > Google: > Da Silva "Raster Algorithms for 2D Primitives" > > evtl auch HAKMEM item 177, fuer ein low-level incremental path > encoding(wegen uC). ich habe gerade keine Zeit das zu Googlen, trotzdem vielen Dank. Ich werde das natürlich möglichst bald nachgooglen. Auch ist wohl noch nicht so ganz klar, was ich eigentlich vorhabe, ich hoffe ich kann das heute Abend etwas genauer ausführen. Prinzipiell ist das was ich letzen Endes in Sachen Bresenham brauche, eine Klasse die ich mit dem Mittelpunkt, Radius und Start-/Endpunkt, bzw. meiner Y-Grenze initalisiere und zwei Methoden bereitstellt: NextPoint() die mir den nächsten gültigen Punkt auf dem Kreis berechnet (idealerweise im Uhrzeigersinn) Reset() die die Klasse auf den Anfangswert zurücksetzt. Das jetzt mal in der Kürze, wie geschrieben, hoffe ich, dass ich das heute Abend noch etwas weiter ausführen kann.
Matthias S. schrieb: > Auch ist wohl noch nicht so ganz klar, was ich > eigentlich vorhabe, ich hoffe ich kann das heute > Abend etwas genauer ausführen. Dochdoch, ich glaube schon. > Prinzipiell ist das was ich letzen Endes in Sachen > Bresenham brauche, eine Klasse die ich mit dem > Mittelpunkt, Radius und Start-/Endpunkt, bzw. meiner > Y-Grenze initalisiere und zwei Methoden bereitstellt: > NextPoint() die mir den nächsten gültigen Punkt auf > dem Kreis berechnet (idealerweise im Uhrzeigersinn) > Reset() die die Klasse auf den Anfangswert zurücksetzt. Ja, das ist verstanden worden. Das Problem ist: Du suchst einen incrementellen Algo- rithmus, der zu gegebenem Radiusvektor (Mittelpunkt --> Kreispunkt) einen Radiusvektor auf den benachbarten Kreispunkt liefert. Das ist mit den Mitteln der Vektor- rechnung ziemlich elegant machbar. Bresenham basiert aber darauf, dass sich die zu zeich- nende Figur als FUNKTION auffassen lässt -- sogar noch schärfer: Bresenham basiert darauf, dass sich die Figur als Funktion auffassen lässt, deren Anstieg den Betrag 1.0 höchstens erreicht, aber garantiert nie überschreitet. Dann kann man die "schnelle" Koordinate in Einer-Schritten ändern, und die "langsame" Koordinate bleibt entweder kon- stant oder macht auch einen Einer-Schritt. Bresenham kann somit einen Vollkreis nie durchgängig in einem Rutsch zeichnen, weil sich der Vollkreis nicht als Funktion im X-Y-Koordinatensystem darstellen lässt. Soll heißen: Den Algorithmus, den Du suchst, gibt es -- aber es ist nicht zwingend ein "Bresenham-Algorithmus".
Oh, ok. Was wäre denn dann ein besser Algorithmus?
Matthias S. schrieb: > Was wäre denn dann ein besser Algorithmus? Fertiggerichte kenne ich keine. Ein paar Anregungen bietet https://de.wikipedia.org/wiki/Rasterung_von_Kreisen Meine Lieblingsvariante (Rekursionsformel für eine ungedämpfte Schwingung) habe ich allerdings auch nicht gefunden.
Matthias S. schrieb: > Ich will auf ein kleines OLED-Display ein Gauge, also eine analoge > Anzeige, darstellen. Wie klein ist es denn? Das bekannte 128x160 Pixel Dings aus China? Bei kleinen Anzeigen würde ich die einzelnen Segmente mit Start- und Endpunkt einfach vorher berechnen und in einer Tabelle ablegen. Dann nur noch die Linien zeichnen.
Warum machst Du es nicht einfach mit der Standard-Kreisformel über sin(winkel)/cos(winkel), wenn Du schon C am nutzt. Damit sind Start/Enwinkel/Radius ja einfach beherrschbar. Problem wäre nur die Winkel-Schrittweite mit der Du die Funktion iterativ aufrufst: Ist die Schrittweite zu gross, dann entstehen Pixel-Lücken. Ist die zu klein, dann erhälst Du ggf. mehrfach den gleiche x-y-Punkt. Lösung wäre: Jeweils den letzten x-y-Punkt merken und bei jedem Aufruf der "nächstes-Pixel-Funktion" solange mit kleiner Winkel-Schrittweite erhöhen bis x oder y sich ändern. Diese Werte dann zurückgeben, und alles wieder von vorne.
Jim Beam schrieb: > Warum machst Du es nicht einfach mit der Standard-Kreisformel über > sin(winkel)/cos(winkel), wenn Du schon C am nutzt. Weil das ganze auf einen Mikrocontroller läuft, und da sin/cos einfach zu teuer sind. Insbesondere, da ich die wohl auch mehrmals durcharbeiten muss. Thomas F. schrieb: > Wie klein ist es denn? Das bekannte 128x160 Pixel Dings aus China? SSD1351 18x128x128 SPI Thomas F. schrieb: > Bei kleinen Anzeigen würde ich die einzelnen Segmente mit Start- und > Endpunkt einfach vorher berechnen und in einer Tabelle ablegen. Dann nur > noch die Linien zeichnen. Das Problem ist es ja, die Linien zu zeichnen ;-) Possetitjel schrieb: > Fertiggerichte kenne ich keine. Ein paar Anregungen bietet > https://de.wikipedia.org/wiki/Rasterung_von_Kreisen > > Meine Lieblingsvariante (Rekursionsformel für eine > ungedämpfte Schwingung) habe ich allerdings auch nicht > gefunden. Auch wieder entweder mit teuren cos/sin, Wurzeln und Potenzen, oder Bresenham bzw. Varianten davon. Aber mal sehen, ob sich auf Basis dessen was basteln lässt. Die Grundidee ist folgende: Die komplette Anzeige soll Kreisförmig sein. Der Kreis hat eine Breite von, jetzt angenommen 6 Pixel. Also brauche ich 6 Kreise, von innen nach außen K1 bis K6. Der Innere Kreis K1 ist der Teilkreis der Skala. Im Bild wäre zu sehen wie der aussehen könnte. Denn habe ich jetzt mit dem Bresenham aus Wikipedia gemacht. Ich glaube da brauche ich keinen Code dazu zu posten, ich habe halt dann statt "setPixel" eine eigene Methode aufgerufen, die so aussieht:
1 | int GaugePixelCounter = 0; |
2 | void CheckAndDrawPixelsOverBorder(byte x, byte y, byte borderY) |
3 | {
|
4 | if (y < borderY) |
5 | {
|
6 | Oled->drawPixel(x, y); |
7 | GaugePixelCounter++; |
8 | }
|
9 | }
|
Mittelpunkt ist hierbei 64,64, Durchmesser 40 und BorderY = 80. Der hat jetzt, wenn ich mich recht erinnere 149Pixel. Ich würde dann als nächstes den äußersten Kreis K6 ausrechnen, aber nicht zeichnen. Angenommen der hat dann 160Pixel. Angezeigt werden soll dann von 0 bis 5 bar. Macht 5 Segmente. Auf K1 wären das knapp 30Pixel/Segment, auf K6 genau 32. Wenn ich jetzt die jeweiligen Pixelpositonen für Pixel Nr. 0/0 (K1/K6 - nicht x,y!); 30/32; 60/64; 90/96; 120/128 und 149/160 ausrechne, habe ich die jeweiligen Start und Endpunkte für die Skalenunterteilungslinien. Die Teilkreise K3, K4 und K5 sollen jetzt den Anzeigebalken darstellen. Also genauso vorgehen: Erstmal ausrechnen, wieviele Pixel das sind, dann den Wertbereich darauf skalieren und bis zum entsprechenden Pixel zeichnen. Soweit die Idee ;-) Walter T. schrieb: > Bei diesem Projekt ist ein Bresenham für beliebige Kreisabschnitte > integriert: > > http://dl1dow.de/artikel/ftmctrl/index.htm -> glcd_gfxfun.c > > Läuft letztendlich auch wieder auf den Standard-Bresenham mit > angepassten Abbruchbedingungen hinaus. Das sind leider keine beliebigen Kreisabschnitte, sondern Oktanten. Da wird dann einfach je nach gewünschten Oktanten die Spiegelung ein oder ausgeschaltet. Trotzdem Danke! Johann Klammer schrieb: > Google: > Da Silva "Raster Algorithms for 2D Primitives" > > evtl auch HAKMEM item 177, fuer ein low-level incremental path > encoding(wegen uC). Beides ist auf jeden Fall mal was, was man sich in ruhiger Minute zu Gemüte führen muss ;-) Bei Da Silva habe ich jetzt beim ersten Überfliegen allerdings festgestellt, dass der wohl auch Oktantenweise zeichnet. Wahrscheinlich wird es darauf hinauslaufen müssen, dass ich den Bresenham 8 mal für jeden Oktanten implementieren muss. Unschön, aber nicht unmöglich.
Matthias S. schrieb: > Das sind leider keine beliebigen Kreisabschnitte, sondern Oktanten. Das sind beliebige Kreisabschnitte. Diese werden aus Oktanten + Rest zusammengesetzt. In diesem Fall für nur einen bestimmten Durchmesser (26 Pixel), weil der Sinus nur als Lookuptable hinterlegt ist - aber das läßt sich ja einfach anpassen.
:
Bearbeitet durch User
Ach... hirnklatsch Ich hätte noch ein bisschen weiter runterscrollen sollen. Bin mir aber noch nicht sicher, ob mir das weiterhilft.
Vielleicht kannst Du ja mit meinen Kreissektor-Funktionen etwas anfangen. Algorithmus stammt von [1]. Die Funktionen sind für die Grafik-Lib U8g2 [2] von Oliver Kraus geschrieben und noch nicht ganz fertig (Das zugehörige Projekt ruht leider seit über einem halben Jahr). [1] https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector [2] https://github.com/olikraus/u8g2/
Matthias S. schrieb: > Possetitjel schrieb: >> Fertiggerichte kenne ich keine. Ein paar Anregungen bietet >> https://de.wikipedia.org/wiki/Rasterung_von_Kreisen >> >> Meine Lieblingsvariante (Rekursionsformel für eine >> ungedämpfte Schwingung) habe ich allerdings auch nicht >> gefunden. > > Auch wieder entweder mit teuren cos/sin, Wurzeln und Potenzen, Quatsch. Die Rekursion ist im Prinzip nur x_n+1 := x_n+f*yn y_n+1 := y_n-f*x_n+1 Das Problem besteht in den diversen Kleinigkeiten.
Leo C. schrieb: > Vielleicht kannst Du ja mit meinen Kreissektor-Funktionen etwas > anfangen. Womit hast Du die Screenshots/Darstellung auf dem PC gemacht?
Possetitjel schrieb: > Quatsch. > Die Rekursion ist im Prinzip nur x_n+1 := x_n+f*yn y_n+1 := y_n-f*x_n+1 > Das Problem besteht in den diversen Kleinigkeiten. Sorry, war auf den Wikipedialink bezogen.
:
Bearbeitet durch User
Walter T. schrieb: > Leo C. schrieb: >> Vielleicht kannst Du ja mit meinen Kreissektor-Funktionen etwas >> anfangen. > > Womit hast Du die Screenshots/Darstellung auf dem PC gemacht? Mit sdl. In der u8g2-lib gibts ein ein Unterverzeichnis sys/sdl mit Beispielen.
Matthias S. schrieb: > Also brauche ich 6 Kreise, von innen nach > außen K1 bis K6.... Bei 120x120 Pixel einfarbig (1 Bit/Pixel) bräuchte man 1,8kByte Speicher für ein bereits fertiges Bild des Zeigerinstruments. Ich würde da nicht lange rumrechen wollen. Dann müsste man nur noch den Zeiger zeichnen.
Leo C. schrieb: > Vielleicht kannst Du ja mit meinen Kreissektor-Funktionen etwas > anfangen. Ähm... Wow. Auf den ersten Blick sieht das tatsächlich nach was passenden aus. Aber da raucht jetzt erstmal der Kopf. Aber ja, ich denke das dürfte wir doch deutlich weiterhelfen, da muss ich mich allerdings noch "etwas" mit dem Code und den Artikel beschäftigen. Ich nutze übrigens die UCG Arduino von Oli Kraus. Vielen Dank!
:
Bearbeitet durch User
Hier ist noch die Funktion, die das Poti auf dem Foto oben gemalt hat.
1 | #ifndef DISPLAY_WIDTH
|
2 | #define DISPLAY_WIDTH 256
|
3 | #endif
|
4 | #define DISPLAY_HIGHT 64
|
5 | |
6 | #define POT_R 37 //Poti radius in pixel
|
7 | #define POT_WIDTH 5
|
8 | #define POT_X0 (DISPLAY_WIDTH-POT_R) //Poti center
|
9 | #define POT_Y0 37
|
10 | |
11 | #define POT_MSG_FONT u8g2_font_crox5t_tr
|
12 | |
13 | |
14 | void draw_pot(u8g2_t *p, uint_fast8_t val, char *text) |
15 | {
|
16 | unsigned v = (val * 270) / 255; |
17 | |
18 | u8g2_DrawDisc_sector(p, POT_X0, POT_Y0, POT_R, 135, 135 + v); |
19 | u8g2_DrawCircle(p, POT_X0, POT_Y0, POT_R-(POT_WIDTH/2), U8G2_DRAW_ALL); |
20 | uint_fast8_t dc = p->draw_color; |
21 | u8g2_SetDrawColor(p, dc ? 0 : 1); // complement draw color |
22 | u8g2_DrawDisc(p, POT_X0, POT_Y0, POT_R-POT_WIDTH, U8G2_DRAW_ALL); |
23 | u8g2_SetDrawColor(p, dc); // set draw color to old value |
24 | |
25 | u8g2_SetFont(p, POT_MSG_FONT); |
26 | u8g2_SetFontPosCenter(p); |
27 | int xpos = u8g2_GetStrWidth(p, text)/2; |
28 | u8g2_DrawStr(p, POT_X0 - xpos, POT_Y0, text); |
29 | }
|
Hi, ich bin jetzt zu einer Lösung gekommen. Die beiden dazugehörigen Dateien im Anhang. Prinzipiell ist das ganze relativ simpel: Es wird ein neuer Kreis initalisiert, der rechnet erstmal die mit dem Bresenham die Punkte eines Oktanten aus und zählt die nur. Dann kann wird mit malloc entsprechend RAM reserviert (darum bitte entsprechend vorsichtig, möglichst nur einmal initalisieren). Dann wird das Array nochmal mit dem Bresenham mit entsprechenden Pixeldaten gefüllt. Jetzt kann man mit "GetNextPixel" einen Pixel nach dem anderen im Uhrzeigersinn unten beginnend durchgehen. Ob weitere Pixel verfügbar sind, kann man mit "NextPixelavaible" prüfen. Zudem werden ungültige Pixel mit den Werten von -1 zurückgeben. Mit der Resetmethode kann der Kreis dann zurückgesetzt und von vorne begonnen werden. Mit GetPixel kann dann ein bestimmter Pixel bestimmt werden. Dessen switch-case Zeug hatte ich anfangs in ähnlicher Form in der GetNextPixel Methode. Da habe ich halt dann einen Pixelindex und einen Oktantenindex mitzählen lassen. Zudem gibt es die GraduatedBresenhamCircle-Klasse. Hier wird zudem eine "BorderY" übergeben. Damit beginnt der Kreis erst mit dem Pixel, dessen Y-Koordinate UNTER diesem Wert liegt (0-Punkt ist bei mir links oben). Den angehängten Kreis habe ich damit gemacht. Getestet habe ich dass ganze auf einem Nucleo F303K8. Da sah man dann mit der ersten Variante (switch-case in GetNextPixel) schon, wie sich der Kreis aufgebaut hat. Ging zwar flott (gefühlt <0,5s), aber man sah es. Jetzt sieht man von dem aufbau nix mehr. Ein kleines Problem kann man jetzt noch erkennen: Ich zeichne in dem Bild eine dicke Kreislinie in dem ich sieben Kreise nebeneinander lege. Dabei gibt es ein paar Pixel die schwarz bleiben. Stört mich für den Moment nicht, ich werde mir dann aber schon noch was besseres überlegen müssen, wie ich das schöner mache. Evtl. kann ich mich da vom Code von Leo C. (rapid) inspirieren lassen. Eine weitere Erweiterung wäre denkbar für Ellipsen. Sollte auch realtiv einfach klappen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.