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 "dict2dict.h"
32
33// appleseed.renderer headers.
34#include "renderer/utility/paramarray.h"
35
36// appleseed.foundation headers.
37#include "foundation/math/vector.h"
38#include "foundation/utility/api/specializedapiarrays.h"
39#include "foundation/utility/containers/dictionary.h"
40#include "foundation/utility/foreach.h"
41#include "foundation/utility/iostreamop.h"
42#include "foundation/utility/string.h"
43
44namespace bpy = boost::python;
45using namespace foundation;
46using namespace renderer;
47using namespace std;
48
49namespace
50{
51 ParamArray& push_dict(ParamArray& dict, const char* name)
52 {
53 return dict.push(name);
54 }
55
56 Dictionary& push_dict(Dictionary& dict, const char* name)
57 {
58 if (!dict.dictionaries().exist(name))
59 dict.dictionaries().insert(name, Dictionary());
60
61 return dict.dictionaries().get(name);
62 }
63
64 template <class Dict>
65 Dict convert_from_bpy_dict(const bpy::dict& d)
66 {
67 Dict result;
68
69 bpy::list values = d.values();
70 bpy::list keys = d.keys();
71
72 for (bpy::ssize_t i = 0, e = bpy::len(d); i < e; ++i)
73 {
74 bpy::object key(keys[i]);
75 bpy::object value(values[i]);
76
77 // keys
78 bpy::extract<const char*> key_extractor(key);
79 if (!key_extractor.check())
80 {
81 PyErr_SetString(PyExc_TypeError, "Incompatible key type. Only strings accepted.");
82 bpy::throw_error_already_set();
83 }
84
85 // string
86 {
87 bpy::extract<const char*> extractor(value);
88 if (extractor.check())
89 {
90 result.insert(key_extractor(), extractor());
91 continue;
92 }
93 }
94
95 if (PyBool_Check(value.ptr()))
96 {
97 bpy::extract<bool> extractor(value);
98 if (extractor.check())
99 {
100 result.insert(key_extractor(), extractor());
101 continue;
102 }
103 }
104
105#if PY_MAJOR_VERSION == 2
106 if (PyInt_Check(value.ptr()))
107 {
108 bpy::extract<int> extractor(value);
109 if (extractor.check())
110 {
111 result.insert(key_extractor(), extractor());
112 continue;
113 }
114 }
115#endif
116
117 if (PyLong_Check(value.ptr()))
118 {
119 bpy::extract<int> extractor(value);
120 if (extractor.check())
121 {
122 result.insert(key_extractor(), extractor());
123 continue;
124 }
125 }
126
127 if (PyFloat_Check(value.ptr()))
128 {
129 bpy::extract<double> extractor(value);
130 if (extractor.check())
131 {
132 result.insert(key_extractor(), extractor());
133 continue;
134 }
135 }
136
137 // Vector2i
138 {
139 bpy::extract<Vector2i> extractor(value);
140 if (extractor.check())
141 {
142 result.insert(key_extractor(), extractor());
143 continue;
144 }
145 }
146
147 // Vector2f
148 {
149 bpy::extract<Vector2f> extractor(value);
150 if (extractor.check())
151 {
152 result.insert(key_extractor(), extractor());
153 continue;
154 }
155 }
156
157 // Vector2d
158 {
159 bpy::extract<Vector2d> extractor(value);
160 if (extractor.check())
161 {
162 result.insert(key_extractor(), extractor());
163 continue;
164 }
165 }
166
167 // todo: add conversions from bpy::tuple to Vector<T, N>.
168 // ...
169
170 // todo: check more types here if needed...
171
172 // dict
173 {
174 bpy::extract<bpy::dict> extractor(value);
175 if (extractor.check())
176 {
177 // recurse
178 push_dict(result, key_extractor()) = convert_from_bpy_dict<Dict>(extractor());
179 continue;
180 }
181 }
182
183 PyErr_SetString(PyExc_TypeError, "Incompatible value type - must be string, int, double or dictionary.");
184 bpy::throw_error_already_set();
185 }
186
187 return result;
188 }
189
190 bpy::object obj_from_string(const string& str)
191 {
192 // Try to guess the type of the value represented by str.
193
194 try // Vector3
195 {
196 Vector3d v = from_string<Vector3d>(str);
197 return bpy::object(v);
198 }
199 catch (ExceptionStringConversionError&) {}
200
201 try // Vector2
202 {
203 Vector2d v = from_string<Vector2d>(str);
204 return bpy::object(v);
205 }
206 catch (ExceptionStringConversionError&) {}
207
208 try // int / double
209 {
210 double d = from_string<double>(str);
211 return bpy::object(d);
212 }
213 catch (ExceptionStringConversionError&) {}
214
215 try // bool
216 {
217 bool b = from_string<bool>(str);
218 return bpy::object(b);
219 }
220 catch (ExceptionStringConversionError&) {}
221
222 // todo: check more types here if needed...
223
224 // As a fallback, return a string.
225 return bpy::object(str);
226 }
227}
228
229Dictionary bpy_dict_to_dictionary(const bpy::dict& d)
230{
231 return convert_from_bpy_dict<Dictionary>(d);
232}
233
234bpy::dict dictionary_to_bpy_dict(const Dictionary& dict)
235{
236 bpy::dict result;
237
238 for (const_each<StringDictionary> it = dict.strings(); it; ++it)
239 result[it->key()] = obj_from_string(it->value());
240
241 // Recurse into subdictionaries.
242 for (const_each<DictionaryDictionary> it = dict.dictionaries(); it; ++it)
243 result[it->key()] = dictionary_to_bpy_dict(it->value());
244
245 return result;
246}
247
248ParamArray bpy_dict_to_param_array(const bpy::dict& d)
249{
250 return convert_from_bpy_dict<ParamArray>(d);
251}
252
253bpy::dict param_array_to_bpy_dict(const ParamArray& array)
254{
255 return dictionary_to_bpy_dict(array);
256}
257
258bpy::dict dictionary_array_to_bpy_dict(
259 const DictionaryArray& array,
260 const char* key)
261{
262 bpy::dict dictionaries;
263
264 for (size_t i = 0, e = array.size(); i < e; ++i)
265 {
266 bpy::dict d(dictionary_to_bpy_dict(array[i]));
267 dictionaries[d[key]] = d;
268 }
269
270 return dictionaries;
271}
272