5

http://img853.imageshack.us/img853/2475/picture1eu.jpg

I've got an ArrayList of Points/Coordinates which represents a Rectilinear Polygon. I now want to break this shape up into rectangles using the stored Points in my ArrayList.

I've started an algorithm, but I can't finish it and I feel there's got to be an easier way:

The ArrayList is called "allCoordinates".
ArrayList "xMatch" and "yMatch" are subsets of allCoordinates.

Algorithm:

ArrayList yMatch = All matching Coordinates in respect to 'y'


So in the case of this diagram above:
(Set 1=[x1, y1]-[x8, y8],
Set2=[x7, y7]-[x2, y2],
Set3=[x4, y4][x5, x5],
Set4=[x3, y3][x6, x6])

ArrayList xMatch = All matching Coordinates in respect to 'x'


So in the case of this diagram above:
(Set 1=[x1, y1]-[x2, y2],
Set2=[x3, y3]-[x4, y4],
Set3=[x5, y5][x6, x6],
Set4=[x7, y7][x8, x8])



So now I have two arrayLists, all vertical Edges and all horizontal Edges. Now I need some way of checking whether they all connect together? Like I said there's got to be an easier way...?

Edit:

Can I just clarify that the rectangles have to be formed from using intersecting lines that start and finish on existing coordinates. For example, a line could be drawn from (x6, y6) horizontally and finish on edge (x1,y1)-(x8,y8). This line would start from an existing coordinate, however it wouldn't finishing on an existing coordinate. Therefore the line would be invalid.

