namespace cpp {}

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


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.
 +>>---  TeX Fehlermeldung
 +
 +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 ''curses''-Bibliothek trägt ihren Namen nicht grundlos:
 +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 "ausgefranstem" rechten Rand, die z.B durch
 +  * 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:
 +//     Intensivkurs C++. Pearson Studium, München (2003), Abschnitt 5.8
 +// [2] Andrew Koenig, Barbara Moo:
 +//     Ruminations on C++. Addison-Wesley, Reading Ms. (1997).
 +
 +#ifndef TEXTBILD_H
 +#define TEXTBILD_H 
 +
 +#include <vector>
 +#include <string>
 +#include <iostream>
 +
 +class Textbild
 +{
 +public:
 +  std::vector<std::string> zeilen;    
 +
 +  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& randausgleich(char fuellzeichen); // alle Zeilen gleich lang
 +
 +  static Textbild split(std::string text, std::string trenner = "\n");
 +};
 +
 +Textbild neben  (Textbild const& links, Textbild const& rechts);
 +Textbild unter  (Textbild const& oben,  Textbild const& unten);
 +Textbild umrahme(Textbild const& bild, char zeichen);
 +
 +std::ostream& operator<<(std::ostream& os, Textbild const& bild);
 +
 +#endif // TEXTBILD_H
 +//~
 +</code>
 +
 +
 +
 +===== Implementierung =====
 +<code cpp>
 +//: textbild.cpp : ASCII-Bilder - R.Richter 2005-03-20
 +//////////////////////////////////////////////////////
 +#include <algorithm>
 +#include <iterator>
 +#include <functional>
 +#include "textbild.h"
 +</code>
 +Der Ausgabeoperator muss die Bildzeilen nur auf den Ausgabenstrom kopieren.
 +Mit Standardalgorithmen ist das auch so formulierbar.
 +Ein ''ostream_iterator<Elementtyp>''
 +veranlasst ''os<<zeilen[i]<<"\n";'' für jede Zeile des Containers. 
 +
 +<code cpp>
 +std::ostream& operator<<(std::ostream& os, Textbild const& bild)
 +{
 +  std::copy(bild.zeilen.begin(), bild.zeilen.end(),
 +            std::ostream_iterator<std::string>(os, "\n"));            
 +}
 +</code>
 +Ein Textbild kann als rechteckige,
 +mit einem Zeichen gefüllte Fläche gebildet werden,
 +deren Höhe und Breite abfragbar sind.
 +
 +<code cpp>
 +Textbild::Textbild(unsigned breite, unsigned hoehe, char fuellzeichen)
 +: zeilen(hoehe, std::string(breite, fuellzeichen))
 +{
 +}
 +
 +unsigned Textbild::hoehe() const
 +{
 +  return zeilen.size();
 +}
 +
 +unsigned Textbild::breite() const
 +{
 +  unsigned maxbreite = 0;
 +
 +  for (unsigned i=0; i < zeilen.size(); ++i)
 +  {
 +    unsigned aktuell = zeilen[i].size();
 +    if (maxbreite < aktuell)
 +      maxbreite = aktuell;
 +  }
 +  return maxbreite;
 +}
 +</code>
 +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& Textbild::randausgleich(char fuellzeichen)
 +{
 +  int b = breite();
 +  for (unsigned i=0; i<hoehe(); ++i)
 +  {
 +    zeilen[i].resize(b, fuellzeichen);               
 +  }
 +  return *this;
 +}
 +</code>
 +Der lesende / schreibende Zugriff auf Einzelzeichen mit der Methode
 +''at(x,y)'' stellt sicher, 
 +dass nicht hinter Feldgrenzen zugriffen wird.
 +Falsch addressierte Breife landen (ungeöffnet) im Papierkorb.
 +
 +<code cpp>
 +char  Textbild::at(unsigned x, unsigned y) const
 +{
 +  if (y >= hoehe() || x >= zeilen[y].size()) return '\0';
 +  return zeilen[y][x];      
 +}
 +
 +char& Textbild::at(unsigned x, unsigned y)
 +{
 +  static char papierkorb;    
 +  if (y >= hoehe() || x >= zeilen[y].size()) return papierkorb = '\0';
 +  return zeilen[y][x];      
 +}
 +</code>
 +Die statische Funktion ''split()'' zum Zerlegen von Texten
 +kann ausgefranste Textbilder erzeugen:
 +
 +<code cpp>
 +Textbild Textbild::split(std::string text, std::string trenner)
 +{
 +  Textbild bild;
 +  if (!text.empty())
 +  {
 +    unsigned start = 0;
 +    unsigned end = text.find(trenner, start);
 +    while (end != std::string::npos)
 +    {
 +      bild.zeilen.push_back(text.substr(start,end-start));
 +      start = end + trenner.size();
 +      end = text.find(trenner, start);         
 +    }
 +    bild.zeilen.push_back(text.substr(start)); 
 +  }
 +  return bild;
 +}
 +</code>
 +Bilder untereinander anordnen ist 
 +(unter Nutzung eines ''back_inserter'') etwas einfacher ...
 +
 +<code cpp>
 +Textbild unter(Textbild const& oben, Textbild const& unten)
 +{
 +  Textbild gesamt(oben);
 +  std::copy(unten.zeilen.begin(), unten.zeilen.end(), 
 +            std::back_inserter(gesamt.zeilen));
 +  return gesamt;
 +}
 +</code>
 +... als die Bilder nebeneinander zu legen:
 +
 +<code cpp>
 +Textbild neben(Textbild const& links, Textbild const& rechts)
 +{
 +  Textbild gesamt;
 +  unsigned bl = links.breite(), i = 0, j = 0;
 +
 +  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;
 +}
 +</code>
 +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, zeichen);
 +
 +  Textbild gerahmt;
 +  gerahmt.zeilen.push_back(randzeile);
 +
 +  for (unsigned i = 0; i < bild.hoehe(); ++i)
 +  {
 +    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;
 +}
 +//~
 +</code>
 +
 +
 +
 +===== Demo- und Testprogramm  =====
 +<code cpp>
 +//: testbild.cpp : Testprogramm ASCII-Bilder - R.Richter 2005-03-20
 +///////////////////////////////////////////////////////////////////
 +#include <iostream>
 +#include "textbild.h"
 +using namespace std;
 +
 +int main()
 +{
 +  Textbild sterne(3, 5, '*');
 +  Textbild punkte(5, 3, '.');
 +  Textbild striche(5, 4, '-');
 +  
 +  for (int i = 0; i < striche.breite(); ++i)
 +  {
 +    striche.at(i,i) = 'O'; // Zugriff auf "Pixel"       
 +  }
 +
 +  std::cout << "Bilder untereinander:\n" << unter(punkte, sterne);
 +  std::cout << "Bilder nebeneinander:\n" << neben(punkte, sterne);
 +
 +  string text = "B ilder\n"
 +                "I neinander\n"
 +                "L egen macht\n"
 +                "D ir Spass?";
 +
 +  std::cout << "Bilder kombiniert, gerahmt:\n"
 +            << umrahme(unter(neben(sterne, punkte), 
 +                             neben(Textbild::split(text), striche)
 +                            ).randausgleich('~'), 
 +                       '#');
 +  return 0;
 +}
 +
 +</code>
 +
 +
 +
 +===== Übersetzen und ausführen =====
 +<code cpp>
 +/*
 +g++ -o testbild testbild.cpp textbild.cpp
 +testbild
 +</code>
 +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- #
 +#####################
 +*/
 +</code>
  
anwenden/asciipic.txt · Zuletzt geändert: 2020-07-26 17:04 von 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki