LSST Applications  21.0.0+04719a4bac,21.0.0-1-ga51b5d4+f5e6047307,21.0.0-11-g2b59f77+a9c1acf22d,21.0.0-11-ga42c5b2+86977b0b17,21.0.0-12-gf4ce030+76814010d2,21.0.0-13-g1721dae+760e7a6536,21.0.0-13-g3a573fe+768d78a30a,21.0.0-15-g5a7caf0+f21cbc5713,21.0.0-16-g0fb55c1+b60e2d390c,21.0.0-19-g4cded4ca+71a93a33c0,21.0.0-2-g103fe59+bb20972958,21.0.0-2-g45278ab+04719a4bac,21.0.0-2-g5242d73+3ad5d60fb1,21.0.0-2-g7f82c8f+8babb168e8,21.0.0-2-g8f08a60+06509c8b61,21.0.0-2-g8faa9b5+616205b9df,21.0.0-2-ga326454+8babb168e8,21.0.0-2-gde069b7+5e4aea9c2f,21.0.0-2-gecfae73+1d3a86e577,21.0.0-2-gfc62afb+3ad5d60fb1,21.0.0-25-g1d57be3cd+e73869a214,21.0.0-3-g357aad2+ed88757d29,21.0.0-3-g4a4ce7f+3ad5d60fb1,21.0.0-3-g4be5c26+3ad5d60fb1,21.0.0-3-g65f322c+e0b24896a3,21.0.0-3-g7d9da8d+616205b9df,21.0.0-3-ge02ed75+a9c1acf22d,21.0.0-4-g591bb35+a9c1acf22d,21.0.0-4-g65b4814+b60e2d390c,21.0.0-4-gccdca77+0de219a2bc,21.0.0-4-ge8a399c+6c55c39e83,21.0.0-5-gd00fb1e+05fce91b99,21.0.0-6-gc675373+3ad5d60fb1,21.0.0-64-g1122c245+4fb2b8f86e,21.0.0-7-g04766d7+cd19d05db2,21.0.0-7-gdf92d54+04719a4bac,21.0.0-8-g5674e7b+d1bd76f71f,master-gac4afde19b+a9c1acf22d,w.2021.13
LSST Data Management Base Package
_coordinates.cc
Go to the documentation of this file.
1 /*
2  * Developed for the LSST Data Management System.
3  * This product includes software developed by the LSST Project
4  * (https://www.lsst.org).
5  * See the COPYRIGHT file at the top-level directory of this distribution
6  * for details of code ownership.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "pybind11/pybind11.h"
23 #include "pybind11/eigen.h"
24 #include "ndarray/pybind11.h"
25 
26 #include "lsst/geom/Point.h"
27 #include "lsst/geom/Extent.h"
30 #include "lsst/utils/python.h"
31 
32 namespace py = pybind11;
33 using namespace pybind11::literals;
34 
35 namespace lsst {
36 namespace geom {
37 namespace {
38 
39 template <typename Derived, typename T, int N>
40 using PyCoordinateBase = py::class_<CoordinateBase<Derived, T, N>>;
41 
42 template <int N>
43 using PyCoordinateExpr = py::class_<CoordinateExpr<N>, CoordinateBase<CoordinateExpr<N>, bool, N>>;
44 
45 template <typename T, int N>
46 using PyExtentBase = py::class_<ExtentBase<T, N>, CoordinateBase<Extent<T, N>, T, N>>;
47 
48 template <typename T, int N>
49 using PyExtent = py::class_<Extent<T, N>, ExtentBase<T, N>>;
50 
51 template <typename T, int N>
52 using PyPointBase = py::class_<PointBase<T, N>, CoordinateBase<Point<T, N>, T, N>>;
53 
54 template <typename T, int N>
55 using PyPoint = py::class_<Point<T, N>, PointBase<T, N>>;
56 
57 template <typename Derived, typename T, int N>
58 void declareCoordinateBase(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
59  static std::string const name = "CoordinateBase" + suffix;
60  wrappers.wrapType(
61  PyCoordinateBase<Derived, T, N>(wrappers.module, name.c_str()),
62  [](auto & mod, auto & cls) {
63  cls.def("__getitem__", [](CoordinateBase<Derived, T, N> &self, int i) -> T {
64  return self[utils::python::cppIndex(N, i)];
65  });
66  cls.def("__setitem__", [](CoordinateBase<Derived, T, N> &self, int i, T value) {
67  self[utils::python::cppIndex(N, i)] = value;
68  });
69  cls.def("__len__", [](CoordinateBase<Derived, T, N> &c) -> int { return N; });
70  }
71  );
72 }
73 
74 template <int N>
75 void declareCoordinateExpr(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
76  static std::string const name = "CoordinateExpr" + suffix;
77  declareCoordinateBase<CoordinateExpr<N>, bool, N>(wrappers, name);
78  wrappers.wrapType(
79  PyCoordinateExpr<N>(wrappers.module, name.c_str()),
80  [](auto & mod, auto & cls) {
81  cls.def(py::init<bool>(), "val"_a = false);
82  cls.def("and_", &CoordinateExpr<N>::and_);
83  cls.def("or_", &CoordinateExpr<N>::or_);
84  cls.def("not_", &CoordinateExpr<N>::not_);
85  mod.def("all", all<N>);
86  mod.def("any", any<N>);
87  }
88  );
89 }
90 
91 template <typename T, int N>
92 void declareExtentBase(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
93  static std::string const name = "ExtentBase" + suffix;
94  declareCoordinateBase<Extent<T, N>, T, N>(wrappers, "Extent" + suffix);
95  wrappers.wrapType(
96  PyExtentBase<T, N>(wrappers.module, name.c_str()),
97  [](auto & mod, auto & cls) {
98  // These are not the usual Python double-underscore operators - they do elementwise comparisons,
99  // returning a CoordinateExpr object with boolean x and y values. NumPy examples to the contrary
100  // notwithstanding, true Python comparison operators are expected to return scalar bools.
101  cls.def("eq", [](ExtentBase<T, N> const &self, Extent<T, N> other) { return self.eq(other); });
102  cls.def("ne", [](ExtentBase<T, N> const &self, Extent<T, N> other) { return self.ne(other); });
103  cls.def("lt", [](ExtentBase<T, N> const &self, Extent<T, N> other) { return self.lt(other); });
104  cls.def("le", [](ExtentBase<T, N> const &self, Extent<T, N> other) { return self.le(other); });
105  cls.def("gt", [](ExtentBase<T, N> const &self, Extent<T, N> other) { return self.gt(other); });
106  cls.def("ge", [](ExtentBase<T, N> const &self, Extent<T, N> other) { return self.ge(other); });
107  cls.def("eq", [](ExtentBase<T, N> const &self, T other) { return self.eq(other); });
108  cls.def("ne", [](ExtentBase<T, N> const &self, T other) { return self.ne(other); });
109  cls.def("lt", [](ExtentBase<T, N> const &self, T other) { return self.lt(other); });
110  cls.def("le", [](ExtentBase<T, N> const &self, T other) { return self.le(other); });
111  cls.def("gt", [](ExtentBase<T, N> const &self, T other) { return self.gt(other); });
112  cls.def("ge", [](ExtentBase<T, N> const &self, T other) { return self.ge(other); });
113 
114  cls.def("asPoint", &ExtentBase<T, N>::asPoint);
115  cls.def("computeNorm", &ExtentBase<T, N>::computeNorm);
116  cls.def("computeSquaredNorm", &ExtentBase<T, N>::computeSquaredNorm);
117  }
118  );
119 }
120 
121 // Common functionality for all Extents, including declaring base classes.
122 template <typename T, int N>
123 PyExtent<T, N> declareExtent(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
124  static std::string const name = "Extent" + suffix;
125  declareExtentBase<T, N>(wrappers, suffix);
126  return wrappers.wrapType(
127  PyExtent<T, N>(wrappers.module, name.c_str()),
128  [](auto & mod, auto & cls) {
129  cls.def(py::init<T>(), "value"_a = static_cast<T>(0));
130  cls.def(py::init<Point<int, N> const &>());
131  cls.def(py::init<Point<T, N> const &>());
132  cls.def(py::init<Extent<int, N> const &>());
133  cls.def(py::init<Extent<T, N> const &>());
134  cls.def(py::init<typename Extent<T, N>::EigenVector>());
135  cls.def("__neg__", [](Extent<T, N> const &self) { return -self; });
136  cls.def("__pos__", [](Extent<T, N> const &self) { return self; });
137  cls.def("__mul__", [](Extent<T, N> const &self, int other) { return self * other; },
138  py::is_operator());
139  cls.def("__mul__", [](Extent<T, N> const &self, double other) { return self * other; },
140  py::is_operator());
141  cls.def("__rmul__", [](Extent<T, N> const &self, int other) { return self * other; },
142  py::is_operator());
143  cls.def("__rmul__", [](Extent<T, N> const &self, double other) { return self * other; },
144  py::is_operator());
145  cls.def("__add__",
146  [](Extent<T, N> const &self, Extent<int, N> const &other) { return self + other; },
147  py::is_operator());
148  cls.def("__add__",
149  [](Extent<T, N> const &self, Extent<double, N> const &other) { return self + other; },
150  py::is_operator());
151  cls.def("__add__",
152  [](Extent<T, N> const &self, Point<int, N> const &other) {
153  return self + Point<T, N>(other);
154  },
155  py::is_operator());
156  cls.def("__add__",
157  [](Extent<T, N> const &self, Point<double, N> const &other) { return self + other; },
158  py::is_operator());
159  cls.def("__sub__",
160  [](Extent<T, N> const &self, Extent<int, N> const &other) {
161  return self - Extent<T, N>(other);
162  },
163  py::is_operator());
164  cls.def("__sub__",
165  [](Extent<T, N> const &self, Extent<double, N> const &other) { return self - other; },
166  py::is_operator());
167  cls.def("__eq__",
168  [](Extent<T, N> const &self, Extent<T, N> const &other) { return self == other; },
169  py::is_operator());
170  cls.def("__ne__",
171  [](Extent<T, N> const &self, Extent<T, N> const &other) { return self != other; },
172  py::is_operator());
173  cls.def("clone", [](Extent<T, N> const &self) { return Extent<T, N>{self}; });
174  }
175  );
176 }
177 
178 // Add functionality only found in N=2 Extents (and delgate to declareExtent for the rest)
179 template <typename T>
180 PyExtent<T, 2> declareExtent2(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
181  return wrappers.wrapType(
182  declareExtent<T, 2>(wrappers, std::string("2") + suffix),
183  [](auto & mod, auto & cls) {
184  /* Members types and enums */
185  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 2; });
186  /* Constructors */
187  cls.def(py::init<int, int>(), "x"_a, "y"_a);
188  cls.def(py::init<double, double>(), "x"_a, "y"_a);
189  /* Members */
190  auto getX = py::overload_cast<>(&Extent<T, 2>::getX, py::const_);
191  auto getY = py::overload_cast<>(&Extent<T, 2>::getY, py::const_);
192  cls.def("getX", getX);
193  cls.def("getY", getY);
194  cls.def("setX", &Extent<T, 2>::setX);
195  cls.def("setY", &Extent<T, 2>::setY);
196  cls.def_property("x", getX, &Extent<T, 2>::setX);
197  cls.def_property("y", getY, &Extent<T, 2>::setY);
198  }
199  );
200 }
201 
202 // Add functionality only found in N=3 Extents (and delgate to declareExtent for the rest)
203 template <typename T>
204 PyExtent<T, 3> declareExtent3(utils::python::WrapperCollection & wrappers, const std::string &suffix) {
205  return wrappers.wrapType(
206  declareExtent<T, 3>(wrappers, std::string("3") + suffix),
207  [](auto & mod, auto & cls) mutable {
208  /* Member types and enums */
209  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 3; });
210  /* Constructors */
211  cls.def(py::init<int, int, int>(), "x"_a, "y"_a, "z"_a);
212  cls.def(py::init<double, double, double>(), "x"_a, "y"_a, "z"_a);
213  /* Members */
214  auto getX = py::overload_cast<>(&Extent<T, 3>::getX, py::const_);
215  auto getY = py::overload_cast<>(&Extent<T, 3>::getY, py::const_);
216  auto getZ = py::overload_cast<>(&Extent<T, 3>::getZ, py::const_);
217  cls.def("getX", getX);
218  cls.def("getY", getY);
219  cls.def("getZ", getZ);
220  cls.def("setX", &Extent<T, 3>::setX);
221  cls.def("setY", &Extent<T, 3>::setY);
222  cls.def("setZ", &Extent<T, 3>::setZ);
223  cls.def_property("x", getX, &Extent<T, 3>::setX);
224  cls.def_property("y", getY, &Extent<T, 3>::setY);
225  cls.def_property("z", getZ, &Extent<T, 3>::setZ);
226  }
227  );
228 }
229 
230 // Declare mixed-type and type-overloaded operators for Extent with dimension
231 // N for both int and double. Because pybind11 tries operators (like any
232 // overload) `in order', int has to come before double in any overloaded
233 // operators that dispatch on a scalar, and hence they have to be defined here
234 // instead of declareExtent.
235 template <int N>
236 void declareExtentOperators(utils::python::WrapperCollection & wrapper,
237  PyExtent<int, N> &clsI, PyExtent<double, N> &clsD) {
238  wrapper.wrap(
239  [clsI, clsD](auto & mod) mutable {
240  // Python's integer division works differently than C++'s for negative numbers - Python
241  // uses floor (rounds towards more negative), while C++ truncates (rounds towards zero).
242  // Therefore one needs to be careful in the definition of division operators.
243  clsI.def("__floordiv__",
244  [](Extent<int, N> const &self, int other) -> Extent<int, N> {
245  return floor(self / static_cast<double>(other));
246  },
247  py::is_operator());
248 
249  clsI.def("__truediv__", [](Extent<int, N> const &self, double other) { return self / other; },
250  py::is_operator());
251  clsD.def("__truediv__", [](Extent<double, N> const &self, double other) { return self / other; },
252  py::is_operator());
253 
254  clsI.def("__ifloordiv__", [](Extent<int, N> &self, int other) -> Extent<int, N> & {
255  self = floor(self / static_cast<double>(other));
256  return self;
257  });
258 
259  clsI.def("__itruediv__", [](Extent<int, N> &self, double other) {
260  PyErr_SetString(PyExc_TypeError, "In-place true division not supported for Extent<int,N>.");
261  throw py::error_already_set();
262  });
263  clsD.def("__itruediv__", [](Extent<double, N> &self, double other) -> Extent<double, N> & {
264  self /= other;
265  return self;
266  });
267 
268  clsI.def("__iadd__", [](Extent<int, N> &self, Extent<int, N> const &other) -> Extent<int, N> & {
269  self += other;
270  return self;
271  });
272  clsD.def(
273  "__iadd__",
274  [](Extent<double, N> &self, Extent<double, N> const &other) -> Extent<double, N> & {
275  self += other;
276  return self;
277  }
278  );
279  clsD.def(
280  "__iadd__",
281  [](Extent<double, N> &self, Extent<int, N> const &other) -> Extent<double, N> & {
282  self += other;
283  return self;
284  }
285  );
286 
287  clsI.def("__isub__", [](Extent<int, N> &self, Extent<int, N> const &other) -> Extent<int, N> & {
288  self -= other;
289  return self;
290  });
291  clsD.def("__isub__", [](Extent<double, N> &self, Extent<double, N> const &other) -> Extent<double, N> & {
292  self -= other;
293  return self;
294  });
295  clsD.def("__isub__", [](Extent<double, N> &self, Extent<int, N> const &other) -> Extent<double, N> & {
296  self -= other;
297  return self;
298  });
299 
300  clsI.def("__imul__", [](Extent<int, N> &self, int other) -> Extent<int, N> & {
301  self *= other;
302  return self;
303  });
304  clsD.def("__imul__", [](Extent<double, N> &self, int other) -> Extent<double, N> & {
305  self *= other;
306  return self;
307  });
308  clsD.def("__imul__", [](Extent<double, N> &self, double other) -> Extent<double, N> & {
309  self *= other;
310  return self;
311  });
312 
313  // Operator-like free functions
314  mod.def("truncate", truncate<N>);
315  mod.def("floor", floor<N>);
316  mod.def("ceil", ceil<N>);
317  // And method versions, since Python doens't have ADL
318  clsD.def("truncate", truncate<N>);
319  clsD.def("floor", floor<N>);
320  clsD.def("ceil", ceil<N>);
321  }
322  );
323 }
324 
325 template <typename T, int N>
326 void declarePointBase(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
327  static std::string const name = "PointBase" + suffix;
328  declareCoordinateBase<Point<T, N>, T, N>(wrappers, "Point" + suffix);
329  wrappers.wrapType(
330  PyPointBase<T, N>(wrappers.module, name.c_str()),
331  [](auto & mod, auto & cls) {
332  // These are not the usual Python double-underscore operators - they do elementwise comparisons,
333  // returning a CoordinateExpr object with boolean x and y values. NumPy examples to the contrary
334  // notwithstanding, true Python comparison operators are expected to return scalar bools.
335  cls.def("eq", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.eq(rhs); });
336  cls.def("ne", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.ne(rhs); });
337  cls.def("lt", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.lt(rhs); });
338  cls.def("le", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.le(rhs); });
339  cls.def("gt", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.gt(rhs); });
340  cls.def("ge", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.ge(rhs); });
341  cls.def("eq", [](PointBase<T, N> const &self, T rhs) { return self.eq(rhs); });
342  cls.def("ne", [](PointBase<T, N> const &self, T rhs) { return self.ne(rhs); });
343  cls.def("lt", [](PointBase<T, N> const &self, T rhs) { return self.lt(rhs); });
344  cls.def("le", [](PointBase<T, N> const &self, T rhs) { return self.le(rhs); });
345  cls.def("gt", [](PointBase<T, N> const &self, T rhs) { return self.gt(rhs); });
346  cls.def("ge", [](PointBase<T, N> const &self, T rhs) { return self.ge(rhs); });
347  /* Members */
348  cls.def("asExtent", &PointBase<T, N>::asExtent);
349  cls.def("shift", &PointBase<T, N>::shift);
350  cls.def("scale", &PointBase<T, N>::scale);
351  cls.def("distanceSquared", &PointBase<T, N>::distanceSquared);
352  cls.def("toString", &PointBase<T, N>::toString);
353  }
354  );
355 }
356 
357 // Common functionality
358 template <typename T, int N>
359 PyPoint<T, N> declarePoint(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
360  static std::string const name = "Point" + suffix;
361  declarePointBase<T, N>(wrappers, suffix);
362  return wrappers.wrapType(
363  PyPoint<T, N>(wrappers.module, name.c_str()),
364  [](auto & mod, auto & cls) {
365  /* Constructors */
366  cls.def(py::init<T>(), "value"_a = static_cast<T>(0));
367  // Note that we can't use T here because both types are needed
368  cls.def(py::init<Point<double, N> const &>());
369  cls.def(py::init<Point<int, N> const &>());
370  cls.def(py::init<Extent<T, N> const &>());
371  cls.def(py::init<typename Point<T, N>::EigenVector>());
372  /* Operators */
373  cls.def("__add__", [](Point<T, N> const &self, Extent<double, N> &other) { return self + other; },
374  py::is_operator());
375  cls.def("__add__", [](Point<T, N> const &self, Extent<int, N> &other) { return self + other; },
376  py::is_operator());
377  cls.def("__sub__", [](Point<T, N> const &self, Point<T, N> &other) { return self - other; },
378  py::is_operator());
379  cls.def("__sub__", [](Point<T, N> const &self, Extent<T, N> &other) { return self - other; },
380  py::is_operator());
381  cls.def("__sub__", [](Point<T, N> const &self, Point<double, N> &other) { return self - other; },
382  py::is_operator());
383  cls.def("__sub__", [](Point<T, N> const &self, Point<int, N> &other) { return self - other; },
384  py::is_operator());
385  cls.def("__sub__", [](Point<T, N> const &self, Extent<double, N> &other) { return self - other; },
386  py::is_operator());
387  cls.def("__sub__", [](Point<T, N> const &self, Extent<int, N> &other) { return self - other; },
388  py::is_operator());
389  cls.def("__eq__", [](Point<T, N> const &self, Point<T, N> const &other) { return self == other; },
390  py::is_operator());
391  cls.def("__ne__", [](Point<T, N> const &self, Point<T, N> const &other) { return self != other; },
392  py::is_operator());
393  /* Members */
394  cls.def("clone", [](Point<T, N> const &self) { return Point<T, N>{self}; });
395  }
396  );
397 }
398 
399 // Add functionality only found in N=2 Points
400 template <typename T>
401 PyPoint<T, 2> declarePoint2(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
402  return wrappers.wrapType(
403  declarePoint<T, 2>(wrappers, std::string("2") + suffix),
404  [](auto & mod, auto & cls) {
405  /* Member types and enums */
406  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 2; });
407  /* Constructors */
408  cls.def(py::init<int, int>(), "x"_a, "y"_a);
409  cls.def(py::init<double, double>(), "x"_a, "y"_a);
410  /* Members */
411  auto getX = py::overload_cast<>(&Point<T, 2>::getX, py::const_);
412  auto getY = py::overload_cast<>(&Point<T, 2>::getY, py::const_);
413  cls.def("getX", getX);
414  cls.def("getY", getY);
415  cls.def("setX", &Point<T, 2>::setX);
416  cls.def("setY", &Point<T, 2>::setY);
417  cls.def_property("x", getX, &Point<T, 2>::setX);
418  cls.def_property("y", getY, &Point<T, 2>::setY);
419  }
420  );
421 }
422 
423 // Add functionality only found in N=3 Points
424 template <typename T>
425 PyPoint<T, 3> declarePoint3(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
426  return wrappers.wrapType(
427  declarePoint<T, 3>(wrappers, std::string("3") + suffix),
428  [](auto & mod, auto & cls) {
429  /* Member types and enums */
430  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 3; });
431  /* Constructors */
432  cls.def(py::init<int, int, int>(), "x"_a, "y"_a, "z"_a);
433  cls.def(py::init<double, double, double>(), "x"_a, "y"_a, "z"_a);
434  /* Members */
435  auto getX = py::overload_cast<>(&Point<T, 3>::getX, py::const_);
436  auto getY = py::overload_cast<>(&Point<T, 3>::getY, py::const_);
437  auto getZ = py::overload_cast<>(&Point<T, 3>::getZ, py::const_);
438  cls.def("getX", getX);
439  cls.def("getY", getY);
440  cls.def("getZ", getZ);
441  cls.def("setX", &Point<T, 3>::setX);
442  cls.def("setY", &Point<T, 3>::setY);
443  cls.def("setZ", &Point<T, 3>::setZ);
444  cls.def_property("x", getX, &Point<T, 3>::setX);
445  cls.def_property("y", getY, &Point<T, 3>::setY);
446  cls.def_property("z", getZ, &Point<T, 3>::setZ);
447  }
448  );
449 }
450 
451 // Declare mixed-type and type-overloaded operators for Point with dimension
452 // N for both int and double. Because pybind11 tries operators (like any
453 // overload) `in order', int has to come before double in any overloaded
454 // operators that dispatch on a scalar, and hence they have to be defined here
455 // instead of declareExtent.
456 template <int N>
457 void declarePointOperators(utils::python::WrapperCollection & wrappers,
458  PyPoint<int, N> &clsI, PyPoint<double, N> &clsD) {
459  wrappers.wrap(
460  [clsI, clsD](auto & mod) mutable {
461  clsI.def("__iadd__", [](Point<int, N> &self, Extent<int, N> const &other) {
462  self += other;
463  return &self;
464  });
465  clsD.def("__iadd__", [](Point<double, N> &self, Extent<int, N> const &other) {
466  self += other;
467  return &self;
468  });
469  clsD.def("__iadd__", [](Point<double, N> &self, Extent<double, N> const &other) {
470  self += other;
471  return &self;
472  });
473  clsI.def("__isub__", [](Point<int, N> &self, Extent<int, N> const &other) {
474  self -= other;
475  return &self;
476  });
477  clsD.def("__isub__", [](Point<double, N> &self, Extent<int, N> const &other) {
478  self -= other;
479  return &self;
480  });
481  clsD.def("__isub__", [](Point<double, N> &self, Extent<double, N> const &other) {
482  self -= other;
483  return &self;
484  });
485  }
486  );
487 }
488 
489 } // anonymous
490 
492  // Only the interface-level classes are defined here, and these functions
493  // call others to define their base classes, since those are never shared.
494 
495  declareCoordinateExpr<2>(wrappers, "2");
496  declareCoordinateExpr<3>(wrappers, "3");
497 
498  auto clsExtent2I = declareExtent2<int>(wrappers, "I");
499  auto clsExtent2D = declareExtent2<double>(wrappers, "D");
500 
501  auto clsExtent3I = declareExtent3<int>(wrappers, "I");
502  auto clsExtent3D = declareExtent3<double>(wrappers, "D");
503 
504  auto clsPoint2I = declarePoint2<int>(wrappers, "I");
505  auto clsPoint2D = declarePoint2<double>(wrappers, "D");
506 
507  auto clsPoint3I = declarePoint3<int>(wrappers, "I");
508  auto clsPoint3D = declarePoint3<double>(wrappers, "D");
509 
510  declareExtentOperators(wrappers, clsExtent2I, clsExtent2D);
511  declareExtentOperators(wrappers, clsExtent3I, clsExtent3D);
512  declarePointOperators(wrappers, clsPoint2I, clsPoint2D);
513  declarePointOperators(wrappers, clsPoint3I, clsPoint3D);
514 
515 }
516 
517 } // namespace geom
518 } // namespace lsst
table::Key< std::string > name
Definition: Amplifier.cc:116
ItemVariant const * other
Definition: Schema.cc:56
A helper class for subdividing pybind11 module across multiple translation units (i....
Definition: python.h:242
def scale(algorithm, min, max=None, frame=None)
Definition: ds9.py:108
Extent< int, N > floor(Extent< double, N > const &input) noexcept
Return the component-wise floor (round towards more negative).
Definition: Extent.cc:109
void wrapCoordinates(utils::python::WrapperCollection &wrappers)
std::size_t cppIndex(std::ptrdiff_t size, std::ptrdiff_t i)
Compute a C++ index from a Python index (negative values count from the end) and range-check.
Definition: python.h:124
A base class for image defects.