Skip to content

Commit 2a73f41

Browse files
Merge pull request #177 from vpython/attach_arrow
Attach arrow
2 parents df03576 + a3a390b commit 2a73f41

File tree

5 files changed

+70
-120
lines changed

5 files changed

+70
-120
lines changed

labextension/vpython/src/glowcommlab.js

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -479,14 +479,14 @@ var attrsb = {'a':'userzoom', 'b':'userspin', 'c':'range', 'd':'autoscale', 'e':
479479
'y':'markers', 'z':'legend', 'A':'label','B':'delta', 'C':'marker_color',
480480
'D':'size_units', 'E':'userpan', 'F':'scroll', 'G':'choices', 'H':'depth', 'I':'round'}
481481

482-
// methods are X in {'m': '23X....'}
482+
// methods are X in {'m': '23X....'} available: u
483483
var methods = {'a':'select', 'b':'pos', 'c':'start', 'd':'stop', 'f':'clear', // unused eghijklmnopvxyzCDFAB
484484
'q':'plot', 's':'add_to_trail',
485-
't':'follow', 'u':'_attach_arrow', 'w':'clear_trail',
485+
't':'follow', 'w':'clear_trail',
486486
'G':'bind', 'H':'unbind', 'I':'waitfor', 'J':'pause', 'K':'pick', 'L':'GSprint',
487487
'M':'delete', 'N':'capture'}
488488

