diff --git a/python_geometry/polygon_utilities.py b/python_geometry/polygon_utilities.py new file mode 100644 index 0000000..ef2c8b1 --- /dev/null +++ b/python_geometry/polygon_utilities.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""docstring""" +import numpy as np + + +def area_of_polygon_2D(polygon, *args): + """ + Computes the signed area of an arbitrary convex polygon as described in + http://paulbourke.net/geometry/polygonmesh/. + The signed area is positive for counter clockwise ordering otherwise + negative. + + Parameters + ---------- + polygon: + np.ndarray, list + The polygon as a list of consecutive 2D points. + + Returns + ------- + float + Area of the polygon. The area is negativ for clockwise point ordering + """ + + if args: + poly = [polygon] + + for pt in args: + if pt.shape != (2,): + for sp in pt: + poly.append(sp) + else: + poly.append(pt) + polygon = np.array(poly) + + area = 0. + for i in range(len(polygon) - 1): + area += (polygon[i][0] * polygon[i + 1][1] + - polygon[i + 1][0] * polygon[i][1]) + + # close the loop with the last crossproduct + area += (polygon[-1][0] * polygon[0][1] - polygon[-1][1] * polygon[0][0]) + + area /= 2. + if area == 0: + raise ValueError('Calculated Area is 0!') + return area diff --git a/tests/test_polygon_utilities.py b/tests/test_polygon_utilities.py new file mode 100644 index 0000000..7df762d --- /dev/null +++ b/tests/test_polygon_utilities.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""docstring""" +from unittest import TestCase +from numpy.testing import assert_equal +from numpy import array + +from python_geometry.polygon_utilities import area_of_polygon_2D + + +class TestArea_of_polygon2D(TestCase): + def test_rectangles(self): + ## square + quad1 = ([0, 0], [1, 0], [1, 1], [0, 1]) + quad2 = ([0, 0], [0, 1], [1, 1], [1, 0]) + quad1_np = array([[0, 0], [1, 0], [1, 1], [0, 1]]) + quad2_np = array([[0, 0], [0, 1], [1, 1], [1, 0]]) + + assert_equal(area_of_polygon_2D(quad1), 1) + assert_equal(area_of_polygon_2D(quad2), -1) + assert_equal(area_of_polygon_2D(quad1_np), 1) + assert_equal(area_of_polygon_2D(quad2_np), -1) + + + # rectangles + quad3 = ([0, 0], [5, 0], [5, 1], [0, 1]) + assert_equal(area_of_polygon_2D(quad3), 5) + + def test_triangles(self): + tria1 = ([0, 0], [1, 0], [1, 1]) + tria1_np = array([[0, 0], [1, 0], [1, 1]]) + tria2 = ([0, 0], [1, 1], [1, 0]) + tria3 = ([0, 0], [5, 10], [2, 5]) + assert_equal(area_of_polygon_2D(tria1), 0.5) + assert_equal(area_of_polygon_2D(tria1_np), 0.5) + assert_equal(area_of_polygon_2D(tria2), -0.5) + assert_equal(area_of_polygon_2D(tria3), 2.5)