Javascript 留下空像素的Bresenham同心圆
我正在使用中点圆算法(也称为Bresenham算法)绘制同心圆。每个圆的半径与下一个圆的半径之差始终为1,因此最终结果应为完整的圆形区域 但是,某些像素保留为空,如所附图像中所示 我正在使用Javascript在HTML5画布上绘制,操作canvas.getContext(“2d”).getImageData(…).data数组 圆圈交替为白色和红色,空像素为黑色。你可能需要放大才能正确理解我的意思 我正在尝试向算法中添加一些代码,以便在绘制相应的圆弧时填充这些像素。似乎没有理由让这些像素属于一个弧而不是下一个弧,所以我不在乎它们是与半径为偶数的弧一起填充,还是与半径为奇数的弧一起填充(我希望我说得很清楚) 像素似乎遵循一种模式,但我不知道那是什么。谁能帮我找到它Javascript 留下空像素的Bresenham同心圆,javascript,algorithm,geometry,bresenham,Javascript,Algorithm,Geometry,Bresenham,我正在使用中点圆算法(也称为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
如果不清楚的话,这是一个预期的结果。