489-
var vecattrs = ['pos', 'up', 'color', 'trail_color', 'axis', 'size', 'origin', '_attach_arrow',
489+
var vecattrs = ['pos', 'up', 'color', 'trail_color', 'axis', 'size', 'origin',
490490
'direction', 'linecolor', 'bumpaxis', 'dot_color', 'ambient', 'add_to_trail', 'textcolor',
491491
'foreground', 'background', 'ray', 'ambient', 'center', 'forward', 'normal',
492492
'marker_color']
@@ -613,23 +613,16 @@ function o2vec3(p) {
613613
}
614614

615615
function handler(data) {
616+
"use strict";
616617
"use strict";
617618

618-
/*
619-
// Debugging what is sent from server:
620-
let found = false
621-
for (let d in data) {
622-
for (const i in data[d]) {
623-
if (!found) {
624-
found = true
625-
console.log('================')
626-
}
627-
if (found) {
628-
console.log(i, JSON.stringify(data[d][i]))
629-
}
630-
}
631-
}
632-
*/
619+
/*
620+
console.log('---------------')
621+
for (var d in data) {
622+
for (var i in data[d]) console.log(i, JSON.stringify(data[d][i]))
623+
}
624+
*/
625+
633626

634627
if (data.cmds !== undefined && data.cmds.length > 0) handle_cmds(data.cmds)
635628
if (data.methods !== undefined && data.methods.length > 0) handle_methods(data.methods)
@@ -838,19 +831,6 @@ function handle_cmds(dcmds) {
838831
// Display frames per second and render time:
839832
//$("<div id='fps'/>").appendTo(glowObjs[idx].title)
840833
}
841-
case 'attach_arrow': {
842-
var attrs = ['pos', 'size', 'axis', 'up', 'color']
843-
var o = glowObjs[cfg['obj']]
844-
delete cfg['obj']
845-
var attr = cfg['attr']
846-
delete cfg['attr']
847-
var val = cfg['attrval']
848-
delete cfg['attrval']
849-
if (attrs.indexOf(attr) < 0) attr = '_attach_arrow'
850-
o.attr = val
851-
glowObjs[idx] = attach_arrow( o, attr, cfg )
852-
break
853-
}
854834
case 'attach_trail': {
855835
if ( typeof cfg['_obj'] === 'string' ) {
856836
var o = cfg['_obj'] // the string '_func'
@@ -939,8 +919,6 @@ async function handle_methods(dmeth) {
939919
glowObjs[idx]['pos'] = val
940920
} else if (method === 'add_to_trail') {
941921
obj['_func'] = val
942-
} else if (method === '_attach_arrow') {
943-
obj.obj._attach_arrow = val
944922
} else if (method === 'bind') {
945923
var evts = val.split(' ')
946924
for (var evt in evts) {
@@ -999,7 +977,6 @@ function handle_attrs(dattrs) {
999977
var attr = cmd['attr']
1000978
var val = cmd['val']
1001979
var triangle_quad = ['v0', 'v1', 'v2', 'v3']
1002-
// vector attrs in attach_arrow have arbitrary names, so check for length 3 array instead
1003980
if (val instanceof vec) {
1004981
if (attr === 'pos' && (obj instanceof points || obj instanceof curve)) {
1005982
var ptlist = []

vpython/vpython.py

Lines changed: 49 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@
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
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
105105
'plot':'q', 'add_to_trail':'s',
106-
'follow':'t', '_attach_arrow':'u', 'clear_trail':'w',
106+
'follow':'t', 'clear_trail':'w',
107107
'bind':'G', 'unbind':'H', 'waitfor':'I', 'pause':'J', 'pick':'K', 'GSprint':'L',
108108
'delete':'M', 'capture':'N'}
109109

110-
__vecattrs = ['pos', 'up', 'color', 'trail_color', 'axis', 'size', 'origin', '_attach_arrow',
110+
__vecattrs = ['pos', 'up', 'color', 'trail_color', 'axis', 'size', 'origin',
111111
'direction', 'linecolor', 'bumpaxis', 'dot_color', 'ambient', 'add_to_trail',
112112
'foreground', 'background', 'ray', 'ambient', 'center', 'forward', 'normal',
113113
'marker_color']
@@ -209,14 +209,18 @@ def empty(cls):
209209
def handle_attach(cls): # called when about to send data to the browser
210210
## update every attach_arrow if relevant vector has changed
211211
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
213216
vval = getattr(obj, aa.attr) # could be 'velocity', for example
214217
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
220224

221225
## update every attach_trail that depends on a function
222226
for aa in cls.attach_trails:
@@ -545,10 +549,6 @@ class standardAttributes(baseObj):
545549
[],
546550
['texture', 'bumpmap', 'visible', 'pickable'],
547551
['v0', 'v1', 'v2', 'v3'] ],
548-
'attach_arrow': [ [ 'color', 'attrval'],
549-
[],
550-
['shaftwidth', 'round', 'scale', 'obj', 'attr'],
551-
[] ],
552552
'attach_trail': [ ['color'],
553553
[],
554554
['radius', 'pps', 'retain', 'type', '_obj'],
@@ -721,14 +721,14 @@ def setup(self, args):
721721

722722
# attribute vectors have these methods which call self.addattr()
723723
# 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']
725725
if not (objName == 'extrusion'): #
726726
self._color.on_change = self._on_color_change
727727
if objName not in noSize:
728728
self._axis.on_change = self._on_axis_change
729729
self._size.on_change = self._on_size_change
730730
self._up.on_change = self._on_up_change
731-
noPos = ['curve', 'points', 'triangle', 'quad', 'attach_arrow']
731+
noPos = ['curve', 'points', 'triangle', 'quad']
732732
if objName not in noPos:
733733
self._pos.on_change = self._on_pos_change
734734
elif objName == 'curve':
@@ -1330,24 +1330,6 @@ def headlength(self,value):
13301330
if not self._constructing:
13311331
self.addattr('headlength')
13321332

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-
13511333
@property
13521334
def round(self):
13531335
return self._round
@@ -1374,10 +1356,34 @@ def shaftwidth(self, value):
13741356
self.addattr("shaftwidth")
13751357

13761358
def stop(self):
1377-
self.addmethod('stop', 'None')
1359+
self._run = self.visible = False
13781360

13791361
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
13811387

13821388
class attach_trail(standardAttributes):
13831389
def __init__(self, obj, **args):
@@ -2428,15 +2434,15 @@ def foreground(self): return self._foreground
24282434
@foreground.setter
24292435
def foreground(self,val):
24302436
if not isinstance(val, vector): raise TypeError('foreground must be a vector')
2431-
self._foreground = vector(value)
2437+
self._foreground = vector(val)
24322438
self.addattr('foreground')
24332439

24342440
@property
24352441
def background(self): return self._background
24362442
@background.setter
24372443
def background(self,val):
24382444
if not isinstance(val,vector): raise TypeError('background must be a vector')
2439-
self._background = vector(value)
2445+
self._background = vector(val)
24402446
self.addattr('background')
24412447

24422448
@property
@@ -3404,13 +3410,13 @@ def setup(self, args):
34043410
## default values of common attributes
34053411
self._constructing = True
34063412
argsToSend = []
3407-
objName = args['_objName']
3413+
self.objName = args['_objName']
34083414
del args['_objName']
34093415
if 'pos' in args:
34103416
self.location = args['pos']
34113417
if self.location == print_anchor:
34123418
#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.')
34143420
argsToSend.append('location')
34153421
del args['pos']
34163422
if 'canvas' in args: ## specified in constructor
@@ -3438,12 +3444,12 @@ def setup(self, args):
34383444

34393445
## override default scalar attributes
34403446
for a,val in args.items():
3441-
if a in controls.attrlists[objName]:
3447+
if a in controls.attrlists[self.objName]:
34423448
argsToSend.append(a)
34433449
setattr(self, '_'+a, val)
34443450
else:
34453451
setattr(self, a, val)
3446-
cmd = {"cmd": objName, "idx": self.idx}
3452+
cmd = {"cmd": self.objName, "idx": self.idx}
34473453
cmd["canvas"] = self.canvas.idx
34483454

34493455
## send only args specified in constructor
@@ -3465,10 +3471,10 @@ def bind(self, value):
34653471

34663472
@property
34673473
def pos(self):
3468-
raise AttributeError(objName+' pos attribute is not available.')
3474+
raise AttributeError(self.objName+' pos attribute is not available.')
34693475
@pos.setter
34703476
def pos(self, value):
3471-
raise AttributeError(objName+' pos attribute cannot be changed.')
3477+
raise AttributeError(self.objName+' pos attribute cannot be changed.')
34723478

34733479
@property
34743480
def disabled(self):

vpython/vpython_libraries/glow.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vpython/vpython_libraries/glowcomm.html

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -397,14 +397,14 @@
397397
'y':'markers', 'z':'legend', 'A':'label','B':'delta', 'C':'marker_color',
398398
'D':'size_units', 'E':'userpan', 'F':'scroll', 'G':'choices', 'H':'depth', 'I':'round'}
399399

400-
// methods are X in {'m': '23X....'}
400+
// methods are X in {'m': '23X....'} available: u
401401
var methods = {'a':'select', 'b':'pos', 'c':'start', 'd':'stop', 'f':'clear', // unused eghijklmnopvxyzCDFAB
402402
'q':'plot', 's':'add_to_trail',
403-
't':'follow', 'u':'_attach_arrow', 'w':'clear_trail',
403+
't':'follow', 'w':'clear_trail',
404404
'G':'bind', 'H':'unbind', 'I':'waitfor', 'J':'pause', 'K':'pick', 'L':'GSprint',
405405
'M':'delete', 'N':'capture'}
406406

407-
var vecattrs = ['pos', 'up', 'color', 'trail_color', 'axis', 'size', 'origin', '_attach_arrow',
407+
var vecattrs = ['pos', 'up', 'color', 'trail_color', 'axis', 'size', 'origin',
408408
'direction', 'linecolor', 'bumpaxis', 'dot_color', 'ambient', 'add_to_trail', 'textcolor',
409409
'foreground', 'background', 'ray', 'ambient', 'center', 'forward', 'normal',
410410
'marker_color']
@@ -539,7 +539,7 @@
539539
for (var i in data[d]) console.log(i, JSON.stringify(data[d][i]))
540540
}
541541
*/
542-
542+
543543

544544
if (data.cmds !== undefined && data.cmds.length > 0) handle_cmds(data.cmds)
545545
if (data.methods !== undefined && data.methods.length > 0) handle_methods(data.methods)
@@ -748,19 +748,6 @@
748748
// Display frames per second and render time:
749749
//$("<div id='fps'/>").appendTo(glowObjs[idx].title)
750750
}
751-
case 'attach_arrow': {
752-
var attrs = ['pos', 'size', 'axis', 'up', 'color']
753-
var o = glowObjs[cfg['obj']]
754-
delete cfg['obj']
755-
var attr = cfg['attr']
756-
delete cfg['attr']
757-
var val = cfg['attrval']
758-
delete cfg['attrval']
759-
if (attrs.indexOf(attr) < 0) attr = '_attach_arrow'
760-
o.attr = val
761-
glowObjs[idx] = attach_arrow( o, attr, cfg )
762-
break
763-
}
764751
case 'attach_trail': {
765752
if ( typeof cfg['_obj'] === 'string' ) {
766753
var o = cfg['_obj'] // the string '_func'
@@ -816,7 +803,6 @@
816803
cfg.objName = obj
817804
cfg.bind = control_handler
818805
cfg = fix_location(cfg)
819-
console.log('menu cfg', cfg)
820806
glowObjs[idx] = menu(cfg)
821807
if (cfg['selected'] === 'None') {
822808
cfg['selected'] = null
@@ -850,8 +836,6 @@
850836
glowObjs[idx]['pos'] = val
851837
} else if (method === 'add_to_trail') {
852838
obj['_func'] = val
853-
} else if (method === '_attach_arrow') {
854-
obj.obj._attach_arrow = val
855839
} else if (method === 'bind') {
856840
var evts = val.split(' ')
857841
for (var evt in evts) {
@@ -909,9 +893,7 @@
909893
var obj = glowObjs[idx]
910894
var attr = cmd['attr']
911895
var val = cmd['val']
912-
console.log(obj, attr, val)
913896
var triangle_quad = ['v0', 'v1', 'v2', 'v3']
914-
// vector attrs in attach_arrow have arbitrary names, so check for length 3 array instead
915897
if (val instanceof vec) {
916898
if (attr === 'pos' && (obj instanceof points || obj instanceof curve)) {
917899
var ptlist = []

0 commit comments

Comments
 (0)