Source: protein.js

/**
 * Represents an aminoacid model.
 * @constructor
 * @param {float} x - The aminoacid's x position.
 * @param {float} y - The aminoacid's y position.
 * @param {Protein} p - The protein that contains the aminoacid chain.
 * @param {Protein} i - The position of the aminoacid in the primary chain.
 */
var Amino = function Amino(x, y, p, i){;
  this.x = x;
  this.y = y;
  //this.protein = p;
  this.i = i;
  this.radius = 5; //pixels
}
/**
 * Represents a protein model.
 * @constructor
 * @param {Object} obj - Protein options {[seq, ang || coord]}.
 * @param {String} obj.seq - String with the aminoacid's type sequence.
 * @param {Array} obj.ang - The angle sequence in radians.
 * @param {Array} obj.coord - The aminocids coordinates (x, y).
 */
var Protein = function(obj){
  this.aminos = [];
  
  if(!obj.seq) obj.seq = fibonacci(this.length);  
  this.seq = obj.seq;
  
  if(obj.ang) {
    obj.coord = angToCoord(obj.ang);
    this.length = obj.ang.length;
    
  } else if(obj.coord) {
    obj.ang = coordToAng(obj.coord);
    this.length = obj.coord.length;
  }

  for(var i = 0; i < this.length; ++i){
    this.aminos[i] = new Amino(obj.coord[i].x, obj.coord[i].y, this, i);
    this.aminos[i].ang = obj.ang[i];
    this.aminos[i].seq = obj.seq[i];
    if(obj.seq[i] == 'A') this.aminos[i].fill = '#ddd';
    else this.aminos[i].fill = '#444';
  }
  this.energy = energy(this);
}
/**
 * Return an array with the angle sequence.
 * @param {int} n - [Optional] The float precision (Number of digits after the decimal point).
 */
Protein.prototype.getAngle = function(n){
  var a = [];
  for(var i = 0; i < this.length; ++i){
    if(!n) a[i] = parseFloat(this.aminos[i].ang);
    else a[i] = parseFloat(this.aminos[i].ang).toFixed(n);
  }
  return a;
}
/**
 * Return an string with the aminoacid's type sequence.
 */
Protein.prototype.getSeq = function(){
  var a = [];
  for(var i = 0; i < this.length; ++i){
    a[i] = this.aminos[i].seq;
  }
  return a;
}
/**
 * Return an array with the aminocids coordinates (x, y).
 */
Protein.prototype.getCoord = function(){
  var a = [];
  for(var i = 0; i < this.length; ++i){
    a[i] = [this.aminos[i].x, this.aminos[i].y];
  }
  return a;
}
/**
 * Render the protein in a canvas.
 * @param {Context} ctx - The canvas context in witch the protein will be rendered.
 */
Protein.prototype.render = function(ctx){
  var context = ctx || this.context;
  if(!context) {
    var canvas = createCanvas(1000, 500);
        canvas.scale = 20;
        canvas.offset = 200;
    context = canvas.getContext('2d');    
    this.context = context;
  }
  var ox, oy, canvas = context.canvas;
  context.fillStyle = 'rgba(255,255,255,0.75)';
  context.fillRect(0,0,canvas.width, canvas.height);
  
  for(var i = 0; i < this.length; ++i){
    //CONNECTION STROKE
    if(i != 0){
      context.beginPath();
      context.moveTo(ox * canvas.scale + canvas.offset, 
                     oy * canvas.scale + canvas.offset)
      context.lineTo(this.aminos[i].x * canvas.scale + canvas.offset, 
                     this.aminos[i].y * canvas.scale + canvas.offset);
      context.closePath();
      context.strokeStyle = this.aminos[i].stroke || 'black';
      context.stroke();
    }
    ox = this.aminos[i].x;
    oy = this.aminos[i].y;
  }
  
  for(var i = 0; i < this.length; ++i){
    //AMINO CIRCLE
    context.beginPath();
    context.arc(
      this.aminos[i].x * canvas.scale + canvas.offset, 
      this.aminos[i].y * canvas.scale + canvas.offset, 
      this.aminos[i].radius,
      0, Math.PI*2, 0); //start angle, end angle, cc
    context.closePath();
    context.strokeStyle = this.aminos[i].stroke || 'black';
    context.stroke();
    context.fillStyle = this.aminos[i].fill || 'black';
    context.fill();
  }
  context.fillStyle = 'black';
  context.fillText(this.energy, 10, 20);
}