LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
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 
491 void wrapCoordinates(utils::python::WrapperCollection & wrappers) {
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
def scale(algorithm, min, max=None, frame=None)
Definition: ds9.py:108
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
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)
A base class for image defects.