LSSTApplications  16.0-1-gce273f5+15,16.0-1-gf77f410+8,16.0-10-g230e10e+8,16.0-10-g5a5abec+4,16.0-10-gc1446dd+8,16.0-11-g5cf5345,16.0-12-g1dc09ba+2,16.0-12-g726f8f3+6,16.0-13-g4c33ca5+8,16.0-13-g5f6c0b1+8,16.0-13-gd9b1b71+8,16.0-14-g71e547a+4,16.0-17-g0bdc215,16.0-17-g6a7bfb3b+8,16.0-18-g95848a16+2,16.0-2-g0febb12+12,16.0-2-g839ba83+46,16.0-2-g9d5294e+35,16.0-3-g404ea43+7,16.0-3-gbc759ec+6,16.0-3-gcfd6c53+33,16.0-4-g03cf288+24,16.0-4-g13a27c5+10,16.0-4-g2cc461c+3,16.0-4-g5f3a788+11,16.0-4-g8a0f11a+30,16.0-4-ga3eb747+1,16.0-43-gaa65a4573+4,16.0-5-g1991253+8,16.0-5-g865efd9+8,16.0-5-gb3f8a4b+40,16.0-5-gd0f1235+4,16.0-6-g316b399+8,16.0-6-gf0acd13+27,16.0-6-gf9cb114+9,16.0-7-ga8e1655+4,16.0-8-g23bbf3f+1,16.0-8-g4dec96c+21,16.0-8-gfd407c0,w.2018.39
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(py::module &mod, std::string const &suffix) {
59  static std::string const name = "CoordinateBase" + suffix;
60  PyCoordinateBase<Derived, T, N> cls(mod, name.c_str());
61 
62  /* Operators */
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 template <int N>
73 void declareCoordinateExpr(py::module &mod, std::string const &suffix) {
74  static std::string const name = "CoordinateExpr" + suffix;
75  declareCoordinateBase<CoordinateExpr<N>, bool, N>(mod, name);
76 
77  PyCoordinateExpr<N> clsCoordinateExpr(mod, name.c_str());
78 
79  /* Constructors */
80  clsCoordinateExpr.def(py::init<bool>(), "val"_a = false);
81 
82  /* Operators */
83  clsCoordinateExpr.def("and_", &CoordinateExpr<N>::and_);
84  clsCoordinateExpr.def("or_", &CoordinateExpr<N>::or_);
85  clsCoordinateExpr.def("not_", &CoordinateExpr<N>::not_);
86 
87  mod.def("all", all<N>);
88  mod.def("any", any<N>);
89 }
90 
91 template <typename T, int N>
92 void declareExtentBase(py::module &mod, std::string const &suffix) {
93  declareCoordinateBase<Extent<T, N>, T, N>(mod, "Extent" + suffix);
94  static std::string const name = "ExtentBase" + suffix;
95 
96  PyExtentBase<T, N> cls(mod, name.c_str());
97 
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  /* Members */
115  cls.def("asPoint", &ExtentBase<T, N>::asPoint);
116  cls.def("computeNorm", &ExtentBase<T, N>::computeNorm);
117  cls.def("computeSquaredNorm", &ExtentBase<T, N>::computeSquaredNorm);
118 }
119 
120 // Common functionality for all Extents, including declaring base classes.
121 template <typename T, int N>
122 PyExtent<T, N> declareExtent(py::module &mod, std::string const &suffix) {
123  declareExtentBase<T, N>(mod, suffix);
124  static std::string const name = "Extent" + suffix;
125 
126  PyExtent<T, N> cls(mod, name.c_str());
127 
128  /* Constructors */
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 
136  /* Operators */
137  cls.def("__neg__", [](Extent<T, N> const &self) { return -self; });
138  cls.def("__pos__", [](Extent<T, N> const &self) { return self; });
139  cls.def("__mul__", [](Extent<T, N> const &self, int other) { return self * other; }, py::is_operator());
140  cls.def("__mul__", [](Extent<T, N> const &self, double other) { return self * other; },
141  py::is_operator());
142  cls.def("__rmul__", [](Extent<T, N> const &self, int other) { return self * other; }, 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__", [](Extent<T, N> const &self, Extent<int, N> const &other) { return self + other; },
146  py::is_operator());
147  cls.def("__add__", [](Extent<T, N> const &self, Extent<double, N> const &other) { return self + other; },
148  py::is_operator());
149  cls.def("__add__",
150  [](Extent<T, N> const &self, Point<int, N> const &other) { return self + Point<T, N>(other); },
151  py::is_operator());
152  cls.def("__add__", [](Extent<T, N> const &self, Point<double, N> const &other) { return self + other; },
153  py::is_operator());
154  cls.def("__sub__",
155  [](Extent<T, N> const &self, Extent<int, N> const &other) { return self - Extent<T, N>(other); },
156  py::is_operator());
157  cls.def("__sub__", [](Extent<T, N> const &self, Extent<double, N> const &other) { return self - other; },
158  py::is_operator());
159  cls.def("__eq__", [](Extent<T, N> const &self, Extent<T, N> const &other) { return self == other; },
160  py::is_operator());
161  cls.def("__ne__", [](Extent<T, N> const &self, Extent<T, N> const &other) { return self != other; },
162  py::is_operator());
163 
164  /* Members */
165  cls.def("clone", [](Extent<T, N> const &self) { return Extent<T, N>{self}; });
166 
167  return cls;
168 }
169 
170 // Add functionality only found in N=2 Extents (and delgate to declareExtent for the rest)
171 template <typename T>
172 PyExtent<T, 2> declareExtent2(py::module &mod, std::string const &suffix) {
173  auto cls = declareExtent<T, 2>(mod, std::string("2") + suffix);
174 
175  /* Members types and enums */
176  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 2; });
177 
178  /* Constructors */
179  cls.def(py::init<int, int>(), "x"_a, "y"_a);
180  cls.def(py::init<double, double>(), "x"_a, "y"_a);
181 
182  /* Members */
183  cls.def("getX", [](Extent<T, 2> const &self) { return self[0]; });
184  cls.def("getY", [](Extent<T, 2> const &self) { return self[1]; });
185  cls.def("setX", [](Extent<T, 2> &self, T other) { self[0] = other; });
186  cls.def("setY", [](Extent<T, 2> &self, T other) { self[1] = other; });
187 
188  return cls;
189 }
190 
191 // Add functionality only found in N=3 Extents (and delgate to declareExtent for the rest)
192 template <typename T>
193 PyExtent<T, 3> declareExtent3(py::module &mod, const std::string &suffix) {
194  auto cls = declareExtent<T, 3>(mod, std::string("3") + suffix);
195 
196  /* Member types and enums */
197  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 3; });
198 
199  /* Constructors */
200  cls.def(py::init<int, int, int>(), "x"_a, "y"_a, "z"_a);
201  cls.def(py::init<double, double, double>(), "x"_a, "y"_a, "z"_a);
202 
203  /* Members */
204  cls.def("getX", [](Extent<T, 3> const &self) { return self[0]; });
205  cls.def("getY", [](Extent<T, 3> const &self) { return self[1]; });
206  cls.def("getZ", [](Extent<T, 3> const &self) { return self[2]; });
207  cls.def("setX", [](Extent<T, 3> &self, T other) { self[0] = other; });
208  cls.def("setY", [](Extent<T, 3> &self, T other) { self[1] = other; });
209  cls.def("setZ", [](Extent<T, 3> &self, T other) { self[2] = other; });
210 
211  return cls;
212 }
213 
214 // Declare mixed-type and type-overloaded operators for Extent with dimension
215 // N for both int and double. Because pybind11 tries operators (like any
216 // overload) `in order', int has to come before double in any overloaded
217 // operators that dispatch on a scalar, and hence they have to be defined here
218 // instead of declareExtent.
219 template <int N>
220 void declareExtentOperators(py::module &mod, PyExtent<int, N> &clsI, PyExtent<double, N> &clsD) {
221  // Python's integer division works differently than C++'s for negative numbers - Python
222  // uses floor (rounds towards more negative), while C++ truncates (rounds towards zero).
223  // Therefore one needs to be careful in the definition of division operators.
224  clsI.def("__floordiv__",
225  [](Extent<int, N> const &self, int other) -> Extent<int, N> {
226  return floor(self / static_cast<double>(other));
227  },
228  py::is_operator());
229 
230  clsI.def("__truediv__", [](Extent<int, N> const &self, double other) { return self / other; },
231  py::is_operator());
232  clsD.def("__truediv__", [](Extent<double, N> const &self, double other) { return self / other; },
233  py::is_operator());
234 
235  clsI.def("__ifloordiv__", [](Extent<int, N> &self, int other) -> Extent<int, N> & {
236  self = floor(self / static_cast<double>(other));
237  return self;
238  });
239 
240  clsI.def("__itruediv__", [](Extent<int, N> &self, double other) {
241  PyErr_SetString(PyExc_TypeError, "In-place true division not supported for Extent<int,N>.");
242  throw py::error_already_set();
243  });
244  clsD.def("__itruediv__", [](Extent<double, N> &self, double other) -> Extent<double, N> & {
245  self /= other;
246  return self;
247  });
248 
249  clsI.def("__iadd__", [](Extent<int, N> &self, Extent<int, N> const &other) -> Extent<int, N> & {
250  self += other;
251  return self;
252  });
253  clsD.def("__iadd__", [](Extent<double, N> &self, Extent<double, N> const &other) -> Extent<double, N> & {
254  self += other;
255  return self;
256  });
257  clsD.def("__iadd__", [](Extent<double, N> &self, Extent<int, N> const &other) -> Extent<double, N> & {
258  self += other;
259  return self;
260  });
261 
262  clsI.def("__isub__", [](Extent<int, N> &self, Extent<int, N> const &other) -> Extent<int, N> & {
263  self -= other;
264  return self;
265  });
266  clsD.def("__isub__", [](Extent<double, N> &self, Extent<double, N> const &other) -> Extent<double, N> & {
267  self -= other;
268  return self;
269  });
270  clsD.def("__isub__", [](Extent<double, N> &self, Extent<int, N> const &other) -> Extent<double, N> & {
271  self -= other;
272  return self;
273  });
274 
275  clsI.def("__imul__", [](Extent<int, N> &self, int other) -> Extent<int, N> & {
276  self *= other;
277  return self;
278  });
279  clsD.def("__imul__", [](Extent<double, N> &self, int other) -> Extent<double, N> & {
280  self *= other;
281  return self;
282  });
283  clsD.def("__imul__", [](Extent<double, N> &self, double other) -> Extent<double, N> & {
284  self *= other;
285  return self;
286  });
287 
288  // Operator-like free functions
289  mod.def("truncate", truncate<N>);
290  mod.def("floor", floor<N>);
291  mod.def("ceil", ceil<N>);
292  // And method versions, since Python doens't have ADL
293  clsD.def("truncate", truncate<N>);
294  clsD.def("floor", floor<N>);
295  clsD.def("ceil", ceil<N>);
296 }
297 
298 template <typename T, int N>
299 void declarePointBase(py::module &mod, std::string const &suffix) {
300  declareCoordinateBase<Point<T, N>, T, N>(mod, "Point" + suffix);
301  static std::string const name = "PointBase" + suffix;
302  PyPointBase<T, N> cls(mod, name.c_str());
303 
304  // These are not the usual Python double-underscore operators - they do elementwise comparisons,
305  // returning a CoordinateExpr object with boolean x and y values. NumPy examples to the contrary
306  // notwithstanding, true Python comparison operators are expected to return scalar bools.
307  cls.def("eq", [](PointBase<T, N> const &self, Point<T, N> const &other) { return self.eq(other); });
308  cls.def("ne", [](PointBase<T, N> const &self, Point<T, N> const &other) { return self.ne(other); });
309  cls.def("lt", [](PointBase<T, N> const &self, Point<T, N> const &other) { return self.lt(other); });
310  cls.def("le", [](PointBase<T, N> const &self, Point<T, N> const &other) { return self.le(other); });
311  cls.def("gt", [](PointBase<T, N> const &self, Point<T, N> const &other) { return self.gt(other); });
312  cls.def("ge", [](PointBase<T, N> const &self, Point<T, N> const &other) { return self.ge(other); });
313  cls.def("eq", [](PointBase<T, N> const &self, T other) { return self.eq(other); });
314  cls.def("ne", [](PointBase<T, N> const &self, T other) { return self.ne(other); });
315  cls.def("lt", [](PointBase<T, N> const &self, T other) { return self.lt(other); });
316  cls.def("le", [](PointBase<T, N> const &self, T other) { return self.le(other); });
317  cls.def("gt", [](PointBase<T, N> const &self, T other) { return self.gt(other); });
318  cls.def("ge", [](PointBase<T, N> const &self, T other) { return self.ge(other); });
319 
320  /* Members */
321  cls.def("asExtent", &PointBase<T, N>::asExtent);
322  cls.def("shift", &PointBase<T, N>::shift);
323  cls.def("scale", &PointBase<T, N>::scale);
324  cls.def("distanceSquared", &PointBase<T, N>::distanceSquared);
325  cls.def("toString", &PointBase<T, N>::toString);
326 }
327 
328 // Common functionality
329 template <typename T, int N>
330 PyPoint<T, N> declarePoint(py::module &mod, std::string const &suffix) {
331  declarePointBase<T, N>(mod, suffix);
332  static std::string const name = "Point" + suffix;
333  PyPoint<T, N> cls(mod, name.c_str());
334 
335  /* Constructors */
336  cls.def(py::init<T>(), "value"_a = static_cast<T>(0));
337  // Note that we can't use T here because both types are needed
338  cls.def(py::init<Point<double, N> const &>());
339  cls.def(py::init<Point<int, N> const &>());
340  cls.def(py::init<Extent<T, N> const &>());
341  cls.def(py::init<typename Point<T, N>::EigenVector>());
342 
343  /* Operators */
344  cls.def("__add__", [](Point<T, N> const &self, Extent<double, N> &other) { return self + other; },
345  py::is_operator());
346  cls.def("__add__", [](Point<T, N> const &self, Extent<int, N> &other) { return self + other; },
347  py::is_operator());
348  cls.def("__sub__", [](Point<T, N> const &self, Point<T, N> &other) { return self - other; },
349  py::is_operator());
350  cls.def("__sub__", [](Point<T, N> const &self, Extent<T, N> &other) { return self - other; },
351  py::is_operator());
352  cls.def("__sub__", [](Point<T, N> const &self, Point<double, N> &other) { return self - other; },
353  py::is_operator());
354  cls.def("__sub__", [](Point<T, N> const &self, Point<int, N> &other) { return self - other; },
355  py::is_operator());
356  cls.def("__sub__", [](Point<T, N> const &self, Extent<double, N> &other) { return self - other; },
357  py::is_operator());
358  cls.def("__sub__", [](Point<T, N> const &self, Extent<int, N> &other) { return self - other; },
359  py::is_operator());
360  cls.def("__eq__", [](Point<T, N> const &self, Point<T, N> const &other) { return self == other; },
361  py::is_operator());
362  cls.def("__ne__", [](Point<T, N> const &self, Point<T, N> const &other) { return self != other; },
363  py::is_operator());
364 
365  /* Members */
366  cls.def("clone", [](Point<T, N> const &self) { return Point<T, N>{self}; });
367 
368  return cls;
369 }
370 
371 // Add functionality only found in N=2 Points
372 template <typename T>
373 PyPoint<T, 2> declarePoint2(py::module &mod, std::string const &suffix) {
374  auto cls = declarePoint<T, 2>(mod, std::string("2") + suffix);
375 
376  /* Member types and enums */
377  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 2; });
378 
379  /* Constructors */
380  cls.def(py::init<int, int>(), "x"_a, "y"_a);
381  cls.def(py::init<double, double>(), "x"_a, "y"_a);
382 
383  /* Members */
384  cls.def("getX", [](Point<T, 2> const &self) { return self[0]; });
385  cls.def("getY", [](Point<T, 2> const &self) { return self[1]; });
386  cls.def("setX", [](Point<T, 2> &self, T other) { self[0] = other; });
387  cls.def("setY", [](Point<T, 2> &self, T other) { self[1] = other; });
388  return cls;
389 }
390 
391 // Add functionality only found in N=3 Points
392 template <typename T>
393 PyPoint<T, 3> declarePoint3(py::module &mod, std::string const &suffix) {
394  auto cls = declarePoint<T, 3>(mod, std::string("3") + suffix);
395 
396  /* Member types and enums */
397  cls.def_property_readonly_static("dimensions", [](py::object /* cls */) { return 3; });
398 
399  /* Constructors */
400  cls.def(py::init<int, int, int>(), "x"_a, "y"_a, "z"_a);
401  cls.def(py::init<double, double, double>(), "x"_a, "y"_a, "z"_a);
402 
403  /* Members */
404  cls.def("getX", [](Point<T, 3> const &self) { return self[0]; });
405  cls.def("getY", [](Point<T, 3> const &self) { return self[1]; });
406  cls.def("getZ", [](Point<T, 3> const &self) { return self[2]; });
407  cls.def("setX", [](Point<T, 3> &self, T other) { self[0] = other; });
408  cls.def("setY", [](Point<T, 3> &self, T other) { self[1] = other; });
409  cls.def("setZ", [](Point<T, 3> &self, T other) { self[2] = other; });
410 
411  return cls;
412 }
413 
414 // Declare mixed-type and type-overloaded operators for Point with dimension
415 // N for both int and double. Because pybind11 tries operators (like any
416 // overload) `in order', int has to come before double in any overloaded
417 // operators that dispatch on a scalar, and hence they have to be defined here
418 // instead of declareExtent.
419 template <int N>
420 void declarePointOperators(py::module &mod, PyPoint<int, N> &clsI, PyPoint<double, N> &clsD) {
421  clsI.def("__iadd__", [](Point<int, N> &self, Extent<int, N> const &other) {
422  self += other;
423  return &self;
424  });
425  clsD.def("__iadd__", [](Point<double, N> &self, Extent<int, N> const &other) {
426  self += other;
427  return &self;
428  });
429  clsD.def("__iadd__", [](Point<double, N> &self, Extent<double, N> const &other) {
430  self += other;
431  return &self;
432  });
433  clsI.def("__isub__", [](Point<int, N> &self, Extent<int, N> const &other) {
434  self -= other;
435  return &self;
436  });
437  clsD.def("__isub__", [](Point<double, N> &self, Extent<int, N> const &other) {
438  self -= other;
439  return &self;
440  });
441  clsD.def("__isub__", [](Point<double, N> &self, Extent<double, N> const &other) {
442  self -= other;
443  return &self;
444  });
445 }
446 
447 PYBIND11_MODULE(coordinates, mod) {
448  // Only the interface-level classes are defined here, and these functions
449  // call others to define their base classes, since those are never shared.
450 
451  declareCoordinateExpr<2>(mod, "2");
452  declareCoordinateExpr<3>(mod, "3");
453 
454  auto clsExtent2I = declareExtent2<int>(mod, "I");
455  auto clsExtent2D = declareExtent2<double>(mod, "D");
456  declareExtentOperators(mod, clsExtent2I, clsExtent2D);
457 
458  auto clsExtent3I = declareExtent3<int>(mod, "I");
459  auto clsExtent3D = declareExtent3<double>(mod, "D");
460  declareExtentOperators(mod, clsExtent3I, clsExtent3D);
461 
462  auto clsPoint2I = declarePoint2<int>(mod, "I");
463  auto clsPoint2D = declarePoint2<double>(mod, "D");
464  declarePointOperators(mod, clsPoint2I, clsPoint2D);
465 
466  auto clsPoint3I = declarePoint3<int>(mod, "I");
467  auto clsPoint3D = declarePoint3<double>(mod, "D");
468  declarePointOperators(mod, clsPoint3I, clsPoint3D);
469 }
470 
471 } // namespace
472 } // namespace geom
473 } // 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:119
def init()
Definition: tests.py:75
def scale(algorithm, min, max=None, frame=None)
Definition: ds9.py:114
STL class.
A base class for image defects.
Definition: cameraGeom.dox:3
PYBIND11_MODULE(cameraSys, mod)
Definition: cameraSys.cc:62
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:108
ItemVariant const * other
Definition: Schema.cc:55