Skip to content

Instantly share code, notes, and snippets.

Created January 6, 2009 11:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/43774 to your computer and use it in GitHub Desktop.
Save anonymous/43774 to your computer and use it in GitHub Desktop.
<html>
<head>
<script language=javascript>
//
// molecular depictions in a canvas element, by Richard Hall, Jan 2009
//
// when I've finished reading Crockford's Javascript - The Good Parts, I should tidy this up...
//
//a really simple atom class
function Atom(type, x, y){
this.type = type;
this.crd = new Array(x, y);
}
//an atom representation string
Atom.prototype.repr = function(){
return('atom '+this.type+':'+this.crd[0]+' '+this.crd[1]);
}
//scale 2d atom coordinates to coordinates that will fit in the canvas context, leaving some border
Atom.prototype.context = function(border, xscale, yscale, xr, yr){
var x = border + (this.crd[0]-xr[0])/(xr[1]-xr[0]) * xscale;
var y = border + (1.0 - (this.crd[1]-yr[0])/(yr[1]-yr[0])) * yscale;
return new Array(x, y);
}
//a really simple bond class
function Bond(a, b, type){
this.a = a;
this.b = b;
this.type = type;
}
//a bond representation string
Bond.prototype.repr = function(){
var atype = new Array('x', '-', '=', '#');
return('bond '+this.a+atype[this.type]+this.b);
}
//a really simple molecule class, built from a jme string...
function Molecule(str){
var i;
this.atoms = new Array;
this.bonds = new Array;
var tok = str.split(" ");
this.natoms = parseInt(tok[0]);
this.nbonds = parseInt(tok[1]);
for(i=2; i<2+3*this.natoms; i+=3){
var type = tok[i];
var xcrd = parseFloat(tok[i+1]);
var ycrd = parseFloat(tok[i+2]);
this.atoms[this.atoms.length] = new Atom(type, xcrd, ycrd);
}
var start = 2+3*this.natoms;
for(var i=start; i<tok.length; i+=3){
var a = parseInt(tok[i])-1;
var b = parseInt(tok[i+1])-1;
var type = parseInt(tok[i+2]);
this.bonds[this.bonds.length] = new Bond(a, b, type);
}
}
//a molecular representation string
Molecule.prototype.repr = function(){
var i;
var s = '';
for(i=0; i<this.atoms.length; i++){
s += this.atoms[i].repr()+'\n';
}
for(i=0; i<this.bonds.length; i++){
s += this.bonds[i].repr()+'\n';
}
return(s);
}
//return the min max extent in a given dimension (0:x, 1:y)
Molecule.prototype.minmax = function(dim){
var minv = 99999.9;
var maxv = -99999.9;
for(var i=0; i<this.atoms.length;i++){
minv = this.atoms[i].crd[dim] < minv ? this.atoms[i].crd[dim] : minv;
maxv = this.atoms[i].crd[dim] > maxv ? this.atoms[i].crd[dim] : maxv;
}
return Array(minv, maxv);
}
//draw a molecule to the canvas element named id
Molecule.prototype.depict = function(id){
var cnv = document.getElementById(id);
var border=20; //the amount of whitespace around the depiction in pixels
//scaling to make the molecule fit the element dimensions
var xreg = this.minmax(0);
var yreg = this.minmax(1);
var xyratio = (xreg[1]-xreg[0])/(yreg[1]-yreg[0]);
//additional scaling if the canvas is not square...
var canvasratio = (cnv.width-2*border)/(cnv.height-2*border);
var xyratio = xyratio/canvasratio;
var xscale, yscale;
if(xyratio > 1){
xscale = (cnv.width-2*border);
yscale = (cnv.height-2*border)/xyratio;
}
else{
xscale = (cnv.width-2*border)*xyratio;
yscale = (cnv.height-2*border);
}
var ctx = cnv.getContext('2d');
//clear the context
ctx.clearRect(0, 0, cnv.width, cnv.height)
//draw the bonds
for(var i=0; i<this.bonds.length;i++){
var a = this.atoms[this.bonds[i].a];
var b = this.atoms[this.bonds[i].b];
var actx = a.context(border, xscale, yscale, xreg, yreg);
var bctx = b.context(border, xscale, yscale, xreg, yreg);
ctx.moveTo(actx[0], actx[1]);
ctx.lineTo(bctx[0], bctx[1]);
}
ctx.stroke();
}
//this function is called when the button is pressed - it copies the jme string from the applet and uses it to build a new molecule
function depict(){
var jmestr = document.getElementById('jme').jmeFile();
if(jmestr.length()){
document.getElementById('jmestr').value = jmestr;
}
var m = new Molecule(document.getElementById('jmestr').value);
//draw the molecule onto the element with id 'mycanvas'
m.depict('mycanvas')
}
</script>
</head>
</head>
<body>
<p>
this is an example of using the canvas widget to depict molecules in 2D - to make life a bit easier, I'm starting with the JME string, so I use their coordinates etc for rendering...wouldn't it would be nice to be able to use a molfile or a smiles string though ;)
</p>
<p>
You'll need a copy of the <a href="http://www.molinspiration.com/jme/index.html">JME Editor</a>, <a href="http://www.mozilla-europe.org/en/firefox/">firefox</a> is the only browser I've tested it in and <a href="http://getfirebug.com/">firebug</a> was used during development.
</p>
<table>
<tr>
<td>
<applet id='jme' code='JME.class' archive='JME.jar' codebase='./' width=300 height=300>
<param name='options' value='nohydrogens,query,multipart'>
</applet>
</br>
<font face="arial,helvetica,sans-serif"><small><a href="http://www.molinspiration.com/jme/index.html">JME Editor</a> courtesy of Peter Ertl, Novartis</small></font>
</td>
<td>
<canvas id=mycanvas width=300 height=300>
</canvas>
</td>
</tr>
</table>
</br>
paste a jme string into the textbox (or draw onto the applet) and click depict - you should see a depiction in the canvas element
</br>
there is a sample string below if you don't have a JME applet to hand - you may need to hack the depict function to get it to work
</br>
<input id=jmestr type=text value="12 13 C 7.32 -7.53 C 7.32 -6.13 C 6.10 -5.43 C 4.89 -6.13 C 4.89 -7.53 C 6.10 -8.23 C 8.53 -8.23 C 9.74 -7.53 C 9.74 -6.13 C 8.53 -5.43 C 6.10 -4.03 C 3.68 -8.23 1 2 1 2 3 2 3 4 1 4 5 2 5 6 1 6 1 2 7 8 2 8 9 1 9 10 2 2 10 1 1 7 1 3 11 1 5 12 1" />
<input onClick=depict() type=button value='depict' />
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment