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