Skip to content

Commit 8456f72

Browse files
Extended the "pybind11" Python bindings for "ImathFun", and re-enabled the testing of its functions (#489)
* Extended the Python bindings for , and re-enabled the testing of its functions Signed-off-by: Joshua Senouf <[email protected]> * Changed the bindings implementation to closely match the existing Boost.Python ones, for compatibility purposes. Signed-off-by: Joshua Senouf <[email protected]> * Tentatively fixing the Linux and Windows CI. Signed-off-by: Joshua Senouf <[email protected]> * Added named arguments and docstrings to the functions. Updated the implementation of atan2 and pow to rely on bespoke functions to bind against, instead of using the C++ functions directly. Signed-off-by: Joshua Senouf <[email protected]> * Only provide a "double binding of the "log" and "log10" functions. Signed-off-by: Joshua Senouf <[email protected]> * remove float instantiation Signed-off-by: Cary Phillips <[email protected]> * provide only double for std math funcs to avoid inadvertent arg cast to float Signed-off-by: Cary Phillips <[email protected]> --------- Signed-off-by: Joshua Senouf <[email protected]> Signed-off-by: Cary Phillips <[email protected]> Co-authored-by: Cary Phillips <[email protected]>
1 parent b69b141 commit 8456f72

File tree

2 files changed

+351
-20
lines changed

2 files changed

+351
-20
lines changed

src/pybind11/PyBindImath/PyBindImathFun.cpp

Lines changed: 340 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,364 @@
44
//
55

66
#include "PyBindImath.h"
7+
8+
#include <pybind11/stl.h>
9+
#include <cmath>
10+
11+
#include <ImathColorAlgo.h>
712
#include <ImathFun.h>
13+
#include <ImathMatrixAlgo.h>
14+
#include <ImathVec.h>
815

916
namespace py = pybind11;
1017

1118
namespace {
1219

20+
static inline float
21+
fun_bias(float x, float b)
22+
{
23+
if (b != 0.5f)
24+
{
25+
static const float inverse_log_half = 1.0f / std::log(0.5f);
26+
const float biasPow = std::log(b) * inverse_log_half;
27+
28+
return std::pow(x, biasPow);
29+
}
30+
31+
return x;
32+
}
33+
34+
static inline float
35+
fun_gain(float x, float g)
36+
{
37+
if (x < 0.5f)
38+
return 0.5f * fun_bias(2.0f * x, 1.0f - g);
39+
else
40+
return 1.0f - 0.5f * fun_bias(2.0f - 2.0f * x, 1.0f - g);
41+
}
42+
43+
template <class T>
44+
static IMATH_NAMESPACE::Vec3<T>
45+
fun_rotationXYZWithUpDir(
46+
const IMATH_NAMESPACE::Vec3<T> &from,
47+
const IMATH_NAMESPACE::Vec3<T> &to,
48+
const IMATH_NAMESPACE::Vec3<T> &up)
49+
{
50+
IMATH_NAMESPACE::Vec3<T> retval;
51+
IMATH_NAMESPACE::extractEulerXYZ(IMATH_NAMESPACE::rotationMatrixWithUpDir(from, to, up), retval);
52+
53+
return retval;
54+
}
55+
56+
template <class T>
57+
static inline IMATH_NAMESPACE::Vec3<T>
58+
fun_rgb2hsv(const IMATH_NAMESPACE::Vec3<T> &rgb)
59+
{
60+
return IMATH_NAMESPACE::rgb2hsv(rgb);
61+
}
62+
63+
template <class T>
64+
static inline IMATH_NAMESPACE::Vec3<T>
65+
fun_hsv2rgb(const IMATH_NAMESPACE::Vec3<T> &rgb)
66+
{
67+
return IMATH_NAMESPACE::hsv2rgb(rgb);
68+
}
69+
1370
template <class T>
71+
static inline T
72+
fun_atan2(T y, T x) {
73+
return std::atan2(y, x);
74+
}
75+
76+
template <class T>
77+
static inline T
78+
fun_pow(T base, T exp) {
79+
return std::pow(base, exp);
80+
}
81+
1482
void
15-
register_imath_funT(py::module& m)
83+
register_fun(py::module& m)
1684
{
17-
m.def("cmp", IMATH_NAMESPACE::cmp<T>);
18-
m.def("cmpt", IMATH_NAMESPACE::cmpt<T>);
19-
m.def("iszero", IMATH_NAMESPACE::iszero<T>);
20-
m.def("equal", IMATH_NAMESPACE::equal<T, T, T>);
85+
m.def(
86+
"abs",
87+
IMATH_NAMESPACE::abs<int>,
88+
py::arg("value"),
89+
"Return the absolute value of the argument.");
90+
m.def(
91+
"sign",
92+
IMATH_NAMESPACE::sign<int>,
93+
py::arg("value"),
94+
"Return \"1\" or \"-1\" based on the sign of the argument.");
95+
m.def(
96+
"clamp",
97+
IMATH_NAMESPACE::clamp<int>,
98+
py::arg("value"),
99+
py::arg("low"),
100+
py::arg("high"),
101+
"Return the first argument clamped using the second and third arguments as a range.");
102+
m.def(
103+
"divs",
104+
IMATH_NAMESPACE::divs,
105+
py::arg("x"),
106+
py::arg("y"),
107+
"Return x/y where the remainder has the same sign as x:\n"
108+
" divs(x,y) == (abs(x) / abs(y)) * (sign(x) * sign(y))\n");
109+
m.def(
110+
"mods",
111+
IMATH_NAMESPACE::mods,
112+
py::arg("x"),
113+
py::arg("y"),
114+
"Return x%y where the remainder has the same sign as x:\n"
115+
" mods(x,y) == x - y * divs(x,y)\n");
116+
m.def(
117+
"divp",
118+
IMATH_NAMESPACE::divp,
119+
py::arg("x"),
120+
py::arg("y"),
121+
"Return x/y where the remainder is always positive:\n"
122+
" divp(x,y) == floor (double(x) / double (y))\n");
123+
m.def(
124+
"modp",
125+
IMATH_NAMESPACE::modp,
126+
py::arg("x"),
127+
py::arg("y"),
128+
"Return x%y where the remainder is always positive:\n"
129+
" modp(x,y) == x - y * divp(x,y)\n");
130+
m.def(
131+
"succf",
132+
IMATH_NAMESPACE::succf,
133+
py::arg("f"));
134+
m.def(
135+
"predf",
136+
IMATH_NAMESPACE::predf,
137+
py::arg("f"));
138+
m.def(
139+
"succd",
140+
IMATH_NAMESPACE::succd,
141+
py::arg("d"));
142+
m.def(
143+
"predd",
144+
IMATH_NAMESPACE::predd,
145+
py::arg("d"));
146+
m.def(
147+
"finitef",
148+
IMATH_NAMESPACE::finitef,
149+
py::arg("f"));
150+
m.def(
151+
"finited",
152+
IMATH_NAMESPACE::finited,
153+
py::arg("d"));
154+
m.def(
155+
"bias",
156+
fun_bias,
157+
py::arg("x"),
158+
py::arg("b"),
159+
"Return a gamma correction that remaps the unit interval such that \"bias(0.5, b) = b\".");
160+
m.def(
161+
"gain",
162+
fun_gain,
163+
py::arg("x"),
164+
py::arg("g"),
165+
"Return a gamma correction that remaps the unit interval with the property that \"gain(0.5, g) = 0.5\"."
166+
"\nThe \"gain()\" function can be thought of as two scaled bias curves forming an \"S\" shape in the unit "
167+
"interval.");
168+
m.def(
169+
"rotationXYZWithUpDir",
170+
fun_rotationXYZWithUpDir<float>,
171+
py::arg("fromDir"),
172+
py::arg("toDir"),
173+
py::arg("upDir"),
174+
"Return the XYZ rotation vector that rotates the first vector argument "
175+
"to the second vector argument, using the third argument as the up-vector.");
176+
177+
m.def(
178+
"log",
179+
[](double value) { return log(value); },
180+
py::arg("value"),
181+
"Return the natural logarithm of the argument.");
182+
m.def(
183+
"log10",
184+
[](double value) { return log10(value); },
185+
py::arg("value"),
186+
"Return the base 10 logarithm of the argument.");
187+
m.def(
188+
"sin",
189+
[](double theta) { return sin(theta); },
190+
py::arg("theta"),
191+
"Return the sine of the argument.");
192+
m.def(
193+
"cos",
194+
[](double theta) { return cos(theta); },
195+
py::arg("theta"),
196+
"Return the cosine of the argument.");
197+
m.def(
198+
"tan",
199+
[](double theta) { return std::tan(theta); },
200+
py::arg("theta"),
201+
"Return the tangent of the argument.");
202+
m.def(
203+
"asin",
204+
[](double x) { return std::asin(x); },
205+
py::arg("x"),
206+
"Return the arcsine of the argument.");
207+
m.def(
208+
"acos",
209+
[](double x) { return std::acos(x); },
210+
py::arg("x"),
211+
"Return the arcosine of the argument.");
212+
m.def(
213+
"atan",
214+
[](double x) { return std::atan(x); },
215+
py::arg("x"),
216+
"Return the arctangent of the argument.");
217+
m.def(
218+
"sqrt",
219+
[](double x) { return std::sqrt(x); },
220+
py::arg("x"),
221+
"Return the square root of the argument.");
222+
m.def(
223+
"exp",
224+
[](double x) { return std::exp(x); },
225+
py::arg("x"),
226+
"Return the exponential of the argument.");
227+
m.def(
228+
"sinh",
229+
[](double x) { return std::sinh(x); },
230+
py::arg("x"),
231+
"Return the hyperbolic sine of the argument.");
232+
m.def(
233+
"cosh",
234+
[](double x) { return std::cosh(x); },
235+
py::arg("x"),
236+
"Return the hyperbolic cosine of the argument.");
21237

22238
}
23239

24-
} // namespace
240+
template <class T>
241+
void
242+
register_fun_fp_T(py::module& m)
243+
{
244+
m.def(
245+
"abs",
246+
IMATH_NAMESPACE::abs<T>,
247+
py::arg("value"),
248+
"Return the absolute value of the argument.");
249+
m.def(
250+
"sign",
251+
IMATH_NAMESPACE::sign<T>,
252+
py::arg("value"),
253+
"Return \"1\" or \"-1\" based on the sign of the argument.");
254+
m.def(
255+
"lerp",
256+
IMATH_NAMESPACE::lerp<T, T>,
257+
py::arg("a"),
258+
py::arg("b"),
259+
py::arg("t"),
260+
"Return the linear interpolation of the first and second arguments, "
261+
"using the third argument as the parameter.");
262+
m.def(
263+
"ulerp",
264+
IMATH_NAMESPACE::ulerp<T, T>,
265+
py::arg("a"),
266+
py::arg("b"),
267+
py::arg("t"));
268+
m.def(
269+
"lerpfactor",
270+
IMATH_NAMESPACE::lerpfactor<T>,
271+
py::arg("m"),
272+
py::arg("a"),
273+
py::arg("b"),
274+
"Return how far m is between a and b, that is return t such that\n"
275+
"if:\n"
276+
" t = lerpfactor(m, a, b);\n"
277+
"then:\n"
278+
" m = lerp(a, b, t);\n"
279+
"\n"
280+
"If a==b, return 0.\n");
281+
m.def(
282+
"clamp",
283+
IMATH_NAMESPACE::clamp<T>,
284+
py::arg("value"),
285+
py::arg("low"),
286+
py::arg("high"),
287+
"Return the first argument clamped using the second and third arguments as a range.");
288+
m.def(
289+
"floor",
290+
IMATH_NAMESPACE::floor<T>,
291+
py::arg("value"),
292+
"Return the closest integer smaller or equal to the argument.");
293+
m.def(
294+
"ceil",
295+
IMATH_NAMESPACE::ceil<T>,
296+
py::arg("value"),
297+
"Return the closest integer greater or equal to the argument.");
298+
m.def(
299+
"trunc",
300+
IMATH_NAMESPACE::trunc<T>,
301+
py::arg("value"),
302+
"Return the closest integer with a magnitude smaller or equal to the argument.");
303+
m.def(
304+
"rgb2hsv",
305+
fun_rgb2hsv<T>,
306+
py::arg("rgb"),
307+
"Return a HSV representation of the RGB argument.");
308+
m.def(
309+
"hsv2rgb",
310+
fun_hsv2rgb<T>,
311+
py::arg("hsv"),
312+
"Return a RGB representation of the HSV argument.");
313+
m.def(
314+
"cmp",
315+
IMATH_NAMESPACE::cmp<T>,
316+
py::arg("a"),
317+
py::arg("b"));
318+
m.def(
319+
"cmpt",
320+
IMATH_NAMESPACE::cmpt<T>,
321+
py::arg("a"),
322+
py::arg("b"),
323+
py::arg("t"));
324+
m.def(
325+
"iszero",
326+
IMATH_NAMESPACE::iszero<T>,
327+
py::arg("a"),
328+
py::arg("t"));
329+
m.def(
330+
"equal",
331+
IMATH_NAMESPACE::equal<T, T, T>,
332+
py::arg("a"),
333+
py::arg("b"),
334+
py::arg("t"));
335+
m.def(
336+
"atan2",
337+
fun_atan2<T>,
338+
py::arg("y"),
339+
py::arg("x"),
340+
"Return the arctangent of the first argument over the second argument.");
341+
m.def(
342+
"pow",
343+
fun_pow<T>,
344+
py::arg("x"),
345+
py::arg("y"),
346+
"Return the first argument raised to the power of the second argument.");
347+
}
348+
349+
} // namespace
25350

26351
namespace PyBindImath {
27352

28353
void
29354
register_imath_fun(py::module& m)
30355
{
31-
register_imath_funT<int>(m);
32-
register_imath_funT<float>(m);
33-
register_imath_funT<double>(m);
356+
// Bindings for functions using explicit argument(s) and return types.
357+
register_fun(m);
358+
359+
// Bindings for functions using floating point types. Only bind
360+
// to double to prevent loss of precision in inadvertent casting
361+
// to float.
362+
//
363+
register_fun_fp_T<float>(m);
364+
register_fun_fp_T<double>(m);
34365
}
35366

36367
} // namespace PyBindImath
37-

0 commit comments

Comments
 (0)