Merge commit '81396a5322a7a48764fcf254d5d933ba1e57bdc5' as 'kiwi'

This commit is contained in:
2024-02-11 15:32:50 -06:00
76 changed files with 13184 additions and 0 deletions

222
kiwi/py/src/constraint.cpp Normal file
View File

@@ -0,0 +1,222 @@
/*-----------------------------------------------------------------------------
| Copyright (c) 2013-2019, Nucleic Development Team.
|
| Distributed under the terms of the Modified BSD License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
#include <algorithm>
#include <sstream>
#include <cppy/cppy.h>
#include <kiwi/kiwi.h>
#include "types.h"
#include "util.h"
namespace kiwisolver
{
namespace
{
PyObject *
Constraint_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
static const char *kwlist[] = {"expression", "op", "strength", 0};
PyObject *pyexpr;
PyObject *pyop;
PyObject *pystrength = 0;
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "OO|O:__new__", const_cast<char **>(kwlist),
&pyexpr, &pyop, &pystrength))
return 0;
if (!Expression::TypeCheck(pyexpr))
return cppy::type_error(pyexpr, "Expression");
kiwi::RelationalOperator op;
if (!convert_to_relational_op(pyop, op))
return 0;
double strength = kiwi::strength::required;
if (pystrength && !convert_to_strength(pystrength, strength))
return 0;
cppy::ptr pycn(PyType_GenericNew(type, args, kwargs));
if (!pycn)
return 0;
Constraint *cn = reinterpret_cast<Constraint *>(pycn.get());
cn->expression = reduce_expression(pyexpr);
if (!cn->expression)
return 0;
kiwi::Expression expr(convert_to_kiwi_expression(cn->expression));
new (&cn->constraint) kiwi::Constraint(expr, op, strength);
return pycn.release();
}
void Constraint_clear(Constraint *self)
{
Py_CLEAR(self->expression);
}
int Constraint_traverse(Constraint *self, visitproc visit, void *arg)
{
Py_VISIT(self->expression);
#if PY_VERSION_HEX >= 0x03090000
// This was not needed before Python 3.9 (Python issue 35810 and 40217)
Py_VISIT(Py_TYPE(self));
#endif
return 0;
}
void Constraint_dealloc(Constraint *self)
{
PyObject_GC_UnTrack(self);
Constraint_clear(self);
self->constraint.~Constraint();
Py_TYPE(self)->tp_free(pyobject_cast(self));
}
PyObject *
Constraint_repr(Constraint *self)
{
std::stringstream stream;
Expression *expr = reinterpret_cast<Expression *>(self->expression);
Py_ssize_t size = PyTuple_GET_SIZE(expr->terms);
for (Py_ssize_t i = 0; i < size; ++i)
{
PyObject *item = PyTuple_GET_ITEM(expr->terms, i);
Term *term = reinterpret_cast<Term *>(item);
stream << term->coefficient << " * ";
stream << reinterpret_cast<Variable *>(term->variable)->variable.name();
stream << " + ";
}
stream << expr->constant;
switch (self->constraint.op())
{
case kiwi::OP_EQ:
stream << " == 0";
break;
case kiwi::OP_LE:
stream << " <= 0";
break;
case kiwi::OP_GE:
stream << " >= 0";
break;
}
stream << " | strength = " << self->constraint.strength();
if (self->constraint.violated())
{
stream << " (VIOLATED)";
}
return PyUnicode_FromString(stream.str().c_str());
}
PyObject *
Constraint_expression(Constraint *self)
{
return cppy::incref(self->expression);
}
PyObject *
Constraint_op(Constraint *self)
{
PyObject *res = 0;
switch (self->constraint.op())
{
case kiwi::OP_EQ:
res = PyUnicode_FromString("==");
break;
case kiwi::OP_LE:
res = PyUnicode_FromString("<=");
break;
case kiwi::OP_GE:
res = PyUnicode_FromString(">=");
break;
}
return res;
}
PyObject *
Constraint_strength(Constraint *self)
{
return PyFloat_FromDouble(self->constraint.strength());
}
PyObject *
Constraint_violated(Constraint *self)
{
if (self->constraint.violated()) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
PyObject *
Constraint_or(PyObject *pyoldcn, PyObject *value)
{
if (!Constraint::TypeCheck(pyoldcn))
std::swap(pyoldcn, value);
double strength;
if (!convert_to_strength(value, strength))
return 0;
PyObject *pynewcn = PyType_GenericNew(Constraint::TypeObject, 0, 0);
if (!pynewcn)
return 0;
Constraint *oldcn = reinterpret_cast<Constraint *>(pyoldcn);
Constraint *newcn = reinterpret_cast<Constraint *>(pynewcn);
newcn->expression = cppy::incref(oldcn->expression);
new (&newcn->constraint) kiwi::Constraint(oldcn->constraint, strength);
return pynewcn;
}
static PyMethodDef
Constraint_methods[] = {
{"expression", (PyCFunction)Constraint_expression, METH_NOARGS,
"Get the expression object for the constraint."},
{"op", (PyCFunction)Constraint_op, METH_NOARGS,
"Get the relational operator for the constraint."},
{"strength", (PyCFunction)Constraint_strength, METH_NOARGS,
"Get the strength for the constraint."},
{"violated", (PyCFunction)Constraint_violated, METH_NOARGS,
"Return whether or not the constraint was violated "
"during the last solver pass."},
{0} // sentinel
};
static PyType_Slot Constraint_Type_slots[] = {
{Py_tp_dealloc, void_cast(Constraint_dealloc)}, /* tp_dealloc */
{Py_tp_traverse, void_cast(Constraint_traverse)}, /* tp_traverse */
{Py_tp_clear, void_cast(Constraint_clear)}, /* tp_clear */
{Py_tp_repr, void_cast(Constraint_repr)}, /* tp_repr */
{Py_tp_methods, void_cast(Constraint_methods)}, /* tp_methods */
{Py_tp_new, void_cast(Constraint_new)}, /* tp_new */
{Py_tp_alloc, void_cast(PyType_GenericAlloc)}, /* tp_alloc */
{Py_tp_free, void_cast(PyObject_GC_Del)}, /* tp_free */
{Py_nb_or, void_cast(Constraint_or)}, /* nb_or */
{0, 0},
};
} // namespace
// Initialize static variables (otherwise the compiler eliminates them)
PyTypeObject *Constraint::TypeObject = NULL;
PyType_Spec Constraint::TypeObject_Spec = {
"kiwisolver.Constraint", /* tp_name */
sizeof(Constraint), /* tp_basicsize */
0, /* tp_itemsize */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE, /* tp_flags */
Constraint_Type_slots /* slots */
};
bool Constraint::Ready()
{
// The reference will be handled by the module to which we will add the type
TypeObject = pytype_cast(PyType_FromSpec(&TypeObject_Spec));
if (!TypeObject)
{
return false;
}
return true;
}
} // namespace kiwisolver