mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
When handling properties and attributes in the Python code we now use native types, together with ElementMap and ElementList.
215 lines
6.6 KiB
C++
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
|