From d9af0c5e51656d2254185097de7e700c383bbc32 Mon Sep 17 00:00:00 2001 From: Colin Megill Date: Thu, 19 Apr 2018 01:53:30 -0700 Subject: [PATCH] replace canvas with webgl scatter --- src/components/scatterplot/scatterplot.js | 144 ++++++++---------- .../scatterplot/setupScatterplot.js | 28 ++-- 2 files changed, 78 insertions(+), 94 deletions(-) diff --git a/src/components/scatterplot/scatterplot.js b/src/components/scatterplot/scatterplot.js index 5ea6ad1a..3063542d 100644 --- a/src/components/scatterplot/scatterplot.js +++ b/src/components/scatterplot/scatterplot.js @@ -27,34 +27,6 @@ import { createDimensions, } from "./util"; -const generatePoints = function (count) { - return Array(count).fill().map(function () { - return [ - Math.random() - 0.5, - Math.random() - 0.5 - ] - }) -} - -const generateColors = function (count) { - return Array(count).fill().map(function () { - return [ - Math.random(), - Math.random(), - Math.random() - ] - }) -} - -const generateSizes = function (count) { - return Array(count).fill().map(function () { - return Math.random() * 10 - }) -} - -// set constants -const count = 1283; - @connect((state) => { const ranges = state.cells.cells && state.cells.cells.data.ranges ? state.cells.cells.data.ranges : null; @@ -81,7 +53,7 @@ class Scatterplot extends React.Component { this.count = 0; this.state = { svg: null, - ctx: null, + // ctx: null, axes: null, dimensions: null, xScale: null, @@ -90,12 +62,18 @@ class Scatterplot extends React.Component { } componentDidMount() { - const {svg, ctx} = setupScatterplot( + const { + svg + // ctx + } = setupScatterplot( width, height, margin ); - this.setState({svg, ctx}) + this.setState({ + svg + // ctx + }) const camera = _camera(this.reglCanvas, {scale: true, rotate: false}); const regl = _regl(this.reglCanvas) @@ -103,9 +81,9 @@ class Scatterplot extends React.Component { const drawPoints = _drawPoints(regl) // preallocate buffers - const pointBuffer = regl.buffer(generatePoints(count)) - const colorBuffer = regl.buffer(generateColors(count)) - const sizeBuffer = regl.buffer(generateSizes(count)) + const pointBuffer = regl.buffer() + const colorBuffer = regl.buffer() + const sizeBuffer = regl.buffer() regl.frame(({viewportWidth, viewportHeight}) => { @@ -120,7 +98,7 @@ class Scatterplot extends React.Component { color: colorBuffer, position: pointBuffer, size: sizeBuffer, - count: count, + count: this.count, view: camera.view(), scale: viewportHeight / viewportWidth }) @@ -137,19 +115,50 @@ class Scatterplot extends React.Component { } componentWillReceiveProps(nextProps) { + this.maybeSetupScalesAndDrawAxes(nextProps); + + } + componentDidUpdate(prevProps) { + if ( + (this.state.xScale && this.state.yScale) && + (this.props.scatterplotXXaccessor && this.props.scatterplotYYaccessor) && + this.props.scatterplotXXaccessor !== prevProps.scatterplotXXaccessor || // was CLU now FTH1 etc + this.props.scatterplotYYaccessor !== prevProps.scatterplotYYaccessor + ) { + this.drawAxesSVG(this.state.xScale, this.state.yScale); + } + + // if ( + // this.state.xScale && + // this.state.yScale + // ) { + // drawScatterplotCanvas( + // this.state.ctx, + // this.state.xScale, + // this.state.yScale, + // this.props.currentCellSelection, + // this.props.opacityForDeselectedCells, + // this.props.expression, + // this.props.scatterplotXXaccessor, + // this.props.scatterplotYYaccessor, + // ) + // } + if ( this.state.regl && this.state.pointBuffer && this.state.colorBuffer && this.state.sizeBuffer && - nextProps.currentCellSelection && - nextProps.expression.data && - nextProps.expression.data.genes && - nextProps.scatterplotXXaccessor && - nextProps.scatterplotYYaccessor + this.props.currentCellSelection && + this.props.expression.data && + this.props.expression.data.genes && + this.props.scatterplotXXaccessor && + this.props.scatterplotYYaccessor && + this.state.xScale && + this.state.yScale ) { - const _currentCellSelectionMap = _.keyBy(nextProps.currentCellSelection, "CellName"); /* move me to the reducer */ + const _currentCellSelectionMap = _.keyBy(this.props.currentCellSelection, "CellName"); /* move me to the reducer */ const positions = []; const colors = []; @@ -161,21 +170,21 @@ class Scatterplot extends React.Component { const glScaleY = d3.scaleLinear() .domain([0, height]) - .range([-.95, .95]) + .range([-1, 1]) /* Construct Vectors */ - _.each(nextProps.expression.data.cells, (cell, i) => { + _.each(this.props.expression.data.cells, (cell, i) => { /* this if is necessary until we are no longer getting expression for all cells, but only for 'world' ...which will mean refetching when we regraph, or 'go back up to all cells' */ if (_currentCellSelectionMap[cell.cellname]) { /* fails silently, sometimes this is undefined, in which case the graph array should be shorter than the cell array, check in reducer */ positions.push([ - glScaleX(this.state.xScale(cell.e[nextProps.expression.data.genes.indexOf(nextProps.scatterplotXXaccessor)])), /* scale each point first to the window as we calculate extents separately below, so no need to repeat */ - glScaleY(this.state.yScale(cell.e[nextProps.expression.data.genes.indexOf(nextProps.scatterplotYYaccessor)])) + glScaleX(this.state.xScale(cell.e[this.props.expression.data.genes.indexOf(this.props.scatterplotXXaccessor)])), /* scale each point first to the window as we calculate extents separately below, so no need to repeat */ + glScaleY(this.state.yScale(cell.e[this.props.expression.data.genes.indexOf(this.props.scatterplotYYaccessor)])) ]) let c = _currentCellSelectionMap[cell.cellname]["__color__"]; @@ -199,13 +208,6 @@ class Scatterplot extends React.Component { } }) - console.log("+++++++++++++++++++++++++++") - console.log('RANDOM', generatePoints(count)) - console.log('------------------------------') - console.log("POINTS", positions) - console.log("+++++++++++++++++++++++++++") - - this.state.pointBuffer(positions) this.state.colorBuffer(colors) this.state.sizeBuffer(sizes) @@ -215,32 +217,6 @@ class Scatterplot extends React.Component { this.count = positions.length } } - componentDidUpdate(prevProps) { - if ( - (this.state.xScale && this.state.yScale) && - (this.props.scatterplotXXaccessor && this.props.scatterplotYYaccessor) && - this.props.scatterplotXXaccessor !== prevProps.scatterplotXXaccessor || // was CLU now FTH1 etc - this.props.scatterplotYYaccessor !== prevProps.scatterplotYYaccessor - ) { - this.drawAxesSVG(this.state.xScale, this.state.yScale); - } - - if ( - this.state.xScale && - this.state.yScale - ) { - drawScatterplotCanvas( - this.state.ctx, - this.state.xScale, - this.state.yScale, - this.props.currentCellSelection, - this.props.opacityForDeselectedCells, - this.props.expression, - this.props.scatterplotXXaccessor, - this.props.scatterplotYYaccessor, - ) - } - } maybeSetupScalesAndDrawAxes(nextProps) { if ( nextProps.expression && @@ -323,8 +299,16 @@ class Scatterplot extends React.Component { width: width + margin.left + margin.right + "px", height: height + margin.top + margin.bottom + "px", }} - > - { this.reglCanvas = canvas}}/> + > + { this.reglCanvas = canvas}}/> + ) } diff --git a/src/components/scatterplot/setupScatterplot.js b/src/components/scatterplot/setupScatterplot.js index 5f6851a5..67e7b96e 100644 --- a/src/components/scatterplot/setupScatterplot.js +++ b/src/components/scatterplot/setupScatterplot.js @@ -18,23 +18,23 @@ const setupScatterplot = ( .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - var canvas = container.append("canvas") - .attr("width", width * devicePixelRatio) - .attr("height", height * devicePixelRatio) - .style("width", width + "px") - .style("height", height + "px") - .style("margin-left", margin.left + 1 + "px") /* magic number: this seems to be visually correct when it is equal to the amount axis is translated by plus or minus... a little? */ - .style("margin-top", margin.top + "px") - - var ctx = canvas.node().getContext("2d"); - ctx.globalCompositeOperation = 'darken'; - ctx.globalAlpha = 0.15; - ctx.lineWidth = 1.5; - ctx.scale(devicePixelRatio, devicePixelRatio); + // var canvas = container.append("canvas") + // .attr("width", width * devicePixelRatio) + // .attr("height", height * devicePixelRatio) + // .style("width", width + "px") + // .style("height", height + "px") + // .style("margin-left", margin.left + 1 + "px") /* magic number: this seems to be visually correct when it is equal to the amount axis is translated by plus or minus... a little? */ + // .style("margin-top", margin.top + "px") + // + // var ctx = canvas.node().getContext("2d"); + // ctx.globalCompositeOperation = 'darken'; + // ctx.globalAlpha = 0.15; + // ctx.lineWidth = 1.5; + // ctx.scale(devicePixelRatio, devicePixelRatio); return { svg, - ctx, + // ctx, } }