From: Andrew Dalke (dalke_at_bioreason.com)
Date: Thu Dec 10 1998 - 20:06:25 CST

[answering David Horita's <horita_at_ncifcrf.gov> questions]

I'll go from the last to the first

| vmd > set sel [atomselect top "name CA and resid 32"]
| vmd > set coords [$sel get {x y z}]
| gives
| vmd > list $coords
| Info) { {40.139999 0.444000 0.237000}}
| so $coords is not a 3 element vector. How do I equate a variable
| with the xyz coordinates such that I can use it as an argument to
| vecdist (or vecsub, for that matter)?

the selection "get" returns a list of tuples, one tuple for each
atom. Since you've only selected one atom, you have a single
element list, though that list contains three term {x, y, z}.

To get what you want, try:
  lassign [$sel get {x y z}] coords
instead of the "set coords ..." This is a TclX command to
assign each element of the list to the given variable. It's
very handy.

Or since you are going to work with two atoms (and it looks like
you only have CA positions), the best thing would probably be:

  set sel [atomselect top "name CA and resid 32 29"]
  lassign [$sel get {x y z}] coords1 coords2
  vecdist $coords1 $coords2

| I haven't figured out the atom selection part of 'label add Bonds'

That's delving back into the history of VMD. The selections for
labels is quite old. You need to know the molecule id and the
atom ids for the bond involved. In the script you can get it with

for the molecule:
  set molid [$sel molid]

for the atoms:
  lassign [$sel get {index}] atomid1 atomid2

then to make the bond label (sorry, "Bond" -- this is case sensitive)

  label add Bonds "$molid/$atomid1" "$molid/$atomid2"

The label selection syntax is <molecule id> "/" <atom id> .

> I have an MD run where I'd like to be able to follow the distance
> between two atoms and display it in a corner of the VMD graphics
> window

  Ahh, fun stuff. I know a couple of people back at UIUC did
