简体   繁体   中英

Connected vertices from polygon, how to get the new polygons

I have a polygon, where some vertices are connected with lines (the startpoint and the endpoint are a vertice of the polygon). For each line (that connects vertices) are 4 rules:

  • The line doesn't intersect with other lines
  • The line doesn't intersect with the polygon
  • The line is completely contained by the polygon
  • The line isn't an edge of the polygon

Example: GeoGebra测试 In the image, the red lines are bad lines, the black lines are polygon edges and the green line is a good line.

  • Line h is good
  • Line i is bad, because it intersects the polygon
  • Line j is bad, because it intersects line h and i
  • Line k is bad, because it is an edge of the polygon
  • Line g is bad, because it is not contained by the polygon

I have an array with the vertices of the polygon, and an array with the lines, like this:

polygon = [
  {x: ..., y: ...},
  ...
]
lines = [
  {
    p1: {x: ..., y: ...},
    p2: {x: ..., y: ...}
  },
  ...
]

The lines array contains only valid lines.

How can I get the polygons that are sliced by the lines.

I want something like this:

function getSlicedPolygons(polygon, lines){
  // No check for valid lines, they are already valid
  // Do the algorithm...
}

What I've tried so far

I start on the first vertex and go next until I reach a connected vertex. From that vertex, I go to the vertex that's connected at the other ending of the line. Now I go next until another vertex that's connected and so on until I reached the vertex where I started. Now I have the first polygon. I couldn't find the others...

Code (implementation, not real code):

function getSlicedPolygons(polygon, line){
    var results = []
    var ofl = 0; // Overflow counter, to prevent infinite looping
    while(lines.length > 0){
        // Iterate through polygon
        var i = 0;
        var r = []; // Array of new indices
        var iterations = 0; // An overflow counter again
        while(i < polygon.length){
            r.push[i]
            // findNextConnectionIndex(...) searches for a line
            // that connects with this vertex and returns the index of
            // the other connected vertex
            var n = findNextConnectionIndex(i, polygon, lines) || i+1
            i=n;
            // Don't loop infinite
            iterations++;
            if(iterations > 10) break;
        }
        var result = [];
        for(var z = 0; z<r.length; z++){
            result.push(polygon[r[z]])
        }
        results.push(result)
        // Now I should do something to get another polygon next 
        // time ...
        // Don't loop infinite
        ofl++;
        if(ofl >= 10) break;
    }
    return results;
}

It returns the same polygon 10 times in an array...

Treat the polygon and its intersecting lines as an undirected graph with loops and apply a loop-detection algorithm to it. Since we know the connecting lines, things become a bit more simple and we can actually solve the problem in O(V) .

This would be a model that suffices to explain the basic principle. We can transform our polygon into a rectangle that is sliced by the list of lines. Since no lines may intersect, this will also apply to the resulting rectangle. Now one can start in an corner of the graph and travel alongside both edges until on both paths a vertex of degree 3 is reached. Thus we've found our first polygon that results from slicing the original polygon. Go on from the two points reached in the previous step until you reach vertices of degree 3 are reached again. Terminate this step when the two paths meet and you've listed all possible polygons.

A diagram of running a single step of this process:

Finding the "corner" vertex

Start from an arbitrary point in the graph/polygon and traverse the vertices along the polygon in an arbitrary direction until a vertex of degree 3 is reached. Store the corresponding slicing line and proceed along the polygon until the vertex of degree 3 is reached. If it is the same slicing line, you've found a "corner" vertex, else store the new slicing line and repeat.

EDIT

A working implementation in python:

def slice_polygon(poly, part):
    # find the "corner point"
    last_slice = None
    last_pt = None

    for pt in poly:
        s = [x for x in part if pt in x]

        if s:
            if last_slice in s:
                break

            last_slice = s[0]

        last_pt = pt

    # find slicing starting from border-point
    a = poly.index(last_pt)
    b = (a + 1) % len(poly) # current positions on the polygon
    sliced_poly = []    # current polygon
    slicing = []    # list of all polygons that are created by the slicing
    while a != b:
        if not [x for x in part if poly[a] in x]:
            # point doesn't lie on slicing-line => add to current polygon
            sliced_poly.insert(0, poly[a])             # prepend point
            a = (a - 1 + len(poly)) % len(poly)  # advance a by one
        elif not [x for x in part if poly[b] in x]:
            # point doesn't lie on slicing-line => add to current polygon
            sliced_poly.append(poly[b])                # append point
            b = (b + 1 + len(poly)) % len(poly)  # advance by one
        else:
            # append points of slicing line
            sliced_poly.insert(0, poly[a])
            sliced_poly.append(poly[b])

            # store created polygon and start over
            slicing.append(sliced_poly)
            sliced_poly = []

            # remove partitioning-line at which the algorithm stopped
            part.remove([x for x in part if poly[a] in x and poly[b] in x][0])

    # add last point to the current polygon, as it's not yet added to it
    sliced_poly.append(poly[a])
    # add last polygon to result-set
    slicing.append(sliced_poly)

    return slicing


# test
polygon = [(150, 110), (270, 40), (425, 90), (560, 150), (465, 290), (250, 290), (90, 220)]
partition = [((270, 40), (250, 290)), ((425, 90), (250, 290))]
print(slice_polygon(polygon, partition))

Output:

[[(425, 90), (560, 150), (465, 290), (250, 290)], [(270, 40), (425, 90), (250, 290)], [(90, 220), (150, 110), (270, 40), (250, 290)]]

Input:
样本输入

Since there is a total of two "corner-points" (at least), we are guaranteed to find at least one, if we traverse the polygon once.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM