/***** * transform.h * Andy Hammerlindl 2002/05/22 * * The transform datatype stores an affine transformation on the plane * The datamembers are x, y, xx, xy, yx, and yy. A pair (x,y) is * transformed as * x' = t.x + t.xx * x + t.xy * y * y' = t.y + t.yx * x + t.yy * y *****/ #ifndef TRANSFORM_H #define TRANSFORM_H #include #include "pair.h" namespace camp { class transform : public gc { double x; double y; double xx; double xy; double yx; double yy; public: transform() : x(0.0), y(0.0), xx(1.0), xy(0.0), yx(0.0), yy(1.0) {} virtual ~transform() {} transform(double x, double y, double xx, double xy, double yx, double yy) : x(x), y(y), xx(xx), xy(xy), yx(yx), yy(yy) {} double getx() const { return x; } double gety() const { return y; } double getxx() const { return xx; } double getxy() const { return xy; } double getyx() const { return yx; } double getyy() const { return yy; } friend transform operator+ (const transform& t, const transform& s) { return transform(t.x + s.x, t.y + s.y, t.xx + s.xx, t.xy + s.xy, t.yx + s.yx, t.yy + s.yy); } friend transform operator- (const transform& t, const transform& s) { return transform(t.x - s.x, t.y - s.y, t.xx - s.xx, t.xy - s.xy, t.yx - s.yx, t.yy - s.yy); } friend transform operator- (const transform& t) { return transform(-t.x, -t.y, -t.xx, -t.xy, -t.yx, -t.yy); } friend pair operator* (const transform& t, const pair& z) { double x = z.getx(), y = z.gety(); return pair(t.x + t.xx * x + t.xy * y, t.y + t.yx * x + t.yy * y); } // Calculates the composition of t and s, so for a pair, z, // t * (s * z) == (t * s) * z // Can be thought of as matrix multiplication. friend transform operator* (const transform& t, const transform& s) { return transform(t.x + t.xx * s.x + t.xy * s.y, t.y + t.yx * s.x + t.yy * s.y, t.xx * s.xx + t.xy * s.yx, t.xx * s.xy + t.xy * s.yy, t.yx * s.xx + t.yy * s.yx, t.yx * s.xy + t.yy * s.yy); } friend bool operator== (const transform& t1, const transform& t2) { return t1.x == t2.x && t1.y == t2.y && t1.xx == t2.xx && t1.xy == t2.xy && t1.yx == t2.yx && t1.yy == t2.yy; } friend bool operator!= (const transform& t1, const transform& t2) { return !(t1 == t2); } bool isIdentity() const { return x == 0.0 && y == 0.0 && xx == 1.0 && xy == 0.0 && yx == 0.0 && yy == 1.0; } bool isIsometry() const { return xx*xx+xy*xy == 1.0 && xx*yx+xy*yy == 0.0 && yx*yx+yy*yy == 1.0; } bool isNull() const { return x == 0.0 && y == 0.0 && xx == 0.0 && xy == 0.0 && yx == 0.0 && yy == 0.0; } // Calculates the determinant, as if it were a matrix. friend double det(const transform& t) { return t.xx * t.yy - t.xy * t.yx; } // Tells if the transformation is invertible (bijective). bool invertible() const { return det(*this) != 0.0; } friend transform inverse(const transform& t) { double d = det(t); if (d == 0.0) reportError("inverting singular transform"); d=1.0/d; return transform((t.xy * t.y - t.yy * t.x)*d, (t.yx * t.x - t.xx * t.y)*d, t.yy*d, -t.xy*d, -t.yx*d, t.xx*d); } friend ostream& operator<< (ostream& out, const transform& t) { return out << "(" << t.x << "," << t.y << "," << t.xx << "," << t.xy << "," << t.yx << "," << t.yy << ")"; } }; // The common transforms static const transform identity; inline transform shift(pair z) { return transform (z.getx(), z.gety(), 1.0, 0.0, 0.0, 1.0); } inline transform xscale(double s) { return transform (0.0, 0.0, s, 0.0, 0.0, 1.0); } inline transform yscale(double s) { return transform (0.0, 0.0, 1.0, 0.0, 0.0, s); } inline transform scale(double s) { return transform (0.0, 0.0, s, 0.0, 0.0, s); } inline transform scale(double x, double y) { return transform (0.0, 0.0, x, 0.0, 0.0, y); } inline transform scale(pair z) { // Equivalent to multiplication by z. double x = z.getx(), y = z.gety(); return transform (0.0, 0.0, x, -y, y, x); } inline transform slant(double s) { return transform (0.0, 0.0, 1.0, s, 0.0, 1.0); } inline transform rotate(double theta) { double s = sin(theta), c = cos(theta); return transform (0.0, 0.0, c, -s, s, c); } // return rotate(angle(v)) if z != (0,0); otherwise return identity. inline transform rotate(pair z) { double d=z.length(); if(d == 0.0) return identity; d=1.0/d; return transform (0.0, 0.0, d*z.getx(), -d*z.gety(), d*z.gety(), d*z.getx()); } inline transform rotatearound(pair z, double theta) { // Notice the operators are applied from right to left. // Could be optimized. return shift(z) * rotate(theta) * shift(-z); } inline transform reflectabout(pair z, pair w) { if (z == w) reportError("points determining line to reflect about must be distinct"); // Also could be optimized. transform basis = shift(z) * scale(w-z); transform flip = yscale(-1.0); return basis * flip * inverse(basis); } // Return the rotational part of t. inline transform rotation(transform t) { pair z(2.0*t.getxx()*t.getyy(),t.getyx()*t.getyy()-t.getxx()*t.getxy()); if(t.getxx() < 0) z=-z; return rotate(atan2(z.gety(),z.getx())); } // Remove the x and y components, so that the new transform maps zero to zero. inline transform shiftless(transform t) { return transform(0, 0, t.getxx(), t.getxy(), t.getyx(), t.getyy()); } // Return the translational component of t. inline transform shift(transform t) { return transform(t.getx(), t.gety(), 1.0, 0, 0, 1.0); } // Return the translational pair of t. inline pair shiftpair(transform t) { return pair(t.getx(), t.gety()); } inline transform matrix(pair lb, pair rt) { pair size=rt-lb; return transform(lb.getx(),lb.gety(),size.getx(),0,0,size.gety()); } } //namespace camp GC_DECLARE_PTRFREE(camp::transform); #endif