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

Reimplement the UI #2

Open
13 of 21 tasks
mzur opened this issue Sep 12, 2018 · 10 comments
Open
13 of 21 tasks

Reimplement the UI #2

mzur opened this issue Sep 12, 2018 · 10 comments

Comments

@mzur
Copy link
Member

mzur commented Sep 12, 2018

The UI did not age very well with Angular 1. Some parts of the UI do not work correctly any more (the spectrum viewer) and the code is hard to maintain. We decided to do a rewrite of the UI in Vue. This can be done incrementally:

  • Implement the image viewer. Maybe use OpenLayers instead of Leaflet? Features:

    • The similarity image is displayed with a (fixed) color map and the angle distance. No bright field image, edge overlay or graticule.

    • The user can choose a fixed reference point on the similarity image to update the display.

    • The user can move the mouse in "freehand mode" over the similarity image to update the reference point in realtime.

    • The user can zoom and pan the similarity image.

  • Implement the color map indicator and dynamic stretching of the color map ("color lens"). Normally we have a color map of, say, 256 values. With the similarity display, the first value represents the lowest possible similarity (e.g. 0) and the last value represents the highest possible similarity (e.g. 1). But a given similarity display does not always include the lowest or highest possible similarities. If we only have similarities between 0.25 and 0.75, dynamic stretching of the color map would stretch the full color map to 0.25-0.75 instead of 0.0-1.0.

    • The color map indicator should display the color map that is applied in the similarity display.

    • The indicator should visualize any dynamic stretching of the color map that is done in the current similarity display.

    • The indicator should display a histogram with the quantity of pixels of a given color of the map.

  • Implement the spectrum viewer. Only the currently visible interval of the spectrum should be drawn so it works efficiently. Find a way to use (fake?) scroll bars that works better than the current approach.

    • Users should be able to zoom and scroll both in x (m/z) and y (intensity) directions.

    • The x axis should display m/z values as labels.

    • The y axis shoud display intensity values (in percent) as labels.

    • On mousemove, the current m/z and intensity values of the mouse position should be shown.

  • Implement selectable spectrum ranges. A range is an interval of m/z values in the spectrum.

    • Users should be able to select one or more ranges in the spectrum viewer.

    • There should be a list of currently selected ranges where existing ranges can be deleted.

    • Ranges can be "active" (default) and "inactive". If there are active ranges, the similarity calculation should be limited to the combined m/z values of active ranges.

    • Users should be able to import and export the m/z intervals of ranges as CSV files.

  • Implement the "mean" display mode. This mode should display the mean image (with color map) of all active m/z ranges instead of the similarity display.

  • Implement the "direct" display mode. This mode should display the single m/z channel (with color map) that is selected on mousemove in the spectrum viewer.

@mzur
Copy link
Member Author

mzur commented Oct 12, 2018

Quick recap 2018-10-12:

  • Fetching of the TXT data file is done here
  • Loading individual image files with an HTML image element is done around here
  • Storing the array of image elements on the GPU is done here (after glmvilib has been initialized)
  • Public functions of glmvilib can be found starting from this line

@mzur
Copy link
Member Author

mzur commented Oct 24, 2018

Recap 2018-10-24:

  • Rendering of a single frame of the angle distance image is done like this:

    1. Glmvilib expects an object with specific properties and functions for each shader program. The object for the angle distance shader is defined here. The new instance of this object is created here.
    2. Each shader program object needs to be registered in glmvilib before it can be used for rendering. This happens here.
    3. Whenever the rendered image should be updated, glmvilib.render needs to be called with the IDs of the shader programs as arguments. The shader programs are executed in the given order of the arguments. This is done here in the old code. A vanilla JS call with an array of shader program IDs can look like this:
      glmvilib.render.apply(null, ['angle-dist', 'other-id', 'third-id']);
  • You can find a working example for how to display a static canvas element in OpenLayers in this gist. It requires a simple custom source definition (ImageCanvas.js) that extends the default ImageStatic of OpenLayers. You can run the gist code as described in the OpenLayers introduction but it should be straight forward to integrate it in your Webpack setup, too.

  • An event listener for the mouse position can be defined like this:

    const map = Map({/*...*/});
    map.on('pointermove', function (event) {
       let [x, y] = event.coordinate;
    });

    Note that the mouse position may be negative (if the mouse is outside the extent of the canvas). Also the OpenLayers y-coordinates start from the bottom up whereas WebGL y-coordinates start from the top down so you have to invert the coordinates like it is done here (after normalizing the x- and y-coordinates to the interval [0, 1]).

  • Any issues with glmvilib and constructor which is a reserved keyword in ES6 may be resolved with a new release of glmvilib.

