auspice
Version:
Web app for visualizing pathogen evolution
146 lines (135 loc) • 5.19 kB
JavaScript
import { rgb } from "d3-color";
import { interpolateRgb } from "d3-interpolate";
import { updateVisibleTipsAndBranchThicknesses} from "../../../actions/tree";
import { branchOpacityFunction } from "../../../util/colorHelpers";
import { NODE_VISIBLE } from "../../../util/globals";
import { getDomId } from "../phyloTree/helpers";
import { getTraitFromNode } from "../../../util/treeMiscHelpers";
/* Callbacks used by the tips / branches when hovered / selected */
export const onTipHover = function onTipHover(d) {
if (d.visibility !== NODE_VISIBLE) return;
const phylotree = d.that.params.orientation[0] === 1 ?
this.state.tree :
this.state.treeToo;
phylotree.svg.select(getDomId("#tip", d.n.name))
.attr("r", (e) => e["r"] + 4);
this.setState({
hovered: {d, type: ".tip"}
});
};
export const onTipClick = function onTipClick(d) {
if (d.visibility !== NODE_VISIBLE) return;
if (this.props.narrativeMode) return;
// console.log("tip click", d)
this.setState({
hovered: null,
selectedTip: d
});
/* are we clicking from tree1 or tree2? */
const tipSelected = d.that.params.orientation[0] === 1 ?
{treeIdx: d.n.arrayIdx} :
{treeTooIdx: d.n.arrayIdx};
this.props.dispatch(updateVisibleTipsAndBranchThicknesses({tipSelected, cladeSelected: this.props.tree.selectedClade}));
};
export const onBranchHover = function onBranchHover(d) {
if (d.visibility !== NODE_VISIBLE) return;
/* emphasize the color of the branch */
for (const id of [getDomId("#branchS", d.n.name), getDomId("#branchT", d.n.name)]) {
if (this.props.colorByConfidence) {
this.state.tree.svg.select(id)
.style("stroke", (el) => { // eslint-disable-line no-loop-func
const entropyValue = getTraitFromNode(this.props.tree.nodes[el.n.arrayIdx], this.props.colorBy, {entropy: true});
const ramp = branchOpacityFunction(entropyValue);
const raw = this.props.tree.nodeColors[el.n.arrayIdx];
const base = el.branchStroke;
return rgb(interpolateRgb(raw, base)(ramp)).toString();
});
} else {
this.state.tree.svg.select(id)
.style("stroke", (el) => this.props.tree.nodeColors[el.n.arrayIdx]);
}
}
if (this.props.temporalConfidence.exists && this.props.temporalConfidence.display && !this.props.temporalConfidence.on) {
const tree = d.that.params.orientation[0] === 1 ? this.state.tree : this.state.treeToo;
if (!("confidenceIntervals" in tree.groups)) {
tree.groups.confidenceIntervals = tree.svg.append("g").attr("id", "confidenceIntervals");
}
tree.groups.confidenceIntervals
.selectAll(".conf")
.data([d])
.enter()
.call((sel) => tree.drawSingleCI(sel, 0.5));
}
this.setState({
hovered: {d, type: ".branch"}
});
};
export const onBranchClick = function onBranchClick(d) {
if (d.visibility !== NODE_VISIBLE) return;
if (this.props.narrativeMode) return;
const root = [undefined, undefined];
let cladeSelected;
if (
d.n.branch_attrs &&
d.n.branch_attrs.labels !== undefined &&
d.n.branch_attrs.labels.clade !== undefined
) {
cladeSelected = d.n.branch_attrs.labels.clade;
}
if (d.that.params.orientation[0] === 1) root[0] = d.n.arrayIdx;
else root[1] = d.n.arrayIdx;
this.props.dispatch(updateVisibleTipsAndBranchThicknesses({root, cladeSelected}));
};
/* onBranchLeave called when mouse-off, i.e. anti-hover */
export const onBranchLeave = function onBranchLeave(d) {
for (const id of [getDomId("#branchT", d.n.name), getDomId("#branchS", d.n.name)]) {
this.state.tree.svg.select(id)
.style("stroke", (el) => el.branchStroke);
}
if (this.props.temporalConfidence.exists && this.props.temporalConfidence.display && !this.props.temporalConfidence.on) {
const tree = d.that.params.orientation[0] === 1 ? this.state.tree : this.state.treeToo;
tree.removeConfidence();
}
if (this.state.hovered) {
this.setState({hovered: null});
}
};
export const onTipLeave = function onTipLeave(d) {
const phylotree = d.that.params.orientation[0] === 1 ?
this.state.tree :
this.state.treeToo;
if (!this.state.selectedTip) {
phylotree.svg.select(getDomId("#tip", d.n.name))
.attr("r", (dd) => dd["r"]);
}
if (this.state.hovered) {
this.setState({hovered: null});
}
};
/* clearSelectedTip when clicking to go away */
export const clearSelectedTip = function clearSelectedTip(d) {
const phylotree = d.that.params.orientation[0] === 1 ?
this.state.tree :
this.state.treeToo;
phylotree.svg.select(getDomId("#tip", d.n.name))
.attr("r", (dd) => dd["r"]);
this.setState({selectedTip: null, hovered: null});
/* restore the tip visibility! */
this.props.dispatch(updateVisibleTipsAndBranchThicknesses(
{tipSelected: {clear: true}, cladeSelected: this.props.tree.selectedClade}
));
};
/**
* @param {node} d tree node object
* @param {int} n total number of nodes in current view
* @return {int} font size of the tip label
*/
export const tipLabelSize = (d, n) => {
if (n > 70) {
return 0;
} else if (n < 20) {
return 14;
}
const fs = 6 + 8 * (70 - n) / (70 - 20);
return fs;
};