cyphesis/rules/python/CoordHelper.h
Erik Ogenvik e1ab2f5f85 Handle native Python types instead of Message.
When handling properties and attributes in the Python code we now use
native types, together with ElementMap and ElementList.
2018-12-16 17:31:35 +01:00

215 lines
6.6 KiB
C++

/*
Copyright (C) 2018 Erik Ogenvik
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef CYPHESIS_COORDHELPER_H
#define CYPHESIS_COORDHELPER_H
#include "external/pycxx/CXX/Objects.hxx"
#include "external/pycxx/CXX/Extensions.hxx"
#include "CyPy_Element.h"
#include <wfmath/vector.h>
#include <wfmath/atlasconv.h>
struct CoordHelper
{
template<typename T>
static Py::Object getattro(T& self, const Py::String& name);
template<typename T>
static int setattro(T& coords, const Py::String& name, const Py::Object& object);
template<typename T>
static Py::Object repr(const T& coords);
template<typename TValue, typename TPythonClass>
static TValue parse(const Py::Object& object);
template<typename TValue>
static Py::Boolean is_valid(const TValue& coords);
template<typename TValue, typename TPythonClass>
static Py::Object rich_compare(const TValue& coords, const Py::Object& object, int type);
template<typename TValue>
static Py::Object sequence_item(const TValue& coords, Py_ssize_t pos);
template<typename TValue>
static int sequence_ass_item(TValue& coords, Py_ssize_t pos, const Py::Object& object);
template<typename TValue>
static void init(TValue& value, const Py::Tuple& args);
};
template<typename T>
Py::Object CoordHelper::getattro(T& self, const Py::String& name)
{
std::string nameStr = name;
if (nameStr == "x") { return Py::Float(self.m_value.x()); }
if (nameStr == "y") { return Py::Float(self.m_value.y()); }
if (nameStr == "z") { return Py::Float(self.m_value.z()); }
return Py::asObject(PyObject_GenericGetAttr(self.selfPtr(), name.ptr()));
}
template<typename T>
int CoordHelper::setattro(T& coords, const Py::String& name, const Py::Object& object)
{
std::string nameStr = name;
if (!object.isNumeric()) {
throw Py::TypeError("Coord attributes must be numeric");
}
Py::Float val(object);
if (nameStr == "x") {
coords.x() = val.as_double();
} else if (nameStr == "y") {
coords.y() = val.as_double();
} else if (nameStr == "z") {
coords.z() = val.as_double();
} else {
throw Py::AttributeError("Coord attribute does not exist");
}
return 0;
}
template<typename T>
Py::Object CoordHelper::repr(const T& coords)
{
char buf[64];
::snprintf(buf, 64, "(%f, %f, %f)", coords.x(), coords.y(), coords.z());
return Py::String(buf);
}
template<typename TValue, typename TPythonClass>
TValue CoordHelper::parse(const Py::Object& object)
{
if (TPythonClass::check(object)) {
return Py::PythonClassObject<TPythonClass>(object).getCxxObject()->m_value;
}
if (object.isList() || object.isTuple()) {
Py::Sequence seq(object);
if (seq.length() == 3) {
if (seq[0].isNumeric() && seq[1].isNumeric() && seq[2].isNumeric()) {
return TValue(Py::Float(seq[0]), Py::Float(seq[1]), Py::Float(seq[2]));
}
}
}
if (object.isNone()) {
return TValue{};
}
throw Py::TypeError("Object can not be converted into a Coord.");
}
template<typename TValue>
Py::Boolean CoordHelper::is_valid(const TValue& coords)
{
return Py::Boolean(coords.isValid());
}
template<typename TValue, typename TPythonClass>
Py::Object CoordHelper::rich_compare(const TValue& coords, const Py::Object& object, int type)
{
if (type == Py_EQ) {
if (!TPythonClass::check(object)) {
return Py::False();
}
return Py::Boolean(coords == TPythonClass::value(object));
} else if (type == Py_NE) {
if (!TPythonClass::check(object)) {
return Py::True();
}
return Py::Boolean(coords != TPythonClass::value(object));
}
throw Py::NotImplementedError("Not implemented");
}
template<typename TValue>
Py::Object CoordHelper::sequence_item(const TValue& coords, Py_ssize_t pos)
{
if (pos < 0 || pos > 2) {
throw Py::IndexError("Index needs to be [0..2]");
}
return Py::Float(coords[pos]);
}
template<typename TValue>
int CoordHelper::sequence_ass_item(TValue& coords, Py_ssize_t pos, const Py::Object& object)
{
if (pos < 0 || pos >= 3) {
throw Py::IndexError("Coords.[]: Index out of range.");
}
if (!object.isNumeric()) {
throw Py::TypeError("Coords.[]: Value must be numeric.");
}
coords[pos] = Py::Float(object).as_double();
return 0;
}
template<typename TValue>
void CoordHelper::init(TValue& value, const Py::Tuple& args)
{
switch (args.size()) {
case 0:
break;
case 1: {
auto arg = args.front();
if (!arg.isSequence()) {
throw Py::TypeError("Coords from single value must be a sequence");
}
Py::Sequence sequence(arg);
if (sequence.size() != 3) {
throw Py::ValueError("Coords from a sequence must be 3 long");
}
for (int i = 0; i < 3; i++) {
auto item = sequence[i];
if (item.isNumeric()) {
value[i] = Py::Float(item).as_double();
} else if (CyPy_ElementList::check(item)) {
value.fromAtlas(CyPy_ElementList::value(item));
} else {
throw Py::TypeError("Coords must take list of floats, or ints");
}
}
value.setValid();
}
break;
case 3:
for (int i = 0; i < 3; i++) {
auto item = args[i];
if (item.isNumeric()) {
value[i] = Py::Float(item).as_double();
} else if (CyPy_ElementList::check(item)) {
value.fromAtlas(CyPy_ElementList::value(item));
} else {
throw Py::TypeError("Coords must take list of floats, or ints");
}
}
value.setValid();
break;
default:
throw Py::TypeError("Coords must take list of floats, or ints, 3 ints or 3 floats");
}
}
#endif //CYPHESIS_COORDHELPER_H