Skip to content

Commit

Permalink
Refactor pybind11 wrapper code into a single module.
Browse files Browse the repository at this point in the history
  • Loading branch information
TallJimbo committed Sep 20, 2018
1 parent 230e10e commit dbd62ff
Show file tree
Hide file tree
Showing 22 changed files with 1,243 additions and 1,229 deletions.
13 changes: 5 additions & 8 deletions python/lsst/geom/SConscript
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
# -*- python -*-
from lsst.sconsUtils import scripts
scripts.BasicSConscript.pybind11(
[
'affineTransform',
'angle/angle',
'box',
'coordinates/coordinates',
'linearTransform',
'spherePoint/spherePoint',
],
['_geom'],
extraSrc={
"_geom": ["_AffineTransform.cc", "_Angle.cc", "_Box.cc", "_coordinates.cc",
"_LinearTransform.cc", "_SpherePoint.cc"]
},
addUnderscore=False
)
119 changes: 119 additions & 0 deletions python/lsst/geom/_AffineTransform.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Developed for the LSST Data Management System.
* This product includes software developed by the LSST Project
* (https://www.lsst.org).
* See the COPYRIGHT file at the top-level directory of this distribution
* for details of code ownership.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/

#include "pybind11/pybind11.h"
#include "pybind11/eigen.h"
#include "pybind11/stl.h"

#include "ndarray/pybind11.h"

#include "lsst/geom/AffineTransform.h"
#include "lsst/utils/python.h"

namespace py = pybind11;
using namespace pybind11::literals;

namespace lsst {
namespace geom {

void wrapAffineTransform(utils::python::WrapperCollection & wrappers) {
wrappers.wrapType(
py::class_<AffineTransform, std::shared_ptr<AffineTransform>>(wrappers.module, "AffineTransform"),
[](auto & mod, auto & cls) mutable {

// Parameters enum is really only used as integer constants.
cls.attr("XX") = py::cast(int(AffineTransform::Parameters::XX));
cls.attr("YX") = py::cast(int(AffineTransform::Parameters::YX));
cls.attr("XY") = py::cast(int(AffineTransform::Parameters::XY));
cls.attr("YY") = py::cast(int(AffineTransform::Parameters::YY));
cls.attr("X") = py::cast(int(AffineTransform::Parameters::X));
cls.attr("Y") = py::cast(int(AffineTransform::Parameters::Y));

/* Constructors */
cls.def(py::init<>());
cls.def(py::init<Eigen::Matrix3d const &>(), "matrix"_a);
cls.def(py::init<Eigen::Matrix2d const &>(), "linear"_a);
cls.def(py::init<Eigen::Vector2d const &>(), "translation"_a);
cls.def(py::init<Eigen::Matrix2d const &, Eigen::Vector2d const &>(),
"linear"_a, "translation"_a);
cls.def(py::init<LinearTransform const &>(), "linear"_a);
cls.def(py::init<Extent2D const &>(), "translation"_a);
cls.def(py::init<LinearTransform const &, Extent2D const &>(), "linear"_a, "translation"_a);

/* Operators and special methods */
cls.def("__mul__", &AffineTransform::operator*, py::is_operator());
cls.def("__call__",
py::overload_cast<Point2D const &>(&AffineTransform::operator(), py::const_));
cls.def("__call__",
py::overload_cast<Extent2D const &>(&AffineTransform::operator(), py::const_));
cls.def("__setitem__", [](AffineTransform &self, int i, double value) {
if (i < 0 || i > 5) {
PyErr_Format(PyExc_IndexError, "Invalid index for AffineTransform: %d", i);
throw py::error_already_set();
}
self[i] = value;
});
cls.def("__getitem__", [](AffineTransform const &self, int row, int col) {
if (row < 0 || row > 2 || col < 0 || col > 2) {
PyErr_Format(PyExc_IndexError, "Invalid index for AffineTransform: %d, %d", row, col);
throw py::error_already_set();
}
return (self.getMatrix())(row, col);
});
cls.def("__getitem__", [](AffineTransform const &self, int i) {
if (i < 0 || i > 5) {
PyErr_Format(PyExc_IndexError, "Invalid index for AffineTransform: %d", i);
throw py::error_already_set();
}
return self[i];
});
cls.def("__str__", [](AffineTransform const &self) {
return py::str(py::cast(self.getMatrix())); }
);
cls.def("__repr__", [](AffineTransform const &self) {
return py::str("AffineTransform(\n{}\n)").format(py::cast(self.getMatrix()));
});
cls.def("__reduce__", [cls](AffineTransform const &self) {
return py::make_tuple(cls, py::make_tuple(py::cast(self.getMatrix())));
});

/* Members */
cls.def("inverted", &AffineTransform::inverted);
cls.def("invert", &AffineTransform::invert);
cls.def("isIdentity", &AffineTransform::isIdentity);
cls.def("getTranslation", (Extent2D & (AffineTransform::*)()) & AffineTransform::getTranslation);
cls.def("getLinear", (LinearTransform & (AffineTransform::*)()) & AffineTransform::getLinear);
cls.def("getMatrix", &AffineTransform::getMatrix);
cls.def("getParameterVector", &AffineTransform::getParameterVector);
cls.def("setParameterVector", &AffineTransform::setParameterVector);
cls.def_static("makeScaling", py::overload_cast<double>(&AffineTransform::makeScaling));
cls.def_static("makeScaling", py::overload_cast<double, double>(&AffineTransform::makeScaling));
cls.def_static("makeRotation", &AffineTransform::makeRotation, "angle"_a);
cls.def_static("makeTranslation", &AffineTransform::makeTranslation, "translation"_a);

/* Non-members */
mod.def("makeAffineTransformFromTriple", makeAffineTransformFromTriple);
}
);
}

} // namespace geom
} // namespace lsst
150 changes: 150 additions & 0 deletions python/lsst/geom/_Angle.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Developed for the LSST Data Management System.
* This product includes software developed by the LSST Project
* (https://www.lsst.org).
* See the COPYRIGHT file at the top-level directory of this distribution
* for details of code ownership.
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/

#include "pybind11/pybind11.h"

#include "lsst/utils/python.h"

#include "lsst/geom/Angle.h"

namespace py = pybind11;

namespace lsst {
namespace geom {

using PyAngle = py::class_<Angle>;
using PyAngleUnit = py::class_<AngleUnit>;

namespace {

template <typename OtherT>
void declareAngleComparisonOperators(PyAngle& cls) {
cls.def("__eq__", [](Angle const& self, OtherT const& other) { return self == other; },
py::is_operator());
cls.def("__ne__", [](Angle const& self, OtherT const& other) { return self != other; },
py::is_operator());
cls.def("__le__", [](Angle const& self, OtherT const& other) { return self <= other; },
py::is_operator());
cls.def("__ge__", [](Angle const& self, OtherT const& other) { return self >= other; },
py::is_operator());
cls.def("__lt__", [](Angle const& self, OtherT const& other) { return self < other; }, py::is_operator());
cls.def("__gt__", [](Angle const& self, OtherT const& other) { return self > other; }, py::is_operator());
}

} // anonymous

void wrapAngle(utils::python::WrapperCollection & wrappers) {
wrappers.wrapType(
PyAngleUnit(wrappers.module, "AngleUnit"),
[](auto & mod, auto & cls) mutable {
cls.def("__eq__", [](AngleUnit const& self, AngleUnit const& other) { return self == other; },
py::is_operator());
cls.def("__ne__", [](AngleUnit const& self, AngleUnit const& other) { return !(self == other); },
py::is_operator());
cls.def("_mul", [](AngleUnit const& self, double other) { return other * self; },
py::is_operator());
cls.def("_rmul", [](AngleUnit const& self, double other) { return other * self; },
py::is_operator());
mod.attr("radians") = py::cast(radians);
mod.attr("degrees") = py::cast(degrees);
mod.attr("hours") = py::cast(hours);
mod.attr("arcminutes") = py::cast(arcminutes);
mod.attr("arcseconds") = py::cast(arcseconds);
mod.attr("milliarcseconds") = py::cast(milliarcseconds);
}
);

wrappers.wrapType(
PyAngle(wrappers.module, "Angle"),
[](auto & mod, auto & cls) mutable {
cls.def(py::init<double, AngleUnit>(), py::arg("val"), py::arg("units") = radians);
cls.def(py::init<>());
declareAngleComparisonOperators<Angle>(cls);
declareAngleComparisonOperators<double>(cls);
declareAngleComparisonOperators<int>(cls);
cls.def("__mul__", [](Angle const& self, double other) { return self * other; },
py::is_operator());
cls.def("__mul__", [](Angle const& self, int other) { return self * other; },
py::is_operator());
cls.def("__rmul__", [](Angle const& self, double other) { return self * other; },
py::is_operator());
cls.def("__rmul__", [](Angle const& self, int other) { return self * other; },
py::is_operator());
cls.def("__imul__", [](Angle& self, double other) { return self *= other; });
cls.def("__imul__", [](Angle& self, int other) { return self *= other; });
cls.def("__add__", [](Angle const& self, Angle const& other) { return self + other; },
py::is_operator());
cls.def("__sub__", [](Angle const& self, Angle const& other) { return self - other; },
py::is_operator());
cls.def("__neg__", [](Angle const& self) { return -self; }, py::is_operator());
cls.def("__iadd__", [](Angle& self, Angle const& other) { return self += other; });
cls.def("__isub__", [](Angle& self, Angle const& other) { return self -= other; });
cls.def("__truediv__", [](Angle const& self, double other) { return self / other; },
py::is_operator());
// Without an explicit wrapper, Python lets Angle / Angle -> Angle
cls.def("__truediv__", [](Angle const& self, Angle const& other) {
throw py::type_error("unsupported operand type(s) for /: 'Angle' and 'Angle'");
});
cls.def("__float__", &Angle::operator double);
cls.def("__abs__", [](Angle const& self) { return std::abs(self.asRadians()) * radians; });

cls.def("__reduce__", [cls](Angle const& self) {
return py::make_tuple(cls, py::make_tuple(py::cast(self.asRadians())));
});
utils::python::addOutputOp(cls, "__str__");
utils::python::addOutputOp(cls, "__repr__");
cls.def("asAngularUnits", &Angle::asAngularUnits);
cls.def("asRadians", &Angle::asRadians);
cls.def("asDegrees", &Angle::asDegrees);
cls.def("asHours", &Angle::asHours);
cls.def("asArcminutes", &Angle::asArcminutes);
cls.def("asArcseconds", &Angle::asArcseconds);
cls.def("asMilliarcseconds", &Angle::asMilliarcseconds);
cls.def("wrap", &Angle::wrap);
cls.def("wrapCtr", &Angle::wrapCtr);
cls.def("wrapNear", &Angle::wrapNear);
cls.def("separation", &Angle::separation);
mod.def("isAngle", isAngle<Angle>);
mod.def("isAngle", isAngle<double>);
}
);

wrappers.wrapFunctions(
[](auto & mod) mutable {
mod.attr("PI") = py::float_(PI);
mod.attr("TWOPI") = py::float_(TWOPI);
mod.attr("HALFPI") = py::float_(HALFPI);
mod.attr("ONE_OVER_PI") = py::float_(ONE_OVER_PI);
mod.attr("SQRTPI") = py::float_(SQRTPI);
mod.attr("INVSQRTPI") = py::float_(INVSQRTPI);
mod.attr("ROOT2") = py::float_(ROOT2);
mod.def("degToRad", degToRad);
mod.def("radToDeg", radToDeg);
mod.def("radToArcsec", radToArcsec);
mod.def("radToMas", radToMas);
mod.def("arcsecToRad", arcsecToRad);
mod.def("masToRad", masToRad);
}
);
}

} // namespace geom
} // namespace lsst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
__all__ = []

from lsst.utils import continueClass
from .angle import Angle, AngleUnit
from ._geom import Angle, AngleUnit


@continueClass # noqa F811
Expand Down

0 comments on commit dbd62ff

Please sign in to comment.