1 /*****
2  * transform.h
3  * Andy Hammerlindl 2002/05/22
4  *
5  * The transform datatype stores an affine transformation on the plane
6  * The datamembers are x, y, xx, xy, yx, and yy.  A pair (x,y) is
7  * transformed as
8  *   x' = t.x + t.xx * x + t.xy * y
9  *   y' = t.y + t.yx * x + t.yy * y
10  *****/
11 
12 #ifndef TRANSFORM_H
13 #define TRANSFORM_H
14 
15 #include <iostream>
16 
17 #include "pair.h"
18 
19 namespace camp {
20 
21 class transform : public gc {
22   double x;
23   double y;
24   double xx;
25   double xy;
26   double yx;
27   double yy;
28 
29 public:
transform()30   transform()
31     : x(0.0), y(0.0), xx(1.0), xy(0.0), yx(0.0), yy(1.0) {}
32 
~transform()33   virtual ~transform() {}
34 
transform(double x,double y,double xx,double xy,double yx,double yy)35   transform(double x, double y,
36             double xx, double xy,
37             double yx, double yy)
38     : x(x), y(y), xx(xx), xy(xy), yx(yx), yy(yy) {}
39 
getx()40   double getx() const { return x; }
gety()41   double gety() const { return y; }
getxx()42   double getxx() const { return xx; }
getxy()43   double getxy() const { return xy; }
getyx()44   double getyx() const { return yx; }
getyy()45   double getyy() const { return yy; }
46 
47   friend transform operator+ (const transform& t, const transform& s)
48   {
49     return transform(t.x + s.x, t.y + s.y,
50                      t.xx + s.xx, t.xy + s.xy,
51                      t.yx + s.yx, t.yy + s.yy);
52   }
53 
54   friend transform operator- (const transform& t, const transform& s)
55   {
56     return transform(t.x - s.x, t.y - s.y,
57                      t.xx - s.xx, t.xy - s.xy,
58                      t.yx - s.yx, t.yy - s.yy);
59   }
60 
61   friend transform operator- (const transform& t)
62   {
63     return transform(-t.x, -t.y,
64                      -t.xx, -t.xy,
65                      -t.yx, -t.yy);
66   }
67 
68   friend pair operator* (const transform& t, const pair& z)
69   {
70     double x = z.getx(), y = z.gety();
71     return pair(t.x + t.xx * x + t.xy * y, t.y + t.yx * x + t.yy * y);
72   }
73 
74   // Calculates the composition of t and s, so for a pair, z,
75   //   t * (s * z) == (t * s) * z
76   // Can be thought of as matrix multiplication.
77   friend transform operator* (const transform& t, const transform& s)
78   {
79     return transform(t.x + t.xx * s.x  + t.xy * s.y,
80                      t.y + t.yx * s.x  + t.yy * s.y,
81                      t.xx * s.xx + t.xy * s.yx,
82                      t.xx * s.xy + t.xy * s.yy,
83                      t.yx * s.xx + t.yy * s.yx,
84                      t.yx * s.xy + t.yy * s.yy);
85   }
86 
87   friend bool operator== (const transform& t1, const transform& t2)
88   {
89     return t1.x  == t2.x  && t1.y  == t2.y  &&
90       t1.xx == t2.xx && t1.xy == t2.xy &&
91       t1.yx == t2.yx && t1.yy == t2.yy;
92   }
93 
94   friend bool operator!= (const transform& t1, const transform& t2)
95   {
96     return !(t1 == t2);
97   }
98 
isIdentity()99   bool isIdentity() const
100   {
101     return x == 0.0 && y == 0.0 &&
102       xx == 1.0 && xy == 0.0 && yx == 0.0 && yy == 1.0;
103   }
104 
isNull()105   bool isNull() const
106   {
107     return x == 0.0 && y == 0.0 &&
108       xx == 0.0 && xy == 0.0 && yx == 0.0 && yy == 0.0;
109   }
110 
111   // Calculates the determinant, as if it were a matrix.
det(const transform & t)112   friend double det(const transform& t)
113   {
114     return t.xx * t.yy - t.xy * t.yx;
115   }
116 
117   // Tells if the transformation is invertible (bijective).
invertible()118   bool invertible() const
119   {
120     return det(*this) != 0.0;
121   }
122 
inverse(const transform & t)123   friend transform inverse(const transform& t)
124   {
125     double d = det(t);
126     if (d == 0.0)
127       reportError("inverting singular transform");
128 
129     d=1.0/d;
130     return transform((t.xy * t.y - t.yy * t.x)*d,
131                      (t.yx * t.x - t.xx * t.y)*d,
132                      t.yy*d, -t.xy*d, -t.yx*d, t.xx*d);
133   }
134 
135   friend ostream& operator<< (ostream& out, const transform& t)
136   {
137     return out << "(" << t.x  << ","
138                << t.y  << ","
139                << t.xx << ","
140                << t.xy << ","
141                << t.yx << ","
142                << t.yy << ")";
143   }
144 };
145 
146 // The common transforms
147 static const transform identity;
148 
shift(pair z)149 inline transform shift(pair z)
150 {
151   return transform (z.getx(), z.gety(), 1.0, 0.0, 0.0, 1.0);
152 }
153 
xscale(double s)154 inline transform xscale(double s)
155 {
156   return transform (0.0, 0.0, s, 0.0, 0.0, 1.0);
157 }
158 
yscale(double s)159 inline transform yscale(double s)
160 {
161   return transform (0.0, 0.0, 1.0, 0.0, 0.0, s);
162 }
163 
scale(double s)164 inline transform scale(double s)
165 {
166   return transform (0.0, 0.0, s, 0.0, 0.0, s);
167 }
168 
scale(double x,double y)169 inline transform scale(double x, double y)
170 {
171   return transform (0.0, 0.0, x, 0.0, 0.0, y);
172 }
173 
scale(pair z)174 inline transform scale(pair z)
175 {
176   // Equivalent to multiplication by z.
177   double x = z.getx(), y = z.gety();
178   return transform (0.0, 0.0, x, -y, y, x);
179 }
180 
slant(double s)181 inline transform slant(double s)
182 {
183   return transform (0.0, 0.0, 1.0, s, 0.0, 1.0);
184 }
185 
rotate(double theta)186 inline transform rotate(double theta)
187 {
188   double s = sin(theta), c = cos(theta);
189   return transform (0.0, 0.0, c, -s, s, c);
190 }
191 
192 // return rotate(angle(v)) if z != (0,0); otherwise return identity.
rotate(pair z)193 inline transform rotate(pair z)
194 {
195   double d=z.length();
196   if(d == 0.0) return identity;
197   d=1.0/d;
198   return transform (0.0, 0.0, d*z.getx(), -d*z.gety(), d*z.gety(), d*z.getx());
199 }
200 
rotatearound(pair z,double theta)201 inline transform rotatearound(pair z, double theta)
202 {
203   // Notice the operators are applied from right to left.
204   // Could be optimized.
205   return shift(z) * rotate(theta) * shift(-z);
206 }
207 
reflectabout(pair z,pair w)208 inline transform reflectabout(pair z, pair w)
209 {
210   if (z == w)
211     reportError("points determining line to reflect about must be distinct");
212 
213   // Also could be optimized.
214   transform basis = shift(z) * scale(w-z);
215   transform flip = yscale(-1.0);
216 
217   return basis * flip * inverse(basis);
218 }
219 
220 // Return the rotational part of t.
rotation(transform t)221 inline transform rotation(transform t)
222 {
223   pair z(2.0*t.getxx()*t.getyy(),t.getyx()*t.getyy()-t.getxx()*t.getxy());
224   if(t.getxx() < 0) z=-z;
225   return rotate(atan2(z.gety(),z.getx()));
226 }
227 
228 // Remove the x and y components, so that the new transform maps zero to zero.
shiftless(transform t)229 inline transform shiftless(transform t)
230 {
231   return transform(0, 0, t.getxx(), t.getxy(), t.getyx(), t.getyy());
232 }
233 
234 // Return the translational component of t.
shift(transform t)235 inline transform shift(transform t)
236 {
237   return transform(t.getx(), t.gety(), 1.0, 0, 0, 1.0);
238 }
239 
240 // Return the translational pair of t.
shiftpair(transform t)241 inline pair shiftpair(transform t)
242 {
243   return pair(t.getx(), t.gety());
244 }
245 
matrix(pair lb,pair rt)246 inline transform matrix(pair lb, pair rt)
247 {
248   pair size=rt-lb;
249   return transform(lb.getx(),lb.gety(),size.getx(),0,0,size.gety());
250 }
251 
252 } //namespace camp
253 
254 GC_DECLARE_PTRFREE(camp::transform);
255 
256 #endif
257