0

I need to loop through a array in circle in arc shape with a small radius (like draw a circle pixel by pixel), but all algorithm i tried, checks duplicate indexes of array (it's got the same x and y several times). I have a radius of 3, with a circle form of 28 elements (not filled), but the algorithm iterate 360 times. I can check if x or y change before i do something, but it's lame.

My code now:

for (int radius = 1; radius < 6; radius++)
{
    for (double i = 0; i < 360; i += 1)
    {
        double angle = i * System.Math.PI / 180;
        int x = (int)(radius * System.Math.Cos(angle)) + centerX;
        int y = (int)(radius * System.Math.Sin(angle)) + centerY;

        // do something
        // if (array[x, y]) ....
    }
}      

PS: I can't use midpoint circle, because i need to increment radius starting from 2 until 6, and not every index is obtained, because his circle it's not real (according trigonometry)

EDIT: What i really need, is scan a full circle edge by edge, starting by center.

360 steps (it's get all coordinates):

Full scan

for (int radius = 2; radius <= 7; radius++)
{
    for (double i = 0; i <= 360; i += 1)
    {
        double angle = i * System.Math.PI / 180;
        int x = (int)(radius * System.Math.Cos(angle));
        int y = (int)(radius * System.Math.Sin(angle));
        print(x, y, "X");
    }
}

Using Midpoint Circle or other algorithm skipping steps (missing coordinates):

Midpoint Circle Algorithm

for (int radius = 2; radius <= 7; radius++)
{
    int x = radius;
    int y = 0;
    int err = 0;
    while (x >= y)
    {
        print(x, y, "X");
        print(y, x, "X");
        print(-y, x, "X");
        print(-y, x, "X");
        print(-x, y, "X");
        print(-x, -y, "X");
        print(-y, -x, "X");
        print(y, -x, "X");
        print(x, -y, "X");

        y += 1;
        err += 1 + 2 * y;
        if (2 * (err - x) + 1 > 0)
        {
            x -= 1;
            err += 1 - 2 * x;
        }
    }
}
Possoli
  • 13
  • 2
  • Why are you doing all that trigonometry? If you just use Bresenham's algorithm, that will be faster and will solve your problem (as long as you are careful about the start and end). [Wikipedia](https://en.wikipedia.org/wiki/Midpoint_circle_algorithm) is your friend. – Toby Speight Nov 24 '16 at 16:19
  • Because i need scan a full circle edge by edge. Bresenham's algorithm not get all coordinates, leaving some indexes out. – Possoli Nov 24 '16 at 18:50
  • Please [edit] your question to explain what you mean by "all coordinates". Bresenham's algorithm does stop at least once for every *x* and for every *y*. It's not clear which values you think you are missing. – Toby Speight Nov 25 '16 at 09:40
  • Thanks for your advice, i edited my question with more information. – Possoli Nov 25 '16 at 10:54
  • I think I'm starting to understand what you mean - are you wanting *every pixel* to be part of *exactly one* circle with integer radius? (And is there a reason the radii must be integers?) – Toby Speight Nov 25 '16 at 11:47
  • Exactly, but i need to check radius by radius, not the full circle at once, for that reason the only way i got it, was scan a full circle with 360 steps (at the end, is 360 * 6 steps, because the radius _for_, start from 2 to 7). – Possoli Nov 25 '16 at 12:41

2 Answers2

0

There are two algorithmic ideas in play here: one is rasterizing a circle. The OP code presents a couple opportunities for improvement on that front: (a) one needn't sample the entire 360 degree circle, realizing that a circle is symmetric across both axes. (x,y) can be reflected in the other three quadrants as (-x,y), (-x,-y), and (x,-y). (b) the step on the loop should be related to the curvature. A simple heuristic is to use the radius as the step. So...

let step = MIN(radius, 90)
for (double i=0; i<90; i += step) {
   add (x,y) to results
   reflect into quadrants 2,3,4 and add to results
}

With these couple improvements, you may no longer care about duplicate samples being generated. If you still do, then the second idea, independent of the circle, is how to hash a pair of ints. There's a good article about that here: Mapping two integers to one, in a unique and deterministic way.

In a nutshell, we compute an int from our x,y pair that's guaranteed to map uniquely, and then check that for duplicates...

cantor(x, y) = 1/2(x + y)(x + y + 1) + y

This works only for positive values of x,y, which is just what you need since we're only computing (and then reflecting) in the first quadrant. For each pair, check that they are unique

let s = an empty set
int step = MIN(radius, 90)
for (double i=0; i<90; i += step) {
    generate (x,y)
    let c = cantor(x,y)
    if (not(s contains c)) {
        add (x,y) to results
        reflect into quadrants 2,3,4 and add to results
        add c to s
    }
}
Community
  • 1
  • 1
danh
  • 55,236
  • 10
  • 89
  • 124
  • Thanks for your time. I've already tried it with a partial circle and skip steps too (but your improvement is more efficient), but for something reason, some coordinates do not show in some radius. My results: ![Skip steps](http://i.imgur.com/OqKT3U9.png). Full steps ![Full steps](http://i.imgur.com/tLh99dR.png) I go to continue improvement the code. Thanks again. – Possoli Nov 24 '16 at 18:26
0

Got it!

It's not beautiful, but work for me.

int maxRadius = 7;

for (int radius = 1; radius <= maxRadius; radius++)
{
    x = position.X - radius;
    y = position.Y - radius;
    x2 = position.X + radius;
    y2 = position.Y + radius;
    for (int i = 0; i <= radius * 2; i++)
    {
        if (InCircle(position.X, position.Y, x + i, y, maxRadius)) // Top X
            myArray[position, x + i, y];    // check array

        if (InCircle(position.X, position.Y, x + i, y2, maxRadius)) // Bottom X
            myArray[position, x + i, y2];    // check array

        if (i > 0 && i < radius * 2)
        {
            if (InCircle(position.X, position.Y, x, y + i, maxRadius)) // Left Y
                myArray[position, x, y + i];    // check array  

            if (InCircle(position.X, position.Y, x2, y + i, maxRadius)) // Right Y
                myArray[position, x2, y + i];    // check array   
        }
    }
}


public static bool InCircle(int originX, int originY, int x, int y, int radius)
{
    int dx = Math.Abs(x - originX);
    if (dx > radius) return false;
    int dy = Math.Abs(y - originY);
    if (dy > radius) return false;
    if (dx + dy <= radius) return true;
    return (dx * dx + dy * dy <= radius * radius);
}
Possoli
  • 13
  • 2