Merge commit '81396a5322a7a48764fcf254d5d933ba1e57bdc5' as 'kiwi'
This commit is contained in:
222
kiwi/py/src/constraint.cpp
Normal file
222
kiwi/py/src/constraint.cpp
Normal 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
|
||||
251
kiwi/py/src/expression.cpp
Normal file
251
kiwi/py/src/expression.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| 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 <sstream>
|
||||
#include <cppy/cppy.h>
|
||||
#include "symbolics.h"
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
namespace kiwisolver
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
PyObject*
|
||||
Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs )
|
||||
{
|
||||
static const char *kwlist[] = { "terms", "constant", 0 };
|
||||
PyObject* pyterms;
|
||||
PyObject* pyconstant = 0;
|
||||
if( !PyArg_ParseTupleAndKeywords(
|
||||
args, kwargs, "O|O:__new__", const_cast<char**>( kwlist ),
|
||||
&pyterms, &pyconstant ) )
|
||||
return 0;
|
||||
cppy::ptr terms( PySequence_Tuple( pyterms ) );
|
||||
if( !terms )
|
||||
return 0;
|
||||
Py_ssize_t end = PyTuple_GET_SIZE( terms.get() );
|
||||
for( Py_ssize_t i = 0; i < end; ++i )
|
||||
{
|
||||
PyObject* item = PyTuple_GET_ITEM( terms.get(), i );
|
||||
if( !Term::TypeCheck( item ) )
|
||||
return cppy::type_error( item, "Term" );
|
||||
}
|
||||
double constant = 0.0;
|
||||
if( pyconstant && !convert_to_double( pyconstant, constant ) )
|
||||
return 0;
|
||||
PyObject* pyexpr = PyType_GenericNew( type, args, kwargs );
|
||||
if( !pyexpr )
|
||||
return 0;
|
||||
Expression* self = reinterpret_cast<Expression*>( pyexpr );
|
||||
self->terms = terms.release();
|
||||
self->constant = constant;
|
||||
return pyexpr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Expression_clear( Expression* self )
|
||||
{
|
||||
Py_CLEAR( self->terms );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Expression_traverse( Expression* self, visitproc visit, void* arg )
|
||||
{
|
||||
Py_VISIT( self->terms );
|
||||
#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
|
||||
Expression_dealloc( Expression* self )
|
||||
{
|
||||
PyObject_GC_UnTrack( self );
|
||||
Expression_clear( self );
|
||||
Py_TYPE( self )->tp_free( pyobject_cast( self ) );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_repr( Expression* self )
|
||||
{
|
||||
std::stringstream stream;
|
||||
Py_ssize_t end = PyTuple_GET_SIZE( self->terms );
|
||||
for( Py_ssize_t i = 0; i < end; ++i )
|
||||
{
|
||||
PyObject* item = PyTuple_GET_ITEM( self->terms, i );
|
||||
Term* term = reinterpret_cast<Term*>( item );
|
||||
stream << term->coefficient << " * ";
|
||||
stream << reinterpret_cast<Variable*>( term->variable )->variable.name();
|
||||
stream << " + ";
|
||||
}
|
||||
stream << self->constant;
|
||||
return PyUnicode_FromString( stream.str().c_str() );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_terms( Expression* self )
|
||||
{
|
||||
return cppy::incref( self->terms );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_constant( Expression* self )
|
||||
{
|
||||
return PyFloat_FromDouble( self->constant );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_value( Expression* self )
|
||||
{
|
||||
double result = self->constant;
|
||||
Py_ssize_t size = PyTuple_GET_SIZE( self->terms );
|
||||
for( Py_ssize_t i = 0; i < size; ++i )
|
||||
{
|
||||
PyObject* item = PyTuple_GET_ITEM( self->terms, i );
|
||||
Term* term = reinterpret_cast<Term*>( item );
|
||||
Variable* pyvar = reinterpret_cast<Variable*>( term->variable );
|
||||
result += term->coefficient * pyvar->variable.value();
|
||||
}
|
||||
return PyFloat_FromDouble( result );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_add( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryAdd, Expression>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_sub( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinarySub, Expression>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_mul( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryMul, Expression>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_div( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryDiv, Expression>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_neg( PyObject* value )
|
||||
{
|
||||
return UnaryInvoke<UnaryNeg, Expression>()( value );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Expression_richcmp( PyObject* first, PyObject* second, int op )
|
||||
{
|
||||
switch( op )
|
||||
{
|
||||
case Py_EQ:
|
||||
return BinaryInvoke<CmpEQ, Expression>()( first, second );
|
||||
case Py_LE:
|
||||
return BinaryInvoke<CmpLE, Expression>()( first, second );
|
||||
case Py_GE:
|
||||
return BinaryInvoke<CmpGE, Expression>()( first, second );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
PyErr_Format(
|
||||
PyExc_TypeError,
|
||||
"unsupported operand type(s) for %s: "
|
||||
"'%.100s' and '%.100s'",
|
||||
pyop_str( op ),
|
||||
Py_TYPE( first )->tp_name,
|
||||
Py_TYPE( second )->tp_name
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef
|
||||
Expression_methods[] = {
|
||||
{ "terms", ( PyCFunction )Expression_terms, METH_NOARGS,
|
||||
"Get the tuple of terms for the expression." },
|
||||
{ "constant", ( PyCFunction )Expression_constant, METH_NOARGS,
|
||||
"Get the constant for the expression." },
|
||||
{ "value", ( PyCFunction )Expression_value, METH_NOARGS,
|
||||
"Get the value for the expression." },
|
||||
{ 0 } // sentinel
|
||||
};
|
||||
|
||||
|
||||
static PyType_Slot Expression_Type_slots[] = {
|
||||
{ Py_tp_dealloc, void_cast( Expression_dealloc ) }, /* tp_dealloc */
|
||||
{ Py_tp_traverse, void_cast( Expression_traverse ) }, /* tp_traverse */
|
||||
{ Py_tp_clear, void_cast( Expression_clear ) }, /* tp_clear */
|
||||
{ Py_tp_repr, void_cast( Expression_repr ) }, /* tp_repr */
|
||||
{ Py_tp_richcompare, void_cast( Expression_richcmp ) }, /* tp_richcompare */
|
||||
{ Py_tp_methods, void_cast( Expression_methods ) }, /* tp_methods */
|
||||
{ Py_tp_new, void_cast( Expression_new ) }, /* tp_new */
|
||||
{ Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */
|
||||
{ Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */
|
||||
{ Py_nb_add, void_cast( Expression_add ) }, /* nb_add */
|
||||
{ Py_nb_subtract, void_cast( Expression_sub ) }, /* nb_sub */
|
||||
{ Py_nb_multiply, void_cast( Expression_mul ) }, /* nb_mul */
|
||||
{ Py_nb_negative, void_cast( Expression_neg ) }, /* nb_neg */
|
||||
{ Py_nb_true_divide, void_cast( Expression_div ) }, /* nb_div */
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// Initialize static variables (otherwise the compiler eliminates them)
|
||||
PyTypeObject* Expression::TypeObject = NULL;
|
||||
|
||||
|
||||
PyType_Spec Expression::TypeObject_Spec = {
|
||||
"kiwisolver.Expression", /* tp_name */
|
||||
sizeof( Expression ), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
Py_TPFLAGS_DEFAULT|
|
||||
Py_TPFLAGS_HAVE_GC|
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
Expression_Type_slots /* slots */
|
||||
};
|
||||
|
||||
|
||||
bool Expression::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;
|
||||
}
|
||||
|
||||
} // namesapce kiwisolver
|
||||
187
kiwi/py/src/kiwisolver.cpp
Normal file
187
kiwi/py/src/kiwisolver.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| Copyright (c) 2013-2021, 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 <cppy/cppy.h>
|
||||
#include <kiwi/kiwi.h>
|
||||
#include "types.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
bool ready_types()
|
||||
{
|
||||
using namespace kiwisolver;
|
||||
if( !Variable::Ready() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( !Term::Ready() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( !Expression::Ready() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( !Constraint::Ready() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( !strength::Ready() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( !Solver::Ready() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_objects( PyObject* mod )
|
||||
{
|
||||
using namespace kiwisolver;
|
||||
|
||||
cppy::ptr kiwiversion( PyUnicode_FromString( KIWI_VERSION ) );
|
||||
if( !kiwiversion )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cppy::ptr pyversion( PyUnicode_FromString( PY_KIWI_VERSION ) );
|
||||
if( !pyversion )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cppy::ptr pystrength( PyType_GenericNew( strength::TypeObject, 0, 0 ) );
|
||||
if( !pystrength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( PyModule_AddObject( mod, "__version__", pyversion.get() ) < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
pyversion.release();
|
||||
|
||||
if( PyModule_AddObject( mod, "__kiwi_version__", kiwiversion.get() ) < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
kiwiversion.release();
|
||||
|
||||
if( PyModule_AddObject( mod, "strength", pystrength.get() ) < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
pystrength.release();
|
||||
|
||||
// Variable
|
||||
cppy::ptr var( pyobject_cast( Variable::TypeObject ) );
|
||||
if( PyModule_AddObject( mod, "Variable", var.get() ) < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var.release();
|
||||
|
||||
// Term
|
||||
cppy::ptr term( pyobject_cast( Term::TypeObject ) );
|
||||
if( PyModule_AddObject( mod, "Term", term.get() ) < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
term.release();
|
||||
|
||||
// Expression
|
||||
cppy::ptr expr( pyobject_cast( Expression::TypeObject ) );
|
||||
if( PyModule_AddObject( mod, "Expression", expr.get() ) < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
expr.release();
|
||||
|
||||
// Constraint
|
||||
cppy::ptr cons( pyobject_cast( Constraint::TypeObject ) );
|
||||
if( PyModule_AddObject( mod, "Constraint", cons.get() ) < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cons.release();
|
||||
|
||||
cppy::ptr solver( pyobject_cast( Solver::TypeObject ) );
|
||||
if( PyModule_AddObject( mod, "Solver", solver.get() ) < 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
solver.release();
|
||||
|
||||
PyModule_AddObject( mod, "DuplicateConstraint", DuplicateConstraint );
|
||||
PyModule_AddObject( mod, "UnsatisfiableConstraint", UnsatisfiableConstraint );
|
||||
PyModule_AddObject( mod, "UnknownConstraint", UnknownConstraint );
|
||||
PyModule_AddObject( mod, "DuplicateEditVariable", DuplicateEditVariable );
|
||||
PyModule_AddObject( mod, "UnknownEditVariable", UnknownEditVariable );
|
||||
PyModule_AddObject( mod, "BadRequiredStrength", BadRequiredStrength );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
kiwi_modexec( PyObject *mod )
|
||||
{
|
||||
if( !ready_types() )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if( !kiwisolver::init_exceptions() )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if( !add_objects( mod ) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef
|
||||
kiwisolver_methods[] = {
|
||||
{ 0 } // Sentinel
|
||||
};
|
||||
|
||||
|
||||
PyModuleDef_Slot kiwisolver_slots[] = {
|
||||
{Py_mod_exec, reinterpret_cast<void*>( kiwi_modexec ) },
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
|
||||
struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_cext",
|
||||
"kiwisolver extension module",
|
||||
0,
|
||||
kiwisolver_methods,
|
||||
kiwisolver_slots,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
PyMODINIT_FUNC PyInit__cext( void )
|
||||
{
|
||||
return PyModuleDef_Init( &moduledef );
|
||||
}
|
||||
338
kiwi/py/src/solver.cpp
Normal file
338
kiwi/py/src/solver.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| 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 <cppy/cppy.h>
|
||||
#include <kiwi/kiwi.h>
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
namespace kiwisolver
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
PyObject*
|
||||
Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs )
|
||||
{
|
||||
if( PyTuple_GET_SIZE( args ) != 0 || ( kwargs && PyDict_Size( kwargs ) != 0 ) )
|
||||
return cppy::type_error( "Solver.__new__ takes no arguments" );
|
||||
PyObject* pysolver = PyType_GenericNew( type, args, kwargs );
|
||||
if( !pysolver )
|
||||
return 0;
|
||||
Solver* self = reinterpret_cast<Solver*>( pysolver );
|
||||
new( &self->solver ) kiwi::Solver();
|
||||
return pysolver;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Solver_dealloc( Solver* self )
|
||||
{
|
||||
self->solver.~Solver();
|
||||
Py_TYPE( self )->tp_free( pyobject_cast( self ) );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_addConstraint( Solver* self, PyObject* other )
|
||||
{
|
||||
if( !Constraint::TypeCheck( other ) )
|
||||
return cppy::type_error( other, "Constraint" );
|
||||
Constraint* cn = reinterpret_cast<Constraint*>( other );
|
||||
try
|
||||
{
|
||||
self->solver.addConstraint( cn->constraint );
|
||||
}
|
||||
catch( const kiwi::DuplicateConstraint& )
|
||||
{
|
||||
PyErr_SetObject( DuplicateConstraint, other );
|
||||
return 0;
|
||||
}
|
||||
catch( const kiwi::UnsatisfiableConstraint& )
|
||||
{
|
||||
PyErr_SetObject( UnsatisfiableConstraint, other );
|
||||
return 0;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_removeConstraint( Solver* self, PyObject* other )
|
||||
{
|
||||
if( !Constraint::TypeCheck( other ) )
|
||||
return cppy::type_error( other, "Constraint" );
|
||||
Constraint* cn = reinterpret_cast<Constraint*>( other );
|
||||
try
|
||||
{
|
||||
self->solver.removeConstraint( cn->constraint );
|
||||
}
|
||||
catch( const kiwi::UnknownConstraint& )
|
||||
{
|
||||
PyErr_SetObject( UnknownConstraint, other );
|
||||
return 0;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_hasConstraint( Solver* self, PyObject* other )
|
||||
{
|
||||
if( !Constraint::TypeCheck( other ) )
|
||||
return cppy::type_error( other, "Constraint" );
|
||||
Constraint* cn = reinterpret_cast<Constraint*>( other );
|
||||
return cppy::incref( self->solver.hasConstraint( cn->constraint ) ? Py_True : Py_False );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_addEditVariable( Solver* self, PyObject* args )
|
||||
{
|
||||
PyObject* pyvar;
|
||||
PyObject* pystrength;
|
||||
if( !PyArg_ParseTuple( args, "OO", &pyvar, &pystrength ) )
|
||||
return 0;
|
||||
if( !Variable::TypeCheck( pyvar ) )
|
||||
return cppy::type_error( pyvar, "Variable" );
|
||||
double strength;
|
||||
if( !convert_to_strength( pystrength, strength ) )
|
||||
return 0;
|
||||
Variable* var = reinterpret_cast<Variable*>( pyvar );
|
||||
try
|
||||
{
|
||||
self->solver.addEditVariable( var->variable, strength );
|
||||
}
|
||||
catch( const kiwi::DuplicateEditVariable& )
|
||||
{
|
||||
PyErr_SetObject( DuplicateEditVariable, pyvar );
|
||||
return 0;
|
||||
}
|
||||
catch( const kiwi::BadRequiredStrength& e )
|
||||
{
|
||||
PyErr_SetString( BadRequiredStrength, e.what() );
|
||||
return 0;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_removeEditVariable( Solver* self, PyObject* other )
|
||||
{
|
||||
if( !Variable::TypeCheck( other ) )
|
||||
return cppy::type_error( other, "Variable" );
|
||||
Variable* var = reinterpret_cast<Variable*>( other );
|
||||
try
|
||||
{
|
||||
self->solver.removeEditVariable( var->variable );
|
||||
}
|
||||
catch( const kiwi::UnknownEditVariable& )
|
||||
{
|
||||
PyErr_SetObject( UnknownEditVariable, other );
|
||||
return 0;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_hasEditVariable( Solver* self, PyObject* other )
|
||||
{
|
||||
if( !Variable::TypeCheck( other ) )
|
||||
return cppy::type_error( other, "Variable" );
|
||||
Variable* var = reinterpret_cast<Variable*>( other );
|
||||
return cppy::incref( self->solver.hasEditVariable( var->variable ) ? Py_True : Py_False );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_suggestValue( Solver* self, PyObject* args )
|
||||
{
|
||||
PyObject* pyvar;
|
||||
PyObject* pyvalue;
|
||||
if( !PyArg_ParseTuple( args, "OO", &pyvar, &pyvalue ) )
|
||||
return 0;
|
||||
if( !Variable::TypeCheck( pyvar ) )
|
||||
return cppy::type_error( pyvar, "Variable" );
|
||||
double value;
|
||||
if( !convert_to_double( pyvalue, value ) )
|
||||
return 0;
|
||||
Variable* var = reinterpret_cast<Variable*>( pyvar );
|
||||
try
|
||||
{
|
||||
self->solver.suggestValue( var->variable, value );
|
||||
}
|
||||
catch( const kiwi::UnknownEditVariable& )
|
||||
{
|
||||
PyErr_SetObject( UnknownEditVariable, pyvar );
|
||||
return 0;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_updateVariables( Solver* self )
|
||||
{
|
||||
self->solver.updateVariables();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_reset( Solver* self )
|
||||
{
|
||||
self->solver.reset();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Solver_dump( Solver* self )
|
||||
{
|
||||
cppy::ptr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) );
|
||||
PyObject_Print( dump_str.get(), stdout, 0 );
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
Solver_dumps( Solver* self )
|
||||
{
|
||||
return PyUnicode_FromString( self->solver.dumps().c_str() );
|
||||
}
|
||||
|
||||
static PyMethodDef
|
||||
Solver_methods[] = {
|
||||
{ "addConstraint", ( PyCFunction )Solver_addConstraint, METH_O,
|
||||
"Add a constraint to the solver." },
|
||||
{ "removeConstraint", ( PyCFunction )Solver_removeConstraint, METH_O,
|
||||
"Remove a constraint from the solver." },
|
||||
{ "hasConstraint", ( PyCFunction )Solver_hasConstraint, METH_O,
|
||||
"Check whether the solver contains a constraint." },
|
||||
{ "addEditVariable", ( PyCFunction )Solver_addEditVariable, METH_VARARGS,
|
||||
"Add an edit variable to the solver." },
|
||||
{ "removeEditVariable", ( PyCFunction )Solver_removeEditVariable, METH_O,
|
||||
"Remove an edit variable from the solver." },
|
||||
{ "hasEditVariable", ( PyCFunction )Solver_hasEditVariable, METH_O,
|
||||
"Check whether the solver contains an edit variable." },
|
||||
{ "suggestValue", ( PyCFunction )Solver_suggestValue, METH_VARARGS,
|
||||
"Suggest a desired value for an edit variable." },
|
||||
{ "updateVariables", ( PyCFunction )Solver_updateVariables, METH_NOARGS,
|
||||
"Update the values of the solver variables." },
|
||||
{ "reset", ( PyCFunction )Solver_reset, METH_NOARGS,
|
||||
"Reset the solver to the initial empty starting condition." },
|
||||
{ "dump", ( PyCFunction )Solver_dump, METH_NOARGS,
|
||||
"Dump a representation of the solver internals to stdout." },
|
||||
{ "dumps", ( PyCFunction )Solver_dumps, METH_NOARGS,
|
||||
"Dump a representation of the solver internals to a string." },
|
||||
{ 0 } // sentinel
|
||||
};
|
||||
|
||||
|
||||
static PyType_Slot Solver_Type_slots[] = {
|
||||
{ Py_tp_dealloc, void_cast( Solver_dealloc ) }, /* tp_dealloc */
|
||||
{ Py_tp_methods, void_cast( Solver_methods ) }, /* tp_methods */
|
||||
{ Py_tp_new, void_cast( Solver_new ) }, /* tp_new */
|
||||
{ Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */
|
||||
{ Py_tp_free, void_cast( PyObject_Del ) }, /* tp_free */
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// Initialize static variables (otherwise the compiler eliminates them)
|
||||
PyTypeObject* Solver::TypeObject = NULL;
|
||||
|
||||
|
||||
PyType_Spec Solver::TypeObject_Spec = {
|
||||
"kiwisolver.Solver", /* tp_name */
|
||||
sizeof( Solver ), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
Py_TPFLAGS_DEFAULT|
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
Solver_Type_slots /* slots */
|
||||
};
|
||||
|
||||
|
||||
bool Solver::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;
|
||||
}
|
||||
|
||||
|
||||
PyObject* DuplicateConstraint;
|
||||
|
||||
PyObject* UnsatisfiableConstraint;
|
||||
|
||||
PyObject* UnknownConstraint;
|
||||
|
||||
PyObject* DuplicateEditVariable;
|
||||
|
||||
PyObject* UnknownEditVariable;
|
||||
|
||||
PyObject* BadRequiredStrength;
|
||||
|
||||
|
||||
bool init_exceptions()
|
||||
{
|
||||
cppy::ptr mod( PyImport_ImportModule( "kiwisolver.exceptions" ) );
|
||||
if( !mod )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DuplicateConstraint = mod.getattr( "DuplicateConstraint" );
|
||||
if( !DuplicateConstraint )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UnsatisfiableConstraint = mod.getattr( "UnsatisfiableConstraint" );
|
||||
if( !UnsatisfiableConstraint )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UnknownConstraint = mod.getattr( "UnknownConstraint" );
|
||||
if( !UnknownConstraint )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DuplicateEditVariable = mod.getattr( "DuplicateEditVariable" );
|
||||
if( !DuplicateEditVariable )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UnknownEditVariable = mod.getattr( "UnknownEditVariable" );
|
||||
if( !UnknownEditVariable )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
BadRequiredStrength = mod.getattr( "BadRequiredStrength" );
|
||||
if( !BadRequiredStrength )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
149
kiwi/py/src/strength.cpp
Normal file
149
kiwi/py/src/strength.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| 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 <cppy/cppy.h>
|
||||
#include <kiwi/kiwi.h>
|
||||
#include "util.h"
|
||||
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-writable-strings"
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||
#endif
|
||||
|
||||
|
||||
namespace kiwisolver
|
||||
{
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
void
|
||||
strength_dealloc( PyObject* self )
|
||||
{
|
||||
Py_TYPE( self )->tp_free( self );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
strength_weak( strength* self )
|
||||
{
|
||||
return PyFloat_FromDouble( kiwi::strength::weak );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
strength_medium( strength* self )
|
||||
{
|
||||
return PyFloat_FromDouble( kiwi::strength::medium );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
strength_strong( strength* self )
|
||||
{
|
||||
return PyFloat_FromDouble( kiwi::strength::strong );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
strength_required( strength* self )
|
||||
{
|
||||
return PyFloat_FromDouble( kiwi::strength::required );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
strength_create( strength* self, PyObject* args )
|
||||
{
|
||||
PyObject* pya;
|
||||
PyObject* pyb;
|
||||
PyObject* pyc;
|
||||
PyObject* pyw = 0;
|
||||
if( !PyArg_ParseTuple( args, "OOO|O", &pya, &pyb, &pyc, &pyw ) )
|
||||
return 0;
|
||||
double a, b, c;
|
||||
double w = 1.0;
|
||||
if( !convert_to_double( pya, a ) )
|
||||
return 0;
|
||||
if( !convert_to_double( pyb, b ) )
|
||||
return 0;
|
||||
if( !convert_to_double( pyc, c ) )
|
||||
return 0;
|
||||
if( pyw && !convert_to_double( pyw, w ) )
|
||||
return 0;
|
||||
return PyFloat_FromDouble( kiwi::strength::create( a, b, c, w ) );
|
||||
}
|
||||
|
||||
|
||||
static PyGetSetDef
|
||||
strength_getset[] = {
|
||||
{ "weak", ( getter )strength_weak, 0,
|
||||
"The predefined weak strength." },
|
||||
{ "medium", ( getter )strength_medium, 0,
|
||||
"The predefined medium strength." },
|
||||
{ "strong", ( getter )strength_strong, 0,
|
||||
"The predefined strong strength." },
|
||||
{ "required", ( getter )strength_required, 0,
|
||||
"The predefined required strength." },
|
||||
{ 0 } // sentinel
|
||||
};
|
||||
|
||||
|
||||
static PyMethodDef
|
||||
strength_methods[] = {
|
||||
{ "create", ( PyCFunction )strength_create, METH_VARARGS,
|
||||
"Create a strength from constituent values and optional weight." },
|
||||
{ 0 } // sentinel
|
||||
};
|
||||
|
||||
|
||||
|
||||
static PyType_Slot strength_Type_slots[] = {
|
||||
{ Py_tp_dealloc, void_cast( strength_dealloc ) }, /* tp_dealloc */
|
||||
{ Py_tp_getset, void_cast( strength_getset ) }, /* tp_getset */
|
||||
{ Py_tp_methods, void_cast( strength_methods ) }, /* tp_methods */
|
||||
{ Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */
|
||||
{ Py_tp_free, void_cast( PyObject_Del ) }, /* tp_free */
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// Initialize static variables (otherwise the compiler eliminates them)
|
||||
PyTypeObject* strength::TypeObject = NULL;
|
||||
|
||||
|
||||
PyType_Spec strength::TypeObject_Spec = {
|
||||
"kiwisolver.Strength", /* tp_name */
|
||||
sizeof( strength ), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
strength_Type_slots /* slots */
|
||||
};
|
||||
|
||||
|
||||
bool strength::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
|
||||
618
kiwi/py/src/symbolics.h
Normal file
618
kiwi/py/src/symbolics.h
Normal file
@@ -0,0 +1,618 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| 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.
|
||||
|----------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
#include <cppy/cppy.h>
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
namespace kiwisolver
|
||||
{
|
||||
|
||||
template<typename Op, typename T>
|
||||
struct UnaryInvoke
|
||||
{
|
||||
PyObject* operator()( PyObject* value )
|
||||
{
|
||||
return Op()( reinterpret_cast<T*>( value ) );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Op, typename T>
|
||||
struct BinaryInvoke
|
||||
{
|
||||
PyObject* operator()( PyObject* first, PyObject* second )
|
||||
{
|
||||
if( T::TypeCheck( first ) )
|
||||
return invoke<Normal>( reinterpret_cast<T*>( first ), second );
|
||||
return invoke<Reverse>( reinterpret_cast<T*>( second ), first );
|
||||
}
|
||||
|
||||
struct Normal
|
||||
{
|
||||
template<typename U>
|
||||
PyObject* operator()( T* primary, U secondary )
|
||||
{
|
||||
return Op()( primary, secondary );
|
||||
}
|
||||
};
|
||||
|
||||
struct Reverse
|
||||
{
|
||||
template<typename U>
|
||||
PyObject* operator()( T* primary, U secondary )
|
||||
{
|
||||
return Op()( secondary, primary );
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Invk>
|
||||
PyObject* invoke( T* primary, PyObject* secondary )
|
||||
{
|
||||
if( Expression::TypeCheck( secondary ) )
|
||||
return Invk()( primary, reinterpret_cast<Expression*>( secondary ) );
|
||||
if( Term::TypeCheck( secondary ) )
|
||||
return Invk()( primary, reinterpret_cast<Term*>( secondary ) );
|
||||
if( Variable::TypeCheck( secondary ) )
|
||||
return Invk()( primary, reinterpret_cast<Variable*>( secondary ) );
|
||||
if( PyFloat_Check( secondary ) )
|
||||
return Invk()( primary, PyFloat_AS_DOUBLE( secondary ) );
|
||||
if( PyLong_Check( secondary ) )
|
||||
{
|
||||
double v = PyLong_AsDouble( secondary );
|
||||
if( v == -1 && PyErr_Occurred() )
|
||||
return 0;
|
||||
return Invk()( primary, v );
|
||||
}
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct BinaryMul
|
||||
{
|
||||
template<typename T, typename U>
|
||||
PyObject* operator()( T first, U second )
|
||||
{
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryMul::operator()( Variable* first, double second )
|
||||
{
|
||||
PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 );
|
||||
if( !pyterm )
|
||||
return 0;
|
||||
Term* term = reinterpret_cast<Term*>( pyterm );
|
||||
term->variable = cppy::incref( pyobject_cast( first ) );
|
||||
term->coefficient = second;
|
||||
return pyterm;
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryMul::operator()( Term* first, double second )
|
||||
{
|
||||
PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 );
|
||||
if( !pyterm )
|
||||
return 0;
|
||||
Term* term = reinterpret_cast<Term*>( pyterm );
|
||||
term->variable = cppy::incref( first->variable );
|
||||
term->coefficient = first->coefficient * second;
|
||||
return pyterm;
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryMul::operator()( Expression* first, double second )
|
||||
{
|
||||
cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) );
|
||||
if( !pyexpr )
|
||||
return 0;
|
||||
Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() );
|
||||
cppy::ptr terms( PyTuple_New( PyTuple_GET_SIZE( first->terms ) ) );
|
||||
if( !terms )
|
||||
return 0;
|
||||
Py_ssize_t end = PyTuple_GET_SIZE( first->terms );
|
||||
for( Py_ssize_t i = 0; i < end; ++i ) // memset 0 for safe error return
|
||||
PyTuple_SET_ITEM( terms.get(), i, 0 );
|
||||
for( Py_ssize_t i = 0; i < end; ++i )
|
||||
{
|
||||
PyObject* item = PyTuple_GET_ITEM( first->terms, i );
|
||||
PyObject* term = BinaryMul()( reinterpret_cast<Term*>( item ), second );
|
||||
if( !term )
|
||||
return 0;
|
||||
PyTuple_SET_ITEM( terms.get(), i, term );
|
||||
}
|
||||
expr->terms = terms.release();
|
||||
expr->constant = first->constant * second;
|
||||
return pyexpr.release();
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryMul::operator()( double first, Variable* second )
|
||||
{
|
||||
return operator()( second, first );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryMul::operator()( double first, Term* second )
|
||||
{
|
||||
return operator()( second, first );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryMul::operator()( double first, Expression* second )
|
||||
{
|
||||
return operator()( second, first );
|
||||
}
|
||||
|
||||
|
||||
struct BinaryDiv
|
||||
{
|
||||
template<typename T, typename U>
|
||||
PyObject* operator()( T first, U second )
|
||||
{
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryDiv::operator()( Variable* first, double second )
|
||||
{
|
||||
if( second == 0.0 )
|
||||
{
|
||||
PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" );
|
||||
return 0;
|
||||
}
|
||||
return BinaryMul()( first, 1.0 / second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryDiv::operator()( Term* first, double second )
|
||||
{
|
||||
if( second == 0.0 )
|
||||
{
|
||||
PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" );
|
||||
return 0;
|
||||
}
|
||||
return BinaryMul()( first, 1.0 / second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryDiv::operator()( Expression* first, double second )
|
||||
{
|
||||
if( second == 0.0 )
|
||||
{
|
||||
PyErr_SetString( PyExc_ZeroDivisionError, "float division by zero" );
|
||||
return 0;
|
||||
}
|
||||
return BinaryMul()( first, 1.0 / second );
|
||||
}
|
||||
|
||||
|
||||
struct UnaryNeg
|
||||
{
|
||||
template<typename T>
|
||||
PyObject* operator()( T value )
|
||||
{
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* UnaryNeg::operator()( Variable* value )
|
||||
{
|
||||
return BinaryMul()( value, -1.0 );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* UnaryNeg::operator()( Term* value )
|
||||
{
|
||||
return BinaryMul()( value, -1.0 );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* UnaryNeg::operator()( Expression* value )
|
||||
{
|
||||
return BinaryMul()( value, -1.0 );
|
||||
}
|
||||
|
||||
|
||||
struct BinaryAdd
|
||||
{
|
||||
template<typename T, typename U>
|
||||
PyObject* operator()( T first, U second )
|
||||
{
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Expression* first, Expression* second )
|
||||
{
|
||||
cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) );
|
||||
if( !pyexpr )
|
||||
return 0;
|
||||
Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() );
|
||||
expr->constant = first->constant + second->constant;
|
||||
expr->terms = PySequence_Concat( first->terms, second->terms );
|
||||
if( !expr->terms )
|
||||
return 0;
|
||||
return pyexpr.release();
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Expression* first, Term* second )
|
||||
{
|
||||
cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) );
|
||||
if( !pyexpr )
|
||||
return 0;
|
||||
PyObject* terms = PyTuple_New( PyTuple_GET_SIZE( first->terms ) + 1 );
|
||||
if( !terms )
|
||||
return 0;
|
||||
Py_ssize_t end = PyTuple_GET_SIZE( first->terms );
|
||||
for( Py_ssize_t i = 0; i < end; ++i )
|
||||
{
|
||||
PyObject* item = PyTuple_GET_ITEM( first->terms, i );
|
||||
PyTuple_SET_ITEM( terms, i, cppy::incref( item ) );
|
||||
}
|
||||
PyTuple_SET_ITEM( terms, end, cppy::incref( pyobject_cast( second ) ) );
|
||||
Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() );
|
||||
expr->terms = terms;
|
||||
expr->constant = first->constant;
|
||||
return pyexpr.release();
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Expression* first, Variable* second )
|
||||
{
|
||||
cppy::ptr temp( BinaryMul()( second, 1.0 ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return operator()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Expression* first, double second )
|
||||
{
|
||||
cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) );
|
||||
if( !pyexpr )
|
||||
return 0;
|
||||
Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() );
|
||||
expr->terms = cppy::incref( first->terms );
|
||||
expr->constant = first->constant + second;
|
||||
return pyexpr.release();
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Term* first, double second )
|
||||
{
|
||||
cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) );
|
||||
if( !pyexpr )
|
||||
return 0;
|
||||
Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() );
|
||||
expr->constant = second;
|
||||
expr->terms = PyTuple_Pack( 1, first );
|
||||
if( !expr->terms )
|
||||
return 0;
|
||||
return pyexpr.release();
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Term* first, Expression* second )
|
||||
{
|
||||
return operator()( second, first );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Term* first, Term* second )
|
||||
{
|
||||
cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) );
|
||||
if( !pyexpr )
|
||||
return 0;
|
||||
Expression* expr = reinterpret_cast<Expression*>( pyexpr.get() );
|
||||
expr->constant = 0.0;
|
||||
expr->terms = PyTuple_Pack( 2, first, second );
|
||||
if( !expr->terms )
|
||||
return 0;
|
||||
return pyexpr.release();
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Term* first, Variable* second )
|
||||
{
|
||||
cppy::ptr temp( BinaryMul()( second, 1.0 ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Variable* first, double second )
|
||||
{
|
||||
cppy::ptr temp( BinaryMul()( first, 1.0 ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return operator()( reinterpret_cast<Term*>( temp.get() ), second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Variable* first, Variable* second )
|
||||
{
|
||||
cppy::ptr temp( BinaryMul()( first, 1.0 ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return operator()( reinterpret_cast<Term*>( temp.get() ), second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Variable* first, Term* second )
|
||||
{
|
||||
cppy::ptr temp( BinaryMul()( first, 1.0 ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return operator()( reinterpret_cast<Term*>( temp.get() ), second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( Variable* first, Expression* second )
|
||||
{
|
||||
cppy::ptr temp( BinaryMul()( first, 1.0 ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return operator()( reinterpret_cast<Term*>( temp.get() ), second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( double first, Variable* second )
|
||||
{
|
||||
return operator()( second, first );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( double first, Term* second )
|
||||
{
|
||||
return operator()( second, first );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinaryAdd::operator()( double first, Expression* second )
|
||||
{
|
||||
return operator()( second, first );
|
||||
}
|
||||
|
||||
|
||||
struct BinarySub
|
||||
{
|
||||
template<typename T, typename U>
|
||||
PyObject* operator()( T first, U second )
|
||||
{
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Variable* first, double second )
|
||||
{
|
||||
return BinaryAdd()( first, -second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Variable* first, Variable* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Variable* first, Term* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Variable* first, Expression* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Term* first, double second )
|
||||
{
|
||||
return BinaryAdd()( first, -second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Term* first, Variable* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Term* first, Term* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Term* first, Expression* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Expression* first, double second )
|
||||
{
|
||||
return BinaryAdd()( first, -second );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Expression* first, Variable* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Expression* first, Term* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( Expression* first, Expression* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( double first, Variable* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( double first, Term* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Term*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<> inline
|
||||
PyObject* BinarySub::operator()( double first, Expression* second )
|
||||
{
|
||||
cppy::ptr temp( UnaryNeg()( second ) );
|
||||
if( !temp )
|
||||
return 0;
|
||||
return BinaryAdd()( first, reinterpret_cast<Expression*>( temp.get() ) );
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename U>
|
||||
PyObject* makecn( T first, U second, kiwi::RelationalOperator op )
|
||||
{
|
||||
cppy::ptr pyexpr( BinarySub()( first, second ) );
|
||||
if( !pyexpr )
|
||||
return 0;
|
||||
cppy::ptr pycn( PyType_GenericNew( Constraint::TypeObject, 0, 0 ) );
|
||||
if( !pycn )
|
||||
return 0;
|
||||
Constraint* cn = reinterpret_cast<Constraint*>( pycn.get() );
|
||||
cn->expression = reduce_expression( pyexpr.get() );
|
||||
if( !cn->expression )
|
||||
return 0;
|
||||
kiwi::Expression expr( convert_to_kiwi_expression( cn->expression ) );
|
||||
new( &cn->constraint ) kiwi::Constraint( expr, op, kiwi::strength::required );
|
||||
return pycn.release();
|
||||
}
|
||||
|
||||
|
||||
struct CmpEQ
|
||||
{
|
||||
template<typename T, typename U>
|
||||
PyObject* operator()( T first, U second )
|
||||
{
|
||||
return makecn( first, second, kiwi::OP_EQ );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct CmpLE
|
||||
{
|
||||
template<typename T, typename U>
|
||||
PyObject* operator()( T first, U second )
|
||||
{
|
||||
return makecn( first, second, kiwi::OP_LE );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct CmpGE
|
||||
{
|
||||
template<typename T, typename U>
|
||||
PyObject* operator()( T first, U second )
|
||||
{
|
||||
return makecn( first, second, kiwi::OP_GE );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace kiwisolver
|
||||
229
kiwi/py/src/term.cpp
Normal file
229
kiwi/py/src/term.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| 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 <sstream>
|
||||
#include <cppy/cppy.h>
|
||||
#include "symbolics.h"
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
namespace kiwisolver
|
||||
{
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs )
|
||||
{
|
||||
static const char *kwlist[] = { "variable", "coefficient", 0 };
|
||||
PyObject* pyvar;
|
||||
PyObject* pycoeff = 0;
|
||||
if( !PyArg_ParseTupleAndKeywords(
|
||||
args, kwargs, "O|O:__new__", const_cast<char**>( kwlist ),
|
||||
&pyvar, &pycoeff ) )
|
||||
return 0;
|
||||
if( !Variable::TypeCheck( pyvar ) )
|
||||
return cppy::type_error( pyvar, "Variable" );
|
||||
double coefficient = 1.0;
|
||||
if( pycoeff && !convert_to_double( pycoeff, coefficient ) )
|
||||
return 0;
|
||||
PyObject* pyterm = PyType_GenericNew( type, args, kwargs );
|
||||
if( !pyterm )
|
||||
return 0;
|
||||
Term* self = reinterpret_cast<Term*>( pyterm );
|
||||
self->variable = cppy::incref( pyvar );
|
||||
self->coefficient = coefficient;
|
||||
return pyterm;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Term_clear( Term* self )
|
||||
{
|
||||
Py_CLEAR( self->variable );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Term_traverse( Term* self, visitproc visit, void* arg )
|
||||
{
|
||||
Py_VISIT( self->variable );
|
||||
#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
|
||||
Term_dealloc( Term* self )
|
||||
{
|
||||
PyObject_GC_UnTrack( self );
|
||||
Term_clear( self );
|
||||
Py_TYPE( self )->tp_free( pyobject_cast( self ) );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_repr( Term* self )
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << self->coefficient << " * ";
|
||||
stream << reinterpret_cast<Variable*>( self->variable )->variable.name();
|
||||
return PyUnicode_FromString( stream.str().c_str() );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_variable( Term* self )
|
||||
{
|
||||
return cppy::incref( self->variable );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_coefficient( Term* self )
|
||||
{
|
||||
return PyFloat_FromDouble( self->coefficient );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_value( Term* self )
|
||||
{
|
||||
Variable* pyvar = reinterpret_cast<Variable*>( self->variable );
|
||||
return PyFloat_FromDouble( self->coefficient * pyvar->variable.value() );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_add( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryAdd, Term>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_sub( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinarySub, Term>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_mul( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryMul, Term>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_div( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryDiv, Term>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_neg( PyObject* value )
|
||||
{
|
||||
return UnaryInvoke<UnaryNeg, Term>()( value );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Term_richcmp( PyObject* first, PyObject* second, int op )
|
||||
{
|
||||
switch( op )
|
||||
{
|
||||
case Py_EQ:
|
||||
return BinaryInvoke<CmpEQ, Term>()( first, second );
|
||||
case Py_LE:
|
||||
return BinaryInvoke<CmpLE, Term>()( first, second );
|
||||
case Py_GE:
|
||||
return BinaryInvoke<CmpGE, Term>()( first, second );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
PyErr_Format(
|
||||
PyExc_TypeError,
|
||||
"unsupported operand type(s) for %s: "
|
||||
"'%.100s' and '%.100s'",
|
||||
pyop_str( op ),
|
||||
Py_TYPE( first )->tp_name,
|
||||
Py_TYPE( second )->tp_name
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef
|
||||
Term_methods[] = {
|
||||
{ "variable", ( PyCFunction )Term_variable, METH_NOARGS,
|
||||
"Get the variable for the term." },
|
||||
{ "coefficient", ( PyCFunction )Term_coefficient, METH_NOARGS,
|
||||
"Get the coefficient for the term." },
|
||||
{ "value", ( PyCFunction )Term_value, METH_NOARGS,
|
||||
"Get the value for the term." },
|
||||
{ 0 } // sentinel
|
||||
};
|
||||
|
||||
|
||||
static PyType_Slot Term_Type_slots[] = {
|
||||
{ Py_tp_dealloc, void_cast( Term_dealloc ) }, /* tp_dealloc */
|
||||
{ Py_tp_traverse, void_cast( Term_traverse ) }, /* tp_traverse */
|
||||
{ Py_tp_clear, void_cast( Term_clear ) }, /* tp_clear */
|
||||
{ Py_tp_repr, void_cast( Term_repr ) }, /* tp_repr */
|
||||
{ Py_tp_richcompare, void_cast( Term_richcmp ) }, /* tp_richcompare */
|
||||
{ Py_tp_methods, void_cast( Term_methods ) }, /* tp_methods */
|
||||
{ Py_tp_new, void_cast( Term_new ) }, /* tp_new */
|
||||
{ Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */
|
||||
{ Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */
|
||||
{ Py_nb_add, void_cast( Term_add ) }, /* nb_add */
|
||||
{ Py_nb_subtract, void_cast( Term_sub ) }, /* nb_subatract */
|
||||
{ Py_nb_multiply, void_cast( Term_mul ) }, /* nb_multiply */
|
||||
{ Py_nb_negative, void_cast( Term_neg ) }, /* nb_negative */
|
||||
{ Py_nb_true_divide, void_cast( Term_div ) }, /* nb_true_divide */
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// Initialize static variables (otherwise the compiler eliminates them)
|
||||
PyTypeObject* Term::TypeObject = NULL;
|
||||
|
||||
|
||||
PyType_Spec Term::TypeObject_Spec = {
|
||||
"kiwisolver.Term", /* tp_name */
|
||||
sizeof( Term ), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
Py_TPFLAGS_DEFAULT|
|
||||
Py_TPFLAGS_HAVE_GC|
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
Term_Type_slots /* slots */
|
||||
};
|
||||
|
||||
|
||||
bool Term::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
|
||||
138
kiwi/py/src/types.h
Normal file
138
kiwi/py/src/types.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| 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.
|
||||
|----------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
#include <Python.h>
|
||||
#include <kiwi/kiwi.h>
|
||||
|
||||
|
||||
namespace kiwisolver
|
||||
{
|
||||
|
||||
extern PyObject* DuplicateConstraint;
|
||||
|
||||
extern PyObject* UnsatisfiableConstraint;
|
||||
|
||||
extern PyObject* UnknownConstraint;
|
||||
|
||||
extern PyObject* DuplicateEditVariable;
|
||||
|
||||
extern PyObject* UnknownEditVariable;
|
||||
|
||||
extern PyObject* BadRequiredStrength;
|
||||
|
||||
|
||||
struct strength
|
||||
{
|
||||
PyObject_HEAD;
|
||||
|
||||
static PyType_Spec TypeObject_Spec;
|
||||
|
||||
static PyTypeObject* TypeObject;
|
||||
|
||||
static bool Ready();
|
||||
};
|
||||
|
||||
|
||||
struct Variable
|
||||
{
|
||||
PyObject_HEAD
|
||||
PyObject* context;
|
||||
kiwi::Variable variable;
|
||||
|
||||
static PyType_Spec TypeObject_Spec;
|
||||
|
||||
static PyTypeObject* TypeObject;
|
||||
|
||||
static bool Ready();
|
||||
|
||||
static bool TypeCheck( PyObject* obj )
|
||||
{
|
||||
return PyObject_TypeCheck( obj, TypeObject ) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Term
|
||||
{
|
||||
PyObject_HEAD
|
||||
PyObject* variable;
|
||||
double coefficient;
|
||||
|
||||
static PyType_Spec TypeObject_Spec;
|
||||
|
||||
static PyTypeObject* TypeObject;
|
||||
|
||||
static bool Ready();
|
||||
|
||||
static bool TypeCheck( PyObject* obj )
|
||||
{
|
||||
return PyObject_TypeCheck( obj, TypeObject ) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Expression
|
||||
{
|
||||
PyObject_HEAD
|
||||
PyObject* terms;
|
||||
double constant;
|
||||
|
||||
static PyType_Spec TypeObject_Spec;
|
||||
|
||||
static PyTypeObject* TypeObject;
|
||||
|
||||
static bool Ready();
|
||||
|
||||
static bool TypeCheck( PyObject* obj )
|
||||
{
|
||||
return PyObject_TypeCheck( obj, TypeObject ) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Constraint
|
||||
{
|
||||
PyObject_HEAD
|
||||
PyObject* expression;
|
||||
kiwi::Constraint constraint;
|
||||
|
||||
static PyType_Spec TypeObject_Spec;
|
||||
|
||||
static PyTypeObject* TypeObject;
|
||||
|
||||
static bool Ready();
|
||||
|
||||
static bool TypeCheck( PyObject* obj )
|
||||
{
|
||||
return PyObject_TypeCheck( obj, TypeObject ) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Solver
|
||||
{
|
||||
PyObject_HEAD
|
||||
kiwi::Solver solver;
|
||||
|
||||
static PyType_Spec TypeObject_Spec;
|
||||
|
||||
static PyTypeObject* TypeObject;
|
||||
|
||||
static bool Ready();
|
||||
|
||||
static bool TypeCheck( PyObject* obj )
|
||||
{
|
||||
return PyObject_TypeCheck( obj, TypeObject ) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool init_exceptions();
|
||||
|
||||
|
||||
} // namespace kiwisolver
|
||||
203
kiwi/py/src/util.h
Normal file
203
kiwi/py/src/util.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| 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.
|
||||
|----------------------------------------------------------------------------*/
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cppy/cppy.h>
|
||||
#include <kiwi/kiwi.h>
|
||||
#include "types.h"
|
||||
|
||||
|
||||
namespace kiwisolver
|
||||
{
|
||||
|
||||
inline bool
|
||||
convert_to_double( PyObject* obj, double& out )
|
||||
{
|
||||
if( PyFloat_Check( obj ) )
|
||||
{
|
||||
out = PyFloat_AS_DOUBLE( obj );
|
||||
return true;
|
||||
}
|
||||
if( PyLong_Check( obj ) )
|
||||
{
|
||||
out = PyLong_AsDouble( obj );
|
||||
if( out == -1.0 && PyErr_Occurred() )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
cppy::type_error( obj, "float, int, or long" );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
convert_pystr_to_str( PyObject* value, std::string& out )
|
||||
{
|
||||
out = PyUnicode_AsUTF8( value );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
convert_to_strength( PyObject* value, double& out )
|
||||
{
|
||||
if( PyUnicode_Check( value ) )
|
||||
{
|
||||
std::string str;
|
||||
if( !convert_pystr_to_str( value, str ) )
|
||||
return false;
|
||||
if( str == "required" )
|
||||
out = kiwi::strength::required;
|
||||
else if( str == "strong" )
|
||||
out = kiwi::strength::strong;
|
||||
else if( str == "medium" )
|
||||
out = kiwi::strength::medium;
|
||||
else if( str == "weak" )
|
||||
out = kiwi::strength::weak;
|
||||
else
|
||||
{
|
||||
PyErr_Format(
|
||||
PyExc_ValueError,
|
||||
"string strength must be 'required', 'strong', 'medium', "
|
||||
"or 'weak', not '%s'",
|
||||
str.c_str()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if( !convert_to_double( value, out ) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
convert_to_relational_op( PyObject* value, kiwi::RelationalOperator& out )
|
||||
{
|
||||
if( !PyUnicode_Check( value ) )
|
||||
{
|
||||
cppy::type_error( value, "str" );
|
||||
return false;
|
||||
}
|
||||
std::string str;
|
||||
if( !convert_pystr_to_str( value, str ) )
|
||||
return false;
|
||||
if( str == "==" )
|
||||
out = kiwi::OP_EQ;
|
||||
else if( str == "<=" )
|
||||
out = kiwi::OP_LE;
|
||||
else if( str == ">=" )
|
||||
out = kiwi::OP_GE;
|
||||
else
|
||||
{
|
||||
PyErr_Format(
|
||||
PyExc_ValueError,
|
||||
"relational operator must be '==', '<=', or '>=', not '%s'",
|
||||
str.c_str()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline PyObject*
|
||||
make_terms( const std::map<PyObject*, double>& coeffs )
|
||||
{
|
||||
typedef std::map<PyObject*, double>::const_iterator iter_t;
|
||||
cppy::ptr terms( PyTuple_New( coeffs.size() ) );
|
||||
if( !terms )
|
||||
return 0;
|
||||
Py_ssize_t size = PyTuple_GET_SIZE( terms.get() );
|
||||
for( Py_ssize_t i = 0; i < size; ++i ) // zero tuple for safe early return
|
||||
PyTuple_SET_ITEM( terms.get(), i, 0 );
|
||||
Py_ssize_t i = 0;
|
||||
iter_t it = coeffs.begin();
|
||||
iter_t end = coeffs.end();
|
||||
for( ; it != end; ++it, ++i )
|
||||
{
|
||||
PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 );
|
||||
if( !pyterm )
|
||||
return 0;
|
||||
Term* term = reinterpret_cast<Term*>( pyterm );
|
||||
term->variable = cppy::incref( it->first );
|
||||
term->coefficient = it->second;
|
||||
PyTuple_SET_ITEM( terms.get(), i, pyterm );
|
||||
}
|
||||
return terms.release();
|
||||
}
|
||||
|
||||
|
||||
inline PyObject*
|
||||
reduce_expression( PyObject* pyexpr ) // pyexpr must be an Expression
|
||||
{
|
||||
Expression* expr = reinterpret_cast<Expression*>( pyexpr );
|
||||
std::map<PyObject*, double> coeffs;
|
||||
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 );
|
||||
coeffs[ term->variable ] += term->coefficient;
|
||||
}
|
||||
cppy::ptr terms( make_terms( coeffs ) );
|
||||
if( !terms )
|
||||
return 0;
|
||||
PyObject* pynewexpr = PyType_GenericNew( Expression::TypeObject, 0, 0 );
|
||||
if( !pynewexpr )
|
||||
return 0;
|
||||
Expression* newexpr = reinterpret_cast<Expression*>( pynewexpr );
|
||||
newexpr->terms = terms.release();
|
||||
newexpr->constant = expr->constant;
|
||||
return pynewexpr;
|
||||
}
|
||||
|
||||
|
||||
inline kiwi::Expression
|
||||
convert_to_kiwi_expression( PyObject* pyexpr ) // pyexpr must be an Expression
|
||||
{
|
||||
Expression* expr = reinterpret_cast<Expression*>( pyexpr );
|
||||
std::vector<kiwi::Term> kterms;
|
||||
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 );
|
||||
Variable* var = reinterpret_cast<Variable*>( term->variable );
|
||||
kterms.push_back( kiwi::Term( var->variable, term->coefficient ) );
|
||||
}
|
||||
return kiwi::Expression( kterms, expr->constant );
|
||||
}
|
||||
|
||||
|
||||
inline const char*
|
||||
pyop_str( int op )
|
||||
{
|
||||
switch( op )
|
||||
{
|
||||
case Py_LT:
|
||||
return "<";
|
||||
case Py_LE:
|
||||
return "<=";
|
||||
case Py_EQ:
|
||||
return "==";
|
||||
case Py_NE:
|
||||
return "!=";
|
||||
case Py_GT:
|
||||
return ">";
|
||||
case Py_GE:
|
||||
return ">=";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace kiwisolver
|
||||
270
kiwi/py/src/variable.cpp
Normal file
270
kiwi/py/src/variable.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| 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 <cppy/cppy.h>
|
||||
#include <kiwi/kiwi.h>
|
||||
#include "symbolics.h"
|
||||
#include "types.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
namespace kiwisolver
|
||||
{
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs )
|
||||
{
|
||||
static const char *kwlist[] = { "name", "context", 0 };
|
||||
PyObject* context = 0;
|
||||
PyObject* name = 0;
|
||||
|
||||
if( !PyArg_ParseTupleAndKeywords(
|
||||
args, kwargs, "|OO:__new__", const_cast<char**>( kwlist ),
|
||||
&name, &context ) )
|
||||
return 0;
|
||||
|
||||
cppy::ptr pyvar( PyType_GenericNew( type, args, kwargs ) );
|
||||
if( !pyvar )
|
||||
return 0;
|
||||
|
||||
Variable* self = reinterpret_cast<Variable*>( pyvar.get() );
|
||||
self->context = cppy::xincref( context );
|
||||
|
||||
if( name != 0 )
|
||||
{
|
||||
if( !PyUnicode_Check( name ) )
|
||||
return cppy::type_error( name, "str" );
|
||||
std::string c_name;
|
||||
if( !convert_pystr_to_str(name, c_name) )
|
||||
return 0; // LCOV_EXCL_LINE
|
||||
new( &self->variable ) kiwi::Variable( c_name );
|
||||
}
|
||||
else
|
||||
{
|
||||
new( &self->variable ) kiwi::Variable();
|
||||
}
|
||||
|
||||
return pyvar.release();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Variable_clear( Variable* self )
|
||||
{
|
||||
Py_CLEAR( self->context );
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Variable_traverse( Variable* self, visitproc visit, void* arg )
|
||||
{
|
||||
Py_VISIT( self->context );
|
||||
#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
|
||||
Variable_dealloc( Variable* self )
|
||||
{
|
||||
PyObject_GC_UnTrack( self );
|
||||
Variable_clear( self );
|
||||
self->variable.~Variable();
|
||||
Py_TYPE( self )->tp_free( pyobject_cast( self ) );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_repr( Variable* self )
|
||||
{
|
||||
return PyUnicode_FromString( self->variable.name().c_str() );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_name( Variable* self )
|
||||
{
|
||||
return PyUnicode_FromString( self->variable.name().c_str() );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_setName( Variable* self, PyObject* pystr )
|
||||
{
|
||||
if( !PyUnicode_Check( pystr ) )
|
||||
return cppy::type_error( pystr, "str" );
|
||||
std::string str;
|
||||
if( !convert_pystr_to_str( pystr, str ) )
|
||||
return 0;
|
||||
self->variable.setName( str );
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_context( Variable* self )
|
||||
{
|
||||
if( self->context )
|
||||
return cppy::incref( self->context );
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_setContext( Variable* self, PyObject* value )
|
||||
{
|
||||
if( value != self->context )
|
||||
{
|
||||
PyObject* temp = self->context;
|
||||
self->context = cppy::incref( value );
|
||||
Py_XDECREF( temp );
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_value( Variable* self )
|
||||
{
|
||||
return PyFloat_FromDouble( self->variable.value() );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_add( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryAdd, Variable>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_sub( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinarySub, Variable>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_mul( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryMul, Variable>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_div( PyObject* first, PyObject* second )
|
||||
{
|
||||
return BinaryInvoke<BinaryDiv, Variable>()( first, second );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_neg( PyObject* value )
|
||||
{
|
||||
return UnaryInvoke<UnaryNeg, Variable>()( value );
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
Variable_richcmp( PyObject* first, PyObject* second, int op )
|
||||
{
|
||||
switch( op )
|
||||
{
|
||||
case Py_EQ:
|
||||
return BinaryInvoke<CmpEQ, Variable>()( first, second );
|
||||
case Py_LE:
|
||||
return BinaryInvoke<CmpLE, Variable>()( first, second );
|
||||
case Py_GE:
|
||||
return BinaryInvoke<CmpGE, Variable>()( first, second );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
PyErr_Format(
|
||||
PyExc_TypeError,
|
||||
"unsupported operand type(s) for %s: "
|
||||
"'%.100s' and '%.100s'",
|
||||
pyop_str( op ),
|
||||
Py_TYPE( first )->tp_name,
|
||||
Py_TYPE( second )->tp_name
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef
|
||||
Variable_methods[] = {
|
||||
{ "name", ( PyCFunction )Variable_name, METH_NOARGS,
|
||||
"Get the name of the variable." },
|
||||
{ "setName", ( PyCFunction )Variable_setName, METH_O,
|
||||
"Set the name of the variable." },
|
||||
{ "context", ( PyCFunction )Variable_context, METH_NOARGS,
|
||||
"Get the context object associated with the variable." },
|
||||
{ "setContext", ( PyCFunction )Variable_setContext, METH_O,
|
||||
"Set the context object associated with the variable." },
|
||||
{ "value", ( PyCFunction )Variable_value, METH_NOARGS,
|
||||
"Get the current value of the variable." },
|
||||
{ 0 } // sentinel
|
||||
};
|
||||
|
||||
|
||||
static PyType_Slot Variable_Type_slots[] = {
|
||||
{ Py_tp_dealloc, void_cast( Variable_dealloc ) }, /* tp_dealloc */
|
||||
{ Py_tp_traverse, void_cast( Variable_traverse ) }, /* tp_traverse */
|
||||
{ Py_tp_clear, void_cast( Variable_clear ) }, /* tp_clear */
|
||||
{ Py_tp_repr, void_cast( Variable_repr ) }, /* tp_repr */
|
||||
{ Py_tp_richcompare, void_cast( Variable_richcmp ) }, /* tp_richcompare */
|
||||
{ Py_tp_methods, void_cast( Variable_methods ) }, /* tp_methods */
|
||||
{ Py_tp_new, void_cast( Variable_new ) }, /* tp_new */
|
||||
{ Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */
|
||||
{ Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */
|
||||
{ Py_nb_add, void_cast( Variable_add ) }, /* nb_add */
|
||||
{ Py_nb_subtract, void_cast( Variable_sub ) }, /* nb_subtract */
|
||||
{ Py_nb_multiply, void_cast( Variable_mul ) }, /* nb_multiply */
|
||||
{ Py_nb_negative, void_cast( Variable_neg ) }, /* nb_negative */
|
||||
{ Py_nb_true_divide, void_cast( Variable_div ) }, /* nb_true_divide */
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// Initialize static variables (otherwise the compiler eliminates them)
|
||||
PyTypeObject* Variable::TypeObject = NULL;
|
||||
|
||||
|
||||
PyType_Spec Variable::TypeObject_Spec = {
|
||||
"kiwisolver.Variable", /* tp_name */
|
||||
sizeof( Variable ), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
Py_TPFLAGS_DEFAULT|
|
||||
Py_TPFLAGS_HAVE_GC|
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
Variable_Type_slots /* slots */
|
||||
};
|
||||
|
||||
|
||||
bool Variable::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
|
||||
Reference in New Issue
Block a user