Algoritmo de linha de Bresenham - Bresenham's line algorithm

O algoritmo de linha de Bresenham é um algoritmo de desenho de linha que determina os pontos de um raster n- dimensional que deve ser selecionado para formar uma aproximação de uma linha reta entre dois pontos . É comumente usado para desenhar linhas primitivas em uma imagem bitmap (por exemplo, na tela de um computador ), pois usa apenas adição, subtração e deslocamento de bits inteiros , todas operações muito baratas em arquiteturas de computador padrão . É um algoritmo de erro incremental . É um dos primeiros algoritmos desenvolvidos no campo da computação gráfica . Uma extensão do algoritmo original pode ser usada para desenhar círculos .

Embora algoritmos como o algoritmo de Wu também sejam freqüentemente usados ​​na computação gráfica moderna porque podem suportar anti-serrilhamento , a velocidade e a simplicidade do algoritmo de linha de Bresenham significa que ele ainda é importante. O algoritmo é usado em hardware, como plotadoras e nos chips gráficos de placas de vídeo modernas . Ele também pode ser encontrado em muitas bibliotecas de gráficos de software . Como o algoritmo é muito simples, ele geralmente é implementado no firmware ou no hardware gráfico de placas de vídeo modernas .

O rótulo "Bresenham" é usado hoje para uma família de algoritmos que estendem ou modificam o algoritmo original de Bresenham.

História

O algoritmo de linha de Bresenham recebeu o nome de Jack Elton Bresenham, que o desenvolveu em 1962 na IBM . Em 2001, Bresenham escreveu:

Eu estava trabalhando no laboratório de computação do laboratório de desenvolvimento da IBM em San Jose. Um plotter Calcomp foi conectado a um IBM 1401 por meio do console da máquina de escrever 1407. [O algoritmo] estava em uso em produção no verão de 1962, possivelmente um mês ou mais antes. Naquela época, os programas eram trocados livremente entre as empresas, de modo que a Calcomp (Jim Newland e Calvin Hefte) tinha cópias. Quando voltei para Stanford no outono de 1962, coloquei uma cópia na biblioteca do Stanford Comp Center. Uma descrição da rotina de desenho de linhas foi aceita para apresentação na convenção nacional ACM de 1963 em Denver, Colorado. Foi um ano em que não foram publicados anais, apenas pauta de palestrantes e temas em uma edição das Comunicações da ACM. Uma pessoa do IBM Systems Journal me perguntou depois de fazer minha apresentação se eles poderiam publicar o artigo. Eu concordei feliz e eles o imprimiram em 1965.

O algoritmo de Bresenham foi estendido para produzir círculos, elipses, curvas bézier cúbicas e quadráticas, bem como versões nativas com suavização de serrilhado.

Método

Ilustração do resultado do algoritmo de linha de Bresenham. (0,0) está no canto superior esquerdo da grade, (1,1) está na extremidade superior esquerda da linha e (11, 5) está na extremidade inferior direita da linha.

As seguintes convenções serão usadas:

  • o canto superior esquerdo é (0,0) de modo que as coordenadas do pixel aumentem nas direções direita e para baixo (por exemplo, que o pixel em (7,4) está diretamente acima do pixel em (7,5)), e
  • os centros dos pixels têm coordenadas inteiras.

Os pontos finais da linha são os pixels em e , onde a primeira coordenada do par é a coluna e a segunda é a linha.

O algoritmo será apresentado inicialmente apenas para o octante em que o segmento desce e para a direita ( e ), e sua projeção horizontal é mais longa que a projeção vertical (a linha tem inclinação positiva menor que 1). Neste octante, para cada coluna x entre e , há exatamente uma linha y (calculada pelo algoritmo) contendo um pixel da linha, enquanto cada linha entre e pode conter vários pixels rasterizados.

O algoritmo de Bresenham escolhe o inteiro y correspondente ao centro do pixel que está mais próximo do y ideal (fracionário) para o mesmo x ; em colunas sucessivas y pode permanecer o mesmo ou aumentar em 1. A equação geral da linha através dos pontos finais é dada por:

