2424 'event_return' , 'extrusion' , 'faces' , 'frame' , 'gcurve' , 'gdots' ,
2525 'ghbars' , 'gobj' , 'graph' , 'gvbars' , 'helix' , 'label' ,
2626 'local_light' , 'menu' , 'meta_canvas' , 'points' , 'pyramid' ,
27- 'quad' , 'radio' , 'ring' , 'set_browser' , ' simple_sphere' , 'sleep' , 'slider' , 'sphere' ,
27+ 'quad' , 'radio' , 'ring' , 'simple_sphere' , 'sleep' , 'slider' , 'sphere' ,
2828 'standardAttributes' , 'text' , 'textures' , 'triangle' , 'vertex' ,
29- 'wtext' , 'winput' ]
29+ 'wtext' , 'winput' , 'keysdown' ]
3030
3131__p = platform .python_version ()
3232_ispython3 = (__p [0 ] == '3' )
4040version = [__version__ , 'jupyter' ]
4141GSversion = [__gs_version__ , 'glowscript' ]
4242
43+ keysdownlist = [] # list of keys currently pressed
44+
4345# To print immediately, do this:
4446# print(.....)
4547# sys.stdout.flush()
9597 'right' :'q' , 'top' :'r' , 'bottom' :'s' , '_cloneid' :'t' ,
9698 'logx' :'u' , 'logy' :'v' , 'dot' :'w' , 'dot_radius' :'x' ,
9799 'markers' :'y' , 'legend' :'z' , 'label' :'A' , 'delta' :'B' , 'marker_color' :'C' ,
98- 'size_units' :'D' , 'userpan' :'E' }
100+ 'size_units' :'D' , 'userpan' :'E' , 'scroll' : 'F' }
99101
100102# methods are X in {'m': '23X....'}
101103# pos is normally updated as an attribute, but for interval-based trails, it is updated (multiply) as a method
@@ -522,7 +524,7 @@ class standardAttributes(baseObj):
522524 ['visible' ],
523525 []],
524526 'compound' :[['pos' , 'color' , 'trail_color' ],
525- ['axis' , 'size' , 'up' ],
527+ ['axis' , 'size' , 'up' , 'origin' ],
526528 ['visible' , 'opacity' ,'shininess' , 'emissive' ,
527529 'make_trail' , 'trail_type' , 'interval' , 'texture' ,
528530 'retain' , 'trail_color' , 'trail_radius' , 'obj_idxs' , 'pickable' ],
@@ -698,7 +700,7 @@ def setup(self, args):
698700
699701
700702 # set canvas
701- if self .canvas == None : ## not specified in constructor
703+ if self .canvas is None : ## not specified in constructor
702704 self .canvas = canvas .get_selected ()
703705 #cmd["attrs"].append({"attr": 'canvas', "value": self.canvas.idx})
704706 cmd ['canvas' ] = self .canvas .idx
@@ -711,7 +713,7 @@ def setup(self, args):
711713 if _special_clone is not None : cmd ["_cloneid" ] = _special_clone
712714 self .appendcmd (cmd )
713715
714- # if ('frame' in args and args['frame'] != None):
716+ # if ('frame' in args and args['frame'] is not None):
715717 # frame.objects.append(self)
716718 # frame.update_obj_list()
717719
@@ -999,9 +1001,9 @@ def rotate(self, angle=None, axis=None, origin=None):
9991001 saveorigin = origin
10001002 if angle == 0 :
10011003 return
1002- if angle == None :
1004+ if angle is None :
10031005 raise TypeError ('You must specify an angle through which to rotate' )
1004- if axis == None :
1006+ if axis is None :
10051007 rotaxis = self .axis
10061008 else :
10071009 rotaxis = axis
@@ -1528,32 +1530,34 @@ def size(self,value): # compound axis and size don't interact
15281530 if not self ._constructing :
15291531 self .addattr ('size' )
15301532
1531- def _world_zaxis (self ):
1532- axis = self ._axis
1533- up = norm (self ._up )
1534- if abs (axis .dot (up )) / sqrt (axis .mag2 ) > 0.98 :
1535- if abs (norm (axis ).dot (vector (- 1 ,0 ,0 ))) > 0.98 :
1536- z_axis = axis .cross (vector (0 ,0 ,1 )).norm ()
1537- else :
1538- z_axis = axis .cross (vector (- 1 ,0 ,0 )).norm ()
1539- else :
1540- z_axis = axis .cross (up ).norm ()
1541- return z_axis
1533+ @property
1534+ def origin (self ):
1535+ return self ._origin
1536+ @origin .setter
1537+ def origin (self ,value ): # compound origin cannot be reset
1538+ if not self ._constructing :
1539+ raise AttributeError ('The compound "origin" attribute is read-only; change "pos" instead.' )
1540+ self ._origin = value
15421541
15431542 def world_to_compound (self , v ):
1544- axis = self ._axis
1545- z_axis = self ._world_zaxis ()
1546- y_axis = z_axis .cross (axis ).norm ()
1547- x_axis = axis .norm ()
1548- v = v - self ._pos
1549- return vector (v .dot (x_axis ), v .dot (y_axis ), v .dot (z_axis ))
1543+ v = v - self ._pos
1544+ x_axis = self ._axis .hat
1545+ y_axis = self ._up .hat
1546+ z_axis = x_axis .cross (y_axis )
1547+ ox = self ._size0 .x / self ._size .x # _size0 is the original size
1548+ oy = self ._size0 .y / self ._size .y
1549+ oz = self ._size0 .z / self ._size .z
1550+ return self ._origin + vector (v .dot (x_axis )* ox , v .dot (y_axis )* oy , v .dot (z_axis )* oz )
15501551
15511552 def compound_to_world (self , v ):
1552- axis = self ._axis
1553- z_axis = self ._world_zaxis ()
1554- y_axis = z_axis .cross (axis ).norm ()
1555- x_axis = axis .norm ()
1556- return self ._pos + (v .x * x_axis ) + (v .y * y_axis ) + (v .z * z_axis )
1553+ v = v - self ._origin
1554+ x_axis = self ._axis .hat
1555+ y_axis = self ._up .hat
1556+ z_axis = x_axis .cross (y_axis )
1557+ ox = self ._size .x / self ._size0 .x # _size0 is the original size
1558+ oy = self ._size .y / self ._size0 .y
1559+ oz = self ._size .z / self ._size0 .z
1560+ return self ._pos + v .x * ox * x_axis + v .y * oy * y_axis + v .z * oz * z_axis
15571561
15581562class vertex (standardAttributes ):
15591563 def __init__ (self , ** args ):
@@ -1961,7 +1965,7 @@ def __init__(self,*args1, **args):
19611965
19621966 super (curveMethods , self ).setup (args )
19631967
1964- if tpos != None :
1968+ if tpos is not None :
19651969 if len (args1 ) > 0 : raise AttributeError ('Malformed constructor' )
19661970 self .append (tpos )
19671971 if len (args1 ) > 0 :
@@ -1985,7 +1989,7 @@ def __init__(self,*args1, **args):
19851989
19861990 super (curveMethods , self ).setup (args )
19871991
1988- if tpos != None :
1992+ if tpos is not None :
19891993 if len (args1 ) > 0 : raise AttributeError ('Malformed constructor' )
19901994 self .append (tpos )
19911995 if len (args1 ) > 0 :
@@ -2278,6 +2282,7 @@ def __init__(self, **args):
22782282 self ._title = ""
22792283 self ._xtitle = ""
22802284 self ._ytitle = ""
2285+ self ._scroll = False
22812286 argsToSend = []
22822287
22832288 ## override default vector attributes
@@ -2292,7 +2297,7 @@ def __init__(self, **args):
22922297
22932298 ## override default scalar attributes
22942299 scalarAttributes = ['width' , 'height' , 'title' , 'xtitle' , 'ytitle' ,'align' ,
2295- 'xmin' , 'xmax' , 'ymin' , 'ymax' , 'logx' , 'logy' , 'fast' ]
2300+ 'xmin' , 'xmax' , 'ymin' , 'ymax' , 'logx' , 'logy' , 'fast' , 'scroll' ]
22962301 for a in scalarAttributes :
22972302 if a in args :
22982303 argsToSend .append (a )
@@ -2305,6 +2310,12 @@ def __init__(self, **args):
23052310
23062311 cmd = {"cmd" : objName , "idx" : self .idx }
23072312
2313+ if self ._scroll :
2314+ if not ('xmin' in argsToSend and 'xmax' in argsToSend ):
2315+ raise AttributeError ("For a scrolling graph, both xmin and xmax must be specified." )
2316+ if self ._xmax <= self ._xmin :
2317+ raise AttributeError ("For a scrolling graph, xmax must be greater than xmin." )
2318+
23082319 ## send only args specified in constructor
23092320 for a in argsToSend :
23102321 aval = getattr (self ,a )
@@ -2318,11 +2329,16 @@ def __init__(self, **args):
23182329 def fast (self ): return self ._fast
23192330 @fast .setter
23202331 def fast (self ,val ):
2321- # if _isnotebook and not val:
2322- # raise AttributeError('"fast = False" is currently not available in a Jupyter notebook.')
23232332 self ._fast = val
23242333 self .addattr ('fast' )
23252334
2335+ @property
2336+ def scroll (self ): return self ._scroll
2337+ @scroll .setter
2338+ def scroll (self ,val ):
2339+ self ._scroll = val
2340+ self .addattr ('scroll' )
2341+
23262342 @property
23272343 def width (self ): return self ._width
23282344 @width .setter
@@ -2814,7 +2830,7 @@ def __init__(self, **args):
28142830
28152831 for a in canvasNonVecAttrs :
28162832 if a in args :
2817- if args [a ] != None :
2833+ if args [a ] is not None :
28182834 setattr (self , '_' + a , args [a ])
28192835 cmd [a ]= args [a ]
28202836 del args [a ]
@@ -3099,11 +3115,13 @@ def objz(self, obj, operation):
30993115
31003116## key events conflict with notebook command mode; not permitted for now
31013117 def handle_event (self , evt ): ## events and scene info updates
3118+ global keysdownlist
31023119 ev = evt ['event' ]
31033120 if ev == 'pick' :
31043121 self .mouse .setpick ( evt )
31053122 self ._waitfor = True # what pick is looking for
31063123 elif ev == '_compound' : # compound, text, extrusion
3124+ print ('compound event return' )
31073125 obj = self ._compound
31083126 p = evt ['pos' ]
31093127 if obj ._objName == 'text' :
@@ -3115,7 +3133,7 @@ def handle_event(self, evt): ## events and scene info updates
31153133 # on_change functions that detect changes in e.g. obj.pos.y
31163134 obj ._pos .value = list_to_vec (p )
31173135 s = evt ['size' ]
3118- obj ._size .value = list_to_vec (s )
3136+ obj ._size .value = obj . _size0 = list_to_vec (s )
31193137 obj ._axis .value = obj ._size ._x * norm (obj ._axis )
31203138 obj ._up .value = list_to_vec (evt ['up' ])
31213139 self ._waitfor = True # what compound and text and extrusion are looking for in _wait()
@@ -3182,6 +3200,8 @@ def handle_event(self, evt): ## events and scene info updates
31823200 if 'autoscale' in evt and self .userzoom and not self ._set_autoscale :
31833201 self ._autoscale = evt ['autoscale' ]
31843202 self ._set_autoscale = False
3203+ if 'keysdown' in evt : keysdownlist = evt ['keysdown' ]
3204+
31853205
31863206 def bind (self , eventtype , whattodo ):
31873207 evts = eventtype .split ()
@@ -3255,7 +3275,7 @@ def __init__(self, **args):
32553275 args ['_objName' ] = "local_light"
32563276 super (local_light , self ).setup (args )
32573277
3258- if (canvas .get_selected () != None ):
3278+ if (canvas .get_selected () is not None ):
32593279 canvas .get_selected ()._lights .append (self )
32603280
32613281class distant_light (standardAttributes ):
@@ -3265,7 +3285,7 @@ def __init__(self, **args):
32653285 self ._direction = vector (0 ,0 ,1 )
32663286 super (distant_light , self ).setup (args )
32673287
3268- if (canvas .get_selected () != None ):
3288+ if (canvas .get_selected () is not None ):
32693289 canvas .get_selected ()._lights .append (self )
32703290
32713291 @property
@@ -4092,11 +4112,9 @@ def print_to_string(*args): # treatment of <br> vs. \n not quite right here
40924112 s = s [:- 1 ]
40934113 return (s )
40944114
4095- # global variable for type of web browser to display vpython
4096- _browsertype = 'default'
4097- def set_browser (type = 'default' ):
4098- global _browsertype
4099- if type == 'pyqt' :
4100- _browsertype = 'pyqt'
4101- else :
4102- _browsertype = 'default'
4115+ def keysdown ():
4116+ global keysdownlist
4117+ keys = []
4118+ for k in keysdownlist : # return a copy of keysdownlist
4119+ keys .append (k )
4120+ return keys
0 commit comments