mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
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>
This commit is contained in:
parent
2008857660
commit
b42e19e36b
4 changed files with 200 additions and 70 deletions
|
@ -13,6 +13,7 @@
|
||||||
// Constants that control the bar layout
|
// Constants that control the bar layout
|
||||||
static const double barWidth = 0.8; // 1.0 = full width of category
|
static const double barWidth = 0.8; // 1.0 = full width of category
|
||||||
static const double subBarWidth = 0.9; // For grouped bar charts
|
static const double subBarWidth = 0.9; // For grouped bar charts
|
||||||
|
static const double barBorderWidth = 1.0;
|
||||||
|
|
||||||
// Default constructor: invalid index.
|
// Default constructor: invalid index.
|
||||||
BarSeries::Index::Index() : bar(-1), subitem(-1)
|
BarSeries::Index::Index() : bar(-1), subitem(-1)
|
||||||
|
@ -86,97 +87,76 @@ BarSeries::~BarSeries()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BarSeries::BarLabel::BarLabel(QGraphicsScene *scene, const std::vector<QString> &labels, int bin_nr, int binCount) :
|
BarSeries::BarLabel::BarLabel(StatsView &view, const std::vector<QString> &labels, int bin_nr, int binCount) :
|
||||||
totalWidth(0.0), totalHeight(0.0), isOutside(false)
|
isOutside(false)
|
||||||
{
|
{
|
||||||
items.reserve(labels.size());
|
QFont f; // make configurable
|
||||||
for (const QString &label: labels) {
|
item = view.createChartItem<ChartTextItem>(ChartZValue::SeriesLabels, f, labels, true);
|
||||||
items.emplace_back(createItem<QGraphicsSimpleTextItem>(scene));
|
|
||||||
items.back()->setText(label);
|
|
||||||
items.back()->setZValue(ZValues::seriesLabels);
|
|
||||||
QRectF rect = items.back()->boundingRect();
|
|
||||||
if (rect.width() > totalWidth)
|
|
||||||
totalWidth = rect.width();
|
|
||||||
totalHeight += rect.height();
|
|
||||||
}
|
|
||||||
highlight(false, bin_nr, binCount);
|
highlight(false, bin_nr, binCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BarSeries::BarLabel::setVisible(bool visible)
|
void BarSeries::BarLabel::setVisible(bool visible)
|
||||||
{
|
{
|
||||||
for (auto &item: items)
|
// item->setVisible(visible); TODO!
|
||||||
item->setVisible(visible);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BarSeries::BarLabel::highlight(bool highlight, int bin_nr, int binCount)
|
void BarSeries::BarLabel::highlight(bool highlight, int bin_nr, int binCount)
|
||||||
{
|
{
|
||||||
QBrush brush(highlight || isOutside ? darkLabelColor : labelColor(bin_nr, binCount));
|
item->setColor(highlight || isOutside ? darkLabelColor : labelColor(bin_nr, binCount));
|
||||||
for (auto &item: items)
|
|
||||||
item->setBrush(brush);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BarSeries::BarLabel::updatePosition(bool horizontal, bool center, const QRectF &rect,
|
void BarSeries::BarLabel::updatePosition(bool horizontal, bool center, const QRectF &rect,
|
||||||
int bin_nr, int binCount)
|
int bin_nr, int binCount)
|
||||||
{
|
{
|
||||||
|
QSizeF itemSize = item->getRect().size();
|
||||||
if (!horizontal) {
|
if (!horizontal) {
|
||||||
if (totalWidth > rect.width()) {
|
if (itemSize.width() > rect.width()) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QPointF pos = rect.center();
|
QPointF pos = rect.center();
|
||||||
|
pos.rx() -= round(itemSize.width() / 2.0);
|
||||||
|
|
||||||
// Heuristics: if the label fits nicely into the bar (bar height is at least twice the label height),
|
// Heuristics: if the label fits nicely into the bar (bar height is at least twice the label height),
|
||||||
// then put the label in the middle of the bar. Otherwise, put it at the top of the bar.
|
// then put the label in the middle of the bar. Otherwise, put it at the top of the bar.
|
||||||
isOutside = !center && rect.height() < 2.0 * totalHeight;
|
isOutside = !center && rect.height() < 2.0 * itemSize.height();
|
||||||
if (isOutside) {
|
if (isOutside) {
|
||||||
pos.ry() = rect.top() - (totalHeight + 2.0); // Leave two pixels(?) space
|
pos.ry() = rect.top() - (itemSize.height() + 2.0); // Leave two pixels(?) space
|
||||||
} else {
|
} else {
|
||||||
if (totalHeight > rect.height()) {
|
if (itemSize.height() > rect.height()) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pos.ry() -= totalHeight / 2.0;
|
pos.ry() -= round(itemSize.height() / 2.0);
|
||||||
}
|
|
||||||
for (auto &it: items) {
|
|
||||||
QPointF itemPos = pos;
|
|
||||||
QRectF rect = it->boundingRect();
|
|
||||||
itemPos.rx() -= rect.width() / 2.0;
|
|
||||||
it->setPos(itemPos);
|
|
||||||
pos.ry() += rect.height();
|
|
||||||
}
|
}
|
||||||
|
item->setPos(pos);
|
||||||
} else {
|
} else {
|
||||||
if (totalHeight > rect.height()) {
|
if (itemSize.height() > rect.height()) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QPointF pos = rect.center();
|
QPointF pos = rect.center();
|
||||||
pos.ry() -= totalHeight / 2.0;
|
pos.ry() -= round(itemSize.height() / 2.0);
|
||||||
|
|
||||||
// Heuristics: if the label fits nicely into the bar (bar width is at least twice the label height),
|
// Heuristics: if the label fits nicely into the bar (bar width is at least twice the label height),
|
||||||
// then put the label in the middle of the bar. Otherwise, put it to the right of the bar.
|
// then put the label in the middle of the bar. Otherwise, put it to the right of the bar.
|
||||||
isOutside = !center && rect.width() < 2.0 * totalWidth;
|
isOutside = !center && rect.width() < 2.0 * itemSize.width();
|
||||||
if (isOutside) {
|
if (isOutside) {
|
||||||
pos.rx() = rect.right() + (totalWidth / 2.0 + 2.0); // Leave two pixels(?) space
|
pos.rx() = round(rect.right() + 2.0); // Leave two pixels(?) space
|
||||||
} else {
|
} else {
|
||||||
if (totalWidth > rect.width()) {
|
if (itemSize.width() > rect.width()) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto &it: items) {
|
item->setPos(pos);
|
||||||
QPointF itemPos = pos;
|
|
||||||
QRectF rect = it->boundingRect();
|
|
||||||
itemPos.rx() -= rect.width() / 2.0;
|
|
||||||
it->setPos(itemPos);
|
|
||||||
pos.ry() += rect.height();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
// If label changed from inside to outside, or vice-versa, the color might change.
|
// If label changed from inside to outside, or vice-versa, the color might change.
|
||||||
highlight(false, bin_nr, binCount);
|
highlight(false, bin_nr, binCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
BarSeries::Item::Item(QGraphicsScene *scene, BarSeries *series, double lowerBound, double upperBound,
|
BarSeries::Item::Item(BarSeries *series, double lowerBound, double upperBound,
|
||||||
std::vector<SubItem> subitemsIn,
|
std::vector<SubItem> subitemsIn,
|
||||||
const QString &binName, const StatsOperationResults &res, int total,
|
const QString &binName, const StatsOperationResults &res, int total,
|
||||||
bool horizontal, bool stacked, int binCount) :
|
bool horizontal, bool stacked, int binCount) :
|
||||||
|
@ -187,10 +167,8 @@ BarSeries::Item::Item(QGraphicsScene *scene, BarSeries *series, double lowerBoun
|
||||||
res(res),
|
res(res),
|
||||||
total(total)
|
total(total)
|
||||||
{
|
{
|
||||||
for (SubItem &item: subitems) {
|
for (SubItem &item: subitems)
|
||||||
item.item->setZValue(ZValues::series);
|
|
||||||
item.highlight(false, binCount);
|
item.highlight(false, binCount);
|
||||||
}
|
|
||||||
updatePosition(series, horizontal, stacked, binCount);
|
updatePosition(series, horizontal, stacked, binCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,13 +181,10 @@ void BarSeries::Item::highlight(int subitem, bool highlight, int binCount)
|
||||||
|
|
||||||
void BarSeries::SubItem::highlight(bool highlight, int binCount)
|
void BarSeries::SubItem::highlight(bool highlight, int binCount)
|
||||||
{
|
{
|
||||||
if (highlight) {
|
if (highlight)
|
||||||
item->setBrush(QBrush(highlightedColor));
|
item->setColor(highlightedColor, highlightedBorderColor);
|
||||||
item->setPen(QPen(highlightedBorderColor));
|
else
|
||||||
} else {
|
item->setColor(binColor(bin_nr, binCount), ::borderColor);
|
||||||
item->setBrush(QBrush(binColor(bin_nr, binCount)));
|
|
||||||
item->setPen(QPen(::borderColor));
|
|
||||||
}
|
|
||||||
if (label)
|
if (label)
|
||||||
label->highlight(highlight, bin_nr, binCount);
|
label->highlight(highlight, bin_nr, binCount);
|
||||||
}
|
}
|
||||||
|
@ -235,9 +210,9 @@ void BarSeries::Item::updatePosition(BarSeries *series, bool horizontal, bool st
|
||||||
double center = (idx + 0.5) * fullSubWidth + from;
|
double center = (idx + 0.5) * fullSubWidth + from;
|
||||||
item.updatePosition(series, horizontal, stacked, center - subWidth / 2.0, center + subWidth / 2.0, binCount);
|
item.updatePosition(series, horizontal, stacked, center - subWidth / 2.0, center + subWidth / 2.0, binCount);
|
||||||
}
|
}
|
||||||
rect = subitems[0].item->rect();
|
rect = subitems[0].item->getRect();
|
||||||
for (auto it = std::next(subitems.begin()); it != subitems.end(); ++it)
|
for (auto it = std::next(subitems.begin()); it != subitems.end(); ++it)
|
||||||
rect = rect.united(it->item->rect());
|
rect = rect.united(it->item->getRect());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BarSeries::SubItem::updatePosition(BarSeries *series, bool horizontal, bool stacked,
|
void BarSeries::SubItem::updatePosition(BarSeries *series, bool horizontal, bool stacked,
|
||||||
|
@ -265,9 +240,10 @@ std::vector<BarSeries::SubItem> BarSeries::makeSubItems(const std::vector<std::p
|
||||||
int bin_nr = 0;
|
int bin_nr = 0;
|
||||||
for (auto &[v, label]: values) {
|
for (auto &[v, label]: values) {
|
||||||
if (v > 0.0) {
|
if (v > 0.0) {
|
||||||
res.push_back({ createItemPtr<QGraphicsRectItem>(scene), {}, from, from + v, bin_nr });
|
res.push_back({ view.createChartItem<ChartBarItem>(ChartZValue::Series, barBorderWidth, horizontal),
|
||||||
|
{}, from, from + v, bin_nr });
|
||||||
if (!label.empty())
|
if (!label.empty())
|
||||||
res.back().label = std::make_unique<BarLabel>(scene, label, bin_nr, binCount());
|
res.back().label = std::make_unique<BarLabel>(view, label, bin_nr, binCount());
|
||||||
}
|
}
|
||||||
if (stacked)
|
if (stacked)
|
||||||
from += v;
|
from += v;
|
||||||
|
@ -293,7 +269,7 @@ void BarSeries::add_item(double lowerBound, double upperBound, std::vector<SubIt
|
||||||
// Don't add empty items, as that messes with the "find item under mouse" routine.
|
// Don't add empty items, as that messes with the "find item under mouse" routine.
|
||||||
if (subitems.empty())
|
if (subitems.empty())
|
||||||
return;
|
return;
|
||||||
items.emplace_back(scene, this, lowerBound, upperBound, std::move(subitems), binName, res,
|
items.emplace_back(this, lowerBound, upperBound, std::move(subitems), binName, res,
|
||||||
total, horizontal, stacked, binCount());
|
total, horizontal, stacked, binCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,10 +299,10 @@ int BarSeries::Item::getSubItemUnderMouse(const QPointF &point, bool horizontal,
|
||||||
// Search the first item whose "end" position is greater than the cursor position.
|
// Search the first item whose "end" position is greater than the cursor position.
|
||||||
bool search_x = horizontal == stacked;
|
bool search_x = horizontal == stacked;
|
||||||
auto it = search_x ? std::lower_bound(subitems.begin(), subitems.end(), point.x(),
|
auto it = search_x ? std::lower_bound(subitems.begin(), subitems.end(), point.x(),
|
||||||
[] (const SubItem &item, double x) { return item.item->rect().right() < x; })
|
[] (const SubItem &item, double x) { return item.item->getRect().right() < x; })
|
||||||
: std::lower_bound(subitems.begin(), subitems.end(), point.y(),
|
: std::lower_bound(subitems.begin(), subitems.end(), point.y(),
|
||||||
[] (const SubItem &item, double y) { return item.item->rect().top() > y; });
|
[] (const SubItem &item, double y) { return item.item->getRect().top() > y; });
|
||||||
return it != subitems.end() && it->item->rect().contains(point) ? it - subitems.begin() : -1;
|
return it != subitems.end() && it->item->getRect().contains(point) ? it - subitems.begin() : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format information in a count-based bar chart.
|
// Format information in a count-based bar chart.
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <QGraphicsRectItem>
|
#include <QRectF>
|
||||||
|
|
||||||
class QGraphicsScene;
|
class QGraphicsScene;
|
||||||
|
class ChartBarItem;
|
||||||
|
class ChartTextItem;
|
||||||
struct InformationBox;
|
struct InformationBox;
|
||||||
struct StatsVariable;
|
struct StatsVariable;
|
||||||
|
|
||||||
|
@ -80,17 +82,16 @@ private:
|
||||||
|
|
||||||
// A label that is composed of multiple lines
|
// A label that is composed of multiple lines
|
||||||
struct BarLabel {
|
struct BarLabel {
|
||||||
std::vector<std::unique_ptr<QGraphicsSimpleTextItem>> items;
|
std::unique_ptr<ChartTextItem> item;
|
||||||
double totalWidth, totalHeight; // Size of the item
|
|
||||||
bool isOutside; // Is shown outside of bar
|
bool isOutside; // Is shown outside of bar
|
||||||
BarLabel(QGraphicsScene *scene, const std::vector<QString> &labels, int bin_nr, int binCount);
|
BarLabel(StatsView &view, const std::vector<QString> &labels, int bin_nr, int binCount);
|
||||||
void setVisible(bool visible);
|
void setVisible(bool visible);
|
||||||
void updatePosition(bool horizontal, bool center, const QRectF &rect, int bin_nr, int binCount);
|
void updatePosition(bool horizontal, bool center, const QRectF &rect, int bin_nr, int binCount);
|
||||||
void highlight(bool highlight, int bin_nr, int binCount);
|
void highlight(bool highlight, int bin_nr, int binCount);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SubItem {
|
struct SubItem {
|
||||||
std::unique_ptr<QGraphicsRectItem> item;
|
std::unique_ptr<ChartBarItem> item;
|
||||||
std::unique_ptr<BarLabel> label;
|
std::unique_ptr<BarLabel> label;
|
||||||
double value_from;
|
double value_from;
|
||||||
double value_to;
|
double value_to;
|
||||||
|
@ -107,7 +108,7 @@ private:
|
||||||
const QString binName;
|
const QString binName;
|
||||||
StatsOperationResults res;
|
StatsOperationResults res;
|
||||||
int total;
|
int total;
|
||||||
Item(QGraphicsScene *scene, BarSeries *series, double lowerBound, double upperBound,
|
Item(BarSeries *series, double lowerBound, double upperBound,
|
||||||
std::vector<SubItem> subitems,
|
std::vector<SubItem> subitems,
|
||||||
const QString &binName, const StatsOperationResults &res, int total, bool horizontal,
|
const QString &binName, const StatsOperationResults &res, int total, bool horizontal,
|
||||||
bool stacked, int binCount);
|
bool stacked, int binCount);
|
||||||
|
@ -118,7 +119,6 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<InformationBox> information;
|
std::unique_ptr<InformationBox> information;
|
||||||
std::vector<Item> items;
|
std::vector<Item> items;
|
||||||
std::vector<BarLabel> barLabels;
|
|
||||||
bool horizontal;
|
bool horizontal;
|
||||||
bool stacked;
|
bool stacked;
|
||||||
QString categoryName;
|
QString categoryName;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
#include <QSGFlatColorMaterial>
|
#include <QSGFlatColorMaterial>
|
||||||
#include <QSGImageNode>
|
#include <QSGImageNode>
|
||||||
|
#include <QSGRectangleNode>
|
||||||
#include <QSGTexture>
|
#include <QSGTexture>
|
||||||
|
|
||||||
static int round_up(double f)
|
static int round_up(double f)
|
||||||
|
@ -116,6 +117,40 @@ void ChartRectItem::resize(QSizeF size)
|
||||||
painter->drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
|
painter->drawRoundedRect(rect, radius, radius, Qt::AbsoluteSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChartTextItem::ChartTextItem(StatsView &v, ChartZValue z, const QFont &f, const std::vector<QString> &text, bool center) :
|
||||||
|
ChartPixmapItem(v, z), f(f), center(center)
|
||||||
|
{
|
||||||
|
QFontMetrics fm(f);
|
||||||
|
double totalWidth = 1.0;
|
||||||
|
fontHeight = static_cast<double>(fm.height());
|
||||||
|
double totalHeight = std::max(1.0, static_cast<double>(text.size()) * fontHeight);
|
||||||
|
|
||||||
|
items.reserve(text.size());
|
||||||
|
for (const QString &s: text) {
|
||||||
|
double w = fm.size(Qt::TextSingleLine, s).width();
|
||||||
|
items.push_back({ s, w });
|
||||||
|
if (w > totalWidth)
|
||||||
|
totalWidth = w;
|
||||||
|
}
|
||||||
|
resize(QSizeF(totalWidth, totalHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChartTextItem::setColor(const QColor &c)
|
||||||
|
{
|
||||||
|
img->fill(Qt::transparent);
|
||||||
|
double y = 0.0;
|
||||||
|
painter->setPen(QPen(c));
|
||||||
|
painter->setFont(f);
|
||||||
|
double totalWidth = getRect().width();
|
||||||
|
for (const auto &[s, w]: items) {
|
||||||
|
double x = center ? round((totalWidth - w) / 2.0) : 0.0;
|
||||||
|
QRectF rect(x, y, w, fontHeight);
|
||||||
|
painter->drawText(rect, s);
|
||||||
|
y += fontHeight;
|
||||||
|
}
|
||||||
|
setTextureDirty();
|
||||||
|
}
|
||||||
|
|
||||||
ChartLineItem::ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width) : ChartItem(v, z),
|
ChartLineItem::ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width) : ChartItem(v, z),
|
||||||
color(color), width(width), positionDirty(false), materialDirty(false)
|
color(color), width(width), positionDirty(false), materialDirty(false)
|
||||||
{
|
{
|
||||||
|
@ -125,6 +160,12 @@ ChartLineItem::~ChartLineItem()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to set points
|
||||||
|
void setPoint(QSGGeometry::Point2D &v, const QPointF &p)
|
||||||
|
{
|
||||||
|
v.set(static_cast<float>(p.x()), static_cast<float>(p.y()));
|
||||||
|
}
|
||||||
|
|
||||||
void ChartLineItem::render()
|
void ChartLineItem::render()
|
||||||
{
|
{
|
||||||
if (!node) {
|
if (!node) {
|
||||||
|
@ -142,8 +183,8 @@ void ChartLineItem::render()
|
||||||
// Attention: width is a geometry property and therefore handled by position dirty!
|
// Attention: width is a geometry property and therefore handled by position dirty!
|
||||||
geometry->setLineWidth(static_cast<float>(width));
|
geometry->setLineWidth(static_cast<float>(width));
|
||||||
auto vertices = geometry->vertexDataAsPoint2D();
|
auto vertices = geometry->vertexDataAsPoint2D();
|
||||||
vertices[0].set(static_cast<float>(from.x()), static_cast<float>(from.y()));
|
setPoint(vertices[0], from);
|
||||||
vertices[1].set(static_cast<float>(to.x()), static_cast<float>(to.y()));
|
setPoint(vertices[1], to);
|
||||||
node->markDirty(QSGNode::DirtyGeometry);
|
node->markDirty(QSGNode::DirtyGeometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,3 +203,77 @@ void ChartLineItem::setLine(QPointF fromIn, QPointF toIn)
|
||||||
positionDirty = true;
|
positionDirty = true;
|
||||||
view.registerDirtyChartItem(*this);
|
view.registerDirtyChartItem(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChartBarItem::ChartBarItem(StatsView &v, ChartZValue z, double borderWidth, bool horizontal) : ChartItem(v, z),
|
||||||
|
borderWidth(borderWidth), horizontal(horizontal),
|
||||||
|
positionDirty(false), colorDirty(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartBarItem::~ChartBarItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChartBarItem::render()
|
||||||
|
{
|
||||||
|
if (!node) {
|
||||||
|
node.reset(view.w()->createRectangleNode());
|
||||||
|
|
||||||
|
borderGeometry.reset(new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4));
|
||||||
|
borderGeometry->setDrawingMode(QSGGeometry::DrawLineLoop);
|
||||||
|
borderGeometry->setLineWidth(static_cast<float>(borderWidth));
|
||||||
|
borderMaterial.reset(new QSGFlatColorMaterial);
|
||||||
|
borderNode.reset(new QSGGeometryNode);
|
||||||
|
borderNode->setGeometry(borderGeometry.get());
|
||||||
|
borderNode->setMaterial(borderMaterial.get());
|
||||||
|
|
||||||
|
node->appendChildNode(borderNode.get());
|
||||||
|
view.addQSGNode(node.get(), zValue);
|
||||||
|
positionDirty = colorDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorDirty) {
|
||||||
|
node->setColor(color);
|
||||||
|
borderMaterial->setColor(borderColor);
|
||||||
|
borderNode->markDirty(QSGNode::DirtyMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (positionDirty) {
|
||||||
|
node->setRect(rect);
|
||||||
|
auto vertices = borderGeometry->vertexDataAsPoint2D();
|
||||||
|
if (horizontal) {
|
||||||
|
setPoint(vertices[0], rect.topLeft());
|
||||||
|
setPoint(vertices[1], rect.topRight());
|
||||||
|
setPoint(vertices[2], rect.bottomRight());
|
||||||
|
setPoint(vertices[3], rect.bottomLeft());
|
||||||
|
} else {
|
||||||
|
setPoint(vertices[0], rect.bottomLeft());
|
||||||
|
setPoint(vertices[1], rect.topLeft());
|
||||||
|
setPoint(vertices[2], rect.topRight());
|
||||||
|
setPoint(vertices[3], rect.bottomRight());
|
||||||
|
}
|
||||||
|
borderNode->markDirty(QSGNode::DirtyGeometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
positionDirty = colorDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChartBarItem::setColor(QColor colorIn, QColor borderColorIn)
|
||||||
|
{
|
||||||
|
color = colorIn;
|
||||||
|
borderColor = borderColorIn;
|
||||||
|
colorDirty = true;
|
||||||
|
view.registerDirtyChartItem(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChartBarItem::setRect(const QRectF &rectIn)
|
||||||
|
{
|
||||||
|
rect = rectIn;
|
||||||
|
positionDirty = true;
|
||||||
|
view.registerDirtyChartItem(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF ChartBarItem::getRect() const
|
||||||
|
{
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ class QSGGeometry;
|
||||||
class QSGGeometryNode;
|
class QSGGeometryNode;
|
||||||
class QSGFlatColorMaterial;
|
class QSGFlatColorMaterial;
|
||||||
class QSGImageNode;
|
class QSGImageNode;
|
||||||
|
class QSGRectangleNode;
|
||||||
class QSGTexture;
|
class QSGTexture;
|
||||||
class StatsView;
|
class StatsView;
|
||||||
enum class ChartZValue : int;
|
enum class ChartZValue : int;
|
||||||
|
@ -20,7 +21,6 @@ public:
|
||||||
ChartItem(StatsView &v, ChartZValue z);
|
ChartItem(StatsView &v, ChartZValue z);
|
||||||
virtual ~ChartItem();
|
virtual ~ChartItem();
|
||||||
virtual void render() = 0; // Only call on render thread!
|
virtual void render() = 0; // Only call on render thread!
|
||||||
QRectF getRect() const;
|
|
||||||
bool dirty; // If true, call render() when rebuilding the scene
|
bool dirty; // If true, call render() when rebuilding the scene
|
||||||
ChartItem *dirtyPrev, *dirtyNext; // Double linked list of dirty items
|
ChartItem *dirtyPrev, *dirtyNext; // Double linked list of dirty items
|
||||||
const ChartZValue zValue;
|
const ChartZValue zValue;
|
||||||
|
@ -64,6 +64,22 @@ private:
|
||||||
double radius;
|
double radius;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Attention: text is only drawn after calling setColor()!
|
||||||
|
class ChartTextItem : public ChartPixmapItem {
|
||||||
|
public:
|
||||||
|
ChartTextItem(StatsView &v, ChartZValue z, const QFont &f, const std::vector<QString> &text, bool center);
|
||||||
|
void setColor(const QColor &color);
|
||||||
|
private:
|
||||||
|
QFont f;
|
||||||
|
double fontHeight;
|
||||||
|
bool center;
|
||||||
|
struct Item {
|
||||||
|
QString s;
|
||||||
|
double width;
|
||||||
|
};
|
||||||
|
std::vector<Item> items;
|
||||||
|
};
|
||||||
|
|
||||||
class ChartLineItem : public ChartItem {
|
class ChartLineItem : public ChartItem {
|
||||||
public:
|
public:
|
||||||
ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width);
|
ChartLineItem(StatsView &v, ChartZValue z, QColor color, double width);
|
||||||
|
@ -74,6 +90,7 @@ private:
|
||||||
QPointF from, to;
|
QPointF from, to;
|
||||||
QColor color;
|
QColor color;
|
||||||
double width;
|
double width;
|
||||||
|
bool horizontal;
|
||||||
bool positionDirty;
|
bool positionDirty;
|
||||||
bool materialDirty;
|
bool materialDirty;
|
||||||
std::unique_ptr<QSGGeometryNode> node;
|
std::unique_ptr<QSGGeometryNode> node;
|
||||||
|
@ -81,4 +98,26 @@ private:
|
||||||
std::unique_ptr<QSGGeometry> geometry;
|
std::unique_ptr<QSGGeometry> geometry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A bar in a bar chart: a rectangle bordered by lines.
|
||||||
|
class ChartBarItem : public ChartItem {
|
||||||
|
public:
|
||||||
|
ChartBarItem(StatsView &v, ChartZValue z, double borderWidth, bool horizontal);
|
||||||
|
~ChartBarItem();
|
||||||
|
void setColor(QColor color, QColor borderColor);
|
||||||
|
void setRect(const QRectF &rect);
|
||||||
|
QRectF getRect() const;
|
||||||
|
void render() override; // Only call on render thread!
|
||||||
|
private:
|
||||||
|
QColor color, borderColor;
|
||||||
|
double borderWidth;
|
||||||
|
QRectF rect;
|
||||||
|
bool horizontal;
|
||||||
|
bool positionDirty;
|
||||||
|
bool colorDirty;
|
||||||
|
std::unique_ptr<QSGRectangleNode> node;
|
||||||
|
std::unique_ptr<QSGGeometryNode> borderNode;
|
||||||
|
std::unique_ptr<QSGFlatColorMaterial> borderMaterial;
|
||||||
|
std::unique_ptr<QSGGeometry> borderGeometry;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue