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#include "unalignedmatrix44.h"
33
34// Standard headers.
35#include <cstddef>
36#include <memory>
37
38namespace bpy = boost::python;
39using namespace foundation;
40using namespace std;
41
42namespace
43{
44 template <typename T>
45 UnalignedMatrix44<T>* construct_matrix_from_list(bpy::list l)
46 {
47 if (bpy::len(l) != 4 * 4)
48 {
49 PyErr_SetString(PyExc_RuntimeError, "Invalid list length given to appleseed.Matrix.__init__");
50 bpy::throw_error_already_set();
51 }
52
53 auto_ptr<UnalignedMatrix44<T> > r(new UnalignedMatrix44<T>());
54
55 for (size_t i = 0; i < 4 * 4; ++i)
56 {
57 bpy::extract<T> ex(l[i]);
58 if (!ex.check())
59 {
60 PyErr_SetString(PyExc_TypeError, "Incompatible type.");
61 bpy::throw_error_already_set();
62 }
63
64 (*r)[i] = ex();
65 }
66
67 return r.release();
68 }
69
70 template <typename T>
71 struct MatrixIndexer
72 {
73 static T get(const UnalignedMatrix44<T>& mat, bpy::tuple indices)
74 {
75 if (bpy::len(indices) != 2)
76 {
77 PyErr_SetString(PyExc_RuntimeError, "Invalid tuple length given to appleseed.Matrix.__get_item__");
78 bpy::throw_error_already_set();
79 }
80
81 int i = 0, j = 0;
82
83 bpy::extract<int> ex0(indices[0]);
84 if (!ex0.check())
85 {
86 PyErr_SetString(PyExc_TypeError, "Incompatible index type. Only ints.");
87 bpy::throw_error_already_set();
88 }
89 else
90 i = ex0();
91
92 bpy::extract<int> ex1(indices[1]);
93 if (!ex1.check())
94 {
95 PyErr_SetString(PyExc_TypeError, "Incompatible index type. Only ints.");
96 bpy::throw_error_already_set();
97 }
98 else
99 j = ex1();
100
101 if (i < 0)
102 i = 4 + i;
103
104 if (j < 0)
105 j = 4 + j;
106
107 if (i >= 0 && i < 4 && j >= 0 && j < 4)
108 return mat(i, j);
109
110 PyErr_SetString(PyExc_IndexError, "Out of bounds access in appleseed.Matrix.__get_item__");
111 boost::python::throw_error_already_set();
112 return T();
113 }
114
115 static void set(UnalignedMatrix44<T>& mat, bpy::tuple indices, const T v)
116 {
117 if (bpy::len(indices) != 2)
118 {
119 PyErr_SetString(PyExc_RuntimeError, "Invalid tuple length given to appleseed.Matrix.__set_item__");
120 bpy::throw_error_already_set();
121 }
122
123 int i = 0, j = 0;
124
125 bpy::extract<int> ex0(indices[0]);
126 if (!ex0.check())
127 {
128 PyErr_SetString(PyExc_TypeError, "Incompatible index type. Only ints.");
129 bpy::throw_error_already_set();
130 }
131 else
132 i = ex0();
133
134 bpy::extract<int> ex1(indices[1]);
135 if (!ex1.check())
136 {
137 PyErr_SetString(PyExc_TypeError, "Incompatible index type. Only ints.");
138 bpy::throw_error_already_set();
139 }
140 else
141 j = ex1();
142
143 if (i < 0)
144 i = 4 + i;
145
146 if (j < 0)
147 j = 4 + j;
148
149 if (i >= 0 && i < 4 && j >= 0 && j < 4)
150 mat(i, j) = v;
151 else
152 {
153 PyErr_SetString(PyExc_IndexError, "Out of bounds access in appleseed.Matrix.__set_item__");
154 boost::python::throw_error_already_set();
155 }
156 }
157 };
158
159 template <typename T>
160 UnalignedMatrix44<T> transpose_matrix(const UnalignedMatrix44<T>& mat)
161 {
162 return UnalignedMatrix44<T>(transpose(mat.as_foundation_matrix()));
163 }
164
165 template <typename T>
166 bpy::tuple matrix_extract_euler_angles(const UnalignedMatrix44<T>& mat)
167 {
168 T yaw, pitch, roll;
169 mat.as_foundation_matrix().extract_euler_angles(yaw, pitch, roll);
170 return bpy::make_tuple(yaw, pitch, roll);
171 }
172
173 void bind_typed_matrix4_extra(bpy::class_<UnalignedMatrix44<float> >& X)
174 {
175 X.def(bpy::init<UnalignedMatrix44<double> >());
176 }
177
178 void bind_typed_matrix4_extra(bpy::class_<UnalignedMatrix44<double> >& X)
179 {
180 X.def(bpy::init<UnalignedMatrix44<float> >());
181 }
182
183 template <typename T>
184 void bind_typed_matrix4(const char* class_name)
185 {
186 UnalignedMatrix44<T>(*rot1)(T, T, T) = &UnalignedMatrix44<T>::make_rotation;
187 UnalignedMatrix44<T>(*rot2)(const Vector<T, 3>&, T) = &UnalignedMatrix44<T>::make_rotation;
188 UnalignedMatrix44<T>(*rot3)(const Quaternion<T>&) = &UnalignedMatrix44<T>::make_rotation;
189
190 bpy::class_<UnalignedMatrix44<T> > X(class_name);
191
192 X.def("identity", &UnalignedMatrix44<T>::identity).staticmethod("identity")
193 .def("make_translation", &UnalignedMatrix44<T>::make_translation).staticmethod("make_translation")
194 .def("make_scaling", &UnalignedMatrix44<T>::make_scaling).staticmethod("make_scaling")
195 .def("make_rotation_x", &UnalignedMatrix44<T>::make_rotation_x).staticmethod("make_rotation_x")
196 .def("make_rotation_y", &UnalignedMatrix44<T>::make_rotation_y).staticmethod("make_rotation_y")
197 .def("make_rotation_z", &UnalignedMatrix44<T>::make_rotation_z).staticmethod("make_rotation_z")
198 .def("make_lookat", &UnalignedMatrix44<T>::make_lookat).staticmethod("make_lookat")
199 .def("make_rotation", rot1).def("make_rotation", rot2).def("make_rotation", rot3).staticmethod("make_rotation")
200
201 .def(bpy::init<T>())
202 .def("__init__", bpy::make_constructor(&construct_matrix_from_list<T>))
203
204 // operator[]
205 .def("__getitem__", &MatrixIndexer<T>::get)
206 .def("__setitem__", &MatrixIndexer<T>::set)
207
208 .def("transpose", &transpose_matrix<T>)
209 .def("inverse", &invert_matrix<T>)
210
211 .def(bpy::self * bpy::self)
212 .def(bpy::self * Vector<T, 4>())
213
214 // Because of a bug in Boost.Python, this needs the extra self_ns qualification.
215 .def(bpy::self_ns::str(bpy::self))
216 .def(bpy::self_ns::repr(bpy::self))
217
218 .def("extract_matrix3", &UnalignedMatrix44<T>::extract_matrix3)
219 .def("extract_translation", &UnalignedMatrix44<T>::extract_translation);
220
221 bind_typed_matrix4_extra(X);
222 }
223}
224
225void bind_matrix()
226{
227 bind_typed_matrix4<float>("Matrix4f");
228 bind_typed_matrix4<double>("Matrix4d");
229
230#ifdef APPLESEED_ENABLE_IMATH_INTEROP
231 bpy::implicitly_convertible<UnalignedMatrix44<float>, Imath::M44f>();
232 bpy::implicitly_convertible<Imath::M44f, UnalignedMatrix44<float> >();
233
234 bpy::implicitly_convertible<UnalignedMatrix44<double>, Imath::M44d>();
235 bpy::implicitly_convertible<Imath::M44d, UnalignedMatrix44<double> >();
236#endif
237}
238