2

I wrote a little program which draws a circular chromatic gradation using Andres' algorithm. Here is the result of an execution :

enter image description here

If the final-user gives the program a shift, in radians, it will use the latter to begin the drawing of the circle at this angle. Thus, the gradation will begin at this angle (and that's what I want).

For example, you can see that the gradation begins at the bottom of the circle and not at its left in the following image.

enter image description here

But as you can see, two problems appear :

  1. First, there are some gaps in the circle ;
  2. Then, the circumference of the circle is quite imprecise.

I think it's a problem of precision. So I asked myself if there were some bad casts but it's not the case. Well, I don't know what is the reason why my circle is so imprecise. Could you help me please ?

Here is the class that draws each pixel of the circle. It takes on board the specified shift, in radians.

/**
 * Constructs a Pixel taking account of a shift and near the position (x0 ; y0)
 * @param x
 * @param y
 * @param color
 * @param angle
 * @param x0
 * @param y0
 */
Pixel(double x, double y, Color color, double angle, double x0, double y0) {
    this.x = (int) (x0 + (x-x0) * Math.cos(angle) - (y-y0) * Math.sin(angle));
    this.y = (int) (y0 + (y-y0) * Math.cos(angle) + (x-x0) * Math.sin(angle));
    this.color = color;
}

Now, the code that draws the circle, using Andres' algorithm and the Pixel class.

case "Andres' algorithm":
    w = 2 * Math.PI;

    for(double current_thickness = 0; current_thickness < this.thickness; current_thickness++) {
        x = 0;
        y = (int) (radius + current_thickness);
        double d = radius + current_thickness - 1;

        while (y >= x) {
            double octant_1_x = x0 + x, octant_1_y = y0 + y;
            double octant_2_x = x0 + y, octant_2_y = y0 + x;
            double octant_3_x = x0 - x, octant_3_y = y0 + y;
            double octant_4_x = x0 - y, octant_4_y = y0 + x;
            double octant_5_x = x0 + x, octant_5_y = y0 - y;
            double octant_6_x = x0 + y, octant_6_y = y0 - x;
            double octant_7_x = x0 - x, octant_7_y =  y0 - y;
            double octant_8_x = x0 - y, octant_8_y = y0 - x;

            max_counter++;

            double[] rgb_gradation_octant_1 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_1_y - y0, octant_1_x - x0) + Math.PI, w);
            updates.add(new Pixel(octant_1_x, octant_1_y,
                    Color.color(rgb_gradation_octant_1[0], rgb_gradation_octant_1[1], rgb_gradation_octant_1[2]),
                    circle_gradation_beginning, x0, y0)); // octant n°1

            double[] rgb_gradation_octant_2 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_2_y - y0, octant_2_x - x0) + Math.PI, w);
            updates.add(new Pixel(octant_2_x, octant_2_y,
                    Color.color(rgb_gradation_octant_2[0], rgb_gradation_octant_2[1], rgb_gradation_octant_2[2]),
                    circle_gradation_beginning, x0, y0));

            double[] rgb_gradation_octant_3 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_3_y - y0, octant_3_x - x0) + Math.PI, w);
            updates.add(new Pixel(octant_3_x, octant_3_y,
                    Color.color(rgb_gradation_octant_3[0], rgb_gradation_octant_3[1], rgb_gradation_octant_3[2]),
                    circle_gradation_beginning, x0, y0));

            double[] rgb_gradation_octant_4 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_4_y - y0, octant_4_x - x0) + Math.PI, w);
            updates.add(new Pixel(octant_4_x, octant_4_y,
                    Color.color(rgb_gradation_octant_4[0], rgb_gradation_octant_4[1], rgb_gradation_octant_4[2]),
                    circle_gradation_beginning, x0, y0)); // octant n°4

            double[] rgb_gradation_octant_5 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_5_y-y0, octant_5_x-x0) + Math.PI, w);
            updates.add(new Pixel(octant_5_x, octant_5_y,
                    Color.color(rgb_gradation_octant_5[0], rgb_gradation_octant_5[1], rgb_gradation_octant_5[2]),
                    circle_gradation_beginning, x0, y0)); // octant n°5

            double[] rgb_gradation_octant_6 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_6_y-y0, octant_6_x-x0) + Math.PI, w);
            updates.add(new Pixel(octant_6_x, octant_6_y,
                    Color.color(rgb_gradation_octant_6[0], rgb_gradation_octant_6[1], rgb_gradation_octant_6[2]),
                    circle_gradation_beginning, x0, y0));

            double[] rgb_gradation_octant_7 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_7_y-y0, octant_7_x-x0) + Math.PI, w);
            updates.add(new Pixel(octant_7_x, octant_7_y,
                    Color.color(rgb_gradation_octant_7[0], rgb_gradation_octant_7[1], rgb_gradation_octant_7[2]),
                    circle_gradation_beginning, x0, y0));

            double[] rgb_gradation_octant_8 = PhotoRetouchingFormulas.chromatic_gradation(Math.atan2(octant_8_y-y0, octant_8_x-x0) + Math.PI, w);
            updates.add(new Pixel(octant_8_x, octant_8_y,
                    Color.color(rgb_gradation_octant_8[0], rgb_gradation_octant_8[1], rgb_gradation_octant_8[2]),
                    circle_gradation_beginning, x0, y0)); // octant n°8

            if (d >= 2 * x) {
                d -= 2 * x + 1;
                x++;
            } else if (d < 2 * (radius + thickness - y)) {
                d += 2 * y - 1;
                y--;
            } else {
                d += 2 * (y - x - 1);
                y--;
                x++;
            }

        }
    }

    gui.getImageAnimation().setMax(max_counter*8);
    break;
JarsOfJam-Scheduler
  • 2,809
  • 3
  • 31
  • 70
  • thank you for providing the link to Andres(??) `Bresenham` (!!) Circle algorithm... https://en.wikipedia.org/wiki/Midpoint_circle_algorithm – Martin Frank Sep 06 '16 at 11:45
  • Hello, it's not the Bresenham's algorithm. Both are quite similar. But I really use Andres' one : https://fr.wikipedia.org/wiki/Algorithme_de_trac%C3%A9_de_cercle_d%27Andres (it seems to be only available in French) – JarsOfJam-Scheduler Sep 06 '16 at 11:47
  • oh um - seems to be identical on first glance, sorry =) have you considered to use antialiasing to improve drawing quality (https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm) – Martin Frank Sep 06 '16 at 11:48
  • I think the problem is somewhere else, and it would be a cheat to use anti-aliasing =/ – JarsOfJam-Scheduler Sep 06 '16 at 11:49
  • je ne parl le francoise ^^ i'll have alook into the french page! – Martin Frank Sep 06 '16 at 11:49
  • I don't think the problem is due to Andres (indeed, my circle is well drawn when the shift is 0°). The problem comes when the sifht is > 0° (even at 360°, there is some gaps). So I believe the `Pixel` constructor contains the problem. – JarsOfJam-Scheduler Sep 06 '16 at 11:52
  • yes, `Pixel`is the problem - the algorithm provides a full circle, so you don't have to do any further math on it. try to draw them plain! – Martin Frank Sep 06 '16 at 12:30
  • Uhm, I'm not sure to understand ? – JarsOfJam-Scheduler Sep 06 '16 at 12:31

1 Answers1

1

you don't have to 'rotate' the pixel, you just have to draw them

Pixel(double x, double y, Color color, double angle, double x0, double y0) {
    this.x = (int) (x + x0);
    this.y = (int) (y + y0);
    this.color = getColorForAngle(angle);
}

Bresenham / Andres provide all pixels of the circle, no need to translate/rotate them

Martin Frank
  • 3,445
  • 1
  • 27
  • 47
  • In fact I want to rotate the circle thanks to the angle, that's why my `Pixel`'s constructor contains the function-calls `Math.cos(angle)`, etc. So I can't use the constructor you wrote =/ – JarsOfJam-Scheduler Sep 07 '16 at 11:27
  • have you tried to use it so you see differences in the rendering? – Martin Frank Sep 07 '16 at 11:36
  • But why should I use it ? There isn't any angle to take into account =/ I already draw my circle without taking account this angle, it worked ; but now I would want to rotate it ^^ – JarsOfJam-Scheduler Sep 07 '16 at 11:46
  • you should not rotate the calculated circle, the point of calculating the circle by bresenham/Andres is the fact that it matches perfect on your rastered screen. instead try to use the pixel coordinates and alter the color! (i have adjusted my answer) – Martin Frank Sep 07 '16 at 12:04
  • Ah okay so according to you, I really can't modify the circle and I have to shift only the gradation ? – JarsOfJam-Scheduler Sep 07 '16 at 12:07
  • yes, the bresenham/Andres provide a perfrect circle for a rastered view (monitor). if you start to rotate these pixel you get rounding errors (resulting in missing pixels and inaccurate diameter). – Martin Frank Sep 07 '16 at 12:10
  • just alter the color for each 'rotation degree' – Martin Frank Sep 07 '16 at 12:11
  • Ah okay, well I'm quite disapointed because I must to modify all my code, but thank you ! :) – JarsOfJam-Scheduler Sep 07 '16 at 12:12
  • i'm really sorry to hear that - i used the algorith from besenheim for both lines and cicles and had also some trouble with it (e.g. getting a disque from a circle made me worries) – Martin Frank Sep 07 '16 at 12:13