diff --git a/cubiomes b/cubiomes index ae58fc4..0af31b4 160000 --- a/cubiomes +++ b/cubiomes @@ -1 +1 @@ -Subproject commit ae58fc49833db481ae493015ddffce317f1ace9b +Subproject commit 0af31b4e7eeb14a58c2bd9a4c4c68b97b4a7d6e8 diff --git a/cubiomes-viewer.pro b/cubiomes-viewer.pro index 1d2f6e7..9e8b305 100644 --- a/cubiomes-viewer.pro +++ b/cubiomes-viewer.pro @@ -13,7 +13,7 @@ QT += core widgets CHARSET = -finput-charset=UTF-8 -fexec-charset=UTF-8 QMAKE_CFLAGS = $$CHARSET -fwrapv -DSTRUCT_CONFIG_OVERRIDE=1 QMAKE_CXXFLAGS = $$QMAKE_CFLAGS -QMAKE_CXXFLAGS_RELEASE *= -O3 +QMAKE_CXXFLAGS_RELEASE *= -O3 -g3 greaterThan(QT_MAJOR_VERSION, 5) { QMAKE_CXXFLAGS += -std=gnu++17 diff --git a/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml b/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml index 4d284b3..01aeace 100644 --- a/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml +++ b/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml @@ -60,6 +60,11 @@ + + 900 + 1600 + + https://github.com/cubitect/cubiomes-viewer https://github.com/cubitect/cubiomes-viewer/issues diff --git a/rc/icons.qrc b/rc/icons.qrc index 07b0ae1..91beae3 100644 --- a/rc/icons.qrc +++ b/rc/icons.qrc @@ -83,5 +83,7 @@ icons/zoom_in.png icons/zoom_out.png icons/zoom.png + icons/chambers.png + icons/chambers_d.png diff --git a/rc/icons/chambers.png b/rc/icons/chambers.png new file mode 100644 index 0000000..880d34b Binary files /dev/null and b/rc/icons/chambers.png differ diff --git a/rc/icons/chambers_d.png b/rc/icons/chambers_d.png new file mode 100644 index 0000000..880d34b Binary files /dev/null and b/rc/icons/chambers_d.png differ diff --git a/src/conditiondialog.cpp b/src/conditiondialog.cpp index ad5e7d1..27b504a 100644 --- a/src/conditiondialog.cpp +++ b/src/conditiondialog.cpp @@ -107,13 +107,17 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi ui->lineBiomeSize->setValidator(new QIntValidator(1, INT_MAX, this)); ui->lineTolerance->setValidator(new QIntValidator(0, 255, this)); - ui->comboY->lineEdit()->setValidator(new QIntValidator(-64, 320, this)); + initComboY(ui->comboY1, initcond ? initcond->y : 256); + initComboY(ui->comboY2, initcond ? initcond->y : 256); - ui->lineMin->setValidator(new QDoubleValidator(-1e6, 1e6, 4, this)); - ui->lineMax->setValidator(new QDoubleValidator(-1e6, 1e6, 4, this)); + QDoubleValidator *paraval = new QDoubleValidator(-1e6, 1e6, 4, this); + ui->lineMin->setValidator(paraval); + ui->lineMax->setValidator(paraval); - ui->lineCoverage->setValidator(new QDoubleValidator(0.0001, 100.0000, 4, this)); - ui->lineConfidence->setValidator(new QDoubleValidator(0.0001, 99.9999, 4, this)); + ui->lineCoverage2->setValidator(new QDoubleValidator(0.0001, 100.0000, 4, this)); + ui->lineCoverage1->setValidator(new QDoubleValidator(0.0001, 100.0000, 4, this)); + ui->lineConfidence1->setValidator(new QDoubleValidator(0.0001, 99.9999, 4, this)); + ui->lineConfidence2->setValidator(new QDoubleValidator(0.0001, 99.9999, 4, this)); //qobject_cast(ui->comboCat->view())->setSpacing(1); //qobject_cast(ui->comboType->view())->setSpacing(1); @@ -301,8 +305,10 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi ui->checkSkipRef->setChecked(false); ui->radioSquare->setChecked(true); ui->checkRadius->setChecked(false); - ui->lineCoverage->setText("50"); - ui->lineConfidence->setText("95"); + ui->lineCoverage1->setText("50"); + ui->lineCoverage2->setText("50"); + ui->lineConfidence1->setText("95"); + ui->lineConfidence2->setText("95"); ui->comboScale->setCurrentIndex(1); onCheckStartChanged(false); on_comboClimatePara_currentIndexChanged(0); @@ -341,8 +347,11 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi on_comboClimatePara_currentIndexChanged(cond.para); ui->comboOctaves->setCurrentIndex(cond.octave); ui->comboMinMax->setCurrentIndex((cond.minmax & Condition::E_LOCATE_MAX) ? 1 : 0); - ui->lineMin->setText((cond.minmax & Condition::E_TEST_LOWER) ? QString::number(cond.vmin) : ""); - ui->lineMax->setText((cond.minmax & Condition::E_TEST_UPPER) ? QString::number(cond.vmax) : ""); + QString vmin, vmax; + if (cond.minmax & Condition::E_TEST_LOWER) vmin = QString::number(cond.vmin); + if (cond.minmax & Condition::E_TEST_UPPER) vmax = QString::number(cond.vmax); + ui->lineMin->setText(vmin); + ui->lineMax->setText(vmax); ui->checkInvertRange->setChecked(cond.flags & Condition::FLG_INVERT); updateMode(); @@ -357,19 +366,10 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi ui->checkApprox->setChecked(cond.flags & Condition::FLG_APPROX); ui->checkMatchAny->setChecked(cond.flags & Condition::FLG_MATCH_ANY); ui->checkSamplePos->setChecked(cond.count == 1); - ui->lineCoverage->setText(QString::number(cond.converage ? cond.converage * 100 : 50)); - ui->lineConfidence->setText(QString::number(cond.confidence ? cond.confidence * 100 : 95)); - - int i, n = ui->comboY->count(); - for (i = 0; i < n; i++) - if (ui->comboY->itemText(i).section(' ', 0, 0).toInt() == cond.y) - break; - if (i >= n) - { - ui->comboY->addItem(QString::number(cond.y)); - ui->comboY2->addItem(QString::number(cond.y)); - } - ui->comboY->setCurrentIndex(i); + ui->lineCoverage1->setText(QString::number(cond.converage ? cond.converage * 100 : 50)); + ui->lineCoverage2->setText(QString::number(cond.converage ? cond.converage * 100 : 50)); + ui->lineConfidence1->setText(QString::number(cond.confidence ? cond.confidence * 100 : 95)); + ui->lineConfidence2->setText(QString::number(cond.confidence ? cond.confidence * 100 : 95)); if (cond.x1 == cond.z1 && cond.x1 == -cond.x2 && cond.x1 == -cond.z2) { @@ -486,6 +486,19 @@ void ConditionDialog::addTempCat(int temp, QString name) } } +void ConditionDialog::initComboY(QComboBox *cb, int y) +{ + if (!cb->lineEdit()->validator()) + cb->lineEdit()->setValidator(new QIntValidator(-64, 320, this)); + int i, n = cb->count(); + for (i = 0; i < n; i++) + if (cb->itemText(i).section(' ', 0, 0).toInt() == y) + break; + if (i >= n) + cb->addItem(QString::number(y)); + cb->setCurrentIndex(i); +} + void ConditionDialog::updateMode() { int filterindex = ui->comboType->currentData().toInt(); @@ -566,7 +579,8 @@ void ConditionDialog::updateMode() ui->checkSkipRef->setEnabled(cnt); ui->labelY->setEnabled(ft.hasy); - ui->comboY->setEnabled(ft.hasy); + ui->comboY1->setEnabled(ft.hasy); + ui->comboY2->setEnabled(ft.hasy); if (filterindex == F_TEMPS) { @@ -578,7 +592,15 @@ void ConditionDialog::updateMode() } else if (filterindex == F_CLIMATE_MINMAX) { - ui->stackedWidget->setCurrentWidget(ui->pageMinMax); + ui->stackedWidget->setCurrentWidget(ui->pageNoise); + ui->stackedNoise->setCurrentWidget(ui->pageNoiseMinMax); + ui->groupBoxNoise->setTitle("Locate climate minimum/maximum"); + } + else if (filterindex == F_NOISE_SAMPLE) + { + ui->stackedWidget->setCurrentWidget(ui->pageNoise); + ui->stackedNoise->setCurrentWidget(ui->pageNoiseSample); + ui->groupBoxNoise->setTitle("Climate noise samples"); } else if (filterindex == F_BIOME_CENTER || filterindex == F_BIOME_CENTER_256) { @@ -1035,7 +1057,7 @@ void ConditionDialog::onAccept() else c.rmax = 0; - c.y = ui->comboY->currentText().section(' ', 0, 0).toInt(); + c.y = ui->comboY1->currentText().section(' ', 0, 0).toInt(); c.flags = 0; if (ui->checkApprox->isChecked()) @@ -1071,8 +1093,8 @@ void ConditionDialog::onAccept() } } c.count = ui->checkSamplePos->isChecked() ? 1 : 0; - c.converage = ui->lineCoverage->text().toFloat() / 100.0; - c.confidence = ui->lineConfidence->text().toFloat() / 100.0; + c.converage = ui->lineCoverage1->text().toFloat() / 100.0; + c.confidence = ui->lineConfidence1->text().toFloat() / 100.0; c.step = 0; if (c.type == F_BIOME || c.type == F_BIOME_NETHER || c.type == F_BIOME_END) c.step = ui->comboScale->currentData().toInt(); @@ -1082,15 +1104,15 @@ void ConditionDialog::onAccept() if (ui->stackedWidget->currentWidget() == ui->pageBiomeCenter) { c.biomeId = ui->comboMatchBiome->currentData().toInt(); + c.y = ui->comboY2->currentText().section(' ', 0, 0).toInt(); c.biomeSize = ui->lineBiomeSize->text().toInt(); c.tol = ui->lineTolerance->text().toInt(); } - if (ui->stackedWidget->currentWidget() == ui->pageMinMax) + if (ui->stackedWidget->currentWidget() == ui->pageNoise) { c.para = ui->comboClimatePara->currentData().toInt(); c.octave = ui->comboOctaves->currentIndex(); - c.minmax = ui->comboMinMax->currentIndex() == 0 ? - Condition::E_LOCATE_MIN : Condition::E_LOCATE_MAX; + c.minmax = ui->comboMinMax->currentIndex() == 0 ? Condition::E_LOCATE_MIN : Condition::E_LOCATE_MAX; bool ok1, ok2; c.vmin = ui->lineMin->text().toFloat(&ok1); if (ok1) c.minmax |= Condition::E_TEST_LOWER; @@ -1098,6 +1120,9 @@ void ConditionDialog::onAccept() if (ok2) c.minmax |= Condition::E_TEST_UPPER; if (ok1 && ok2 && c.vmin > c.vmax) std::swap(c.vmin, c.vmax); + c.count = ui->checkSamplePos2->isChecked() ? 1 : 0; + c.converage = ui->lineCoverage2->text().toFloat() / 100.0; + c.confidence = ui->lineConfidence2->text().toFloat() / 100.0; } if (ui->stackedWidget->currentWidget() == ui->pageTemps) { @@ -1653,13 +1678,13 @@ void ConditionDialog::on_comboOctaves_currentIndexChanged(int) ui->comboOctaves->setToolTip(ui->comboOctaves->currentData(Qt::ToolTipRole).toString()); } -void ConditionDialog::on_comboY_currentTextChanged(const QString &text) +void ConditionDialog::on_comboY1_currentTextChanged(const QString &text) { if (ui->comboY2->currentText() != text) ui->comboY2->setCurrentText(text); } void ConditionDialog::on_comboY2_currentTextChanged(const QString &text) { - if (ui->comboY->currentText() != text) - ui->comboY->setCurrentText(text); + if (ui->comboY1->currentText() != text) + ui->comboY1->setCurrentText(text); } diff --git a/src/conditiondialog.h b/src/conditiondialog.h index 470c88b..5ba310c 100644 --- a/src/conditiondialog.h +++ b/src/conditiondialog.h @@ -56,6 +56,7 @@ class ConditionDialog : public QDialog virtual ~ConditionDialog(); void addTempCat(int temp, QString name); + void initComboY(QComboBox *cb, int y); void updateMode(); void updateBiomeSelection(); bool warnIfBad(Condition cond); @@ -112,7 +113,7 @@ private slots: void on_comboClimatePara_currentIndexChanged(int index); void on_comboOctaves_currentIndexChanged(int index); - void on_comboY_currentTextChanged(const QString &text); + void on_comboY1_currentTextChanged(const QString &text); void on_comboY2_currentTextChanged(const QString &text); private: diff --git a/src/conditiondialog.ui b/src/conditiondialog.ui index e8ca63f..f8335e7 100644 --- a/src/conditiondialog.ui +++ b/src/conditiondialog.ui @@ -392,7 +392,7 @@ QPushButton:hover { 0 - 2 + 5 @@ -631,14 +631,14 @@ yield each sampled position individually - + Proportion of the area that has to be of the included biomes - + Statistical confidence that the coverage has been reached @@ -649,7 +649,7 @@ yield each sampled position individually - + true @@ -1100,7 +1100,7 @@ yield each sampled position individually - + 0 @@ -1115,55 +1115,203 @@ yield each sampled position individually 0 - + Locate climate minimum/maximum - - - - <html><head/><body><p>Lower bound (inclusive)</p></body></html> - - - -Infinity + + + + + + + 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + Minimum + + + + + Maximum + + + + + + + + Yield position of: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 180 + 20 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Instead of estimating the center of the allowed biome area, +yield each sampled position individually + + + Yield individual samples + + + + + + + + + + Statistical confidence that the coverage has been reached + + + Confidence (%): + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Statistical confidence that the coverage has been reached + + + + + + + Required coverage (%): + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 180 + 20 + + + + + + - - + + - Generate up to octave: + If value is in range: - - - - Qt::Vertical - - - - 20 - 252 - + + + + - - + - - - - Only match if value is outside the given range - - - Invert range + + + + + Monospace + - - + + - Yield position of: + Generate up to octave: @@ -1184,45 +1332,41 @@ yield each sampled position individually - - - - If value is in range: + + + + <html><head/><body><p>Lower bound (inclusive)</p></body></html> + + + -Infinity - - - - - Minimum - - - - - Maximum - - - - - - - - - + + + + Only match if value is outside the given range + - - + Invert range - - - - - Monospace - + + + + Qt::Horizontal - + + QSizePolicy::Preferred + + + + 180 + 20 + + + diff --git a/src/config.cpp b/src/config.cpp index 66e6065..f695c18 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -148,7 +148,7 @@ QString mapopt2display(int opt) case D_VILLAGE: return QApplication::translate("Map", "Village"); case D_MANSION: return QApplication::translate("Map", "Woodland Mansion"); case D_MONUMENT: return QApplication::translate("Map", "Ocean Monument"); - case D_RUINS: return QApplication::translate("Map", "Ocean Ruins"); + case D_OCEANRUIN: return QApplication::translate("Map", "Ocean Ruin"); case D_SHIPWRECK: return QApplication::translate("Map", "Shipwreck"); case D_TREASURE: return QApplication::translate("Map", "Buried Treasure"); case D_MINESHAFT: return QApplication::translate("Map", "Mineshaft"); @@ -156,7 +156,8 @@ QString mapopt2display(int opt) case D_GEODE: return QApplication::translate("Map", "Geode"); case D_OUTPOST: return QApplication::translate("Map", "Pillager Outpost"); case D_ANCIENTCITY: return QApplication::translate("Map", "Ancient City"); - case D_TRAILS: return QApplication::translate("Map", "Trail Ruins"); + case D_TRAILRUINS: return QApplication::translate("Map", "Trail Ruins"); + case D_CHAMBERS: return QApplication::translate("Map", "Trial Chambers"); case D_PORTAL: return QApplication::translate("Map", "Ruined Portal"); case D_PORTALN: return QApplication::translate("Map", "Ruined Portal (Nether)"); case D_SPAWN: return QApplication::translate("Map", "Spawn"); @@ -182,7 +183,7 @@ const char *mapopt2str(int opt) // to resource string case D_VILLAGE: return "village"; case D_MANSION: return "mansion"; case D_MONUMENT: return "monument"; - case D_RUINS: return "ruins"; + case D_OCEANRUIN: return "ruins"; case D_SHIPWRECK: return "shipwreck"; case D_TREASURE: return "treasure"; case D_MINESHAFT: return "mineshaft"; @@ -190,7 +191,8 @@ const char *mapopt2str(int opt) // to resource string case D_GEODE: return "geode"; case D_OUTPOST: return "outpost"; case D_ANCIENTCITY: return "ancient_city"; - case D_TRAILS: return "trails"; + case D_TRAILRUINS: return "trails"; + case D_CHAMBERS: return "chambers"; case D_PORTAL: return "portal"; case D_PORTALN: return "portaln"; case D_SPAWN: return "spawn"; @@ -214,7 +216,7 @@ int str2mapopt(const char *s) // from resource string if (!strcmp(s, "village")) return D_VILLAGE; if (!strcmp(s, "mansion")) return D_MANSION; if (!strcmp(s, "monument")) return D_MONUMENT; - if (!strcmp(s, "ruins")) return D_RUINS; + if (!strcmp(s, "ruins")) return D_OCEANRUIN; if (!strcmp(s, "shipwreck")) return D_SHIPWRECK; if (!strcmp(s, "treasure")) return D_TREASURE; if (!strcmp(s, "mineshaft")) return D_MINESHAFT; @@ -222,7 +224,8 @@ int str2mapopt(const char *s) // from resource string if (!strcmp(s, "geode")) return D_GEODE; if (!strcmp(s, "outpost")) return D_OUTPOST; if (!strcmp(s, "ancient_city")) return D_ANCIENTCITY; - if (!strcmp(s, "trails")) return D_TRAILS; + if (!strcmp(s, "trails")) return D_TRAILRUINS; + if (!strcmp(s, "chambers")) return D_CHAMBERS; if (!strcmp(s, "portal")) return D_PORTAL; if (!strcmp(s, "portaln")) return D_PORTALN; if (!strcmp(s, "spawn")) return D_SPAWN; @@ -245,7 +248,7 @@ int mapopt2stype(int opt) case D_VILLAGE: return Village; case D_MANSION: return Mansion; case D_MONUMENT: return Monument; - case D_RUINS: return Ocean_Ruin; + case D_OCEANRUIN: return Ocean_Ruin; case D_SHIPWRECK: return Shipwreck; case D_TREASURE: return Treasure; case D_MINESHAFT: return Mineshaft; @@ -253,7 +256,8 @@ int mapopt2stype(int opt) case D_GEODE: return Geode; case D_OUTPOST: return Outpost; case D_ANCIENTCITY: return Ancient_City; - case D_TRAILS: return Trail_Ruin; + case D_TRAILRUINS: return Trail_Ruins; + case D_CHAMBERS: return Trial_Chambers; case D_PORTAL: return Ruined_Portal; case D_PORTALN: return Ruined_Portal_N; case D_FORTESS: return Fortress; diff --git a/src/config.h b/src/config.h index a0c322e..4909816 100644 --- a/src/config.h +++ b/src/config.h @@ -109,7 +109,7 @@ enum { D_VILLAGE, D_MANSION, D_MONUMENT, - D_RUINS, + D_OCEANRUIN, D_SHIPWRECK, D_TREASURE, D_MINESHAFT, @@ -117,7 +117,8 @@ enum { D_GEODE, D_OUTPOST, D_ANCIENTCITY, - D_TRAILS, + D_TRAILRUINS, + D_CHAMBERS, D_PORTAL, D_PORTALN, D_FORTESS, diff --git a/src/formsearchcontrol.cpp b/src/formsearchcontrol.cpp index bcd1ebd..6e32bae 100644 --- a/src/formsearchcontrol.cpp +++ b/src/formsearchcontrol.cpp @@ -155,7 +155,7 @@ FormSearchControl::FormSearchControl(MainWindow *parent) FormSearchControl::~FormSearchControl() { stimer.stop(); - sthread.stop(); // tell search to stop at next convenience + sthread.stopSearch(); delete ui; } @@ -227,9 +227,7 @@ bool FormSearchControl::setSearchConfig(SearchConfig s, bool quiet) void FormSearchControl::stopSearch() { - sthread.stop(); - //sthread.quit(); // tell the event loop to exit - //sthread.wait(); // wait for search to finish + sthread.stopSearch(); onBufferTimeout(); } @@ -394,7 +392,7 @@ void FormSearchControl::on_buttonStart_clicked() searchLockUi(true); nextupdate = 0; updt = 20; - sthread.start(); + sthread.startSearch(); elapsed.start(); stimer.start(250); } @@ -635,12 +633,12 @@ int FormSearchControl::searchResultsAdd(std::vector seeds, bool counto if (n >= config.maxMatching) { - sthread.stop(); + sthread.stopSearch(); discarded = true; } if (n + (ssize_t)seeds.size() > config.maxMatching) { - sthread.stop(); + sthread.stopSearch(); discarded = true; seeds.resize(config.maxMatching - n); } @@ -671,7 +669,7 @@ int FormSearchControl::searchResultsAdd(std::vector seeds, bool counto int addcnt = n - nold; if (ui->checkStop->isChecked() && addcnt) - sthread.stop(); + sthread.stopSearch(); if (!countonly && discarded) { diff --git a/src/headless.cpp b/src/headless.cpp index 0f251a7..914c727 100644 --- a/src/headless.cpp +++ b/src/headless.cpp @@ -149,7 +149,7 @@ void Headless::run() session.writeHeader(resultstream); resultstream.flush(); - sthread.start(); + sthread.startSearch(); elapsed.start(); if (resultfile.isOpen()) diff --git a/src/layerdialog.cpp b/src/layerdialog.cpp index df01123..e507256 100644 --- a/src/layerdialog.cpp +++ b/src/layerdialog.cpp @@ -73,7 +73,7 @@ bool getLayerOptionInfo(LayerOptInfo *info, int mode, int disp, WorldInfo wi) if (disp == 0) { - txt += QString("%1.. x%2").arg(QChar(0x03A3)).arg(amptot, 0, 'f', 6); + txt += QString("%1.. (all) x%2").arg(QChar(0x03A3)).arg(amptot, 0, 'f', 6); tip += QApplication::translate("LayerDialog", "All octaves"); } else diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e517cd5..cfcdf51 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -174,7 +174,7 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent addMapAction(D_MONUMENT); addMapAction(D_IGLOO); addMapAction(D_MANSION); - addMapAction(D_RUINS); + addMapAction(D_OCEANRUIN); addMapAction(D_SHIPWRECK); addMapAction(D_TREASURE); addMapAction(D_WELL); @@ -182,7 +182,8 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent addMapAction(D_OUTPOST); addMapAction(D_PORTAL); addMapAction(D_ANCIENTCITY); - addMapAction(D_TRAILS); + addMapAction(D_TRAILRUINS); + addMapAction(D_CHAMBERS); ui->toolBar->addSeparator(); addMapAction(D_FORTESS); addMapAction(D_BASTION); diff --git a/src/mapview.cpp b/src/mapview.cpp index a26a39b..5ac7bed 100644 --- a/src/mapview.cpp +++ b/src/mapview.cpp @@ -381,6 +381,7 @@ void MapView::showContextMenu(const QPoint &pos) std::vector<_cpy_dat> cpy_dat; VarPos vp = getActivePos(); + const QPixmap *icon = 0; if (vp.type != -1) { // structure has a known size / location @@ -389,7 +390,7 @@ void MapView::showContextMenu(const QPoint &pos) { const Piece& pc = vp.pieces[0]; midx = (pc.bb0.x + pc.bb1.x) >> 1; - midy = (pc.bb0.y); + midy = (pc.bb0.y) + 1; midz = (pc.bb0.z + pc.bb1.z) >> 1; } else @@ -405,6 +406,7 @@ void MapView::showContextMenu(const QPoint &pos) } } cpy_dat.push_back({ tr("Copy tp:"), QString::asprintf("/tp @p %d %d %d", midx, midy, midz) }); + icon = &getMapIcon(world ? world->selopt : -1, &vp); } cpy_dat.push_back({ tr("Copy tp:"), QString::asprintf("/tp @p %d ~ %d", vp.p.x, vp.p.z) }); cpy_dat.push_back({ tr("Copy coords:"), QString::asprintf("%d %d", vp.p.x, vp.p.z) }); @@ -434,7 +436,10 @@ void MapView::showContextMenu(const QPoint &pos) while (txtWidth(menu->fontMetrics(), txt + " ") < wmax) txt += " "; txt += it.cpy; - menu->addAction(txt, [=](){ this->copyText(it.cpy); }); + if (icon) + menu->addAction(*icon, txt, [=](){ this->copyText(it.cpy); }); + else + menu->addAction(txt, [=](){ this->copyText(it.cpy); }); } //menu->addAction(tr("Animation"), this, &MapView::runAni); diff --git a/src/search.cpp b/src/search.cpp index c3c5acb..0f830fe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -249,7 +249,15 @@ QString ConditionTree::set(const std::vector& cv, int mc) } SearchThreadEnv::SearchThreadEnv() - : condtree(),mc(),large(),seed(),surfdim(DIM_UNDEF),octaves(),l_states() +: condtree() +, mc() +, large() +, seed() +, surfdim(DIM_UNDEF) +, octaves() +, searchpass(PASS_FAST_48) +, stop() +, l_states() { memset(&g, 0, sizeof(g)); memset(&sn, 0, sizeof(sn)); @@ -323,7 +331,7 @@ void SearchThreadEnv::init4Noise(int nptype, int octaves) { if (octaves <= 0) octaves = INT_MAX; - if (g.bn.nptype == nptype && this->octaves >= octaves) + if (g.bn.nptype == nptype && this->octaves == octaves) return; // already initialized for parameter if (seed == g.seed && g.bn.nptype == -1) return; // fully initialized biome noise @@ -340,12 +348,22 @@ void SearchThreadEnv::prepareSurfaceNoise(int dim) } } +// The position buffers can exceed the stacksize on some platforms, +// and dynamic heap allocation is too slow. So instead, we assign a +// static memory region on the heap. +static Pos* getPosBuf(uint32_t node) +{ +#if DEBUG + if (node >= 100) return NULL; // fatal +#endif + thread_local std::vector buf(100 * MAX_INSTANCES); + return &buf[node * MAX_INSTANCES]; +} + static -int testTreeAt( +int _testTreeAt( Pos at, // relative origin SearchThreadEnv * env, // thread-local environment - int pass, // search pass - std::atomic_bool * abort, // abort signal Pos * path, // output center position(s) int node ) @@ -356,8 +374,7 @@ int testTreeAt( int st, br; int rx1, rz1, rx2, rz2; Pos pos; - - std::vector inst(MAX_INSTANCES); + Pos *inst = getPosBuf(node); switch (c.type) { @@ -424,8 +441,8 @@ int testTreeAt( int sta = COND_OK; for (int b : branches) { - int stb = testTreeAt(pos, env, pass, abort, path, b); - if (*abort) + int stb = _testTreeAt(pos, env, path, b); + if (*env->stop) return COND_FAILED; if (stb < sta) sta = stb; @@ -471,8 +488,8 @@ int testTreeAt( st = COND_OK; for (int b : branches) { - int sta = testTreeAt(pos, env, pass, abort, path, b); - if (*abort) + int sta = _testTreeAt(pos, env, path, b); + if (*env->stop) return COND_FAILED; if (sta < st) st = sta; @@ -497,8 +514,8 @@ int testTreeAt( st = COND_FAILED; for (int b : branches) { - int sta = testTreeAt(at, env, pass, abort, path, b); - if (*abort) + int sta = _testTreeAt(at, env, path, b); + if (*env->stop) return COND_FAILED; if (sta > st) st = sta; @@ -528,8 +545,8 @@ int testTreeAt( st = COND_OK; for (int b : branches) { - int sta = testTreeAt(at, env, pass, abort, path, b); - if (*abort) + int sta = _testTreeAt(at, env, path, b); + if (*env->stop) return COND_FAILED; if (sta == COND_OK) { st = COND_FAILED; break; } else if (sta == COND_FAILED) { st = COND_OK; break; } @@ -544,8 +561,8 @@ int testTreeAt( Pos *buf = path ? path : &inst[0]; for (int b : branches) { - int sta = testTreeAt(at, env, pass, abort, buf, b); - if (*abort) + int sta = _testTreeAt(at, env, buf, b); + if (*env->stop) return COND_FAILED; if (sta < st) { st = sta; @@ -555,8 +572,8 @@ int testTreeAt( } if (st <= COND_MAYBE_POS_INVAL) return st; - int sta = runCheckScript(L, at, env, pass, buf, &c); - if (*abort) + int sta = runCheckScript(L, at, env, env->searchpass, buf, &c); + if (*env->stop) return COND_FAILED; if (sta < st) st = sta; @@ -568,7 +585,7 @@ int testTreeAt( if (branches.empty()) { // this is a leaf node => check only for presence of instances int icnt = c.count; - st = testCondAt(at, env, pass, abort, &inst[0], &icnt, &c); + st = testCondAt(at, env, &inst[0], &icnt, &c); if (path && st >= COND_MAYBE_POS_VALID) { if (icnt == 1) @@ -593,7 +610,7 @@ int testTreeAt( } else { - st = testCondAt(at, env, pass, abort, &inst[0], NULL, &c); + st = testCondAt(at, env, &inst[0], NULL, &c); if (st == COND_FAILED || st == COND_MAYBE_POS_INVAL) return st; pos = inst[0]; // center point of instances @@ -602,8 +619,8 @@ int testTreeAt( { if (st == COND_FAILED) break; - int sta = testTreeAt(pos, env, pass, abort, path, b); - if (*abort) + int sta = _testTreeAt(pos, env, path, b); + if (*env->stop) return COND_FAILED; if (sta < st) st = sta; @@ -616,7 +633,7 @@ int testTreeAt( { // check each instance individually, splitting the instances into // independent subbranches that are combined via OR int icnt = MAX_INSTANCES; - st = testCondAt(at, env, pass, abort, &inst[0], &icnt, &c); + st = testCondAt(at, env, &inst[0], &icnt, &c); if (st == COND_FAILED || st == COND_MAYBE_POS_INVAL) return st; int sta = COND_FAILED; @@ -627,8 +644,8 @@ int testTreeAt( pos = inst[i]; for (int b : branches) // AND dependent conditions { - int stc = testTreeAt(pos, env, pass, abort, path, b); - if (*abort) + int stc = _testTreeAt(pos, env, path, b); + if (*env->stop) return COND_FAILED; // worst branch dictates status for instance if (stc < stb) @@ -659,17 +676,18 @@ int testTreeAt( Pos at, // relative origin SearchThreadEnv * env, // thread-local environment int pass, // search pass - std::atomic_bool * abort, // abort signal Pos * path // ok trigger positions ) { if (pass != PASS_FAST_48) { // do a fast check before continuing with slower checks - int st = testTreeAt(at, env, PASS_FAST_48, abort, NULL, 0); + env->searchpass = PASS_FAST_48; + int st = _testTreeAt(at, env, NULL, 0); if (st == COND_FAILED) return st; } - return testTreeAt(at, env, pass, abort, path, 0); + env->searchpass = pass; + return _testTreeAt(at, env, path, 0); } @@ -1007,6 +1025,48 @@ static int f_biome_sampler(Generator *g, int scale, int x, int y, int z, void *d return 0; } +static int f_noise_sampler(Generator *g, int scale, int x, int y, int z, void *data) +{ + (void) y; + sample_boime_t *info = (sample_boime_t*) data; + if (info->stop && *info->stop) + return -2; + if (info->rmaxsq) + { + int dx = (x * scale) - info->at.x; + int dz = (z * scale) - info->at.z; + int64_t rsq = dx*(int64_t)dx + dz*(int64_t)dz; + if (rsq >= info->rmaxsq) + return -1; + } + + const Condition *cond = info->cond; + double v = sampleDoublePerlin(&g->bn.climate[cond->para], x, 0, z); + double vmin = cond->minmax & Condition::E_TEST_LOWER ? cond->vmin : -INFINITY; + double vmax = cond->minmax & Condition::E_TEST_UPPER ? cond->vmax : +INFINITY; + + v *= 10000; + + bool ok = (v > vmin && v < vmax); + if (cond->flags & Condition::FLG_INVERT) + ok = !ok; + + if (ok) + { + x *= scale; + z *= scale; + if (info->imax && info->n < MAX_INSTANCES) + info->cent[info->n] = Pos{x, z}; + info->xsum += x; + info->zsum += z; + info->n++; + return 1; + } + return 0; +} + + + /* Tests if a condition is satisfied with 'at' as origin for a search pass. * If sufficiently satisfied (check return value) then: * when 'imax' is NULL, the center position is written to 'cent[0]' @@ -1018,8 +1078,6 @@ int testCondAt( Pos at, // relative origin SearchThreadEnv * env, // thread-local environment - int pass, // search pass - std::atomic_bool * abort, // abort signal Pos * cent, // output center position(s) int * imax, // max instances (NULL for avg) const Condition * cond // condition to check @@ -1035,7 +1093,7 @@ testCondAt( int i, n, icnt; int64_t s, r, rmin, rmax; const uint64_t *seeds; - std::vector p(MAX_INSTANCES); + Pos *p = getPosBuf(0); const FilterInfo& finfo = g_filterinfo.list[cond->type]; @@ -1251,7 +1309,7 @@ testCondAt( icnt = 0; // Note "<=" - for (rz = rz1; rz <= rz2 && !*abort; rz++) + for (rz = rz1; rz <= rz2 && !*env->stop; rz++) { for (rx = rx1; rx <= rx2; rx++) { @@ -1271,9 +1329,10 @@ testCondAt( { continue; } - if (pass == PASS_FULL_64 || (pass == PASS_FULL_48 && !finfo.dep64)) + if ((env->searchpass == PASS_FULL_64) || + (env->searchpass == PASS_FULL_48 && !finfo.dep64)) { - if (*abort) return COND_FAILED; + if (*env->stop) return COND_FAILED; if (st == Village && cond->varflags) { // we can test for abandoned villages before the @@ -1344,9 +1403,9 @@ testCondAt( return COND_OK; else { - if (pass == PASS_FULL_64) + if (env->searchpass == PASS_FULL_64) return COND_FAILED; - if (pass == PASS_FULL_48 && !finfo.dep64) + if (env->searchpass == PASS_FULL_48 && !finfo.dep64) return COND_FAILED; return COND_MAYBE_POS_VALID; } @@ -1363,9 +1422,9 @@ testCondAt( cent->z = zt / icnt; } - if (pass == PASS_FULL_64) + if (env->searchpass == PASS_FULL_64) return COND_OK; - if (pass == PASS_FULL_48 && !finfo.dep64) + if (env->searchpass == PASS_FULL_48 && !finfo.dep64) return COND_OK; // some non-exhaustive structure clusters do not // have known center positions with 48-bit seeds @@ -1460,10 +1519,10 @@ testCondAt( case F_SPAWN: cent->x = cent->z = 0; - if (pass != PASS_FULL_64) + if (env->searchpass != PASS_FULL_64) return COND_MAYBE_POS_INVAL; - if (*abort) return COND_FAILED; + if (*env->stop) return COND_FAILED; env->init4Dim(DIM_OVERWORLD); pc = getSpawn(&env->g); if (rmax) @@ -1584,7 +1643,7 @@ testCondAt( return cond->count == 0 ? COND_OK : COND_FAILED; // pre-biome-checks complete, the area appears to line up with possible generation positions - if (pass != PASS_FULL_64) + if (env->searchpass != PASS_FULL_64) { return COND_MAYBE_POS_INVAL; } @@ -1602,7 +1661,7 @@ testCondAt( env->init4Dim(DIM_OVERWORLD); while (nextStronghold(&sh, &env->g) > 0) { - if (*abort) + if (*env->stop) break; bool inside; if (rmax) @@ -1722,13 +1781,16 @@ testCondAt( case F_BIOME_SAMPLE: + case F_NOISE_SAMPLE: - if (pass != PASS_FULL_64) + if (env->searchpass != PASS_FULL_64) return COND_MAYBE_POS_INVAL; if (cond->confidence <= 0 || cond->confidence >= 1) return COND_FAILED; if (cond->converage <= 0 || cond->converage > 1) return COND_FAILED; + if (cond->type == F_NOISE_SAMPLE && env->mc <= MC_1_17) + return COND_FAILED; s = 2; rx1 = x1 >> s; @@ -1739,7 +1801,6 @@ testCondAt( int w = rx2 - rx1 + 1; int h = rz2 - rz1 + 1; Range r = {1<y : cond->y >> 2, 1}; - env->init4Dim(DIM_OVERWORLD); sample_boime_t sample; sample.cond = cond; sample.at = at; @@ -1749,14 +1810,24 @@ testCondAt( sample.zsum = 0; sample.imax = imax; sample.cent = cent; - sample.stop = abort; + sample.stop = env->stop; uint64_t rng; setSeed(&rng, env->seed); - int ok = monteCarloBiomes( - &env->g, r, &rng, cond->converage, cond->confidence, - &f_biome_sampler, &sample); + int (*f)(Generator *, int, int, int, int, void *); + + if (cond->type == F_NOISE_SAMPLE) + { + env->init4Noise(cond->para, cond->octave); + f = f_noise_sampler; + } + else + { + env->init4Dim(DIM_OVERWORLD); + f = f_biome_sampler; + } + int ok = monteCarloBiomes(&env->g, r, &rng, cond->converage, cond->confidence, f, &sample); if (imax && cond->count == 1) { *imax = sample.n; @@ -1780,7 +1851,7 @@ testCondAt( case F_BIOME_4_RIVER: case F_BIOME_256_OTEMP: - if (env->mc >= MC_1_18) + if (env->mc > MC_1_17) return COND_FAILED; s = cond->type == F_BIOME_4_RIVER ? 2 : 8; @@ -1791,15 +1862,15 @@ testCondAt( cent->x = (x1 + x2) >> 1; cent->z = (z1 + z2) >> 1; if (imax) *imax = 1; - if (pass == PASS_FAST_48) + if (env->searchpass == PASS_FAST_48) return COND_MAYBE_POS_VALID; - if (pass == PASS_FULL_48) + if (env->searchpass == PASS_FULL_48) { if (env->mc < MC_1_13 || cond->type != F_BIOME_256_OTEMP) return COND_MAYBE_POS_VALID; } valid = COND_FAILED; - if (rx2 >= rx1 || rz2 >= rz1 || !*abort) + if (rx2 >= rx1 || rz2 >= rz1 || !*env->stop) { int w = rx2-rx1+1; int h = rz2-rz1+1; @@ -1819,7 +1890,7 @@ testCondAt( case F_TEMPS: - if (env->mc >= MC_1_18) + if (env->mc > MC_1_17) return COND_FAILED; rx1 = x1 >> 10; rz1 = z1 >> 10; @@ -1828,7 +1899,7 @@ testCondAt( cent->x = (x1 + x2) >> 1; cent->z = (z1 + z2) >> 1; if (imax) *imax = 1; - if (pass != PASS_FULL_64) + if (env->searchpass != PASS_FULL_64) return COND_MAYBE_POS_VALID; env->init4Dim(DIM_OVERWORLD); if (checkForTemps(&env->g.ls, env->seed, rx1, rz1, rx2-rx1+1, rz2-rz1+1, cond->temps)) @@ -1857,11 +1928,11 @@ testCondAt( cent->x = (x1 + x2) >> 1; cent->z = (z1 + z2) >> 1; if (imax) *imax = 1; - if (pass == PASS_FAST_48) + if (env->searchpass == PASS_FAST_48) return COND_MAYBE_POS_VALID; // the Nether and End require only the 48-bit seed // (except voronoi uses the full 64-bits) - if (pass != PASS_FULL_64 && (finfo.dep64 || s == 0)) + if (env->searchpass != PASS_FULL_64 && (finfo.dep64 || s == 0)) return COND_MAYBE_POS_VALID; else { @@ -1870,14 +1941,14 @@ testCondAt( int y = (s == 0 ? cond->y : cond->y >> 2); Range r = {1<g, NULL, r, finfo.dim, env->seed, - &cond->bf, (volatile char*)abort) > 0; + &cond->bf, (volatile char*)env->stop) > 0; } return valid ? COND_OK : COND_FAILED; case F_BIOME_CENTER: case F_BIOME_CENTER_256: - if (pass == PASS_FULL_64) + if (env->searchpass == PASS_FULL_64) { s = cond->type == F_BIOME_CENTER ? 2 : 8; rx1 = x1 >> s; @@ -1893,7 +1964,7 @@ testCondAt( { // exclusion icnt = getBiomeCenters( cent, NULL, 1, &env->g, r, cond->biomeId, cond->biomeSize, cond->tol, - (volatile char*)abort + (volatile char*)env->stop ); if (icnt == 0) { @@ -1907,7 +1978,7 @@ testCondAt( { // just check there are at least *inst (== cond->count) instances *imax = icnt = getBiomeCenters( cent, NULL, cond->count, &env->g, r, cond->biomeId, cond->biomeSize, cond->tol, - (volatile char*)abort + (volatile char*)env->stop ); if (cond->skipref && icnt > 0) { // remove origin instance @@ -1928,7 +1999,7 @@ testCondAt( { // we need the average position of all instances icnt = getBiomeCenters( &p[0], NULL, MAX_INSTANCES, &env->g, r, cond->biomeId, cond->biomeSize, cond->tol, - (volatile char*)abort + (volatile char*)env->stop ); xt = zt = 0; int j = 0; @@ -1952,13 +2023,13 @@ testCondAt( return COND_MAYBE_POS_INVAL; case F_CLIMATE_MINMAX: - if (env->mc < MC_1_18 || cond->para >= NP_MAX) + if (env->mc <= MC_1_17 || cond->para >= NP_MAX) return COND_FAILED; rx1 = x1 >> 2; rz1 = z1 >> 2; rx2 = x2 >> 2; rz2 = z2 >> 2; - if (pass != PASS_FULL_64) + if (env->searchpass != PASS_FULL_64) return COND_MAYBE_POS_INVAL; { track_minmax_t info = {at, at, +INFINITY, -INFINITY}; @@ -2000,7 +2071,7 @@ testCondAt( } case F_CLIMATE_NOISE: - if (env->mc < MC_1_18) + if (env->mc <= MC_1_17) return COND_FAILED; rx1 = x1 >> 2; rz1 = z1 >> 2; @@ -2009,8 +2080,9 @@ testCondAt( cent->x = (x1 + x2) >> 1; cent->z = (z1 + z2) >> 1; if (imax) *imax = 1; - if (pass != PASS_FULL_64) + if (env->searchpass != PASS_FULL_64) return COND_MAYBE_POS_VALID; + else { int w = rx2 - rx1 + 1; int h = rz2 - rz1 + 1; @@ -2069,7 +2141,7 @@ testCondAt( cent->x = x1; cent->z = z1; if (imax) *imax = 1; - if (pass != PASS_FULL_64) + if (env->searchpass != PASS_FULL_64) return COND_MAYBE_POS_VALID; env->init4Dim(DIM_OVERWORLD); env->prepareSurfaceNoise(DIM_OVERWORLD); diff --git a/src/search.h b/src/search.h index d73a0de..d0c9f77 100644 --- a/src/search.h +++ b/src/search.h @@ -90,6 +90,7 @@ enum F_WELL, F_TRAILS, F_BIOME_SAMPLE, + F_NOISE_SAMPLE, // new filters should be added here at the end to keep some downwards compatibility FILTER_MAX, }; @@ -308,15 +309,23 @@ static const struct FilterList : private FilterInfo }; list[F_CLIMATE_NOISE] = FilterInfo{ - CAT_BIOMES, 0, LOC_REC, 0, 4, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 4, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++, "overworld", QT_TRANSLATE_NOOP("Filter", "Climate parameters"), QT_TRANSLATE_NOOP("Filter", "Custom limits for the required and allowed climate noise parameters that " "the specified area should cover.") }; + list[F_NOISE_SAMPLE] = FilterInfo{ + CAT_BIOMES, 1, LOC_RAD, 0, 4, BR_SPLIT, MC_1_18, MC_NEWEST, 0, 0, disp++, + "overworld", + QT_TRANSLATE_NOOP("Filter", "Climate noise samples"), + QT_TRANSLATE_NOOP("Filter", + "Samples climate noise in a given area to find if a proportion of the " + "biomes match a set of allowed biomes.") + }; list[F_CLIMATE_MINMAX] = FilterInfo{ - CAT_BIOMES, 0, LOC_REC, 0, 4, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 4, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++, "overworld", QT_TRANSLATE_NOOP("Filter", "Locate climate extreme"), QT_TRANSLATE_NOOP("Filter", @@ -358,7 +367,7 @@ static const struct FilterList : private FilterInfo "" }; list[F_HEIGHT] = FilterInfo{ - CAT_OTHER, 0, LOC_POS, 0, 4, BR_NONE, MC_1_1, MC_NEWEST, 0, 0, disp++, + CAT_OTHER, 1, LOC_POS, 0, 4, BR_NONE, MC_1_1, MC_NEWEST, 0, 0, disp++, "height", QT_TRANSLATE_NOOP("Filter", "Surface height"), QT_TRANSLATE_NOOP("Filter", @@ -446,7 +455,7 @@ static const struct FilterList : private FilterInfo list[F_RUINS] = FilterInfo{ CAT_STRUCT, 1, LOC_RAD, Ocean_Ruin, 1, BR_CLUST, MC_1_13, MC_NEWEST, 0, 0, disp++, "ruins", - QT_TRANSLATE_NOOP("Filter", "Ocean ruins"), + QT_TRANSLATE_NOOP("Filter", "Ocean ruin"), "" }; @@ -489,7 +498,7 @@ static const struct FilterList : private FilterInfo }; list[F_TRAILS] = FilterInfo{ - CAT_STRUCT, 1, LOC_RAD, Trail_Ruin, 1, BR_CLUST, MC_1_20, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, Trail_Ruins, 1, BR_CLUST, MC_1_20, MC_NEWEST, 0, 0, disp++, "trails", QT_TRANSLATE_NOOP("Filter", "Trail ruins"), "" @@ -637,6 +646,24 @@ static_assert( "Layout of Condition has changed!" ); + +#define MAX_INSTANCES 4096 // should be at least 128 + +enum +{ + COND_FAILED = 0, // seed does not meet the condition + COND_MAYBE_POS_INVAL = 1, // search pass insufficient for result + COND_MAYBE_POS_VALID = 2, // search pass insufficient, but known center + COND_OK = 3, // seed satisfies the condition +}; + +enum +{ + PASS_FAST_48, // only do fast checks that do not require biome gen + PASS_FULL_48, // include possible biome checks for 48-bit seeds + PASS_FULL_64, // run full test on a 64-bit seed +}; + struct ConditionTree { std::vector condvec; @@ -658,6 +685,9 @@ struct SearchThreadEnv int surfdim; int octaves; + int searchpass; + std::atomic_bool *stop; + std::map l_states; SearchThreadEnv(); @@ -671,24 +701,6 @@ struct SearchThreadEnv void prepareSurfaceNoise(int dim); }; - -#define MAX_INSTANCES 4096 // should be at least 128 - -enum -{ - COND_FAILED = 0, // seed does not meet the condition - COND_MAYBE_POS_INVAL = 1, // search pass insufficient for result - COND_MAYBE_POS_VALID = 2, // search pass insufficient, but known center - COND_OK = 3, // seed satisfies the condition -}; - -enum -{ - PASS_FAST_48, // only do fast checks that do not require biome gen - PASS_FULL_48, // include possible biome checks for 48-bit seeds - PASS_FULL_64, // run full test on a 64-bit seed -}; - /* Checks if a seed satisfies the conditions tree. * Returns the lowest condition fulfillment status. */ @@ -696,15 +708,12 @@ int testTreeAt( Pos at, // relative origin SearchThreadEnv * env, // thread-local environment int pass, // search pass - std::atomic_bool * abort, // abort signal - Pos * path = 0 // ok trigger positions + Pos * path // ok trigger positions ); int testCondAt( Pos at, // relative origin SearchThreadEnv * env, // thread-local environment - int pass, // search pass - std::atomic_bool * abort, // abort signal Pos * cent, // output center position(s) int * imax, // max instances (NULL for avg) const Condition * cond // condition to check diff --git a/src/searchthread.cpp b/src/searchthread.cpp index 2cf1379..3b5235f 100644 --- a/src/searchthread.cpp +++ b/src/searchthread.cpp @@ -129,7 +129,7 @@ bool Session::load(QWidget *widget, QTextStream& stream, bool quiet) SearchMaster::SearchMaster(QWidget *parent) : QObject(parent) , mutex() - , abort() + , stop() , proghist() , progtimer() , itemtimer() @@ -151,11 +151,12 @@ SearchMaster::SearchMaster(QWidget *parent) , smax() , isdone() { + env.stop = &stop; } SearchMaster::~SearchMaster() { - stop(); + stopSearch(); } bool SearchMaster::set(QWidget *widget, const Session& s) @@ -313,7 +314,7 @@ bool SearchMaster::set(QWidget *widget, const Session& s) this->smin = s.sc.smin; this->smax = s.sc.smax; this->isdone = false; - this->abort = false; + this->stop = false; return true; } @@ -393,7 +394,7 @@ static bool applyTranspose(std::vector& slist, return !slist.empty(); } -void SearchMaster::presearch() +void SearchMaster::preSearch() { uint64_t sstart = seed; @@ -567,13 +568,13 @@ void SearchMaster::presearch() } } -void SearchMaster::start() +void SearchMaster::startSearch() { - stop(); - abort = false; - presearch(); + stopSearch(); + stop = false; + preSearch(); - if (abort) + if (stop) { emit searchFinish(false); return; @@ -607,9 +608,9 @@ void SearchMaster::start() } } -void SearchMaster::stop() +void SearchMaster::stopSearch() { - abort = true; + stop = true; if (workers.empty()) return; @@ -871,10 +872,10 @@ bool SearchMaster::requestItem(SearchWorker *item) high = 0; low++; - for (; low <= MASK48 && !abort; low++) + for (; low <= MASK48 && !stop; low++) { env.setSeed(low); - if (testTreeAt(origin, &env, PASS_FAST_48, &abort) + if (testTreeAt(origin, &env, PASS_FAST_48, nullptr) != COND_FAILED) { break; @@ -909,7 +910,7 @@ void SearchMaster::onWorkerFinished() for (SearchWorker *worker: workers) delete worker; workers.clear(); - emit searchFinish(isdone && !abort); + emit searchFinish(isdone && !stop); } @@ -919,7 +920,6 @@ SearchWorker::SearchWorker(SearchMaster *master) { this->slist = master->slist.empty() ? NULL : master->slist.data(); this->len = master->slist.size(); - this->abort = &master->abort; this->prog = master->prog; this->idx = master->idx; @@ -927,12 +927,11 @@ SearchWorker::SearchWorker(SearchMaster *master) this->scnt = 0; this->seed = master->seed; - this->env = new SearchThreadEnv(); + this->env.stop = &master->stop; } SearchWorker::~SearchWorker() { - delete env; } bool SearchWorker::getNextItem() @@ -944,25 +943,21 @@ bool SearchWorker::getNextItem() void SearchWorker::run() { Pos origin = {0,0}; - env->init(master->mc, master->large, master->condtree); - - volatile char b[1024*1024]; - for (size_t i = 0; i < sizeof(b); i++) - b[i] = (char)i + b[i % 256]; + env.init(master->mc, master->large, master->condtree); switch (master->searchtype) { case SEARCH_LIST: - while (!*abort && getNextItem()) + while (!*env.stop && getNextItem()) { // seed = slist[..] uint64_t ie = idx+scnt < len ? idx+scnt : len; for (uint64_t i = idx; i < ie; i++) { seed = slist[i]; - env->setSeed(seed); - if (testTreeAt(origin, env, PASS_FULL_64, abort) == COND_OK) + env.setSeed(seed); + if (testTreeAt(origin, &env, PASS_FULL_64, nullptr) == COND_OK) { - if (!*abort) + if (!*env.stop) emit result(seed); } } @@ -972,7 +967,7 @@ void SearchWorker::run() break; case SEARCH_48ONLY: - while (!*abort && getNextItem()) + while (!*env.stop && getNextItem()) { if (slist) { @@ -980,10 +975,10 @@ void SearchWorker::run() for (uint64_t i = idx; i < ie; i++) { seed = slist[i]; - env->setSeed(seed); - if (testTreeAt(origin, env, PASS_FULL_48, abort) != COND_FAILED) + env.setSeed(seed); + if (testTreeAt(origin, &env, PASS_FULL_48, nullptr) != COND_FAILED) { - if (!*abort) + if (!*env.stop) emit result(seed); } } @@ -993,10 +988,10 @@ void SearchWorker::run() seed = sstart; for (int i = 0; i < scnt; i++) { - env->setSeed(seed); - if (testTreeAt(origin, env, PASS_FULL_48, abort) != COND_FAILED) + env.setSeed(seed); + if (testTreeAt(origin, &env, PASS_FULL_48, nullptr) != COND_FAILED) { - if (!*abort) + if (!*env.stop) emit result(seed); } @@ -1011,7 +1006,7 @@ void SearchWorker::run() break; case SEARCH_INC: - while (!*abort && getNextItem()) + while (!*env.stop && getNextItem()) { if (slist) { // seed = (high << 48) | slist[..] @@ -1022,10 +1017,10 @@ void SearchWorker::run() { seed = (high << 48) | slist[lowidx]; - env->setSeed(seed); - if (testTreeAt(origin, env, PASS_FULL_64, abort) == COND_OK) + env.setSeed(seed); + if (testTreeAt(origin, &env, PASS_FULL_64, nullptr) == COND_OK) { - if (!*abort) + if (!*env.stop) emit result(seed); } @@ -1044,10 +1039,10 @@ void SearchWorker::run() seed = sstart; for (int i = 0; i < scnt; i++) { - env->setSeed(seed); - if (testTreeAt(origin, env, PASS_FULL_64, abort) == COND_OK) + env.setSeed(seed); + if (testTreeAt(origin, &env, PASS_FULL_64, nullptr) == COND_OK) { - if (!*abort) + if (!*env.stop) emit result(seed); } @@ -1062,7 +1057,7 @@ void SearchWorker::run() break; case SEARCH_BLOCKS: - while (!*abort && getNextItem()) + while (!*env.stop && getNextItem()) { // seed = ([..] << 48) | low if (slist && idx >= len) { @@ -1076,8 +1071,8 @@ void SearchWorker::run() else low = sstart & MASK48; - env->setSeed(low); - if (testTreeAt(origin, env, PASS_FULL_48, abort) == COND_FAILED) + env.setSeed(low); + if (testTreeAt(origin, &env, PASS_FULL_48, nullptr) == COND_FAILED) { continue; } @@ -1086,10 +1081,10 @@ void SearchWorker::run() { seed = (high << 48) | low; - env->setSeed(seed); - if (testTreeAt(origin, env, PASS_FULL_64, abort) == COND_OK) + env.setSeed(seed); + if (testTreeAt(origin, &env, PASS_FULL_64, nullptr) == COND_OK) { - if (!*abort) + if (!*env.stop) emit result(seed); } diff --git a/src/searchthread.h b/src/searchthread.h index 436ca77..6293585 100644 --- a/src/searchthread.h +++ b/src/searchthread.h @@ -37,10 +37,10 @@ struct SearchMaster : QObject bool set(QWidget *widget, const Session& s); - void presearch(); + void preSearch(); - void start(); - void stop(); + void startSearch(); + void stopSearch(); // Get search progress: // status : progress status summary @@ -69,7 +69,7 @@ public slots: std::vector workers; QMutex mutex; - std::atomic_bool abort; + std::atomic_bool stop; std::deque proghist; QElapsedTimer progtimer; @@ -114,7 +114,6 @@ struct SearchWorker : QThread const uint64_t * slist; // candidate list uint64_t len; // number of candidates - std::atomic_bool * abort; /// current work item uint64_t prog; // search space progress @@ -126,7 +125,7 @@ struct SearchWorker : QThread // (or the last entry in the seed list) private: - SearchThreadEnv * env; + SearchThreadEnv env; }; diff --git a/src/tablocations.cpp b/src/tablocations.cpp index 9e3fc74..8fcf704 100644 --- a/src/tablocations.cpp +++ b/src/tablocations.cpp @@ -61,6 +61,12 @@ QTreeWidgetItem *setConditionTreeItems(ConditionTree& ctree, int node, int64_t s return item; } +AnalysisLocations::AnalysisLocations(QObject *parent) +: QThread(parent), wi(),stop(),sidx(),pidx() +{ + env.stop = &stop; +} + QString AnalysisLocations::set(WorldInfo wi, const std::vector& conds) { this->wi = wi; @@ -85,7 +91,7 @@ void AnalysisLocations::run() Pos at = pos[pidx.load()]; Pos cpos[MAX_INSTANCES] = {}; - if (testTreeAt(at, &env, PASS_FULL_64, &stop, cpos) + if (testTreeAt(at, &env, PASS_FULL_64, cpos) != COND_OK) { continue; diff --git a/src/tablocations.h b/src/tablocations.h index 02a54e8..cbc4de7 100644 --- a/src/tablocations.h +++ b/src/tablocations.h @@ -16,8 +16,7 @@ class AnalysisLocations : public QThread { Q_OBJECT public: - explicit AnalysisLocations(QObject *parent = nullptr) - : QThread(parent), wi(),stop(),sidx(),pidx() {} + explicit AnalysisLocations(QObject *parent = nullptr); QString set(WorldInfo wi, const std::vector& conds); diff --git a/src/util.h b/src/util.h index f776f34..7fa1359 100644 --- a/src/util.h +++ b/src/util.h @@ -80,7 +80,6 @@ struct RandGen // get a random 64-bit integer uint64_t getRnd64(); - enum { S_TEXT, S_NUMERIC, S_RANDOM }; int str2seed(const QString &str, uint64_t *out); diff --git a/src/world.cpp b/src/world.cpp index 7311b96..418cb47 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -201,11 +201,15 @@ void getStructs(std::vector *out, const StructureConfig sconf, { SurfaceNoise sn; initSurfaceNoise(&sn, DIM_END, wi.seed); - id = isViableEndCityTerrain(&g, &sn, p.x, p.z); - if (!id) + int y = isViableEndCityTerrain(&g, &sn, p.x, p.z); + if (!y) continue; int n = getEndCityPieces(pieces, wi.seed, p.x >> 4, p.z >> 4); - vp.pieces.assign(pieces, pieces+n); + if (n) + { + vp.pieces.assign(pieces, pieces+n); + vp.pieces[0].bb0.y = y; // height of end city pieces are relative to surface + } } else if (sconf.structType == Ruined_Portal || sconf.structType == Ruined_Portal_N) { @@ -235,12 +239,12 @@ void getStructs(std::vector *out, const StructureConfig sconf, static QMutex g_mutex; -static qreal cubic_hermite(qreal p[4], qreal u) +static float cubic_hermite(float p[4], float u) { - qreal a = p[1]; - qreal b = 0.5 * (-p[0] + p[2]); - qreal c = p[0] - 2.5*p[1] + 2*p[2] - 0.5*p[3]; - qreal d = 0.5 * (-p[0] + 3*p[1] - 3*p[2] + p[3]); + float a = p[1]; + float b = 0.5 * (-p[0] + p[2]); + float c = p[0] - 2.5*p[1] + 2*p[2] - 0.5*p[3]; + float d = 0.5 * (-p[0] + 3*p[1] - 3*p[2] + p[3]); return a + b*u + c*u*u + d*u*u*u; } @@ -262,21 +266,28 @@ void applyHeightShading(unsigned char *rgb, Range r, px -= bd; pz -= bd; pw += 2*bd; ph += 2*bd; } - std::vector buf(pw * ph); - for (int j = 0; j < ph; j++) + std::vector buf(pw * ph); + if (ps == 0) + { + mapApproxHeight(&buf[0], 0, g, sn, px, pz, pw, ph); + } + else { - for (int i = 0; i < pw; i++) + for (int j = 0; j < ph; j++) { - if (abort && *abort) return; - int samplex = (((px + i) << ps)) * r.scale / 4; - int samplez = (((pz + j) << ps)) * r.scale / 4; - float y = 0; - mapApproxHeight(&y, 0, g, sn, samplex, samplez, 1, 1); - buf[j*pw+i] = y; + for (int i = 0; i < pw; i++) + { + if (abort && *abort) return; + int samplex = (((px + i) << ps)) * r.scale / 4; + int samplez = (((pz + j) << ps)) * r.scale / 4; + float y = 0; + mapApproxHeight(&y, 0, g, sn, samplex, samplez, 1, 1); + buf[j*pw+i] = y; + } } } // interpolate height - std::vector height((w+2) * (h+2)); + std::vector height((w+2) * (h+2)); for (int j = 0; j < h+2; j++) { for (int i = 0; i < w+2; i++) @@ -284,12 +295,12 @@ void applyHeightShading(unsigned char *rgb, Range r, if (abort && *abort) return; int pi = ((x + i - 1) >> ps) - px - bd; int pj = ((z + j - 1) >> ps) - pz - bd; - qreal di = ((x + i - 1) & st) / (qreal)(st + 1); - qreal dj = ((z + j - 1) & st) / (qreal)(st + 1); - qreal v = 0; + float di = ((x + i - 1) & st) / (float)(st + 1); + float dj = ((z + j - 1) & st) / (float)(st + 1); + float v = 0; if (bicubic) { - qreal p[] = { + float p[] = { cubic_hermite(&buf.at((pj+0)*pw + pi), di), cubic_hermite(&buf.at((pj+1)*pw + pi), di), cubic_hermite(&buf.at((pj+2)*pw + pi), di), @@ -299,10 +310,10 @@ void applyHeightShading(unsigned char *rgb, Range r, } else // bilinear { - qreal v00 = buf.at((pj+0)*pw + pi+0); - qreal v01 = buf.at((pj+0)*pw + pi+1); - qreal v10 = buf.at((pj+1)*pw + pi+0); - qreal v11 = buf.at((pj+1)*pw + pi+1); + float v00 = buf.at((pj+0)*pw + pi+0); + float v01 = buf.at((pj+0)*pw + pi+1); + float v10 = buf.at((pj+1)*pw + pi+0); + float v11 = buf.at((pj+1)*pw + pi+1); v = lerp2(di, dj, v00, v01, v10, v11); } height[j*(w+2)+i] = v; @@ -310,24 +321,24 @@ void applyHeightShading(unsigned char *rgb, Range r, } if (abort && *abort) return; // apply shading based on height changes - qreal mul = 0.25 / r.scale; - qreal lout = 0.65; - qreal lmin = 0.5; - qreal lmax = 1.5; - qreal ymax = r.scale == 1 ? r.y : r.y << 2; + float mul = 0.25 / r.scale; + float lout = 0.65; + float lmin = 0.5; + float lmax = 1.5; + float ymax = r.scale == 1 ? r.y : r.y << 2; for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { int tw = w+2; - qreal t01 = height[(j+0)*tw + i+1]; - qreal t10 = height[(j+1)*tw + i+0]; - qreal t11 = height[(j+1)*tw + i+1]; - qreal t12 = height[(j+1)*tw + i+2]; - qreal t21 = height[(j+2)*tw + i+1]; - qreal d0 = t01 + t10; - qreal d1 = t12 + t21; - qreal light = 1.0; + float t01 = height[(j+0)*tw + i+1]; + float t10 = height[(j+1)*tw + i+0]; + float t11 = height[(j+1)*tw + i+1]; + float t12 = height[(j+1)*tw + i+2]; + float t21 = height[(j+2)*tw + i+1]; + float d0 = t01 + t10; + float d1 = t12 + t21; + float light = 1.0; uchar *col = rgb + 3*(j*w + i); if (mode == HV_GRAYSCALE) { @@ -336,7 +347,7 @@ void applyHeightShading(unsigned char *rgb, Range r, col[0] = 0xff; col[1] = col[2] = 0; continue; } - qreal v = t11; + float v = t11; uchar c = (v <= 0) ? 0 : (v > 0xff) ? 0xff : (uchar)(v); col[0] = col[1] = col[2] = c; continue; @@ -348,14 +359,14 @@ void applyHeightShading(unsigned char *rgb, Range r, if (light > lmax) light = lmax; if (mode == HV_CONTOURS || mode == HV_CONTOURS_SHADING) { - qreal spacing = 16.0; - qreal tmin = std::min({t01, t10, t12, t21}); + float spacing = 16.0; + float tmin = std::min({t01, t10, t12, t21}); if (floor(tmin / spacing) != floor(t11 / spacing)) light *= 0.5; } for (int k = 0; k < 3; k++) { - qreal c = col[k] * light; + float c = col[k] * light; col[k] = (c <= 0) ? 0 : (c > 0xff) ? 0xff : (uchar)(c); } } @@ -401,7 +412,7 @@ void Quad::run() int nptype = -1; if (g->mc >= MC_1_18) nptype = g->bn.nptype; if (g->mc <= MC_B1_7) nptype = g->bnb.nptype; - if (dim == 0 && nptype >= 0) + if (dim == DIM_OVERWORLD && nptype >= 0) { // climate parameter const int *extremes = getBiomeParaExtremes(g->mc); int cmin = extremes[nptype*2 + 0]; @@ -420,7 +431,7 @@ void Quad::run() g_mutex.unlock(); biomesToImage(rgb, g_biomeColors, biomes, w, h, 1, 1); - if (lopt.mode == LOPT_HEIGHT_4 && dim == 0) + if (lopt.mode == LOPT_HEIGHT_4) { int stepbits = (hd ? 0 : 2); applyHeightShading(rgb, r, g, sn, stepbits, lopt.disp[lopt.mode], false, isdel); @@ -782,7 +793,7 @@ void QWorld::setDim(int dim, LayerOpt lopt) this->dim = dim; this->lopt = lopt; applySeed(&g, dim, wi.seed); - initSurfaceNoise(&sn, DIM_OVERWORLD, g.seed); + initSurfaceNoise(&sn, dim, g.seed); int pixs, lcnt; if (g.mc >= MC_1_18 || dim != DIM_OVERWORLD) @@ -862,15 +873,17 @@ QString QWorld::getBiomeName(Pos p) return c + "=" + QString::number(id); } QString ret = getBiomeDisplay(wi.mc, id); - if (lopt.mode == LOPT_HEIGHT_4 && dim == DIM_OVERWORLD) - ret = QString::asprintf("Y~%d ", estimateSurface(p)) + ret; + if (lopt.mode == LOPT_HEIGHT_4) + { + int y = estimateSurface(p); + if (y > 0) + ret = QString::asprintf("Y~%d ", y) + ret; + } return ret; } int QWorld::estimateSurface(Pos p) { - if (dim != DIM_OVERWORLD) - return 64; float y = 0; mapApproxHeight(&y, 0, &g, &sn, p.x>>2, p.z>>2, 1, 1); return (int) floor(y);