From 5a00d88be9ed366220806a4abc9ccabdd2184fee Mon Sep 17 00:00:00 2001 From: maj00r Date: Sun, 18 Oct 2026 13:02:01 +0200 Subject: [PATCH] feat(editor): a scenery begun from nothing is asked for its terrain as the first track is laid With the lay tool on, a scenery with no tracks nor terrain chunks gets the offer of the terrain model and the aerial photos around the camera; without a place yet it asks for the PUWG 1992 coordinates of this point, as geoportal.gov.pl gives them, and keeps them as the place of its orthophoto. The offer can be turned off (editor setting terrain_offer). Self-test nmtoffer. --- application/editormode.cpp | 1 + application/editormode.h | 8 +++ application/editormodenmt.cpp | 80 +++++++++++++++++++++++ application/editormodeselftestterrain.cpp | 15 +++++ editor/editorSettings.cpp | 3 + editor/editorSettings.hpp | 4 ++ lang/editor_pl.po | 18 +++++ 7 files changed, 129 insertions(+) diff --git a/application/editormode.cpp b/application/editormode.cpp index 16467e6f..ae048a29 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -1481,6 +1481,7 @@ bool editor_mode::update() render_open_scenery_popup(); render_shortcuts_window(); render_first_steps(); + render_nmt_prompt(); render_orthophoto_window(); render_switch_designer(); render_environment_window(); diff --git a/application/editormode.h b/application/editormode.h index 57b9db65..74414ebb 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -348,6 +348,10 @@ class editor_mode : public application_mode, private editor_track::observer std::size_t shaped{0}; std::string status; std::string error; + bool offered{false}; // the terrain was offered to the scenery begun without it + bool prompt{false}; // the offer is shown + double north{0.0}; // the place typed into the offer, PUWG 1992 + double east{0.0}; }; editor_nmt m_nmt; nmt_state m_nmt_state; @@ -362,6 +366,10 @@ class editor_mode : public application_mode, private editor_track::observer void nmt_shape_tracks(); void nmt_update(); void render_nmt_ui(); + // a scenery begun from nothing, with no tracks nor terrain, is asked as the first track is laid whether it wants the terrain + void nmt_offer(); + void render_nmt_prompt(); + void nmt_accept_offer(); // the tool of the terrain the left mouse button works enum class terrain_tool { diff --git a/application/editormodenmt.cpp b/application/editormodenmt.cpp index b5ef539f..2dfe9fa2 100644 --- a/application/editormodenmt.cpp +++ b/application/editormodenmt.cpp @@ -16,6 +16,8 @@ http://mozilla.org/MPL/2.0/. #include "application/editormodeshared.h" #include "editor/editorFormat.hpp" #include "editor/editorGeometry.hpp" +#include "editor/editorSettings.hpp" +#include "utilities/Globals.h" #include "simulation/simulation.h" #include "utilities/Logs.h" #include "utilities/translation.h" @@ -248,6 +250,7 @@ void editor_mode::nmt_shape_tracks() void editor_mode::nmt_update() { auto &state{m_nmt_state}; + nmt_offer(); if (state.fitting) { bool all{true}; @@ -335,3 +338,80 @@ void editor_mode::render_nmt_ui() ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.3f, 1.0f), "%s", state.error.c_str()); ImGui::TextDisabled("%s", STR_C("Terrain model: GUGiK, geoportal.gov.pl")); } + +void editor_mode::nmt_offer() +{ + auto &state{m_nmt_state}; + if (state.offered || false == m_lay.active || false == EditorSettings.terrain_offer()) + return; + state.offered = true; + if (m_streamer.resident() > 0 || std::any_of(simulation::Paths.sequence().begin(), simulation::Paths.sequence().end(), [](TTrack const *Track) { return Track != nullptr && rail(*Track); })) + return; + nmt_locate(); + state.north = m_orthophoto.settings().north; + state.east = m_orthophoto.settings().east; + state.prompt = true; +} + +void editor_mode::nmt_accept_offer() +{ + auto &state{m_nmt_state}; + if (false == m_nmt.placed()) + { + auto config{m_orthophoto.settings()}; + config.north = state.north; + config.east = state.east; + m_orthophoto.settings(config); + m_orthophoto_origin_edit = {config.north, config.east}; + save_orthophoto_settings(); + } + // no tracks to fit to: the heights of the model as they are + state.offset = 0.0; + nmt_make(nmt_keys_around()); + state.prompt = false; +} + +void editor_mode::render_nmt_prompt() +{ + auto &state{m_nmt_state}; + if (false == state.prompt) + return; + char const *const title{STR_C("Terrain for the new scenery###nmtoffer")}; + if (false == ImGui::IsPopupOpen(title)) + ImGui::OpenPopup(title); + ImGui::SetNextWindowSize(ImVec2(520.0f * Global.ui_scale, 0.0f)); + if (false == ImGui::BeginPopupModal(title, nullptr, ImGuiWindowFlags_NoResize)) + return; + ImGui::TextWrapped("%s", STR_C("The scenery has no terrain yet. Load the terrain model and the aerial photos around here? The tracks laid then go along the real ground.")); + bool const placed{m_nmt.placed()}; + if (false == placed) + { + ImGui::TextWrapped("%s", STR_C("Where is it? PUWG 1992 of this point, X to the north and Y to the east, as geoportal.gov.pl gives them:")); + ImGui::SetNextItemWidth(150.0f); + ImGui::InputDouble("X##nmtnorth", &state.north, 0.0, 0.0, "%.0f"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(150.0f); + ImGui::InputDouble("Y##nmteast", &state.east, 0.0, 0.0, "%.0f"); + } + auto const known{placed || (state.north > 100000.0 && state.east > 100000.0)}; + ImGui::BeginDisabled(false == known); + if (ImGui::Button(STR_C("Load the terrain"))) + { + nmt_accept_offer(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndDisabled(); + ImGui::SameLine(); + if (ImGui::Button(STR_C("Without terrain"))) + { + state.prompt = false; + ImGui::CloseCurrentPopup(); + } + bool ask{EditorSettings.terrain_offer()}; + if (ImGui::Checkbox(STR_C("Ask with every new scenery"), &ask)) + { + EditorSettings.terrain_offer(ask); + EditorSettings.save(); + } + ImGui::EndPopup(); +} diff --git a/application/editormodeselftestterrain.cpp b/application/editormodeselftestterrain.cpp index c95bfb18..1337757b 100644 --- a/application/editormodeselftestterrain.cpp +++ b/application/editormodeselftestterrain.cpp @@ -82,6 +82,21 @@ editor_mode::selftest_result editor_mode::selftest_terrain(std::string const &co WriteLog("SELFTEST nmtmake " + where + ": " + m_nmt_state.status); return selftest_result::handled; } + if (command == "nmtoffer") + { + // the offer of the terrain to a scenery begun from nothing: logged, and taken with the place given, if any + double north{0.0}, east{0.0}; + words >> north >> east; + WriteLog(format("SELFTEST nmtoffer: shown %d", m_nmt_state.prompt ? 1 : 0)); + if (north > 0.0 && east > 0.0 && m_nmt_state.prompt) + { + m_nmt_state.north = north; + m_nmt_state.east = east; + nmt_accept_offer(); + WriteLog("SELFTEST nmtoffer taken: " + m_nmt_state.status); + } + return selftest_result::handled; + } if (command == "nmtwait") { // till the model is in and the chunks waiting for it are made diff --git a/editor/editorSettings.cpp b/editor/editorSettings.cpp index e92679cf..515d5cdd 100644 --- a/editor/editorSettings.cpp +++ b/editor/editorSettings.cpp @@ -104,6 +104,8 @@ bool editorSettings::load() parse(value, m_palette); else if (key == "first_steps") m_first_steps = (value == "1"); + else if (key == "terrain_offer") + m_terrain_offer = (value == "1"); else if (key == "track_tools_all") m_track_tools_more = (value == "1"); else if (key.rfind(origin_prefix, 0) == 0) @@ -150,6 +152,7 @@ bool editorSettings::save() stream << "nodebank_card " << m_nodebank_card << "\n"; stream << "colours " << m_palette << "\n"; stream << "first_steps " << (m_first_steps ? 1 : 0) << "\n"; + stream << "terrain_offer " << (m_terrain_offer ? 1 : 0) << "\n"; stream << "track_tools_all " << (m_track_tools_more ? 1 : 0) << "\n"; stream << std::fixed << std::setprecision(3); for (auto const &origin : m_orthophoto_origins) diff --git a/editor/editorSettings.hpp b/editor/editorSettings.hpp index 29408332..84fea1e7 100644 --- a/editor/editorSettings.hpp +++ b/editor/editorSettings.hpp @@ -56,6 +56,9 @@ public: bool first_steps() const { return m_first_steps; } void first_steps(bool Show) { m_first_steps = Show; } + // a scenery begun with no terrain is offered the terrain model as the first track is laid + bool terrain_offer() const { return m_terrain_offer; } + void terrain_offer(bool Offer) { m_terrain_offer = Offer; } bool track_tools_more() const { return m_track_tools_more; } void track_tools_more(bool Show) { m_track_tools_more = Show; } @@ -69,6 +72,7 @@ private: int m_nodebank_card{96}; int m_palette{0}; bool m_first_steps{true}; + bool m_terrain_offer{true}; bool m_track_tools_more{false}; }; diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 5c05ea7b..e219e72f 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -7121,3 +7121,21 @@ msgstr ". To za dużo: położenie scenerii jest pewnie złe, sprawdź ortofotom msgid "There's terrain there already" msgstr "Tam już jest teren" + +msgid "Terrain for the new scenery###nmtoffer" +msgstr "Teren dla nowej scenerii###nmtoffer" + +msgid "The scenery has no terrain yet. Load the terrain model and the aerial photos around here? The tracks laid then go along the real ground." +msgstr "Sceneria nie ma jeszcze terenu. Wczytać teren z NMT i zdjęcia lotnicze wokół tego miejsca? Tory układane potem pójdą po prawdziwym terenie." + +msgid "Where is it? PUWG 1992 of this point, X to the north and Y to the east, as geoportal.gov.pl gives them:" +msgstr "Gdzie to jest? Współrzędne PUWG 1992 tego miejsca, X na północ i Y na wschód, jak podaje geoportal.gov.pl:" + +msgid "Load the terrain" +msgstr "Wczytaj teren" + +msgid "Without terrain" +msgstr "Bez terenu" + +msgid "Ask with every new scenery" +msgstr "Pytaj przy każdej nowej scenerii"