From 156fb5c10065ac14584751bc1a7bc4204524a869 Mon Sep 17 00:00:00 2001 From: bkmartinjr Date: Mon, 14 May 2018 11:56:44 -0700 Subject: [PATCH 1/3] performance - improve caching in histogram and graph --- src/components/continuous/histogramBrush.js | 66 +++++++++++++------ src/components/graph/graph.js | 51 +++++++------- .../updateCellSelectionMiddleware.js | 5 +- 3 files changed, 77 insertions(+), 45 deletions(-) diff --git a/src/components/continuous/histogramBrush.js b/src/components/continuous/histogramBrush.js index c33a22782..492412530 100644 --- a/src/components/continuous/histogramBrush.js +++ b/src/components/continuous/histogramBrush.js @@ -47,6 +47,46 @@ class HistogramBrush extends React.Component { } componentDidMount() {} componentDidUpdate() {} + + calcHistogramCache(nextProps) { + // recalculate expensive stuff + const allValuesForContinuousFieldAsArray = _.map( + nextProps.currentCellSelection, + nextProps.metadataField + ); + + this.histogramCache = {}; + + this.histogramCache.x = d3 + .scaleLinear() + .domain([nextProps.ranges.min, nextProps.ranges.max]) + .range([0, this.width]); + + this.histogramCache.y = d3 + .scaleLinear() + .range([this.height - this.marginBottom, 0]); + // .range([height - margin.bottom, margin.top]); + + this.histogramCache.bins = d3 + .histogram() + .domain(this.histogramCache.x.domain()) + .thresholds(40)(allValuesForContinuousFieldAsArray); + + this.histogramCache.numValues = allValuesForContinuousFieldAsArray.length; + } + + componentWillMount() { + this.calcHistogramCache(this.props); + } + componentWillReceiveProps(nextProps) { + if ( + this.props.metadataField !== nextProps.metadataField || + !this.histogramCache + ) { + this.calcHistogramCache(nextProps); + } + } + onBrush(selection, x) { return () => { if (d3.event.selection) { @@ -65,24 +105,10 @@ class HistogramBrush extends React.Component { }; } drawHistogram(svgRef) { - const allValuesForContinuousFieldAsArray = _.map( - this.props.currentCellSelection, - this.props.metadataField - ); - - var x = d3 - .scaleLinear() - .domain(d3.extent(allValuesForContinuousFieldAsArray, d => +d)) - .range([0, this.width]); - // .range([margin.left, width - margin.right]); - - var y = d3.scaleLinear().range([this.height - this.marginBottom, 0]); - // .range([height - margin.bottom, margin.top]); - - const bins = d3 - .histogram() - .domain(x.domain()) - .thresholds(40)(allValuesForContinuousFieldAsArray); + const x = this.histogramCache.x; + const y = this.histogramCache.y; + const bins = this.histogramCache.bins; + const numValues = this.histogramCache.numValues; d3 .select(svgRef) @@ -96,13 +122,13 @@ class HistogramBrush extends React.Component { return x(d.x0) + 1; }) .attr("y", function(d) { - return y(d.length / allValuesForContinuousFieldAsArray.length); + return y(d.length / numValues); }) .attr("width", function(d) { return Math.abs(x(d.x1) - x(d.x0) - 1); }) .attr("height", function(d) { - return y(0) - y(d.length / allValuesForContinuousFieldAsArray.length); + return y(0) - y(d.length / numValues); }); if (!this.state.brush && !this.state.axis) { diff --git a/src/components/graph/graph.js b/src/components/graph/graph.js index 7fafc2008..67aae6033 100644 --- a/src/components/graph/graph.js +++ b/src/components/graph/graph.js @@ -95,7 +95,7 @@ class Graph extends React.Component { scale: viewportHeight / viewportWidth }); - this.setState({camera}) + this.setState({ camera }); camera.tick(); }); @@ -111,35 +111,43 @@ class Graph extends React.Component { if (this.state.regl && nextProps.vertices) { const vertices = nextProps.currentCellSelection; const vertexCount = vertices.length; - const positions = new Float32Array(2 * vertexCount); const colors = new Float32Array(3 * vertexCount); const sizes = new Float32Array(vertexCount); - // d3.scaleLinear().domain([0,1]).range([-1,1]) - const glScaleX = scaleLinear([0, 1], [-1, 1]); - // d3.scaleLinear().domain([0,1]).range([1,-1]) - const glScaleY = scaleLinear([0, 1], [1, -1]); + // Cache a scaled graph + if (!this.scaledGraphVec || this.props.graphVec != nextProps.graphVec) { + const positions = new Float32Array(2 * vertexCount); + + // d3.scaleLinear().domain([0,1]).range([-1,1]) + const glScaleX = scaleLinear([0, 1], [-1, 1]); + // d3.scaleLinear().domain([0,1]).range([1,-1]) + const glScaleY = scaleLinear([0, 1], [1, -1]); + + const graphVec = nextProps.graphVec; + for (var i = 0; i < vertexCount; i++) { + const cell = vertices[i]; + const cellIdx = cell.__cellIndex__; + + const x = glScaleX(graphVec[2 * cellIdx]); + const y = glScaleY(graphVec[2 * cellIdx + 1]); + positions[2 * i] = x; + positions[2 * i + 1] = y; + } + this.scaledGraphVec = positions; + } /* - Construct Vectors + Construct Size & Color Vectors */ - const graphVec = nextProps.graphVec; for (var i = 0; i < vertexCount; i++) { const cell = vertices[i]; - const cellIdx = cell.__cellIndex__; - const x = glScaleX(graphVec[2 * cellIdx]); - const y = glScaleY(graphVec[2 * cellIdx + 1]); - positions[2 * i] = x; - positions[2 * i + 1] = y; - colors.set(cell.__colorRGB__, 3 * i); - sizes[i] = cell.__selected__ ? 4 : 0.2; /* make this a function of the number of total cells, including regraph */ } - this.state.pointBuffer({ data: positions, dimension: 2 }); + this.state.pointBuffer({ data: this.scaledGraphVec, dimension: 2 }); this.state.colorBuffer({ data: colors, dimension: 3 }); this.state.sizeBuffer({ data: sizes, dimension: 1 }); this.count = vertexCount; @@ -165,7 +173,7 @@ class Graph extends React.Component { const inverse = mat4.invert([], this.state.camera.view()); // transform screen coordinates -> cell coordinates - const invert = (pin) => { + const invert = pin => { const x = 2 * pin[0] / globals.graphWidth - 1; const y = 2 * (1 - pin[1] / globals.graphHeight) - 1; const pout = [x + inverse[12], y + inverse[13]]; @@ -198,10 +206,7 @@ class Graph extends React.Component { render() { return ( -
+
-
+
{ - metadata has cellname and index, and that's all we ever need to reference cell info */ let newSelection = s.controls.currentCellSelection.slice(0); - _.forEach(newSelection, cell => (cell.__selected__ = true)); + // _.forEach(newSelection, cell => (cell.__selected__ = true)); + for (let i = 0; i < newSelection.length; i++) { + newSelection[i].__selected__ = true; + } /* in plain language... From fe655135e446963632f07aead8cb9ec5c50160d4 Mon Sep 17 00:00:00 2001 From: bkmartinjr Date: Mon, 14 May 2018 15:57:39 -0700 Subject: [PATCH 2/3] fix incorrect inequality check in caching code --- src/components/graph/graph.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/graph/graph.js b/src/components/graph/graph.js index 67aae6033..7d8ca50e4 100644 --- a/src/components/graph/graph.js +++ b/src/components/graph/graph.js @@ -115,7 +115,7 @@ class Graph extends React.Component { const sizes = new Float32Array(vertexCount); // Cache a scaled graph - if (!this.scaledGraphVec || this.props.graphVec != nextProps.graphVec) { + if (!this.scaledGraphVec || this.props.graphVec !== nextProps.graphVec) { const positions = new Float32Array(2 * vertexCount); // d3.scaleLinear().domain([0,1]).range([-1,1]) From 1e88764132092a94ddd9c7ccd9b74915795f7fdb Mon Sep 17 00:00:00 2001 From: bkmartinjr Date: Mon, 14 May 2018 16:04:31 -0700 Subject: [PATCH 3/3] move initializer to constructor --- src/components/continuous/histogramBrush.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/continuous/histogramBrush.js b/src/components/continuous/histogramBrush.js index 492412530..4ac59c34b 100644 --- a/src/components/continuous/histogramBrush.js +++ b/src/components/continuous/histogramBrush.js @@ -36,6 +36,7 @@ class HistogramBrush extends React.Component { this.width = 300; this.height = 100; this.marginBottom = 20; + this.histogramCache = {}; this.state = { svg: null, @@ -55,8 +56,6 @@ class HistogramBrush extends React.Component { nextProps.metadataField ); - this.histogramCache = {}; - this.histogramCache.x = d3 .scaleLinear() .domain([nextProps.ranges.min, nextProps.ranges.max]) @@ -81,7 +80,7 @@ class HistogramBrush extends React.Component { componentWillReceiveProps(nextProps) { if ( this.props.metadataField !== nextProps.metadataField || - !this.histogramCache + !this.histogramCache.x ) { this.calcHistogramCache(nextProps); }