/* Copyright (c) 1992 by AT&T Bell Laboratories. */ /* Advanced C++ Programming Styles and Idioms */ /* James O. Coplien */ /* All rights reserved. */ #include // #include typedef long Display, Window, GContext; /* * File Shapes.h * Author: J. O. Coplien * * This header file contains declarations for classes used in * a demonstration Shape library. Many member functions are * defined herein as inlines. */ /*-----------------------------------------------------------*/ /* class Angle is an angle in degrees or radians. It is a */ /* class so that it can be easily ported to systems using */ /* either degrees or radians, and also so arithmetic */ /* precision(float, double, or int) can be changed. */ /*-----------------------------------------------------------*/ class Angle { friend double cos(Angle); friend double sin(Angle); public: Angle(double d) { radians = d; } Angle () { radians = 0; } Angle(const Angle& a) { radians = a.radians; } inline Angle &operator=(const Angle& a) { radians = a.radians; return *this; } Angle operator+(const Angle&) const; private: double radians; }; /*-----------------------------------------------------------*/ /* class Coordinate describes a point on the Cartesian plane.*/ /* It can be in micrometers, pixels, inches, or any other */ /* convenient units and scale. */ /*-----------------------------------------------------------*/ class Coordinate { public: inline long x() const { return x_rep; } inline long y() const { return y_rep; } Coordinate &operator=(const Coordinate &c) { x_rep = c.x_rep; y_rep = c.y_rep; return *this; } Coordinate(long x, long y) { x_rep = x; y_rep = y; } Coordinate(const Coordinate &c) { x_rep = c.x_rep; y_rep = c.y_rep; } Coordinate() { x_rep = y_rep = 0;} ~Coordinate() { } void rotateAbout(Coordinate,Angle); private: long x_rep, y_rep; }; /*-----------------------------------------------------------*/ /* These functions overload the normal trig functions, */ /* arranging so that they can be called with Angle as an */ /* argument. Since overloading cannot be done on the basis */ /* of return value--only on the basis of argument */ /* type--the trig function atan, which needs to be able */ /* to return an object of type Angle, cannot be overloaded */ /* and must be made a whole new function. */ /*-----------------------------------------------------------*/ inline double cos(Angle a) { return ::cos(a.radians); } inline double sin(Angle a) { return ::sin(a.radians); } inline Angle Angleatan(double t) { Angle a(::atan(t)); return a; } /*-----------------------------------------------------------*/ /* Class Color encapsulated the implementation of color */ /* encoding for the graphics package being used; its */ /* internals can be accessed just by low-level interface */ /* routines, while high-level routines deal at a more general*/ /* level. This makes porting possible across a wide variety */ /* of machines. */ /* Flashing is an attribute of color. The commonly used */ /* constants Black and White are predeclared as constant */ /* global color objects. */ /*-----------------------------------------------------------*/ enum Flash { FlashBlack, FlashWhite, FlashOff }; class Color { public: inline double red() { return red_rep; } inline double green() { return green_rep; } inline double blue() { return blue_rep; } Color(double r=0.0, double g=0.0, double b=0.0, Flash f=FlashOff) { red_rep = r; green_rep = g; blue_rep = b; flashing = f; } inline Color& operator=(const Color& c) { red_rep = c.red_rep; green_rep=c.green_rep; blue_rep = c.blue_rep; return *this; } ~Color() { } private: double red_rep, green_rep, blue_rep; Flash flashing; }; const Color Black, White = Color(1.0, 1.0, 1.0); /*-----------------------------------------------------------*/ /* Shape is the base class for all of the graphical shapes. */ /* It defines the signature (operations) possible on a shape,*/ /* and also provides the actual implementation of operators */ /* common to all shapes (like move, common to all with some */ /* exceptions). */ /*-----------------------------------------------------------*/ class Shape { public: // rotate about center virtual void rotate(Angle a) { } virtual void move(Coordinate xy) { erase(); center_val=xy; draw(); } virtual void draw() = 0; // renderer virtual void erase() { color = Black; draw(); } virtual void redraw() { erase(); draw(); } Shape(); virtual ~Shape(); virtual Coordinate origin() const { return center_val; } virtual Coordinate center() const { return center_val; } inline Shape *next() const { return next_pointer; } inline Shape *prev() const { return prev_pointer; } inline void insert(Shape* i) { i->next_pointer= this; i->prev_pointer=prev_pointer; prev_pointer->next_pointer=i; prev_pointer = i; } inline void append(Shape* i) { i->next_pointer=next(); i->prev_pointer = this; next_pointer->prev_pointer=i; next_pointer = i; } protected: /* * These are protected instead of private so they can be * accessed by routines in the derived classes */ Coordinate center_val; // nominal center Color color; // shape's color /* * These class static variables are used by the underlying * window package (e.g., X) for general graphics setup. */ static Display display; static Window window; static GContext gc; private: Shape *next_pointer; // pointer to next in list Shape *prev_pointer; // pointer to previous in list }; /*-----------------------------------------------------------*/ /* Line is a special shape, since it is usually the */ /* rendering component of all other shapes. It alone among */ /* shapes has a rotateAbout member function which is used as */ /* the primitive for rotating other shapes. The rotate */ /* operation is a degenerate form of rotateAbout. The center */ /* is defined as the average of the endpoints; the origin is */ /* the first endpoint. */ /*-----------------------------------------------------------*/ class Line : public Shape { public: void rotate(Angle a) { rotateAbout(center(),a); } void rotateAbout(Coordinate,Angle); void draw(); inline Line &operator=(const Line &l) { (void)this->Shape::operator=(*this); p=Coordinate(l.p); q=Coordinate(l.q); return *this; } Line(Coordinate,Coordinate); Line(Line& l) { p = l.p; q = l.q; center_val = l.center_val; } Line() { p = Coordinate(0,0); q = Coordinate(0,0); } ~Line() { erase(); } inline Coordinate e() const { return q; } inline Coordinate origin() const { return p; } private: Coordinate p, q; // line endpoints }; /*-----------------------------------------------------------*/ /* Rect is made up of four lines. Origin is upper-left hand */ /* corner, and is established from the constructor's first */ /* parameter. Center is geometric center, as expected. The */ /* operation erase is inherited from Shape. Reps are */ /* protected instead of private so that subclass Square can */ /* use them. */ /*-----------------------------------------------------------*/ class Rect : public Shape { public: void rotate(Angle); void draw(); void move(Coordinate); //upper left corner, x size, y size Rect(Coordinate,long,long); ~Rect() { erase(); } inline Coordinate origin() const { return l1.origin(); } protected: Line l1, l2, l3, l4; }; /*-----------------------------------------------------------*/ /* Ellipse's center is geometric center; origin is center. */ /* Ellipse is unimplemented. */ /*-----------------------------------------------------------*/ class Ellipse : public Shape { public: void rotate(Angle); void draw(); // center, major axis, minor axis Ellipse(Coordinate,long,long); ~Ellipse() { erase(); } protected: long major, minor; }; /*-----------------------------------------------------------*/ /* A triangle is made up of three lines. Its center is */ /* calculated as the average of all points. */ /*-----------------------------------------------------------*/ class Triangle : public Shape { public: void rotate(Angle); void draw(); void move(Coordinate); Triangle(Coordinate,Coordinate,Coordinate); ~Triangle() { erase(); } private: Line l1, l2, l3; }; /*-----------------------------------------------------------*/ /* A square is just a degenerate rectangle. All member */ /* functions are inherited from Rect; only the constructor */ /* is customized, and it does nothing but call Rect's */ /* constructor. */ /*-----------------------------------------------------------*/ class Square : public Rect { public: Square(Coordinate ctr, long x): Rect(ctr, x, x) { } }; /*-----------------------------------------------------------*/ /* */ /* File Shapes.c, containing Shape code for example program */ /* */ /*-----------------------------------------------------------*/ extern "C" { extern void doXInitialization(Shape&); // extern void // XDrawLine(Display, Window, GContext, int, int, int, int); } /* * CLASS ANGLE */ Angle Angle::operator+(const Angle& angle) const { Angle retval = angle; retval.radians += radians; return retval; } /* * CLASS COORDINATE */ void Coordinate::rotateAbout(Coordinate pivot, Angle angle) { long xdistance = pivot.x()-x(), ydistance = pivot.y()-y(); long xdistsquared = xdistance * xdistance, ydistsquared = ydistance * ydistance; double r = ::sqrt( xdistsquared + ydistsquared ); Angle newangle = angle + Angleatan(double(ydistance)/double(xdistance)); x_rep = pivot.x() + long(r * ::cos(newangle)); y_rep = pivot.y() + long(r * ::sin(newangle)); } /* * CLASS SHAPE */ /* flag for underlying graphics package */ static int X_initialized = 0; Shape::Shape() { center_val = Coordinate(0,0); next_pointer=prev_pointer=0; color = White; if( !X_initialized) { doXInitialization(*this); X_initialized = 1; } } /* * CLASS LINE */ void Line::rotateAbout(Coordinate c, Angle angle) { erase(); p.rotateAbout(c, angle); q.rotateAbout(c, angle); draw(); } void Line::draw() { if (p.x()-q.x() && p.y()-q.y()) { // XDrawLine(display, window, gc, p.x(), // p.y(), q.x(), q.y()); } } Line::Line(Coordinate p1, Coordinate p2) { p = p1; q = p2; center_val = Coordinate((p.x()+q.x())/2, (p.y()+q.y())/2); color = Color(White); draw(); } /* * CLASS RECTANGLE */ void Rect::rotate(Angle angle) { erase(); l1.rotateAbout(center(), angle); l2.rotateAbout(center(), angle); l3.rotateAbout(center(), angle); l4.rotateAbout(center(), angle); draw(); } void Rect::draw() { l1.draw(); l2.draw(); l3.draw(); l4.draw(); } void Rect::move(Coordinate c) { /* * Argument is center. Move center there; find out how far * center moved and displace all points by the same amount. */ erase(); long xmoved = c.x() - center().x(); long ymoved = c.y() - center().y(); center_val = c; l1 = Line(Coordinate(l1.origin().x()+xmoved, l1.origin().y()+ymoved), Coordinate(l1.e().x()+xmoved,l1.e().y()+ymoved) ); l2 = Line(Coordinate(l2.origin().x()+xmoved, l2.origin().y()+ymoved), Coordinate(l2.e().x()+xmoved,l2.e().y()+ymoved) ); l3 = Line(Coordinate(l3.origin().x()+xmoved, l3.origin().y()+ymoved), Coordinate(l3.e().x()+xmoved,l3.e().y()+ymoved) ); l4 = Line(Coordinate(l4.origin().x()+xmoved, l4.origin().y()+ymoved), Coordinate(l4.e().x()+xmoved,l4.e().y()+ymoved) ); draw(); } Rect::Rect(Coordinate topLeft, long xsize, long ysize) { Coordinate a(topLeft); Coordinate b(a.x()+xsize, a.y()); Coordinate c(a.x(),a.y()+ysize); Coordinate d(b.x(),c.y()); l1 = Line(a,b); l2 = Line(b,c); l3 = Line(c,d); l4 = Line(d,a); center_val = Coordinate((l1.origin().x()+l2.e().x())/2, (l4.origin().y()+l4.e().y())/2); draw(); } /* * CLASS TRIANGLE */ void Triangle::rotate(Angle angle) { erase(); l1.rotateAbout(center(), angle); l2.rotateAbout(center(), angle); l3.rotateAbout(center(), angle); draw(); } void Triangle::move(Coordinate c) { /* * Argument is center. Move center there; find out how far * center moved and displace all points by the same amount. */ erase(); long xmoved = c.x() - center().x(); long ymoved = c.y() - center().y(); center_val = c; l1 = Line(Coordinate(l1.origin().x()+xmoved, l1.origin().y()+ymoved), Coordinate(l1.e().x()+xmoved,l1.e().y()+ymoved) ); l2 = Line(Coordinate(l2.origin().x()+xmoved, l2.origin().y()+ymoved), Coordinate(l2.e().x()+xmoved,l2.e().y()+ymoved) ); l3 = Line(Coordinate(l3.origin().x()+xmoved, l3.origin().y()+ymoved), Coordinate(l3.e().x()+xmoved,l3.e().y()+ymoved) ); draw(); } void Triangle::draw() { l1.draw(); l2.draw(); l3.draw(); } Triangle::Triangle(Coordinate a, Coordinate b, Coordinate c) { l1 = Line(a,b); l2 = Line(b,c); l3 = Line(c,a); center_val = Coordinate((l1.e().x()+l2.e().x()+l3.e().x())/3, (l1.e().y()+l2.e().y()+l3.e().y())/3); draw(); } /*-----------------------------------------------------------*/ /* File main.c -- Driver program for Shapes example */ /*-----------------------------------------------------------*/ const int XSIZE = 800; const int YSIZE = 1024; void rotateList(Shape *s, Angle a) { for (Shape *f = s; f; f = f->next()) f->rotate(a); } int main() { Coordinate origin (0,0); /* * windowBorder is not only the frame for the screen, * but also serves as list head for the list of shapes */ Rect windowBorder(origin, XSIZE, YSIZE); Shape * t = new Triangle( Coordinate(100, 100), Coordinate(200, 200), Coordinate(300, 100) ); windowBorder.append(t); // Shape * c = new Circle(Coordinate(500,652), 150); // t->insert(c); rotateList(&windowBorder, 90.0); return 0; }