PyVRML97 2.3.4b3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyvrml97-2.3.4b3.dist-info/METADATA +65 -0
- pyvrml97-2.3.4b3.dist-info/RECORD +39 -0
- pyvrml97-2.3.4b3.dist-info/WHEEL +5 -0
- pyvrml97-2.3.4b3.dist-info/top_level.txt +1 -0
- vrml/__init__.py +22 -0
- vrml/_bytes.py +85 -0
- vrml/arrays.py +86 -0
- vrml/cache.py +250 -0
- vrml/copier.py +27 -0
- vrml/csscolors.py +177 -0
- vrml/event.py +24 -0
- vrml/field.py +476 -0
- vrml/fieldtypes.py +1491 -0
- vrml/node.py +569 -0
- vrml/nodepath.py +70 -0
- vrml/olist.py +126 -0
- vrml/protofunctions.py +233 -0
- vrml/protonamespace.py +13 -0
- vrml/route.py +167 -0
- vrml/vrml200x/__init__.py +0 -0
- vrml/vrml200x/parser.py +87 -0
- vrml/vrml97/__init__.py +6 -0
- vrml/vrml97/_transformmatrix.py +133 -0
- vrml/vrml97/_transformmatrix_accel.py +63 -0
- vrml/vrml97/basenamespaces.py +28 -0
- vrml/vrml97/basenodes.py +648 -0
- vrml/vrml97/linearise.py +579 -0
- vrml/vrml97/nodepath.py +135 -0
- vrml/vrml97/nodetypes.py +95 -0
- vrml/vrml97/nurbs.py +79 -0
- vrml/vrml97/parseprocessor.py +452 -0
- vrml/vrml97/parser.py +72 -0
- vrml/vrml97/scenegraph.py +181 -0
- vrml/vrml97/script.py +25 -0
- vrml/vrml97/shaders.py +123 -0
- vrml/vrml97/transformmatrix.py +193 -0
- vrml/weakkeydictfix.py +36 -0
- vrml/weaklist.py +163 -0
- vrml/weaktuple.py +138 -0
vrml/csscolors.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Module to allow using strings to specify colors"""
|
|
2
|
+
def stringToColor( value ):
|
|
3
|
+
"""Given a string/unicode value, determine appropriate color"""
|
|
4
|
+
value = value.lower()
|
|
5
|
+
possible = cssColors.get( value )
|
|
6
|
+
if possible:
|
|
7
|
+
return possible
|
|
8
|
+
if value and value[0] == '#':
|
|
9
|
+
# HTML-style #hex encoding
|
|
10
|
+
possible = int( value[1:],16)
|
|
11
|
+
return toFloat(possible,16),toFloat(possible,8), toFloat(possible,0)
|
|
12
|
+
else:
|
|
13
|
+
raise ValueError( """String %(value)r couldn't be recognised as a color name, or #FFFFFF style encoding"""%locals())
|
|
14
|
+
def toInt( value ):
|
|
15
|
+
"""Take float value and give single-byte integer equivalent"""
|
|
16
|
+
return int(round(value*255,0))
|
|
17
|
+
def toFloat( value, shift=0 ):
|
|
18
|
+
"""Take single-byte integer value return floating point equivalent"""
|
|
19
|
+
return ((value&(255<<shift))>>shift)/255.0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
cssColors = {
|
|
23
|
+
'black' : (0.0,0.0,0.0),
|
|
24
|
+
'dimgray' : (0.4118,0.4118,0.4118),
|
|
25
|
+
'gray' : (0.502,0.502,0.502),
|
|
26
|
+
'darkgray' : (0.6627,0.6627,0.6627),
|
|
27
|
+
'silver' : (0.7529,0.7529,0.7529),
|
|
28
|
+
'lightgrey' : (0.8275,0.8275,0.8275),
|
|
29
|
+
'gainsboro' : (0.8627,0.8627,0.8627),
|
|
30
|
+
'whitesmoke' : (0.9608,0.9608,0.9608),
|
|
31
|
+
'white' : (1.0,1.0,1.0),
|
|
32
|
+
'snow' : (1.0,0.9804,0.9804),
|
|
33
|
+
'rosybrown' : (0.7373,0.5608,0.5608),
|
|
34
|
+
'lightcoral' : (0.9412,0.502,0.502),
|
|
35
|
+
'indianred' : (0.8039,0.3608,0.3608),
|
|
36
|
+
'brown' : (0.6471,0.1647,0.1647),
|
|
37
|
+
'firebrick' : (0.698,0.1333,0.1333),
|
|
38
|
+
'maroon' : (0.502,0.0,0.0),
|
|
39
|
+
'darkred' : (0.5451,0.0,0.0),
|
|
40
|
+
'red' : (1.0,0.0,0.0),
|
|
41
|
+
'mistyrose' : (1.0,0.8941,0.8824),
|
|
42
|
+
'salmon' : (0.9804,0.502,0.4471),
|
|
43
|
+
'tomato' : (1.0,0.3882,0.2784),
|
|
44
|
+
'darksalmon' : (0.9137,0.5882,0.4784),
|
|
45
|
+
'coral' : (1.0,0.498,0.3137),
|
|
46
|
+
'orangered' : (1.0,0.2706,0.0),
|
|
47
|
+
'lightsalmon' : (1.0,0.6275,0.4784),
|
|
48
|
+
'sienna' : (0.6275,0.3216,0.1765),
|
|
49
|
+
'seashell' : (1.0,0.9608,0.9333),
|
|
50
|
+
'chocolate' : (0.8235,0.4118,0.1176),
|
|
51
|
+
'saddlebrown' : (0.5451,0.2706,0.0745),
|
|
52
|
+
'sandybrown' : (0.9569,0.6431,0.3765),
|
|
53
|
+
'peachpuff' : (1.0,0.8549,0.7255),
|
|
54
|
+
'peru' : (0.8039,0.5216,0.2471),
|
|
55
|
+
'linen' : (0.9804,0.9412,0.902),
|
|
56
|
+
'bisque' : (1.0,0.8941,0.7686),
|
|
57
|
+
'burlywood' : (0.8706,0.7216,0.5294),
|
|
58
|
+
'darkorange' : (1.0,0.549,0.0),
|
|
59
|
+
'antiquewhite' : (0.9804,0.9216,0.8431),
|
|
60
|
+
'tan' : (0.8235,0.7059,0.549),
|
|
61
|
+
'blanchedalmond' : (1.0,0.9216,0.8039),
|
|
62
|
+
'navajowhite' : (1.0,0.8706,0.6784),
|
|
63
|
+
'papayawhip' : (1.0,0.9373,0.8353),
|
|
64
|
+
'moccasin' : (1.0,0.8941,0.7098),
|
|
65
|
+
'oldlace' : (0.9922,0.9608,0.902),
|
|
66
|
+
'wheat' : (0.9608,0.8706,0.702),
|
|
67
|
+
'orange' : (1.0,0.6471,0.0),
|
|
68
|
+
'floralwhite' : (1.0,0.9804,0.9412),
|
|
69
|
+
'goldenrod' : (0.8549,0.6471,0.1255),
|
|
70
|
+
'cornsilk' : (1.0,0.9725,0.8627),
|
|
71
|
+
'gold' : (1.0,0.8431,0.0),
|
|
72
|
+
'lemonchiffon' : (1.0,0.9804,0.8039),
|
|
73
|
+
'palegoldenrod' : (0.9333,0.9098,0.6667),
|
|
74
|
+
'khaki' : (0.9412,0.902,0.549),
|
|
75
|
+
'darkkhaki' : (0.7412,0.7176,0.4196),
|
|
76
|
+
'ivory' : (1.0,1.0,0.9412),
|
|
77
|
+
'beige' : (0.9608,0.9608,0.8627),
|
|
78
|
+
'lightyellow' : (1.0,1.0,0.8784),
|
|
79
|
+
'lightgoldenrodyellow' : (0.9804,0.9804,0.8235),
|
|
80
|
+
'darkgoldenrod' : (0.7216,0.7137,0.0431),
|
|
81
|
+
'olive' : (0.502,0.502,0.0),
|
|
82
|
+
'yellow' : (1.0,1.0,0.0),
|
|
83
|
+
'yellowgreen' : (0.6627,0.8039,0.1961),
|
|
84
|
+
'olivedrab' : (0.4196,0.5569,0.1373),
|
|
85
|
+
'darkolivegreen' : (0.3333,0.4196,0.1843),
|
|
86
|
+
'greenyellow' : (0.6784,1.0,0.1843),
|
|
87
|
+
'chartreuse' : (0.498,1.0,0.0),
|
|
88
|
+
'lawngreen' : (0.4863,0.9882,0.0),
|
|
89
|
+
'honeydew' : (0.9412,1.0,0.9412),
|
|
90
|
+
'darkseagreen' : (0.5608,0.7373,0.5608),
|
|
91
|
+
'lightgreen' : (0.5647,0.9333,0.5647),
|
|
92
|
+
'palegreen' : (0.5961,0.9843,0.5961),
|
|
93
|
+
'forestgreen' : (0.1333,0.5451,0.1333),
|
|
94
|
+
'limegreen' : (0.1961,0.8039,0.1961),
|
|
95
|
+
'darkgreen' : (0.0,0.3922,0.0),
|
|
96
|
+
'green' : (0.0,0.502,0.0),
|
|
97
|
+
'lime' : (0.0,1.0,0.0),
|
|
98
|
+
'mediumseagreen' : (0.2353,0.702,0.4431),
|
|
99
|
+
'seagreen' : (0.1804,0.5451,0.3412),
|
|
100
|
+
'mintcream' : (0.9608,1.0,0.9804),
|
|
101
|
+
'springgreen' : (0.0,1.0,0.498),
|
|
102
|
+
'mediumspringgreen' : (0.0,0.9804,0.6039),
|
|
103
|
+
'mediumaquamarine' : (0.4,0.8039,0.6667),
|
|
104
|
+
'aquamarine' : (0.498,1.0,0.8314),
|
|
105
|
+
'turquoise' : (0.251,0.8784,0.8157),
|
|
106
|
+
'mediumturquoise' : (0.2824,0.8196,0.8),
|
|
107
|
+
'lightseagreen' : (0.1255,0.698,0.6667),
|
|
108
|
+
'azure' : (0.9412,1.0,1.0),
|
|
109
|
+
'lightcyan' : (0.8784,1.0,1.0),
|
|
110
|
+
'paleturquoise' : (0.6863,0.9333,0.9333),
|
|
111
|
+
'darkslategray' : (0.1843,0.3098,0.3098),
|
|
112
|
+
'teal' : (0.0,0.502,0.502),
|
|
113
|
+
'darkcyan' : (0.0,0.5451,0.5451),
|
|
114
|
+
'aqua' : (0.0,1.0,1.0),
|
|
115
|
+
'cyan' : (0.0,1.0,1.0),
|
|
116
|
+
'cadetblue' : (0.3725,0.6196,0.6275),
|
|
117
|
+
'darkturquoise' : (0.0,0.8078,0.8196),
|
|
118
|
+
'powderblue' : (0.6902,0.8784,0.902),
|
|
119
|
+
'lightblue' : (0.6784,0.8471,0.902),
|
|
120
|
+
'deepskyblue' : (0.0,0.749,1.0),
|
|
121
|
+
'skyblue' : (0.5294,0.8078,0.9294),
|
|
122
|
+
'lightskyblue' : (0.5294,0.8078,0.9804),
|
|
123
|
+
'steelblue' : (0.2745,0.5098,0.7059),
|
|
124
|
+
'aliceblue' : (0.9412,0.9725,1.0),
|
|
125
|
+
'slategray' : (0.4392,0.502,0.5647),
|
|
126
|
+
'lightslategray' : (0.4667,0.5333,0.6),
|
|
127
|
+
'dodgerblue' : (0.1176,0.5647,1.0),
|
|
128
|
+
'lightsteelblue' : (0.6902,0.7686,0.8706),
|
|
129
|
+
'cornflowerblue' : (0.3922,0.5843,0.9294),
|
|
130
|
+
'royalblue' : (0.0157,0.0863,0.5647),
|
|
131
|
+
'ghostwhite' : (0.9725,0.9725,1.0),
|
|
132
|
+
'lavender' : (0.902,0.902,0.9804),
|
|
133
|
+
'midnightblue' : (0.098,0.098,0.4392),
|
|
134
|
+
'navy' : (0.0,0.0,0.502),
|
|
135
|
+
'darkblue' : (0.0,0.0,0.5451),
|
|
136
|
+
'mediumblue' : (0.0,0.0,0.8039),
|
|
137
|
+
'blue' : (0.0,0.0,1.0),
|
|
138
|
+
'darkslateblue' : (0.2824,0.2392,0.5451),
|
|
139
|
+
'slateblue' : (0.4157,0.3529,0.8039),
|
|
140
|
+
'mediumslateblue' : (0.4824,0.4078,0.9333),
|
|
141
|
+
'mediumpurple' : (0.5765,0.4392,0.8588),
|
|
142
|
+
'blueviolet' : (0.5412,0.1686,0.8863),
|
|
143
|
+
'indigo' : (0.2941,0.0,0.5098),
|
|
144
|
+
'darkorchid' : (0.6,0.1961,0.8),
|
|
145
|
+
'darkviolet' : (0.5804,0.0,0.8275),
|
|
146
|
+
'mediumorchid' : (0.7294,0.3333,0.8275),
|
|
147
|
+
'thistle' : (0.8471,0.749,0.8471),
|
|
148
|
+
'plum' : (0.8667,0.6275,0.8667),
|
|
149
|
+
'violet' : (0.9333,0.5098,0.9333),
|
|
150
|
+
'purple' : (0.502,0.0,0.502),
|
|
151
|
+
'darkmagenta' : (0.5451,0.0,0.5451),
|
|
152
|
+
'fuchsia' : (1.0,0.0,1.0),
|
|
153
|
+
'magenta' : (1.0,0.0,1.0),
|
|
154
|
+
'orchid' : (0.8549,0.4392,0.8392),
|
|
155
|
+
'mediumvioletred' : (0.7804,0.0824,0.5216),
|
|
156
|
+
'deeppink' : (1.0,0.0784,0.5765),
|
|
157
|
+
'hotpink' : (1.0,0.4118,0.7059),
|
|
158
|
+
'lavenderblush' : (1.0,0.9412,0.9608),
|
|
159
|
+
'palevioletred' : (0.8588,0.4392,0.5765),
|
|
160
|
+
'pink' : (1.0,0.7529,0.8039),
|
|
161
|
+
'crimson' : (0.8627,0.0784,0.2353),
|
|
162
|
+
'lightpink' : (1.0,0.7137,0.7569),
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
import unittest
|
|
167
|
+
from vrml import arrays
|
|
168
|
+
class ColorTests(unittest.TestCase):
|
|
169
|
+
def testForwardName (self):
|
|
170
|
+
for name, value in cssColors.items():
|
|
171
|
+
assert stringToColor( name ) == value
|
|
172
|
+
def testForwardHexidecimal (self):
|
|
173
|
+
for name, value in cssColors.items ():
|
|
174
|
+
representation = '#%02x%02x%02x'%tuple(map(toInt,value))
|
|
175
|
+
result = stringToColor( representation )
|
|
176
|
+
assert arrays.allclose( result, value, 0.001), """Expected %r, got %r"""%(value, result)
|
|
177
|
+
unittest.main ()
|
vrml/event.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Routable event base-class for VRML environments"""
|
|
2
|
+
|
|
3
|
+
class Event( object ):
|
|
4
|
+
"""Routable event for VRML environments"""
|
|
5
|
+
def __init__( self ):
|
|
6
|
+
"""Initialize the Event object"""
|
|
7
|
+
self.visitedNodes = {}
|
|
8
|
+
def visited (self, key, value = None):
|
|
9
|
+
"""Check for or register visitation of the given key
|
|
10
|
+
|
|
11
|
+
key -- an opaque hashable value, normally the node and
|
|
12
|
+
field/event as a tuple.
|
|
13
|
+
value -- if provided, sets the current value, otherwise
|
|
14
|
+
signals that the current value should be returned
|
|
15
|
+
|
|
16
|
+
return value: previous key value (possibly None)
|
|
17
|
+
"""
|
|
18
|
+
if value is None:
|
|
19
|
+
return self.visitedNodes.get(key)
|
|
20
|
+
else:
|
|
21
|
+
previousValue = self.visitedNodes.get(key)
|
|
22
|
+
self.visitedNodes[key] = value
|
|
23
|
+
return previousValue
|
|
24
|
+
|
vrml/field.py
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
"""property sub-class providing VRML field semantics"""
|
|
2
|
+
|
|
3
|
+
from pydispatch import dispatcher, robustapply
|
|
4
|
+
import weakref
|
|
5
|
+
import sys
|
|
6
|
+
from vrml import protonamespace
|
|
7
|
+
|
|
8
|
+
# conditional import via package entry points
|
|
9
|
+
try:
|
|
10
|
+
from vrml_accelerate import fieldaccel2
|
|
11
|
+
except Exception as err:
|
|
12
|
+
fieldaccel2 = None
|
|
13
|
+
|
|
14
|
+
from ._bytes import bytes, unicode, long
|
|
15
|
+
|
|
16
|
+
baseFieldTypes = protonamespace.ProtoNamespace({})
|
|
17
|
+
baseEventTypes = protonamespace.ProtoNamespace({})
|
|
18
|
+
|
|
19
|
+
### stuff used by the various field sub-types
|
|
20
|
+
NUMERIC_TYPES = (int, float, long)
|
|
21
|
+
SEQUENCE_TYPES = (tuple, list)
|
|
22
|
+
if sys.version_info[0] >= 3:
|
|
23
|
+
MAP_TYPE = type(map(int, [0]))
|
|
24
|
+
ZIP_TYPE = type(zip([], []))
|
|
25
|
+
RANGE_TYPE = type(range(3))
|
|
26
|
+
UNPACK_TYPES = (MAP_TYPE, ZIP_TYPE, RANGE_TYPE)
|
|
27
|
+
SEQUENCE_TYPES += UNPACK_TYPES
|
|
28
|
+
else:
|
|
29
|
+
MAP_TYPE = None
|
|
30
|
+
ZIP_TYPE = None
|
|
31
|
+
RANGE_TYPE = type(xrange(3))
|
|
32
|
+
UNPACK_TYPES = ()
|
|
33
|
+
_NULL = []
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def register(cls):
|
|
37
|
+
"""Register a new Field or Event class"""
|
|
38
|
+
name = typeName(cls)
|
|
39
|
+
if issubclass(cls, Event):
|
|
40
|
+
dictionary = baseEventTypes
|
|
41
|
+
else:
|
|
42
|
+
dictionary = baseFieldTypes
|
|
43
|
+
if name in dictionary:
|
|
44
|
+
print(
|
|
45
|
+
'Warning: redefining field-type %s from %s to %s'
|
|
46
|
+
% (name, dictionary.get(name), cls)
|
|
47
|
+
)
|
|
48
|
+
dictionary[name] = cls
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def typeName(cls):
|
|
52
|
+
"""Get the name of a field/event"""
|
|
53
|
+
if hasattr(cls, 'fieldType'):
|
|
54
|
+
return cls.fieldType
|
|
55
|
+
else:
|
|
56
|
+
return cls.__name__.split('.')[-1]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def newField(name, dataType, exposure=1, default=_NULL):
|
|
60
|
+
"""Create a new field with support for using strings to specify type
|
|
61
|
+
|
|
62
|
+
name -- string name
|
|
63
|
+
dataType -- string (or Field sub-class) specifying datatype
|
|
64
|
+
exposure -- boolean (0/1) indicating whether this is an exposed field
|
|
65
|
+
default -- default value for the field
|
|
66
|
+
"""
|
|
67
|
+
if isinstance(dataType, (bytes, unicode)):
|
|
68
|
+
dataType = baseFieldTypes[dataType]
|
|
69
|
+
return dataType(name, exposure, default)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def newEvent(name, dataType, direction=1):
|
|
73
|
+
"""Create a new event object (a specialised Field)
|
|
74
|
+
name -- string name
|
|
75
|
+
dataType --
|
|
76
|
+
direction -- 0 == in, 1 == out
|
|
77
|
+
"""
|
|
78
|
+
if isinstance(dataType, (bytes, unicode)):
|
|
79
|
+
dataType = baseEventTypes[dataType]
|
|
80
|
+
return dataType(name, direction)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if fieldaccel2:
|
|
84
|
+
BaseField = fieldaccel2.BaseField
|
|
85
|
+
else:
|
|
86
|
+
|
|
87
|
+
class BaseField(object):
|
|
88
|
+
def __init__(self, name, default):
|
|
89
|
+
self.name = name
|
|
90
|
+
self.defaultobj = default
|
|
91
|
+
if hasattr(default, '__call__'):
|
|
92
|
+
self.call_default = True
|
|
93
|
+
else:
|
|
94
|
+
self.call_default = False
|
|
95
|
+
|
|
96
|
+
def __get__(self, client, cls=None):
|
|
97
|
+
"""Retrieve value for given instance (or self for cls)"""
|
|
98
|
+
if client is None:
|
|
99
|
+
return self
|
|
100
|
+
idict = client.__dict__
|
|
101
|
+
current = idict.get(self.name, _NULL)
|
|
102
|
+
if current is _NULL:
|
|
103
|
+
return self.getDefault(client)
|
|
104
|
+
return current
|
|
105
|
+
|
|
106
|
+
fget = __get__
|
|
107
|
+
|
|
108
|
+
def __set__(self, client, value):
|
|
109
|
+
"""Set value for given instance"""
|
|
110
|
+
value = self._set(client, value)
|
|
111
|
+
dispatcher.send(
|
|
112
|
+
('set', self),
|
|
113
|
+
client,
|
|
114
|
+
value=value,
|
|
115
|
+
)
|
|
116
|
+
# return value
|
|
117
|
+
|
|
118
|
+
def fset(self, client, value, notify=True):
|
|
119
|
+
value = self._set(client, value)
|
|
120
|
+
if notify:
|
|
121
|
+
dispatcher.send(
|
|
122
|
+
('set', self),
|
|
123
|
+
client,
|
|
124
|
+
value=value,
|
|
125
|
+
)
|
|
126
|
+
return value
|
|
127
|
+
|
|
128
|
+
def _set(self, client, value):
|
|
129
|
+
try:
|
|
130
|
+
value = self.coerce(value)
|
|
131
|
+
except ValueError as x:
|
|
132
|
+
raise ValueError(
|
|
133
|
+
"""Field %s could not accept value %s (%s)""" % (self, value, x)
|
|
134
|
+
)
|
|
135
|
+
except TypeError as x:
|
|
136
|
+
raise ValueError(
|
|
137
|
+
"""Field %s could not accept value %s of type %s (%s)"""
|
|
138
|
+
% (self, value, type(value), x)
|
|
139
|
+
)
|
|
140
|
+
if isinstance(client, type):
|
|
141
|
+
setattr(client, self.name, value)
|
|
142
|
+
else:
|
|
143
|
+
client.__dict__[self.name] = value
|
|
144
|
+
return value
|
|
145
|
+
|
|
146
|
+
def coerce(self, value):
|
|
147
|
+
"""Coerce the given value to our type"""
|
|
148
|
+
return value
|
|
149
|
+
|
|
150
|
+
def check(self, value):
|
|
151
|
+
"Raise ValueError if isn't correct type"
|
|
152
|
+
return value
|
|
153
|
+
|
|
154
|
+
def getDefault(self, client=None):
|
|
155
|
+
"""Get the default value of this field
|
|
156
|
+
|
|
157
|
+
if client, set client's attribute to default
|
|
158
|
+
without sending a notification event.
|
|
159
|
+
"""
|
|
160
|
+
if self.call_default:
|
|
161
|
+
defaultobj = self.defaultobj()
|
|
162
|
+
else:
|
|
163
|
+
defaultobj = self.defaultobj
|
|
164
|
+
if client is not None:
|
|
165
|
+
defaultobj = self._set(client, defaultobj)
|
|
166
|
+
return defaultobj
|
|
167
|
+
|
|
168
|
+
def __delete__(self, client):
|
|
169
|
+
"""Delete our value from client's dictionary"""
|
|
170
|
+
try:
|
|
171
|
+
client.__dict__[self.name]
|
|
172
|
+
except KeyError:
|
|
173
|
+
raise AttributeError(self.name)
|
|
174
|
+
|
|
175
|
+
def fdel(self, client, notify=True):
|
|
176
|
+
"""Delete with notify"""
|
|
177
|
+
self.__delete__(client)
|
|
178
|
+
if notify:
|
|
179
|
+
dispatcher.send(
|
|
180
|
+
('del', self),
|
|
181
|
+
client,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class Field(BaseField):
|
|
186
|
+
"""Property sub-class with VRML field semantics
|
|
187
|
+
|
|
188
|
+
The field basically binds a name, a dataType, and
|
|
189
|
+
a default value (with some other meta-data that isn't
|
|
190
|
+
actually used by the current implementation).
|
|
191
|
+
|
|
192
|
+
Fields are normally accessed through the protofunctions
|
|
193
|
+
module, which retrieves field objects from node or
|
|
194
|
+
prototype objects.
|
|
195
|
+
|
|
196
|
+
The field offers vrml.dispatcher notification of
|
|
197
|
+
changes to values (see fget, fset and fdel methods).
|
|
198
|
+
Which allows code to watch for those changes, a
|
|
199
|
+
facility you can see in the OpenGLContext.scenegraph.cache
|
|
200
|
+
module.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
nodes = 0
|
|
204
|
+
defaultDefault = None
|
|
205
|
+
|
|
206
|
+
def __init__(
|
|
207
|
+
self,
|
|
208
|
+
name,
|
|
209
|
+
exposure=1,
|
|
210
|
+
default=_NULL,
|
|
211
|
+
):
|
|
212
|
+
"""Initialise the field object
|
|
213
|
+
|
|
214
|
+
name -- string name
|
|
215
|
+
exposure -- boolean (0/1) indicating whether this is an exposed field
|
|
216
|
+
default -- default value for the field
|
|
217
|
+
"""
|
|
218
|
+
self.exposure = exposure
|
|
219
|
+
if default is _NULL:
|
|
220
|
+
default = self.defaultDefault
|
|
221
|
+
super(Field, self).__init__(name, default)
|
|
222
|
+
setattr(self, "__doc__", str(self))
|
|
223
|
+
|
|
224
|
+
def fhas(self, client):
|
|
225
|
+
"""Determine whether the client currently has a non-default value"""
|
|
226
|
+
if isinstance(client, type):
|
|
227
|
+
return hasattr(client, self.name)
|
|
228
|
+
elif self.name in client.__dict__:
|
|
229
|
+
return 1
|
|
230
|
+
else:
|
|
231
|
+
return 0
|
|
232
|
+
|
|
233
|
+
def copy(self, client=None, copier=None):
|
|
234
|
+
"""Copy this property's value/definition for client node/proto
|
|
235
|
+
|
|
236
|
+
if client is a prototype, copy this field definition
|
|
237
|
+
for use in a new prototype.
|
|
238
|
+
|
|
239
|
+
if client is a node, and it has a set value for this
|
|
240
|
+
field, then returns self.copyValue( currentValue )
|
|
241
|
+
|
|
242
|
+
otherwise returns _NULL, a singleton object which
|
|
243
|
+
shouldn't turn up anywhere else.
|
|
244
|
+
"""
|
|
245
|
+
if isinstance(client, type):
|
|
246
|
+
# copy the field definition itself...
|
|
247
|
+
return self.__class__(
|
|
248
|
+
self.name,
|
|
249
|
+
self.exposure,
|
|
250
|
+
self.copyValue(self.defaultobj, copier),
|
|
251
|
+
)
|
|
252
|
+
elif self.fhas(client):
|
|
253
|
+
return self.copyValue(self.fget(client), copier)
|
|
254
|
+
else:
|
|
255
|
+
return _NULL
|
|
256
|
+
|
|
257
|
+
def copyValue(self, value, copier=None):
|
|
258
|
+
"""Copy a value for copier"""
|
|
259
|
+
return value
|
|
260
|
+
|
|
261
|
+
def typeName(self):
|
|
262
|
+
"""Get the typeName of this field"""
|
|
263
|
+
return typeName(self.__class__)
|
|
264
|
+
|
|
265
|
+
def __str__(self):
|
|
266
|
+
"""Get a human-friendly representation of the field"""
|
|
267
|
+
if self.exposure:
|
|
268
|
+
exposed = "exposedField"
|
|
269
|
+
else:
|
|
270
|
+
exposed = "field"
|
|
271
|
+
if self.defaultobj is list:
|
|
272
|
+
default = '[]'
|
|
273
|
+
else:
|
|
274
|
+
default = str(self.defaultobj)[:20]
|
|
275
|
+
return '%s %s %s %s' % (
|
|
276
|
+
exposed,
|
|
277
|
+
self.typeName(),
|
|
278
|
+
self.name,
|
|
279
|
+
default,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
def vrmlstr(self, value, lineariser):
|
|
283
|
+
"""Convert the given value to a VRML97 representation"""
|
|
284
|
+
return ""
|
|
285
|
+
|
|
286
|
+
def fieldVrmlstr(self, lineariser):
|
|
287
|
+
"""Write the field's definition to the lineariser
|
|
288
|
+
|
|
289
|
+
Basically this gives you a VRML97 fragment
|
|
290
|
+
which can be used for creating a PROTO which
|
|
291
|
+
will have the equivalent of this field available.
|
|
292
|
+
"""
|
|
293
|
+
if self.exposure:
|
|
294
|
+
exposed = "exposedField"
|
|
295
|
+
else:
|
|
296
|
+
exposed = "field"
|
|
297
|
+
lineariser.buffer.write(
|
|
298
|
+
'%s %s %s '
|
|
299
|
+
% (
|
|
300
|
+
exposed,
|
|
301
|
+
self.typeName(),
|
|
302
|
+
self.name,
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
result = self.vrmlstr(
|
|
306
|
+
# coerce is necessary because the
|
|
307
|
+
# default values are often not in
|
|
308
|
+
# the canonical representation
|
|
309
|
+
self.coerce(self.getDefault()),
|
|
310
|
+
lineariser,
|
|
311
|
+
)
|
|
312
|
+
if result:
|
|
313
|
+
lineariser.buffer.write(result)
|
|
314
|
+
return
|
|
315
|
+
|
|
316
|
+
def watch(self, node, receiver, signal=dispatcher.Any):
|
|
317
|
+
"""Make receiver receive all update events for this field+node
|
|
318
|
+
|
|
319
|
+
receiver( signal, sender, value=None )
|
|
320
|
+
|
|
321
|
+
signal -- ('del',self), ('set',self) etc...
|
|
322
|
+
sender -- node
|
|
323
|
+
value -- new value set (for set values)
|
|
324
|
+
"""
|
|
325
|
+
return dispatcher.connect(
|
|
326
|
+
receiver=receiver,
|
|
327
|
+
sender=node,
|
|
328
|
+
signal=signal,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
def __lt__(self, other):
|
|
332
|
+
if isinstance(other, Field):
|
|
333
|
+
return (self.__class__.__name__, self.name) < (
|
|
334
|
+
other.__class__.__name__,
|
|
335
|
+
other.name,
|
|
336
|
+
)
|
|
337
|
+
return False
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class WeakField(object):
|
|
341
|
+
"""A Mix-in for fields which stores weak-references to values"""
|
|
342
|
+
|
|
343
|
+
def fset(self, client, value, notify=1):
|
|
344
|
+
"""Set the client's value for this property
|
|
345
|
+
|
|
346
|
+
if notify is true send a notification event.
|
|
347
|
+
"""
|
|
348
|
+
if isinstance(value, weakref.ReferenceType):
|
|
349
|
+
value = value()
|
|
350
|
+
if not value:
|
|
351
|
+
if not isinstance(client, type):
|
|
352
|
+
self.fdel(client, notify=notify)
|
|
353
|
+
return None
|
|
354
|
+
value = weakref.ref(value)
|
|
355
|
+
value = super(WeakField, self).fset(client, value, notify=notify)
|
|
356
|
+
return value()
|
|
357
|
+
|
|
358
|
+
def fget(self, client):
|
|
359
|
+
"""Get the client's value for this property
|
|
360
|
+
|
|
361
|
+
if notify is true send a notification event.
|
|
362
|
+
"""
|
|
363
|
+
value = super(WeakField, self).fget(client)
|
|
364
|
+
if not value:
|
|
365
|
+
# is already default value, since refs are always non-null
|
|
366
|
+
return value
|
|
367
|
+
if isinstance(value, weakref.ReferenceType):
|
|
368
|
+
value = value()
|
|
369
|
+
# value now really is the ref'd value, or None
|
|
370
|
+
if value is None:
|
|
371
|
+
if not isinstance(client, type):
|
|
372
|
+
self.fdel(client, notify=0)
|
|
373
|
+
return None # super( WeakField, self).fget( client )
|
|
374
|
+
return value
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class Event(object):
|
|
378
|
+
"""An Event-handling Port definition
|
|
379
|
+
|
|
380
|
+
The event is currently non-functional, it's just
|
|
381
|
+
here to allow VRML content to parse and be represented
|
|
382
|
+
in-memory.
|
|
383
|
+
"""
|
|
384
|
+
|
|
385
|
+
def __init__(self, name, direction=1):
|
|
386
|
+
"""Initialise the field object
|
|
387
|
+
|
|
388
|
+
name -- string name
|
|
389
|
+
direction -- 0 == in, 1 == out
|
|
390
|
+
"""
|
|
391
|
+
self.name = name
|
|
392
|
+
self.direction = direction
|
|
393
|
+
|
|
394
|
+
def __set__(self, client, value, notify=1):
|
|
395
|
+
"""Set an event value"""
|
|
396
|
+
method = getattr(client, 'on_%s' % (self.name,), None)
|
|
397
|
+
if method is not None:
|
|
398
|
+
robustapply.robustApply(
|
|
399
|
+
method,
|
|
400
|
+
value,
|
|
401
|
+
event=self,
|
|
402
|
+
)
|
|
403
|
+
# now set as a normal property...
|
|
404
|
+
client.__dict__[self.name] = value
|
|
405
|
+
# and then send event letting world know...
|
|
406
|
+
if notify:
|
|
407
|
+
dispatcher.send(('set', self), client, value=value)
|
|
408
|
+
|
|
409
|
+
def __get__(self, client=None, cls=None):
|
|
410
|
+
"""Get an event's last value"""
|
|
411
|
+
if client is None:
|
|
412
|
+
return self
|
|
413
|
+
try:
|
|
414
|
+
return client.__dict__[self.name]
|
|
415
|
+
except KeyError:
|
|
416
|
+
raise AttributeError(
|
|
417
|
+
"""Event %s doesn't have a value for %s"""
|
|
418
|
+
% (
|
|
419
|
+
self.name,
|
|
420
|
+
client,
|
|
421
|
+
)
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
def clone(self, name=None, direction=None):
|
|
425
|
+
"""Clone this property"""
|
|
426
|
+
if name is None:
|
|
427
|
+
name = self.name
|
|
428
|
+
if direction is None:
|
|
429
|
+
direction = self.direction
|
|
430
|
+
return self.__class__(name, direction)
|
|
431
|
+
|
|
432
|
+
def typeName(self):
|
|
433
|
+
"""Get the typeName of this field"""
|
|
434
|
+
return typeName(self.__class__)
|
|
435
|
+
|
|
436
|
+
def __str__(self):
|
|
437
|
+
"""Get a human-friendly representation of the event"""
|
|
438
|
+
if self.direction:
|
|
439
|
+
exposed = "eventOut"
|
|
440
|
+
else:
|
|
441
|
+
exposed = "eventIn"
|
|
442
|
+
return '%s %s %s' % (exposed, self.typeName(), self.name)
|
|
443
|
+
|
|
444
|
+
def eventVrmlstr(self, lineariser):
|
|
445
|
+
"""Write the event's definition to the lineariser
|
|
446
|
+
|
|
447
|
+
Basically this gives you a VRML97 fragment
|
|
448
|
+
which can be used for creating a PROTO which
|
|
449
|
+
will have the equivalent of this event available.
|
|
450
|
+
"""
|
|
451
|
+
if self.direction:
|
|
452
|
+
exposed = "eventOut"
|
|
453
|
+
else:
|
|
454
|
+
exposed = "eventIn"
|
|
455
|
+
base = '%s %s %s' % (
|
|
456
|
+
exposed,
|
|
457
|
+
self.typeName(),
|
|
458
|
+
self.name,
|
|
459
|
+
)
|
|
460
|
+
lineariser.buffer.write(base)
|
|
461
|
+
return base
|
|
462
|
+
|
|
463
|
+
def watch(self, node, receiver, signal=dispatcher.Any):
|
|
464
|
+
"""Make receiver receive all update events for this field+node
|
|
465
|
+
|
|
466
|
+
receiver( signal, sender, value=None )
|
|
467
|
+
|
|
468
|
+
signal -- ('del',self), ('set',self) etc...
|
|
469
|
+
sender -- node
|
|
470
|
+
value -- new value set (for set values)
|
|
471
|
+
"""
|
|
472
|
+
return dispatcher.connect(
|
|
473
|
+
receiver=receiver,
|
|
474
|
+
sender=node,
|
|
475
|
+
signal=signal,
|
|
476
|
+
)
|