947bf936396436e65fa95854267605afbd9cd673
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / utilities.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Defines various package utilities objects.
6 """
7
8 from __future__ import division
9
10 import itertools
11 import os
12 import re
13 from collections import OrderedDict
14
15 import PyOpenColorIO as ocio
16
17 __author__ = 'ACES Developers'
18 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
19 __license__ = ''
20 __maintainer__ = 'ACES Developers'
21 __email__ = 'aces@oscars.org'
22 __status__ = 'Production'
23
24 __all__ = ['ColorSpace',
25            'mat44_from_mat33',
26            'filter_words',
27            'files_walker',
28            'replace',
29            'sanitize',
30            'compact',
31            'colorspace_prefixed_name',
32            'unpack_default']
33
34
35 class ColorSpace(object):
36     """
37     A container for data needed to define an *OCIO* *ColorSpace*.
38     """
39
40     def __init__(self,
41                  name,
42                  aliases=None,
43                  description=None,
44                  bit_depth=ocio.Constants.BIT_DEPTH_F32,
45                  equality_group='',
46                  family=None,
47                  is_data=False,
48                  to_reference_transforms=None,
49                  from_reference_transforms=None,
50                  allocation_type=ocio.Constants.ALLOCATION_UNIFORM,
51                  allocation_vars=None,
52                  aces_transform_id=None):
53         """
54         Constructor for ColorSpace container class
55
56         Parameters
57         ----------
58         name : str or unicode
59             Name of the colorspace
60         All other arguments are optional
61
62         Returns
63         -------
64         None
65         """
66
67         if aliases is None:
68             aliases = []
69
70         if to_reference_transforms is None:
71             to_reference_transforms = []
72
73         if from_reference_transforms is None:
74             from_reference_transforms = []
75
76         if allocation_vars is None:
77             allocation_vars = [0, 1]
78
79         self.name = name
80         self.aliases = aliases
81         self.bit_depth = bit_depth
82         self.description = description
83         self.equality_group = equality_group
84         self.family = family
85         self.is_data = is_data
86         self.to_reference_transforms = to_reference_transforms
87         self.from_reference_transforms = from_reference_transforms
88         self.allocation_type = allocation_type
89         self.allocation_vars = allocation_vars
90         self.aces_transform_id = aces_transform_id
91
92
93 def mat44_from_mat33(mat33):
94     """
95     Creates a 4x4 matrix from given 3x3 matrix.
96
97     Parameters
98     ----------
99     mat33 : array of float
100         A 3x3 matrix
101
102     Returns
103     -------
104     array of float
105          A 4x4 matrix
106     """
107
108     return [mat33[0], mat33[1], mat33[2], 0,
109             mat33[3], mat33[4], mat33[5], 0,
110             mat33[6], mat33[7], mat33[8], 0,
111             0, 0, 0, 1]
112
113
114 def filter_words(words, filters_in=None, filters_out=None, flags=0):
115     """
116     A function to filter strings in an array
117
118     Parameters
119     ----------
120     words : array of str or unicode
121         Array of strings
122     filters_in : array of str or unicode, optional
123         Words to match
124     filters_out : array of str or unicode, optional
125         Words to NOT match
126     flags : int, optional
127         Flags for re.search
128
129     Returns
130     -------
131     array of str or unicode
132          An array of matched or unmatched strings
133     """
134
135     filtered_words = []
136     for word in words:
137         if filters_in:
138             filter_matched = False
139             for filter in filters_in:
140                 if re.search(filter, word, flags):
141                     filter_matched = True
142                     break
143             if not filter_matched:
144                 continue
145
146         if filters_out:
147             filter_matched = False
148             for filter in filters_out:
149                 if re.search(filter, word, flags):
150                     filter_matched = True
151                     break
152             if filter_matched:
153                 continue
154         filtered_words.append(word)
155     return filtered_words
156
157
158 def files_walker(directory, filters_in=None, filters_out=None, flags=0):
159     """
160     A function to walk a directory hierarchy, only returning items that do or
161     do not match the specified filters
162
163     Parameters
164     ----------
165     directory : str or unicode
166         The starting point for directory walking
167     filters_in : array of str or unicode, optional
168         File or directory names to match
169     filters_out : array of str or unicode, optional
170         File or directory names to NOT match
171     flags : int, optional
172         Flags for re.search
173
174     Returns
175     -------
176     iterable
177          The next matching file or directory name
178     """
179
180     for parent_directory, directories, files in os.walk(
181             directory, topdown=False, followlinks=True):
182         for file in files:
183             path = os.path.join(parent_directory, file)
184             if os.path.isfile(path):
185                 if not filter_words((path,), filters_in, filters_out, flags):
186                     continue
187
188                 yield path
189
190
191 def replace(string, data):
192     """
193     Replaces the data occurrences in the string.
194
195     Parameters
196     ----------
197     string : str or unicode
198         String to manipulate.
199     data : dict
200         Replacement occurrences.
201
202     Returns
203     -------
204     unicode
205         Manipulated string.
206
207     Examples
208     --------
209     >>> patterns = {'John' : 'Luke',
210     ...             'Jane' : 'Anakin',
211     ...             'Doe' : 'Skywalker',
212     ...             'Z6PO' : 'R2D2'}
213     >>> data = 'Users are: John Doe, Jane Doe, Z6PO.'
214     >>> replace(data,patterns )
215     u'Users are: Luke Skywalker, Anakin Skywalker, R2D2.'
216     """
217
218     for old, new in data.iteritems():
219         string = string.replace(old, new)
220     return string
221
222
223 def sanitize(path):
224     """
225     Replaces occurrences of ' ', '(', or ')' in the string with an underscore.
226
227     Parameters
228     ----------
229     path : str or unicode
230         Path string to manipulate.
231
232     Returns
233     -------
234     unicode
235         Manipulated string.
236     """
237
238     return replace(path, {' ': '_', ')': '_', '(': '_'})
239
240
241 def compact(string):
242     """
243     Removes blanks, underscores, dashes and parentheses.
244
245     Parameters
246     ----------
247     string : str or unicode
248         String to compact.
249
250     Returns
251     -------
252     str or unicode
253          A compact version of that string.
254     """
255
256     return replace(string.lower(),
257                    OrderedDict(((' ', '_'),
258                                 ('(', '_'),
259                                 (')', '_'),
260                                 ('.', '_'),
261                                 ('-', '_'),
262                                 ('___', '_'),
263                                 ('__', '_'),
264                                 ('_', ''))))
265
266
267 def colorspace_prefixed_name(colorspace):
268     """
269     Returns given *OCIO* colorspace prefixed name with its family name.
270
271     Parameters
272     ----------
273     colorspace : Colorspace
274         Colorspace to prefix.
275
276     Returns
277     -------
278     str or unicode
279          Family prefixed *OCIO* colorspace name.
280     """
281     prefix = colorspace.family.replace('/', ' - ')
282
283     return '%s - %s' % (prefix, colorspace.name)
284
285
286 def unpack_default(iterable, length=3, default=None):
287     """
288     Unpacks given iterable maintaining given length and filling missing
289     entries with given default.
290
291     Parameters
292     ----------
293     iterable : object
294         Iterable.
295     length : int
296         Iterable length.
297     default : object
298         Filling default object.
299
300     Returns
301     -------
302     iterable
303     """
304
305     return itertools.islice(
306         itertools.chain(iter(iterable), itertools.repeat(default)), length)