1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
|
# VRML node prototype class (SGbuilder)
# Wed Oct 31 16:18:35 CET 2001
'''Prototype2 -- VRML 97 sceneGraph/Node/Script/ROUTE/IS implementations'''
import copy, types # extern
import strop as string # builtin
from utils import typeclasses, err, namespace # XXX
## TODO: namespace must go
class baseProto:
def __vrmlStr__( self, **namedargs ):
'''Generate a VRML 97-syntax string representing this Prototype
**namedargs -- key:value
passed arguments for the linearisation object
see lineariser4.Lineariser
'''
import lineariser4
lineariser = apply( lineariser4.Lineariser, (), namedargs )
return apply( lineariser.linear, ( self, ), namedargs )
toString = __vrmlStr__
# added stuff for linking support for target scenegraph
def setTargetnode(self, node):
self.__dict__['_targetnode'] = node
def getTargetnode(self):
try:
return self.__dict__['_targetnode']
except:
return None
class Prototype(baseProto):
''' A VRML 97 Prototype object
A Prototype is a callable object which produces Node instances
the Node uses a pointer to its Prototype to provide much of the
Node's standard functionality.
Prototype's are often stored in a sceneGraph's protoTypes namespace,
where you can access them as sceneGraph.protoTypes.nodeGI . They are
also commonly found in Nodes' PROTO attributes.
Attributes:
__gi__ -- constant string "PROTO"
nodeGI -- string gi
The "generic identifier" of the node type, i.e. the name of the node
fieldDictionary -- string name: (string name, string dataType, boolean exposed)
defaultDictionary -- string name: object defaultValue
Will be blank for EXTERNPROTO's and Script prototypes
eventDictionary -- string name: (string name, string dataType, boolean eventOut)
sceneGraph -- object sceneGraph
MFNodeNames -- list of field name strings
Allows for easy calculation of "children" nodes
SFNodeNames -- list of field name strings
Allows for easy calculation of "children" nodes
'''
__gi__ = "PROTO"
def __init__(self, gi, fieldDict=None, defaultDict=None, eventDict=None, sGraph=None):
'''
gi -- string gi
see attribute nodeGI
fieldDict -- string name: (string name, string dataType, boolean exposed)
see attribute fieldDictionary
defaultDict -- string name: object defaultValue
see attribute defaultDictionary
eventDict -- string name: (string name, string dataType, boolean eventOut)
see attribute eventDictionary
sceneGraph -- object sceneGraph
see attribute sceneGraph
'''
self.nodeGI = checkName( gi )
self.fieldDictionary = {}
self.defaultDictionary = {}
self.eventDictionary = {}
self.SFNodeNames = []
self.MFNodeNames = []
self.sceneGraph = sGraph
# setup the fields/events
for definition in (fieldDict or {}).values():
self.addField( definition, (defaultDict or {}).get( definition[0]))
for definition in (eventDict or {}).values():
self.addEvent( definition )
def getSceneGraph( self ):
''' Retrieve the sceneGraph object (may be None object)
see attribute sceneGraph'''
return self.sceneGraph
def setSceneGraph( self, sceneGraph ):
''' Set the sceneGraph object (may be None object)
see attribute sceneGraph'''
self.sceneGraph = sceneGraph
def getChildren(self, includeSceneGraph=None, includeDefaults=1, *args, **namedargs):
''' Calculate the current children of the PROTO and return as a list of nodes
if includeDefaults:
include those default values which are node values
if includeSceneGraph:
include the sceneGraph object if it is not None
see attribute MFNodeNames
see attribute SFNodeNames
see attribute sceneGraph
'''
temp = []
if includeDefaults:
for attrname in self.SFNodeNames:
try:
temp.append( self.defaultDictionary[attrname] )
except KeyError: # sceneGraph object is not copied...
pass
for attrname in self.MFNodeNames:
try:
temp[len(temp):] = self.defaultDictionary[attrname]
except KeyError:
pass
if includeSceneGraph and self.sceneGraph:
temp.append( self.getSceneGraph() )
return temp
def addField (self, definition, default = None):
''' Add a single field definition to the Prototype
definition -- (string name, string dataType, boolean exposed)
default -- object defaultValue
see attribute fieldDictionary
see attribute defaultDictionary
'''
if type (definition) == types.InstanceType:
definition = definition.getDefinition()
default = definition.getDefault ()
self.removeField( definition[0] )
self.fieldDictionary[definition [0]] = definition
if default is not None:
default = fieldcoercian.FieldCoercian()( default, definition[1] )
self.defaultDictionary [definition [0]] = default
if definition[1] == 'SFNode':
self.SFNodeNames.append(definition[0])
elif definition[1] == 'MFNode':
self.MFNodeNames.append(definition[0])
def removeField (self, key):
''' Remove a single field from the Prototype
key -- string fieldName
The name of the field to remove
'''
if self.fieldDictionary.has_key (key):
del self.fieldDictionary [key]
if self.defaultDictionary.has_key (key):
del self.defaultDictionary [key]
for attribute in (self.SFNodeNames, self.MFNodeNames):
while key in attribute:
attribute.remove(key)
def addEvent(self, definition):
''' Add a single event definition to the Prototype
definition -- (string name, string dataType, boolean eventOut)
see attribute eventDictionary
'''
if type (definition) == types.InstanceType:
definition = definition.getDefinition()
self.eventDictionary[definition [0]] = definition
def removeEvent(self, key):
''' Remove a single event from the Prototype
key -- string eventName
The name of the event to remove
'''
if self.eventDictionary.has_key (key):
del self.eventDictionary [key]
def getField( self, key ):
'''Return a Field or Event object representing a given name
key -- string name
The name of the field or event to retrieve
will attempt to match key, key[4:], and key [:-8]
corresponding to key, set_key and key_changed
see class Field
see class Event
'''
# print self.fieldDictionary, self.eventDictionary
for tempkey in (key, key[4:], key[:-8]):
if self.fieldDictionary.has_key( tempkey ):
return Field( self.fieldDictionary[tempkey], self.defaultDictionary.get(tempkey) )
elif self.eventDictionary.has_key( tempkey ):
return Event( self.eventDictionary[tempkey] )
raise AttributeError, key
def getDefault( self, key ):
'''Return the default value for the given field
key -- string name
The name of the field
Will attempt to match key, key[4:], and key [:-8]
corresponding to key, set_key and key_changed
see attribute defaultDictionary
'''
for key in (key, key[4:], key[:-8]):
if self.defaultDictionary.has_key( key ):
val = self.defaultDictionary[key]
if type(val) in typeclasses.MutableTypes:
val = copy.deepcopy( val )
return val
elif self.fieldDictionary.has_key( key ):
'''We have the field, but we don't have a default, we are likely an EXTERNPROTO'''
return None
raise AttributeError, key
def setDefault (self, key, value):
'''Set the default value for the given field
key -- string name
The name of the field to set
value -- object defaultValue
The default value, will be checked for type and coerced if necessary
'''
field = self.getField (key)
self.defaultDictionary [field.name]= field.coerce (value)
def clone( self, children = 1, sceneGraph = 1 ):
'''Return a copy of this Prototype
children -- boolean
if true, copy the children of the Prototype, otherwise include them
sceneGraph -- boolean
if true, copy the sceneGraph of the Prototype
'''
if sceneGraph:
sceneGraph = self.sceneGraph
else:
sceneGraph = None
# defaults should always be copied before modification, but this is still dangerous...
defaultDictionary = self.defaultDictionary.copy()
if not children:
for attrname in self.SFNodeNames+self.MFNodeNames:
try:
del defaultDictionary[attrname]
except KeyError: # sceneGraph object is not copied...
pass
# now make a copy
if self.__gi__ == "PROTO":
newNode = self.__class__(
self.nodeGI,
self.fieldDictionary,
defaultDictionary,
self.eventDictionary,
sceneGraph,
)
else:
newNode = self.__class__(
self.nodeGI,
self.url,
self.fieldDictionary,
self.eventDictionary,
)
return newNode
def __call__(self, *args, **namedargs):
'''Create a new Node instance associated with this Prototype
*args, **namedargs -- passed to the Node.__init__
see class Node
'''
node = apply( Node, (self, )+args, namedargs )
return node
def __repr__ ( self ):
'''Create a simple Python representation'''
return '''%s( %s )'''%( self.__class__.__name__, self.nodeGI )
class ExternalPrototype( Prototype ):
'''Sub-class of Prototype
The ExternalPrototype is a minor sub-classing of the Prototype
it does not have any defaults, nor a sceneGraph
Attributes:
__gi__ -- constant string "EXTERNPROTO"
url -- string list urls
implementation source for the ExternalPrototype
'''
__gi__ = "EXTERNPROTO"
def __init__(self, gi, url=None, fieldDict=None, eventDict=None):
'''
gi -- string gi
see attribute nodeGI
url -- string list url
MFString-compatible list of url's for EXTERNPROTO
fieldDict -- string name: (string name, string dataType, boolean exposed)
see attribute fieldDictionary
eventDict -- string name: (string name, string dataType, boolean eventOut)
see attribute eventDictionary
'''
if url is None:
url = []
self.url = url
Prototype.__init__( self, gi, fieldDict=fieldDict, eventDict=eventDict)
from vrml import fieldcoercian # XXX
class Field:
''' Representation of a Prototype Field
The Field object is a simple wrapper to provide convenient
access to field coercian and meta- information
'''
def __init__( self, specification, default=None ):
self.name, self.type, self.exposure = specification
self.default = default
def getDefinition (self):
return self.name, self.type, self.exposure
def getDefault (self):
return self.default
def coerce( self, value ):
''' Coerce value to the appropriate dataType for this Field '''
return fieldcoercian.FieldCoercian()( value,self.type, )
def __repr__( self ):
if hasattr (self, "default"):
return '%s( (%s,%s,%s), %s)'%( self.__class__.__name__, self.name, self.type, self.exposure, self.default)
else:
return '%s( (%s,%s,%s),)'%( self.__class__.__name__, self.name, self.type, self.exposure)
def __str__( self ):
if self.exposure:
exposed = "exposedField"
else:
exposed = field
if hasattr (self, "default"):
default = ' ' + str( self.default)
else:
default = ""
return '%s %s %s%s'%(exposed, self.type, self.name, default)
class Event (Field):
def __str__( self ):
if self.exposure:
exposed = "eventOut"
else:
exposed = "eventIn"
return '%s %s %s'%(exposed, self.type, self.name)
### Translation strings for VRML node names...
translationstring = '''][0123456789{}"'#,.\\ \000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023'''
NAMEFIRSTCHARTRANSLATOR = string.maketrans( translationstring, '_'*len(translationstring) )
translationstring = '''][{}"'#,.\\ \000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023'''
NAMERESTCHARTRANSLATOR = string.maketrans( translationstring, '_'*len(translationstring) )
del translationstring
def checkName( name ):
'''Convert arbitrary string to a valid VRML id'''
if type(name) is types.StringType:
if not name:
return name
return string.translate( name[:1], NAMEFIRSTCHARTRANSLATOR) + string.translate( name[1:], NAMERESTCHARTRANSLATOR)
else:
raise TypeError, "VRML Node Name must be a string, was a %s: %s"%(type(name), name)
class Node(baseProto):
''' A VRML 97 Node object
A Node object represents a VRML 97 node. Attributes of the Node
can be set/retrieved with standard python setattr/getattr syntax.
VRML 97 attributes may be passed to the constructor as named
arguments.
Attributes:
__gi__ -- string PROTOname
DEF -- string DEFName
The DEF name of the node, will be coerced to be a valid
identifier (with "" being considered valid)
PROTO -- Prototype PROTO
The node's Prototype object
attributeDictionary -- string name: object value
Dictionary in which VRML 97 attributes are stored
'''
DEF = '' # the default name for all nodes (arbitrary)
def __init__(self, PROTO, name='', attrDict=None, *args, **namedargs):
'''Normally this method is only called indirectly via the Prototype() interface
PROTO -- Prototype PROTO
see attribute PROTO
name -- string DEFName
see attribute DEF
attrDict -- string name: object value
see attribute attributeDictionary
**namedargs -- string name: object value
added to attrDict to create attributeDictionary
'''
self.__dict__["PROTO"] = PROTO
self.DEF = name
self.__dict__["attributeDictionary"] = {}
## print attrDict, namedargs
for dict in (attrDict or {}), namedargs:
if dict:
for key, value in dict.items ():
self.__setattr__( key, value, check=1 )
def __setattr__( self, key, value, check=1, raw=0 ):
'''Set attribute on Node
key -- string attributeName
value -- object attributeValue
check -- boolean check
if false, put values for unrecognized keys into __dict__
otherwise, raise an AttributeError
'''
if key == "DEF":
self.__dict__["DEF"] = checkName( value )
return None
elif key == "PROTO":
self.__dict__["PROTO"] = value
try:
field = self.PROTO.getField( key )
if (hasattr( value, "__gi__") and value.__gi__ == "IS") or raw:
self.attributeDictionary[ field.name] = value
else:
self.attributeDictionary[ field.name] = field.coerce( value )
except ValueError, x:
raise ValueError( "Could not coerce value %s into value of VRML type %s for %s node %s's field %s"%( value, field.type, self.__gi__, self.DEF, key), x.args)
except (AttributeError), x:
if check:
raise AttributeError("%s is not a known field for node %s"%(key, repr(self)))
else:
self.__dict__[key] = value
def __getattr__( self, key, default = 1 ):
''' Retrieve an attribute when standard lookup fails
key -- string attributeName
default -- boolean default
if true, return the default value if the node does not have local value
otherwise, raise AttributeError
'''
if key != "attributeDictionary":
if self.__dict__.has_key( key):
return self.__dict__[ key ]
elif self.attributeDictionary.has_key( key):
return self.attributeDictionary[key]
if key != "PROTO":
if key == "__gi__":
return self.PROTO.nodeGI
elif default:
try:
default = self.PROTO.getDefault( key )
if type( default ) in typeclasses.MutableTypes:
# we need a copy, not the original
default = copy.deepcopy( default )
self.__setattr__( key, default, check=0, raw=1 )
return default
except AttributeError:
pass
raise AttributeError, key
def __delattr__( self, key ):
''' Delete an attribute from the Node
key -- string attributeName
'''
if key != "attributeDictionary":
if self.attributeDictionary.has_key( key):
del self.attributeDictionary[key]
elif self.__dict__.has_key( key):
del self.__dict__[ key ]
raise AttributeError, key
def __repr__(self):
''' Create simple python representation '''
return '<%s(%s): %s>'%(self.__gi__, `self.DEF`, self.attributeDictionary.keys() )
def getChildrenNames( self, current = 1, *args, **namedargs ):
''' Get the (current) children of Node
returns two lists: MFNode children, SFNode children
current -- boolean currentOnly
if true, only return current children
otherwise, include all potential children
'''
MFNODES, SFNODES = self.PROTO.MFNodeNames, self.PROTO.SFNodeNames
mns, sns = [],[]
for key in MFNODES:
if current and self.attributeDictionary.has_key(key):
mns.append(key)
elif not current:
mns.append(key)
for key in SFNODES:
if self.attributeDictionary.has_key(key):
sns.append(key)
elif not current:
sns.append(key)
return mns,sns
def calculateChildren(self, *args, **namedargs):
'''Calculate the current children of the Node as list of Nodes
'''
MFNODES, SFNODES = self.getChildrenNames( )
temp = []
for key in MFNODES:
try:
temp.extend( self.__getattr__( key, default=0 ) )
except AttributeError:
pass
for key in SFNODES:
try:
temp.append( self.__getattr__(key, default = 0 ) )
except AttributeError:
pass
return temp
def clone(self, newclass=None, name=None, children=None, attrDeepCopy=1, *args, **namedargs):
'''Return a copy of this Node
newclass -- object newClass or None
optionally use a different Prototype as base
name -- string DEFName or None or 1
if 1, copy from current
elif None, set to ""
else, set to passed value
children -- boolean copyChildren
if true, copy the children of this node
otherwise, skip children
attrDeepCopy -- boolean deepCopy
if true, use deepcopy
otherwise, use copy
'''
if attrDeepCopy:
cpy = copy.deepcopy
else:
cpy = copy.copy
newattrs = self.attributeDictionary.copy()
if not children:
mnames,snames = self.getChildrenNames( )
for key in mnames+snames:
try:
del(newattrs[key])
except KeyError:
pass
for key, val in newattrs.items():
if type(val) in typeclasses.MutableTypes:
newattrs[key] = cpy(val)
# following is Node specific, won't work for sceneGraphs, scripts, etceteras
if name == 1: # asked to copy the name
name = self.DEF
elif name is None: # asked to clear the name
name = ''
if not newclass:
newclass = self.PROTO
return newclass( name, newattrs )
def __cmp__( self, other, stop=None ):
''' Compare this node to another object/node
other -- object otherNode
stop -- boolean stopIfFailure
if true, failure to find comparison causes match failure (i.e. considered unequal)
'''
if hasattr( other, '__gi__') and other.__gi__ == self.__gi__:
try:
return cmp( self.DEF, other.DEF) or cmp( self.attributeDictionary, other.attributeDictionary )
except:
if not stop:
try:
return other.__cmp__( self , 1) # 1 being stop...
except:
pass
return -1 # could be one, doesn't really matter
def Script( name="", attrDict=None, fieldDict=None, defaultDict=None, eventDict=None, **namedarguments):
''' Create a script node (and associated prototype)
name -- string DEFName
attrDict -- string name: object value
see class Node.attributeDictionary
fieldDict -- string name: (string name, string dataType, boolean exposure)
see class Prototype.fieldDictionary
defaultDict -- string name: object value
see class Prototype.defaultDictionary
eventDict -- string name: (string name, string dataType, boolean eventOut)
'''
fieldDictionary = {
'directOutput':('directOutput', 'SFBool',0),
'url':('url',"MFString",0),
'mustEvaluate':('mustEvaluate', 'SFBool',0),
}
fieldDictionary.update( fieldDict or {})
defaultDictionary = {
"directOutput":0,
"url":[],
"mustEvaluate":0,
}
defaultDictionary.update( defaultDict or {})
PROTO = Prototype(
"Script",
fieldDictionary,
defaultDictionary ,
eventDict = eventDict,
)
if attrDict is not None:
attrDict.update( namedarguments )
else:
attrDict = namedarguments
return PROTO( name, attrDict )
class NullNode:
'''NULL SFNode value
There should only be a single NULL instance for
any particular system. It should, for all intents and
purposes just sit there inertly
'''
__gi__ = 'NULL'
DEF = ''
__walker_is_temporary_item__ = 1 # hacky signal to walking engine not to reject this node as already processed
def __repr__(self):
return '<NULL vrml SFNode>'
def __vrmlStr__(self,*args,**namedargs):
return ' NULL '
toString = __vrmlStr__
def __nonzero__(self ):
return 0
def __call__(self, *args, **namedargs):
return self
def __cmp__( self, other ):
if hasattr( other, '__gi__') and other.__gi__ == self.__gi__:
return 0
return -1 # could be one, doesn't really matter
def clone( self ):
return self
NULL = NullNode()
class fieldRef:
'''IS Prototype field reference
'''
__gi__ = 'IS'
DEF = ''
def __init__(self, declaredName):
self.declaredName = declaredName
def __repr__(self):
return 'IS %s'%self.declaredName
def __vrmlStr__(self,*args,**namedargs):
return 'IS %s'%self.declaredName
toString = __vrmlStr__
def __cmp__( self, other ):
if hasattr( other, '__gi__') and other.__gi__ == self.__gi__:
return cmp( self.declaredName, other.declaredName )
return -1 # could be one, doesn't really matter
def clone( self ):
return self.__class__( self.declaredName )
IS = fieldRef
class ROUTE:
''' VRML 97 ROUTE object
The ROUTE object keeps track of its source and destination nodes and attributes
It generally lives in a sceneGraph's "routes" collection
'''
__gi__ = 'ROUTE'
def __init__( self, fromNode, fromField, toNode, toField ):
if type(fromNode) is types.StringType:
raise TypeError( "String value for ROUTE fromNode",fromNode)
if type(toNode) is types.StringType:
raise TypeError( "String value for ROUTE toNode",toNode)
self.fromNode = fromNode
self.fromField = fromField
self.toNode = toNode
self.toField = toField
def __getitem__( self, index ):
return (self.fromNode, self.fromField, self.toNode, self.toField)[index]
def __setitem__( self, index, value ):
attribute = ("fromNode","fromField","toNode", "toField")[index]
setattr( self, attribute, value )
def __repr__( self ):
return 'ROUTE %s.%s TO %s.%s'%( self.fromNode.DEF, self.fromField, self.toNode.DEF, self.toField )
def clone( self ):
return self.__class__(
self.fromNode,
self.fromField,
self.toNode,
self.toField,
)
class sceneGraph(baseProto):
''' A VRML 97 sceneGraph
Attributes:
__gi__ -- constant string "sceneGraph"
DEF -- constant string ""
children -- Node list
List of the root children of the sceneGraph, nodes/scripts only
routes -- ROUTE list
List of the routes within the sceneGraph
defNames -- string DEFName: Node node
Mapping of DEF names to their respective nodes
protoTypes -- Namespace prototypes
Namespace (with chaining lookup) collection of prototypes
getattr( sceneGraph.protoTypes, 'nodeGI' ) retrieves a prototype
'''
__gi__ = 'sceneGraph'
DEF = ''
def __init__(self, root=None, protoTypes=None, routes=None, defNames=None, children=None, *args, **namedargs):
'''
root -- sceneGraph root or Dictionary root or Module root or None
Base object for root of protoType namespace hierarchy
protoTypes -- string nodeGI: Prototype PROTO
Dictionary of prototype definitions
routes -- ROUTE list or (string sourcenode, string sourceeventOut, string destinationnode, string destinationeventOut) list
List of route objects or tuples to be added to the sceneGraph
see attribute routes
defNames -- string DEFName: Node node
see attribute defNames
children -- Node list
see attribute children
'''
if children is None:
self.children = []
else:
self.children = children
if routes is None:
self.routes = [] # how will we efficiently handle routes?
else:
self.routes = routes
if defNames == None:
self.defNames = {} # maps 'defName':Node
else:
self.defNames = defNames
if protoTypes is None:
protoTypes = {}
if root is None:
from vrml import basenodes # XXX
self.protoTypes = namespace.NameSpace(
protoTypes,
children = [namespace.NameSpace(basenodes)]
)
else: # there is a root file, so need to use it as the children instead of basenodes...
if hasattr( root, "protoTypes"):
self.protoTypes = namespace.NameSpace(
protoTypes,
children = [root.protoTypes]
)
else:
self.protoTypes = namespace.NameSpace(
protoTypes,
children = [ namespace.NameSpace(root) ]
)
def __getinitargs__( self ):
# we only copy our explicit protos, our routes, our defNames, and our children
# inherited protos will be pulled along by their nodes...
return None, self.protoTypes._base, self.routes, self.defNames, self.children
def __getstate__( self ):
return {}
def __setstate__( self, dict ):
pass
def __del__( self, id=id ):
'''
Need to clean up the namespace's mutual references,
this can be done without affecting the cascade by just
eliminating the key/value pairs. The namespaces will
no longer contain the prototypes, but they will still
chain up to the higher-level namespaces, and the nodes
will have those prototypes still in use.
'''
## print 'del sceneGraph', id(self )
try:
## import pdb
## pdb.set_trace()
## self.protoTypes.__dict__.clear()
self.protoTypes._base.clear()
del self.protoTypes.__namespace_cascade__[:]
except:
print 'unable to free references'
def addRoute(self, routeTuple, getNewNodes=0):
''' Add a single route to the sceneGraph
routeTuple -- ROUTE route or (string sourcenode, string sourceeventOut, string destinationnode, string destinationeventOut)
getNewNodes -- boolean getNewNodes
if true, look up sourcenode and destinationnode within the current defNames to determine source/destination nodes
otherwise, just use current if available
'''
# create and wire together the Routes here,
# should just be a matter of pulling the events and passing the nodes...
## import pdb
## pdb.set_trace()
if type( routeTuple) in ( types.TupleType, types.ListType):
(fromNode, fromField, toNode, toField ) = routeTuple
if type(fromNode) is types.StringType:
# get the node instead of the string...
if self.defNames.has_key( fromNode ):
fromNode = self.defNames[fromNode]
else:
err.err( "ROUTE from an unknown node %s "%(routeTuple) )
return 0
if type(toNode) is types.StringType:
# get the node instead of the string...
if self.defNames.has_key( toNode ):
toNode = self.defNames[toNode]
else:
err.err( "ROUTE to an unknown node %s "%(routeTuple) )
return 0
routeTuple = ROUTE( fromNode, fromField, toNode, toField)
elif getNewNodes:
# get the nodes with the same names...
if self.defNames.has_key( routeTuple[0].DEF ):
routeTuple[0] = self.defNames[routeTuple[0].DEF]
else:
err.err( "ROUTE from an unknown node %s "%(routeTuple) )
return 0
if self.defNames.has_key( routeTuple[2].DEF ):
routeTuple[2] = self.defNames[routeTuple[2].DEF]
else:
err.err( "ROUTE to an unknown node %s "%(routeTuple) )
return 0
# should be a Route node now, append to our ROUTE list...
self.routes.append(routeTuple)
return 1
def regDefName(self, defName, object):
''' Register a DEF name for a particular object
defName -- string DEFName
object -- Node node
'''
object.DEF = defName
self.defNames[defName] = object
def addProto(self, proto):
'''Register a Prototype for this sceneGraph
proto -- Prototype PROTO
'''
setattr( self.protoTypes, proto.__gi__, proto )
#toString = __vrmlStr__
#__vrmlStr__ = toString
## def __setattr__( self, key, value ):
## if key == 'protoTypes' and type( value) is types.ListType:
## import pdb
## pdb.set_trace()
## raise TypeError( "Invalid type for protoTypes attribute of sceneGraph %s"%(`value`) )
## else:
## self.__dict__[key] = value
DEFAULTFIELDVALUES ={
"SFBool": 0,
"SFString": "",
"SFFloat": 0,
"SFTime": 0,
"SFVec3f": (0, 0,0),
"SFVec2f": (0,0),
"SFRotation": (0, 1,0, 0),
"SFInt32": 0,
"SFImage": (0,0,0),
"SFColor": (0,0, 0),
"SFNode": NULL,
"MFString": [],
"MFFloat": [],
"MFTime": [],
"MFVec3f": [],
"MFVec2f": [],
"MFRotation": [],
"MFInt32": [],
"MFColor": [],
"MFNode": [],
}
|