coordinate system fixes for embedded graph (#768)

* change pan speed to 1 per issue #722

* correct handle scaling of graph when aspect ratio less than one

* add package lock

* add invert to our scale functions

* correctly transform to/from gl coordinates

* remove unused import

* fix naming of import

* update smoke tests
This commit is contained in:
Bruce Martin
2019-05-20 14:22:41 -07:00
committed by GitHub
parent de3407d875
commit fcc05f6a00
8 changed files with 69 additions and 41 deletions
+15 -1
View File
@@ -1,5 +1,6 @@
// jshint esversion: 6
const mat4 = require("gl-mat4");
const vec3 = require("gl-vec3");
// opacity: https://github.com/spacetx/starfish/blob/master/viz/draw/regions.js
@@ -38,7 +39,20 @@ export default function(regl) {
uniforms: {
distance: regl.prop("distance"),
view: regl.prop("view"),
projection: ({viewportWidth, viewportHeight}) => mat4.perspective([], Math.PI / 2, viewportWidth / viewportHeight, 0.01, 1000)
projection: ({ viewportWidth, viewportHeight }) => {
const aspectRatio = viewportWidth / viewportHeight;
let m = mat4.perspective(
[],
Math.PI / 2,
viewportWidth / viewportHeight,
0.01,
1000
);
if (aspectRatio < 1) {
m = mat4.scale(m, m, vec3.fromValues(1, 1, 1 / aspectRatio));
}
return m;
}
},
count: regl.prop("count"),
+30 -24
View File
@@ -175,18 +175,19 @@ class Graph extends React.Component {
const glScaleX = scaleLinear([0, 1], [-1, 1]);
const glScaleY = scaleLinear([0, 1], [1, -1]);
const offset = [d3.mean(X) - 0.5, d3.mean(Y) - 0.5];
for (let i = 0, { positions } = renderCache; i < nObs; i += 1) {
positions[2 * i] = glScaleX(X[i] - offset[0]);
positions[2 * i + 1] = glScaleY(Y[i] - offset[1]);
positions[2 * i] = glScaleX(X[i]);
positions[2 * i + 1] = glScaleY(Y[i]);
}
pointBuffer({
data: renderCache.positions,
dimension: 2
});
stateChanges.offset = offset;
stateChanges.transform = {
glScaleX,
glScaleY
};
}
// Colors for each point - a cached value that only changes when
@@ -271,11 +272,11 @@ class Graph extends React.Component {
mode !== prevState.mode ||
stateChanges.svg
) {
const { tool, container, offset } = this.state;
const { tool, container, transform } = this.state;
this.selectionToolUpdate(
stateChanges.tool ? stateChanges.tool : tool,
stateChanges.container ? stateChanges.container : container,
stateChanges.offset ? stateChanges.offset : offset
stateChanges.transform ? stateChanges.transform : transform
);
}
@@ -435,7 +436,7 @@ class Graph extends React.Component {
this.setState({ pendingClipPercentiles: null });
};
brushToolUpdate(tool, container, offset) {
brushToolUpdate(tool, container, transform) {
/*
this is called from componentDidUpdate(), so be very careful using
anything from this.state, which may be updated asynchronously.
@@ -449,8 +450,14 @@ class Graph extends React.Component {
if there is a selection, make sure the brush tool matches
*/
const screenCoords = [
this.mapPointToScreen(currentSelection.brushCoords.northwest, offset),
this.mapPointToScreen(currentSelection.brushCoords.southeast, offset)
this.mapPointToScreen(
currentSelection.brushCoords.northwest,
transform
),
this.mapPointToScreen(
currentSelection.brushCoords.southeast,
transform
)
];
if (!toolCurrentSelection) {
/* tool is not selected, so just move the brush */
@@ -477,7 +484,7 @@ class Graph extends React.Component {
}
}
lassoToolUpdate(tool, container, offset) {
lassoToolUpdate(tool, container, transform) {
/*
this is called from componentDidUpdate(), so be very careful using
anything from this.state, which may be updated asynchronously.
@@ -488,7 +495,7 @@ class Graph extends React.Component {
if there is a current selection, make sure the lasso tool matches
*/
const polygon = currentSelection.polygon.map(p =>
this.mapPointToScreen(p, offset)
this.mapPointToScreen(p, transform)
);
tool.move(polygon);
} else {
@@ -496,7 +503,7 @@ class Graph extends React.Component {
}
}
selectionToolUpdate(tool, container, offset) {
selectionToolUpdate(tool, container, transform) {
/*
this is called from componentDidUpdate(), so be very careful using
anything from this.state, which may be updated asynchronously.
@@ -504,10 +511,10 @@ class Graph extends React.Component {
const { selectionTool } = this.props;
switch (selectionTool) {
case "brush":
this.brushToolUpdate(tool, container, offset);
this.brushToolUpdate(tool, container, transform);
break;
case "lasso":
this.lassoToolUpdate(tool, container, offset);
this.lassoToolUpdate(tool, container, transform);
break;
default:
/* punt? */
@@ -564,7 +571,8 @@ class Graph extends React.Component {
accounting for current pan/zoom camera.
*/
const { responsive } = this.props;
const { regl, camera, offset } = this.state;
const { regl, camera, transform } = this.state;
const { glScaleX, glScaleY } = transform;
const gl = regl._gl;
@@ -579,19 +587,20 @@ class Graph extends React.Component {
const y = 2 * (1 - pin[1] / (responsive.height - this.graphPaddingTop)) - 1;
const pout = [
x * inverse[14] * aspect + inverse[12],
y * inverse[14] + inverse[13]
-(y * inverse[14] + inverse[13])
];
return [(pout[0] + 1) / 2 + offset[0], (pout[1] + 1) / 2 + offset[1]];
return [glScaleX.invert(pout[0]), glScaleY.invert(pout[1])];
}
mapPointToScreen(xyCell, offset) {
mapPointToScreen(xyCell, transform) {
/*
Map an XY coordinate from cell/point domain to screen range. Inverse
of mapScreenToPoint()
*/
const { responsive } = this.props;
const { regl, camera } = this.state;
const { glScaleX, glScaleY } = transform;
const gl = regl._gl;
@@ -603,12 +612,9 @@ class Graph extends React.Component {
// variable names are choosen to reflect inverse of those used
// in mapScreenToPoint().
const pout = [
(xyCell[0] - offset[0]) * 2 - 1,
(xyCell[1] - offset[1]) * 2 - 1
];
const pout = [glScaleX(xyCell[0]), glScaleY(xyCell[1])];
const x = (pout[0] - inverse[12]) / aspect / inverse[14];
const y = (pout[1] - inverse[13]) / inverse[14];
const y = (-pout[1] - inverse[13]) / inverse[14];
const pin = [
Math.round(((x + 1) * (responsive.width - this.graphPaddingRight)) / 2),
+1 -2
View File
@@ -5,10 +5,9 @@ const mp = require("mouse-position");
const mb = require("mouse-pressed");
const key = require("key-pressed");
const panSpeed = 0.4;
const panSpeed = 1.0; // changed from 0.4 to 1.0 per issue #722
const scaleSpeed = 0.5;
const scaleMax = 3;
// const scaleMin = 1.15
const scaleMin = 1.03;
function attachCamera(canvas, opts) {
+10 -4
View File
@@ -9,8 +9,14 @@
// this is is equivalent to d3.scaleLinear().domain([0,1]).range([-1,1])
export default (domain, range) => {
const domainStart = domain[0];
const scale = (range[1] - range[0]) / (domain[1] - domain[0]);
const rangeStart = range[0];
return value => (value - domainStart) * scale + rangeStart;
const domainStart = domain[0];
const scale = (range[1] - range[0]) / (domain[1] - domain[0]);
const invScale = 1 / scale;
const rangeStart = range[0];
const f = value => (value - domainStart) * scale + rangeStart;
// inverter
f.invert = value => (value - rangeStart) * invScale + domainStart;
return f;
};