Corte/recorte de polígonos arbitrarios (con implementación de código C#)

Original: https://www.cnblogs.com/lsxqw2004/p/4843417.html 

Esta implementación se refiere principalmente al artículo "Un algoritmo de recorte de polígono efectivo" (Liu Yongkui, Gao Yun, Huang Youqun) publicado en el "Journal of Software" en 2003. La mayoría de las teorías y algoritmos utilizados se basan en este artículo. detalle, y extrajo algunas teorías importantes en el documento para resumir. Además, también se realiza un análisis tentativo de algunas situaciones que no pueden ser tratadas en la descripción del artículo.

El recorte de polígono se utiliza para recortar la parte del polígono recortado (también llamado polígono sólido, representado por S en adelante) fuera de la ventana (también llamado polígono de recorte, representado por C en adelante). El polígono de resultado recortado se compone de los límites del polígono sólido que se encuentran dentro del polígono de recorte y los límites del polígono de recorte que se encuentran dentro del polígono sólido. Vea abajo

Figura 1

Esta figura se usará más adelante para describir el proceso de recorte. Esta figura es más representativa que la figura dada en el documento original, principalmente debido a C1. En la figura del documento, el polígono de corte no tiene puntos en el polígono sólido. Por lo tanto, las personas que lo leen por primera vez tienden a pensar erróneamente que la lista de polígonos de corte y puntos de intersección no tiene ningún efecto.

Como se muestra en la figura, el polígono de corte y el polígono sólido son respectivamente C1C2C3C4 y S1S2S3S4S5 (dado que los dos polígonos deben estar en la misma dirección, el polígono de corte debe invertirse a C1C4C3C2. Esta inversión también es condicional, y la primera punto de intersección debe permanecer sin cambios. Consulte la implementación del código para obtener más detalles), los resultados que queremos obtener son los dos polígonos de resultado de C1-I6->I3-I4->I5(-C1) y S4->I1-I2( ->S4) (donde "- "Indica el borde del polígono de corte en el polígono sólido, use "->" para indicar el borde del polígono sólido en el polígono de corte)

La implementación del algoritmo se divide en tres etapas:

Etapa 1: Calcule el primer punto de intersección y juzgue si los dos polígonos están en la misma dirección según el acceso mutuo de los dos polígonos en el primer punto de intersección. Si no están en la misma dirección, invierta el polígono de corte para hacer los dos polígonos tienen la misma dirección.

Fase 2: use cada lado del polígono sólido para cortar el polígono de recorte a su vez e inserte los puntos de intersección en las listas vinculadas de los dos polígonos en el orden correcto.

Fase 3: recorrer la lista de intersecciones para obtener el resultado final.

El siguiente artículo seguirá estos 3 pasos para presentar los puntos de implementación relevantes y los métodos principales en el código. Finalmente, al final del artículo se mencionan algunos casos especiales.

Nivel 1

En la etapa 1, es necesario juzgar si los dos polígonos están en la misma dirección (la importancia de la misma dirección se describe más adelante), y uno de los puntos más importantes es encontrar el punto de intersección del corte. el punto de intersección también es muy importante en la segunda etapa Aquí presentamos el La segunda etapa de intersección ya no se presentará.

(Nota: hay muchas maneras de juzgar si un polígono es en el sentido de las agujas del reloj o en el sentido contrario. El código C# de este artículo utilizará el método del documento original)

El método descrito en el documento original se basa en un teorema:

Teorema 1: si los lados de dos polígonos que se intersecan tienen la misma orientación (tanto en sentido horario como antihorario), entonces el punto de intersección que es un punto de entrada para un polígono debe ser un punto de salida para el otro polígono.

Si las direcciones de los dos polígonos son iguales, después de encontrar un punto de entrada o salida para uno de los polígonos, se determinará la entrada y salida del otro polígono. De esta forma, solo es necesario marcar la entrada y salida de uno de los polígonos al otro polígono al encontrar el punto de intersección. La entrada y la salida vistas desde el punto de vista de otro polígono están invertidas.

Para juzgar si dos polígonos están en la misma dirección, es necesario juzgar la entrada y la salida del punto de intersección. Si la entrada y la salida del mismo punto de intersección son diferentes para dos polígonos, entonces los dos polígonos están en la misma dirección; de lo contrario, los dos polígonos tienen direcciones opuestas.

Juicio de la entrada-salida del punto de intersección (excluyendo el caso en que la tangente interseca al polígono en el vértice o coincide con un lado del polígono).

Cuando una línea recta corta un polígono, el primer punto que corta el polígono debe ser el punto de entrada, y el segundo punto debe ser el punto de salida, para que el punto de entrada y el punto de salida aparezcan cíclicamente.

Cuando el borde de un polígono corta a otro polígono, las propiedades de entrada y salida de todos los puntos de intersección del polígono también se alternan.

Del análisis anterior, se puede ver que una operación muy crítica en todo el proceso de implementación es encontrar el punto de intersección del segmento de línea (el punto de intersección entre la línea y el polígono en la etapa 1) y el polígono (es decir, cortar el polígono con una línea). Y en este proceso, se juzga si el punto de intersección está dentro o fuera del polígono de corte.

punto de intersección

Aquí hay una introducción al método de punto de intersección llamado método de cambio escalonado que se proporciona en el documento original. Este método se basa en el hecho de que el valor x del punto de intersección permanece sin cambios después de la transformación escalonada de dos líneas rectas, y el proceso de encontrar el punto de intersección se simplifica al cambiar la línea tangente a una línea recta con una pendiente de 0 . Supongamos que el segmento de línea de corte es (x1, y1), (x2, y2), y el polígono a cortar está compuesto por v1, v2 ... vn. El proceso de encontrar el punto de intersección se puede describir de la siguiente manera:

1. Encuentre la pendiente d, d es la pendiente de la línea tangente . Después de cambiar (x1, y1) y (x2, y2) de acuerdo con esta pendiente, se puede obtener una línea recta horizontal. Establecemos esta línea recta horizontal como y =yc, entonces es el siguiente conjunto de ecuaciones:

disponible, hay

Explique que el documento original decía que aquí se requiere x1<x2 (vea la implementación del código para saber que x1=x2 x1>x2 se tratan de manera especial). x1≠x2 y y1≠y2 también deben cumplirse. Cuando x1=x2, significa que la línea tangente es una línea vertical y debe cortarse en la dirección vertical. Primero, encuentre la coordenada y del punto de intersección. Si y1=y2, no hay necesidad de un proceso de corte incorrecto, solo use el corte tangente directamente. Garantizar x1<x2 es para asegurar que el orden en el que se insertan los puntos de intersección en el polígono sólido es correcto.

La siguiente figura es un ejemplo, la línea negra es la línea anterior al corte incorrecto, la línea verde es la línea posterior al corte incorrecto, la línea sólida es la línea tangente y la línea discontinua es una línea de corte en el polígono:

Figura 2

2. La coordenada y de cada punto vi(xi, yi) en el polígono se escalona y cambia según la fórmula para obtener yi':

3. Calcular la coordenada x Ixj del punto de intersección de cada lado ((xi,yi'),(xi+1,yi+1')) y la tangente del polígono después del corte erróneo:

Por ejemplo, para el ejemplo de la figura anterior, inserte xi, xi+1 para obtener Ix=6.8.

Entonces, Iy se puede obtener por corte incorrecto, la fórmula:

entonces

Después del cálculo, Iy=4,6. Imagen final del sistema de coordenadas:

 imagen 3

Un problema que requiere especial atención en el procesamiento de corte de polígonos es el caso en el que un vértice de un polígono cae sobre un borde de otro polígono. Esta situación afectará el juicio de la entrada y salida del punto de intersección, lo que puede conducir a resultados erróneos. Este caso donde los puntos coinciden con los bordes se puede manejar en el proceso anterior de cortar polígonos con líneas. Hablamos de varias situaciones:

1. El punto de la recta tangente cae en un lado del polígono

La siguiente figura puede ilustrar bien esta situación:

Figura 4

Esto sucede cuando por cálculo encontramos que el valor x Ix de la coordenada del punto de intersección es igual al valor máximo o mínimo de la coordenada x de la tangente.

Este es el caso de los siguientes dos conjuntos de polígonos:

Figura 5

Si no se considera el segundo caso, sólo falta contar los puntos donde el valor x Ix de estas coordenadas es igual al valor máximo o mínimo de la coordenada x de la recta tangente en el punto de intersección (el punto de intersección real y el punto de intersección virtual no se cuentan en este caso), es decir, este caso se puede ignorar, es decir, suponemos que el punto es disjunto. Pero este tipo de procesamiento no es aplicable al segundo caso.Necesitamos conocer la entrada y la salida de esta intersección especial para decidir si continuar a lo largo del polígono sólido o el polígono de corte después de encontrar esta intersección.

Para este caso, simplemente tome el punto de intersección donde el valor x Ix de la coordenada es igual al valor máximo o mínimo de la coordenada x de la línea tangente como el punto real de intersección.

2. Un punto del lado de un polígono cae en una tangente

Como se muestra abajo:

Figura 6

Después del cálculo del error de corte, si el valor y de la coordenada del punto del polígono de error de corte es igual a yc, significa que se ha producido esta situación. Dado que el borde del polígono cortado no necesita calcular la entrada-salida del punto de intersección (por supuesto, es una excepción cuando se juzga la dirección de dos polígonos, es decir, cuando se juzga la dirección de dos polígonos, usted necesita elegir un punto de intersección generalizado), simplemente ignore el borde de este polígono, no los tome para calcular la intersección con la tangente (en pocas palabras, esta intersección puede ignorarse).

El método anterior se puede utilizar correctamente como se muestra en la siguiente figura:

Figura 7

3. El caso de bordes superpuestos

El procesamiento de los dos casos anteriores es una solución aceptable porque no hemos cambiado las coordenadas de los nodos del polígono, por lo que no tendrá un impacto erróneo en el resultado final. Para el caso en que dos polígonos tienen bordes coincidentes (excepto por la coincidencia de dos nodos de polígono, esta cuestión se discutirá más adelante), como dos polígonos (donde el polígono sólido es una línea sólida y la línea punteada es un polígono de corte, lo mismo que el ejemplo a continuación), a través de lo anterior El método se puede manejar correctamente, y no hay necesidad de juzgar específicamente la coincidencia, podemos usarlo como un caso especial del caso 2:

Figura 8

4. Cuando las situaciones 1 y 2 mencionadas anteriormente ocurren al mismo tiempo, es la situación que los nodos del polígono se superponen, lo que se refleja en el sistema de coordenadas de la siguiente manera:

Figura 9

Mira 2 ejemplos típicos de esta situación:

Figura 10

Para el método de procesamiento de esta situación, después del cálculo del corte erróneo, el valor y de la coordenada del punto del polígono erróneo es igual a yc, y el valor x de la coordenada del punto de intersección es exactamente igual a la coordenada x de el punto final de la tangente, es decir, la aparición actual En la situación anterior, seleccionamos un punto en el lado del polígono como punto de intersección y otro punto como punto sin intersección. O usa el ejemplo anterior para resolver

Si se usa I1I2 para cortar los lados C1C2, C2C3, ... del polígono en secuencia, suponemos que si el segundo punto en el lado del polígono (para C1C2 y C2C3, los segundos puntos son C2 y C3 respectivamente, y el efecto de usar el primer punto es el mismo) aparece En el caso 4, creemos que hay un punto de intersección real. Incluso si I1I2 se usa para cortar C1C2, creemos que hay un punto de intersección, y cuando I1I2 se usa para cortar C2C3, no tienen un punto de intersección de acuerdo con las reglas anteriores. De esta manera la situación se maneja correctamente.

Como resultado, la introducción anterior puede manejar todo tipo de cálculos de intersección, y también es fácil marcar las propiedades de entrada y salida en el orden de par e impar. Veamos la segunda etapa.

Etapa 2

El método introducido anteriormente ya puede obtener la entrada y salida del punto de intersección y el punto de intersección. Y juzga la dirección de los dos polígonos según la entrada y la salida. Para direcciones diferentes, uno de los polígonos debe invertirse. El siguiente es otro punto clave de todo el algoritmo, el proceso de cortar y recortar el polígono con el borde del polígono sólido e insertar el punto de intersección en la lista enlazada de los dos polígonos.

1. Primero conecte el polígono de corte y el polígono sólido en las siguientes dos listas enlazadas (la dirección se ha procesado en la sección anterior).

Figura 11

2. Use cada lado del polígono sólido S para cortar el polígono de corte C a su vez, e inserte el punto de intersección en la lista vinculada del polígono sólido S y el polígono de corte C a su vez, y marque la entrada y salida de la intersección polígono sólido para el polígono de corte al mismo tiempo durante este proceso.

Al comienzo de la ejecución, primero use S1S5 para cortar y cortar el polígono C, se generarán dos puntos de intersección I6 e I3, y la relación posicional de las coordenadas x de estos puntos S1, I6, I3 y S5 se puede usar como la secuencia para la inserción. De esta forma, cada arista en S se usa para cortar C e insertarla en la lista enlazada, y finalmente se obtiene el siguiente modelo:

Figura 12

En la siguiente figura se pueden ver mejor las respectivas situaciones de cada lista enlazada (nótese que la entrada y salida de polígonos con puntos de intersección relativamente diferentes son opuestas, que también es la principal característica de los polígonos en la misma dirección):

Figura 13

De esta manera, después de completar la inserción del punto de corte e intersección, puede ingresar a la etapa 3.

etapa 3

Después de tener las dos listas vinculadas anteriores, recorra las listas vinculadas de polígonos sólidos y de polígonos de recorte para obtener la lista vinculada de polígonos de salida. El proceso transversal comienza con un punto de intersección donde el polígono sólido ingresa al polígono de recorte (requiere que solía ser 0). La primera intersección de este tipo en la figura anterior es I6.

Todos los puntos en el polígono de la entidad entre el punto de entrada y el siguiente punto de intersección en la lista vinculada del polígono de la entidad (debe ser el punto de salida, ya que este punto en la figura es I3) se utilizan como el polígono de resultado (indicado por el rojo flecha en la figura a continuación) Un proceso de generación de resultados, es decir, a lo largo de la dirección NextS de I6, correspondiente a la dirección indicada por la flecha del triángulo sólido en la figura). Además, cada vez que se atraviesa un nodo o intersección, su uso se establece en 1, lo mismo a continuación.

Dado que I3 es el punto de salida del polígono sólido para la línea del polígono de recorte, el siguiente proceso no puede continuar a lo largo del borde del polígono sólido y debe girar en la dirección del polígono de recorte (es decir, a lo largo de la dirección Next2 de I3 , correspondiente a la dirección de la flecha del triángulo en la figura) para continuar Buscar puntos hasta encontrar el siguiente punto de intersección (este punto de intersección debe ser el punto de entrada del polígono sólido para el polígono de recorte). (Se menciona en el documento que la dirección NextC de I3 es el punto de entrada del polígono de recorte en relación con el polígono sólido, así que continúe a lo largo de la dirección NextC. Es precisamente porque los dos polígonos están en la misma dirección. Debido al teorema 1, se debe garantizar que la dirección NextC de I3 es el punto de entrada del polígono de recorte en relación con el polígono sólido, por lo que esta es la importancia de que los dos polígonos estén en la misma dirección ). Agregue estos puntos secuencialmente al polígono de salida.

Repita este proceso (es decir, camine a lo largo de NextS cuando encuentre el punto de entrada y siga a NextC cuando encuentre el punto de salida. Además, tenga en cuenta que cuando encuentre Vertex (ya sea un polígono sólido o un polígono recortado) durante este proceso, debe estar a lo largo de Su siguiente) hasta que encuentre el punto de intersección donde comienza el polígono de resultado actual (para este ejemplo, I6). Esto genera un polígono resultante.

Una vez que se completa el procesamiento anterior, si todavía hay un punto de intersección donde se utiliza es 0 en la lista vinculada de polígonos de entidad, significa que hay otros polígonos de resultado. Encuentre el primer punto de entrada donde se usa es 0 y luego continúe con el proceso descrito anteriormente para obtener los polígonos de resultado restantes. Hasta que todo usado es 1 para terminar.

La siguiente figura muestra el proceso de unión de puntos:

Figura 14

Otra version:

Figura 15

De esta manera, se puede obtener el resultado.

El algoritmo implementado en el documento de referencia de este artículo admite polígonos generales, incluidos, entre otros, polígonos cóncavos, y también puede encontrar la unión o la diferencia de polígonos después de una modificación simple, lo que tiene una cierta gama de aplicaciones. En el proceso de salida de polígonos, cuando encuentre el punto de entrada (el polígono sólido es relativo al polígono de corte), siga la dirección del polígono de corte (es decir, la dirección NextC del punto de intersección), y cuando encuentre el punto de salida (el polígono sólido es relativo al polígono de corte) a lo largo de La dirección del polígono sólido (es decir, la dirección NextS del punto de intersección) puede obtener la "unión" del polígono.

Para obtener la "diferencia" de polígonos, solo necesita hacer que las direcciones de los dos polígonos sean opuestas al comienzo de la etapa 2 (es decir, la dirección de los polígonos es la misma para la intersección, y se requiere la dirección opuesta para la diferencia).

Para el corte de polígonos con agujeros, se da una idea en el documento, pero la implementación específica es demasiado complicada, por lo que no se implementa aquí. Los zapatos de los niños interesados ​​​​deben estudiarse por sí mismos. El principio básico es mantener la dirección del lado exterior del polígono vacío consistente con el polígono de corte, y la dirección del lado interior y el lado exterior son opuestas, de modo que cuando el lado interior se corta con el polígono de corte, el entrada-salida de la intersección se puede mantener correcta.

Dado que el algoritmo utilizado en este artículo necesita atravesar la lista de polígonos desde el punto de intersección, se requiere que el polígono de corte y el polígono sólido tengan un punto de intersección real (diferente del punto de enfoque virtual, es decir, el punto de intersección entre la línea de extensión de un lado del polígono y otro polígono).

Los siguientes dos polígonos adicionales que en realidad no se intersecan se analizan a continuación.

Primero mire algunos conjuntos de ejemplos:

Figura 16

Bajo el procesamiento de este caso, todavía se pueden procesar polígonos cóncavos, que no serán compatibles con los siguientes esquemas. En las situaciones anteriores, (a) después del corte, el polígono resultante es el polígono de corte. En el caso de (b), el resultado después del corte es un polígono sólido. Para (c), el resultado está vacío.

Después del proceso anterior de usar el corte del borde del polígono sólido para cortar la línea del polígono, si el número de puntos de intersección es 0, significa que se ha producido la situación anterior. Los siguientes métodos se pueden utilizar para distinguir estas tres situaciones:

Toma un punto en el polígono cortante, si está dentro del polígono sólido es el caso (a)

Tome un punto en el polígono sólido, si está dentro del polígono de corte es el caso (b)

Si ninguno de los dos casos anteriores es cierto, es el caso (c)

Este tipo de juicio es adecuado para el siguiente anidamiento, pero también es aplicable al caso donde los bordes se superponen al mismo tiempo (es necesario juzgar, si el punto está en el borde del polígono, también se cuenta como dentro del polígono. Al mismo tiempo, también depende de si los dos polígonos se superponen. Solo piensa que no tienen punto de intersección):

Figura 17

Un problema involucrado aquí es, si un polígono anida a otro polígono, cómo juzgar la anidación. Porque cuando un polígono está anidado en otro polígono, cualquier vértice del polígono también está dentro del otro polígono. Entonces, la pregunta de si un polígono contiene otro polígono puede convertirse en una pregunta de si un polígono contiene un punto.

Para el problema de si un punto está en un polígono, existe un método clásico que se puede utilizar para juzgar.Hacer un rayo desde este punto en cualquier dirección (en la implementación del código, hacer un rayo horizontal a la derecha, que es el más fácil de implementar), si hay un número impar de rayos y polígonos Los puntos de intersección indican que el punto está dentro del polígono; de lo contrario (sin incluir la intersección) indican que el punto está fuera del polígono. Vea la sección de implementación de código para más detalles.

Código:

Use el método descrito en el documento original para juzgar si dos polimorfos están en la misma dirección y obtenga el primer punto de intersección que debe completarse al mismo tiempo, pero no puedo escribir un código que pueda formar una lista enlazada y juzgar el en -fuera/dirección del polígono a través de un corte. Por lo tanto, en la siguiente implementación, el corte de la dirección de juicio se separa del corte de la lista enlazada (es decir, el primer punto de intersección se calcula dos veces en la fase 1 y la fase 2, y la primera vez solo se usa para juzgar la dirección). 

La estructura de datos utilizada por el algoritmo.

En este algoritmo, se utilizan dos listas enlazadas para representar el polígono de corte y el polígono sólido respectivamente.

La estructura de datos del nodo/intersección:

public abstract class VertexBase
{
    public double X { get; set; }
    public double Y { get; set; }
 
    public string Name { get; set; }
 
    public void SetXY(double x, double y)
    {
        X = x;
        Y = y;
    }
 
    public Point ToPoint()
    {
        return new Point(X,Y);
    }
}
 
public class Vertex : VertexBase
{
    [DebuggerNonUserCode]
    public Vertex(double x, double y)
    {
        X = x;
        Y = y;
    }
 
    public VertexBase Next { get; set; }
}
 
public class Intersection : VertexBase
{
    [DebuggerNonUserCode]
    public Intersection(double x, double y)
    {
        X = x;
        Y = y;
    }
 
    public CrossInOut CrossDi { get; set; }
    public bool Used { get; set; }
    public VertexBase NextS { get; set; }
    public VertexBase NextC { get; set; }
}

Next in Vertex se usa para representar la referencia del siguiente nodo o intersección (puede ser Vertex o Intersection) NextS y NextC in Intersection almacenan la referencia del siguiente nodo o intersección en la lista enlazada S y la lista enlazada C respectivamente. Utilizado en Intersección registra si el punto de intersección se emite durante el proceso de registro del resultado de salida, y CrossDi indica la entrada y salida del polígono S en relación con el polígono C en la intersección.

El archivo ArbitraryPolygonCut.cs en la solución contiene el código del núcleo del algoritmo (el código no está en la lista por razones de espacio, hay un enlace de Github en la parte posterior, descárguelo y compruébelo usted mismo), entre los cuales

List<List<VertexBase>> Cut(List<VertexBase> listS, List<VertexBase> listC)

Es la principal función de entrada del algoritmo, y los comentarios dentro incluyen la identificación de varias etapas.La función más importante para cortar y juzgar la dirección de la etapa uno es:

Tuple<CrossInOut, int, bool> CutByLineForCrossDi(VertexBase v1, VertexBase v2, List<VertexBase> list, bool withIdx, int line2Idx = 0)

La función de cortar y unir nodos en la etapa 2 es:

List<Intersection> CutByLine(Vertex s1, Vertex s2, LinkedList<VertexBase> linkC)

Las 2 funciones de corte anteriores también tienen 2 funciones que terminan en Vertical, que se utilizan para tratar el caso en el que la tangente es vertical.

Para el caso completamente disjunto, utilice

List<VertexBase> ProcessNoCross(List<VertexBase> listS, List<VertexBase> listC)

función a manejar, que llama

bool IsVertexInPolygon(VertexBase v, List<VertexBase> list)

Para juzgar si el punto está dentro del polígono.

El código viene con un programa de prueba, que viene con un patrón de prueba parcialmente guardado (archivo que termina en .cut), que contiene varios casos cubiertos en el documento. También puede crear sus propios gráficos a través del programa para probar.

Por último pon una foto:

Descarga de código:

Github

El código se puede usar en cualquier proyecto, pero la prueba de esta implementación no es perfecta, pruébelo nuevamente para escenarios de producción. Por favor cite la fuente si el documento es para publicación.

Vuelva a imprimir este artículo, por favor mantenga el enlace .

Supongo que te gusta

Origin blog.csdn.net/tangyin025/article/details/123434644
Recomendado
Clasificación