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

Global tree properties #212

Merged
merged 9 commits into from
Jun 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/moving-pictures/empress-tree-tandem.qzv
Binary file not shown.
Binary file modified docs/moving-pictures/empress-tree.qzv
Binary file not shown.
59 changes: 11 additions & 48 deletions empress/support_files/js/canvas-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,29 +123,9 @@ define(["glMatrix", "SelectedNodeMenu"], function (gl, SelectedNodeMenu) {
var center = canvas.offsetWidth / 2;
var mX = e.clientX - center;
var mY = center - e.clientY;
var curPos = gl.vec4.fromValues(mX, mY, 0, 1);

// move tree
var transVec = gl.vec3.create();
gl.vec3.sub(transVec, transVec, curPos);
var transMat = gl.mat4.create();
gl.mat4.fromTranslation(transMat, transVec);
gl.mat4.multiply(drawer.worldMat, transMat, drawer.worldMat);

// zoom tree
var zoomBy = e.deltaY < 0 ? drawer.scaleBy : 1 / drawer.scaleBy;
var zoomVec = gl.vec3.fromValues(zoomBy, zoomBy, zoomBy);
var zoomMat = gl.mat4.create();
gl.mat4.fromScaling(zoomMat, zoomVec);
gl.mat4.multiply(drawer.worldMat, zoomMat, drawer.worldMat);

// move tree back to original place
transVec = gl.vec3.fromValues(curPos[0], curPos[1], curPos[2]);
transMat = gl.mat4.create();
gl.mat4.fromTranslation(transMat, transVec);
gl.mat4.multiply(drawer.worldMat, transMat, drawer.worldMat);

// draw tree
// zoom tree centered at curPos
drawer.zoom(mX, mY, e.deltaY < 0);
drawer.draw();

// update the node selection menu
Expand Down Expand Up @@ -349,46 +329,29 @@ define(["glMatrix", "SelectedNodeMenu"], function (gl, SelectedNodeMenu) {
nodeId,
moveTree = true
) {
var empress = this.empress;
var selectedNodeMenu = this.selectedNodeMenu;
var drawer = this.drawer;

// multiple nodes can have the same name
var idList = empress._nameToKeys[nodeId];
var idList = this.empress._nameToKeys[nodeId];

if (idList !== undefined) {
// get first node
var node = empress._treeData[idList[0]];
var node = this.empress._treeData[idList[0]];

if (idList.length > 1) {
node = empress._treeData[empress._tree.size - 1];
node = this.empress._treeData[this.empress._tree.size - 1];
}

if (moveTree) {
// create matrix to translate node to center of screen
var center = gl.vec3.fromValues(
drawer.treeSpaceCenterX,
drawer.treeSpaceCenterY,
1
);
var centerMat = gl.mat4.create();
gl.mat4.fromTranslation(centerMat, center);

var nodePos = gl.vec3.fromValues(
-1 * empress.getX(node),
-1 * empress.getY(node),
1
this.drawer.centerCameraOn(
this.empress.getX(node),
this.empress.getY(node)
);
var worldMat = gl.mat4.create();
gl.mat4.fromTranslation(drawer.worldMat, nodePos);
gl.mat4.multiply(drawer.worldMat, drawer.worldMat, centerMat);
}

// show menu
selectedNodeMenu.setSelectedNodes(idList);
selectedNodeMenu.showNodeMenu();
this.selectedNodeMenu.setSelectedNodes(idList);
this.selectedNodeMenu.showNodeMenu();

empress.drawTree();
this.empress.drawTree();
} else {
this.quickSearchBar.classList.add("invalid-search");
}
Expand Down
96 changes: 88 additions & 8 deletions empress/support_files/js/drawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ define(["glMatrix", "Camera"], function (gl, Camera) {

// the dimension of the canvas
this.dim = null;

this.showTreeNodes = true;
}

/**
Expand Down Expand Up @@ -286,6 +288,20 @@ define(["glMatrix", "Camera"], function (gl, Camera) {
this.fillBufferData_(this.sProg_.nodeVertBuff, data);
};

/**
* Display the tree nodes.
kwcantrell marked this conversation as resolved.
Show resolved Hide resolved
* Note: Currently Empress will only display the nodes that had an assigned
* name in the newick string. (I.E. Empress will not show any node that
* starts with EmpressNode)
*
* Note: this will only take effect after draw() is called.
*
* @param{Boolean} showTreeNodes If true the empress with display the tree
* nodes.
*/
Drawer.prototype.setTreeNodeVisibility = function (showTreeNodes) {
this.showTreeNodes = showTreeNodes;
};
/**
* Draws tree and other metadata
*/
Expand All @@ -305,11 +321,13 @@ define(["glMatrix", "Camera"], function (gl, Camera) {
// set the mvp attribute
c.uniformMatrix4fv(s.mvpMat, false, mvp);

// draw tree nodes
c.uniform1i(s.isSingle, 1);
c.uniform1f(s.pointSize, 4.0);
this.bindBuffer(s.nodeVertBuff);
c.drawArrays(c.POINTS, 0, this.nodeSize);
// draw tree node circles, if requested
if (this.showTreeNodes) {
c.uniform1i(s.isSingle, 1);
c.uniform1f(s.pointSize, 4.0);
this.bindBuffer(s.nodeVertBuff);
c.drawArrays(c.POINTS, 0, this.nodeSize);
}
fedarko marked this conversation as resolved.
Show resolved Hide resolved

// draw selected node
c.uniform1f(s.pointSize, 9.0);
Expand Down Expand Up @@ -380,12 +398,74 @@ define(["glMatrix", "Camera"], function (gl, Camera) {
* @private
*/
Drawer.prototype._findViewingCenter = function () {
var width = this.treeContainer.offsetWidth;
var height = this.treeContainer.offsetHeight;
var center = this.toTreeCoords(width / 2, height / 2);
var width = this.treeContainer.offsetWidth,
height = this.treeContainer.offsetHeight,
center = this.toTreeCoords(width / 2, height / 2);
this.treeSpaceCenterX = center.x;
this.treeSpaceCenterY = center.y;
};

/**
* Centers the viewing window on the point (x, y).
*
* Note: This function will reset all other camera options (such as zoom).
*
*
* @param{Number} x The x position
* @param{Number} y The y position
*/
Drawer.prototype.centerCameraOn = function (x, y) {
// create matrix to translate node to center of screen
var center = gl.vec3.fromValues(
this.treeSpaceCenterX,
this.treeSpaceCenterY,
0
);

var centerMat = gl.mat4.create();
gl.mat4.fromTranslation(centerMat, center);

var nodePos = gl.vec3.fromValues(-x, -y, 0);

var worldMat = gl.mat4.create();
gl.mat4.fromTranslation(worldMat, nodePos);
gl.mat4.multiply(this.worldMat, centerMat, worldMat);
};

/**
* Zooms the viewing window in or out.
*
* @param{Number} x The x position to center the zoom operation on.
* @param{Number} y The y position to center the zoom operation on.
* @param{Boolean} zoomIn If true, the camera will zoom in. If false, the
* camera will zoom out.
* Note: if zoomIn=false then the camera will zoom
* out by 1 / zoomAmount.
* @param{Number} zoomAmount The amout to zoom in or out.
*/
Drawer.prototype.zoom = function (x, y, zoomIn, zoomAmount = this.scaleBy) {
var zoomAt = gl.vec4.fromValues(x, y, 0, 1);
// move tree
var transVec = gl.vec3.create();
gl.vec3.sub(transVec, transVec, zoomAt);
var transMat = gl.mat4.create();
gl.mat4.fromTranslation(transMat, transVec);
gl.mat4.multiply(this.worldMat, transMat, this.worldMat);

// zoom tree
if (!zoomIn) zoomAmount = 1 / zoomAmount;

var zoomVec = gl.vec3.fromValues(zoomAmount, zoomAmount, zoomAmount),
zoomMat = gl.mat4.create();
gl.mat4.fromScaling(zoomMat, zoomVec);
gl.mat4.multiply(this.worldMat, zoomMat, this.worldMat);

// move tree back to original place
transVec = gl.vec3.fromValues(zoomAt[0], zoomAt[1], zoomAt[2]);
transMat = gl.mat4.create();
gl.mat4.fromTranslation(transMat, transVec);
gl.mat4.multiply(this.worldMat, transMat, this.worldMat);
};

return Drawer;
});
78 changes: 74 additions & 4 deletions empress/support_files/js/empress.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ define([
this._defaultLayout = defaultLayout;
this._currentLayout = defaultLayout;

/**
* type {Object}
* Maps tree layouts to the average point of each layout
*/
this.layoutAvgPoint = {};
/**
* @type{Number}
* The line width used for drawing "thick" lines.
Expand All @@ -174,13 +179,12 @@ define([
*/
Empress.prototype.initialize = function () {
this._drawer.initialize();
this._drawer.loadNodeBuff(this.getNodeCoords());
this.drawTree();
this._events.setMouseEvents();
var nodeNames = Object.keys(this._nameToKeys);
nodeNames = nodeNames.filter((n) => !n.startsWith("EmpressNode"));
nodeNames.sort();
this._events.autocomplete(nodeNames);
this.centerLayoutAvgPoint();
};

/**
Expand Down Expand Up @@ -1034,8 +1038,9 @@ define([
// The - 1 mimics the behavior of SidePanel._updateSample()
this.thickenSameSampleLines(this._currentLineWidth - 1);
}
this._drawer.loadNodeBuff(this.getNodeCoords());
this.drawTree();
// this._drawer.loadNodeBuff(this.getNodeCoords());
// this.drawTree();
this.centerLayoutAvgPoint();
} else {
// This should never happen under normal circumstances (the
// input to this function should always be an existing layout
Expand Down Expand Up @@ -1092,5 +1097,70 @@ define([
return this._featureMetadataColumns;
};

/**
* Display the tree nodes.
* Note: Currently Empress will only display the nodes that had an assigned
* name in the newick string. (I.E. Empress will not show any node that
* starts with EmpressNode)
fedarko marked this conversation as resolved.
Show resolved Hide resolved
*
* @param{Boolean} showTreeNodes If true then empress will display the tree
* nodes.
*/
Empress.prototype.setTreeNodeVisibility = function (showTreeNodes) {
this._drawer.setTreeNodeVisibility(showTreeNodes);
this.drawTree();
};

/**
* Centers the viewing window at the average of the current layout.
*/
Empress.prototype.centerLayoutAvgPoint = function () {
if (!(this._currentLayout in this.layoutAvgPoint)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this helps defer the work until it is actually needed and it caches the results. Very useful!

// Add up x and y coordinates of all nodes in the tree (using
// current layout).
var x = 0,
y = 0,
zoomAmount = 0,
node;
for (var i = 1; i <= this._tree.size; i++) {
node = this._treeData[i];
x += this.getX(node);
y += this.getY(node);
zoomAmount = Math.max(
zoomAmount,
Math.abs(this.getX(node)),
Math.abs(this.getY(node))
);
}

// each layout's avegerage point is define as followed:
// [x, y, zoomAmount] where x is the average of all x coordinates,
// y is the average of all y coordinates, and zoomAmount takes the
// largest x or y coordinate and normaizes it by dim / 2 (where
// dim is the dimension of the canvas).
// Note: zoomAmount is defined be a simple heuristic that should
// allow the majority of the tree to be visible in the viewing
// window.
this.layoutAvgPoint[this._currentLayout] = [
x / this._tree.size,
y / this._tree.size,
(2 * zoomAmount) / this._drawer.dim,
];
}

// center the viewing window on the average point of the current layout
// and zoom out so the majority of the tree is visible.
var cX = this.layoutAvgPoint[this._currentLayout][0],
cY = this.layoutAvgPoint[this._currentLayout][1];
this._drawer.centerCameraOn(cX, cY);
this._drawer.zoom(
this._drawer.treeSpaceCenterX,
this._drawer.treeSpaceCenterY,
false,
this.layoutAvgPoint[this._currentLayout][2]
);
this.drawTree();
};

return Empress;
});
1 change: 1 addition & 0 deletions empress/support_files/js/select-node-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ define(["underscore", "util"], function (_, util) {
this.fmTable.classList.add("hidden");
this.fmTable.innerHTML = "";
this.drawer.loadSelectedNodeBuff([]);
this.empress.drawTree();
fedarko marked this conversation as resolved.
Show resolved Hide resolved
};

/**
Expand Down
21 changes: 21 additions & 0 deletions empress/support_files/js/side-panel-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ define(["underscore", "Colorer"], function (_, Colorer) {

this.legend = legend;

// tree properties components
this.treeNodesChk = document.getElementById("display-nodes-chk");
this.recenterBtn = document.getElementById("center-tree-btn");

// sample GUI components
this.sChk = document.getElementById("sample-chk");
this.sSel = document.getElementById("sample-options");
Expand Down Expand Up @@ -346,6 +350,23 @@ define(["underscore", "Colorer"], function (_, Colorer) {
};
};

/**
* Add the callback events for the global tree properties tab. The callback
* events include things like centering the tree and showing tree nodes.
*
* Other things such as changing the defualt color of the tree will be
* added.
*/
SidePanel.prototype.addTreePropertiesTab = function () {
var scope = this;
this.treeNodesChk.onchange = function () {
scope.empress.setTreeNodeVisibility(scope.treeNodesChk.checked);
};
this.recenterBtn.onclick = function () {
scope.empress.centerLayoutAvgPoint();
};
fedarko marked this conversation as resolved.
Show resolved Hide resolved
};

SidePanel.prototype.updateFeatureMethodDesc = function () {
if (this.fMethodChk.checked) {
this.fMethodDesc.textContent =
Expand Down
Loading