graph brush selection

This commit is contained in:
Colin Megill
2017-11-16 20:04:58 -08:00
parent de42ec3da3
commit 382cc45035
3 changed files with 134 additions and 26 deletions
+80 -21
View File
@@ -20,29 +20,35 @@ const y = d3.scaleLinear()
*******************************************
******************************************/
export const setupGraphElements = (data) => {
export const setupGraphElements = (
handleBrushSelectAction,
handleBrushDeselectAction
) => {
var canvas = d3.select("#graphAttachPoint")
.append("canvas")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("class", `${styles.graphCanvas}`);
var ctx = canvas.node().getContext("2d");
var svg = d3.select("#graphAttachPoint").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("class", `${styles.graphSVG}`)
.append("g")
.attr("transform", "translate(" + margin.left + " " + margin.top + ")");
// .append("g")
// .attr("transform", "translate(" + margin.left + " " + margin.top + ")");
var chartArea = d3.select("#graphAttachPoint").append("div")
.style("left", margin.left + "px")
.style("top", margin.top + "px");
var canvas = chartArea.append("canvas")
.attr("width", width)
.attr("height", height)
.attr("class", `${styles.graphCanvas}`);
var context = canvas.node().getContext("2d");
setupGraphBrush(
svg,
handleBrushSelectAction,
handleBrushDeselectAction
);
return {
svg,
context
ctx
}
}
@@ -61,6 +67,7 @@ export const drawGraph = (
ranges,
metadata,
continuousSelection,
graphBrushSelection,
) => {
/* clear canvas */
@@ -100,9 +107,48 @@ export const drawGraph = (
0, /* sAngle */
2 * Math.PI /* eAngle */
);
let pointIsInsideBrushBounds;
if (graphBrushSelection) {
pointIsInsideBrushBounds = (
x(p[1]) >= graphBrushSelection.northwestX &&
x(p[1]) <= graphBrushSelection.southeastX &&
y(p[2]) >= graphBrushSelection.northwestY &&
y(p[2]) <= graphBrushSelection.southeastY
);
// if (i === 5) {
// console.log(
// pointIsInsideBrushBounds,
// x(p[1]),
// y(p[2]),
// graphBrushSelection,
// );
// }
}
if (pointIsInsideBrushBounds) {
console.log('inside', x(p[1]), y(p[2]), graphBrushSelection)
context.fillStyle = "rgb(255,0,0)";
} else {
context.fillStyle = "rgb(0,0,0)"
}
// if (color) {
// if (i === 10) {
// console.log(p[0], x(p[1]), y(p[2]), _.find(metadata, {CellName: p[0]}))
// }
// context.fillStyle = d3.interpolateViridis(colorScale(
// _.find(metadata, {CellName: p[0]})[color] /* this.state.cells.metadata["23452345325"]["ERCC_reads"] = 20000 would be much faster as key value lookup */
// ));
// context.strokeStyle = "red";
// }
context.fill();
if (i < 20) {
// if (i < 20) {
// console.log(
// '0 to 1 scale', expressionToColorScale(expressionsCountsMap[p[0]]),
// 'color', d3.interpolateViridis(expressionToColorScale(
@@ -110,14 +156,27 @@ export const drawGraph = (
// ))
// )
// console.log('meta', _.find(metadata, {CellName: p[0]}), color, p)
}
// }
// context.fillStyle = d3.interpolateViridis(expressionToColorScale(
// expressionsCountsMap[p[0]]
// ));
if (color) {
context.fillStyle = d3.interpolateViridis(colorScale(
_.find(metadata, {CellName: p[0]})[color] /* this.state.cells.metadata["23452345325"]["ERCC_reads"] = 20000 would be much faster as key value lookup */
));
}
});
}
const setupGraphBrush = (
svg,
handleBrushSelectAction,
handleBrushDeselectAction
) => {
svg.append("g")
.call(
d3.brush()
.extent([
[0, 0],
[width, height]
])
.on("brush", handleBrushSelectAction)
.on("end", handleBrushDeselectAction)
);
}
+49 -5
View File
@@ -18,6 +18,7 @@ import ColorControl from "../controls/color";
metadata,
color: state.controls.color,
continuousSelection: state.controls.continuousSelection,
graphBrushSelection: state.controls.graphBrushSelection
}
})
class Graph extends React.Component {
@@ -27,33 +28,76 @@ class Graph extends React.Component {
this.state = {
drawn: false,
svg: null,
context: null,
ctx: null,
brush: null,
};
}
handleBrushSelectAction() {
/*
No idea why d3 event scope works like this
but apparently
it does
https://bl.ocks.org/EfratVil/0e542f5fc426065dd1d4b6daaa345a9f
*/
const s = d3.event.selection;
/*
event describing brush position:
@-------|
| |
| |
|-------@
*/
const brushCoords = {
northwestX: s[0][0],
northwestY: s[0][1],
southeastX: s[1][0],
southeastY: s[1][1]
}
brushCoords.dx = brushCoords.southeastX - brushCoords.northwestX;
brushCoords.dy = brushCoords.southeastY - brushCoords.northwestY;
this.props.dispatch({
type: "graph brush selection change",
brushCoords
})
}
handleBrushDeselectAction() {
// function brushended() {
// if (!d3.event.selection) {
// console.log('graph brush deselect')
// }
// }
console.log('handle deselect')
}
componentWillReceiveProps(nextProps) {
/* maybe should do a check here to confirm ref exists and pass it? */
if (
this.state.context &&
this.state.ctx &&
nextProps.vertices
// nextProps.expressions &&
// nextProps.expressionsCountsMap &&
) {
drawGraph(
nextProps.vertices,
this.state.context,
this.state.ctx,
nextProps.expressionsCountsMap,
nextProps.color,
nextProps.ranges, /* assumption that this exists if vertices does both are on cells */
nextProps.metadata,
nextProps.continuousSelection, /* continuousSelected should probably inform a global 'is active' array rather than be consumed so specifically here */
nextProps.graphBrushSelection,
)
}
}
componentDidMount() {
const {svg, context} = setupGraphElements();
this.setState({svg, context});
const {svg, ctx} = setupGraphElements(
this.handleBrushSelectAction.bind(this),
this.handleBrushDeselectAction.bind(this)
);
this.setState({svg, ctx});
}
render() {
+5
View File
@@ -1,6 +1,7 @@
const Controls = (state = {
color: null,
continuousSelection: null,
graphBrushSelection: null
}, action) => {
switch (action.type) {
case "color changed":
@@ -12,6 +13,10 @@ const Controls = (state = {
continuousSelection: action.data,
});
}
case "graph brush selection change":
return Object.assign({}, state, {
graphBrushSelection: action.brushCoords
})
default:
return state;
}