Minimol

A minimal molecular viewer

...implemented with the help of Processing.js. Click and drag on the molecule to rotate, zoom and translate.


float[][] atoms = {{ 0.710900, 0.037800, 0.156700 },
         { 2.503200, 0.016200, -0.016200 },
         { 2.891000, -0.757600, -1.285100 },
         { 2.464200, -2.218700, -1.241500 },
         { 3.023400, 1.457700, -0.000800 },
         { 2.284300, 2.444200, -0.015900 },
         { 4.501400, 1.700400, 0.054300 },
         { 5.440400, 0.664600, 0.139400 },
         { 6.807300, 0.939900, 0.086400 },
         { 7.250400, 2.257200, 0.023400 },
         { 6.328300, 3.301100, 0.002600 },
         { 4.959700, 3.024900, 0.004100 },
         { 2.868300, -0.484600, 0.886500 },
         { 3.973200, -0.754200, -1.439100 },
         { 2.481000, -0.267500, -2.175500 },
         { 2.843500, -2.709100, -0.338600 },
         { 2.864400, -2.753100, -2.108600 },
         { 1.376200, -2.320800, -1.261800 },
         { 5.150800, -0.375700, 0.237800 },
         { 7.524100, 0.122200, 0.097500 },
         { 8.315900, 2.470000, 0.000500 },
         { 6.672500, 4.332000, -0.022800 },
         { 4.252400, 3.852800, -0.032800 }};
bonds = {{ 0, 1 },
         { 1, 4 },
         { 1, 2 },
         { 1, 12 },
         { 2, 3 },
         { 2, 13 },
         { 2, 14 },
         { 3, 15 },
         { 3, 16 },
         { 3, 17 },
         { 4, 6 },
         { 4, 5 },
         { 6, 11 },
         { 6, 7 },
         { 7, 8 },
         { 7, 18 },
         { 8, 9 },
         { 8, 19 },
         { 9, 10 },
         { 9, 20 },
         { 10, 11 },
         { 10, 21 },
         { 11, 22 }};
elements = [17, 6, 6, 6, 7, 1, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
color[] CPK = new color[17];
CPK[1] = color(255, 255, 255);
CPK[7] = color(0, 0, 255);
CPK[6] = color(150, 150, 150);
CPK[17] = color(0, 255, 0);

float[] angles = {0, 0, 0}; // Angles around x,y,z axes
float[][] coords = new float[atoms.length][3];
int myredraw = 1;
float zoom = 20;
float[] dragorigin = [0, 0];
float[] anglesorigin = [0, 0];
float zoomorigin;
float[] trans = [width/2, height/2];
float[] transorigin;

void setup() {
  size(320, 240);
  noStroke();
  centremol();
  for(i=0;i<atoms.length;i++) {
    coords[i] = {atoms[i][0], atoms[i][1], atoms[i][2]};
  }
}
void centremol() {
  for(int i=0; i < atoms.length; i++) {
    atoms[i][0] = atoms[i][0];
    atoms[i][1] = atoms[i][1];
    atoms[i][2] = atoms[i][2];
  }
  float totx = 0;
  float toty = 0;
  float totz = 0;
  for(i=0; i < atoms.length; i++) {
    totx += atoms[i][0];
    toty += atoms[i][1];
    totz += atoms[i][2];
  }
  meanx = totx / atoms.length;
  meany = toty / atoms.length;
  meanz = totz / atoms.length;
  for(i=0; i < atoms.length; i++) {
    atoms[i][0] = atoms[i][0] - meanx;
    atoms[i][1] = atoms[i][1] - meany;
    atoms[i][2] = atoms[i][2] - meanz;
  }
}

void draw_atoms() {
  // A poor man's z-order sort (not worth spending much time
  // on as you can sort using a function in javascript)
  float[] depth = new float[atoms.length];
  for(int i=0;i<atoms.length;i++) depth[i] = coords[i][2];
  for(int i=0; i < atoms.length; i++) {
    max = 0;
    for(int j=0; j < atoms.length; j++) {
      if(depth[j] < depth[max]) {
        max = j;
      }
    }
    depth[max] = +9999;

    x = coords[max][0];
    y = coords[max][1];
    z = coords[max][2];
    // fill(color(0, 0, 0));
    fill(CPK[elements[max]]);

    radius = 1;
    if (elements[max]==1) radius=0.5;
    ellipse(x, y, radius, radius);
    fill(color(255, 255, 255));
    ellipse(x - 0.05, y - 0.2, 0.1, 0.1);

    fill(color(180, 180, 180, 150));
    ellipse(x, z/5 + 5, 1, 0.3);
  }
}
void mousePressed() {
  dragorigin = [mouseX, mouseY];
  // Can't just say anglesorigin = angles (doesn't make a copy)
  anglesorigin = [angles[0], angles[1], angles[2]];
  zoomorigin = zoom;
  transorigin = [trans[0], trans[1]];
}
void mouseDragged() {
  if (mouseButton == LEFT) {
    angles[0] = anglesorigin[0] + (dragorigin[1] - mouseY)/50;
    angles[1] = anglesorigin[1] + (dragorigin[0] - mouseX)/50;
  }
  else if (mouseButton == RIGHT) {
    zoom = zoomorigin + (dragorigin[1] - mouseY)/10;
    angles[2] = anglesorigin[2] + (dragorigin[0] - mouseX)/50;
  }
  else if (mouseButton == CENTER) {
    trans[0] = transorigin[0] - (dragorigin[0] - mouseX);
    trans[1] = transorigin[1] - (dragorigin[1] - mouseY);
  }
  myredraw = 1;
}
void rotateAround() {
  // Rotate around X
  float c = cos(angles[0]);
  float s = sin(angles[0]);
  for (i=0;i<atoms.length;i++) {
    coords[i][0] = atoms[i][0];
    coords[i][1] = atoms[i][1] * c - atoms[i][2] * s;
    coords[i][2] = atoms[i][1] * s + atoms[i][2] * c;
  }
  // Rotate around Y
  float c = cos(angles[1]);
  float s = sin(angles[1]);
  for (i=0;i<atoms.length;i++) {
    t = coords[i][0] * c - coords[i][2] * s;
    u = coords[i][0] * s + coords[i][2] * c;
    coords[i][0] = t;
    coords[i][2] = u;
  }
  // Rotate around Z
  float c = cos(angles[2]);
  float s = sin(angles[2]);
  for (i=0;i<atoms.length;i++) {
    t = coords[i][0] * c - coords[i][1] * s;
    u = coords[i][0] * s + coords[i][1] * c;
    coords[i][0] = t;
    coords[i][1] = u;
  }
}
void draw_bonds() {
  strokeWeight(0.1);
  for(i=0; i< bonds.length; i++) {
    start = bonds[i][0];
    end = bonds[i][1];
    line(coords[start][0], coords[start][1], coords[end][0], coords[end][1]);
  }
}
void draw() {
  if (myredraw) {
    background(226);
    //translate(width/2, height/2);
    translate(trans[0], trans[1]);
    scale(zoom);
    rotateAround();
    draw_bonds();
    draw_atoms();
    myredraw = 0;
  }
}