Commit graph

89 commits

Author SHA1 Message Date
Berthold Stoeger
122092707c statistics: delete chart items when root node is deleted
When reparenting the statistics widget, QtQuick deletes
the rootNode and all the child nodes. It is unclear whether
this is a bug or intended behavior. In any case, it means
that the pointers to QSG nodes in the chart items become
stale.

To avoid this, delete all chart items in the root node's
destructor, before QtQuick can do anything. It is unclear
from which context this is called (render or UI) and whether
this is even valid. In some tests, it seemed to work.

The difficulty is that all the stale pointers to chart items
have to be deleted as well. All in all, the QSG memory
management is a big nuisance and very brittle.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-02-06 10:00:39 -08:00
Berthold Stoeger
54ff31f5c1 statistics: do resizing in UI thread, not render thread
The updatePaintNode() function, which is run on the render
thread detected a geometry change and initiated recalculation
of the chart layout.

This means that plotAreaChanged() was called in two different
thread contexts, which is questionable. Instead, hook into
the geometryChanged() function and recalculate the chart items
there.

This fixes a rendering bug, because the old code would first
delete unneeded items and then rerender the chart. Thus, old
grid and tick items were still visible.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-21 13:42:14 -08:00
Dirk Hohndel
f4d806c77a Merge branch 'tvalue' 2021-01-20 09:54:03 -08:00
Dirk Hohndel
29060feaa8 statistics/legend: fix dragging legend on touch screen
While this didn't appear to be needed when dragging the legend with a
mouse, on a touch screen for some reason the drag ended after 30 pixels
either way horizontally (but no apparent limit vertically). By setting
this flag to true, drags on a tablet appear to work as expected.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
4f58e9aa62 statistics: render bar and pie labels onto fill color
The labels in bar an pie charts are realized as individual
QSG pixmap nodes with an alpha channel. Sadly, rendering
bright labels onto a transparent background gives very
ugly artifacts.

As a stop gap measure, until the problem is understood,
render on a background with the color of the pie slice
or bar.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
2e2019dea7 statistics: move colors to statscolors.h
Most colors were already collected there, but a few were dispersed
throughout the source files.

For future themeability, move the remaining colors to this common
place.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
ba259fb1d6 statistics: make confidence area and regression line opt-in
This is not perfect - the polygon of the confidence area is
calculated even if it is not shown. Oh well.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
ff536e98fc statistics: don't replot chart when changing features
Up to now, when the user changed the visibility of chart features
(legend, quartiles, labels, etc.) the whole chart was replot.
Instead, only change the visibility status of these items.

After all, this modularity is one of the things the conversion
to QSG was all about.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
e32e6d63a7 statistics: leak textures on exit
The scatter plot items shared their textures. These were
std::unique_ptrs and cleaned up on exit. Owing to QSG's
broken memory model, freeing the textures after QApplication
terminated its threads led to crashes. Therefore, leak the
textures. Not satisfying, but ultimately harmless and better
than a crash.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
9e61a6372a statistics: fix range in categorical axes
Fix a bug that was fixed in b5c8d0dbb4 and reintroduced in
e7907c494f. Here is the original commit message:

The range for a one-bin chart is [-0.5,0.5], thus the range
in an n-bin chart is [-0.5,n-0.5], not [-0.5,n+0.5].

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
db69c38245 statistics: refactor QSG memory management
The code was wrong, because it deleted the ChartItems in the
main UI thread, not the render thread. This would delete the
QSG nodes in the UI thread and then crash on mobile.

Therefore refactor this part of the code by adding the
items to be deleted to a list that will be deleted by the
render thread.

As a drop in replacement of std::unique_ptr, implement
a silly ChartItemPtr class, which auto-initializes to null.

