Add Color.__eq__/__ne__ for value comparison, closes #307
Color had __hash__ but no __eq__/__ne__, violating the Python convention that hashable objects must support equality comparison. Two Color objects with identical RGBA values would not compare equal. Now supports comparison with Color objects, tuples, and lists: Color(255, 0, 0) == Color(255, 0, 0) # True Color(255, 0, 0) == (255, 0, 0) # True Color(255, 0, 0) != (0, 0, 0) # True Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
41d5007371
commit
95463bdc78
2 changed files with 66 additions and 8 deletions
|
|
@ -367,36 +367,92 @@ PyObject* PyColor::lerp(PyColorObject* self, PyObject* args)
|
|||
{
|
||||
PyObject* other_obj;
|
||||
float t;
|
||||
|
||||
|
||||
if (!PyArg_ParseTuple(args, "Of", &other_obj, &t)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Validate other color
|
||||
if (!PyObject_IsInstance(other_obj, (PyObject*)&mcrfpydef::PyColorType)) {
|
||||
PyErr_SetString(PyExc_TypeError, "First argument must be a Color");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyColorObject* other = (PyColorObject*)other_obj;
|
||||
|
||||
|
||||
// Clamp t to [0, 1]
|
||||
if (t < 0.0f) t = 0.0f;
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
|
||||
|
||||
// Perform linear interpolation
|
||||
sf::Uint8 r = static_cast<sf::Uint8>(self->data.r + (other->data.r - self->data.r) * t);
|
||||
sf::Uint8 g = static_cast<sf::Uint8>(self->data.g + (other->data.g - self->data.g) * t);
|
||||
sf::Uint8 b = static_cast<sf::Uint8>(self->data.b + (other->data.b - self->data.b) * t);
|
||||
sf::Uint8 a = static_cast<sf::Uint8>(self->data.a + (other->data.a - self->data.a) * t);
|
||||
|
||||
|
||||
// Create new Color object
|
||||
auto type = &mcrfpydef::PyColorType;
|
||||
PyColorObject* result = (PyColorObject*)type->tp_alloc(type, 0);
|
||||
|
||||
|
||||
if (result) {
|
||||
result->data = sf::Color(r, g, b, a);
|
||||
}
|
||||
|
||||
|
||||
return (PyObject*)result;
|
||||
}
|
||||
|
||||
PyObject* PyColor::richcompare(PyObject* left, PyObject* right, int op)
|
||||
{
|
||||
// Only support == and !=
|
||||
if (op != Py_EQ && op != Py_NE) {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
// Extract RGBA from left operand
|
||||
sf::Color left_c, right_c;
|
||||
|
||||
if (PyObject_IsInstance(left, (PyObject*)&mcrfpydef::PyColorType)) {
|
||||
left_c = ((PyColorObject*)left)->data;
|
||||
} else {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
// Extract RGBA from right operand - accept Color, tuple, or list
|
||||
if (PyObject_IsInstance(right, (PyObject*)&mcrfpydef::PyColorType)) {
|
||||
right_c = ((PyColorObject*)right)->data;
|
||||
} else if (PyTuple_Check(right) || PyList_Check(right)) {
|
||||
Py_ssize_t size = PySequence_Size(right);
|
||||
if (size < 3 || size > 4) {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
PyObject* r = PySequence_GetItem(right, 0);
|
||||
PyObject* g = PySequence_GetItem(right, 1);
|
||||
PyObject* b = PySequence_GetItem(right, 2);
|
||||
if (!PyLong_Check(r) || !PyLong_Check(g) || !PyLong_Check(b)) {
|
||||
Py_DECREF(r); Py_DECREF(g); Py_DECREF(b);
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
right_c.r = (sf::Uint8)PyLong_AsLong(r);
|
||||
right_c.g = (sf::Uint8)PyLong_AsLong(g);
|
||||
right_c.b = (sf::Uint8)PyLong_AsLong(b);
|
||||
Py_DECREF(r); Py_DECREF(g); Py_DECREF(b);
|
||||
if (size == 4) {
|
||||
PyObject* a = PySequence_GetItem(right, 3);
|
||||
if (PyLong_Check(a)) right_c.a = (sf::Uint8)PyLong_AsLong(a);
|
||||
Py_DECREF(a);
|
||||
} else {
|
||||
right_c.a = 255;
|
||||
}
|
||||
} else {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
bool equal = (left_c.r == right_c.r && left_c.g == right_c.g &&
|
||||
left_c.b == right_c.b && left_c.a == right_c.a);
|
||||
|
||||
if (op == Py_EQ) {
|
||||
return PyBool_FromLong(equal);
|
||||
} else {
|
||||
return PyBool_FromLong(!equal);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public:
|
|||
static sf::Color fromPy(PyColorObject*);
|
||||
static PyObject* repr(PyObject*);
|
||||
static Py_hash_t hash(PyObject*);
|
||||
static PyObject* richcompare(PyObject*, PyObject*, int);
|
||||
static int init(PyColorObject*, PyObject*, PyObject*);
|
||||
static PyObject* pynew(PyTypeObject* type, PyObject* args=NULL, PyObject* kwds=NULL);
|
||||
static PyObject* get_member(PyObject*, void*);
|
||||
|
|
@ -75,6 +76,7 @@ namespace mcrfpydef {
|
|||
" anim = mcrfpy.Animation('fill_color.r', 255, 0.5, 'linear')\n"
|
||||
" anim.start(frame)\n"
|
||||
),
|
||||
.tp_richcompare = PyColor::richcompare,
|
||||
.tp_methods = PyColor::methods,
|
||||
.tp_getset = PyColor::getsetters,
|
||||
.tp_init = (initproc)PyColor::init,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue