From 2e0a69b24930a5997eacd17aff6308466876d315 Mon Sep 17 00:00:00 2001 From: abdelrahmanabdelghany2 Date: Wed, 17 Jan 2024 12:45:11 +0200 Subject: [PATCH 1/4] Add perimeter function and python interface --- gdstk/gdstk.pyi | 1 + include/gdstk/polygon.hpp | 3 +++ python/docstrings.cpp | 7 +++++++ python/polygon_object.cpp | 6 ++++++ setup.py | 2 ++ src/polygon.cpp | 18 ++++++++++++++++++ 6 files changed, 37 insertions(+) diff --git a/gdstk/gdstk.pyi b/gdstk/gdstk.pyi index ce6941db1..b43fd15d2 100644 --- a/gdstk/gdstk.pyi +++ b/gdstk/gdstk.pyi @@ -433,6 +433,7 @@ class Polygon: ): ... def apply_repetition(self) -> list[Self]: ... def area(self) -> float: ... + def perimeter(self) -> float: ... def bounding_box(self) -> tuple[tuple[float, float], tuple[float, float]]: ... def contain(self, *points: tuple[float, float] | complex) -> bool | tuple[bool, ...]: ... def contain_all(self, *points: tuple[float, float] | complex) -> bool: ... diff --git a/include/gdstk/polygon.hpp b/include/gdstk/polygon.hpp index 02d50e396..4a44cd1a5 100644 --- a/include/gdstk/polygon.hpp +++ b/include/gdstk/polygon.hpp @@ -45,6 +45,9 @@ struct Polygon { // Polygon area excluding repetitions with sign indicating orientation // (positive for counter clockwise) double signed_area() const; + + // Total polygon perimeter including any repetitions + double perimeter() const; // Check if the points are inside this polygon (points lying on the edges // or coinciding with a vertex of the polygon are considered inside). diff --git a/python/docstrings.cpp b/python/docstrings.cpp index 8c156937d..1cbda1cac 100644 --- a/python/docstrings.cpp +++ b/python/docstrings.cpp @@ -392,6 +392,13 @@ Polygon area. Returns: Area of the polygon.)!"); +PyDoc_STRVAR(polygon_object_perimeter_doc, R"!(perimeter() -> float + +Polygon perimeter. + +Returns: + Perimeter of the polygon.)!"); + PyDoc_STRVAR(polygon_object_bounding_box_doc, R"!(bounding_box() -> tuple Calculate the polygon bounding box. diff --git a/python/polygon_object.cpp b/python/polygon_object.cpp index 2eb651a67..987d930c3 100644 --- a/python/polygon_object.cpp +++ b/python/polygon_object.cpp @@ -66,6 +66,11 @@ static PyObject* polygon_object_area(PolygonObject* self, PyObject*) { return PyFloat_FromDouble(area); } +static PyObject* polygon_object_perimeter(PolygonObject* self, PyObject*) { + const double perimeter = self->polygon->perimeter(); + return PyFloat_FromDouble(perimeter); +} + static PyObject* polygon_object_bounding_box(PolygonObject* self, PyObject*) { Vec2 min, max; self->polygon->bounding_box(min, max); @@ -427,6 +432,7 @@ static PyMethodDef polygon_object_methods[] = { {"copy", (PyCFunction)polygon_object_copy, METH_NOARGS, polygon_object_copy_doc}, {"__deepcopy__", (PyCFunction)polygon_object_deepcopy, METH_VARARGS | METH_KEYWORDS, polygon_object_deepcopy_doc}, {"area", (PyCFunction)polygon_object_area, METH_NOARGS, polygon_object_area_doc}, + {"perimeter", (PyCFunction)polygon_object_perimeter, METH_NOARGS, polygon_object_perimeter_doc}, {"bounding_box", (PyCFunction)polygon_object_bounding_box, METH_NOARGS, polygon_object_bounding_box_doc}, {"contain", (PyCFunction)polygon_object_contain, METH_VARARGS, polygon_object_contain_doc}, diff --git a/setup.py b/setup.py index 4e0bd2869..ee6b70706 100644 --- a/setup.py +++ b/setup.py @@ -102,4 +102,6 @@ def run(self): include_package_data=True, cmdclass={"build_ext": CMakeBuilder}, zip_safe=False, + name="Mabrains_gdstk", + version="0.0.1" ) diff --git a/src/polygon.cpp b/src/polygon.cpp index 329938cab..ea99799e2 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -80,6 +80,24 @@ double Polygon::signed_area() const { return 0.5 * result; } +double Polygon::perimeter() const { + if (point_array.count < 2) return 0; + + double result = 0; + Vec2* p = point_array.items; + Vec2 v0 = *p++; + + for (uint64_t num = point_array.count - 1; num > 0; num--) { + Vec2 v1 = *p++ - v0; + result += v1.length(); + v0 += v1; + } + + if (repetition.type != RepetitionType::None) result *= repetition.get_count(); + + return result; +} + // Based on algorithm 7 from: Kai Hormann, Alexander Agathos, “The point in // polygon problem for arbitrary polygons,” Computational Geometry, Volume 20, // Issue 3, 2001, Pages 131-144, ISSN 0925-7721. From da12dedfca47e41edd571dd899c597b3e964c0b7 Mon Sep 17 00:00:00 2001 From: abdelrahmanabdelghany2 Date: Wed, 17 Jan 2024 15:22:12 +0200 Subject: [PATCH 2/4] Add_polygon_perimeter_test --- src/polygon.cpp | 10 +++++----- tests/polygon_test.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/polygon.cpp b/src/polygon.cpp index ea99799e2..c436b0b59 100644 --- a/src/polygon.cpp +++ b/src/polygon.cpp @@ -81,23 +81,23 @@ double Polygon::signed_area() const { } double Polygon::perimeter() const { - if (point_array.count < 2) return 0; - + if (point_array.count < 3) return 0; double result = 0; Vec2* p = point_array.items; Vec2 v0 = *p++; - + for (uint64_t num = point_array.count - 1; num > 0; num--) { Vec2 v1 = *p++ - v0; result += v1.length(); v0 += v1; } - + result += (point_array.items[0] - point_array.items[point_array.count-1]).length(); if (repetition.type != RepetitionType::None) result *= repetition.get_count(); - return result; } + + // Based on algorithm 7 from: Kai Hormann, Alexander Agathos, “The point in // polygon problem for arbitrary polygons,” Computational Geometry, Volume 20, // Issue 3, 2001, Pages 131-144, ISSN 0925-7721. diff --git a/tests/polygon_test.py b/tests/polygon_test.py index dc352a45b..1582a933a 100644 --- a/tests/polygon_test.py +++ b/tests/polygon_test.py @@ -34,6 +34,27 @@ def test_area_size(): numpy.testing.assert_array_equal(poly.points, [[0, 0], [1, 0], [1, 1], [0, 1]]) +def test_perimeter_size(): + poly1 = gdstk.Polygon([(0, 0), (1, 0), 1j]) + assert poly1.layer == 0 + assert poly1.datatype == 0 + assert poly1.size == 3 + assert poly1.perimeter() == 3.0 + numpy.testing.assert_array_equal(poly1.points, [[0, 0], [1, 0], [0, 1]]) + + poly2 = gdstk.Polygon([(0, 0), 1j, (1, 0)], 1, 2) + assert poly2.layer == 1 + assert poly2.datatype == 2 + assert poly2.size == 3 + assert poly2.perimeter() == 3.0 + numpy.testing.assert_array_equal(poly2.points, [[0, 0], [0, 1], [1, 0]]) + + poly3 = gdstk.Polygon([(0, 0), (1, 0), 1 + 1j, 1j]) + assert poly3.size == 4 + assert poly3.perimeter() == 4.0 + numpy.testing.assert_array_equal(poly3.points, [[0, 0], [1, 0], [1, 1], [0, 1]]) + + def test_bounding_box(): poly = gdstk.Polygon([-1 + 0j, -2j, 3 + 0j, 4j]) assert poly.bounding_box() == ((-1, -2), (3, 4)) @@ -71,6 +92,7 @@ def test_copy(): numpy.testing.assert_array_equal(p1.points, points) numpy.testing.assert_array_equal(p2.points, points) + def test_deepcopy(): points = [[-1, 0], [0, -2], [3, 0], [0, 4]] p1 = gdstk.Polygon(points, 5, 6) From 2a03d76a3ea4458d56104ec6e79fd179d7565685 Mon Sep 17 00:00:00 2001 From: abdelrahmanabdelghany2 Date: Thu, 18 Jan 2024 15:17:17 +0200 Subject: [PATCH 3/4] Fix polygon perimeter method tests --- tests/polygon_test.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/polygon_test.py b/tests/polygon_test.py index 1582a933a..914b4d701 100644 --- a/tests/polygon_test.py +++ b/tests/polygon_test.py @@ -35,19 +35,19 @@ def test_area_size(): def test_perimeter_size(): - poly1 = gdstk.Polygon([(0, 0), (1, 0), 1j]) + poly1 = gdstk.Polygon([(0, 0), (2, 0), 2 + 2j, 2j]) assert poly1.layer == 0 assert poly1.datatype == 0 - assert poly1.size == 3 - assert poly1.perimeter() == 3.0 - numpy.testing.assert_array_equal(poly1.points, [[0, 0], [1, 0], [0, 1]]) + assert poly1.size == 4 + assert poly1.perimeter() == 8.0 + numpy.testing.assert_array_equal(poly1.points, [[0, 0], [2, 0], [2, 2], [0, 2]]) - poly2 = gdstk.Polygon([(0, 0), 1j, (1, 0)], 1, 2) + poly2 = gdstk.Polygon([(0, 0), (1, 0), 1 + 1j, 1j], 1, 2) assert poly2.layer == 1 assert poly2.datatype == 2 - assert poly2.size == 3 - assert poly2.perimeter() == 3.0 - numpy.testing.assert_array_equal(poly2.points, [[0, 0], [0, 1], [1, 0]]) + assert poly2.size == 4 + assert poly2.perimeter() == 4.0 + numpy.testing.assert_array_equal(poly2.points, [[0, 0], [1, 0], [1, 1], [0, 1]]) poly3 = gdstk.Polygon([(0, 0), (1, 0), 1 + 1j, 1j]) assert poly3.size == 4 From f964d1e31808dfc0780d11d899302fdfd557b747 Mon Sep 17 00:00:00 2001 From: abdelrahmanabdelghany2 Date: Thu, 18 Jan 2024 15:38:57 +0200 Subject: [PATCH 4/4] Undo changes to setup.py --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index ee6b70706..4e0bd2869 100644 --- a/setup.py +++ b/setup.py @@ -102,6 +102,4 @@ def run(self): include_package_data=True, cmdclass={"build_ext": CMakeBuilder}, zip_safe=False, - name="Mabrains_gdstk", - version="0.0.1" )