.

Como sabemos a coluna, x , a linha do pixel, y , é dada arredondando essa quantidade para o número inteiro mais próximo:

.

A inclinação depende apenas das coordenadas do ponto final e pode ser pré-computada, e o y ideal para valores inteiros sucessivos de x pode ser calculado começando e adicionando repetidamente a inclinação.

Na prática, o algoritmo não acompanha a coordenada y, que aumenta em m = ∆y / ∆x cada vez que x aumenta em um; ele mantém um limite de erro em cada estágio, que representa o negativo da distância de (a) o ponto onde a linha sai do pixel até (b) a borda superior do pixel. Este valor é primeiro definido como (devido ao uso das coordenadas centrais do pixel) e é incrementado em m cada vez que a coordenada x é incrementada em um. Se o erro se tornar maior que 0,5 , sabemos que a linha se moveu um pixel para cima e que devemos incrementar nossa coordenada y e reajustar o erro para representar a distância do topo do novo pixel - o que é feito subtraindo um de erro.

Derivação

Para derivar o algoritmo de Bresenham, duas etapas devem ser executadas. O primeiro passo é transformar a equação de uma linha da forma típica de interceptação em declive em algo diferente; e então usar essa nova equação para traçar uma linha com base na ideia de acúmulo de erro.

Equação linear

y = f (x) =. 5x + 1 ou f (x, y) = x-2y + 2
Meios planos positivos e negativos

A forma de declive-interceptação de uma linha é escrita como

onde m é a inclinação eb é a interceptação y. Esta é uma função de apenas x e seria útil fazer esta equação escrita como uma função de x e y. Usando manipulação algébrica e reconhecimento de que a inclinação é a "subida sobre a corrida" ou então

Deixando esta última equação ser uma função de xey, então ela pode ser escrita como

onde as constantes estão

A linha é então definida para algumas constantes A, B e C em qualquer lugar . Para qualquer um que não esteja na linha, então . Tudo sobre este formulário envolve apenas inteiros se xey forem inteiros, pois as constantes são necessariamente inteiros.

Por exemplo, a linha então isso poderia ser escrito como . O ponto (2,2) está na linha

e o ponto (2,3) não está na linha

e nem é o ponto (2,1)

Observe que os pontos (2,1) e (2,3) estão em lados opostos da linha e f (x, y) é avaliado como positivo ou negativo. Uma linha divide um plano em metades e o semiplano que tem um f negativo (x, y) pode ser chamado de semiplano negativo e a outra metade pode ser chamada de semiplano positivo. Esta observação é muito importante no restante da derivação.

Algoritmo

Claramente, o ponto de partida está na linha

apenas porque a linha está definida para começar e terminar em coordenadas inteiras (embora seja inteiramente razoável querer desenhar uma linha com pontos finais não inteiros).

Ponto candidato (2,2) em azul e dois pontos candidatos em verde (3,2) e (3,3)

Tendo em mente que a inclinação é menor ou igual a um, o problema agora se apresenta se o próximo ponto deve ser em ou . Talvez intuitivamente, o ponto deva ser escolhido com base no que está mais próximo da linha em . Se estiver mais próximo do primeiro, inclua o primeiro ponto na linha, se o último, então o último. Para responder a isso, avalie a função de linha no ponto médio entre esses dois pontos:

Se o valor disso for positivo, a linha ideal está abaixo do ponto médio e mais próxima do ponto candidato ; na verdade, a coordenada y avançou. Caso contrário, a linha ideal passa através ou acima do ponto médio e a coordenada y não avançou; neste caso, escolha o ponto . Essa observação é fundamental para entender! O valor da função de linha neste ponto médio é o único determinante de qual ponto deve ser escolhido.

A imagem adjacente mostra o ponto azul (2,2) escolhido para estar na linha com dois pontos candidatos em verde (3,2) e (3,3). O ponto preto (3, 2,5) é o ponto médio entre os dois pontos candidatos.

