views:

111

answers:

3

The following problem is in 2D, so some simplifications can be made when suggesting answers.

I need to create closed areas (defined either by line segments or just set of points - convex polygon) from a set of points/line segments.

Basically I used Voronoi to generate "roads". Then I changed some of the data. Now I need a way to loop through that data (which is still line segments but doesn't comply with Voronoi anymore) and generate "neigbourhoods" that are bordered with the "roads".

I looked at some graph diagrams and shortest path theories, but I could not figure it out.

Logically it could be done by starting at left edge from one point, finding the way back to that point using the shortest path with available lines (using only clockwise directions). Then mark this line set down and remove from the data. Then you can repeat the same process and get all the areas like that.

I tried to implement that but it did not get me anywhere as I could not figure out a way to write a C++ code that could do that. Problem was with choosing the most counterclockwise line from available lines from a specific point. All angle based math I did gave wrong answers because the way sin/cos are implemented in c++.

So to summarize - if you can help me with a totally new approach to the problem its good, if not could you help me find a way to write the part of the code that finds the shortest clockwise path back to the beginning point using the line segment set as paths back.

EDIT: Added a picture to illustrate what I want to do.

Check the image here - (need 10 reputations before I can post it here :P)

alt text

I have a set of points (purple small dots). Another array defines what points make up a line (road). I want a way to define the area that is surrounded by roads so I can put buildings or smaller roads inside that and test against the edges so every region is separated. Hope this gives you more info on how to solve this problem.

Thank you for your help!

+1  A: 
Moron
This does not help much as I have the problem of selecting which points(or lines) make up one of the "areas". I need to make a collection of points that define one of the "districts" in the city road map. And after that I can repeat that to do this for every area so I end up with a collection of point(line) sets that define each city cell to fill with buildings. Hope this makes sense as its hard to explain ;)
Marten
Don't know if it would work as every intersection between lines is a purple point. This would mean that no line ever intersects a segment between two purple points. Or did I misunderstood you ?
Marten
@Marten: In the figure the points I am talking about look blue to me. There are also some orange and red. These are the points which you used to form the Voronoi diagram initially I suppose and are completely surrounded by the black roads.
Moron
Yes, that would seem more logical ;) But the problem is that I used the Voronoi lines and segmented them into smaller sections to make the road less straight which means that I can find some of the segments like that but not all.
Marten
@Marten: If you have data left regarding the purple points, perhaps you could consider a 'virtual' road between two purple points, run the above method on the virtual roads and get back the real roads, using the relevant purple endpoints.
Moron
Sorry I don't understand. My purple points are what make up the lines for black road segments. Blue/red dots are the Voronoi diagram points.Or did you mean that I should try to find all "virtual" roads from one purple point to all others ? Really feel that I bit of more than I can chew here :(
Marten
@Marten: I see, perhaps I misunderstood. Anyway, do you still have data from the Voronoi left? Why don't you create the 'areas' first i.e. right after the Voronoi step and only _then_ segment the roads to make them crooked?
Moron
Then I would end up with a problem how to draw the smaller roads up to the bigger ones that have now changed. The idea is to create bounding area that I could use to draw the smaller roads into.Yes, I think I have most of the Voronoi data available if needed.
Marten
@Marten: Don't get the problem. You have a set of roads forming the area after Voronoi. Now you split each road into crooked segement. You have a new set of roads forming the area, which is what you wanted, isnt it? All we did was interchange the area creation and data modification step. Perhaps you should clarify exactly what changes you made after the Voronoi step...
Moron
After voronoi I divided the road into segments and allowed some degree of variance when moving from from begin to end so I ended up with crooked roads. Problem is that that road follows the general direction that Voronoi had but is not the same (sometimes closer /further from voronoi point). This means that if I would do the area step before the crooked the road will cut into or move away from the area and if I used that area to limit my smaller roads there would be overlaps or empty spaces. My problem is that although it seems that I have areas I dont know points for sets for area.
Marten
@Marten: Represent the area by the set of roads bounding it. Say you had a rectangle of four roads R1, R2, R3, R4. Now if you replace R1 by the crooked roads R5,R6,R7, the new area is R2,R3,R4,R5,R6,R7. So your area changes as you change the roads. You haven't lost any space or created empty spaces. Once you are done with creating crooked roads, you now have a polygon representing each area, by the roads bounding it. Representing an area by the set of its bounding roads is as good a representation as any other.
Moron
A: 

Your idea to follow the line segments along the shortest path (e.g. follow the most clockwise one if you have multiple choices) is good.

