PageRenderTime 141ms CodeModel.GetById 20ms app.highlight 17ms RepoModel.GetById 74ms app.codeStats 0ms

/Utilities/Datatypes/Size.cs

#
C# | 581 lines | 322 code | 47 blank | 212 comment | 7 complexity | b9ea9ac8b3c862ea335f1b0af1f824e4 MD5 | raw file
  1using System;
  2using System.ComponentModel;
  3using System.Diagnostics;
  4using System.IO;
  5using System.Runtime.InteropServices;
  6using Delta.Utilities.Helpers;
  7using NUnit.Framework;
  8
  9namespace Delta.Utilities.Datatypes
 10{
 11	/// <summary>
 12	/// Size struct, has just Width and Height as floats and is used to manage
 13	/// sizes, most importantly for our quadratic screen space and the
 14	/// Rectangle struct.
 15	/// </summary>
 16	[Serializable]
 17	[StructLayout(LayoutKind.Explicit)]
 18	[DebuggerDisplay("Size=({Width}, {Height})")]
 19	[Description("Expand to edit this Size")]
 20	[TypeConverter(typeof(ExpandableObjectConverter))]
 21	public struct Size : ISaveLoadBinary, IEquatable<Size>
 22	{
 23		#region Constants
 24		/// <summary>
 25		/// Zero size, both Width and Height are 0.
 26		/// </summary>
 27		/// <returns>Size</returns>
 28		public static readonly Size Zero = new Size(0.0f);
 29
 30		/// <summary>
 31		/// One size, has both Width and Height set to 1.
 32		/// </summary>
 33		public static readonly Size One = new Size(1.0f);
 34
 35		/// <summary>
 36		/// Half size, has both Width and Height set to 0.5.
 37		/// </summary>
 38		public static readonly Size Half = new Size(0.5f);
 39
 40		/// <summary>
 41		/// Quarter size, has both Width and Height set to 0.25.
 42		/// </summary>
 43		public static readonly Size Quarter = new Size(0.25f);
 44		#endregion
 45
 46		#region Max (Static)
 47		/// <summary>
 48		/// Returns the max. dimesions based on both values.
 49		/// e.g. Max((2,5) (4,4)) -> (4,5)
 50		/// </summary>
 51		/// <param name="value1">value1</param>
 52		/// <param name="value2">value2</param>
 53		/// <returns>the max. dimesions based on both values</returns>
 54		public static Size Max(Size value1, Size value2)
 55		{
 56			return new Size(MathHelper.Max(value1.Width, value2.Width),
 57				MathHelper.Max(value1.Height, value2.Height));
 58		}
 59		#endregion
 60
 61		#region FromString (Static)
 62		/// <summary>
 63		/// Convert a string to a Size, works the same way as for Point.FromString
 64		/// </summary>
 65		/// <param name="pointString">The string in the correct format
 66		/// (with or without brackets, comma or space seperated).</param>
 67		/// <returns>
 68		/// Size from the given string or Zero if parsing failed.
 69		/// </returns>
 70		public static Size FromString(string pointString)
 71		{
 72			return (Size)Point.FromString(pointString);
 73		}
 74		#endregion
 75
 76		#region Width (Public)
 77		/// <summary>
 78		/// Width
 79		/// </summary>
 80		[FieldOffset(0)]
 81		public float Width;
 82		#endregion
 83
 84		#region Height (Public)
 85		/// <summary>
 86		/// Height
 87		/// </summary>
 88		[FieldOffset(4)]
 89		public float Height;
 90		#endregion
 91
 92		#region WidthProperty (Public)
 93		/// <summary>
 94		/// Property-wrapper for using the X field in the editor.
 95		/// </summary>
 96		[Browsable(true)]
 97		[DisplayName("Width")]
 98		public float WidthProperty
 99		{
100			get
101			{
102				return Width;
103			}
104			set
105			{
106				Width = value;
107			}
108		}
109		#endregion
110
111		#region HeightProperty (Public)
112		/// <summary>
113		/// Property-wrapper for using the Y field in the editor
114		/// </summary>
115		[Browsable(true)]
116		[DisplayName("Height")]
117		public float HeightProperty
118		{
119			get
120			{
121				return Height;
122			}
123			set
124			{
125				Height = value;
126			}
127		}
128		#endregion
129
130		#region WidthHalf (Public)
131		/// <summary>
132		/// The half width of the size.
133		/// </summary>
134		[Browsable(false)]
135		public float WidthHalf
136		{
137			get
138			{
139				return Width * 0.5f;
140			}
141		}
142		#endregion
143
144		#region HeightHalf (Public)
145		/// <summary>
146		/// The half height of the size.
147		/// </summary>
148		[Browsable(false)]
149		public float HeightHalf
150		{
151			get
152			{
153				return Height * 0.5f;
154			}
155		}
156		#endregion
157
158		#region IsZero (Public)
159		/// <summary>
160		/// Returns "true" if the size hasn't any width AND height.
161		/// </summary>
162		[Browsable(false)]
163		public bool IsZero
164		{
165			get
166			{
167				return Width == 0.0f && Height == 0.0f;
168			}
169		}
170		#endregion
171
172		#region Constructors
173		/// <summary>
174		/// Create size
175		/// </summary>
176		/// <param name="setDimension">setDimension</param>
177		public Size(float setDimension)
178			: this(setDimension, setDimension)
179		{
180		}
181
182		/// <summary>
183		/// Create size
184		/// </summary>
185		/// <param name="setHeight">setHeight</param>
186		/// <param name="setWidth">setWidht</param>
187		public Size(float setWidth, float setHeight)
188			: this()
189		{
190			Width = setWidth;
191			Height = setHeight;
192		}
193
194		/// <summary>
195		/// Create size
196		/// </summary>
197		/// <param name="dataReader">Data reader</param>
198		public Size(BinaryReader dataReader)
199			: this()
200		{
201			Load(dataReader);
202		}
203		#endregion
204
205		#region IEquatable<Size> Members
206		/// <summary>
207		/// Check if another size has almost the same values (using
208		/// MathHelper.Epsilon).
209		/// </summary>
210		/// <param name="other">Other size to compare to</param>
211		/// <returns>True if the other size has nearly the same values</returns>
212		public bool Equals(Size other)
213		{
214			return
215				// Allow a difference of the Epsilon (in both directions)
216				// for the width value range
217				Width - MathHelper.Epsilon <= other.Width &&
218				Width + MathHelper.Epsilon >= other.Width &&
219				// and height value range
220				Height - MathHelper.Epsilon <= other.Height &&
221				Height + MathHelper.Epsilon >= other.Height;
222		}
223		#endregion
224
225		#region ISaveLoadBinary Members
226		/// <summary>
227		/// Load size from binary stream (8 bytes, 2 floats)
228		/// </summary>
229		/// <param name="reader">reader</param>
230		public void Load(BinaryReader reader)
231		{
232			Width = reader.ReadSingle();
233			Height = reader.ReadSingle();
234		}
235
236		/// <summary>
237		/// Save size to binary stream (8 bytes, 2 floats)
238		/// </summary>
239		/// <param name="writer">writer</param>
240		public void Save(BinaryWriter writer)
241		{
242			writer.Write(Width);
243			writer.Write(Height);
244		}
245		#endregion
246
247		#region op_Equality (Operator)
248		/// <summary>
249		/// Operator for equality
250		/// </summary>
251		/// <param name="value1">Size 1</param>
252		/// <param name="value2">Size 2</param>
253		/// <returns>True if both sizes are the same</returns>
254		public static bool operator ==(Size value1, Size value2)
255		{
256			return value1.Width == value2.Width &&
257			       value1.Height == value2.Height;
258		}
259		#endregion
260
261		#region op_Inequality (Operator)
262		/// <summary>
263		/// Operator for inequality
264		/// </summary>
265		/// <param name="value1">Size 1</param>
266		/// <param name="value2">Size 2</param>
267		/// <returns>True if both sizes are not the same</returns>
268		public static bool operator !=(Size value1, Size value2)
269		{
270			return value1.Width != value2.Width ||
271			       value1.Height != value2.Height;
272		}
273		#endregion
274
275		#region op_Addition (Operator)
276		/// <summary>
277		/// Operator for addition
278		/// </summary>
279		/// <param name="value1">Size 1</param>
280		/// <param name="value2">Size 2</param>
281		/// <returns>Added size from both sizes</returns>
282		public static Size operator +(Size value1, Size value2)
283		{
284			return new Size(value1.Width + value2.Width,
285				value1.Height + value2.Height);
286		}
287		#endregion
288
289		#region op_Subtraction (Operator)
290		/// <summary>
291		/// Operator for subtraction
292		/// </summary>
293		/// <param name="value1">Size 1</param>
294		/// <param name="value2">Size 2</param>
295		/// <returns>Subtracted size from both sizes</returns>
296		public static Size operator -(Size value1, Size value2)
297		{
298			return new Size(value1.Width - value2.Width,
299				value1.Height - value2.Height);
300		}
301		#endregion
302
303		#region op_Multiply (Operator)
304		/// <summary>
305		/// Operator for multiplication points
306		/// </summary>
307		/// <param name="value1">Size 1</param>
308		/// <param name="value2">Size 2</param>
309		/// <returns>
310		/// Size with both Width multiplied and both Heights multiplied.
311		/// </returns>
312		public static Size operator *(Size value1, Size value2)
313		{
314			return new Size(value1.Width * value2.Width,
315				value1.Height * value2.Height);
316		}
317
318		/// <summary>
319		/// Operator for multiplying a Size with a scale factor. 
320		/// </summary>
321		/// <param name="value">Size value</param>
322		/// <param name="scaleFactor">Scale Factor</param>
323		/// <returns>
324		/// Size with both Width and Height multiplied by scaleFactor
325		/// </returns>
326		public static Size operator *(Size value, float scaleFactor)
327		{
328			return new Size(value.Width * scaleFactor, value.Height * scaleFactor);
329		}
330
331		/// <summary>
332		/// Operator for multiplying a Size with a scale factor. 
333		/// </summary>
334		/// <param name="scaleFactor">Scale Factor</param>
335		/// <param name="value">Size value</param>
336		/// <returns>
337		/// Size with both Width and Height multiplied by scaleFactor
338		/// </returns>
339		public static Size operator *(float scaleFactor, Size value)
340		{
341			return new Size(value.Width * scaleFactor, value.Height * scaleFactor);
342		}
343		#endregion
344
345		#region op_Division (Operator)
346		/// <summary>
347		/// Operator to divide a Size with a float.
348		/// </summary>
349		/// <param name="value">Value</param>
350		/// <param name="divisor">Divisor</param>
351		/// <returns>value.Width / divisor, value.Height / divisor</returns>
352		public static Size operator /(Size value, float divisor)
353		{
354			return new Size(value.Width / divisor, value.Height / divisor);
355		}
356
357		/// <summary>
358		/// Operator to divide a float with a Size.
359		/// </summary>
360		/// <param name="divisor">Divisor</param>
361		/// <param name="value">Value</param>
362		/// <returns>value / divisor.Width, value / divisor.Height</returns>
363		public static Size operator /(float value, Size divisor)
364		{
365			return new Size(value / divisor.Width, value / divisor.Height);
366		}
367
368		/// <summary>
369		/// Operator to divide a Size with another Size.
370		/// </summary>
371		/// <param name="divisor">Divisor</param>
372		/// <param name="value">Value</param>
373		/// <returns>value.Width / divisor.Width, value.Height / divisor.Height
374		/// </returns>
375		public static Size operator /(Size value, Size divisor)
376		{
377			return new Size(value.Width / divisor.Width,
378				value.Height / divisor.Height);
379		}
380		#endregion
381
382		#region op_Explicit (Operator)
383		/// <summary>
384		/// Operator to implicit convert Point to Size.
385		/// </summary>
386		/// <param name="anyPoint">Any point value</param>
387		/// <returns>Point converted to size</returns>
388		public static explicit operator Size(Point anyPoint)
389		{
390			return new Size(anyPoint.X, anyPoint.Y);
391		}
392		#endregion
393
394		#region Equals (Public)
395		/// <summary>
396		/// Check in another object is a Size and equal to this size.
397		/// </summary>
398		/// <param name="obj">Object to compare to</param>
399		/// <returns>True if both sizes are the same</returns>
400		public override bool Equals(object obj)
401		{
402			return (obj is Size)
403			       	? Equals((Size)obj)
404			       	: base.Equals(obj);
405		}
406		#endregion
407
408		#region GetHashCode (Public)
409		/// <summary>
410		/// Get hash code
411		/// </summary>
412		/// <returns>Hash code from Width and Height</returns>
413		public override int GetHashCode()
414		{
415			return Width.GetHashCode() + Height.GetHashCode();
416		}
417		#endregion
418
419		#region Round (Public)
420		/// <summary>
421		/// Round the width and height values to the nearest integer value.
422		/// </summary>
423		/// <returns>Size with rounded width and height values.</returns>
424		public Size Round()
425		{
426			return new Size(MathHelper.Round(Width), MathHelper.Round(Height));
427		}
428		#endregion
429
430		#region ToString (Public)
431		/// <summary>
432		/// To string
433		/// </summary>
434		/// <returns>string</returns>
435		public override string ToString()
436		{
437			return "(" + Width.ToInvariantString() + ", " +
438			       Height.ToInvariantString() + ")";
439		}
440		#endregion
441
442		#region ToCommaString (Public)
443		/// <summary>
444		/// Returns the vector as a string that can be used in a Setting files,
445		/// which is just using the x, y format (and works fine with FromString).
446		/// </summary>
447		/// <returns>vector</returns>
448		public string ToCommaString()
449		{
450			return Width.ToInvariantString() + ", " +
451			       Height.ToInvariantString();
452		}
453		#endregion
454
455		/// <summary>
456		/// Tests
457		/// </summary>
458		internal class SizeTests
459		{
460			#region SizeOf
461			/// <summary>
462			/// Checks if the size of Point is exactly 8 bytes (2 floats: X and Y)
463			/// </summary>
464			[Test]
465			public void SizeOf()
466			{
467				// Size consists of 2 floats: Width and Height
468				Assert.Equal(2 * 4, Marshal.SizeOf(typeof(Size)));
469			}
470			#endregion
471
472			#region SizeTest
473			/// <summary>
474			/// Size test
475			/// </summary>
476			[Test]
477			public void SizeTest()
478			{
479				Size Size1 = new Size(2, 2);
480				Size Size2 = new Size(1, 1);
481
482				Assert.Equal(Size2.Width, Size1.WidthHalf);
483				Assert.Equal(Size2.Height, Size1.HeightHalf);
484			}
485			#endregion
486
487			#region Equality
488			/// <summary>
489			/// Equality
490			/// </summary>
491			[Test]
492			public void Equality()
493			{
494				Assert.Equal(new Size(10, 10), new Size(10));
495				Assert.NotEqual(new Size(10, 5), new Size(10f, 10f));
496			}
497			#endregion
498
499			#region Addition
500			/// <summary>
501			/// Addition
502			/// </summary>
503			[Test]
504			public void Addition()
505			{
506				Assert.Equal(new Size(9, 8.8f), new Size(5, 3) + new Size(4, 5.8f));
507				Assert.Equal(new Size(-1, -2.8f),
508					new Size(-5, 3) + new Size(4, -5.8f));
509			}
510			#endregion
511
512			#region Substraction
513			/// <summary>
514			/// Substraction
515			/// </summary>
516			[Test]
517			public void Substraction()
518			{
519				Assert.Equal(new Size(1, -2.8f), new Size(5, 3) - new Size(4, 5.8f));
520				Assert.Equal(new Size(-9, 8.8f), new Size(-5, 3) - new Size(4, -5.8f));
521			}
522			#endregion
523
524			#region Multiplication
525			/// <summary>
526			/// Multiplication
527			/// </summary>
528			[Test]
529			public void Multiplication()
530			{
531				// with a scale factor
532				Assert.Equal(new Size(10, 20), new Size(2, 4) * 5);
533				Assert.Equal(new Size(-1, -2), new Size(2, 4) * -0.5f);
534				Assert.Equal(new Size(0.5f, 1), 0.25f * new Size(2, 4));
535			}
536			#endregion
537
538			#region NearlyEquals
539			/// <summary>
540			/// Nearly equals
541			/// </summary>
542			[Test]
543			public void NearlyEquals()
544			{
545				Size testSize = new Size(2, 3);
546
547				// Check the size directly
548				Assert.True(testSize.Equals(testSize),
549					"testSize is not equal testSize");
550				// by the "object" overload from .NET
551				Assert.True(testSize.Equals((object)testSize),
552					"testSize is not equal testSize");
553
554				// and the nearly equal check
555				Assert.True(testSize.Equals(
556					new Size(2 + MathHelper.Epsilon, 3 - MathHelper.Epsilon)),
557					"Size.NearlyEquals() chaecks a too small epsilon");
558
559				// Finally check the "bad" false cases with unequal values
560				Assert.False(testSize.Equals(new Size(4, 3)),
561					new Size(4, 3) + " shouldn't be (nearly) equal to " + testSize);
562				// and a too big epsilon
563				Assert.False(testSize.Equals(
564					new Size(2 + (2 * MathHelper.Epsilon), 3)),
565					"Size.NearlyEquals() checks a too big epsilon");
566			}
567			#endregion
568
569			#region ToString
570			/// <summary>
571			/// To string
572			/// </summary>
573			[Test]
574			public new void ToString()
575			{
576				Assert.Equal("(5, 3.5)", new Size(5, 3.5f).ToString());
577			}
578			#endregion
579		}
580	}
581}