App: add Expression built-in function mscale/minvert/create

mscale(matrix,vector|tuple) for scaling matrix

minvert(matrix|placement|rotation) for inverting matrix, placement or
rotation

create(type,arg...) for creating Python object of given type (currently
only support 'matrix', 'placement', 'rotation', 'vector'
This commit is contained in:
Zheng, Lei
2019-09-29 17:38:07 +08:00
committed by wmayer
parent b2ba93c4fc
commit 2df4fb754b
2 changed files with 97 additions and 0 deletions

View File

@@ -30,6 +30,8 @@
# pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor"
#endif
#include <boost/algorithm/string/predicate.hpp>
#include <Base/Console.h>
#include "Base/Exception.h"
#include <Base/Interpreter.h>
@@ -39,6 +41,10 @@
#include <App/DocumentObject.h>
#include <App/PropertyUnits.h>
#include <Base/QuantityPy.h>
#include <Base/MatrixPy.h>
#include <Base/PlacementPy.h>
#include <Base/RotationPy.h>
#include <Base/VectorPy.h>
#include <QStringList>
#include <string>
#include <sstream>
@@ -1771,6 +1777,7 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
case TRUNC:
case CEIL:
case FLOOR:
case MINVERT:
if (args.size() != 1)
EXPR_THROW("Invalid number of arguments: exactly one required.");
break;
@@ -1791,6 +1798,8 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f
case COUNT:
case MIN:
case MAX:
case CREATE:
case MSCALE:
if (args.size() == 0)
EXPR_THROW("Invalid number of arguments: at least one required.");
break;
@@ -2039,11 +2048,87 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std
for(auto &arg : args)
tuple.setItem(i++,arg->getPyValue());
return tuple;
} else if (f == MSCALE) {
if(args.size() < 2)
_EXPR_THROW("Function requires at least two arguments.",expr);
Py::Object pymat = args[0]->getPyValue();
Py::Object pyscale;
if(PyObject_TypeCheck(pymat.ptr(),&Base::MatrixPy::Type)) {
if(args.size() == 2) {
Py::Object obj = args[1]->getPyValue();
if(obj.isSequence() && PySequence_Size(obj.ptr())==3)
pyscale = Py::Tuple(Py::Sequence(obj));
} else if(args.size() == 4) {
Py::Tuple tuple(3);
tuple.setItem(0,args[1]->getPyValue());
tuple.setItem(1,args[2]->getPyValue());
tuple.setItem(2,args[3]->getPyValue());
pyscale = tuple;
}
}
if(!pyscale.isNone()) {
Base::Vector3d vec;
if (!PyArg_ParseTuple(pyscale.ptr(), "ddd", &vec.x,&vec.y,&vec.z))
PyErr_Clear();
else {
auto mat = static_cast<Base::MatrixPy*>(pymat.ptr())->value();
mat.scale(vec);
return Py::Object(new Base::MatrixPy(mat));
}
}
_EXPR_THROW("Function requires arguments to be either "
"(matrix,vector) or (matrix,number,number,number).", expr);
}
if(args.empty())
_EXPR_THROW("Function requires at least one argument.",expr);
if (f == MINVERT) {
Py::Object pyobj = args[0]->getPyValue();
Py::Tuple args;
if (PyObject_TypeCheck(pyobj.ptr(),&Base::MatrixPy::Type)) {
auto m = static_cast<Base::MatrixPy*>(pyobj.ptr())->value();
if (fabs(m.determinant()) <= DBL_EPSILON)
_EXPR_THROW("Cannot invert singular matrix.",expr);
m.inverseGauss();
return Py::Object(new Base::MatrixPy(m));
} else if (PyObject_TypeCheck(pyobj.ptr(),&Base::PlacementPy::Type)) {
const auto &pla = *static_cast<Base::PlacementPy*>(pyobj.ptr())->getPlacementPtr();
return Py::Object(new Base::PlacementPy(pla.inverse()));
} else if (PyObject_TypeCheck(pyobj.ptr(),&Base::RotationPy::Type)) {
const auto &rot = *static_cast<Base::RotationPy*>(pyobj.ptr())->getRotationPtr();
return Py::Object(new Base::RotationPy(rot.inverse()));
}
_EXPR_THROW("Function requires the first argument to be either Matrix, Placement or Rotation.",expr);
} else if (f == CREATE) {
Py::Object pytype = args[0]->getPyValue();
if(!pytype.isString())
_EXPR_THROW("Function requires the first argument to be a string.",expr);
std::string type(pytype.as_string());
Py::Object res;
if(boost::iequals(type,"matrix"))
res = Py::Object(new Base::MatrixPy(Base::Matrix4D()));
else if(boost::iequals(type,"vector"))
res = Py::Object(new Base::VectorPy(Base::Vector3d()));
else if(boost::iequals(type,"placement"))
res = Py::Object(new Base::PlacementPy(Base::Placement()));
else if(boost::iequals(type,"rotation"))
res = Py::Object(new Base::RotationPy(Base::Rotation()));
else
_EXPR_THROW("Unknown type '" << type << "'.",expr);
if(args.size()>1) {
Py::Tuple tuple(args.size()-1);
for(unsigned i=1;i<args.size();++i)
tuple.setItem(i-1,args[i]->getPyValue());
Py::Dict dict;
PyObjectBase::__PyInit(res.ptr(),tuple.ptr(),dict.ptr());
}
return res;
}
Py::Object e1 = args[0]->getPyValue();
Quantity v1 = pyToQuantity(e1,expr,"Invalid first argument.");
Py::Object e2;
@@ -2368,6 +2453,12 @@ void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const
ss << "list("; break;;
case TUPLE:
ss << "tuple("; break;;
case MSCALE:
ss << "mscale("; break;;
case MINVERT:
ss << "minvert("; break;;
case CREATE:
ss << "create("; break;;
default:
assert(0);
}
@@ -3184,6 +3275,9 @@ static void initParser(const App::DocumentObject *owner)
registered_functions["cath"] = FunctionExpression::CATH;
registered_functions["list"] = FunctionExpression::LIST;
registered_functions["tuple"] = FunctionExpression::TUPLE;
registered_functions["mscale"] = FunctionExpression::MSCALE;
registered_functions["minvert"] = FunctionExpression::MINVERT;
registered_functions["create"] = FunctionExpression::CREATE;
// Aggregates
registered_functions["sum"] = FunctionExpression::SUM;

View File

@@ -269,6 +269,9 @@ public:
CATH,
LIST,
TUPLE,
MSCALE, // matrix scale by vector
MINVERT, // invert matrix/placement/rotation
CREATE, // create new object of a given type
// Aggregates
AGGREGATES,