Open cornhundred opened 5 years ago
Using the current camera system, resetting to a particular zoom/pan level might require initializing the camera with a single instantaneous zoom/pan interaction that represents the previous incremental zooming/panning.
https://github.com/ismms-himc/clustergrammer-gl/blob/master/src/cameras/camera_interaction.js
Also, slow down zooming a little by default (maybe allow this to be adjusted by users).
Camera update notes (paraphrased from gitter channel @rreusser comments):
Using d3-zoom
the main code is here: https://observablehq.com/@rreusser/regl-tools#persistentZoom
function persistentZoom (xScale, yScale, originalXScale, originalYScale, callback) {
return d3.zoom().on('zoom', function () {
let range
let t = d3.event.transform;
range = xScale.range().map(t.invertX, t);
xScale.domain(originalXScale.domain())
xScale.domain(range.map(xScale.invert, xScale));
range = yScale.range().map(t.invertY, t);
yScale.domain(originalYScale.domain())
yScale.domain(range.map(yScale.invert, yScale));
});
}
note, the scales are d3 scales (see scale definitions under sample usage)
let xScale = d3.scaleLinear()
.domain([-3, 3])
.range([viewport.margin.l, viewport.width - viewport.margin.r])
.clamp(true);
let yScale = constrainLinearScaleAspectRatio(
d3.scaleLinear()
.domain([-1.5, 1.5])
.range([viewport.height - viewport.margin.b, viewport.margin.t])
.clamp(true),
xScale, 1);
usage looks like this
svg.call(
persistentZoom(currentXScale, currentYScale, xScale, yScale)
.scaleExtent([0.01, 10000])
.on('zoom.plot', () => (dirty = true))
);
Using the d3 scales with webgl is a different manner, the function below translates the scales into a 2d view matrix that can be used in webgl
https://observablehq.com/@rreusser/regl-tools#createReglViewportConfiguration
function createReglViewportConfiguration (regl) {
const viewport3 = mat3create();
let command = regl({
scissor: {
enable: true,
box: {
x: (ctx, props) => ctx.pixelRatio * props.margin.l,
y: (ctx, props) => ctx.pixelRatio * props.margin.b,
width: (ctx, props) => ctx.framebufferWidth - ctx.pixelRatio * (props.margin.r + props.margin.l),
height: (ctx, props) => ctx.framebufferHeight - ctx.pixelRatio * (props.margin.t + props.margin.b)
}
},
viewport: {
x: (ctx, props) => ctx.pixelRatio * props.margin.l,
y: (ctx, props) => ctx.pixelRatio * props.margin.b,
width: (ctx, props) => ctx.framebufferWidth - ctx.pixelRatio * (props.margin.r + props.margin.l),
height: (ctx, props) => ctx.framebufferHeight - ctx.pixelRatio * (props.margin.t + props.margin.b)
},
uniforms: {
viewportResolution: (ctx, props) => [ctx.viewportWidth, ctx.viewportHeight],
framebufferResolution: ctx => [ctx.framebufferWidth, ctx.framebufferHeight],
inverseViewportResolution: (ctx, props) => [1 / ctx.viewportWidth, 1 / ctx.viewportHeight],
inverseFramebufferResolution: ctx => [1 / ctx.framebufferWidth, 1 / ctx.framebufferHeight],
}
});
return function (viewport, callback) {
command(viewport, callback);
}
}
Sometimes the camera and zoom object can get out of sync. They are reset when the user zooms all the way out (as a fail safe). We should ultimately prevent this mis-syncing from happening or add an additional fail safe that resets the camera when the user has finished interacting with the visualization.