McRogueFace/src/PyVector.cpp

466 lines
14 KiB
C++

#include "PyVector.h"
#include "PyObjectUtils.h"
#include <cmath>
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}
};
PyMethodDef PyVector::methods[] = {
{"magnitude", (PyCFunction)PyVector::magnitude, METH_NOARGS, "Return the length of the vector"},
{"magnitude_squared", (PyCFunction)PyVector::magnitude_squared, METH_NOARGS, "Return the squared length of the vector"},
{"normalize", (PyCFunction)PyVector::normalize, METH_NOARGS, "Return a unit vector in the same direction"},
{"dot", (PyCFunction)PyVector::dot, METH_O, "Return the dot product with another vector"},
{"distance_to", (PyCFunction)PyVector::distance_to, METH_O, "Return the distance to another vector"},
{"angle", (PyCFunction)PyVector::angle, METH_NOARGS, "Return the angle in radians from the positive X axis"},
{"copy", (PyCFunction)PyVector::copy, METH_NOARGS, "Return a copy of this vector"},
{NULL}
};
namespace mcrfpydef {
PyNumberMethods PyVector_as_number = {
.nb_add = PyVector::add,
.nb_subtract = PyVector::subtract,
.nb_multiply = PyVector::multiply,
.nb_remainder = 0,
.nb_divmod = 0,
.nb_power = 0,
.nb_negative = PyVector::negative,
.nb_positive = 0,
.nb_absolute = PyVector::absolute,
.nb_bool = PyVector::bool_check,
.nb_invert = 0,
.nb_lshift = 0,
.nb_rshift = 0,
.nb_and = 0,
.nb_xor = 0,
.nb_or = 0,
.nb_int = 0,
.nb_reserved = 0,
.nb_float = 0,
.nb_inplace_add = 0,
.nb_inplace_subtract = 0,
.nb_inplace_multiply = 0,
.nb_inplace_remainder = 0,
.nb_inplace_power = 0,
.nb_inplace_lshift = 0,
.nb_inplace_rshift = 0,
.nb_inplace_and = 0,
.nb_inplace_xor = 0,
.nb_inplace_or = 0,
.nb_floor_divide = 0,
.nb_true_divide = PyVector::divide,
.nb_inplace_floor_divide = 0,
.nb_inplace_true_divide = 0,
.nb_index = 0,
.nb_matrix_multiply = 0,
.nb_inplace_matrix_multiply = 0
};
}
PyVector::PyVector(sf::Vector2f target)
:data(target) {}
PyObject* PyVector::pyObject()
{
PyTypeObject* type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
if (!type) return nullptr;
PyVectorObject* obj = (PyVectorObject*)type->tp_alloc(type, 0);
Py_DECREF(type);
if (obj) {
obj->data = data;
}
return (PyObject*)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 << "<Vector (" << v.x << ", " << v.y << ")>";
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 = NULL;
float x=0, y=0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Of", const_cast<char**>(keywords), &leader, &y))
{
//PyErr_SetString(PyExc_TypeError, "mcrfpy.Vector requires a 2-tuple or two numeric values");
return -1;
}
if (leader == NULL || leader == Py_None)
{
self->data = sf::Vector2f();
return 0;
}
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);
return 0;
}
// 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)
{
PyVectorObject* self = (PyVectorObject*)obj;
if (reinterpret_cast<long>(closure) == 0) {
// x
return PyFloat_FromDouble(self->data.x);
} else {
// y
return PyFloat_FromDouble(self->data.y);
}
}
int PyVector::set_member(PyObject* obj, PyObject* value, void* closure)
{
PyVectorObject* self = (PyVectorObject*)obj;
float val;
if (PyFloat_Check(value)) {
val = PyFloat_AsDouble(value);
} else if (PyLong_Check(value)) {
val = PyLong_AsDouble(value);
} else {
PyErr_SetString(PyExc_TypeError, "Vector members must be numeric");
return -1;
}
if (reinterpret_cast<long>(closure) == 0) {
// x
self->data.x = val;
} else {
// y
self->data.y = val;
}
return 0;
}
PyVectorObject* PyVector::from_arg(PyObject* args)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
if (PyObject_IsInstance(args, (PyObject*)type)) return (PyVectorObject*)args;
auto obj = (PyVectorObject*)type->tp_alloc(type, 0);
// Handle different input types
if (PyTuple_Check(args)) {
// It's already a tuple, pass it directly to init
int err = init(obj, args, NULL);
if (err) {
Py_DECREF(obj);
return NULL;
}
} else {
// Wrap single argument in a tuple for init
PyObject* tuple = PyTuple_Pack(1, args);
if (!tuple) {
Py_DECREF(obj);
return NULL;
}
int err = init(obj, tuple, NULL);
Py_DECREF(tuple);
if (err) {
Py_DECREF(obj);
return NULL;
}
}
return obj;
}
// Arithmetic operations
PyObject* PyVector::add(PyObject* left, PyObject* right)
{
// Check if both operands are vectors
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
PyVectorObject* vec1 = nullptr;
PyVectorObject* vec2 = nullptr;
if (PyObject_IsInstance(left, (PyObject*)type) && PyObject_IsInstance(right, (PyObject*)type)) {
vec1 = (PyVectorObject*)left;
vec2 = (PyVectorObject*)right;
} else {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
auto result = (PyVectorObject*)type->tp_alloc(type, 0);
if (result) {
result->data = sf::Vector2f(vec1->data.x + vec2->data.x, vec1->data.y + vec2->data.y);
}
return (PyObject*)result;
}
PyObject* PyVector::subtract(PyObject* left, PyObject* right)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
PyVectorObject* vec1 = nullptr;
PyVectorObject* vec2 = nullptr;
if (PyObject_IsInstance(left, (PyObject*)type) && PyObject_IsInstance(right, (PyObject*)type)) {
vec1 = (PyVectorObject*)left;
vec2 = (PyVectorObject*)right;
} else {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
auto result = (PyVectorObject*)type->tp_alloc(type, 0);
if (result) {
result->data = sf::Vector2f(vec1->data.x - vec2->data.x, vec1->data.y - vec2->data.y);
}
return (PyObject*)result;
}
PyObject* PyVector::multiply(PyObject* left, PyObject* right)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
PyVectorObject* vec = nullptr;
double scalar = 0.0;
// Check for Vector * scalar
if (PyObject_IsInstance(left, (PyObject*)type) && (PyFloat_Check(right) || PyLong_Check(right))) {
vec = (PyVectorObject*)left;
scalar = PyFloat_AsDouble(right);
}
// Check for scalar * Vector
else if ((PyFloat_Check(left) || PyLong_Check(left)) && PyObject_IsInstance(right, (PyObject*)type)) {
scalar = PyFloat_AsDouble(left);
vec = (PyVectorObject*)right;
}
else {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
auto result = (PyVectorObject*)type->tp_alloc(type, 0);
if (result) {
result->data = sf::Vector2f(vec->data.x * scalar, vec->data.y * scalar);
}
return (PyObject*)result;
}
PyObject* PyVector::divide(PyObject* left, PyObject* right)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
// Only support Vector / scalar
if (!PyObject_IsInstance(left, (PyObject*)type) || (!PyFloat_Check(right) && !PyLong_Check(right))) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
PyVectorObject* vec = (PyVectorObject*)left;
double scalar = PyFloat_AsDouble(right);
if (scalar == 0.0) {
PyErr_SetString(PyExc_ZeroDivisionError, "Vector division by zero");
return NULL;
}
auto result = (PyVectorObject*)type->tp_alloc(type, 0);
if (result) {
result->data = sf::Vector2f(vec->data.x / scalar, vec->data.y / scalar);
}
return (PyObject*)result;
}
PyObject* PyVector::negative(PyObject* self)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
PyVectorObject* vec = (PyVectorObject*)self;
auto result = (PyVectorObject*)type->tp_alloc(type, 0);
if (result) {
result->data = sf::Vector2f(-vec->data.x, -vec->data.y);
}
return (PyObject*)result;
}
PyObject* PyVector::absolute(PyObject* self)
{
PyVectorObject* vec = (PyVectorObject*)self;
return PyFloat_FromDouble(std::sqrt(vec->data.x * vec->data.x + vec->data.y * vec->data.y));
}
int PyVector::bool_check(PyObject* self)
{
PyVectorObject* vec = (PyVectorObject*)self;
return (vec->data.x != 0.0f || vec->data.y != 0.0f) ? 1 : 0;
}
PyObject* PyVector::richcompare(PyObject* left, PyObject* right, int op)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
if (!PyObject_IsInstance(left, (PyObject*)type) || !PyObject_IsInstance(right, (PyObject*)type)) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
PyVectorObject* vec1 = (PyVectorObject*)left;
PyVectorObject* vec2 = (PyVectorObject*)right;
bool result = false;
switch (op) {
case Py_EQ:
result = (vec1->data.x == vec2->data.x && vec1->data.y == vec2->data.y);
break;
case Py_NE:
result = (vec1->data.x != vec2->data.x || vec1->data.y != vec2->data.y);
break;
default:
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
if (result)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
// Vector-specific methods
PyObject* PyVector::magnitude(PyVectorObject* self, PyObject* Py_UNUSED(ignored))
{
float mag = std::sqrt(self->data.x * self->data.x + self->data.y * self->data.y);
return PyFloat_FromDouble(mag);
}
PyObject* PyVector::magnitude_squared(PyVectorObject* self, PyObject* Py_UNUSED(ignored))
{
float mag_sq = self->data.x * self->data.x + self->data.y * self->data.y;
return PyFloat_FromDouble(mag_sq);
}
PyObject* PyVector::normalize(PyVectorObject* self, PyObject* Py_UNUSED(ignored))
{
float mag = std::sqrt(self->data.x * self->data.x + self->data.y * self->data.y);
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
auto result = (PyVectorObject*)type->tp_alloc(type, 0);
if (result) {
if (mag > 0.0f) {
result->data = sf::Vector2f(self->data.x / mag, self->data.y / mag);
} else {
// Zero vector remains zero
result->data = sf::Vector2f(0.0f, 0.0f);
}
}
return (PyObject*)result;
}
PyObject* PyVector::dot(PyVectorObject* self, PyObject* other)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
if (!PyObject_IsInstance(other, (PyObject*)type)) {
PyErr_SetString(PyExc_TypeError, "Argument must be a Vector");
return NULL;
}
PyVectorObject* vec2 = (PyVectorObject*)other;
float dot_product = self->data.x * vec2->data.x + self->data.y * vec2->data.y;
return PyFloat_FromDouble(dot_product);
}
PyObject* PyVector::distance_to(PyVectorObject* self, PyObject* other)
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
if (!PyObject_IsInstance(other, (PyObject*)type)) {
PyErr_SetString(PyExc_TypeError, "Argument must be a Vector");
return NULL;
}
PyVectorObject* vec2 = (PyVectorObject*)other;
float dx = self->data.x - vec2->data.x;
float dy = self->data.y - vec2->data.y;
float distance = std::sqrt(dx * dx + dy * dy);
return PyFloat_FromDouble(distance);
}
PyObject* PyVector::angle(PyVectorObject* self, PyObject* Py_UNUSED(ignored))
{
float angle_rad = std::atan2(self->data.y, self->data.x);
return PyFloat_FromDouble(angle_rad);
}
PyObject* PyVector::copy(PyVectorObject* self, PyObject* Py_UNUSED(ignored))
{
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
auto result = (PyVectorObject*)type->tp_alloc(type, 0);
if (result) {
result->data = self->data;
}
return (PyObject*)result;
}