Welcome to mirror list, hosted at ThFree Co, Russian Federation.

scenegraph.py « vrml « modules « python « intern - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 2f137b1e259ff0a4f63ead70e15742825463b429 (plain)
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": [],
}