cworner1
  • 461
  • 4
  • 13
  • There are an infinite number of ways to break a rectilinear polygon into rectangles. Does it matter which way you do it? – Robert Cooper Nov 26 '12 at 16:55
  • Would make sense to assume he wants (one of) the smallest set of rectangles. – jimpic Nov 26 '12 at 16:58
  • I don't think so. I say that it doesn't matter I'm not sure how they would vary? – cworner1 Nov 26 '12 at 17:00
  • @jimpic Well, maybe he wants the smallest number of rectangles. But maybe he just want to paint a shape using rectangles, in which case it might be better to do something else (e.g. not care about overlapping rectangles, or just use a library paint a polygon). – Robert Cooper Nov 26 '12 at 17:01
  • @Robert Cooper Okay, well the rectlinear polygon represents a decking area. I need to work out how many decking lengths it would take to deck that decking area. My first step is to break the polygon into rectangles. – cworner1 Nov 26 '12 at 17:05
  • @cworner1 Aha! Now that's a different problem. If you're just trying to find the area of the polygon, that's one thing. If you have boards which are certain dimensions, and you want to figure out the minimum number boards you need to cover your deck if you can saw pieces, I'm afraid you are trying to solve an NP-hard problem. If you're fine getting just a good answer, not necessarily the best answer, there's probably some easy ways to do it. – Robert Cooper Nov 26 '12 at 17:12
  • @ Robert Cooper I understand what your leading to but I've simplified it for myself (I won't bore you with the details). But in simple terms: The decking either runs vertically, or Horizontally. It can't be mixed in both directions. So with that in mind, I've got all the necessary coordinates to break the polygon into rectangles. I now need an algorithm to convert coordinates to rectangles. Once I have my rectangles I can then find the area and divide it by the dimensions of a deck board. – cworner1 Nov 26 '12 at 20:42
  • What you need that for?, maybe I know a better solution to your problem with well known existing algorithms. – AlexWien Nov 26 '12 at 21:01
  • @ AlexWien, I work in a decking company, my job is to work out how much materials a deck will require. Decking boards is one of those items and so this is how I've decided to calculate how many decking boards I will need given any rectilinear polygon. – cworner1 Nov 27 '12 at 08:17
  • If your problem is still the same as earlier (http://stackoverflow.com/questions/13399666/java-fit-minimum-amount-of-rectangles-into-polygon), I recommend using that description over what you have posted here. It's a lot clearer, imo. – Leon Bouquiet Nov 27 '12 at 16:30
  • @Astrotrain. This is a subsection of that question you've linked. Its still part of the same problem but this time I'm asking for a lot less. The rest I've got covered. I've broken it up because I got slated last time for asking too much (as I'm sure you've noticed from the comments) – cworner1 Nov 27 '12 at 16:41
  • I noticed, and I think that the commentators are wrong, because it was a well written question with definitely no lack of effort on your part. I saw that that post was originally tagged Java, and it draws a programmer crowd (who think "I don't get it, what do you want built?"), not an algorithmic crowd... Anyway, I have a couple of ideas for the complete problem, I'll try to post them here. – Leon Bouquiet Nov 27 '12 at 17:56
  • In my answer I update a link to a solution. – AlexWien Nov 28 '12 at 18:58
  • @cworner1: See my post below, could you indicate if the problem statement as I interpret it is correct? – Leon Bouquiet Nov 28 '12 at 21:12
  • @Astrotrain I've been going over your part solution with a fine tooth comb. I've been trying to fully understand your post but I keep get interrupted. Please be patient. It all makes sense but I'd like to comment on a few things just to make sure we're on the same wave length – cworner1 Nov 29 '12 at 10:23
  • What's the reason that you only want to use vertices from the original polygon to form the rectangles with? It might seem like an easy way to prevent you from calculating intersection points, but it actually complicates matters a lot. Also, most of the examples you provided (http://stackoverflow.com/questions/13399666/java-fit-minimum-amount-of-rectangles-into-polygon and http://img29.imageshack.us/img29/6349/173zh.jpg) cannot be broken into rectangles without introducing extra vertices. If possible, I would suggest dropping this constraint. – Leon Bouquiet Nov 29 '12 at 20:54
  • @ Astrotrain. In my array of Coordinates includes a set of dissecting vertices (the starting and finishing points). In other words. The polygon has already been dissected in (what I believe to be) the correct manner. I've not explained this because again I was trying to keep things simple. I believe I have implemented a working algorithm tonight. I'm going to test it tomorrow back at work. I'll post my answer if it works. – cworner1 Nov 29 '12 at 22:12

4 Answers4

8

As you may have noticed by now, I keep coming back to this problem. Partly because I like to puzzle out these kinds of problems, but also because it annoyed me a little that I couldn't find an elegant algorithm for something that seems so easy.

Well, don't be fooled, this is not a trivial problem that you can solve with some simple point manipulation, although it certainly looks that way. Part of the reason for this is that, although you think you're only working with points, you're implicitly also manipulating line segments and the areas enclosed by them, which also have their own constraints (i.e. the segments should always form one or more closed chains, and cannot intersect except at the vertices we define them with).

Also, your algorithm has to work for any kind of input you feed it. That is, it is not allowed to produce no answer or a wrong answer just because the polygon you fed it is oriented in a way that your algorithm doesn't like.
What you can do however, is restrict the type of input that your algorithm accepts. Requiring that the input polygon contains only axis-aligned segments is therefore perfectly valid.
(The difference between "oriented the wrong way" and "only axis-aligned segments" is that the first is a vague criteria that cannot be determined without testing it on the algorithm - whereas the second requirement can).

To recapitulate, we're looking for a way to horizontally partition any rectilinear polygon (i.e. consisting of only axis-aligned line segments) into rectangles.

Example of a rectilinear polygon Example of a rectilinear polygon

Here's the plan:

  1. Pick our building blocks
  2. Allow extra vertices
  3. Aligning on a grid.
  4. Working with unequally-sized grid cells.
  5. Which cells are inside your polygon?
  6. Constructing rectangles.

Pick our building blocks

Even though these are implementation details, getting this clear up front might help you getting a better picture of how the algorithm works.

In code, we'll be using the objects of the following types as basic building blocks to represent our polygon with:

  • Point, consisting of an x and y coordinate. (e.g use Point2D.Double)
  • Segment, consisting of a start and end Point (e.g. use Line2D.Double)
  • Polygon, consisting of a closed chain of Segments (e.g. use an ArrayList of Line2D.Double), where one segment ends on the same point as the starting point for the next segment.

Also, we'll be using a grid, which can be modelled as a 2-dimensional array.

Allow extra vertices.

You stated that "the rectangles have to be formed from using intersecting lines that start and finish on existing coordinates.". However, observe that most rectilinear polygons cannot be partitioned into rectangles by only using the existing vertices - see the example above (as well as the caravan examples you provided earlier).

Hence, this constraint will have to go - even though we won't actually be creating new vertices explicitly.

Aligning on a grid.

Thought experiment: What if your polygon existed only of (axis-aligned) segments of length 10, 20, 30 or 40... i.e. multiples of 10? We could then project our polygon on a grid, and use the grid cells that lie inside the polygon to compose the rectangles with. Also, determining the dimensions of these rectangles would be a breeze: just count the number of horizontally consecutive cells that lie inside the polygon and multiply by 10; that's your width.

Grid-aligned polygon Grid-aligned polygon

Good idea, except:

  • The polygon doesn't consist of only segments of same-or-multiple length
  • How do we determine which grid cells lie inside the polygon?

We'll tackle each of these problems next.

Working with unequally-sized grid cells.

If you think about it, there is no real reason for all the grid cells to have the same width and height. Hence, we can devise a grid that is spaced in such a way that our polygon aligns perfectly onto it.

To get the widths of the grid's horizontal axes:

  • Collect all x-coordinates of the vertices of which the polygon is composed.
  • De-duplicate and sort them on increasing value.
  • The difference between two subsequent values then define the width of that column.

Grid aligned to the polygon Grid aligned to the polygon

Obviously, the cell's heights can be determined in the same way. You should determine these widths and heights, and store them into two arrays called gridWidths and gridHeights, respectively.

Now that we know the number of cells and their dimensions, we can start modelling the grid contents.

Which cells are inside your polygon?

Remember that our polygon is stored as a chain of line segments. To determine if a grid cell lies inside this polygon we can use the even-odd rule: Construct a line segment from outside* the polygon to the center of the cell we want to test, and count the number of intersections between this line segment and the segments of the polygon (i.e. use Line2D.Double's intersectsLine() method). If the number is even it lies outside the polygon, if it is odd it is inside the polygon.

*- It's best to construct only horizontal line segments between center of the cells (as shown in the example), so that you don't risk intersecting the segment endpoints - this may could count as 0 or 2 intersections, messing up your count total.

So, we will model this grid as grid, a 2-dimensional array of booleans. Process each cell of the grid this way, and mark the corresponding spot in grid as either true (inside polygon) or false (outside polygon).

Inside (T) or outside(F) based on the even-odd rule Inside (T) or outside(F) based on the even-odd rule

Constructing rectangles.

Now that we have grid representation of the polygon, as well as the actual widths and heights of each cell, we can start constructing rectangles. I will assume that you're only interested in the widths and heights of each rectangle, and not in the actual vertex coordinates that form the rectangle (although that is now easy too).

Foreach row in grid
  run_start = null
  Foreach cell C in row
    if C is marked as true and run_start = null
      //Found the start of a new run
      run_start = C
    else if C is marked as false and run_start != null
      //Found the end of a run
      The cells [run_start...C] form a horizontal rectangle.
      Use the gridWidths and gridHeights arrays to determine the 
      dimensions, and report this rectangle as a result
      
      //Prepare for the next run
      run_start = null

The polygon, partitioned into rectangles. The polygon, partitioned into rectangles.

Note that the two rectangles in the top left share the same starting and ending x-coordinates, and could therefore be considered as one rectangle. If you wanted, you could implement an additional pass that merges these type of rectangles.

Conclusion

By mapping the rectilinear polygon onto a grid, we can easily partition it horizontally in rectangles without having to resort to more advanced geometric data structures.
Note that there are some optimizations possible to make this algorithm run faster, but I don't think it really matters for the current input sizes, and it would make the implementation more difficult.

Community
  • 1
  • 1
Leon Bouquiet
  • 4,159
  • 3
  • 25
  • 36
4

This is not easy:

I think you will not successfull solving that on your own:

More info see

Preparata, Shamos: Computational Geometry: Chapter 8: The Geometry of Rectangles.

You should first be familar with Plane Sweep Algorithms and Intervall Trees.

If I find more, i will update. Found more:

Algorithm for finding the fewest rectangles to cover a set of rectangles without overlapping

Community
  • 1
  • 1
AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • Thank you for your research. However I have seen this post before. The solution you have posted dissects the polygon in a different way to how I dissect the polygon (there's a good reason for that). The Coordinates I have saved already include the correct dissection. The question is how can I create rectangles from MY array of coordinates. – cworner1 Nov 29 '12 at 08:56
  • How they do it in the link above? Maybe you can do it the same way. – AlexWien Nov 29 '12 at 09:08
  • In the link above the dissections are made in both directions - parallel to X-axis and parallel to Y-axis. My algorithm dissects in just one direction, either X-Axis parallel or Y-Axis parallel. Not both. The difference being the length of each decking board will be shorter than they need to. – cworner1 Nov 29 '12 at 09:26
  • My intiution would also say to make a plane sweep on both axis. – AlexWien Nov 29 '12 at 09:33
  • Might be difficult to explain this, but I will try. Have a look at photo 2 and 4 in [this]http://stackoverflow.com/questions/13399666/java-fit-minimum-amount-of-rectangles-into-polygon post. The algorithm you've linked would dissect the veranda into more pieces that is necessary. What I'm looking to do is cover a polygon in "deck board" rectangles with the minimum amount of cuts. For example. A deck board is 32mm wide and 5.1m long. If I can use a 5.1m board without having to cut the board then thats better than having to cut it up unecessarily. – cworner1 Nov 29 '12 at 09:38
1

Note: I'm not going for a theoretically optimal solution here, but just for an approach that produces the correct answer and works fast enough on an input polygon of say 100 vertices. Also, special cases such as an input polygon with holes in it are not considered now. Also, polygons that are not x-monotone* need pre-processing which I won't discuss yet.
*Meaning: starting at any leftmost position in P, you can reach any other position by moving up, down or right, but without ever going left.

Assumptions
As stated in your earlier post, part of the question is in which direction to lay the decking boards (or "rectangles") in order to minimize the number of decking boards used. I will assume that your input polygon P will consist of mostly axis-aligned segments, so that the choice in direction is reduced to either horizontal or vertical. For the remainder, I will assume that a single decking board is always oriented vertically (i.e. runs from top to bottom). For calculating the result with horizontal decking boards, just rotate P by 90 degrees.

Problem statement
We'll be trying to cover P with decking boards, each with an unlimited length and a maximum width of W. More specifically, we're looking for a coverage that minimizes the total length of all decking boards used. The parts of the used decking boards that fall outside P (i.e. the wastage) cannot be used to cover other parts of P. In order to minimize the wastage, it makes sense to align either the left or the right border of a decking board against a vertex of P, or to place a decking board right next to another decking board.

Solution
The first step towards this is to partition P into a collection of vertical slabs: take the distinct x-coordinates of all vertices in P and sort them: each pair of consecutive x-coordinates then defines a slab S of P. Figure 1

Next recognise that, for each slab S, we have 4 possible ways to align the (one or more) decking boards to it:
* (SL) Start Left, i.e. align the left side of the decking board with the left side of the slab.
* (CL) Continue Left, i.e. continue the pattern of decking boards as determined by the slab to the left of it.
* (CR) Continue Right, i.e. continue the pattern of decking boards as determined by the slab to the right of it.
* (SR) Start Right, i.e. align the right side of the decking board with the right side of the slab.

Hence, if we consider all possible combinations of the 4 alignments for each of the slabs S, we have all the possible decking configurations. Note that not all combinations of alignments are allowed; SL and SR can be used for any slab, but CL can only be used if the slab to the left of it is either SL or CL, and CR can only be used if the slab to the right of it is either SR or CR.

-Snip- The problem appears to be significantly different from what is attempted to solve here, so I won't be finishing this post.

Community
  • 1
  • 1
Leon Bouquiet
  • 4,159
  • 3
  • 25
  • 36
  • "More specifically, we're looking for a coverage that minimises the total length of all decking boards used." - Not true, not sure whether this is a mistake or I've read it wrong. We're looking at minimising the amount of pieces used. So instead of using 3 deck boards @ 100 pixels I'd rather use 1 board @ 300 pixels (or whatever unit measurement you want to use). It means less cutting for the staff. – cworner1 Nov 29 '12 at 10:55
  • Can you explain why the 3rd slab (starting from the left) is labelled 'SR'. I was under the assumption we were starting from the left side and working our way to the right, in which case it should be labelled 'CR' - to continue right from the previous slab. – cworner1 Nov 29 '12 at 11:00
  • Yep, the rest I can make sense of. Just so you get a perspective on scale etc... Each slab is going to take a lot more that just one or two deck boards: http://img29.imageshack.us/img29/6349/173zh.jpg This is the old method or drawing manually. – cworner1 Nov 29 '12 at 11:14
  • Minimizing the total board length was an assumption of mine, since that minimizes the amount of material used (which usually is the requirement in these kind of problems). But I understand the amount of cutting is also a criteria. Is it the only criteria (i.e. do you only care about how much cutting is done, regardless of how much material you have to throw away), or is it a combination of cutting and amount of material used? – Leon Bouquiet Nov 29 '12 at 11:27
  • Well to simplify things to begin with - My objective was to lay the decking vertically, then display a cutting list. Lay the decking Horizontally - display a cutting list. Then let the user decide which layout is better based on the two sets of cutting lists. So there's no "optimal" algorithm involved. – cworner1 Nov 29 '12 at 11:32
  • I'm not sure whether I've got my wires crossed here. But if a slab is 10ft high (y(max)-y(min)) and the decking boards are running vertically then surely there is no alternative but to cut a board at 10ft. I fail to see how you can further optimise this. – cworner1 Nov 29 '12 at 11:38
  • Regarding the third slab labelled 'SR': The idea behind the algorithm is that by aligning the boards differently, you get more wastage in some parts and less in others. Because there is no telling beforehand which combination of alignments produces the optimal result (at least in terms of material used), the algorithm will consider all possible aligment combinations, and calculate the amount of material used for each combination. The alignment combination that requires the least amount of material then forms the solution. – Leon Bouquiet Nov 29 '12 at 11:38
  • I see the drawing that you posted, and I didn't realise that the decking boards were that 'thin' (horizontally). Now I also get why cutting is more of a concern than material usage (does it ever happen that you have to cut a board vertically, i.e. over the long side?). – Leon Bouquiet Nov 29 '12 at 11:46
  • Yes, however in the eyes of this program it does not matter. This is to count stock. If you have to cut a board in half then more than likely the other half is not reusable so we count it as a full board. – cworner1 Nov 29 '12 at 11:52
  • Can I just say that I really appreciate your time and effort tackling the bigger problem posted previously. However, since posting that particular question I've sub-divided the problem and come up with a partial answer. The only question I needed answering was this particular question above - how to make rectangles from coordinates. I didn't want to interrupt your solution attempt because perhaps when I've finished my algorithm it still might not work, in which case I will seriously consider your algorithm. However, your solution sounds more complex to my current algorithm and... – cworner1 Nov 29 '12 at 12:19
  • ...will involve a complete re-write of my code. I'm telling you this because at this stage I would rather try and finish my code then reimplement with yours. I don't want to be accused to have wasted your time. – cworner1 Nov 29 '12 at 12:20
  • That's ok, as it turns out I was trying to solve a different problem than you're having, so it's probably better that you ignore my approach. And just like you said, thanks for letting me know now, and not after I wrote down the complete algorithm :) – Leon Bouquiet Nov 29 '12 at 12:51
0

I found a solution but its probably not the most optimal.


link

So here we have our coordinates and my two ArrayLists mentioned earlier - xMatch and yMatch.

xMatch = ArrayList of Coordinates pairs with matching x-coordinates
yMatch = ArrayList of Coordinates pairs with matching y-coordinates

I made a class called "Point2Point" which saves two lots of coorindates in a specific order. Both xMatch and yMatch are of the "Point2Point" type. Like any vector the order is important. The order I used was:


Point1 = starting point
Point2 = finishing point.

So now using a "for" loop I matched an element from xMatch with an element from yMatch in respect to Point1 (the starting point). Pairing these up would give you the following shape:



Link2


Now this diagram you can see that in order for these two vectors to be part of a rectangle the coordinate (?, ?) must exist. Using the properties of a rectange I know that (?, ?) is equal to (yMatch.Point2.x, xMatch.Point2.y) or in respect to this diagram (x3, y2).


So now that I know which coordinates to look for I can search my ArrayList "allCoordinates" to see whether this coordinates exists. If it does - I have a rectangle, if not - its a dud!!

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
cworner1
  • 461
  • 4
  • 13
  • You're only keeping track of the points, and are defining the edges implicitly (as pairs of points). Have you considered representing your polygon as a [doubly-connected edge list](http://coderender.blogspot.nl/2011/11/dcel-data-structure-c-implementation.html) (or "DCEL")? It provides you with a lot more ways to manipulate your polygon (such as splitting edges and faces), which makes solving your problem easier (or more likely, "possible"). Only downside: I haven't been able to find a good java implementation yet. – Leon Bouquiet Nov 30 '12 at 20:00
  • Hold on, I think I have a better solution that takes advantage of the axis-aligned nature of the polygon.... – Leon Bouquiet Dec 01 '12 at 12:07