/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
- using System;
- using System.Collections.Generic;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
-
- namespace Delta.Rendering.Basics.Drawing
- {
- /// <summary>
- /// Circle class to draw circles either as line circles or as filled ones.
- /// </summary>
- public static class Circle
- {
- #region Constants
- /// <summary>
- /// Cached sin and cos value calculations based on the input radius,
- /// which is often the same (almost always the same value in fact).
- /// </summary>
- private static readonly Dictionary<float, Point[]> cachedSinCosValues =
- new Dictionary<float, Point[]>();
- #endregion
-
- #region DrawOutline (Static)
- /// <summary>
- /// Draw circle outline with lines. This method is quite fast, but mostly
- /// used for debug code to show circles, bounding circles and for other
- /// simple 2d cases. You can render thousands of circles even on mobile
- /// devices, just make sure they share the radius as much as possible for
- /// best performance. You can also tweak Circle.MaxCirclePoints for
- /// better performance (lower value) or more roundness (higher value).
- /// </summary>
- /// <param name="screenPosition">Screen position</param>
- /// <param name="radius">Radius</param>
- /// <param name="circleColor">Circle color</param>
- public static void DrawOutline(Point screenPosition, float radius,
- Color circleColor)
- {
- // Skip if the circle is certainly outside of the quadratic space.
- // Checking the screen area would work too, but is a little slower and
- // won't exclude much more anyway.
- if (screenPosition.X > 1.0f + radius ||
- screenPosition.Y > 1.0f + radius ||
- screenPosition.X < -radius ||
- screenPosition.Y < -radius)
- {
- // Skip this circle rendering, not visible this frame.
- return;
- }
-
- Point[] circlePoints = GrabCachedCirclePoints(radius);
-
- // Now go around in a circle and draw all line parts (very fast code,
- // all we do is use our cached positions, as long as the code for
- // Draw2DLine is fast this can render out millions of circles per sec)
- Point lastPoint = screenPosition;
- // Note: We do not use the Point operators here to save a tiny bit
- // of extra performance :)
- lastPoint.X += circlePoints[0].X;
- lastPoint.Y += circlePoints[0].Y;
- Point circlePoint = screenPosition;
- DrawManager drawInstance = DrawManager.Instance;
- for (int i = 1; i < circlePoints.Length; i++)
- {
- // Create a point on the unit circle with the given radius and
- // location in screen coordinates. Because sin/cos calculations are
- // slow we are trying to cache them (they are mostly the same).
- circlePoint.X = screenPosition.X + circlePoints[i].X;
- circlePoint.Y = screenPosition.Y + circlePoints[i].Y;
- drawInstance.Draw2DLine(ref lastPoint, ref circlePoint,
- ref circleColor);
- lastPoint = circlePoint;
- }
- }
- #endregion
-
- #region DrawFilled (Static)
- /// <summary>
- /// Draw a filled solid circle with the given color. If you want to use
- /// this for debugging or marking something better use a transparent
- /// color or use DrawOutline instead to just show a circle line around.
- /// Same as DrawOutline this method is quite fast. For bigger circles it
- /// is however mostly fill rate limited as quite a bit of pixels need to
- /// be filled (which is especially slow on mobile devices).
- /// </summary>
- /// <param name="screenPosition">Screen position</param>
- /// <param name="radius">Radius</param>
- /// <param name="circleColor">Circle color</param>
- public static void DrawFilled(Point screenPosition, float radius,
- Color circleColor)
- {
- // Skip if the circle is certainly outside of the quadratic space.
- // Checking the screen area would work too, but is a little slower and
- // won't exclude much more anyway.
- if (screenPosition.X > 1.0f + radius ||
- screenPosition.Y > 1.0f + radius ||
- screenPosition.X < -radius ||
- screenPosition.Y < -radius)
- {
- // Skip this circle rendering, not visible this frame.
- return;
- }
-
- Point[] circlePoints = GrabCachedCirclePoints(radius);
-
- // Now go around in a circle and draw all line parts (very fast code,
- // all we do is use our cached positions, as long as the code for
- // Draw2DLine is fast this can render out millions of circles per sec)
- Point lastPoint = screenPosition;
- // Note: We do not use the Point operators here to save a tiny bit
- // of extra performance :)
- lastPoint.X += circlePoints[0].X;
- lastPoint.Y += circlePoints[0].Y;
- Point circlePoint = screenPosition;
- DrawManager drawInstance = DrawManager.Instance;
- for (int i = 1; i < circlePoints.Length; i++)
- {
- // Create a point on the unit circle with the given radius and
- // location in screen coordinates. Because sin/cos calculations are
- // slow we are trying to cache them (they are mostly the same).
- circlePoint.X = screenPosition.X + circlePoints[i].X;
- circlePoint.Y = screenPosition.Y + circlePoints[i].Y;
- drawInstance.DrawPolygon(ref screenPosition, ref lastPoint,
- ref circlePoint, ref circleColor);
- lastPoint = circlePoint;
- } // for
- }
- #endregion
-
- #region MaxCirclePoints (Static)
- /// <summary>
- /// Number of points used to draw a circle with lines. 64 was chosen for
- /// performance reasons. This way we can draw hundreds of circles for
- /// debugging or even for game code without hurting the overall fps much.
- /// See Delta.Rendering.BasicTests.DrawTests.DrawCirclePerformance.
- /// </summary>
- public static int MaxCirclePoints = 96; //64;//64;//80;//100;
- #endregion
-
- #region Methods (Private)
-
- #region GrabCachedCirclePoints
- /// <summary>
- /// Grab cached circle points and calculates how many circle points are
- /// needed to make it round enough while keeping the performance as good
- /// as possible with as few as possible circle points (only use
- /// MaxCirclePoints for really big circles bigger than the screen).
- /// </summary>
- /// <param name="radius">The radius.</param>
- /// <returns>Array of points needed for drawing the circle.</returns>
- private static Point[] GrabCachedCirclePoints(float radius)
- {
- int circlePointsDynamic =
- (int)(MaxCirclePoints * Math.Max(0.22f + radius / 2, radius));
- float theta = 360f / (circlePointsDynamic - 1);
-
- // Get or build cached sin/cos values for the circle, very optimized :)
- Point[] cachedSinCos;
- if (cachedSinCosValues.TryGetValue(radius, out cachedSinCos) == false)
- {
- // No cache found yet, create a new pre-calculated cache
- cachedSinCos = new Point[circlePointsDynamic];
- for (int i = 0; i < circlePointsDynamic; i++)
- {
- float thetaStep = theta * i;
- cachedSinCos[i] = radius *
- new Point(MathHelper.Sin(thetaStep),
- MathHelper.Cos(thetaStep));
- }
- // And add the result to the cache dictionary
- cachedSinCosValues.Add(radius, cachedSinCos);
- }
-
- return cachedSinCos;
- }
- #endregion
-
- #endregion
- }
- }