8

I have a set of rectangles that have the same width and height and are always adiacent. I know the position of all the vertices, each one having only 4. (because is a square).

This image can explain this: enter image description here

If there are any gaps, is ok if the algorithm will 'fill' the gap.

I searched a lot, and could not find anything good.. I need an simple algorithm, it doesn't have to be that efficient.. Let's say that we got 7 rectangles like in the second polygon example from the image. Is ok if I first merge 1 with 2, then merge our new polygon with 3, and so on, it doesn't have to be that fast, because there will be maximum 50 rectangles.

Boldijar Paul
  • 5,405
  • 9
  • 46
  • 94

2 Answers2

6

Because your shape consists of rectangles only and they are always adjacent, the algorithm of merging is much simpler than it would be without those assumptions.

  • Create a list of ALL edges from your rectangles. One rectangle has 4 edges.
  • Let the Edge be a class with properly defined compareTo() and equals().
  • Sort the edges list (uses compareTo).
  • Iterate through the list. If the same edge is present in the list TWICE, remove them both from the list.
  • The remaining edges are the edges of your polygon.
rpax
  • 4,468
  • 7
  • 33
  • 57
Dariusz
  • 21,561
  • 9
  • 74
  • 114
  • This is just what I was about to post. Simple and quick – Michał Rybak Jan 08 '14 at 13:45
  • @Dariusz Are you guaranteed to end up with a single polygon at the end? What if the set of rectangles is disconnected and merging results in 2 or more separate rectangles? This still works, but OP might want to know how many polygons remain, and after this algorithm, it would be difficult to say. – Trenin Jan 08 '14 at 13:48
  • @Trenin he is bound to be building a path after using it. If a path is complete and there are still edges left, it means there's a second shape. Though there should be additional checks to see if one shape is not within another, to prevent unnecessary filling. – Dariusz Jan 08 '14 at 13:53
  • @Dariusz What if there is more than one nested shape? I am trying to see how to detect and resolve nested shapes, and it is not trivial. – Trenin Jan 08 '14 at 13:58
  • @Dariusz Just checked the wording of the OP, and 'filling gaps' might mean nested shapes get absorbed into the larger polygon. Still, not trivial to detect a nested shape... – Trenin Jan 08 '14 at 14:04
  • These are rectangles, and we have very specific input data. Nested shapes are always nested completely or not nested at all. Hence the detection is simple - for example, check a single vertex of one shape for being within the other. – Dariusz Jan 08 '14 at 14:09
  • After merging adjacent rectangles, you no longer have simple rectangles. Consider a set of squares arranged like the pixels in an enlarged number `6`. I don't think it is trivial to detect if a nested shape exists inside the closed off section of the 6, as opposed to other areas within the convex set which would be a second shape. – Trenin Jan 08 '14 at 14:14
  • This doesn't work for me, this is my code http://pastebin.com/6wpDSNs9 . I am wrong somewhere? – Boldijar Paul Jan 08 '14 at 14:48
  • @Trenin I don't get your example with `6`. The third image posted by OP seems to confirm that we don't want nested edges to be absorbed. – Michał Rybak Jan 08 '14 at 14:54
  • @Michal Rybak That isn't clear to me. OP said the algorithm *could* fill the gaps. *could* to me means it can either fill the gaps or not. Regardless, even if these nested edges aren't absorbed, it is still not easy to detect if you have a shape inside of a shape, or if the shape is simply hollow. – Trenin Jan 08 '14 at 14:56
  • @MichałRybak After this algorithm, you will be left with a bunch of edges. By following the paths, you will end up with 1 or more cycles. Does each cycle correspond to a shape? Or is a cycle the hollow portion inside a shape? Not trivial to detect which is which. – Trenin Jan 08 '14 at 15:13
  • @Dariusz These are not simple rectangle once you start merging. – Trenin Jan 08 '14 at 15:14
3

I really l like the efficiency of Dariusz's answer. And it might be that it meets all your requirements, in which case go with it.

However, there are a few problems that come to mind.

What if there are multiple shapes after merging? How can you detect if these shapes are separate, or nested? For that matter, if you are simply given a set of edges, it is not easy to tell if it constitutes a shape, or the void left inside a shape.

For example, consider the following diagram after merging adjacent squares:

