Blog

Signals Blog

WebGL for Chemistry Part 4: Bond, James Bond

The last article in this series showed how to build a simple molecule viewer that could read a 3D molfile and represent atoms as shaded spheres. In this article, we'll take the rendering a step further by adding bonds to the view.

All Articles in This Series

  1. WebGL for Chemistry Part 1: Introduction
  2. WebGL for Chemistry Part 2: Displaying a Sphere in a View
  3. WebGL for Chemistry Part 3: Hello, Benzene!
  4. WebGL for Chemistry Part 4: Bond, James Bond

Demo

If you haven't already done so, take a look at the live demo, which I tested on Mac OS X running Firefox 4.0. You should see an image similar to the screenshot above. Scrolling the mouse wheel up and down zooms the view.

High-Level Overview of Changes

This iteration adds two major changes:

Reading Bonds

Bonds are now read with the following code executed immediately after atom processing:

for (var i = 0; i < bondCount; i++) {
  var line = lines[i + 4 + atomCount];

  bonds.push({
    source: atoms[parseInt(line.substring(0, 3)) - 1],
    target: atoms[parseInt(line.substring(3, 6)) - 1],
    getLength: function() {
      var sub = this.source.position.sub(this.target.position);
      return Math.pow((Math.pow(sub.x, 2) + Math.pow(sub.y, 2) + Math.pow(sub.z, 2)), 0.5);
    },
    getMidpoint: function() {
      return this.source.position.add(this.target.position).scale(0.5);
    },
    getDirection: function() {
      return this.target.position.sub(this.source.position).unit();
    }
  });
}
 

Here we simply create an object literal representation of a bond based on each bond line in the molfile. The representation contains a reference to the source and target atoms in addition to some convenience geometry functions that we'll use during rendering (see below).

Note that we're approaching the point where both atoms and bonds, and indeed molecule itself might be better represented through their own class-like data structure. But for now, the code serves the purpose.

Rendering Bonds

In m3d#createView, we iterate over each bond in the molecule, calling m3D.View#drawBond for each:

for (var i = 0; i < molecule.bonds.length; i++) {
  view.drawBond(molecule.bonds[i]);    
}
 

The real work is done in m3d.View#drawBond:

/**
 * @param {object} bond The bond to draw
 */
m3d.View.prototype.drawBond = function(bond) {
  var cylinder = new PhiloGL.O3D.Cylinder({ radius: 0.2, height: bond.getLength()}); 

  this.translateCylinder_(bond, cylinder);
  this.rotateCylinder_(bond, cylinder);
  this.scene_.add(cylinder);
};
 

Here, we create a Cylinder for each bond, then translate it to the midpoint between its atoms:

/**
 * @private
 */
m3d.View.prototype.translateCylinder_ = function(bond, cylinder) {
  var midpoint = bond.getMidpoint();

  cylinder.matrix.$translate(midpoint.x, midpoint.y, midpoint.z);
};
 

and finally rotate the cylinder into place

/**
 * @private
 */
m3d.View.prototype.rotateCylinder_ = function(bond, cylinder) {
  var cylinderDirection = new PhiloGL.Vec3(0, 1, 0);
  var bondDirection = bond.getDirection();
  var angle = Math.acos(bondDirection.dot(cylinderDirection));
  var axis = cylinderDirection.$cross(bondDirection).$unit();

  cylinder.matrix.$rotateAxis(angle, axis);
};
 

Theres a bit of matrix math here that, simply put, I don't understand. For now it's enough that the code works - future installments may discuss how we got here.

Special thanks to Nicolas Garcia Belmonte, creator of PhiloGL, for giving me the code to get the cylinders properly rotated and translated. I was really quite stuck, but the solution turned out to be not so complicated after all. See the entire discussion here.

Conclusions

With the completion of this iteration, we now have a WebGL-based molecule viewer that can read a molfile and display a 3D representation of the encoded atoms and bonds. We also have a reasonable, though limited, infrastructure in place to begin making the view look more like that you'd expect to see in a more mature tool. Future articles will show the way.