mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-09 00:38:12 +08:00
Viewport fills entire screen (#430)
* full pane webgl and svg * fixes for full pane selection and centering * force graph remount, clear brush state
This commit is contained in:
@@ -297,6 +297,9 @@ const resetInterface = () => (dispatch, getState) => {
|
|||||||
type: "reset World to eq Universe",
|
type: "reset World to eq Universe",
|
||||||
universe
|
universe
|
||||||
});
|
});
|
||||||
|
dispatch({
|
||||||
|
type: "increment graph render counter"
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import actions from "../actions";
|
|||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
loading: state.controls.loading,
|
loading: state.controls.loading,
|
||||||
error: state.controls.error
|
error: state.controls.error,
|
||||||
|
graphRenderCounter: state.controls.graphRenderCounter
|
||||||
}))
|
}))
|
||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -54,7 +55,7 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading } = this.props;
|
const { loading, error, graphRenderCounter } = this.props;
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Helmet title="cellxgene" />
|
<Helmet title="cellxgene" />
|
||||||
@@ -79,10 +80,8 @@ class App extends React.Component {
|
|||||||
marginLeft: 350 /* but responsive */
|
marginLeft: 350 /* but responsive */
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? null : <Graph />}
|
{loading ? null : <Graph key={graphRenderCounter} />}
|
||||||
|
|
||||||
<Legend />
|
<Legend />
|
||||||
{}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function(regl) {
|
|||||||
uniforms: {
|
uniforms: {
|
||||||
distance: regl.prop("distance"),
|
distance: regl.prop("distance"),
|
||||||
view: regl.prop("view"),
|
view: regl.prop("view"),
|
||||||
projection: () => mat4.perspective([], Math.PI / 2, 1, 0.01, 1000)
|
projection: ({viewportWidth, viewportHeight}) => mat4.perspective([], Math.PI / 2, viewportWidth / viewportHeight, 0.01, 1000)
|
||||||
},
|
},
|
||||||
|
|
||||||
count: regl.prop("count"),
|
count: regl.prop("count"),
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import { connect } from "react-redux";
|
|||||||
import mat4 from "gl-mat4";
|
import mat4 from "gl-mat4";
|
||||||
import _regl from "regl";
|
import _regl from "regl";
|
||||||
import { Button, AnchorButton, Tooltip } from "@blueprintjs/core";
|
import { Button, AnchorButton, Tooltip } from "@blueprintjs/core";
|
||||||
import { worldEqUniverse } from "../../util/stateManager/world";
|
import * as globals from "../../globals";
|
||||||
|
|
||||||
import setupSVGandBrushElements from "./setupSVGandBrush";
|
import setupSVGandBrushElements from "./setupSVGandBrush";
|
||||||
import actions from "../../actions";
|
import actions from "../../actions";
|
||||||
import _camera from "../../util/camera";
|
import _camera from "../../util/camera";
|
||||||
@@ -30,9 +29,9 @@ class Graph extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.count = 0;
|
this.count = 0;
|
||||||
this.inverse = mat4.identity([]);
|
this.inverse = mat4.identity([]);
|
||||||
this.graphPaddingTop = 100;
|
this.graphPaddingTop = 0;
|
||||||
this.graphPaddingBottom = 45;
|
this.graphPaddingBottom = 45;
|
||||||
this.graphPaddingRight = 10;
|
this.graphPaddingRight = globals.leftSidebarWidth;
|
||||||
this.renderCache = {
|
this.renderCache = {
|
||||||
positions: null,
|
positions: null,
|
||||||
colors: null
|
colors: null
|
||||||
@@ -126,18 +125,24 @@ class Graph extends React.Component {
|
|||||||
const glScaleX = scaleLinear([0, 1], [-1, 1]);
|
const glScaleX = scaleLinear([0, 1], [-1, 1]);
|
||||||
const glScaleY = scaleLinear([0, 1], [1, -1]);
|
const glScaleY = scaleLinear([0, 1], [1, -1]);
|
||||||
|
|
||||||
|
const offset = [d3.mean(obsLayout.X) - 0.5, d3.mean(obsLayout.Y) - 0.5];
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let i = 0, { positions } = this.renderCache;
|
let i = 0, { positions } = this.renderCache;
|
||||||
i < cellCount;
|
i < cellCount;
|
||||||
i += 1
|
i += 1
|
||||||
) {
|
) {
|
||||||
positions[2 * i] = glScaleX(obsLayout.X[i]);
|
positions[2 * i] = glScaleX(obsLayout.X[i] - offset[0]);
|
||||||
positions[2 * i + 1] = glScaleY(obsLayout.Y[i]);
|
positions[2 * i + 1] = glScaleY(obsLayout.Y[i] - offset[1]);
|
||||||
}
|
}
|
||||||
pointBuffer({
|
pointBuffer({
|
||||||
data: this.renderCache.positions,
|
data: this.renderCache.positions,
|
||||||
dimension: 2
|
dimension: 2
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
offset
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Colors for each point - a cached value that only changes when
|
// Colors for each point - a cached value that only changes when
|
||||||
@@ -196,7 +201,7 @@ class Graph extends React.Component {
|
|||||||
this.handleBrushSelectAction.bind(this),
|
this.handleBrushSelectAction.bind(this),
|
||||||
this.handleBrushDeselectAction.bind(this),
|
this.handleBrushDeselectAction.bind(this),
|
||||||
responsive,
|
responsive,
|
||||||
this.graphPaddingTop
|
this.graphPaddingRight
|
||||||
);
|
);
|
||||||
this.setState({ svg: newSvg, brush });
|
this.setState({ svg: newSvg, brush });
|
||||||
}
|
}
|
||||||
@@ -251,7 +256,7 @@ class Graph extends React.Component {
|
|||||||
an event on procedural deselect because it is move: null
|
an event on procedural deselect because it is move: null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { camera } = this.state;
|
const { camera, offset } = this.state;
|
||||||
const { dispatch, responsive } = this.props;
|
const { dispatch, responsive } = this.props;
|
||||||
|
|
||||||
if (d3.event.sourceEvent !== null) {
|
if (d3.event.sourceEvent !== null) {
|
||||||
@@ -262,6 +267,7 @@ class Graph extends React.Component {
|
|||||||
https://bl.ocks.org/EfratVil/0e542f5fc426065dd1d4b6daaa345a9f
|
https://bl.ocks.org/EfratVil/0e542f5fc426065dd1d4b6daaa345a9f
|
||||||
*/
|
*/
|
||||||
const s = d3.event.selection;
|
const s = d3.event.selection;
|
||||||
|
const gl = this.state.regl._gl;
|
||||||
/*
|
/*
|
||||||
event describing brush position:
|
event describing brush position:
|
||||||
@-------|
|
@-------|
|
||||||
@@ -270,19 +276,23 @@ class Graph extends React.Component {
|
|||||||
|-------@
|
|-------@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// get aspect ratio
|
||||||
|
const aspect = gl.drawingBufferWidth / gl.drawingBufferHeight;
|
||||||
|
|
||||||
// compute inverse view matrix
|
// compute inverse view matrix
|
||||||
const inverse = mat4.invert([], camera.view());
|
const inverse = mat4.invert([], camera.view());
|
||||||
|
|
||||||
// transform screen coordinates -> cell coordinates
|
// transform screen coordinates -> cell coordinates
|
||||||
const invert = pin => {
|
const invert = pin => {
|
||||||
const x = (2 * pin[0]) / (responsive.height - this.graphPaddingTop) - 1;
|
const x =
|
||||||
|
(2 * pin[0]) / (responsive.width - this.graphPaddingRight) - 1;
|
||||||
const y =
|
const y =
|
||||||
2 * (1 - pin[1] / (responsive.height - this.graphPaddingTop)) - 1;
|
2 * (1 - pin[1] / (responsive.height - this.graphPaddingTop)) - 1;
|
||||||
const pout = [
|
const pout = [
|
||||||
x * inverse[14] + inverse[12],
|
x * inverse[14] * aspect + inverse[12],
|
||||||
y * inverse[14] + inverse[13]
|
y * inverse[14] + inverse[13]
|
||||||
];
|
];
|
||||||
return [(pout[0] + 1) / 2, (pout[1] + 1) / 2];
|
return [(pout[0] + 1) / 2 + offset[0], (pout[1] + 1) / 2 + offset[1]];
|
||||||
};
|
};
|
||||||
|
|
||||||
const brushCoords = {
|
const brushCoords = {
|
||||||
@@ -366,6 +376,7 @@ class Graph extends React.Component {
|
|||||||
style={{ marginRight: 10 }}
|
style={{ marginRight: 10 }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(actions.regraph());
|
dispatch(actions.regraph());
|
||||||
|
dispatch({ type: "increment graph render counter" });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
subset to current selection
|
subset to current selection
|
||||||
@@ -421,12 +432,10 @@ class Graph extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
marginRight: 50,
|
|
||||||
marginTop: 50,
|
|
||||||
zIndex: -9999,
|
zIndex: -9999,
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
right: this.graphPaddingRight,
|
top: 0,
|
||||||
bottom: this.graphPaddingBottom
|
right: 0
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -437,7 +446,7 @@ class Graph extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<div style={{ padding: 0, margin: 0 }}>
|
<div style={{ padding: 0, margin: 0 }}>
|
||||||
<canvas
|
<canvas
|
||||||
width={responsive.height - this.graphPaddingTop}
|
width={responsive.width - this.graphPaddingRight}
|
||||||
height={responsive.height - this.graphPaddingTop}
|
height={responsive.height - this.graphPaddingTop}
|
||||||
ref={canvas => {
|
ref={canvas => {
|
||||||
this.reglCanvas = canvas;
|
this.reglCanvas = canvas;
|
||||||
|
|||||||
@@ -12,22 +12,18 @@ export default (
|
|||||||
handleBrushSelectAction,
|
handleBrushSelectAction,
|
||||||
handleBrushDeselectAction,
|
handleBrushDeselectAction,
|
||||||
responsive,
|
responsive,
|
||||||
graphPaddingTop
|
graphPaddingRight
|
||||||
) => {
|
) => {
|
||||||
const side = responsive.height - graphPaddingTop;
|
|
||||||
const svg = d3
|
const svg = d3
|
||||||
.select("#graphAttachPoint")
|
.select("#graphAttachPoint")
|
||||||
.append("svg")
|
.append("svg")
|
||||||
.attr("width", side)
|
.attr("width", responsive.width - graphPaddingRight)
|
||||||
.attr("height", side)
|
.attr("height", responsive.height)
|
||||||
.attr("class", `${styles.graphSVG}`);
|
.attr("class", `${styles.graphSVG}`);
|
||||||
|
|
||||||
const brush = d3
|
const brush = d3
|
||||||
.brush()
|
.brush()
|
||||||
.extent([
|
.extent([[0, 0], [responsive.width - graphPaddingRight, responsive.height]])
|
||||||
[0, 0],
|
|
||||||
[responsive.height - graphPaddingTop, responsive.height - graphPaddingTop]
|
|
||||||
])
|
|
||||||
.on("brush", handleBrushSelectAction)
|
.on("brush", handleBrushSelectAction)
|
||||||
.on("end", handleBrushDeselectAction);
|
.on("end", handleBrushDeselectAction);
|
||||||
|
|
||||||
|
|||||||
Vendored
+8
-1
@@ -54,6 +54,7 @@ const Controls = (
|
|||||||
scatterplotXXaccessor: null, // just easier to read
|
scatterplotXXaccessor: null, // just easier to read
|
||||||
scatterplotYYaccessor: null,
|
scatterplotYYaccessor: null,
|
||||||
axesHaveBeenDrawn: false,
|
axesHaveBeenDrawn: false,
|
||||||
|
graphRenderCounter: 0 /* integer as <Component key={graphRenderCounter} - a change in key forces a remount */,
|
||||||
__storedStateForCelllist1__: null /* will need procedural control of brush ie., brush.extent https://bl.ocks.org/micahstubbs/3cda05ca68cba260cb81 */,
|
__storedStateForCelllist1__: null /* will need procedural control of brush ie., brush.extent https://bl.ocks.org/micahstubbs/3cda05ca68cba260cb81 */,
|
||||||
__storedStateForCelllist2__: null
|
__storedStateForCelllist2__: null
|
||||||
},
|
},
|
||||||
@@ -410,7 +411,13 @@ const Controls = (
|
|||||||
...state,
|
...state,
|
||||||
opacityForDeselectedCells: action.data
|
opacityForDeselectedCells: action.data
|
||||||
};
|
};
|
||||||
|
case "increment graph render counter": {
|
||||||
|
const c = state.graphRenderCounter + 1;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
graphRenderCounter: c
|
||||||
|
};
|
||||||
|
}
|
||||||
/*******************************
|
/*******************************
|
||||||
Categorical metadata
|
Categorical metadata
|
||||||
*******************************/
|
*******************************/
|
||||||
|
|||||||
Reference in New Issue
Block a user