1 #include "3d_image_exporter.hpp"
2 #include "image_3d_exporter_wrapper.hpp"
3 #include "nlohmann/json.hpp"
4 #include "util.hpp"
5 #define PYCAIRO_NO_IMPORT
6 #include <py3cairo.h>
7
PyImage3DExporter_new(PyTypeObject * type,PyObject * args,PyObject * kwds)8 static PyObject *PyImage3DExporter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
9 {
10 PyImage3DExporter *self;
11 self = (PyImage3DExporter *)type->tp_alloc(type, 0);
12 if (self != NULL) {
13 self->exporter = nullptr;
14 }
15 return (PyObject *)self;
16 }
17
PyImage3DExporter_dealloc(PyObject * pself)18 static void PyImage3DExporter_dealloc(PyObject *pself)
19 {
20 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
21 delete self->exporter;
22 Py_TYPE(self)->tp_free((PyObject *)self);
23 }
24
PyImage3DExporter_render_to_png(PyObject * pself,PyObject * args)25 static PyObject *PyImage3DExporter_render_to_png(PyObject *pself, PyObject *args)
26 {
27 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
28 const char *filename = nullptr;
29 if (!PyArg_ParseTuple(args, "s", &filename))
30 return NULL;
31 try {
32 auto surf = self->exporter->render_to_surface();
33 surf->write_to_png(filename);
34 }
35 catch (const std::exception &e) {
36 PyErr_SetString(PyExc_IOError, e.what());
37 return NULL;
38 }
39 catch (...) {
40 PyErr_SetString(PyExc_IOError, "unknown exception");
41 return NULL;
42 }
43 Py_RETURN_NONE;
44 }
45
PyImage3DExporter_render_to_surface(PyObject * pself,PyObject * args)46 static PyObject *PyImage3DExporter_render_to_surface(PyObject *pself, PyObject *args)
47 {
48 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
49 try {
50 cairo_surface_t *csurf = nullptr;
51 {
52 auto surf = self->exporter->render_to_surface();
53 csurf = surf->cobj();
54 cairo_surface_reference(csurf);
55 }
56 return PycairoSurface_FromSurface(csurf, NULL);
57 }
58 catch (const std::exception &e) {
59 PyErr_SetString(PyExc_IOError, e.what());
60 return NULL;
61 }
62 catch (...) {
63 PyErr_SetString(PyExc_IOError, "unknown exception");
64 return NULL;
65 }
66 Py_RETURN_NONE;
67 }
68
PyImage3DExporter_view_all(PyObject * pself,PyObject * args)69 static PyObject *PyImage3DExporter_view_all(PyObject *pself, PyObject *args)
70 {
71 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
72 self->exporter->view_all();
73 Py_RETURN_NONE;
74 }
75
PyImage3DExporter_load_3d_models(PyObject * pself,PyObject * args)76 static PyObject *PyImage3DExporter_load_3d_models(PyObject *pself, PyObject *args)
77 {
78 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
79 self->exporter->load_3d_models();
80 Py_RETURN_NONE;
81 }
82
83 static PyMethodDef PyImage3DExporter_methods[] = {
84 {"render_to_png", (PyCFunction)PyImage3DExporter_render_to_png, METH_VARARGS, "Render to PNG"},
85 {"render_to_surface", (PyCFunction)PyImage3DExporter_render_to_surface, METH_NOARGS, "Render to cairo surface"},
86 {"view_all", (PyCFunction)PyImage3DExporter_view_all, METH_NOARGS, "View all"},
87 {"load_3d_models", (PyCFunction)PyImage3DExporter_load_3d_models, METH_NOARGS, "Load 3D models"},
88 {NULL} /* Sentinel */
89 };
90
91 using Color = horizon::Color;
92
ortho_get(PyObject * pself,void * pa)93 static PyObject *ortho_get(PyObject *pself, void *pa)
94 {
95 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
96 return PyBool_FromLong(self->exporter->get_projection() == horizon::Canvas3DBase::Projection::ORTHO);
97 }
98
ortho_set(PyObject * pself,PyObject * pval,void * pa)99 static int ortho_set(PyObject *pself, PyObject *pval, void *pa)
100 {
101 if (!pval) {
102 PyErr_SetString(PyExc_AttributeError, "can't delete attr");
103 return -1;
104 }
105 if (!PyBool_Check(pval)) {
106 PyErr_SetString(PyExc_TypeError, "must be bool");
107 return -1;
108 }
109 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
110 if (pval == Py_True) {
111 self->exporter->set_projection(horizon::Canvas3DBase::Projection::ORTHO);
112 }
113 else {
114 self->exporter->set_projection(horizon::Canvas3DBase::Projection::PERSP);
115 }
116 return 0;
117 }
118
119
120 template <typename T> struct FnGetSet {
121 using Set = void (horizon::Image3DExporterWrapper::*)(const T &v);
122 using Get = const T &(horizon::Image3DExporterWrapper::*)() const;
123 Get get;
124 Set set;
125 };
126
127 #define GET_SET(t_, x_) \
128 static FnGetSet<t_> get_set_##x_{ \
129 &horizon::Image3DExporterWrapper::get_##x_, \
130 &horizon::Image3DExporterWrapper::set_##x_, \
131 };
132
133
134 // clang-format off
135 #define ATTRS \
136 X(bool, render_background) \
137 X(bool, show_models) \
138 X(bool, show_dnp_models) \
139 X(bool, show_silkscreen) \
140 X(bool, show_solder_mask) \
141 X(bool, show_solder_paste) \
142 X(bool, show_substrate) \
143 X(bool, use_layer_colors) \
144 X(bool, show_copper) \
145 X(float, cam_azimuth) \
146 X(float, cam_elevation) \
147 X(float, cam_distance) \
148 X(float, cam_fov) \
149 X(float, center_x) \
150 X(float, center_y) \
151 X(Color, background_top_color) \
152 X(Color, background_bottom_color) \
153 X(Color, solder_mask_color) \
154 X(Color, silkscreen_color) \
155 X(Color, substrate_color)
156 // clang-format on
157
158
159 #define X GET_SET
160 ATTRS
161 #undef X
162
bool_attr_get(PyObject * pself,void * pa)163 static PyObject *bool_attr_get(PyObject *pself, void *pa)
164 {
165 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
166 auto getset = static_cast<FnGetSet<bool> *>(pa);
167 return PyBool_FromLong(std::invoke(getset->get, self->exporter));
168 }
169
bool_attr_set(PyObject * pself,PyObject * pval,void * pa)170 static int bool_attr_set(PyObject *pself, PyObject *pval, void *pa)
171 {
172 if (!pval) {
173 PyErr_SetString(PyExc_AttributeError, "can't delete attr");
174 return -1;
175 }
176 if (!PyBool_Check(pval)) {
177 PyErr_SetString(PyExc_TypeError, "must be bool");
178 return -1;
179 }
180 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
181 auto getset = static_cast<FnGetSet<bool> *>(pa);
182 std::invoke(getset->set, self->exporter, pval == Py_True);
183 return 0;
184 }
185
float_attr_get(PyObject * pself,void * pa)186 static PyObject *float_attr_get(PyObject *pself, void *pa)
187 {
188 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
189 auto getset = static_cast<FnGetSet<float> *>(pa);
190 return PyFloat_FromDouble(std::invoke(getset->get, self->exporter));
191 }
192
float_attr_set(PyObject * pself,PyObject * pval,void * pa)193 static int float_attr_set(PyObject *pself, PyObject *pval, void *pa)
194 {
195 if (!pval) {
196 PyErr_SetString(PyExc_AttributeError, "can't delete attr");
197 return -1;
198 }
199 if (!PyNumber_Check(pval)) {
200 PyErr_SetString(PyExc_TypeError, "must be number");
201 return -1;
202 }
203 auto pfloat = PyNumber_Float(pval);
204 if (!pfloat)
205 return -1;
206 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
207 auto getset = static_cast<FnGetSet<float> *>(pa);
208 std::invoke(getset->set, self->exporter, PyFloat_AsDouble(pfloat));
209 Py_DecRef(pfloat);
210 return 0;
211 }
212
213
Color_attr_get(PyObject * pself,void * pa)214 static PyObject *Color_attr_get(PyObject *pself, void *pa)
215 {
216 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
217 auto getset = static_cast<FnGetSet<Color> *>(pa);
218 auto c = std::invoke(getset->get, self->exporter);
219 return Py_BuildValue("(fff)", c.r, c.g, c.b);
220 }
221
Color_attr_set(PyObject * pself,PyObject * pval,void * pa)222 static int Color_attr_set(PyObject *pself, PyObject *pval, void *pa)
223 {
224 if (!pval) {
225 PyErr_SetString(PyExc_AttributeError, "can't delete attr");
226 return -1;
227 }
228 if (!PySequence_Check(pval)) {
229 PyErr_SetString(PyExc_TypeError, "must be sequence");
230 return -1;
231 }
232 if (PySequence_Length(pval) != 3) {
233 PyErr_SetString(PyExc_TypeError, "must be sequence of length 3");
234 return -1;
235 }
236 Color c;
237 for (size_t idx = 0; idx < 3; idx++) {
238 auto elem = PySequence_GetItem(pval, idx);
239 if (!elem)
240 return -1;
241 if (!PyNumber_Check(elem)) {
242 Py_DecRef(elem);
243 PyErr_SetString(PyExc_TypeError, "elem must be number");
244 return -1;
245 }
246 auto pfloat = PyNumber_Float(elem);
247 if (!pfloat) {
248 Py_DecRef(elem);
249 return -1;
250 }
251 float f = PyFloat_AsDouble(pfloat);
252 switch (idx) {
253 case 0:
254 c.r = f;
255 break;
256
257 case 1:
258 c.g = f;
259 break;
260
261 case 2:
262 c.b = f;
263 break;
264
265 default:;
266 }
267 Py_DecRef(pfloat);
268 Py_DecRef(elem);
269 }
270 auto self = reinterpret_cast<PyImage3DExporter *>(pself);
271 auto getset = static_cast<FnGetSet<Color> *>(pa);
272 std::invoke(getset->set, self->exporter, c);
273 return 0;
274 }
275
276 static PyGetSetDef PyImage3DExporter_getset[] = {
277 #define X(t_, x_) {#x_, &t_##_attr_get, &t_##_attr_set, NULL, static_cast<void *>(&get_set_##x_)},
278 ATTRS
279 #undef X
280 {"ortho", &ortho_get, &ortho_set, NULL, NULL},
281 {NULL},
282 /* Sentinel */};
283
__anon4d20b6d30102null284 PyTypeObject Image3DExporterType = [] {
285 PyTypeObject r = {PyVarObject_HEAD_INIT(NULL, 0)};
286 r.tp_name = "horizon.Image3DExporter";
287 r.tp_basicsize = sizeof(PyImage3DExporter);
288
289 r.tp_itemsize = 0;
290 r.tp_dealloc = PyImage3DExporter_dealloc;
291 r.tp_flags = Py_TPFLAGS_DEFAULT;
292 r.tp_doc = "Image3DExporer";
293
294 r.tp_methods = PyImage3DExporter_methods;
295 r.tp_getset = PyImage3DExporter_getset;
296 r.tp_new = PyImage3DExporter_new;
297 return r;
298 }();
299