This turns the deterministic and easily controlled memory
management into a steaming pile of insanity. Obviously,
this can be made much more elegant, but this has to do for now.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
9d3de1801e statistics: remove QSceneGraph
All items are now painted with QSG.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
51f67c6350 statistics: remove float z-value
These values were used for items on the QGraphicsScene and
have been replaced by integer values used on the QSG scene.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
01accbfa3e statistics: replace chart title by QSG node
This one is trivial, since everything is there already:
Replace the QGraphicsSimpleTextItem with a ChartTextItem.
Only few functions have to be renamed.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
2480d3b7e0 statistics: remove QGraphicsView from StatSeries
All series are converted to QSG. Thus, the pointer to the
QGraphicsView can be removed from the common base class.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
b068b2b0e7 statistics: replace PieSeries by QSG nodes
Since there are no disk-segment QSG primitives (one could draw
a triangle fan, but that doesn't seem optimal), this draws
into a pixmap and blits that as a QSG node.

Since this is the only series without axis, it needs a function
that returns the size of the plot area. This didn't exist, so
add it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
b07a7fe5f1 statistics: convert scatter series to use QSG
The original plan to reuse the ChartPixmapItem for the
scatteritems was dumped, because it is unclear if the
textures are shared if generated for each item.

Instead, a new ChartScatterItem was created, where all
items share the same textures (one for highlighted,
one for non-highlighted). This means that the rendering
of the scatter items is now done in the chartitem.cpp
file, which feels like a layering violation. Not good,
but the easiest for now.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
409f159e1d statistics: convert box-and-whiskers plot to QSG
This is lazy: Derive from the bar chart item and add whiskers
in the subclassed render() function. The code is ugly, because
the base class function clears the dirty flags and therefore
the derived class has to remember them. Oh well.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
5d5ebfcf3c statistics: implement showing / hiding of items in QSG
To replace the QGraphicsScene, we need the possibility of
showing and hiding items.

Turns out, the QSG API is completely insane.

Whether an item should be shown is queried by the virtual
function isSubtreeBlocked(), which is supposed to be
overriden by the derived classes.

However, the common nodes for rectangles and pixmaps are
supposed to be created by QQuickWindow, for hardware
optimization. This gives nodes that cannot be derived
from and therefore whether the item is shown or not cannot
be controlled.

There are therefore two distinct cases to consider: The
node is allocated by the code directly or indirectly by
QQuickWindow.

In the latter case, we use a proxy node with the only
purpose of having a "visible" flag and add the obtained
node as a child.

This madness is performed with template trickery to get
unified code.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
b42e19e36b statistics: convert bar series to QSGNodes
To this end, two new ChartItems were added: A "bar" (a rectangle
with a border) and a "text" (multiple lines of text).

It turns out that the text on the bars now looks atrocious.
The reason appears to be that the antialiasing of the font-rendering
does not blend into the alpha channel, but into a supposed
background color? This will have to be investigated.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
2008857660 statistics: render regression item using QSGNode
Render the confidence area and the regression line into a pixmap
and show that using a QSGNode.

It is unclear whether it is preferred to do it this way or to
triangulate the confidence area into triangles to be drawn by
the shader.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
faf3e7079d statistics: keep track of dirty items in double-linked list
So far the items to be recalculated in the drawing thread
had a "dirty" flag and were kept in one array par z-level.

Once the series are implemented in terms of QSGNodes, there
may lots of these items. To make this more efficient when
only one or two of these items change (e.g. highlighting due
to mouseover), keep the dirty items in a linked list.

Of course, this makes the draw first version of the chart
less efficient.

There are more fancy ways of implementing the double-linked
list, but the few ns gained in the render thread are hardly
worth it.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
ada5e8a49d statistics: turn axes into QSGNode
Render the labels and the title into a pixmap and render
the ticks and the base line using individual QSGNodes.
Attempting to render the ticks likewise into the pixmap
gave horrible results, because (quite obviously) rendering
with QPainter and the QSG shader gives non-matching ticks
and grid lines.

The memory management had to be changed a bit: The ChartItems
were collected in the root QSGNode. However, the axes are added
before the first plotting, so this node might not exist.
Therefore, store the axes in the StatsView object.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
9b7565e81a statistics: turn ChartGrid into QSGNodes
Turn the background grid into QSGNodes. Each grid line is
represented by a QSG line item. An alternative would be
drawing the grid into a QImage and blasting that onto the
screen. It is unclear which one is preferred.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
c74975632e statistics: draw background using a QSGRectangle node
Currently, the background was drawn as solid color onto
the chart-scene. This is of course incompatible with doing
the grid as QSGNodes. Therefore, make the scene image
transparent and use a QSGRectangle as background color.

We could also simply omit the background and show the
widget's background. However, that would mean setting
the background color in two seperate code paths
(desktop and mobile). I found no way of directly setting
the background of the QQuickItem.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
218c844ad4 statistics: convert HistogramMarkers to QSGNodes
This is in analogy to the quartile markers.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
790d2b2ddb statistics: convert QuartileMarkers to QSGNodes
Slowly converting the QGraphicsScene items to QSGNodes to
avoid full replot of the scene.

This adds a new abstraction for line-nodes. Since the render()
function here is fundamentally different from the pixmap-nodes
we had so far, this has to be made virtual.

Also, move the quartile markers to their own source file,
since the StatsView source file is quite huge already.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
b1c0d42408 statistics: remember position of legend when resizing
The position of the legend was reset when resizing. This was
OK as long as the legend wasn't movable.

To avoid resetting the position, store the center position
of the legend relatively to the size of the canvas. On
resize restore the center to the same relative size.

To avoid code duplication, move the sanitizing of the
coordinates from the StatsView to the Legend.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
e1c0cace95 statistics: add notion of Z-value to chart items
The chart items were drawn in order of creation. To control this,
add a notion of Z-value. In contrast to QGraphicsScene, make
this a small integer value.

To controll order of drawing, a plain QSGNode is created for
every possible Z-Value and items are added to these nodes.
Thus, items are rendered by Z-value and if the Z-value is equal
by order of creation.

Likewise split the list of chart-items into Z-values, so that
items can be quickly unregistered: The items that will be
removed individually will usuall be part of Z-levels with only
few items (e.g. legend, infobox). Z-levels with many items
(notably the series) will always be fully rebuilt.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
785d5189f6 statistics: turn infobox into a QSGNode
A small step in converting from QGraphicsScene to QQuickItem.
This is the second item to be converted (after the legend)
and for now items are drawn in order of creation, which means
that the infobox is on top of the legend. This will have
to be made deterministic in follow-up commits.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
d7878dad36 statistics: pass view to series
The series were passed a pointer to the QGraphicsScene to add
their item. In the future these items will be replaced by
QSGNodes. To add these, the series need a reference to the StatsView.
Therefore pass it in the constructor. Once everything is
replaces by QSGNodes, remove the QGraphicsScene member.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
1a869833d8 statistics: implement moving of legend
Catch mouse move events and move the legend accordingly.
Currently, this is the only item that can be dragged and
therefore there is no need of doing some kind of fancy
interface. Simply keep a pointer to the legend if it is
dragged.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Berthold Stoeger
2b961414d7 statistics: draw legend as a QSGNode
In order not to waste CPU by constantly rerendering the chart,
we must use these weird OpenGL QSGNode things. The interface
is appallingly low-level and unfriendly.

As a first test, try to convert the legend. Create a wrapper
class that represents a rectangular item with a texture
and that will certainly need some (lots of) optimization.

Make sure that all low-level QSG-objects are only accessed
in the rendering thread. This means that the wrapper has
to maintain a notion of "dirtiness" of the state. I.e.
which part of the QSG-objects have to be modified.

From the low-level wrapper derive a class that draws a rounded
rectangle for every resize. The child class of that must then
paint on the rectangle after every resize.

That looks all not very fortunate, but it displays a
legend and will make it possible to move the legend
without and drawing operations, only shifting around
an OpenGL surface.

The render thread goes through all chart-items and
rerenders them if dirty. Currently, on deletion
of these items, this list is not reset. I.e. currently
it is not supported to remove individual items.
Only the full scene can be cleared!

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-20 08:47:18 +01:00
Dirk Hohndel
ecf3ac12eb mobile/statistics: make icons available in mobile app
They were bundled via the desktop qrc, but they need a shared qrc that
can be used in both apps.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-19 12:35:29 -08:00
Robert C. Helling
625fa139a9 Corret the t-value von a C.I. of 95%
It's 1.960, not 2

Suggested-by: Willem Ferguson <willemferguson@zoology.up.ac.za>
Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-19 20:54:19 +01:00
Robert C. Helling
25cfd852b9 Add back central line for linear regression
Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-14 20:51:23 +01:00
Robert C. Helling
d6712bc5ac Plot proper confidence regions
I was coninced that that rather than doing an order of
magnitude estimate of the confidence region it's better
to have the correct concave shapes that indicate the
95% confidence level for the regression line.

It also turned out that the previous expression was
missing a factor of 1/sqrt(n).

Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-14 20:51:23 +01:00
Robert C. Helling
5775bd7b27 Silence some compiler warnings
This is what clang suggested in compiler warnings.

Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-14 20:51:23 +01:00
Robert C. Helling
d83c9b5246 Indicate goodness of fit of regression line
The goodness of fit of a regression line is the percentage
of the variance of the y values that is explained by the
dependence on the x values.

Set the alpha value of the regression line to this goodness
of fit.

Further, set the width of the regression line to a standard
deviation of the values from the regression line valies.

Signed-off-by: Robert C. Helling <helling@atdotde.de>
2021-01-14 20:51:23 +01:00
Dirk Hohndel
e610fb2481 statistics: add count property to chart list
In order to be able to correctly size the chart type popup, we'll need
access to the total count or rows as a property that signals changes to
QML.

The hack to use rowCount() as the READ function requires that rowCount()
can be called without argument, therefore the addition of a default
parent.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-13 11:39:36 -08:00
Berthold Stoeger
46bb242c69 mobile/statistics: export ChartListModel to QML
For QML, the roles have to be associated dynamically with
name. Moreover, the model has to be registered as a QML
type to make it accessible from QML.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-13 11:39:36 -08:00
Berthold Stoeger
108f6fed2f statistics: print ellipsis in case of too little space
In categorical axes all labels were printed leading to a big
tohu wa-bohu for two many bins. Therefore, if a label is
larger than the space between two ticks, replace by an ellipsis.

Adjust the size of the ellipsis (".", ".." or "...") to the
available space.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-11 13:29:15 +01:00
Berthold Stoeger
bdecd98ef5 statistics: consider overhang of horizontal axes
The old code didn't consider that labels can peak out of
horizontal axes if labels are under ticks.

This commit takes this into account. However, it must be
noted that this is only heuristics: Before setting the
size of the axes, the actual minimum and maximum label are
not known, because we round to "nice" numbers. But the
size of the axis can only be set after knowing the overhang,
leading to a circular dependency. Therefore, the code
currently simply uses the minimum and maximum value of
the data, hoping that the "nice" values will not format
to something significantly larger. We could do a multi-pass
scheme, but let's not for now.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-11 12:59:17 +01:00
Berthold Stoeger
233e686b92 statistics: split people binner
The people binner (called "buddies") is too coarse. Split into
buddies, dive guide and people (the old "buddies", which is
a combination of buddies and dive guide).

Reported-by: Peter Zaal <peter.zaal@gmail.com>
Reported-by: Rick Walsh <rickmwalsh@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 15:56:40 -08:00
Dirk Hohndel
b40354c7f2 build-system: compile stats code on mobile OSs
Android and iOS use qmake, so add the code to the .pro file.
This also removes all remnants of QCharts includes and uses and all the
references to QCharts in our various build systems.

That was a brief but extremely useful detour.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2021-01-10 15:16:52 -08:00
Berthold Stoeger
e7907c494f statistics: convert chart to QQuickItem
It turns out that the wrong base class was used for the chart.
QQuickWidget can only be used on desktop, not in a mobile UI.

Therefore, turn this into a QQuickItem and move the container
QQuickWidget into desktop-only code.

Currently, this code is insane: The chart is rendered onto a
QGraphicsScene (as it was before), which is then rendered into
a QImage, which is transformed into a QSGTexture, which is then
projected onto the device. This is performed on every mouse
move event, since these events in general change the position
of the info-box.

The plan is to slowly convert elements such as the info-box into
QQuickItems. Browsing the QtQuick documentation, this will
not be much fun.

Also note that the rendering currently tears, flickers and has
antialiasing artifacts, most likely owing to integer (QImage)
to floating point (QGraphicsScene, QQuickItem) conversion
problems. The data flow is
QGraphicsScene (float) -> QImage (int) -> QQuickItem (float).

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 15:16:52 -08:00
Berthold Stoeger
f6b857b8fe statistics: fix 20 m binner
Copy&paste error: the 20 m binner binned to 10 m.

Reported-by: Peter Zaal <peter.zaal@gmail.com>
Reported-by: Rick Walsh <rickmwalsh@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 14:45:12 -08:00
Berthold Stoeger
bbdd34d069 statistics: add count to box and whisker plots
When calculating the quartiles, we need the count of dives
anyway, which makes it trivial to export this value to
the frontend.

Fixes an erroneous "mean", which should be "median".

Suggested-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 14:45:12 -08:00
Berthold Stoeger
de62638b73 statistics: add tags variable
Trivially a copy of the "buddies" code.

Suggested-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 13:42:48 -08:00
Berthold Stoeger
2d864c3e9d statistics: sort dive sites
The dive sites where sorted by location in RAM, which is just
silly. Add a DiveSiteWrapper that sorts by name, though that
should probably be improved.

Suggested-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-10 13:42:48 -08:00