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>
This makes sense and is easy to implement.
Suggested-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
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>
This was requested on the mailing list and it makes sense to
have it. Of course, not all charts make sense: e.g. a plot dive-#
vs. count is a bit redundant...
Sadly, this can't use the generic IntRangeBinner, because dive-#s
start at 1, not 0.
Suggested-by: Christof Arnosti <charno@charno.ch>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This was requested on the mailing list. Reduce code size somewhat
by deriving the binner and the variable classes from common
base classes with a mean-vs-max flag.
Suggested-by: Christof Arnosti <charno@charno.ch>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The grid is based on the axis ticks. If labels in histogram
axes were skipped (because there are too many bins), it could
happen that the grid was incomplete, because the first and/or
last tick were missing. Add these explicitly.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The info box was placed either above or below the mouse-pointer.
If the pointer is at the center and the infobox higher than
half the chart, it would cross the border. Detect this case
and place the info box at the center.
Same logic for right/left, though that should typically not
happen.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The coordinates of these were calculated when creating the feature.
This is wrong, because the min/max values of the axes can change
on resize to get "nice" number. Therefore, recalculate after resizing.
This means that the general "LineMarker" class has to be split into
two classes, one for regression lines and one for median/mean
markers.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Easy enough to implement, but one weirdness:
To get the height of the rotated text, one has to access the
width() member of the boundingRect. I'm not sure if that makes
sense, but so be it.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Replace by custom implementation, with the ultimate goal to
remove the QtCharts module. This doesn't yet display axis
titles or a grid.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The bars were set to the z-value of the labels. Not an issue,
since the labels are generated after the bars and therefore
plot later. Still, do the right thing.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Since we want to get rid of QtCharts, we have to render our own
title. Simply keep around a QGraphicsSimpleTextItem and put in
the center of the chart. Define the borders to the scene as
constants.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
In the future we want to use our own axis implementation to
convert from/to screen coordinates. For this purpose, we
need to save the axes with the series. Especially if we want
to support multiple series on different axes.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
The chart was passed as argument to the function recalculating
the axis labels. Instead, pass the chart in the constructor of
the axes and save it. This gains us flexibility for the future:
There will be more functions that need to access the chart (e.g.
resizing of the axes).
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
We don't really give a user visible error message which is kind of a problem,
but at least we don't crash anymore.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Recently code was added to reset variable 1 binner if the second
variable does not support an unbinned first variable.
It forgot to check whether a binner was already set. Do this.
But validate the old binner first!
This code is extremely fragile and will have to be redone.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>