mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-15 12:47:56 +08:00
Add frontend
This commit is contained in:
38
client/src/components/graph/drawSpatialImageRegl.js
Normal file
38
client/src/components/graph/drawSpatialImageRegl.js
Normal file
@@ -0,0 +1,38 @@
|
||||
export default function drawSpatialImageRegl(regl) {
|
||||
return regl({
|
||||
frag: `
|
||||
precision mediump float;
|
||||
uniform sampler2D texture;
|
||||
varying vec2 uv;
|
||||
void main () {
|
||||
gl_FragColor = texture2D(texture, uv);
|
||||
}`,
|
||||
|
||||
vert: `
|
||||
precision mediump float;
|
||||
attribute vec2 position;
|
||||
varying vec2 uv;
|
||||
const float zMiddle = 0.;
|
||||
uniform mat3 projView;
|
||||
|
||||
vec2 norm(vec2 position) {
|
||||
return ((position - 0.5) * 2.0) * vec2(1., -1.);
|
||||
}
|
||||
void main() {
|
||||
uv = position;
|
||||
vec3 xy = projView * vec3(norm(position), 1.);
|
||||
gl_Position = vec4(xy.xy, 0, 1.);
|
||||
}`,
|
||||
|
||||
attributes: {
|
||||
position: [-1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1],
|
||||
},
|
||||
|
||||
uniforms: {
|
||||
projView: regl.prop("projView"),
|
||||
texture: regl.prop("spatialImageAsTexture"),
|
||||
},
|
||||
|
||||
count: 6,
|
||||
});
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
createColorTable,
|
||||
createColorQuery,
|
||||
} from "../../util/stateManager/colorHelpers";
|
||||
import _drawSpatialImage from "./drawSpatialImageRegl";
|
||||
import * as globals from "../../globals";
|
||||
|
||||
import GraphOverlayLayer from "./overlays/graphOverlayLayer";
|
||||
@@ -87,6 +88,7 @@ class Graph extends React.Component {
|
||||
const camera = _camera(canvas);
|
||||
const regl = _regl(canvas);
|
||||
const drawPoints = _drawPoints(regl);
|
||||
const drawSpatialImage = _drawSpatialImage(regl);
|
||||
|
||||
// preallocate webgl buffers
|
||||
const pointBuffer = regl.buffer();
|
||||
@@ -100,6 +102,7 @@ class Graph extends React.Component {
|
||||
pointBuffer,
|
||||
colorBuffer,
|
||||
flagBuffer,
|
||||
drawSpatialImage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -232,6 +235,7 @@ class Graph extends React.Component {
|
||||
pointBuffer: null,
|
||||
colorBuffer: null,
|
||||
flagBuffer: null,
|
||||
drawSpatialImage: null,
|
||||
|
||||
// component rendering derived state - these must stay synchronized
|
||||
// with the reducer state they were generated from.
|
||||
@@ -317,7 +321,10 @@ class Graph extends React.Component {
|
||||
if (e.type !== "wheel") e.preventDefault();
|
||||
if (camera.handleEvent(e, projectionTF)) {
|
||||
this.renderCanvas();
|
||||
this.setState((state) => ({ ...state, updateOverlay: !state.updateOverlay }));
|
||||
this.setState((state) => ({
|
||||
...state,
|
||||
updateOverlay: !state.updateOverlay,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -509,6 +516,13 @@ class Graph extends React.Component {
|
||||
return { toolSVG: newToolSVG, tool, container };
|
||||
};
|
||||
|
||||
loadTextureFromUrl = (src) => new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = reject;
|
||||
img.src = src;
|
||||
});
|
||||
|
||||
fetchAsyncProps = async (props) => {
|
||||
const {
|
||||
annoMatrix,
|
||||
@@ -551,6 +565,10 @@ class Graph extends React.Component {
|
||||
pointDilationLabel
|
||||
);
|
||||
|
||||
this.spatialImage = await this.loadTextureFromUrl(
|
||||
"/api/v0.2/spatial/image"
|
||||
);
|
||||
|
||||
const { width, height } = viewport;
|
||||
return {
|
||||
positions,
|
||||
@@ -721,6 +739,7 @@ class Graph extends React.Component {
|
||||
flagBuffer,
|
||||
camera,
|
||||
projectionTF,
|
||||
drawSpatialImage,
|
||||
} = this.state;
|
||||
this.renderPoints(
|
||||
regl,
|
||||
@@ -729,7 +748,8 @@ class Graph extends React.Component {
|
||||
pointBuffer,
|
||||
flagBuffer,
|
||||
camera,
|
||||
projectionTF
|
||||
projectionTF,
|
||||
drawSpatialImage
|
||||
);
|
||||
});
|
||||
|
||||
@@ -797,7 +817,8 @@ class Graph extends React.Component {
|
||||
pointBuffer,
|
||||
flagBuffer,
|
||||
camera,
|
||||
projectionTF
|
||||
projectionTF,
|
||||
drawSpatialImage
|
||||
) {
|
||||
const { annoMatrix } = this.props;
|
||||
if (!this.reglCanvas || !annoMatrix) return;
|
||||
@@ -809,7 +830,7 @@ class Graph extends React.Component {
|
||||
regl.poll();
|
||||
regl.clear({
|
||||
depth: 1,
|
||||
color: [1, 1, 1, 1],
|
||||
color: [0, 0, 0, 0],
|
||||
});
|
||||
drawPoints({
|
||||
distance: camera.distance(),
|
||||
@@ -821,6 +842,12 @@ class Graph extends React.Component {
|
||||
nPoints: schema.dataframe.nObs,
|
||||
minViewportDimension: Math.min(width, height),
|
||||
});
|
||||
drawSpatialImage({
|
||||
projView,
|
||||
spatialImageAsTexture: regl.texture({
|
||||
data: this.spatialImage,
|
||||
}),
|
||||
});
|
||||
regl._gl.flush();
|
||||
}
|
||||
|
||||
@@ -951,32 +978,29 @@ const ErrorLoading = ({ displayName, error, width, height }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const StillLoading = ({ displayName, width, height }) =>
|
||||
const StillLoading = ({ displayName, width, height }) => (
|
||||
/*
|
||||
Render a busy/loading indicator
|
||||
*/
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
fontWeight: 500,
|
||||
top: height / 2,
|
||||
width,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
fontWeight: 500,
|
||||
top: height / 2,
|
||||
width,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
justifyItems: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
justifyItems: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Button minimal loading intent="primary" />
|
||||
<span style={{ fontStyle: "italic" }}>Loading {displayName}</span>
|
||||
</div>
|
||||
<Button minimal loading intent="primary" />
|
||||
<span style={{ fontStyle: "italic" }}>Loading {displayName}</span>
|
||||
</div>
|
||||
)
|
||||
;
|
||||
|
||||
</div>
|
||||
);
|
||||
export default Graph;
|
||||
|
||||
Reference in New Issue
Block a user