Multi-culture implementation ValueSerializers, Parse and ToString for types from...
[mono.git] / mcs / class / WindowsBase / System.Windows.Media / Matrix.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Chris Toshok (toshok@ximian.com)
24 //
25
26 using System;
27 using System.ComponentModel;
28 using System.Globalization;
29 using System.Windows.Markup;
30 using System.Windows.Media.Converters;
31 using System.Windows.Threading;
32
33 namespace System.Windows.Media {
34
35         [Serializable] 
36         [TypeConverter (typeof(MatrixConverter))] 
37         [ValueSerializer (typeof (MatrixValueSerializer))]
38         public struct Matrix : IFormattable {
39
40                 double _m11;
41                 double _m12;
42                 double _m21;
43                 double _m22;
44                 double _offsetX;
45                 double _offsetY;
46
47                 public Matrix (double m11,
48                                double m12,
49                                double m21,
50                                double m22,
51                                double offsetX,
52                                double offsetY)
53                 {
54                         this._m11 = m11;
55                         this._m12 = m12;
56                         this._m21 = m21;
57                         this._m22 = m22;
58                         this._offsetX = offsetX;
59                         this._offsetY = offsetY;
60                 }
61
62                 public void Append (Matrix matrix)
63                 {
64                         double _m11;
65                         double _m21;
66                         double _m12;
67                         double _m22;
68                         double _offsetX;
69                         double _offsetY;
70
71                         _m11 = this._m11 * matrix.M11 + this._m12 * matrix.M21;
72                         _m12 = this._m11 * matrix.M12 + this._m12 * matrix.M22;
73                         _m21 = this._m21 * matrix.M11 + this._m22 * matrix.M21;
74                         _m22 = this._m21 * matrix.M12 + this._m22 * matrix.M22;
75
76                         _offsetX = this._offsetX * matrix.M11 + this._offsetY * matrix.M21 + matrix.OffsetX;
77                         _offsetY = this._offsetX * matrix.M12 + this._offsetY * matrix.M22 + matrix.OffsetY;
78
79                         this._m11 = _m11;
80                         this._m12 = _m12;
81                         this._m21 = _m21;
82                         this._m22 = _m22;
83                         this._offsetX = _offsetX;
84                         this._offsetY = _offsetY;
85                 }
86
87                 public bool Equals (Matrix value)
88                 {
89                         return (_m11 == value.M11 &&
90                                 _m12 == value.M12 &&
91                                 _m21 == value.M21 &&
92                                 _m22 == value.M22 &&
93                                 _offsetX == value.OffsetX &&
94                                 _offsetY == value.OffsetY);
95                 }
96
97                 public override bool Equals (object o)
98                 {
99                         if (!(o is Matrix))
100                                 return false;
101
102                         return Equals ((Matrix)o);
103                 }
104
105                 public static bool Equals (Matrix matrix1,
106                                            Matrix matrix2)
107                 {
108                         return matrix1.Equals (matrix2);
109                 }
110
111                 public override int GetHashCode ()
112                 {
113                         unchecked
114                         {
115                                 var hashCode = _m11.GetHashCode ();
116                                 hashCode = (hashCode * 397) ^ _m12.GetHashCode ();
117                                 hashCode = (hashCode * 397) ^ _m21.GetHashCode ();
118                                 hashCode = (hashCode * 397) ^ _m22.GetHashCode ();
119                                 hashCode = (hashCode * 397) ^ _offsetX.GetHashCode ();
120                                 hashCode = (hashCode * 397) ^ _offsetY.GetHashCode ();
121                                 return hashCode;
122                         }
123                 }
124
125                 public void Invert ()
126                 {
127                         if (!HasInverse)
128                                 throw new InvalidOperationException ("Transform is not invertible.");
129
130                         double d = Determinant;
131
132                         /* 1/(ad-bc)[d -b; -c a] */
133
134                         double _m11 = this._m22;
135                         double _m12 = -this._m12;
136                         double _m21 = -this._m21;
137                         double _m22 = this._m11;
138
139                         double _offsetX = this._m21 * this._offsetY - this._m22 * this._offsetX;
140                         double _offsetY = this._m12 * this._offsetX - this._m11 * this._offsetY;
141
142                         this._m11 = _m11 / d;
143                         this._m12 = _m12 / d;
144                         this._m21 = _m21 / d;
145                         this._m22 = _m22 / d;
146                         this._offsetX = _offsetX / d;
147                         this._offsetY = _offsetY / d;
148                 }
149
150                 public static Matrix Multiply (Matrix trans1,
151                                                Matrix trans2)
152                 {
153                         Matrix m = trans1;
154                         m.Append (trans2);
155                         return m;
156                 }
157
158                 public static bool operator == (Matrix matrix1,
159                                                 Matrix matrix2)
160                 {
161                         return matrix1.Equals (matrix2);
162                 }
163
164                 public static bool operator != (Matrix matrix1,
165                                                 Matrix matrix2)
166                 {
167                         return !matrix1.Equals (matrix2);
168                 }
169
170                 public static Matrix operator * (Matrix trans1,
171                                                  Matrix trans2)
172                 {
173                         Matrix result = trans1;
174                         result.Append (trans2);
175                         return result;
176                 }
177
178                 public static Matrix Parse (string source)
179                 {
180                         if (source == null)
181                                 throw new ArgumentNullException ("source");
182                         Matrix value;
183                         if (source.Trim () == "Identity")
184                         {
185                                 value = Identity;
186                         }
187                         else
188                         {
189                                 var tokenizer = new NumericListTokenizer (source, CultureInfo.InvariantCulture);
190                                 double m11;
191                                 double m12;
192                                 double m21;
193                                 double m22;
194                                 double offsetX;
195                                 double offsetY;
196                                 if (double.TryParse (tokenizer.GetNextToken (), NumberStyles.Float, CultureInfo.InvariantCulture, out m11)
197                                     && double.TryParse (tokenizer.GetNextToken (), NumberStyles.Float, CultureInfo.InvariantCulture, out m12)
198                                     && double.TryParse (tokenizer.GetNextToken (), NumberStyles.Float, CultureInfo.InvariantCulture, out m21)
199                                     && double.TryParse (tokenizer.GetNextToken (), NumberStyles.Float, CultureInfo.InvariantCulture, out m22)
200                                     && double.TryParse (tokenizer.GetNextToken (), NumberStyles.Float, CultureInfo.InvariantCulture, out offsetX)
201                                     && double.TryParse (tokenizer.GetNextToken (), NumberStyles.Float, CultureInfo.InvariantCulture, out offsetY))
202                                 {
203                                         if (!tokenizer.HasNoMoreTokens ())
204                                         {
205                                                 throw new InvalidOperationException ("Invalid Matrix format: " + source);
206                                         }
207                                         value = new Matrix (m11, m12, m21, m22, offsetX, offsetY);
208                                 }
209                                 else
210                                 {
211                                         throw new FormatException (string.Format ("Invalid Matrix format: {0}", source));
212                                 }
213                         }
214                         return value;
215                 }
216
217                 public void Prepend (Matrix matrix)
218                 {
219                         double _m11;
220                         double _m21;
221                         double _m12;
222                         double _m22;
223                         double _offsetX;
224                         double _offsetY;
225
226                         _m11 = matrix.M11 * this._m11 + matrix.M12 * this._m21;
227                         _m12 = matrix.M11 * this._m12 + matrix.M12 * this._m22;
228                         _m21 = matrix.M21 * this._m11 + matrix.M22 * this._m21;
229                         _m22 = matrix.M21 * this._m12 + matrix.M22 * this._m22;
230
231                         _offsetX = matrix.OffsetX * this._m11 + matrix.OffsetY * this._m21 + this._offsetX;
232                         _offsetY = matrix.OffsetX * this._m12 + matrix.OffsetY * this._m22 + this._offsetY;
233
234                         this._m11 = _m11;
235                         this._m12 = _m12;
236                         this._m21 = _m21;
237                         this._m22 = _m22;
238                         this._offsetX = _offsetX;
239                         this._offsetY = _offsetY;
240                 }
241
242                 public void Rotate (double angle)
243                 {
244                         // R_theta==[costheta -sintheta; sintheta costheta],    
245                         double theta = angle * Math.PI / 180;
246
247                         Matrix r_theta = new Matrix (Math.Cos (theta), Math.Sin(theta),
248                                                      -Math.Sin (theta), Math.Cos(theta),
249                                                      0, 0);
250
251                         Append (r_theta);
252                 }
253
254                 public void RotateAt (double angle,
255                                       double centerX,
256                                       double centerY)
257                 {
258                         Translate (-centerX, -centerY);
259                         Rotate (angle);
260                         Translate (centerX, centerY);
261                 }
262
263                 public void RotateAtPrepend (double angle,
264                                              double centerX,
265                                              double centerY)
266                 {
267                         Matrix m = Matrix.Identity;
268                         m.RotateAt (angle, centerX, centerY);
269                         Prepend (m);
270                 }
271
272                 public void RotatePrepend (double angle)
273                 {
274                         Matrix m = Matrix.Identity;
275                         m.Rotate (angle);
276                         Prepend (m);
277                 }
278
279                 public void Scale (double scaleX,
280                                    double scaleY)
281                 {
282                         Matrix scale = new Matrix (scaleX, 0,
283                                                    0, scaleY,
284                                                    0, 0);
285
286                         Append (scale);
287                 }
288
289                 public void ScaleAt (double scaleX,
290                                      double scaleY,
291                                      double centerX,
292                                      double centerY)
293                 {
294                         Translate (-centerX, -centerY);
295                         Scale (scaleX, scaleY);
296                         Translate (centerX, centerY);
297                 }
298
299                 public void ScaleAtPrepend (double scaleX,
300                                             double scaleY,
301                                             double centerX,
302                                             double centerY)
303                 {
304                         Matrix m = Matrix.Identity;
305                         m.ScaleAt (scaleX, scaleY, centerX, centerY);
306                         Prepend (m);
307                 }
308
309                 public void ScalePrepend (double scaleX,
310                                           double scaleY)
311                 {
312                         Matrix m = Matrix.Identity;
313                         m.Scale (scaleX, scaleY);
314                         Prepend (m);
315                 }
316
317                 public void SetIdentity ()
318                 {
319                         _m11 = _m22 = 1.0;
320                         _m12 = _m21 = 0.0;
321                         _offsetX = _offsetY = 0.0;
322                 }
323
324                 public void Skew (double skewX,
325                                   double skewY)
326                 {
327                         Matrix skew_m = new Matrix (1, Math.Tan (skewY * Math.PI / 180),
328                                                     Math.Tan (skewX * Math.PI / 180), 1,
329                                                     0, 0);
330                         Append (skew_m);
331                 }
332
333                 public void SkewPrepend (double skewX,
334                                          double skewY)
335                 {
336                         Matrix m = Matrix.Identity;
337                         m.Skew (skewX, skewY);
338                         Prepend (m);
339                 }
340
341                 public override string ToString ()
342                 {
343                         return ToString (null);
344                 }
345
346                 public string ToString (IFormatProvider provider)
347                 {
348                         return ToString (null, provider);
349                 }
350
351                 string IFormattable.ToString (string format,
352                         IFormatProvider provider)
353                 {
354                         return ToString (provider);
355                 }
356
357                 private string ToString (string format, IFormatProvider provider)
358                 {
359                         if (IsIdentity)
360                                 return "Identity";
361
362                         if (provider == null)
363                                 provider = CultureInfo.CurrentCulture;
364
365                         if (format == null)
366                                 format = string.Empty;
367
368                         var separator = NumericListTokenizer.GetSeparator (provider);
369
370                         var matrixFormat = string.Format (
371                                 "{{0:{0}}}{1}{{1:{0}}}{1}{{2:{0}}}{1}{{3:{0}}}{1}{{4:{0}}}{1}{{5:{0}}}",
372                                 format, separator);
373                         return string.Format (provider, matrixFormat,
374                                 _m11, _m12, _m21, _m22, _offsetX, _offsetY);
375                 }
376
377                 public Point Transform (Point point)
378                 {
379                         return Point.Multiply (point, this);
380                 }
381
382                 public void Transform (Point[] points)
383                 {
384                         for (int i = 0; i < points.Length; i ++)
385                                 points[i] = Transform (points[i]);
386                 }
387
388                 public Vector Transform (Vector vector)
389                 {
390                         return Vector.Multiply (vector, this);
391                 }
392
393                 public void Transform (Vector[] vectors)
394                 {
395                         for (int i = 0; i < vectors.Length; i ++)
396                                 vectors[i] = Transform (vectors[i]);
397                 }
398
399                 public void Translate (double offsetX,
400                                        double offsetY)
401                 {
402                         this._offsetX += offsetX;
403                         this._offsetY += offsetY;
404                 }
405
406                 public void TranslatePrepend (double offsetX,
407                                               double offsetY)
408                 {
409                         Matrix m = Matrix.Identity;
410                         m.Translate (offsetX, offsetY);
411                         Prepend (m);
412                 }
413
414                 public double Determinant {
415                         get { return _m11 * _m22 - _m12 * _m21; }
416                 }
417
418                 public bool HasInverse {
419                         get { return Determinant != 0; }
420                 }
421
422                 public static Matrix Identity {
423                         get { return new Matrix (1.0, 0.0, 0.0, 1.0, 0.0, 0.0); }
424                 }
425
426                 public bool IsIdentity {
427                         get { return Equals (Matrix.Identity); }
428                 }
429
430                 public double M11 { 
431                         get { return _m11; }
432                         set { _m11 = value; }
433                 }
434                 public double M12 { 
435                         get { return _m12; }
436                         set { _m12 = value; }
437                 }
438                 public double M21 { 
439                         get { return _m21; }
440                         set { _m21 = value; }
441                 }
442                 public double M22 { 
443                         get { return _m22; }
444                         set { _m22 = value; }
445                 }
446                 public double OffsetX { 
447                         get { return _offsetX; }
448                         set { _offsetX = value; }
449                 }
450                 public double OffsetY { 
451                         get { return _offsetY; }
452                         set { _offsetY = value; }
453                 }
454         }
455
456 }