Blender V4.3
BPy_Stroke.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BPy_Stroke.h"
10
11#include "../BPy_Convert.h"
12#include "../BPy_Id.h"
13#include "../BPy_MediumType.h"
17
18#include "BLI_sys_types.h"
19
20#ifdef __cplusplus
21extern "C" {
22#endif
23
24using namespace Freestyle;
25
27
28/*----------------------Stroke methods ----------------------------*/
29
30// Stroke ()
31// template<class InputVertexIterator> Stroke (InputVertexIterator begin, InputVertexIterator end)
32//
33// pb: - need to be able to switch representation: InputVertexIterator <=> position
34// - is it even used ? not even in SWIG version
35
37 /* Wrap. */
38 Stroke_doc,
39 "Class hierarchy: :class:`Interface1D` > :class:`Stroke`\n"
40 "\n"
41 "Class to define a stroke. A stroke is made of a set of 2D vertices\n"
42 "(:class:`StrokeVertex`), regularly spaced out. This set of vertices\n"
43 "defines the stroke's backbone geometry. Each of these stroke vertices\n"
44 "defines the stroke's shape and appearance at this vertex position.\n"
45 "\n"
46 ".. method:: Stroke()\n"
47 " Stroke(brother)\n"
48 "\n"
49 " Creates a :class:`Stroke` using the default constructor or copy constructor\n");
50
51static int Stroke_init(BPy_Stroke *self, PyObject *args, PyObject *kwds)
52{
53 static const char *kwlist[] = {"brother", nullptr};
54 PyObject *brother = nullptr;
55
56 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!", (char **)kwlist, &Stroke_Type, &brother)) {
57 return -1;
58 }
59 if (!brother) {
60 self->s = new Stroke();
61 }
62 else {
63 self->s = new Stroke(*(((BPy_Stroke *)brother)->s));
64 }
65 self->py_if1D.if1D = self->s;
66 self->py_if1D.borrowed = false;
67 return 0;
68}
69
70static PyObject *Stroke_iter(PyObject *self)
71{
72 StrokeInternal::StrokeVertexIterator sv_it(((BPy_Stroke *)self)->s->strokeVerticesBegin());
74}
75
76static Py_ssize_t Stroke_sq_length(BPy_Stroke *self)
77{
78 return self->s->strokeVerticesSize();
79}
80
81static PyObject *Stroke_sq_item(BPy_Stroke *self, Py_ssize_t keynum)
82{
83 if (keynum < 0) {
84 keynum += Stroke_sq_length(self);
85 }
86 if (keynum < 0 || keynum >= Stroke_sq_length(self)) {
87 PyErr_Format(PyExc_IndexError, "Stroke[index]: index %d out of range", keynum);
88 return nullptr;
89 }
90 return BPy_StrokeVertex_from_StrokeVertex(self->s->strokeVerticeAt(keynum));
91}
92
94 /* Wrap. */
95 Stroke_compute_sampling_doc,
96 ".. method:: compute_sampling(n)\n"
97 "\n"
98 " Compute the sampling needed to get N vertices. If the\n"
99 " specified number of vertices is less than the actual number of\n"
100 " vertices, the actual sampling value is returned. (To remove Vertices,\n"
101 " use the RemoveVertex() method of this class.)\n"
102 "\n"
103 " :arg n: The number of stroke vertices we eventually want\n"
104 " in our Stroke.\n"
105 " :type n: int\n"
106 " :return: The sampling that must be used in the Resample(float)\n"
107 " method.\n"
108 " :rtype: float");
109
110static PyObject *Stroke_compute_sampling(BPy_Stroke *self, PyObject *args, PyObject *kwds)
111{
112 static const char *kwlist[] = {"n", nullptr};
113 int i;
114
115 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", (char **)kwlist, &i)) {
116 return nullptr;
117 }
118 return PyFloat_FromDouble(self->s->ComputeSampling(i));
119}
120
122 /* Wrap. */
123 Stroke_resample_doc,
124 ".. method:: resample(n)\n"
125 " resample(sampling)\n"
126 "\n"
127 " Resamples the stroke so using one of two methods with the goal\n"
128 " of creating a stroke with fewer points and the same shape.\n"
129 "\n"
130 " :arg n: Resamples the stroke so that it eventually has N points. That means\n"
131 " it is going to add N-vertices_size, where vertices_size is the\n"
132 " number of points we already have. If vertices_size >= N, no\n"
133 " resampling is done.\n"
134 " :type n: int\n"
135 " :arg sampling: Resamples the stroke with a given sampling value. If the\n"
136 " sampling is smaller than the actual sampling value, no resampling is done.\n"
137 " :type sampling: float");
138
139static PyObject *Stroke_resample(BPy_Stroke *self, PyObject *args, PyObject *kwds)
140{
141 static const char *kwlist_1[] = {"n", nullptr};
142 static const char *kwlist_2[] = {"sampling", nullptr};
143 int i;
144 float f;
145
146 if (PyArg_ParseTupleAndKeywords(args, kwds, "i", (char **)kwlist_1, &i)) {
147 if (self->s->Resample(i) < 0) {
148 PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex count) failed");
149 return nullptr;
150 }
151 }
152 else if ((void)PyErr_Clear(),
153 PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f))
154 {
155 if (self->s->Resample(f) < 0) {
156 PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex interval) failed");
157 return nullptr;
158 }
159 }
160 else {
161 PyErr_SetString(PyExc_TypeError, "invalid argument");
162 return nullptr;
163 }
164 Py_RETURN_NONE;
165}
166
168 /* Wrap. */
169 Stroke_insert_vertex_doc,
170 ".. method:: insert_vertex(vertex, next)\n"
171 "\n"
172 " Inserts the StrokeVertex given as argument into the Stroke before the\n"
173 " point specified by next. The length and curvilinear abscissa are\n"
174 " updated consequently.\n"
175 "\n"
176 " :arg vertex: The StrokeVertex to insert in the Stroke.\n"
177 " :type vertex: :class:`StrokeVertex`\n"
178 " :arg next: A StrokeVertexIterator pointing to the StrokeVertex\n"
179 " before which vertex must be inserted.\n"
180 " :type next: :class:`StrokeVertexIterator`");
181
182static PyObject *Stroke_insert_vertex(BPy_Stroke *self, PyObject *args, PyObject *kwds)
183{
184 static const char *kwlist[] = {"vertex", "next", nullptr};
185 PyObject *py_sv = nullptr, *py_sv_it = nullptr;
186
187 if (!PyArg_ParseTupleAndKeywords(args,
188 kwds,
189 "O!O!",
190 (char **)kwlist,
192 &py_sv,
194 &py_sv_it))
195 {
196 return nullptr;
197 }
198
199 /* Make the wrapped StrokeVertex internal. */
200 ((BPy_StrokeVertex *)py_sv)->py_cp.py_if0D.borrowed = true;
201
202 StrokeVertex *sv = ((BPy_StrokeVertex *)py_sv)->sv;
204 self->s->InsertVertex(sv, sv_it);
205 Py_RETURN_NONE;
206}
207
209 /* Wrap. */
210 Stroke_remove_vertex_doc,
211 ".. method:: remove_vertex(vertex)\n"
212 "\n"
213 " Removes the StrokeVertex given as argument from the Stroke. The length\n"
214 " and curvilinear abscissa are updated consequently.\n"
215 "\n"
216 " :arg vertex: the StrokeVertex to remove from the Stroke.\n"
217 " :type vertex: :class:`StrokeVertex`");
218
219static PyObject *Stroke_remove_vertex(BPy_Stroke *self, PyObject *args, PyObject *kwds)
220{
221 static const char *kwlist[] = {"vertex", nullptr};
222 PyObject *py_sv = nullptr;
223
224 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", (char **)kwlist, &StrokeVertex_Type, &py_sv))
225 {
226 return nullptr;
227 }
228 if (((BPy_StrokeVertex *)py_sv)->sv) {
229 self->s->RemoveVertex(((BPy_StrokeVertex *)py_sv)->sv);
230 }
231 else {
232 PyErr_SetString(PyExc_TypeError, "invalid argument");
233 return nullptr;
234 }
235 Py_RETURN_NONE;
236}
237
239 /* Wrap. */
240 Stroke_remove_all_vertices_doc,
241 ".. method:: remove_all_vertices()\n"
242 "\n"
243 " Removes all vertices from the Stroke.");
244
246{
247 self->s->RemoveAllVertices();
248 Py_RETURN_NONE;
249}
250
252 /* Wrap. */
253 Stroke_update_length_doc,
254 ".. method:: update_length()\n"
255 "\n"
256 " Updates the 2D length of the Stroke.");
257
259{
260 self->s->UpdateLength();
261 Py_RETURN_NONE;
262}
263
265 /* Wrap. */
266 Stroke_stroke_vertices_begin_doc,
267 ".. method:: stroke_vertices_begin(t=0.0)\n"
268 "\n"
269 " Returns a StrokeVertexIterator pointing on the first StrokeVertex of\n"
270 " the Stroke. One can specify a sampling value to re-sample the Stroke\n"
271 " on the fly if needed.\n"
272 "\n"
273 " :arg t: The resampling value with which we want our Stroke to be\n"
274 " resampled. If 0 is specified, no resampling is done.\n"
275 " :type t: float\n"
276 " :return: A StrokeVertexIterator pointing on the first StrokeVertex.\n"
277 " :rtype: :class:`StrokeVertexIterator`");
278
279static PyObject *Stroke_stroke_vertices_begin(BPy_Stroke *self, PyObject *args, PyObject *kwds)
280{
281 static const char *kwlist[] = {"t", nullptr};
282 float f = 0.0f;
283
284 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|f", (char **)kwlist, &f)) {
285 return nullptr;
286 }
287 StrokeInternal::StrokeVertexIterator sv_it(self->s->strokeVerticesBegin(f));
289}
290
292 /* Wrap. */
293 Stroke_stroke_vertices_end_doc,
294 ".. method:: stroke_vertices_end()\n"
295 "\n"
296 " Returns a StrokeVertexIterator pointing after the last StrokeVertex\n"
297 " of the Stroke.\n"
298 "\n"
299 " :return: A StrokeVertexIterator pointing after the last StrokeVertex.\n"
300 " :rtype: :class:`StrokeVertexIterator`");
301
303{
304 StrokeInternal::StrokeVertexIterator sv_it(self->s->strokeVerticesEnd());
306}
307
309 /* Wrap. */
310 Stroke_reversed_doc,
311 ".. method:: __reversed__()\n"
312 "\n"
313 " Returns a StrokeVertexIterator iterating over the vertices of the Stroke\n"
314 " in the reversed order (from the last to the first).\n"
315 "\n"
316 " :return: A StrokeVertexIterator pointing after the last StrokeVertex.\n"
317 " :rtype: :class:`StrokeVertexIterator`");
318
320{
321 StrokeInternal::StrokeVertexIterator sv_it(self->s->strokeVerticesEnd());
323}
324
326 /* Wrap. */
327 Stroke_stroke_vertices_size_doc,
328 ".. method:: stroke_vertices_size()\n"
329 "\n"
330 " Returns the number of StrokeVertex constituting the Stroke.\n"
331 "\n"
332 " :return: The number of stroke vertices.\n"
333 " :rtype: int");
334
336{
337 return PyLong_FromLong(self->s->strokeVerticesSize());
338}
339
340static PyMethodDef BPy_Stroke_methods[] = {
341 {"compute_sampling",
342 (PyCFunction)Stroke_compute_sampling,
343 METH_VARARGS | METH_KEYWORDS,
344 Stroke_compute_sampling_doc},
345 {"resample", (PyCFunction)Stroke_resample, METH_VARARGS | METH_KEYWORDS, Stroke_resample_doc},
346 {"remove_all_vertices",
347 (PyCFunction)Stroke_remove_all_vertices,
348 METH_NOARGS,
349 Stroke_remove_all_vertices_doc},
350 {"remove_vertex",
351 (PyCFunction)Stroke_remove_vertex,
352 METH_VARARGS | METH_KEYWORDS,
353 Stroke_remove_vertex_doc},
354 {"insert_vertex",
355 (PyCFunction)Stroke_insert_vertex,
356 METH_VARARGS | METH_KEYWORDS,
357 Stroke_insert_vertex_doc},
358 {"update_length", (PyCFunction)Stroke_update_length, METH_NOARGS, Stroke_update_length_doc},
359 {"stroke_vertices_begin",
360 (PyCFunction)Stroke_stroke_vertices_begin,
361 METH_VARARGS | METH_KEYWORDS,
362 Stroke_stroke_vertices_begin_doc},
363 {"stroke_vertices_end",
364 (PyCFunction)Stroke_stroke_vertices_end,
365 METH_NOARGS,
366 Stroke_stroke_vertices_end_doc},
367 {"__reversed__", (PyCFunction)Stroke_reversed, METH_NOARGS, Stroke_reversed_doc},
368 {"stroke_vertices_size",
369 (PyCFunction)Stroke_stroke_vertices_size,
370 METH_NOARGS,
371 Stroke_stroke_vertices_size_doc},
372 {nullptr, nullptr, 0, nullptr},
373};
374
375/*----------------------Stroke get/setters ----------------------------*/
376
378 /* Wrap. */
379 Stroke_medium_type_doc,
380 "The MediumType used for this Stroke.\n"
381 "\n"
382 ":type: :class:`MediumType`");
383
384static PyObject *Stroke_medium_type_get(BPy_Stroke *self, void * /*closure*/)
385{
386 return BPy_MediumType_from_MediumType(self->s->getMediumType());
387}
388
389static int Stroke_medium_type_set(BPy_Stroke *self, PyObject *value, void * /*closure*/)
390{
391 if (!BPy_MediumType_Check(value)) {
392 PyErr_SetString(PyExc_TypeError, "value must be a MediumType");
393 return -1;
394 }
395 self->s->setMediumType(MediumType_from_BPy_MediumType(value));
396 return 0;
397}
398
400 /* Wrap. */
401 Stroke_texture_id_doc,
402 "The ID of the texture used to simulate th marks system for this Stroke.\n"
403 "\n"
404 ":type: int");
405
406static PyObject *Stroke_texture_id_get(BPy_Stroke *self, void * /*closure*/)
407{
408 return PyLong_FromLong(self->s->getTextureId());
409}
410
411static int Stroke_texture_id_set(BPy_Stroke *self, PyObject *value, void * /*closure*/)
412{
413 uint i = PyLong_AsUnsignedLong(value);
414 if (PyErr_Occurred()) {
415 return -1;
416 }
417 self->s->setTextureId(i);
418 return 0;
419}
420
422 /* Wrap. */
423 Stroke_tips_doc,
424 "True if this Stroke uses a texture with tips, and false otherwise.\n"
425 "\n"
426 ":type: bool");
427
428static PyObject *Stroke_tips_get(BPy_Stroke *self, void * /*closure*/)
429{
430 return PyBool_from_bool(self->s->hasTips());
431}
432
433static int Stroke_tips_set(BPy_Stroke *self, PyObject *value, void * /*closure*/)
434{
435 if (!PyBool_Check(value)) {
436 return -1;
437 }
438 self->s->setTips(bool_from_PyBool(value));
439 return 0;
440}
441
443 /* Wrap. */
444 Stroke_length_2d_doc,
445 "The 2D length of the Stroke.\n"
446 "\n"
447 ":type: float");
448
449static PyObject *Stroke_length_2d_get(BPy_Stroke *self, void * /*closure*/)
450{
451 return PyFloat_FromDouble(self->s->getLength2D());
452}
453
454static int Stroke_length_2d_set(BPy_Stroke *self, PyObject *value, void * /*closure*/)
455{
456 float scalar;
457 if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
458 /* parsed item not a number */
459 PyErr_SetString(PyExc_TypeError, "value must be a number");
460 return -1;
461 }
462 self->s->setLength(scalar);
463 return 0;
464}
465
467 /* Wrap. */
468 Stroke_id_doc,
469 "The Id of this Stroke.\n"
470 "\n"
471 ":type: :class:`Id`");
472
473static PyObject *Stroke_id_get(BPy_Stroke *self, void * /*closure*/)
474{
475 Id id(self->s->getId());
476 return BPy_Id_from_Id(id); // return a copy
477}
478
479static int Stroke_id_set(BPy_Stroke *self, PyObject *value, void * /*closure*/)
480{
481 if (!BPy_Id_Check(value)) {
482 PyErr_SetString(PyExc_TypeError, "value must be an Id");
483 return -1;
484 }
485 self->s->setId(*(((BPy_Id *)value)->id));
486 return 0;
487}
488
489static PyGetSetDef BPy_Stroke_getseters[] = {
490 {"medium_type",
493 Stroke_medium_type_doc,
494 nullptr},
495 {"texture_id",
496 (getter)Stroke_texture_id_get,
497 (setter)Stroke_texture_id_set,
498 Stroke_texture_id_doc,
499 nullptr},
500 {"tips", (getter)Stroke_tips_get, (setter)Stroke_tips_set, Stroke_tips_doc, nullptr},
501 {"length_2d",
502 (getter)Stroke_length_2d_get,
503 (setter)Stroke_length_2d_set,
504 Stroke_length_2d_doc,
505 nullptr},
506 {"id", (getter)Stroke_id_get, (setter)Stroke_id_set, Stroke_id_doc, nullptr},
507 {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
508};
509
510/*-----------------------BPy_Stroke type definition ------------------------------*/
511
512static PySequenceMethods BPy_Stroke_as_sequence = {
513 /*sq_length*/ (lenfunc)Stroke_sq_length,
514 /*sq_concat*/ nullptr,
515 /*sq_repeat*/ nullptr,
516 /*sq_item*/ (ssizeargfunc)Stroke_sq_item,
517 /*was_sq_slice*/ nullptr, /* DEPRECATED. */
518 /*sq_ass_item*/ nullptr,
519 /*was_sq_ass_slice*/ nullptr, /* DEPRECATED. */
520 /*sq_contains*/ nullptr,
521 /*sq_inplace_concat*/ nullptr,
522 /*sq_inplace_repeat*/ nullptr,
523};
524
525PyTypeObject Stroke_Type = {
526 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
527 /*tp_name*/ "Stroke",
528 /*tp_basicsize*/ sizeof(BPy_Stroke),
529 /*tp_itemsize*/ 0,
530 /*tp_dealloc*/ nullptr,
531 /*tp_vectorcall_offset*/ 0,
532 /*tp_getattr*/ nullptr,
533 /*tp_setattr*/ nullptr,
534 /*tp_as_async*/ nullptr,
535 /*tp_repr*/ nullptr,
536 /*tp_as_number*/ nullptr,
537 /*tp_as_sequence*/ &BPy_Stroke_as_sequence,
538 /*tp_as_mapping*/ nullptr,
539 /*tp_hash*/ nullptr,
540 /*tp_call*/ nullptr,
541 /*tp_str*/ nullptr,
542 /*tp_getattro*/ nullptr,
543 /*tp_setattro*/ nullptr,
544 /*tp_as_buffer*/ nullptr,
545 /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
546 /*tp_doc*/ Stroke_doc,
547 /*tp_traverse*/ nullptr,
548 /*tp_clear*/ nullptr,
549 /*tp_richcompare*/ nullptr,
550 /*tp_weaklistoffset*/ 0,
551 /*tp_iter*/ (getiterfunc)Stroke_iter,
552 /*tp_iternext*/ nullptr,
553 /*tp_methods*/ BPy_Stroke_methods,
554 /*tp_members*/ nullptr,
555 /*tp_getset*/ BPy_Stroke_getseters,
556 /*tp_base*/ &Interface1D_Type,
557 /*tp_dict*/ nullptr,
558 /*tp_descr_get*/ nullptr,
559 /*tp_descr_set*/ nullptr,
560 /*tp_dictoffset*/ 0,
561 /*tp_init*/ (initproc)Stroke_init,
562 /*tp_alloc*/ nullptr,
563 /*tp_new*/ nullptr,
564};
565
567
568#ifdef __cplusplus
569}
570#endif
unsigned int uint
PyObject * BPy_StrokeVertexIterator_from_StrokeVertexIterator(StrokeInternal::StrokeVertexIterator &sv_it, bool reversed)
PyObject * BPy_Id_from_Id(Id &id)
bool bool_from_PyBool(PyObject *b)
PyObject * BPy_StrokeVertex_from_StrokeVertex(StrokeVertex &sv)
PyObject * BPy_MediumType_from_MediumType(Stroke::MediumType n)
Stroke::MediumType MediumType_from_BPy_MediumType(PyObject *obj)
PyObject * PyBool_from_bool(bool b)
#define BPy_Id_Check(v)
Definition BPy_Id.h:27
PyTypeObject Interface1D_Type
#define BPy_MediumType_Check(v)
PyTypeObject StrokeVertexIterator_Type
PyTypeObject StrokeVertex_Type
static PyObject * Stroke_remove_vertex(BPy_Stroke *self, PyObject *args, PyObject *kwds)
static int Stroke_medium_type_set(BPy_Stroke *self, PyObject *value, void *)
static PyObject * Stroke_update_length(BPy_Stroke *self)
static Py_ssize_t Stroke_sq_length(BPy_Stroke *self)
static PyObject * Stroke_texture_id_get(BPy_Stroke *self, void *)
PyTypeObject Stroke_Type
static PyObject * Stroke_iter(PyObject *self)
static PyObject * Stroke_remove_all_vertices(BPy_Stroke *self)
static PyObject * Stroke_resample(BPy_Stroke *self, PyObject *args, PyObject *kwds)
static int Stroke_length_2d_set(BPy_Stroke *self, PyObject *value, void *)
static PyObject * Stroke_stroke_vertices_end(BPy_Stroke *self)
static PyObject * Stroke_medium_type_get(BPy_Stroke *self, void *)
static PySequenceMethods BPy_Stroke_as_sequence
static PyObject * Stroke_stroke_vertices_begin(BPy_Stroke *self, PyObject *args, PyObject *kwds)
static PyObject * Stroke_insert_vertex(BPy_Stroke *self, PyObject *args, PyObject *kwds)
static PyObject * Stroke_length_2d_get(BPy_Stroke *self, void *)
static PyMethodDef BPy_Stroke_methods[]
static int Stroke_id_set(BPy_Stroke *self, PyObject *value, void *)
static int Stroke_tips_set(BPy_Stroke *self, PyObject *value, void *)
PyDoc_STRVAR(Stroke_doc, "Class hierarchy: :class:`Interface1D` > :class:`Stroke`\n" "\n" "Class to define a stroke. A stroke is made of a set of 2D vertices\n" "(:class:`StrokeVertex`), regularly spaced out. This set of vertices\n" "defines the stroke's backbone geometry. Each of these stroke vertices\n" "defines the stroke's shape and appearance at this vertex position.\n" "\n" ".. method:: Stroke()\n" " Stroke(brother)\n" "\n" " Creates a :class:`Stroke` using the default constructor or copy constructor\n")
static PyObject * Stroke_tips_get(BPy_Stroke *self, void *)
static PyObject * Stroke_reversed(BPy_Stroke *self)
static int Stroke_texture_id_set(BPy_Stroke *self, PyObject *value, void *)
static PyObject * Stroke_stroke_vertices_size(BPy_Stroke *self)
static PyObject * Stroke_compute_sampling(BPy_Stroke *self, PyObject *args, PyObject *kwds)
static int Stroke_init(BPy_Stroke *self, PyObject *args, PyObject *kwds)
static PyGetSetDef BPy_Stroke_getseters[]
static PyObject * Stroke_sq_item(BPy_Stroke *self, Py_ssize_t keynum)
static PyObject * Stroke_id_get(BPy_Stroke *self, void *)
PyObject * self
inherits from class Rep
Definition AppCanvas.cpp:20