Skip to content

Commit

Permalink
add python module
Browse files Browse the repository at this point in the history
  • Loading branch information
carrotIndustries committed May 12, 2019
1 parent 78aea4c commit 5bc72b4
Show file tree
Hide file tree
Showing 8 changed files with 420 additions and 0 deletions.
39 changes: 39 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,11 @@ SRC_OCE = \
src/util/step_importer.cpp\
src/export_step/export_step.cpp\

SRC_PYTHON = \
src/python_module/horizonmodule.cpp \
src/python_module/util.cpp \
src/python_module/schematic.cpp \
src/python_module/project.cpp \

SRC_ALL = $(sort $(SRC_COMMON) $(SRC_IMP) $(SRC_POOL_UTIL) $(SRC_PRJ_UTIL) $(SRC_POOL_UPDATE_PARA) $(SRC_PGM_TEST) $(SRC_POOL_PRJ_MGR) $(SRC_GEN_PKG))

Expand Down Expand Up @@ -536,14 +541,39 @@ else
LDFLAGS += -fuse-ld=gold
endif

SRC_SHARED = $(SRC_COMMON) \
src/pool/pool_cached.cpp \
src/export_pdf/export_pdf.cpp \
src/canvas/canvas.cpp \
src/canvas/render.cpp \
src/canvas/draw.cpp \
src/canvas/text.cpp \
src/canvas/hershey_fonts.cpp \
src/canvas/image.cpp\
src/canvas/selectables.cpp\
src/canvas/fragment_cache.cpp\
src/util/text_data.cpp \
3rd_party/polypartition/polypartition.cpp\
3rd_party/poly2tri/common/shapes.cpp\
3rd_party/poly2tri/sweep/cdt.cpp\
3rd_party/poly2tri/sweep/sweep.cpp\
3rd_party/poly2tri/sweep/sweep_context.cpp\
3rd_party/poly2tri/sweep/advancing_front.cpp\
src/export_bom/export_bom.cpp

# Object files
OBJ_ALL = $(SRC_ALL:.cpp=.o)
OBJ_ROUTER = $(SRC_ROUTER:.cpp=.o)
OBJ_COMMON = $(SRC_COMMON:.cpp=.o)
OBJ_OCE = $(SRC_OCE:.cpp=.o)
OBJ_PYTHON = $(SRC_PYTHON:.cpp=.o)
OBJ_SHARED = $(SRC_SHARED:.cpp=.oshared)



INC_ROUTER = -I3rd_party/router/include/ -I3rd_party/router -I3rd_party
INC_OCE = -I/opt/opencascade/inc/ -I/mingw64/include/oce/ -I/usr/include/oce -I/usr/include/opencascade -I${CASROOT}/include/opencascade -I/usr/local/include/OpenCASCADE
INC_PYTHON = $(shell pkg-config --cflags python3)
LDFLAGS_OCE = -L /opt/opencascade/lib/ -L${CASROOT}/lib -lTKSTEP -lTKernel -lTKXCAF -lTKXSBase -lTKBRep -lTKCDF -lTKXDESTEP -lTKLCAF -lTKMath -lTKMesh -lTKTopAlgo -lTKPrim -lTKBO -lTKG3d
ifeq ($(OS),Windows_NT)
LDFLAGS_OCE += -lTKV3d
Expand Down Expand Up @@ -583,15 +613,24 @@ horizon-pgm-test: $(OBJ_COMMON) $(SRC_PGM_TEST:.cpp=.o)
horizon-gen-pkg: $(OBJ_COMMON) $(SRC_GEN_PKG:.cpp=.o)
$(CXX) $^ $(LDFLAGS) $(INC) $(CXXFLAGS) $(shell $(PKGCONFIG) --libs $(LIBS_COMMON) glibmm-2.4 giomm-2.4) -o $@

horizon.so: $(OBJ_PYTHON) $(OBJ_SHARED)
$(CXX) $^ $(LDFLAGS) $(INC) $(CXXFLAGS) $(shell $(PKGCONFIG) --libs $(LIBS_COMMON) python3 glibmm-2.4 giomm-2.4) -lpodofo -shared -o $@

$(OBJ_ALL): %.o: %.cpp
$(CXX) -c $(INC) $(CXXFLAGS) $< -o $@

$(OBJ_SHARED): %.oshared: %.cpp
$(CXX) -c $(INC) $(CXXFLAGS) -fPIC $< -o $@

$(OBJ_ROUTER): %.o: %.cpp
$(CXX) -c $(INC) $(INC_ROUTER) $(CXXFLAGS) $< -o $@

$(OBJ_OCE): %.o: %.cpp
$(CXX) -c $(INC) $(INC_OCE) $(CXXFLAGS) $< -o $@

$(OBJ_PYTHON): %.o: %.cpp
$(CXX) -c -fPIC $(INC) $(INC_PYTHON) $(CXXFLAGS) $< -o $@

$(OBJ_RES): %.res: %.rc
windres $< -O coff -o $@

