Interface Design für eine Grafik Library
-
Ich entwickle gerade eine Library, mit der man ohne sich groß mit irgend welchen komplexen Systemfunktionen beschäftigen zu müssen ein wenig auf dem Bildschirm malen kann. Ich weiss aber nur nicht genau, wie ich das Interface entwerfen soll, ich habe zZ. 3 Möglichkeiten.
1. Es gibt Klassen wie Pixel und Linie, mit denen die grafischen Objekte representiert werden, diese sind mit Hilfe von Klassen implementiert, die nur Eigenschaften representieren (Pimpl-Idiom)
class pixel { Point *impl; public: explicit pixel(int x=0, int y=0, color farbe=0); //... };
2. Ich habe nur Klassen, die die Eigenschaften der grafischen Objekte representieren
class point { public: explicit point(int x=0, int y=0, color farbe=0); void paint(void) const; //... };
3. Ich habe einfach nur Funktionen wie putpixel
void putpixel(int x, int y, color farbe);
Wie würdet ihr das designen?
-
Also Funktionen auswendig lernen kann man auch brav mit Systemroutinen, und damit würde sich deine Grafik-Libary nicht viel von Allegro, etc. unterscheiden...
Ich persönlich würde allerdings auch keine Klasse "pixel" erstellen (für jeden Pixel dann ein eigenes Objekt? Oder für mehrere (falscher Name)).
Eher eine Klasse "pixelscreen : public basic_screen" mit Konstruktor der die Größe übernimmt. Eine Methode putpixel(), mit der man an eine Position des Screens einen Pixel setzen kann.
MfG SideWinder
-
Soll deine Grafiklibrary im Textmodus (80x25 Zeichen) oder in einem Grafikmodus laufen ?
Was ist das Ziel deiner Grafiklib ? Geschwindigkeit ? Portabilität ? ... ?
-
Das Ziel ist, dass man sie möglichst leicht bedienen kann
zZ. benutzt man das zB. so
int main(void) { grafic handler(cout,cin); putpixel(x,y,color); }
und cout und cin werden automatisch so modifiziert, dass man damit auf das Ausgabe Fenster schreiben kann.
Zur Zeit benutze ich die SDL, auch wenn mich stört, dass man zur Ausgabe die SDL_ttf library benutzen muss
-
...also eine Grafiklibrary die mehrere Ausgabeformate (Text, SDL...) unterstützt ?
-
Ich finde, dass Möglichkeit 3 recht gut klingt.
edit: Beistrich[ Dieser Beitrag wurde am 18.01.2003 um 17:42 Uhr von nman editiert. ]
-
Bei Variante 3 machst du es aber quasi unmöglich , bzw sehr schwer verschiedene Ausgabelibs zu verwenden (Text und SDL z.B.).
Außerdem sind globale Funktionen hässlich.Stell dir vor nachher kommen noch ein paar Funktionen für Dreiecke, Rechtecke, Kreise usw hinzu. Da platzt der globale Namensraum ja fast
-
Original erstellt von Headhunter:
Außerdem sind globale Funktionen hässlich.
Stell dir vor nachher kommen noch ein paar Funktionen für Dreiecke, Rechtecke, Kreise usw hinzu. Da platzt der globale Namensraum ja fastNaja, ich dachte auch mehr an sowas wie krlib (kingruedis lib) als namespace oder so...
Und wirklich schwer wird das mit mehreren Ausgabemöglichkeiten auch nicht.
-
krlib : rofl
Zugriff auf mehrere Ausgabegeräte mit globalen Funktionen (was putpixel ja ist, auch wenn es in einem namespace steckt) :
int ausgabe; void putpixel(int x, int y, color farbe) { if (ausgabe == SDL) { //blabla } else if (ausgabe == TEXT) { //zugriff auf ein paar funktionen } else if (ausgabe == DRUCKER) { //tu was } } int main () { ausgabe = SDL; putpixel (20,20,2); }
Zugriff auf mehrere Ausgabegeräte mit Klassen
//abc == abstract basic class class CABCGraphic { public : virtual void putpixel(int x, int y, color farbe) = 0; //hier kommt noch mehr }; // class CSDLGraphic : public CABCGraphic { public : virtual void putpixel (int,int,color); }; // class CTextGraphic : public CABCGraphic { public : virtual void putpixel (int,int,color); }; // class CPrinterGraphic : public CABCGraphic { public : virtual void putpixel (int,int,color); }; int main () { CABCGraphic* g = new CSDLGraphic; g->putpixel (20,20,2); }
Mh, die C++ Variante hat zwar was mehr Text, ist aber -imo- besser. Versuch mal bei der globalen Variante ein neues Ausgabegerät, z.b. OpenGL, hinzuzufügen wenn die krlib schon 1000+ Zeilen hat. Viel Spaß :p
-
@Headhunter
meine Library arbeitet anders. Es gibt eine Klasse grafic, die initalisiert die Library und ersetzt die Buffer von cout und cin, durch die entsprechenden Buffer, die für die Umleitung zuständig sind und räumt im Destruktor diese auch wieder auf.Die anderen Library Funktionen/Klassen greifen auf das Handle mit dem Singleton Prinzip zu, deswegen wär das mit den einzelnen Funktionen kein Problem.
Version 3 find ich aber ein bisschen schlecht, dass ist mir zu C mäßig. Sides Vorschlag ist irgend wie ganz vernünftig, ich sehe aber 2 Probleme
1. der Programmierer neigt dazu ein globales grafic Object anzulegen
2. der Programmierer könnte auf die Idee kommen mehrere grafic Objekte anzulegenWas haltet ihr von Version 2?
-
Eh, was macht das Schlüsselwort explicit ?? Kenn ich nich
Was hälst du davon deine Funktionen statisch zu machen ? Also Zugriff auf die Elemente ohne eine Instanz davon anzulegen. Das hat den Vorteil, dass du in jeder Datei in der du die Grafikklasse benötigst auf export verzichten kannst.
Außerdem würde das das Singletonprinzip überflüssig machen.Mit Sides Vorschlag kann ich leider nichts anfangen. Was meinst du mit basicscreen und pixelscreen ?
-
Alle Member statisch? Dann würde ich doch trotzdem mit dem Singleton Prinzip arbeiten müssen, da static Member ja nur andere static Member benutzen dürfen. Aber was soll an Singleton schlimm sein?
explicit sorgt dafür, dass man den Konstruktor explizit aufrufen muss und der Compiler folgendes nicht mehr erlaubt
pixel p=1;
Side meint sicher so etwas
class grafic { handle hnd; public: grafic() { create_hnd(hnd); } void putpixel(int x, int y, color col); //... }
-
Wo ist das Problem mit Static ?
Mach's doch so :
class CGraphic { public : static void putpixel (int, int, color); static void setbg (color); private : static handle m_handle; static bool bereit; static int fensterx, fenstery; };
Du brauchst wahrscheinlich eh nur eine Instanz von CGraphic. Es passt ganz gut dort alles static zu machen. Um zu verhindern dass jm eine Instanz deiner Klasse bildet, kannst du den Konstruktor ja private machen.
So ähnlich wird das auch bei der Clanlib (www.clanlib.org, wie SDL nur in C++ && mehr highlevel) gemacht. Es ist einfach viel bequemer auf CL_Display::get_width() zuzugreifen anstatt auf g_display.get_width ()
-
irgend wie mag ich das nicht so. Was hälst du den von Version2?
-
Könntest du deine Variante 2 nochmal was ausführlicher Erläutern ?
Was machst du mit der Klasse point ?
-
Die Klassen beinhalten nur die Eigenschaften der grafischen Elemente.
class point { int x_, y_; color col; public: explicit point(int x=0, int y=0, color c=0) : x_(x), y_(y), col(c) { } inline ~point() { } void paint(void) const; inline int x(void) const { return x_; } //... };
Wenn man nun ein Punkt malen will, dann macht man das so:
point pt(1,5,gruen); pt.paint();
-
Ich finde das extrem umständlich
Stell dir vor du willst ein paar Punkte mehr zeichnen, für mich wäre das zuviel Schreibarbeit.
Mh, wie wäre dies hier :
class CGraphicalObject { protected //jedes objekt hat eine farbe color m_color; //linke obere Ecke des Objektes int m_x, m_y; public : //jedes objekt kann gezeichnet werden virtual void paint () = 0; }; class CRect : public CGraphicalObject { private : //höhe und breite int m_h, m_w; public : virtual void paint (int x, int y, int w, int h, in color); }; class CCircle : public CGraphicalObject { ///// }
Mit diesem Ansatz hast du aber immer noch das Problem, dass es recht umständlich ist verschiedene Ausgabegeräte anzusprechen.
Mein "Lieblingsansatz" ist der 9.te Post von oben, wo alles von der CABCGraphic abgeleitet wird. Im Prinzip muss deine Graphiclib nur Punkte zeichnen können. Aus Punkten kannst du dann alles zusammensetzen.
Dann kannst du auch eine Klasse CRect, CCircle oder sonstwas bauen. Diese greifen auf ein -am Besten 100% statisches- von CABCGraphic abgeleitetes Objekt zu. D.h. deiner Zeichenklassen ist es Kackegal ob du mit OGL, SDL, Text oder sonstwas arbeitest.
Darauf kommt es imo auch an : Versuche vor den Zeichenfunktionen die unterliegenden Ausgabegeräte zu verstecken und zu kapseln !
-
@kingruedi liege ich richtig? du hast http://www.c-plusplus.net/titel_21.htm noch nicht gelesen?
das fliegen gewicht muster könnte passen und ein oder zwei ander muster(habe das buch jetzt nicht da)also was ich mir unter user freundlich vorstele ist
int main() { pixel pix( kordinate( 10, 10 ), farbe( 255, 255, 255 ) ); pix.draw() line lin( kordinate( 100, 100 ), kordinate( 200, 200 ), farbe( 255, 255, 255 ) ); }
überlege einfach wie würdes du diese zeichen arbeit am tisch machen und versuche das zu programmieren.
eine funktion putpixel ist so als ob du mit ein stift zu einer kordinate gehst und ein punkt malst, wenn du jetzt den punkt in bewegng haben willst, muss du das blatt wegschmeissen und ein neuen pixel malen
das wird bei vielen objekten ziemlich aufwändig.dagegen, ein pixel objekt ist so als ob du ein stück papier abschneidest und auf das blatt legst, du kannst es verschieben und co. (aber pixel ausschneiden ist aufwändiger als ein pixel mit den stift zu malen)
[ Dieser Beitrag wurde am 19.01.2003 um 06:32 Uhr von Dimah editiert. ]
-
Ich denke der Ansatz ist zu Granular... das macht Sinn für eine Lib, die sich auf Vektorgrafiken abstützt. Aber wenn es pixelbasiert sein soll, dann arbeitet man so nicht.
Schaut man sich andere Implementationen an, so wird regelmäßig als kleinstes Objekt eher auf der Ebene des Canvas mit der Klassenbildung begonnen - der Hintergrund ist doch klar:
Wenn ich ein
pix(400, 100, farbe(255, 128, 0)).draw();
schreiben muß um einen Punkt zu zeichnen, dann wird das Objekt auf dem Stack angelegt, die Werte werden in Membervariablen umkopiert, intern dann die Zeichenfunktion aufgerufen und die Membervariablen vom Stack auf den Stack umkopiert und die eigentliche Grafikfunktion aufgerufen. Das klingt nicht schnell. Und sich darauf verlassen, daß der Compiler das schon durchoptimiert... da hätte ich Bauchschmerzen.
Außerdem will ich gerne so ein Problem gelöst haben:
pix(400, 100, farbe(255, 128, 0)).draw(); line(200, 100).draw(); // soll die gleiche Farbe wie der Punkt haben!
Macht man die Farbe zu einer Eigenschaft des Punktes, so verzerrt das etwas... eigentlich ist die Farbe eine Eigenschaft des aktuellen Zeichenstiftes. Also muß bei der Linienkonstruktion - da die Farbe wieder als Attribut der Linie auftaucht - die aktuelle Farbe ermittelt werden (woher? vom letzten Punkt?) und zwischengespeichert zu werden, um erneut verwendet zu werden im draw(). Der Informationsfluß ist zu aufwendig, zu viele Kopien.
-
hmm dann stimm ich für beides..
in php habe ich es genossen, auch sowas machen zu können
for ($x=0; $x<=$breite; $x++){ for ($y=0; $y<=$breite; $y++){ $fn =sqrt($x*$x +$y*$y); $rundung=$fn%$breite; imagesetpixel($bild,$x,$y,$farbe[$rundung]); } }
also daß der letzte parameter, die farbe.. auch pixelweise gesetzt werden kann..
damit farbverläufe herstellen ist nett.aber nur am rande