python/src/casters.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief `Vec2d`, `Vec3d` and `Bounds2d` as Python tuples of floats. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/util/geometry.h" | ||
| 6 | |||
| 7 | #include <nanobind/nanobind.h> | ||
| 8 | #include <nanobind/stl/array.h> | ||
| 9 | |||
| 10 | #include <array> | ||
| 11 | #include <cstddef> | ||
| 12 | |||
| 13 | namespace terraingen::python { | ||
| 14 | |||
| 15 | /// The components of a value, in the order Python sees them. | ||
| 16 | 239 | inline std::array<double, 2> Doubles(const Vec2d& v) { | |
| 17 | 239 | return {v.x, v.y}; | |
| 18 | } | ||
| 19 | |||
| 20 | 1192 | inline std::array<double, 3> Doubles(const Vec3d& v) { | |
| 21 | 1192 | return {v.x, v.y, v.z}; | |
| 22 | } | ||
| 23 | |||
| 24 | 10 | inline std::array<double, 4> Doubles(const Bounds2d& b) { | |
| 25 | 10 | return {b.minX, b.minY, b.maxX, b.maxY}; | |
| 26 | } | ||
| 27 | |||
| 28 | /// The value whose `Doubles` are `a`. | ||
| 29 | 531 | template <typename T, std::size_t N> T FromDoubles(const std::array<double, N>& a) { | |
| 30 | if constexpr (N == 2) { | ||
| 31 | 322 | return T{a[0], a[1]}; | |
| 32 | } else if constexpr (N == 3) { | ||
| 33 | 169 | return T{a[0], a[1], a[2]}; | |
| 34 | } else { | ||
| 35 | 40 | return T{a[0], a[1], a[2], a[3]}; | |
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace terraingen::python | ||
| 40 | |||
| 41 | namespace nanobind::detail { | ||
| 42 | |||
| 43 | /// Converts `T` from any sequence of numbers and to a tuple of floats. | ||
| 44 | template <typename T> struct doubles_caster { | ||
| 45 | 681 | NB_TYPE_CASTER(T, const_name("tuple")) | |
| 46 | |||
| 47 | using Array = decltype(terraingen::python::Doubles(T{})); | ||
| 48 | |||
| 49 | 531 | bool from_python(handle src, uint32_t flags, cleanup_list* cleanup) noexcept { | |
| 50 | make_caster<Array> caster; | ||
| 51 | 531 | if (!caster.from_python(src, flags, cleanup)) { | |
| 52 | ✗ | return false; | |
| 53 | } | ||
| 54 | 531 | value = terraingen::python::FromDoubles<T>(caster.operator cast_t<Array>()); | |
| 55 | 531 | return true; | |
| 56 | } | ||
| 57 | |||
| 58 | 1441 | static handle from_cpp(const T& v, rv_policy /*policy*/, cleanup_list* /*cleanup*/) noexcept { | |
| 59 | 1441 | const Array values = terraingen::python::Doubles(v); | |
| 60 | 1441 | PyObject* tuple = PyTuple_New(static_cast<Py_ssize_t>(values.size())); | |
| 61 | 11070 | for (std::size_t i = 0; tuple != nullptr && i < values.size(); ++i) { | |
| 62 | 4094 | PyObject* item = PyFloat_FromDouble(values[i]); | |
| 63 | 4094 | if (item == nullptr || PyTuple_SetItem(tuple, static_cast<Py_ssize_t>(i), item) != 0) { | |
| 64 | ✗ | Py_DECREF(tuple); | |
| 65 | ✗ | return {}; | |
| 66 | } | ||
| 67 | } | ||
| 68 | 1441 | return tuple; | |
| 69 | } | ||
| 70 | }; | ||
| 71 | |||
| 72 | template <> struct type_caster<terraingen::Vec2d> : doubles_caster<terraingen::Vec2d> { | ||
| 73 | static constexpr auto Name = const_name("tuple[float, float]"); | ||
| 74 | }; | ||
| 75 | |||
| 76 | template <> struct type_caster<terraingen::Vec3d> : doubles_caster<terraingen::Vec3d> { | ||
| 77 | static constexpr auto Name = const_name("tuple[float, float, float]"); | ||
| 78 | }; | ||
| 79 | |||
| 80 | template <> struct type_caster<terraingen::Bounds2d> : doubles_caster<terraingen::Bounds2d> { | ||
| 81 | static constexpr auto Name = const_name("tuple[float, float, float, float]"); | ||
| 82 | }; | ||
| 83 | |||
| 84 | } // namespace nanobind::detail | ||
| 85 |