Expand Down
43 changes: 43 additions & 0 deletions src/python_module/horizonmodule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <Python.h>

#include "pool/pool_manager.hpp"

#include <giomm/init.h>

#include "util.hpp"
#include "schematic.hpp"
#include "project.hpp"

PyDoc_STRVAR(module_doc, "Parts of horizon as a module");

static struct PyModuleDef horizonmodule = {
PyModuleDef_HEAD_INIT, "horizon", module_doc, -1, NULL, NULL, NULL, NULL, NULL};

extern "C" {
PyMODINIT_FUNC PyInit_horizon(void);
}

PyMODINIT_FUNC PyInit_horizon(void)
{
Gio::init();
horizon::PoolManager::init();

if (PyType_Ready(&ProjectType) < 0)
return NULL;

if (PyType_Ready(&SchematicType) < 0)
return NULL;

if (!json_init())
return NULL;

PyObject *m;

m = PyModule_Create(&horizonmodule);
if (m == NULL)
return NULL;

Py_INCREF(&ProjectType);
PyModule_AddObject(m, "Project", (PyObject *)&ProjectType);
return m;
}
108 changes: 108 additions & 0 deletions src/python_module/project.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "project.hpp"
#include "schematic.hpp"
#include "pool/pool_manager.hpp"

ProjectWrapper::ProjectWrapper(const std::string &path) : project(horizon::Project::new_from_file(path))
{
}

static PyObject *PyProject_get_name(PyObject *pself)
{
auto self = reinterpret_cast<PyProject *>(pself);
return PyUnicode_FromString(self->project->project.name.c_str());
}

static PyObject *PyProject_get_title(PyObject *pself)
{
auto self = reinterpret_cast<PyProject *>(pself);
return PyUnicode_FromString(self->project->project.title.c_str());
}

static PyObject *PyProject_open_top_schematic(PyObject *pself)
{
auto self = reinterpret_cast<PyProject *>(pself);
if (horizon::PoolManager::get().get_by_uuid(self->project->project.pool_uuid) == nullptr) {
PyErr_SetString(PyExc_FileNotFoundError, "pool not found");
return NULL;
}
SchematicWrapper *schematic = nullptr;
try {
auto top_block = self->project->project.get_top_block();
schematic = new SchematicWrapper(self->project->project, top_block.uuid);
}
catch (const std::exception &e) {
PyErr_SetString(PyExc_IOError, e.what());
return NULL;
}
catch (...) {
PyErr_SetString(PyExc_IOError, "unknown exception");
return NULL;
}

PySchematic *sch = PyObject_New(PySchematic, &SchematicType);
sch->schematic = schematic;
return reinterpret_cast<PyObject *>(sch);
}


static PyObject *PyProject_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyProject *self;
self = (PyProject *)type->tp_alloc(type, 0);
if (self != NULL) {
self->project = nullptr;
}
return (PyObject *)self;
}

static void PyProject_dealloc(PyObject *pself)
{
auto self = reinterpret_cast<PyProject *>(pself);
delete self->project;
Py_TYPE(self)->tp_free((PyObject *)self);
}

static int PyProject_init(PyObject *pself, PyObject *args, PyObject *kwds)
{
auto self = reinterpret_cast<PyProject *>(pself);
const char *path;
if (!PyArg_ParseTuple(args, "s", &path))
return -1;
ProjectWrapper *new_project = nullptr;
try {
new_project = new ProjectWrapper(path);
}
catch (const std::exception &e) {
PyErr_SetString(PyExc_IOError, e.what());
return -1;
}
catch (...) {
PyErr_SetString(PyExc_IOError, "unknown exception");
return -1;
}
delete self->project;
self->project = new_project;
return 0;
}

static PyMethodDef PyProject_methods[] = {
{"get_name", (PyCFunction)PyProject_get_name, METH_NOARGS, "Return project name"},
{"get_title", (PyCFunction)PyProject_get_title, METH_NOARGS, "Return project title"},
{"open_top_schematic", (PyCFunction)PyProject_open_top_schematic, METH_NOARGS, "Open top block"},
{NULL} /* Sentinel */
};


PyTypeObject ProjectType = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "horizon.Project",
.tp_basicsize = sizeof(PyProject),

.tp_itemsize = 0,
.tp_dealloc = PyProject_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "Project",

.tp_methods = PyProject_methods,
.tp_init = PyProject_init,
.tp_new = PyProject_new,
};
17 changes: 17 additions & 0 deletions src/python_module/project.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once
#include <Python.h>
#include "project/project.hpp"

extern PyTypeObject ProjectType;

class ProjectWrapper {
public:
ProjectWrapper(const std::string &path);
horizon::Project project;
};

