mirror of
https://github.com/Cubitect/cubiomes-viewer.git
synced 2025-01-09 04:18:15 +08:00
Undates for 2.6.0
* added UI event buffering to the anaylsis results, making it much more performant with many seeds (#122) * added custom separator option for csv export (#122) * added DejaVuSans monospace font for a more consistent look (#107) * added filter for biome center locations with scale 1:256 for versions up to 1.17 (#63) * changed biome statistics UI to display seeds as rows (#122) * changed matching seed list and some anaylsis results to be tristate sortable * changed zoom limits for the goto dialog, allowing a larger manual zoom range (#162) * changed abandoned village and end ship modifiers to be a tristate with exclude option (#168) * fixed incorrect progress display for anaylses (#165) * fixed stronghold filter so it doesn't skip the last inner ring stronghold (#171) * fixed slightly inaccurate biome check location for some villages and bastions (#168) * + few more minor fixes and tweaks
This commit is contained in:
parent
3be941f0a9
commit
a2bfd58002
2
cubiomes
2
cubiomes
@ -1 +1 @@
|
||||
Subproject commit 0bf8cb0cce10b75b7996fcd72792fc2a6a4014c2
|
||||
Subproject commit 10e297d17da679ecb70a6b91b46d619c59aa24e4
|
BIN
rc/fonts/DejaVuSansMono.ttf
Normal file
BIN
rc/fonts/DejaVuSansMono.ttf
Normal file
Binary file not shown.
@ -29,9 +29,9 @@
|
||||
<file>icons/map.png</file>
|
||||
<file>icons/quad.png</file>
|
||||
<file>icons/tempcat.png</file>
|
||||
<file>icons/check0.png</file>
|
||||
<file>icons/check1.png</file>
|
||||
<file>icons/check2.png</file>
|
||||
<file>icons/check_unchecked.png</file>
|
||||
<file>icons/check_include.png</file>
|
||||
<file>icons/check_exclude.png</file>
|
||||
<file>icons/origin.png</file>
|
||||
<file>icons/treasure.png</file>
|
||||
<file>icons/treasure_d.png</file>
|
||||
@ -69,5 +69,7 @@
|
||||
<file>icons/portal_lit.png</file>
|
||||
<file>icons/portal_giant.png</file>
|
||||
<file>icons/end_ship.png</file>
|
||||
<file>icons/check_exclude_d.png</file>
|
||||
<file>icons/check_include_d.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
BIN
rc/icons/check_exclude.png
Normal file
BIN
rc/icons/check_exclude.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 242 B |
BIN
rc/icons/check_exclude_d.png
Normal file
BIN
rc/icons/check_exclude_d.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 248 B |
BIN
rc/icons/check_include.png
Normal file
BIN
rc/icons/check_include.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 244 B |
BIN
rc/icons/check_include_d.png
Normal file
BIN
rc/icons/check_include_d.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 236 B |
BIN
rc/icons/check_unchecked.png
Normal file
BIN
rc/icons/check_unchecked.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 143 B |
@ -211,5 +211,6 @@
|
||||
<file>dark.qss</file>
|
||||
<file>fonts/DejaVuSans.ttf</file>
|
||||
<file>fonts/DejaVuSans-Bold.ttf</file>
|
||||
<file>fonts/DejaVuSansMono.ttf</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@ -5,8 +5,8 @@
|
||||
#include <QString>
|
||||
|
||||
#define VERS_MAJOR 2
|
||||
#define VERS_MINOR 5
|
||||
#define VERS_PATCH 1 // negative patch number designates a development version
|
||||
#define VERS_MINOR 6
|
||||
#define VERS_PATCH 0 // negative patch number designates a development version
|
||||
|
||||
// returns +1 if newer, -1 if older and 0 if equal
|
||||
inline int cmpVers(int major, int minor, int patch)
|
||||
|
@ -55,9 +55,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
|
||||
QString mcs = tr("MC %1", "Minecraft version").arg(p_mcs ? p_mcs : "?");
|
||||
ui->labelMC->setText(mcs);
|
||||
|
||||
QFont mono = QFont("Monospace", 10);
|
||||
mono.setStyleHint(QFont::TypeWriter);
|
||||
ui->lineSummary->setFont(mono);
|
||||
ui->lineSummary->setFont(g_font_mono);
|
||||
|
||||
// prevent bold font of group box title getting inherited
|
||||
QFont dfont = font();
|
||||
@ -159,11 +157,14 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
|
||||
}
|
||||
|
||||
QString tristyle =
|
||||
"QCheckBox::indicator:unchecked { image: url(:/icons/check0.png); }\n"
|
||||
"QCheckBox::indicator:indeterminate { image: url(:/icons/check1.png); }\n"
|
||||
"QCheckBox::indicator:checked { image: url(:/icons/check2.png); }\n";
|
||||
"QCheckBox::indicator:indeterminate { image: url(:/icons/check_include.png); }\n"
|
||||
"QCheckBox::indicator:checked { image: url(:/icons/check_exclude.png); }\n"
|
||||
"QCheckBox::indicator:indeterminate:disabled { image: url(:/icons/check_include_d.png); }\n"
|
||||
"QCheckBox::indicator:checked:disabled { image: url(:/icons/check_exclude_d.png); }\n";
|
||||
ui->scrollBiomes->setStyleSheet(tristyle);
|
||||
ui->scrollNoise->setStyleSheet(tristyle);
|
||||
ui->checkAbandoned->setStyleSheet(tristyle);
|
||||
ui->checkEndShip->setStyleSheet(tristyle);
|
||||
|
||||
memset(climaterange, 0, sizeof(climaterange));
|
||||
memset(climatecomplete, 0, sizeof(climatecomplete));
|
||||
@ -210,16 +211,9 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
|
||||
ids.push_back(id);
|
||||
IdCmp cmp(IdCmp::SORT_LEX, mc, DIM_UNDEF);
|
||||
std::sort(ids.begin(), ids.end(), cmp);
|
||||
QStringList allowed_matches;
|
||||
|
||||
for (int id : ids)
|
||||
{
|
||||
if (isOverworld(mc, id))
|
||||
{
|
||||
QString s = biome2str(mc, id);
|
||||
ui->comboMatchBiome->addItem(getBiomeIcon(id), s, QVariant::fromValue(id));
|
||||
allowed_matches.append(s);
|
||||
}
|
||||
|
||||
const int *lim = getBiomeParaLimits(mc, id);
|
||||
if (!lim)
|
||||
continue;
|
||||
@ -288,6 +282,9 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
|
||||
ui->comboBoxRelative->setCurrentIndex(initindex);
|
||||
on_comboBoxRelative_activated(initindex);
|
||||
|
||||
ui->comboMatchBiome->insertItem(0, biome2str(mc, cond.biomeId), QVariant::fromValue(cond.biomeId));
|
||||
ui->comboMatchBiome->setCurrentIndex(0);
|
||||
|
||||
updateMode();
|
||||
|
||||
ui->spinBox->setValue(cond.count);
|
||||
@ -352,25 +349,16 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
|
||||
}
|
||||
}
|
||||
|
||||
int idx = ui->comboMatchBiome->findData(QVariant::fromValue(cond.biomeId));
|
||||
if (idx >= 0)
|
||||
{
|
||||
ui->comboMatchBiome->setCurrentIndex(idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString bstr = biome2str(mc, cond.biomeId);
|
||||
ui->comboMatchBiome->insertItem(0, QIcon(":/icons/check2.png"), bstr, QVariant::fromValue(cond.biomeId));
|
||||
ui->comboMatchBiome->setCurrentIndex(0);
|
||||
allowed_matches.append(bstr);
|
||||
}
|
||||
ui->lineBiomeSize->setText(QString::number(cond.biomeSize));
|
||||
ui->lineTollerance->setText(QString::number(cond.tol));
|
||||
|
||||
auto totristate = [](uint16_t st, uint16_t msk) {
|
||||
return (st & msk) ? (st & Condition::VAR_NOT) ? Qt::Checked : Qt::PartiallyChecked : Qt::Unchecked;
|
||||
};
|
||||
ui->checkStartPieces->setChecked(cond.varflags & Condition::VAR_WITH_START);
|
||||
ui->checkAbandoned->setChecked(cond.varflags & Condition::VAR_ABANODONED);
|
||||
ui->checkEndShip->setChecked(cond.varflags & Condition::VAR_ENDSHIP);
|
||||
ui->checkDenseBB->setChecked(cond.varflags & Condition::VAR_DENSE_BB);
|
||||
ui->checkAbandoned->setCheckState(totristate(cond.varflags, Condition::VAR_ABANODONED));
|
||||
ui->checkEndShip->setCheckState(totristate(cond.varflags, Condition::VAR_ENDSHIP));
|
||||
for (VariantCheckBox *cb : qAsConst(variantboxes))
|
||||
{
|
||||
int idx = cb->sp - g_start_pieces;
|
||||
@ -390,12 +378,6 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
|
||||
setClimateLimits(climaterange[1], cond.limex, false);
|
||||
}
|
||||
|
||||
|
||||
QRegularExpressionValidator *reval = new QRegularExpressionValidator(
|
||||
QRegularExpression("(" + allowed_matches.join("|") + ")"), this
|
||||
);
|
||||
ui->comboMatchBiome->lineEdit()->setValidator(reval);
|
||||
|
||||
on_lineSquare_editingFinished();
|
||||
|
||||
onClimateLimitChanged();
|
||||
@ -480,7 +462,7 @@ void ConditionDialog::updateMode()
|
||||
{
|
||||
ui->stackedWidget->setCurrentWidget(ui->pageClimates);
|
||||
}
|
||||
else if (filterindex == F_BIOME_CENTER)
|
||||
else if (filterindex == F_BIOME_CENTER || filterindex == F_BIOME_CENTER_256)
|
||||
{
|
||||
ui->stackedWidget->setCurrentWidget(ui->pageBiomeCenter);
|
||||
}
|
||||
@ -637,41 +619,80 @@ void ConditionDialog::updateBiomeSelection()
|
||||
}
|
||||
}
|
||||
|
||||
// separate available biomes
|
||||
QLayoutItem *sep = ui->gridLayoutBiomes->takeAt(ui->gridLayoutBiomes->indexOf(separator));
|
||||
std::vector<int> unavailable;
|
||||
std::map<int, QLayoutItem*> items;
|
||||
for (const auto& it : biomecboxes)
|
||||
{
|
||||
int id = it.first;
|
||||
QCheckBox *cb = it.second;
|
||||
int idx = ui->gridLayoutBiomes->indexOf(cb);
|
||||
items[id] = ui->gridLayoutBiomes->takeAt(idx);
|
||||
if (std::find(available.begin(), available.end(), id) == available.end())
|
||||
unavailable.push_back(id);
|
||||
}
|
||||
|
||||
IdCmp cmp = {IdCmp::SORT_LEX, mc, DIM_UNDEF};
|
||||
std::sort(available.begin(), available.end(), cmp);
|
||||
std::sort(unavailable.begin(), unavailable.end(), cmp);
|
||||
|
||||
int row = 0;
|
||||
for (int i = 0, len = available.size(), mod = (len+1)/2; i < len; i++)
|
||||
if (ui->stackedWidget->currentWidget() == ui->pageBiomes)
|
||||
{
|
||||
int id = available[i];
|
||||
biomecboxes[id]->setEnabled(true);
|
||||
QLayoutItem *item = items[id];
|
||||
ui->gridLayoutBiomes->addItem(item, row+i%mod, i/mod);
|
||||
// separate available biomes
|
||||
QLayoutItem *sep = ui->gridLayoutBiomes->takeAt(ui->gridLayoutBiomes->indexOf(separator));
|
||||
std::vector<int> unavailable;
|
||||
std::map<int, QLayoutItem*> items;
|
||||
for (const auto& it : biomecboxes)
|
||||
{
|
||||
int id = it.first;
|
||||
QCheckBox *cb = it.second;
|
||||
int idx = ui->gridLayoutBiomes->indexOf(cb);
|
||||
items[id] = ui->gridLayoutBiomes->takeAt(idx);
|
||||
if (std::find(available.begin(), available.end(), id) == available.end())
|
||||
unavailable.push_back(id);
|
||||
}
|
||||
std::sort(unavailable.begin(), unavailable.end(), cmp);
|
||||
|
||||
int row = 0;
|
||||
for (int i = 0, len = available.size(), mod = (len+1)/2; i < len; i++)
|
||||
{
|
||||
int id = available[i];
|
||||
biomecboxes[id]->setEnabled(true);
|
||||
QLayoutItem *item = items[id];
|
||||
ui->gridLayoutBiomes->addItem(item, row+i%mod, i/mod);
|
||||
}
|
||||
row = (available.size() + 1) / 2;
|
||||
ui->gridLayoutBiomes->addItem(sep, row, 0, 1, 2);
|
||||
row++;
|
||||
for (int i = 0, len = unavailable.size(), mod = (len+1)/2; i < len; i++)
|
||||
{
|
||||
int id = unavailable[i];
|
||||
biomecboxes[id]->setEnabled(false);
|
||||
QLayoutItem *item = items[id];
|
||||
ui->gridLayoutBiomes->addItem(item, row+i%mod, i/mod);
|
||||
}
|
||||
}
|
||||
row = (available.size() + 1) / 2;
|
||||
ui->gridLayoutBiomes->addItem(sep, row, 0, 1, 2);
|
||||
row++;
|
||||
for (int i = 0, len = unavailable.size(), mod = (len+1)/2; i < len; i++)
|
||||
|
||||
if (ui->stackedWidget->currentWidget() == ui->pageBiomeCenter)
|
||||
{
|
||||
int id = unavailable[i];
|
||||
biomecboxes[id]->setEnabled(false);
|
||||
QLayoutItem *item = items[id];
|
||||
ui->gridLayoutBiomes->addItem(item, row+i%mod, i/mod);
|
||||
QStringList allowed_matches;
|
||||
QVariant curid = ui->comboMatchBiome->currentData();
|
||||
ui->comboMatchBiome->clear();
|
||||
|
||||
for (int id: available)
|
||||
{
|
||||
QString s = biome2str(mc, id);
|
||||
ui->comboMatchBiome->addItem(getBiomeIcon(id), s, QVariant::fromValue(id));
|
||||
allowed_matches.append(s);
|
||||
}
|
||||
if (curid.isValid())
|
||||
{
|
||||
int idx = ui->comboMatchBiome->findData(curid);
|
||||
if (idx >= 0)
|
||||
{
|
||||
ui->comboMatchBiome->setCurrentIndex(idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString s = QString("%1 %2").arg(WARNING_CHAR).arg(biome2str(mc, curid.toInt()));
|
||||
ui->comboMatchBiome->insertItem(0, getBiomeIcon(curid.toInt(), true), s, curid);
|
||||
ui->comboMatchBiome->setCurrentIndex(0);
|
||||
allowed_matches.append(s);
|
||||
}
|
||||
}
|
||||
|
||||
QRegularExpressionValidator *reval = new QRegularExpressionValidator(
|
||||
QRegularExpression("(" + allowed_matches.join("|") + ")"), this
|
||||
);
|
||||
ui->comboMatchBiome->lineEdit()->setValidator(reval);
|
||||
|
||||
on_lineBiomeSize_textChanged("");
|
||||
}
|
||||
}
|
||||
|
||||
@ -703,7 +724,7 @@ int ConditionDialog::warnIfBad(Condition cond)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cond.type == F_BIOME_CENTER)
|
||||
else if (cond.type == F_BIOME_CENTER || cond.type == F_BIOME_CENTER_256)
|
||||
{
|
||||
int w = cond.x2 - cond.x1 + 1;
|
||||
int h = cond.z2 - cond.z1 + 1;
|
||||
@ -918,10 +939,23 @@ void ConditionDialog::on_buttonOk_clicked()
|
||||
c.flags |= MATCH_ANY;
|
||||
|
||||
c.varflags = c.varstart = 0;
|
||||
c.varflags |= ui->checkStartPieces->isChecked() * Condition::VAR_WITH_START;
|
||||
c.varflags |= ui->checkAbandoned->isChecked() * Condition::VAR_ABANODONED;
|
||||
c.varflags |= ui->checkEndShip->isChecked() * Condition::VAR_ENDSHIP;
|
||||
c.varflags |= ui->checkDenseBB->isChecked() * Condition::VAR_DENSE_BB;
|
||||
if (ui->checkStartPieces->isChecked())
|
||||
c.varflags |= Condition::VAR_WITH_START;
|
||||
if (ui->checkDenseBB->isChecked())
|
||||
c.varflags |= Condition::VAR_DENSE_BB;
|
||||
if (ui->checkAbandoned->checkState() != Qt::Unchecked)
|
||||
{
|
||||
c.varflags |= Condition::VAR_ABANODONED;
|
||||
if (ui->checkAbandoned->checkState() == Qt::Checked)
|
||||
c.varflags |= Condition::VAR_NOT;
|
||||
}
|
||||
if (ui->checkEndShip->checkState() != Qt::Unchecked)
|
||||
{
|
||||
c.varflags |= Condition::VAR_ENDSHIP;
|
||||
if (ui->checkAbandoned->checkState() == Qt::Checked)
|
||||
c.varflags |= Condition::VAR_NOT;
|
||||
}
|
||||
|
||||
for (VariantCheckBox *cb : qAsConst(variantboxes))
|
||||
{
|
||||
if (!cb->isChecked())
|
||||
@ -1090,9 +1124,15 @@ void ConditionDialog::onClimateLimitChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void ConditionDialog::on_lineBiomeSize_textChanged(const QString &text)
|
||||
void ConditionDialog::on_lineBiomeSize_textChanged(const QString &)
|
||||
{
|
||||
double area = text.toInt();
|
||||
ui->labelBiomeSize->setText(QString::asprintf("(%g sq. chunks)", area / 16));
|
||||
int filterindex = ui->comboBoxType->currentData().toInt();
|
||||
double area = ui->lineBiomeSize->text().toInt();
|
||||
QString s;
|
||||
if (filterindex == F_BIOME_CENTER_256)
|
||||
s = QString::asprintf("(~%g sq. chunks)", area * 256);
|
||||
else
|
||||
s = QString::asprintf("(%g sq. chunks)", area / 16.0);
|
||||
ui->labelBiomeSize->setText(s);
|
||||
}
|
||||
|
||||
|
@ -489,7 +489,7 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="pageNone">
|
||||
<property name="enabled">
|
||||
@ -701,8 +701,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>60</width>
|
||||
<height>20</height>
|
||||
<width>634</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
@ -1013,8 +1013,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>60</width>
|
||||
<height>20</height>
|
||||
<width>634</width>
|
||||
<height>253</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
@ -1068,6 +1068,9 @@
|
||||
<property name="text">
|
||||
<string>Abandoned</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
@ -1080,8 +1083,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>60</width>
|
||||
<height>20</height>
|
||||
<width>634</width>
|
||||
<height>335</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3_1">
|
||||
@ -1137,8 +1140,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>60</width>
|
||||
<height>20</height>
|
||||
<width>634</width>
|
||||
<height>335</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3_2">
|
||||
@ -1242,8 +1245,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>60</width>
|
||||
<height>20</height>
|
||||
<width>634</width>
|
||||
<height>335</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3_3">
|
||||
@ -1284,6 +1287,9 @@
|
||||
<property name="text">
|
||||
<string>End ship</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
|
@ -28,9 +28,7 @@ ConfigDialog::ConfigDialog(QWidget *parent, Config *config)
|
||||
}
|
||||
#endif
|
||||
|
||||
QFont mono = QFont("Monospace", 9);
|
||||
mono.setStyleHint(QFont::TypeWriter);
|
||||
ui->buttonBiomeColor->setFont(mono);
|
||||
ui->buttonBiomeColor->setFont(g_font_mono);
|
||||
|
||||
ui->lineMatching->setValidator(new QIntValidator(1, 99999999, ui->lineMatching));
|
||||
|
||||
@ -56,6 +54,9 @@ void ConfigDialog::initSettings(Config *config)
|
||||
ui->lineMatching->setText(QString::number(config->maxMatching));
|
||||
ui->lineGridSpacing->setText(config->gridSpacing ? QString::number(config->gridSpacing) : "");
|
||||
ui->spinCacheSize->setValue(config->mapCacheSize);
|
||||
ui->lineSep->setText(config->separator);
|
||||
int idx = config->quote == "\'" ? 1 : config->quote== "\"" ? 2 : 0;
|
||||
ui->comboQuote->setCurrentIndex(idx);
|
||||
|
||||
setBiomeColorPath(config->biomeColorPath);
|
||||
}
|
||||
@ -72,6 +73,9 @@ Config ConfigDialog::getSettings()
|
||||
conf.maxMatching = ui->lineMatching->text().toInt();
|
||||
conf.gridSpacing = ui->lineGridSpacing->text().toInt();
|
||||
conf.mapCacheSize = ui->spinCacheSize->value();
|
||||
conf.separator = ui->lineSep->text();
|
||||
int idx = ui->comboQuote->currentIndex();
|
||||
conf.quote = idx == 1 ? "\'" : idx == 2 ? "\"" : "";
|
||||
|
||||
if (!conf.maxMatching) conf.maxMatching = 65536;
|
||||
|
||||
|
@ -10,13 +10,13 @@
|
||||
<normaloff>:/icons/logo.png</normaloff>:/icons/logo.png</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="groupSession">
|
||||
<property name="title">
|
||||
<string>Session</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0" rowspan="2" colspan="3">
|
||||
<item row="0" column="0" rowspan="2" colspan="2">
|
||||
<widget class="QCheckBox" name="checkRestore">
|
||||
<property name="text">
|
||||
<string>Restore previous session at launch</string>
|
||||
@ -30,7 +30,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="spinAutosave">
|
||||
<property name="suffix">
|
||||
<string> min</string>
|
||||
@ -49,7 +49,52 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="3" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="lineSep"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>CSV cell quotation:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>CSV column separator:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="comboQuote">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Where necessary</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Single quotes (')</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Double quotes (")</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="groupSearch">
|
||||
<property name="title">
|
||||
<string>Search</string>
|
||||
@ -68,7 +113,17 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QGroupBox" name="groupMisc">
|
||||
<property name="title">
|
||||
<string>Miscellaneous</string>
|
||||
@ -84,17 +139,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="2" colspan="2">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="groupInterface">
|
||||
<property name="title">
|
||||
<string>Interface</string>
|
||||
@ -119,13 +164,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="4">
|
||||
<widget class="QCheckBox" name="checkBBoxes">
|
||||
<property name="text">
|
||||
<string>Outline known bounding boxes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2" colspan="2">
|
||||
<widget class="QPushButton" name="buttonBiomeColor">
|
||||
<property name="text">
|
||||
@ -187,16 +225,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="4">
|
||||
<widget class="QCheckBox" name="checkSmooth">
|
||||
<property name="toolTip">
|
||||
<string>Simulate innertia for the map view</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Smooth map motion</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
@ -259,13 +287,30 @@ Leave blank for the default behaviour</string>
|
||||
<item row="4" column="2" colspan="3">
|
||||
<widget class="QLineEdit" name="lineGridSpacing"/>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="4">
|
||||
<item row="6" column="0" colspan="5">
|
||||
<widget class="QCheckBox" name="checkDockable">
|
||||
<property name="text">
|
||||
<string>Undockable map</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="5">
|
||||
<widget class="QCheckBox" name="checkSmooth">
|
||||
<property name="toolTip">
|
||||
<string>Simulate innertia for the map view</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Smooth map motion</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="5">
|
||||
<widget class="QCheckBox" name="checkBBoxes">
|
||||
<property name="text">
|
||||
<string>Outline known bounding boxes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
13
src/cutil.h
13
src/cutil.h
@ -159,16 +159,18 @@ inline int str2seed(const QString &str, uint64_t *out)
|
||||
return S_TEXT;
|
||||
}
|
||||
|
||||
|
||||
struct IdCmp
|
||||
{
|
||||
enum
|
||||
{
|
||||
SORT_ID,
|
||||
SORT_LEX,
|
||||
SORT_DIM,
|
||||
};
|
||||
|
||||
IdCmp(int mode, int mc, int dim) : mode(mode),mc(mc),dim(dim) {}
|
||||
IdCmp(int mode, int mc, int dim) : mode(mode),mc(mc),dim(dim)
|
||||
{
|
||||
}
|
||||
|
||||
int mode;
|
||||
int mc;
|
||||
@ -190,6 +192,13 @@ struct IdCmp
|
||||
}
|
||||
if (v1 ^ v2)
|
||||
return v1;
|
||||
if (mode == SORT_DIM)
|
||||
{
|
||||
int d1 = getDimension(id1);
|
||||
int d2 = getDimension(id2);
|
||||
if (d1 != d2)
|
||||
return (d1==0 ? 0 : d1==-1 ? 1 : 2) < (d2==0 ? 0 : d2==-1 ? 1 : 2);
|
||||
}
|
||||
const char *s1 = biome2str(mc, id1);
|
||||
const char *s2 = biome2str(mc, id2);
|
||||
if (!s1 && !s2) return id1 < id2;
|
||||
|
@ -40,9 +40,7 @@ FormConditions::FormConditions(QWidget *parent)
|
||||
qRegisterMetaType< Condition >("Condition");
|
||||
qRegisterMetaTypeStreamOperators< Condition >("Condition");
|
||||
|
||||
QFont mono = QFont("Monospace", 9);
|
||||
mono.setStyleHint(QFont::TypeWriter);
|
||||
ui->listConditionsFull->setFont(mono);
|
||||
ui->listConditionsFull->setFont(g_font_mono);
|
||||
}
|
||||
|
||||
FormConditions::~FormConditions()
|
||||
|
@ -79,9 +79,7 @@ FormGen48::FormGen48(MainWindow *parent)
|
||||
connect(ui->lineSalt, SIGNAL(editingFinished()), SLOT(onChange()));
|
||||
connect(ui->lineListSalt, SIGNAL(editingFinished()), SLOT(onChange()));
|
||||
|
||||
QFont mono = QFont("Monospace", 9);
|
||||
mono.setStyleHint(QFont::TypeWriter);
|
||||
ui->lineList48->setFont(mono);
|
||||
ui->lineList48->setFont(g_font_mono);
|
||||
|
||||
cond.type = 0;
|
||||
Gen48Settings defaults;
|
||||
|
@ -12,12 +12,91 @@
|
||||
#include <QClipboard>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QFontMetrics>
|
||||
|
||||
|
||||
QVariant SeedTableModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (role == Qt::DisplayRole)
|
||||
{
|
||||
if (index.column() == COL_SEED)
|
||||
return seeds[index.row()].txtSeed;
|
||||
if (index.column() == COL_HEX48)
|
||||
return seeds[index.row()].txtHex48;
|
||||
if (index.column() == COL_TOP16)
|
||||
return seeds[index.row()].txtTop16;
|
||||
}
|
||||
else if (role == Qt::UserRole)
|
||||
{
|
||||
if (index.column() == COL_HEX48)
|
||||
return seeds[index.row()].varHex48;
|
||||
if (index.column() == COL_TOP16)
|
||||
return seeds[index.row()].varTop16;
|
||||
return seeds[index.row()].varSeed;
|
||||
}
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
QVariant SeedTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (section < 0)
|
||||
return QVariant::Invalid;
|
||||
if (role == Qt::InitialSortOrderRole)
|
||||
return QVariant::fromValue(Qt::DescendingOrder);
|
||||
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
|
||||
{
|
||||
if (section == COL_SEED)
|
||||
return QVariant::fromValue(tr("seed"));
|
||||
if (section == COL_TOP16)
|
||||
return QVariant::fromValue(tr("top 16"));
|
||||
if (section == COL_HEX48)
|
||||
return QVariant::fromValue(tr("lower 48 bit"));
|
||||
}
|
||||
if (role == Qt::DisplayRole && orientation == Qt::Vertical)
|
||||
return QVariant::fromValue(section + 1);
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
int SeedTableModel::insertSeeds(QVector<uint64_t> newseeds)
|
||||
{
|
||||
int row = seeds.size();
|
||||
beginInsertRows(QModelIndex(), row, row + newseeds.size()-1);
|
||||
for (uint64_t seed : qAsConst(newseeds))
|
||||
{
|
||||
Seed s;
|
||||
s.seed = seed;
|
||||
s.varSeed = QVariant::fromValue(seed);
|
||||
s.varTop16 = QVariant::fromValue((quint64)(seed>>48) & 0xFFFF);
|
||||
s.varHex48 = QVariant::fromValue((quint64)(seed & MASK48));
|
||||
s.txtSeed = QVariant::fromValue(QString::asprintf("%" PRId64, seed));
|
||||
s.txtTop16 = QVariant::fromValue(QString::asprintf("%04llx", (quint64)(seed>>48) & 0xFFFF));
|
||||
s.txtHex48 = QVariant::fromValue(QString::asprintf("%012llx", (quint64)(seed & MASK48)));
|
||||
seeds.append(s);
|
||||
}
|
||||
endInsertRows();
|
||||
return row;
|
||||
}
|
||||
|
||||
void SeedTableModel::removeRow(int row)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
seeds.removeAt(row);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void SeedTableModel::reset()
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), 0, seeds.size());
|
||||
seeds.clear();
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
FormSearchControl::FormSearchControl(MainWindow *parent)
|
||||
: QWidget(parent)
|
||||
, parent(parent)
|
||||
, ui(new Ui::FormSearchControl)
|
||||
, model(new SeedTableModel(this))
|
||||
, proxy(new SeedSortProxy(this))
|
||||
, protodialog()
|
||||
, sthread(this)
|
||||
, stimer()
|
||||
@ -28,22 +107,38 @@ FormSearchControl::FormSearchControl(MainWindow *parent)
|
||||
, slist64()
|
||||
, smin(0)
|
||||
, smax(~(uint64_t)0)
|
||||
, qbuf()
|
||||
, nextupdate()
|
||||
{
|
||||
ui->setupUi(this);
|
||||
protodialog = new ProtoBaseDialog(this);
|
||||
|
||||
QFont mono = QFont("Monospace", 9);
|
||||
mono.setStyleHint(QFont::TypeWriter);
|
||||
ui->listResults->setFont(mono);
|
||||
ui->progressBar->setFont(mono);
|
||||
ui->labelStatus->setFont(mono);
|
||||
ui->results->setFont(g_font_mono);
|
||||
ui->progressBar->setFont(g_font_mono);
|
||||
ui->labelStatus->setFont(g_font_mono);
|
||||
|
||||
ui->listResults->horizontalHeader()->setFont(mono);
|
||||
proxy->setSourceModel(model);
|
||||
ui->results->setModel(proxy);
|
||||
|
||||
ui->results->horizontalHeader()->setFont(g_font_mono);
|
||||
ui->results->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||
ui->results->verticalHeader()->setDefaultSectionSize(QFontMetrics(g_font_mono).height());
|
||||
ui->results->setColumnWidth(SeedTableModel::COL_SEED, 200);
|
||||
ui->results->setColumnWidth(SeedTableModel::COL_TOP16, 60);
|
||||
ui->results->setColumnWidth(SeedTableModel::COL_HEX48, 120);
|
||||
|
||||
connect(ui->results->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &FormSearchControl::onSort);
|
||||
ui->results->sortByColumn(-1, Qt::AscendingOrder);
|
||||
|
||||
connect(&sthread, &SearchMaster::searchFinish, this, &FormSearchControl::searchFinish, Qt::QueuedConnection);
|
||||
|
||||
connect(&stimer, &QTimer::timeout, this, QOverload<>::of(&FormSearchControl::resultTimeout));
|
||||
|
||||
connect(
|
||||
ui->results->selectionModel(),
|
||||
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||
SLOT(onSeedSelectionChanged()));
|
||||
|
||||
searchProgressReset();
|
||||
ui->spinThreads->setMaximum(QThread::idealThreadCount());
|
||||
ui->spinThreads->setValue(QThread::idealThreadCount());
|
||||
@ -62,11 +157,11 @@ FormSearchControl::~FormSearchControl()
|
||||
|
||||
QVector<uint64_t> FormSearchControl::getResults()
|
||||
{
|
||||
int n = ui->listResults->rowCount();
|
||||
int n = proxy->rowCount();
|
||||
QVector<uint64_t> results = QVector<uint64_t>(n);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
results[i] = ui->listResults->item(i, 0)->data(Qt::UserRole).toULongLong();
|
||||
results[i] = proxy->data(proxy->index(i, SeedTableModel::COL_SEED), Qt::UserRole).toULongLong();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
@ -210,8 +305,7 @@ void FormSearchControl::closeProtobaseMsg()
|
||||
|
||||
void FormSearchControl::on_buttonClear_clicked()
|
||||
{
|
||||
ui->listResults->clearContents();
|
||||
ui->listResults->setRowCount(0);
|
||||
model->reset();
|
||||
searchProgressReset();
|
||||
ui->lineStart->setText("0");
|
||||
}
|
||||
@ -266,6 +360,7 @@ void FormSearchControl::on_buttonStart_clicked()
|
||||
ui->buttonStart->setText(tr("Abort search"));
|
||||
ui->buttonStart->setIcon(QIcon(":/icons/cancel.png"));
|
||||
searchLockUi(true);
|
||||
nextupdate = 0;
|
||||
sthread.start();
|
||||
elapsed.start();
|
||||
stimer.start(250);
|
||||
@ -310,17 +405,18 @@ void FormSearchControl::on_buttonMore_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
void FormSearchControl::on_listResults_itemSelectionChanged()
|
||||
void FormSearchControl::onSeedSelectionChanged()
|
||||
{
|
||||
int row = ui->listResults->currentRow();
|
||||
if (row >= 0 && row < ui->listResults->rowCount())
|
||||
int row = ui->results->currentIndex().row();
|
||||
if (row >= 0 && row < ui->results->model()->rowCount())
|
||||
{
|
||||
uint64_t s = ui->listResults->item(row, 0)->data(Qt::UserRole).toULongLong();
|
||||
QModelIndex idx = ui->results->model()->index(row, SeedTableModel::COL_SEED);
|
||||
uint64_t s = ui->results->model()->data(idx, Qt::UserRole).toULongLong();
|
||||
emit selectedSeedChanged(s);
|
||||
}
|
||||
}
|
||||
|
||||
void FormSearchControl::on_listResults_customContextMenuRequested(const QPoint &pos)
|
||||
void FormSearchControl::on_results_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
QMenu menu(this);
|
||||
|
||||
@ -330,19 +426,19 @@ void FormSearchControl::on_listResults_customContextMenuRequested(const QPoint &
|
||||
QAction *actremove = menu.addAction(QIcon::fromTheme("list-remove"),
|
||||
tr("Remove selected seed"), this,
|
||||
&FormSearchControl::removeCurrent, QKeySequence::Delete);
|
||||
actremove->setEnabled(!ui->listResults->selectedItems().empty());
|
||||
actremove->setEnabled(!ui->results->selectionModel()->hasSelection());
|
||||
|
||||
QAction *actcopy = menu.addAction(QIcon::fromTheme("edit-copy"),
|
||||
tr("Copy list to clipboard"), this,
|
||||
&FormSearchControl::copyResults, QKeySequence::Copy);
|
||||
actcopy->setEnabled(ui->listResults->rowCount() > 0);
|
||||
actcopy->setEnabled(ui->results->model()->rowCount() > 0);
|
||||
|
||||
int n = pasteList(true);
|
||||
QAction *actpaste = menu.addAction(QIcon::fromTheme("edit-paste"),
|
||||
tr("Paste %n seed(s) from clipboard", "", n), this,
|
||||
&FormSearchControl::pasteResults, QKeySequence::Paste);
|
||||
actpaste->setEnabled(n > 0);
|
||||
menu.exec(ui->listResults->mapToGlobal(pos));
|
||||
menu.exec(ui->results->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void FormSearchControl::on_buttonSearchHelp_clicked()
|
||||
@ -409,11 +505,51 @@ int FormSearchControl::pasteList(bool dummy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FormSearchControl::onSort(int, Qt::SortOrder)
|
||||
{
|
||||
// We want to achieve: none -> descending -> ascending -> none
|
||||
// The headerview flips the indicator with the logic:
|
||||
// if (same_section)
|
||||
// new_order = old_order == descending ? ascending : descending
|
||||
// else
|
||||
// new_order = InitialSortOrder (else ascending)
|
||||
QHeaderView *header = ui->results->horizontalHeader();
|
||||
|
||||
if (proxy->order == Qt::AscendingOrder && proxy->column != -1)
|
||||
{
|
||||
header->setSortIndicatorShown(false);
|
||||
header->setSortIndicator(-1, Qt::DescendingOrder);
|
||||
proxy->column = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
header->setSortIndicatorShown(true);
|
||||
}
|
||||
}
|
||||
|
||||
void FormSearchControl::onSearchResult(uint64_t seed)
|
||||
{
|
||||
qbuf.push_back(seed);
|
||||
quint64 ns = elapsed.nsecsElapsed();
|
||||
if (ns > nextupdate)
|
||||
{
|
||||
quint64 buffer_ms = 100; // advanced option
|
||||
QTimer::singleShot(buffer_ms, this, &FormSearchControl::onBufferTimeout);
|
||||
nextupdate = ns + buffer_ms * 1e6;
|
||||
}
|
||||
}
|
||||
|
||||
void FormSearchControl::onBufferTimeout()
|
||||
{
|
||||
searchResultsAdd(qbuf, false);
|
||||
qbuf.clear();
|
||||
nextupdate = 0;
|
||||
}
|
||||
|
||||
int FormSearchControl::searchResultsAdd(QVector<uint64_t> seeds, bool countonly)
|
||||
{
|
||||
const Config& config = parent->config;
|
||||
int ns = ui->listResults->rowCount();
|
||||
int ns = model->seeds.size();
|
||||
int n = ns;
|
||||
if (n >= config.maxMatching)
|
||||
return 0;
|
||||
@ -425,12 +561,9 @@ int FormSearchControl::searchResultsAdd(QVector<uint64_t> seeds, bool countonly)
|
||||
QSet<uint64_t> current;
|
||||
current.reserve(n + seeds.size());
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
uint64_t seed = ui->listResults->item(i, 0)->data(Qt::UserRole).toULongLong();
|
||||
current.insert(seed);
|
||||
}
|
||||
current.insert(model->seeds[i].seed);
|
||||
|
||||
ui->listResults->setSortingEnabled(false);
|
||||
QVector<uint64_t> newseeds;
|
||||
for (uint64_t s : seeds)
|
||||
{
|
||||
if (current.contains(s))
|
||||
@ -441,19 +574,15 @@ int FormSearchControl::searchResultsAdd(QVector<uint64_t> seeds, bool countonly)
|
||||
continue;
|
||||
}
|
||||
current.insert(s);
|
||||
QTableWidgetItem* s48item = new QTableWidgetItem();
|
||||
QTableWidgetItem* seeditem = new QTableWidgetItem();
|
||||
s48item->setData(Qt::UserRole, QVariant::fromValue(s));
|
||||
s48item->setText(QString::asprintf("%012llx|%04x",
|
||||
(qulonglong)(s & MASK48), (uint)(s >> 48) & 0xffff));
|
||||
seeditem->setData(Qt::DisplayRole, QVariant::fromValue((int64_t)s));
|
||||
ui->listResults->insertRow(n);
|
||||
ui->listResults->setItem(n, 0, s48item);
|
||||
ui->listResults->setItem(n, 1, seeditem);
|
||||
newseeds.append(s);
|
||||
n++;
|
||||
}
|
||||
ui->listResults->setSortingEnabled(true);
|
||||
|
||||
if (!newseeds.empty())
|
||||
{
|
||||
ui->results->setSortingEnabled(false);
|
||||
model->insertSeeds(newseeds);
|
||||
ui->results->setSortingEnabled(true);
|
||||
}
|
||||
if (countonly == false && n >= config.maxMatching)
|
||||
{
|
||||
sthread.stop();
|
||||
@ -640,18 +769,19 @@ void FormSearchControl::resultTimeout()
|
||||
|
||||
void FormSearchControl::removeCurrent()
|
||||
{
|
||||
int row = ui->listResults->currentRow();
|
||||
int row = ui->results->selectionModel()->currentIndex().row();
|
||||
if (row >= 0)
|
||||
ui->listResults->removeRow(row);
|
||||
model->removeRow(row);
|
||||
}
|
||||
|
||||
void FormSearchControl::copyResults()
|
||||
{
|
||||
QString text;
|
||||
int n = ui->listResults->rowCount();
|
||||
int n = ui->results->model()->rowCount();
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
uint64_t seed = ui->listResults->item(i, 0)->data(Qt::UserRole).toULongLong();
|
||||
QModelIndex idx = ui->results->model()->index(i, SeedTableModel::COL_SEED);
|
||||
uint64_t seed = ui->results->model()->data(idx, Qt::UserRole).toULongLong();
|
||||
text += QString::asprintf("%" PRId64 "\n", seed);
|
||||
}
|
||||
|
||||
@ -661,7 +791,7 @@ void FormSearchControl::copyResults()
|
||||
|
||||
void FormSearchControl::keyReleaseEvent(QKeyEvent *event)
|
||||
{
|
||||
if (ui->listResults->hasFocus())
|
||||
if (ui->results->hasFocus())
|
||||
{
|
||||
if (event->matches(QKeySequence::Delete))
|
||||
removeCurrent();
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <QKeyEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QElapsedTimer>
|
||||
#include <QAbstractTableModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <deque>
|
||||
|
||||
@ -19,6 +21,72 @@ class FormSearchControl;
|
||||
|
||||
class MainWindow;
|
||||
|
||||
class SeedTableModel : public QAbstractTableModel
|
||||
{
|
||||
public:
|
||||
explicit SeedTableModel(QObject *parent = nullptr) :
|
||||
QAbstractTableModel(parent) {}
|
||||
|
||||
enum { COL_SEED, COL_TOP16, COL_HEX48, COL_MAX };
|
||||
|
||||
virtual int rowCount(const QModelIndex&) const override { return seeds.size(); }
|
||||
virtual int columnCount(const QModelIndex&) const override { return COL_MAX; }
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role) const override;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
|
||||
int insertSeeds(QVector<uint64_t> seeds);
|
||||
void removeRow(int row);
|
||||
void reset();
|
||||
|
||||
struct Seed
|
||||
{
|
||||
uint64_t seed;
|
||||
QVariant varSeed, varHex48, varTop16;
|
||||
QVariant txtSeed, txtHex48, txtTop16;
|
||||
};
|
||||
QList<Seed> seeds;
|
||||
};
|
||||
|
||||
class SeedSortProxy : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SeedSortProxy(QObject *parent = nullptr) : QSortFilterProxyModel(parent),column(),order(Qt::DescendingOrder) {}
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
|
||||
{
|
||||
if (orientation == Qt::Vertical && role == Qt::DisplayRole)
|
||||
return QVariant::fromValue(section + 1);
|
||||
return QSortFilterProxyModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
virtual bool lessThan(const QModelIndex& a, const QModelIndex& b) const override
|
||||
{
|
||||
uint64_t av = sourceModel()->data(a, Qt::UserRole).toULongLong();
|
||||
uint64_t bv = sourceModel()->data(b, Qt::UserRole).toULongLong();
|
||||
if (a.column() == SeedTableModel::COL_SEED)
|
||||
return (int64_t) bv < (int64_t) av;
|
||||
else
|
||||
return bv < av;
|
||||
}
|
||||
|
||||
virtual void sort(int column, Qt::SortOrder order) override
|
||||
{
|
||||
if (column >= columnCount())
|
||||
return;
|
||||
if (this->column == -1)
|
||||
QSortFilterProxyModel::sort(-1, order);
|
||||
else
|
||||
QSortFilterProxyModel::sort(column, order);
|
||||
this->column = column;
|
||||
this->order = order;
|
||||
}
|
||||
|
||||
int column;
|
||||
Qt::SortOrder order;
|
||||
};
|
||||
|
||||
class FormSearchControl : public QWidget
|
||||
{
|
||||
@ -53,8 +121,9 @@ public slots:
|
||||
void on_buttonStart_clicked();
|
||||
void on_buttonMore_clicked();
|
||||
|
||||
void on_listResults_itemSelectionChanged();
|
||||
void on_listResults_customContextMenuRequested(const QPoint& pos);
|
||||
void onSort(int column, Qt::SortOrder);
|
||||
void onSeedSelectionChanged();
|
||||
void on_results_customContextMenuRequested(const QPoint& pos);
|
||||
|
||||
void on_buttonSearchHelp_clicked();
|
||||
|
||||
@ -62,6 +131,8 @@ public slots:
|
||||
|
||||
void pasteResults();
|
||||
int pasteList(bool dummy);
|
||||
void onBufferTimeout();
|
||||
void onSearchResult(uint64_t seed);
|
||||
int searchResultsAdd(QVector<uint64_t> seeds, bool countonly);
|
||||
void searchProgressReset();
|
||||
void searchProgress(uint64_t last, uint64_t end, int64_t seed);
|
||||
@ -75,9 +146,12 @@ protected:
|
||||
|
||||
public:
|
||||
struct TProg { uint64_t ns, prog; };
|
||||
|
||||
private:
|
||||
MainWindow *parent;
|
||||
Ui::FormSearchControl *ui;
|
||||
SeedTableModel *model;
|
||||
SeedSortProxy *proxy;
|
||||
ProtoBaseDialog *protodialog;
|
||||
SearchMaster sthread;
|
||||
QTimer stimer;
|
||||
@ -94,6 +168,10 @@ private:
|
||||
|
||||
// min and max seeds values
|
||||
uint64_t smin, smax;
|
||||
|
||||
// found seeds that are waiting to be added to results
|
||||
QVector<uint64_t> qbuf;
|
||||
quint64 nextupdate;
|
||||
};
|
||||
|
||||
#endif // FORMSEARCHCONTROL_H
|
||||
|
@ -32,68 +32,6 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableWidget" name="listResults">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>180</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>21</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Hex low-48 | top-16</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignLeading|AlignVCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Seed</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignLeading|AlignVCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="4">
|
||||
@ -286,6 +224,46 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QTableView" name="results">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <QDoubleValidator>
|
||||
#include <QKeyEvent>
|
||||
#include <QClipboard>
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
GotoDialog::GotoDialog(MapView *map, qreal x, qreal z, qreal scale)
|
||||
@ -15,8 +16,8 @@ GotoDialog::GotoDialog(MapView *map, qreal x, qreal z, qreal scale)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
scalemin = 1.0 / 64;
|
||||
scalemax = 1024;
|
||||
scalemin = 1.0 / 4096;
|
||||
scalemax = 65536;
|
||||
ui->lineX->setValidator(new QDoubleValidator(-3e7, 3e7, 1, ui->lineX));
|
||||
ui->lineZ->setValidator(new QDoubleValidator(-3e7, 3e7, 1, ui->lineZ));
|
||||
ui->lineScale->setValidator(new QDoubleValidator(scalemin, scalemax, 16, ui->lineScale));
|
||||
@ -40,6 +41,14 @@ void GotoDialog::on_buttonBox_clicked(QAbstractButton *button)
|
||||
qreal x = ui->lineX->text().toDouble();
|
||||
qreal z = ui->lineZ->text().toDouble();
|
||||
qreal scale = ui->lineScale->text().toDouble();
|
||||
if (scale > 4096)
|
||||
{
|
||||
int button = QMessageBox::warning(this, tr("Unsafe Scale"),
|
||||
tr("Setting a very large scale may be unsafe.\n"
|
||||
"Continue anyway?"), QMessageBox::Abort|QMessageBox::Yes);
|
||||
if (button == QMessageBox::Abort)
|
||||
return;
|
||||
}
|
||||
if (scale < scalemin) scale = scalemin;
|
||||
if (scale > scalemax) scale = scalemax;
|
||||
ui->lineScale->setText(QString::asprintf("%.4f", scale));
|
||||
|
20
src/main.cpp
20
src/main.cpp
@ -15,6 +15,9 @@ unsigned char g_tempsColors[256][3];
|
||||
|
||||
ExtGenSettings g_extgen;
|
||||
|
||||
QFont g_font_default;
|
||||
QFont g_font_mono;
|
||||
|
||||
extern "C"
|
||||
int getStructureConfig_override(int stype, int mc, StructureConfig *sconf)
|
||||
{
|
||||
@ -36,26 +39,31 @@ int main(int argc, char *argv[])
|
||||
initBiomeColors(g_biomeColors);
|
||||
initBiomeTypeColors(g_tempsColors);
|
||||
|
||||
QApplication a(argc, argv);
|
||||
QApplication app(argc, argv);
|
||||
QCoreApplication::setApplicationName("cubiomes-viewer");
|
||||
|
||||
QTranslator translator;
|
||||
translator.load("en_US", ":/lang");
|
||||
a.installTranslator(&translator);
|
||||
app.installTranslator(&translator);
|
||||
|
||||
//int fontid = QFontDatabase::addApplicationFont(":/fonts/test.ttf");
|
||||
int fontid = QFontDatabase::addApplicationFont(":/fonts/DejaVuSans.ttf");
|
||||
if (fontid >= 0)
|
||||
{
|
||||
QFontDatabase::addApplicationFont(":/fonts/DejaVuSans-Bold.ttf");
|
||||
QFont fontdef = QFontDatabase::applicationFontFamilies(fontid).at(0);
|
||||
fontdef.setPointSize(10);
|
||||
a.setFont(fontdef);
|
||||
int fontid_mono = QFontDatabase::addApplicationFont(":/fonts/DejaVuSansMono.ttf");
|
||||
|
||||
g_font_default = QFontDatabase::applicationFontFamilies(fontid).at(0);
|
||||
g_font_default.setPointSize(10);
|
||||
g_font_mono = QFontDatabase::applicationFontFamilies(fontid_mono).at(0);
|
||||
g_font_mono.setPointSize(9);
|
||||
|
||||
app.setFont(g_font_default);
|
||||
}
|
||||
|
||||
MainWindow mw;
|
||||
mw.show();
|
||||
int ret = a.exec();
|
||||
int ret = app.exec();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <QtDebug>
|
||||
#include <QDataStream>
|
||||
#include <QMenu>
|
||||
#include <QFont>
|
||||
#include <QFileDialog>
|
||||
#include <QTextStream>
|
||||
#include <QSettings>
|
||||
@ -354,6 +353,8 @@ void MainWindow::saveSettings()
|
||||
settings.setValue("config/gridSpacing", config.gridSpacing);
|
||||
settings.setValue("config/mapCacheSize", config.mapCacheSize);
|
||||
settings.setValue("config/biomeColorPath", config.biomeColorPath);
|
||||
settings.setValue("config/separator", config.separator);
|
||||
settings.setValue("config/quote", config.quote);
|
||||
|
||||
settings.setValue("world/estimateTerrain", g_extgen.estimateTerrain);
|
||||
settings.setValue("world/saltOverride", g_extgen.saltOverride);
|
||||
@ -412,6 +413,8 @@ void MainWindow::loadSettings()
|
||||
config.gridSpacing = settings.value("config/gridSpacing", config.gridSpacing).toInt();
|
||||
config.mapCacheSize = settings.value("config/mapCacheSize", config.mapCacheSize).toInt();
|
||||
config.biomeColorPath = settings.value("config/biomeColorPath", config.biomeColorPath).toString();
|
||||
config.separator = settings.value("config/separator", config.separator).toString();
|
||||
config.quote = settings.value("config/quote", config.quote).toString();
|
||||
|
||||
if (!config.biomeColorPath.isEmpty())
|
||||
onBiomeColorChange();
|
||||
@ -587,7 +590,7 @@ bool MainWindow::loadProgress(QString fnam, bool keepresults, bool quiet)
|
||||
int button = QMessageBox::warning(this, tr("Warning"),
|
||||
tr("File was created with a newer version.\n"
|
||||
"Progress may be incomplete or broken.\n\n"
|
||||
"Continue anyway?"),
|
||||
"Continue loading progress anyway?"),
|
||||
QMessageBox::Abort|QMessageBox::Yes);
|
||||
if (button == QMessageBox::Abort)
|
||||
return false;
|
||||
|
@ -52,9 +52,7 @@ MapView::MapView(QWidget *parent)
|
||||
{
|
||||
memset(sshow, 0, sizeof(sshow));
|
||||
|
||||
QFont mono = QFont("Monospace", 9);
|
||||
mono.setStyleHint(QFont::TypeWriter);
|
||||
setFont(mono);
|
||||
setFont(g_font_mono);
|
||||
|
||||
QPalette pal = palette();
|
||||
pal.setColor(QPalette::Background, Qt::black);
|
||||
@ -67,7 +65,7 @@ MapView::MapView(QWidget *parent)
|
||||
|
||||
overlay = new MapOverlay(this);
|
||||
overlay->setMouseTracking(true);
|
||||
overlay->setFont(mono);
|
||||
overlay->setFont(g_font_mono);
|
||||
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
|
||||
@ -226,7 +224,7 @@ void MapView::showContextMenu(const QPoint &pos)
|
||||
menu.addAction(tr("Copy tp: ")+tp, [=](){ this->copyText(tp); });
|
||||
menu.addAction(tr("Copy block: ")+coords, [=](){ this->copyText(coords); });
|
||||
menu.addAction(tr("Copy chunk: ")+chunk, [=](){ this->copyText(chunk); });
|
||||
menu.addAction(tr("Go to coordinates..."), this, &MapView::onGoto);
|
||||
menu.addAction(tr("Go to coordinates..."), this, &MapView::onGoto, QKeySequence(Qt::CTRL + Qt::Key_G));
|
||||
menu.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
@ -312,12 +310,14 @@ void MapView::resizeEvent(QResizeEvent *e)
|
||||
|
||||
void MapView::wheelEvent(QWheelEvent *e)
|
||||
{
|
||||
qreal zoommin = 1.0 / 2048.0, zoommax = 128.0;
|
||||
const qreal ang = e->angleDelta().y() / 8; // e->delta() / 8;
|
||||
if (ang < 0 && blocks2pix < zoommin) return;
|
||||
if (ang > 0 && blocks2pix > zoommax) return;
|
||||
blocks2pix *= pow(2, ang/100);
|
||||
qreal scalemin = 128.0, scalemax = 1.0 / 4096.0;
|
||||
if (blocks2pix > scalemin) blocks2pix = scalemin;
|
||||
if (blocks2pix < scalemax) blocks2pix = scalemax;
|
||||
update();//repaint();
|
||||
if (ang < 0 && blocks2pix < zoommin) blocks2pix = zoommin;
|
||||
if (ang > 0 && blocks2pix > zoommax) blocks2pix = zoommax;
|
||||
update();
|
||||
}
|
||||
|
||||
void MapView::mousePressEvent(QMouseEvent *e)
|
||||
|
@ -12,7 +12,7 @@ class MapOverlay : public QWidget
|
||||
|
||||
public:
|
||||
explicit MapOverlay(QWidget *parent = nullptr)
|
||||
: QWidget(parent),pos{},bname() {}
|
||||
: QWidget(parent),pos{0,0},bname() {}
|
||||
~MapOverlay() {}
|
||||
|
||||
public slots:
|
||||
|
@ -61,9 +61,7 @@ PresetDialog::PresetDialog(QWidget *parent, WorldInfo wi, bool showEamples)
|
||||
connect(ui->buttonOk, &QPushButton::clicked, this, &QDialog::accept);
|
||||
connect(ui->buttonCancel, &QPushButton::clicked, this, &QDialog::reject);
|
||||
|
||||
QFont mono = QFont("Monospace", 9);
|
||||
mono.setStyleHint(QFont::TypeWriter);
|
||||
ui->listFilters->setFont(mono);
|
||||
ui->listFilters->setFont(g_font_mono);
|
||||
|
||||
if (showEamples)
|
||||
ui->tabWidget->setCurrentWidget(ui->tabExamples);
|
||||
|
@ -533,7 +533,13 @@ static bool isVariantOk(const Condition *c, WorldGen *g, int stype, int varbiome
|
||||
{
|
||||
if (g->mc < MC_1_10) return true;
|
||||
getVariant(&sv, stype, g->mc, g->seed, pos->x, pos->z, varbiome);
|
||||
if ((c->varflags & Condition::VAR_ABANODONED) && !sv.abandoned) return false;
|
||||
if (c->varflags & Condition::VAR_ABANODONED)
|
||||
{
|
||||
if ((c->varflags & Condition::VAR_NOT) && sv.abandoned)
|
||||
return false;
|
||||
if (!(c->varflags & Condition::VAR_NOT) && !sv.abandoned)
|
||||
return false;
|
||||
}
|
||||
if (!(c->varflags & Condition::VAR_WITH_START) || g->mc < MC_1_14) return true;
|
||||
}
|
||||
else if (stype == Bastion)
|
||||
@ -555,10 +561,11 @@ static bool isVariantOk(const Condition *c, WorldGen *g, int stype, int varbiome
|
||||
if (!(c->varflags & Condition::VAR_ENDSHIP)) return true;
|
||||
Piece pieces[END_CITY_PIECES_MAX];
|
||||
int i, n = getEndCityPieces(pieces, g->seed, pos->x >> 4, pos->z >> 4);
|
||||
bool withship = !(c->varflags & Condition::VAR_NOT);
|
||||
for (i = 0; i < n; i++)
|
||||
if (pieces[i].type == END_SHIP)
|
||||
return true;
|
||||
return false;
|
||||
return withship;
|
||||
return !withship;
|
||||
}
|
||||
else if (stype == Fortress)
|
||||
{
|
||||
@ -1261,7 +1268,7 @@ L_qm_any:
|
||||
else
|
||||
{ // check if the area is entirely outside the radii ranges in which strongholds can generate
|
||||
if (rmax < 1408*1408)
|
||||
return COND_FAILED;
|
||||
return cond->count == 0 ? COND_OK : COND_FAILED;
|
||||
rmin = sqrt(rmin);
|
||||
rmax = sqrt(rmax);
|
||||
r = (rmax - 1408) / 3072; // maximum relevant ring number
|
||||
@ -1296,7 +1303,7 @@ L_qm_any:
|
||||
gen->init4Dim(0);
|
||||
while (nextStronghold(&sh, &gen->g) > 0)
|
||||
{
|
||||
if (*abort || sh.ringnum > r)
|
||||
if (*abort)
|
||||
break;
|
||||
bool inside;
|
||||
if (rmax)
|
||||
@ -1333,8 +1340,7 @@ L_qm_any:
|
||||
icnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sh.ringnum == r && sh.ringidx+1 == sh.ringmax)
|
||||
if (sh.ringnum > r)
|
||||
break;
|
||||
}
|
||||
if (cond->count == 0)
|
||||
@ -1510,15 +1516,17 @@ L_noise_biome:
|
||||
|
||||
|
||||
case F_BIOME_CENTER:
|
||||
case F_BIOME_CENTER_256:
|
||||
if (pass == PASS_FULL_64)
|
||||
{
|
||||
rx1 = ((cond->x1 << 2) + at.x) >> 2;
|
||||
rz1 = ((cond->z1 << 2) + at.z) >> 2;
|
||||
rx2 = ((cond->x2 << 2) + at.x) >> 2;
|
||||
rz2 = ((cond->z2 << 2) + at.z) >> 2;
|
||||
s = finfo.pow2;
|
||||
rx1 = ((cond->x1 << s) + at.x) >> s;
|
||||
rz1 = ((cond->z1 << s) + at.z) >> s;
|
||||
rx2 = ((cond->x2 << s) + at.x) >> s;
|
||||
rz2 = ((cond->z2 << s) + at.z) >> s;
|
||||
int w = rx2 - rx1 + 1;
|
||||
int h = rz2 - rz1 + 1;
|
||||
Range r = {4, rx1, rz1, w, h, cond->y >> 2, 1};
|
||||
Range r = {finfo.step, rx1, rz1, w, h, cond->y >> 2, 1};
|
||||
gen->init4Dim(0);
|
||||
|
||||
if (cond->count == 0)
|
||||
|
21
src/search.h
21
src/search.h
@ -84,6 +84,7 @@ enum
|
||||
F_ANCIENT_CITY,
|
||||
F_LOGIC_NOT,
|
||||
F_BIOME_CENTER,
|
||||
F_BIOME_CENTER_256,
|
||||
// new filters should be added here at the end to keep some downwards compatibility
|
||||
FILTER_MAX,
|
||||
};
|
||||
@ -315,13 +316,6 @@ static const struct FilterList
|
||||
"at layer OCEAN TEMPERATURE with scale 1:256. "
|
||||
"This generation layer depends only on the lower 48-bits of the seed.")
|
||||
};
|
||||
list[F_BIOME_CENTER] = FilterInfo{
|
||||
CAT_BIOMES, 1, 1, 1, 0, 0, 0, 4, 2, 1, MC_1_0, MC_NEWEST, 0, 1, disp++,
|
||||
":icons/map.png",
|
||||
_("Locate biome center 1:4"),
|
||||
_("Finds the center position of a given biome. This requires the full "
|
||||
"generation of those biomes and ")
|
||||
};
|
||||
list[F_CLIMATE_NOISE] = FilterInfo{
|
||||
CAT_BIOMES, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_18, MC_NEWEST, 0, 0, disp++,
|
||||
":icons/map.png",
|
||||
@ -329,6 +323,18 @@ static const struct FilterList
|
||||
_("Custom limits for the required and allowed climate noise parameters that "
|
||||
"the specified area should cover.")
|
||||
};
|
||||
list[F_BIOME_CENTER] = FilterInfo{
|
||||
CAT_BIOMES, 1, 1, 1, 0, 0, 0, 4, 2, 1, MC_1_0, MC_NEWEST, 0, 1, disp++,
|
||||
":icons/map.png",
|
||||
_("Locate biome center 1:4"),
|
||||
_("Finds the center position of a given biome.")
|
||||
};
|
||||
list[F_BIOME_CENTER_256] = FilterInfo{
|
||||
CAT_BIOMES, 1, 1, 1, 0, 0, 0, 256, 8, 1, MC_1_0, MC_1_17, 0, 1, disp++,
|
||||
":icons/map.png",
|
||||
_("Locate biome center 1:256"),
|
||||
_("Finds the center position of a given biome. Based on the 1:256 biome layer.")
|
||||
};
|
||||
list[F_TEMPS] = FilterInfo{
|
||||
CAT_BIOMES, 1, 1, 1, 0, 0, 0, 1024, 10, 0, MC_1_7, MC_1_17, 0, 0, disp++,
|
||||
":icons/tempcat.png",
|
||||
@ -586,6 +592,7 @@ struct /*__attribute__((packed))*/ Condition
|
||||
VAR_ABANODONED = 0x02, // zombie village
|
||||
VAR_ENDSHIP = 0x04, // end city ship
|
||||
VAR_DENSE_BB = 0x08, // fortress with a 2x2 arrangement of start/crossings
|
||||
VAR_NOT = 0x10, // invert flag (e.g. not abandoned)
|
||||
};
|
||||
int16_t type;
|
||||
uint16_t meta;
|
||||
|
@ -121,6 +121,7 @@ bool SearchMaster::set(
|
||||
}
|
||||
if (finfo.cat == CAT_BIOMES &&
|
||||
c.type != F_BIOME_CENTER &&
|
||||
c.type != F_BIOME_CENTER_256 &&
|
||||
c.type != F_TEMPS &&
|
||||
c.type != F_CLIMATE_NOISE)
|
||||
{
|
||||
@ -221,7 +222,7 @@ bool SearchMaster::set(
|
||||
static int check(uint64_t s48, void *data)
|
||||
{
|
||||
(void) data;
|
||||
const StructureConfig sconf = {};
|
||||
const StructureConfig sconf = {0,0,0,0,0};
|
||||
return isQuadBaseFeature24(sconf, s48, 7+1, 7+1, 9+1) != 0;
|
||||
}
|
||||
|
||||
@ -487,8 +488,8 @@ void SearchMaster::run()
|
||||
{
|
||||
SearchWorker *worker = new SearchWorker(this);
|
||||
QObject::connect(
|
||||
worker, &SearchWorker::results,
|
||||
parent, &FormSearchControl::searchResultsAdd,
|
||||
worker, &SearchWorker::result,
|
||||
parent, &FormSearchControl::onSearchResult,
|
||||
Qt::BlockingQueuedConnection);
|
||||
QObject::connect(
|
||||
worker, &SearchWorker::finished,
|
||||
@ -766,9 +767,8 @@ void SearchWorker::run()
|
||||
== COND_OK
|
||||
)
|
||||
{
|
||||
QVector<uint64_t> matches = {seed};
|
||||
if (!*abort)
|
||||
emit results(matches, false);
|
||||
emit result(seed);
|
||||
}
|
||||
}
|
||||
//if (ie == len) // done
|
||||
@ -793,9 +793,8 @@ void SearchWorker::run()
|
||||
== COND_OK
|
||||
)
|
||||
{
|
||||
QVector<uint64_t> matches = {seed};
|
||||
if (!*abort)
|
||||
emit results(matches, false);
|
||||
emit result(seed);
|
||||
}
|
||||
|
||||
if (++lowidx >= len)
|
||||
@ -818,9 +817,8 @@ void SearchWorker::run()
|
||||
== COND_OK
|
||||
)
|
||||
{
|
||||
QVector<uint64_t> matches = {seed};
|
||||
if (!*abort)
|
||||
emit results(matches, false);
|
||||
emit result(seed);
|
||||
}
|
||||
|
||||
if (seed == ~(uint64_t)0)
|
||||
@ -865,9 +863,8 @@ void SearchWorker::run()
|
||||
== COND_OK
|
||||
)
|
||||
{
|
||||
QVector<uint64_t> matches = {seed};
|
||||
if (!*abort)
|
||||
emit results(matches, false);
|
||||
emit result(seed);
|
||||
}
|
||||
|
||||
if (++high >= 0x10000)
|
||||
|
@ -77,7 +77,7 @@ public:
|
||||
virtual void run() override;
|
||||
|
||||
signals:
|
||||
int results(QVector<uint64_t> seeds, bool countonly);
|
||||
void result(uint64_t seed);
|
||||
|
||||
public:
|
||||
SearchMaster * master;
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
#include <QFont>
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -31,6 +32,10 @@ struct ExtGenSettings
|
||||
// Keep the extended generator settings in global scope.
|
||||
extern ExtGenSettings g_extgen;
|
||||
|
||||
// global references to the default fonts
|
||||
extern QFont g_font_default;
|
||||
extern QFont g_font_mono;
|
||||
|
||||
struct WorldInfo
|
||||
{
|
||||
int mc;
|
||||
@ -69,6 +74,8 @@ struct Config
|
||||
int gridSpacing;
|
||||
int mapCacheSize;
|
||||
QString biomeColorPath;
|
||||
QString separator;
|
||||
QString quote;
|
||||
|
||||
Config() { reset(); }
|
||||
|
||||
@ -85,6 +92,8 @@ struct Config
|
||||
gridSpacing = 0;
|
||||
mapCacheSize = 256;
|
||||
biomeColorPath = "";
|
||||
separator = ";";
|
||||
quote = "";
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10,14 +10,7 @@
|
||||
<normaloff>:/icons/logo.png</normaloff>:/icons/logo.png</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupVis">
|
||||
<property name="title">
|
||||
<string>Maximum scale for structure visibility</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@ -27,6 +20,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="groupVis">
|
||||
<property name="title">
|
||||
<string>Maximum scale for structure visibility</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
|
@ -18,22 +18,22 @@ void AnalysisBiomes::run()
|
||||
Generator g;
|
||||
setupGenerator(&g, wi.mc, wi.large);
|
||||
|
||||
for (int64_t seed : qAsConst(seeds))
|
||||
for (idx = 0; idx < seeds.size(); idx++)
|
||||
{
|
||||
if (stop) break;
|
||||
wi.seed = seed;
|
||||
if (locate < 0)
|
||||
runStatistics(&g);
|
||||
else
|
||||
wi.seed = seeds[idx];
|
||||
if (dat.locate >= 0)
|
||||
runLocate(&g);
|
||||
else
|
||||
runStatistics(&g);
|
||||
}
|
||||
}
|
||||
|
||||
void AnalysisBiomes::runStatistics(Generator *g)
|
||||
{
|
||||
QVector<uint64_t> idcnt(256);
|
||||
int w = x2 - x1 + 1;
|
||||
int h = z2 - z1 + 1;
|
||||
int w = dat.x2 - dat.x1 + 1;
|
||||
int h = dat.z2 - dat.z1 + 1;
|
||||
uint64_t n = w * (uint64_t)h;
|
||||
|
||||
for (int d = 0; d < 3; d++)
|
||||
@ -42,16 +42,16 @@ void AnalysisBiomes::runStatistics(Generator *g)
|
||||
continue;
|
||||
applySeed(g, dims[d], wi.seed);
|
||||
|
||||
if (samples >= n)
|
||||
if (dat.samples >= n)
|
||||
{ // full area gen => generate 512x512 areas at a time
|
||||
const int step = 512;
|
||||
for (int x = x1; x <= x2 && !stop; x += step)
|
||||
for (int x = dat.x1; x <= dat.x2 && !stop; x += step)
|
||||
{
|
||||
for (int z = z1; z <= z2 && !stop; z += step)
|
||||
for (int z = dat.z1; z <= dat.z2 && !stop; z += step)
|
||||
{
|
||||
int w = x2-x+1 < step ? x2-x+1 : step;
|
||||
int h = z2-z+1 < step ? z2-z+1 : step;
|
||||
Range r = {scale, x, z, w, h, wi.y, 1};
|
||||
int w = dat.x2-x+1 < step ? dat.x2-x+1 : step;
|
||||
int h = dat.z2-z+1 < step ? dat.z2-z+1 : step;
|
||||
Range r = {dat.scale, x, z, w, h, wi.y, 1};
|
||||
int *ids = allocCache(g, r);
|
||||
genBiomes(g, ids, r);
|
||||
for (int i = 0; i < w*h; i++)
|
||||
@ -64,7 +64,7 @@ void AnalysisBiomes::runStatistics(Generator *g)
|
||||
{
|
||||
std::vector<uint64_t> order;
|
||||
|
||||
if (samples * 2 >= n)
|
||||
if (dat.samples * 2 >= n)
|
||||
{ // dense regime => shuffle indeces
|
||||
order.resize(n);
|
||||
for (uint64_t i = 0; i < n; i++)
|
||||
@ -78,14 +78,14 @@ void AnalysisBiomes::runStatistics(Generator *g)
|
||||
order[i] = order[idx];
|
||||
order[idx] = t;
|
||||
}
|
||||
order.resize(samples);
|
||||
order.resize(dat.samples);
|
||||
}
|
||||
else
|
||||
{ // sparse regime => fill randomly without reuse
|
||||
std::unordered_set<uint64_t> used;
|
||||
order.reserve(samples);
|
||||
used.reserve(samples);
|
||||
for (uint64_t i = 0; order.size() < samples; i++)
|
||||
order.reserve(dat.samples);
|
||||
used.reserve(dat.samples);
|
||||
for (uint64_t i = 0; order.size() < dat.samples; i++)
|
||||
{
|
||||
if (!(i & 0xffff) && stop)
|
||||
break;
|
||||
@ -96,12 +96,12 @@ void AnalysisBiomes::runStatistics(Generator *g)
|
||||
}
|
||||
}
|
||||
|
||||
for (uint64_t i = 0; i < samples && !stop; i++)
|
||||
for (uint64_t i = 0; i < dat.samples && !stop; i++)
|
||||
{
|
||||
uint64_t idx = order[i];
|
||||
int x = (int) (idx % w);
|
||||
int z = (int) (idx / w);
|
||||
int id = getBiomeAt(g, scale, x1+x, wi.y, z1+z);
|
||||
int id = getBiomeAt(g, dat.scale, dat.x1+x, wi.y, dat.z1+z);
|
||||
idcnt[ id & 0xff ]++;
|
||||
}
|
||||
}
|
||||
@ -117,9 +117,9 @@ void AnalysisBiomes::runLocate(Generator *g)
|
||||
enum { MAX_LOCATE = 4096 };
|
||||
Pos pos[MAX_LOCATE];
|
||||
int siz[MAX_LOCATE];
|
||||
Range r = {4, x1, z1, x2-x1+1, z2-z1+1, wi.y, 1};
|
||||
Range r = {4, dat.x1, dat.z1, dat.x2-dat.x1+1, dat.z2-dat.z1+1, wi.y, 1};
|
||||
int n = getBiomeCenters(
|
||||
pos, siz, MAX_LOCATE, g, r, locate, minsize, tolerance,
|
||||
pos, siz, MAX_LOCATE, g, r, dat.locate, minsize, tolerance,
|
||||
(volatile char*)&stop
|
||||
);
|
||||
if (n && !stop)
|
||||
@ -148,64 +148,173 @@ QVariant BiomeTableModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole || index.row() < 0 || index.column() < 0)
|
||||
return QVariant::Invalid;
|
||||
int id = ids[index.row()];
|
||||
int col = index.column();
|
||||
uint64_t seed = seeds[col];
|
||||
int id = ids[index.column()];
|
||||
uint64_t seed = seeds[index.row()];
|
||||
return cnt[id][seed];
|
||||
}
|
||||
|
||||
QVariant BiomeTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole || section < 0)
|
||||
if (section < 0)
|
||||
return QVariant::Invalid;
|
||||
if (orientation == Qt::Horizontal)
|
||||
if (role == Qt::InitialSortOrderRole)
|
||||
return QVariant::fromValue(Qt::AscendingOrder);
|
||||
if (role == Qt::DisplayRole && orientation == Qt::Vertical)
|
||||
{
|
||||
if (section < seeds.size())
|
||||
return QVariant::fromValue(seeds[section]);
|
||||
return QVariant::fromValue((int64_t)seeds[section]);
|
||||
}
|
||||
else
|
||||
if (orientation == Qt::Horizontal)
|
||||
{
|
||||
if (section < ids.size())
|
||||
return QVariant::fromValue(QString(biome2str(cmp.mc, ids[section])));
|
||||
{
|
||||
if (role == Qt::DisplayRole)
|
||||
return QVariant::fromValue(QString(biome2str(cmp.mc, ids[section])));
|
||||
else
|
||||
return QVariant::fromValue(ids[section]);
|
||||
}
|
||||
}
|
||||
return QVariant::Invalid;
|
||||
}
|
||||
|
||||
int BiomeTableModel::insertId(int id)
|
||||
void BiomeTableModel::insertIds(QSet<int>& nids)
|
||||
{
|
||||
QList<int>::iterator it = std::lower_bound(ids.begin(), ids.end(), id, cmp);
|
||||
if (it != ids.end() && *it == id)
|
||||
return -1;
|
||||
int row = std::distance(ids.begin(), it);
|
||||
//beginInsertRows(QModelIndex(), row, row);
|
||||
ids.insert(row, id);
|
||||
//endInsertRows();
|
||||
return row;
|
||||
for (int id : qAsConst(nids))
|
||||
{
|
||||
QList<int>::iterator it = std::lower_bound(ids.begin(), ids.end(), id, cmp);
|
||||
if (it != ids.end() && *it == id)
|
||||
continue;
|
||||
int i = std::distance(ids.begin(), it);
|
||||
beginInsertColumns(QModelIndex(), i, i);
|
||||
ids.insert(i, id);
|
||||
endInsertColumns();
|
||||
}
|
||||
}
|
||||
|
||||
int BiomeTableModel::insertSeed(uint64_t seed)
|
||||
void BiomeTableModel::insertSeeds(QList<uint64_t>& nseeds)
|
||||
{
|
||||
int col = seeds.size();
|
||||
//beginInsertColumns(QModelIndex(), col, col);
|
||||
seeds.append(seed);
|
||||
//endInsertColumns();
|
||||
return col;
|
||||
int i = seeds.size();
|
||||
beginInsertRows(QModelIndex(), i, i+nseeds.size()-1);
|
||||
seeds.append(nseeds);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void BiomeTableModel::reset(int mc)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), 0, ids.size());
|
||||
ids.clear();
|
||||
endRemoveRows();
|
||||
beginRemoveColumns(QModelIndex(), 0, seeds.size());
|
||||
beginResetModel();
|
||||
seeds.clear();
|
||||
endRemoveColumns();
|
||||
ids.clear();
|
||||
cnt.clear();
|
||||
cmp.mode = IdCmp::SORT_LEX;
|
||||
cmp.mode = IdCmp::SORT_DIM;
|
||||
cmp.dim = DIM_UNDEF;
|
||||
cmp.mc = mc;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
BiomeHeader::BiomeHeader(QWidget *parent)
|
||||
: QHeaderView(Qt::Horizontal, parent)
|
||||
, hover(-1)
|
||||
, pressed(-1)
|
||||
{
|
||||
setSectionsClickable(true);
|
||||
setHighlightSections(true);
|
||||
connect(this, &QHeaderView::sectionPressed, this, &BiomeHeader::onSectionPress);
|
||||
}
|
||||
|
||||
void BiomeHeader::onSectionPress(int section)
|
||||
{
|
||||
pressed = section;
|
||||
}
|
||||
|
||||
bool BiomeHeader::event(QEvent *e)
|
||||
{
|
||||
switch (e->type())
|
||||
{
|
||||
case QEvent::HoverEnter:
|
||||
case QEvent::HoverMove:
|
||||
hover = logicalIndexAt(((QHoverEvent*)e)->pos());
|
||||
break;
|
||||
case QEvent::Leave:
|
||||
case QEvent::HoverLeave:
|
||||
hover = -1;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return QHeaderView::event(e);
|
||||
}
|
||||
|
||||
void BiomeHeader::paintSection(QPainter *painter, const QRect& rect, int section) const
|
||||
{
|
||||
if (!rect.isValid() || !model())
|
||||
return;
|
||||
|
||||
QStyleOptionHeader opt;
|
||||
initStyleOption(&opt);
|
||||
|
||||
QStyle::State state = QStyle::State_None;
|
||||
state |= QStyle::State_Enabled;
|
||||
state |= QStyle::State_Active;
|
||||
if (section == hover)
|
||||
state |= QStyle::State_MouseOver;
|
||||
if (section == pressed)
|
||||
state |= QStyle::State_Sunken;
|
||||
|
||||
QString s = model()->headerData(section, orientation()).toString();
|
||||
painter->setFont(font());
|
||||
QFontMetrics fm(font());
|
||||
int indicator_height = 0;
|
||||
int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
|
||||
QStyleOptionHeader::SortIndicator sortindicator = QStyleOptionHeader::None;
|
||||
|
||||
if (isSortIndicatorShown() && sortIndicatorSection() == section)
|
||||
{
|
||||
if (sortIndicatorOrder() == Qt::AscendingOrder)
|
||||
sortindicator = QStyleOptionHeader::SortDown;
|
||||
else
|
||||
sortindicator = QStyleOptionHeader::SortUp;
|
||||
indicator_height = 20;
|
||||
}
|
||||
|
||||
int x = -rect.height() + margin + indicator_height;
|
||||
int y = rect.left() + (rect.width() + fm.descent()) / 2 + margin;
|
||||
|
||||
opt.rect = rect;
|
||||
opt.section = section;
|
||||
opt.state = state;
|
||||
|
||||
QPointF oldBO = painter->brushOrigin();
|
||||
painter->save();
|
||||
|
||||
painter->setBrushOrigin(opt.rect.topLeft());
|
||||
style()->drawControl(QStyle::CE_Header, &opt, painter, this);
|
||||
|
||||
painter->restore();
|
||||
|
||||
painter->rotate(-90);
|
||||
painter->drawText(x, y, s);
|
||||
painter->rotate(+90);
|
||||
|
||||
if (sortindicator != QStyleOptionHeader::None)
|
||||
{
|
||||
opt.sortIndicator = sortindicator;
|
||||
opt.rect = rect.adjusted(0, rect.bottom()-rect.y()-indicator_height, 0, 0);
|
||||
style()->drawControl(QStyle::CE_Header, &opt, painter, this);
|
||||
painter->setBrushOrigin(oldBO);
|
||||
}
|
||||
painter->setBrushOrigin(oldBO);
|
||||
}
|
||||
|
||||
QSize BiomeHeader::sectionSizeFromContents(int section) const
|
||||
{
|
||||
if (!model())
|
||||
return QSize();
|
||||
int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
|
||||
QFontMetrics fm(font());
|
||||
int w = fm.boundingRect(model()->headerData(section, orientation()).toString()).width();
|
||||
return QSize(fm.height() + 2*margin, w + 2*margin);
|
||||
}
|
||||
|
||||
|
||||
|
||||
TabBiomes::TabBiomes(MainWindow *parent)
|
||||
: QWidget(parent)
|
||||
@ -214,23 +323,29 @@ TabBiomes::TabBiomes(MainWindow *parent)
|
||||
, thread()
|
||||
, model(new BiomeTableModel(this))
|
||||
, proxy(new BiomeSortProxy(this))
|
||||
, sortcol(-1)
|
||||
, elapsed()
|
||||
, updt(20)
|
||||
, nextupdate()
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
proxy->setSourceModel(model);
|
||||
ui->table->setModel(proxy);
|
||||
|
||||
QHeaderView *header = ui->table->horizontalHeader();
|
||||
connect(header, &QHeaderView::sortIndicatorChanged, this, &TabBiomes::onSort);
|
||||
BiomeHeader *header = new BiomeHeader(ui->table);
|
||||
ui->table->setHorizontalHeader(header);
|
||||
//QHeaderView *header = ui->table->horizontalHeader();
|
||||
connect(header, &QHeaderView::sortIndicatorChanged, this, &TabBiomes::onTableSort);
|
||||
|
||||
QFont font = QFont("monospace", 9);
|
||||
ui->table->setFont(font);
|
||||
ui->table->setFont(g_font_mono);
|
||||
ui->table->setSortingEnabled(true);
|
||||
ui->table->sortByColumn(-1, Qt::AscendingOrder);
|
||||
|
||||
ui->treeWidget->setColumnWidth(0, 160);
|
||||
ui->treeWidget->setColumnWidth(1, 120);
|
||||
ui->treeWidget->sortByColumn(0, Qt::AscendingOrder);
|
||||
ui->treeLocate->setColumnWidth(0, 160);
|
||||
ui->treeLocate->setColumnWidth(1, 120);
|
||||
ui->treeLocate->sortByColumn(-1, Qt::DescendingOrder);
|
||||
ui->treeLocate->setSortingEnabled(true);
|
||||
connect(ui->treeLocate->header(), &QHeaderView::sectionClicked, this, &TabBiomes::onLocateHeaderClick);
|
||||
|
||||
QIntValidator *intval = new QIntValidator(-60e6, 60e6, this);
|
||||
ui->lineX1->setValidator(intval);
|
||||
@ -255,9 +370,7 @@ TabBiomes::TabBiomes(MainWindow *parent)
|
||||
str2biome[s] = id;
|
||||
}
|
||||
|
||||
QStringList bnames;
|
||||
for (auto& it : str2biome)
|
||||
bnames.append(it.first);
|
||||
const QStringList bnames = str2biome.keys();
|
||||
QRegularExpressionValidator *reval = new QRegularExpressionValidator(
|
||||
QRegularExpression("(" + bnames.join("|") + ")"), this
|
||||
);
|
||||
@ -266,6 +379,8 @@ TabBiomes::TabBiomes(MainWindow *parent)
|
||||
|
||||
TabBiomes::~TabBiomes()
|
||||
{
|
||||
thread.stop = true;
|
||||
thread.wait(500);
|
||||
delete ui;
|
||||
}
|
||||
|
||||
@ -347,86 +462,22 @@ void TabBiomes::refreshBiomes(int activeid)
|
||||
}
|
||||
}
|
||||
|
||||
void TabBiomes::onAnalysisSeedDone(uint64_t seed, QVector<uint64_t> idcnt)
|
||||
void TabBiomes::onLocateHeaderClick()
|
||||
{
|
||||
// save state of table UI
|
||||
int selr = ui->table->selectionModel()->currentIndex().row();
|
||||
int selc = ui->table->selectionModel()->currentIndex().column();
|
||||
int posr = ui->table->verticalScrollBar()->value();
|
||||
int posc = ui->table->horizontalScrollBar()->value();
|
||||
|
||||
int ncol = proxy->columnCount();
|
||||
std::vector<int> colwidth(ncol, 60);
|
||||
for (int c = 0; c < ncol; c++)
|
||||
colwidth[c] = ui->table->columnWidth(c);
|
||||
|
||||
// create new model
|
||||
ui->table->setSortingEnabled(false);
|
||||
ui->table->setModel(nullptr);
|
||||
BiomeTableModel *m_new = new BiomeTableModel(this);
|
||||
|
||||
m_new->ids = model->ids;
|
||||
m_new->seeds = model->seeds;
|
||||
m_new->cnt = model->cnt;
|
||||
|
||||
if (m_new->insertSeed(seed) < selc)
|
||||
selc++;
|
||||
|
||||
for (int id = 0; id < 256; id++)
|
||||
int section = ui->treeLocate->header()->sortIndicatorSection();
|
||||
if (ui->treeLocate->header()->sortIndicatorOrder() == Qt::AscendingOrder && sortcol == section)
|
||||
{
|
||||
if (idcnt[id] == 0)
|
||||
continue;
|
||||
int r = m_new->insertId(id);
|
||||
if (r >= 0 && r < selr)
|
||||
selr++;
|
||||
m_new->cnt[id][seed] = QVariant::fromValue(idcnt[id]);
|
||||
ui->treeLocate->sortByColumn(-1, Qt::DescendingOrder);
|
||||
section = -1;
|
||||
}
|
||||
|
||||
delete model;
|
||||
model = m_new;
|
||||
|
||||
// restore state of table UI for new model
|
||||
proxy->setSourceModel(model);
|
||||
ui->table->setModel(proxy);
|
||||
ui->table->setSortingEnabled(true);
|
||||
|
||||
for (int c = 0; c < ncol; c++)
|
||||
ui->table->setColumnWidth(c, colwidth[c]);
|
||||
ui->table->resizeColumnToContents(ncol);
|
||||
|
||||
int rowheight = QFontMetrics(ui->table->font()).height() + 4;
|
||||
for (int r = 0, nrow = proxy->rowCount(); r < nrow; r++)
|
||||
ui->table->setRowHeight(r, rowheight);
|
||||
|
||||
ui->table->selectionModel()->setCurrentIndex(proxy->index(selr, selc), QItemSelectionModel::SelectCurrent);
|
||||
ui->table->verticalScrollBar()->setValue(posr);
|
||||
ui->table->horizontalScrollBar()->setValue(posc);
|
||||
|
||||
|
||||
QString progress = QString::asprintf(" (%d/%d)", model->seeds.size()+1, thread.seeds.size());
|
||||
ui->pushStart->setText(tr("Stop") + progress);
|
||||
sortcol = section;
|
||||
}
|
||||
|
||||
void TabBiomes::onAnalysisSeedItem(QTreeWidgetItem *item)
|
||||
{
|
||||
ui->treeWidget->addTopLevelItem(item);
|
||||
|
||||
QString progress = QString::asprintf(" (%d/%d)", ui->treeWidget->topLevelItemCount()+1, thread.seeds.size());
|
||||
ui->pushStart->setText(tr("Stop") + progress);
|
||||
}
|
||||
|
||||
void TabBiomes::onAnalysisFinished()
|
||||
{
|
||||
ui->pushExport->setEnabled(!model->ids.empty() || ui->treeWidget->topLevelItemCount());
|
||||
ui->pushStart->setChecked(false);
|
||||
ui->pushStart->setText(tr("Analyze"));
|
||||
}
|
||||
|
||||
void TabBiomes::onSort(int column, Qt::SortOrder)
|
||||
void TabBiomes::onTableSort(int, Qt::SortOrder)
|
||||
{
|
||||
QHeaderView *header = ui->table->horizontalHeader();
|
||||
|
||||
if (proxy->order == Qt::DescendingOrder && proxy->column == column)
|
||||
if (proxy->order == Qt::DescendingOrder && proxy->column != -1)
|
||||
{
|
||||
header->setSortIndicatorShown(false);
|
||||
header->setSortIndicator(-1, Qt::AscendingOrder);
|
||||
@ -438,6 +489,113 @@ void TabBiomes::onSort(int column, Qt::SortOrder)
|
||||
}
|
||||
}
|
||||
|
||||
void TabBiomes::onAnalysisSeedDone(uint64_t seed, QVector<uint64_t> idcnt)
|
||||
{
|
||||
idcnt.push_back(seed);
|
||||
qbufs.push_back(idcnt);
|
||||
quint64 ns = elapsed.nsecsElapsed();
|
||||
if (ns > nextupdate)
|
||||
{
|
||||
nextupdate = ns + updt * 1e6;
|
||||
QTimer::singleShot(updt, this, &TabBiomes::onBufferTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
void TabBiomes::onAnalysisSeedItem(QTreeWidgetItem *item)
|
||||
{
|
||||
qbufl.push_back(item);
|
||||
quint64 ns = elapsed.nsecsElapsed();
|
||||
if (ns > nextupdate)
|
||||
{
|
||||
nextupdate = ns + updt * 1e6;
|
||||
QTimer::singleShot(updt, this, &TabBiomes::onBufferTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
void TabBiomes::onAnalysisFinished()
|
||||
{
|
||||
onBufferTimeout();
|
||||
on_tabWidget_currentChanged(-1);
|
||||
ui->pushStart->setChecked(false);
|
||||
ui->pushStart->setText(tr("Analyze"));
|
||||
}
|
||||
|
||||
void TabBiomes::onBufferTimeout()
|
||||
{
|
||||
if (qbufs.empty() && qbufl.empty())
|
||||
return;
|
||||
|
||||
uint64_t t = -elapsed.elapsed();
|
||||
|
||||
if (!qbufs.empty())
|
||||
{
|
||||
ui->table->setSortingEnabled(false);
|
||||
ui->table->setUpdatesEnabled(false);
|
||||
|
||||
QMap<int, int> colwidth;
|
||||
for (int c = 0, n = model->ids.size(); c < n; c++)
|
||||
colwidth[model->ids[c]] = ui->table->columnWidth(c);
|
||||
|
||||
QList<uint64_t> new_seeds;
|
||||
QSet<int> new_ids;
|
||||
QFontMetrics fm(font());
|
||||
|
||||
for (int i = 0, n = qbufs.size(); i < n; i++)
|
||||
{
|
||||
QVector<uint64_t>& scnt = qbufs[i];
|
||||
uint64_t seed = scnt.back();
|
||||
scnt.resize(scnt.size()-1);
|
||||
|
||||
new_seeds.push_back(seed);
|
||||
for (int id = 0, idn = scnt.size(); id < idn; id++)
|
||||
{
|
||||
uint64_t cnt = scnt[id];
|
||||
if (cnt == 0)
|
||||
continue;
|
||||
new_ids.insert(id);
|
||||
model->cnt[id][seed] = QVariant::fromValue(cnt);
|
||||
int w = fm.boundingRect(QString::number(cnt) + "_").width() + 2;
|
||||
if (w > colwidth[id])
|
||||
colwidth[id] = w;
|
||||
}
|
||||
}
|
||||
model->insertIds(new_ids);
|
||||
model->insertSeeds(new_seeds);
|
||||
|
||||
ui->table->setUpdatesEnabled(true);
|
||||
ui->table->setSortingEnabled(true);
|
||||
|
||||
//ui->table->resizeColumnsToContents();
|
||||
for (int i = 0, n = proxy->columnCount(); i < n; i++)
|
||||
{
|
||||
int id = proxy->headerData(i, Qt::Horizontal, Qt::UserRole).toInt();
|
||||
ui->table->setColumnWidth(i, colwidth[id]);
|
||||
}
|
||||
int rowheight = fm.height() + 4;
|
||||
for (int i = 0, n = proxy->rowCount(); i < n; i++)
|
||||
ui->table->setRowHeight(i, rowheight);
|
||||
qbufs.clear();
|
||||
}
|
||||
|
||||
if (!qbufl.empty())
|
||||
{
|
||||
ui->treeLocate->setSortingEnabled(false);
|
||||
ui->treeLocate->setUpdatesEnabled(false);
|
||||
ui->treeLocate->addTopLevelItems(qbufl);
|
||||
ui->treeLocate->setUpdatesEnabled(true);
|
||||
ui->treeLocate->setSortingEnabled(true);
|
||||
qbufl.clear();
|
||||
}
|
||||
|
||||
QString progress = QString::asprintf(" (%d/%d)", thread.idx.load(), thread.seeds.size());
|
||||
ui->pushStart->setText(tr("Stop") + progress);
|
||||
|
||||
t += elapsed.elapsed();
|
||||
if (8*t > updt)
|
||||
updt = 4*t;
|
||||
nextupdate = elapsed.nsecsElapsed() + 1e6 * updt;
|
||||
}
|
||||
|
||||
void TabBiomes::on_pushStart_clicked()
|
||||
{
|
||||
if (thread.isRunning())
|
||||
@ -446,6 +604,10 @@ void TabBiomes::on_pushStart_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
updt = 20;
|
||||
nextupdate = 0;
|
||||
elapsed.start();
|
||||
|
||||
parent->getSeed(&thread.wi);
|
||||
thread.seeds.clear();
|
||||
if (ui->comboSeedSource->currentIndex() == 0)
|
||||
@ -469,19 +631,23 @@ void TabBiomes::on_pushStart_clicked()
|
||||
|
||||
if (ui->radioFullSample->isChecked())
|
||||
{
|
||||
thread.samples = ~0ULL;
|
||||
thread.dat.samples = ~0ULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
thread.samples = ui->lineSamples->text().toULongLong();
|
||||
thread.dat.samples = ui->lineSamples->text().toULongLong();
|
||||
scale = 4;
|
||||
s = 2;
|
||||
}
|
||||
|
||||
if (ui->tabWidget->currentWidget() == ui->tabLocate)
|
||||
{
|
||||
ui->treeWidget->clear();
|
||||
thread.locate = str2biome[ui->comboBiome->currentText()];
|
||||
//ui->treeWidget->clear();
|
||||
ui->treeLocate->setSortingEnabled(false);
|
||||
while (ui->treeLocate->topLevelItemCount() > 0)
|
||||
delete ui->treeLocate->takeTopLevelItem(0);
|
||||
ui->treeLocate->setSortingEnabled(true);
|
||||
thread.dat.locate = str2biome[ui->comboBiome->currentText()];
|
||||
thread.minsize = ui->lineBiomeSize->text().toInt();
|
||||
thread.tolerance = ui->lineTolerance->text().toInt();
|
||||
if (thread.minsize <= 0)
|
||||
@ -492,14 +658,19 @@ void TabBiomes::on_pushStart_clicked()
|
||||
else
|
||||
{
|
||||
model->reset(thread.wi.mc);
|
||||
thread.locate = -1;
|
||||
thread.dat.locate = -1;
|
||||
}
|
||||
|
||||
thread.scale = scale;
|
||||
thread.x1 = x1 >> s;
|
||||
thread.z1 = z1 >> s;
|
||||
thread.x2 = x2 >> s;
|
||||
thread.z2 = z2 >> s;
|
||||
thread.dat.scale = scale;
|
||||
thread.dat.x1 = x1 >> s;
|
||||
thread.dat.z1 = z1 >> s;
|
||||
thread.dat.x2 = x2 >> s;
|
||||
thread.dat.z2 = z2 >> s;
|
||||
|
||||
if (thread.dat.locate < 0)
|
||||
dats = thread.dat;
|
||||
else
|
||||
datl = thread.dat;
|
||||
|
||||
ui->pushExport->setEnabled(false);
|
||||
ui->pushStart->setChecked(true);
|
||||
@ -507,6 +678,18 @@ void TabBiomes::on_pushStart_clicked()
|
||||
thread.start();
|
||||
}
|
||||
|
||||
static
|
||||
void csvline(QTextStream& stream, const QString& qte, const QString& sep, QStringList& cols)
|
||||
{
|
||||
if (qte.isEmpty())
|
||||
{
|
||||
for (QString& s : cols)
|
||||
if (s.contains(sep))
|
||||
s = "\"" + s + "\"";
|
||||
}
|
||||
stream << qte << cols.join(sep) << qte << "\n";
|
||||
}
|
||||
|
||||
void TabBiomes::on_pushExport_clicked()
|
||||
{
|
||||
QString fnam = QFileDialog::getSaveFileName(
|
||||
@ -524,41 +707,53 @@ void TabBiomes::on_pushExport_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
QString qte = parent->config.quote;
|
||||
QString sep = parent->config.separator;
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream << "#X1; " << thread.x1 << "; (" << (thread.x1*thread.scale) << ")\n";
|
||||
stream << "#Z1; " << thread.z1 << "; (" << (thread.z1*thread.scale) << ")\n";
|
||||
stream << "#X2; " << thread.x2 << "; (" << (thread.x2*thread.scale) << ")\n";
|
||||
stream << "#Z2; " << thread.z2 << "; (" << (thread.z2*thread.scale) << ")\n";
|
||||
stream << "#scale; 1:" << thread.scale << "\n";
|
||||
stream << "Sep=" + sep + "\n";
|
||||
sep = qte + sep + qte;
|
||||
|
||||
if (thread.locate < 0)
|
||||
if (ui->tabWidget->currentWidget() == ui->tabStats)
|
||||
{
|
||||
if (thread.samples != ~0ULL)
|
||||
stream << "#samples; " << thread.samples << "\n";
|
||||
stream << qte << "#X1" << sep << dats.x1 << sep << "(" << (dats.x1*dats.scale) << ")" << qte << "\n";
|
||||
stream << qte << "#Z1" << sep << dats.z1 << sep << "(" << (dats.z1*dats.scale) << ")" << qte << "\n";
|
||||
stream << qte << "#X2" << sep << dats.x2 << sep << "(" << (dats.x2*dats.scale) << ")" << qte << "\n";
|
||||
stream << qte << "#Z2" << sep << dats.z2 << sep << "(" << (dats.z2*dats.scale) << ")" << qte << "\n";
|
||||
stream << qte << "#scale" << sep << "1:" << dats.scale << qte << "\n";
|
||||
if (dats.samples != ~0ULL)
|
||||
stream << qte << "#samples" << sep << dats.samples << qte << "\n";
|
||||
|
||||
QList<QString> header;
|
||||
header.append(tr("biome\\seed"));
|
||||
QStringList header = { tr("seed") };
|
||||
for (int col = 0, ncol = proxy->columnCount(); col < ncol; col++)
|
||||
header.append(proxy->headerData(col, Qt::Horizontal).toString());
|
||||
stream << header.join("; ") << "\n";
|
||||
csvline(stream, qte, sep, header);
|
||||
|
||||
for (int row = 0, nrow = proxy->rowCount(); row < nrow; row++)
|
||||
{
|
||||
QList<QString> entries;
|
||||
entries.append(proxy->headerData(row, Qt::Vertical).toString());
|
||||
QStringList cols;
|
||||
cols.append(proxy->headerData(row, Qt::Vertical).toString());
|
||||
for (int col = 0, ncol = proxy->columnCount(); col < ncol; col++)
|
||||
{
|
||||
QString cntstr = proxy->data(proxy->index(row, col)).toString();
|
||||
entries.append(cntstr == "" ? "0" : cntstr);
|
||||
cols.append(cntstr == "" ? "0" : cntstr);
|
||||
}
|
||||
stream << entries.join("; ") << "\n";
|
||||
csvline(stream, qte, sep, cols);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (ui->tabWidget->currentWidget() == ui->tabLocate)
|
||||
{
|
||||
stream << "#biome; " << biome2str(MC_NEWEST, thread.locate) << "\n";
|
||||
stream << "seed; area; x; z;\n";
|
||||
QTreeWidgetItemIterator it(ui->treeWidget);
|
||||
stream << qte << "#X1" << sep << datl.x1 << sep << "(" << (datl.x1*datl.scale) << ")" << qte << "\n";
|
||||
stream << qte << "#Z1" << sep << datl.z1 << sep << "(" << (datl.z1*datl.scale) << ")" << qte << "\n";
|
||||
stream << qte << "#X2" << sep << datl.x2 << sep << "(" << (datl.x2*datl.scale) << ")" << qte << "\n";
|
||||
stream << qte << "#Z2" << sep << datl.z2 << sep << "(" << (datl.z2*datl.scale) << ")" << qte << "\n";
|
||||
stream << qte << "#scale" << sep << "1:" << datl.scale << qte << "\n";
|
||||
stream << qte << "#biome" << sep << biome2str(MC_NEWEST, datl.locate) << qte << "\n";
|
||||
|
||||
QStringList header = { tr("seed"), tr("area"), tr("x"), tr("z") };
|
||||
csvline(stream, qte, sep, header);
|
||||
|
||||
QTreeWidgetItemIterator it(ui->treeLocate);
|
||||
QString seed;
|
||||
for (; *it; ++it)
|
||||
{
|
||||
@ -573,7 +768,7 @@ void TabBiomes::on_pushExport_clicked()
|
||||
cols.append(item->text(1));
|
||||
cols.append(item->text(2));
|
||||
cols.append(item->text(3));
|
||||
stream << cols.join(";") << "\n";
|
||||
csvline(stream, qte, sep, cols);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -616,7 +811,7 @@ void TabBiomes::on_lineBiomeSize_textChanged(const QString &text)
|
||||
ui->labelBiomeSize->setText(QString::asprintf("(%g sq. chunks)", area / 16));
|
||||
}
|
||||
|
||||
void TabBiomes::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column)
|
||||
void TabBiomes::on_treeLocate_itemClicked(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
(void) column;
|
||||
QVariant dat;
|
||||
@ -639,3 +834,15 @@ void TabBiomes::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column)
|
||||
}
|
||||
}
|
||||
|
||||
void TabBiomes::on_tabWidget_currentChanged(int)
|
||||
{
|
||||
bool ok = false;
|
||||
if (!thread.isRunning())
|
||||
{
|
||||
if (ui->tabWidget->currentWidget() == ui->tabStats)
|
||||
ok = !model->ids.empty();
|
||||
if (ui->tabWidget->currentWidget() == ui->tabLocate)
|
||||
ok = ui->treeLocate->topLevelItemCount() > 0;
|
||||
}
|
||||
ui->pushExport->setEnabled(ok);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class AnalysisBiomes : public QThread
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AnalysisBiomes(QObject *parent = nullptr)
|
||||
: QThread(parent) {}
|
||||
: QThread(parent),idx() {}
|
||||
|
||||
virtual void run() override;
|
||||
void runStatistics(Generator *g);
|
||||
@ -35,11 +35,14 @@ public:
|
||||
QVector<uint64_t> seeds;
|
||||
WorldInfo wi;
|
||||
std::atomic_bool stop;
|
||||
std::atomic_int idx;
|
||||
int dims[3];
|
||||
int x1, z1, x2, z2;
|
||||
int scale;
|
||||
uint64_t samples;
|
||||
int locate;
|
||||
struct Dat {
|
||||
int x1, z1, x2, z2;
|
||||
int scale;
|
||||
int locate;
|
||||
uint64_t samples;
|
||||
} dat;
|
||||
int minsize;
|
||||
int tolerance;
|
||||
};
|
||||
@ -50,14 +53,14 @@ public:
|
||||
explicit BiomeTableModel(QObject *parent = nullptr) :
|
||||
QAbstractTableModel(parent), cmp(IdCmp::SORT_LEX, -1, DIM_UNDEF) {}
|
||||
|
||||
virtual int rowCount(const QModelIndex&) const override { return ids.size(); }
|
||||
virtual int columnCount(const QModelIndex&) const override { return seeds.size(); }
|
||||
virtual int rowCount(const QModelIndex&) const override { return seeds.size(); }
|
||||
virtual int columnCount(const QModelIndex&) const override { return ids.size(); }
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role) const override;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
|
||||
int insertId(int id);
|
||||
int insertSeed(uint64_t seed);
|
||||
void insertIds(QSet<int>& ids);
|
||||
void insertSeeds(QList<uint64_t>& seeds);
|
||||
void reset(int mc);
|
||||
|
||||
QList<int> ids;
|
||||
@ -71,7 +74,7 @@ class BiomeSortProxy : public QSortFilterProxyModel
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BiomeSortProxy(QObject *parent = nullptr) : QSortFilterProxyModel(parent),column(),order() {}
|
||||
BiomeSortProxy(QObject *parent = nullptr) : QSortFilterProxyModel(parent),column(),order(Qt::AscendingOrder) {}
|
||||
|
||||
virtual bool lessThan(const QModelIndex& a, const QModelIndex& b) const override
|
||||
{
|
||||
@ -82,6 +85,8 @@ public:
|
||||
|
||||
virtual void sort(int column, Qt::SortOrder order) override
|
||||
{
|
||||
if (column >= columnCount())
|
||||
return;
|
||||
if (this->column == -1)
|
||||
QSortFilterProxyModel::sort(-1, order);
|
||||
else
|
||||
@ -94,6 +99,22 @@ public:
|
||||
Qt::SortOrder order;
|
||||
};
|
||||
|
||||
class BiomeHeader : public QHeaderView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BiomeHeader(QWidget *parent = nullptr);
|
||||
|
||||
void onSectionPress(int section);
|
||||
virtual bool event(QEvent *e) override;
|
||||
virtual void paintSection(QPainter *painter, const QRect& rect, int section) const override;
|
||||
virtual QSize sectionSizeFromContents(int section) const override;
|
||||
|
||||
int hover;
|
||||
int pressed;
|
||||
};
|
||||
|
||||
class TabBiomes : public QWidget, public ISaveTab
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -109,23 +130,21 @@ public:
|
||||
void refreshBiomes(int activeid = -1);
|
||||
|
||||
private slots:
|
||||
void onLocateHeaderClick();
|
||||
void onTableSort(int column, Qt::SortOrder);
|
||||
void onAnalysisSeedDone(uint64_t seed, QVector<uint64_t> idcnt);
|
||||
void onAnalysisSeedItem(QTreeWidgetItem *item);
|
||||
void onAnalysisFinished();
|
||||
void onSort(int column, Qt::SortOrder);
|
||||
void onBufferTimeout();
|
||||
|
||||
void on_pushStart_clicked();
|
||||
void on_pushExport_clicked();
|
||||
|
||||
void on_table_doubleClicked(const QModelIndex &index);
|
||||
|
||||
void on_buttonFromVisible_clicked();
|
||||
|
||||
void on_radioFullSample_toggled(bool checked);
|
||||
|
||||
void on_lineBiomeSize_textChanged(const QString &arg1);
|
||||
|
||||
void on_treeWidget_itemClicked(QTreeWidgetItem *item, int column);
|
||||
void on_treeLocate_itemClicked(QTreeWidgetItem *item, int column);
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
|
||||
private:
|
||||
Ui::TabBiomes *ui;
|
||||
@ -133,7 +152,15 @@ private:
|
||||
AnalysisBiomes thread;
|
||||
BiomeTableModel *model;
|
||||
BiomeSortProxy *proxy;
|
||||
std::map<QString, int> str2biome;
|
||||
QMap<QString, int> str2biome;
|
||||
AnalysisBiomes::Dat dats, datl;
|
||||
int sortcol;
|
||||
|
||||
QElapsedTimer elapsed;
|
||||
uint64_t updt;
|
||||
uint64_t nextupdate;
|
||||
QVector<QVector<uint64_t>> qbufs;
|
||||
QList<QTreeWidgetItem*> qbufl;
|
||||
};
|
||||
|
||||
#endif // TABBIOMES_H
|
||||
|
@ -238,7 +238,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QTreeWidget" name="treeWidget">
|
||||
<widget class="QTreeWidget" name="treeLocate">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Monospace</family>
|
||||
|
@ -34,10 +34,10 @@ void AnalysisStructures::run()
|
||||
Generator g;
|
||||
setupGenerator(&g, wi.mc, wi.large);
|
||||
|
||||
for (int64_t seed : qAsConst(seeds))
|
||||
for (idx = 0; idx < seeds.size(); idx++)
|
||||
{
|
||||
if (stop) break;
|
||||
wi.seed = seed;
|
||||
wi.seed = seeds[idx];
|
||||
if (quad)
|
||||
runQuads(&g);
|
||||
else
|
||||
@ -71,7 +71,7 @@ void AnalysisStructures::runStructs(Generator *g)
|
||||
sdim = DIM_NETHER;
|
||||
if (sconf.properties & STRUCT_END)
|
||||
sdim = DIM_END;
|
||||
getStructs(&st, sconf, wi, sdim, x1, z1, x2, z2);
|
||||
getStructs(&st, sconf, wi, sdim, area.x1, area.z1, area.x2, area.z2);
|
||||
if (st.empty())
|
||||
continue;
|
||||
|
||||
@ -102,7 +102,7 @@ void AnalysisStructures::runStructs(Generator *g)
|
||||
{
|
||||
applySeed(g, 0, wi.seed);
|
||||
Pos pos = getSpawn(g);
|
||||
if (pos.x >= x1 && pos.x <= x2 && pos.z >= z1 && pos.z <= z2)
|
||||
if (pos.x >= area.x1 && pos.x <= area.x2 && pos.z >= area.z1 && pos.z <= area.z2)
|
||||
{
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem(seeditem);
|
||||
item->setText(C_SEED, "-");
|
||||
@ -124,8 +124,8 @@ void AnalysisStructures::runStructs(Generator *g)
|
||||
applySeed(g, DIM_OVERWORLD, wi.seed);
|
||||
|
||||
// get the maximum relevant ring number
|
||||
int rx1 = abs(x1), rx2 = abs(x2);
|
||||
int rz1 = abs(z1), rz2 = abs(z2);
|
||||
int rx1 = abs(area.x1), rx2 = abs(area.x2);
|
||||
int rz1 = abs(area.z1), rz2 = abs(area.z2);
|
||||
int xt = (rx1 > rx2 ? rx1 : rx2) + 112+8;
|
||||
int zt = (rz1 > rz2 ? rz1 : rz2) + 112+8;
|
||||
int rmax = xt*xt + zt*zt;
|
||||
@ -136,7 +136,7 @@ void AnalysisStructures::runStructs(Generator *g)
|
||||
if (stop || sh.ringnum > rmax)
|
||||
break;
|
||||
Pos pos = sh.pos;
|
||||
if (pos.x >= x1 && pos.x <= x2 && pos.z >= z1 && pos.z <= z2)
|
||||
if (pos.x >= area.x1 && pos.x <= area.x2 && pos.z >= area.z1 && pos.z <= area.z2)
|
||||
shp.push_back(pos);
|
||||
}
|
||||
|
||||
@ -222,6 +222,10 @@ TabStructures::TabStructures(MainWindow *parent)
|
||||
, ui(new Ui::TabStructures)
|
||||
, parent(parent)
|
||||
, thread(this)
|
||||
, sortcols(-1)
|
||||
, sortcolq(-1)
|
||||
, nextupdate()
|
||||
, updt(100)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -229,10 +233,12 @@ TabStructures::TabStructures(MainWindow *parent)
|
||||
ui->treeStructs->setColumnWidth(C_COUNT, 50);
|
||||
ui->treeStructs->setColumnWidth(C_X, 65);
|
||||
ui->treeStructs->setColumnWidth(C_Z, 65);
|
||||
ui->treeStructs->sortByColumn(0, Qt::AscendingOrder);
|
||||
ui->treeStructs->sortByColumn(-1, Qt::AscendingOrder);
|
||||
connect(ui->treeStructs->header(), &QHeaderView::sectionClicked, this, [=](){ onHeaderClick(ui->treeStructs); } );
|
||||
|
||||
ui->treeQuads->setColumnWidth(0, 160);
|
||||
ui->treeQuads->sortByColumn(0, Qt::AscendingOrder);
|
||||
ui->treeQuads->sortByColumn(-1, Qt::AscendingOrder);
|
||||
connect(ui->treeQuads->header(), &QHeaderView::sectionClicked, this, [=](){ onHeaderClick(ui->treeQuads); } );
|
||||
|
||||
connect(&thread, &AnalysisStructures::itemDone, this, &TabStructures::onAnalysisItemDone, Qt::BlockingQueuedConnection);
|
||||
connect(&thread, &AnalysisStructures::quadDone, this, &TabStructures::onAnalysisQuadDone, Qt::BlockingQueuedConnection);
|
||||
@ -244,6 +250,8 @@ TabStructures::TabStructures(MainWindow *parent)
|
||||
|
||||
TabStructures::~TabStructures()
|
||||
{
|
||||
thread.stop = true;
|
||||
thread.wait(500);
|
||||
delete ui;
|
||||
}
|
||||
|
||||
@ -285,31 +293,85 @@ void TabStructures::load(QSettings& settings)
|
||||
ui->radioAll->setChecked(true);
|
||||
}
|
||||
|
||||
void TabStructures::onHeaderClick(QTreeView *tree)
|
||||
{
|
||||
int& col = (tree == ui->treeStructs) ? sortcols : sortcolq;
|
||||
int section = tree->header()->sortIndicatorSection();
|
||||
if (tree->header()->sortIndicatorOrder() == Qt::AscendingOrder && col == section)
|
||||
{
|
||||
tree->sortByColumn(-1, Qt::DescendingOrder);
|
||||
section = -1;
|
||||
}
|
||||
col = section;
|
||||
}
|
||||
|
||||
void TabStructures::onAnalysisItemDone(QTreeWidgetItem *item)
|
||||
{
|
||||
ui->treeStructs->addTopLevelItem(item);
|
||||
ui->treeStructs->resizeColumnToContents(C_DETAIL);
|
||||
|
||||
QString progress = QString::asprintf(" (%d/%d)", ui->treeStructs->topLevelItemCount()+1, thread.seeds.size());
|
||||
ui->pushStart->setText(tr("Stop") + progress);
|
||||
qbufs.push_back(item);
|
||||
quint64 ns = elapsed.nsecsElapsed();
|
||||
if (ns > nextupdate)
|
||||
{
|
||||
nextupdate = ns + updt * 1e6;
|
||||
QTimer::singleShot(updt, this, &TabStructures::onBufferTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
void TabStructures::onAnalysisQuadDone(QTreeWidgetItem *item)
|
||||
{
|
||||
ui->treeQuads->addTopLevelItem(item);
|
||||
item->setExpanded(true);
|
||||
|
||||
QString progress = QString::asprintf(" (%d/%d)", ui->treeQuads->topLevelItemCount()+1, thread.seeds.size());
|
||||
ui->pushStart->setText(tr("Stop") + progress);
|
||||
qbufq.push_back(item);
|
||||
quint64 ns = elapsed.nsecsElapsed();
|
||||
if (ns > nextupdate)
|
||||
{
|
||||
nextupdate = ns + updt * 1e6;
|
||||
QTimer::singleShot(updt, this, &TabStructures::onBufferTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
void TabStructures::onAnalysisFinished()
|
||||
{
|
||||
ui->pushExport->setEnabled(ui->treeStructs->topLevelItemCount() > 0);
|
||||
onBufferTimeout();
|
||||
on_tabWidget_currentChanged(-1);
|
||||
ui->treeStructs->setSortingEnabled(true);
|
||||
ui->treeQuads->setSortingEnabled(true);
|
||||
ui->pushStart->setChecked(false);
|
||||
ui->pushStart->setText(tr("Analyze"));
|
||||
}
|
||||
|
||||
void TabStructures::onBufferTimeout()
|
||||
{
|
||||
if (qbufs.empty() && qbufq.empty())
|
||||
return;
|
||||
uint64_t t = -elapsed.elapsed();
|
||||
if (!qbufs.empty())
|
||||
{
|
||||
ui->treeStructs->setSortingEnabled(false);
|
||||
ui->treeStructs->setUpdatesEnabled(false);
|
||||
ui->treeStructs->addTopLevelItems(qbufs);
|
||||
ui->treeStructs->resizeColumnToContents(C_DETAIL);
|
||||
ui->treeStructs->setUpdatesEnabled(true);
|
||||
ui->treeStructs->setSortingEnabled(true);
|
||||
qbufs.clear();
|
||||
}
|
||||
if (!qbufq.empty())
|
||||
{
|
||||
ui->treeQuads->setSortingEnabled(false);
|
||||
ui->treeQuads->setUpdatesEnabled(false);
|
||||
ui->treeQuads->addTopLevelItems(qbufq);
|
||||
for (QTreeWidgetItem *item: qAsConst(qbufq))
|
||||
item->setExpanded(true);
|
||||
ui->treeQuads->setUpdatesEnabled(true);
|
||||
ui->treeQuads->setSortingEnabled(true);
|
||||
qbufq.clear();
|
||||
}
|
||||
QString progress = QString::asprintf(" (%d/%d)", thread.idx.load(), thread.seeds.size());
|
||||
ui->pushStart->setText(tr("Stop") + progress);
|
||||
|
||||
t += elapsed.elapsed();
|
||||
if (8*t > updt)
|
||||
updt = 4*t;
|
||||
nextupdate = elapsed.nsecsElapsed() + 1e6 * updt;
|
||||
}
|
||||
|
||||
void TabStructures::onTreeItemClicked(QTreeWidgetItem *item, int column)
|
||||
{
|
||||
(void) column;
|
||||
@ -340,6 +402,10 @@ void TabStructures::on_pushStart_clicked()
|
||||
thread.stop = true;
|
||||
return;
|
||||
}
|
||||
updt = 20;
|
||||
nextupdate = 0;
|
||||
elapsed.start();
|
||||
|
||||
parent->getSeed(&thread.wi);
|
||||
thread.seeds.clear();
|
||||
if (ui->comboSeedSource->currentIndex() == 0)
|
||||
@ -353,10 +419,7 @@ void TabStructures::on_pushStart_clicked()
|
||||
int z2 = ui->lineZ2->text().toInt();
|
||||
if (x2 < x1) std::swap(x1, x2);
|
||||
if (z2 < z1) std::swap(z1, z2);
|
||||
thread.x1 = x1;
|
||||
thread.z1 = z1;
|
||||
thread.x2 = x2;
|
||||
thread.z2 = z2;
|
||||
thread.area = AnalysisStructures::Dat{x1, z1, x2, z2};
|
||||
|
||||
thread.collect = ui->checkCollect->isChecked();
|
||||
|
||||
@ -366,14 +429,20 @@ void TabStructures::on_pushStart_clicked()
|
||||
if (ui->tabWidget->currentWidget() == ui->tabStructures)
|
||||
{
|
||||
thread.quad = false;
|
||||
dats = thread.area;
|
||||
ui->treeStructs->setSortingEnabled(false);
|
||||
while (ui->treeStructs->topLevelItemCount() > 0)
|
||||
delete ui->treeStructs->takeTopLevelItem(0);
|
||||
ui->treeStructs->setSortingEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
thread.quad = true;
|
||||
while (ui->treeStructs->topLevelItemCount() > 0)
|
||||
delete ui->treeStructs->takeTopLevelItem(0);
|
||||
datq = thread.area;
|
||||
ui->treeQuads->setSortingEnabled(false);
|
||||
while (ui->treeQuads->topLevelItemCount() > 0)
|
||||
delete ui->treeQuads->takeTopLevelItem(0);
|
||||
ui->treeQuads->setSortingEnabled(true);
|
||||
}
|
||||
|
||||
ui->pushExport->setEnabled(false);
|
||||
@ -382,6 +451,18 @@ void TabStructures::on_pushStart_clicked()
|
||||
thread.start();
|
||||
}
|
||||
|
||||
static
|
||||
void csvline(QTextStream& stream, const QString& qte, const QString& sep, QStringList& cols)
|
||||
{
|
||||
if (qte.isEmpty())
|
||||
{
|
||||
for (QString& s : cols)
|
||||
if (s.contains(sep))
|
||||
s = "\"" + s + "\"";
|
||||
}
|
||||
stream << qte << cols.join(sep) << qte << "\n";
|
||||
}
|
||||
|
||||
void TabStructures::on_pushExport_clicked()
|
||||
{
|
||||
QString fnam = QFileDialog::getSaveFileName(
|
||||
@ -399,74 +480,104 @@ void TabStructures::on_pushExport_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
QString qte = parent->config.quote;
|
||||
QString sep = parent->config.separator;
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream << "Sep=" + sep + "\n";
|
||||
sep = qte + sep + qte;
|
||||
|
||||
stream << "#X1; " << thread.x1 << "\n";
|
||||
stream << "#Z1; " << thread.z1 << "\n";
|
||||
stream << "#X2; " << thread.x2 << "\n";
|
||||
stream << "#Z2; " << thread.z2 << "\n";
|
||||
|
||||
QTreeWidgetItemIterator it(ui->treeStructs);
|
||||
|
||||
if (ui->checkCollect->isChecked())
|
||||
if (ui->tabWidget->currentWidget() == ui->tabStructures)
|
||||
{
|
||||
stream << "seed; structure; x; z; details\n";
|
||||
QString seed;
|
||||
QString structure;
|
||||
for (; *it; ++it)
|
||||
{
|
||||
QTreeWidgetItem *item = *it;
|
||||
if (item->text(C_SEED) != "-")
|
||||
seed = item->text(C_SEED);
|
||||
if (!item->text(C_STRUCT).isEmpty())
|
||||
structure = item->text(C_STRUCT);
|
||||
if (!item->data(0, Qt::UserRole+2).isValid())
|
||||
continue;
|
||||
stream << qte << "#X1" << sep << dats.x1 << qte << "\n";
|
||||
stream << qte << "#Z1" << sep << dats.z1 << qte << "\n";
|
||||
stream << qte << "#X2" << sep << dats.x2 << qte << "\n";
|
||||
stream << qte << "#Z2" << sep << dats.z2 << qte << "\n";
|
||||
|
||||
QStringList cols;
|
||||
cols.append(seed);
|
||||
cols.append(structure);
|
||||
cols.append(item->text(C_X));
|
||||
cols.append(item->text(C_Z));
|
||||
cols.append(item->text(C_DETAIL));
|
||||
stream << cols.join(";") << "\n";
|
||||
if (ui->checkCollect->isChecked())
|
||||
{
|
||||
QStringList header = { tr("seed"), tr("structure"), tr("x"), tr("z"), tr("details") };
|
||||
csvline(stream, qte, sep, header);
|
||||
QString seed;
|
||||
QString structure;
|
||||
for (QTreeWidgetItemIterator it(ui->treeStructs); *it; ++it)
|
||||
{
|
||||
QTreeWidgetItem *item = *it;
|
||||
if (item->text(C_SEED) != "-")
|
||||
seed = item->text(C_SEED);
|
||||
if (!item->text(C_STRUCT).isEmpty())
|
||||
structure = item->text(C_STRUCT);
|
||||
if (!item->data(0, Qt::UserRole+2).isValid())
|
||||
continue;
|
||||
|
||||
QStringList cols;
|
||||
cols.append(seed);
|
||||
cols.append(structure);
|
||||
cols.append(item->text(C_X));
|
||||
cols.append(item->text(C_Z));
|
||||
cols.append(item->text(C_DETAIL));
|
||||
csvline(stream, qte, sep, cols);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::set<QString> structures;
|
||||
std::map<uint64_t, std::map<QString, QString>> cnt; // [seed][stype]
|
||||
|
||||
uint64_t seed;
|
||||
QString structure;
|
||||
for (QTreeWidgetItemIterator it(ui->treeStructs); *it; ++it)
|
||||
{
|
||||
QTreeWidgetItem *item = *it;
|
||||
if (item->data(0, Qt::UserRole).isValid())
|
||||
seed = item->data(0, Qt::UserRole).toLongLong();
|
||||
if (!item->text(C_STRUCT).isEmpty())
|
||||
structures.insert((structure = item->text(C_STRUCT)));
|
||||
if (!item->text(C_COUNT).isEmpty())
|
||||
cnt[seed][structure] = item->text(C_COUNT);
|
||||
}
|
||||
|
||||
QStringList header = { tr("seed") };
|
||||
for (auto& sit : structures)
|
||||
header.append(sit);
|
||||
csvline(stream, qte, sep, header);
|
||||
for (auto& m : cnt)
|
||||
{
|
||||
QStringList cols;
|
||||
cols << QString::asprintf("%" PRId64, m.first);
|
||||
for (auto& sit : structures)
|
||||
{
|
||||
QString cntstr = m.second[sit];
|
||||
if (cntstr.isEmpty())
|
||||
cntstr = "0";
|
||||
cols.append(cntstr);
|
||||
}
|
||||
csvline(stream, qte, sep, cols);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else if(ui->tabWidget->currentWidget() == ui->tabQuads)
|
||||
{
|
||||
std::set<QString> structures;
|
||||
std::map<uint64_t, std::map<QString, QString>> cnt; // [seed][stype]/[row][col]
|
||||
stream << qte << "#X1" << sep << datq.x1 << qte << "\n";
|
||||
stream << qte << "#Z1" << sep << datq.z1 << qte << "\n";
|
||||
stream << qte << "#X2" << sep << datq.x2 << qte << "\n";
|
||||
stream << qte << "#Z2" << sep << datq.z2 << qte << "\n";
|
||||
|
||||
uint64_t seed;
|
||||
QString structure;
|
||||
for (; *it; ++it)
|
||||
QStringList header = { tr("seed"), tr("type"), tr("distance"), tr("x"), tr("z"), tr("radius"), tr("spawn area") };
|
||||
csvline(stream, qte, sep, header);
|
||||
QString seed;
|
||||
for (QTreeWidgetItemIterator it(ui->treeQuads); *it; ++it)
|
||||
{
|
||||
QTreeWidgetItem *item = *it;
|
||||
if (item->data(0, Qt::UserRole).isValid())
|
||||
seed = item->data(0, Qt::UserRole).toLongLong();
|
||||
if (!item->text(C_STRUCT).isEmpty())
|
||||
structures.insert((structure = item->text(C_STRUCT)));
|
||||
if (!item->text(C_COUNT).isEmpty())
|
||||
cnt[seed][structure] = item->text(C_COUNT);
|
||||
}
|
||||
|
||||
QStringList header;
|
||||
header.append("seed");
|
||||
for (auto& sit : structures)
|
||||
header.append(sit);
|
||||
stream << header.join(";") << "\n";
|
||||
for (auto& m : cnt)
|
||||
{
|
||||
QStringList cols;
|
||||
cols << QString::asprintf("%" PRId64, m.first);
|
||||
for (auto& sit : structures)
|
||||
if (item->text(0) != "-")
|
||||
{
|
||||
QString cntstr = m.second[sit];
|
||||
if (cntstr.isEmpty())
|
||||
cntstr = "0";
|
||||
cols.append(cntstr);
|
||||
seed = item->text(0);
|
||||
continue;
|
||||
}
|
||||
stream << cols.join(";") << "\n";
|
||||
QStringList cols = { seed };
|
||||
for (int i = 1, n = item->columnCount(); i < n; i++)
|
||||
cols.append(item->text(i));
|
||||
csvline(stream, qte, sep, cols);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -486,3 +597,16 @@ void TabStructures::on_buttonFromVisible_clicked()
|
||||
ui->lineX2->setText( QString::number(bx1) );
|
||||
ui->lineZ2->setText( QString::number(bz1) );
|
||||
}
|
||||
|
||||
void TabStructures::on_tabWidget_currentChanged(int)
|
||||
{
|
||||
bool ok = false;
|
||||
if (!thread.isRunning())
|
||||
{
|
||||
if (ui->tabWidget->currentWidget() == ui->tabStructures)
|
||||
ok = ui->treeStructs->topLevelItemCount() > 0;
|
||||
if (ui->tabWidget->currentWidget() == ui->tabQuads)
|
||||
ok = ui->treeQuads->topLevelItemCount() > 0;
|
||||
}
|
||||
ui->pushExport->setEnabled(ok);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class AnalysisStructures : public QThread
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AnalysisStructures(QObject *parent = nullptr)
|
||||
: QThread(parent) {}
|
||||
: QThread(parent),idx() {}
|
||||
|
||||
virtual void run() override;
|
||||
void runStructs(Generator *g);
|
||||
@ -27,7 +27,8 @@ public:
|
||||
QVector<uint64_t> seeds;
|
||||
WorldInfo wi;
|
||||
std::atomic_bool stop;
|
||||
int x1, z1, x2, z2;
|
||||
std::atomic_int idx;
|
||||
struct Dat { int x1, z1, x2, z2; } area;
|
||||
bool mapshow[STRUCT_NUM];
|
||||
bool collect;
|
||||
bool quad;
|
||||
@ -45,23 +46,33 @@ public:
|
||||
virtual void load(QSettings& settings) override;
|
||||
|
||||
private slots:
|
||||
void onHeaderClick(QTreeView *tree);
|
||||
|
||||
void onAnalysisItemDone(QTreeWidgetItem *item);
|
||||
void onAnalysisQuadDone(QTreeWidgetItem *item);
|
||||
void onAnalysisFinished();
|
||||
void onBufferTimeout();
|
||||
|
||||
void onTreeItemClicked(QTreeWidgetItem *item, int column);
|
||||
|
||||
void on_pushStart_clicked();
|
||||
|
||||
void on_pushExport_clicked();
|
||||
|
||||
void on_buttonFromVisible_clicked();
|
||||
void on_tabWidget_currentChanged(int index);
|
||||
|
||||
|
||||
private:
|
||||
Ui::TabStructures *ui;
|
||||
MainWindow *parent;
|
||||
AnalysisStructures thread;
|
||||
AnalysisStructures::Dat dats, datq;
|
||||
int sortcols, sortcolq;
|
||||
|
||||
QElapsedTimer elapsed;
|
||||
uint64_t nextupdate;
|
||||
uint64_t updt;
|
||||
QList<QTreeWidgetItem*> qbufs;
|
||||
QList<QTreeWidgetItem*> qbufq;
|
||||
};
|
||||
|
||||
#endif // TABSTRUCTURES_H
|
||||
|
@ -54,9 +54,10 @@ void AnalysisTriggers::run()
|
||||
{
|
||||
stop = false;
|
||||
|
||||
for (int64_t seed : qAsConst(seeds))
|
||||
for (idx = 0; idx < seeds.size(); idx++)
|
||||
{
|
||||
if (stop) break;
|
||||
int64_t seed = seeds[idx];
|
||||
wi.seed = seed;
|
||||
QTreeWidgetItem *seeditem = new QTreeWidgetItem();
|
||||
seeditem->setText(0, QString::asprintf("%" PRId64, seed));
|
||||
@ -97,19 +98,23 @@ TabTriggers::TabTriggers(MainWindow *parent)
|
||||
, ui(new Ui::TabTriggers)
|
||||
, parent(parent)
|
||||
, thread()
|
||||
, nextupdate()
|
||||
, updt(20)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->treeWidget->setColumnWidth(1, 280);
|
||||
ui->treeWidget->setColumnWidth(2, 65);
|
||||
ui->treeWidget->setColumnWidth(3, 65);
|
||||
ui->treeWidget->setSortingEnabled(false); // sortable triggers are not necessary
|
||||
|
||||
ui->treeWidget->sortByColumn(0, Qt::AscendingOrder);
|
||||
connect(&thread, &AnalysisTriggers::itemDone, this, &TabTriggers::onAnalysisItemDone, Qt::BlockingQueuedConnection);
|
||||
connect(&thread, &AnalysisTriggers::finished, this, &TabTriggers::onAnalysisFinished);
|
||||
}
|
||||
|
||||
TabTriggers::~TabTriggers()
|
||||
{
|
||||
thread.stop = true;
|
||||
thread.wait(500);
|
||||
delete ui;
|
||||
}
|
||||
|
||||
@ -126,18 +131,45 @@ void TabTriggers::load(QSettings& settings)
|
||||
|
||||
void TabTriggers::onAnalysisItemDone(QTreeWidgetItem *item)
|
||||
{
|
||||
ui->treeWidget->addTopLevelItem(item);
|
||||
QString progress = QString::asprintf(" (%d/%d)", ui->treeWidget->topLevelItemCount()+1, thread.seeds.size());
|
||||
ui->pushStart->setText(tr("Stop") + progress);
|
||||
qbuf.push_back(item);
|
||||
quint64 ns = elapsed.nsecsElapsed();
|
||||
if (ns > nextupdate)
|
||||
{
|
||||
nextupdate = ns + updt * 1e6;
|
||||
QTimer::singleShot(updt, this, &TabTriggers::onBufferTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
void TabTriggers::onAnalysisFinished()
|
||||
{
|
||||
onBufferTimeout();
|
||||
ui->pushExport->setEnabled(ui->treeWidget->topLevelItemCount() > 0);
|
||||
ui->pushStart->setChecked(false);
|
||||
ui->pushStart->setText(tr("Analyze"));
|
||||
}
|
||||
|
||||
void TabTriggers::onBufferTimeout()
|
||||
{
|
||||
uint64_t t = -elapsed.elapsed();
|
||||
|
||||
if (!qbuf.empty())
|
||||
{
|
||||
ui->treeWidget->setUpdatesEnabled(false);
|
||||
ui->treeWidget->addTopLevelItems(qbuf);
|
||||
ui->treeWidget->setUpdatesEnabled(true);
|
||||
|
||||
QString progress = QString::asprintf(" (%d/%d)", thread.idx.load(), thread.seeds.size());
|
||||
ui->pushStart->setText(tr("Stop") + progress);
|
||||
|
||||
qbuf.clear();
|
||||
}
|
||||
|
||||
t += elapsed.elapsed();
|
||||
if (8*t > updt)
|
||||
updt = 4*t;
|
||||
nextupdate = elapsed.nsecsElapsed() + 1e6 * updt;
|
||||
}
|
||||
|
||||
void TabTriggers::on_pushStart_clicked()
|
||||
{
|
||||
if (thread.isRunning())
|
||||
@ -145,6 +177,10 @@ void TabTriggers::on_pushStart_clicked()
|
||||
thread.stop = true;
|
||||
return;
|
||||
}
|
||||
updt = 20;
|
||||
nextupdate = 0;
|
||||
elapsed.start();
|
||||
|
||||
parent->getSeed(&thread.wi);
|
||||
thread.conds = parent->formCond->getConditions();
|
||||
thread.seeds.clear();
|
||||
@ -153,6 +189,7 @@ void TabTriggers::on_pushStart_clicked()
|
||||
else
|
||||
thread.seeds = parent->formControl->getResults();
|
||||
|
||||
//ui->treeWidget->setSortingEnabled(false);
|
||||
while (ui->treeWidget->topLevelItemCount() > 0)
|
||||
delete ui->treeWidget->takeTopLevelItem(0);
|
||||
|
||||
@ -191,6 +228,18 @@ void TabTriggers::on_pushExpand_clicked()
|
||||
ui->treeWidget->expandAll();
|
||||
}
|
||||
|
||||
static
|
||||
void csvline(QTextStream& stream, const QString& qte, const QString& sep, QStringList& cols)
|
||||
{
|
||||
if (qte.isEmpty())
|
||||
{
|
||||
for (QString& s : cols)
|
||||
if (s.contains(sep))
|
||||
s = "\"" + s + "\"";
|
||||
}
|
||||
stream << qte << cols.join(sep) << qte << "\n";
|
||||
}
|
||||
|
||||
void TabTriggers::on_pushExport_clicked()
|
||||
{
|
||||
QString fnam = QFileDialog::getSaveFileName(
|
||||
@ -208,8 +257,15 @@ void TabTriggers::on_pushExport_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
QString qte = parent->config.quote;
|
||||
QString sep = parent->config.separator;
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream << "#seed; condition; x; z\n";
|
||||
stream << "Sep=" + sep + "\n";
|
||||
sep = qte + sep + qte;
|
||||
|
||||
QStringList header = { tr("seed"), tr("condition"), tr("x"), tr("z") };
|
||||
csvline(stream, qte, sep, header);
|
||||
|
||||
QTreeWidgetItemIterator it(ui->treeWidget);
|
||||
for (; *it; ++it)
|
||||
@ -218,12 +274,11 @@ void TabTriggers::on_pushExport_clicked()
|
||||
QStringList cols;
|
||||
for (int i = 0, n = item->columnCount(); i < n; i++)
|
||||
{
|
||||
QString txt = item->text(i);
|
||||
if (txt == "-") txt = "";
|
||||
if (i == 1) txt = "\"" + txt + "\"";
|
||||
cols << txt;
|
||||
QString s = item->text(i);
|
||||
if (s == "-") s = "";
|
||||
cols.append(s);
|
||||
}
|
||||
stream << cols.join("; ") << "\n";
|
||||
csvline(stream, qte, sep, cols);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ class AnalysisTriggers : public QThread
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AnalysisTriggers(QObject *parent = nullptr)
|
||||
: QThread(parent) {}
|
||||
: QThread(parent), conds(),seeds(),wi(),stop(),idx() {}
|
||||
|
||||
virtual void run() override;
|
||||
|
||||
@ -30,6 +30,7 @@ public:
|
||||
QVector<uint64_t> seeds;
|
||||
WorldInfo wi;
|
||||
std::atomic_bool stop;
|
||||
std::atomic_int idx;
|
||||
};
|
||||
|
||||
|
||||
@ -47,17 +48,22 @@ public:
|
||||
private slots:
|
||||
void onAnalysisItemDone(QTreeWidgetItem *item);
|
||||
void onAnalysisFinished();
|
||||
void onBufferTimeout();
|
||||
|
||||
void on_pushStart_clicked();
|
||||
void on_pushExpand_clicked();
|
||||
void on_pushExport_clicked();
|
||||
|
||||
void on_treeWidget_itemClicked(QTreeWidgetItem *item, int column);
|
||||
|
||||
private:
|
||||
Ui::TabTriggers *ui;
|
||||
MainWindow *parent;
|
||||
AnalysisTriggers thread;
|
||||
|
||||
QElapsedTimer elapsed;
|
||||
uint64_t nextupdate;
|
||||
uint64_t updt;
|
||||
QList<QTreeWidgetItem*> qbuf;
|
||||
};
|
||||
|
||||
#endif // TABTRIGGERS_H
|
||||
|
@ -86,16 +86,16 @@ const QPixmap& getMapIcon(int opt, VarPos *vp)
|
||||
return icons[opt];
|
||||
}
|
||||
|
||||
QIcon getBiomeIcon(int id)
|
||||
QIcon getBiomeIcon(int id, bool warn)
|
||||
{
|
||||
static QPixmap pixmap(14, 14);
|
||||
pixmap.fill(QColor(0,0,0,0));
|
||||
QPainter p(&pixmap);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
QPainterPath path;
|
||||
path.addRoundedRect(QRectF(1, 1, 12, 12), 3, 3);
|
||||
QPen pen(Qt::black, 1);
|
||||
p.setPen(pen);
|
||||
int b = warn ? 2 : 1;
|
||||
path.addRoundedRect(pixmap.rect().adjusted(b, b, -b, -b), 3, 3);
|
||||
p.setPen(QPen(warn ? Qt::red : Qt::black, b));
|
||||
QColor col(g_biomeColors[id][0], g_biomeColors[id][1], g_biomeColors[id][2]);
|
||||
p.fillPath(path, col);
|
||||
p.drawPath(path);
|
||||
@ -561,7 +561,7 @@ QWorld::QWorld(WorldInfo wi, int dim, int layeropt)
|
||||
, selx()
|
||||
, selz()
|
||||
, selopt(-1)
|
||||
, selvp(Pos{}, -1)
|
||||
, selvp(Pos{0,0}, -1)
|
||||
, qual()
|
||||
{
|
||||
setupGenerator(&g, wi.mc, wi.large);
|
||||
|
@ -165,7 +165,7 @@ struct VarPos
|
||||
};
|
||||
|
||||
const QPixmap& getMapIcon(int opt, VarPos *variation = 0);
|
||||
QIcon getBiomeIcon(int id);
|
||||
QIcon getBiomeIcon(int id, bool warn = false);
|
||||
|
||||
void getStructs(std::vector<VarPos> *out, const StructureConfig sconf,
|
||||
WorldInfo wi, int dim, int x0, int z0, int x1, int z1);
|
||||
|
Loading…
Reference in New Issue
Block a user