Bresenham concentric circles leaving empty pixels

半城伤御伤魂 提交于 2020-01-02 06:01:51

问题


I am using the midpoint circle algorithm, also known as Bresenham's, to draw concentric circles. The difference between each circle's radius and that of the next is always 1, so the final result should be a full circular area.

However, some pixels are left empty, as shown in the attached image.

I'm using Javascript to paint on an HTML5 canvas, manipulating the canvas.getContext("2d").getImageData(...).data array.

The circles are alternatively white and red, and the empty pixels are black. You might have to zoom in in order to see what I mean properly.

I'm trying to add some code to the algorithm so that those pixels are filled when drawing the corresponding arc. There seems to be no reason for any of those pixels to belong to one arc rather than the next one, so I don't care if they are filled along with arcs that have an even radius or with arcs that have an odd radius (I hope I'm making myself clear).

The pixels seem to be following a pattern, but I'm clueless about what could that be. Could anyone help me find it?

function drawCircles(radius, x, y){
    var f = 1 - radius;
    var ddF_x = 1;
    var ddF_y = -2 * radius;
    var x = 0;
    var y = radius;

    //Colors
    var red = 255;       
    var green = radius%2==0?255:0;       
    var blue = radius%2==0?255:0;        

    paintPixel(x, y + radius, red, green, blue);
    paintPixel(x, y - radius, red, green, blue);
    paintPixel(x + radius, y, red, green, blue);
    paintPixel(x - radius, y, red, green, blue);    

    while(x < y){
        // ddF_x == 2 * x + 1;
        // ddF_y == -2 * y;
        // f == x*x + y*y - radius*radius + 2*x - y + 1;
        if(f >= 0) 
        {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;    
        paintPixel(x + x, y + y, red, green, blue);
        paintPixel(x - x, y + y, red, green, blue);
        paintPixel(x + x, y - y, red, green, blue);
        paintPixel(x - x, y - y, red, green, blue);
        paintPixel(x + y, y + x, red, green, blue);
        paintPixel(x - y, y + x, red, green, blue);
        paintPixel(x + y, y - x, red, green, blue);
        paintPixel(x - y, y - x, red, green, blue);
    }

}

function paintPixel(x, y, red, green, blue){
    imageData.data[grid[y][x]] = red;
    imageData.data[grid[y][x]+1] = green;
    imageData.data[grid[y][x]+2] = blue;
    imageData.data[grid[y][x]+3] = 255; //Alpha
}

回答1:


Bresenham's is designed to plot a line using one pixel by n-pixel areas. At 45 degrees, it will plot one pixel then another (+1,+1) to it. This gives an average thickness between the centres of the two pixels of 1/√2. An exact plot of a one pixel thick line has a thickness of 1. The black dots are due to this difference between the thickness of the Bresenham algorithm line and the true thickness.

If you extend the pixels plotted to include all pixels the centre of the true line crosses, it should not have any gaps, as its thickness will never be less than one. One way of doing this is to uses Bresenham's twice with inside and outside radii, and plotting pixels based on the difference between the two.




回答2:


If you design your Bresenham-style circle drawer to compute boundary outlines instead of pixels, you can generate circles that nest perfectly. Conceptually, a boundary outline is a list of pixel edges, instead of pixel centers. This fits well with Bresenham-style operations: register a horizontal edge when incrementing the x-coordinate, and a vertical edge when incrementing the y-coordinate.

For each circle, compute two outlines: one for outer_radius, and again for (outer_radius - pen_diameter). Draw the pixels between the two outlines: with a bit of cleverness, you should be able to run both outline generators in the same loop, and do the pixel drawing online.

Of course, circles drawn with this boundary technique will look different from circles generated directly. However, IIRC, the boundary technique may be more robust than the direct technique, anyway...




回答3:


This looks like an aliasing problem for sure. Since the missing pixels seem to be more dense at 45° angles, I suspect that the root problem has to do with the distance calculations. Along the diagonal, the distance across a pixel is about 41% more than when measured along the axes. This can cause the pixel center to be further from either circle. Without seeing your code, it's hard to say more.

One fix might be to simply solid-fill the circle with one circle color and then just draw the other circle color.




回答4:


<canvas width="500" height="500" style="background:#000;">
</canvas>​

var canvas = $("canvas")[0];
var cen = $("canvas").width()/2;
var len = cen, i = len;
var ctx = canvas.getContext("2d");
var red = "#f00";
var white = "#fff";


for (; i > 0; i--){
    ctx.beginPath();
    ctx.arc(cen, cen, i, 0, 2 * Math.PI, false);
    ctx.fillStyle = i % 2 ? red : white;
    ctx.fill();
}​

http://jsfiddle.net/RmHC3/

No black dots. :)




回答5:


Well, I teach Assembly Language at Technological University of Honduras (UTH) and for some reason I was trying to draw lines and circles but I tried to find an algorithm different than Bresenham's one, and I found these ones (for line and circle) that solve that holes in original Bresenham's when you fill circles with concentric circles or when you fill rectangles with oblique lines.

Note1: This algorithm is not the same than Supercover Algorithm's, but you can use that one for the same purpouse.

Note2: I only use integer arithmetic and logic function to accomplish the jobs.

This is a screenshot (using Emu8086 in a Windows XP VirtualBox and compiling the program to an .exe file).

This code should be optimized but due to is made for teaching purposes then I just program in such a way students can easily understand.

            data segment
                ; Las variables que comienzan con _ son variables usadas en los procedimientos
                _migaja         dw ?
                _x              dw ?
                _y              dw ?
                _x2             dw ?
                _y2             dw ?
                _color          dw ?
                _deltaX         dw ?
                _deltaY         dw ?
                _deltaX_abs     dw ?
                _deltaY_abs     dw ?
                _error          dw ?
                _error_x        dw ?
                _error_y        dw ?
                _error_xy       dw ?
                _error_x_abs    dw ?
                _error_y_abs    dw ?
                _error_xy_abs   dw ?
                _cambio_y       dw ?
                _color_inicial  db ?
                _color_relleno  db ?
                _xc             dw ?
                _yc             dw ?
                _radio          dw ?
                ; Variables usadas en la parte principal
                i               dw ?   
                xcentro        dw 160
                ycentro        dw 100
                radio          dw 1
                color          dw 0
            ends

            stack segment
                dw   32767  dup(0)
            ends

            code segment
            start:
                mov ax, data
                mov ds, ax
                mov es, ax
                call videoMode

                mov color, 10

                pre_ciclo:
                    mov radio, 0


                ciclo:
                    cmp radio, 100
                    jge salir_ciclo
                    push xcentro
                    push ycentro
                    push radio
                    push color
                    call circulo
                    inc radio
                    jmp ciclo        
                salir_ciclo:


                mov ah, 1
                int 21h

                mov ax, 4c00h
                int 21h    
            ends

                videoMode PROC
                    mov ah, 0
                    mov al, 13h
                    int 10h
                    ret
                ENDP

                setPixel PROC
                    pop _migaja
                    pop ax
                    pop dx
                    pop cx
                    push _migaja
                    mov ah, 0Ch
                    int 10h        
                    ret
                ENDP

                circulo PROC
                    ; Este procedimiento dibuja un circulo en (x,y) de radio r
                    ; Hecho por Ing. Yury Euceda© para los alumnos de UTH Agosto 2014
                    pop _migaja
                    pop _color
                    pop _radio
                    pop _yc
                    pop _xc
                    push _migaja

                    ; Defino el error inicial
                    pre_ciclo_circle:
                        mov _error, 0
                        mov _x, 0
                        mov ax, _radio
                        mov _y, ax

                    ciclo_circulo:
                        push cx


                        mov cx, _x
                        add cx, _xc
                        mov dx, _yc
                        add dx, _y
                        mov ax, _color
                        mov ah, 0Ch
                        int 10h
                        push dx
                        mov dx, _yc
                        sub dx, _y
                        int 10h
                        push cx
                        mov cx, _xc
                        sub cx, _x
                        int 10h
                        pop cx
                        pop dx
                        mov cx, _xc
                        sub cx, _x            
                        int 10h


                        pop cx
                        cmp _y, 0
                        je salir_ciclo_circulo
                        ; Calculo error si suben ambos
                        mov ax, _x
                        shl ax, 1
                        inc ax
                        add ax, _error
                        mov _error_x, ax
                        mov _error_x_abs, ax
                        mov _error_xy, ax
                        mov _error_xy_abs, ax
                        mov ax, _y
                        shl ax, 1
                        neg ax
                        inc ax
                        add _error_xy, ax
                        add _error_xy_abs, ax
                        add ax, _error
                        mov _error_y, ax
                        mov _error_y_abs, ax

                        ; Calculo los valores absolutos de los errores
                        cmp _error_x_abs, 0
                        jge continuar1_circulo
                        neg _error_x_abs
                        continuar1_circulo:
                        cmp _error_y_abs, 0
                        jge continuar2_circulo
                        neg _error_y_abs
                        continuar2_circulo:
                        cmp _error_xy_abs, 0
                        jge continuar3_circulo
                        neg _error_xy_abs
                        continuar3_circulo:
                        ; Ahora voy a decidir que error absoluto es el menor
                        inc _x            
                        dec _y
                        mov ax, _error_xy
                        mov _error, ax
                        mov ax, _error_xy_abs

                        compare_a_b_circulo:
                            cmp ax, _error_y_abs    ; compare a con b
                            jg compare_b_c_circulo          ; si a > b compare b con c
                            cmp ax, _error_xy_abs   ; sino compare a con c
                            jg continuar_loop_circulo       ; si es mayor continue loop
                            inc _y
                            mov ax, _error_x
                            mov _error, ax                
                            jmp continuar_loop_circulo
                        compare_b_c_circulo:
                            mov ax, _error_y_abs
                            cmp ax, _error_xy_abs
                            jg continuar_loop_circulo
                            dec _x
                            mov ax, _error_y
                            mov _error, ax                
                        continuar_loop_circulo:
                    jmp ciclo_circulo
                    salir_ciclo_circulo:
                    ret
                ENDP


                linea PROC
                    ; Este procedimiento dibuja una linea desde (x1,y1) hasta (x2,y2)
                    ; Hecho por Ing. Yury Euceda© para los alumnos de UTH Agosto 2014
                    pop _migaja
                    pop _color
                    pop _y2
                    pop _x2
                    pop _y
                    pop _x
                    push _migaja

                    mov ax, _x
                    cmp ax, _x2
                    jle calcular_deltaX
                    xchg ax, _x2
                    mov _x, ax
                    mov ax, _y
                    xchg ax, _y2
                    mov _y, ax 



                    calcular_deltaX:
                        ; Calculo deltaX = X2 - X
                        mov ax, _x2
                        sub ax, _x
                        mov _deltaX, ax
                        mov _deltaX_abs, ax
                        cmp ax, 0
                        jge calcular_deltaY
                        neg _deltaX_abs

                    calcular_deltaY:        
                        ; Calculo deltaY = Y2 - Y
                        mov ax, _y2
                        sub ax, _y
                        mov _deltaY, ax
                        mov _deltaY_abs, ax
                        cmp ax, 0
                        jge calcular_cambio
                        neg _deltaY_abs

                    calcular_cambio:
                        mov _cambio_y, 1
                        cmp _deltaY, 0
                        jge pre_ciclo_linea
                        neg _cambio_y        

                    ; Defino el error inicial
                    pre_ciclo_linea:
                        mov _error, 0
                        mov ax, _deltaY_abs
                        cmp _deltaX_abs, ax
                        jge asignar_deltaX
                        mov cx, _deltaY_abs
                        inc cx
                        jmp ciclo_linea

                        asignar_deltaX:
                        mov cx, _deltaX_abs
                        inc cx

                    ciclo_linea:
                        push cx
                        push _x
                        push _y
                        push _color
                        call setPixel
                        pop cx
                        ; Calculo error si suben ambos
                        mov ax, _error
                        add ax, _deltaY_abs         ; ax  = error + deltaY
                        mov _error_x, ax
                        mov _error_x_abs, ax
                        sub ax, _deltaX_abs         ; ax = error + deltaY - deltaX
                        mov _error_xy, ax
                        mov _error_xy_abs, ax
                        sub ax, _deltaY_abs         ; ax = error - deltaX
                        mov _error_y, ax
                        mov _error_y_abs, ax
                        ; Calculo los valores absolutos de los errores
                        cmp _error_x_abs, 0
                        jge continuar1
                        neg _error_x_abs
                        continuar1:
                        cmp _error_y_abs, 0
                        jge continuar2
                        neg _error_y_abs
                        continuar2:
                        cmp _error_xy_abs, 0
                        jge continuar3
                        neg _error_xy_abs
                        continuar3:

                        comparar_x_con_y:
                            mov ax      , _error_y_abs
                            cmp _error_x_abs, ax
                            jge comparar_y_con_xy
                            mov ax      , _error_xy_abs
                            cmp _error_x_abs, ax
                            jg cambiar_xy
                            inc _x
                            mov ax, _error_x
                            mov _error, ax
                            jmp continuar_loop

                        comparar_y_con_xy:
                            mov ax      , _error_xy_abs
                            cmp _error_y_abs, ax
                            jge cambiar_xy
                            mov ax, _cambio_y
                            add _y, ax
                            mov ax, _error_y
                            mov _error, ax
                            jmp continuar_loop

                        cambiar_xy:
                            inc _x
                            mov ax, _cambio_y
                            add _y, ax
                            mov ax, _error_xy
                            mov _error, ax


                        continuar_loop:

                    loop ciclo_linea            
                    ret
                ENDP


                rellenar PROC
                    pop _migaja
                    pop ax
                    pop dx
                    pop cx
                    push _migaja
                    mov _color_relleno, aL
                    mov ah, 0Dh
                    int 10h
                    mov _color_inicial, aL 
                    ; Llamo la recursiva
                    push cx
                    push dx
                    call rellenar_recursiva
                    pop dx
                    pop cx
                    ret
                ENDP

                rellenar_recursiva PROC
                    pop _migaja
                    ; Saco los parametros de la pila
                    pop dx
                    pop cx
                    ; Vuelvo a meterlos a la pila :)
                    push cx
                    push dx
                    push _migaja

                    ; valido que el punto este en rango
                    cmp cx, 0
                    jl salir_rellenar
                    cmp cx, 319
                    jg salir_rellenar
                    cmp dx, 0
                    jl salir_rellenar
                    cmp dx, 199
                    jg salir_rellenar
                    ; Extraigo el color del pixel en CX,DX
                    mov ah, 0Dh
                    int 10h
                    ; Lo comparo con el color inicial
                    cmp _color_inicial, aL
                    ; Si no es igual salgase
                    jne salir_rellenar
                    ; Si es igual entonces lo pinto
                    mov aL, _color_relleno
                    mov ah, 0Ch
                    int 10h

                    ; Pinto el norte
                    dec dx
                    push cx
                    push dx
                    call rellenar_recursiva
                    pop dx
                    pop cx
                    inc dx
                    ; Pinto el este
                    inc cx
                    push cx
                    push dx
                    call rellenar_recursiva
                    pop dx
                    pop cx
                    dec cx
                    ; Pinto el sur
                    inc dx
                    push cx
                    push dx
                    call rellenar_recursiva
                    pop dx
                    pop cx
                    dec dx
                    ; Pinto el oeste
                    dec cx
                    push cx
                    push dx
                    call rellenar_recursiva
                    pop dx
                    pop cx
                    inc cx
                    salir_rellenar:
                    ret
                ENDP


            end start


来源:https://stackoverflow.com/questions/12201907/bresenham-concentric-circles-leaving-empty-pixels

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!