diff --git a/src/pymod/bridge_sample/bridge.cc b/src/pymod/bridge_sample/bridge.cc new file mode 100644 index 000000000..87c9db6af --- /dev/null +++ b/src/pymod/bridge_sample/bridge.cc @@ -0,0 +1,123 @@ + +#include + +#include "pyaConvert.h" +#include "pyaRefs.h" +#include "dbPolygon.h" + +static PyObject *BridgeError; + +static PyObject * +bridge_a2p (PyObject * /*self*/, PyObject *args) +{ + PyObject *a = NULL; + if (! PyArg_ParseTuple (args, "O", &a)) { + return NULL; + } + + // Iterate over the array elements + pya::PythonRef iterator (PyObject_GetIter (a)); + if (! iterator) { + return NULL; + } + + // Prepare a vector of points we can create the polygon from later + std::vector points; + + PyObject *item; + while ((item = PyIter_Next (iterator.get ())) != NULL) { + + // Iterate over the x/y pair + pya::PythonRef xy_iterator (PyObject_GetIter (item)); + if (! xy_iterator) { + return NULL; + } + + double c[2] = { 0.0, 0.0 }; + + // Gets the x and y value + for (int i = 0; i < 2; ++i) { + pya::PythonRef xy_item (PyIter_Next (xy_iterator.get ())); + if (! xy_item) { + return NULL; + } + if (pya::test_type (xy_item.get ())) { + c[i] = pya::python2c (xy_item.get ()); + } + } + + points.push_back (db::DPoint (c[0], c[1])); + + } + + // Handle iteration errors + if (PyErr_Occurred()) { + return NULL; + } + + // Create and return a new object of db::DSimplePolygon type + db::DSimplePolygon *poly = new db::DSimplePolygon (); + poly->assign_hull (points.begin (), points.end ()); + return pya::c2python_new (poly); +} + +static PyObject * +bridge_p2a (PyObject * /*self*/, PyObject *args) +{ + // Parse the command line arguments + PyObject *p = NULL; + if (! PyArg_ParseTuple (args, "O", &p)) { + return NULL; + } + + // Report an error if the input isn't a db::DSimplePolygon + if (! pya::test_type (p)) { + PyErr_SetString (BridgeError, "Expected a db::DSimplePolygon type"); + return NULL; + } + + // Obtain the db::DSimplePolygon + const db::DSimplePolygon &poly = pya::python2c (p); + + // Prepare an array for the points + PyObject *array = PyList_New (poly.hull ().size ()); + Py_INCREF (array); + + // Iterate over the points and fill the array with x/y tuples + int i = 0; + for (db::DSimplePolygon::polygon_contour_iterator pt = poly.hull ().begin (); pt != poly.hull ().end (); ++pt, ++i) { + PyObject *point = PyTuple_New (2); + PyTuple_SET_ITEM (point, 0, pya::c2python ((*pt).x ())); + PyTuple_SET_ITEM (point, 1, pya::c2python ((*pt).y ())); + PyList_SetItem (array, i, point); + } + + return array; +} + +static PyMethodDef BridgeMethods[] = { + { + "p2a", bridge_p2a, METH_VARARGS, + "Converts a DSimplePolygon to an array." + }, + { + "a2p", bridge_a2p, METH_VARARGS, + "Converts an array to a DSimplePolygon." + }, + { NULL, NULL, 0, NULL } // terminal +}; + +PyMODINIT_FUNC +initbridge () +{ + PyObject *m; + + m = Py_InitModule ("bridge", BridgeMethods); + if (m == NULL) { + return; + } + + BridgeError = PyErr_NewException ((char *) "bridge.error", NULL, NULL); + Py_INCREF (BridgeError); + PyModule_AddObject (m, "error", BridgeError); +} diff --git a/src/pymod/bridge_sample/bridge_sample.pro b/src/pymod/bridge_sample/bridge_sample.pro new file mode 100644 index 000000000..8c860b483 --- /dev/null +++ b/src/pymod/bridge_sample/bridge_sample.pro @@ -0,0 +1,92 @@ + +# The "bridge" library is a sample library illustrating how to access +# KLayout data from PyObject and vice versa. +# +# It demonstrates how to translate a db::SimplePolygon object to an +# array of x/y floating point pairs and back. +# +# To build this module externally you'll need to supply these variables in +# qmake: +# - KLAYOUTINCLUDE: path to the KLayout sources +# - KLAYOUTLIBDIR: path to the KLayout libraries (.so/.dll) +# - PYTHONINCLUDE: path to the Python include file +# - PYTHONLIBFILE: path to the Python library file +# +# Normally, these paths are set by the build script. +# +# To use the bridge library, ... + +!isEmpty(KLAYOUTINCLUDE) { + INC = $$KLAYOUTINCLUDE +} else { + INC = $$PWD/../.. +} + +!isEmpty(KLAYOUTLIBDIR) { + LIBDIR = $$KLAYOUTLIBDIR +} else { + LIBDIR = $$OUT_PWD/../.. +} + +# That's were we are going to put our Python module to +DESTDIR = $$OUT_PWD/../.. + +# We're going to build a library +TEMPLATE = lib + +# That's how we name our library +TARGET = bridge + +# The only source +SOURCES = bridge.cc + +# Include QtCore required for some includes +QT = core + +# Further dependencies include: +# - Python (of course) +# - GSI (generic scripting interface) +# - TL (basic toolkit) +# - PYA (Python binding for GSI) +INCLUDEPATH += $$PYTHONINCLUDE $$INC/tl/tl $$INC/gsi/gsi $$INC/pya/pya +DEPENDPATH += $$PYTHONINCLUDE $$INC/tl/tl $$INC/gsi/gsi $$INC/pya/pya +LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_gsi -lklayout_pya + +# Also include DB as this is our sample +INCLUDEPATH += $$INC/db/db +DEPENDPATH += $$INC/db/db +LIBS += -L$$LIBDIR -lklayout_db + +# Pull in RPATH +!isEmpty(RPATH) { + QMAKE_RPATHDIR += $$RPATH +} + +# Some standard compiler warnings on +QMAKE_CXXFLAGS_WARN_ON += \ + -pedantic \ + -Woverloaded-virtual \ + -Wsign-promo \ + -Wsynth \ + -Wno-deprecated \ + -Wno-long-long \ + -Wno-strict-aliasing \ + -Wno-deprecated-declarations \ + -Wno-reserved-user-defined-literal \ + +# Python is somewhat sloppy and relies on the compiler initializing fields +# of strucs to 0: +QMAKE_CXXFLAGS_WARN_ON += \ + -Wno-missing-field-initializers + +win32 { + # to avoid the major version being appended to the dll name - in this case -lxyz won't link it again + # because the library is called xyx0.dll. + CONFIG += skip_target_version_ext +} else { + # Make the target library without the "lib" prefix on Linux + QMAKE_POST_LINK += $(COPY) $(DESTDIR)$(TARGET) $$DESTDIR/$${TARGET}.so +} + +# nothing to install as we're building a test library +INSTALLS =