Javascript 留下空像素的Bresenham同心圆

Javascript 留下空像素的Bresenham同心圆,javascript,algorithm,geometry,bresenham,Javascript,Algorithm,Geometry,Bresenham,我正在使用中点圆算法(也称为Bresenham算法)绘制同心圆。每个圆的半径与下一个圆的半径之差始终为1,因此最终结果应为完整的圆形区域 但是,某些像素保留为空,如所附图像中所示 我正在使用Javascript在HTML5画布上绘制,操作canvas.getContext(“2d”).getImageData(…).data数组 圆圈交替为白色和红色,空像素为黑色。你可能需要放大才能正确理解我的意思 我正在尝试向算法中添加一些代码,以便在绘制相应的圆弧时填充这些像素。似乎没有理由让这些像素属于

我正在使用中点圆算法(也称为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
}
函数绘图圆(半径,x,y){
var f=1-半径;
var-ddF_x=1;
var ddF_y=-2*半径;
var x=0;
var y=半径;
//颜色
var-red=255;
绿色变量=半径%2==0?255:0;
var blue=半径%2==0?255:0;
颜料像素(x,y+半径,红色,绿色,蓝色);
颜料像素(x,y-半径,红色,绿色,蓝色);
颜料像素(x+半径,y,红色,绿色,蓝色);
颜料像素(x半径、y半径、红色、绿色、蓝色);
while(x=0)
{
y--;
ddF_y+=2;
f+=ddF_y;
}
x++;
ddF_x+=2;
f+=ddF_x;
颜料像素(x+x、y+y、红色、绿色、蓝色);
颜料像素(x-x,y+y,红色,绿色,蓝色);
颜料像素(x+x,y-y,红色,绿色,蓝色);
颜料像素(x-x,y-y,红色,绿色,蓝色);
颜料像素(x+y、y+x、红色、绿色、蓝色);
颜料像素(x-y、y+x、红色、绿色、蓝色);
颜料像素(x+y、y-x、红色、绿色、蓝色);
颜料像素(x-y,y-x,红色,绿色,蓝色);
}
}
功能颜料像素(x、y、红色、绿色、蓝色){
imageData.data[grid[y][x]]=红色;
imageData.data[grid[y][x]+1]=绿色;
imageData.data[grid[y][x]+2]=蓝色;
imageData.data[grid[y][x]+3]=255;//Alpha
}

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

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


<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();
}​
​ var canvas=$(“canvas”)[0]; var cen=$(“画布”).width()/2; var len=cen,i=len; var ctx=canvas.getContext(“2d”); var red=“#f00”; var white=“#fff”; 对于(;i>0;i--){ ctx.beginPath(); ctx.arc(cen,cen,i,0,2*Math.PI,false); ctx.fillStyle=i%2?红色:白色; ctx.fill(); }​


没有黑点。:)

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


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

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

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


当然,使用此边界技术绘制的圆与直接生成的圆看起来不同。然而,Irc,边界技术可能比直接技术更健壮,无论如何…

< p>嗯,我在洪都拉斯科技大学(UTH)教汇编语言,由于某种原因,我试图画直线和圆,但我试图找到一种不同于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

如果不清楚的话,这是一个预期的结果。