mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-16 21:37:59 +08:00
drawing graph
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import styles from './parallelCoordinates.css';
|
||||
import renderQueue from "./renderQueue";
|
||||
import {
|
||||
margin,
|
||||
width,
|
||||
@@ -17,89 +18,6 @@ Canvas Parallel coordinates with svg brushing via /* via https://bl.ocks.org/syn
|
||||
******************************************
|
||||
******************************************/
|
||||
|
||||
/*****************************************
|
||||
******************************************
|
||||
Render Queue via http://bl.ocks.org/syntagmatic/raw/3341641/render-queue.js
|
||||
******************************************
|
||||
******************************************/
|
||||
|
||||
const renderQueue = (function(callback1234) {
|
||||
var _queue = [], // data to be rendered
|
||||
_rate = 1000, // number of calls per frame
|
||||
_invalidate = function() {}, // invalidate last render queue
|
||||
_clear = function() {}; // clearing function
|
||||
|
||||
var rq = function(data) {
|
||||
if (data) rq.data(data);
|
||||
_invalidate();
|
||||
_clear();
|
||||
rq.render();
|
||||
};
|
||||
|
||||
rq.render = function() {
|
||||
var valid = true;
|
||||
_invalidate = rq.invalidate = function() {
|
||||
valid = false;
|
||||
};
|
||||
|
||||
function doFrame() {
|
||||
if (!valid) return true;
|
||||
var chunk = _queue.splice(0,_rate);
|
||||
chunk.map(callback1234);
|
||||
timer_frame(doFrame);
|
||||
}
|
||||
|
||||
doFrame();
|
||||
};
|
||||
|
||||
rq.data = function(data) {
|
||||
_invalidate();
|
||||
_queue = data.slice(0); // creates a copy of the data
|
||||
return rq;
|
||||
};
|
||||
|
||||
rq.add = function(data) {
|
||||
_queue = _queue.concat(data);
|
||||
};
|
||||
|
||||
rq.rate = function(value) {
|
||||
if (!arguments.length) return _rate;
|
||||
_rate = value;
|
||||
return rq;
|
||||
};
|
||||
|
||||
rq.remaining = function() {
|
||||
return _queue.length;
|
||||
};
|
||||
|
||||
// clear the canvas
|
||||
rq.clear = function(func) {
|
||||
if (!arguments.length) {
|
||||
_clear();
|
||||
return rq;
|
||||
}
|
||||
_clear = func;
|
||||
return rq;
|
||||
};
|
||||
|
||||
rq.invalidate = _invalidate;
|
||||
|
||||
var timer_frame = window.requestAnimationFrame
|
||||
|| window.webkitRequestAnimationFrame
|
||||
|| window.mozRequestAnimationFrame
|
||||
|| window.oRequestAnimationFrame
|
||||
|| window.msRequestAnimationFrame
|
||||
|| function(callback) { setTimeout(callback, 17); };
|
||||
|
||||
return rq;
|
||||
});
|
||||
|
||||
/*****************************************
|
||||
******************************************
|
||||
MAIN: drawParallelCoordinates
|
||||
******************************************
|
||||
******************************************/
|
||||
|
||||
const drawParallelCoordinates = (data) => {
|
||||
|
||||
const d3_functor = (v) => {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*****************************************
|
||||
******************************************
|
||||
Render Queue via http://bl.ocks.org/syntagmatic/raw/3341641/render-queue.js
|
||||
******************************************
|
||||
******************************************/
|
||||
|
||||
const renderQueue = (function(callback1234) {
|
||||
var _queue = [], // data to be rendered
|
||||
_rate = 1000, // number of calls per frame
|
||||
_invalidate = function() {}, // invalidate last render queue
|
||||
_clear = function() {}; // clearing function
|
||||
|
||||
var rq = function(data) {
|
||||
if (data) rq.data(data);
|
||||
_invalidate();
|
||||
_clear();
|
||||
rq.render();
|
||||
};
|
||||
|
||||
rq.render = function() {
|
||||
var valid = true;
|
||||
_invalidate = rq.invalidate = function() {
|
||||
valid = false;
|
||||
};
|
||||
|
||||
function doFrame() {
|
||||
if (!valid) return true;
|
||||
var chunk = _queue.splice(0,_rate);
|
||||
chunk.map(callback1234);
|
||||
timer_frame(doFrame);
|
||||
}
|
||||
|
||||
doFrame();
|
||||
};
|
||||
|
||||
rq.data = function(data) {
|
||||
_invalidate();
|
||||
_queue = data.slice(0); // creates a copy of the data
|
||||
return rq;
|
||||
};
|
||||
|
||||
rq.add = function(data) {
|
||||
_queue = _queue.concat(data);
|
||||
};
|
||||
|
||||
rq.rate = function(value) {
|
||||
if (!arguments.length) return _rate;
|
||||
_rate = value;
|
||||
return rq;
|
||||
};
|
||||
|
||||
rq.remaining = function() {
|
||||
return _queue.length;
|
||||
};
|
||||
|
||||
// clear the canvas
|
||||
rq.clear = function(func) {
|
||||
if (!arguments.length) {
|
||||
_clear();
|
||||
return rq;
|
||||
}
|
||||
_clear = func;
|
||||
return rq;
|
||||
};
|
||||
|
||||
rq.invalidate = _invalidate;
|
||||
|
||||
var timer_frame = window.requestAnimationFrame
|
||||
|| window.webkitRequestAnimationFrame
|
||||
|| window.mozRequestAnimationFrame
|
||||
|| window.oRequestAnimationFrame
|
||||
|| window.msRequestAnimationFrame
|
||||
|| function(callback) { setTimeout(callback, 17); };
|
||||
|
||||
return rq;
|
||||
});
|
||||
|
||||
export default renderQueue;
|
||||
|
||||
@@ -1,118 +1,67 @@
|
||||
var margin = {top: 20, right: 10, bottom: 30, left: 40},
|
||||
import styles from "./graph.css";
|
||||
import renderQueue from "../continuous/renderQueue";
|
||||
|
||||
const margin = {top: 20, right: 10, bottom: 30, left: 40},
|
||||
width = 960 - margin.left - margin.right,
|
||||
height = 500 - margin.top - margin.bottom;
|
||||
|
||||
var svg = d3.select("body").append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + " " + margin.top + ")");
|
||||
|
||||
var factory = d3.geom.quadtree()
|
||||
.extent([
|
||||
[0, 0],
|
||||
[width, height]
|
||||
]);
|
||||
|
||||
var x = d3.scale.linear()
|
||||
const x = d3.scaleLinear()
|
||||
.range([0, width]);
|
||||
|
||||
var y = d3.scale.linear()
|
||||
const y = d3.scaleLinear()
|
||||
.range([height, 0]);
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(x)
|
||||
.orient("bottom");
|
||||
/******************************************
|
||||
*******************************************
|
||||
Main
|
||||
*******************************************
|
||||
******************************************/
|
||||
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(y)
|
||||
.orient("left");
|
||||
const drawGraph = (data) => {
|
||||
|
||||
var xg = svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(0," + height + ")");
|
||||
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 + ")");
|
||||
|
||||
var yg = svg.append("g")
|
||||
.attr("class", "y axis");
|
||||
var chartArea = d3.select("#graphAttachPoint").append("div")
|
||||
.style("left", margin.left + "px")
|
||||
.style("top", margin.top + "px");
|
||||
|
||||
var chartArea = d3.select("body").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 canvas = chartArea.append("canvas")
|
||||
.attr("width", width)
|
||||
.attr("height", height);
|
||||
var context = canvas.node().getContext("2d");
|
||||
|
||||
var context = canvas.node().getContext("2d");
|
||||
context.fillStyle = "rgba(0,0,0,.5)";
|
||||
|
||||
context.fillStyle = "#f0f";
|
||||
const draw = () => {
|
||||
|
||||
// Layer on top of canvas, example of selection details
|
||||
var highlight = chartArea.append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.append("circle")
|
||||
.attr("r", 7)
|
||||
.classed("hidden", true);
|
||||
// Update canvas
|
||||
context.clearRect(0, 0, width, height);
|
||||
|
||||
redraw();
|
||||
data.forEach((p, i) => {
|
||||
|
||||
function redraw() {
|
||||
context.beginPath();
|
||||
/* context.arc(x,y,r,sAngle,eAngle,counterclockwise); */
|
||||
context.arc(
|
||||
x(p[1]), /* x */
|
||||
y(p[2]), /* y */
|
||||
2, /* r */
|
||||
0, /* sAngle */
|
||||
2 * Math.PI /* eAngle */
|
||||
);
|
||||
context.fill();
|
||||
|
||||
// Randomize the scale
|
||||
var scale = 1 + Math.floor(Math.random() * 10);
|
||||
});
|
||||
}
|
||||
|
||||
// Redraw axes
|
||||
x.domain([0, scale]);
|
||||
y.domain([0, scale]);
|
||||
xg.call(xAxis);
|
||||
yg.call(yAxis);
|
||||
|
||||
var points = randomPoints(scale);
|
||||
|
||||
var tree = factory(points);
|
||||
|
||||
// Update canvas
|
||||
context.clearRect(0, 0, width, height);
|
||||
|
||||
points.forEach(function(p,i){
|
||||
|
||||
context.beginPath();
|
||||
context.arc(x(p[0]), y(p[1]), 5, 0, 2 * Math.PI);
|
||||
context.fill();
|
||||
|
||||
});
|
||||
|
||||
canvas.on("mousemove",function(){
|
||||
|
||||
var mouse = d3.mouse(this),
|
||||
closest = tree.find([x.invert(mouse[0]), y.invert(mouse[1])]);
|
||||
|
||||
highlight.attr("cx", x(closest[0]))
|
||||
.attr("cy", y(closest[1]));
|
||||
|
||||
});
|
||||
|
||||
canvas.on("mouseover",function(){
|
||||
highlight.classed("hidden", false);
|
||||
});
|
||||
|
||||
canvas.on("mouseout",function(){
|
||||
highlight.classed("hidden", true);
|
||||
});
|
||||
draw();
|
||||
|
||||
}
|
||||
|
||||
function randomPoints(scale) {
|
||||
|
||||
// Get points
|
||||
return d3.range(1000).map(function(d){
|
||||
|
||||
return [
|
||||
Math.random() * scale,
|
||||
Math.random() * scale
|
||||
];
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
export default drawGraph;
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
path,
|
||||
line {
|
||||
.graphCanvas path {
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
div,
|
||||
svg,
|
||||
canvas {
|
||||
#graphWrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#graphAttachPoint,
|
||||
.graphSVG,
|
||||
.graphCanvas {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
svg {
|
||||
#graphSVG {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
import React from 'react';
|
||||
import _ from "lodash";
|
||||
// import styles from "./joy.css";
|
||||
// import drawGraph from "./drawGraph";
|
||||
// import joyParser from "./joyParser";
|
||||
import styles from "./graph.css";
|
||||
import drawGraph from "./drawGraph";
|
||||
|
||||
class Graph extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
||||
drawn: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.data) {
|
||||
console.log('graph data 55', nextProps.data)
|
||||
// drawGraph(graphParser(nextProps.data));
|
||||
/* maybe should do a check here to confirm ref exists and pass it? */
|
||||
if (nextProps.data && !this.state.drawn) {
|
||||
console.log('About to draw graph. Data:', nextProps.data)
|
||||
drawGraph(nextProps.data.data)
|
||||
this.setState({drawn: true});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="joyplot_wrapper" style={{marginTop: 50}}>
|
||||
<div id="graphWrapper">
|
||||
<h3> Graph </h3>
|
||||
<div id="joyplot"> </div>
|
||||
<div id="graphAttachPoint"> </div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class Home extends React.Component {
|
||||
graph: null,
|
||||
};
|
||||
}
|
||||
componentWillMount() {
|
||||
componentDidMount() {
|
||||
// d3.csv("http://ec2-34-234-75-156.compute-1.amazonaws.com/api/v0.1/metadata", (d) => {
|
||||
// console.log("from ec2",d)
|
||||
// })
|
||||
@@ -51,13 +51,22 @@ class Home extends React.Component {
|
||||
<Container>
|
||||
<Helmet title="cellxgene" />
|
||||
<h1><Link to="/page2">cellxgene</Link></h1>
|
||||
<button
|
||||
className={buttonStyles.primaryButton}>
|
||||
Showing a cluster informed downsampling of 10,000 cells. Refresh.
|
||||
</button>
|
||||
<h3 style={{marginTop: 50}}> Gene selection criteria </h3>
|
||||
<Joy data={this.state.heatmap && this.state.heatmap.data}/>
|
||||
{false ? <Joy data={this.state.heatmap && this.state.heatmap.data}/> : ""}
|
||||
|
||||
<Categorical/>
|
||||
<Continuous/>
|
||||
<h3 style={{marginTop: 50}}> T-SNE plot </h3>
|
||||
<button onClick={() => { this.setState({showClustersPlaceholderImage: true}) }} style={{marginBottom: 20}} className={buttonStyles.primaryButton}>Compute clustering using [n] cells in current metadata selection</button>
|
||||
<button
|
||||
onClick={() => { this.setState({showClustersPlaceholderImage: true}) }}
|
||||
style={{marginBottom: 20}}
|
||||
className={buttonStyles.primaryButton}>
|
||||
Compute clustering using [n] cells in current metadata selection
|
||||
</button>
|
||||
<Graph data={this.state.graph}/>
|
||||
{this.state.showClustersPlaceholderImage ? <img width={750} src="https://s10.postimg.org/s6z3zbcvt/tsne.png"></img> : null}
|
||||
</Container>
|
||||
|
||||
Reference in New Issue
Block a user