From 1c12e8719c1ed8002c80ee744178f8278e2c7e8c Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sat, 30 Mar 2024 22:32:28 -0400 Subject: [PATCH 1/4] Not bad for a quick first salvo, but I cannot figure out why init isn't cooperating. --- src/McRFPy_API.cpp | 2 +- src/PyVector.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++++ src/PyVector.h | 42 +++++++++++++++++ src/UI.h | 13 ++++++ 4 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 src/PyVector.cpp create mode 100644 src/PyVector.h diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index e5621f0..9f4a013 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -56,7 +56,7 @@ PyObject* PyInit_mcrfpy() using namespace mcrfpydef; PyTypeObject* pytypes[] = { /*SFML exposed types*/ - &PyColorType, /*&PyLinkedColorType,*/ &PyFontType, &PyTextureType, + &PyColorType, /*&PyLinkedColorType,*/ &PyFontType, &PyTextureType, &PyVectorType, /*UI widgets*/ &PyUICaptionType, &PyUISpriteType, &PyUIFrameType, &PyUIEntityType, &PyUIGridType, diff --git a/src/PyVector.cpp b/src/PyVector.cpp new file mode 100644 index 0000000..9438b42 --- /dev/null +++ b/src/PyVector.cpp @@ -0,0 +1,113 @@ +#include "PyVector.h" + +PyGetSetDef PyVector::getsetters[] = { + {"x", (getter)PyVector::get_member, (setter)PyVector::set_member, "X/horizontal component", (void*)0}, + {"y", (getter)PyVector::get_member, (setter)PyVector::set_member, "Y/vertical component", (void*)1}, + {NULL} +}; + +PyVector::PyVector(sf::Vector2f target) +:data(target) {} + +PyObject* PyVector::pyObject() +{ + PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyVectorType, 0); + Py_INCREF(obj); + PyVectorObject* self = (PyVectorObject*)obj; + self->data = data; + return obj; +} + +sf::Vector2f PyVector::fromPy(PyObject* obj) +{ + PyVectorObject* self = (PyVectorObject*)obj; + return self->data; +} + +sf::Vector2f PyVector::fromPy(PyVectorObject* self) +{ + return self->data; +} + +Py_hash_t PyVector::hash(PyObject* obj) +{ + auto self = (PyVectorObject*)obj; + Py_hash_t value = 0; + value += self->data.x; + value << 8; value += self->data.y; + + return value; +} + +PyObject* PyVector::repr(PyObject* obj) +{ + PyVectorObject* self = (PyVectorObject*)obj; + std::ostringstream ss; + sf::Vector2f v = self->data; + ss << ""; + + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + + +int PyVector::init(PyVectorObject* self, PyObject* args, PyObject* kwds) +{ + using namespace mcrfpydef; + static const char* keywords[] = { "x", "y", nullptr }; + PyObject* leader; + double x=0, y=0; + if (!PyArg_ParseTupleAndKeywords, args, kwds, "|Of", leader, &y) + { + //PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values"); + return 0; + } + + // if the "r" arg is already a vector, yoink that color value + if (PyObject_IsInstance(leader, (PyObject*)&PyVectorType)) + { + self->data = ((PyVectorObject*)leader)->data; + return 0; + } + // else if the "r" arg is a 3-tuple, initialize to (r, g, b, 255) + // (if the "r" arg is a 4-tuple, initialize to (r, g, b, a)) + else if (PyTuple_Check(leader)) + { + if (PyTuple_Size(leader) != 2) + { + PyErr_SetString(PyExc_TypeError, "Invalid tuple length: mcrfpy.Vector requires a 2-tuple"); + return -1; + } + x = PyFloat_AsDouble(PyTuple_GetItem(leader, 0)); + y = PyFloat_AsDouble(PyTuple_GetItem(leader, 1)); + + self->data = sf::Vector2f(x, y); + } + // else - + else if (!PyFloat_Check(leader) && !(PyLong_Check(leader))) + { + PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values"); + return -1; + } + if (PyFloat_Check(leader)) x = PyFloat_AsDouble(leader); + else x = PyLong_AsDouble(leader); + self->data = sf::Vector2f(x, y); + return 0; +} + +PyObject* PyVector::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds) +{ + return (PyObject*)type->tp_alloc(type, 0); +} + +PyObject* PyVector::get_member(PyObject* obj, void* closure) +{ + // TODO + return Py_None; +} + +int PyVector::set_member(PyObject* obj, PyObject* value, void* closure) +{ + // TODO + return 0; +} diff --git a/src/PyVector.h b/src/PyVector.h new file mode 100644 index 0000000..6ea0622 --- /dev/null +++ b/src/PyVector.h @@ -0,0 +1,42 @@ +#pragma once +#include "Common.h" +#include "Python.h" + +typedef struct { + PyObject_HEAD + sf::Vector2f data; +} PyVectorObject; + +class PyVector +{ +public: + sf::Vector2f data; + PyVector(sf::Vector2f); + PyVector(); + PyObject* pyObject(); + static sf::Vector2f fromPy(PyObject*); + static sf::Vector2f fromPy(PyVectorObject*); + static PyObject* repr(PyObject*); + static Py_hash_t hash(PyObject*); + static int init(PyVectorObject*, PyObject*, PyObject*); + static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL); + static PyObject* get_member(PyObject*, void*); + static int set_member(PyObject*, PyObject*, void*); + + static PyGetSetDef getsetters[]; +}; + +namespace mcrfpydef { + static PyTypeObject PyVectorType = { + .tp_name = "mcrfpy.Vector", + .tp_basicsize = sizeof(PyVectorObject), + .tp_itemsize = 0, + .tp_repr = PyVector::repr, + .tp_hash = PyVector::hash, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("SFML Vector Object"), + .tp_getset = PyVector::getsetters, + .tp_init = (initproc)PyVector::init, + .tp_new = PyVector::pynew, + }; +} diff --git a/src/UI.h b/src/UI.h index f0f64db..1a47a40 100644 --- a/src/UI.h +++ b/src/UI.h @@ -12,6 +12,7 @@ #include "PyTexture.h" #include "PyColor.h" //#include "PyLinkedColor.h" +#include "PyVector.h" enum PyObjectsEnum : int { @@ -587,6 +588,17 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c return 0; } + static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) + { + return PyVector(self->data->text.getPosition()).pyObject(); + } + + static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) + { + self->data->text.setPosition(PyVector::fromPy(value)); + return 0; + } + static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) { // validate closure (should be impossible to be wrong, but it's thorough) @@ -730,6 +742,7 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c static PyGetSetDef PyUICaption_getsetters[] = { {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, From 8871f6be6e699630cc1ffca131e812ed96917bbd Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sat, 30 Mar 2024 22:51:55 -0400 Subject: [PATCH 2/4] Parse arguments: no args & Vector object args work, tuples and bare numerics still do not --- src/PyVector.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/PyVector.cpp b/src/PyVector.cpp index 9438b42..b76e038 100644 --- a/src/PyVector.cpp +++ b/src/PyVector.cpp @@ -55,16 +55,22 @@ int PyVector::init(PyVectorObject* self, PyObject* args, PyObject* kwds) { using namespace mcrfpydef; static const char* keywords[] = { "x", "y", nullptr }; - PyObject* leader; + PyObject* leader = NULL; double x=0, y=0; - if (!PyArg_ParseTupleAndKeywords, args, kwds, "|Of", leader, &y) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Of", const_cast(keywords), &leader, &y)) { //PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values"); + self->data = sf::Vector2f(); return 0; } + else if (leader == NULL || leader == Py_None) + { + self->data = sf::Vector2f(); + return 0; + } // if the "r" arg is already a vector, yoink that color value - if (PyObject_IsInstance(leader, (PyObject*)&PyVectorType)) + else if (PyObject_IsInstance(leader, (PyObject*)&PyVectorType)) { self->data = ((PyVectorObject*)leader)->data; return 0; From c13e18528912950a3581cbbe1aee2c372ba734f2 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sun, 31 Mar 2024 13:51:29 -0400 Subject: [PATCH 3/4] PyColor fix - Init corrections --- src/PyColor.cpp | 83 ++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/src/PyColor.cpp b/src/PyColor.cpp index 50eae73..4f7f5f8 100644 --- a/src/PyColor.cpp +++ b/src/PyColor.cpp @@ -65,77 +65,62 @@ PyObject* PyColor::repr(PyObject* obj) } -int PyColor::init(PyColorObject* self, PyObject* args, PyObject* kwds) -{ - using namespace mcrfpydef; +int PyColor::init(PyColorObject* self, PyObject* args, PyObject* kwds) { + //using namespace mcrfpydef; static const char* keywords[] = { "r", "g", "b", "a", nullptr }; PyObject* leader; int r = -1, g = -1, b = -1, a = 255; - if (!PyArg_ParseTupleAndKeywords, args, kwds, "O|iii", leader, &g, &b, &a) - { - PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a color object, 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)"); + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iii", const_cast(keywords), &leader, &g, &b, &a)) { + PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)"); return -1; } - // if the "r" arg is already a color, yoink that color value - if (PyObject_IsInstance(leader, (PyObject*)&PyColorType)) - { - self->data = ((PyColorObject*)leader)->data; - return 0; - } - // else if the "r" arg is a 3-tuple, initialize to (r, g, b, 255) - // (if the "r" arg is a 4-tuple, initialize to (r, g, b, a)) - else if (PyTuple_Check(leader)) - { - if (PyTuple_Size(leader) < 3 && PyTuple_Size(leader) > 4) - { - PyErr_SetString(PyExc_TypeError, "Invalid tuple length: mcrfpy.Color requires a color object, 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)"); + //std::cout << "Arg parsing succeeded. Values: " << r << " " << g << " " << b << " " << a < 4) { + PyErr_SetString(PyExc_TypeError, "Invalid tuple length: mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)"); return -1; } r = PyLong_AsLong(PyTuple_GetItem(leader, 0)); g = PyLong_AsLong(PyTuple_GetItem(leader, 1)); b = PyLong_AsLong(PyTuple_GetItem(leader, 2)); - //a = 255; //default value - - if (PyTuple_Size(leader) == 4) - { + if (tupleSize == 4) { a = PyLong_AsLong(PyTuple_GetItem(leader, 3)); } + } + // Color name (not implemented yet) + else if (PyUnicode_Check(leader)) { + PyErr_SetString(PyExc_NotImplementedError, "Color names aren't ready yet"); + return -1; + } + // Check if the leader is actually an integer for the r value + else if (PyLong_Check(leader)) { + r = PyLong_AsLong(leader); + // Additional validation not shown; g, b are required to be parsed + } else { + PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)"); + return -1; + } - // value validation - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); - return -1; - } - self->data = sf::Color(r, g, b, a); - } - // else if the "r" arg is a string, initialize to {color lookup function value} - else if (PyUnicode_Check(leader)) - { - PyErr_SetString(Py_NotImplemented, "Color names aren't ready yet"); - return -1; - } - // else - - else if (!PyLong_Check(leader)) - { - PyErr_SetString(PyExc_TypeError, "mcrfpy.Color requires a color object, 3-tuple, 4-tuple, color name, or integer values within 0-255 (r, g, b, optionally a)"); - return -1; - } - r = PyLong_AsLong(leader); - // assert r, g, b are present and ints in range (0, 255) - if not enough ints were provided to the args/kwds parsed by init, g and/or b will still hold -1. - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "R, G, B values are required, A value is optional; Color values must be between 0 and 255."); + // Validate color values + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) { + PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); return -1; } + self->data = sf::Color(r, g, b, a); return 0; } PyObject* PyColor::pynew(PyTypeObject* type, PyObject* args, PyObject* kwds) { - return (PyObject*)type->tp_alloc(type, 0); + auto obj = (PyObject*)type->tp_alloc(type, 0); + //Py_INCREF(obj); + return obj; } PyObject* PyColor::get_member(PyObject* obj, void* closure) From 5edebdd64316abec6e941d49b210f93d7e88a2d1 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sun, 31 Mar 2024 14:21:07 -0400 Subject: [PATCH 4/4] PyVector init should be pretty reliable now --- src/PyVector.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/PyVector.cpp b/src/PyVector.cpp index b76e038..adf2aff 100644 --- a/src/PyVector.cpp +++ b/src/PyVector.cpp @@ -56,28 +56,19 @@ int PyVector::init(PyVectorObject* self, PyObject* args, PyObject* kwds) using namespace mcrfpydef; static const char* keywords[] = { "x", "y", nullptr }; PyObject* leader = NULL; - double x=0, y=0; + float x=0, y=0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Of", const_cast(keywords), &leader, &y)) { //PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values"); - self->data = sf::Vector2f(); - return 0; + return -1; } - else if (leader == NULL || leader == Py_None) + if (leader == NULL || leader == Py_None) { self->data = sf::Vector2f(); return 0; } - // if the "r" arg is already a vector, yoink that color value - else if (PyObject_IsInstance(leader, (PyObject*)&PyVectorType)) - { - self->data = ((PyVectorObject*)leader)->data; - return 0; - } - // else if the "r" arg is a 3-tuple, initialize to (r, g, b, 255) - // (if the "r" arg is a 4-tuple, initialize to (r, g, b, a)) - else if (PyTuple_Check(leader)) + if (PyTuple_Check(leader)) { if (PyTuple_Size(leader) != 2) { @@ -88,6 +79,7 @@ int PyVector::init(PyVectorObject* self, PyObject* args, PyObject* kwds) y = PyFloat_AsDouble(PyTuple_GetItem(leader, 1)); self->data = sf::Vector2f(x, y); + return 0; } // else - else if (!PyFloat_Check(leader) && !(PyLong_Check(leader)))