##################
##################
##################
###            ###
###  ########  ###
###  ########  ###
###  ########  ###
###  ########  ###
###            ###
##################
##################
##################

Here there are really 2 shapes - 1 inside another. However, there are 3 sets of connected edges. In order to see if the inner rectangle is a shape or a void within a shape, you must start at the outer rectangle and work inwards. Doing so will result in knowing that the shape is basically an outline of a rectangle surrounding another rectangle. If you were to remove the outer edges, however, the resulting shape would simply be a hollow rectangle - one shape.

Assuming this is relevant to your problem (it might not be), then the following algorithm may be more suitable:

Instead of throwing the set of all the edges of all the rectangles together at the beginning, keep each rectangle separate in a list of Polygons. Each Polygon has its own set of edges.

Merge Polygons in this list that share an edge until you are left with a set of distinct Polygons (i.e. no more merges can take place).

List<Polygon> plist;

// Populate the list with the polygons...

for (int i = 0; i < plist.size(); i++) {
  Polygon p1 = plist.get(i);
  boolean distinct = false;
  while (!distinct) {
    distinct = true;
    for (int j = plist.size() - 1; j > i; j--) {
        Polygon p2 = plist.get(j);
        if (p1.sharesEdge(p2)) {
          // Merge the two polygons
          p1.merge(p2);
          // One less shape
          plist.remove(j);
          distinct = false;
        } // if (p1.sharesEdge(p2))
      } // for (int j=plist.size()-1 ; j>i; j--) 
    } // while (!distinct)
} // for (int i=0; i<plist.size(); i++)

At the end, you will have a list of separate Polygons in plist.

sharesEdge simply loops over the edges of each Polygon and sees if they have any in common.

merge does exactly as Dariusz's answer - remove pairs of edges.

Some assumptions - all initial polygons have edges of unit length. If this is not true, then you may need to split edges up when merging and have a more complicated method of checking for shared edges.

If nested shapes need to be handled by absorbing them into the larger shape (i.e. filling the gaps), then it gets a little tricky. You'd start by creating a path of the edges. If the edges are all connected, then this is a simple shape where its edges define the perimeter. If not, then there should be one outer perimeter, and one or more inner perimeters. Ignore the inner perimeters and resolve the shape to to simple - i.e. only the edges in the outer perimeter are kept. Then, loop over the shapes and see if any of the shapes is inside the other. If so, remove it.

aSemy
  • 5,485
  • 2
  • 25
  • 51
Trenin
  • 2,041
  • 1
  • 14
  • 20
  • Firstly, i don't have the list of edges, i have the list of vertices. How can I merge with libGDX polygon?http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Polygon.html Please check my comment in Dariusz's answer. – Boldijar Paul Jan 08 '14 at 15:00
  • @Paul In your code, you are removing the members of the list `noduri` but still looping over them. You cannot modify the members of the list without changing your loop. – Trenin Jan 08 '14 at 15:06
  • @Paul Besides, removing pairs of vertices is not guaranteed to work. If two squares share a corner, but no edge, then they cannot be merged. – Trenin Jan 08 '14 at 15:07
  • @Paul I think you could probably write code to convert a set of vertices to a set of unit edges and back. Then you could use Dariusz's answer, or mine. – Trenin Jan 08 '14 at 15:08
  • Look at this photo, i am wrong in what i want to explain? http://postimg.org/image/of8u14ujf/ and please tell me what should i change to my code, and remember i have a list of vertices that have x and y position, not the edges. – Boldijar Paul Jan 08 '14 at 15:12
  • @Paul If you want one polygon like in your picture, then you can't remove that vertex. Without that vertex, you'd end up with angles connecting the remaining vertices. Can you even express the desired polygon with your code? If so, then I would replace pairs of vertices with a single vertex. – Trenin Jan 08 '14 at 15:17
  • @Paul I just told you - don't remove both, just one. Also change your loop structure so that you aren't removing members of the list while you are looping over them. Check out my code for how I handle modifying the list while looping over it. I remove elements from the end so that my next `j` index is guaranteed to be one I haven't seen and I will not miss any entries. – Trenin Jan 08 '14 at 15:23
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/44795/discussion-between-paul-and-trenin) – Boldijar Paul Jan 08 '14 at 15:33