Initial revision
This commit is contained in:
833
intern/python/modules/vrml/scenegraph.py
Normal file
833
intern/python/modules/vrml/scenegraph.py
Normal file
@@ -0,0 +1,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": [],
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user