101
101
102
102
# methods are X in {'m': '23X....'}
103
103
# pos is normally updated as an attribute, but for interval-based trails, it is updated (multiply) as a method
104
- __methods = {'select' :'a' , 'pos' :'b' , 'start' :'c' , 'stop' :'d' , 'clear' :'f' , # unused eghijklmnopvxyzCDFAB
104
+ __methods = {'select' :'a' , 'pos' :'b' , 'start' :'c' , 'stop' :'d' , 'clear' :'f' , # unused eghijklmnopvxyzCDFABu
105
105
'plot' :'q' , 'add_to_trail' :'s' ,
106
- 'follow' :'t' , '_attach_arrow' : 'u' , ' clear_trail' :'w' ,
106
+ 'follow' :'t' , 'clear_trail' :'w' ,
107
107
'bind' :'G' , 'unbind' :'H' , 'waitfor' :'I' , 'pause' :'J' , 'pick' :'K' , 'GSprint' :'L' ,
108
108
'delete' :'M' , 'capture' :'N' }
109
109
110
- __vecattrs = ['pos' , 'up' , 'color' , 'trail_color' , 'axis' , 'size' , 'origin' , '_attach_arrow' ,
110
+ __vecattrs = ['pos' , 'up' , 'color' , 'trail_color' , 'axis' , 'size' , 'origin' ,
111
111
'direction' , 'linecolor' , 'bumpaxis' , 'dot_color' , 'ambient' , 'add_to_trail' ,
112
112
'foreground' , 'background' , 'ray' , 'ambient' , 'center' , 'forward' , 'normal' ,
113
113
'marker_color' ]
@@ -209,14 +209,18 @@ def empty(cls):
209
209
def handle_attach (cls ): # called when about to send data to the browser
210
210
## update every attach_arrow if relevant vector has changed
211
211
for aa in cls .attach_arrows :
212
- obj = baseObj .object_registry [aa ._obj ]
212
+ if not aa ._run : continue
213
+ obj = baseObj .object_registry [aa ._object ]
214
+ if not hasattr (obj , aa .attr ): # no longer an attribute of the object
215
+ continue
213
216
vval = getattr (obj , aa .attr ) # could be 'velocity', for example
214
217
if not isinstance (vval , vector ):
215
- raise AttributeError ("attach_arrow value must be a vector." )
216
- if (isinstance (aa ._last_val , vector ) and aa ._last_val .equals (vval )):
217
- continue
218
- aa .addmethod ('_attach_arrow' , vval .value )
219
- aa ._last_val = vector (vval ) # keep copy of last vector
218
+ raise AttributeError ('attach_arrow attribute "' + aa .attr + '" value must be a vector.' )
219
+ if aa ._last_pos .equals (obj ._pos ) and aa ._last_val .equals (vval ): continue
220
+ aa ._last_val = vector (vval ) # keep copies of last vectors
221
+ aa ._last_pos = vector (obj ._pos )
222
+ aa .pos = obj ._pos
223
+ aa .axis = aa ._scale * vval
220
224
221
225
## update every attach_trail that depends on a function
222
226
for aa in cls .attach_trails :
@@ -545,10 +549,6 @@ class standardAttributes(baseObj):
545
549
[],
546
550
['texture' , 'bumpmap' , 'visible' , 'pickable' ],
547
551
['v0' , 'v1' , 'v2' , 'v3' ] ],
548
- 'attach_arrow' : [ [ 'color' , 'attrval' ],
549
- [],
550
- ['shaftwidth' , 'round' , 'scale' , 'obj' , 'attr' ],
551
- [] ],
552
552
'attach_trail' : [ ['color' ],
553
553
[],
554
554
['radius' , 'pps' , 'retain' , 'type' , '_obj' ],
@@ -721,14 +721,14 @@ def setup(self, args):
721
721
722
722
# attribute vectors have these methods which call self.addattr()
723
723
# The vector class calls a change function when there's a change in x, y, or z.
724
- noSize = ['points' , 'label' , 'vertex' , 'triangle' , 'quad' , 'attach_arrow' , ' attach_trail' ]
724
+ noSize = ['points' , 'label' , 'vertex' , 'triangle' , 'quad' , 'attach_trail' ]
725
725
if not (objName == 'extrusion' ): #
726
726
self ._color .on_change = self ._on_color_change
727
727
if objName not in noSize :
728
728
self ._axis .on_change = self ._on_axis_change
729
729
self ._size .on_change = self ._on_size_change
730
730
self ._up .on_change = self ._on_up_change
731
- noPos = ['curve' , 'points' , 'triangle' , 'quad' , 'attach_arrow' ]
731
+ noPos = ['curve' , 'points' , 'triangle' , 'quad' ]
732
732
if objName not in noPos :
733
733
self ._pos .on_change = self ._on_pos_change
734
734
elif objName == 'curve' :
@@ -1330,24 +1330,6 @@ def headlength(self,value):
1330
1330
if not self ._constructing :
1331
1331
self .addattr ('headlength' )
1332
1332
1333
- class attach_arrow (standardAttributes ):
1334
- def __init__ (self , obj , attr , ** args ):
1335
- attrs = ['pos' , 'size' , 'axis' , 'up' , 'color' ]
1336
- args ['_default_size' ] = None
1337
- a = getattr (obj , attr ) # This raises an error if obj does not have attr
1338
- if not isinstance (a , vector ): raise AttributeError ('The attach_arrow attribute "' + attr + '" is not a vector.' )
1339
- self .obj = args ['obj' ] = obj .idx
1340
- self .attr = args ['attr' ] = attr # could be for example "velocity"
1341
- self .attrval = args ['attrval' ] = getattr (baseObj .object_registry [self .obj ], attr )
1342
- args ['_objName' ] = "attach_arrow"
1343
- self ._last_val = None
1344
- self ._scale = 1
1345
- self ._shaftwidth = 0
1346
- self ._round = False
1347
- super (attach_arrow , self ).setup (args )
1348
- # Only if the attribute is a user attribute do we need to add to attach_arrows:
1349
- if attr not in attrs : baseObj .attach_arrows .append (self )
1350
-
1351
1333
@property
1352
1334
def round (self ):
1353
1335
return self ._round
@@ -1374,10 +1356,34 @@ def shaftwidth(self, value):
1374
1356
self .addattr ("shaftwidth" )
1375
1357
1376
1358
def stop (self ):
1377
- self .addmethod ( 'stop' , 'None' )
1359
+ self ._run = self . visible = False
1378
1360
1379
1361
def start (self ):
1380
- self .addmethod ('start' , 'None' )
1362
+ self ._run = self .visible = True
1363
+
1364
+ def attach_arrow (o , attr , ** args ): # factory function returns arrow with special attributes
1365
+ '''
1366
+ The object "o" with a vector attribute "p" will have an arrow attached with options such as "color".
1367
+ The length of the arrow will be args.scale*o.p", updated with every render of the scene.
1368
+ If one creates a new attachment with "arr = attach_arrow(obj, attr, options)" you
1369
+ can later change (for example) its color with "arr.color = ..."
1370
+ '''
1371
+ if not hasattr (o , attr ): raise AttributeError ('Cannot attach an arrow to an object that has no "' + attr + '" attribute.' )
1372
+ if not isinstance (getattr (o , attr ), vector ): raise AttributeError ('The attach_arrow attribute "' + attr + '" is not a vector.' )
1373
+ if not isinstance (o .pos , vector ): raise AttributeError ("The object's pos attribute is not a vector." )
1374
+
1375
+ scale = 1
1376
+ if 'scale' in args : scale = args ['scale' ]
1377
+ shaftwidth = 0.5 * o ._size .y
1378
+ if 'shaftwidth' in args : shaftwidth = args ['shaftwidth' ]
1379
+ c = o .color
1380
+ if 'color' in args : c = args ['color' ]
1381
+ # Set _last_val to strange values so that the first update to WebGL won't match:
1382
+ a = arrow (canvas = o .canvas , pickable = False , _object = o .idx , attr = attr , color = c ,
1383
+ scale = scale , shaftwidth = shaftwidth , _run = True ,
1384
+ _last_val = vector (134.472 , 789.472 , 465.472 ), _last_pos = vector (134.472 , 789.472 , 465.472 ))
1385
+ baseObj .attach_arrows .append (a )
1386
+ return a
1381
1387
1382
1388
class attach_trail (standardAttributes ):
1383
1389
def __init__ (self , obj , ** args ):
@@ -2428,15 +2434,15 @@ def foreground(self): return self._foreground
2428
2434
@foreground .setter
2429
2435
def foreground (self ,val ):
2430
2436
if not isinstance (val , vector ): raise TypeError ('foreground must be a vector' )
2431
- self ._foreground = vector (value )
2437
+ self ._foreground = vector (val )
2432
2438
self .addattr ('foreground' )
2433
2439
2434
2440
@property
2435
2441
def background (self ): return self ._background
2436
2442
@background .setter
2437
2443
def background (self ,val ):
2438
2444
if not isinstance (val ,vector ): raise TypeError ('background must be a vector' )
2439
- self ._background = vector (value )
2445
+ self ._background = vector (val )
2440
2446
self .addattr ('background' )
2441
2447
2442
2448
@property
@@ -3404,13 +3410,13 @@ def setup(self, args):
3404
3410
## default values of common attributes
3405
3411
self ._constructing = True
3406
3412
argsToSend = []
3407
- objName = args ['_objName' ]
3413
+ self . objName = args ['_objName' ]
3408
3414
del args ['_objName' ]
3409
3415
if 'pos' in args :
3410
3416
self .location = args ['pos' ]
3411
3417
if self .location == print_anchor :
3412
3418
#self.location = [-1, print_anchor]
3413
- raise AttributeError (objName + ': Cannot specify "print_anchor" in VPython 7.' )
3419
+ raise AttributeError (self . objName + ': Cannot specify "print_anchor" in VPython 7.' )
3414
3420
argsToSend .append ('location' )
3415
3421
del args ['pos' ]
3416
3422
if 'canvas' in args : ## specified in constructor
@@ -3438,12 +3444,12 @@ def setup(self, args):
3438
3444
3439
3445
## override default scalar attributes
3440
3446
for a ,val in args .items ():
3441
- if a in controls .attrlists [objName ]:
3447
+ if a in controls .attrlists [self . objName ]:
3442
3448
argsToSend .append (a )
3443
3449
setattr (self , '_' + a , val )
3444
3450
else :
3445
3451
setattr (self , a , val )
3446
- cmd = {"cmd" : objName , "idx" : self .idx }
3452
+ cmd = {"cmd" : self . objName , "idx" : self .idx }
3447
3453
cmd ["canvas" ] = self .canvas .idx
3448
3454
3449
3455
## send only args specified in constructor
@@ -3465,10 +3471,10 @@ def bind(self, value):
3465
3471
3466
3472
@property
3467
3473
def pos (self ):
3468
- raise AttributeError (objName + ' pos attribute is not available.' )
3474
+ raise AttributeError (self . objName + ' pos attribute is not available.' )
3469
3475
@pos .setter
3470
3476
def pos (self , value ):
3471
- raise AttributeError (objName + ' pos attribute cannot be changed.' )
3477
+ raise AttributeError (self . objName + ' pos attribute cannot be changed.' )
3472
3478
3473
3479
@property
3474
3480
def disabled (self ):
0 commit comments