我正在使用中点圆算法(也称为Bresenham's)来绘制同心圆。每个圆的半径与下一个圆的半径之间的差值始终为1,因此最终结果应该是一个完整的圆形区域。
但是,有些像素是空的,如附图所示。
我正在使用Javascript在HTML5画布上绘画,操纵canvas.getContext(“2d”)。getImageData(...)。data array。
圆圈是白色和红色,空像素是黑色。您可能需要放大才能正确理解我的意思。
我正在尝试向算法添加一些代码,以便在绘制相应的弧时填充这些像素。似乎没有任何理由让这些像素属于一个弧而不是下一个弧,所以我不在乎它们是否与具有偶数半径的弧或具有奇数半径的弧一起被填充(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
}
答案 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();
}
没有黑点。 :)
答案 4 :(得分:1)
好吧,我在洪都拉斯理工大学教授汇编语言(UTH),出于某种原因,我试图绘制线条和圆圈,但我试图找到一种与Bresenham不同的算法,我找到了这些算法(用于线和当你用同心圆填充圆圈或用斜线填充矩形时,解决原始Bresenham中的那些洞。
注1:此算法与Supercover Algorithm的算法不同,但您可以将该算法用于同一个purpouse。
注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