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