4

同心円を描くために、ブレゼンハムとしても知られる中点円アルゴリズムを使用しています。各円の半径と次の円の半径の差は常に 1 であるため、最終結果は完全な円形領域になります。

ただし、添付の画像に示すように、一部のピクセルは空のままです。

JavaScript を使用して HTML5 キャンバスにペイントし、canvas.getContext("2d").getImageData(...).data 配列を操作しています。

円は交互に白と赤になり、空のピクセルは黒になります。私が言っていることを正しく理解するには、ズームインする必要があるかもしれません。

ブレゼンハム同心円

対応する円弧を描画するときにそれらのピクセルが塗りつぶされるように、アルゴリズムにコードを追加しようとしています。これらのピクセルのいずれかが、次のアークではなく 1 つのアークに属する理由はないように思われるため、半径が偶数のアークまたは奇数の半径のアークで埋められているかどうかは気にしません (私は私が自分自身を明確にしていることを願っています)。

ピクセルはパターンに従っているように見えますが、それが何であるかはわかりません. 誰かがそれを見つけるのを手伝ってくれますか?

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 のものは、1 ピクセル x n ピクセルの領域を使用して線をプロットするように設計されています。45 度では、1 つのピクセルをプロットしてから別の (+1,+1) ピクセルをプロットします。これにより、1/√2 の 2 つのピクセルの中心間の平均厚さが得られます。1 ピクセルの太さの線の正確なプロットの太さは 1 です。黒い点は、ブレゼンハム アルゴリズムの線の太さと実際の太さの違いによるものです。

真の線の中心が交差するすべてのピクセルを含むようにプロットされたピクセルを拡張する場合、その太さが 1 未満になることはないため、ギャップはありません。これを行う 1 つの方法は、内側と外側の半径で Bresenham を 2 回使用し、2 つの差に基づいてピクセルをプロットすることです。

于 2012-08-30T17:37:46.743 に答える
2

ピクセルではなく境界線を計算するように Bresenham スタイルのサークル ドロワーを設計すると、完全に入れ子になった円を生成できます。概念的には、境界線はピクセルの中心ではなく、ピクセルエッジのリストです。これは Bresenham スタイルの操作によく適合します。x 座標をインクリメントするときは水平エッジを登録し、y 座標をインクリメントするときは垂直エッジを登録します。

円ごとに、2 つのアウトラインを計算します。1 つは 用outer_radius、もう 1 つは 用(outer_radius - pen_diameter)です。2 つのアウトラインの間にピクセルを描画します。少し賢くすることで、両方のアウトライン ジェネレーターを同じループで実行し、オンラインでピクセル描画を行うことができます。

もちろん、この境界技法で描かれた円は、直接生成された円とは異なって見えます。ただし、IIRC、バウンダリ手法は直接手法よりも堅牢である可能性があります.

于 2012-08-30T21:47:28.467 に答える
1

さて、私はホンジュラス工科大学 (UTH) でアセンブリ言語を教えており、何らかの理由で線と円を描こうとしていましたが、ブレゼンハムのものとは異なるアルゴリズムを見つけようとしました。円を同心円で塗りつぶすとき、または長方形を斜線で塗りつぶすときに、元のブレゼンハムの穴を解決します。

注 1: このアルゴリズムは Supercover Algorithm のものと同じではありませんが、同じ目的でそのアルゴリズムを使用できます。

注 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 に答える
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% 長くなります。これにより、ピクセルの中心がいずれかの円から遠ざかる可能性があります。コードを見なければ、それ以上のことは言えません。

解決策の 1 つは、円を 1 つの円の色で単純に塗りつぶしてから、別の円の色を描画することです。

于 2012-08-30T17:04:11.770 に答える