1
2//
3// This source file is part of appleseed.
4// Visit http://appleseedhq.net/ for additional information and resources.
5//
6// This software is released under the MIT license.
7//
8// Copyright (c) 2012-2013 Esteban Tovagliari, Jupiter Jazz Limited
9// Copyright (c) 2014-2017 Esteban Tovagliari, The appleseedhq Organization
10//
11// Permission is hereby granted, free of charge, to any person obtaining a copy
12// of this software and associated documentation files (the "Software"), to deal
13// in the Software without restriction, including without limitation the rights
14// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15// copies of the Software, and to permit persons to whom the Software is
16// furnished to do so, subject to the following conditions:
17//
18// The above copyright notice and this permission notice shall be included in
19// all copies or substantial portions of the Software.
20//
21// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27// THE SOFTWARE.
28//
29
30// appleseed.python headers.
31#include "pyseed.h" // has to be first, to avoid redefinition warnings
32
33// appleseed.foundation headers.
34#include "foundation/math/vector.h"
35#include "foundation/utility/iostreamop.h"
36
37// Standard headers.
38#include <cstddef>
39#include <memory>
40
41namespace bpy = boost::python;
42using namespace foundation;
43using namespace std;
44
45namespace
46{
47 template <typename T, size_t N>
48 Vector<T, N>* construct_vec_from_list(bpy::list l)
49 {
50 if (bpy::len(l) != N)
51 {
52 PyErr_SetString(PyExc_RuntimeError, "Invalid list length given to appleseed.Vector");
53 bpy::throw_error_already_set();
54 }
55
56 auto_ptr<Vector<T, N> > r(new Vector<T, N>());
57
58 for (size_t i = 0; i < N; ++i)
59 {
60 bpy::extract<T> ex(l[i]);
61 if (!ex.check())
62 {
63 PyErr_SetString(PyExc_TypeError, "Incompatible type.");
64 bpy::throw_error_already_set();
65 }
66
67 (*r)[i] = ex();
68 }
69
70 return r.release();
71 }
72
73 template <typename T, size_t N>
74 struct VectorHelper {};
75
76 template <typename T>
77 struct VectorHelper<T, 2>
78 {
79 typedef Vector<T, 2> VectorType;
80
81 static VectorType* construct(const T x, const T y)
82 {
83 return new VectorType(x, y);
84 }
85
86 static T norm(const VectorType& v)
87 {
88 return foundation::norm(v);
89 }
90
91 static VectorType normalize(const VectorType& v)
92 {
93 return foundation::normalize(v);
94 }
95
96 static T dot(const VectorType& lhs, const VectorType& rhs)
97 {
98 return foundation::dot(lhs, rhs);
99 }
100 };
101
102 template <typename T>
103 struct VectorHelper<T, 3>
104 {
105 typedef Vector<T, 3> VectorType;
106
107 static VectorType* construct(const T x, const T y, const T z)
108 {
109 return new VectorType(x, y, z);
110 }
111
112 static T norm(const VectorType& v)
113 {
114 return foundation::norm(v);
115 }
116
117 static VectorType normalize(const VectorType& v)
118 {
119 return foundation::normalize(v);
120 }
121
122 static T dot(const VectorType& lhs, const VectorType& rhs)
123 {
124 return foundation::dot(lhs, rhs);
125 }
126
127 static VectorType cross(const VectorType& lhs, const VectorType& rhs)
128 {
129 return foundation::cross(lhs, rhs);
130 }
131 };
132
133 template <typename T>
134 struct VectorHelper<T, 4>
135 {
136 typedef Vector<T, 4> VectorType;
137
138 static VectorType* construct(const T x, const T y, const T z, const T w)
139 {
140 return new VectorType(x, y, z, w);
141 }
142
143 static T norm(const VectorType& v)
144 {
145 return foundation::norm(v);
146 }
147
148 static VectorType normalize(const VectorType& v)
149 {
150 return foundation::normalize(v);
151 }
152
153 static T dot(const VectorType& lhs, const VectorType& rhs)
154 {
155 return foundation::dot(lhs, rhs);
156 }
157 };
158
159 template <typename T, size_t N>
160 struct vector_indexer
161 {
162 static T get(const Vector<T, N>& x, int i)
163 {
164 if (i < 0)
165 i = N + i;
166
167 if (i >= 0 && i < N)
168 return x[i];
169 else
170 {
171 PyErr_SetString(PyExc_IndexError, "Invalid index in appleseed.Vector");
172 boost::python::throw_error_already_set();
173 }
174
175 return T();
176 }
177
178 static void set(Vector<T, N>& x, int i, const T& v)
179 {
180 if (i < 0)
181 i = N + i;
182
183 if (i >= 0 && i < N)
184 x[i] = v;
185 else
186 {
187 PyErr_SetString(PyExc_IndexError, "Invalid index in appleseed.Vector");
188 boost::python::throw_error_already_set();
189 }
190 }
191 };
192
193 template <typename T, size_t N>
194 void do_bind_vector(const char* class_name)
195 {
196 bpy::class_<Vector<T, N> >(class_name)
197 .def(bpy::init<>())
198 .def(bpy::init<T>())
199 .def("__init__", bpy::make_constructor(&VectorHelper<T, N>::construct))
200 .def("__init__", bpy::make_constructor(&construct_vec_from_list<T, N>))
201
202 // operator[]
203 .def("__getitem__", &vector_indexer<T, N>::get)
204 .def("__setitem__", &vector_indexer<T, N>::set)
205
206 // Operators.
207 .def(bpy::self += bpy::self)
208 .def(bpy::self + bpy::self)
209 .def(bpy::self -= bpy::self)
210 .def(bpy::self - bpy::self)
211
212 .def(bpy::self *= T())
213 .def(bpy::self * T())
214 .def(T() * bpy::self)
215
216 .def(bpy::self /= T())
217 .def(bpy::self / bpy::self)
218 .def(bpy::self / T())
219 .def(-bpy::self)
220 .def(bpy::self == bpy::self)
221 .def(bpy::self != bpy::self)
222
223 // Because of a bug in Boost.Python, this needs the extra self_ns qualification.
224 .def(bpy::self_ns::str(bpy::self))
225 .def(bpy::self_ns::repr(bpy::self));
226
227 bpy::def("dot", &VectorHelper<T, N>::dot);
228 }
229}
230
231void bind_vector()
232{
233 do_bind_vector<int, 2>("Vector2i");
234 do_bind_vector<float, 2>("Vector2f");
235 do_bind_vector<double, 2>("Vector2d");
236
237 do_bind_vector<int, 3>("Vector3i");
238 do_bind_vector<float, 3>("Vector3f");
239 do_bind_vector<double, 3>("Vector3d");
240
241 do_bind_vector<int, 4>("Vector4i");
242 do_bind_vector<float, 4>("Vector4f");
243 do_bind_vector<double, 4>("Vector4d");
244
245 bpy::def("norm", &VectorHelper<float, 2>::norm);
246 bpy::def("norm", &VectorHelper<double, 2>::norm);
247 bpy::def("norm", &VectorHelper<float, 3>::norm);
248 bpy::def("norm", &VectorHelper<double, 3>::norm);
249 bpy::def("norm", &VectorHelper<float, 4>::norm);
250 bpy::def("norm", &VectorHelper<double, 4>::norm);
251
252 bpy::def("normalize", &VectorHelper<float, 2>::normalize);
253 bpy::def("normalize", &VectorHelper<double, 2>::normalize);
254 bpy::def("normalize", &VectorHelper<float, 3>::normalize);
255 bpy::def("normalize", &VectorHelper<double, 3>::normalize);
256 bpy::def("normalize", &VectorHelper<float, 4>::normalize);
257 bpy::def("normalize", &VectorHelper<double, 4>::normalize);
258
259 bpy::def("cross", &VectorHelper<float, 3>::cross);
260 bpy::def("cross", &VectorHelper<double, 3>::cross);
261
262#ifdef APPLESEED_ENABLE_IMATH_INTEROP
263 bpy::implicitly_convertible<Vector2i, Imath::V2i>();
264 bpy::implicitly_convertible<Imath::V2i, Vector2i>();
265
266 bpy::implicitly_convertible<Vector2f, Imath::V2f>();
267 bpy::implicitly_convertible<Imath::V2f, Vector2f>();
268
269 bpy::implicitly_convertible<Vector2d, Imath::V2d>();
270 bpy::implicitly_convertible<Imath::V2d, Vector2d>();
271
272 bpy::implicitly_convertible<Vector3i, Imath::V3i>();
273 bpy::implicitly_convertible<Imath::V3i, Vector3i>();
274
275 bpy::implicitly_convertible<Vector3f, Imath::V3f>();
276 bpy::implicitly_convertible<Imath::V3f, Vector3f>();
277
278 bpy::implicitly_convertible<Vector3d, Imath::V3d>();
279 bpy::implicitly_convertible<Imath::V3d, Vector3d>();
280#endif
281}
282