1 //
2 // SPDX-License-Identifier: BSD-3-Clause
3 // Copyright Contributors to the OpenEXR Project.
4 //
5 
6 // clang-format off
7 
8 #include <Python.h>
9 #include <boost/python.hpp>
10 #include <boost/format.hpp>
11 #include "PyImath.h"
12 #include "PyImathMathExc.h"
13 #include "PyImathVec.h"
14 #include "PyImathFrustum.h"
15 #include "PyImathDecorators.h"
16 #include "PyImathExport.h"
17 
18 namespace PyImath{
19 using namespace boost::python;
20 using namespace IMATH_NAMESPACE;
21 
22 template <class T> struct FrustumName {static const char *value;};
23 template <> const char *FrustumName<float>::value = "Frustumf";
24 template <> const char *FrustumName<double>::value = "Frustumd";
25 
26 template <class T> struct FrustumTestName {static const char *value;};
27 template <> const char *FrustumTestName<float>::value = "FrustumTestf";
28 template <> const char *FrustumTestName<double>::value = "FrustumTestd";
29 
30 
31 template <class T>
Frustum_repr(const Frustum<T> & f)32 static std::string Frustum_repr(const Frustum<T> &f)
33 {
34     std::stringstream stream;
35     stream << FrustumName<T>::value << "(" << f.nearPlane() << ", " << f.farPlane() << ", "
36            << f.left() << ", " << f.right() << ", " << f.top() << ", "
37            << f.bottom() << ", " << f.orthographic() << ")";
38     return stream.str();
39 }
40 
41 template <class T>
42 static void
modifyNearAndFar(Frustum<T> & f,T nearPlane,T farPlane)43 modifyNearAndFar(Frustum<T> &f, T nearPlane, T farPlane)
44 {
45     MATH_EXC_ON;
46     f.modifyNearAndFar (nearPlane, farPlane);
47 }
48 
49 template <class T>
50 static T
fovx(Frustum<T> & f)51 fovx(Frustum<T> &f)
52 {
53     MATH_EXC_ON;
54     return f.fovx();
55 }
56 
57 template <class T>
58 static T
fovy(Frustum<T> & f)59 fovy(Frustum<T> &f)
60 {
61     MATH_EXC_ON;
62     return f.fovy();
63 }
64 
65 template <class T>
66 static T
aspect(Frustum<T> & f)67 aspect(Frustum<T> &f)
68 {
69     MATH_EXC_ON;
70     return f.aspect();
71 }
72 
73 template <class T>
74 static Matrix44<T>
projectionMatrix(Frustum<T> & f)75 projectionMatrix(Frustum<T> &f)
76 {
77     MATH_EXC_ON;
78     return f.projectionMatrix();
79 }
80 
81 template <class T>
82 static Frustum<T>
window(Frustum<T> & f,T l,T r,T b,T t)83 window (Frustum<T> &f, T l, T r, T b, T t)
84 {
85     MATH_EXC_ON;
86     return f.window(l, r, b, t);
87 }
88 
89 template <class T>
90 static Line3<T>
projectScreenToRay(Frustum<T> & f,const Vec2<T> & p)91 projectScreenToRay (Frustum<T> &f, const Vec2<T> &p)
92 {
93     MATH_EXC_ON;
94     return f.projectScreenToRay(p);
95 }
96 
97 template <class T>
98 static Line3<T>
projectScreenToRayTuple(Frustum<T> & f,const tuple & t)99 projectScreenToRayTuple(Frustum<T> &f, const tuple &t)
100 {
101     MATH_EXC_ON;
102     if(t.attr("__len__")() == 2)
103     {
104         Vec2<T> point;
105         point.x = extract<T>(t[0]);
106         point.y = extract<T>(t[1]);
107         return f.projectScreenToRay(point);
108     }
109     else
110         throw std::invalid_argument ( "projectScreenToRay expects tuple of length 2");
111 
112 }
113 
114 template <class T>
115 static Vec2<T>
projectPointToScreen(Frustum<T> & f,const Vec3<T> & p)116 projectPointToScreen (Frustum<T> &f, const Vec3<T> &p)
117 {
118     MATH_EXC_ON;
119     return f.projectPointToScreen(p);
120 }
121 
122 template <class T>
123 static Vec2<T>
projectPointToScreenTuple(Frustum<T> & f,const tuple & t)124 projectPointToScreenTuple(Frustum<T> &f, const tuple &t)
125 {
126     MATH_EXC_ON;
127     if(t.attr("__len__")() == 3)
128     {
129         Vec3<T> point;
130         point.x = extract<T>(t[0]);
131         point.y = extract<T>(t[1]);
132         point.z = extract<T>(t[2]);
133         return f.projectPointToScreen(point);
134     }
135     else
136         throw std::invalid_argument ( "projectPointToScreen expects tuple of length 3");
137 
138 }
139 
140 template <class T>
141 static Vec2<T>
projectPointToScreenObj(Frustum<T> & f,const object & o)142 projectPointToScreenObj(Frustum<T> &f, const object &o)
143 {
144     MATH_EXC_ON;
145     Vec3<T> v;
146     if (PyImath::V3<T>::convert (o.ptr(), &v))
147         return f.projectPointToScreen(v);
148     else
149         throw std::invalid_argument ( "projectPointToScreen expects tuple of length 3");
150 }
151 
152 template <class T>
153 static T
ZToDepth(Frustum<T> & f,long z,long min,long max)154 ZToDepth(Frustum<T> &f, long z, long min, long max)
155 {
156     MATH_EXC_ON;
157     return f.ZToDepth(z, min, max);
158 }
159 
160 template <class T>
161 static T
normalizedZToDepth(Frustum<T> & f,T z)162 normalizedZToDepth(Frustum<T> &f, T z)
163 {
164     MATH_EXC_ON;
165     return f.normalizedZToDepth(z);
166 }
167 
168 template <class T>
169 static long
DepthToZ(Frustum<T> & f,T depth,long min,long max)170 DepthToZ(Frustum<T> &f, T depth, long min, long max)
171 {
172     MATH_EXC_ON;
173     return f.DepthToZ(depth, min, max);
174 }
175 
176 template <class T>
177 static T
worldRadius(Frustum<T> & f,const Vec3<T> & p,T radius)178 worldRadius(Frustum<T> &f, const Vec3<T> &p, T radius)
179 {
180     MATH_EXC_ON;
181     return f.worldRadius(p, radius);
182 }
183 
184 template <class T>
185 static T
worldRadiusTuple(Frustum<T> & f,const tuple & t,T radius)186 worldRadiusTuple(Frustum<T> &f, const tuple &t, T radius)
187 {
188     MATH_EXC_ON;
189     if(t.attr("__len__")() == 3)
190     {
191         Vec3<T> point;
192         point.x = extract<T>(t[0]);
193         point.y = extract<T>(t[1]);
194         point.z = extract<T>(t[2]);
195         return f.worldRadius(point, radius);
196     }
197     else
198         throw std::invalid_argument ( "worldRadius expects tuple of length 3");
199 }
200 
201 template <class T>
202 static T
screenRadius(Frustum<T> & f,const Vec3<T> & p,T radius)203 screenRadius(Frustum<T> &f, const Vec3<T> &p, T radius)
204 {
205     MATH_EXC_ON;
206     return f.screenRadius(p, radius);
207 }
208 
209 template <class T>
210 static T
screenRadiusTuple(Frustum<T> & f,const tuple & t,T radius)211 screenRadiusTuple(Frustum<T> &f, const tuple &t, T radius)
212 {
213     MATH_EXC_ON;
214     if(t.attr("__len__")() == 3)
215     {
216         Vec3<T> point;
217         point.x = extract<T>(t[0]);
218         point.y = extract<T>(t[1]);
219         point.z = extract<T>(t[2]);
220         return f.screenRadius(point, radius);
221     }
222     else
223       throw std::invalid_argument ("screenRadius expects tuple of length 3");
224 }
225 
226 template <class T>
227 static void
planes1(Frustum<T> & f,Plane3<T> * p)228 planes1(Frustum<T> &f, Plane3<T> *p)
229 {
230     MATH_EXC_ON;
231     f.planes(p);
232 }
233 
234 template <class T>
235 static void
planes2(Frustum<T> & f,Plane3<T> * p,const Matrix44<T> & m)236 planes2(Frustum<T> &f, Plane3<T> *p, const Matrix44<T> &m)
237 {
238     MATH_EXC_ON;
239     f.planes(p, m);
240 }
241 
242 template <class T>
243 static tuple
planes3(Frustum<T> & f,const Matrix44<T> & mat)244 planes3(Frustum<T> &f, const Matrix44<T> &mat)
245 {
246     MATH_EXC_ON;
247     IMATH_NAMESPACE::Plane3<T> p[6];
248     f.planes(p,mat);
249 
250     tuple t = make_tuple(p[0],p[1],p[2],p[3],p[4],p[5]);
251 
252     return t;
253 }
254 
255 template <class T>
256 static tuple
planes4(Frustum<T> & f)257 planes4(Frustum<T> &f)
258 {
259     MATH_EXC_ON;
260     IMATH_NAMESPACE::Plane3<T> p[6];
261     f.planes(p);
262 
263     tuple t = make_tuple(p[0],p[1],p[2],p[3],p[4],p[5]);
264 
265     return t;
266 }
267 
268 template <class T>
269 class_<Frustum<T> >
register_Frustum()270 register_Frustum()
271 {
272     void (IMATH_NAMESPACE::Frustum<T>::*set1)(T,T,T,T,T,T,bool) = &IMATH_NAMESPACE::Frustum<T>::set;
273     void (IMATH_NAMESPACE::Frustum<T>::*set2)(T,T,T,T,T)        = &IMATH_NAMESPACE::Frustum<T>::set;
274     const char *name = FrustumName<T>::value;
275 
276     class_< Frustum<T> > frustum_class(name,name,init<Frustum<T> >("copy construction"));
277     frustum_class
278         .def(init<>("Frustum() default construction"))
279         .def(init<T,T,T,T,T,T,bool>("Frustum(nearPlane,farPlane,left,right,top,bottom,ortho) construction"))
280         .def(init<T,T,T,T,T>("Frustum(nearPlane,farPlane,fovx,fovy,aspect) construction"))
281         .def(self == self) // NOSONAR - suppress SonarCloud bug report.
282         .def(self != self) // NOSONAR - suppress SonarCloud bug report.
283         .def("__repr__",&Frustum_repr<T>)
284         .def("set", set1,
285         	 "F.set(nearPlane, farPlane, left, right, top, bottom, "
286 	 		 "[ortho])\n"
287 			 "F.set(nearPlane, farPlane, fovx, fovy, aspect)       "
288 			 "         -- sets the entire state of "
289 			 "frustum F as specified.  Only one of "
290 			 "fovx or fovy may be non-zero.")
291         .def("set", set2)
292 
293         .def("modifyNearAndFar", &modifyNearAndFar<T>,
294         	 "F.modifyNearAndFar(nearPlane, farPlane) -- modifies "
295 			 "the already-valid frustum F as specified")
296 
297         .def("setOrthographic", &Frustum<T>::setOrthographic,
298         	 "F.setOrthographic(b) -- modifies the "
299 			 "already-valid frustum F to be orthographic "
300 			 "or not")
301 
302         .def("nearPlane", &Frustum<T>::nearPlane,
303         	 "F.nearPlane() -- returns the coordinate of the "
304 			 "near clipping plane of frustum F")
305 
306         .def("farPlane", &Frustum<T>::farPlane,
307         	 "F.farPlane() -- returns the coordinate of the "
308 			 "far clipping plane of frustum F")
309 
310         // The following two functions provide backwards compatibility
311         // with the previous API for this class.
312 
313         .def("near", &Frustum<T>::nearPlane,
314         	 "F.near() -- returns the coordinate of the "
315 			 "near clipping plane of frustum F")
316 
317         .def("far", &Frustum<T>::farPlane,
318         	 "F.far() -- returns the coordinate of the "
319 			 "far clipping plane of frustum F")
320 
321         .def("left", &Frustum<T>::left,
322         	 "F.left() -- returns the left coordinate of "
323 			 "the near clipping window of frustum F")
324 
325         .def("right", &Frustum<T>::right,
326         	 "F.right() -- returns the right coordinate of "
327 			 "the near clipping window of frustum F")
328 
329         .def("top", &Frustum<T>::top,
330 	 		 "F.top() -- returns the top coordinate of "
331 			 "the near clipping window of frustum F")
332 
333         .def("bottom", &Frustum<T>::bottom,
334   			 "F.bottom() -- returns the bottom coordinate "
335 			 "of the near clipping window of frustum F")
336 
337         .def("orthographic", &Frustum<T>::orthographic,
338         	 "F.orthographic() -- returns whether frustum "
339 			 "F is orthographic or not")
340 
341         .def("planes", planes1<T>,
342 	 		 "F.planes([M]) -- returns a sequence of 6 "
343 			 "Plane3s, the sides of the frustum F "
344 			 "(top, right, bottom, left, nearPlane, farPlane), "
345 			 "optionally transformed by the matrix M "
346 	 		 "if specified")
347         .def("planes", planes2<T>)
348         .def("planes", planes3<T>)
349         .def("planes", planes4<T>)
350 
351         .def("fovx", &fovx<T>,
352         	 "F.fovx() -- derives and returns the "
353 	 		 "x field of view (in radians) for frustum F")
354 
355         .def("fovy", &fovy<T>,
356         	 "F.fovy() -- derives and returns the "
357 	 		 "y field of view (in radians) for frustum F")
358 
359         .def("aspect", &aspect<T>,
360         	 "F.aspect() -- derives and returns the "
361 	 		 "aspect ratio for frustum F")
362 
363         .def("projectionMatrix", &projectionMatrix<T>,
364         	 "F.projectionMatrix() -- derives and returns "
365 	 		 "the projection matrix for frustum F")
366 
367         .def("window", &window<T>,
368         	 "F.window(l,r,b,t) -- takes a rectangle in "
369 	 		 "the screen space (i.e., -1 <= l <= r <= 1, "
370 			 "-1 <= b <= t <= 1) of F and returns a new "
371 			 "Frustum whose near clipping-plane window "
372 	 		 "is that rectangle in local space")
373 
374         .def("projectScreenToRay", &projectScreenToRay<T>,
375         	 "F.projectScreenToRay(V) -- returns a Line3 "
376 	 		 "through V, a V2 point in screen space")
377 
378         .def("projectScreenToRay", &projectScreenToRayTuple<T>)
379 
380         .def("projectPointToScreen", &projectPointToScreen<T>,
381         	 "F.projectPointToScreen(V) -- returns the "
382 			 "projection of V3 V into screen space")
383 
384         .def("projectPointToScreen", &projectPointToScreenTuple<T>)
385 
386         .def("projectPointToScreen", &projectPointToScreenObj<T>)
387 
388         .def("ZToDepth", &ZToDepth<T>,
389         	 "F.ZToDepth(z, zMin, zMax) -- returns the "
390 			 "depth (Z in the local space of the "
391 			 "frustum F) corresponding to z (a result of "
392 			 "transformation by F's projection matrix) "
393 			 "after normalizing z to be between zMin "
394 			 "and zMax")
395 
396         .def("normalizedZToDepth", &normalizedZToDepth<T>,
397         	 "F.normalizedZToDepth(z) -- returns the "
398 			 "depth (Z in the local space of the "
399 			 "frustum F) corresponding to z (a result of "
400 	 		 "transformation by F's projection matrix), "
401 	 		 "which is assumed to have been normalized "
402 	 		 "to [-1, 1]")
403 
404         .def("DepthToZ", &DepthToZ<T>,
405         	 "F.DepthToZ(depth, zMin, zMax) -- converts "
406 			 "depth (Z in the local space of the frustum "
407 			 "F) to z (a result of  transformation by F's "
408 			 "projection matrix) which is normalized to "
409 			 "[zMin, zMax]")
410 
411         .def("worldRadius", &worldRadius<T>,
412         	 "F.worldRadius(V, r) -- returns the radius "
413 			 "in F's local space corresponding to the "
414 	 		 "point V and radius r in screen space")
415 
416         .def("worldRadius", &worldRadiusTuple<T>)
417 
418         .def("screenRadius", &screenRadius<T>,
419         	 "F.screenRadius(V, r) -- returns the radius "
420 			 "in screen space corresponding to "
421 			 "the point V and radius r in F's local "
422 			 "space")
423 
424         .def("screenRadius", &screenRadiusTuple<T>)
425 
426         ;
427 
428     decoratecopy(frustum_class);
429 
430     return frustum_class;
431 }
432 
433 template <class T,class T2>
434 struct IsVisibleTask : public Task
435 {
436     const IMATH_NAMESPACE::FrustumTest<T>& frustumTest;
437     const PyImath::FixedArray<T2>& points;
438     PyImath::FixedArray<int>& results;
439 
IsVisibleTaskPyImath::IsVisibleTask440     IsVisibleTask(const IMATH_NAMESPACE::FrustumTest<T>& ft, const PyImath::FixedArray<T2> &p, PyImath::FixedArray<int> &r)
441         : frustumTest(ft), points(p), results(r) {}
442 
executePyImath::IsVisibleTask443     void execute(size_t start, size_t end)
444     {
445         for(size_t p = start; p < end; ++p)
446             results[p] = frustumTest.isVisible(IMATH_NAMESPACE::Vec3<T>(points[p]));
447     }
448 };
449 
450 template <class T,class T2>
451 PyImath::FixedArray<int>
frustumTest_isVisible(IMATH_NAMESPACE::FrustumTest<T> & ft,const PyImath::FixedArray<T2> & points)452 frustumTest_isVisible(IMATH_NAMESPACE::FrustumTest<T>& ft, const PyImath::FixedArray<T2>& points)
453 {
454     size_t numPoints = points.len();
455     PyImath::FixedArray<int> mask(numPoints);
456 
457     IsVisibleTask<T,T2> task(ft,points,mask);
458     dispatchTask(task,numPoints);
459     return mask;
460 }
461 
462 template <class T>
463 class_<FrustumTest<T> >
register_FrustumTest()464 register_FrustumTest()
465 {
466     const char *name = FrustumTestName<T>::value;
467 
468     bool (FrustumTest<T>::*isVisibleS)(const Sphere3<T> &) const = &FrustumTest<T>::isVisible;
469     bool (FrustumTest<T>::*isVisibleB)(const Box<Vec3<T> > &) const = &FrustumTest<T>::isVisible;
470     bool (FrustumTest<T>::*isVisibleV)(const Vec3<T> &) const = &FrustumTest<T>::isVisible;
471     bool (FrustumTest<T>::*completelyContainsS)(const Sphere3<T> &) const = &FrustumTest<T>::completelyContains;
472     bool (FrustumTest<T>::*completelyContainsB)(const Box<Vec3<T> > &) const = &FrustumTest<T>::completelyContains;
473 
474     class_< FrustumTest<T> > frustumtest_class(name,name,init<const IMATH_NAMESPACE::Frustum<T>&,const IMATH_NAMESPACE::Matrix44<T>&>("create a frustum test object from a frustum and transform"));
475     frustumtest_class
476         .def("isVisible",isVisibleS)
477         .def("isVisible",isVisibleB)
478         .def("isVisible",isVisibleV)
479         .def("isVisible",&frustumTest_isVisible<T,IMATH_NAMESPACE::V3f>)
480         .def("completelyContains",completelyContainsS)
481         .def("completelyContains",completelyContainsB)
482         ;
483 
484     decoratecopy(frustumtest_class);
485 
486     return frustumtest_class;
487 }
488 
489 template PYIMATH_EXPORT class_<Frustum<float> > register_Frustum<float>();
490 template PYIMATH_EXPORT class_<Frustum<double> > register_Frustum<double>();
491 template PYIMATH_EXPORT class_<FrustumTest<float> > register_FrustumTest<float>();
492 template PYIMATH_EXPORT class_<FrustumTest<double> > register_FrustumTest<double>();
493 }
494