/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}