En geometría computacional , el polígono de visibilidad o región de visibilidad para un punto p en el plano entre obstáculos es la región poligonal posiblemente ilimitada de todos los puntos del plano visibles desde p . El polígono de visibilidad también se puede definir para la visibilidad desde un segmento o un polígono. Los polígonos de visibilidad son útiles en robótica , videojuegos y en varios problemas de optimización como el problema de la ubicación de instalaciones y el problema de la galería de arte .
Si el polígono de visibilidad está acotado, entonces es un polígono en forma de estrella . Un polígono de visibilidad está acotado si todos los rayos que salen del punto terminan en algún obstáculo. Este es el caso, por ejemplo, si los obstáculos son los bordes de un polígono simple y p está dentro del polígono. En este último caso, el polígono de visibilidad puede encontrarse en tiempo lineal . [1] [2] [3] [4]
Formalmente, podemos definir el problema del polígono de visibilidad plana como tal. Sea un conjunto de obstáculos (segmentos o polígonos) en . Sea un punto en que no está dentro de un obstáculo. Entonces, el polígono de visibilidad de puntos es el conjunto de puntos en , tales que para cada punto en , el segmento no interseca ningún obstáculo en .
De igual forma, el polígono de visibilidad de segmento o polígono de visibilidad de arista es la porción visible a cualquier punto a lo largo de un segmento de línea .
Los polígonos de visibilidad son útiles en robótica . Por ejemplo, en la localización de robots , un robot que utiliza sensores como un lidar detectará obstáculos que puede ver, lo que es similar a un polígono de visibilidad. [5]
También son útiles en los videojuegos , con numerosos tutoriales en línea que explican algoritmos simples para implementarlos. [6] [7] [8]
Se han propuesto numerosos algoritmos para calcular el polígono de visibilidad de puntos. Para distintas variantes del problema (por ejemplo, distintos tipos de obstáculos), los algoritmos varían en complejidad temporal.
Los algoritmos ingenuos son fáciles de entender e implementar, pero no son óptimos , ya que pueden ser mucho más lentos que otros algoritmos.
En la vida real, un punto luminoso ilumina la región visible para él porque emite luz en todas las direcciones. Esto se puede simular disparando rayos en muchas direcciones. Supongamos que el punto es y el conjunto de obstáculos es . Entonces, el pseudocódigo se puede expresar de la siguiente manera:
El algoritmo naive_bad_algorithm( , ) es := para : // dispara un rayo con ángulo := para cada obstáculo en : := min( , distancia desde el obstáculo en esta dirección) añadir vértice para retornar
Ahora bien, si fuera posible probar todos los ángulos infinitos, el resultado sería correcto. Lamentablemente, es imposible probar realmente cada dirección debido a las limitaciones de las computadoras. Se puede crear una aproximación lanzando muchos rayos, digamos 50, espaciados uniformemente. Sin embargo, esta no es una solución exacta, ya que dos rayos adyacentes podrían pasar por alto obstáculos pequeños por completo. Además, es muy lento, ya que uno puede tener que lanzar muchos rayos para obtener una solución aproximadamente similar, y el polígono de visibilidad de salida puede tener muchos más vértices de los necesarios.
El algoritmo descrito anteriormente se puede mejorar significativamente tanto en velocidad como en precisión si se tiene en cuenta que basta con disparar rayos a los vértices de cada obstáculo, ya que cualquier curva o esquina a lo largo del límite de un polígono de visibilidad debe deberse a alguna esquina (es decir, un vértice) de un obstáculo.
El algoritmo naive_better_algorithm( , ) es := para cada obstáculo en : para cada vértice de : // dispara un rayo desde a := distancia desde a := ángulo de con respecto a para cada obstáculo en : := min( , distancia desde a ) añadir vértice para retornar
El algoritmo anterior puede no ser correcto. Ver la discusión en Discusión.
La complejidad temporal de este algoritmo es . Esto se debe a que el algoritmo dispara un rayo a cada uno de los vértices y, para comprobar dónde termina el rayo, tiene que comprobar la intersección con cada uno de los obstáculos. Esto es suficiente para muchas aplicaciones sencillas, como los videojuegos, y como tal, muchos tutoriales en línea enseñan este método. [8] Sin embargo, como veremos más adelante, hay algoritmos más rápidos (o incluso más rápidos si el obstáculo es un polígono simple o si hay un número fijo de agujeros poligonales).
Dado un polígono simple y un punto , un algoritmo de tiempo lineal es óptimo para calcular la región en que es visible desde . Este algoritmo fue propuesto por primera vez en 1981. [2] Sin embargo, es bastante complicado. En 1983, se propuso un algoritmo conceptualmente más simple, [3] que tenía un error menor que se corrigió en 1987. [4]
El último algoritmo se explicará brevemente aquí. Simplemente recorre el límite del polígono , procesando los vértices en el orden en que aparecen, mientras mantiene una pila de vértices donde es la parte superior de la pila. La pila constituye los vértices encontrados hasta ahora que son visibles para . Si, más tarde durante la ejecución del algoritmo, se encuentran algunos vértices nuevos que oscurecen parte de , entonces los vértices oscurecidos en se extraerán de la pila. Para cuando el algoritmo finalice, consistirá en todos los vértices visibles, es decir, el polígono de visibilidad deseado. En 2014 se publicó una implementación eficiente. [9]
Para un punto de un polígono con agujeros y vértices en total, se puede demostrar que, en el peor de los casos, un algoritmo es óptimo. Dicho algoritmo se propuso en 1995 junto con su prueba de optimalidad. [10] Sin embargo, se basa en el algoritmo de triangulación de polígonos en tiempo lineal de Chazelle, que es extremadamente complejo.
Para un punto entre un conjunto de segmentos que no se intersecan excepto en sus puntos finales, se puede demostrar que, en el peor de los casos, un algoritmo es óptimo. Esto se debe a que un algoritmo de polígono de visibilidad debe generar los vértices del polígono de visibilidad en orden ordenado, por lo que el problema de la clasificación se puede reducir al cálculo de un polígono de visibilidad. [11]
Tenga en cuenta que cualquier algoritmo que calcule un polígono de visibilidad para un punto entre segmentos se puede utilizar para calcular un polígono de visibilidad para todos los demás tipos de obstáculos poligonales, ya que cualquier polígono se puede descomponer en segmentos.
En 1987 se propuso un algoritmo de divide y vencerás para calcular el polígono de visibilidad . [12]
En 1985 [13] y 1986 [11] se propuso un algoritmo de barrido angular , es decir, de barrido de plano rotacional para calcular el polígono de visibilidad . La idea es primero ordenar todos los puntos finales del segmento por ángulo polar y luego iterarlos en ese orden. Durante la iteración, la línea de eventos se mantiene como un montón . En 2014 se publicó una implementación eficiente. [9]
Para un punto entre segmentos que generalmente se intersecan, el problema del polígono de visibilidad es reducible, en tiempo lineal, al problema de la envolvente inferior . Por el argumento de Davenport-Schinzel , la envolvente inferior en el peor de los casos tiene número de vértices, donde es la función de Ackermann inversa . John Hershberger creó en 1989 un algoritmo óptimo de divide y vencerás en el peor de los casos que se ejecuta en el tiempo. [14]