And you can find this line without any sin/cos calls.

The idea goes like this:

Assume that you have two lines to choose from. Call the point where the lines meet A (e.g. your current position). The endpoints of your two lines are called B and C.

These three points build a triangle. Now take a look how the three points are oriented. If you follow the points from A to B to C the direction will either be clockwise or counter-clockwise. Obviously if the order is clockwise, the line that goes from A to C will be the most clockwise one. Otherwise it's the line that goes from A to B.

If you have more than two lines to choose from just pick the first two, discard the line that goes into the wrong direction and do the same test until you end up with a single line. That's the most clockwise one.

Now about the math: how do you find out the winding-order of three points without calling sin/cos or even worse: atan:

You can do this with the cross product. First build two vectors that give you the direction from A to B and from A to C:

   u.x = B.x - A.x
   u.y = B.y - A.y

   v.x = C.x - A.x
   v.y = C.y - A.y

Now we can calculate the signed area of the parallelogram spawned by these two vectors:

   signed_area = (u.x * v.y) - (u.y * v.x);

The winding is the sign of the area. e.g.

  if (signed_area > 0)
  { 
     // order is clockwise. Pick Line B
  }
  else if (signed_area < 0)
  { 
     // order is counter-clockwise. Pick Line A
  }
  else 
  {
    // the lines are colinear.
  }

Note: I haven't tied the code and the decision on the sign could be exactly the other way around. That's a detail of the math that I will never get into my head. I always have to try it out with known data.

Nils Pipenbrinck
Will try your solution on the first chance I get and let you know how it worked out. Thank you!
Marten
The cross product will tell you left or right but not directly "sharper left" or "shallower right". You'll need to normalize the input vectors and then compute the dot product as `straightness = (u.x * v.x) + (u.y * v.y);`, since cross product will return the same value for a 45 degree bend as it will for a 135 degree one (it reflects around the perpendicular, whereas dot product reflects around the parallel). So- cross product for "left or right" and dot product for the angle of the turn.
dash-tom-bang
A: 

It sounds like you want to partition the some region into non-overlapping convex polygons. If I'm understanding you correctly, you can't just toss segments after using them to form a polygon, because each internal segment will border on two polygons.

Instead, you should have two flags for each segment, for whether you have built a polygon to the segment's "left" and "right". If you have a bordered area, the border segments need to have their "outer" side flag set to start with since you don't want to use that side in a polygon. Then find segments with either flag unset, and use Nils's answer to work your way around the polygon. Some segments will be "reversed"; if you're going clockwise, you want to set the 'left' flag on the 'forward' segments and the 'right' flag on the 'backward' ones, and vice versa. (You can build all the polygons in one order, it just doesn't matter which you choose.) Note that the first segment can be interpreted as either depending on which of its flags you need to set. When all segments are flagged in both directions, you are done.

If you're partitioning the plane rather than a bounded region, some segments will be rays; you'll also need some special code to connect rays that are adjacent when sorted by slope into fake "polygons".

Pseudocode for the bounded case:

foreach seg in boundary segments {
    if left of seg is outside region {
         seg.leftDone = true
    } else {
         seg.rightDone = true
    }
}
while any seg.leftDone or seg.rightDone is false {
    seg = pick a segment with either flag unset
    start = seg
    polygon = new Polygon()
    reversed = not start.rightDone
    do {
        if reversed {
            seg.rightDone = true
            endpoint = seg.start
            polygon.addSegment(seg.reverse())
        } else {
            seg.leftDone = true
            endpoint = seg.end
            polygon.addSegment(seg)
        }

        next = findNextClockwiseSeg(seg, endpoint); // Nils's answer works

        seg = next
        reversed = (seg.end == endpoint)
    } while start != seg;
    result.addPolygon(polygon)
}
Walter Mundt
From what I understand I want to do exactly that with a bounded region. But how do I mark the bounding edges flag set if I don't know which ones they are. I just have a set of points and the info that says what points are connected to form a line.
Marten
Is your region convex? If so, you can start at any point with minimum X, and follow "most clockwise" edges around the region marking the outside ones. It's just like marking an inner polygon, but touches the opposite flags.
Walter Mundt
Sorry for being a bit dumb, but don't know if its convex or not. I thought I might add line segments where x/y are at min/max values to connect all the edge lines into closed regions also (i just don't draw them but use to calculate areas).
Marten
Sorry for the delay, I didn't see the notification of your comment. In that case, you can run a convex hull on the points and add fake 'roads' as needed. Run the above algorithm, then discard any areas with any fake roads in their boundaries.
Walter Mundt