diff options
Diffstat (limited to 'intern/python/modules/vrml')
-rw-r--r-- | intern/python/modules/vrml/__init__.py | 1 | ||||
-rw-r--r-- | intern/python/modules/vrml/basenodes.py | 974 | ||||
-rw-r--r-- | intern/python/modules/vrml/fieldcoercian.py | 310 | ||||
-rw-r--r-- | intern/python/modules/vrml/loader.py | 97 | ||||
-rw-r--r-- | intern/python/modules/vrml/parser.py | 426 | ||||
-rw-r--r-- | intern/python/modules/vrml/scenegraph.py | 833 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/__init__.py | 1 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/collapse.py | 169 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/err.py | 37 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/namespace.py | 225 | ||||
-rw-r--r-- | intern/python/modules/vrml/utils/typeclasses.py | 50 |
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 |