// Mein erstes Programm in C++ // Da diese Klassen ziemlich klein sind und zu unvollständig, // um jemals wiederverwendet zu werden, verzichte ich auf das // Theater mit x Class Header Dateien und getrennten Modulen: #include #include class Object {}; // abstrakte Basisklasse class Collection : public Object { public: virtual void operator += (Object&) {}; // Einfügeoperation virtual void operator -- () {}; // Löschoperation // Um hier eine Löschoperation -= (Object sub) implementieren zu // können, müßte ein virtual boolean Object::operator == (Object) // existieren und in alle Klassen eingebaut werden. Da für unseren // Testlauf kein Löschen gefordert und die Funktionalität der // Löschoperation in der Aufgabenstellung offen gelassen wurde, // ist ein unäres --, welches ein willkürlich festgelegtes Element // löscht, einfacher zu implementieren. virtual void iteration () {}; // Initialisator für: virtual Object* next () { return NULL; }; // nur ein Iterator, keine verschachtelten }; struct Link { Object * element; Link * next; }; // In List werden nur Objektreferenzen eingetragen, nicht aber das Objekt // kopiert. Wie man das besser macht, habe ich noch nicht herausgefunden. class List: public Collection { Link * first, * second, * iterator; public: List () { // Konstruktor first = iterator = NULL; } void operator += (Object& add) { second = new Link; second -> element = & add; second -> next = first; first = second; } void iteration () { iterator = first; } Object* next () { register Object * value = NULL; if (iterator) { value = iterator -> element; iterator = iterator -> next; } else iterator = first; return value; } void operator -- () { delete first -> element; second = first -> next; delete first; first = second; } // liefert SIGSEGV, wenn auf leere List angewandt }; struct Vector { // Vektorgrafik im R² double X, Y; Vector (double x = 0, double y = 0) {X = x; Y = y; } void operator *= (double r) { X *= r; Y *= r; } void operator += (Vector v) { X += v.X; Y += v.Y; } void operator -= (Vector v) { X -= v.X; Y -= v.Y; } void rotate (double angle); }; void Vector::rotate (double angle) { angle *= PI / 180; double C = cos (angle), S = sin (angle), Z = X * C - Y * S; Y = X * S + Y * C; X = Z; } const Vector Null (0,0); ostream & operator << (ostream & out, Vector V) { out << " (" << V.X << "," << V.Y << ") "; return out; } class Shape: public Object { protected: Vector Center; public: virtual void center (Vector& result) { result = Center; } virtual void recenter (Vector where = Null) { Center = where; } virtual void rotate (double angle = 90) { angle = 0; } virtual void draw () { cerr << "draw?" << endl; } }; class Circle: public Shape { double Diameter; public: Circle (Vector C = Null, double D = 1) { Center = C; Diameter = D; } void draw () { cout << "circle at" << Center; cout << "diameter " << Diameter << endl; } }; // Ausgabe der Grafik im pic-Format class Rectangle: public Shape { double Width, Height, Incline; public: Rectangle (Vector C = Null, double W = 1, double H = 1, double I = 0) { Center = C; Width = W; Height = H; Incline = I; } void rotate (double angle = 90) { Incline += angle; } void draw (); }; const char to[] = "to"; void Rectangle::draw () { Vector East (Width / 2, 0); East.rotate (Incline); Vector North (0, Height / 2); North.rotate (Incline); Vector Northeast = North; Northeast += East; Vector Northwest = North; Northwest -= East; Vector Southwest = Center; Southwest -= Northeast; Vector Southeast = Center; Southeast -= Northwest; Northeast += Center; Northwest += Center; cout << "line from" << Northeast << to << Northwest << to; cout << Southwest << to << Southeast << to << Northeast << endl; } class Graphics: public Shape, public List { // Hilfsvariablen : Vector C; Shape *S; // Daß hier ein Cast von Object* nach Shape* möglich ist, // ist ja wohl ein Sicherheitsloch in C++. Shape ist schließlich // keine Basisklasse von Object und man könnte hier versuchen, // Objekte zu drehen, die gar keine Geometrie haben. Wozu das // wohl führen würde. Jedoch ohne es wäre die Aufgabe unlösbar. public: void center (Vector & result) { int count = 0; result = Null; for (iteration(); S = (Shape*) next(); ++ count) { S->center (C); result += C; } if (count) result *= 1.0 / count; } // Es war nicht definiert, was unter Zentrum einer Menge von Formen // zu verstehen sein soll, ich berechne hier den Schwerpunkt unter // der Annahme, alle Elemente seien gleich schwer. Genau genommen, // bräuchte man hier noch ein virtual double Shape::weight(). Das // spare ich mir mal. void recenter (Vector where = Null) { center (Center); where -= Center; for (iteration (); S = (Shape*) next();) { S -> center (C); C += where; S -> recenter (C); } } // Die Methoden rotate und recenter funktionieren nur dann // ordnungsgemäß, wenn keine Figuren mehrfach eingetragen sind. void rotate (double angle = 90) { center (Center); for (iteration (); S = (Shape*) next (); ) { S -> center (C); C -= Center; // um den Schwerpunkt C.rotate (angle); S -> rotate (angle); C += Center; S -> recenter (C); } } void draw () { for (iteration (); S = (Shape*) next(); S -> draw ()); } }; // Testgraphik, etwas chaotisch int main () { // Zwei Nackommastellen, keine Exponentialdarstellung, bitte: cout.setf (ios::fixed); cout.precision (2); cout << ".PS\n"; Rectangle SquareInch, SmallSquare (Null, 0.4, 0.4), Tower (Vector (0.7, 4), 1, 6), Board (Vector (2,0.75), 5, 0.2), Decline (Vector (3), 3, 0, -20); Circle O (Null, 0.1), Ball (Vector (3, 2.5), 3); Graphics G, H; G += * (Shape *) & H; G += Tower; H += SmallSquare; G += Ball; G += Board; H += SquareInch; G += Decline; H += O; G.draw(); H --; Vector V; G.center (V); V += Vector (-1, 0.25); G.recenter (V); G.draw(); cout << "circle invis \"X\" at" << V << endl; // makiert Schwerpunkt G.rotate (-1); G.draw (); G.rotate (-1); G.draw (); G.rotate (-1); G.draw (); G.rotate (-1); G.draw (); cout << ".PE\n"; return 0; // Es funktioniert alles: Kreise, Quadrate, einfügen, löschen // Graph mit Teilgraphelement, verschieben (und damit implizit // auch die Schwerpunktberechnung) und rotieren. // Vergleiche Makefile für den korrekten Aufruf mit Bildausgabe. }