PageRenderTime 39ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Rendering/Basics/Drawing/Circle.cs

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