something like this (that is, showing data in the corner of the
screen). The most spiff is Willy Wrigger's "dials" which can be had
at http://www.ks.uiuc.edu/Research/vmd/script_library/scripts/dials/ .
It's pretty easy to understand since the main loop of the program
controls the display (eg, you cannot use the animation menu, you
can't load new frames, etc.)

[.... pretend the following is a VMD Tech *grin* ...]

  There's a lot you can do along these lines. Here's a way to
implement most of what the Bond labeling does (it doesn't draw the
dotted line, but if you understand what this does the rest is easy :).
This script uses some rather deep understanding of how VMD works.
Long timers on this list should recognize some of the tricks (eg,
using a trace on vmd_frame to be informed when the coordinates
change).

> I'm sufficiently baffled by the scripting language to not
> know how to do this.

  I've made it so you can just use the script. If you don't want to
figure it out, follow the use instructions. The full example is
here because you can use it without understanding, and because
others on the list might have similar questions.

 = = = = = = = = =
set display_distance_count 0
proc display_distance {sel1 sel2 {color white} {format "%4.2f"}} {
  # check the inputs
  if {[$sel1 num] != 1 || [$sel2 num] != 1} {
    die "Can only have one atom in either selection"
  }
  set molid1 [$sel1 molid]
  set molid2 [$sel2 molid]
  lassign [$sel1 get index] atom1
  lassign [$sel2 get index] atom2

  # create a window for the text
  mol load graphics "bond $molid1:$atom1 $molid2:$atom2"
  set w [molinfo top]
  # and add some text in the given color
  graphics $w color $color
  lassign [$sel1 get {x y z}] xyz1
  lassign [$sel2 get {x y z}] xyz2
  set pos [vecscale 0.5 [vecadd $xyz1 $xyz2]]
  set val [vecdist $xyz1 $xyz2]
  # it between the two atoms)
  set g_id [graphics $w text $pos [format $format $val]]

  # make a copy of the current selection
  set s1 [atomselect $molid1 "index $atom1"]
  set s2 [atomselect $molid2 "index $atom2"]
  # and make them be eternal
  # (this is pretty deep VMD selection mechanics. Basically selections
  # only live for the life time of the function that creates them.
  # For performance reasons I don't want to recreate the selection
  # every time the callback is created, so I make selections and tell
  # them not to be deleted at the end of the function. I can then
  # use the selections elsewhere in the code)
  $s1 global
  $s2 global

  # create a function to handle updates of this bond
  global display_distance_count
  set fctn_name "display_distance_$display_distance_count"
  incr display_distance_count
  # The following string will be eval'ed
  set fctn "
proc $fctn_name {args} {
  # If the graphics window no longer exists, remove the callback
  if {\[lsearch \[molinfo list\] $w\] == -1} {
    global vmd_frame
    if {$molid1 == $molid2} {
      trace vdelete vmd_frame($molid1) w $fctn_name
    } else {
      trace vdelete vmd_frame($molid1) w $fctn_name
      trace vdelete vmd_frame($molid2) w $fctn_name
    }
    # remove the atom selection (otherwise we'll lose some memory)
    $s1 delete
    $s2 delete
    # and remove myself
    rename $fctn_name {}
    return
  }
  # get the coordinates
  lassign \[$s1 get {x y z}\] xyz1
  lassign \[$s2 get {x y z}\] xyz2
  set dist \[vecdist \$xyz1 \$xyz2\]
  # and draw them
  graphics $w replace $g_id
  graphics $w text {$pos} \[format {$format} \$dist\]
}
"
  eval $fctn

  # update when the frame changes
  global vmd_frame
  if {$molid1 == $molid2} {
    trace variable vmd_frame($molid1) w $fctn_name
  } else {
    trace variable vmd_frame($molid1) w $fctn_name
    trace variable vmd_frame($molid2) w $fctn_name
  }
}
 = = = = = = = = =

To use it, copy and paste the above block of text into the VMD text
console. Select the two atoms of interest and place them in different
selections. For example:

  set q1 [atomselect top "name CA and resid 5"]
  set q2 [atomselect top "name P and resid 99"]

Then call the new function as:

  display_distance $q1 $q2

(optional parameters are the color to use (default = "white") and the
format string for converting a floating point number into text
(default = "%4.2f"))

This creates a new graphics window which contains the distance
information located at the point 1/2 between the two atoms. There is
some strangeness about position. Since the atoms can be in different
molecules, and since molecules can have different
scales/translations/etc, it may not appear that the label is between
the two atoms. To get it that way, you'll have to do "Reset View"
from the popup menu, or use the control-R (^R) hot key in the display
window.

When you animate the display, the label will update but not the
position (that's left for the student as an exercise :). I left it
this way so you could freeze everything except the graphics window
(use the "molecule" window) and move the label around to where you
want it to be (eg, lower left corner). Then you can freeze the label
and unfreeze everything else.

How does it all work?

  The function display_distance creates a new function,
display_distance_# where the number increases for each new function
(eg, display_distance_0, display_distance_1, display_distance_2, ...).

(An advantage of Tcl is we can make new functions on the fly. A
disadvantage of Tcl is if this were an object-oriented langauge we
would just make a new object instead of all this fancy work.)

  The first section of the code does some error checking and creates
new, "internal" selections. (This is the hardest part of this
function to understand.) These new selections refer to the same atoms
as those passed in, but are managed differently. Specifically, they
are converted to "global variables" so we only need to create them
once instead of reselecting the atoms at every callback.
  If you know some Tcl you'll recognize that the selections are
actually functions so should be global by default. However, VMD plays
some tricks with them to get automatic cleanup when the current
function scope ends. Otherwise, selections would have to be explictly
deleted and it makes algorithm development more complicated. Bonus
points to someone who cares to describe to the list how the mechanism
works :)
 If Tcl did reference counting we wouldn't need tricks like this.

  The 2nd section sets up the graphics display with the right color
and shows some initial text. This is all documented in the VMD
manual.

  The 3rd section of display_distance creates the new function. This
function uses a lot of information garnered in the 1st half so we can
do string substitution directly to get those values (eg, the atom
selection function names, the molecule ids, the location of the text
in the graphics display, the graphics display id, etc.). I'll skip
discussion of the function itself for a moment.

  The last part evalutes the text, which creates a new function. It
then sets up a callback to that function from vmd_trace. The
complicated part here is that the two atoms may be from the same
molecule or different molecules. If they are from the same molecule
there should only be one callback. If they are from two molecules,
you'll want to update the display when either one changes. The "if"
at the end checks the two conditions.

  The new function contains a check to see if the graphics "molecule"
used to display the text has been deleted. If so, it deletes the
traces (checking again if there was one or two atoms involved),
deletes the global atom selections, and removed the callback function
itself. If the display is still present, it computes the distance and
renders it into that graphics buffer.

To do: if either of the two molecules (or one) involved is deleted,
the label should be deleted as well. This is an extension of the
check to see if the graphics molecule was deleted.

  Got it all? :)

                                                Andrew Dalke
                                                dalke_at_bioreason.com