Calculate area given list of directions

Assuming some starting point (say, (0,0)) and the y direction is positive upwards:

  • left adds (-1,0) to the last point.
  • right adds (+1,0) to the last point.
  • up adds (0,+1) to the last point.
  • down adds (0,-1) to the last point.

A sequence of directions would then produce a list of (x,y) vertex co-ordinates from which the area of the resulting (implied closed) polygon can be found from How do I calculate the surface area of a 2d polygon?

EDIT

Here's an implementation and test in Python. The first two functions are from the answer linked above:

def segments(p):
    return zip(p, p[1:] + [p[0]])

def area(p):
    return 0.5 * abs(sum(x0*y1 - x1*y0
                         for ((x0, y0), (x1, y1)) in segments(p)))

def mkvertices(pth):
    vert = [(0,0)]
    for (dx,dy) in pth:
        vert.append((vert[-1][0]+dx,vert[-1][1]+dy))
    return vert

left = (-1,0)
right = (+1,0)
up = (0,+1)
down = (0,-1)

#  _
# | |_
# |__|
print (area(mkvertices([up, up, right, down, right, down, left, left])))
#  _
# |_|_
#   |_|
print (area(mkvertices([up, right, down, down, right, up, left, left])))

Output:

3.0
0.0

Note that this approach fails for polygons that contain intersecting lines as in the second example.


Since the polygon you create has axis-aligned edges only, you can calculate the total area from vertical slabs.

Let's say we are given a list of vertices V. I assume we have wrapping in this list, so we can query V.next(v) for every vertex v in V. For the last one, the result is the first.

First, try to find the leftmost and rightmost point, and the vertex where the leftmost point is reached (in linear time).

x = 0                       // current x-position
xMin = inf, xMax = -inf     // leftmost and rightmost point
leftVertex = null           // leftmost vertex
foreach v in V
    x = x + (v is left ? -1 : v is right ? 1 : 0)
    xMax = max(x, xMax)
    if x < xMin
        xMin = x
        leftVertex = V.next(v)

Now we create a simple data structure: for every vertical slab we keep a max heap (a sorted list is fine as well, but we only need to repetitively fetch the maximum element in the end).

width = xMax - xMin
heaps = new MaxHeap[width]

We start tracing the shape from vertex leftVertex now (the leftmost vertex we found in the first step). We now choose that this vertex has x/y-position (0, 0), just because it is convenient.

x = 0, y = 0
v = leftVertex
do
    if v is left
        x = x-1         // use left endpoint for index
        heaps[x].Add(y) // first dec, then store
    if v is right
        heaps[x].Add(y) // use left endpoint for index
        x = x+1         // first store, then inc
    if v is up
        y = y+1
    if v is down
        y = y-1

    v = V.next(v)
until v = leftVertex

You can build this structure in O(n log n) time, because adding to a heap costs logarithmic time.

Finally, we need to compute the area from the heap. For a well-formed input, we need to get two contiguous y-values from the heap and subtract them.

area = 0
foreach heap in heaps
    while heap not empty
        area += heap.PopMax() - heap.PopMax() // each polygon's area

return area

Again, this takes O(n log n) time.


I ported the algorithm to a java implementation (see Ideone). Two sample runs:

public static void main (String[] args) {
    //  _
    // | |_
    // |_ _ |
    Direction[] input = { Direction.Up, Direction.Up, 
                          Direction.Right, Direction.Down,
                          Direction.Right, Direction.Down,
                          Direction.Left, Direction.Left };

    System.out.println(computeArea(input));

    //  _
    // |_|_
    //   |_|
    Direction[] input2 = { Direction.Up, Direction.Right, 
                           Direction.Down, Direction.Down,
                           Direction.Right, Direction.Up,
                           Direction.Left, Direction.Left };

    System.out.println(computeArea(input2));
}

Returns (as expected):

3
2

Tags:

Algorithm