typedef struct {
PyObject_HEAD
/* Type-specific fields go here. */
ProjectWrapper *project;
} PyProject;
126 changes: 126 additions & 0 deletions src/python_module/schematic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include "schematic.hpp"
#include "project/project.hpp"
#include "pool/pool_manager.hpp"
#include "block/block.hpp"
#include "schematic/schematic.hpp"
#include "pool/pool_cached.hpp"
#include "nlohmann/json.hpp"
#include "export_pdf/export_pdf.hpp"
#include "export_bom/export_bom.hpp"
#include "util.hpp"
#include <podofo/podofo.h>

SchematicWrapper::SchematicWrapper(const horizon::Project &prj, const horizon::UUID &block_uuid)
: pool(horizon::PoolManager::get().get_by_uuid(prj.pool_uuid)->base_path, prj.pool_cache_directory),
block(horizon::Block::new_from_file(prj.blocks.at(block_uuid).block_filename, pool)),
schematic(horizon::Schematic::new_from_file(prj.blocks.at(block_uuid).schematic_filename, block, pool))
{
schematic.expand();
}


static PyObject *PySchematic_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PySchematic *self;
self = (PySchematic *)type->tp_alloc(type, 0);
if (self != NULL) {
self->schematic = nullptr;
}
return (PyObject *)self;
}

static void PySchematic_dealloc(PyObject *pself)
{
auto self = reinterpret_cast<PySchematic *>(pself);
delete self->schematic;
Py_TYPE(self)->tp_free((PyObject *)self);
}


static PyObject *PySchematic_get_pdf_export_settings(PyObject *pself)
{
auto self = reinterpret_cast<PySchematic *>(pself);
auto settings = self->schematic->schematic.pdf_export_settings.serialize();
return py_from_json(settings);
}

static PyObject *PySchematic_export_pdf(PyObject *pself, PyObject *args)
{
auto self = reinterpret_cast<PySchematic *>(pself);
PyObject *py_export_settings = nullptr;
if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &py_export_settings))
return NULL;
try {
auto settings_json = json_from_py(py_export_settings);
horizon::PDFExportSettings settings(settings_json);
horizon::export_pdf(self->schematic->schematic, settings, nullptr);
}
catch (const std::exception &e) {
PyErr_SetString(PyExc_IOError, e.what());
return NULL;
}
catch (const PoDoFo::PdfError &e) {
PyErr_SetString(PyExc_IOError, e.what());
return NULL;
}
catch (...) {
PyErr_SetString(PyExc_IOError, "unknown exception");
return NULL;
}
Py_RETURN_NONE;
}


static PyObject *PySchematic_get_bom_export_settings(PyObject *pself)
{
auto self = reinterpret_cast<PySchematic *>(pself);
auto settings = self->schematic->block.bom_export_settings.serialize();
return py_from_json(settings);
}

static PyObject *PySchematic_export_bom(PyObject *pself, PyObject *args)
{
auto self = reinterpret_cast<PySchematic *>(pself);
PyObject *py_export_settings = nullptr;
if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &py_export_settings))
return NULL;
try {
auto settings_json = json_from_py(py_export_settings);
horizon::BOMExportSettings settings(settings_json);
horizon::export_BOM(settings.output_filename, self->schematic->block, settings);
}
catch (const std::exception &e) {
PyErr_SetString(PyExc_IOError, e.what());
return NULL;
}
catch (...) {
PyErr_SetString(PyExc_IOError, "unknown exception");
return NULL;
}

Py_RETURN_NONE;
}


static PyMethodDef PySchematic_methods[] = {
{"get_pdf_export_settings", (PyCFunction)PySchematic_get_pdf_export_settings, METH_NOARGS,
"Return pdf export settings"},
{"get_bom_export_settings", (PyCFunction)PySchematic_get_bom_export_settings, METH_NOARGS,
"Return bom export settings"},
{"export_pdf", (PyCFunction)PySchematic_export_pdf, METH_VARARGS, "Export as pdf"},
{"export_bom", (PyCFunction)PySchematic_export_bom, METH_VARARGS, "Export BOM"},
{NULL} /* Sentinel */
};

PyTypeObject SchematicType = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "horizon.Schematic",
.tp_basicsize = sizeof(PySchematic),

.tp_itemsize = 0,
.tp_dealloc = PySchematic_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "Schematic",

.tp_methods = PySchematic_methods,
.tp_new = PySchematic_new,
};
22 changes: 22 additions & 0 deletions src/python_module/schematic.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include <Python.h>
#include "block/block.hpp"
#include "schematic/schematic.hpp"
#include "project/project.hpp"
#include "pool/pool_cached.hpp"

extern PyTypeObject SchematicType;

class SchematicWrapper {
public:
SchematicWrapper(const horizon::Project &prj, const horizon::UUID &block_uuid);
horizon::PoolCached pool;
horizon::Block block;
horizon::Schematic schematic;
};

typedef struct {
PyObject_HEAD
/* Type-specific fields go here. */
SchematicWrapper *schematic;
} PySchematic;
Loading

1 comment on commit 5bc72b4

@endofexclusive
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very nice!
And a good demonstration of how well though out the the Horizon implementation is: zero changes of the Horizon internals needed for this feature. Thanks!

Please sign in to comment.