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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>