LSSTApplications  18.1.0
LSSTDataManagementBasePackage
_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  cls.def("getX", [](Extent<T, 2> const &self) { return self[0]; });
191  cls.def("getY", [](Extent<T, 2> const &self) { return self[1]; });
192  cls.def("setX", [](Extent<T, 2> &self, T other) { self[0] = other; });
193  cls.def("setY", [](Extent<T, 2> &self, T other) { self[1] = other; });
194  }
195  );
196 }
197 
198 // Add functionality only found in N=3 Extents (and delgate to declareExtent for the rest)
199 template <typename T>
200 PyExtent<T, 3> declareExtent3(utils::python::WrapperCollection & wrappers, const std::string &suffix) {
201  return wrappers.wrapType(
202  declareExtent<T, 3>(wrappers, std::string("3") + suffix),
203  [](auto & mod, auto & cls) mutable {
204  /* Member types and enums */
205  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 3; });
206  /* Constructors */
207  cls.def(py::init<int, int, int>(), "x"_a, "y"_a, "z"_a);
208  cls.def(py::init<double, double, double>(), "x"_a, "y"_a, "z"_a);
209  /* Members */
210  cls.def("getX", [](Extent<T, 3> const &self) { return self[0]; });
211  cls.def("getY", [](Extent<T, 3> const &self) { return self[1]; });
212  cls.def("getZ", [](Extent<T, 3> const &self) { return self[2]; });
213  cls.def("setX", [](Extent<T, 3> &self, T other) { self[0] = other; });
214  cls.def("setY", [](Extent<T, 3> &self, T other) { self[1] = other; });
215  cls.def("setZ", [](Extent<T, 3> &self, T other) { self[2] = other; });
216  }
217  );
218 }
219 
220 // Declare mixed-type and type-overloaded operators for Extent with dimension
221 // N for both int and double. Because pybind11 tries operators (like any
222 // overload) `in order', int has to come before double in any overloaded
223 // operators that dispatch on a scalar, and hence they have to be defined here
224 // instead of declareExtent.
225 template <int N>
226 void declareExtentOperators(utils::python::WrapperCollection & wrapper,
227  PyExtent<int, N> &clsI, PyExtent<double, N> &clsD) {
228  wrapper.wrap(
229  [clsI, clsD](auto & mod) mutable {
230  // Python's integer division works differently than C++'s for negative numbers - Python
231  // uses floor (rounds towards more negative), while C++ truncates (rounds towards zero).
232  // Therefore one needs to be careful in the definition of division operators.
233  clsI.def("__floordiv__",
234  [](Extent<int, N> const &self, int other) -> Extent<int, N> {
235  return floor(self / static_cast<double>(other));
236  },
237  py::is_operator());
238 
239  clsI.def("__truediv__", [](Extent<int, N> const &self, double other) { return self / other; },
240  py::is_operator());
241  clsD.def("__truediv__", [](Extent<double, N> const &self, double other) { return self / other; },
242  py::is_operator());
243 
244  clsI.def("__ifloordiv__", [](Extent<int, N> &self, int other) -> Extent<int, N> & {
245  self = floor(self / static_cast<double>(other));
246  return self;
247  });
248 
249  clsI.def("__itruediv__", [](Extent<int, N> &self, double other) {
250  PyErr_SetString(PyExc_TypeError, "In-place true division not supported for Extent<int,N>.");
251  throw py::error_already_set();
252  });
253  clsD.def("__itruediv__", [](Extent<double, N> &self, double other) -> Extent<double, N> & {
254  self /= other;
255  return self;
256  });
257 
258  clsI.def("__iadd__", [](Extent<int, N> &self, Extent<int, N> const &other) -> Extent<int, N> & {
259  self += other;
260  return self;
261  });
262  clsD.def(
263  "__iadd__",
264  [](Extent<double, N> &self, Extent<double, N> const &other) -> Extent<double, N> & {
265  self += other;
266  return self;
267  }
268  );
269  clsD.def(
270  "__iadd__",
271  [](Extent<double, N> &self, Extent<int, N> const &other) -> Extent<double, N> & {
272  self += other;
273  return self;
274  }
275  );
276 
277  clsI.def("__isub__", [](Extent<int, N> &self, Extent<int, N> const &other) -> Extent<int, N> & {
278  self -= other;
279  return self;
280  });
281  clsD.def("__isub__", [](Extent<double, N> &self, Extent<double, N> const &other) -> Extent<double, N> & {
282  self -= other;
283  return self;
284  });
285  clsD.def("__isub__", [](Extent<double, N> &self, Extent<int, N> const &other) -> Extent<double, N> & {
286  self -= other;
287  return self;
288  });
289 
290  clsI.def("__imul__", [](Extent<int, N> &self, int other) -> Extent<int, N> & {
291  self *= other;
292  return self;
293  });
294  clsD.def("__imul__", [](Extent<double, N> &self, int other) -> Extent<double, N> & {
295  self *= other;
296  return self;
297  });
298  clsD.def("__imul__", [](Extent<double, N> &self, double other) -> Extent<double, N> & {
299  self *= other;
300  return self;
301  });
302 
303  // Operator-like free functions
304  mod.def("truncate", truncate<N>);
305  mod.def("floor", floor<N>);
306  mod.def("ceil", ceil<N>);
307  // And method versions, since Python doens't have ADL
308  clsD.def("truncate", truncate<N>);
309  clsD.def("floor", floor<N>);
310  clsD.def("ceil", ceil<N>);
311  }
312  );
313 }
314 
315 template <typename T, int N>
316 void declarePointBase(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
317  static std::string const name = "PointBase" + suffix;
318  declareCoordinateBase<Point<T, N>, T, N>(wrappers, "Point" + suffix);
319  wrappers.wrapType(
320  PyPointBase<T, N>(wrappers.module, name.c_str()),
321  [](auto & mod, auto & cls) {
322  // These are not the usual Python double-underscore operators - they do elementwise comparisons,
323  // returning a CoordinateExpr object with boolean x and y values. NumPy examples to the contrary
324  // notwithstanding, true Python comparison operators are expected to return scalar bools.
325  cls.def("eq", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.eq(rhs); });
326  cls.def("ne", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.ne(rhs); });
327  cls.def("lt", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.lt(rhs); });
328  cls.def("le", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.le(rhs); });
329  cls.def("gt", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.gt(rhs); });
330  cls.def("ge", [](PointBase<T, N> const &self, Point<T, N> const &rhs) { return self.ge(rhs); });
331  cls.def("eq", [](PointBase<T, N> const &self, T rhs) { return self.eq(rhs); });
332  cls.def("ne", [](PointBase<T, N> const &self, T rhs) { return self.ne(rhs); });
333  cls.def("lt", [](PointBase<T, N> const &self, T rhs) { return self.lt(rhs); });
334  cls.def("le", [](PointBase<T, N> const &self, T rhs) { return self.le(rhs); });
335  cls.def("gt", [](PointBase<T, N> const &self, T rhs) { return self.gt(rhs); });
336  cls.def("ge", [](PointBase<T, N> const &self, T rhs) { return self.ge(rhs); });
337  /* Members */
338  cls.def("asExtent", &PointBase<T, N>::asExtent);
339  cls.def("shift", &PointBase<T, N>::shift);
340  cls.def("scale", &PointBase<T, N>::scale);
341  cls.def("distanceSquared", &PointBase<T, N>::distanceSquared);
342  cls.def("toString", &PointBase<T, N>::toString);
343  }
344  );
345 }
346 
347 // Common functionality
348 template <typename T, int N>
349 PyPoint<T, N> declarePoint(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
350  static std::string const name = "Point" + suffix;
351  declarePointBase<T, N>(wrappers, suffix);
352  return wrappers.wrapType(
353  PyPoint<T, N>(wrappers.module, name.c_str()),
354  [](auto & mod, auto & cls) {
355  /* Constructors */
356  cls.def(py::init<T>(), "value"_a = static_cast<T>(0));
357  // Note that we can't use T here because both types are needed
358  cls.def(py::init<Point<double, N> const &>());
359  cls.def(py::init<Point<int, N> const &>());
360  cls.def(py::init<Extent<T, N> const &>());
361  cls.def(py::init<typename Point<T, N>::EigenVector>());
362  /* Operators */
363  cls.def("__add__", [](Point<T, N> const &self, Extent<double, N> &other) { return self + other; },
364  py::is_operator());
365  cls.def("__add__", [](Point<T, N> const &self, Extent<int, N> &other) { return self + other; },
366  py::is_operator());
367  cls.def("__sub__", [](Point<T, N> const &self, Point<T, N> &other) { return self - other; },
368  py::is_operator());
369  cls.def("__sub__", [](Point<T, N> const &self, Extent<T, N> &other) { return self - other; },
370  py::is_operator());
371  cls.def("__sub__", [](Point<T, N> const &self, Point<double, N> &other) { return self - other; },
372  py::is_operator());
373  cls.def("__sub__", [](Point<T, N> const &self, Point<int, N> &other) { return self - other; },
374  py::is_operator());
375  cls.def("__sub__", [](Point<T, N> const &self, Extent<double, N> &other) { return self - other; },
376  py::is_operator());
377  cls.def("__sub__", [](Point<T, N> const &self, Extent<int, N> &other) { return self - other; },
378  py::is_operator());
379  cls.def("__eq__", [](Point<T, N> const &self, Point<T, N> const &other) { return self == other; },
380  py::is_operator());
381  cls.def("__ne__", [](Point<T, N> const &self, Point<T, N> const &other) { return self != other; },
382  py::is_operator());
383  /* Members */
384  cls.def("clone", [](Point<T, N> const &self) { return Point<T, N>{self}; });
385  }
386  );
387 }
388 
389 // Add functionality only found in N=2 Points
390 template <typename T>
391 PyPoint<T, 2> declarePoint2(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
392  return wrappers.wrapType(
393  declarePoint<T, 2>(wrappers, std::string("2") + suffix),
394  [](auto & mod, auto & cls) {
395  /* Member types and enums */
396  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 2; });
397  /* Constructors */
398  cls.def(py::init<int, int>(), "x"_a, "y"_a);
399  cls.def(py::init<double, double>(), "x"_a, "y"_a);
400  /* Members */
401  cls.def("getX", [](Point<T, 2> const &self) { return self[0]; });
402  cls.def("getY", [](Point<T, 2> const &self) { return self[1]; });
403  cls.def("setX", [](Point<T, 2> &self, T other) { self[0] = other; });
404  cls.def("setY", [](Point<T, 2> &self, T other) { self[1] = other; });
405  }
406  );
407 }
408 
409 // Add functionality only found in N=3 Points
410 template <typename T>
411 PyPoint<T, 3> declarePoint3(utils::python::WrapperCollection & wrappers, std::string const &suffix) {
412  return wrappers.wrapType(
413  declarePoint<T, 3>(wrappers, std::string("3") + suffix),
414  [](auto & mod, auto & cls) {
415  /* Member types and enums */
416  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 3; });
417  /* Constructors */
418  cls.def(py::init<int, int, int>(), "x"_a, "y"_a, "z"_a);
419  cls.def(py::init<double, double, double>(), "x"_a, "y"_a, "z"_a);
420  /* Members */
421  cls.def("getX", [](Point<T, 3> const &self) { return self[0]; });
422  cls.def("getY", [](Point<T, 3> const &self) { return self[1]; });
423  cls.def("getZ", [](Point<T, 3> const &self) { return self[2]; });
424  cls.def("setX", [](Point<T, 3> &self, T other) { self[0] = other; });
425  cls.def("setY", [](Point<T, 3> &self, T other) { self[1] = other; });
426  cls.def("setZ", [](Point<T, 3> &self, T other) { self[2] = other; });
427  }
428  );
429 }
430 
431 // Declare mixed-type and type-overloaded operators for Point with dimension
432 // N for both int and double. Because pybind11 tries operators (like any
433 // overload) `in order', int has to come before double in any overloaded
434 // operators that dispatch on a scalar, and hence they have to be defined here
435 // instead of declareExtent.
436 template <int N>
437 void declarePointOperators(utils::python::WrapperCollection & wrappers,
438  PyPoint<int, N> &clsI, PyPoint<double, N> &clsD) {
439  wrappers.wrap(
440  [clsI, clsD](auto & mod) mutable {
441  clsI.def("__iadd__", [](Point<int, N> &self, Extent<int, N> const &other) {
442  self += other;
443  return &self;
444  });
445  clsD.def("__iadd__", [](Point<double, N> &self, Extent<int, N> const &other) {
446  self += other;
447  return &self;
448  });
449  clsD.def("__iadd__", [](Point<double, N> &self, Extent<double, N> const &other) {
450  self += other;
451  return &self;
452  });
453  clsI.def("__isub__", [](Point<int, N> &self, Extent<int, N> const &other) {
454  self -= other;
455  return &self;
456  });
457  clsD.def("__isub__", [](Point<double, N> &self, Extent<int, N> const &other) {
458  self -= other;
459  return &self;
460  });
461  clsD.def("__isub__", [](Point<double, N> &self, Extent<double, N> const &other) {
462  self -= other;
463  return &self;
464  });
465  }
466  );
467 }
468 
469 } // anonymous
470 
472  // Only the interface-level classes are defined here, and these functions
473  // call others to define their base classes, since those are never shared.
474 
475  declareCoordinateExpr<2>(wrappers, "2");
476  declareCoordinateExpr<3>(wrappers, "3");
477 
478  auto clsExtent2I = declareExtent2<int>(wrappers, "I");
479  auto clsExtent2D = declareExtent2<double>(wrappers, "D");
480 
481  auto clsExtent3I = declareExtent3<int>(wrappers, "I");
482  auto clsExtent3D = declareExtent3<double>(wrappers, "D");
483 
484  auto clsPoint2I = declarePoint2<int>(wrappers, "I");
485  auto clsPoint2D = declarePoint2<double>(wrappers, "D");
486 
487  auto clsPoint3I = declarePoint3<int>(wrappers, "I");
488  auto clsPoint3D = declarePoint3<double>(wrappers, "D");
489 
490  declareExtentOperators(wrappers, clsExtent2I, clsExtent2D);
491  declareExtentOperators(wrappers, clsExtent3I, clsExtent3D);
492  declarePointOperators(wrappers, clsPoint2I, clsPoint2D);
493  declarePointOperators(wrappers, clsPoint3I, clsPoint3D);
494 
495 }
496 
497 } // namespace geom
498 } // namespace lsst
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
def init()
Definition: tests.py:75
def scale(algorithm, min, max=None, frame=None)
Definition: ds9.py:109
void wrapCoordinates(utils::python::WrapperCollection &wrappers)
STL class.
A base class for image defects.
T c_str(T... args)
Extent< int, N > floor(Extent< double, N > const &input) noexcept
Return the component-wise floor (round towards more negative).
Definition: Extent.cc:109
ItemVariant const * other
Definition: Schema.cc:56
A helper class for subdividing pybind11 module across multiple translation units (i.e.
Definition: python.h:242