Advertisement

Draw 2D empty and filled circle

Started by November 28, 2015 07:23 PM
12 comments, last by BlackJoker 8 years, 11 months ago

I want to draw 2d circle using line list, which will look something like that, but without empty spaces to look like the solid border:

clip_image006.png

And also I want to draw filled circle usign triangle list or strip.

Does anyone could help me with algorithms for that?

Well cosine and sine are you best friends when it comes to rendering circles.

cosine of an angle equals the x- and sine of an angle equals the y-coordinate.

ehk5.png

Of course you can't generate a "perfect circle". It will always have edges. If you have more vertices the circle will look less angular and vice versa.

I hope this enough of a hint.

Sincerely,
Julien

It's not a shame to make mistakes. As a programmer I am doing mistakes on a daily basis. - JonathanKlein

Advertisement

Well, I know about cos and sin, but I also wish to know how to shift points smoothly that there was no gaps between them? Because even if I rotate points only on 1 degree, It will still have gaps and circle will not looks solid if I want to draw it with horizontal lines like in the screenshot above.

I wouldn't say this is a good solution but.. Create a grid of the desired resolution (for example, if the circle is supposed to be of size d pixels in diameter then you have a grid of size d*d). It could be a simple array of booleans of length d*d. Each position represents a location such that index 0 is (-r, r) and index d*d-1 is (r, -r) thus the centre is 0,0. You then loop though each element and calculate it's distance from the origin (sqrt(x*x + y*y). If it is <= than the radius of your circle then you set the value to true (that will give you a filled circle).

Once you have that you go across it row by row and make lines that start at a value that is true and end at the last value that is true (remember most rows will have 2 lines). You can then delete the array. In order to do an outline you just need to have an inner value too such that the distance has to be >= inner value and <= the outer value.

I can't say it's the most efficient method but it should get the job done. Instead of using the distance you could use the squared distance which will help and since it's a circle you know that it will be mirrored in many ways so you could do some optimizations and only actually work out values for a quarter of the circle and mirror your lines. Then you'll only need to do r*r amount of values instead of d*d.

Edit: It occurs to me that if you do it per pixels then you will end up will a solid circle rather than what is in your image. In that case just don't make lines (or work out values ) for alternating lines to leave a gap. Now it's only r*r/2 values.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

That alternating line texturing is, well: Texturing. So why not use a texture ? A 1x2 would suffice (wrapped). Make sure texcoords are aligned and scaled to screen pixels (if that's what you're after). Or do it procedurally in the pixel shader grabbing the screen coord, as suggested.

Now draw a triangulated ring with e.g. a tri-strip. Alternatively mask with another ring shaped texture (can be done procedurally, too).

Edit: Ignore the above, I was fixated on the alternatiing line pattern. Here's some code for triangulating a ring.

I found an algorithm to draw filled and empty circles:

Here is one for filled circle:


void DrawFilledCircle(Vector3 startPoint, Vector2 radius, Color color)
      {
         List<VertexPositionTextureColor> circle = new List<VertexPositionTextureColor>();
         //Center of the circle
         float xPos = startPoint.X;
         float yPos = startPoint.Y;
         
         float x1 = xPos;
         float y1 = yPos;

         for (int i = 0; i <= 363; i += 3)
         {
            float angle = (i / 57.3f);
            float x2 = xPos + (radius.X * (float)Math.Sin(angle));
            float y2 = yPos + (radius.Y * (float)Math.Cos(angle));
            circle.Add(new VertexPositionTextureColor(new Vector3(xPos, yPos, 0), Vector2.Zero,  color));
            circle.Add(new VertexPositionTextureColor(new Vector3(x1, y1, 0), Vector2.Zero, color));
            circle.Add(new VertexPositionTextureColor(new Vector3(x2, y2, 0), Vector2.Zero, color));
            
            y1 = y2;
            x1 = x2;
         }

         for (int i = 0; i < circle.Count; i++)
         {
            var vertex = circle[i];
            vertex.TextureCoordinate = new Vector2(0.5f + (vertex.Position.X - startPoint.X) / (2 * radius.X),
                             0.5f + (vertex.Position.Y - startPoint.Y) / (2 * radius.Y));
            circle[i] = vertex;
         }

         circle2Dfilled = Buffer.Vertex.New(GraphicsDevice, circle.ToArray());
      }

And here is an algorithm for empty circle:


void DrawEmptyCircle(Vector3 startPoint, Vector2 radius, Color color)
      {
         List<VertexPositionColor> circle = new List<VertexPositionColor>();
         float X, Y;

         var stepDegree = 0.3f;
         for (float angle = 0; angle <= 360; angle += stepDegree)
         {
            X = startPoint.X + radius.X * (float)Math.Cos((angle));
            Y = startPoint.Y + radius.Y * (float)Math.Sin((angle));
            Vector3 point = new Vector3(X, Y, 0);
            circle.Add(new VertexPositionColor(point, color));
         }
         circle2D = Buffer.Vertex.New(GraphicsDevice, circle.ToArray());
      }

The only thing here is that I dont know how to control thickness of the circle border. It depends from stepDegree value. If it bigger, the border also bigger, but dependency is not so obvious. For ex, value 0.1 will give me at least 2-3 pixels width for the circle border. So, I have no idea how to correctly control border thickness with this.

If anyone have idea, you are welcome.

Forgot to mention that filled circle I draw with TriangleStrip and empty circle - with LineStrip.

Advertisement
You can't draw a thick line with a line list, not sensibly anyway. Your line gets thick probably because the degree-radians mismatch produces a self-intersecting polyline with several windings. This is too chaotic to control, it will always have gaps. Use triangles, like that geo-shader sample I linked to.

Then again... draw a textured quad with this pixel shader tongue.png


float4 RingShortPS(float2 tex: TEXCOORD) : SV_Target
{
    float inner = 0.8;             // relative inner radius. Could be from a constant buffer.
    float r = length(2*tex-1);     // sign-normalize tex-coords (could be done in VS)
    return min(r < 1, r > inner);  // difference of the two circle disks
}

Looks cool, but I want to create the same in my code.

I tried to do it with triangles, but fail. Maybe you can prompt me an algorithm for that?

Yeah, please ignore, this was just for fun.

Look at the code I linked to, it's HLSL but consider it pseudo-code. That's the algorithm. It's a triangle-strip, alternating inner and outer side of the ring. tristream is your list of vertices. It's basically two circles at once, with different radii.

I tried to make something similar, but end up with this:


float thickness = 10;
         for (int i = 0; i <= 363; i++)
         {
            float x = startPoint.X + (radius.X * (float)Math.Cos(MathUtil.DegreesToRadians(i)));
            float y = startPoint.Y + (radius.Y * (float)Math.Sin(MathUtil.DegreesToRadians(i)));
            circle.Add(new VertexPositionColor(new Vector3(x, y, 0), color));
            x += thickness;
            circle.Add(new VertexPositionColor(new Vector3(x, y, 0), color));
            x = startPoint.X + (radius.X * (float)Math.Cos(MathUtil.DegreesToRadians(i)));
            y = startPoint.Y + (radius.Y * (float)Math.Sin(MathUtil.DegreesToRadians(i)));
            circle.Add(new VertexPositionColor(new Vector3(x, y+ thickness, 0), color));

         }

This topic is closed to new replies.

Advertisement