mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
statistics: highlight selected bar
When all items of a bar in a bar chart are selected, highlight them by overlaying with a checkerboard pattern. A gray checkerboard seems to work reasonably well, regardless of base color. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
d63d4cd3c3
commit
06a091643e
5 changed files with 107 additions and 5 deletions
|
@ -6,6 +6,7 @@
|
||||||
#include "statstranslations.h"
|
#include "statstranslations.h"
|
||||||
#include "statsview.h"
|
#include "statsview.h"
|
||||||
#include "zvalues.h"
|
#include "zvalues.h"
|
||||||
|
#include "core/dive.h"
|
||||||
#include "core/selection.h"
|
#include "core/selection.h"
|
||||||
|
|
||||||
#include <math.h> // for lrint()
|
#include <math.h> // for lrint()
|
||||||
|
@ -183,11 +184,14 @@ void BarSeries::Item::highlight(int subitem, bool highlight, int binCount)
|
||||||
subitems[subitem].highlight(highlight, binCount);
|
subitems[subitem].highlight(highlight, binCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For single-bin charts, selected items are marked with a special fill and border color.
|
||||||
|
// For multi-bin charts, they are marked by a differend border color and border width.
|
||||||
void BarSeries::SubItem::highlight(bool highlight, int binCount)
|
void BarSeries::SubItem::highlight(bool highlight, int binCount)
|
||||||
{
|
{
|
||||||
fill = highlight ? highlightedColor : binColor(bin_nr, binCount);
|
fill = highlight ? highlightedColor : binColor(bin_nr, binCount);
|
||||||
QColor border = highlight ? highlightedBorderColor : ::borderColor;
|
QColor border = highlight ? highlightedBorderColor : ::borderColor;
|
||||||
item->setColor(fill, border);
|
item->setColor(fill, border);
|
||||||
|
item->setSelected(selected);
|
||||||
if (label)
|
if (label)
|
||||||
label->highlight(highlight, bin_nr, binCount, fill);
|
label->highlight(highlight, bin_nr, binCount, fill);
|
||||||
}
|
}
|
||||||
|
@ -243,9 +247,10 @@ std::vector<BarSeries::SubItem> BarSeries::makeSubItems(std::vector<SubItemDesc>
|
||||||
int bin_nr = 0;
|
int bin_nr = 0;
|
||||||
for (auto &[v, dives, label]: items) {
|
for (auto &[v, dives, label]: items) {
|
||||||
if (v > 0.0) {
|
if (v > 0.0) {
|
||||||
|
bool selected = std::all_of(dives.begin(), dives.end(), [] (const dive *d) { return d->selected; });
|
||||||
res.push_back({ view.createChartItem<ChartBarItem>(ChartZValue::Series, barBorderWidth, horizontal),
|
res.push_back({ view.createChartItem<ChartBarItem>(ChartZValue::Series, barBorderWidth, horizontal),
|
||||||
std::move(dives),
|
std::move(dives),
|
||||||
{}, from, from + v, bin_nr });
|
{}, from, from + v, bin_nr, selected });
|
||||||
if (!label.empty())
|
if (!label.empty())
|
||||||
res.back().label = std::make_unique<BarLabel>(view, label, bin_nr, binCount());
|
res.back().label = std::make_unique<BarLabel>(view, label, bin_nr, binCount());
|
||||||
}
|
}
|
||||||
|
@ -418,3 +423,19 @@ bool BarSeries::selectItemsUnderMouse(const QPointF &pos, bool)
|
||||||
setSelection(dives, dives.empty() ? nullptr : dives.front());
|
setSelection(dives, dives.empty() ? nullptr : dives.front());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BarSeries::divesSelected(const QVector<dive *> &)
|
||||||
|
{
|
||||||
|
for (Item &item: items) {
|
||||||
|
for (SubItem &subitem: item.subitems) {
|
||||||
|
bool selected = std::all_of(subitem.dives.begin(), subitem.dives.end(), [] (const dive *d) { return d->selected; });
|
||||||
|
if (subitem.selected != selected) {
|
||||||
|
subitem.selected = selected;
|
||||||
|
|
||||||
|
Index idx(&item - &items[0], &subitem - &item.subitems[0]);
|
||||||
|
bool highlight = idx == highlighted;
|
||||||
|
item.highlight(idx.subitem, highlight, binCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -104,6 +104,7 @@ private:
|
||||||
double value_from;
|
double value_from;
|
||||||
double value_to;
|
double value_to;
|
||||||
int bin_nr;
|
int bin_nr;
|
||||||
|
bool selected;
|
||||||
QColor fill;
|
QColor fill;
|
||||||
void updatePosition(BarSeries *series, bool horizontal, bool stacked,
|
void updatePosition(BarSeries *series, bool horizontal, bool stacked,
|
||||||
double from, double to, int binCount);
|
double from, double to, int binCount);
|
||||||
|
@ -146,6 +147,7 @@ private:
|
||||||
bool stacked);
|
bool stacked);
|
||||||
std::vector<QString> makeInfo(const Item &item, int subitem) const;
|
std::vector<QString> makeInfo(const Item &item, int subitem) const;
|
||||||
int binCount() const;
|
int binCount() const;
|
||||||
|
void divesSelected(const QVector<dive *> &) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <QSGImageNode>
|
#include <QSGImageNode>
|
||||||
#include <QSGRectangleNode>
|
#include <QSGRectangleNode>
|
||||||
#include <QSGTexture>
|
#include <QSGTexture>
|
||||||
|
#include <QSGTextureMaterial>
|
||||||
|
|
||||||
static int round_up(double f)
|
static int round_up(double f)
|
||||||
{
|
{
|
||||||
|
@ -140,6 +141,7 @@ static QSGTexture *createScatterTexture(StatsView &view, const QColor &color, co
|
||||||
static QSGTexture *scatterItemTexture = nullptr;
|
static QSGTexture *scatterItemTexture = nullptr;
|
||||||
static QSGTexture *scatterItemSelectedTexture = nullptr;
|
static QSGTexture *scatterItemSelectedTexture = nullptr;
|
||||||
static QSGTexture *scatterItemHighlightedTexture = nullptr;
|
static QSGTexture *scatterItemHighlightedTexture = nullptr;
|
||||||
|
static QSGTexture *selectedTexture = nullptr; // A checkerboard pattern.
|
||||||
|
|
||||||
QSGTexture *ChartScatterItem::getTexture() const
|
QSGTexture *ChartScatterItem::getTexture() const
|
||||||
{
|
{
|
||||||
|
@ -330,6 +332,12 @@ void setPoint(QSGGeometry::Point2D &v, const QPointF &p)
|
||||||
v.set(static_cast<float>(p.x()), static_cast<float>(p.y()));
|
v.set(static_cast<float>(p.x()), static_cast<float>(p.y()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPoint(QSGGeometry::TexturedPoint2D &v, const QPointF &p, const QPointF &t)
|
||||||
|
{
|
||||||
|
v.set(static_cast<float>(p.x()), static_cast<float>(p.y()),
|
||||||
|
static_cast<float>(t.x()), static_cast<float>(t.y()));
|
||||||
|
}
|
||||||
|
|
||||||
void ChartLineItem::render()
|
void ChartLineItem::render()
|
||||||
{
|
{
|
||||||
if (!node) {
|
if (!node) {
|
||||||
|
@ -395,8 +403,8 @@ void ChartRectLineItem::render()
|
||||||
}
|
}
|
||||||
|
|
||||||
ChartBarItem::ChartBarItem(StatsView &v, ChartZValue z, double borderWidth, bool horizontal) : HideableChartItem(v, z),
|
ChartBarItem::ChartBarItem(StatsView &v, ChartZValue z, double borderWidth, bool horizontal) : HideableChartItem(v, z),
|
||||||
borderWidth(borderWidth), horizontal(horizontal),
|
borderWidth(borderWidth), selected(false), horizontal(horizontal),
|
||||||
positionDirty(false), colorDirty(false)
|
positionDirty(false), colorDirty(false), selectedDirty(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,6 +412,18 @@ ChartBarItem::~ChartBarItem()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSGTexture *ChartBarItem::getSelectedTexture() const
|
||||||
|
{
|
||||||
|
if (!selectedTexture) {
|
||||||
|
QImage img(2, 2, QImage::Format_ARGB32);
|
||||||
|
img.fill(Qt::transparent);
|
||||||
|
img.setPixelColor(0, 0, selectionOverlayColor);
|
||||||
|
img.setPixelColor(1, 1, selectionOverlayColor);
|
||||||
|
selectedTexture = view.w()->createTextureFromImage(img, QQuickWindow::TextureHasAlphaChannel);
|
||||||
|
}
|
||||||
|
return selectedTexture;
|
||||||
|
}
|
||||||
|
|
||||||
void ChartBarItem::render()
|
void ChartBarItem::render()
|
||||||
{
|
{
|
||||||
if (!node) {
|
if (!node) {
|
||||||
|
@ -419,7 +439,7 @@ void ChartBarItem::render()
|
||||||
|
|
||||||
node->node->appendChildNode(borderNode.get());
|
node->node->appendChildNode(borderNode.get());
|
||||||
view.addQSGNode(node.get(), zValue);
|
view.addQSGNode(node.get(), zValue);
|
||||||
positionDirty = colorDirty = true;
|
positionDirty = colorDirty = selectedDirty = true;
|
||||||
}
|
}
|
||||||
updateVisible();
|
updateVisible();
|
||||||
|
|
||||||
|
@ -448,11 +468,48 @@ void ChartBarItem::render()
|
||||||
borderNode->markDirty(QSGNode::DirtyGeometry);
|
borderNode->markDirty(QSGNode::DirtyGeometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
positionDirty = colorDirty = false;
|
if (selectedDirty) {
|
||||||
|
if (selected) {
|
||||||
|
if (!selectionNode) {
|
||||||
|
// Create the selection overlay if it didn't exist up to now.
|
||||||
|
selectionGeometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4));
|
||||||
|
selectionGeometry->setDrawingMode(QSGGeometry::DrawTriangleFan);
|
||||||
|
selectionMaterial.reset(new QSGTextureMaterial);
|
||||||
|
selectionMaterial->setTexture(getSelectedTexture());
|
||||||
|
selectionMaterial->setHorizontalWrapMode(QSGTexture::Repeat);
|
||||||
|
selectionMaterial->setVerticalWrapMode(QSGTexture::Repeat);
|
||||||
|
selectionNode.reset(new QSGGeometryNode);
|
||||||
|
selectionNode->setGeometry(selectionGeometry.get());
|
||||||
|
selectionNode->setMaterial(selectionMaterial.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
node->node->appendChildNode(selectionNode.get());
|
||||||
|
|
||||||
|
// Update the position of the selection overlay, even if the position didn't change.
|
||||||
|
positionDirty = true;
|
||||||
|
} else {
|
||||||
|
if (selectionNode)
|
||||||
|
node->node->removeChildNode(selectionNode.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected && positionDirty) {
|
||||||
|
// The checkerboard texture is 2x2. By dividing the coordinates by 4, every square is 2x2 pixels on the screen.
|
||||||
|
auto selectionVertices = selectionGeometry->vertexDataAsTexturedPoint2D();
|
||||||
|
selectionNode->markDirty(QSGNode::DirtyGeometry);
|
||||||
|
setPoint(selectionVertices[0], rect.topLeft(), QPointF());
|
||||||
|
setPoint(selectionVertices[1], rect.topRight(), QPointF(rect.width() / 4.0, 0.0));
|
||||||
|
setPoint(selectionVertices[2], rect.bottomRight(), QPointF(rect.width() / 4.0, rect.height() / 4.0));
|
||||||
|
setPoint(selectionVertices[3], rect.bottomLeft(), QPointF(0.0, rect.height() / 4.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
positionDirty = colorDirty = selectedDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChartBarItem::setColor(QColor colorIn, QColor borderColorIn)
|
void ChartBarItem::setColor(QColor colorIn, QColor borderColorIn)
|
||||||
{
|
{
|
||||||
|
if (color == colorIn)
|
||||||
|
return;
|
||||||
color = colorIn;
|
color = colorIn;
|
||||||
borderColor = borderColorIn;
|
borderColor = borderColorIn;
|
||||||
colorDirty = true;
|
colorDirty = true;
|
||||||
|
@ -461,11 +518,22 @@ void ChartBarItem::setColor(QColor colorIn, QColor borderColorIn)
|
||||||
|
|
||||||
void ChartBarItem::setRect(const QRectF &rectIn)
|
void ChartBarItem::setRect(const QRectF &rectIn)
|
||||||
{
|
{
|
||||||
|
if (rect == rectIn)
|
||||||
|
return;
|
||||||
rect = rectIn;
|
rect = rectIn;
|
||||||
positionDirty = true;
|
positionDirty = true;
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChartBarItem::setSelected(bool selectedIn)
|
||||||
|
{
|
||||||
|
if (selected == selectedIn)
|
||||||
|
return;
|
||||||
|
selected = selectedIn;
|
||||||
|
selectedDirty = true;
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
QRectF ChartBarItem::getRect() const
|
QRectF ChartBarItem::getRect() const
|
||||||
{
|
{
|
||||||
return rect;
|
return rect;
|
||||||
|
|
|
@ -15,6 +15,7 @@ class QSGFlatColorMaterial;
|
||||||
class QSGImageNode;
|
class QSGImageNode;
|
||||||
class QSGRectangleNode;
|
class QSGRectangleNode;
|
||||||
class QSGTexture;
|
class QSGTexture;
|
||||||
|
class QSGTextureMaterial;
|
||||||
class StatsView;
|
class StatsView;
|
||||||
enum class ChartZValue : int;
|
enum class ChartZValue : int;
|
||||||
|
|
||||||
|
@ -148,18 +149,27 @@ public:
|
||||||
~ChartBarItem();
|
~ChartBarItem();
|
||||||
void setColor(QColor color, QColor borderColor);
|
void setColor(QColor color, QColor borderColor);
|
||||||
void setRect(const QRectF &rect);
|
void setRect(const QRectF &rect);
|
||||||
|
void setSelected(bool selected);
|
||||||
QRectF getRect() const;
|
QRectF getRect() const;
|
||||||
void render() override; // Only call on render thread!
|
void render() override; // Only call on render thread!
|
||||||
protected:
|
protected:
|
||||||
QColor color, borderColor;
|
QColor color, borderColor;
|
||||||
double borderWidth;
|
double borderWidth;
|
||||||
QRectF rect;
|
QRectF rect;
|
||||||
|
bool selected;
|
||||||
bool horizontal;
|
bool horizontal;
|
||||||
bool positionDirty;
|
bool positionDirty;
|
||||||
bool colorDirty;
|
bool colorDirty;
|
||||||
|
bool selectedDirty;
|
||||||
std::unique_ptr<QSGGeometryNode> borderNode;
|
std::unique_ptr<QSGGeometryNode> borderNode;
|
||||||
std::unique_ptr<QSGFlatColorMaterial> borderMaterial;
|
std::unique_ptr<QSGFlatColorMaterial> borderMaterial;
|
||||||
std::unique_ptr<QSGGeometry> borderGeometry;
|
std::unique_ptr<QSGGeometry> borderGeometry;
|
||||||
|
private:
|
||||||
|
// Overlay for selected items. Created on demand.
|
||||||
|
std::unique_ptr<QSGGeometryNode> selectionNode;
|
||||||
|
std::unique_ptr<QSGTextureMaterial> selectionMaterial;
|
||||||
|
std::unique_ptr<QSGGeometry> selectionGeometry;
|
||||||
|
QSGTexture *getSelectedTexture() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A box-and-whiskers item. This is a bit lazy: derive from the bar item and add whiskers.
|
// A box-and-whiskers item. This is a bit lazy: derive from the bar item and add whiskers.
|
||||||
|
|
|
@ -25,6 +25,7 @@ inline const QColor regressionItemColor(Qt::red);
|
||||||
inline const QColor meanMarkerColor(Qt::green);
|
inline const QColor meanMarkerColor(Qt::green);
|
||||||
inline const QColor medianMarkerColor(Qt::red);
|
inline const QColor medianMarkerColor(Qt::red);
|
||||||
inline const QColor selectionLassoColor(Qt::black);
|
inline const QColor selectionLassoColor(Qt::black);
|
||||||
|
inline const QColor selectionOverlayColor(Qt::lightGray);
|
||||||
|
|
||||||
QColor binColor(int bin, int numBins);
|
QColor binColor(int bin, int numBins);
|
||||||
QColor labelColor(int bin, size_t numBins);
|
QColor labelColor(int bin, size_t numBins);
|
||||||
|
|
Loading…
Reference in a new issue