13
13
import sys
14
14
from . import __version__ , __gs_version__
15
15
from ._notebook_helpers import _isnotebook
16
- from ._vector_import_helper import (vector , mag , norm , cross , dot , adjust_up ,
16
+ from ._vector_import_helper import (vector , mag , norm , dot , adjust_up ,
17
17
adjust_axis , object_rotate )
18
18
19
19
# List of names that will be imported from this file with import *
97
97
'right' :'q' , 'top' :'r' , 'bottom' :'s' , '_cloneid' :'t' ,
98
98
'logx' :'u' , 'logy' :'v' , 'dot' :'w' , 'dot_radius' :'x' ,
99
99
'markers' :'y' , 'legend' :'z' , 'label' :'A' , 'delta' :'B' , 'marker_color' :'C' ,
100
- 'size_units' :'D' , 'userpan' :'E' , 'scroll' :'F' , 'choices' :'G' , 'depth' :'H' }
100
+ 'size_units' :'D' , 'userpan' :'E' , 'scroll' :'F' , 'choices' :'G' , 'depth' :'H' , 'round' : 'I' }
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
113
113
'marker_color' ]
114
114
115
115
__textattrs = ['text' , 'align' , 'caption' , 'title' , 'xtitle' , 'ytitle' , 'selected' , 'label' , 'capture' ,
116
- 'append_to_caption' , 'append_to_title' , 'bind' , 'unbind' , 'pause' , 'GSprint' , 'choices' ]
116
+ 'append_to_caption' , 'append_to_title' , 'bind' , 'unbind' , 'pause' , 'GSprint' ]
117
117
118
118
def _encode_attr2 (sendval , val , ismethods ):
119
119
s = ''
@@ -129,10 +129,8 @@ def _encode_attr2(sendval, val, ismethods):
129
129
s += "{:.16G}," .format (p )
130
130
s = s [:- 1 ]
131
131
elif sendval == 'plot' or sendval == 'data' :
132
- if sendval == 'data' and len (val ) == 0 : s += "None, None,"
133
- else :
134
- for p in val :
135
- s += "{:.16G},{:.16G}," .format (p [0 ], p [1 ])
132
+ for p in val :
133
+ s += "{:.16G},{:.16G}," .format (p [0 ], p [1 ])
136
134
s = s [:- 1 ]
137
135
elif sendval in ['v0' , 'v1' , 'v2' , 'v3' ]: # val is the vertex object referenced by triangle or quad
138
136
s += str (val .idx )
@@ -330,10 +328,10 @@ def __del__(self):
330
328
# Now there is no threading in Jupyter VPython. Data is sent to the
331
329
# browser from the trigger() function, which is called by a
332
330
# canvas_update event sent to Python from the browser (glowcomm.js), currently
333
- # every 17 milliseconds. When trigger() is called, it immediately signals
334
- # the browser to set a timeout of 17 ms to send another signal to Python.
331
+ # every 33 milliseconds. When trigger() is called, it immediately signals
332
+ # the browser to set a timeout of 33 ms to send another signal to Python.
335
333
# Note that a typical VPython program starts out by creating objects (constructors) and
336
- # specifying their attributes. The 17 ms signal from the browser is adequate to ensure
334
+ # specifying their attributes. The 33 ms signal from the browser is adequate to ensure
337
335
# prompt data transmissions to the browser.
338
336
339
337
# The situation with non-notebook use is similar, but the http server is threaded,
@@ -402,10 +400,9 @@ def handle_close(self, data):
402
400
def _wait (cvs ): # wait for an event
403
401
cvs ._waitfor = None
404
402
if _isnotebook : baseObj .trigger () # in notebook environment must send methods immediately
405
- t = clock ()
406
403
while cvs ._waitfor is None :
407
- rate (100 )
408
- if _isnotebook : baseObj . trigger () # restart activity in glowcomm.html
404
+ rate (30 )
405
+ return cvs . _waitfor
409
406
410
407
class color (object ):
411
408
black = vector (0 ,0 ,0 )
@@ -491,7 +488,7 @@ class standardAttributes(baseObj):
491
488
'shininess' , 'emissive' , 'texture' , 'frame' , 'material' ,
492
489
'make_trail' , 'trail_type' , 'interval' ,
493
490
'retain' , 'trail_color' , 'trail_radius' , 'texture' ,
494
- 'shaftwidth' , 'headwidth' , 'headlength' , 'pickable' ],
491
+ 'round' , ' shaftwidth' , 'headwidth' , 'headlength' , 'pickable' ],
495
492
['red' , 'green' , 'blue' ,'length' , 'width' , 'height' ]],
496
493
'ring' :[['pos' , 'color' , 'trail_color' , 'size' ],
497
494
['axis' , 'up' ],
@@ -546,7 +543,7 @@ class standardAttributes(baseObj):
546
543
['v0' , 'v1' , 'v2' , 'v3' ] ],
547
544
'attach_arrow' : [ [ 'color' , 'attrval' ],
548
545
[],
549
- ['shaftwidth' , 'scale' , 'obj' , 'attr' ],
546
+ ['shaftwidth' , 'round' , ' scale' , 'obj' , 'attr' ],
550
547
[] ],
551
548
'attach_trail' : [ ['color' ],
552
549
[],
@@ -587,7 +584,6 @@ def setup(self, args):
587
584
del args ['_objName' ]
588
585
589
586
# default values
590
- self ._sizing = True # axis/size connection is the default; False for sphere, ring, text, compound
591
587
self ._pos = vector (0 ,0 ,0 )
592
588
self ._axis = vector (1 ,0 ,0 )
593
589
self ._up = vector (0 ,1 ,0 )
@@ -616,6 +612,7 @@ def setup(self, args):
616
612
self ._pickable = True
617
613
self ._save_oldaxis = None # used in linking axis and up
618
614
self ._save_oldup = None # used in linking axis and up
615
+ self ._round = False
619
616
_special_clone = None
620
617
if '_cloneid' in args : # text, extrusion, or compound is being cloned
621
618
_special_clone = args ['_cloneid' ]
@@ -764,52 +761,32 @@ def up(self,value):
764
761
def axis (self ):
765
762
return self ._axis
766
763
@axis .setter
767
- def axis (self ,value ): # sphere or ring or text or compound have no axis/size link
768
- currentaxis = self ._axis
769
- self ._axis = value
770
- if value .mag2 == 0 :
771
- if self ._save_oldaxis is None : self ._save_oldaxis = currentaxis
772
- else :
773
- if self ._save_oldaxis is not None :
774
- self ._save_oldaxis = adjust_up (self ._axis , value , self ._up , self ._save_oldaxis ) # this sets self._axis and self._up
764
+ def axis (self ,value ):
765
+ self ._save_oldaxis = adjust_up (self ._axis , value , self ._up , self ._save_oldaxis ) # this sets self._axis and self._up
775
766
if not self ._constructing :
776
767
self .addattr ('axis' )
777
- if self ._sizing :
778
- self ._size ._x = value .mag # changing axis length changes size.x
768
+ self ._size ._x = value .mag # changing axis length changes size.x
779
769
780
770
@property
781
771
def size (self ):
782
772
return self ._size
783
773
@size .setter
784
- def size (self ,value ): # sphere or ring or text or compound have no axis/size link
785
- currentaxis = self ._axis
786
- self ._size = value
787
- if value .x == 0 :
788
- if self ._save_oldaxis is not None :
789
- currentaxis = self ._save_oldaxis
790
- self ._save_oldaxis = None
791
- else :
792
- currentaxis = vector (1 ,0 ,0 )
774
+ def size (self ,value ):
775
+ self ._size .value = value
793
776
if not self ._constructing :
794
777
self .addattr ('size' )
795
- if self ._sizing :
796
- self ._axis = currentaxis .norm ()* value .x
778
+ a = self ._axis .norm () * value .x
779
+ if mag (self ._axis ) == 0 :
780
+ a = vector (value .x ,0 ,0 )
781
+ self ._axis .value = a # changing size changes length of axis
797
782
798
783
@property
799
784
def length (self ):
800
785
return self ._size .x
801
786
@length .setter
802
787
def length (self ,value ):
803
- if value == 0 :
804
- if self ._save_oldaxis is None : self ._save_oldaxis = vector (self ._axis .x , self ._axis .y , self ._axis .z )
805
- self ._axis = vector (0 ,0 ,0 )
806
- self ._size ._x = 0
807
- else :
808
- if self ._save_oldaxis is not None :
809
- self ._axis = self ._save_oldaxis
810
- self ._save_oldaxis = None
811
- if self ._size ._x == 0 : self .axis = vector (value , 0 , 0 )
812
- else : self .axis = value * self ._axis .norm () # this will set length
788
+ self ._axis = self ._axis .norm () * value
789
+ self ._size ._x = value
813
790
if not self ._constructing :
814
791
self .addattr ('axis' )
815
792
self .addattr ('size' )
@@ -1039,24 +1016,6 @@ def rotate(self, angle=None, axis=None, origin=None):
1039
1016
self ._pos .value = newpos
1040
1017
self .addattr ('pos' )
1041
1018
1042
- def bounding_box (self ):
1043
- centered = ['box' , 'compound' , 'ellipsoid' , 'sphere' , 'simple_sphere' , 'ring' ]
1044
- x = norm (self ._axis )
1045
- y = norm (self ._up )
1046
- z = norm (cross (x ,y ))
1047
- L = self ._size .x
1048
- H = self ._size .y
1049
- W = self ._size .z
1050
- p = vector (self ._pos ) # make a copy of pos, so changes to p won't affect the object
1051
- if self ._objName not in centered :
1052
- p = p + 0.5 * L * x # move to center
1053
- pts = []
1054
- for dx in [- L / 2 , L / 2 ]:
1055
- for dy in [- H / 2 , H / 2 ]:
1056
- for dz in [- W / 2 , W / 2 ]:
1057
- pts .append (p + dx * x + dy * y + dz * z )
1058
- return pts
1059
-
1060
1019
def _on_size_change (self ): # the vector class calls this when there's a change in x, y, or z
1061
1020
self ._axis .value = self ._axis .norm () * self ._size .x # update axis length when box.size.x is changed
1062
1021
self .addattr ('size' )
@@ -1145,7 +1104,6 @@ def __init__(self, **args):
1145
1104
args ['_default_size' ] = vector (2 ,2 ,2 )
1146
1105
args ['_objName' ] = "sphere"
1147
1106
super (sphere , self ).setup (args )
1148
- self ._sizing = False # no axis/size connection
1149
1107
1150
1108
@property
1151
1109
def radius (self ):
@@ -1236,7 +1194,6 @@ def __init__(self, **args):
1236
1194
args ['_default_size' ] = vector (0.2 ,2.2 ,2.2 )
1237
1195
args ['_objName' ] = "ring"
1238
1196
super (ring , self ).setup (args )
1239
- self ._sizing = False # no axis/size connection
1240
1197
1241
1198
@property
1242
1199
def thickness (self ):
@@ -1286,12 +1243,20 @@ class arrow(standardAttributes):
1286
1243
def __init__ (self , ** args ):
1287
1244
args ['_default_size' ] = vector (1 ,0.2 ,0.2 )
1288
1245
args ['_objName' ] = "arrow"
1246
+ self ._round = False
1289
1247
self ._shaftwidth = 0
1290
1248
self ._headwidth = 0
1291
1249
self ._headlength = 0
1292
1250
1293
1251
super (arrow , self ).setup (args )
1294
1252
1253
+ @property
1254
+ def round (self ):
1255
+ return self ._round
1256
+ @round .setter
1257
+ def round (self ,value ):
1258
+ raise AttributeError ('Cannot change the "round" attribute of an arrow.' )
1259
+
1295
1260
@property
1296
1261
def shaftwidth (self ):
1297
1262
return self ._shaftwidth
@@ -1323,17 +1288,27 @@ class attach_arrow(standardAttributes):
1323
1288
def __init__ (self , obj , attr , ** args ):
1324
1289
attrs = ['pos' , 'size' , 'axis' , 'up' , 'color' ]
1325
1290
args ['_default_size' ] = None
1291
+ a = getattr (obj , attr ) # This raises an error if obj does not have attr
1292
+ if not isinstance (a , vector ): raise AttributeError ('The attach_arrow attribute "' + attr + '" is not a vector.' )
1326
1293
self .obj = args ['obj' ] = obj .idx
1327
1294
self .attr = args ['attr' ] = attr # could be for example "velocity"
1328
1295
self .attrval = args ['attrval' ] = getattr (baseObj .object_registry [self .obj ], attr )
1329
1296
args ['_objName' ] = "attach_arrow"
1330
1297
self ._last_val = None
1331
1298
self ._scale = 1
1332
1299
self ._shaftwidth = 0
1300
+ self ._round = False
1333
1301
super (attach_arrow , self ).setup (args )
1334
1302
# Only if the attribute is a user attribute do we need to add to attach_arrows:
1335
1303
if attr not in attrs : baseObj .attach_arrows .append (self )
1336
1304
1305
+ @property
1306
+ def round (self ):
1307
+ return self ._round
1308
+ @round .setter
1309
+ def round (self ,value ):
1310
+ raise AttributeError ('Cannot change the "round" attribute of an attach_arrow.' )
1311
+
1337
1312
@property
1338
1313
def scale (self ):
1339
1314
return self ._scale
@@ -1492,7 +1467,6 @@ def __init__(self, objList, **args):
1492
1467
self .compound_idx += 1
1493
1468
args ['_objName' ] = 'compound' + str (self .compound_idx )
1494
1469
super (compound , self ).setup (args )
1495
- self ._sizing = False # no axis/size connection
1496
1470
1497
1471
for obj in objList :
1498
1472
# GlowScript will make the objects invisible, so need not set obj.visible
@@ -1856,8 +1830,8 @@ def pop(self, *args):
1856
1830
return val
1857
1831
1858
1832
def point (self ,N ):
1859
- if N >= len (self ._pts ) or (N < 0 and - N >= len (self .pts )):
1860
- raise ValueError ('N = {} is outside the bounds 0-{} of the curve points' .format (N , len (self ._pos ) ))
1833
+ if N >= len (self ._pts ) or (N < 0 and - N > len (self ._pts )):
1834
+ raise ValueError ('N = {} is outside the bounds 0-{} of the curve points' .format (N , len (self ._pts ) - 1 ))
1861
1835
info = self ._pts [N ]
1862
1836
if 'color' not in info : info ['color' ] = self .color
1863
1837
if 'radius' not in info : info ['radius' ] = self .radius
@@ -2026,8 +2000,6 @@ def setup(self, args):
2026
2000
self ._legend = False
2027
2001
self ._interval = - 1
2028
2002
self ._graph = None
2029
- self ._visible = True
2030
- self ._data = []
2031
2003
objName = args ['_objName' ]
2032
2004
del args ['_objName' ]
2033
2005
self ._constructing = True ## calls are from constructor
@@ -2163,8 +2135,6 @@ def preresolve2(self, args):
2163
2135
raise AttributeError ("Cannot currently change color in a plot statement." )
2164
2136
if 'pos' in args :
2165
2137
return self .resolveargs (args ['pos' ])
2166
- elif 'data' in args :
2167
- return self .resolveargs (args ['data' ])
2168
2138
else :
2169
2139
raise AttributeError ("Must be plot(x,y) or plot(pos=[x,y]) or plot([x,y]) or plot([x,y], ...) or plot([ [x,y], ... ])" )
2170
2140
@@ -2173,18 +2143,8 @@ def plot(self, *args1, **args2):
2173
2143
p = self .preresolve1 (args1 )
2174
2144
else :
2175
2145
p = self .preresolve2 (args2 )
2176
- self ._data = self ._data + p
2177
2146
self .addmethod ('plot' , p )
2178
2147
2179
- @property
2180
- def visible (self ):
2181
- return self ._visible
2182
- @visible .setter
2183
- def visible (self ,value ):
2184
- self ._visible = value
2185
- if not self ._constructing :
2186
- self .addattr ('visible' )
2187
-
2188
2148
def delete (self ):
2189
2149
self .addmethod ('delete' , 'None' )
2190
2150
@@ -2881,8 +2841,7 @@ def __init__(self, **args):
2881
2841
baseObj ._canvas_constructing = False
2882
2842
2883
2843
def follow (self , obj ): ## should allow a function also
2884
- if obj is None : self .addmethod ('follow' , 'None' )
2885
- else : self .addmethod ('follow' , obj .idx )
2844
+ self .addmethod ('follow' , obj .idx )
2886
2845
2887
2846
def select (self ):
2888
2847
canvas .selected = self
@@ -3369,7 +3328,6 @@ class controls(baseObj):
3369
3328
def setup (self , args ):
3370
3329
super (controls , self ).__init__ () ## get idx, attrsupdt from baseObj
3371
3330
## default values of common attributes
3372
- self ._disabled = False
3373
3331
self ._constructing = True
3374
3332
argsToSend = []
3375
3333
objName = args ['_objName' ]
@@ -3604,8 +3562,7 @@ def choices(self):
3604
3562
return self ._choices
3605
3563
@choices .setter
3606
3564
def choices (self , value ):
3607
- self ._choices = value
3608
- self .addattr ('choices' )
3565
+ raise AttributeError ('choices cannot be modified after a menu is created' )
3609
3566
3610
3567
@property
3611
3568
def index (self ):
@@ -3889,7 +3846,6 @@ def end_face_color(self,value):
3889
3846
class text (standardAttributes ):
3890
3847
3891
3848
def __init__ (self , ** args ):
3892
- self ._sizing = False # no axis/size connection
3893
3849
args ['_default_size' ] = vector (1 ,1 ,1 ) # to keep standardAttributes happy
3894
3850
args ['_objName' ] = "text"
3895
3851
self ._height = 1 ## not derived from size
@@ -3944,10 +3900,10 @@ def axis(self):
3944
3900
return self ._axis
3945
3901
@axis .setter
3946
3902
def axis (self ,value ): # changing axis does not affect size
3947
- old = vector (self .axis )
3903
+ oldaxis = vector (self .axis )
3948
3904
u = self .up
3949
3905
self ._axis .value = value
3950
- self ._save_oldaxis = adjust_up (norm (old ), self ._axis , self ._up , self ._save_oldaxis )
3906
+ self ._save_oldaxis = adjust_up (norm (oldaxis ), self ._axis , self ._up , self ._save_oldaxis )
3951
3907
self .addattr ('axis' )
3952
3908
self .addattr ('up' )
3953
3909
@@ -3985,7 +3941,7 @@ def depth(self, val): # sign issue ??
3985
3941
if abs (val ) < 0.01 * self ._height :
3986
3942
if val < 0 : val = - 0.01 * self ._height
3987
3943
else : val = 0.01 * self ._height
3988
- self ._depth = val
3944
+ self ._depth = value
3989
3945
self .addattr ('depth' )
3990
3946
3991
3947
@property
0 commit comments