1313import sys
1414from . import __version__ , __gs_version__
1515from ._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 ,
1717 adjust_axis , object_rotate )
1818
1919# List of names that will be imported from this file with import *
9797 'right' :'q' , 'top' :'r' , 'bottom' :'s' , '_cloneid' :'t' ,
9898 'logx' :'u' , 'logy' :'v' , 'dot' :'w' , 'dot_radius' :'x' ,
9999 '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' }
101101
102102# methods are X in {'m': '23X....'}
103103# pos is normally updated as an attribute, but for interval-based trails, it is updated (multiply) as a method
113113 'marker_color' ]
114114
115115__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' ]
117117
118118def _encode_attr2 (sendval , val , ismethods ):
119119 s = ''
@@ -129,10 +129,8 @@ def _encode_attr2(sendval, val, ismethods):
129129 s += "{:.16G}," .format (p )
130130 s = s [:- 1 ]
131131 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 ])
136134 s = s [:- 1 ]
137135 elif sendval in ['v0' , 'v1' , 'v2' , 'v3' ]: # val is the vertex object referenced by triangle or quad
138136 s += str (val .idx )
@@ -330,10 +328,10 @@ def __del__(self):
330328# Now there is no threading in Jupyter VPython. Data is sent to the
331329# browser from the trigger() function, which is called by a
332330# 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.
335333# 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
337335# prompt data transmissions to the browser.
338336
339337# The situation with non-notebook use is similar, but the http server is threaded,
@@ -402,10 +400,9 @@ def handle_close(self, data):
402400def _wait (cvs ): # wait for an event
403401 cvs ._waitfor = None
404402 if _isnotebook : baseObj .trigger () # in notebook environment must send methods immediately
405- t = clock ()
406403 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
409406
410407class color (object ):
411408 black = vector (0 ,0 ,0 )
@@ -491,7 +488,7 @@ class standardAttributes(baseObj):
491488 'shininess' , 'emissive' , 'texture' , 'frame' , 'material' ,
492489 'make_trail' , 'trail_type' , 'interval' ,
493490 'retain' , 'trail_color' , 'trail_radius' , 'texture' ,
494- 'shaftwidth' , 'headwidth' , 'headlength' , 'pickable' ],
491+ 'round' , ' shaftwidth' , 'headwidth' , 'headlength' , 'pickable' ],
495492 ['red' , 'green' , 'blue' ,'length' , 'width' , 'height' ]],
496493 'ring' :[['pos' , 'color' , 'trail_color' , 'size' ],
497494 ['axis' , 'up' ],
@@ -546,7 +543,7 @@ class standardAttributes(baseObj):
546543 ['v0' , 'v1' , 'v2' , 'v3' ] ],
547544 'attach_arrow' : [ [ 'color' , 'attrval' ],
548545 [],
549- ['shaftwidth' , 'scale' , 'obj' , 'attr' ],
546+ ['shaftwidth' , 'round' , ' scale' , 'obj' , 'attr' ],
550547 [] ],
551548 'attach_trail' : [ ['color' ],
552549 [],
@@ -587,7 +584,6 @@ def setup(self, args):
587584 del args ['_objName' ]
588585
589586 # default values
590- self ._sizing = True # axis/size connection is the default; False for sphere, ring, text, compound
591587 self ._pos = vector (0 ,0 ,0 )
592588 self ._axis = vector (1 ,0 ,0 )
593589 self ._up = vector (0 ,1 ,0 )
@@ -616,6 +612,7 @@ def setup(self, args):
616612 self ._pickable = True
617613 self ._save_oldaxis = None # used in linking axis and up
618614 self ._save_oldup = None # used in linking axis and up
615+ self ._round = False
619616 _special_clone = None
620617 if '_cloneid' in args : # text, extrusion, or compound is being cloned
621618 _special_clone = args ['_cloneid' ]
@@ -764,52 +761,32 @@ def up(self,value):
764761 def axis (self ):
765762 return self ._axis
766763 @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
775766 if not self ._constructing :
776767 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
779769
780770 @property
781771 def size (self ):
782772 return self ._size
783773 @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
793776 if not self ._constructing :
794777 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
797782
798783 @property
799784 def length (self ):
800785 return self ._size .x
801786 @length .setter
802787 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
813790 if not self ._constructing :
814791 self .addattr ('axis' )
815792 self .addattr ('size' )
@@ -1039,24 +1016,6 @@ def rotate(self, angle=None, axis=None, origin=None):
10391016 self ._pos .value = newpos
10401017 self .addattr ('pos' )
10411018
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-
10601019 def _on_size_change (self ): # the vector class calls this when there's a change in x, y, or z
10611020 self ._axis .value = self ._axis .norm () * self ._size .x # update axis length when box.size.x is changed
10621021 self .addattr ('size' )
@@ -1145,7 +1104,6 @@ def __init__(self, **args):
11451104 args ['_default_size' ] = vector (2 ,2 ,2 )
11461105 args ['_objName' ] = "sphere"
11471106 super (sphere , self ).setup (args )
1148- self ._sizing = False # no axis/size connection
11491107
11501108 @property
11511109 def radius (self ):
@@ -1236,7 +1194,6 @@ def __init__(self, **args):
12361194 args ['_default_size' ] = vector (0.2 ,2.2 ,2.2 )
12371195 args ['_objName' ] = "ring"
12381196 super (ring , self ).setup (args )
1239- self ._sizing = False # no axis/size connection
12401197
12411198 @property
12421199 def thickness (self ):
@@ -1286,12 +1243,20 @@ class arrow(standardAttributes):
12861243 def __init__ (self , ** args ):
12871244 args ['_default_size' ] = vector (1 ,0.2 ,0.2 )
12881245 args ['_objName' ] = "arrow"
1246+ self ._round = False
12891247 self ._shaftwidth = 0
12901248 self ._headwidth = 0
12911249 self ._headlength = 0
12921250
12931251 super (arrow , self ).setup (args )
12941252
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+
12951260 @property
12961261 def shaftwidth (self ):
12971262 return self ._shaftwidth
@@ -1323,17 +1288,27 @@ class attach_arrow(standardAttributes):
13231288 def __init__ (self , obj , attr , ** args ):
13241289 attrs = ['pos' , 'size' , 'axis' , 'up' , 'color' ]
13251290 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.' )
13261293 self .obj = args ['obj' ] = obj .idx
13271294 self .attr = args ['attr' ] = attr # could be for example "velocity"
13281295 self .attrval = args ['attrval' ] = getattr (baseObj .object_registry [self .obj ], attr )
13291296 args ['_objName' ] = "attach_arrow"
13301297 self ._last_val = None
13311298 self ._scale = 1
13321299 self ._shaftwidth = 0
1300+ self ._round = False
13331301 super (attach_arrow , self ).setup (args )
13341302 # Only if the attribute is a user attribute do we need to add to attach_arrows:
13351303 if attr not in attrs : baseObj .attach_arrows .append (self )
13361304
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+
13371312 @property
13381313 def scale (self ):
13391314 return self ._scale
@@ -1492,7 +1467,6 @@ def __init__(self, objList, **args):
14921467 self .compound_idx += 1
14931468 args ['_objName' ] = 'compound' + str (self .compound_idx )
14941469 super (compound , self ).setup (args )
1495- self ._sizing = False # no axis/size connection
14961470
14971471 for obj in objList :
14981472 # GlowScript will make the objects invisible, so need not set obj.visible
@@ -1856,8 +1830,8 @@ def pop(self, *args):
18561830 return val
18571831
18581832 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 ))
18611835 info = self ._pts [N ]
18621836 if 'color' not in info : info ['color' ] = self .color
18631837 if 'radius' not in info : info ['radius' ] = self .radius
@@ -2026,8 +2000,6 @@ def setup(self, args):
20262000 self ._legend = False
20272001 self ._interval = - 1
20282002 self ._graph = None
2029- self ._visible = True
2030- self ._data = []
20312003 objName = args ['_objName' ]
20322004 del args ['_objName' ]
20332005 self ._constructing = True ## calls are from constructor
@@ -2163,8 +2135,6 @@ def preresolve2(self, args):
21632135 raise AttributeError ("Cannot currently change color in a plot statement." )
21642136 if 'pos' in args :
21652137 return self .resolveargs (args ['pos' ])
2166- elif 'data' in args :
2167- return self .resolveargs (args ['data' ])
21682138 else :
21692139 raise AttributeError ("Must be plot(x,y) or plot(pos=[x,y]) or plot([x,y]) or plot([x,y], ...) or plot([ [x,y], ... ])" )
21702140
@@ -2173,18 +2143,8 @@ def plot(self, *args1, **args2):
21732143 p = self .preresolve1 (args1 )
21742144 else :
21752145 p = self .preresolve2 (args2 )
2176- self ._data = self ._data + p
21772146 self .addmethod ('plot' , p )
21782147
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-
21882148 def delete (self ):
21892149 self .addmethod ('delete' , 'None' )
21902150
@@ -2881,8 +2841,7 @@ def __init__(self, **args):
28812841 baseObj ._canvas_constructing = False
28822842
28832843 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 )
28862845
28872846 def select (self ):
28882847 canvas .selected = self
@@ -3369,7 +3328,6 @@ class controls(baseObj):
33693328 def setup (self , args ):
33703329 super (controls , self ).__init__ () ## get idx, attrsupdt from baseObj
33713330 ## default values of common attributes
3372- self ._disabled = False
33733331 self ._constructing = True
33743332 argsToSend = []
33753333 objName = args ['_objName' ]
@@ -3604,8 +3562,7 @@ def choices(self):
36043562 return self ._choices
36053563 @choices .setter
36063564 def choices (self , value ):
3607- self ._choices = value
3608- self .addattr ('choices' )
3565+ raise AttributeError ('choices cannot be modified after a menu is created' )
36093566
36103567 @property
36113568 def index (self ):
@@ -3889,7 +3846,6 @@ def end_face_color(self,value):
38893846class text (standardAttributes ):
38903847
38913848 def __init__ (self , ** args ):
3892- self ._sizing = False # no axis/size connection
38933849 args ['_default_size' ] = vector (1 ,1 ,1 ) # to keep standardAttributes happy
38943850 args ['_objName' ] = "text"
38953851 self ._height = 1 ## not derived from size
@@ -3944,10 +3900,10 @@ def axis(self):
39443900 return self ._axis
39453901 @axis .setter
39463902 def axis (self ,value ): # changing axis does not affect size
3947- old = vector (self .axis )
3903+ oldaxis = vector (self .axis )
39483904 u = self .up
39493905 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 )
39513907 self .addattr ('axis' )
39523908 self .addattr ('up' )
39533909
@@ -3985,7 +3941,7 @@ def depth(self, val): # sign issue ??
39853941 if abs (val ) < 0.01 * self ._height :
39863942 if val < 0 : val = - 0.01 * self ._height
39873943 else : val = 0.01 * self ._height
3988- self ._depth = val
3944+ self ._depth = value
39893945 self .addattr ('depth' )
39903946
39913947 @property
0 commit comments