Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tree controller #492

Merged
merged 15 commits into from Mar 25, 2021
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);
ElDeveloper marked this conversation as resolved.
Show resolved Hide resolved
}

// 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))) {
kwcantrell marked this conversation as resolved.
Show resolved Hide resolved
// 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