Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

camera update and resetting with custom zoom/pan #19

Open
cornhundred opened this issue Jun 13, 2019 · 3 comments
Open

camera update and resetting with custom zoom/pan #19

cornhundred opened this issue Jun 13, 2019 · 3 comments
Labels
bug Something isn't working

Comments

@cornhundred
Copy link
Contributor

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.

@cornhundred cornhundred added the bug Something isn't working label Jun 13, 2019
@cornhundred
Copy link
Contributor Author

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

@cornhundred
Copy link
Contributor Author

Also, slow down zooming a little by default (maybe allow this to be adjusted by users).

@cornhundred cornhundred changed the title failsafe camera resetting with custom zoom/pan camera update and resetting with custom zoom/pan Nov 7, 2019
@cornhundred
Copy link
Contributor Author

cornhundred commented Nov 10, 2019

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);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant