Initial revision
[blender.git] / intern / python / modules / vrml / fieldcoercian.py
1 '''
2 Field coercian routines.
3
4 To replace the field coercian routines, you must edit
5 basenodes.py and node.py to import some other coercian
6 routines.  Basenodes.py is for use by the parser, node
7 is used by each node as it checks the validity of its
8 attributes.
9 '''
10
11 import types, sys, string
12 from utils import typeclasses, collapse
13
14 class FieldCoercian:
15         '''
16         A Field Coercian class allows for creating new behaviours
17         when dealing with the conversion of fields to-and-from
18         particular field types.  This allows the programmer to
19         use alternate representations of fields (such as matrix arrays)
20         '''
21         def SFString( self, someobj, targetType=types.StringType, targetName='SFString', convertfunc=str ):
22                 '''
23                 Allowable types:
24                         simple string -> unchanged
25                         instance ( an IS ) -> unchanged
26                         sequence of length == 1 where first element is a string -> returns first element
27                         sequence of length > 1 where all elements are strings -> returns string.join( someobj, '')
28                 '''
29                 t = type(someobj)
30                 if t is targetType:
31                         return someobj
32                 if t in typeclasses.SequenceTypes:
33                         if len( someobj) == 1 and type( someobj[0] ) is targetType:
34                                 return someobj[0] # 
35                         elif len(someobj) > 1:
36                                 try:
37                                         return string.join( someobj, '')
38                                 except:
39                                         pass # is not a sequence of strings...
40                 ### if we get here, then an incorrect value was passed
41                 raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
42                 
43         def MFString( self, someobj, targetType=types.StringType, targetName='SFString', convertfunc=str ):
44                 '''
45                 Allowable Types:
46                         simple string -> wrapped in a list
47                         instance (an IS ) -> unchanged
48                         sequence of strings (of any length) -> equivalent list returned
49                 '''
50                 t = type(someobj)
51                 if t is targetType: # a bare string...
52                         return [someobj]
53                 elif t in typeclasses.SequenceTypes: # is a sequence
54                         if not filter( lambda x, t=targetType: x is not t, map( type, someobj) ): # are all strings...
55                                 if t is not types.ListType:
56                                         return list( someobj )
57                                 else:
58                                         return someobj
59                 ### if we get here, then an incorrect value was passed
60                 raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
61
62         def SFBool( self, someobj, targetType=types.IntType, targetName='SFBool', convertfunc=int):
63                 '''
64                 Allowable Types:
65                         instance (an IS) -> unchanged
66                         Any object which is testable for truth/falsehood -> 1 or 0 respectively
67                 SFBool should always succeed
68                 '''
69                 if (type(someobj) in typeclasses.SequenceTypes):
70                         try:
71                                 if hasattr( someobj[0], '__gi__'):
72                                         return someobj[0]
73                                 else:
74                                         someobj = someobj[0]
75                         except IndexError: # is a null MFNode
76                                 pass
77                 if someobj:
78                         return 1
79                 else:
80                         return 0
81                         
82         def SFNode( self, someobj, targetType=types.InstanceType, targetName='SFNode', convertfunc=None):
83                 '''
84                 Allowable Types:
85                         instance of a Node -> unchanged
86                         instance (an IS or USE) -> unchanged
87                         sequence of length == 1 where first element is as above -> return first element
88                 '''
89                 if hasattr( someobj, '__gi__'): # about the only test I have without requiring that elements inherit from Node
90                         return someobj
91                 elif (type(someobj) in typeclasses.SequenceTypes):
92                         try:
93                                 if hasattr( someobj[0], '__gi__'):
94                                         return someobj[0]
95                         except IndexError: # is a null MFNode
96                                 pass
97                 raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
98                 
99         def MFNode( self, someobj, targetType=types.InstanceType, targetName='MFNode', convertfunc=None):
100                 '''
101                 Allowable Types:
102                         instance (an IS) -> unchanged
103                         instance of a Node -> wrapped with a list
104                         sequence where all elements are nodes -> returned as list of same
105                 '''
106                 if hasattr( someobj, '__gi__') and someobj.__gi__ != "IS":
107                         # is this a bare SFNode? wrap with a list and return
108                         return [someobj]
109                 elif hasattr( someobj, "__gi__"): # is this an IS node
110                         return someobj
111                 elif type(someobj) in typeclasses.SequenceTypes:
112                         try:
113                                 map( getattr, someobj, ['__gi__']*len(someobj) )
114                                 # is this an IS node wrapped in a list?
115                                 if len(someobj) == 1 and someobj[0].__gi__ == "IS":
116                                         return someobj[0]
117                                 # okay, assume is really nodes...
118                                 if type(someobj) is types.ListType:
119                                         return someobj
120                                 else:
121                                         return list(someobj)
122                         except AttributeError: # something isn't a node
123                                 pass
124                 raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
125
126         def SFNumber( self, someobj, targetType, targetName, convertfunc=int ):
127                 '''
128                 Allowable Types:
129                         bare number -> numerically coerced to correct type
130                         instance ( an IS ) -> unchanged
131                         sequence of length == 1 where first element is a string -> returns first element
132                 '''
133                 t = type(someobj)
134                 if t is targetType or t is types.InstanceType:
135                         return someobj
136                 elif t in typeclasses.NumericTypes:
137                         return convertfunc( someobj)
138                 elif t in typeclasses.SequenceTypes:
139                         if len( someobj) == 1 and type( someobj[0] ):
140                                 return convertfunc( someobj[0] ) # 
141                 ### if we get here, then an incorrect value was passed
142                 raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
143         def MFInt32 ( self, someobject ):
144                 ''' Convert value into a MFInt32 field value (preferably an array, otherwise a list of integers) '''
145                 t = type(someobject)
146                 value = None
147                 if t in typeclasses.SequenceTypes: # is a sequence
148                         try:
149                                 value = map( int, someobject)
150                         except:
151                                 try:
152                                         value = map( int, collapse.collapse2_safe( someobject) )
153                                 except:
154                                         pass
155                 elif t in typeclasses.NumericTypes or t is types.StringType:
156                         value = [int(someobject)]
157                 if value is None:
158                         ### if we get here, then an incorrect value was passed
159                         raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
160                 return value
161         SFImage = MFInt32
162         def MFFloat( self, someobject ):
163                 ''' Convert value into a MFFloat field value (preferably an array, otherwise a list of integers) '''
164                 t = type(someobject)
165                 value = None
166                 if t in typeclasses.SequenceTypes: # is a sequence
167                         try:
168                                 value = map( float, someobject)
169                         except:
170                                 try:
171                                         value = map( float, collapse.collapse2_safe( someobject))
172                                 except:
173                                         pass
174                 elif t in typeclasses.NumericTypes or t is types.StringType:
175                         value = [float(someobj)]
176                 if value is None:
177                         ### if we get here, then an incorrect value was passed
178                         raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
179                 return value
180         def SFVec3f (self,  value):
181                 ''' Create a new SFVec3f value from value '''
182                 t = type(value)
183                 try:
184                         value = x,y,z = map (float, value)
185                 except ValueError:
186                         try:
187                                 value =  (x,y,z) = map( float, value[0] )
188                         except (IndexError, ValueError):
189                                 raise ValueError (''' Invalid value for field type SFVec3f: %s'''%(value))
190                 return value
191         def SFRotation(self,  value):
192                 ''' Create a new SFRotation value from value '''
193                 t = type(value)
194                 try:
195                         value = x,y,z, a = map (float, value)
196                 except ValueError:
197                         try:
198                                 value =  (x,y,z, a) = map( float, value[0] )
199                         except (IndexError, ValueError):
200                                 raise ValueError (''' Invalid value for field type SFRotation: %s'''%(value))
201                 # get the normalized vector for x,y,z
202 ##              length = (x*x+y*y+z*z)**.5 or 0.0000
203 ##              value = (x/length,y/length,z/length, a)
204                 return value
205         def SFVec2f (self,  value):
206                 ''' Create a new SFVec3f value from value '''
207                 t = type(value)
208                 try:
209                         value = x,y = map (float, value)
210                 except ValueError:
211                         try:
212                                 value =  (x,y) = map( float, value[0] )
213                         except (IndexError, ValueError):
214                                 raise ValueError (''' Invalid value for field type SFVec3f: %s'''%(value))
215                 return value
216         def SFColor(self,  value):
217                 ''' Create a new SFVec3f value from value '''
218                 t = type(value)
219                 try:
220                         r,g,b = map (float, value)
221                 except ValueError:
222                         try:
223                                 r,g,b = map( float, value[0] )
224                         except (IndexError, ValueError):
225                                 raise ValueError (''' Invalid value for field type SFColor: %s'''%(value))
226                 r = max( (0.0, min((r,1.0))) )
227                 g = max( (0.0, min((g,1.0))) )
228                 b = max( (0.0, min((b,1.0))) )
229                 return value
230                 
231         def MFCompoundNumber( self, someobj, targetName='SFVec3f', convertfunc=float, type=type):
232                 '''
233                 Allowable Types:
234                         instance ( an IS ) -> unchanged
235                         # instance ( a matrix ) -> reshaped (eventually)
236                         list of lists, sub-sequences of proper length -> unchanged
237                         sequence of numeric types of proper length -> converted to list, diced
238                 '''
239 ##              if targetName == 'SFColor':
240 ##                      import pdb
241 ##                      pdb.set_trace()
242                 converter = getattr( self, targetName )
243                 t = type( someobj)
244                 reporterror = 0
245                 if t is types.InstanceType:
246                         return someobj
247                 elif t in typeclasses.SequenceTypes:
248                         if not someobj:
249                                 return []
250                         if type( someobj[0] ) is not types.StringType and type( someobj[0] ) in typeclasses.SequenceTypes:
251                                 try:
252                                         return map( converter, someobj )
253                                 except ValueError:
254                                         pass
255                         elif type( someobj[0] ) in typeclasses.NumericTypes or type( someobj[0] ) is types.StringType:
256                                 # a single-level list?
257                                 base = map( convertfunc, someobj )
258                                 # if we get here, someobj is a list
259                                 if targetName[-2:] == '2f': # vec2f
260                                         tlen = 2
261                                 elif targetName[-2:] == 'on': # rotation
262                                         tlen = 4
263                                 else:
264                                         tlen = 3
265                                 value = []
266                                 while base:
267                                         value.append( converter( base[:tlen]) )
268                                         del base[:tlen]
269                                 return value
270                 raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
271         def __call__( self, someobj, targetName):
272                 func, args = self.algomap[targetName]
273 ##              try:
274 ##                      if targetName == 'SFInt32':
275 ##                              import pdb
276 ##                              pdb.set_trace()
277                 if hasattr( someobj, "__gi__") and someobj.__gi__ == "IS":
278                         return someobj
279                 else:
280                         return apply( func, (self, someobj)+args )
281 ##              except TypeError:
282 ##                      print someobj, targetName
283 ##                      print func, args
284 ##                      raise
285
286         algomap = { \
287                 'SFString': (SFString, (types.StringType, 'SFString', str)), \
288                 'MFString': (MFString, (types.StringType, 'MFString', str)), \
289                 'SFInt32': (SFNumber, (types.IntType, 'SFInt32', int)), \
290                 'SFFloat': (SFNumber, (types.FloatType, 'SFFloat', float)), \
291                 'SFTime': (SFNumber, (types.FloatType, 'SFFloat', float)), \
292                 'SFColor': (SFColor, ()), \
293                 'SFVec2f': (SFVec2f, ()), \
294                 'SFVec3f': (SFVec3f, ()), \
295                 'SFNode': (SFNode, (types.InstanceType, 'SFNode', None)), \
296                 'SFBool': (SFBool, (types.IntType, 'SFBool', int)), \
297                 'SFNode': (SFNode, (types.InstanceType, 'SFNode', None)), \
298                 'MFInt32': (MFInt32, ()), \
299                 'SFImage': (MFInt32, ()), \
300                 'MFTime': (MFFloat, ()), \
301                 'MFFloat': (MFFloat, ()), \
302                 'MFColor': (MFCompoundNumber, ('SFColor', float)), \
303                 'MFVec2f': (MFCompoundNumber, ('SFVec2f', float)), \
304                 'MFVec3f': (MFCompoundNumber, ('SFVec3f', float)), \
305                 'SFRotation': (SFRotation, ()), \
306                 'MFRotation': (MFCompoundNumber, ('SFRotation', float)), \
307                 'MFNode': (MFNode, (types.InstanceType, 'MFNode', None)) \
308         }
309           
310 FIELDCOERCE = FieldCoercian ()