PageRenderTime 73ms CodeModel.GetById 40ms app.highlight 8ms RepoModel.GetById 12ms app.codeStats 1ms

/Rendering/Basics/Drawing/Circle.cs

#
C# | 178 lines | 95 code | 13 blank | 70 comment | 7 complexity | 03a0ada075d39f5a2de6dabcbad610fc MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using Delta.Utilities.Datatypes;
  4using Delta.Utilities.Helpers;
  5
  6namespace Delta.Rendering.Basics.Drawing
  7{
  8	/// <summary>
  9	/// Circle class to draw circles either as line circles or as filled ones.
 10	/// </summary>
 11	public static class Circle
 12	{
 13		#region Constants
 14		/// <summary>
 15		/// Cached sin and cos value calculations based on the input radius,
 16		/// which is often the same (almost always the same value in fact).
 17		/// </summary>
 18		private static readonly Dictionary<float, Point[]> cachedSinCosValues =
 19			new Dictionary<float, Point[]>();
 20		#endregion
 21
 22		#region DrawOutline (Static)
 23		/// <summary>
 24		/// Draw circle outline with lines. This method is quite fast, but mostly
 25		/// used for debug code to show circles, bounding circles and for other
 26		/// simple 2d cases. You can render thousands of circles even on mobile
 27		/// devices, just make sure they share the radius as much as possible for
 28		/// best performance. You can also tweak Circle.MaxCirclePoints for
 29		/// better performance (lower value) or more roundness (higher value).
 30		/// </summary>
 31		/// <param name="screenPosition">Screen position</param>
 32		/// <param name="radius">Radius</param>
 33		/// <param name="circleColor">Circle color</param>
 34		public static void DrawOutline(Point screenPosition, float radius,
 35			Color circleColor)
 36		{
 37			// Skip if the circle is certainly outside of the quadratic space.
 38			// Checking the screen area would work too, but is a little slower and
 39			// won't exclude much more anyway.
 40			if (screenPosition.X > 1.0f + radius ||
 41			    screenPosition.Y > 1.0f + radius ||
 42			    screenPosition.X < -radius ||
 43			    screenPosition.Y < -radius)
 44			{
 45				// Skip this circle rendering, not visible this frame.
 46				return;
 47			}
 48
 49			Point[] circlePoints = GrabCachedCirclePoints(radius);
 50
 51			// Now go around in a circle and draw all line parts (very fast code,
 52			// all we do is use our cached positions, as long as the code for
 53			// Draw2DLine is fast this can render out millions of circles per sec)
 54			Point lastPoint = screenPosition;
 55			// Note: We do not use the Point operators here to save a tiny bit
 56			// of extra performance :)
 57			lastPoint.X += circlePoints[0].X;
 58			lastPoint.Y += circlePoints[0].Y;
 59			Point circlePoint = screenPosition;
 60			DrawManager drawInstance = DrawManager.Instance;
 61			for (int i = 1; i < circlePoints.Length; i++)
 62			{
 63				// Create a point on the unit circle with the given radius and
 64				// location in screen coordinates. Because sin/cos calculations are
 65				// slow we are trying to cache them (they are mostly the same).
 66				circlePoint.X = screenPosition.X + circlePoints[i].X;
 67				circlePoint.Y = screenPosition.Y + circlePoints[i].Y;
 68				drawInstance.Draw2DLine(ref lastPoint, ref circlePoint,
 69					ref circleColor);
 70				lastPoint = circlePoint;
 71			}
 72		}
 73		#endregion
 74
 75		#region DrawFilled (Static)
 76		/// <summary>
 77		/// Draw a filled solid circle with the given color. If you want to use
 78		/// this for debugging or marking something better use a transparent
 79		/// color or use DrawOutline instead to just show a circle line around.
 80		/// Same as DrawOutline this method is quite fast. For bigger circles it
 81		/// is however mostly fill rate limited as quite a bit of pixels need to
 82		/// be filled (which is especially slow on mobile devices).
 83		/// </summary>
 84		/// <param name="screenPosition">Screen position</param>
 85		/// <param name="radius">Radius</param>
 86		/// <param name="circleColor">Circle color</param>
 87		public static void DrawFilled(Point screenPosition, float radius,
 88			Color circleColor)
 89		{
 90			// Skip if the circle is certainly outside of the quadratic space.
 91			// Checking the screen area would work too, but is a little slower and
 92			// won't exclude much more anyway.
 93			if (screenPosition.X > 1.0f + radius ||
 94			    screenPosition.Y > 1.0f + radius ||
 95			    screenPosition.X < -radius ||
 96			    screenPosition.Y < -radius)
 97			{
 98				// Skip this circle rendering, not visible this frame.
 99				return;
100			}
101
102			Point[] circlePoints = GrabCachedCirclePoints(radius);
103
104			// Now go around in a circle and draw all line parts (very fast code,
105			// all we do is use our cached positions, as long as the code for
106			// Draw2DLine is fast this can render out millions of circles per sec)
107			Point lastPoint = screenPosition;
108			// Note: We do not use the Point operators here to save a tiny bit
109			// of extra performance :)
110			lastPoint.X += circlePoints[0].X;
111			lastPoint.Y += circlePoints[0].Y;
112			Point circlePoint = screenPosition;
113			DrawManager drawInstance = DrawManager.Instance;
114			for (int i = 1; i < circlePoints.Length; i++)
115			{
116				// Create a point on the unit circle with the given radius and
117				// location in screen coordinates. Because sin/cos calculations are
118				// slow we are trying to cache them (they are mostly the same).
119				circlePoint.X = screenPosition.X + circlePoints[i].X;
120				circlePoint.Y = screenPosition.Y + circlePoints[i].Y;
121				drawInstance.DrawPolygon(ref screenPosition, ref lastPoint,
122					ref circlePoint, ref circleColor);
123				lastPoint = circlePoint;
124			} // for
125		}
126		#endregion
127
128		#region MaxCirclePoints (Static)
129		/// <summary>
130		/// Number of points used to draw a circle with lines. 64 was chosen for
131		/// performance reasons. This way we can draw hundreds of circles for
132		/// debugging or even for game code without hurting the overall fps much.
133		/// See Delta.Rendering.BasicTests.DrawTests.DrawCirclePerformance.
134		/// </summary>
135		public static int MaxCirclePoints = 96; //64;//64;//80;//100;
136		#endregion
137
138		#region Methods (Private)
139
140		#region GrabCachedCirclePoints
141		/// <summary>
142		/// Grab cached circle points and calculates how many circle points are
143		/// needed to make it round enough while keeping the performance as good
144		/// as possible with as few as possible circle points (only use
145		/// MaxCirclePoints for really big circles bigger than the screen).
146		/// </summary>
147		/// <param name="radius">The radius.</param>
148		/// <returns>Array of points needed for drawing the circle.</returns>
149		private static Point[] GrabCachedCirclePoints(float radius)
150		{
151			int circlePointsDynamic =
152				(int)(MaxCirclePoints * Math.Max(0.22f + radius / 2, radius));
153			float theta = 360f / (circlePointsDynamic - 1);
154
155			// Get or build cached sin/cos values for the circle, very optimized :)
156			Point[] cachedSinCos;
157			if (cachedSinCosValues.TryGetValue(radius, out cachedSinCos) == false)
158			{
159				// No cache found yet, create a new pre-calculated cache
160				cachedSinCos = new Point[circlePointsDynamic];
161				for (int i = 0; i < circlePointsDynamic; i++)
162				{
163					float thetaStep = theta * i;
164					cachedSinCos[i] = radius *
165					                  new Point(MathHelper.Sin(thetaStep),
166					                  	MathHelper.Cos(thetaStep));
167				}
168				// And add the result to the cache dictionary
169				cachedSinCosValues.Add(radius, cachedSinCos);
170			}
171
172			return cachedSinCos;
173		}
174		#endregion
175
176		#endregion
177	}
178}