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/node.py
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
"""Base-class for scenegraph nodes
|
|
2
|
+
|
|
3
|
+
Requires Python 2.2.x, as it makes
|
|
4
|
+
extensive use of properties
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from vrml import field, fieldtypes, weaklist, weakkeydictfix
|
|
8
|
+
from vrml import copier as copiermodule
|
|
9
|
+
from vrml import olist
|
|
10
|
+
from vrml.protofunctions import *
|
|
11
|
+
from pydispatch import dispatcher
|
|
12
|
+
import weakref
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Node(object):
|
|
16
|
+
"""A generic scene graph node
|
|
17
|
+
|
|
18
|
+
Unlike earlier versions of the library,
|
|
19
|
+
this implementation of the Node class is
|
|
20
|
+
basically a regular python class. This is
|
|
21
|
+
possible because it uses the python 2.2.x
|
|
22
|
+
property/descriptor API extensively.
|
|
23
|
+
|
|
24
|
+
Technically this is a multiple-hierarchy DAG
|
|
25
|
+
node, as there can be any number of node
|
|
26
|
+
children attributes, and nodes may appear
|
|
27
|
+
multiple times in the hierarchy.
|
|
28
|
+
|
|
29
|
+
Attributes of note:
|
|
30
|
+
" DEF" field
|
|
31
|
+
a simple string field which stores the
|
|
32
|
+
DEF name of a node instance
|
|
33
|
+
" scenegraph" attribute
|
|
34
|
+
pointer to the node's implementation
|
|
35
|
+
scenegraph (at the moment, this is not
|
|
36
|
+
actually used for anything)
|
|
37
|
+
" PROTO" attribute
|
|
38
|
+
stores the PROTO name of the node
|
|
39
|
+
externalURL attribute
|
|
40
|
+
stores the MFString url for the node's
|
|
41
|
+
externproto definition if appropriate
|
|
42
|
+
toString method
|
|
43
|
+
convenience access to the lineariser
|
|
44
|
+
to give VRML97-formatted representation of
|
|
45
|
+
the node
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
DEF = fieldtypes.SFString(' DEF', 1, '')
|
|
49
|
+
# scenegraph = None # will be created below...
|
|
50
|
+
# rootSceneGraph = None # will be created below
|
|
51
|
+
externalURL = fieldtypes.MFString('externalURL', 1)
|
|
52
|
+
PROTO = ""
|
|
53
|
+
|
|
54
|
+
def __init__(self, **namedarguments):
|
|
55
|
+
"""Initialise the node with appropriate named args
|
|
56
|
+
|
|
57
|
+
All properties/attributes must be specified with
|
|
58
|
+
named arguments, and the property/attribute must
|
|
59
|
+
exist within the Node's class/prototype.
|
|
60
|
+
|
|
61
|
+
This will raise AttributeError/ValueError/TypeError
|
|
62
|
+
if the values or the property names are inappropriate.
|
|
63
|
+
|
|
64
|
+
Note that all Node objects have the attribute/property
|
|
65
|
+
exposedField SFString DEF ""
|
|
66
|
+
defined. You may therefore specify a DEF name by
|
|
67
|
+
passing it as a named argument.
|
|
68
|
+
"""
|
|
69
|
+
for key, value in namedarguments.items():
|
|
70
|
+
try:
|
|
71
|
+
f = getField(self, key)
|
|
72
|
+
except AttributeError:
|
|
73
|
+
raise AttributeError(
|
|
74
|
+
"""Unrecognised attribute %r for node type %r"""
|
|
75
|
+
% (key, self.__class__.__name__)
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
if not (hasattr(f, '__get__') and hasattr(f, '__set__')):
|
|
79
|
+
raise TypeError(
|
|
80
|
+
"""Attempt to set a non-field attribute %s to %s for node type %s"""
|
|
81
|
+
% (key, value, self.__class__.__name__)
|
|
82
|
+
)
|
|
83
|
+
f.fset(self, value)
|
|
84
|
+
|
|
85
|
+
def __str__(self):
|
|
86
|
+
"""Get a friendly representation of the Node"""
|
|
87
|
+
if 'DEF' in self.__dict__ and defName(self):
|
|
88
|
+
return """%s( DEF=%r @0x%X )""" % (
|
|
89
|
+
self.__class__.__name__,
|
|
90
|
+
defName(self),
|
|
91
|
+
id(self),
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
return """%s( @0x%X )""" % (
|
|
95
|
+
self.__class__.__name__,
|
|
96
|
+
id(self),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def __repr__(self):
|
|
100
|
+
"""Get a code-like representation of the Node
|
|
101
|
+
|
|
102
|
+
Basically every attribute except for sub-nodes values
|
|
103
|
+
are returned as a full representation.
|
|
104
|
+
"""
|
|
105
|
+
attributes = []
|
|
106
|
+
for field in getFields(self):
|
|
107
|
+
if field.name in self.__dict__:
|
|
108
|
+
value = getattr(self, field.name)
|
|
109
|
+
if isinstance(value, Node):
|
|
110
|
+
representation = str(value)
|
|
111
|
+
else:
|
|
112
|
+
representation = repr(value)
|
|
113
|
+
attributes.append(
|
|
114
|
+
"""%s = %s"""
|
|
115
|
+
% (
|
|
116
|
+
field.name,
|
|
117
|
+
representation,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return """%s(\n\t%s\n)""" % (
|
|
122
|
+
self.__class__.__name__,
|
|
123
|
+
",\n\t".join(attributes),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def copy(self, copier=None):
|
|
127
|
+
"""Copy this node for copier"""
|
|
128
|
+
if copier is None:
|
|
129
|
+
copier = copiermodule.Copier()
|
|
130
|
+
previous = copier.use(self)
|
|
131
|
+
if previous is not None:
|
|
132
|
+
return previous
|
|
133
|
+
dictionary = {}
|
|
134
|
+
for field in getFields(self):
|
|
135
|
+
if field.fhas(self):
|
|
136
|
+
dictionary[field.name] = field.copy(self, copier)
|
|
137
|
+
newNode = type(self).__new__(type(self))
|
|
138
|
+
newNode.__dict__.update(dictionary)
|
|
139
|
+
copier.use(self, newNode)
|
|
140
|
+
return newNode
|
|
141
|
+
|
|
142
|
+
def toString(self, **namedargs):
|
|
143
|
+
'''Generate a VRML 97-syntax string representing this Prototype
|
|
144
|
+
**namedargs -- key:value
|
|
145
|
+
passed arguments for the linearisation object
|
|
146
|
+
see lineariser4.Lineariser
|
|
147
|
+
'''
|
|
148
|
+
from vrml.vrml97 import linearise
|
|
149
|
+
|
|
150
|
+
return linearise.linearise(self, **namedargs)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class PrototypedNode(object):
|
|
154
|
+
"""Prototyped node mix-in
|
|
155
|
+
|
|
156
|
+
Note the presence of a " scenegraph" property
|
|
157
|
+
for the node created below (due to mutual dependencies).
|
|
158
|
+
This is filled by the instantiate method to provide the
|
|
159
|
+
actual implementation of the node.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
def __init__(self, *arguments, **namedarguments):
|
|
163
|
+
"""Initialise the node with appropriate named args
|
|
164
|
+
|
|
165
|
+
Also attempts to instantiate the sub-node scenegraph
|
|
166
|
+
for the PrototypedNode
|
|
167
|
+
"""
|
|
168
|
+
PrototypedNode.instantiate(self)
|
|
169
|
+
super(PrototypedNode, self).__init__(*arguments, **namedarguments)
|
|
170
|
+
|
|
171
|
+
def instantiate(self):
|
|
172
|
+
"""Make a copy of the class scenegraph-template for this node
|
|
173
|
+
|
|
174
|
+
Also needs to bind IS mappings/routes for the template,
|
|
175
|
+
and negotiate not-yet-loaded external prototypes and
|
|
176
|
+
the like.
|
|
177
|
+
"""
|
|
178
|
+
from vrml import route
|
|
179
|
+
|
|
180
|
+
isMappings = None
|
|
181
|
+
for cls in self.__class__.__mro__[:-1]:
|
|
182
|
+
template = PrototypedNode.scenegraph.fget(cls)
|
|
183
|
+
if template:
|
|
184
|
+
# use ismaps for the instantiated scenegraph
|
|
185
|
+
isMappings = list(ismaps(cls).items())
|
|
186
|
+
break
|
|
187
|
+
if isMappings is None:
|
|
188
|
+
# no scenegraph defined...
|
|
189
|
+
from vrml.vrml97 import scenegraph
|
|
190
|
+
|
|
191
|
+
template = scenegraph.SceneGraph()
|
|
192
|
+
PrototypedNode.scenegraph.fset(cls, template)
|
|
193
|
+
isMappings = []
|
|
194
|
+
# raise ValueError( """Attempting to instantiate a prototyped node with no scenegraph defined: %s"""%( self,))
|
|
195
|
+
|
|
196
|
+
copier = copiermodule.Copier()
|
|
197
|
+
sg = template.copy(copier)
|
|
198
|
+
for fieldName, mappings in isMappings:
|
|
199
|
+
sourceField = getField(self, fieldName)
|
|
200
|
+
for destination, destinationField in mappings:
|
|
201
|
+
r = route.IS(
|
|
202
|
+
source=self,
|
|
203
|
+
sourceField=fieldName,
|
|
204
|
+
destination=destination,
|
|
205
|
+
destinationField=destinationField,
|
|
206
|
+
)
|
|
207
|
+
sg.routes.append(r)
|
|
208
|
+
if hasattr(sourceField, 'getDefault'):
|
|
209
|
+
default = sourceField.getDefault()
|
|
210
|
+
try:
|
|
211
|
+
getField(destination, destinationField).fset(
|
|
212
|
+
destination,
|
|
213
|
+
default,
|
|
214
|
+
notify=0,
|
|
215
|
+
)
|
|
216
|
+
except AttributeError:
|
|
217
|
+
pass
|
|
218
|
+
PrototypedNode.scenegraph.fset(self, sg)
|
|
219
|
+
|
|
220
|
+
def renderedChildren(self, types=None):
|
|
221
|
+
"""Get the rendered children of the scenegraph"""
|
|
222
|
+
if types:
|
|
223
|
+
return [
|
|
224
|
+
node
|
|
225
|
+
for node in PrototypedNode.scenegraph.fget(self).children
|
|
226
|
+
if isinstance(node, types)
|
|
227
|
+
]
|
|
228
|
+
else:
|
|
229
|
+
return PrototypedNode.scenegraph.fget(self).children
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def prototype(
|
|
233
|
+
name,
|
|
234
|
+
fields=(),
|
|
235
|
+
sceneGraph=None,
|
|
236
|
+
externalURL=None,
|
|
237
|
+
baseClasses=(PrototypedNode, Node),
|
|
238
|
+
):
|
|
239
|
+
"""Build a new prototype class
|
|
240
|
+
|
|
241
|
+
name -- string name
|
|
242
|
+
fields -- sequence of vrml.field objects
|
|
243
|
+
sceneGraph -- the source scenegraph for prototyped nodes
|
|
244
|
+
externalURL -- MFString URL or None
|
|
245
|
+
baseClasses -- base classes for the new class
|
|
246
|
+
"""
|
|
247
|
+
environment = {
|
|
248
|
+
'PROTO': name,
|
|
249
|
+
}
|
|
250
|
+
for field in fields:
|
|
251
|
+
environment[field.name] = field
|
|
252
|
+
returnValue = type(
|
|
253
|
+
name,
|
|
254
|
+
baseClasses,
|
|
255
|
+
environment,
|
|
256
|
+
)
|
|
257
|
+
if sceneGraph is not None:
|
|
258
|
+
setSceneGraph(returnValue, sceneGraph)
|
|
259
|
+
if externalURL is not None:
|
|
260
|
+
setExternalURL(returnValue, externalURL)
|
|
261
|
+
return returnValue
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class NullNode(Node):
|
|
265
|
+
'''NULL SFNode value
|
|
266
|
+
There should only be a single NULL instance for
|
|
267
|
+
any particular system. It should, for all intents and
|
|
268
|
+
purposes just sit there inertly
|
|
269
|
+
'''
|
|
270
|
+
|
|
271
|
+
PROTO = 'NULL'
|
|
272
|
+
|
|
273
|
+
def __repr__(self):
|
|
274
|
+
"""Get code-like representation of NULL node"""
|
|
275
|
+
return '<NULL vrml SFNode>'
|
|
276
|
+
|
|
277
|
+
def __nonzero__(self):
|
|
278
|
+
"""Make the NULL node evaluate to false"""
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
__bool__ = __nonzero__
|
|
282
|
+
|
|
283
|
+
def __eq__(self, other):
|
|
284
|
+
"""Compare the NULL node to other objects"""
|
|
285
|
+
try:
|
|
286
|
+
if protoName(self) == protoName(other):
|
|
287
|
+
return 0
|
|
288
|
+
except (AttributeError, TypeError, ValueError):
|
|
289
|
+
return -1 # could be 1, doesn't really matter
|
|
290
|
+
|
|
291
|
+
def clone(self):
|
|
292
|
+
"""Replicate the null object (return another pointer to it)"""
|
|
293
|
+
return self
|
|
294
|
+
|
|
295
|
+
def __str__(self):
|
|
296
|
+
"""Get a human-friendly representation of the NULL node"""
|
|
297
|
+
return "NULL"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
NULL = NullNode()
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class _SFNode(object):
|
|
304
|
+
"""Base-class for SFNode-type fields
|
|
305
|
+
|
|
306
|
+
The optionally restricted SFNode field type
|
|
307
|
+
allows a node to hold a reference to another node
|
|
308
|
+
within the directed acyclic graph.
|
|
309
|
+
|
|
310
|
+
There are two primary attributes:
|
|
311
|
+
|
|
312
|
+
requiredTypes -- a type or tuple of types that
|
|
313
|
+
are acceptable as values for the field
|
|
314
|
+
allowNULL -- whether to allow the NULL node as
|
|
315
|
+
a value for the field
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
nodes = 1
|
|
319
|
+
requiredTypes = ()
|
|
320
|
+
allowNULL = 1
|
|
321
|
+
|
|
322
|
+
def fset(self, client, value, notify=1):
|
|
323
|
+
"""Set the client's value for this property
|
|
324
|
+
|
|
325
|
+
notify -- if true send a notification event
|
|
326
|
+
|
|
327
|
+
The SFNode tries to update the value's root
|
|
328
|
+
attribute to point to the root of the client
|
|
329
|
+
*iff* the value doesn't currently point at
|
|
330
|
+
a valid root. (That is, it only updates root
|
|
331
|
+
if there is no current root). This is done
|
|
332
|
+
without sending notify events.
|
|
333
|
+
"""
|
|
334
|
+
value = super(_SFNode, self).fset(client, value, notify)
|
|
335
|
+
if value:
|
|
336
|
+
clientRoot = Node.rootSceneGraph.fget(client)
|
|
337
|
+
if clientRoot:
|
|
338
|
+
valueRoot = Node.rootSceneGraph.fget(value)
|
|
339
|
+
if not valueRoot:
|
|
340
|
+
Node.rootSceneGraph.fset(value, clientRoot, notify=0)
|
|
341
|
+
return value
|
|
342
|
+
|
|
343
|
+
def defaultDefault(self):
|
|
344
|
+
"""Default SFNode value"""
|
|
345
|
+
return NULL
|
|
346
|
+
|
|
347
|
+
def coerce(self, value):
|
|
348
|
+
"""Coerce value to an SFNode reference"""
|
|
349
|
+
if self.requiredTypes and isinstance(value, self.requiredTypes):
|
|
350
|
+
return value
|
|
351
|
+
elif value is None and self.allowNULL:
|
|
352
|
+
return NULL
|
|
353
|
+
elif isinstance(value, str):
|
|
354
|
+
raise ValueError(
|
|
355
|
+
"""SFNode field %s was set to a string, not currently supported: %s"""
|
|
356
|
+
% (self, value[:30])
|
|
357
|
+
)
|
|
358
|
+
elif isinstance(value, field.SEQUENCE_TYPES) and len(value) == 1:
|
|
359
|
+
return self.coerce(value[0])
|
|
360
|
+
elif not self.requiredTypes:
|
|
361
|
+
return value
|
|
362
|
+
else:
|
|
363
|
+
raise ValueError(
|
|
364
|
+
"""Attempted to set value for an %s field which is not compatible: %s, needed instance of %s"""
|
|
365
|
+
% (self.name, repr(value), self.requiredTypes)
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
def vrmlstr(self, value, lineariser):
|
|
369
|
+
"""Convert the given value to a VRML97 representation"""
|
|
370
|
+
return lineariser._linear(value)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
class SFNode(_SFNode, field.Field):
|
|
374
|
+
"""(Restricted) SFNode type
|
|
375
|
+
|
|
376
|
+
This is the publically available SFNode type,
|
|
377
|
+
a sub-class of _SFNode and field.Field
|
|
378
|
+
"""
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
SFNode.requiredTypes = (Node,)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
class SFNodeEvt(_SFNode, field.Event):
|
|
385
|
+
fieldType = 'SFNode'
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
field.register(SFNode)
|
|
389
|
+
field.register(SFNodeEvt)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class WeakSFNode(_SFNode, field.WeakField, field.Field):
|
|
393
|
+
"""Weak-referenced SFNode field-type"""
|
|
394
|
+
|
|
395
|
+
fieldType = 'WeakSFNode'
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
class RootScenegraphNode(WeakSFNode):
|
|
399
|
+
fieldType = 'RootScenegraphNode'
|
|
400
|
+
|
|
401
|
+
def fset(self, client, value, notify=1):
|
|
402
|
+
"""Set the root scenegraph node (recursively)
|
|
403
|
+
|
|
404
|
+
TODO: this will blow up on cyclic graphs!
|
|
405
|
+
"""
|
|
406
|
+
result = super(RootScenegraphNode, self).fset(client, value, notify)
|
|
407
|
+
for field in getFields(client.__class__):
|
|
408
|
+
if (
|
|
409
|
+
isinstance(field, SFNode)
|
|
410
|
+
and not isinstance(field, RootScenegraphNode)
|
|
411
|
+
and not field is PrototypedNode.scenegraph
|
|
412
|
+
):
|
|
413
|
+
try:
|
|
414
|
+
child = field.__get__(client)
|
|
415
|
+
except ValueError:
|
|
416
|
+
pass
|
|
417
|
+
else:
|
|
418
|
+
self.fset(child, value, notify=False)
|
|
419
|
+
elif isinstance(field, MFNode):
|
|
420
|
+
try:
|
|
421
|
+
for child in field.__get__(client):
|
|
422
|
+
self.fset(child, value, notify=False)
|
|
423
|
+
except AttributeError:
|
|
424
|
+
pass
|
|
425
|
+
elif field.name == ' DEF':
|
|
426
|
+
try:
|
|
427
|
+
DEF = field.__get__(client)
|
|
428
|
+
value.regDefName(DEF, client)
|
|
429
|
+
except AttributeError:
|
|
430
|
+
pass
|
|
431
|
+
return result
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
field.register(WeakSFNode)
|
|
435
|
+
field.register(RootScenegraphNode)
|
|
436
|
+
|
|
437
|
+
PrototypedNode.scenegraph = SFNode(' scenegraph', 1, NULL)
|
|
438
|
+
Node.rootSceneGraph = RootScenegraphNode(' root', 1, NULL)
|
|
439
|
+
assert PrototypedNode.scenegraph.name == " scenegraph", PrototypedNode.scenegraph.name
|
|
440
|
+
assert Node.rootSceneGraph.name == " root", Node.rootSceneGraph.name
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _changeSender(nodeRef, field):
|
|
444
|
+
"""Utility function to send node-change messages on olist updates"""
|
|
445
|
+
|
|
446
|
+
def onOListChange(sender, signal, value):
|
|
447
|
+
client = nodeRef()
|
|
448
|
+
if client:
|
|
449
|
+
dispatcher.send(
|
|
450
|
+
('set', field),
|
|
451
|
+
client,
|
|
452
|
+
value=sender,
|
|
453
|
+
subsignal=signal,
|
|
454
|
+
subvalue=value,
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
return onOListChange
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
class _MFNode(object):
|
|
461
|
+
"""(Restricted) MFNode field-type-definition"""
|
|
462
|
+
|
|
463
|
+
nodes = 1
|
|
464
|
+
defaultDefault = olist.OList
|
|
465
|
+
baseSFNode = SFNode('GeneralSFNode')
|
|
466
|
+
baseObjectType = olist.OList
|
|
467
|
+
|
|
468
|
+
def fset(self, client, value, notify=1):
|
|
469
|
+
"""Set the client's value for this property
|
|
470
|
+
|
|
471
|
+
notify -- if true send a notification event
|
|
472
|
+
|
|
473
|
+
The MFNode tries to update the value's root
|
|
474
|
+
attribute to point to the root of the client
|
|
475
|
+
*iff* the value doesn't currently point at
|
|
476
|
+
a valid root. (That is, it only updates root
|
|
477
|
+
if there is no current root). This is done
|
|
478
|
+
without sending notify events.
|
|
479
|
+
"""
|
|
480
|
+
previous = client.__dict__.get(self.name)
|
|
481
|
+
if previous is not None:
|
|
482
|
+
previous[:] = self.coerce(value)
|
|
483
|
+
value = previous
|
|
484
|
+
else:
|
|
485
|
+
value = super(_MFNode, self).fset(client, value, notify)
|
|
486
|
+
# register for updates to the list...
|
|
487
|
+
# we just send "changed" events for the field whenever
|
|
488
|
+
# there's an update to the list... a bit wasteful, as
|
|
489
|
+
# our clients might want to know about just the changed
|
|
490
|
+
# values, but for now...
|
|
491
|
+
value.setSender(client, field=self)
|
|
492
|
+
cs = _changeSender(weakref.ref(client), self)
|
|
493
|
+
dispatcher.connect(
|
|
494
|
+
cs,
|
|
495
|
+
sender=client,
|
|
496
|
+
signal=olist.OList.DEL_CHILD_EVT,
|
|
497
|
+
weak=False, # don't weakref receiver so it will hang around...
|
|
498
|
+
)
|
|
499
|
+
dispatcher.connect(
|
|
500
|
+
cs,
|
|
501
|
+
sender=client,
|
|
502
|
+
signal=olist.OList.NEW_CHILD_EVT,
|
|
503
|
+
weak=False, # don't weakref receiver so it will hang around...
|
|
504
|
+
)
|
|
505
|
+
if value:
|
|
506
|
+
clientRoot = Node.rootSceneGraph.fget(client)
|
|
507
|
+
if clientRoot:
|
|
508
|
+
for val in value:
|
|
509
|
+
valueRoot = Node.rootSceneGraph.fget(val)
|
|
510
|
+
if not valueRoot:
|
|
511
|
+
Node.rootSceneGraph.fset(val, clientRoot, notify=0)
|
|
512
|
+
return value
|
|
513
|
+
|
|
514
|
+
def coerce(self, value):
|
|
515
|
+
"""Coerce value to an MFNode list-of-objects"""
|
|
516
|
+
SF = self.__class__.baseSFNode
|
|
517
|
+
if SF.requiredTypes and isinstance(value, SF.requiredTypes):
|
|
518
|
+
return self.baseObjectType([value])
|
|
519
|
+
elif not value:
|
|
520
|
+
return self.baseObjectType([])
|
|
521
|
+
elif isinstance(value, field.SEQUENCE_TYPES):
|
|
522
|
+
return self.baseObjectType([SF.coerce(item) for item in value])
|
|
523
|
+
else:
|
|
524
|
+
raise ValueError(
|
|
525
|
+
"""Attempted to set value for an %s field which is not compatible: %s"""
|
|
526
|
+
% (self.name, repr(value))
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
def vrmlstr(self, value, lineariser):
|
|
530
|
+
"""Convert the given value to a VRML97 representation"""
|
|
531
|
+
return lineariser._mfnode(value)
|
|
532
|
+
|
|
533
|
+
def copyValue(self, value, copier=None):
|
|
534
|
+
"""Copy a value for copier"""
|
|
535
|
+
SF = self.__class__.baseSFNode
|
|
536
|
+
return [SF.copyValue(node, copier) for node in value]
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
##class WeakMFNode( MFNode ):
|
|
540
|
+
## """Weak-referencing version of an MFNode field-type"""
|
|
541
|
+
## baseObjectType = weaklist.WeakList
|
|
542
|
+
## fieldType = 'WeakMFNode'
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
class MFNode(_MFNode, field.Field):
|
|
546
|
+
"""MFNode Field class"""
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class MFNodeEvt(_MFNode, field.Event):
|
|
550
|
+
"""MFNode Event class"""
|
|
551
|
+
|
|
552
|
+
fieldType = 'MFNode'
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
field.register(MFNode)
|
|
556
|
+
##field.register( WeakMFNode )
|
|
557
|
+
field.register(MFNodeEvt)
|
|
558
|
+
|
|
559
|
+
ISMAPS = weakkeydictfix.WeakKeyDictionary()
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def ismaps(node):
|
|
563
|
+
"""Get the isMaps for the given node
|
|
564
|
+
|
|
565
|
+
Returns a field-name:(sub-node,field) mapping
|
|
566
|
+
Not currently functional
|
|
567
|
+
"""
|
|
568
|
+
current = ISMAPS.setdefault(node, {})
|
|
569
|
+
return current
|
vrml/nodepath.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Representation and manipulation of scenegraph paths
|
|
2
|
+
"""
|
|
3
|
+
from vrml import node, weaklist
|
|
4
|
+
try:
|
|
5
|
+
xrange
|
|
6
|
+
except NameError:
|
|
7
|
+
xrange = range
|
|
8
|
+
|
|
9
|
+
class NodePath( list ):
|
|
10
|
+
"""Path within a scenegraph from root to particular node
|
|
11
|
+
|
|
12
|
+
Has minimal operations, most high-level functionality is
|
|
13
|
+
provided by sub-classes such as vrml.vrml97.nodepath and
|
|
14
|
+
OpenGLContext.scenegraph.nodepath
|
|
15
|
+
"""
|
|
16
|
+
def __repr__( self ):
|
|
17
|
+
"""Code-like representation of the node path
|
|
18
|
+
|
|
19
|
+
Note: this doesn't use super for determining
|
|
20
|
+
the base representation, as that might wind up
|
|
21
|
+
creating a name like:
|
|
22
|
+
WeakNodePath( WeakTuple( Node, Node,...))
|
|
23
|
+
"""
|
|
24
|
+
return '%s(%s)'%(self.__class__.__name__, list.__repr__( self ))
|
|
25
|
+
def __str__( self ):
|
|
26
|
+
"""Simple representation of a node-path for human consumption"""
|
|
27
|
+
return "%s(%s)"%(
|
|
28
|
+
self.__class__.__name__,
|
|
29
|
+
"->".join([
|
|
30
|
+
str( N )
|
|
31
|
+
for N in self
|
|
32
|
+
]))
|
|
33
|
+
def common (self, other):
|
|
34
|
+
"""Return the common root sub-path between ourselves and other
|
|
35
|
+
|
|
36
|
+
If there is no common sub-root, returns an empty path
|
|
37
|
+
"""
|
|
38
|
+
result = []
|
|
39
|
+
for index in range( min(len(self),len(other))):
|
|
40
|
+
if self [index] is other [index]:
|
|
41
|
+
result.append (self [index])
|
|
42
|
+
else:
|
|
43
|
+
break
|
|
44
|
+
return self.__class__(result)
|
|
45
|
+
def __add__(self, other):
|
|
46
|
+
"""Return a new path with other as tail"""
|
|
47
|
+
if isinstance( other, node.Node ):
|
|
48
|
+
other = [other]
|
|
49
|
+
return self.__class__( super(NodePath, self).__add__( other))
|
|
50
|
+
def __getslice__(self, start, stop):
|
|
51
|
+
"""Return a new path with our items from start to stop"""
|
|
52
|
+
return self.__class__(super (NodePath, self).__getslice__(start, stop))
|
|
53
|
+
def __eq__( self, other ):
|
|
54
|
+
"""Check whether we are equal to another path"""
|
|
55
|
+
if len(self) != len(other):
|
|
56
|
+
return 0
|
|
57
|
+
for index in range(len(self)):
|
|
58
|
+
if self [index] is not other [index]:
|
|
59
|
+
return 0
|
|
60
|
+
return 1
|
|
61
|
+
|
|
62
|
+
class WeakNodePath( NodePath, weaklist.WeakList ):
|
|
63
|
+
"""Node path that uses weak-references to nodes
|
|
64
|
+
|
|
65
|
+
You hold strong references to these paths, then
|
|
66
|
+
wrap all uses of them with checks for
|
|
67
|
+
weakref.ReferenceError to check for dead paths.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
|