Arbitrary polygon cutting/cropping (with C# code implementation)

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

This implementation mainly refers to the paper "An Effective Polygon Clipping Algorithm" (Liu Yongkui, Gao Yun, Huang Youqun) published in the "Journal of Software" in 2003. Most of the theories and algorithms used are based on this paper. Explained in detail, and extracted some important theories in the paper to summarize. In addition, a tentative analysis is also carried out for some situations that cannot be dealt with in the paper description.

The polygon clipping is used to clip the part of the clipped polygon (also called solid polygon, represented by S hereinafter) outside the window (also called clipping polygon, represented by C hereinafter). The clipped result polygon is composed of the boundaries of the solid polygon that lie inside the clip polygon and the boundaries of the clip polygon that lie inside the solid polygon. See below

figure 1

This figure will be used later to describe the clipping process. This figure is more representative than the figure given in the original paper, mainly because of C1. In the figure in the paper, the cutting polygon does not have points in the solid polygon. Therefore, people who read it for the first time tend to mistakenly think that the list of cutting polygons and intersection points has no effect.

As shown in the figure, the cutting polygon and the solid polygon are respectively C1C2C3C4 and S1S2S3S4S5 (since the two polygons need to be in the same direction, the cutting polygon needs to be reversed to C1C4C3C2. This inversion is also conditional, and the first intersection point needs to remain unchanged. See the code implementation for details), the results we want to get are the two result polygons of C1-I6->I3-I4->I5(-C1) and S4->I1-I2(->S4) (where "- "Indicates the edge of the cutting polygon in the solid polygon, use "->" to indicate the edge of the solid polygon in the cutting polygon)

The implementation of the algorithm is divided into three stages:

Stage 1: Calculate the first intersection point, and judge whether the two polygons are in the same direction based on the mutual access of the two polygons at the first intersection point. If they are not in the same direction, reverse the cutting polygon to make the two polygons have the same direction.

Phase 2: Use each side of the solid polygon to cut the clipping polygon in turn and insert the intersection points into the linked lists of the two polygons in the correct order.

Phase 3: Traverse the intersection list to obtain the final result.

The following article will follow these 3 steps to introduce the relevant implementation points and the main methods in the code. Finally, some special cases are mentioned at the end of the article.

stage 1

In stage 1, it is necessary to judge whether the two polygons are in the same direction (the importance of the same direction is described later), and one of the most important points is to find the intersection point of the cut. Of course, the intersection point is also very important in the second stage. Here we introduce the The second stage of intersection will not be introduced anymore.

(Note: There are many ways to judge whether a polygon is clockwise or counterclockwise. The C# code in this article will use the method in the original paper)

The method described in the original paper is based on a theorem:

Theorem 1: If the sides of two intersecting polygons have the same orientation (both clockwise or counterclockwise), then the intersection point that is an entry point for one polygon must be an exit point for the other polygon.

If the directions of the two polygons are the same, after finding an entry or exit point for one of the polygons, the entry and exit of the other polygon will be determined. In this way, it is only necessary to mark the entry and exit of one of the polygons to the other polygon when finding the intersection point. The ingress and egress seen from another polygon's point of view are reversed.

To judge whether two polygons are in the same direction, it is necessary to judge the entry and exit of the intersection point. If the entry and exit of the same intersection point are different for two polygons, then the two polygons are in the same direction; otherwise, the two polygons have opposite directions.

Judgment of the in-out of the intersection point (excluding the case where the tangent intersects the polygon at the vertex or coincides with a side of the polygon.)

When a straight line cuts a polygon, the first point that intersects the polygon must be the entry point, and the second point must be the exit point, so that the entry point and the exit point appear cyclically.

When the edge of one polygon cuts another polygon, the in and out properties of all intersection points on the polygon also alternate.

From the above analysis, it can be seen that a very critical operation in the whole implementation process is to find the intersection point of the line segment (the intersection point between the line and the polygon in stage 1) and the polygon (that is, cut the polygon with a line). And in this process, it is judged whether the intersection point is in or out of the cut polygon.

intersection point

Here is an introduction to the intersection point method called the staggered change method given in the original paper. This method is based on the fact that the x value of the intersection point remains unchanged after the staggered transformation of two straight lines, and the process of finding the intersection point is simplified by changing the tangent line to a straight line with a slope of 0. Suppose the cutting line segment is (x1, y1), (x2, y2), and the polygon to be cut is composed of v1, v2 ... vn. The process of finding the intersection point can be described as follows:

1. Find the slope d, d is the slope of the tangent line . After changing (x1, y1) and (x2, y2) according to this slope, a horizontal straight line can be obtained. We set this horizontal straight line as y=yc, then it is as follows equation set:

available, there is

Explain that the original paper said that x1<x2 is required here (see the code implementation to know that x1=x2 x1>x2 are treated specially). x1≠x2 and y1≠y2 also need to be satisfied. When x1=x2, it means that the tangent line is a vertical line and should be cut in the vertical direction. First, find the coordinate y of the intersection point. If y1=y2, there is no need for wrong cutting process, just use tangent cutting directly. Guaranteeing x1<x2 is to ensure that the order in which the intersection points are inserted into the solid polygon is correct.

The following figure is an example, the black line is the line before the miscut, the green line is the line after the miscut, the solid line is the tangent line, and the dashed line is a cut line on the polygon:

figure 2

2. The y coordinate of each point vi(xi, yi) on the polygon is staggered and changed according to the formula to get yi':

3. Calculate the x-coordinate Ixj of the intersection point of each side ((xi,yi'),(xi+1,yi+1')) and the tangent of the polygon after the miscutting:

For example, for the example in the previous figure, insert xi, xi+1 to get Ix=6.8.

Then, Iy can be obtained by anti-wrong cutting, the formula:

so

After calculation, Iy=4.6. Final coordinate system image:

 image 3

One problem that needs special attention in polygon cutting processing is the case where a vertex of one polygon falls on an edge of another polygon. This situation will affect the judgment of the entry and exit of the intersection point, which may lead to erroneous results. This case where points coincide with edges can be handled in the above process of cutting polygons with lines. We discuss several situations:

1. The point of the tangent line falls on one side of the polygon

The following figure can well illustrate this situation:

Figure 4

This happens when, by calculation, we find that the x-value Ix of the coordinate of the intersection point is equal to the maximum or minimum value of the coordinate x of the tangent.

This is the case for the following two sets of polygons:

Figure 5

If the second case is not considered, we only need to count the points where the x value Ix of these coordinates is equal to the maximum or minimum value of the coordinate x of the tangent line in the intersection point (the actual intersection point and the virtual intersection point are not counted in this case) that is This case can be ignored, that is, we assume that point is disjoint. But this kind of processing is not applicable to the second case. We need to know the in and out of this special intersection to decide whether to continue along the solid polygon or the cutting polygon after encountering this intersection.

For this case, just take the point of intersection where the x-value Ix of the coordinate is equal to the maximum or minimum value of the coordinate x of the tangent line as the actual point of intersection.

2. A point of a polygon side falls on a tangent

As shown below:

Figure 6

After the miscut calculation, if the y value of the coordinate of the point of the miscut polygon is equal to yc, it means that this situation has occurred. Since the edge of the cut polygon does not need to calculate the in-out of the intersection point (of course, it is an exception when judging the direction of two polygons, that is to say, when judging the direction of two polygons, you need to choose a generalized intersection point), just ignore the edge of this polygon , do not take them to calculate the intersection with the tangent (simply put, this intersection can be ignored).

The above method can be used correctly as shown in the figure below:

Figure 7

3. The case of overlapping edges

The processing of the above two cases is an acceptable solution because we have not changed the coordinates of the polygon nodes, so it will not have a wrong impact on the final result. For the case where two polygons have coincident edges (except for the coincidence of two polygon nodes, this question will be discussed later), such as two polygons (where the solid polygon is a solid line, and the dotted line is a cutting polygon, the same as the example below), through the above The method can be handled correctly, and there is no need to specifically judge the coincidence, we can use it as a special case of case 2:

Figure 8

4. When the situations 1 and 2 mentioned above happen at the same time, it is the situation that the polygon nodes overlap, which is reflected in the coordinate system as follows:

Figure 9

Look at 2 typical examples of this situation:

Figure 10

For the processing method of this situation, after the miscut calculation, the y value of the coordinate of the point of the miscut polygon is equal to yc, and the x value of the coordinate of the intersection point is exactly equal to the x coordinate of the tangent end point, that is, the current occurrence In the above situation, we pick a point on the side of the polygon as an intersection point, and another point as no intersection point. Or use the example above to solve

If I1I2 is used to cut the sides C1C2, C2C3, ... of the polygon in sequence, we assume that if the second point on the side of the polygon (for C1C2 and C2C3, the second points are C2 and C3 respectively, and the effect of using the first point is the same) appears In case 4, we believe that there is an actual intersection point. Even if I1I2 is used to cut C1C2, we think there is an intersection point, and when I1I2 is used to cut C2C3, they do not have an intersection point according to the above rules. This way the situation is handled correctly.

As a result, the above introduction can handle all kinds of intersection calculations, and it is also easy to mark the in and out properties in the order of even and odd. Let's look at the second stage.

stage 2

The method introduced above can already obtain the in-out property of the intersection point and the intersection point. And judge the direction of the two polygons according to the in and out. For different directions, one of the polygons needs to be reversed. The following is another key point of the whole algorithm, the process of cutting and clipping the polygon with the edge of the solid polygon and inserting the intersection point into the linked list of the two polygons.

1. First connect the cutting polygon and the solid polygon into the following two linked lists (the direction has been processed in the previous section).

Figure 11

2. Use each side of the solid polygon S to cut the cutting polygon C in turn, and insert the intersection point into the linked list of the solid polygon S and the cutting polygon C in turn, and mark the entry and exit of the intersection solid polygon for the cutting polygon at the same time during this process.

At the beginning of execution, first use S1S5 to cut and cut polygon C, two intersection points I6 and I3 will be generated, and the positional relationship of the x coordinates of these points S1, I6, I3 and S5 can be used as the sequence for insertion. In this way, each edge in S is used to cut C and insert into the linked list, and finally the following model is obtained:

Figure 12

The following figure can better see the respective situations of each linked list (note that the entry and exit of polygons with relatively different intersection points are opposite, which is also the main feature of polygons in the same direction):

Figure 13

In this way, after the cutting and intersection point insertion is completed, you can enter stage 3.

stage 3

After having the above two linked lists, traverse the solid polygon and clipping polygon linked lists to obtain the output polygon linked list. The traversal process starts with such an intersection point where the solid polygon enters the clipping polygon (requires used to be 0). The first such intersection in the figure above is I6.

All the points on the entity polygon between the entry point and the next intersection point in the entity polygon linked list (must be the exit point, for this point in the figure is I3) are all used as the result polygon (indicated by the red arrow in the figure below) A process of generating results, that is, along the NextS direction of I6, corresponding to the direction indicated by the solid triangle arrow in the figure). In addition, every time a node or intersection is traversed, its used is set to 1, the same below.

Since I3 is the exit point of the solid polygon for the clipping polygon line, the following process cannot continue along the edge of the solid polygon, and needs to turn to the direction of the clipping polygon (that is, along the Next2 direction of I3, corresponding to the direction of the triangle arrow in the figure) to continue Find points until the next intersection point is encountered (this intersection point must be the entry point of the solid polygon for the clipping polygon). (It is mentioned in the paper that the NextC direction of I3 is the entry point of the clipping polygon relative to the solid polygon, so continue along the NextC direction. It is precisely because the two polygons are in the same direction. Due to Theorem 1, the NextC direction of I3 must be guaranteed is the entry point of the clipping polygon relative to the solid polygon, so this is the importance of the two polygons being in the same direction .) Add these points sequentially to the output polygon.

Repeat this process (that is, walk along NextS when encountering the entry point, and follow NextC when encountering the exit point. In addition, note that when encountering Vertex (whether it is a solid polygon or a clipping polygon) during this process, it must be along the Its Next) until it encounters the intersection point where the current result polygon starts (for this example, I6). This outputs a resulting polygon.

After the above processing is completed, if there is still an intersection point where used is 0 in the linked list of entity polygons, it means that there are other result polygons. Find the first inpoint where used is 0, and then continue the process described above to get the remaining result polygons. Until all used is 1 to end.

The following figure shows the process of joining points:

Figure 14

Another version:

Figure 15

In this way, the result can be obtained.

The algorithm implemented in this article’s reference paper supports general polygons, including but not limited to concave polygons, and can also find the union or difference of polygons after simple modification, which has a certain range of applications. In the process of outputting polygons, when encountering the entry point (the solid polygon is relative to the cutting polygon), follow the direction of the cutting polygon (that is, the NextC direction of the intersection point), and when encountering the exit point (the solid polygon is relative to the cutting polygon) along The direction of the solid polygon (that is, the NextS direction of the intersection point) can get the "union" of the polygon.

To get the "difference" of polygons, you only need to make the directions of the two polygons opposite at the beginning of stage 2 (that is, the direction of the polygons is the same for the intersection, and the opposite direction is required for the difference).

For the cutting of polygons with holes, an idea is given in the paper, but the specific implementation is too complicated, so it is not implemented here. Interested children's shoes should be studied by themselves. The basic principle is to keep the direction of the outer side of the empty polygon consistent with the cutting polygon, and the direction of the inner side and the outer side are opposite, so that when the inner side is cut with the cutting polygon, the in-out of the intersection can be kept correct.

Since the algorithm used in this article needs to traverse the polygon list from the intersection point, it is required that the cutting polygon and the solid polygon must have an actual intersection point (different from the virtual focus point, that is, the intersection point between the extension line of one polygon side and another polygon).

The next two additional polygons that do not actually intersect are discussed below.

First look at a few sets of examples:

Figure 16

Under the processing of this case, concave polygons can still be processed, which will not be supported by the following schemes. In the above situations, (a) after cutting, the resulting polygon is the cutting polygon. In the case of (b), the result after cutting is a solid polygon. For (c), the result is empty.

After the previous process of using the edge cutting of the solid polygon to cut the polygon line, if the number of intersection points is 0, it means that the above situation has occurred. The following methods can be used to distinguish these three situations:

Take a point in the cutting polygon, if it is inside the solid polygon it is case (a)

Take a point in the solid polygon, if it is inside the cutting polygon it is case (b)

If neither of the above two cases is true, it is case (c)

This kind of judgment is suitable for the following nesting, but it is also applicable to the case where the edges overlap at the same time (it needs to be judged, if the point is on the edge of the polygon, it is also counted as inside the polygon. At the same time, it also depends on if the two polygons overlap. Just think that they have no intersection point):

Figure 17

A problem involved here is, if a polygon nests another polygon, how to judge the nesting. Because when a polygon is nested in another polygon, any vertex of the polygon is also inside the other polygon. So the question of whether a polygon contains another polygon can be turned into a question of whether a polygon contains a point.

For the problem of whether a point is in a polygon, there is a classic method that can be used to judge. Make a ray from this point in any direction (in the code implementation, make a horizontal ray to the right, which is the easiest to implement), if there is an odd number of ray and polygon Intersection points indicate that the point is inside the polygon, otherwise (including no intersection) indicate that the point is outside the polygon. See the code implementation section for details.

Code:

Use the method described in the original paper to judge whether two polymorphs are in the same direction and obtain the first intersection point should be completed at the same time, but I can’t write a code that can form a linked list and judge the in-out/polygon direction through one cut. Therefore, in the following implementation, the cutting of the judgment direction is separated from the cutting of the linked list (that is, the first intersection point is calculated twice in phase 1 and phase 2, and the first time is only used for direction judgment). 

The data structure used by the algorithm

In this algorithm, two linked lists are used to represent the cutting polygon and the solid polygon respectively.

The data structure of the node/intersection:

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 is used to represent the reference of the next node or intersection (it may be Vertex or Intersection). NextS and NextC in Intersection store the reference of the next node or intersection in the S linked list and the C linked list respectively. Used in Intersection records whether the intersection point is output during the process of recording the output result, and CrossDi indicates the entry and exit of the S polygon relative to the C polygon at the intersection.

The ArbitraryPolygonCut.cs file in the solution contains the code of the core of the algorithm (the code is not listed for reasons of space, there is a Github link in the back, download and check it yourself), among which

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

It is the main entry function of the algorithm, and the comments inside include the identification of several stages. The most important function of cutting and judging the direction of stage one is:

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

The function of cutting and linking nodes in stage 2 is:

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

The above 2 cutting functions also have 2 functions ending in Vertical, which are used to deal with the case where the tangent is vertical.

For the completely disjoint case, use

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

function to handle, which calls

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

To judge whether the point is within the polygon.

The code comes with a test program, which comes with a partially saved test pattern (file ending in .cut), which contains several cases covered in the document. You can also create your own graphics through the program to test.

Finally put a picture:

Code download:

Github

The code can be used in any project, but the test of this implementation is not perfect, please test it again for production scenarios. Please cite the source if the document is for publication.

Reprint this article, please keep the link .

Guess you like

Origin blog.csdn.net/tangyin025/article/details/123434644