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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/python/modules/vrml')
-rw-r--r--intern/python/modules/vrml/__init__.py1
-rw-r--r--intern/python/modules/vrml/basenodes.py974
-rw-r--r--intern/python/modules/vrml/fieldcoercian.py310
-rw-r--r--intern/python/modules/vrml/loader.py97
-rw-r--r--intern/python/modules/vrml/parser.py426
-rw-r--r--intern/python/modules/vrml/scenegraph.py833
-rw-r--r--intern/python/modules/vrml/utils/__init__.py1
-rw-r--r--intern/python/modules/vrml/utils/collapse.py169
-rw-r--r--intern/python/modules/vrml/utils/err.py37
-rw-r--r--intern/python/modules/vrml/utils/namespace.py225
-rw-r--r--intern/python/modules/vrml/utils/typeclasses.py50
11 files changed, 3123 insertions, 0 deletions
diff --git a/intern/python/modules/vrml/__init__.py b/intern/python/modules/vrml/__init__.py
new file mode 100644
index 00000000000..9e2ebe0fb86
--- /dev/null
+++ b/intern/python/modules/vrml/__init__.py
@@ -0,0 +1 @@
+"""The VRML import module"""
diff --git a/intern/python/modules/vrml/basenodes.py b/intern/python/modules/vrml/basenodes.py
new file mode 100644
index 00000000000..484f000738a
--- /dev/null
+++ b/intern/python/modules/vrml/basenodes.py
@@ -0,0 +1,974 @@
+from scenegraph import Prototype, NULL, sceneGraph, IS, Script, ExternalPrototype, ROUTE
+PROTO = Prototype
+EXTERNPROTO = ExternalPrototype
+
+Anchor = Prototype( "Anchor",
+ {
+ 'bboxSize':('bboxSize', 'SFVec3f', 0),
+ 'children':('children', 'MFNode', 1),
+ 'parameter':('parameter', 'MFString', 1),
+ 'url':('url', 'MFString', 1),
+ 'description':('description', 'SFString', 1),
+ 'bboxCenter':('bboxCenter', 'SFVec3f', 0),
+ },
+ {
+ 'bboxSize':[-1.0, -1.0, -1.0],
+ 'children':[],
+ 'parameter':[],
+ 'url':[],
+ 'description':'',
+ 'bboxCenter':[0.0, 0.0, 0.0],
+ },
+ {
+ 'addChildren':('addChildren', 'MFNode', 0),
+ 'removeChildren':('removeChildren', 'MFNode', 0),
+ },
+)
+Appearance = Prototype( "Appearance",
+ {
+ 'material':('material', 'SFNode', 1),
+ 'texture':('texture', 'SFNode', 1),
+ 'textureTransform':('textureTransform', 'SFNode', 1),
+ },
+ {
+ 'material':NULL,
+ 'texture':NULL,
+ 'textureTransform':NULL,
+ },
+ {
+ },
+)
+AudioClip = Prototype( "AudioClip",
+ {
+ 'pitch':('pitch', 'SFFloat', 1),
+ 'loop':('loop', 'SFBool', 1),
+ 'description':('description', 'SFString', 1),
+ 'stopTime':('stopTime', 'SFTime', 1),
+ 'startTime':('startTime', 'SFTime', 1),
+ 'url':('url', 'MFString', 1),
+ },
+ {
+ 'pitch':1.0,
+ 'loop':0,
+ 'description':'',
+ 'stopTime':0.0,
+ 'startTime':0.0,
+ 'url':[],
+ },
+ {
+ 'isActive':('isActive', 'SFBool', 1),
+ 'duration_changed':('duration_changed', 'SFTime', 1),
+ },
+)
+Background = Prototype( "Background",
+ {
+ 'groundAngle':('groundAngle', 'MFFloat', 1),
+ 'skyAngle':('skyAngle', 'MFFloat', 1),
+ 'frontUrl':('frontUrl', 'MFString', 1),
+ 'bottomUrl':('bottomUrl', 'MFString', 1),
+ 'groundColor':('groundColor', 'MFColor', 1),
+ 'backUrl':('backUrl', 'MFString', 1),
+ 'skyColor':('skyColor', 'MFColor', 1),
+ 'topUrl':('topUrl', 'MFString', 1),
+ 'rightUrl':('rightUrl', 'MFString', 1),
+ 'leftUrl':('leftUrl', 'MFString', 1),
+ },
+ {
+ 'groundAngle':[],
+ 'skyAngle':[],
+ 'frontUrl':[],
+ 'bottomUrl':[],
+ 'groundColor':[],
+ 'backUrl':[],
+ 'skyColor':[[0.0, 0.0, 0.0]],
+ 'topUrl':[],
+ 'rightUrl':[],
+ 'leftUrl':[],
+ },
+ {
+ 'isBound':('isBound', 'SFBool', 1),
+ 'set_bind':('set_bind', 'SFBool', 0),
+ },
+)
+Billboard = Prototype( "Billboard",
+ {
+ 'bboxCenter':('bboxCenter', 'SFVec3f', 0),
+ 'bboxSize':('bboxSize', 'SFVec3f', 0),
+ 'children':('children', 'MFNode', 1),
+ 'axisOfRotation':('axisOfRotation', 'SFVec3f', 1),
+ },
+ {
+ 'bboxCenter':[0.0, 0.0, 0.0],
+ 'bboxSize':[-1.0, -1.0, -1.0],
+ 'children':[],
+ 'axisOfRotation':[0.0, 1.0, 0.0],
+ },
+ {
+ 'addChildren':('addChildren', 'MFNode', 0),
+ 'removeChildren':('removeChildren', 'MFNode', 0),
+ },
+)
+Box = Prototype( "Box",
+ {
+ 'size':('size', 'SFVec3f', 0),
+ },
+ {
+ 'size':[2.0, 2.0, 2.0],
+ },
+ {
+ },
+)
+
+
+Collision = Prototype( "Collision",
+ {
+ 'bboxCenter':('bboxCenter', 'SFVec3f', 0),
+ 'bboxSize':('bboxSize', 'SFVec3f', 0),
+ 'children':('children', 'MFNode', 1),
+ 'collide':('collide', 'SFBool', 1),
+ 'proxy':('proxy', 'SFNode', 0),
+ },
+ {
+ 'bboxCenter':[0.0, 0.0, 0.0],
+ 'bboxSize':[-1.0, -1.0, -1.0],
+ 'children':[],
+ 'collide':1,
+ 'proxy':NULL,
+ },
+ {
+ 'addChildren':('addChildren', 'MFNode', 0),
+ 'removeChildren':('removeChildren', 'MFNode', 0),
+ 'collideTime':('collideTime', 'SFTime', 1),
+ },
+)
+Color = Prototype( "Color",
+ {
+ 'color':('color', 'MFColor', 1),
+ },
+ {
+ 'color':[],
+ },
+ {
+ },
+)
+ColorInterpolator = Prototype( "ColorInterpolator",
+ {
+ 'key':('key', 'MFFloat', 1),
+ 'keyValue':('keyValue', 'MFColor', 1),
+ },
+ {
+ 'key':[],
+ 'keyValue':[],
+ },
+ {
+ 'value_changed':('value_changed', 'SFColor', 1),
+ 'set_fraction':('set_fraction', 'SFFloat', 0),
+ },
+)
+Cone = Prototype( "Cone",
+ {
+ 'bottomRadius':('bottomRadius', 'SFFloat', 0),
+ 'side':('side', 'SFBool', 0),
+ 'bottom':('bottom', 'SFBool', 0),
+ 'height':('height', 'SFFloat', 0),
+ },
+ {
+ 'bottomRadius':1.0,
+ 'side':1,
+ 'bottom':1,
+ 'height':2.0,
+ },
+ {
+ },
+)
+Coordinate = Prototype( "Coordinate",
+ {
+ 'point':('point', 'MFVec3f', 1),
+ },
+ {
+ 'point':[],
+ },
+ {
+ },
+)
+CoordinateInterpolator = Prototype( "CoordinateInterpolator",
+ {
+ 'key':('key', 'MFFloat', 1),
+ 'keyValue':('keyValue', 'MFVec3f', 1),
+ },
+ {
+ 'key':[],
+ 'keyValue':[],
+ },
+ {
+ 'value_changed':('value_changed', 'MFVec3f', 1),
+ 'set_fraction':('set_fraction', 'SFFloat', 0),
+ },
+)
+Cylinder = Prototype( "Cylinder",
+ {
+ 'bottom':('bottom', 'SFBool', 0),
+ 'side':('side', 'SFBool', 0),
+ 'radius':('radius', 'SFFloat', 0),
+ 'top':('top', 'SFBool', 0),
+ 'height':('height', 'SFFloat', 0),
+ },
+ {
+ 'bottom':1,
+ 'side':1,
+ 'radius':1.0,
+ 'top':1,
+ 'height':2.0,
+ },
+ {
+ },
+)
+CylinderSensor = Prototype( "CylinderSensor",
+ {
+ 'maxAngle':('maxAngle', 'SFFloat', 1),
+ 'autoOffset':('autoOffset', 'SFBool', 1),
+ 'minAngle':('minAngle', 'SFFloat', 1),
+ 'enabled':('enabled', 'SFBool', 1),
+ 'offset':('offset', 'SFFloat', 1),
+ 'diskAngle':('diskAngle', 'SFFloat', 1),
+ },
+ {
+ 'maxAngle':-1.0,
+ 'autoOffset':1,
+ 'minAngle':0.0,
+ 'enabled':1,
+ 'offset':0.0,
+ 'diskAngle':0.262,
+ },
+ {
+ 'rotation_changed':('rotation_changed', 'SFRotation', 1),
+ 'isActive':('isActive', 'SFBool', 1),
+ 'trackPoint_changed':('trackPoint_changed', 'SFVec3f', 1),
+ },
+)
+DirectionalLight = Prototype( "DirectionalLight",
+ {
+ 'color':('color', 'SFColor', 1),
+ 'ambientIntensity':('ambientIntensity', 'SFFloat', 1),
+ 'intensity':('intensity', 'SFFloat', 1),
+ 'on':('on', 'SFBool', 1),
+ 'direction':('direction', 'SFVec3f', 1),
+ },
+ {
+ 'color':[1.0, 1.0, 1.0],
+ 'ambientIntensity':0.0,
+ 'intensity':1.0,
+ 'on':1,
+ 'direction':[0.0, 0.0, -1.0],
+ },
+ {
+ },
+)
+ElevationGrid = Prototype( "ElevationGrid",
+ {
+ 'xSpacing':('xSpacing', 'SFFloat', 0),
+ 'zSpacing':('zSpacing', 'SFFloat', 0),
+ 'xDimension':('xDimension', 'SFInt32', 0),
+ 'colorPerVertex':('colorPerVertex', 'SFBool', 0),
+ 'height':('height', 'MFFloat', 0),
+ 'texCoord':('texCoord', 'SFNode', 1),
+ 'normalPerVertex':('normalPerVertex', 'SFBool', 0),
+ 'ccw':('ccw', 'SFBool', 0),
+ 'color':('color', 'SFNode', 1),
+ 'normal':('normal', 'SFNode', 1),
+ 'creaseAngle':('creaseAngle', 'SFFloat', 0),
+ 'solid':('solid', 'SFBool', 0),
+ 'zDimension':('zDimension', 'SFInt32', 0),
+ },
+ {
+ 'xSpacing':0.0,
+ 'zSpacing':0.0,
+ 'xDimension':0,
+ 'colorPerVertex':1,
+ 'height':[],
+ 'texCoord':NULL,
+ 'normalPerVertex':1,
+ 'ccw':1,
+ 'color':NULL,
+ 'normal':NULL,
+ 'creaseAngle':0.0,
+ 'solid':1,
+ 'zDimension':0,
+ },
+ {
+ 'set_height':('set_height', 'MFFloat', 0),
+ },
+)
+Extrusion = Prototype( "Extrusion",
+ {
+ 'endCap':('endCap', 'SFBool', 0),
+ 'scale':('scale', 'MFVec2f', 0),
+ 'ccw':('ccw', 'SFBool', 0),
+ 'crossSection':('crossSection', 'MFVec2f', 0),
+ 'solid':('solid', 'SFBool', 0),
+ 'convex':('convex', 'SFBool', 0),
+ 'creaseAngle':('creaseAngle', 'SFFloat', 0),
+ 'spine':('spine', 'MFVec3f', 0),
+ 'beginCap':('beginCap', 'SFBool', 0),
+ 'orientation':('orientation', 'MFRotation', 0),
+ },
+ {
+ 'endCap':1,
+ 'scale':[[1.0, 1.0]],
+ 'ccw':1,
+ 'crossSection':[[1.0, 1.0], [1.0, -1.0], [-1.0, -1.0], [-1.0, 1.0], [1.0, 1.0]],
+ 'solid':1,
+ 'convex':1,
+ 'creaseAngle':0.0,
+ 'spine':[[0.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
+ 'beginCap':1,
+ 'orientation':[[0.0, 0.0, 1.0, 0.0]],
+ },
+ {
+ 'set_scale':('set_scale', 'MFVec2f', 0),
+ 'set_spine':('set_spine', 'MFVec3f', 0),
+ 'set_orientation':('set_orientation', 'MFRotation', 0),
+ 'set_crossSection':('set_crossSection', 'MFVec2f', 0),
+ },
+)
+Fog = Prototype( "Fog",
+ {
+ 'fogType':('fogType', 'SFString', 1),
+ 'color':('color', 'SFColor', 1),
+ 'visibilityRange':('visibilityRange', 'SFFloat', 1),
+ },
+ {
+ 'fogType':'LINEAR',
+ 'color':[1.0, 1.0, 1.0],
+ 'visibilityRange':0.0,
+ },
+ {
+ 'isBound':('isBound', 'SFBool', 1),
+ 'set_bind':('set_bind', 'SFBool', 0),
+ },
+)
+FontStyle = Prototype( "FontStyle",
+ {
+ 'justify':('justify', 'MFString', 0),
+ 'leftToRight':('leftToRight', 'SFBool', 0),
+ 'spacing':('spacing', 'SFFloat', 0),
+ 'horizontal':('horizontal', 'SFBool', 0),
+ 'language':('language', 'SFString', 0),
+ 'topToBottom':('topToBottom', 'SFBool', 0),
+ 'size':('size', 'SFFloat', 0),
+ 'style':('style', 'SFString', 0),
+ 'family':('family', 'SFString', 0),
+ },
+ {
+ 'justify':['BEGIN'],
+ 'leftToRight':1,
+ 'spacing':1.0,
+ 'horizontal':1,
+ 'language':'',
+ 'topToBottom':1,
+ 'size':1.0,
+ 'style':'PLAIN',
+ 'family':'SERIF',
+ },
+ {
+ },
+)
+Group = Prototype( "Group",
+ {
+ 'bboxSize':('bboxSize', 'SFVec3f', 0),
+ 'children':('children', 'MFNode', 1),
+ 'bboxCenter':('bboxCenter', 'SFVec3f', 0),
+ },
+ {
+ 'bboxSize':[-1.0, -1.0, -1.0],
+ 'children':[],
+ 'bboxCenter':[0.0, 0.0, 0.0],
+ },
+ {
+ 'addChildren':('addChildren', 'MFNode', 0),
+ 'removeChildren':('removeChildren', 'MFNode', 0),
+ },
+)
+ImageTexture = Prototype( "ImageTexture",
+ {
+ 'repeatS':('repeatS', 'SFBool', 0),
+ 'url':('url', 'MFString', 1),
+ 'repeatT':('repeatT', 'SFBool', 0),
+ },
+ {
+ 'repeatS':1,
+ 'url':[],
+ 'repeatT':1,
+ },
+ {
+ },
+)
+IndexedFaceSet = Prototype( "IndexedFaceSet",
+ {
+ 'texCoordIndex':('texCoordIndex', 'MFInt32', 0),
+ 'normalIndex':('normalIndex', 'MFInt32', 0),
+ 'coordIndex':('coordIndex', 'MFInt32', 0),
+ 'convex':('convex', 'SFBool', 0),
+ 'texCoord':('texCoord', 'SFNode', 1),
+ 'normalPerVertex':('normalPerVertex', 'SFBool', 0),
+ 'coord':('coord', 'SFNode', 1),
+ 'ccw':('ccw', 'SFBool', 0),
+ 'color':('color', 'SFNode', 1),
+ 'normal':('normal', 'SFNode', 1),
+ 'creaseAngle':('creaseAngle', 'SFFloat', 0),
+ 'solid':('solid', 'SFBool', 0),
+ 'colorPerVertex':('colorPerVertex', 'SFBool', 0),
+ 'colorIndex':('colorIndex', 'MFInt32', 0),
+ },
+ {
+ 'texCoordIndex':[],
+ 'normalIndex':[],
+ 'coordIndex':[],
+ 'convex':1,
+ 'texCoord':NULL,
+ 'normalPerVertex':1,
+ 'coord':NULL,
+ 'ccw':1,
+ 'color':NULL,
+ 'normal':NULL,
+ 'creaseAngle':0.0,
+ 'solid':1,
+ 'colorPerVertex':1,
+ 'colorIndex':[],
+ },
+ {
+ 'set_normalIndex':('set_normalIndex', 'MFInt32', 0),
+ 'set_colorIndex':('set_colorIndex', 'MFInt32', 0),
+ 'set_texCoordIndex':('set_texCoordIndex', 'MFInt32', 0),
+ 'set_coordIndex':('set_coordIndex', 'MFInt32', 0),
+ },
+)
+IndexedLineSet = Prototype( "IndexedLineSet",
+ {
+ 'coordIndex':('coordIndex', 'MFInt32', 0),
+ 'coord':('coord', 'SFNode', 1),
+ 'colorIndex':('colorIndex', 'MFInt32', 0),
+ 'colorPerVertex':('colorPerVertex', 'SFBool', 0),
+ 'color':('color', 'SFNode', 1),
+ },
+ {
+ 'coordIndex':[],
+ 'coord':NULL,
+ 'colorIndex':[],
+ 'colorPerVertex':1,
+ 'color':NULL,
+ },
+ {
+ 'set_colorIndex':('set_colorIndex', 'MFInt32', 0),
+ 'set_coordIndex':('set_coordIndex', 'MFInt32', 0),
+ },
+)
+Inline = Prototype( "Inline",
+ {
+ 'url':('url', 'MFString', 1),
+ 'bboxSize':('bboxSize', 'SFVec3f', 0),
+ 'bboxCenter':('bboxCenter', 'SFVec3f', 0),
+ },
+ {
+ 'url':[],
+ 'bboxSize':[-1.0, -1.0, -1.0],
+ 'bboxCenter':[0.0, 0.0, 0.0],
+ },
+ {
+ },
+)
+LOD = Prototype( "LOD",
+ {
+ 'level':('level', 'MFNode', 1),
+ 'range':('range', 'MFFloat', 0),
+ 'center':('center', 'SFVec3f', 0),
+ },
+ {
+ 'level':[],
+ 'range':[],
+ 'center':[0.0, 0.0, 0.0],
+ },
+ {
+ },
+)
+Material = Prototype( "Material",
+ {
+ 'emissiveColor':('emissiveColor', 'SFColor', 1),
+ 'transparency':('transparency', 'SFFloat', 1),
+ 'shininess':('shininess', 'SFFloat', 1),
+ 'diffuseColor':('diffuseColor', 'SFColor', 1),
+ 'ambientIntensity':('ambientIntensity', 'SFFloat', 1),
+ 'specularColor':('specularColor', 'SFColor', 1),
+ },
+ {
+ 'emissiveColor':[0.0, 0.0, 0.0],
+ 'transparency':0.0,
+ 'shininess':0.2,
+ 'diffuseColor':[0.8, 0.8, 0.8],
+ 'ambientIntensity':0.2,
+ 'specularColor':[0.0, 0.0, 0.0],
+ },
+ {
+ },
+)
+MovieTexture = Prototype( "MovieTexture",
+ {
+ 'loop':('loop', 'SFBool', 1),
+ 'speed':('speed', 'SFFloat', 1),
+ 'repeatT':('repeatT', 'SFBool', 0),
+ 'repeatS':('repeatS', 'SFBool', 0),
+ 'url':('url', 'MFString', 1),
+ 'startTime':('startTime', 'SFTime', 1),
+ 'stopTime':('stopTime', 'SFTime', 1),
+ },
+ {
+ 'loop':0,
+ 'speed':1.0,
+ 'repeatT':1,
+ 'repeatS':1,
+ 'url':[],
+ 'startTime':0.0,
+ 'stopTime':0.0,
+ },
+ {
+ 'isActive':('isActive', 'SFBool', 1),
+ 'duration_changed':('duration_changed', 'SFFloat', 1),
+ },
+)
+NavigationInfo = Prototype( "NavigationInfo",
+ {
+ 'avatarSize':('avatarSize', 'MFFloat', 1),
+ 'speed':('speed', 'SFFloat', 1),
+ 'headlight':('headlight', 'SFBool', 1),
+ 'visibilityLimit':('visibilityLimit', 'SFFloat', 1),
+ 'type':('type', 'MFString', 1),
+ },
+ {
+ 'avatarSize':[0.25, 1.6, 0.75],
+ 'speed':1.0,
+ 'headlight':1,
+ 'visibilityLimit':0.0,
+ 'type':['WALK'],
+ },
+ {
+ 'isBound':('isBound', 'SFBool', 1),
+ 'set_bind':('set_bind', 'SFBool', 0),
+ },
+)
+Normal = Prototype( "Normal",
+ {
+ 'vector':('vector', 'MFVec3f', 1),
+ },
+ {
+ 'vector':[],
+ },
+ {
+ },
+)
+NormalInterpolator = Prototype( "NormalInterpolator",
+ {
+ 'key':('key', 'MFFloat', 1),
+ 'keyValue':('keyValue', 'MFVec3f', 1),
+ },
+ {
+ 'key':[],
+ 'keyValue':[],
+ },
+ {
+ 'value_changed':('value_changed', 'MFVec3f', 1),
+ 'set_fraction':('set_fraction', 'SFFloat', 0),
+ },
+)
+OrientationInterpolator = Prototype( "OrientationInterpolator",
+ {
+ 'key':('key', 'MFFloat', 1),
+ 'keyValue':('keyValue', 'MFRotation', 1),
+ },
+ {
+ 'key':[],
+ 'keyValue':[],
+ },
+ {
+ 'value_changed':('value_changed', 'SFRotation', 1),
+ 'set_fraction':('set_fraction', 'SFFloat', 0),
+ },
+)
+PixelTexture = Prototype( "PixelTexture",
+ {
+ 'repeatS':('repeatS', 'SFBool', 0),
+ 'image':('image', 'SFImage', 1),
+ 'repeatT':('repeatT', 'SFBool', 0),
+ },
+ {
+ 'repeatS':1,
+ 'image':[0, 0, 0],
+ 'repeatT':1,
+ },
+ {
+ },
+)
+PlaneSensor = Prototype( "PlaneSensor",
+ {
+ 'offset':('offset', 'SFVec3f', 1),
+ 'autoOffset':('autoOffset', 'SFBool', 1),
+ 'minPosition':('minPosition', 'SFVec2f', 1),
+ 'enabled':('enabled', 'SFBool', 1),
+ 'maxPosition':('maxPosition', 'SFVec2f', 1),
+ },
+ {
+ 'offset':[0.0, 0.0, 0.0],
+ 'autoOffset':1,
+ 'minPosition':[0.0, 0.0],
+ 'enabled':1,
+ 'maxPosition':[-1.0, -1.0],
+ },
+ {
+ 'translation_changed':('translation_changed', 'SFVec3f', 1),
+ 'isActive':('isActive', 'SFBool', 1),
+ 'trackPoint_changed':('trackPoint_changed', 'SFVec3f', 1),
+ },
+)
+PointLight = Prototype( "PointLight",
+ {
+ 'ambientIntensity':('ambientIntensity', 'SFFloat', 1),
+ 'color':('color', 'SFColor', 1),
+ 'location':('location', 'SFVec3f', 1),
+ 'radius':('radius', 'SFFloat', 1),
+ 'attenuation':('attenuation', 'SFVec3f', 1),
+ 'intensity':('intensity', 'SFFloat', 1),
+ 'on':('on', 'SFBool', 1),
+ },
+ {
+ 'ambientIntensity':0.0,
+ 'color':[1.0, 1.0, 1.0],
+ 'location':[0.0, 0.0, 0.0],
+ 'radius':100.0,
+ 'attenuation':[1.0, 0.0, 0.0],
+ 'intensity':1.0,
+ 'on':1,
+ },
+ {
+ },
+)
+PointSet = Prototype( "PointSet",
+ {
+ 'coord':('coord', 'SFNode', 1),
+ 'color':('color', 'SFNode', 1),
+ },
+ {
+ 'coord':NULL,
+ 'color':NULL,
+ },
+ {
+ },
+)
+PositionInterpolator = Prototype( "PositionInterpolator",
+ {
+ 'key':('key', 'MFFloat', 1),
+ 'keyValue':('keyValue', 'MFVec3f', 1),
+ },
+ {
+ 'key':[],
+ 'keyValue':[],
+ },
+ {
+ 'value_changed':('value_changed', 'SFVec3f', 1),
+ 'set_fraction':('set_fraction', 'SFFloat', 0),
+ },
+)
+ProximitySensor = Prototype( "ProximitySensor",
+ {
+ 'size':('size', 'SFVec3f', 1),
+ 'center':('center', 'SFVec3f', 1),
+ 'enabled':('enabled', 'SFBool', 1),
+ },
+ {
+ 'size':[0.0, 0.0, 0.0],
+ 'center':[0.0, 0.0, 0.0],
+ 'enabled':1,
+ },
+ {
+ 'enterTime':('enterTime', 'SFTime', 1),
+ 'isActive':('isActive', 'SFBool', 1),
+ 'orientation_changed':('orientation_changed', 'SFRotation', 1),
+ 'exitTime':('exitTime', 'SFTime', 1),
+ 'position_changed':('position_changed', 'SFVec3f', 1),
+ },
+)
+ScalarInterpolator = Prototype( "ScalarInterpolator",
+ {
+ 'key':('key', 'MFFloat', 1),
+ 'keyValue':('keyValue', 'MFFloat', 1),
+ },
+ {
+ 'key':[],
+ 'keyValue':[],
+ },
+ {
+ 'value_changed':('value_changed', 'SFFloat', 1),
+ 'set_fraction':('set_fraction', 'SFFloat', 0),
+ },
+)
+Shape = Prototype( "Shape",
+ {
+ 'appearance':('appearance', 'SFNode', 1),
+ 'geometry':('geometry', 'SFNode', 1),
+ },
+ {
+ 'appearance':NULL,
+ 'geometry':NULL,
+ },
+ {
+ },
+)
+Sound = Prototype( "Sound",
+ {
+ 'spatialize':('spatialize', 'SFBool', 0),
+ 'maxFront':('maxFront', 'SFFloat', 1),
+ 'minBack':('minBack', 'SFFloat', 1),
+ 'maxBack':('maxBack', 'SFFloat', 1),
+ 'minFront':('minFront', 'SFFloat', 1),
+ 'location':('location', 'SFVec3f', 1),
+ 'intensity':('intensity', 'SFFloat', 1),
+ 'direction':('direction', 'SFVec3f', 1),
+ 'source':('source', 'SFNode', 1),
+ 'priority':('priority', 'SFFloat', 1),
+ },
+ {
+ 'spatialize':1,
+ 'maxFront':10.0,
+ 'minBack':1.0,
+ 'maxBack':10.0,
+ 'minFront':1.0,
+ 'location':[0.0, 0.0, 0.0],
+ 'intensity':1.0,
+ 'direction':[0.0, 0.0, 1.0],
+ 'source':NULL,
+ 'priority':0.0,
+ },
+ {
+ },
+)
+Sphere = Prototype( "Sphere",
+ {
+ 'radius':('radius', 'SFFloat', 0),
+ },
+ {
+ 'radius':1.0,
+ },
+ {
+ },
+)
+SphereSensor = Prototype( "SphereSensor",
+ {
+ 'offset':('offset', 'SFRotation', 1),
+ 'autoOffset':('autoOffset', 'SFBool', 1),
+ 'enabled':('enabled', 'SFBool', 1),
+ },
+ {
+ 'offset':[0.0, 1.0, 0.0, 0.0],
+ 'autoOffset':1,
+ 'enabled':1,
+ },
+ {
+ 'rotation_changed':('rotation_changed', 'SFRotation', 1),
+ 'isActive':('isActive', 'SFBool', 1),
+ 'trackPoint_changed':('trackPoint_changed', 'SFVec3f', 1),
+ },
+)
+SpotLight = Prototype( "SpotLight",
+ {
+ 'attenuation':('attenuation', 'SFVec3f', 1),
+ 'ambientIntensity':('ambientIntensity', 'SFFloat', 1),
+ 'cutOffAngle':('cutOffAngle', 'SFFloat', 1),
+ 'direction':('direction', 'SFVec3f', 1),
+ 'color':('color', 'SFColor', 1),
+ 'location':('location', 'SFVec3f', 1),
+ 'radius':('radius', 'SFFloat', 1),
+ 'intensity':('intensity', 'SFFloat', 1),
+ 'beamWidth':('beamWidth', 'SFFloat', 1),
+ 'on':('on', 'SFBool', 1),
+ },
+ {
+ 'attenuation':[1.0, 0.0, 0.0],
+ 'ambientIntensity':0.0,
+ 'cutOffAngle':0.785398,
+ 'direction':[0.0, 0.0, -1.0],
+ 'color':[1.0, 1.0, 1.0],
+ 'location':[0.0, 0.0, 0.0],
+ 'radius':100.0,
+ 'intensity':1.0,
+ 'beamWidth':1.570796,
+ 'on':1,
+ },
+ {
+ },
+)
+Switch = Prototype( "Switch",
+ {
+ 'choice':('choice', 'MFNode', 1),
+ 'whichChoice':('whichChoice', 'SFInt32', 1),
+ },
+ {
+ 'choice':[],
+ 'whichChoice':-1,
+ },
+ {
+ },
+)
+Text = Prototype( "Text",
+ {
+ 'maxExtent':('maxExtent', 'SFFloat', 1),
+ 'string':('string', 'MFString', 1),
+ 'fontStyle':('fontStyle', 'SFNode', 1),
+ 'length':('length', 'MFFloat', 1),
+ },
+ {
+ 'maxExtent':0.0,
+ 'string':[],
+ 'fontStyle':NULL,
+ 'length':[],
+ },
+ {
+ },
+)
+TextureCoordinate = Prototype( "TextureCoordinate",
+ {
+ 'point':('point', 'MFVec2f', 1),
+ },
+ {
+ 'point':[],
+ },
+ {
+ },
+)
+TextureTransform = Prototype( "TextureTransform",
+ {
+ 'center':('center', 'SFVec2f', 1),
+ 'scale':('scale', 'SFVec2f', 1),
+ 'rotation':('rotation', 'SFFloat', 1),
+ 'translation':('translation', 'SFVec2f', 1),
+ },
+ {
+ 'center':[0.0, 0.0],
+ 'scale':[1.0, 1.0],
+ 'rotation':0.0,
+ 'translation':[0.0, 0.0],
+ },
+ {
+ },
+)
+TimeSensor = Prototype( "TimeSensor",
+ {
+ 'loop':('loop', 'SFBool', 1),
+ 'cycleInterval':('cycleInterval', 'SFTime', 1),
+ 'enabled':('enabled', 'SFBool', 1),
+ 'stopTime':('stopTime', 'SFTime', 1),
+ 'startTime':('startTime', 'SFTime', 1),
+ },
+ {
+ 'loop':0,
+ 'cycleInterval':1.0,
+ 'enabled':1,
+ 'stopTime':0.0,
+ 'startTime':0.0,
+ },
+ {
+ 'fraction_changed':('fraction_changed', 'SFFloat', 1),
+ 'isActive':('isActive', 'SFBool', 1),
+ 'time':('time', 'SFTime', 1),
+ 'cycleTime':('cycleTime', 'SFTime', 1),
+ },
+)
+TouchSensor = Prototype( "TouchSensor",
+ {
+ 'enabled':('enabled', 'SFBool', 1),
+ },
+ {
+ 'enabled':1,
+ },
+ {
+ 'hitNormal_changed':('hitNormal_changed', 'SFVec3f', 1),
+ 'hitPoint_changed':('hitPoint_changed', 'SFVec3f', 1),
+ 'touchTime':('touchTime', 'SFTime', 1),
+ 'hitTexCoord_changed':('hitTexCoord_changed', 'SFVec2f', 1),
+ 'isActive':('isActive', 'SFBool', 1),
+ 'isOver':('isOver', 'SFBool', 1),
+ },
+)
+Transform = Prototype( "Transform",
+ {
+ 'bboxSize':('bboxSize', 'SFVec3f', 0),
+ 'children':('children', 'MFNode', 1),
+ 'scaleOrientation':('scaleOrientation', 'SFRotation', 1),
+ 'rotation':('rotation', 'SFRotation', 1),
+ 'translation':('translation', 'SFVec3f', 1),
+ 'bboxCenter':('bboxCenter', 'SFVec3f', 0),
+ 'center':('center', 'SFVec3f', 1),
+ 'scale':('scale', 'SFVec3f', 1),
+ },
+ {
+ 'bboxSize':[-1.0, -1.0, -1.0],
+ 'children':[],
+ 'scaleOrientation':[0.0, 0.0, 1.0, 0.0],
+ 'rotation':[0.0, 0.0, 1.0, 0.0],
+ 'translation':[0.0, 0.0, 0.0],
+ 'bboxCenter':[0.0, 0.0, 0.0],
+ 'center':[0.0, 0.0, 0.0],
+ 'scale':[1.0, 1.0, 1.0],
+ },
+ {
+ 'addChildren':('addChildren', 'MFNode', 0),
+ 'removeChildren':('removeChildren', 'MFNode', 0),
+ },
+)
+Viewpoint = Prototype( "Viewpoint",
+ {
+ 'jump':('jump', 'SFBool', 1),
+ 'orientation':('orientation', 'SFRotation', 1),
+ 'fieldOfView':('fieldOfView', 'SFFloat', 1),
+ 'position':('position', 'SFVec3f', 1),
+ 'description':('description', 'SFString', 0),
+ },
+ {
+ 'jump':1,
+ 'orientation':[0.0, 0.0, 1.0, 0.0],
+ 'fieldOfView':0.785398,
+ 'position':[0.0, 0.0, 10.0],
+ 'description':'',
+ },
+ {
+ 'isBound':('isBound', 'SFBool', 1),
+ 'set_bind':('set_bind', 'SFBool', 0),
+ 'bindTime':('bindTime', 'SFTime', 1),
+ },
+)
+VisibilitySensor = Prototype( "VisibilitySensor",
+ {
+ 'size':('size', 'SFVec3f', 1),
+ 'center':('center', 'SFVec3f', 1),
+ 'enabled':('enabled', 'SFBool', 1),
+ },
+ {
+ 'size':[0.0, 0.0, 0.0],
+ 'center':[0.0, 0.0, 0.0],
+ 'enabled':1,
+ },
+ {
+ 'exitTime':('exitTime', 'SFTime', 1),
+ 'isActive':('isActive', 'SFBool', 1),
+ 'enterTime':('enterTime', 'SFTime', 1),
+ },
+)
+WorldInfo = Prototype( "WorldInfo",
+ {
+ 'title':('title', 'SFString', 0),
+ 'info':('info', 'MFString', 0),
+ },
+ {
+ 'title':'',
+ 'info':[],
+ },
+ {
+ },
+)
diff --git a/intern/python/modules/vrml/fieldcoercian.py b/intern/python/modules/vrml/fieldcoercian.py
new file mode 100644
index 00000000000..a90f1101b5a
--- /dev/null
+++ b/intern/python/modules/vrml/fieldcoercian.py
@@ -0,0 +1,310 @@
+'''
+Field coercian routines.
+
+To replace the field coercian routines, you must edit
+basenodes.py and node.py to import some other coercian
+routines. Basenodes.py is for use by the parser, node
+is used by each node as it checks the validity of its
+attributes.
+'''
+
+import types, sys, string
+from utils import typeclasses, collapse
+
+class FieldCoercian:
+ '''
+ A Field Coercian class allows for creating new behaviours
+ when dealing with the conversion of fields to-and-from
+ particular field types. This allows the programmer to
+ use alternate representations of fields (such as matrix arrays)
+ '''
+ def SFString( self, someobj, targetType=types.StringType, targetName='SFString', convertfunc=str ):
+ '''
+ Allowable types:
+ simple string -> unchanged
+ instance ( an IS ) -> unchanged
+ sequence of length == 1 where first element is a string -> returns first element
+ sequence of length > 1 where all elements are strings -> returns string.join( someobj, '')
+ '''
+ t = type(someobj)
+ if t is targetType:
+ return someobj
+ if t in typeclasses.SequenceTypes:
+ if len( someobj) == 1 and type( someobj[0] ) is targetType:
+ return someobj[0] #
+ elif len(someobj) > 1:
+ try:
+ return string.join( someobj, '')
+ except:
+ pass # is not a sequence of strings...
+ ### if we get here, then an incorrect value was passed
+ raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
+
+ def MFString( self, someobj, targetType=types.StringType, targetName='SFString', convertfunc=str ):
+ '''
+ Allowable Types:
+ simple string -> wrapped in a list
+ instance (an IS ) -> unchanged
+ sequence of strings (of any length) -> equivalent list returned
+ '''
+ t = type(someobj)
+ if t is targetType: # a bare string...
+ return [someobj]
+ elif t in typeclasses.SequenceTypes: # is a sequence
+ if not filter( lambda x, t=targetType: x is not t, map( type, someobj) ): # are all strings...
+ if t is not types.ListType:
+ return list( someobj )
+ else:
+ return someobj
+ ### if we get here, then an incorrect value was passed
+ raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
+
+ def SFBool( self, someobj, targetType=types.IntType, targetName='SFBool', convertfunc=int):
+ '''
+ Allowable Types:
+ instance (an IS) -> unchanged
+ Any object which is testable for truth/falsehood -> 1 or 0 respectively
+ SFBool should always succeed
+ '''
+ if (type(someobj) in typeclasses.SequenceTypes):
+ try:
+ if hasattr( someobj[0], '__gi__'):
+ return someobj[0]
+ else:
+ someobj = someobj[0]
+ except IndexError: # is a null MFNode
+ pass
+ if someobj:
+ return 1
+ else:
+ return 0
+
+ def SFNode( self, someobj, targetType=types.InstanceType, targetName='SFNode', convertfunc=None):
+ '''
+ Allowable Types:
+ instance of a Node -> unchanged
+ instance (an IS or USE) -> unchanged
+ sequence of length == 1 where first element is as above -> return first element
+ '''
+ if hasattr( someobj, '__gi__'): # about the only test I have without requiring that elements inherit from Node
+ return someobj
+ elif (type(someobj) in typeclasses.SequenceTypes):
+ try:
+ if hasattr( someobj[0], '__gi__'):
+ return someobj[0]
+ except IndexError: # is a null MFNode
+ pass
+ raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
+
+ def MFNode( self, someobj, targetType=types.InstanceType, targetName='MFNode', convertfunc=None):
+ '''
+ Allowable Types:
+ instance (an IS) -> unchanged
+ instance of a Node -> wrapped with a list
+ sequence where all elements are nodes -> returned as list of same
+ '''
+ if hasattr( someobj, '__gi__') and someobj.__gi__ != "IS":
+ # is this a bare SFNode? wrap with a list and return
+ return [someobj]
+ elif hasattr( someobj, "__gi__"): # is this an IS node
+ return someobj
+ elif type(someobj) in typeclasses.SequenceTypes:
+ try:
+ map( getattr, someobj, ['__gi__']*len(someobj) )
+ # is this an IS node wrapped in a list?
+ if len(someobj) == 1 and someobj[0].__gi__ == "IS":
+ return someobj[0]
+ # okay, assume is really nodes...
+ if type(someobj) is types.ListType:
+ return someobj
+ else:
+ return list(someobj)
+ except AttributeError: # something isn't a node
+ pass
+ raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
+
+ def SFNumber( self, someobj, targetType, targetName, convertfunc=int ):
+ '''
+ Allowable Types:
+ bare number -> numerically coerced to correct type
+ instance ( an IS ) -> unchanged
+ sequence of length == 1 where first element is a string -> returns first element
+ '''
+ t = type(someobj)
+ if t is targetType or t is types.InstanceType:
+ return someobj
+ elif t in typeclasses.NumericTypes:
+ return convertfunc( someobj)
+ elif t in typeclasses.SequenceTypes:
+ if len( someobj) == 1 and type( someobj[0] ):
+ return convertfunc( someobj[0] ) #
+ ### if we get here, then an incorrect value was passed
+ raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
+ def MFInt32 ( self, someobject ):
+ ''' Convert value into a MFInt32 field value (preferably an array, otherwise a list of integers) '''
+ t = type(someobject)
+ value = None
+ if t in typeclasses.SequenceTypes: # is a sequence
+ try:
+ value = map( int, someobject)
+ except:
+ try:
+ value = map( int, collapse.collapse2_safe( someobject) )
+ except:
+ pass
+ elif t in typeclasses.NumericTypes or t is types.StringType:
+ value = [int(someobject)]
+ if value is None:
+ ### if we get here, then an incorrect value was passed
+ raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
+ return value
+ SFImage = MFInt32
+ def MFFloat( self, someobject ):
+ ''' Convert value into a MFFloat field value (preferably an array, otherwise a list of integers) '''
+ t = type(someobject)
+ value = None
+ if t in typeclasses.SequenceTypes: # is a sequence
+ try:
+ value = map( float, someobject)
+ except:
+ try:
+ value = map( float, collapse.collapse2_safe( someobject))
+ except:
+ pass
+ elif t in typeclasses.NumericTypes or t is types.StringType:
+ value = [float(someobj)]
+ if value is None:
+ ### if we get here, then an incorrect value was passed
+ raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
+ return value
+ def SFVec3f (self, value):
+ ''' Create a new SFVec3f value from value '''
+ t = type(value)
+ try:
+ value = x,y,z = map (float, value)
+ except ValueError:
+ try:
+ value = (x,y,z) = map( float, value[0] )
+ except (IndexError, ValueError):
+ raise ValueError (''' Invalid value for field type SFVec3f: %s'''%(value))
+ return value
+ def SFRotation(self, value):
+ ''' Create a new SFRotation value from value '''
+ t = type(value)
+ try:
+ value = x,y,z, a = map (float, value)
+ except ValueError:
+ try:
+ value = (x,y,z, a) = map( float, value[0] )
+ except (IndexError, ValueError):
+ raise ValueError (''' Invalid value for field type SFRotation: %s'''%(value))
+ # get the normalized vector for x,y,z
+## length = (x*x+y*y+z*z)**.5 or 0.0000
+## value = (x/length,y/length,z/length, a)
+ return value
+ def SFVec2f (self, value):
+ ''' Create a new SFVec3f value from value '''
+ t = type(value)
+ try:
+ value = x,y = map (float, value)
+ except ValueError:
+ try:
+ value = (x,y) = map( float, value[0] )
+ except (IndexError, ValueError):
+ raise ValueError (''' Invalid value for field type SFVec3f: %s'''%(value))
+ return value
+ def SFColor(self, value):
+ ''' Create a new SFVec3f value from value '''
+ t = type(value)
+ try:
+ r,g,b = map (float, value)
+ except ValueError:
+ try:
+ r,g,b = map( float, value[0] )
+ except (IndexError, ValueError):
+ raise ValueError (''' Invalid value for field type SFColor: %s'''%(value))
+ r = max( (0.0, min((r,1.0))) )
+ g = max( (0.0, min((g,1.0))) )
+ b = max( (0.0, min((b,1.0))) )
+ return value
+
+ def MFCompoundNumber( self, someobj, targetName='SFVec3f', convertfunc=float, type=type):
+ '''
+ Allowable Types:
+ instance ( an IS ) -> unchanged
+ # instance ( a matrix ) -> reshaped (eventually)
+ list of lists, sub-sequences of proper length -> unchanged
+ sequence of numeric types of proper length -> converted to list, diced
+ '''
+## if targetName == 'SFColor':
+## import pdb
+## pdb.set_trace()
+ converter = getattr( self, targetName )
+ t = type( someobj)
+ reporterror = 0
+ if t is types.InstanceType:
+ return someobj
+ elif t in typeclasses.SequenceTypes:
+ if not someobj:
+ return []
+ if type( someobj[0] ) is not types.StringType and type( someobj[0] ) in typeclasses.SequenceTypes:
+ try:
+ return map( converter, someobj )
+ except ValueError:
+ pass
+ elif type( someobj[0] ) in typeclasses.NumericTypes or type( someobj[0] ) is types.StringType:
+ # a single-level list?
+ base = map( convertfunc, someobj )
+ # if we get here, someobj is a list
+ if targetName[-2:] == '2f': # vec2f
+ tlen = 2
+ elif targetName[-2:] == 'on': # rotation
+ tlen = 4
+ else:
+ tlen = 3
+ value = []
+ while base:
+ value.append( converter( base[:tlen]) )
+ del base[:tlen]
+ return value
+ raise ValueError, """Attempted to set value for an %s field which is not compatible: %s"""%( targetName, `someobj` )
+ def __call__( self, someobj, targetName):
+ func, args = self.algomap[targetName]
+## try:
+## if targetName == 'SFInt32':
+## import pdb
+## pdb.set_trace()
+ if hasattr( someobj, "__gi__") and someobj.__gi__ == "IS":
+ return someobj
+ else:
+ return apply( func, (self, someobj)+args )
+## except TypeError:
+## print someobj, targetName
+## print func, args
+## raise
+
+ algomap = { \
+ 'SFString': (SFString, (types.StringType, 'SFString', str)), \
+ 'MFString': (MFString, (types.StringType, 'MFString', str)), \
+ 'SFInt32': (SFNumber, (types.IntType, 'SFInt32', int)), \
+ 'SFFloat': (SFNumber, (types.FloatType, 'SFFloat', float)), \
+ 'SFTime': (SFNumber, (types.FloatType, 'SFFloat', float)), \
+ 'SFColor': (SFColor, ()), \
+ 'SFVec2f': (SFVec2f, ()), \
+ 'SFVec3f': (SFVec3f, ()), \
+ 'SFNode': (SFNode, (types.InstanceType, 'SFNode', None)), \
+ 'SFBool': (SFBool, (types.IntType, 'SFBool', int)), \
+ 'SFNode': (SFNode, (types.InstanceType, 'SFNode', None)), \
+ 'MFInt32': (MFInt32, ()), \
+ 'SFImage': (MFInt32, ()), \
+ 'MFTime': (MFFloat, ()), \
+ 'MFFloat': (MFFloat, ()), \
+ 'MFColor': (MFCompoundNumber, ('SFColor', float)), \
+ 'MFVec2f': (MFCompoundNumber, ('SFVec2f', float)), \
+ 'MFVec3f': (MFCompoundNumber, ('SFVec3f', float)), \
+ 'SFRotation': (SFRotation, ()), \
+ 'MFRotation': (MFCompoundNumber, ('SFRotation', float)), \
+ 'MFNode': (MFNode, (types.InstanceType, 'MFNode', None)) \
+ }
+
+FIELDCOERCE = FieldCoercian ()
diff --git a/intern/python/modules/vrml/loader.py b/intern/python/modules/vrml/loader.py
new file mode 100644
index 00000000000..dd53fe49fd3
--- /dev/null
+++ b/intern/python/modules/vrml/loader.py
@@ -0,0 +1,97 @@
+# The VRML loader
+# supports gzipped files
+#
+# TODO: better progress monitoring
+
+import parser
+
+def quiet(txt):
+ pass
+
+debug = quiet
+
+def debug1(txt):
+ print "Loader:", txt
+
+g_last = 0
+
+def getFileType(file):
+ "returns the file type string from 'file'"
+ file.seek(0)
+ magic = file.readline()
+ if magic[:3] == '\037\213\010':
+ file.seek(0)
+ return "gzip"
+ elif magic[:10] == '#VRML V2.0':
+ file.seek(0)
+ return "vrml"
+ else:
+ file.seek(0)
+ return ""
+
+class Loader:
+ def __init__(self, url, progress = None):
+ self.url = url
+ self.debug = debug
+ self.fail = debug
+ self.monitor = debug
+ self.progress = progress
+ self.nodes = 0 # number of nodes parsed
+
+ def getGzipFile(self, file):
+ '''Return gzip file (only called when gzip type is recognised)'''
+ # we now have the local filename and the headers
+ # read the first few bytes, check for gzip magic number
+ self.monitor( "gzip-encoded file... loading gzip library")
+ try:
+ import gzip
+ file = gzip.open(file,"rb")
+ return file
+ except ImportError, value:
+ self.fail("Gzip library unavailable, compressed file cannot be read")
+ except:
+ self.fail("Failed to open Gzip file")
+
+ return None
+
+ def load(self):
+ self.debug("try: load file from %s" % self.url)
+ url = self.url
+
+ # XXX
+ try:
+ file = open(url, 'rb')
+ except IOError, val:
+ self.debug("couldn't open file %s" % url)
+ return None
+
+ if getFileType(file) == 'gzip':
+ file.close()
+ file = self.getGzipFile(url)
+ try:
+ data = file.read()
+ except MemoryError, value:
+ self.fail("Insufficient memory to load file as string", value)
+ return None
+ except IOError, value:
+ self.fail("I/O Error while reading data from file %s "% url)
+ p = parser.Parser(data)
+ if self.progress:
+ scenegraph = p.parse(self.progress)
+ print "progress"
+ else:
+ scenegraph = p.parse()
+
+ self.nodes = p.progresscount # progress
+ del p
+ return scenegraph
+
+
+def load(url, progress = None):
+ l = Loader(url, progress)
+ return l.load()
+
+def test(name = None):
+ if not name:
+ name = '/tmp/gna.wrl'
+ return load(name)
diff --git a/intern/python/modules/vrml/parser.py b/intern/python/modules/vrml/parser.py
new file mode 100644
index 00000000000..1f238126550
--- /dev/null
+++ b/intern/python/modules/vrml/parser.py
@@ -0,0 +1,426 @@
+from TextTools import TextTools
+
+from simpleparse import generator
+
+import scenegraph as proto
+import strop as string
+
+IMPORT_PARSE_TIME = 0.4
+PROGRESS_DEPTH = 5
+
+class UnfinishedError(Exception):
+ pass
+
+class Parser:
+ def __init__( self, data ):
+ self.data = data
+ self.position = 0
+ self.result = proto.sceneGraph()
+ self.finalised = None
+ self.sceneGraphStack = [self.result]
+ self.prototypeStack = []
+ self.nodeStack = []
+ self.fieldTypeStack = []
+ self.readHeader()
+ self.depth = 0
+ self.progresscount = 0
+ def _lines( self, index=None ):
+ if index is None:
+ index = self.position
+ return TextTools.countlines (self.data[:index])
+ def parse( self, progressCallback=None ):
+ datalength = float( len( self.data ))
+ while self.readNext():
+ if progressCallback:
+ if not progressCallback(IMPORT_PARSE_TIME * self.position/datalength ):
+ raise UnfinishedError(
+ "Did not complete parsing, cancelled by user. Stopped at line %s" %(self._lines())
+ )
+ if self.position < len( self.data ):
+ raise UnfinishedError(
+ '''Unable to complete parsing of file, stopped at line %s:\n%s...'''%(self._lines(), self.data[self.position:self.position+120])
+ )
+ return self.result
+ def readHeader( self ):
+ '''Read the file header'''
+ success, tags, next = TextTools.tag( self.data, HEADERPARSER, self.position )
+ if success:
+ self.datalength = len( self.data )
+ #print "header ok"
+ return success
+ else:
+ try:
+ self.decompress()
+ success, tags, next = TextTools.tag( self.data, HEADERPARSER, self.position )
+ self.datalength = len( self.data )
+ return success
+ except:
+ raise ValueError( "Could not find VRML97 header in file!" )
+ def readNext( self):
+ '''Read the next root-level construct'''
+ success, tags, next = TextTools.tag( self.data, ROOTITEMPARSER, self.position )
+## print 'readnext', success
+ if self.position >= self.datalength:
+ print 'reached file end'
+ return None
+ if success:
+# print ' successful parse'
+ self.position = next
+ map (self.rootItem_Item, tags )
+ return success
+ else:
+ return None
+ def rootItem (self, (type, start, stop, (item,))):
+ ''' Process a single root item '''
+ self.rootItem_Item( item )
+ def rootItem_Item( self, item ):
+ result = self._dispatch(item)
+ if result is not None:
+## print "non-null result"
+## print id( self.sceneGraphStack[-1] ), id(self.result )
+ self.sceneGraphStack[-1].children.append( result )
+ def _getString (self, (tag, start, stop, sublist)):
+ ''' Return the raw string for a given interval in the data '''
+ return self.data [start: stop]
+
+ def _dispatch (self, (tag, left, right, sublist)):
+ ''' Dispatch to the appropriate processing function based on tag value '''
+## print "dispatch", tag
+ self.depth += 1
+ if self.depth < PROGRESS_DEPTH:
+ self.progresscount += 1
+ try:
+ meth = getattr (self, tag)
+ except AttributeError:
+ raise AttributeError("Unknown parse tag '%s' found! Check the parser definition!" % (tag))
+ ret = meth( (tag, left, right, sublist) )
+ self.depth -= 1
+ return ret
+
+ def Proto(self, (tag, start, stop, sublist)):
+ ''' Create a new prototype in the current sceneGraph '''
+ # first entry is always ID
+ ID = self._getString ( sublist [0])
+ print "PROTO",ID
+ newNode = proto.Prototype (ID)
+## print "\t",newNode
+ setattr ( self.sceneGraphStack [-1].protoTypes, ID, newNode)
+ self.prototypeStack.append( newNode )
+ # process the rest of the entries with the given stack
+ map ( self._dispatch, sublist [1:] )
+ self.prototypeStack.pop( )
+ def fieldDecl(self,(tag, left, right, (exposure, datatype, name, field))):
+ ''' Create a new field declaration for the current prototype'''
+ # get the definition in recognizable format
+ exposure = self._getString (exposure) == "exposedField"
+ datatype = self._getString (datatype)
+ name = self._getString (name)
+ # get the vrml value for the field
+ self.fieldTypeStack.append( datatype )
+ field = self._dispatch (field)
+ self.fieldTypeStack.pop( )
+ self.prototypeStack[-1].addField ((name, datatype, exposure), field)
+ def eventDecl(self,(tag, left, right, (direction, datatype, name))):
+ # get the definition in recognizable format
+ direction = self._getString (direction) == "eventOut"
+ datatype = self._getString (datatype)
+ name = self._getString (name)
+ # get the vrml value for the field
+ self.prototypeStack[-1].addEvent((name, datatype, direction))
+ def decompress( self ):
+ pass
+ def ExternProto( self, (tag, start, stop, sublist)):
+ ''' Create a new external prototype from a tag list'''
+ # first entry is always ID
+ ID = self._getString ( sublist [0])
+ newNode = proto.Prototype (ID)
+ setattr ( self.sceneGraphStack [-1].protoTypes, ID, newNode)
+ self.prototypeStack.append( newNode )
+ # process the rest of the entries with the given stack
+ map ( self._dispatch, sublist [1:] )
+ self.prototypeStack.pop( )
+ def ExtProtoURL( self, (tag, start, stop, sublist)):
+ ''' add the url to the external prototype '''
+## print sublist
+ values = self.MFString( sublist )
+ self.prototypeStack[-1].url = values
+ return values
+ def extFieldDecl(self, (tag, start, stop, (exposure, datatype, name))):
+ ''' An external field declaration, no default value '''
+ # get the definition in recognizable format
+ exposure = self._getString (exposure) == "exposedField"
+ datatype = self._getString (datatype)
+ name = self._getString (name)
+ # get the vrml value for the field
+ self.prototypeStack[-1].addField ((name, datatype, exposure))
+ def ROUTE(self, (tag, start, stop, names )):
+ ''' Create a new route object, add the current sceneGraph '''
+ names = map(self._getString, names)
+ self.sceneGraphStack [-1].addRoute( names )
+ def Node (self, (tag, start, stop, sublist)):
+ ''' Create new node, returning the value to the caller'''
+## print 'node'
+
+ if sublist[0][0] == 'name':
+ name = self._getString ( sublist [0])
+ ID = self._getString ( sublist [1])
+ rest = sublist [2:]
+ else:
+ name = ""
+ ID = self._getString ( sublist [0])
+ rest = sublist [1:]
+ try:
+ prototype = getattr ( self.sceneGraphStack [-1].protoTypes, ID)
+ except AttributeError:
+ #raise NameError ('''Prototype %s used without declaration! %s:%s'''%(ID, start, stop) )
+ print ('''### Prototype %s used without declaration! %s:%s'''%(ID, start, stop) )
+
+ return None
+ newNode = prototype(name)
+ if name:
+ self.sceneGraphStack [-1].regDefName( name, newNode )
+ self.nodeStack.append (newNode)
+ map (self._dispatch, rest)
+ self.nodeStack.pop ()
+## print 'node finished'
+ return newNode
+ def Attr(self, (tag, start, stop, (name, value))):
+ ''' An attribute of a node or script '''
+ name = self._getString ( name )
+ self.fieldTypeStack.append( self.nodeStack[-1].PROTO.getField( name ).type )
+ value = self._dispatch( value )
+ self.fieldTypeStack.pop()
+ if hasattr( self.nodeStack[-1], "__setattr__" ):
+ self.nodeStack[-1].__setattr__( name, value, raw=1 )
+ else:
+ # use slower coercing versions...
+ setattr( self.nodeStack[-1], name, value )
+ def Script( self, (tag, start, stop, sublist)):
+ ''' A script node (can be a root node)'''
+ # what's the DEF name...
+ if sublist and sublist[0][0] == 'name':
+ name = self._getString ( sublist [0])
+ rest = sublist [1:]
+ else:
+ name = ""
+ rest = sublist
+ # build the script node...
+ newNode = proto.Script( name )
+ # register with sceneGraph
+ if name:
+ self.sceneGraphStack [-1].regDefName( name, newNode )
+ self.nodeStack.append (newNode)
+ map( self._dispatch, rest )
+ self.nodeStack.pop ()
+ return newNode
+ def ScriptEventDecl( self,(tag, left, right, sublist)):
+ # get the definition in recognizable format
+ direction, datatype, name = sublist[:3] # must have at least these...
+ direction = self._getString (direction) == "eventOut"
+ datatype = self._getString (datatype)
+ name = self._getString (name)
+ # get the vrml value for the field
+ self.nodeStack[-1].PROTO.addEvent((name, datatype, direction))
+ if sublist[3:]:
+ # will this work???
+ setattr( self.nodeStack[-1], name, self._dispatch( sublist[3] ) )
+ def ScriptFieldDecl(self,(tag, left, right, (exposure, datatype, name, field))):
+ ''' Create a new field declaration for the current prototype'''
+ # get the definition in recognizable format
+ exposure = self._getString (exposure) == "exposedField"
+ datatype = self._getString (datatype)
+ name = self._getString (name)
+ # get the vrml value for the field
+ self.fieldTypeStack.append( datatype )
+ field = self._dispatch (field)
+ self.fieldTypeStack.pop( )
+ self.nodeStack[-1].PROTO.addField ((name, datatype, exposure))
+ setattr( self.nodeStack[-1], name, field )
+ def SFNull(self, tup):
+ ''' Create a reference to the SFNull node '''
+## print 'hi'
+ return proto.NULL
+ def USE( self, (tag, start, stop, (nametuple,) )):
+ ''' Create a reference to an already defined node'''
+ name = self._getString (nametuple)
+ if self.depth < PROGRESS_DEPTH:
+ self.progresscount += 1
+ try:
+ node = self.sceneGraphStack [-1].defNames [name]
+ return node
+ except KeyError:
+ raise NameError ('''USE without DEF for node %s %s:%s'''%(name, start, stop))
+ def IS(self, (tag, start, stop, (nametuple,))):
+ ''' Create a field reference '''
+ name = self._getString (nametuple)
+ if not self.prototypeStack [-1].getField (name):
+ raise Exception (''' Attempt to create IS mapping of non-existent field %s %s:%s'''%(name, start, stop))
+ return proto.IS(name)
+ def Field( self, (tag, start, stop, sublist)):
+ ''' A field value (of any type) '''
+
+ if sublist and sublist[0][0] in ('USE','Script','Node','SFNull'):
+ if self.fieldTypeStack[-1] == 'SFNode':
+ return self._dispatch( sublist[0] )
+ else:
+ return map( self._dispatch, sublist )
+ elif self.fieldTypeStack[-1] == 'MFNode':
+ return []
+ else:
+ # is a simple data type...
+ function = getattr( self, self.fieldTypeStack[-1] )
+ try:
+ return function( sublist )
+ except ValueError:
+ traceback.print_exc()
+ print sublist
+ raise
+
+ def SFBool( self, (tup,) ):
+ '''Boolean, in Python tradition is either 0 or 1'''
+ return self._getString(tup) == 'TRUE'
+ def SFFloat( self, (x,) ):
+ return string.atof( self._getString(x) )
+ SFTime = SFFloat
+ def SFInt32( self, (x,) ):
+ return string.atoi( self._getString(x), 0 ) # allow for non-decimal numbers
+ def SFVec3f( self, (x,y,z) ):
+ return map( string.atof, map(self._getString, (x,y,z)) )
+ def SFVec2f( self, (x,y) ):
+ return map( string.atof, map(self._getString, (x,y)) )
+ def SFColor( self, (r,g,b) ):
+ return map( string.atof, map(self._getString, (r,g,b)) )
+ def SFRotation( self, (x,y,z,a) ):
+ return map( string.atof, map(self._getString, (x,y,z,a)) )
+
+ def MFInt32( self, tuples ):
+ result = []
+ # localisation
+ atoi = string.atoi
+ append = result.append
+ data = self.data
+ for tag, start, stop, children in tuples:
+ append( atoi( data[start:stop], 0) )
+ return result
+ SFImage = MFInt32
+ def MFFloat( self, tuples ):
+ result = []
+ # localisation
+ atof = string.atof
+ append = result.append
+ data = self.data
+ for tag, start, stop, children in tuples:
+ append( atof( data[start:stop]) )
+ return result
+ MFTime = MFFloat
+ def MFVec3f( self, tuples, length=3, typename='MFVec3f'):
+ result = []
+ # localisation
+ atof = string.atof
+ data = self.data
+ while tuples:
+ newobj = []
+ for tag, start, stop, children in tuples[:length]:
+ newobj.append( atof(data[start:stop] ))
+ if len(newobj) != length:
+ raise ValueError(
+ '''Incorrect number of elements in %s field at line %s'''%(typename, self._lines(stop))
+ )
+ result.append( newobj )
+ del tuples[:length]
+ return result
+ def MFVec2f( self, tuples):
+ return self.MFVec3f( tuples, length=2, typename='MFVec2f')
+ def MFRotation( self, tuples ):
+ return self.MFVec3f( tuples, length=4, typename='MFRotation')
+ def MFColor( self, tuples ):
+ return self.MFVec3f( tuples, length=3, typename='MFColor')
+
+ def MFString( self, tuples ):
+ bigresult = []
+ for (tag, start, stop, sublist) in tuples:
+ result = []
+ for element in sublist:
+ if element[0] == 'CHARNODBLQUOTE':
+ result.append( self.data[element[1]:element[2]] )
+ elif element[0] == 'ESCAPEDCHAR':
+ result.append( self.data[element[1]+1:element[2]] )
+ elif element[0] == 'SIMPLEBACKSLASH':
+ result.append( '\\' )
+ bigresult.append( string.join( result, "") )
+ return bigresult
+## result = []
+## for tuple in tuples:
+## result.append( self.SFString( tuple) )
+## return result
+ def SFString( self, tuples ):
+ '''Return the (escaped) string as a simple Python string'''
+ if tuples:
+ (tag, start, stop, sublist) = tuples[0]
+ if len( tuples ) > 1:
+ print '''Warning: SFString field has more than one string value''', self.data[tuples[0][1]:tuples[-1][2]]
+ result = []
+ for element in sublist:
+ if element[0] == 'CHARNODBLQUOTE':
+ result.append( self.data[element[1]:element[2]] )
+ elif element[0] == 'ESCAPEDCHAR':
+ result.append( self.data[element[1]+1:element[2]] )
+ elif element[0] == 'SIMPLEBACKSLASH':
+ result.append( '\\' )
+ return string.join( result, "")
+ else:
+ raise ValueError( "NULL SFString parsed???!!!" )
+ def vrmlScene( self, (tag, start, stop, sublist)):
+ '''A (prototype's) vrml sceneGraph'''
+ newNode = proto.sceneGraph (root=self.sceneGraphStack [-1])
+ self.sceneGraphStack.append (newNode)
+ #print 'setting proto sceneGraph', `newNode`
+ self.prototypeStack[-1].sceneGraph = newNode
+ results = filter (None, map (self._dispatch, sublist))
+ if results:
+ # items which are not auto-magically inserted into their parent
+ for result in results:
+ newNode.children.append( result)
+ self.sceneGraphStack.pop()
+
+PARSERDECLARATION = r'''header := -[\n]*
+rootItem := ts,(Proto/ExternProto/ROUTE/('USE',ts,USE,ts)/Script/Node),ts
+vrmlScene := rootItem*
+Proto := 'PROTO',ts,nodegi,ts,'[',ts,(fieldDecl/eventDecl)*,']', ts, '{', ts, vrmlScene,ts, '}', ts
+fieldDecl := fieldExposure,ts,dataType,ts,name,ts,Field,ts
+fieldExposure := 'field'/'exposedField'
+dataType := 'SFBool'/'SFString'/'SFFloat'/'SFTime'/'SFVec3f'/'SFVec2f'/'SFRotation'/'SFInt32'/'SFImage'/'SFColor'/'SFNode'/'MFBool'/'MFString'/'MFFloat'/'MFTime'/'MFVec3f'/'MFVec2f'/'MFRotation'/'MFInt32'/'MFColor'/'MFNode'
+eventDecl := eventDirection, ts, dataType, ts, name, ts
+eventDirection := 'eventIn'/'eventOut'
+ExternProto := 'EXTERNPROTO',ts,nodegi,ts,'[',ts,(extFieldDecl/eventDecl)*,']', ts, ExtProtoURL
+extFieldDecl := fieldExposure,ts,dataType,ts,name,ts
+ExtProtoURL := '['?,(ts,SFString)*, ts, ']'?, ts # just an MFString by another name :)
+ROUTE := 'ROUTE',ts, name,'.',name, ts, 'TO', ts, name,'.',name, ts
+Node := ('DEF',ts,name,ts)?,nodegi,ts,'{',ts,(Proto/ExternProto/ROUTE/Attr)*,ts,'}', ts
+Script := ('DEF',ts,name,ts)?,'Script',ts,'{',ts,(ScriptFieldDecl/ScriptEventDecl/Proto/ExternProto/ROUTE/Attr)*,ts,'}', ts
+ScriptEventDecl := eventDirection, ts, dataType, ts, name, ts, ('IS', ts, IS,ts)?
+ScriptFieldDecl := fieldExposure,ts,dataType,ts,name,ts,(('IS', ts,IS,ts)/Field),ts
+SFNull := 'NULL', ts
+
+# should really have an optimised way of declaring a different reporting name for the same production...
+USE := name
+IS := name
+nodegi := name
+Attr := name, ts, (('IS', ts,IS,ts)/Field), ts
+Field := ( '[',ts,((SFNumber/SFBool/SFString/('USE',ts,USE,ts)/Script/Node),ts)*, ']', ts )/((SFNumber/SFBool/SFNull/SFString/('USE',ts,USE,ts)/Script/Node),ts)+
+
+name := -[][0-9{}\000-\020"'#,.\\ ], -[][{}\000-\020"'#,.\\ ]*
+SFNumber := [-+]*, ( ('0',[xX],[0-9]+) / ([0-9.]+,([eE],[-+0-9.]+)?))
+SFBool := 'TRUE'/'FALSE'
+SFString := '"',(CHARNODBLQUOTE/ESCAPEDCHAR/SIMPLEBACKSLASH)*,'"'
+CHARNODBLQUOTE := -[\134"]+
+SIMPLEBACKSLASH := '\134'
+ESCAPEDCHAR := '\\"'/'\134\134'
+<ts> := ( [ \011-\015,]+ / ('#',-'\012'*,'\n')+ )*
+'''
+
+
+PARSERTABLE = generator.buildParser( PARSERDECLARATION )
+HEADERPARSER = PARSERTABLE.parserbyname( "header" )
+ROOTITEMPARSER = PARSERTABLE.parserbyname( "rootItem" )
+
diff --git a/intern/python/modules/vrml/scenegraph.py b/intern/python/modules/vrml/scenegraph.py
new file mode 100644
index 00000000000..2f137b1e259
--- /dev/null
+++ b/intern/python/modules/vrml/scenegraph.py
@@ -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": [],
+}
+
+
+
diff --git a/intern/python/modules/vrml/utils/__init__.py b/intern/python/modules/vrml/utils/__init__.py
new file mode 100644
index 00000000000..9d708a9084c
--- /dev/null
+++ b/intern/python/modules/vrml/utils/__init__.py
@@ -0,0 +1 @@
+"""utilities"""
diff --git a/intern/python/modules/vrml/utils/collapse.py b/intern/python/modules/vrml/utils/collapse.py
new file mode 100644
index 00000000000..25da50c2adb
--- /dev/null
+++ b/intern/python/modules/vrml/utils/collapse.py
@@ -0,0 +1,169 @@
+'''
+Destructive Functions for "collapsing" Sequences into single levels
+
+>>> from mcf.utils import collapse
+
+>>> collapse.test([[[1],[2,3]],[[]],[4],5,[6]])
+
+[1, 2, 3, 4, 5, 6] # note that is the same root list
+
+>>> collapse.collapse2([[[1],[2,3]],[[]],(4,()),(5,),[6]])
+
+[1, 2, 3, 4, 5, 6] # note is the same root list
+'''
+import copy, types, sys
+from types import ListType, TupleType # this now only supports the obsolete stuff...
+
+def hyperCollapse( inlist, allowedmap, type=type, list=list, itype=types.InstanceType, maxint= sys.maxint):
+ '''
+ Destructively flatten a mixed hierarchy to a single level.
+ Non-recursive, many speedups and obfuscations by Tim Peters :)
+ '''
+ try:
+ # for every possible index
+ for ind in xrange( maxint):
+ # while that index currently holds a list
+ expandable = 1
+ while expandable:
+ expandable = 0
+ if allowedmap.has_key( type(inlist[ind]) ):
+ # expand that list into the index (and subsequent indicies)
+ inlist[ind:ind+1] = list( inlist[ind])
+ expandable = 1
+
+ # alternately you could iterate through checking for isinstance on all possible
+ # classes, but that would be very slow
+ elif type( inlist[ind] ) is itype and allowedmap.has_key( inlist[ind].__class__ ):
+ # here figure out some way to generically expand that doesn't risk
+ # infinite loops...
+ templist = []
+ for x in inlist[ind]:
+ templist.append( x)
+ inlist[ind:ind+1] = templist
+ expandable = 1
+ except IndexError:
+ pass
+ return inlist
+
+
+def collapse(inlist, type=type, ltype=types.ListType, maxint= sys.maxint):
+ '''
+ Destructively flatten a list hierarchy to a single level.
+ Non-recursive, and (as far as I can see, doesn't have any
+ glaring loopholes).
+ Further speedups and obfuscations by Tim Peters :)
+ '''
+ try:
+ # for every possible index
+ for ind in xrange( maxint):
+ # while that index currently holds a list
+ while type(inlist[ind]) is ltype:
+ # expand that list into the index (and subsequent indicies)
+ inlist[ind:ind+1] = inlist[ind]
+ #ind = ind+1
+ except IndexError:
+ pass
+ return inlist
+
+def collapse_safe(inlist):
+ '''
+ As collapse, but works on a copy of the inlist
+ '''
+ return collapse( inlist[:] )
+
+def collapse2(inlist, ltype=(types.ListType, types.TupleType), type=type, maxint= sys.maxint ):
+ '''
+ Destructively flatten a list hierarchy to a single level.
+ Will expand tuple children as well, but will fail if the
+ top level element is not a list.
+ Non-recursive, and (as far as I can see, doesn't have any
+ glaring loopholes).
+ '''
+ ind = 0
+ try:
+ while 1:
+ while type(inlist[ind]) in ltype:
+ try:
+ inlist[ind:ind+1] = inlist[ind]
+ except TypeError:
+ inlist[ind:ind+1] = list(inlist[ind])
+ ind = ind+1
+ except IndexError:
+ pass
+ return inlist
+
+def collapse2_safe(inlist):
+ '''
+ As collapse2, but works on a copy of the inlist
+ '''
+ return collapse( list(inlist) )
+
+def old_buggy_collapse(inlist):
+ '''Always return a one-level list of all the non-list elements in listin,
+ rewritten to be non-recursive 96-12-28 Note that the new versions work
+ on the original list, not a copy of the original.'''
+ if type(inlist)==TupleType:
+ inlist = list(inlist)
+ elif type(inlist)!=ListType:
+ return [inlist]
+ x = 0
+ while 1:
+ try:
+ y = inlist[x]
+ if type(y) == ListType:
+ ylen = len(y)
+ if ylen == 1:
+ inlist[x] = y[0]
+ if type(inlist[x]) == ListType:
+ x = x - 1 # need to collapse that list...
+ elif ylen == 0:
+ del(inlist[x])
+ x = x-1 # list has been shortened
+ else:
+ inlist[x:x+1]=y
+ x = x+1
+ except IndexError:
+ break
+ return inlist
+
+
+def old_buggy_collapse2(inlist):
+ '''As collapse, but also collapse tuples, rewritten 96-12-28 to be non-recursive'''
+ if type(inlist)==TupleType:
+ inlist = list(inlist)
+ elif type(inlist)!=ListType:
+ return [inlist]
+ x = 0
+ while 1:
+ try:
+ y = inlist[x]
+ if type(y) in [ListType, TupleType]:
+ ylen = len(y)
+ if ylen == 1:
+ inlist[x] = y[0]
+ if type(inlist[x]) in [ListType,TupleType]:
+ x = x-1 #(to deal with that element)
+ elif ylen == 0:
+ del(inlist[x])
+ x = x-1 # list has been shortened, will raise exception with tuples...
+ else:
+ inlist[x:x+1]=list(y)
+ x = x+1
+ except IndexError:
+ break
+ return inlist
+
+
+def oldest_buggy_collapse(listin):
+ 'Always return a one-level list of all the non-list elements in listin'
+ if type(listin) == ListType:
+ return reduce(lambda x,y: x+y, map(collapse, listin), [])
+ else: return [listin]
+
+def oldest_buggy_collapse2(seqin):
+
+ if type(seqin) in [ListType, TupleType]:
+ return reduce(lambda x,y: x+y, map(collapse2, seqin), [])
+ else:
+ return [seqin]
+
diff --git a/intern/python/modules/vrml/utils/err.py b/intern/python/modules/vrml/utils/err.py
new file mode 100644
index 00000000000..3c6591a6873
--- /dev/null
+++ b/intern/python/modules/vrml/utils/err.py
@@ -0,0 +1,37 @@
+'''
+err.py Encapsulated writing to sys.stderr
+
+The idea of this module is that, for a GUI system (or a more advanced UI),
+you can just import a different err module (or object) and keep
+your code the same. (For instance, you often want a status window
+which flashes warnings and info, and have error messages pop up an
+alert to get immediate attention.
+'''
+
+import sys
+
+def err(message, Code=0):
+ '''
+ report an error, with an optional error code
+ '''
+ if Code:
+ sys.stderr.write('Error #%i: %s\n'%(Code,message))
+ else:
+ sys.stderr.write('Error: %s\n'%message)
+def warn(message, Code=0):
+ '''
+ report a warning, with an optional error code
+ '''
+ if Code:
+ sys.stderr.write('Warning #%i: %s\n'%(Code,message))
+ else:
+ sys.stderr.write('Warning: %s\n'%message)
+def info(message, Code=0):
+ '''
+ report information/status, with an optional error code
+ '''
+ if Code:
+ sys.stderr.write('Info #%i: %s\n'%(Code,message))
+ else:
+ sys.stderr.write('Info: %s\n'%message)
+
diff --git a/intern/python/modules/vrml/utils/namespace.py b/intern/python/modules/vrml/utils/namespace.py
new file mode 100644
index 00000000000..dd9f0b7dea6
--- /dev/null
+++ b/intern/python/modules/vrml/utils/namespace.py
@@ -0,0 +1,225 @@
+'''
+NameSpace v0.04:
+
+A "NameSpace" is an object wrapper around a _base dictionary
+which allows chaining searches for an 'attribute' within that
+dictionary, or any other namespace which is defined as part
+of the search path (depending on the downcascade variable, is
+either the hier-parents or the hier-children).
+
+You can assign attributes to the namespace normally, and read
+them normally. (setattr, getattr, a.this = that, a.this)
+
+I use namespaces for writing parsing systems, where I want to
+differentiate between sources (have multiple sources that I can
+swap into or out of the namespace), but want to be able to get
+at them through a single interface. There is a test function
+which gives you an idea how to use the system.
+
+In general, call NameSpace(someobj), where someobj is a dictionary,
+a module, or another NameSpace, and it will return a NameSpace which
+wraps up the keys of someobj. To add a namespace to the NameSpace,
+just call the append (or hier_addchild) method of the parent namespace
+with the child as argument.
+
+### NOTE: if you pass a module (or anything else with a dict attribute),
+names which start with '__' will be removed. You can avoid this by
+pre-copying the dict of the object and passing it as the arg to the
+__init__ method.
+
+### NOTE: to properly pickle and/or copy module-based namespaces you
+will likely want to do: from mcf.utils import extpkl, copy_extend
+
+### Changes:
+ 97.05.04 -- Altered to use standard hierobj interface, cleaned up
+ interface by removing the "addparent" function, which is reachable
+ by simply appending to the __parent__ attribute, though normally
+ you would want to use the hier_addchild or append functions, since
+ they let both objects know about the addition (and therefor the
+ relationship will be restored if the objects are stored and unstored)
+
+ 97.06.26 -- Altered the getattr function to reduce the number of
+ situations in which infinite lookup loops could be created
+ (unfortunately, the cost is rather high). Made the downcascade
+ variable harden (resolve) at init, instead of checking for every
+ lookup. (see next note)
+
+ 97.08.29 -- Discovered some _very_ weird behaviour when storing
+ namespaces in mcf.store dbases. Resolved it by storing the
+ __namespace_cascade__ attribute as a normal attribute instead of
+ using the __unstore__ mechanism... There was really no need to
+ use the __unstore__, but figuring out how a functions saying
+ self.__dict__['__namespace_cascade__'] = something
+ print `self.__dict__['__namespace_cascade__']` can print nothing
+ is a bit beyond me. (without causing an exception, mind you)
+
+ 97.11.15 Found yet more errors, decided to make two different
+ classes of namespace. Those based on modules now act similar
+ to dummy objects, that is, they let you modify the original
+ instead of keeping a copy of the original and modifying that.
+
+ 98.03.15 -- Eliminated custom pickling methods as they are no longer
+ needed for use with Python 1.5final
+
+ 98.03.15 -- Fixed bug in items, values, etceteras with module-type
+ base objects.
+'''
+import copy, types, string
+
+from mcf.utils import hierobj
+
+class NameSpace(hierobj.Hierobj):
+ '''
+ An hierarchic NameSpace, allows specification of upward or downward
+ chaining search for resolving names
+ '''
+ def __init__(self, val = None, parents=None, downcascade=1,children=[]):
+ '''
+ A NameSpace can be initialised with a dictionary, a dummied
+ dictionary, another namespace, or something which has a __dict__
+ attribute.
+ Note that downcascade is hardened (resolved) at init, not at
+ lookup time.
+ '''
+ hierobj.Hierobj.__init__(self, parents, children)
+ self.__dict__['__downcascade__'] = downcascade # boolean
+ if val is None:
+ self.__dict__['_base'] = {}
+ else:
+ if type( val ) == types.StringType:
+ # this is a reference to a module which has been pickled
+ val = __import__( val, {},{}, string.split( val, '.') )
+ try:
+ # See if val's a dummy-style object which has a _base
+ self.__dict__['_base']=copy.copy(val._base)
+ except (AttributeError,KeyError):
+ # not a dummy-style object... see if it has a dict attribute...
+ try:
+ if type(val) != types.ModuleType:
+ val = copy.copy(val.__dict__)
+ except (AttributeError, KeyError):
+ pass
+ # whatever val is now, it's going to become our _base...
+ self.__dict__['_base']=val
+ # harden (resolve) the reference to downcascade to speed attribute lookups
+ if downcascade: self.__dict__['__namespace_cascade__'] = self.__childlist__
+ else: self.__dict__['__namespace_cascade__'] = self.__parent__
+ def __setattr__(self, var, val):
+ '''
+ An attempt to set an attribute should place the attribute in the _base
+ dictionary through a setitem call.
+ '''
+ # Note that we use standard attribute access to allow ObStore loading if the
+ # ._base isn't yet available.
+ try:
+ self._base[var] = val
+ except TypeError:
+ setattr(self._base, var, val)
+ def __getattr__(self,var):
+## print '__getattr__', var
+ return self.__safe_getattr__(var, {}) # the {} is a stopdict
+
+ def __safe_getattr__(self, var,stopdict):
+ '''
+ We have a lot to do in this function, if the attribute is an unloaded
+ but stored attribute, we need to load it. If it's not in the stored
+ attributes, then we need to load the _base, then see if it's in the
+ _base.
+ If it's not found by then, then we need to check our resource namespaces
+ and see if it's in them.
+ '''
+ # we don't have a __storedattr__ or it doesn't have this key...
+ if var != '_base':
+ try:
+ return self._base[var]
+ except (KeyError,TypeError), x:
+ try:
+ return getattr(self._base, var)
+ except AttributeError:
+ pass
+ try: # with pickle, it tries to get the __setstate__ before restoration is complete
+ for cas in self.__dict__['__namespace_cascade__']:
+ try:
+ stopdict[id(cas)] # if succeeds, we've already tried this child
+ # no need to do anything, if none of the children succeeds we will
+ # raise an AttributeError
+ except KeyError:
+ stopdict[id(cas)] = None
+ return cas.__safe_getattr__(var,stopdict)
+ except (KeyError,AttributeError):
+ pass
+ raise AttributeError, var
+ def items(self):
+ try:
+ return self._base.items()
+ except AttributeError:
+ pass
+ try:
+ return self._base.__dict__.items()
+ except AttributeError:
+ pass
+ def keys(self):
+ try:
+ return self._base.keys()
+ except AttributeError:
+ pass
+ try:
+ return self._base.__dict__.keys()
+ except AttributeError:
+ pass
+ def has_key( self, key ):
+ try:
+ return self._base.has_key( key)
+ except AttributeError:
+ pass
+ try:
+ return self._base.__dict__.has_key( key)
+ except AttributeError:
+ pass
+ def values(self):
+ try:
+ return self._base.values()
+ except AttributeError:
+ pass
+ try:
+ return self._base.__dict__.values()
+ except AttributeError:
+ pass
+
+ def __getinitargs__(self):
+ if type( self._base ) is types.ModuleType:
+ base = self._base.__name__
+ else:
+ base = self._base
+ return (base, self.__parent__, self.__downcascade__, self.__childlist__)
+ def __getstate__(self):
+ return None
+ def __setstate__(self,*args):
+ pass
+ def __deepcopy__(self, memo=None):
+ d = id(self)
+ if memo is None:
+ memo = {}
+ elif memo.has_key(d):
+ return memo[d]
+ if type(self._base) == types.ModuleType:
+ rest = tuple(map( copy.deepcopy, (self.__parent__, self.__downcascade__, self.__childlist__) ))
+ new = apply(self.__class__, (self._base,)+rest )
+ else:
+ new = tuple(map( copy.deepcopy, (self._base, self.__parent__, self.__downcascade__, self.__childlist__) ))
+ return new
+## def __del__( self, id=id ):
+## print 'del namespace', id( self )
+
+
+def test():
+ import string
+ a = NameSpace(string)
+ del(string)
+ a.append(NameSpace({'a':23,'b':42}))
+ import math
+ a.append(NameSpace(math))
+ print 'The returned object should allow access to the attributes of the string,\nand math modules, and two simple variables "a" and "b" (== 23 and42 respectively)'
+ return a
+
+
diff --git a/intern/python/modules/vrml/utils/typeclasses.py b/intern/python/modules/vrml/utils/typeclasses.py
new file mode 100644
index 00000000000..ed798dfe3da
--- /dev/null
+++ b/intern/python/modules/vrml/utils/typeclasses.py
@@ -0,0 +1,50 @@
+'''
+Classes of Types
+
+Often you want to be able to say:
+ if type(obj) in MutableTypes:
+ yada
+
+This module is intended to make that easier.
+Just import and use :)
+'''
+import types
+
+MutableTypes = [ types.ListType, types.DictType, types.InstanceType ]
+MutableSequenceTypes = [ types.ListType ]
+SequenceTypes = [ types.ListType, types.StringType, types.TupleType ]
+NumericTypes = [ types.IntType, types.FloatType, types.LongType, types.ComplexType ]
+MappingTypes = [ types.DictType ]
+
+def regarray():
+ if globals().has_key('array'):
+ return 1
+ try:
+ import array
+ SequenceTypes.append( array.ArrayType )
+ MutableTypes.append( array.ArrayType )
+ MutableSequenceTypes.append( array.ArrayType )
+ return 1
+ except ImportError:
+ return 0
+
+def regnumpy():
+ '''
+ Call if you want to register numpy arrays
+ according to their types.
+ '''
+ if globals().has_key('Numeric'):
+ return 1
+ try:
+ import Numeric
+ SequenceTypes.append( Numeric.ArrayType )
+ MutableTypes.append( Numeric.ArrayType )
+ MutableSequenceTypes.append( Numeric.ArrayType )
+ return 1
+ except ImportError:
+ return 0
+
+# for now, I'm going to always register these, if the module becomes part of the base distribution
+# it might be better to leave it out so numpy isn't always getting loaded...
+regarray()
+regnumpy() \ No newline at end of file