Skip to content

Commit

Permalink
Merge pull request #1880 from nextstrain/james/improve-tree-perf
Browse files Browse the repository at this point in the history
Improve tree performance for trees with over 4000 tips
  • Loading branch information
jameshadfield authored Nov 6, 2024
2 parents f046672 + 3a18d77 commit 2ae2280
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 18 deletions.
3 changes: 2 additions & 1 deletion src/components/tree/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ const Tree = connect((state: RootState) => ({
tipLabelKey: state.controls.tipLabelKey,
narrativeMode: state.narrative.display,
animationPlayPauseButton: state.controls.animationPlayPauseButton,
showOnlyPanels: state.controls.showOnlyPanels
showOnlyPanels: state.controls.showOnlyPanels,
performanceFlags: state.controls.performanceFlags,
}))(UnconnectedTree);

export default Tree;
19 changes: 9 additions & 10 deletions src/components/tree/phyloTree/change.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,12 @@ const createUpdateCall = (treeElem, properties) => (selection) => {

const genericSelectAndModify = (svg, treeElem, updateCall, transitionTime) => {
// console.log("general svg update for", treeElem);
svg.selectAll(treeElem)
.filter((d) => d.update)
.transition().duration(transitionTime)
.call(updateCall);
if (!transitionTime) {
/* https://github.com/d3/d3-timer#timerFlush */
timerFlush();
// console.log("\t\t--FLUSHING TIMER--");
let selection = svg.selectAll(treeElem)
.filter((d) => d.update);
if (transitionTime) {
selection = selection.transition().duration(transitionTime);
}
selection.call(updateCall);
};

/* use D3 to select and modify elements, such that a given element is only ever modified _once_
Expand Down Expand Up @@ -271,7 +268,8 @@ export const change = function change({
branchThickness = undefined,
/* other data */
focus = undefined,
scatterVariables = undefined
scatterVariables = undefined,
performanceFlags = {},
}) {
// console.log("\n** phylotree.change() (time since last run:", Date.now() - this.timeLastRenderRequested, "ms) **\n\n");
timerStart("phylotree.change()");
Expand All @@ -280,10 +278,11 @@ export const change = function change({
const svgPropsToUpdate = new Set(); /* which SVG properties shall be changed. E.g. "fill", "stroke" */
const useModifySVGInStages = newLayout; /* use modifySVGInStages rather than modifySVG. Not used often. */


/* calculate dt */
const idealTransitionTime = 500;
let transitionTime = idealTransitionTime;
if ((Date.now() - this.timeLastRenderRequested) < idealTransitionTime * 2) {
if ((Date.now() - this.timeLastRenderRequested) < idealTransitionTime * 2 || performanceFlags.get("skipTreeAnimation")===true) {
transitionTime = 0;
}

Expand Down
11 changes: 5 additions & 6 deletions src/components/tree/phyloTree/labels.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { timerFlush } from "d3-timer";
import { NODE_VISIBLE } from "../../../util/globals";
import { numericToDateObject, prettifyDate } from "../../../util/dateHelpers";
import { getTraitFromNode } from "../../../util/treeMiscHelpers";
Expand Down Expand Up @@ -108,16 +107,16 @@ export const updateBranchLabels = function updateBranchLabels(dt) {
);
const labelSize = branchLabelSize(this.params.branchLabelKey);
const fontWeight = branchLabelFontWeight(this.params.branchLabelKey);
this.groups.branchLabels
let selection = this.groups.branchLabels
.selectAll(".branchLabel")
.transition()
.duration(dt)
.attr("x", (d) => d.xTip - 5)
if (dt) {
selection = selection.transition().duration(dt);
}
selection.attr("x", (d) => d.xTip - 5)
.attr("y", (d) => d.yTip - this.params.branchLabelPadY)
.style("visibility", visibility)
.style("font-weight", fontWeight)
.style("font-size", labelSize);
if (!dt) timerFlush();
};

export const removeBranchLabels = function removeBranchLabels() {
Expand Down
1 change: 1 addition & 0 deletions src/components/tree/reactD3Interface/change.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export const changePhyloTreeViaPropsComparison = (mainTree, phylotree, oldProps,
const change = Object.keys(args).length;
if (change) {
args.animationInProgress = newProps.animationPlayPauseButton === "Pause";
args.performanceFlags = newProps.performanceFlags;
// console.log('\n\n** ', phylotree.id, 'changePhyloTreeViaPropsComparison **', args);
phylotree.change(args);
}
Expand Down
28 changes: 28 additions & 0 deletions src/middleware/performanceFlags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as types from "../actions/types";

/**
* Performance flags (reduxState.controls.performanceFlags) represent flags
* for which we enable/disable certain functionality in Auspice. These flags
* shouldn't be depended on, i.e. Auspice should work just fine without them
* (but may be a little slow).
*/


export const performanceFlags = (_store) => (next) => (action) => {
let modifiedAction;
switch (action.type) {
case types.URL_QUERY_CHANGE_WITH_COMPUTED_STATE: /* fallthrough */
case types.CLEAN_START: {
modifiedAction = {...action};
modifiedAction.controls.performanceFlags = calculate(action)
}
}
return next(modifiedAction || action); // send action to other middleware / reducers
};

function calculate({tree}) {
const flags = new Map();
const totalTipCount = tree?.nodes?.[0]?.fullTipCount;
flags.set("skipTreeAnimation", totalTipCount > 4000);
return flags;
}
3 changes: 2 additions & 1 deletion src/reducers/controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ export const getDefaultControlsState = () => {
measurementsDisplay: undefined,
measurementsShowOverallMean: undefined,
measurementsShowThreshold: undefined,
measurementsFilters: {}
measurementsFilters: {},
performanceFlags: new Map(),
};
};

Expand Down
2 changes: 2 additions & 0 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { changeURLMiddleware } from "./middleware/changeURL";
import rootReducer from "./reducers";
// import { loggingMiddleware } from "./middleware/logActions";
import { keepScatterplotStateInSync } from "./middleware/scatterplot";
import { performanceFlags } from "./middleware/performanceFlags";

const middleware = [
keepScatterplotStateInSync,
changeURLMiddleware,
performanceFlags,
// loggingMiddleware
];

Expand Down

0 comments on commit 2ae2280

Please sign in to comment.