Add System.Drawing.Drawing2D
[mono.git] / mcs / class / System.Drawing / System.Drawing.Drawing2D / Matrix.cs
1 //
2 // System.Drawing.Drawing2D.Matrix.cs
3 //
4 // Author:
5 //   Stefan Maierhofer <sm@cg.tuwien.ac.at>
6 //
7 // (C) Ximian, Inc.  http://www.ximian.com
8 //
9
10 using System;
11 using System.Drawing;
12
13 namespace System.Drawing.Drawing2D
14 {
15     public sealed class Matrix : MarshalByRefObject, IDisposable
16     {
17         // initialize to identity
18         private float[] m = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
19         
20         // constructors
21         public Matrix() { }
22         
23         /* TODO: depends on System.Drawing.Drawing2D.Rectangle
24         public Matrix(Rectangle rect , Point[] plgpts)
25         {
26             // TODO
27         }
28         */
29         
30         /* TODO: depends on System.Drawing.Drawing2D.RectangleF
31         public Matrix(RectangleF rect , PointF[] pa)
32         {
33             // TODO
34         }
35         */
36         public Matrix(float m11, float m12, 
37                       float m21, float m22, 
38                       float dx, float dy)
39         {
40             m[0] = m11; m[1] = m12;
41             m[2] = m21; m[3] = m22;
42             m[4] = dx; m[5] = dy;
43         }
44         
45         // properties
46         public float[] Elements
47         {
48             get { return m; }
49         }
50         
51         public bool IsIdentity
52         {
53             get 
54             {
55                 if ( (m[0] == 1.0f) && (m[1] == 0.0f) &&
56                      (m[2] == 0.0f) && (m[3] == 1.0f) &&
57                      (m[4] == 0.0f) && (m[5] == 0.0f) )
58                     return true;
59                 else 
60                     return false;
61             }
62         }
63         
64         public bool IsInvertible
65         {
66             get 
67             { 
68                 // matrix M is invertible if det(M) != 0
69                 float det = m[0] * m[3] - m[2] * m[1];
70                 if (det != 0.0f) return true;
71                 else return false;
72             }
73         }
74         
75         public float OffsetX
76         {
77             get { return m[4]; }
78         }
79         
80         public float OffsetY
81         {
82             get { return m[5]; }
83         }
84         
85         // methods
86         public Matrix Clone()
87         {
88             return new Matrix(m[0], m[1], m[2], m[3], m[4], m[5]);
89         }
90         
91         public void Dispose() { }
92         
93         public override bool Equals(object obj)
94         {
95             if (obj is Matrix)
96             {
97                 float[] a = ((Matrix)obj).Elements;
98                 if ( m[0] == a[0] && m[1] == a[1] &&
99                      m[2] == a[2] && m[3] == a[3] &&
100                      m[4] == a[4] && m[5] == a[5] ) 
101                     return true;
102                 else 
103                     return false;
104             }
105             else
106             {
107                 return false;
108             }
109         }
110         
111         ~Matrix() {}
112         
113         public override int GetHashCode()
114         {
115             // TODO: find a better hash function than det(M)
116             return (int)(m[0] * m[3] - m[2] * m[1]);
117         }
118         
119         public void Invert()
120         {
121             float det = m[0] * m[3] - m[2] * m[1];
122             if (det != 0.0f)    // if invertible
123             {
124                 float[] r = 
125                 {
126                     m[3] / det, 
127                     -m[1] / det,
128                     -m[2] / det,
129                      m[0] / det,
130                     (-m[3] * m[4] + m[1] * m[5]) / det,
131                     (m[2] * m[4] - m[0] * m[5]) / det
132                 };
133                 m = r;
134             }
135         }
136         
137         public void Multiply(Matrix matrix)
138         {
139             Multiply(matrix, MatrixOrder.Prepend);
140         }
141         
142         public void Multiply(Matrix matrix, MatrixOrder order)
143         {
144             switch (order)
145             {
146                 case MatrixOrder.Prepend:
147                     // this = matrix * this
148                     float[] p = matrix.Elements;
149                     float[] r0 = 
150                     {
151                         p[0] * m[0] + p[1] * m[2],
152                         p[0] * m[1] + p[1] * m[3],
153                         p[2] * m[0] + p[3] * m[2],
154                         p[2] * m[1] + p[3] * m[3],
155                         p[4] * m[0] + p[5] * m[2] + m[4],
156                         p[4] * m[1] + p[5] * m[3] + m[5]
157                     };
158                     m = r0;
159                     break;
160                 case MatrixOrder.Append:
161                     // this = this * matrix
162                     float[] a = matrix.Elements;
163                     float[] r1 = 
164                     {
165                         m[0] * a[0] + m[1] * a[2],
166                         m[0] * a[1] + m[1] * a[3],
167                         m[2] * a[0] + m[3] * a[2],
168                         m[2] * a[1] + m[3] * a[3],
169                         m[4] * a[0] + m[5] * a[2] + a[4],
170                         m[4] * a[1] + m[5] * a[3] + a[5]
171                     };
172                     m = r1;
173                     break;
174             }
175         }
176         
177         public void Reset()
178         {
179             m[0] = 1.0f; m[1] = 0.0f;
180             m[2] = 0.0f; m[3] = 1.0f;
181             m[4] = 0.0f; m[5] = 0.0f;
182         }
183         
184         public void Rotate(float angle)
185         {
186             Rotate(angle, MatrixOrder.Prepend);
187         }
188         
189         public void Rotate(float angle, MatrixOrder order)
190         {
191             angle *= (float)(Math.PI / 180.0);  // degrees to randians
192             float cos = (float)Math.Cos(angle);
193             float sin = (float)Math.Sin(angle);
194             switch (order)
195             {
196                 case MatrixOrder.Prepend:
197                     // this = rotation * this
198                     float[] r0 = 
199                     {
200                          cos * m[0] + sin * m[2],
201                          cos * m[1] + sin * m[3],
202                         -sin * m[0] + cos * m[2],
203                         -sin * m[1] + cos * m[3],
204                         m[4],
205                         m[5]
206                     };
207                     m = r0;
208                     break;
209                 case MatrixOrder.Append:
210                     // this = this * rotation
211                     float[] r1 = 
212                     {
213                         m[0] * cos + m[1] * -sin,
214                         m[0] * sin + m[1] *  cos,
215                         m[2] * cos + m[3] * -sin,
216                         m[2] * sin + m[3] *  cos,
217                         m[4] * cos + m[5] * -sin,
218                         m[4] * sin + m[5] *  cos
219                     };
220                     m = r1;
221                     break;
222             }
223         }
224         
225         public void RotateAt(float angle, PointF point)
226         {
227             RotateAt(angle, point, MatrixOrder.Prepend);
228         }
229         
230         public void RotateAt(float angle, PointF point, MatrixOrder order)
231         {
232             angle *= (float)(Math.PI / 180.0);  // degrees to randians
233             float cos = (float)Math.Cos(angle);
234             float sin = (float)Math.Sin(angle);
235             float e4 = -point.X * cos + point.Y * sin + point.X;
236             float e5 = -point.X * sin - point.Y * cos + point.Y;
237             switch (order)
238             {
239                 case MatrixOrder.Prepend:
240                     // this = rotation * this
241                     float[] r0 = 
242                     {
243                         cos * m[0] + sin * m[2],
244                         cos * m[1] + sin * m[3],
245                         -sin * m[0] + cos * m[2],
246                         -sin * m[1] + cos * m[3],
247                         e4 * m[0] + e5 * m[2] + m[4],
248                         e4 * m[1] + e5 * m[3] + m[5]
249                     };
250                     m = r0;
251                     break;
252                 case MatrixOrder.Append:
253                     // this = this * rotation
254                     float[] r1 = 
255                     {
256                         m[0] * cos + m[1] * -sin,
257                         m[0] * sin + m[1] * cos,
258                         m[2] * cos + m[3] * -sin,
259                         m[2] * sin + m[3] * cos,
260                         m[4] * cos + m[5] * -sin + e4,
261                         m[4] * sin + m[5] * cos + e5
262                     };
263                     m = r1;
264                     break;
265             }
266         }
267         
268         public void Scale(float scaleX, float scaleY)
269         {
270             Scale(scaleX, scaleY, MatrixOrder.Prepend);
271         }
272         
273         public void Scale(float scaleX, float scaleY, MatrixOrder order)
274         {
275             switch (order)
276             {
277                 case MatrixOrder.Prepend:
278                     // this = scale * this
279                     m[0] *= scaleX; m[1] *= scaleX;
280                     m[2] *= scaleY; m[3] *= scaleY;
281                     break;
282                 case MatrixOrder.Append:
283                     // this = this * scale
284                     m[0] *= scaleX; m[1] *= scaleY;
285                     m[2] *= scaleX; m[3] *= scaleY;
286                     m[4] *= scaleX; m[5] *= scaleY;
287                     break;
288             }
289         }
290         
291         public void Shear(float shearX, float shearY)
292         {
293             Shear(shearX, shearY, MatrixOrder.Prepend);
294         }
295         
296         // LAMESPEC: quote from beta 2 sdk docs: "[To be supplied!]"
297         //
298         // assuming transformation matrix:
299         //
300         //      (1       shearY  0)
301         //      (shearX  1       0)
302         //      (0       0       1)
303         //
304         public void Shear(float shearX, float shearY, MatrixOrder order)
305         {
306             switch (order)
307             {
308                 case MatrixOrder.Prepend:
309                     // this = shear * this
310                     float[] r0 = 
311                     {
312                         m[0] + shearY * m[2],
313                         m[1] + shearY * m[3],
314                         shearX * m[0] + m[2],
315                         shearX * m[1] + m[3],
316                         m[4],
317                         m[5]
318                     };
319                     m = r0;
320                     break;
321                 case MatrixOrder.Append:
322                     // this = this * shear
323                     float[] r1 = 
324                     {
325                         m[0] + m[1] * shearX,
326                         m[0] * shearY + m[1],
327                         m[2] + m[3] * shearX,
328                         m[2] * shearY + m[3],
329                         m[4] + m[5] * shearX ,
330                         m[4] * shearY + m[5]
331                     };
332                     m = r1;
333                     break;
334             }
335         }
336         
337         public void TransformPoints(Point[] pts)
338         {
339             for (int i = 0; i < pts.Length; i++)
340             {
341                 float x = (float)pts[i].X;
342                 float y = (float)pts[i].Y;
343                 pts[i].X = (int)(x * m[0] + y * m[2] + m[4]);
344                 pts[i].Y = (int)(x * m[1] + y * m[3] + m[5]);
345             }
346         }
347         
348         public void TransformPoints(PointF[] pts)
349         {
350             for (int i = 0; i < pts.Length; i++)
351             {
352                 float x = pts[i].X;
353                 float y = pts[i].Y;
354                 pts[i].X = x * m[0] + y * m[2] + m[4];
355                 pts[i].Y = x * m[1] + y * m[3] + m[5];
356             }
357         }
358         
359         public void TransformVectors(Point[] pts)
360         {
361             for (int i = 0; i < pts.Length; i++)
362             {
363                 float x = (float)pts[i].X;
364                 float y = (float)pts[i].Y;
365                 pts[i].X = (int)(x * m[0] + y * m[2]);
366                 pts[i].Y = (int)(x * m[1] + y * m[3]);
367             }
368         }
369         
370         public void TransformVectors(PointF[] pts)
371         {
372             for (int i = 0; i < pts.Length; i++)
373             {
374                 float x = pts[i].X;
375                 float y = pts[i].Y;
376                 pts[i].X = x * m[0] + y * m[2];
377                 pts[i].Y = x * m[1] + y * m[3];
378             }
379         }
380         
381         public void Translate(float offsetX, float offsetY)
382         {
383             Translate(offsetX, offsetY, MatrixOrder.Prepend);
384         }
385         
386         public void Translate(float offsetX, float offsetY, MatrixOrder order)
387         {
388             switch (order)
389             {
390                 case MatrixOrder.Prepend:
391                     // this = translation * this
392                     m[4] = offsetX * m[0] + offsetY * m[2] + m[4];
393                     m[5] = offsetX * m[1] + offsetY * m[3] + m[5];
394                     break;
395                 case MatrixOrder.Append:
396                     // this = this * translation
397                     m[4] += offsetX;
398                     m[5] += offsetY;
399                     break;
400             }
401         }
402         
403         // LAMESPEC: quote from beta 2 sdk docs: "[To be supplied!]"
404         public void VectorTransformPoints(Point[] pts)
405         {
406             // TODO
407         }
408         
409         /* some simple test (TODO: remove)
410         public static void Main()
411         {
412             PointF[] p = {new PointF(1.0f, 2.0f)};
413             Console.WriteLine("(" + p[0].X + " " + p[0].Y + ")");
414             Matrix m = new Matrix();
415             
416             m.Translate(1.0f, 1.0f); 
417             m.Scale(2.0f, 2.0f); 
418             m.Rotate(180.0f);
419             
420             m.TransformPoints(p);
421             Console.WriteLine("(" + p[0].X + " " + p[0].Y + ")");
422             m.Invert();
423             m.TransformPoints(p);
424             Console.WriteLine("(" + p[0].X + " " + p[0].Y + ")");
425         }
426         */
427     }
428 }