anwenden:asciipic
no way to compare when less than two revisions
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
— | anwenden:asciipic [2020-07-26 17:04] (aktuell) – angelegt - Externe Bearbeitung 127.0.0.1 | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
+ | ====== Textbild : ASCII pictures ====== | ||
+ | > You can't do that in horizontal mode. | ||
+ | >> | ||
+ | |||
+ | Um Bilder zu erzeugen, benötigt man keine Grafikkarte. | ||
+ | Es genügt ein Textbildschirm. | ||
+ | Einzelne Buchstaben des Bildschirmfensters ansprechen ist jedoch mühsam: | ||
+ | Cursorsteuerung erfordert | ||
+ | compiler- oder systemspezifische Befehle. | ||
+ | (Die '' | ||
+ | curse = Fluch.) | ||
+ | Die Standardausgabe schreibt der Reihe nach, | ||
+ | von links nach rechts, eine Zeile nach der anderen. | ||
+ | Warum also nicht vorher | ||
+ | die auszugebenden Bilder im Haupspeicher aufbereiten? | ||
+ | Dann können Texte im Fenster manipuliert | ||
+ | oder Kurvenverläufe grafisch dargestellt werden. | ||
+ | Dazu muss eine einfach zu handhabende Klasse her. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Klassenschnittstelle ===== | ||
+ | Die grundlegende Idee hat Andrew Koenig beschrieben [1, 2]. | ||
+ | Ein Text oder eine Ascii-Grafik ist nicht weiter als | ||
+ | zeilenweise angeordnete Zeichenketten, | ||
+ | ein Vektor aus string-Objekten. | ||
+ | Dieser kann sogar öffentlich bleiben. | ||
+ | Es wäre viel zu schade, all die nützlichen Methoden | ||
+ | der Standardcontainerklassen zu verbergen. | ||
+ | Ein paar Methoden und Funktionen sollen die Handhabung erleichtern: | ||
+ | |||
+ | |||
+ | * Konstruktoren für rechteckige Bilder, | ||
+ | * die Abfrage von Breite und Höhe, | ||
+ | * der geprüfte Zugriff auf einzelne Zeichen des Bildes, | ||
+ | * das Auffüllen von Bildern mit " | ||
+ | * das Zerlegen einer Zeichenkette entstehen. | ||
+ | * Horizontales bzw. | ||
+ | * vertikales Aneinandersetzen, | ||
+ | * Umrahmen und | ||
+ | * der Ausgabeoperator werden als globale Funktionen bereitgestellt. | ||
+ | |||
+ | <code cpp> | ||
+ | //: textbild.h : ASCII-Bilder - R.Richter 2005-03-20 | ||
+ | //////////////////////////////////////////////////// | ||
+ | // Lit.: | ||
+ | // [1] Andrew Koenig, Barbara Moo: | ||
+ | // | ||
+ | // [2] Andrew Koenig, Barbara Moo: | ||
+ | // | ||
+ | |||
+ | #ifndef TEXTBILD_H | ||
+ | #define TEXTBILD_H | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | class Textbild | ||
+ | { | ||
+ | public: | ||
+ | std:: | ||
+ | |||
+ | Textbild() {} | ||
+ | Textbild(unsigned breite, unsigned hoehe, char fuellzeichen = ' '); | ||
+ | unsigned breite() const; | ||
+ | unsigned hoehe () const; | ||
+ | char at(unsigned x, unsigned y) const; | ||
+ | char& at(unsigned x, unsigned y); | ||
+ | Textbild& | ||
+ | |||
+ | static Textbild split(std:: | ||
+ | }; | ||
+ | |||
+ | Textbild neben (Textbild const& links, Textbild const& rechts); | ||
+ | Textbild unter (Textbild const& oben, Textbild const& unten); | ||
+ | Textbild umrahme(Textbild const& bild, char zeichen); | ||
+ | |||
+ | std:: | ||
+ | |||
+ | #endif // TEXTBILD_H | ||
+ | //~ | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Implementierung ===== | ||
+ | <code cpp> | ||
+ | //: textbild.cpp : ASCII-Bilder - R.Richter 2005-03-20 | ||
+ | ////////////////////////////////////////////////////// | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include " | ||
+ | </ | ||
+ | Der Ausgabeoperator muss die Bildzeilen nur auf den Ausgabenstrom kopieren. | ||
+ | Mit Standardalgorithmen ist das auch so formulierbar. | ||
+ | Ein '' | ||
+ | veranlasst '' | ||
+ | |||
+ | <code cpp> | ||
+ | std:: | ||
+ | { | ||
+ | std:: | ||
+ | std:: | ||
+ | } | ||
+ | </ | ||
+ | Ein Textbild kann als rechteckige, | ||
+ | mit einem Zeichen gefüllte Fläche gebildet werden, | ||
+ | deren Höhe und Breite abfragbar sind. | ||
+ | |||
+ | <code cpp> | ||
+ | Textbild:: | ||
+ | : zeilen(hoehe, | ||
+ | { | ||
+ | } | ||
+ | |||
+ | unsigned Textbild:: | ||
+ | { | ||
+ | return zeilen.size(); | ||
+ | } | ||
+ | |||
+ | unsigned Textbild:: | ||
+ | { | ||
+ | unsigned maxbreite = 0; | ||
+ | |||
+ | for (unsigned i=0; i < zeilen.size(); | ||
+ | { | ||
+ | unsigned aktuell = zeilen[i].size(); | ||
+ | if (maxbreite < aktuell) | ||
+ | maxbreite = aktuell; | ||
+ | } | ||
+ | return maxbreite; | ||
+ | } | ||
+ | </ | ||
+ | Die Breite eines Textbildes wird durch die längste Zeile bestimmt. | ||
+ | Nicht alle Textbilder bestehen nur aus gleich langen Zeilen. | ||
+ | Daher ist die Breitenbestimmung auch aufwendiger. | ||
+ | Aus rechtsseitig ausgefransten Bildern lassen sich jedoch | ||
+ | Rechtecke machen, indem kürzere Zeilen aufgefüllt werden: | ||
+ | |||
+ | <code cpp> | ||
+ | Textbild& | ||
+ | { | ||
+ | int b = breite(); | ||
+ | for (unsigned i=0; i< | ||
+ | { | ||
+ | zeilen[i].resize(b, | ||
+ | } | ||
+ | return *this; | ||
+ | } | ||
+ | </ | ||
+ | Der lesende / schreibende Zugriff auf Einzelzeichen mit der Methode | ||
+ | '' | ||
+ | dass nicht hinter Feldgrenzen zugriffen wird. | ||
+ | Falsch addressierte Breife landen (ungeöffnet) im Papierkorb. | ||
+ | |||
+ | <code cpp> | ||
+ | char Textbild:: | ||
+ | { | ||
+ | if (y >= hoehe() || x >= zeilen[y].size()) return ' | ||
+ | return zeilen[y][x]; | ||
+ | } | ||
+ | |||
+ | char& Textbild:: | ||
+ | { | ||
+ | static char papierkorb; | ||
+ | if (y >= hoehe() || x >= zeilen[y].size()) return papierkorb = ' | ||
+ | return zeilen[y][x]; | ||
+ | } | ||
+ | </ | ||
+ | Die statische Funktion '' | ||
+ | kann ausgefranste Textbilder erzeugen: | ||
+ | |||
+ | <code cpp> | ||
+ | Textbild Textbild:: | ||
+ | { | ||
+ | Textbild bild; | ||
+ | if (!text.empty()) | ||
+ | { | ||
+ | unsigned start = 0; | ||
+ | unsigned end = text.find(trenner, | ||
+ | while (end != std:: | ||
+ | { | ||
+ | bild.zeilen.push_back(text.substr(start, | ||
+ | start = end + trenner.size(); | ||
+ | end = text.find(trenner, | ||
+ | } | ||
+ | bild.zeilen.push_back(text.substr(start)); | ||
+ | } | ||
+ | return bild; | ||
+ | } | ||
+ | </ | ||
+ | Bilder untereinander anordnen ist | ||
+ | (unter Nutzung eines '' | ||
+ | |||
+ | <code cpp> | ||
+ | Textbild unter(Textbild const& oben, Textbild const& unten) | ||
+ | { | ||
+ | Textbild gesamt(oben); | ||
+ | std:: | ||
+ | std:: | ||
+ | return gesamt; | ||
+ | } | ||
+ | </ | ||
+ | ... als die Bilder nebeneinander zu legen: | ||
+ | |||
+ | <code cpp> | ||
+ | Textbild neben(Textbild const& links, Textbild const& rechts) | ||
+ | { | ||
+ | Textbild gesamt; | ||
+ | unsigned bl = links.breite(), | ||
+ | |||
+ | while (i < links.hoehe() || j < rechts.hoehe()) | ||
+ | { | ||
+ | std::string s; | ||
+ | if (i < links.hoehe()) | ||
+ | s += links.zeilen[i++]; | ||
+ | |||
+ | if (j < rechts.hoehe()) | ||
+ | { | ||
+ | s.resize(bl, | ||
+ | s += rechts.zeilen[j++]; | ||
+ | } | ||
+ | gesamt.zeilen.push_back(s); | ||
+ | } | ||
+ | return gesamt; | ||
+ | } | ||
+ | </ | ||
+ | Umrahmungen beginnen und enden mit einer Randzeile. | ||
+ | Die dazwischen liegenden Zeilen werden links und rechts | ||
+ | von je einem Randzeichen und einem Leerzeichen eingeschlossen. | ||
+ | |||
+ | <code cpp> | ||
+ | Textbild umrahme(Textbild const& bild, char zeichen) | ||
+ | { | ||
+ | unsigned maxbreite = bild.breite(); | ||
+ | std::string randzeile(maxbreite+4, | ||
+ | |||
+ | Textbild gerahmt; | ||
+ | gerahmt.zeilen.push_back(randzeile); | ||
+ | |||
+ | for (unsigned i = 0; i < bild.hoehe(); | ||
+ | { | ||
+ | std::string s(1, zeichen); | ||
+ | s += " " + bild.zeilen[i]; | ||
+ | s.resize(maxbreite+4, | ||
+ | s[maxbreite+3] = zeichen; | ||
+ | gerahmt.zeilen.push_back(s); | ||
+ | } | ||
+ | gerahmt.zeilen.push_back(randzeile); | ||
+ | return gerahmt; | ||
+ | } | ||
+ | //~ | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Demo- und Testprogramm | ||
+ | <code cpp> | ||
+ | //: testbild.cpp : Testprogramm ASCII-Bilder - R.Richter 2005-03-20 | ||
+ | /////////////////////////////////////////////////////////////////// | ||
+ | #include < | ||
+ | #include " | ||
+ | using namespace std; | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | Textbild sterne(3, 5, ' | ||
+ | Textbild punkte(5, 3, ' | ||
+ | Textbild striche(5, 4, ' | ||
+ | | ||
+ | for (int i = 0; i < striche.breite(); | ||
+ | { | ||
+ | striche.at(i, | ||
+ | } | ||
+ | |||
+ | std::cout << " | ||
+ | std::cout << " | ||
+ | |||
+ | string text = "B ilder\n" | ||
+ | "I neinander\n" | ||
+ | "L egen macht\n" | ||
+ | "D ir Spass?"; | ||
+ | |||
+ | std::cout << " | ||
+ | << umrahme(unter(neben(sterne, | ||
+ | | ||
+ | ).randausgleich(' | ||
+ | '#' | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Übersetzen und ausführen ===== | ||
+ | <code cpp> | ||
+ | /* | ||
+ | g++ -o testbild testbild.cpp textbild.cpp | ||
+ | testbild | ||
+ | </ | ||
+ | Nach dem Übersetzen erzeugt das Demoprogramm die Ausgabe: | ||
+ | |||
+ | |||
+ | <code cpp> | ||
+ | Bilder untereinander: | ||
+ | ..... | ||
+ | ..... | ||
+ | ..... | ||
+ | *** | ||
+ | *** | ||
+ | *** | ||
+ | *** | ||
+ | *** | ||
+ | Bilder nebeneinander: | ||
+ | .....*** | ||
+ | .....*** | ||
+ | .....*** | ||
+ | *** | ||
+ | *** | ||
+ | Bilder kombiniert, gerahmt: | ||
+ | ##################### | ||
+ | # ***.....~~~~~~~~~ # | ||
+ | # ***.....~~~~~~~~~ # | ||
+ | # ***.....~~~~~~~~~ # | ||
+ | # ***~~~~~~~~~~~~~~ # | ||
+ | # ***~~~~~~~~~~~~~~ # | ||
+ | # B ilder O---- # | ||
+ | # I neinander -O--- # | ||
+ | # L egen macht--O-- # | ||
+ | # D ir Spass? ---O- # | ||
+ | ##################### | ||
+ | */ | ||
+ | </ | ||
anwenden/asciipic.txt · Zuletzt geändert: 2020-07-26 17:04 von 127.0.0.1