Select Git revision
DrawTree.js 7.42 KiB
/*****************************************************************************/
//
// Copyright 2015 Andrew Cohen
//
// This file is part of CloneView - the tool for displaying stem cell lineaging in a webpage.
//
// CloneView is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// CloneView is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with CloneView in file "gnu gpl v3.txt". If not, see
// <http://www.gnu.org/licenses/>.
//
/*****************************************************************************/
// recursively set the x location for every node
function InitTree(tree,idx) {
if (tree[idx].LeftChild>0) {
tree=InitTree(tree,tree[idx].LeftChild);
tree=InitTree(tree,tree[idx].RightChild);
tree[idx].xLocation= (tree[tree[idx].LeftChild].xLocation + tree[tree[idx].RightChild].xLocation)/2;
tree[idx].depth=Math.max(tree[tree[idx].LeftChild].depth,tree[tree[idx].RightChild].depth)+1;
// deeper node goes to the left!
if (tree[tree[idx].LeftChild].depth<tree[tree[idx].RightChild].depth) {
var tmp = tree[idx].LeftChild;
tree[idx].LeftChild=tree[idx].RightChild;
tree[idx].RightChild=tmp;
}
}
else {
tree[idx].xLocation = tree.glastX;
tree[idx].depth=0;
tree.glastX++;
}
if (tree[idx].EndTime>tree.gmaxTime)
tree.gmaxTime=tree[idx].EndTime;
return tree;
} //InitTree
function nodeFromID(tree,id) {
var i;
if (0==id)
return 0;
for (i=0;i<tree.length;i++)
if (tree[i].TrackID==id)
return i;
console.log('nodeFromID not found!\n');
return 0;
}
function remapTreeNodes(tree) {
for (var i=0;i<tree.length;i++) {
tree[i].LeftChild = nodeFromID(tree,tree[i].LeftChild);
tree[i].RightChild = nodeFromID(tree,tree[i].RightChild);
tree[i].Parent = nodeFromID(tree,tree[i].Parent);
}
return tree;
}
function DrawTree(canvas,tree) {
var context = canvas.getContext("2d");
var sx,sy; // scale factor
tree.glastX=0.0; // x coordinate of farthest right child
tree.gmaxTime = 0.0; // y coordinate of oldest child
tree=remapTreeNodes(tree);
// run init tree twice! first time sets most prolific to the left
tree=InitTree(tree,0);
tree.glastX=0.0; // x coordinate of farthest right child
tree.gmaxTime = 0.0; // y coordinate of oldest child
// 2nd time sets x locations
tree=InitTree(tree,0);
// now draw!
sx = 0.95*canvas.width/tree.glastX;
sy = 0.95*canvas.height/tree.gmaxTime;
for (var i=0;i<tree.length;i++) {
tree[i].xLocation+=1;
tree[i].StartTime+=1;
tree[i].EndTime+=1;
}
context.strokeStyle='black';
context.beginPath();
context.fillStyle='white';
for (var i=0;i<tree.length;i++) {
context.moveTo(tree[i].xLocation*sx,tree[i].StartTime*sy);
context.lineTo(tree[i].xLocation*sx,tree[i].EndTime*sy);
// children? if so, draw horizontal
if (tree[i].LeftChild) {
context.moveTo(tree[tree[i].LeftChild].xLocation*sx,tree[tree[i].LeftChild].StartTime*sy);
context.lineTo(tree[tree[i].RightChild].xLocation*sx,tree[tree[i].RightChild].StartTime*sy );
}
if (undefined != tree[i].Phenotype) {
// 1==dead, 2,3==ambiguous >=4 actual pheno marker
if (1==tree[i].Phenotype.ID) {
// make an 'x'
var lineStep=2;
context.moveTo(tree[i].xLocation*sx-lineStep,tree[i].EndTime*sy-lineStep);
context.lineTo(tree[i].xLocation*sx+lineStep,tree[i].EndTime*sy+lineStep);
context.moveTo(tree[i].xLocation*sx+lineStep,tree[i].EndTime*sy-lineStep);
context.lineTo(tree[i].xLocation*sx-lineStep,tree[i].EndTime*sy+lineStep);
}
else if (tree[i].Phenotype.ID<4) {
// have to flush drawing q so i can fill on top for circles
context.stroke();
context.beginPath();
context.arc(tree[i].xLocation*sx,tree[i].EndTime*sy, 2, 0, 2 * Math.PI,false);
context.fill(); // draw this arc
context.stroke();
context.beginPath(); // set up next draw
}
}
}
context.stroke();
context.fill();
return tree;
} // DrawTree
function DrawTreeFluorescent(canvas,tree) {
var context = canvas.getContext("2d");
var sx,sy; // scale factor
tree.glastX=0.0; // x coordinate of farthest right child
tree.gmaxTime = 0.0; // y coordinate of oldest child
tree=remapTreeNodes(tree);
// run init tree twice! first time sets most prolific to the left
tree=InitTree(tree,0);
tree.glastX=0.0; // x coordinate of farthest right child
tree.gmaxTime = 0.0; // y coordinate of oldest child
// 2nd time sets x locations
tree=InitTree(tree,0);
tree.glastX+=1;
// now draw!
sx = 0.95*canvas.width/tree.glastX;
sy = 0.95*canvas.height/tree.gmaxTime;
for (var i=0;i<tree.length;i++) {
tree[i].xLocation+=1;
tree[i].StartTime+=1;
tree[i].EndTime+=1;
}
context.strokeStyle='LightGray';
context.lineWidth=1;
context.beginPath();
context.fillStyle='white';
for (var i=0;i<tree.length;i++) {
context.moveTo(tree[i].xLocation*sx,tree[i].StartTime*sy);
context.lineTo(tree[i].xLocation*sx,tree[i].EndTime*sy);
context.stroke();
// draw fluorescent curves
context.strokeStyle='green';
//context.lineWidth=2;
context.beginPath();
for (var f=0;f<tree[i].GreenStats.length-1;f++) {
fval1 = tree[i].GreenStats[f];
fval1 = Math.min(fval1,0.95);
fval2 = tree[i].GreenStats[f+1];
fval2 = Math.min(fval2,0.95);
context.moveTo((tree[i].xLocation+ fval1)*sx,(tree[i].StartTime+f)*sy);
context.lineTo((tree[i].xLocation+ fval2)*sx,(tree[i].StartTime+f+1)*sy);
}
context.stroke();
context.beginPath();
context.strokeStyle='red';
for (var f=0;f<tree[i].RedStats.length-1;f++) {
fval1 = tree[i].RedStats[f];
fval1 = Math.min(fval1,0.95);
fval2 = tree[i].RedStats[f+1];
fval2 = Math.min(fval2,0.95);
context.moveTo((tree[i].xLocation+ fval1)*sx,(tree[i].StartTime+f)*sy);
context.lineTo((tree[i].xLocation+ fval2)*sx,(tree[i].StartTime+f+1)*sy);
}
context.stroke();
context.lineWidth=1;
context.strokeStyle='LightGray';
context.beginPath();
// children? if so, draw horizontal
if (tree[i].LeftChild) {
context.moveTo(tree[tree[i].LeftChild].xLocation*sx,tree[tree[i].LeftChild].StartTime*sy);
context.lineTo(tree[tree[i].RightChild].xLocation*sx,tree[tree[i].RightChild].StartTime*sy );
}
if (undefined != tree[i].Phenotype) {
// 1==dead, 2,3==ambiguous >=4 actual pheno marker
if (1==tree[i].Phenotype.ID) {
// make an 'x'
var lineStep=2;
context.moveTo(tree[i].xLocation*sx-lineStep,tree[i].EndTime*sy-lineStep);
context.lineTo(tree[i].xLocation*sx+lineStep,tree[i].EndTime*sy+lineStep);
context.moveTo(tree[i].xLocation*sx+lineStep,tree[i].EndTime*sy-lineStep);
context.lineTo(tree[i].xLocation*sx-lineStep,tree[i].EndTime*sy+lineStep);
}
else if (tree[i].Phenotype.ID<4) {
// have to flush drawing q so i can fill on top for circles
context.stroke();
context.beginPath();
context.arc(tree[i].xLocation*sx,tree[i].EndTime*sy, 2, 0, 2 * Math.PI,false);
context.fill(); // draw this arc
context.stroke();
context.beginPath(); // set up next draw
}
}
}
context.stroke();
context.fill();
return tree;
} // DrawTreeFluorescent