December 26, 2011

Chemdoodle web components tricks #2: showing atom numbers

Due to the release of ChemDoodle web components version 4.5 showing atom numbers have been much simplified, and this post has been superseded by a new script in a new post Chemdoodle web components tricks #4: showing atom numbers with the altLabel property

My second try on using the chemdoodle web compontents. Very nice GPL licensed javascript library for chemical 2D (and 3D if you have a webGL supporting browser).

One thing missing from the API is atom numbers (i.e. numbering from 1,2...10 for all atoms). Here I'll show how to display atom numbers using a viewercanvas.

Standard canvas with a caffeine molecule and colored atom labels (from my last post on scaling):

And here, the same canvas with atom numbers shown:

The javascript code for the standard scaled canvas:

 
  var tutorial2_testmol = new ChemDoodle.ViewerCanvas('tutorial2_testmol', 300, 300);
  tutorial2_testmol.specs.atoms_useJMOLColors = true;
  var caffeineMolFile = 'Molecule Name\n  CHEMDOOD08070920033D 0   0.00000     0.00000     0\n[Insert Comment Here]\n 14 15  0  0  0  0  0  0  0  0  1 V2000\n   -0.3318    2.0000    0.0000   O 0  0  0  1  0  0  0  0  0  0  0  0\n   -0.3318    1.0000    0.0000   C 0  0  0  1  0  0  0  0  0  0  0  0\n   -1.1980    0.5000    0.0000   N 0  0  0  1  0  0  0  0  0  0  0  0\n    0.5342    0.5000    0.0000   C 0  0  0  1  0  0  0  0  0  0  0  0\n   -1.1980   -0.5000    0.0000   C 0  0  0  1  0  0  0  0  0  0  0  0\n   -2.0640    1.0000    0.0000   C 0  0  0  4  0  0  0  0  0  0  0  0\n    1.4804    0.8047    0.0000   N 0  0  0  1  0  0  0  0  0  0  0  0\n    0.5342   -0.5000    0.0000   C 0  0  0  1  0  0  0  0  0  0  0  0\n   -2.0640   -1.0000    0.0000   O 0  0  0  1  0  0  0  0  0  0  0  0\n   -0.3318   -1.0000    0.0000   N 0  0  0  1  0  0  0  0  0  0  0  0\n    2.0640   -0.0000    0.0000   C 0  0  0  2  0  0  0  0  0  0  0  0\n    1.7910    1.7553    0.0000   C 0  0  0  4  0  0  0  0  0  0  0  0\n    1.4804   -0.8047    0.0000   N 0  0  0  1  0  0  0  0  0  0  0  0\n   -0.3318   -2.0000    0.0000   C 0  0  0  4  0  0  0  0  0  0  0  0\n  1  2  2  0  0  0  0\n  3  2  1  0  0  0  0\n  4  2  1  0  0  0  0\n  3  5  1  0  0  0  0\n  3  6  1  0  0  0  0\n  7  4  1  0  0  0  0\n  4  8  2  0  0  0  0\n  9  5  2  0  0  0  0\n 10  5  1  0  0  0  0\n 10  8  1  0  0  0  0\n  7 11  1  0  0  0  0\n  7 12  1  0  0  0  0\n 13  8  1  0  0  0  0\n 13 11  2  0  0  0  0\n 10 14  1  0  0  0  0\nM  END\n> \n07-08-2009\n';
  var caffeine = ChemDoodle.readMOL(caffeineMolFile);
  // get the dimension of the molecule
  var size = caffeine.getDimension();
  // find the scale by taking the minimum of the canvas/size ratios
  var scale = Math.min(tutorial2_testmol.width/size.x, tutorial2_testmol.height/size.y);
  // load the molecule first (this function automatically sets scale, so we need to change specs after)
  tutorial2_testmol.loadMolecule(caffeine);
  // change the specs.scale value to the scale calculated, shrinking it slightly so that text is not cut off
  tutorial2_testmol.specs.scale = scale*.9;
  // repaint the canvas
  tutorial2_testmol.repaint(); 

