2009-08-03 57 views
23

Actualmente estoy usando el algoritmo de Bresenham para dibujar líneas, pero tienen (por supuesto) un píxel de grosor. Mi pregunta es ¿cuál es la forma más eficiente de dibujar líneas de grosor arbitrario?¿cómo creo una línea de espesor arbitrario con Bresenham?

El lenguaje que estoy usando es C.

+1

Reetiquetado "independiente del idioma" ya que el lenguaje de implementación no es realmente relevante. –

+0

aquí hay una pregunta relacionada con esto: http://stackoverflow.com/questions/101718/drawing-a-variable-width-line-in-opengl-no-gllinewidth –

+0

@banister: ¿tiene alguna demo para compartir con nosotros? – sdkie

Respuesta

10

Creo que la mejor forma de hacerlo es dibujar un rectángulo en lugar de una línea desde una línea con anchura es un objeto bidimensional. Tratar de dibujar un conjunto de líneas paralelas para evitar sobregiros (para reducir el ancho de banda de escritura) y underdraw (píxeles faltantes) sería bastante complejo. No es demasiado difícil calcular los puntos de esquina del rectángulo desde el punto inicial y final y el ancho.

+2

No obtuve su solución. ¿Puedes elaborar por favor? – sdkie

9

Aquí hay una versión modificada del algoritmo de Bresenham para dibujar líneas engrosadas.

Es posible que también desee echar un vistazo a Anti-Grain Geometry, una biblioteca para la representación de software de gráficos de alta calidad y alto rendimiento. Eche un vistazo a demo page para tener una idea de lo que puede hacer.

4

Algunas rutas fácil de usar:

  1. para cualquier anchura n, donde n es impar. para cualquier punto p trazado también trazar los puntos arriba/abajo para n/2 (si la línea es> 45 grados ángulo de lado a lado).
    • no es realmente una línea correcta del grosor correcto, más parecido a un lápiz de cursiva, pero muy rápido.
  2. para el punto de inicio p (x, y) seleccione los puntos t0 y b de forma que estén centrados en p pero n píxeles separados. para el punto final haz lo mismo dando como resultado t1 b1. Dibuja líneas desde t0 -> t1, t1-> b1, b1 -> t0, b0 -> t1. Completa el rectángulo resultante.
    • El truco aquí es elegir los puntos de modo que parezcan ortogonales a la dirección de la ruta.
  3. por cada punto p en la línea en lugar de dibujar un punto dibujar un círculo.
    • Esto tiene la ventaja de hacer que los puntos finales estén "limpios" sin importar la orientación.
    • no debería haber necesidad de mostrar ningún círculo en sólido excepto el primero.
    • algo lento
2

Asumo que fueras a robar tramos horizontales de una línea de delimitación a otro, y calcular el valor de x en cada una de las líneas por el método de Bresenham a medida que avanza (en una sola lazo).

No lo he intentado.

Es posible que los puntos finales necesiten un poco de atención, para que no parezcan extrañamente cortados.

+0

sí, esta fue la estrategia que también tenía en mente y estoy de acuerdo en que los puntos finales necesitarán algo de reflexión. – horseyguy

0

Para mi aplicación de impresora térmica integrada, utilizando el algoritmo de Bresenham, la línea era demasiado delgada. No tengo GL ni nada elegante. Terminé simplemente decrementando el valor de Y y dibujando más líneas debajo del primero. Cada número de espesor agregó otra línea. Muy rápido de implementar y hecho para los resultados deseados, imprimiendo desde mapas de bits monocromáticos hasta los térmicos.

+0

Me gustó tu idea, ¿cómo has probado/calculado la distancia deseada entre líneas para que puedas imitar el grosor? fue por ensayo y error? –

+0

Cada punto tiene otro punto debajo ... sin cálculos; la línea era visiblemente más gruesa. Terminé usando 3 líneas, cada una con un valor Y menor que el anterior. Cualquier mínimo y menor que y fue cambiado a Y mínimo. –

+0

Lo siento, extraño confundir sobre el control del grosor y el control del ancho de la línea. –

15

Tome otro bucle de Bresenham y utilícelo para modificar la posición inicial y final de la línea original en dirección rectangular. El problema es encontrar eficientemente el punto de inicio correcto y no dibujar ningún píxel dos veces (u omitir un píxel) mientras dibuja la siguiente línea.

El código C en funcionamiento y probado está disponible en Github C code.

Aquí una página de prueba que incluye algunas líneas de muestra creadas por este código. Los píxeles negros son los puntos de partida para el algoritmo.

Test page with bresenham lines with different thickness

+2

Si pudiera, votaría más esta respuesta, porque es la única que dirige al lector a una implementación limpia y funcional en C, tal como solicitó el OP. – msteiger

+0

¡Eso es tan inteligente! – hexaflexagonal

7

Para una mejor precisión, y también un buen rendimiento para líneas más gruesas, especialmente, puede dibujar la línea como un polígono. Algunos pseudo código:

draw_line(x1,y1,x2,y2,thickness) 
    Point p[4]; 
    angle = atan2(y2-y1,x2-x1); 
    p[0].x = x1 + thickness*cos(angle+PI/2); 
    p[0].y = y1 + thickness*sin(angle+PI/2); 
    p[1].x = x1 + thickness*cos(angle-PI/2); 
    p[1].y = y1 + thickness*sin(angle-PI/2); 
    p[2].x = x2 + thickness*cos(angle-PI/2); 
    p[2].y = y2 + thickness*sin(angle-PI/2); 
    p[3].x = x2 + thickness*cos(angle+PI/2); 
    p[3].y = y2 + thickness*sin(angle+PI/2); 
    draw_polygon(p,4) 

y, opcionalmente, un círculo se pueden extraer en cada punto final.

+0

En cierto modo, esto reduce el problema a uno más complejo. Para que funcione (en Python), tuve que ajustar las cosas, sin embargo: a) intercambiar el pecado y el cos. b) en lugar de multiplicar por grosor, multiplica por grosor/2.0. – Ant6n

+0

@ Antón Creo que es el * ángulo * que está mal. Esto debería ser 'atan2 (y2-y1, x2-x1)'. – colinta

+0

@colina Se arregló. – Fabel

0

Hace un tiempo tuve el mismo problema. Basado en este paper, creé una implementación de referencia de Matlab, que me gustaría compartir en GitHub.

1

Hago esto bastante a menudo para generar imágenes de fibras y esferas para simulaciones de medios porosos. Tengo una forma muy simple de hacer esto utilizando una técnica de análisis de imágenes muy estándar conocida como la "transformación a distancia". Esto requiere tener acceso a un paquete de análisis de imagen. Yo uso Python con Scipy, así que esto no es problema. He aquí una demostración para convertir puntos distribuidos aleatoriamente en dos esferas:

import scipy as sp 
import scipy.ndimage as spim 

im1 = sp.rand(100, 100) < 0.995 # Create random points in space 
dt = spim.distance_transform_edt(im1) 
im2 = dt < 5 # To create sphere with a radius of 5 

random seeds, distance map, final spheres

Y eso es todo! La transformación de distancia puede ser lenta para imágenes muy grandes, pero hay una versión eficiente por ahí. Por ejemplo, ImageJ tiene una paralelizada. Obviamente, para crear fibras gruesas, solo crea su imagen de delgadas, luego aplique los pasos 2 y 3 anteriores.

Cuestiones relacionadas