/* 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 #include struct CoordHelper { template static Py::Object getattro(T& self, const Py::String& name); template static int setattro(T& coords, const Py::String& name, const Py::Object& object); template static Py::Object repr(const T& coords); template static TValue parse(const Py::Object& object); template static Py::Boolean is_valid(const TValue& coords); template static Py::Object rich_compare(const TValue& coords, const Py::Object& object, int type); template static Py::Object sequence_item(const TValue& coords, Py_ssize_t pos); template static int sequence_ass_item(TValue& coords, Py_ssize_t pos, const Py::Object& object); template static void init(TValue& value, const Py::Tuple& args); }; template 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 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 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 TValue CoordHelper::parse(const Py::Object& object) { if (TPythonClass::check(object)) { return Py::PythonClassObject(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 Py::Boolean CoordHelper::is_valid(const TValue& coords) { return Py::Boolean(coords.isValid()); } template 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 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 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 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