2024-08-21 18:06:30 +01:00
|
|
|
#include "wildmonchart.h"
|
|
|
|
#include "ui_wildmonchart.h"
|
2024-08-22 04:28:33 +01:00
|
|
|
#include "config.h"
|
2024-08-21 18:06:30 +01:00
|
|
|
|
2024-08-21 21:52:43 +01:00
|
|
|
#include "log.h"
|
|
|
|
|
2024-08-21 18:06:30 +01:00
|
|
|
#include <QtCharts>
|
|
|
|
|
2024-08-22 04:28:33 +01:00
|
|
|
// TODO: Make level range its own chart(s)?
|
2024-08-21 21:52:43 +01:00
|
|
|
// TODO: Draw species icons below legend icons?
|
2024-08-22 04:28:33 +01:00
|
|
|
// TODO: Add hover behavior to display species name (and click prompt?)
|
2024-08-21 21:52:43 +01:00
|
|
|
|
|
|
|
struct ChartData {
|
|
|
|
int minLevel;
|
|
|
|
int maxLevel;
|
|
|
|
QMap<int, double> values; // One value for each wild encounter group
|
|
|
|
};
|
|
|
|
|
2024-08-22 04:28:33 +01:00
|
|
|
WildMonChart::WildMonChart(QWidget *parent, const EncounterTableModel *table) :
|
2024-08-21 18:06:30 +01:00
|
|
|
QWidget(parent),
|
|
|
|
ui(new Ui::WildMonChart)
|
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
setWindowFlags(Qt::Window);
|
|
|
|
|
|
|
|
ui->chartView->setRenderHint(QPainter::Antialiasing);
|
|
|
|
|
2024-08-21 21:52:43 +01:00
|
|
|
setTable(table);
|
2024-08-21 18:06:30 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
WildMonChart::~WildMonChart() {
|
|
|
|
delete ui;
|
|
|
|
};
|
|
|
|
|
2024-08-22 04:28:33 +01:00
|
|
|
void WildMonChart::setTable(const EncounterTableModel *table) {
|
2024-08-21 21:52:43 +01:00
|
|
|
this->table = table;
|
2024-08-21 18:06:30 +01:00
|
|
|
updateChart();
|
|
|
|
}
|
|
|
|
|
2024-08-22 04:28:33 +01:00
|
|
|
static const bool showLevelRange = false;
|
|
|
|
|
2024-08-21 18:06:30 +01:00
|
|
|
void WildMonChart::updateChart() {
|
2024-08-21 21:52:43 +01:00
|
|
|
if (!this->table)
|
2024-08-21 18:06:30 +01:00
|
|
|
return;
|
|
|
|
|
2024-08-22 04:28:33 +01:00
|
|
|
setWindowTitle(QString("Wild Pokémon Summary -- %1").arg(this->table->encounterField().name));
|
|
|
|
|
2024-08-21 21:52:43 +01:00
|
|
|
// Read data about encounter groups, e.g. for "fishing_mons" we want to know indexes 2-4 belong to good_rod (group index 1).
|
|
|
|
// Each group will be represented as a separate bar on the graph.
|
|
|
|
QList<QString> groupNames;
|
|
|
|
QMap<int, int> tableIndexToGroupIndex;
|
|
|
|
int groupIndex = 0;
|
2024-08-22 04:28:33 +01:00
|
|
|
for (auto groupPair : this->table->encounterField().groups) {
|
|
|
|
groupNames.prepend(groupPair.first);
|
2024-08-21 21:52:43 +01:00
|
|
|
for (auto i : groupPair.second) {
|
|
|
|
tableIndexToGroupIndex.insert(i, groupIndex);
|
|
|
|
}
|
|
|
|
groupIndex++;
|
|
|
|
}
|
|
|
|
const int numGroups = qMax(1, groupNames.length()); // Implicitly 1 group when none are listed
|
2024-08-21 18:06:30 +01:00
|
|
|
|
2024-08-21 21:52:43 +01:00
|
|
|
// Read data from the table, combining data for duplicate species entries
|
2024-08-22 04:28:33 +01:00
|
|
|
const QList<double> tableValues = this->table->percentages();
|
|
|
|
const QVector<WildPokemon> tablePokemon = this->table->encounterData().wildPokemon;
|
2024-08-21 21:52:43 +01:00
|
|
|
QMap<QString, ChartData> speciesToChartData;
|
|
|
|
for (int i = 0; i < qMin(tableValues.length(), tablePokemon.length()); i++) {
|
|
|
|
const double value = tableValues.at(i);
|
|
|
|
const WildPokemon pokemon = tablePokemon.at(i);
|
|
|
|
groupIndex = tableIndexToGroupIndex.value(i, 0);
|
|
|
|
|
|
|
|
if (speciesToChartData.contains(pokemon.species)) {
|
2024-08-21 18:06:30 +01:00
|
|
|
// Duplicate species entry
|
2024-08-21 21:52:43 +01:00
|
|
|
ChartData *entry = &speciesToChartData[pokemon.species];
|
|
|
|
entry->values[groupIndex] += value;
|
|
|
|
if (entry->minLevel > pokemon.minLevel)
|
|
|
|
entry->minLevel = pokemon.minLevel;
|
|
|
|
if (entry->maxLevel < pokemon.maxLevel)
|
|
|
|
entry->maxLevel = pokemon.maxLevel;
|
2024-08-21 18:06:30 +01:00
|
|
|
} else {
|
|
|
|
// New species entry
|
2024-08-21 21:52:43 +01:00
|
|
|
ChartData entry;
|
|
|
|
entry.minLevel = pokemon.minLevel;
|
|
|
|
entry.maxLevel = pokemon.maxLevel;
|
|
|
|
entry.values.insert(groupIndex, value);
|
|
|
|
speciesToChartData.insert(pokemon.species, entry);
|
2024-08-21 18:06:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate chart
|
2024-08-21 21:52:43 +01:00
|
|
|
QList<QBarSet*> barSets;
|
2024-08-22 04:28:33 +01:00
|
|
|
const QString speciesPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_species_prefix);
|
2024-08-21 21:52:43 +01:00
|
|
|
for (auto mapPair = speciesToChartData.cbegin(), end = speciesToChartData.cend(); mapPair != end; mapPair++) {
|
|
|
|
const ChartData entry = mapPair.value();
|
|
|
|
|
2024-08-21 18:06:30 +01:00
|
|
|
// Strip 'SPECIES_' prefix
|
2024-08-22 04:28:33 +01:00
|
|
|
QString label = mapPair.key();
|
|
|
|
if (label.startsWith(speciesPrefix))
|
|
|
|
label.remove(0, speciesPrefix.length());
|
|
|
|
|
|
|
|
// Add level range to label
|
|
|
|
if (showLevelRange) {
|
|
|
|
if (entry.minLevel == entry.maxLevel)
|
|
|
|
label.append(QString(" (Lv %1)").arg(entry.minLevel));
|
|
|
|
else
|
|
|
|
label.append(QString(" (Lv %1-%2)").arg(entry.minLevel).arg(entry.maxLevel));
|
|
|
|
}
|
2024-08-21 21:52:43 +01:00
|
|
|
|
|
|
|
auto set = new QBarSet(label);
|
2024-08-22 04:28:33 +01:00
|
|
|
|
|
|
|
// Add encounter chance data (in reverse order, to match the table's group order visually)
|
|
|
|
for (int i = numGroups - 1; i >= 0; i--)
|
2024-08-21 21:52:43 +01:00
|
|
|
set->append(entry.values.value(i, 0));
|
|
|
|
|
2024-08-22 04:28:33 +01:00
|
|
|
// Insert bar set. We order them from lowest to highest total, left-to-right.
|
2024-08-21 21:52:43 +01:00
|
|
|
int i = 0;
|
|
|
|
for (; i < barSets.length(); i++){
|
|
|
|
if (barSets.at(i)->sum() > set->sum())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
barSets.insert(i, set);
|
2024-08-21 18:06:30 +01:00
|
|
|
}
|
|
|
|
|
2024-08-21 21:52:43 +01:00
|
|
|
auto series = new QHorizontalPercentBarSeries();
|
|
|
|
series->setLabelsVisible();
|
2024-08-22 04:28:33 +01:00
|
|
|
//series->setLabelsPrecision(x); // This appears to have no effect for any value 'x'? Ideally we'd display 1-2 decimal places
|
2024-08-21 21:52:43 +01:00
|
|
|
series->append(barSets);
|
2024-08-21 18:06:30 +01:00
|
|
|
|
2024-08-21 21:52:43 +01:00
|
|
|
auto chart = new QChart();
|
|
|
|
chart->addSeries(series);
|
|
|
|
chart->setAnimationOptions(QChart::SeriesAnimations);
|
|
|
|
chart->legend()->setVisible(true);
|
|
|
|
chart->legend()->setShowToolTips(true);
|
|
|
|
chart->legend()->setAlignment(Qt::AlignBottom);
|
|
|
|
|
2024-08-22 04:28:33 +01:00
|
|
|
// X-axis is the values (percentages). We're already showing percentages on the bar, so we just display 0/50/100%
|
2024-08-21 21:52:43 +01:00
|
|
|
auto axisX = new QValueAxis();
|
2024-08-22 04:28:33 +01:00
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0))
|
|
|
|
// Not critical, but the percentage ticks on the x-axis have no need for decimals.
|
|
|
|
// This property doesn't exist prior to Qt 6.7
|
|
|
|
axisX->setLabelDecimals(0);
|
|
|
|
#endif
|
|
|
|
axisX->setTickCount(3);
|
2024-08-21 21:52:43 +01:00
|
|
|
chart->addAxis(axisX, Qt::AlignBottom);
|
|
|
|
series->attachAxis(axisX);
|
|
|
|
|
|
|
|
// Y-axis is the names of encounter groups (e.g. Old Rod, Good Rod...)
|
|
|
|
if (numGroups > 1) {
|
|
|
|
auto axisY = new QBarCategoryAxis();
|
|
|
|
axisY->setCategories(groupNames);
|
|
|
|
chart->addAxis(axisY, Qt::AlignLeft);
|
|
|
|
series->attachAxis(axisY);
|
|
|
|
}
|
2024-08-21 18:06:30 +01:00
|
|
|
|
2024-08-21 21:52:43 +01:00
|
|
|
delete ui->chartView->chart();
|
|
|
|
ui->chartView->setChart(chart);
|
2024-08-22 04:28:33 +01:00
|
|
|
|
|
|
|
// Turn off the animation once it's played, otherwise it replays any time the window changes size.
|
|
|
|
QTimer::singleShot(chart->animationDuration() + 500, [this] {
|
|
|
|
if (ui->chartView->chart())
|
|
|
|
ui->chartView->chart()->setAnimationOptions(QChart::NoAnimation);
|
|
|
|
});
|
2024-08-21 18:06:30 +01:00
|
|
|
}
|