Algoritmo para aritmética inteira

Alternativamente, a diferença entre os pontos pode ser usada em vez de avaliar f (x, y) nos pontos médios. Este método alternativo permite a aritmética apenas de inteiros, que geralmente é mais rápida do que usar a aritmética de ponto flutuante. Para derivar o método alternativo, defina a diferença como a seguir:

Para a primeira decisão, esta formulação é equivalente ao método do ponto médio desde o ponto de partida. Simplificar essa expressão resulta em:

Assim como no método do ponto médio, se D for positivo, escolha , caso contrário, escolha .

Se for escolhido, a mudança em D será:

Se for escolhido, a mudança em D será:

Se o novo D for positivo, então é escolhido, caso contrário . Esta decisão pode ser generalizada acumulando o erro em cada ponto subsequente.

Traçando a linha de (0,1) a (6,4) mostrando um gráfico de linhas de grade e pixels

Toda a derivação para o algoritmo é feita. Um problema de desempenho é o fator 1/2 no valor inicial de D. Como tudo isso é sobre o sinal da diferença acumulada, então tudo pode ser multiplicado por 2 sem consequência.

Isso resulta em um algoritmo que usa apenas aritmética de inteiros.

plotLine(x0, y0, x1, y1)
    dx = x1 - x0
    dy = y1 - y0
    D = 2*dy - dx
    y = y0

    for x from x0 to x1
        plot(x,y)
        if D > 0
            y = y + 1
            D = D - 2*dx
        end if
        D = D + 2*dy

Executar este algoritmo de (0,1) a (6,4) produz as seguintes diferenças com dx = 6 e dy = 3:

D=2*3-6=0
Loop from 0 to 6
 * x=0: plot(0, 1), D≤0: D=0+6=6
 * x=1: plot(1, 1), D>0: D=6-12=-6, y=1+1=2, D=-6+6=0
 * x=2: plot(2, 2), D≤0: D=0+6=6
 * x=3: plot(3, 2), D>0: D=6-12=-6, y=2+1=3, D=-6+6=0
 * x=4: plot(4, 3), D≤0: D=0+6=6
 * x=5: plot(5, 3), D>0: D=6-12=-6, y=3+1=4, D=-6+6=0
 * x=6: plot(6, 4), D≤0: D=0+6=6

O resultado deste gráfico é mostrado à direita. A plotagem pode ser visualizada plotando na interseção das linhas (círculos azuis) ou preenchendo as caixas de pixel (quadrados amarelos). Independentemente disso, a plotagem é a mesma.

Todos os casos

No entanto, como mencionado acima, isso é apenas para o octante zero, ou seja, as linhas começando na origem com um gradiente entre 0 e 1, onde x aumenta em exatamente 1 por iteração ey aumenta em 0 ou 1.

O algoritmo pode ser estendido para cobrir gradientes entre 0 e -1, verificando se y precisa aumentar ou diminuir (ou seja, dy <0)

plotLineLow(x0, y0, x1, y1)
    dx = x1 - x0
    dy = y1 - y0
    yi = 1
    if dy < 0
        yi = -1
        dy = -dy
    end if
    D = (2 * dy) - dx
    y = y0

    for x from x0 to x1
        plot(x, y)
        if D > 0
            y = y + yi
            D = D + (2 * (dy - dx))
        else
            D = D + 2*dy
        end if

Ao alternar os eixos xey, uma implementação para gradientes íngremes positivos ou negativos pode ser escrita como

plotLineHigh(x0, y0, x1, y1)
    dx = x1 - x0
    dy = y1 - y0
    xi = 1
    if dx < 0
        xi = -1
        dx = -dx
    end if
    D = (2 * dx) - dy
    x = x0

    for y from y0 to y1
        plot(x, y)
        if D > 0
            x = x + xi
            D = D + (2 * (dx - dy))
        else
            D = D + 2*dx
        end if

