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

251
kiwi/py/src/expression.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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