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

Hidden scatterplot instance throws an error when shown interactively #20

Closed
plankter opened this issue Jun 4, 2020 · 10 comments
Closed
Labels
bug Something isn't working

Comments

@plankter
Copy link

plankter commented Jun 4, 2020

I could reproduce the issue #17. The problem occurs when plot is created, but hidden (in my case i created multiple plots in different Vuetify tabs). Probably this is due to canvas.getBoundingClientRect() returning (0, 0) somewhere in regl-scatterplot code in such cases.

I created a small demo to show this behavior. Second canvas is hidden, and then should be displayed when one makes a selection on the first canvas. But instead it'll throw errors like the following when mouse is over second canvas:

Uncaught TypeError: Cannot read property '0' of null
    at Module.transformMat4 (vec4.js:529)
    at getScatterGlPos (index.js:201)
    at raycast (index.js:206)
    at mouseMoveHandler (index.js:372)

Sorry if the demo is a bit awkward.

https://github.com/plankter/regl-scatterplot-two-instances

@flekschas flekschas added the bug Something isn't working label Jun 4, 2020
@flekschas
Copy link
Owner

Thanks for providing a demo. I'll take a look.

By the way, is this essentially the same as #17?

@plankter
Copy link
Author

plankter commented Jun 4, 2020

Yes, i think it is the same issue.

@flekschas
Copy link
Owner

I am not sure why you decided to use .hidden but I don't think it should be used in this scenario: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/hidden

This is quite different from using the CSS property display to control the visibility of an element. The hidden property applies to all presentation modes and should not be used to hide content that is meant to be directly accessible to the user.

Inappropriate use cases include:

  • Hiding panels in a tabbed dialog box
  • Hiding content in one presentation while intending it to be visible in others

When I replace canvas2.hidden = true; with canvas2.style.opacity = 0;, everything works as expected.

I'll try to find out why but I also suspect it's because the canvas element is 0x0 pixels in size.

@flekschas
Copy link
Owner

It seems to be related to the fact that canvas element does not have a size. Everything works also fine when I use canvas2.style.visibility = 'hidden'.

It appears as if canvas.hiddenputs the canvas element into a not-normal state. Do you know what the functional difference is to visually just hiding the canvas element?

@plankter
Copy link
Author

plankter commented Jun 4, 2020

I was trying to replicate behavior of Vue's v-show conditional rendering (https://vuejs.org/v2/guide/conditional.html#v-show) and also Vuetify uses the same approach in their Tabs component (https://vuetifyjs.com/en/components/tabs/).

In my application i have multiple tabs with separate regl-scatterplot instances. When user makes a selection on one plot, other plots are updated as well (i.e. subsetting tSNE vs PCA plots) because of cross-filtering. I discovered that when both plots are visible, then everything works properly, but if i use tabs or v-show directive, then plots, although they were initialized, are updated, but any interaction with them throws the mentioned exception.

Plots have zero width and height when hidden due to getBoundingClientRect() method, i suppose. Maybe predefined size can be used instead? I mean the size defined in createScatterplot method.

UPDATE: you are right about hidden property, but according to Vue docs:

The difference is that an element with v-show will always be rendered and remain in the DOM; v-show only toggles the display CSS property of the element.

and i still have this problem with v-show directive.

@plankter
Copy link
Author

plankter commented Jun 4, 2020

Here is a short screencast demonstrating the issue. As you can see, the first plot (with cell background image) is updated properly according to cell mask after i select cells on the PCA plot, but any mouse movement (zoom/panning) is throwing an error (can be seen on console panel):

https://drive.google.com/file/d/1HfXmnEAiiVFT61rKoUMlgBZiQzCIq2Yo/view

@flekschas
Copy link
Owner

flekschas commented Jun 4, 2020

Okay, the issue is that you need to call scatterplot.set({ width, height }). Why? The canvas resized after you do canvas.hidden = false;. regl-scatterplot doesn't recognize resize events on its own, that's the responsibility of the wrapper application.

E.g.:

canvas2.hidden = false;
window.requestAnimationFrame(() => {
  const { width, height } = canvas2.getBoundingClientRect();
  // Second scatterplot
  scatterplots[1].set({ width, height });
});

Besides, another simple fix is to manually set a proper width and height before hiding the canvas.

E.g., the following will work fine:

window.requestAnimationFrame(() => {
  const { width, height } = canvas2.getBoundingClientRect();
  canvas2.width = width * window.devicePixelRatio;
  canvas2.height = height * window.devicePixelRatio;
  canvas2.hidden = true;
});

The error was caused by dom-2d-camera because the camera's bounds weren't updated and, for example, for zooming one would divide by height, which was still 0 and therefore resulted in a NaN.

I'll update the README.md to make it more obvious that the wrapping application needs to handle the resizing.

@flekschas flekschas changed the title Displaying background image throws an error when plot is hidden Hidden scatterplot instance throws an error when shown interactively Jun 4, 2020
flekschas added a commit that referenced this issue Jun 4, 2020
@flekschas
Copy link
Owner

Let me know if this solves your issue.

@plankter
Copy link
Author

plankter commented Jun 5, 2020

Thank you for the suggestion, it really worked. I found out that Vuetify has a very useful directive v-intersect (https://vuetifyjs.com/en/directives/intersect/) that is using Intersection Observer browser API. I just added v-intersect="onIntersect" to canvas element with a method:

onIntersect(entries, observer, isIntersecting) {
  if (isIntersecting) {
    const canvas = this.$refs.canvas as Element;
    const { width, height } = canvas.getBoundingClientRect();
    this.scatterplot.set({ width, height });
  }
}

And issue is gone! Thanks again for your time and efforts.

@flekschas
Copy link
Owner

Awesome! I am glad it's working now. I added a note to the README.md to give people a hint.

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

2 participants