@mzur
Copy link
Member Author

mzur commented Nov 7, 2018

Recap 2018-11-07:

Smoothing for the zoom in OpenLayers can be disabled like this.

On chaining multiple shaders: Instead of rendering to the null framebuffer, the AngleDist shader renders to a texture. This texture is picked up by the ColorMap shader to apply the color map.

Before the shader can render to a texture, the texture has to be created. This happens here through the setUpDistanceTexture function. The function makes use of the glmvilib assets object and the helpers to create a new framebuffer and texture, and bind the two together. Whenever a shader uses the new framebuffer as target, it will render to the texture. The pointer to the framebuffer is stored in assets.framebuffers.distances, which is used here to define the render target of the AngleDist shader.

The color map is applied in the ColorMap shader. Originally, this shader used the output of the ColorLens shader as input source but right now this should be changed to use assets.textures.distanceTexture of AngleDist, instead.

The color map itself is loaded as Uint8Array by the colorMap service from the CSV file. In our case it can be hard-coded somewhere. The texture that holds the color map is created here. There is no need to dynamically update the color map with the updateColorMaps function, it can be set directly in the setUp function (replace null with the Uint8Array).

Edit: The this object should now be usable as intended in setUp, callback and postCallback functions of shaders. Here is the latest release of glmvilib.

@fabian-ep
Copy link
Collaborator

fabian-ep commented Nov 9, 2018

Edit: The this object should now be usable as intended in setUp, callback and postCallback functions of shaders. Here is the latest release of glmvilib.

Klappt, danke!

Die ColorMap auch schon (fast). ;)
bildschirmfoto 2018-11-09 um 12 21 25

@mzur
Copy link
Member Author

mzur commented Nov 12, 2018

Recap 2018-11-12:

Die RGB fire.csv gibt es hier.

Infos zum "dynamic stretching" der Color Map (2. oben):

Für das Stretching werden die Werte der aktuell minimalen und maximalen Distanz benötigt. Diese müssen nach jedem Rendern mit AngleDist aus dem WebGL Framebuffer ausgelesen werden. In der alten Version läuft das über die postCallback Funktion nach jedem Rendern von RGBSelection. In der neuen Version kann dies einfach nach AngleDist passieren.

Die Werte werden mit glmvilib.getPixels aus dem aktuell aktiven Framebuffer ausgelesen und in ein Uint8Array geschrieben. Aus den ausgelesenen Distanzwerten wird dann ein Histogramm erstellt, in dem für jede mögliche Distanz (0 bis 255) die Zahl der mit dieser Distanz vorkommenden Pixel enthalten ist. Aus diesem Histogramm werden wiederum die Werte der aktuell minimalen und maximalen Distanz ermittelt. Der alte Code ist für mehrere gleichzeitige Marker/Farbkanäle ausgelegt. Wir brauchen aber nur einen.

Die aktuelle Color Map, das ermittelte Histogramm und die minimale/maximale Distanz werden für die Darstellung des "Color Map Indicator" im UI verwendet (colorScale, colorScaleCanvas, colorScaleHistogram). Das colorScaleCanvas ist mit WebGL implementiert. Für einen einzigen Marker kann man es aber ganz einfach wie das Histogramm mit dem Canvas 2D Context implementieren.

