4

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

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

我正在使用 Javascript 在 HTML5 画布上绘画,操作 canvas.getContext("2d").getImageData(...).data 数组。

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

布雷森汉姆同心圆

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

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

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
}
4

5 回答 5

2

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

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

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

于 2012-08-30T21:47:28.467 回答
2

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

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

于 2012-08-30T17:37:46.743 回答
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/

没有黑点。:)

于 2012-08-30T17:15:16.863 回答
1

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

一种解决方法可能是简单地用一种圆圈颜色填充圆圈,然后只绘制另一种圆圈颜色。

于 2012-08-30T17:04:11.770 回答
1

好吧,我在洪都拉斯科技大学(UTH)教汇编语言,出于某种原因,我试图画线和圆,但我试图找到一种不同于 Bresenham 的算法,我发现这些(用于线和圆)当您用同心圆填充圆形或用斜线填充矩形时,解决原始 Bresenham 中的孔。

注意 1:此算法与 Supercover 算法不同,但您可以将其用于相同目的。

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

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

在此处输入图像描述

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

            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
于 2015-10-07T06:19:21.717 回答