[blogging here]

You ask -- What the heck are these quaternions? Here's an example of 
where quaternions come in handy in Jmol.

Say you have two files that you want to display sequentially, 1.xyz and 
2.xyz. They are connected in some way, perhaps because they are two 
files along the same mechanistic pathway. When the user clicks a link to 
switch from Model 1 to Model 2 you want the user to experience no jump 
of orientation -- just a subtly different file. How can that be done?

1) Well, if the two files have the same basic coordinates, there's no 
real problem. You just use

  load 1.xyz
  ...user changes orientation...
  save orientation;
  load 2.xyz
  restore orientation;

The save/restore orientation does the trick.

2) But what if the INITIAL orientations of the two models are different? 
That is, what if generally you might have to use

  load 2.xyz
  moveto ....

to make 2.xyz line up with 1.xyz? What then? The problem is that if the 
user has manipulated the model, you can do that restore to get back to 
THEIR orientation, but that's for the wrong model, and you can't just 
put the restore before the moveto command, because the moveto will 
override that. What to do?

3) The solution is to use quaternions. Jmol can report its rotational 
state in terms of a quaternion. All you do is:

  q1 = script("show rotation")

That's it. You now have saved the rotational state. AND, Jmol can now 
rotate any model by that quaternion:

  rotate @q1

So, to apply a user's changes to a DIFFERENT model:

  load 1.xyz
  ...user changes orientation...
  q1 = script("show rotation")
  load 2.xyz
  moveto 0.0 ...
  rotate @q1

This is different. It saves the user's changes as a quaternion and then 
applies that quaternion after the moveto. Provided you have used 0 
seconds for the time on that moveto, you won't even see a flicker with 
Jmol 11.6.RC7 (this was recently fixed).

4) Ah, but there's a catch! The catch is that the user also may have 
changed the zoom or the translation. The solution again uses 
quaternions, but this time in combination with save/restore. Quaternions 
encode the rotation of a model in a RELATIVE SENSE. Thus, if

  q1 = [first rotational state]
  q2 = [later rotational state]

then the division

  q3 = q2 / q1

gives you the CHANGE in going from the first state to the second 
(sometimes referred to as the "quaternion difference" or "quaternion 
derivative").

So, check this out:

  load 1.xyz
  ...user changes orientation...
  q1 = script("show rotation")
  save orientation o1;
  load 2.xyz
  moveto 0.0 ...
  q2 = script("show rotation")
  restore orientation o1;
  rotate @{q2 / q1}
  rotate @q1

See what that does? We are saving:

  q1  the user's current rotation of model 1
  o1  the user's full orientation change, including rotation q1, zoom, 
and translation
  q2  our "default" rotation for model 2

The restore takes us to rotation q1 and applies any user zoom or 
translation changes.
The first rotation takes us back to our default rotation for Model 2, q2.
The second rotation applies the change the user made.

By the way, sequential rotations are applied using LEFT multiplication. 
Those two rotations can be combined as:

  rotate @{q1 * q2 / q1}

That's called a similarity transform.

5) Now, what if you have to rotate BOTH files to get the default 
orientation you want? Then it's a bit more tricky, but not too bad:

  load 1.xyz
  moveto 0.0 ...
  q1a = script("show rotation")
  ...user changes orientation...
  q1b = script("show rotation")
  save orientation o1;
  load 2.xyz
  moveto 0.0 ...
  q2 = script("show rotation")
  restore orientation o1;
  rotate @{q2 / q1b}
  rotate @{q1b / q1a}

See what that does? We are saving:

  q1a  our "default" rotation for model 1
  q1b  the user's current rotation of model 1
  o1   the user's full orientation change, including rotation q1b, zoom, 
and translation
  q2   our "default" rotation for model 2

The restore takes us to rotation q1b, along with the user's changes to 
translation and zoom.
The first rotation takes us back to our default rotation for Model 2, 
q2, but leaves the user's zoom and translation unchanged.
The second rotation applies the change the user made relative to our 
default for Model 1.


6) Finally, what if we want to allow the user to switch back and forth 
between models with NO orientation jump? This is really cool. Here you go:

First, load an initial file:

  load 1.xyz
  moveto 0.0 ...
  q = script("show rotation")


Then any later load of ANY file looks like this:

  qrel = quaternion(script("show rotation")) / q
  save orientation o1;
  load xxx.xyz
  moveto 0.0 ...
  q = script("show rotation")
  restore orientation o1;
  rotate @{qrel * q / quaternion(script("show rotation"))}

and now the code is fully symmetric. Put in any file name for "xxx.xyz", 
and this code will preserve orientation based on a custom default rotation.

7) Summary

Quaternions encode rotations. Their multiplication represents a series 
of rotations, and their division represents changes made to rotational 
states. Jmol web page developers can use quaternions to record 
customized defaults as well as changes the user has made to those 
defaults. These changes can then be applied to OTHER models so as to 
create a seamless transition from one model to another.


Bob

-- 
Robert M. Hanson
Professor of Chemistry
St. Olaf College
Northfield, MN
http://www.stolaf.edu/people/hansonr


If nature does not answer first what we want,
it is better to take what answer we get. 

-- Josiah Willard Gibbs, Lecture XXX, Monday, February 5, 1900



-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Jmol-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/jmol-users

Reply via email to