and the javascript code for the canvas with the atom numbers:

 
  var tutorial2_testmol2 = new ChemDoodle.ViewerCanvas('tutorial2_testmol2', 300, 300);
  tutorial2_testmol2.specs.atoms_useJMOLColors = true;
  var caffeineMolFile = 'Molecule Name\n  CHEMDOOD08070920033D 0   0.00000     0.00000     0\n[Insert Comment Here]\n 14 15  0  0  0  0  0  0  0  0  1 V2000\n   -0.3318    2.0000    0.0000   O 0  0  0  1  0  0  0  0  0  0  0  0\n   -0.3318    1.0000    0.0000   C 0  0  0  1  0  0  0  0  0  0  0  0\n   -1.1980    0.5000    0.0000   N 0  0  0  1  0  0  0  0  0  0  0  0\n    0.5342    0.5000    0.0000   C 0  0  0  1  0  0  0  0  0  0  0  0\n   -1.1980   -0.5000    0.0000   C 0  0  0  1  0  0  0  0  0  0  0  0\n   -2.0640    1.0000    0.0000   C 0  0  0  4  0  0  0  0  0  0  0  0\n    1.4804    0.8047    0.0000   N 0  0  0  1  0  0  0  0  0  0  0  0\n    0.5342   -0.5000    0.0000   C 0  0  0  1  0  0  0  0  0  0  0  0\n   -2.0640   -1.0000    0.0000   O 0  0  0  1  0  0  0  0  0  0  0  0\n   -0.3318   -1.0000    0.0000   N 0  0  0  1  0  0  0  0  0  0  0  0\n    2.0640   -0.0000    0.0000   C 0  0  0  2  0  0  0  0  0  0  0  0\n    1.7910    1.7553    0.0000   C 0  0  0  4  0  0  0  0  0  0  0  0\n    1.4804   -0.8047    0.0000   N 0  0  0  1  0  0  0  0  0  0  0  0\n   -0.3318   -2.0000    0.0000   C 0  0  0  4  0  0  0  0  0  0  0  0\n  1  2  2  0  0  0  0\n  3  2  1  0  0  0  0\n  4  2  1  0  0  0  0\n  3  5  1  0  0  0  0\n  3  6  1  0  0  0  0\n  7  4  1  0  0  0  0\n  4  8  2  0  0  0  0\n  9  5  2  0  0  0  0\n 10  5  1  0  0  0  0\n 10  8  1  0  0  0  0\n  7 11  1  0  0  0  0\n  7 12  1  0  0  0  0\n 13  8  1  0  0  0  0\n 13 11  2  0  0  0  0\n 10 14  1  0  0  0  0\nM  END\n> \n07-08-2009\n';
  var caffeine = ChemDoodle.readMOL(caffeineMolFile);
  // get the dimension of the molecule
  var size = caffeine.getDimension();
  // find the scale by taking the minimum of the canvas/size ratios
  var scale = Math.min(tutorial2_testmol2.width/size.x, tutorial2_testmol2.height/size.y);
  // load the molecule first (this function automatically sets scale, so we need to change specs after)
  tutorial2_testmol2.loadMolecule(caffeine);
  // change the specs.scale value to the scale calculated, shrinking it slightly so that text is not cut off
  tutorial2_testmol2.specs.scale = scale*.9;
  tutorial2_testmol2.repaint();
  var molcenter = caffeine.getCenter();
  
  tutorial2_testmol2.drawChildExtras = function(ctx){
    ctx.translate(this.width/2, this.height/2);
    ctx.rotate(this.specs.rotateAngle);
    ctx.scale(this.specs.scale, this.specs.scale);
    ctx.translate(-this.width/2, -this.height/2);
    //draw atom numbers
    ctx.font = 'bold ' + 2.5*scale + 'px sans-serif';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
 //iterate through all atoms
    for (var i = 0, ii=caffeine.atoms.length; i<ii; i++) {
      var atom = caffeine.atoms[i];
      //draw a white circle behind the atom number
      ctx.fillStyle = "white";
      ctx.strokeStyle = "white";
      ctx.beginPath();
      ctx.arc(atom['x'],atom['y'],scale*1.8,0,Math.PI*2,true);
      ctx.fill();
      ctx.stroke();
      //draw the atom number
   thisatomcolor = ChemDoodle.ELEMENT[atom['label']].jmolColor;
      ctx.fillStyle = thisatomcolor;
      ctx.fillText(i + 1, atom['x'], atom['y']);
    }
  }

  tutorial2_testmol2.repaint();

The trickiest part about this is that while the first four lines in the drawChildExtras function (as suggested by Kevin in the comments and the fourth line is needed to simplify the atom locations), do scale the drawing on the canvas correctly, they do not scale or translate the molecule. That's why I had to subtract the x and y coordinates of the center of the molecule to get the positions right.

4 comments:

  1. Ahh, this is a nice series. I will chime in as much as possible.

    To transform the context in drawChildExtras(), as is done by the inner function of the Canvas, place the following at the beginning of the function:

    ctx.translate(this.width / 2, this.height / 2);
    ctx.rotate(this.specs.rotateAngle);
    ctx.scale(this.specs.scale, this.specs.scale);

    To obtain the Jmol color of an element, access the ELEMENT array (http://web.chemdoodle.com/api#element):

    // returns '#FF0D0D'
    ChemDoodle.ELEMENT['O'].jmolColor

    Also, I noticed you are having some Javascript namespace clashes on your blog frontpage, because the posts reuse Javascript and Canvas names. To resolve this, just prepend your variable names with a tag specific to the blogpost, like 'tutorial2_testmol2'.

    As food for thought, a nice feature of Javascript is the ability to modify source at runtime. We developed the web components with the module source pattern, so you can pull out and modify/replace the Atom class functions, which may be an even easier method for changing how they work. That being said, I will add a specification to display atom numbers to our development list.

    ReplyDelete
  2. Thanks again Kevin, I'll try to modify the code accordingly in a few days when I'm
    back from vacation.

    ReplyDelete
  3. The code has now been updated using the stuff suggested by Kevin.

    ReplyDelete
  4. Code updated once more to simplify it further by resetting the x and y coordinates of the molecule in the drawChildExtras function, and by using the text.Baseline functionality to center the atom numbers text on the y-center of the atoms.

    ReplyDelete