PyVRML97 2.3.4b3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyvrml97-2.3.4b3.dist-info/METADATA +65 -0
- pyvrml97-2.3.4b3.dist-info/RECORD +39 -0
- pyvrml97-2.3.4b3.dist-info/WHEEL +5 -0
- pyvrml97-2.3.4b3.dist-info/top_level.txt +1 -0
- vrml/__init__.py +22 -0
- vrml/_bytes.py +85 -0
- vrml/arrays.py +86 -0
- vrml/cache.py +250 -0
- vrml/copier.py +27 -0
- vrml/csscolors.py +177 -0
- vrml/event.py +24 -0
- vrml/field.py +476 -0
- vrml/fieldtypes.py +1491 -0
- vrml/node.py +569 -0
- vrml/nodepath.py +70 -0
- vrml/olist.py +126 -0
- vrml/protofunctions.py +233 -0
- vrml/protonamespace.py +13 -0
- vrml/route.py +167 -0
- vrml/vrml200x/__init__.py +0 -0
- vrml/vrml200x/parser.py +87 -0
- vrml/vrml97/__init__.py +6 -0
- vrml/vrml97/_transformmatrix.py +133 -0
- vrml/vrml97/_transformmatrix_accel.py +63 -0
- vrml/vrml97/basenamespaces.py +28 -0
- vrml/vrml97/basenodes.py +648 -0
- vrml/vrml97/linearise.py +579 -0
- vrml/vrml97/nodepath.py +135 -0
- vrml/vrml97/nodetypes.py +95 -0
- vrml/vrml97/nurbs.py +79 -0
- vrml/vrml97/parseprocessor.py +452 -0
- vrml/vrml97/parser.py +72 -0
- vrml/vrml97/scenegraph.py +181 -0
- vrml/vrml97/script.py +25 -0
- vrml/vrml97/shaders.py +123 -0
- vrml/vrml97/transformmatrix.py +193 -0
- vrml/weakkeydictfix.py +36 -0
- vrml/weaklist.py +163 -0
- vrml/weaktuple.py +138 -0
vrml/vrml97/linearise.py
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
"""object for linearizing a scene graph to VRML97
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
from __future__ import unicode_literals
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from cStringIO import StringIO
|
|
8
|
+
except ImportError:
|
|
9
|
+
from io import StringIO
|
|
10
|
+
from vrml import arrays
|
|
11
|
+
from vrml.protofunctions import *
|
|
12
|
+
from vrml import node
|
|
13
|
+
|
|
14
|
+
defaults = {
|
|
15
|
+
'subelspacer': ', ',
|
|
16
|
+
'courtesyspace': ' ',
|
|
17
|
+
'curindent': '',
|
|
18
|
+
'indent': '\t',
|
|
19
|
+
'numsep': ',',
|
|
20
|
+
'full_element_separator': '\n',
|
|
21
|
+
'mffieldsep': '\n',
|
|
22
|
+
# EndComments say: if more than this many lines are between node start
|
|
23
|
+
# and end, put a comment at the closing bracket saying what node/proto is
|
|
24
|
+
# being closed.
|
|
25
|
+
'EndComments': 10,
|
|
26
|
+
}
|
|
27
|
+
minimal1 = {
|
|
28
|
+
'subelspacer': ', ',
|
|
29
|
+
'numsep': ' ',
|
|
30
|
+
'curindent': '',
|
|
31
|
+
'indent': ' ',
|
|
32
|
+
'full_element_separator': '\n',
|
|
33
|
+
'courtesyspace': '',
|
|
34
|
+
'mffieldsep': ' ',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def namekey(node):
|
|
39
|
+
return node.name
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def linearise(value, linvalues=defaults, **namedargs):
|
|
43
|
+
"""Linearise the given (node) value to a string"""
|
|
44
|
+
l = Lineariser(linvalues, **namedargs)
|
|
45
|
+
return l.linear(value)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Lineariser:
|
|
49
|
+
'''
|
|
50
|
+
A data structure & methods for linearising
|
|
51
|
+
sceneGraphs, nodes, scripts and prototypes.
|
|
52
|
+
Should be used only once as member vars are not
|
|
53
|
+
cleared after the initial linearisation.
|
|
54
|
+
'''
|
|
55
|
+
|
|
56
|
+
def __init__(self, linvalues=None, alreadydone=None, *args, **namedargs):
|
|
57
|
+
if linvalues is None:
|
|
58
|
+
linvalues = defaults
|
|
59
|
+
if namedargs:
|
|
60
|
+
linvalues = linvalues.copy()
|
|
61
|
+
linvalues.update(namedargs)
|
|
62
|
+
self.linvalues = linvalues
|
|
63
|
+
if alreadydone is None:
|
|
64
|
+
self.alreadydone = {}
|
|
65
|
+
else:
|
|
66
|
+
self.alreadydone = alreadydone
|
|
67
|
+
|
|
68
|
+
def linear(
|
|
69
|
+
self,
|
|
70
|
+
clientNode,
|
|
71
|
+
buffer=None,
|
|
72
|
+
skipProtos=None,
|
|
73
|
+
skipUnusedProtos=None,
|
|
74
|
+
*args,
|
|
75
|
+
**namedargs
|
|
76
|
+
):
|
|
77
|
+
'''
|
|
78
|
+
Linearise a node, script, or scenegraph
|
|
79
|
+
'''
|
|
80
|
+
# prototypes in this dictionary will not be linearised
|
|
81
|
+
self.skipProtos = {}
|
|
82
|
+
# skipUnusedProtos skips the "prototype collection" linearisation step
|
|
83
|
+
# this has the effect of not outputing any prototype which is not actually
|
|
84
|
+
# used in the file. By default is "off", that is, all protos are linearised
|
|
85
|
+
self.skipUnusedProtos = skipUnusedProtos
|
|
86
|
+
# protobuffer is a seperate buffer into which the prototype definitions are stored
|
|
87
|
+
self.protobuffer = StringIO()
|
|
88
|
+
self.protobuffer.write('#VRML V2.0 utf8\n')
|
|
89
|
+
# protoalreadydone is used in place of the scenegraph-specific
|
|
90
|
+
# node alreadydone. This allows us to push all protos up to the
|
|
91
|
+
# top level of the hierarchy (thus making the process of linearisation much simpler)
|
|
92
|
+
self.protoalreadydone = {}
|
|
93
|
+
# main working algo...
|
|
94
|
+
self.typecache = {
|
|
95
|
+
'Script': self._Script,
|
|
96
|
+
'NULL': self._nullNode,
|
|
97
|
+
'sceneGraph': self._sceneGraph,
|
|
98
|
+
}
|
|
99
|
+
self.buffer = buffer or StringIO()
|
|
100
|
+
self.alreadydone.clear()
|
|
101
|
+
self.cursceneGraph = (
|
|
102
|
+
[]
|
|
103
|
+
) # used to look up whether we need to output a prototype...
|
|
104
|
+
self.curproto = []
|
|
105
|
+
self.indentationlevel = 0
|
|
106
|
+
if type(clientNode) in (list, tuple):
|
|
107
|
+
for child in clientNode:
|
|
108
|
+
self._linear(child)
|
|
109
|
+
self.buffer.write('\n')
|
|
110
|
+
else:
|
|
111
|
+
self._linear(clientNode)
|
|
112
|
+
del self.typecache # to clear references to this node...
|
|
113
|
+
self.alreadydone.clear()
|
|
114
|
+
# side effect has filled up protobuffer for us
|
|
115
|
+
rval = self.protobuffer.getvalue() + self.buffer.getvalue()
|
|
116
|
+
self.buffer.close()
|
|
117
|
+
self.protobuffer.close()
|
|
118
|
+
return rval
|
|
119
|
+
|
|
120
|
+
### High-level constructs...
|
|
121
|
+
def _sceneGraph(self, clientNode):
|
|
122
|
+
'''
|
|
123
|
+
A little niave, the sceneGraph just outputs everything
|
|
124
|
+
in its prototypes, then everything in its childlist, then all its ROUTES
|
|
125
|
+
'''
|
|
126
|
+
[self._preroute(clientNode, route) for route in clientNode.routes]
|
|
127
|
+
if clientNode is None:
|
|
128
|
+
startind = self.buffer.tell()
|
|
129
|
+
if len(self.cursceneGraph) == 0: # new file
|
|
130
|
+
self.buffer.write('#VRML V2.0 utf8\n')
|
|
131
|
+
self.alreadydone[id(clientNode)] = (
|
|
132
|
+
startind,
|
|
133
|
+
self.buffer.tell(),
|
|
134
|
+
) # register this scenegraph's extents
|
|
135
|
+
return None
|
|
136
|
+
startind = self._canUse(clientNode)
|
|
137
|
+
if type(startind) != int: # this node has already been declared
|
|
138
|
+
self.buffer.write(startind)
|
|
139
|
+
return None
|
|
140
|
+
# use a seperate Node alreadydone for each sceneGraph
|
|
141
|
+
# so that we don't have cross-barrier USEs occuring
|
|
142
|
+
oldalreadydone = self.alreadydone
|
|
143
|
+
self.alreadydone = {}
|
|
144
|
+
|
|
145
|
+
# localise buffer as we will be accessing it many times
|
|
146
|
+
buffer = self.buffer
|
|
147
|
+
# header now part of the protobuffer
|
|
148
|
+
# if not self.cursceneGraph: # top level
|
|
149
|
+
# buffer.write( '#VRML V2.0 utf8\n' ) # should only do this when it's the top-level object
|
|
150
|
+
self.cursceneGraph.append(clientNode)
|
|
151
|
+
|
|
152
|
+
# linearise the "free" prototypes (any actually registered with the sceneGraph)
|
|
153
|
+
if (
|
|
154
|
+
not self.skipUnusedProtos
|
|
155
|
+
): # are supposed to linearise all prototypes currently in the sceneGraph, regardless of whether they are used
|
|
156
|
+
for proto in clientNode.protoTypes.values():
|
|
157
|
+
if not id(proto) in self.protoalreadydone:
|
|
158
|
+
self._proto(proto)
|
|
159
|
+
# linearise the node/script children, they will include their prototypes if they are not already done
|
|
160
|
+
for child in clientNode.children:
|
|
161
|
+
self._linear(child)
|
|
162
|
+
buffer.write(self.linvalues['full_element_separator'])
|
|
163
|
+
# linearise the routes
|
|
164
|
+
for route in clientNode.routes:
|
|
165
|
+
# should check here to make sure the ROUTEs are valid
|
|
166
|
+
self._route(route)
|
|
167
|
+
# buffer.write( '%(full_element_separator)sROUTE %%s.%%s TO %%s.%%s'%self.linvalues%route )
|
|
168
|
+
|
|
169
|
+
# restore original alreadydone dictionary
|
|
170
|
+
self.alreadydone = oldalreadydone
|
|
171
|
+
self.alreadydone[id(clientNode)] = (
|
|
172
|
+
startind,
|
|
173
|
+
buffer.tell(),
|
|
174
|
+
) # register this scenegraph's extents
|
|
175
|
+
del self.cursceneGraph[-1]
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
def _proto(self, clientNode):
|
|
179
|
+
"""Linearise a prototype, return whether the prototype is actually linearised"""
|
|
180
|
+
# check that we haven't yet done this prototype, register the fact that we've already started it
|
|
181
|
+
if type(clientNode) != type:
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
clientName = protoName(clientNode)
|
|
185
|
+
|
|
186
|
+
if builtin(clientNode):
|
|
187
|
+
# this prototype should not be linearised
|
|
188
|
+
return 0
|
|
189
|
+
if id(clientNode) in self.protoalreadydone:
|
|
190
|
+
# this precise prototype has been linearised already...
|
|
191
|
+
return 1
|
|
192
|
+
elif self.protoalreadydone.get(name(clientNode)):
|
|
193
|
+
# another prototype with the same name has already been linearised
|
|
194
|
+
self.protoalreadydone[id(clientNode)] = 1
|
|
195
|
+
return 1
|
|
196
|
+
self.curproto.append(clientNode)
|
|
197
|
+
self.protoalreadydone[clientName] = self.protoalreadydone[id(clientNode)] = 1
|
|
198
|
+
|
|
199
|
+
# we don't want the prototypes constantly moving inward :)
|
|
200
|
+
oldindent = self.indentationlevel
|
|
201
|
+
self._indent(0)
|
|
202
|
+
oldbuffer = self.buffer
|
|
203
|
+
buffer = self.buffer = StringIO() # local buffer only for this particular proto
|
|
204
|
+
|
|
205
|
+
# write header (PROTO x [, EXTERNPROTO x [ )
|
|
206
|
+
# TODO: the externalURL descriptor no longer works, likely because the proto node
|
|
207
|
+
# is now a type, so it is trying to access an instance variable
|
|
208
|
+
externalURL = clientNode.externalURL
|
|
209
|
+
if hasattr(externalURL, '__get__'):
|
|
210
|
+
externalURL = clientNode.externalURL.__get__(clientNode)
|
|
211
|
+
if externalURL:
|
|
212
|
+
buffer.write('EXTERNPROTO %s [' % (clientName,))
|
|
213
|
+
else:
|
|
214
|
+
buffer.write('PROTO %s [' % (clientName,))
|
|
215
|
+
# write the declaration...
|
|
216
|
+
self._indent()
|
|
217
|
+
self._eventDict(clientNode)
|
|
218
|
+
self._fieldDict(clientNode, requireDefault=1) # clientNode.__gi__ == "PROTO")
|
|
219
|
+
self._dedent()
|
|
220
|
+
linvalues = self.linvalues
|
|
221
|
+
if externalURL:
|
|
222
|
+
buffer.write('\n] ')
|
|
223
|
+
from vrml import fieldtypes
|
|
224
|
+
|
|
225
|
+
buffer.write(fieldtypes.MFString_vrmlstr(externalURL, self))
|
|
226
|
+
buffer.write('\n')
|
|
227
|
+
else:
|
|
228
|
+
buffer.write('\n] {\n')
|
|
229
|
+
# the following will write everything into the current proto's buffer
|
|
230
|
+
# references to prototypes not already linearised will cause a recursive
|
|
231
|
+
# call to proto that will write those into the protobuffer before returning
|
|
232
|
+
sg = getSceneGraph(clientNode)
|
|
233
|
+
if sg is not None:
|
|
234
|
+
self._sceneGraph(sg)
|
|
235
|
+
if 'EndComments' in linvalues and linvalues['EndComments'] * 60 < (
|
|
236
|
+
(buffer.tell())
|
|
237
|
+
):
|
|
238
|
+
buffer.write('\n}#End PROTO %s\n' % (clientName))
|
|
239
|
+
else:
|
|
240
|
+
buffer.write('\n}\n')
|
|
241
|
+
self.alreadydone[id(clientNode)] = (
|
|
242
|
+
self.protobuffer.tell(),
|
|
243
|
+
self.protobuffer.tell() + buffer.tell(),
|
|
244
|
+
)
|
|
245
|
+
# note that we _always_ write out the prototype to the
|
|
246
|
+
# prototype-specific buffer! We do not write them into
|
|
247
|
+
# the main buffer. I suppose we could, but most of the
|
|
248
|
+
# time you want the prototypes all at the front of the
|
|
249
|
+
# file anyway.
|
|
250
|
+
self.protobuffer.write(buffer.getvalue())
|
|
251
|
+
# clear out the memory used by the buffer
|
|
252
|
+
buffer.close()
|
|
253
|
+
# return to the original buffer
|
|
254
|
+
self.buffer = oldbuffer
|
|
255
|
+
self._indent(oldindent)
|
|
256
|
+
# note that startind is irrelevant for prototypes,
|
|
257
|
+
# as they cannot be repeated, only multiply referenced.
|
|
258
|
+
self.curproto.pop()
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
def _Node(self, clientNode, *args, **namedargs):
|
|
262
|
+
'''Linearise an individual node'''
|
|
263
|
+
# if we don't already have this nodes prototype in the
|
|
264
|
+
# root namespace, insert it there. For now we don't allow
|
|
265
|
+
# nested namespaces. This is a serious limitation and should
|
|
266
|
+
# be fixed at some point in time.
|
|
267
|
+
self._proto(getPrototype(clientNode))
|
|
268
|
+
buffer = self.buffer
|
|
269
|
+
startind = self._canUse(clientNode)
|
|
270
|
+
if type(startind) != int: # this node has already been declared
|
|
271
|
+
buffer.write(startind)
|
|
272
|
+
return None
|
|
273
|
+
# now calculate the representation of this node...
|
|
274
|
+
defName = self._defName(clientNode)
|
|
275
|
+
namedargs['linvalues'] = linvalues = self.linvalues
|
|
276
|
+
namedargs['alreadydone'] = self.alreadydone
|
|
277
|
+
buffer.write(
|
|
278
|
+
'%s%s {'
|
|
279
|
+
% (
|
|
280
|
+
defName,
|
|
281
|
+
protoName(clientNode),
|
|
282
|
+
)
|
|
283
|
+
)
|
|
284
|
+
position = buffer.tell()
|
|
285
|
+
self._indent()
|
|
286
|
+
self._attrDict(clientNode)
|
|
287
|
+
|
|
288
|
+
# write the node-ending comment
|
|
289
|
+
if buffer.tell() == position:
|
|
290
|
+
buffer.write("%(courtesyspace)s}" % linvalues)
|
|
291
|
+
elif 'EndComments' in linvalues and linvalues['EndComments'] * 60 < (
|
|
292
|
+
(buffer.tell() - startind)
|
|
293
|
+
):
|
|
294
|
+
DEF = name(clientNode)
|
|
295
|
+
PROTO = protoName(clientNode)
|
|
296
|
+
buffer.write(
|
|
297
|
+
'%(full_element_separator)s%(curindent)s} #EndNode %%s'
|
|
298
|
+
% linvalues
|
|
299
|
+
% (DEF or PROTO)
|
|
300
|
+
)
|
|
301
|
+
else:
|
|
302
|
+
buffer.write('%(full_element_separator)s%(curindent)s}' % linvalues)
|
|
303
|
+
self._dedent()
|
|
304
|
+
self.alreadydone[id(clientNode)] = startind, buffer.tell()
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
def _Script(self, clientNode):
|
|
308
|
+
'''
|
|
309
|
+
Scripts should be output in the following format:
|
|
310
|
+
DEF defName Script {
|
|
311
|
+
fields
|
|
312
|
+
events
|
|
313
|
+
attributes
|
|
314
|
+
}
|
|
315
|
+
Both fields or attributes can have "IS's", and possibly
|
|
316
|
+
the attributes as well.
|
|
317
|
+
'''
|
|
318
|
+
buffer = self.buffer
|
|
319
|
+
startind = self._canUse(clientNode)
|
|
320
|
+
if type(startind) != int: # this node has already been declared
|
|
321
|
+
buffer.write(startind)
|
|
322
|
+
return None
|
|
323
|
+
# Note: we assume that defNames are being stored in the Node as well as the sceneGraph, if not, will need to do a reverse lookup there
|
|
324
|
+
DEF = self._defName(clientNode)
|
|
325
|
+
buffer.write('%s Script {' % (DEF,))
|
|
326
|
+
self._indent()
|
|
327
|
+
self._indent()
|
|
328
|
+
linvalues = self.linvalues
|
|
329
|
+
|
|
330
|
+
self._eventDict(getPrototype(clientNode))
|
|
331
|
+
self._fieldDict(
|
|
332
|
+
getPrototype(clientNode),
|
|
333
|
+
requireDefault=1,
|
|
334
|
+
skipFields=(' DEF', 'url', 'directOutput', 'mustEvaluate'),
|
|
335
|
+
)
|
|
336
|
+
self._dedent()
|
|
337
|
+
self._attrDict(clientNode)
|
|
338
|
+
PROTO = protoName(clientNode)
|
|
339
|
+
buffer.write(
|
|
340
|
+
'%(full_element_separator)s%(curindent)s}#%%s' % linvalues % (DEF or PROTO)
|
|
341
|
+
)
|
|
342
|
+
self._dedent()
|
|
343
|
+
self.alreadydone[id(clientNode)] = startind, buffer.tell()
|
|
344
|
+
return None
|
|
345
|
+
|
|
346
|
+
def _attrDict(self, object):
|
|
347
|
+
"""Write out the attribute dictionary for an object"""
|
|
348
|
+
buffer = self.buffer
|
|
349
|
+
linvalues = self.linvalues
|
|
350
|
+
if self.curproto:
|
|
351
|
+
set = node.ismaps(self.curproto[-1])
|
|
352
|
+
isMaps = {}
|
|
353
|
+
for fieldName, fieldList in set.items():
|
|
354
|
+
for n, field in fieldList:
|
|
355
|
+
if n is object:
|
|
356
|
+
isMaps[field] = fieldName
|
|
357
|
+
else:
|
|
358
|
+
isMaps = {}
|
|
359
|
+
if protoName(object) == "Script":
|
|
360
|
+
items = [
|
|
361
|
+
field
|
|
362
|
+
for field in getFields(object)
|
|
363
|
+
if field.name in ('url', 'mustEvaluate', 'directOutput')
|
|
364
|
+
]
|
|
365
|
+
else:
|
|
366
|
+
items = [
|
|
367
|
+
field
|
|
368
|
+
for field in getFields(object)
|
|
369
|
+
if field.name and field.name[0] != ' '
|
|
370
|
+
]
|
|
371
|
+
items.sort()
|
|
372
|
+
for field in items:
|
|
373
|
+
# following slows us down, but prevents the chaff from showing up...
|
|
374
|
+
val = field.fget(object)
|
|
375
|
+
default = field.getDefault()
|
|
376
|
+
if field.name in isMaps:
|
|
377
|
+
buffer.write(
|
|
378
|
+
'%(full_element_separator)s%(curindent)s%(indent)s%%s IS %%s\t'
|
|
379
|
+
% linvalues
|
|
380
|
+
% (field.name, isMaps.get(field.name))
|
|
381
|
+
)
|
|
382
|
+
elif (
|
|
383
|
+
default is not None and not arrays.safeCompare(default, val)
|
|
384
|
+
) or default is None:
|
|
385
|
+
buffer.write(
|
|
386
|
+
'%(full_element_separator)s%(curindent)s%(indent)s%%s\t'
|
|
387
|
+
% linvalues
|
|
388
|
+
% (field.name,)
|
|
389
|
+
)
|
|
390
|
+
self._sffield(val, field)
|
|
391
|
+
|
|
392
|
+
def _eventDict(self, clientNode):
|
|
393
|
+
'''
|
|
394
|
+
Event Dictionaries have two possible sources of information,
|
|
395
|
+
the eventDict and the isNames dictionary. The first provides
|
|
396
|
+
name, type, out, the second provides any IS bindings which
|
|
397
|
+
need to be created.
|
|
398
|
+
'''
|
|
399
|
+
# need to get the IS/USE for the field if available
|
|
400
|
+
buffer = self.buffer
|
|
401
|
+
|
|
402
|
+
fields = sorted(getFields(clientNode, events=1), key=namekey)
|
|
403
|
+
|
|
404
|
+
for field in [f for f in fields if (f.name and f.name[0] != ' ')]:
|
|
405
|
+
buffer.write('%(full_element_separator)s%(curindent)s' % (self.linvalues))
|
|
406
|
+
field.eventVrmlstr(self)
|
|
407
|
+
# XXX do IS-mapping here!
|
|
408
|
+
|
|
409
|
+
def _fieldDict(self, clientNode, requireDefault=1, skipFields=('DEF',)):
|
|
410
|
+
buffer = self.buffer
|
|
411
|
+
fields = [
|
|
412
|
+
field
|
|
413
|
+
for field in getFields(clientNode)
|
|
414
|
+
if (
|
|
415
|
+
(field.name not in skipFields) and field.name and (field.name[0] != ' ')
|
|
416
|
+
)
|
|
417
|
+
]
|
|
418
|
+
fields.sort(key=namekey)
|
|
419
|
+
for field in fields:
|
|
420
|
+
buffer.write('%(full_element_separator)s%(curindent)s' % (self.linvalues))
|
|
421
|
+
field.fieldVrmlstr(self)
|
|
422
|
+
|
|
423
|
+
def _fieldref(self, clientNode, *args, **namedargs):
|
|
424
|
+
self.buffer.write('IS %s' % clientNode.declaredName)
|
|
425
|
+
return None
|
|
426
|
+
|
|
427
|
+
def _preroute(self, sceneGraph, clientNode):
|
|
428
|
+
"""Pre-scans all routes, forces all routed nodes to have DEF names"""
|
|
429
|
+
for child in (clientNode.source, clientNode.destination):
|
|
430
|
+
DEF = defName(child)
|
|
431
|
+
if not DEF:
|
|
432
|
+
count = 0
|
|
433
|
+
PROTO = protoName(child)
|
|
434
|
+
while 1:
|
|
435
|
+
name = "%s_%s" % (PROTO, count)
|
|
436
|
+
if sceneGraph.getDEF(name) is None:
|
|
437
|
+
sceneGraph.regDefName(name, child)
|
|
438
|
+
break
|
|
439
|
+
count += 1
|
|
440
|
+
|
|
441
|
+
def _route(self, clientNode):
|
|
442
|
+
'''Linearise a route'''
|
|
443
|
+
# should check here to make sure the ROUTEs are valid
|
|
444
|
+
buffer = self.buffer
|
|
445
|
+
|
|
446
|
+
sourcenode = defName(clientNode.source)
|
|
447
|
+
destinationnode = defName(clientNode.destination)
|
|
448
|
+
values = (
|
|
449
|
+
sourcenode,
|
|
450
|
+
clientNode.sourceField,
|
|
451
|
+
destinationnode,
|
|
452
|
+
clientNode.destinationField,
|
|
453
|
+
)
|
|
454
|
+
buffer.write(
|
|
455
|
+
'%(full_element_separator)sROUTE %%s.%%s TO %%s.%%s'
|
|
456
|
+
% self.linvalues
|
|
457
|
+
% values
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
def _sffield(self, anyobj, field, *args, **namedargs):
|
|
461
|
+
'''
|
|
462
|
+
Any to String takes an object and checks how it should
|
|
463
|
+
be linearised given that it is supposed to become a fieldType
|
|
464
|
+
This is done by first determining if the field has a __vrmlStr__
|
|
465
|
+
attribute. If it doesn't, a standard coerce_to is called with
|
|
466
|
+
the particular fieldType as the source. and 'String' as the
|
|
467
|
+
target.
|
|
468
|
+
This is necessary because the SFNode field can have any of Scripts,
|
|
469
|
+
Nodes, ProtoTypes and ExternProtos (well, not according to the
|
|
470
|
+
parsers, but someone might attempt it).
|
|
471
|
+
'''
|
|
472
|
+
try:
|
|
473
|
+
if field is node.RootScenegraphNode:
|
|
474
|
+
return
|
|
475
|
+
pName = protoName(anyobj)
|
|
476
|
+
handler = self.typecache.get(pName)
|
|
477
|
+
if handler:
|
|
478
|
+
return handler(anyobj)
|
|
479
|
+
if isinstance(anyobj, list):
|
|
480
|
+
return self._mfnode(anyobj)
|
|
481
|
+
return self._Node(anyobj)
|
|
482
|
+
except AttributeError:
|
|
483
|
+
if hasattr(field, 'vrmlstr'):
|
|
484
|
+
result = getattr(field, 'vrmlstr')(anyobj, self)
|
|
485
|
+
if result is not None:
|
|
486
|
+
self.buffer.write(result)
|
|
487
|
+
elif hasattr(self, field.typeName()):
|
|
488
|
+
result = getattr(self, field.typeName())(anyobj)
|
|
489
|
+
if result is not None:
|
|
490
|
+
self.buffer.write(result)
|
|
491
|
+
else:
|
|
492
|
+
raise TypeError(
|
|
493
|
+
'''Unknown fieldType %s, cannot convert to string''' % field
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
### Utility functions...
|
|
497
|
+
def _dedent(self):
|
|
498
|
+
self.indentationlevel = self.indentationlevel - 1
|
|
499
|
+
self.linvalues['curindent'] = self.linvalues['indent'] * self.indentationlevel
|
|
500
|
+
|
|
501
|
+
def _indent(self, exact=None):
|
|
502
|
+
if exact is not None:
|
|
503
|
+
self.indentationlevel = exact
|
|
504
|
+
else:
|
|
505
|
+
self.indentationlevel = self.indentationlevel + 1
|
|
506
|
+
self.linvalues['curindent'] = self.linvalues['indent'] * self.indentationlevel
|
|
507
|
+
|
|
508
|
+
def _canUse(self, clientNode):
|
|
509
|
+
if id(clientNode) in self.alreadydone:
|
|
510
|
+
DEF = defName(clientNode)
|
|
511
|
+
if DEF:
|
|
512
|
+
return 'USE ' + DEF
|
|
513
|
+
# else have to linearise again, should warn the user
|
|
514
|
+
else:
|
|
515
|
+
keyvals = self.alreadydone[id(clientNode)]
|
|
516
|
+
index = self.buffer.tell()
|
|
517
|
+
try:
|
|
518
|
+
start, stop = keyvals
|
|
519
|
+
self.buffer.seek(start)
|
|
520
|
+
val = self.buffer.read(stop - start)
|
|
521
|
+
self.buffer.seek(index)
|
|
522
|
+
return (
|
|
523
|
+
'#WARNING HERE -- USE of node with no DEF name, Node duplicated\n'
|
|
524
|
+
+ val
|
|
525
|
+
)
|
|
526
|
+
except TypeError:
|
|
527
|
+
return '''#ERROR HERE -- USE of a parent node that has no DEF name USE ignored'''
|
|
528
|
+
else:
|
|
529
|
+
ind = self.alreadydone[id(clientNode)] = self.buffer.tell()
|
|
530
|
+
return ind
|
|
531
|
+
|
|
532
|
+
def _nullNode(self, clientNode):
|
|
533
|
+
self.buffer.write('NULL')
|
|
534
|
+
|
|
535
|
+
def _defName(self, clientNode):
|
|
536
|
+
DEF = defName(clientNode)
|
|
537
|
+
if DEF:
|
|
538
|
+
return 'DEF %s ' % (DEF)
|
|
539
|
+
else:
|
|
540
|
+
return ''
|
|
541
|
+
|
|
542
|
+
def _linear(self, clientNode):
|
|
543
|
+
'''Linearise a particular client node of whatever type by dispatching to
|
|
544
|
+
appropriate method...'''
|
|
545
|
+
if type(clientNode) == type:
|
|
546
|
+
method = self._proto
|
|
547
|
+
else:
|
|
548
|
+
name = protoName(clientNode)
|
|
549
|
+
method = self.typecache.get(name, self._Node)
|
|
550
|
+
return method(clientNode)
|
|
551
|
+
|
|
552
|
+
### Field-type handlers...
|
|
553
|
+
def _mfnode(self, anyobj, *args, **namedargs):
|
|
554
|
+
'''
|
|
555
|
+
Really, this will handle any list of elements where all elements
|
|
556
|
+
have a __vrmlStr__ method, but since most of those are nodes, we'll
|
|
557
|
+
keep the name for now.
|
|
558
|
+
format:
|
|
559
|
+
[(mffieldsep)
|
|
560
|
+
(curindent)(indent)child
|
|
561
|
+
...
|
|
562
|
+
(curindent)]
|
|
563
|
+
or:
|
|
564
|
+
[ ]
|
|
565
|
+
'''
|
|
566
|
+
buffer = self.buffer
|
|
567
|
+
linvalues = self.linvalues
|
|
568
|
+
if anyobj: # first test to see if there's any point doing the processing
|
|
569
|
+
self._indent()
|
|
570
|
+
buffer.write('[ ')
|
|
571
|
+
for el in anyobj:
|
|
572
|
+
buffer.write(
|
|
573
|
+
'%(full_element_separator)s%(curindent)s%(indent)s' % linvalues
|
|
574
|
+
)
|
|
575
|
+
self._linear(el)
|
|
576
|
+
buffer.write('%(full_element_separator)s%(curindent)s]' % linvalues)
|
|
577
|
+
self._dedent()
|
|
578
|
+
else:
|
|
579
|
+
buffer.write(' [%(courtesyspace)s]' % linvalues)
|