Skip to content

Commit

Permalink
Merge pull request #414 from nextstrain/debounce-map-to-screen
Browse files Browse the repository at this point in the history
Debounce map to screen
  • Loading branch information
trvrb authored Aug 12, 2017
2 parents 093ef78 + 240ad2a commit 845159b
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 25 deletions.
3 changes: 2 additions & 1 deletion src/actions/treeProperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const updateVisibleTipsAndBranchThicknesses = function (
* @param {string|false} newMax optional
* @return {null} side-effects: a single action
*/
export const changeDateFilter = function ({newMin = false, newMax = false}) {
export const changeDateFilter = function ({newMin = false, newMax = false, quickdraw = false}) {
return (dispatch, getState) => {
const { tree, controls } = getState();
if (!tree.nodes) {return;}
Expand All @@ -67,6 +67,7 @@ export const changeDateFilter = function ({newMin = false, newMax = false}) {
const data = calculateVisiblityAndBranchThickness(tree, controls, dates);
dispatch({
type: types.CHANGE_DATES_VISIBILITY_THICKNESS,
quickdraw,
dateMin: newMin ? newMin : controls.dateMin,
dateMax: newMax ? newMax : controls.dateMax,
visibility: data.visibility,
Expand Down
17 changes: 9 additions & 8 deletions src/components/controls/date-range-inputs.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import { modifyURLquery } from "../../util/urlHelpers";
import { numericToCalendar, calendarToNumeric } from "../../util/dateHelpers";
import { changeDateFilter } from "../../actions/treeProperties";
import d3 from "d3";
import {
MAP_ANIMATION_PLAY_PAUSE_BUTTON
} from "../../actions/types.js";
import { MAP_ANIMATION_PLAY_PAUSE_BUTTON } from "../../actions/types.js";

moment.updateLocale("en", {
longDateFormat: {
Expand All @@ -34,7 +32,7 @@ class DateRangeInputs extends React.Component {
super(props);
this.state = {
lastSliderUpdateTime: Date.now()
}
};
}
static contextTypes = {
router: React.PropTypes.object.isRequired
Expand Down Expand Up @@ -84,6 +82,7 @@ class DateRangeInputs extends React.Component {
}

updateFromSlider(debounce, numDateValues) {
/* debounce: boolean. TRUE: both debounce and quickdraw.*/
this.maybeClearMapAnimationInterval()

if (debounce) {
Expand All @@ -95,22 +94,24 @@ class DateRangeInputs extends React.Component {
// console.log("UPDATING", currentTime, this.state.lastSliderUpdateTime)
this.setState({lastSliderUpdateTime: currentTime});
}

// {numDateValues} is an array of numDates received from Slider
// [numDateStart, numDateEnd]
const newRange = {min: numericToCalendar(this.props.dateFormat, this.props.dateScale, numDateValues[0]),
max: numericToCalendar(this.props.dateFormat, this.props.dateScale, numDateValues[1])};
if (this.props.dateMin !== newRange.min && this.props.dateMax === newRange.max) { // update min
this.props.dispatch(changeDateFilter({newMin: newRange.min}));
this.props.dispatch(changeDateFilter({newMin: newRange.min, quickdraw: debounce}));
modifyURLquery(this.context.router, {dmin: newRange.min}, true);
} else if (this.props.dateMin === newRange.min &&
this.props.dateMax !== newRange.max) { // update max
this.props.dispatch(changeDateFilter({newMax: newRange.max}));
this.props.dispatch(changeDateFilter({newMax: newRange.max, quickdraw: debounce}));
modifyURLquery(this.context.router, {dmax: newRange.max}, true);
} else if (this.props.dateMin !== newRange.min &&
this.props.dateMax !== newRange.max) { // update both
this.props.dispatch(changeDateFilter({newMin: newRange.min, newMax: newRange.max}));
this.props.dispatch(changeDateFilter({newMin: newRange.min, newMax: newRange.max, quickdraw: debounce}));
modifyURLquery(this.context.router, {dmin: newRange.min, dmax: newRange.max}, true);
} else if (debounce === false) {
/* this occurs when no dates have actually changed BUT we need to redraw (e.g. quickdraw has come off) */
this.props.dispatch(changeDateFilter({quickdraw: debounce}));
}
return null;
}
Expand Down
14 changes: 7 additions & 7 deletions src/components/map/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import { enableAnimationDisplay, animationWindowWidth, animationTick, twoColumnB
import computeResponsive from "../../util/computeResponsive";
import {getLatLongs} from "../../util/mapHelpersLatLong";
import { modifyURLquery } from "../../util/urlHelpers";
import {
createDemeAndTransmissionData,
updateDemeAndTransmissionDataColAndVis,
updateDemeAndTransmissionDataLatLong
import {
createDemeAndTransmissionData,
updateDemeAndTransmissionDataColAndVis,
updateDemeAndTransmissionDataLatLong
} from "../../util/mapHelpersLatLong";

import { changeDateFilter } from "../../actions/treeProperties";
Expand Down Expand Up @@ -502,7 +502,7 @@ class Map extends React.Component {
resetAnimation() {
clearInterval(window.NEXTSTRAIN.mapAnimationLoop);
window.NEXTSTRAIN.mapAnimationLoop = null;
this.props.dispatch(changeDateFilter({newMin: this.props.absoluteDateMin, newMax: this.props.absoluteDateMax}));
this.props.dispatch(changeDateFilter({newMin: this.props.absoluteDateMin, newMax: this.props.absoluteDateMax, quickdraw: false}));
this.props.dispatch({
type: MAP_ANIMATION_PLAY_PAUSE_BUTTON,
data: "Play"
Expand Down Expand Up @@ -541,7 +541,7 @@ class Map extends React.Component {
max: numericToCalendar(this.props.dateFormat, this.props.dateScale, rightWindow)};

/* first pass sets the timer to absolute min and absolute min + windowRange because they reference above initial time window */
this.props.dispatch(changeDateFilter({newMin: newWindow.min, newMax: newWindow.max}));
this.props.dispatch(changeDateFilter({newMin: newWindow.min, newMax: newWindow.max, quickdraw: true}));
// don't modifyURLquery

if (!this.props.mapAnimationCumulative) {
Expand All @@ -552,7 +552,7 @@ class Map extends React.Component {
if (rightWindow >= end) {
clearInterval(window.NEXTSTRAIN.mapAnimationLoop)
window.NEXTSTRAIN.mapAnimationLoop = null;
this.props.dispatch(changeDateFilter({newMin: this.props.absoluteDateMin, newMax: this.props.absoluteDateMax}));
this.props.dispatch(changeDateFilter({newMin: this.props.absoluteDateMin, newMax: this.props.absoluteDateMax, quickdraw: false}));
this.props.dispatch({
type: MAP_ANIMATION_PLAY_PAUSE_BUTTON,
data: "Play"
Expand Down
1 change: 1 addition & 0 deletions src/components/tree/treeView.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ there are actually backlinks from the phylotree tree
@connect((state) => {
return {
tree: state.tree,
quickdraw: state.controls.quickdraw,
browserDimensions: state.browserDimensions.browserDimensions,
// map: state.map,
splitTreeAndMap: state.controls.splitTreeAndMap,
Expand Down
14 changes: 10 additions & 4 deletions src/components/tree/treeViewFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ export const salientPropChanges = (props, nextProps, tree) => {
const branchThickness = props.tree.branchThicknessVersion !== nextProps.tree.branchThicknessVersion;
const layout = props.layout !== nextProps.layout;
const distanceMeasure = props.distanceMeasure !== nextProps.distanceMeasure;
const rerenderAllElements = nextProps.quickdraw === false && props.quickdraw === true;

/* branch labels & confidence use 0: no change, 1: turn off, 2: turn on */
const branchLabels = props.showBranchLabels === nextProps.showBranchLabels ? 0 : nextProps.showBranchLabels ? 2 : 1;
Expand Down Expand Up @@ -300,7 +301,9 @@ export const salientPropChanges = (props, nextProps, tree) => {
branchTransitionTime,
tipTransitionTime,
branchLabels,
confidence
confidence,
quickdraw: nextProps.quickdraw,
rerenderAllElements
};
};

Expand Down Expand Up @@ -336,15 +339,14 @@ export const updateStylesAndAttrs = (changes, nextProps, tree) => {
// console.log("branch width change detected - update branch stroke-widths")
branchStyleToUpdate["stroke-width"] = nextProps.tree.branchThickness;
}

/* implement style * attr changes */
if (Object.keys(branchAttrToUpdate).length || Object.keys(branchStyleToUpdate).length) {
// console.log("applying branch attr", Object.keys(branchAttrToUpdate), "branch style changes", Object.keys(branchStyleToUpdate))
tree.updateMultipleArray(".branch", branchAttrToUpdate, branchStyleToUpdate, changes.branchTransitionTime);
tree.updateMultipleArray(".branch", branchAttrToUpdate, branchStyleToUpdate, changes.branchTransitionTime, changes.quickdraw);
}
if (Object.keys(tipAttrToUpdate).length || Object.keys(tipStyleToUpdate).length) {
// console.log("applying tip attr", Object.keys(tipAttrToUpdate), "tip style changes", Object.keys(tipStyleToUpdate))
tree.updateMultipleArray(".tip", tipAttrToUpdate, tipStyleToUpdate, changes.tipTransitionTime);
tree.updateMultipleArray(".tip", tipAttrToUpdate, tipStyleToUpdate, changes.tipTransitionTime, changes.quickdraw);
}

if (changes.layout) { /* swap layouts */
Expand All @@ -370,4 +372,8 @@ export const updateStylesAndAttrs = (changes, nextProps, tree) => {
/* some updates may necessitate an updating of the CIs (e.g. ∆ branch thicknesses) */
tree.updateConfidence(changes.tipTransitionTime);
}

if (changes.rerenderAllElements) {
tree.rerenderAllElements();
}
};
3 changes: 3 additions & 0 deletions src/reducers/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const getDefaultState = function () {
filters: {},
dateScale: d3.time.scale().domain([new Date(2000, 0, 0), new Date(2100, 0, 0)]).range([2000, 2100]),
dateFormat: d3.time.format("%Y-%m-%d"),
quickdraw: false, // if true, components may skip expensive computes.
mapAnimationDurationInMilliseconds: 30000, // in milliseconds
mapAnimationStartDate: null, // Null so it can pull the absoluteDateMin as the default
mapAnimationCumulative: false,
Expand Down Expand Up @@ -221,6 +222,7 @@ const Controls = (state = getDefaultState(), action) => {
});
case types.CHANGE_DATES_VISIBILITY_THICKNESS:
return Object.assign({}, state, {
quickdraw: action.quickdraw,
dateMin: action.dateMin ? action.dateMin : state.dateMin,
dateMax: action.dateMax ? action.dateMax : state.dateMax
});
Expand All @@ -242,6 +244,7 @@ const Controls = (state = getDefaultState(), action) => {
});
case types.MAP_ANIMATION_PLAY_PAUSE_BUTTON:
return Object.assign({}, state, {
quickdraw: action.data === "Play" ? false : true,
mapAnimationPlayPauseButton: action.data
});
case types.CHANGE_ANIMATION_START:
Expand Down
30 changes: 27 additions & 3 deletions src/util/phyloTree.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import d3 from "d3";
import { dataFont, darkGrey } from "../globalStyles";
import {debounce} from "lodash";

/*
* adds the total number of descendant leaves to each node in the tree
Expand Down Expand Up @@ -118,6 +119,10 @@ var PhyloTree = function(treeJson) {
this.yScale = d3.scale.linear();
this.zoomNode = this.nodes[0];
addLeafCount(this.nodes[0]);

/* debounced functions (AFAIK you can't define these as normal prototypes as they need "this") */
this.debouncedMapToScreen = debounce(this.mapToScreen, this.params.mapToScreenDebounceTime,
{leading: false, trailing: true, maxWait: this.params.mapToScreenDebounceTime});
};

/*
Expand Down Expand Up @@ -161,6 +166,7 @@ PhyloTree.prototype.setDefaults = function () {
tipLabelFill: "#555",
tipLabelPadX: 8,
tipLabelPadY: 2,
mapToScreenDebounceTime: 500
};
};

Expand Down Expand Up @@ -1266,7 +1272,7 @@ PhyloTree.prototype.updateTimeBar = function(d){
* @param {object} styles object containing the styles to change
* @param {int} dt time in milliseconds
*/
PhyloTree.prototype.updateMultipleArray = function(treeElem, attrs, styles, dt) {
PhyloTree.prototype.updateMultipleArray = function(treeElem, attrs, styles, dt, quickdraw) {
// assign new values and decide whether to update
this.nodes.forEach(function(d, i) {
d.update = false;
Expand All @@ -1289,8 +1295,12 @@ PhyloTree.prototype.updateMultipleArray = function(treeElem, attrs, styles, dt)
}
});
let updatePath = false;
if (styles["stroke-width"]){
this.mapToScreen();
if (styles["stroke-width"]) {
if (quickdraw) {
this.debouncedMapToScreen();
} else {
this.mapToScreen();
}
updatePath = true;
}

Expand Down Expand Up @@ -1329,6 +1339,20 @@ PhyloTree.prototype.updateMultipleArray = function(treeElem, attrs, styles, dt)
}
};

/* this need a bit more work as the quickdraw functionality improves */
PhyloTree.prototype.rerenderAllElements = function () {
// console.log("rerenderAllElements")
this.mapToScreen();
this.svg.selectAll(".branch")
.transition().duration(0)
.style("stroke-width", (d) => d["stroke-width"]);
this.svg.selectAll(".branch")
.transition().duration(0)
.filter(".S")
.attr("d", (d) => d.branch[0]);
};


/**
* as updateAttributeArray, but accepts a callback function rather than an array
* with the values. will create array and call updateAttributeArray
Expand Down
4 changes: 2 additions & 2 deletions src/util/quantify-performance.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export const setUpPerf = function () {
console.log("Performance for a single animation tick:");
window.Perf.show();
}
if (window.Perf.counter === 10) {
console.log("Performance for 10 animation ticks:");
if (window.Perf.counter === 50) {
console.log("Performance for 50 animation ticks:");
window.Perf.show();
window.Perf.stop();
}
Expand Down

0 comments on commit 845159b

Please sign in to comment.