Skip to content

Commit

Permalink
Merge pull request #612 from nextstrain/tree-render-order
Browse files Browse the repository at this point in the history
Tree render order
  • Loading branch information
jameshadfield authored Aug 5, 2018
2 parents 78f737a + 71d4644 commit 7176134
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 129 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ rules:
no-unneeded-ternary: ["error", { "defaultAssignment": true }]
quote-props: ["error", "as-needed"]
prefer-const: ["error", {"destructuring": "all"}]
indent: ["error", 2, {"MemberExpression": "off"}]
parserOptions:
ecmaVersion: 6
sourceType: module
Expand Down
10 changes: 2 additions & 8 deletions src/components/tree/phyloTree/change.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ const svgSetters = {
".vaccineCross": {
d: (d) => d.vaccineCross
},
".vaccineDottedLine": {
d: (d) => d.vaccineLine
},
".conf": {
d: (d) => d.confLine
}
Expand All @@ -53,9 +50,6 @@ const svgSetters = {
"stroke": (d) => d.tipStroke,
"visibility": (d) => d["visibility"]
},
".vaccineDottedLine": {
opacity: (d) => d.that.distance === "num_date" ? 1 : 0
},
".conf": {
"stroke": (d) => d.branchStroke,
"stroke-width": calcConfidenceWidth
Expand Down Expand Up @@ -159,7 +153,7 @@ export const modifySVG = function modifySVG(elemsToUpdate, svgPropsToUpdate, tra
else this.hideGrid();
}
if (elemsToUpdate.has('.regression')) {
this.svg.selectAll(".regression").remove();
this.removeRegression();
if (this.layout === "clock" && this.distance === "num_date") this.drawRegression();
}

Expand All @@ -179,7 +173,7 @@ export const modifySVG = function modifySVG(elemsToUpdate, svgPropsToUpdate, tra

/* branch labels */
if (extras.newBranchLabellingKey) {
this.svg.selectAll('.branchLabel').remove();
this.removeBranchLabels();
if (extras.newBranchLabellingKey !== "none") {
this.drawBranchLabels(extras.newBranchLabellingKey);
}
Expand Down
26 changes: 17 additions & 9 deletions src/components/tree/phyloTree/confidence.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@

export const removeConfidence = function removeConfidence(dt) {
this.confidencesInSVG = false;
if (!("confidenceIntervals" in this.groups)) return;

if (dt) {
this.svg.selectAll(".conf")
this.groups.confidenceIntervals
.selectAll("*")
.transition().duration(dt)
.style("opacity", 0)
.remove();
.style("opacity", 0)
.remove();
} else {
this.svg.selectAll(".conf").remove();
this.groups.confidenceIntervals.selectAll("*").remove();
}
};

export const drawConfidence = function drawConfidence(dt) {
this.confidencesInSVG = true;
if (!("confidenceIntervals" in this.groups)) {
this.groups.confidenceIntervals = this.svg.append("g").attr("id", "confidenceIntervals");
}
if (dt) {
this.confidence = this.svg.append("g").selectAll(".conf")
this.groups.confidenceIntervals
.selectAll(".conf")
.data(this.nodes)
.enter()
.call((sel) => this.drawSingleCI(sel, 0));
this.svg.selectAll(".conf")
this.groups.confidenceIntervals
.transition().duration(dt)
.style("opacity", 0.5);
.style("opacity", 0.5);
} else {
this.confidence = this.svg.append("g").selectAll(".conf")
this.groups.confidenceIntervals
.selectAll(".conf")
.data(this.nodes)
.enter()
.call((sel) => this.drawSingleCI(sel, 0.5));
.call((sel) => this.drawSingleCI(sel, 0.5));
}
};

Expand Down
95 changes: 59 additions & 36 deletions src/components/tree/phyloTree/grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ import { min, max } from "d3-array";
import { timerStart, timerEnd } from "../../../util/perf";

export const hideGrid = function hideGrid() {
this.svg.selectAll(".majorGrid").style('visibility', 'hidden');
this.svg.selectAll(".minorGrid").style('visibility', 'hidden');
this.svg.selectAll(".gridTick").style('visibility', 'hidden');
if ("majorGrid" in this.groups) {
this.groups.majorGrid.selectAll("*").style('visibility', 'hidden');
}
if ("minorGrid" in this.groups) {
this.groups.minorGrid.selectAll("*").style('visibility', 'hidden');
}
if ("gridText" in this.groups) {
this.groups.gridText.selectAll("*").style('visibility', 'hidden');
}
};

const calculateMajorGridSeperation = (range) => {
Expand Down Expand Up @@ -144,46 +150,63 @@ export const addGrid = function addGrid(layout) {
/* D3 commands to add grid + text to the DOM */

// add major grid to svg
const majorGrid = this.svg.selectAll('.majorGrid').data(majorGridPoints);
majorGrid.exit().remove(); // EXIT
majorGrid.enter().append("path") // ENTER
.merge(majorGrid) // ENTER + UPDATE
.attr("d", gridline(this.xScale, this.yScale, layout))
.attr("class", "majorGrid")
.style("fill", "none")
.style("visibility", (d) => d[1])
.style("stroke", this.params.majorGridStroke)
.style("stroke-width", this.params.majorGridWidth);
if (!("majorGrid" in this.groups)) {
this.groups.majorGrid = this.svg.append("g").attr("id", "majorGrid");
}
this.groups.majorGrid.selectAll("*").remove();
this.groups.majorGrid
.selectAll('.majorGrid')
.data(majorGridPoints)
.enter()
.append("path")
.attr("d", gridline(this.xScale, this.yScale, layout))
.attr("class", "majorGrid")
.style("fill", "none")
.style("visibility", (d) => d[1])
.style("stroke", this.params.majorGridStroke)
.style("stroke-width", this.params.majorGridWidth);

// add minor grid to SVG
const minorGrid = this.svg.selectAll('.minorGrid').data(minorGridPoints);
minorGrid.exit().remove(); // EXIT
minorGrid.enter().append("path") // ENTER
.merge(minorGrid) // ENTER + UPDATE
.attr("d", gridline(this.xScale, this.yScale, layout))
.attr("class", "minorGrid")
.style("fill", "none")
.style("visibility", (d) => d[1])
.style("stroke", this.params.minorGridStroke)
.style("stroke-width", this.params.minorGridWidth);
if (!("minorGrid" in this.groups)) {
this.groups.minorGrid = this.svg.append("g").attr("id", "minorGrid");
}
this.groups.minorGrid.selectAll("*").remove();
this.svg.selectAll(".minorGrid").remove();
this.groups.minorGrid
.selectAll('.minorGrid')
.data(minorGridPoints)
.enter()
.append("path")
.attr("d", gridline(this.xScale, this.yScale, layout))
.attr("class", "minorGrid")
.style("fill", "none")
.style("visibility", (d) => d[1])
.style("stroke", this.params.minorGridStroke)
.style("stroke-width", this.params.minorGridWidth);


/* draw the text labels for majorGridPoints */
const gridLabels = this.svg.selectAll('.gridTick').data(majorGridPoints);
const precisionX = Math.max(0, -Math.floor(Math.log10(step)));
const precisionY = Math.max(0, -Math.floor(Math.log10(yStep)));
gridLabels.exit().remove(); // EXIT
gridLabels.enter().append("text") // ENTER
.merge(gridLabels) // ENTER + UPDATE
.text((d) => d[0].toFixed(d[2]==='y' ? precisionY : precisionX))
.attr("class", "gridTick")
.style("font-size", this.params.tickLabelSize)
.style("font-family", this.params.fontFamily)
.style("fill", this.params.tickLabelFill)
.style("text-anchor", textAnchor(layout))
.style("visibility", (d) => d[1])
.attr("x", xTextPos(this.xScale, layout))
.attr("y", yTextPos(this.yScale, layout));
if (!("gridText" in this.groups)) {
this.groups.gridText = this.svg.append("g").attr("id", "gridText");
}
this.groups.gridText.selectAll("*").remove();
this.svg.selectAll(".gridText").remove();
this.groups.gridText
.selectAll('.gridText')
.data(majorGridPoints)
.enter()
.append("text")
.text((d) => d[0].toFixed(d[2]==='y' ? precisionY : precisionX))
.attr("class", "gridText")
.style("font-size", this.params.tickLabelSize)
.style("font-family", this.params.fontFamily)
.style("fill", this.params.tickLabelFill)
.style("text-anchor", textAnchor(layout))
.style("visibility", (d) => d[1])
.attr("x", xTextPos(this.xScale, layout))
.attr("y", yTextPos(this.yScale, layout));

this.grid=true;
timerEnd("addGrid");
Expand Down
28 changes: 23 additions & 5 deletions src/components/tree/phyloTree/labels.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { timerFlush } from "d3-timer";

export const updateTipLabels = function updateTipLabels(dt) {
this.svg.selectAll('.tipLabel').remove();
if ("tipLabels" in this.groups) {
this.groups.tipLabels.selectAll("*").remove();
} else {
this.groups.tipLabels = this.svg.append("g").attr("id", "tipLabels");
}

const tLFunc = this.callbacks.tipLabel;
const xPad = this.params.tipLabelPadX;
const yPad = this.params.tipLabelPadY;
Expand All @@ -20,7 +25,8 @@ export const updateTipLabels = function updateTipLabels(dt) {
}

window.setTimeout(() => {
this.tipLabels = this.svg.append("g").selectAll('.tipLabel')
this.groups.tipLabels
.selectAll('.tipLabel')
.data(inViewTerminalNodes)
.enter()
.append("text")
Expand Down Expand Up @@ -79,7 +85,8 @@ export const updateBranchLabels = function updateBranchLabels(dt) {
const visibility = createBranchLabelVisibility(this.params.branchLabelKey, this.layout, this.zoomNode.n.tipCount);
const labelSize = branchLabelSize(this.params.branchLabelKey);
const fontWeight = branchLabelFontWeight(this.params.branchLabelKey);
this.svg.selectAll('.branchLabel')
this.groups.branchLabels
.selectAll('.branchLabel')
.transition().duration(dt)
.attr("x", (d) => d.xTip - 5)
.attr("y", (d) => d.yTip - this.params.branchLabelPadY)
Expand All @@ -89,18 +96,29 @@ export const updateBranchLabels = function updateBranchLabels(dt) {
if (!dt) timerFlush();
};

export const removeBranchLabels = function removeBranchLabels() {
if ("branchLabels" in this.groups) {
this.groups.branchLabels.selectAll("*").remove();
}
};

export const drawBranchLabels = function drawBranchLabels(key) {
/* salient props: this.zoomNode.n.tipCount, this.zoomNode.n.fullTipCount */
this.params.branchLabelKey = key;
const labelSize = branchLabelSize(key);
const fontWeight = branchLabelFontWeight(key);
const visibility = createBranchLabelVisibility(key, this.layout, this.zoomNode.n.tipCount);
this.svg.append("g").selectAll('.branchLabel')

if (!("branchLabels" in this.groups)) {
this.groups.branchLabels = this.svg.append("g").attr("id", "branchLabels");
}
this.groups.branchLabels
.selectAll('.branchLabel')
.data(this.nodes.filter((d) => d.n.attr.labels && d.n.attr.labels[key]))
.enter()
.append("text")
.attr("class", "branchLabel")
.attr("x", (d) => d.xTip + ((this.params.orientation[0]>0)?-5:5) )
.attr("x", (d) => d.xTip + ((this.params.orientation[0]>0)?-5:5))
.attr("y", (d) => d.yTip - this.params.branchLabelPadY)
.style("text-anchor", (this.params.orientation[0]>0)?"end":"start")
.style("visibility", visibility)
Expand Down
1 change: 0 additions & 1 deletion src/components/tree/phyloTree/layouts.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,6 @@ export const mapToScreen = function mapToScreen() {
const xTipCross = this.xScale(d.xCross); /* x position of the center of the cross */
const yTipCross = this.yScale(d.yCross); /* x position of the center of the cross */
d.vaccineCross = ` M ${xTipCross-n},${yTipCross-n} L ${xTipCross+n},${yTipCross+n} M ${xTipCross-n},${yTipCross+n} L ${xTipCross+n},${yTipCross-n}`;
d.vaccineLine = ` M ${d.xTip},${d.yTip} L ${xTipCross},${yTipCross}`;
});
}

Expand Down
5 changes: 5 additions & 0 deletions src/components/tree/phyloTree/phyloTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const PhyloTree = function PhyloTree(reduxNodes, debugId) {
this.grid = false;
this.attributes = ['r', 'cx', 'cy', 'id', 'class', 'd'];
this.params = createDefaultParams();
this.groups = {};
/* by storing DOM <g> elements, we can quickly refer to groups here rather than scanning the DOM.
It also helps preserve the initial order of groups in the DOM as we are not creating new ones upon updates */
this.debugId = debugId; /* super useful when one is trying to debug multiple trees! */
/* create this.nodes, which is an array of nodes with properties used by phylotree for drawing.
this.nodes is the same length as reduxNodes such that this.nodes[i] is related to reduxNodes[i]
Expand Down Expand Up @@ -61,6 +64,7 @@ PhyloTree.prototype.drawTips = renderers.drawTips;
PhyloTree.prototype.drawBranches = renderers.drawBranches;
PhyloTree.prototype.drawVaccines = renderers.drawVaccines;
PhyloTree.prototype.drawRegression = renderers.drawRegression;
PhyloTree.prototype.removeRegression = renderers.removeRegression;

/* C A L C U L A T E G E O M E T R I E S E T C ( M O D I F I E S N O D E S , N O T S V G ) */
PhyloTree.prototype.setDistance = layouts.setDistance;
Expand All @@ -80,6 +84,7 @@ PhyloTree.prototype.updateConfidence = confidence.updateConfidence;

/* L A B E L S ( T I P , B R A N C H , C O N F I D E N C E ) */
PhyloTree.prototype.drawBranchLabels = labels.drawBranchLabels;
PhyloTree.prototype.removeBranchLabels = labels.removeBranchLabels;
PhyloTree.prototype.updateBranchLabels = labels.updateBranchLabels;
PhyloTree.prototype.updateTipLabels = labels.updateTipLabels;

Expand Down
Loading

0 comments on commit 7176134

Please sign in to comment.