Bresenham同心圆留空像素

时间:2012-08-30 17:00:02

标签: javascript algorithm geometry bresenham

我正在使用中点圆算法(也称为Bresenham's)来绘制同心圆。每个圆的半径与下一个圆的半径之间的差值始终为1,因此最终结果应该是一个完整的圆形区域。

但是,有些像素是空的,如附图所示。

我正在使用Javascript在HTML5画布上绘画,操纵canvas.getContext(“2d”)。getImageData(...)。data array。

圆圈是白色和红色,空像素是黑色。您可能需要放大才能正确理解我的意思。

Bresenham concentric circles

我正在尝试向算法添加一些代码,以便在绘制相应的弧时填充这些像素。似乎没有任何理由让这些像素属于一个弧而不是下一个弧,所以我不在乎它们是否与具有偶数半径的弧或具有奇数半径的弧一起被填充(I希望我能说清楚自己。)

像素似乎遵循一种模式,但我对这可能是什么一无所知。谁能帮我找到它?

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
}

5 个答案:

答案 0 :(得分:3)

Bresenham的设计是使用一个像素乘n像素区域绘制一条线。在45度时,它将绘制一个像素,然后绘制另一个像素(+ 1,+ 1)。这给出了两个像素的中心之间的平均厚度1 /√2。一条像素粗线的精确图具有1的厚度。黑点是由于Bresenham算法线的厚度与真实厚度之间的这种差异。

如果扩展绘制的像素以包括所有像素,则真实线的中心交叉,它不应有任何间隙,因为其厚度永远不会小于1。一种方法是使用Bresenham两次内外半径,并根据两者之间的差异绘制像素。

答案 1 :(得分:2)

如果您设计Bresenham风格的圆形抽屉来计算边界轮廓而不是像素,则可以生成完美嵌套的圆圈。从概念上讲,边界轮廓是像素边缘的列表,而不是像素中心。这与Bresenham式操作非常吻合:在递增x坐标时记录水平边缘,在递增y坐标时记录垂直边缘。

对于每个圈子,计算两个轮廓:一个用于outer_radius,另一个用于(outer_radius - pen_diameter)。在两个轮廓之间绘制像素:有点聪明,你应该能够在同一个循环中运行两个轮廓生成器,并在线进行像素绘制。

当然,使用此边界技术绘制的圆圈与直接生成的圆圈看起来不同。然而,无论如何,IIRC,边界技术可能比直接技术更强大......

答案 2 :(得分:1)

这肯定是一个别名问题。由于丢失的像素似乎在45°角更密集,我怀疑根问题与距离计算有关。沿着对角线,像素的距离比沿轴测量的距离大约多41%。这可能导致像素中心距离任何一个圆圈更远。没有看到你的代码,很难说更多。

一个修复方法可能是用一个圆形颜色简单地填充圆形,然后只绘制另一个圆形颜色。

答案 3 :(得分:1)

<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/

没有黑点。 :)

答案 4 :(得分:1)

好吧,我在洪都拉斯理工大学教授汇编语言(UTH),出于某种原因,我试图绘制线条和圆圈,但我试图找到一种与Bresenham不同的算法,我找到了这些算法(用于线和当你用同心圆填充圆圈或用斜线填充矩形时,解决原始Bresenham中的那些洞。

注1:此算法与Supercover Algorithm的算法不同,但您可以将该算法用于同一个purpouse。

注2:我只使用整数算术和逻辑函数来完成这些工作。

这是一个屏幕截图(在Windows XP VirtualBox中使用Emu8086并将程序编译为.exe文件)。

enter image description here

这段代码应该进行优化,但由于是出于教学目的,我只是以学生可以轻松理解的方式进行编程。

            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