Uma solução completa precisaria detectar se x1> x0 ou y1> y0 e reverter as coordenadas de entrada antes de desenhar, assim

plotLine(x0, y0, x1, y1)
    if abs(y1 - y0) < abs(x1 - x0)
        if x0 > x1
            plotLineLow(x1, y1, x0, y0)
        else
            plotLineLow(x0, y0, x1, y1)
        end if
    else
        if y0 > y1
            plotLineHigh(x1, y1, x0, y0)
        else
            plotLineHigh(x0, y0, x1, y1)
        end if
    end if

Em implementações de baixo nível que acessam a memória de vídeo diretamente, seria típico que os casos especiais de linhas verticais e horizontais fossem tratados separadamente, pois podem ser altamente otimizados.

Algumas versões usam os princípios de Bresenham de erro incremental de número inteiro para realizar todos os desenhos de linhas de octante, equilibrando o erro positivo e negativo entre as coordenadas xey. Observe que o pedido não é necessariamente garantido; em outras palavras, a linha pode ser traçada de (x0, y0) a (x1, y1) ou de (x1, y1) a (x0, y0).

plotLine(int x0, int y0, int x1, int y1)
    dx =  abs(x1-x0);
    sx = x0<x1 ? 1 : -1;
    dy = -abs(y1-y0);
    sy = y0<y1 ? 1 : -1;
    err = dx+dy;  /* error value e_xy */
    while (true)   /* loop */
        plot(x0, y0);
        if (x0 == x1 && y0 == y1) break;
        e2 = 2*err;
        if (e2 >= dy) /* e_xy+e_x > 0 */
            err += dy;
            x0 += sx;
        end if
        if (e2 <= dx) /* e_xy+e_y < 0 */
            err += dx;
            y0 += sy;
        end if
    end while

Algoritmos semelhantes

O algoritmo de Bresenham pode ser interpretado como um analisador diferencial digital ligeiramente modificado (usando 0,5 como limite de erro em vez de 0, que é necessário para rasterização de polígonos não sobrepostos).

O princípio de usar um erro incremental no lugar de operações de divisão tem outras aplicações em gráficos. É possível usar esta técnica para calcular as coordenadas U, V durante a varredura raster de polígonos mapeados com textura. Os motores de renderização do software voxel heightmap vistos em alguns jogos de PC também usaram esse princípio.

Bresenham também publicou um algoritmo computacional Run-Slice (em oposição ao Run-Length). Este método foi representado em várias patentes dos EUA:

5.815.163 Método e aparelho para desenhar fatias de linha durante o cálculo
5.740.345 Método e aparelho para exibir dados gráficos de computador armazenados em um formato compactado com um sistema de indexação de cores eficiente
5.657.435 Execute o mecanismo de desenho de linha de fatia com recursos de escala não linear
5.627.957 Execute o mecanismo de desenho de linha de fatia com recursos de processamento aprimorados
5.627.956 Execute o mecanismo de desenho de linha de corte com recursos de alongamento
5.617.524 Execute o mecanismo de desenho de linha de fatia com recursos de sombreamento
5.611.029 Execute o mecanismo de desenho de linha de fatia com recursos de sombreamento não linear
5.604.852 Método e aparelho para exibir uma curva paramétrica em uma tela de vídeo
5.600.769 Execute o mecanismo de desenho de linha de fatia com técnicas de recorte aprimoradas

Uma extensão do algoritmo que lida com linhas grossas foi criada por Alan Murphy na IBM.

Veja também

Notas

Referências

Leitura adicional

em vez de [qual] para uso de extensão de círculo: Relatório Técnico 1964 27 de janeiro -11- Algoritmo de Círculo TR-02-286 IBM San Jose Lab ou Um Algoritmo Linear para Exibição Digital Incremental de Arcos Circulares Fevereiro de 1977 Comunicações do ACM 20 (2 ): 100-106 DOI: 10.1145 / 359423.359432

links externos