Die minimale/maximale Distanz wird außerdem in dem ColorLens Shader verwendet. Dieser kümmert sich dann darum, dass die von AngleDist ermittelten Distanzen auf [0, 1] gestreckt werden. Zum Verständnis, eine Streckung würde Formal so aussehen: f(x) = x - x_min / (x_max - x_min). Das ist hier etwas umständlich implementiert, um eine Division zu vermeiden, die angeblich langsamer in WebGL ist. Die Performance spielt aber an dieser Stelle gar keine so große Rolle. Trotzdem muss aber eine Division durch 0 vermieden werden.

@mzur
Copy link
Member Author

mzur commented Nov 26, 2018

Recap 2018-11-26:

Sonstige Features über die wir gesprochen haben:

  • Strecken und Verschieben der Color Map Anzeige, um den aktuellen Effekt der Color Lens darzustellen.
  • Linien am oberen und unteren Ende der Color Map Anzeige.
  • Visueller Indikator/Marker des aktuell ausgewählten Pixels.

Die Werte für den Spectrum Viewer werden mit dem SelectionInfo Shader ermittelt. Dieser wird in dem Marker ausgeführt, wenn immer ein Mausklick passiert. Damit erhält man einen Intensitätswert (0 - 255) für jeden Kanal des Spektrums, wobei 0 einer Intensität von 0 % und 255 einer Intensität von 100 % entspricht. Die Label der x-Achse (d.h. die Namen der Kanäle) werden aus den Dateinamen der Input-PNGs ermittelt.

Folgende Features sollte der Spectrum Viewer haben:

  • Man kann in Richtung der x-Achse (nice to have: auch y-Achse) zoomen und scrollen.
  • Es werden die Label der x-Achse (Namen der Kanäle) und y-Achse (Intensitäten) angezeigt.
  • Es wird der Name des Kanals und die Intensität der aktuellen Mausposition im Spectrum Viewer angezeigt.

Folgendes sollte leicht zu implementieren sein (oder schon funktionieren):

  • Man kann ein oder mehrere Bereiche im Spectrum Viewer selektieren. Ein Bereich ist durch einen Anfangskanal und einen Endkanal definiert und wird im Spectrum Viewer visuell hervorgehoben.

@fabian-ep
Copy link
Collaborator

Mir ist beim implementieren der ColorScale aufgefallen, dass eine Handvoll Pixel schwarz erscheinen, wenn man mit der Maus darüber fährt. Ist da vlt. noch ein Fehler in der ColorLens (Division durch 0 oder so)?

@mzur
Copy link
Member Author

mzur commented Nov 27, 2018

Ja, das habe ich auch schon bemerkt. Das ist allerdings in der alten Version auch schon so, also musst du dich da nicht drum kümmern (es sei denn du möchtest 😉).

@mzur
Copy link
Member Author

mzur commented Nov 27, 2018

Wobei so ein schwarzes Pixel den interessanten Effekt hat, dass das schwarze div unter der Color Map die Höhe 0 hat (und bei 0 auch auch ein Balken im Histrogramm sichtbar ist), die Color Map aber trotzdem gestaucht ist:

screen shot 2018-11-27 at 08 29 58

Dieser Fall sollte zwar nicht auftreten, aber vielleicht ist hier ausnahmsweise nicht 1 der höchste Wert. Funktioniert dann vielleicht das Verschieben der Color Map nach unten (mit dem weißen div) noch nicht richtig?

@mzur
Copy link
Member Author

mzur commented Dec 4, 2018

Recap 2018-12-04:

Folgende Features haben wir besprochen:

  • Spectrum: Limitierung von y-Achse auf [0, 100] und von x-Achse auf [0, channels].
  • Spectrum: Anzeige der x-Achsen Labels.
  • Spectrum: Anzeige von horizontalen Linien z.B. bei y aus [25, 50, 75].
  • Spectrum: Anzeige als eine durchgehende Linie anstelle von separaten Punkten. Ggf. zusätzlich Punkte bei höheren Zoomstufen.
  • Readme mit Anleitung für Development- und Production-Builds.
  • Fehler in ColorScale.
  • Visueller Indikator/Marker des aktuell ausgewählten Pixels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants