Skip to content

Commit

Permalink
Tree controller (#492)
Browse files Browse the repository at this point in the history
* bp shear

* style fix

* checkpoint

* checkpoint

* Finished TreeController

* tree-controller

* fix doc issue

* style fix

* Apply suggestions from code review

merged @ElDeveloper's suggestions

Co-authored-by: Yoshiki Vázquez Baeza <yoshiki@ucsd.edu>

* fix style

* addressed comments

* removed shear

* renamed function

Co-authored-by: Yoshiki Vázquez Baeza <yoshiki@ucsd.edu>
  • Loading branch information
kwcantrell and ElDeveloper committed Mar 25, 2021
1 parent 33ade9c commit 1939236
Show file tree
Hide file tree
Showing 7 changed files with 1,002 additions and 58 deletions.
25 changes: 19 additions & 6 deletions empress/support_files/js/bp-tree.js
Expand Up @@ -643,7 +643,7 @@ define(["ByteArray", "underscore"], function (ByteArray, _) {
*/
BPTree.prototype.inOrderNodes = function () {
if (this._inorder !== null) {
return this._inorder;
return _.clone(this._inorder);
}

// the root node of the tree
Expand All @@ -658,7 +658,7 @@ define(["ByteArray", "underscore"], function (ByteArray, _) {
// append children to stack
nodeStack = nodeStack.concat(this.getChildren(curNode));
}
return this._inorder;
return _.clone(this._inorder);
};

/**
Expand Down Expand Up @@ -958,7 +958,7 @@ define(["ByteArray", "underscore"], function (ByteArray, _) {
*/
BPTree.prototype.getNodesWithName = function (name) {
if (name in this._nameToNodes) {
return this._nameToNodes[name];
return _.clone(this._nameToNodes[name]);
}

this._nameToNodes[name] = [];
Expand All @@ -968,7 +968,7 @@ define(["ByteArray", "underscore"], function (ByteArray, _) {
}
}

return this._nameToNodes[name];
return _.clone(this._nameToNodes[name]);
};

/**
Expand All @@ -980,7 +980,9 @@ define(["ByteArray", "underscore"], function (ByteArray, _) {
*
* @param {Set} keepTips The set of tip names to keep.
*
* @return {BPTree} The new BPTree.
* @return {Object} An object containing the new tree ("tree") and two maps that
* convert the original postorder positions to the sheared
* tree postorder positions ("newToOld") and vice-versa ("oldToNew").
*/
BPTree.prototype.shear = function (keepTips) {
// closure
Expand Down Expand Up @@ -1022,19 +1024,30 @@ define(["ByteArray", "underscore"], function (ByteArray, _) {
}

var newBitArray = [];
var shearedToFull = new Map();
var fullToSheared = new Map();
var postorderPos = 1;
for (i = 0; i < mask.length; i++) {
if (mask[i] !== undefined) {
newBitArray.push(mask[i]);
}

// get name and length of node
// Note: names and lengths of nodes are stored in postorder

if (mask[i] === 0) {
names.push(this.name(i));
lengths.push(this.length(i));
shearedToFull.set(postorderPos, this.postorder(i));
fullToSheared.set(this.postorder(i), postorderPos);
postorderPos += 1;
}
}
return new BPTree(newBitArray, names, lengths, null);
return {
shearedToFull: shearedToFull,
fullToSheared: fullToSheared,
tree: new BPTree(newBitArray, names, lengths, null),
};
};

return BPTree;
Expand Down
87 changes: 45 additions & 42 deletions empress/support_files/js/empress.js
Expand Up @@ -11,6 +11,7 @@ define([
"chroma",
"LayoutsUtil",
"ExportUtil",
"TreeController",
], function (
_,
Camera,
Expand All @@ -23,7 +24,8 @@ define([
util,
chroma,
LayoutsUtil,
ExportUtil
ExportUtil,
TreeController
) {
/**
* @class EmpressTree
Expand Down Expand Up @@ -86,7 +88,7 @@ define([
* The phylogenetic balance parenthesis tree
* @private
*/
this._tree = tree;
this._tree = new TreeController(tree);

/**
* Used to index into _treeData
Expand Down Expand Up @@ -375,21 +377,22 @@ define([
* Also updates this._maxDisplacement.
*/
Empress.prototype.getLayoutInfo = function () {
var data, i;
var data,
i,
j = 1;
// set up length getter
var branchMethod = this.branchMethod;
var checkLengthsChange = LayoutsUtil.shouldCheckBranchLengthsChanged(
branchMethod
);
var lengthGetter = LayoutsUtil.getLengthMethod(
branchMethod,
this._tree
this._tree.getTree()
);

// Rectangular
if (this._currentLayout === "Rectangular") {
data = LayoutsUtil.rectangularLayout(
this._tree,
this._tree.getTree(),
4020,
4020,
// since lengths for "ignoreLengths" are set by `lengthGetter`,
Expand All @@ -404,61 +407,64 @@ define([
checkLengthsChange
);
this._yrscf = data.yScalingFactor;
for (i = 1; i <= this._tree.size; i++) {
for (i of this._tree.postorderTraversal((includeRoot = true))) {
// remove old layout information
this._treeData[i].length = this._numOfNonLayoutParams;

// store new layout information
this._treeData[i][this._tdToInd.xr] = data.xCoord[i];
this._treeData[i][this._tdToInd.yr] = data.yCoord[i];
this._treeData[i][this._tdToInd.xr] = data.xCoord[j];
this._treeData[i][this._tdToInd.yr] = data.yCoord[j];
this._treeData[i][this._tdToInd.highestchildyr] =
data.highestChildYr[i];
data.highestChildYr[j];
this._treeData[i][this._tdToInd.lowestchildyr] =
data.lowestChildYr[i];
data.lowestChildYr[j];
j += 1;
}
} else if (this._currentLayout === "Circular") {
data = LayoutsUtil.circularLayout(
this._tree,
this._tree.getTree(),
4020,
4020,
this.leafSorting,
undefined,
lengthGetter,
checkLengthsChange
);
for (i = 1; i <= this._tree.size; i++) {
for (i of this._tree.postorderTraversal((includeRoot = true))) {
// remove old layout information
this._treeData[i].length = this._numOfNonLayoutParams;

// store new layout information
this._treeData[i][this._tdToInd.xc0] = data.x0[i];
this._treeData[i][this._tdToInd.yc0] = data.y0[i];
this._treeData[i][this._tdToInd.xc1] = data.x1[i];
this._treeData[i][this._tdToInd.yc1] = data.y1[i];
this._treeData[i][this._tdToInd.angle] = data.angle[i];
this._treeData[i][this._tdToInd.arcx0] = data.arcx0[i];
this._treeData[i][this._tdToInd.arcy0] = data.arcy0[i];
this._treeData[i][this._tdToInd.xc0] = data.x0[j];
this._treeData[i][this._tdToInd.yc0] = data.y0[j];
this._treeData[i][this._tdToInd.xc1] = data.x1[j];
this._treeData[i][this._tdToInd.yc1] = data.y1[j];
this._treeData[i][this._tdToInd.angle] = data.angle[j];
this._treeData[i][this._tdToInd.arcx0] = data.arcx0[j];
this._treeData[i][this._tdToInd.arcy0] = data.arcy0[j];
this._treeData[i][this._tdToInd.arcstartangle] =
data.arcStartAngle[i];
data.arcStartAngle[j];
this._treeData[i][this._tdToInd.arcendangle] =
data.arcEndAngle[i];
data.arcEndAngle[j];
j += 1;
}
} else {
data = LayoutsUtil.unrootedLayout(
this._tree,
this._tree.getTree(),
4020,
4020,
undefined,
lengthGetter,
checkLengthsChange
);
for (i = 1; i <= this._tree.size; i++) {
for (i of this._tree.postorderTraversal((includeRoot = true))) {
// remove old layout information
this._treeData[i].length = this._numOfNonLayoutParams;

// store new layout information
this._treeData[i][this._tdToInd.x2] = data.xCoord[i];
this._treeData[i][this._tdToInd.y2] = data.yCoord[i];
this._treeData[i][this._tdToInd.x2] = data.xCoord[j];
this._treeData[i][this._tdToInd.y2] = data.yCoord[j];
j += 1;
}
}
this._drawer.loadTreeCoordsBuff(this.getTreeCoords());
Expand Down Expand Up @@ -595,7 +601,7 @@ define([
);
}
// iterate through the tree in postorder, skip root
for (var node = 1; node < tree.size; node++) {
for (var node of this._tree.postorderTraversal()) {
// name of current node
// var node = this._treeData[node];
var parent = tree.postorder(
Expand Down Expand Up @@ -724,7 +730,7 @@ define([
addPoint();
}
// iterate through the tree in postorder, skip root
for (var node = 1; node < tree.size; node++) {
for (var node of this._tree.postorderTraversal()) {
if (!this.getNodeInfo(node, "visible")) {
continue;
}
Expand Down Expand Up @@ -891,7 +897,7 @@ define([
throw new Error("getNodeCoords() drawNodeCircles is out of range");
}

for (var node = 1; node <= tree.size; node++) {
for (var node of this._tree.postorderTraversal((includeRoot = true))) {
if (!comp(node)) {
continue;
}
Expand Down Expand Up @@ -1232,7 +1238,7 @@ define([
this._addThickVerticalLineCoords(coords, tree.size, lwScaled);
}
// iterate through the tree in postorder, skip root
for (var node = 1; node < this._tree.size; node++) {
for (var node of this._tree.postorderTraversal()) {
// name of current node
var parent = tree.postorder(
tree.parent(tree.postorderselect(node))
Expand Down Expand Up @@ -1448,7 +1454,7 @@ define([
this._maxDisplacement = null;
return;
}
for (var node = 1; node < this._tree.size; node++) {
for (var node of this._tree.postorderTraversal()) {
if (this._tree.isleaf(this._tree.postorderselect(node))) {
maxD = this[compFunc](node, maxD);
}
Expand Down Expand Up @@ -1936,7 +1942,7 @@ define([
} else {
halfAngleRange = Math.PI / this._tree.numleaves();
}
for (node = 1; node < this._tree.size; node++) {
for (var node of this._tree.postorderTraversal()) {
if (this._tree.isleaf(this._tree.postorderselect(node))) {
var name = this.getNodeInfo(node, "name");
var fm;
Expand Down Expand Up @@ -2069,7 +2075,7 @@ define([
// For the circular layout, how to speed this up is less clear -- I
// suspect it should be possible using WebGL and some fancy
// trigonometry somehow, but I'm not sure.
for (var node = 1; node < this._tree.size; node++) {
for (var node of this._tree.postorderTraversal()) {
if (this._tree.isleaf(this._tree.postorderselect(node))) {
if (this._currentLayout === "Rectangular") {
var y = this.getY(node);
Expand Down Expand Up @@ -2376,7 +2382,7 @@ define([
if (!ignoreAbsentTips) {
// find "non-represented" tips
// Note: the following uses postorder traversal
for (i = 1; i < tree.size; i++) {
for (i of this._tree.postorderTraversal()) {
if (tree.isleaf(tree.postorderselect(i))) {
var represented = false;
for (j = 0; j < categories.length; j++) {
Expand All @@ -2396,7 +2402,7 @@ define([
// root (at index tree.size) in this loop, we iterate over all its
// descendants; so in the event that all leaves are unique,
// the root can still get assigned to a group.
for (i = 1; i < tree.size; i++) {
for (i of this._tree.postorderTraversal()) {
var node = i;
var parent = tree.postorder(tree.parent(tree.postorderselect(i)));

Expand Down Expand Up @@ -2676,7 +2682,7 @@ define([
var x = 0,
y = 0,
zoomAmount = 0;
for (var node = 1; node <= this._tree.size; node++) {
for (var node of this._tree.postorderTraversal((includeRoot = true))) {
// node = this._treeData[node];
x += this.getX(node);
y += this.getY(node);
Expand Down Expand Up @@ -2767,7 +2773,7 @@ define([
this._collapsedClades = {};
// Note: currently collapseClades is the only method that set
// the node visibility property.
for (var i = 1; i <= this._tree.size; i++) {
for (var i of this._tree.postorderTraversal((includeRoot = true))) {
this.setNodeInfo(i, "visible", true);
}

Expand Down Expand Up @@ -2807,7 +2813,7 @@ define([
// was not called. Thus, this loop is used to guarantee that if an
// internal node belongs to a group then all of its descendants belong
// to the same group.
for (var i = 1; i <= this._tree.size; i++) {
for (var i of this._tree.postorderTraversal()) {
var parent = this._tree.postorder(
this._tree.parent(this._tree.postorderselect(i))
);
Expand All @@ -2823,10 +2829,7 @@ define([
// collaped.
// Collapsing a clade will set the .visible property of members to
// false and will then be skipped in the for loop.
var inorder = this._tree.inOrderNodes();
for (var node in inorder) {
node = inorder[node];

for (var node of this._tree.inOrderTraversal()) {
// dont collapse clade
if (this._dontCollapse.has(node)) {
continue;
Expand Down

0 comments on commit 1939236

Please sign in to comment.