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