From 8c5795348f7d6c5e794e828de66adf56f4cd69ec Mon Sep 17 00:00:00 2001 From: maj00r Date: Thu, 22 Oct 2026 07:35:31 +0200 Subject: [PATCH] feat(editor): the signalling mode finds the station of a switch, lays it out as a schematic along its main track and keeps its name, code and line in the scenery --- CMakeLists.txt | 2 + application/editormode.cpp | 3 + application/editormode.h | 6 +- application/editormodesrk.cpp | 642 +++++++++++++++++++++++++++++ application/editormodesrk.h | 81 ++++ application/editormodetrack.cpp | 9 +- application/editormodetrackux.cpp | 7 + application/editormodeviewport.cpp | 5 +- application/editortool.h | 2 +- editor/editorIcons.hpp | 1 + editor/editorStation.cpp | 399 ++++++++++++++++++ editor/editorStation.hpp | 65 +++ editor/icons/editor_icons.png | Bin 46348 -> 47042 bytes editor/icons/editor_icons.py | 11 +- lang/editor_pl.po | 99 ++++- scene/scenelayers.cpp | 5 +- 16 files changed, 1325 insertions(+), 12 deletions(-) create mode 100644 application/editormodesrk.cpp create mode 100644 application/editormodesrk.h create mode 100644 editor/editorStation.cpp create mode 100644 editor/editorStation.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d85d027..4e3430f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,6 +191,7 @@ set(SOURCES "editor/editorGroundFollow.cpp" "editor/editorAlignmentStore.cpp" "editor/editorSwitchStore.cpp" +"editor/editorStation.cpp" "editor/editorSwitchDesign.cpp" "editor/editorConnections.cpp" "application/editormodeconnection.cpp" @@ -230,6 +231,7 @@ set(SOURCES "application/editormodegauge.cpp" "application/editormodeinfra.cpp" "application/editormodespeed.cpp" +"application/editormodesrk.cpp" "application/editormodejoints.cpp" "application/editormodeparallel.cpp" "application/editormodelineside.cpp" diff --git a/application/editormode.cpp b/application/editormode.cpp index 7ed6c292..3de5f4a5 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -1505,12 +1505,14 @@ bool editor_mode::update() draw_tape(); m_speed.update(); m_joints.update(); + m_srk.update(); draw_track_hints(); draw_empty_scenery_hint(); draw_profile_overlay(); draw_infra_overlay(); m_speed.draw_overlay(); m_joints.draw_overlay(); + m_srk.draw_overlay(); } else { @@ -2978,6 +2980,7 @@ bool editor_mode::save() switch_store::tidy(); switch_store::settle(); infra_store(); + m_srk.store(); if (false == m_saved_camera.has_value() || glm::distance(m_saved_camera->first, glm::dvec3{Camera.Pos}) > 0.5 || glm::distance(m_saved_camera->second, glm::vec3{Camera.Angle}) > 0.01f) { m_saved_camera = std::make_pair(glm::dvec3{Camera.Pos}, glm::vec3{Camera.Angle}); diff --git a/application/editormode.h b/application/editormode.h index 3fb5bf59..e2692fc8 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/. #include "application/editormodelineside.h" #include "application/editormodeparallel.h" #include "application/editormodespeed.h" +#include "application/editormodesrk.h" #include "application/editormodevehicle.h" #include "application/editortool.h" #include "scene/sceneeditor.h" @@ -1544,7 +1545,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri editor_icons::icon picture; int family; // 0 builds the track, 1 equips it, 2 checks it }; - static std::array const &track_modes(); + static std::array const &track_modes(); bool track_plan_tab() const { return m_track_tab == track_tab::straights || m_track_tab == track_tab::route; } track_tab plan_tab_for(TTrack const *Track) const; // the objects along the track: a tab for each tool @@ -2380,7 +2381,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri hekto_tool m_hekto{*this, *this, *this, *this, *this, *this}; fouling_tool m_fouling{*this, *this, *this, *this, *this}; parallel_tool m_parallel{*this, *this, *this, *this}; - std::vector m_tools{&m_gauge, &m_speed, &m_joints, &m_vehicle, &m_hekto, &m_fouling, &m_parallel}; + srk_tool m_srk{*this, *this, *this}; + std::vector m_tools{&m_gauge, &m_speed, &m_joints, &m_vehicle, &m_hekto, &m_fouling, &m_parallel, &m_srk}; void infra_rebase(); void render_infra_search(); diff --git a/application/editormodesrk.cpp b/application/editormodesrk.cpp new file mode 100644 index 00000000..db2a8598 --- /dev/null +++ b/application/editormodesrk.cpp @@ -0,0 +1,642 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "application/editormodesrk.h" +#include "application/editorgestures.h" +#include "application/editormodeshared.h" +#include "application/editorprojection.h" +#include "application/editoruilayer.h" +#include "editor/editorFormat.hpp" +#include "editor/editorGeometry.hpp" +#include "editor/editorSwitchStore.hpp" + +#include "simulation/simulation.h" +#include "utilities/Globals.h" +#include "utilities/Logs.h" +#include "world/Track.h" + +#include "imgui/imgui.h" +#include "utilities/translation.h" +#include +#include +#include +#include + +namespace +{ + +char const *const kMark{"//$s"}; +double constexpr kOverlayRange{4000.0}; + +ImU32 constexpr kPlain{IM_COL32(170, 176, 188, 255)}; +ImU32 constexpr kSwitch{IM_COL32(240, 200, 90, 255)}; +ImU32 constexpr kApproach{IM_COL32(95, 100, 112, 255)}; +ImU32 constexpr kHovered{IM_COL32(255, 255, 255, 255)}; + +bool is_switch(TTrack const &Track) +{ + return Track.eType != tt_Normal; +} + +// a word of the record line: blanks taken out, - for none +std::string word_of(std::string const &Text) +{ + std::string result; + for (auto const c : Text) + if (false == std::isspace(static_cast(c))) + result += c; + return result.empty() ? std::string{"-"} : result; +} + +std::string shown(std::string const &Word) +{ + return Word == "-" ? std::string{} : Word; +} + +} // namespace + +void srk_tool::load() +{ + if (m_loaded) + return; + m_loaded = true; + m_records.clear(); + if (scene::Layers.empty()) + return; + for (auto const layer : scene::Layers.marked_layers(kMark)) + for (auto const &text : scene::Layers.marked(layer, kMark)) + { + std::istringstream words{text}; + std::string word; + if ((words >> word).fail() || word != "station") + continue; + record entry; + entry.layer = layer; + words >> entry.code; + while (words >> word) + { + if (word == "line") + words >> entry.line; + else if (word == "anchor") + words >> entry.anchor; + else if (word == "name") + { + std::getline(words, entry.name); + entry.name.erase(0, entry.name.find_first_not_of(' ')); + break; + } + } + entry.code = shown(entry.code); + entry.line = shown(entry.line); + if (false == entry.anchor.empty()) + m_records.push_back(std::move(entry)); + } +} + +void srk_tool::store() +{ + if (false == m_changed || scene::Layers.empty()) + return; + m_changed = false; + std::vector layers{scene::Layers.marked_layers(kMark)}; + for (auto const &entry : m_records) + if (std::find(layers.begin(), layers.end(), entry.layer) == layers.end()) + layers.push_back(entry.layer); + for (auto const layer : layers) + { + std::vector lines; + for (auto const &entry : m_records) + { + if (entry.layer != layer) + continue; + if (lines.empty()) + lines.emplace_back("# stations described in the track editor, the simulation skips these lines"); + lines.push_back(format("station %s line %s anchor %s name %s", word_of(entry.code).c_str(), word_of(entry.line).c_str(), entry.anchor.c_str(), entry.name.c_str())); + } + scene::Layers.mark(layer, kMark, std::move(lines)); + } +} + +void srk_tool::open(TTrack &Track) +{ + m_status.clear(); + auto found{station::find(Track)}; + if (false == found.has_value()) + { + m_status = STR_C("No switch near this track: it isn't a part of a station"); + return; + } + m_layout = std::move(found); + m_hover = -1; + m_view_state.exaggeration = 0.0f; + m_history_size = m_history.history_size(); + // the record of the station, by any of its switches + m_record = -1; + for (int index = 0; index < static_cast(m_records.size()) && m_record < 0; ++index) + if (std::any_of(m_layout->switches.begin(), m_layout->switches.end(), [&](TTrack const *Switch) { return Switch->name() == m_records[index].anchor; })) + m_record = index; + m_anchor = m_record >= 0 ? m_records[m_record].anchor : m_layout->switches.front()->name(); +} + +void srk_tool::refresh() +{ + m_history_size = m_history.history_size(); + auto *anchor{m_anchor.empty() ? nullptr : simulation::Paths.find(m_anchor)}; + if (anchor == nullptr || anchor->m_editorremoved) + { + m_layout.reset(); + return; + } + auto const view{m_view_state}; + open(*anchor); + if (m_layout.has_value()) + m_view_state = view; +} + +srk_tool::record &srk_tool::record_of_open() +{ + if (m_record < 0) + { + record entry; + auto const *first{m_layout->switches.front()}; + entry.anchor = first->name(); + entry.layer = scene::Layers.resolve(first->layer()); + if (false == scene::Layers.valid(entry.layer) || false == scene::Layers.accepts(entry.layer)) + entry.layer = scene::Layers.active(); + if (false == scene::Layers.valid(entry.layer)) + entry.layer = 1; + m_records.push_back(std::move(entry)); + m_record = static_cast(m_records.size()) - 1; + m_anchor = m_records.back().anchor; + } + return m_records[m_record]; +} + +std::string srk_tool::title() const +{ + if (m_record >= 0) + { + auto const &entry{m_records[m_record]}; + if (false == entry.name.empty()) + return entry.code.empty() ? entry.name : entry.name + " (" + entry.code + ")"; + if (false == entry.code.empty()) + return entry.code; + } + return format(STR_C("Station of %s"), m_anchor.c_str()); +} + +void srk_tool::find_all() +{ + m_found.clear(); + for (auto *track : station::stations()) + m_found.emplace_back(track->name(), station::switches_of(*track).size()); + std::sort(m_found.begin(), m_found.end(), [](auto const &A, auto const &B) { return A.second > B.second; }); +} + +void srk_tool::update() +{ + if (false == m_open) + return; + load(); + if (m_layout.has_value() && m_history.history_size() != m_history_size) + refresh(); + else if (false == m_layout.has_value() && false == m_anchor.empty() && m_history.history_size() != m_history_size) + refresh(); + // a track of another station selected opens it + auto *track{m_selection.selected_track()}; + if (track == nullptr || track == m_picked) + return; + m_picked = track; + if (m_layout.has_value() && std::any_of(m_layout->paths.begin(), m_layout->paths.end(), [&](station::path const &Path) { return Path.track == track && false == Path.approach; })) + return; + open(*track); +} + +void srk_tool::forget(scene::basic_node const *Node) +{ + if (Node == m_picked) + m_picked = nullptr; + if (m_layout.has_value() && std::any_of(m_layout->paths.begin(), m_layout->paths.end(), [&](station::path const &Path) { return Path.track == Node; })) + { + // laid out again from its anchor at the next update + m_layout.reset(); + m_hover = -1; + m_history_size = 0; + } +} + +void srk_tool::draw_overlay() const +{ + if (false == m_open || false == m_layout.has_value()) + return; + screen_projection const projection; + auto *drawlist{ImGui::GetBackgroundDrawList(ImGui::GetMainViewport())}; + glm::dvec3 const camera{Global.pCamera.Pos}; + if (m_hover >= 0 && m_hover < static_cast(m_layout->paths.size())) + { + auto const &points{m_layout->paths[m_hover].points}; + for (std::size_t i = 1; i < points.size(); ++i) + projection.line(drawlist, points[i - 1], points[i], kHovered, 4.0f); + } + for (auto const *track : m_layout->switches) + { + auto const start{track->m_paths.front().points[segment_data::point::start] + glm::dvec3{0.0, 1.0, 0.0}}; + ImVec2 screen; + if ((false == Global.EditorOrtho && glm::distance(start, camera) > kOverlayRange) || false == projection.project(start, screen)) + continue; + auto const text{track->name()}; + auto const size{ImGui::CalcTextSize(text.c_str())}; + drawlist->AddRectFilled(ImVec2(screen.x - 3.0f, screen.y - size.y - 4.0f), ImVec2(screen.x + size.x + 3.0f, screen.y), IM_COL32(0, 0, 0, 160), 3.0f); + drawlist->AddText(ImVec2(screen.x, screen.y - size.y - 2.0f), kSwitch, text.c_str()); + } +} + +void srk_tool::render_found() +{ + if (main_button(STR_C("Find the stations of the scenery"), ImVec2(-1.0f, tall_button_height()))) + find_all(); + item_tooltip("Each group of switches joined by tracks shorter than 1.5 km is a station"); + if (m_found.empty()) + return; + ImGui::TextDisabled("%s", format(STR_P("%zu station", "%zu stations", m_found.size()), m_found.size()).c_str()); + if (false == ImGui::BeginChild("##srkfound", ImVec2(0.0f, std::min(300.0f, ImGui::GetTextLineHeightWithSpacing() * (m_found.size() + 1.0f))), true)) + { + ImGui::EndChild(); + return; + } + for (std::size_t index = 0; index < m_found.size(); ++index) + { + auto const &[name, count]{m_found[index]}; + auto const described{std::find_if(m_records.begin(), m_records.end(), [&](record const &Entry) { return Entry.anchor == name; })}; + auto label{format(STR_P("%s: %zu switch", "%s: %zu switches", count), name.c_str(), count)}; + if (described != m_records.end() && false == described->name.empty()) + label = described->name + " - " + label; + if (ImGui::Selectable((label + "##srkfound" + std::to_string(index)).c_str(), false)) + if (auto *track{simulation::Paths.find(name)}; track != nullptr) + { + open(*track); + m_selection.set_current_node(track); + m_picked = track; + m_view.focus_track(*track, 0, track->m_paths.front().points[segment_data::point::start]); + } + } + ImGui::EndChild(); +} + +void srk_tool::render_options() +{ + if (false == m_layout.has_value()) + { + ImGui::TextDisabled("%s", STR_C("The signalling of a station: its schematic, the numbers of the switches and the tracks, the track circuits and the signals")); + ImGui::Spacing(); + gesture_rows({{"LMB on a switch", "opens its station"}}); + if (false == m_status.empty()) + ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.3f, 1.0f), "%s", m_status.c_str()); + ImGui::Spacing(); + render_found(); + return; + } + auto const &layout{*m_layout}; + ImGui::TextUnformatted(title().c_str()); + std::unordered_set tracks; + for (auto const &path : layout.paths) + if (false == path.approach) + tracks.insert(path.track); + metric_tiles({{format("%zu", layout.switches.size()), STR_C("switches"), kSwitch}, + {format("%zu", tracks.size() - layout.switches.size()), STR_C("tracks"), kPlain}, + {format("%.0f m", layout.to - layout.from), STR_C("length"), ImGui::GetColorU32(ImGuiCol_Text)}}); + if (begin_fields("##srkstation")) + { + auto const text = [&](char const *Label, char const *Id, std::string const &Value, char const *Hint, auto const &Set) { + field_row(Label); + std::array buffer{}; + Value.copy(buffer.data(), buffer.size() - 1); + if (ImGui::InputTextWithHint(Id, Hint, buffer.data(), buffer.size())) + { + Set(std::string{buffer.data()}); + m_changed = true; + } + }; + auto const *entry{m_record >= 0 ? &m_records[m_record] : nullptr}; + text("Name", "##srkname", entry != nullptr ? entry->name : std::string{}, "Biała Podlaska", [&](std::string const &Value) { record_of_open().name = Value; }); + text("Code", "##srkcode", entry != nullptr ? entry->code : std::string{}, "BPd", [&](std::string const &Value) { record_of_open().code = word_of(Value) == "-" ? std::string{} : word_of(Value); }); + item_tooltip("The abbreviation of the station: the names of its switches, track circuits and signals begin with it"); + text("Line", "##srkline", entry != nullptr ? entry->line : std::string{}, "2", [&](std::string const &Value) { record_of_open().line = word_of(Value) == "-" ? std::string{} : word_of(Value); }); + item_tooltip("The number of the railway line the station lies on, as the block sections between the stations are named after it"); + ImGui::EndTable(); + } + ImGui::Spacing(); + if (ImGui::CollapsingHeader(format("%s (%zu)###srkswitches", STR_C("Switches"), layout.switches.size()).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) + { + if (ImGui::BeginTable("##srkswitchlist", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_ScrollY, ImVec2(0.0f, std::min(320.0f, ImGui::GetTextLineHeightWithSpacing() * (layout.switches.size() + 1.5f))))) + { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn(STR_C("Switch"), ImGuiTableColumnFlags_WidthStretch, 0.35f); + ImGui::TableSetupColumn(STR_C("Type"), ImGuiTableColumnFlags_WidthStretch, 0.45f); + ImGui::TableSetupColumn(STR_C("km"), ImGuiTableColumnFlags_WidthStretch, 0.2f); + ImGui::TableHeadersRow(); + auto *selected{m_selection.selected_track()}; + for (std::size_t index = 0; index < layout.switches.size(); ++index) + { + auto *track{layout.switches[index]}; + auto const start{track->m_paths.front().points[segment_data::point::start]}; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable((track->name() + "##srksw" + std::to_string(index)).c_str(), track == selected, ImGuiSelectableFlags_SpanAllColumns)) + { + m_selection.set_current_node(track); + m_picked = track; + m_view.focus_track(*track, 0, start); + } + ImGui::TableNextColumn(); + auto const record{switch_store::named(*track)}; + ImGui::TextUnformatted(record ? switch_store::label(*record).c_str() : track->eType == tt_Switch ? STR_C("switch") : track->eType == tt_Cross ? STR_C("crossing") : STR_C("turntable")); + ImGui::TableNextColumn(); + auto const along{station::project(layout, start).x}; + if (layout.kilometrage) + ImGui::Text("%.3f", station::km_of(layout, along)); + else + ImGui::Text("%.0f m", along); + } + ImGui::EndTable(); + } + } + ImGui::Spacing(); + if (false == m_status.empty()) + ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.3f, 1.0f), "%s", m_status.c_str()); + if (ImGui::Button(STR_C("Another station"), ImVec2(-1.0f, 0.0f))) + { + m_layout.reset(); + m_anchor.clear(); + m_record = -1; + m_hover = -1; + } + item_tooltip("Leaves this station; LMB on a switch opens another one"); +} + +void srk_tool::fit_view(float const Width, float const Height) +{ + auto const &layout{*m_layout}; + auto &view{m_view_state}; + auto const span{std::max(50.0, layout.to - layout.from)}; + view.from = layout.from - span * 0.08 - 30.0; + view.to = layout.to + span * 0.08 + 30.0; + view.centre = (layout.low + layout.high) * 0.5; + auto const across{(view.to - view.from) / std::max(1.0f, Width)}; + auto const vertical{(layout.high - layout.low + 20.0) / std::max(1.0f, Height * 0.75f)}; + view.exaggeration = static_cast(std::clamp(across / vertical, 1.0, 500.0)); +} + +void srk_tool::render_canvas(float const Height) +{ + auto const &layout{*m_layout}; + auto &view{m_view_state}; + auto const &io{ImGui::GetIO()}; + ImVec2 const size{std::max(200.0f, ImGui::GetContentRegionAvail().x), std::max(80.0f, Height)}; + ImVec2 const corner{ImGui::GetCursorScreenPos()}; + ImGui::InvisibleButton("##srkcanvas", size, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonMiddle); + bool const hovered{ImGui::IsItemHovered()}; + if (view.exaggeration <= 0.0f) + fit_view(size.x, size.y); + // the wheel zooms about the mouse, with Ctrl it exaggerates the offsets; a drag moves the plot + if (hovered && io.MouseWheel != 0.0f) + { + if (io.KeyCtrl) + view.exaggeration = std::clamp(view.exaggeration * std::pow(1.2f, io.MouseWheel), 1.0f, 500.0f); + else + { + auto const at{view.from + (io.MousePos.x - corner.x) / size.x * (view.to - view.from)}; + auto const factor{std::pow(0.8, static_cast(io.MouseWheel))}; + view.from = at - (at - view.from) * factor; + view.to = at + (view.to - at) * factor; + } + } + auto const across{(view.to - view.from) / size.x}; + auto const vertical{across / view.exaggeration}; + if (ImGui::IsItemActive() && (ImGui::IsMouseDragging(ImGuiMouseButton_Left, kDragPixels) || ImGui::IsMouseDragging(ImGuiMouseButton_Middle, kDragPixels))) + { + m_dragging = true; + view.from -= io.MouseDelta.x * across; + view.to -= io.MouseDelta.x * across; + view.centre += io.MouseDelta.y * vertical; + } + auto const x_of = [&](double const Chainage) { return corner.x + static_cast((Chainage - view.from) / across); }; + auto const y_of = [&](double const Offset) { return corner.y + size.y * 0.5f - static_cast((Offset - view.centre) / vertical); }; + auto const screen_of = [&](glm::dvec2 const &Point) { return ImVec2(x_of(Point.x), y_of(Point.y)); }; + + // the path under the mouse + if (hovered && false == m_dragging) + { + m_hover = -1; + auto nearest{7.0f * 7.0f}; + auto const &mouse{io.MousePos}; + for (int index = 0; index < static_cast(layout.paths.size()); ++index) + { + auto const &points{layout.paths[index].schematic}; + for (std::size_t i = 1; i < points.size(); ++i) + { + auto const a{screen_of(points[i - 1])}; + auto const b{screen_of(points[i])}; + auto const dx{b.x - a.x}; + auto const dy{b.y - a.y}; + auto const length2{dx * dx + dy * dy}; + auto const t{length2 > 0.0f ? std::clamp(((mouse.x - a.x) * dx + (mouse.y - a.y) * dy) / length2, 0.0f, 1.0f) : 0.0f}; + auto const ex{a.x + t * dx - mouse.x}; + auto const ey{a.y + t * dy - mouse.y}; + if (ex * ex + ey * ey < nearest) + { + nearest = ex * ex + ey * ey; + m_hover = index; + } + } + } + } + else if (false == hovered && false == ImGui::IsItemActive()) + m_hover = -1; + if (ImGui::IsItemDeactivated()) + { + if (false == m_dragging && m_hover >= 0) + { + auto const &path{layout.paths[m_hover]}; + m_selection.set_current_node(path.track); + m_picked = path.track; + m_view.focus_track(*path.track, path.index, path.points[path.points.size() / 2]); + } + m_dragging = false; + } + + auto *draw{ImGui::GetWindowDrawList()}; + ImVec2 const corner_end{corner.x + size.x, corner.y + size.y}; + draw->AddRectFilled(corner, corner_end, IM_COL32(22, 24, 28, 255)); + draw->PushClipRect(corner, corner_end, true); + // the km, or the chainage, along the top + { + auto const metres{across * 90.0}; + double step{10.0}; + for (auto const candidate : {10.0, 20.0, 50.0, 100.0, 200.0, 500.0, 1000.0, 2000.0, 5000.0}) + if ((step = candidate) >= metres) + break; + auto const origin{layout.kilometrage ? layout.km * 1000.0 : 0.0}; + for (auto mark{std::ceil((view.from + origin) / step) * step}; mark <= view.to + origin; mark += step) + { + auto const x{x_of(mark - origin)}; + draw->AddLine(ImVec2(x, corner.y), ImVec2(x, corner_end.y), IM_COL32(255, 255, 255, 16)); + auto const label{layout.kilometrage ? format("%.3f", mark / 1000.0) : format("%.0f m", mark)}; + draw->AddText(ImVec2(x + 3.0f, corner.y + 2.0f), IM_COL32(150, 155, 165, 255), label.c_str()); + } + } + auto const *selected{m_selection.selected_track()}; + auto const draw_path = [&](station::path const &Path, ImU32 const Colour, float const Width) { + for (std::size_t i = 1; i < Path.schematic.size(); ++i) + draw->AddLine(screen_of(Path.schematic[i - 1]), screen_of(Path.schematic[i]), Colour, Width); + }; + for (auto const &path : layout.paths) + if (path.approach) + draw_path(path, kApproach, 2.0f); + for (auto const &path : layout.paths) + if (false == path.approach) + draw_path(path, is_switch(*path.track) ? kSwitch : kPlain, 2.5f); + for (auto const &path : layout.paths) + if (path.track == selected) + draw_path(path, overlay_color::selected, 4.0f); + if (m_hover >= 0) + draw_path(layout.paths[m_hover], kHovered, 4.0f); + // the switches: a dot at the start, the name over or under it in turn + for (std::size_t index = 0; index < layout.switches.size(); ++index) + { + auto const *track{layout.switches[index]}; + auto const at{screen_of(station::project(layout, track->m_paths.front().points[segment_data::point::start]))}; + draw->AddCircleFilled(at, 3.5f, kSwitch); + auto const text{track->name()}; + auto const extent{ImGui::CalcTextSize(text.c_str())}; + ImVec2 const place{at.x - extent.x * 0.5f, index % 2 == 0 ? at.y - extent.y - 6.0f : at.y + 6.0f}; + draw->AddRectFilled(ImVec2(place.x - 2.0f, place.y - 1.0f), ImVec2(place.x + extent.x + 2.0f, place.y + extent.y + 1.0f), IM_COL32(22, 24, 28, 200), 2.0f); + draw->AddText(place, kSwitch, text.c_str()); + } + draw->PopClipRect(); + if (hovered && m_hover >= 0 && false == m_dragging) + { + auto const &path{layout.paths[m_hover]}; + auto const along{view.from + (io.MousePos.x - corner.x) * across}; + ImGui::BeginTooltip(); + ImGui::TextUnformatted(path.track->name().empty() ? STR_C("(no name)") : path.track->name().c_str()); + ImGui::TextDisabled("%s, %.1f m", is_switch(*path.track) ? STR_C("switch") : path.approach ? STR_C("line out of the station") : STR_C("track"), path.length); + if (layout.kilometrage) + ImGui::TextDisabled("km %.3f", station::km_of(layout, along)); + ImGui::TextDisabled("%s", STR_C("LMB: select it")); + ImGui::EndTooltip(); + } +} + +void srk_tool::render_windows() +{ + if (false == m_open || false == m_layout.has_value()) + return; + auto const &io{ImGui::GetIO()}; + // along the bottom of the 3d view, as the vertical profile + auto const low{editor_ui::view_min()}; + auto const high{editor_ui::view_max()}; + auto const width{std::max(480.0f, high.x - low.x)}; + auto const height{std::clamp((high.y - low.y) * 0.4f, 220.0f, std::max(220.0f, high.y - low.y - 80.0f))}; + ImGui::SetNextWindowPos(ImVec2(low.x, high.y - height), ImGuiCond_Appearing); + ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_Appearing); + auto const name{std::string{STR_C("Station schematic")} + " " + title() + "###srkstrip"}; + if (ImGui::Begin(name.c_str(), nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) + { + auto const position{ImGui::GetWindowPos()}; + auto const size{ImGui::GetWindowSize()}; + if (false == ImGui::IsWindowDocked() && (position.y + size.y > io.DisplaySize.y + 1.0f || position.x + 80.0f > io.DisplaySize.x || position.x + size.x < 80.0f)) + ImGui::SetWindowPos(ImVec2(std::clamp(position.x, 0.0f, std::max(0.0f, io.DisplaySize.x - size.x)), std::max(0.0f, std::min(position.y, io.DisplaySize.y - size.y)))); + if (ImGui::Button(STR_C("Fit"))) + m_view_state.exaggeration = 0.0f; + item_tooltip("Shows the whole station"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(140.0f); + ImGui::SliderFloat("##srkexaggeration", &m_view_state.exaggeration, 1.0f, 500.0f, STR_C("offsets x%.0f"), ImGuiSliderFlags_Logarithmic); + item_tooltip("How much the offsets of the tracks are stretched against the lengths, also Ctrl+wheel over the plot"); + ImGui::SameLine(); + ImGui::TextDisabled("%s", STR_C("Wheel: zoom, drag: move, LMB on a track: select it")); + render_canvas(ImGui::GetContentRegionAvail().y); + } + ImGui::End(); +} + +editor_selftest_result srk_tool::selftest(std::string const &Command, std::istringstream &Words) +{ + if (Command == "srkopen") + { + // srkopen track=: the station of the track, as a click on it opens it + auto const values{selftest_values(Words)}; + auto const found{values.find("track")}; + auto *track{found != values.end() ? simulation::Paths.find(found->second) : nullptr}; + m_open = true; + load(); + if (track == nullptr) + { + WriteLog("SELFTEST srkopen: no such track"); + return editor_selftest_result::handled; + } + open(*track); + if (false == m_layout.has_value()) + { + WriteLog("SELFTEST srkopen: " + m_status); + return editor_selftest_result::handled; + } + auto const &layout{*m_layout}; + std::unordered_set tracks; + std::size_t approach{0}; + for (auto const &path : layout.paths) + if (path.approach) + ++approach; + else + tracks.insert(path.track); + WriteLog(format("SELFTEST srkopen %s: %zu switches, %zu tracks, %zu paths out, axis %.0f m, switches over %.0f..%.0f, offsets %.1f..%.1f, km %s, record %d", m_anchor.c_str(), layout.switches.size(), tracks.size(), approach, + layout.chainage.back(), layout.from, layout.to, layout.low, layout.high, layout.kilometrage ? format("%.3f", layout.km).c_str() : "-", m_record)); + for (auto const *track : layout.switches) + { + auto const at{station::project(layout, track->m_paths.front().points[segment_data::point::start])}; + WriteLog(format("SELFTEST srkopen switch %s at %.1f offset %.1f", track->name().c_str(), at.x, at.y)); + } + return editor_selftest_result::handled; + } + if (Command == "srkstations") + { + load(); + find_all(); + WriteLog(format("SELFTEST srkstations: %zu", m_found.size())); + for (auto const &[name, count] : m_found) + WriteLog(format("SELFTEST srkstations %s: %zu switches", name.c_str(), count)); + return editor_selftest_result::handled; + } + if (Command == "srkset") + { + // srkset code= line= name= + if (false == m_layout.has_value()) + { + WriteLog("SELFTEST srkset: no station open"); + return editor_selftest_result::handled; + } + auto const values{selftest_values(Words)}; + auto &entry{record_of_open()}; + if (auto const code{values.find("code")}; code != values.end()) + entry.code = code->second; + if (auto const line{values.find("line")}; line != values.end()) + entry.line = line->second; + if (auto const name{values.find("name")}; name != values.end()) + { + entry.name = name->second; + std::replace(entry.name.begin(), entry.name.end(), '_', ' '); + } + m_changed = true; + WriteLog("SELFTEST srkset: " + title()); + return editor_selftest_result::handled; + } + return editor_selftest_result::unhandled; +} diff --git a/application/editormodesrk.h b/application/editormodesrk.h new file mode 100644 index 00000000..2460bda5 --- /dev/null +++ b/application/editormodesrk.h @@ -0,0 +1,81 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include "application/editortool.h" +#include "editor/editorStation.hpp" +#include "scene/scenelayers.h" + +#include +#include +#include +#include + +// the signalling of a station (SRK): the station found from a switch of it, its schematic, its name and code +class srk_tool final : public editor_tool +{ + public: + srk_tool(editor_selection &Selection, editor_view &View, editor_history &History) : m_selection{Selection}, m_view{View}, m_history{History} {} + void update() override; + void draw_overlay() const override; + void render_options() override; + void render_windows() override; + void forget(scene::basic_node const *Node) override; + editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) override; + void set_open(bool const Open) { m_open = Open; } + // the stations described go to the files of their layers, as "//$s" lines + void store(); + + private: + editor_selection &m_selection; + editor_view &m_view; + editor_history &m_history; + // a station described: found again by a switch of it + struct record + { + scene::layer_handle layer{null_handle}; + std::string anchor; // the name of a switch of the station + std::string code; // the abbreviation of the station, as MMz + std::string name; + std::string line; // the number of the railway line + }; + // the schematic: the chainage across, the offsets upwards, exaggerated + struct view + { + double from{0.0}; + double to{1000.0}; + double centre{0.0}; + float exaggeration{0.0f}; // 0: fit at the next draw + }; + bool m_open{false}; + bool m_loaded{false}; + bool m_changed{false}; + std::vector m_records; + std::optional m_layout; + int m_record{-1}; // of the open station, -1 while it has none + std::string m_anchor; // the switch the open station was found from + std::size_t m_history_size{0}; + TTrack const *m_picked{nullptr}; // the track selected when the station was looked for + view m_view_state; + int m_hover{-1}; // path of the layout under the mouse in the schematic + bool m_dragging{false}; + std::vector> m_found; // the stations of the scenery: a switch and the count of them + std::string m_status; + + void load(); + void open(TTrack &Track); + void refresh(); + record &record_of_open(); + std::string title() const; + void render_found(); + void render_canvas(float const Height); + void fit_view(float const Width, float const Height); + void find_all(); +}; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index bdb4d216..7da24b60 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -411,9 +411,9 @@ void editor_mode::restore_track_snapshot(EditorSnapshot const &Snapshot, std::ve profile_restore(); } -std::array const &editor_mode::track_modes() +std::array const &editor_mode::track_modes() { - static std::array const modes{{ + static std::array const modes{{ {"Select", "Esc", track_tab::path, "LMB on a path selects it: its points, control vectors and parameters", editor_icons::icon::select, 0}, {"Lay track", "L", track_tab::lay, "Lays new track through the clicked points, on the ground or from a free end of a track", editor_icons::icon::lay_track, 0}, {"Plan", "G", track_tab::straights, "The plan of the line laid: LMB on a straight drags its ends or the middle, on a curve its vertex and radius. C works too", editor_icons::icon::curve, 0}, @@ -421,6 +421,7 @@ std::array const &editor_mode::track_modes() {"Parallel", "N", track_tab::parallel, "A second track beside a track: LMB on it, then the side and the spacing", editor_icons::icon::parallel, 0}, {"Turntable", "U", track_tab::turntable, "Places a turntable, leads tracks out of it and fits the tracks around to it", editor_icons::icon::turntable, 1}, {"Signals", "H", track_tab::signals, "Signals by the track, from the templates of the scenery, with the event the train reads them by", editor_icons::icon::signal, 1}, + {"Signalling", "I", track_tab::srk, "The signalling of a station: LMB on a switch lays its station out as a schematic", editor_icons::icon::station, 1}, {"Objects", "B", track_tab::lineside, "Along the track: hectometre posts, fouling point markers, trainsets", editor_icons::icon::objects, 1}, {"Profile", "P", track_tab::profile, "Vertical profile (grade line) along the line", editor_icons::icon::profile, 2}, {"Speed", "V", track_tab::speed, "Speed limits of the paths against the speed their geometry allows", editor_icons::icon::speed, 2}, @@ -565,6 +566,7 @@ std::string editor_mode::track_mode_name() const case track_tab::infra: return STR_C("Infrastructure"); case track_tab::parallel: return STR_C("Parallel track"); case track_tab::signals: return STR_C("Signal"); + case track_tab::srk: return STR_C("Signalling"); case track_tab::turntable: return STR_C("Turntable"); default: return STR_C("Select"); } @@ -577,6 +579,7 @@ void editor_mode::render_track_inspector() m_speed.set_open(m_track_window_open && m_track_tab == track_tab::speed); m_infra.open = m_track_window_open && m_track_tab == track_tab::infra; m_joints.set_open(m_track_window_open && m_track_tab == track_tab::joints); + m_srk.set_open(m_track_window_open && m_track_tab == track_tab::srk); if (false == m_track_window_open || m_track_tab != track_tab::lineside) { m_sweep.open = false; @@ -618,6 +621,7 @@ void editor_mode::render_track_inspector() m_profile.strip_top = -1.0f; if (m_track_tab == track_tab::profile && m_track_window_open) render_profile_strip(); + m_srk.render_windows(); } void editor_mode::render_track_tool_options() @@ -659,6 +663,7 @@ void editor_mode::render_track_tool_options() break; case track_tab::lineside: render_lineside_ui(); break; case track_tab::signals: render_signal_ui(); break; + case track_tab::srk: m_srk.render_options(); break; case track_tab::turntable: render_turntable_ui(); break; default: render_track_set_ui(); diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index f40348d3..1c562ed1 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -458,6 +458,13 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods) } return intent; } + if (m_track_tab == track_tab::srk && m_hover.track != nullptr) + { + intent.track = m_hover.track; + intent.path = m_hover.path; + intent.position = m_hover.point; + return make(kind::select, m_hover.track->eType != tt_Normal ? "Click: the station of this switch" : "Click: the station of the switch nearest along this track"); + } if (m_track_style.picking && (m_track_tab == track_tab::path || m_track_tab == track_tab::turnout)) { intent.track = m_hover.track; diff --git a/application/editormodeviewport.cpp b/application/editormodeviewport.cpp index 1bbed3fa..3f9887f7 100644 --- a/application/editormodeviewport.cpp +++ b/application/editormodeviewport.cpp @@ -177,10 +177,10 @@ void editor_mode::work_area_tools(std::vector &Buttons) add(track_tab::straights, false); add(track_tab::turnout, false); add(track_tab::parallel, false); - auto const advanced{m_track_tab == track_tab::turntable || m_track_tab == track_tab::signals || m_track_tab == track_tab::lineside || m_track_tab == track_tab::profile || m_track_tab == track_tab::speed || + auto const advanced{m_track_tab == track_tab::turntable || m_track_tab == track_tab::signals || m_track_tab == track_tab::srk || m_track_tab == track_tab::lineside || m_track_tab == track_tab::profile || m_track_tab == track_tab::speed || m_track_tab == track_tab::joints || m_gauge.is_open()}; auto const more{EditorSettings.track_tools_more()}; - Buttons.push_back({icon::more, more ? STR_C("Less") : STR_C("More"), nullptr, STR_C("Turntable, signals, objects, trainset, profile, speed, joints, gauge"), more, [more]() { + Buttons.push_back({icon::more, more ? STR_C("Less") : STR_C("More"), nullptr, STR_C("Turntable, signals, signalling, objects, trainset, profile, speed, joints, gauge"), more, [more]() { EditorSettings.track_tools_more(false == more); EditorSettings.save(); }, true}); @@ -188,6 +188,7 @@ void editor_mode::work_area_tools(std::vector &Buttons) break; add(track_tab::turntable, true); add(track_tab::signals, false); + add(track_tab::srk, false); add(track_tab::lineside, false); Buttons.push_back({icon::vehicle, STR_C("Trainset"), nullptr, STR_C("A trainset on the selected track: written to the scenery on save, and to drive at once"), vehicle, [this, vehicle]() { show_track_tab(vehicle ? track_tab::path : track_tab::lineside); diff --git a/application/editortool.h b/application/editortool.h index f93503f8..744a7646 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -36,7 +36,7 @@ enum class editor_selftest_result enum class editor_first_step { tracks, lay, laid, wheel, join, turnout, undo, save, drive, look, top, select, shape, profile, profile_point, profile_apply, terrain, nmt, plan, plan_band, plan_apply, signal, posts, count }; -enum class editor_track_tab { straights, route, path, turnout, profile, speed, joints, infra, lay, lineside, turntable, signals, parallel }; +enum class editor_track_tab { straights, route, path, turnout, profile, speed, joints, infra, lay, lineside, turntable, signals, parallel, srk }; struct track_hover { diff --git a/editor/editorIcons.hpp b/editor/editorIcons.hpp index eec9f4a8..8b583b40 100644 --- a/editor/editorIcons.hpp +++ b/editor/editorIcons.hpp @@ -51,6 +51,7 @@ enum class icon gauge, turntable, parallel, + station, more, // the fields of work work_surroundings, diff --git a/editor/editorStation.cpp b/editor/editorStation.cpp new file mode 100644 index 00000000..c60aa4a0 --- /dev/null +++ b/editor/editorStation.cpp @@ -0,0 +1,399 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#include "stdafx.h" +#include "editor/editorStation.hpp" +#include "editor/editorAlignmentStore.hpp" +#include "editor/editorGeometry.hpp" +#include "editor/editorSwitchStore.hpp" +#include "editor/editorTrack.hpp" +#include "simulation/simulation.h" +#include "world/Track.h" + +#include +#include +#include +#include +#include + +namespace station +{ + +using geometry::bezier; +using geometry::plan_distance; +using geometry::same_point; + +namespace +{ + +bool rail(TTrack const &Track) +{ + return (Track.iCategoryFlag & 15) == 1 && false == Track.m_editorremoved && false == Track.m_paths.empty(); +} + +// switches, crossings and turntables: where the plain track between them ends +bool node(TTrack const &Track) +{ + return Track.eType != tt_Normal; +} + +glm::dvec3 end_of(TTrack const &Track, int const Path, bool const Atend) +{ + return Track.m_paths[Path].points[Atend ? segment_data::point::end : segment_data::point::start]; +} + +TTrack *neighbour(TTrack const &Track, int const Path, bool const Atend) +{ + if (Track.SwitchExtension != nullptr && (Track.eType == tt_Switch || Track.eType == tt_Cross)) + return Atend ? Track.SwitchExtension->pNexts[Path & 1] : Track.SwitchExtension->pPrevs[Path & 1]; // NOSONAR + return Atend ? Track.trNext : Track.trPrev; +} + +// the end of the track at the point +bool end_at(TTrack const &Track, glm::dvec3 const &Point, int &Path, bool &Atend) +{ + for (int path = 0; path < static_cast(Track.m_paths.size()); ++path) + for (auto const atend : {false, true}) + if (glm::distance(end_of(Track, path, atend), Point) < same_point) + { + Path = path; + Atend = atend; + return true; + } + return false; +} + +// the ends of the paths of a switch, each place once: its start, the ends of its paths +std::vector> ends_of(TTrack const &Track) +{ + std::vector> result; + for (int path = 0; path < static_cast(Track.m_paths.size()); ++path) + for (auto const atend : {false, true}) + { + auto const point{end_of(Track, path, atend)}; + if (std::none_of(result.begin(), result.end(), [&](auto const &End) { return glm::distance(end_of(Track, End.first, End.second), point) < same_point; })) + result.emplace_back(path, atend); + } + return result; +} + +double plan_length(TTrack const &Track, int const Path) +{ + return bezier{Track.m_paths[Path]}.plan_length(16); +} + +// plain track from an end of a switch on, up to the next switch, a free end or past the limit +struct chain +{ + std::vector tracks; + double length{0.0}; + TTrack *reached{nullptr}; // the switch met at its other end + bool ended{false}; // at a free end +}; + +chain follow(TTrack const &From, int const Path, bool const Atend, double const Limit) +{ + chain result; + auto point{end_of(From, Path, Atend)}; + auto *next{neighbour(From, Path, Atend)}; + while (next != nullptr && result.length <= Limit) + { + if (false == rail(*next)) + break; + if (node(*next)) + { + result.reached = next; + return result; + } + int path{0}; + bool atend{false}; + if (false == end_at(*next, point, path, atend) || std::find(result.tracks.begin(), result.tracks.end(), next) != result.tracks.end()) + break; + result.tracks.push_back(next); + result.length += plan_length(*next, 0); + point = end_of(*next, 0, false == atend); + next = neighbour(*next, 0, false == atend); + } + result.ended = next == nullptr || false == rail(*next); + return result; +} + +// the switches and the plain tracks of the station, and the lines leading out of it +struct area +{ + std::vector switches; + std::vector plain; + std::vector exits; +}; + +area collect(TTrack &From, options const &Options) +{ + area result; + if (false == rail(From)) + return result; + auto *first{node(From) ? &From : nullptr}; + if (first == nullptr) + { + auto nearest{Options.link}; + for (auto const atend : {false, true}) + if (auto const line{follow(From, 0, atend, Options.link)}; line.reached != nullptr && line.length < nearest) + { + nearest = line.length; + first = line.reached; + } + } + if (first == nullptr) + return result; + std::unordered_set known{first}; + result.switches.push_back(first); + for (std::size_t i = 0; i < result.switches.size(); ++i) + { + auto const &from{*result.switches[i]}; + for (auto const &[path, atend] : ends_of(from)) + { + auto line{follow(from, path, atend, Options.link)}; + if (line.reached == nullptr && false == line.ended) + { + result.exits.push_back(std::move(line)); + continue; + } + for (auto *track : line.tracks) + if (known.insert(track).second) + result.plain.push_back(track); + if (line.reached != nullptr && known.insert(line.reached).second) + result.switches.push_back(line.reached); + } + } + return result; +} + +std::vector points_of(TTrack const &Track, int const Path, double &Length) +{ + bezier const curve{Track.m_paths[Path]}; + Length = curve.plan_length(16); + auto const count{std::clamp(static_cast(Length / 2.0), 2, 64)}; + std::vector result; + for (int i = 0; i <= count; ++i) + result.push_back(curve.point(static_cast(i) / count)); + return result; +} + +// the map shows the east to the right and the north up: the x of the scenery runs to the west +glm::dvec2 map_of(glm::dvec3 const &Point) +{ + return {-Point.x, Point.z}; +} + +} // namespace + +std::optional find(TTrack &From, options const &Options) +{ + auto const found{collect(From, Options)}; + if (found.switches.empty()) + return std::nullopt; + std::unordered_set switches(found.switches.begin(), found.switches.end()); + // the axis: the run through the station along the main paths of its switches which passes the most of them + std::vector starts; + for (auto const &line : found.exits) + if (false == line.tracks.empty()) + starts.push_back(line.tracks.front()); + if (starts.empty()) + starts.push_back(found.switches.front()); + editor_track::route best; + std::size_t most{0}; + double longest{0.0}; + for (auto *start : starts) + { + auto route{editor_track::run_route(*start, 20000.0)}; + std::size_t passed{0}; + double length{0.0}; + for (auto const &span : route.spans) + { + passed += switches.count(span.track); + length += plan_length(*span.track, span.path); + } + if (passed > most || (passed == most && length > longest)) + { + most = passed; + longest = length; + best = std::move(route); + } + } + layout result; + for (auto const &span : best.spans) + { + double length{0.0}; + auto points{points_of(*span.track, span.path, length)}; + if (false == span.forward) + std::reverse(points.begin(), points.end()); + for (auto const &point : points) + if (result.axis.empty() || plan_distance(result.axis.back(), point) > 0.01) + result.axis.push_back(point); + } + if (result.axis.size() < 2) + return std::nullopt; + auto const measure = [&]() { + result.chainage.assign(1, 0.0); + for (std::size_t i = 1; i < result.axis.size(); ++i) + result.chainage.push_back(result.chainage.back() + plan_distance(result.axis[i - 1], result.axis[i])); + }; + measure(); + // the chainage runs with the km of the switches, when they have one and most of them agree on it + double constexpr kAgree{50.0}; // m + std::vector> marks; // the chainage and the km, in m + for (auto *track : found.switches) + { + auto const start{end_of(*track, 0, false)}; + std::optional km; + if (auto const record{switch_store::named(*track)}; record && record->km) + km = record->km; + else + km = alignment_store::chainage_at(start); + if (km) + marks.emplace_back(project(result, start).x, *km); + } + auto const length{result.chainage.back()}; + struct fit + { + std::size_t agree{0}; + double origin{0.0}; + }; + auto const fit_of = [&](bool const Reversed) { + std::vector origins; + for (auto const &[chainage, km] : marks) + origins.push_back(km - (Reversed ? length - chainage : chainage)); + std::nth_element(origins.begin(), origins.begin() + origins.size() / 2, origins.end()); + fit result{0, origins[origins.size() / 2]}; + for (auto const origin : origins) + result.agree += std::abs(origin - result.origin) <= kAgree ? 1 : 0; + return result; + }; + if (false == marks.empty()) + { + auto const along{fit_of(false)}; + auto const against{fit_of(true)}; + auto const reversed{against.agree > along.agree}; + auto const &best{reversed ? against : along}; + if (best.agree * 2 >= marks.size()) + { + if (reversed) + { + std::reverse(result.axis.begin(), result.axis.end()); + measure(); + } + result.kilometrage = true; + result.km = best.origin / 1000.0; + } + } + // the paths, laid out along the axis + auto const add = [&](TTrack &Track, bool const Approach) { + for (int index = 0; index < static_cast(Track.m_paths.size()); ++index) + { + path entry; + entry.track = &Track; + entry.index = index; + entry.approach = Approach; + entry.points = points_of(Track, index, entry.length); + for (auto const &point : entry.points) + entry.schematic.push_back(project(result, point)); + result.paths.push_back(std::move(entry)); + } + }; + for (auto *track : found.switches) + add(*track, false); + for (auto *track : found.plain) + add(*track, false); + for (auto const &line : found.exits) + { + double length{0.0}; + for (auto *track : line.tracks) + { + if (length >= Options.approach) + break; + add(*track, true); + length += plan_length(*track, 0); + } + } + result.from = std::numeric_limits::max(); + result.to = std::numeric_limits::lowest(); + result.low = std::numeric_limits::max(); + result.high = std::numeric_limits::lowest(); + for (auto const &entry : result.paths) + { + if (entry.approach) + continue; + for (auto const &point : entry.schematic) + { + if (switches.count(entry.track) > 0) + { + result.from = std::min(result.from, point.x); + result.to = std::max(result.to, point.x); + } + result.low = std::min(result.low, point.y); + result.high = std::max(result.high, point.y); + } + } + result.switches = found.switches; + std::sort(result.switches.begin(), result.switches.end(), [&](TTrack const *A, TTrack const *B) { return project(result, end_of(*A, 0, false)).x < project(result, end_of(*B, 0, false)).x; }); + return result; +} + +glm::dvec2 project(layout const &Layout, glm::dvec3 const &Point) +{ + auto const point{map_of(Point)}; + auto best{std::numeric_limits::max()}; + glm::dvec2 result{0.0}; + for (std::size_t i = 1; i < Layout.axis.size(); ++i) + { + auto const a{map_of(Layout.axis[i - 1])}; + auto const b{map_of(Layout.axis[i])}; + auto const along{b - a}; + auto const length{glm::length(along)}; + if (length < 1e-9) + continue; + auto const unit{along / length}; + auto const t{std::clamp(glm::dot(point - a, unit), 0.0, length)}; + auto const foot{a + unit * t}; + auto const distance{glm::distance(point, foot)}; + if (distance >= best) + continue; + best = distance; + auto const side{point - foot}; + result = {Layout.chainage[i - 1] + t, unit.x * side.y - unit.y * side.x}; + } + return result; +} + +double km_of(layout const &Layout, double const Chainage) +{ + return Layout.km + Chainage / 1000.0; +} + +std::vector stations(options const &Options) +{ + std::vector result; + std::unordered_set taken; + for (auto *track : simulation::Paths.sequence()) + { + if (track == nullptr || false == rail(*track) || false == node(*track) || taken.count(track) > 0) + continue; + auto const found{collect(*track, Options)}; + for (auto const *member : found.switches) + taken.insert(member); + result.push_back(track); + } + return result; +} + +std::vector switches_of(TTrack &Switch, options const &Options) +{ + return collect(Switch, Options).switches; +} + +} // namespace station diff --git a/editor/editorStation.hpp b/editor/editorStation.hpp new file mode 100644 index 00000000..96c283a0 --- /dev/null +++ b/editor/editorStation.hpp @@ -0,0 +1,65 @@ +/* +This Source Code Form is subject to the +terms of the Mozilla Public License, v. +2.0. If a copy of the MPL was not +distributed with this file, You can +obtain one at +http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include +#include + +#include + +class TTrack; + +// a station as the signalling sees it: its switches, the tracks between them and the lines leading out, and its schematic, +// the tracks laid out along the main track through it +namespace station +{ + +struct options +{ + double link{1500.0}; // m, switches joined by plain track no longer than this belong to the same station + double approach{400.0}; // m, of each line leading out, taken beyond the outermost switch +}; + +struct path +{ + TTrack *track{nullptr}; + int index{0}; // of the path of the track + std::vector points; // along the path, world space + std::vector schematic; // the chainage along the axis and the offset to the left of it, as the map shows it + double length{0.0}; // in the plan + bool approach{false}; // on a line leading out of the station +}; + +struct layout +{ + std::vector switches; // switches, crossings and turntables, by the chainage of their start + std::vector paths; + std::vector axis; // the main route through the station + std::vector chainage; // of the points of the axis + double from{0.0}; // the chainage over which the switches lie + double to{0.0}; + double low{0.0}; // the offsets of the outermost tracks of the station + double high{0.0}; + bool kilometrage{false}; // the chainage runs with the km of the switches + double km{0.0}; // at chainage 0, when it does +}; + +// the station of the switch, or of the plain track near one +std::optional find(TTrack &From, options const &Options = {}); +// the chainage along the axis and the offset to the left of it, as the map shows it +glm::dvec2 project(layout const &Layout, glm::dvec3 const &Point); +// km at the chainage, when the station knows its kilometrage +double km_of(layout const &Layout, double const Chainage); +// one switch of each station of the scenery +std::vector stations(options const &Options = {}); +// the switches of the station of the switch, without laying it out +std::vector switches_of(TTrack &Switch, options const &Options = {}); + +} // namespace station diff --git a/editor/icons/editor_icons.png b/editor/icons/editor_icons.png index affe75b5021dd99818f350ec3adb81cf0c483fa3..c720314949e1c43ffdcc2d39690640ffc635a039 100644 GIT binary patch literal 47042 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

+94g;`Ri|;SNhUC#~ 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 JYSgU5{tsfq-rxWL literal 46348 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 z zWJv6%^A0j!de}|9;N(x*YG>K-g66n(iA8Jsp_HJ;=`%&mFU5H?$y-daywZ%9{xW)Q zPKw^?*@%G}ZA8F$$jHchBM%IidDbb(r3_Ym8yw8|*10PdX4v*V7B>c;zccffbZs}wEgdBNVSKZ6YZ^6rxh;AH>UCWjb*FG`hGN~|0f zBvxU=Ln8CUv9WRy5VN9rn{a)OZ@ROjfzw^L_7DX66^cg z%~MfIpcSU>E^8rkFBZkMvNUIYIaBNAg6Sh1a5-O0vtAMAH2T1Ph6|MNYu1Zd*=SP((GtCvWB;5km zD_sV7$oGFJ?Uj1|6OLz8SX^E~{P)j>@k7^jhJ$N6RjkjWMgbjN94dJuNOvSzLDT@!Jp_EO+lo@&H1<;g(Zi{edwy=z9y_h8A&yrb zI{u-Ji;ie_xaBANWb;#;9LAY|lhECMIU&2w_df<&TGA}}6^jKtd7C|FFuY%7CEFe^ zcJBGYD99j8RCf$H+nYtCWO`1Ii{4qZDt3?8GKNfh9uaZXQ#1y?lB$>=z(+lYCfloe z`%ygX=gd|en(3Z^DLfS@xiuqbzlC4_eK_s)=li5{00&3`n>y4pMRAr!!sp(>RP)<9@{x;T)$F0-W_I$0hQcVFdyDN zR?hKUEWN<{@KAS_nPguDoH+4Jkdin)cNGsy$`cUJWV-(pluhO|<_X}-U8wbDR^iW{ zFmst+_}TL#k#U)*tlFD&-CrvzUNtk6Mv2J6JY=86t1To0usA)+_T`fU=D&{@QC(i5 z*9SBqxX$QBQl$p)5j3`zOE&IptI5nBal1i^@uiP{X!32kPGYqjY^eZ;Tt-}F ze!N|eF3sNY>wNHo>-ojns}ytUU#qY5;*HNvmgQbEpeut-YAh|j|A%V*>Zro$)|dX0 z!|lq&Zfbed8}w1KOB*=VXKJT*MRpLE+F}r~RSsMg&|2|-&)ZwpOALMsJHhkwF9QAt zs8Q{cMV$8PWDFJQfV=V}9vAi{eK(jq`VVb$9mk+;eH#^bhZs8TKh~`B8T5a&+kfx7 z;$wKYWc$wd0P9CQczp+uYj!7Em8%rEcN@&<+VlTUICnE5XnzdWfSrjEvhWf$>GTg{ z+<<}w;wk$P@{amwnFt9R-!!@&nh+@h+_0Y4CrG1pg^IPac91!cs}4Vn$KxZkiiz!5 zaw|10T!mVcmcw$uWbl_&Vl9ckUYX`Ln_V`ew;u^KmDBpXl$N^Q4ASM7YGdQm)22eF zM7#=UUzZrV12{l)64Z*nIBvQ%!gzK2AeawN+woSvx|_q|`1Q-&>$Uyb8<4*YWQPK6a_k(qxm|w0r(H-OG0JxMY^G)5c16 zhAS7qA7o|loKDB<@t;EObPaBuF2fh=|pV5cVig#!GP%gly!9@HOhbE9~)ytG?k={!sfyC!=?7y96u5i>;l~W!H9Z3>iS!JrF@}bWHLF^3;~J zxzRE9m6p@X%-avTdO+*AcHR)$f|LZsA)&qo96tiy)F4KT@kqHZUSG7n6@4xxr+FeX zI?j)gL;43LFMoB+_yyLGh>wmWd-k0<9_j(UV&WWo?D22{v+zvgVy$yVwRi`BkxTRh zMt$Kn0IT)Rs?~G+akQv-H3@E^X2#L&XvN*W;n%_&E_+35?H;GBLr_ca6qPD`%;;sh zizien;iN_IUEfB(ZBAB*soUMbeZP^4*&O6WkUgx47uVv2&kdZD4$SjCanR9=l$^gs zNW5+jU`_OeuM?ma^gVT_d0CbR(lp_)c3Xz7>xN>Qagzb9X$ zpb&nyrZ3u?z>w1=#$WGaBd%t|b0g4kk^q77&WSi1w4c)RflKhhXb?TG1z+VD_)e3( zodFMBBJn2W_>;>k8cxs)sUtQZHc;#S@91Aia1wNiLQt$-dV&HW{Ye!cPAAw_-F=|R z!dg>rBgsRuvG?ph74;|3Q}ch9S?nl3;4--VqkUa1+6B<(gmfEVs{tFIfq8u;=lEMc z5(WnHxF((c`-VjZ`j_+*3lDB2pTiA@hXyWY`(Jp zlmu&lj{z@$Ea(p9@sK^IXd9^{+71D&RNTnidx#cydg!DCSJ{n8;ebC8b7TR8dF5>8 zc$dM}o<60hOnyTom$Lt-%*7uXBsC~Mv-CEEr}!GqzyRHlgk=3 z4gNhhvj$w)C0^iqg0UgNSyfSwdB4opZ2gTcKih=q+^>J|oam4#_jFm6dQIjzmkh5u zPP=2)Xg6)U-N}4Guz};)i}YOe?BY6|1U-PrJU$|@=)hh_voYh;9CtyIn9+74AEbZT z^%|e#waGMnaGDGg|u^i80Clty&$daDim!L^I}qU1Rs=@I3sX+v9U0{9Ui%C z-Af*2W59xnl-%+njp4EC=8@Ux!nFivN^?x!9B<;nTYPLq|JVM$_}d(ga_`xC(Qd%YvgUwfs?S49 zA$cEpJA9-xkaghRp#y9SaZS?V{>9k0^^uR zr{>A~W;{#lhz|)1STzfwhWQ=A#qVV)N7ShxikC+a&vf2vTa|$4nF3LQ#~M@|oRP=o z@g$jhFP;@1S3wr3uu|}6G2J5E9>SNBtD5#Xl@g)1)q?eE!&#>KSx>|#naE6VW0B8C zpJ%+K_uL7>s=V#QMwhj`(=vm?L+aMLJ18$tC@thaNbBS5C`|AO{s}j*G=H;6LVXwz z4fA_ii1)tRtws6C;}LaW6iJe{eL1AbQHY@Jn2^8jg^^!MC9l`TAE!15sClJF;Xgi@ zlxpj-`AY&WoNmpVkPIRUk6;I)MJ9b%kA<@u*25B<9YF3v4)VUj!+vNttYKA&-iHFD zTo=luk)O-3Y0bYD*X;yw+PhTf+5TwBp^)~fw2^-@_KgW!rfgIJ^{nylfeudqshXX- zjn9=}NLL&R6-V`0O2N*2_MEF+1y!Y#`MfA(Up)lP3nMX7rzm^5w(~UZR#zJ74IBXzpe<*qOn{t-rYVvAP`zvu?6Fni_#4&36`IWFIJ0{1+hp zwNFDwEaZ!R$jbyUBx>LU;_ADUlw}qG!)&W`a*Hrn`BSK-iporezsAk*h$AdS5q!T) zMrlngEk-UkR?^2<`;>iPk}yxWOG44#TZ@b0UlxC4c}?nzuQ8zaXa%TAKcrTiQz9=E zJ)T1tVMo)fTE->vp`P6CophnR;Te7VL8xY zdgP^AF|-z>>b3HQbH@sVxP|A=D$n{w?s8qF2@ylieLpSyk<(a3bQhF=RKtafUI<2% ztvJ=f`-)-vEd6AbOrMy8G-lM3zgIktG-+G#l|Q;-IFAYD9Z;vfZUvY?EoG04OT1lP z`ETEft~ymM2{V|gCxw57p`c5&TuWFXyYsU{Pk!2;`6pgiBp1ih~KOu1cR2(Fpvy{Dng@vnHfk!J?AQCKC)JI$mKe6(iC-}|() zaTe@ZHvVod%c!j^P{s?rGpPVsq)0j!;rk^cUb%z8u@u~dor<;sDj zN&Cua5Pw61eVeB4I3RCCq2kv)4foDVtYLqA0sH5^0SR1T+B&~E9chnyGgjAK@J}-; zN|Bj_y^Uu^5oYp%oA+3wTg^kGz9q%CP?k?~Dm>AWrx9#l-f5+7y#zWTT&89?feiYM zNg@l+;h-s(qbf1OFWwea09mrcl3T+cHmQXm)&FOnMXsl zU{bf#YFXb)sMargX`BO@ilWq_d;R*LAI_&m9Ftxt)VVR+QU6H zkGq$60U%d3T{;pMnQ-KF#z&g%Yp&SoO-DC9y)B{#QxWPw&Uob)8;CUlu)H2w z@*yk16&Dbo7j|F$Az2cJb%sY&@2;9Co;RMBKQt+c1t|&ehmfw^m*WyP`sy)c21F?l z(dQ}p6ZZidBzTJcc}d}G3Ah0@OR?C3Wb@^vRG)j8Gh~lke+F-hv6ojN=ksn zOh^5rTmcF45S%}bPh`3{Nsnbq=`D1}_wg@#*nx@PTW-HARUlFMHBeR0<-X4KR2bIu zM7SimPPHOI5k%UM3~0%((n)MBE%lP8g8Y4*kZmJ~kP*W>Jq->+`Lo=I=c`B%k9u^* zO`121-*HX)P#Yh*?^Ix9k3Tw8g7z76D<7usHG$d}deUu+2m%C+cEMM%qgJKDSDmms zX3clGtA`iw3+OE#C3#F*tGP#kHSAew6@r!YC*CCl@0XNn?F;{G4<>i;WFpALshE^1 z^bcU)Ml&Ih&Fb`2kPmE9BbcQQZlTh*>4_BG1YTR4r-#<_O(C(IK3Q8!q~mt%fHDfd z{PKolHoDJg&)11kx|sHtrN2;8K(IWRx$jqZPesY_)yw99Wja0)rJf4{++<4X(Xz(H z$6IRw4|Gr*D#+vk6cm^oD1XyQ6-M$rteBg(eS<$0?(B{Kk9$%|K%Kg#@h-}w3+;edOQQU0 z4aefVV{r$&)!>uJAvVRP1=elYa|n|`j`c32TP4-g!>d|^!yMNorR}JE?F5vHp1d>0 zs6%H$N%2%${Pqv?FG|x(=HKCU4 z7uK>Y9zXTQakt&1IEhB=wYWDpH0WpB<^dcfzy#L<$sq@4b*A=sd!S7NF(L{f{`~Oz zk3j-+NOglBMbd()cD8xXOLan(sEhK9GRgz4FpMdyBj?|(kbqx)Q*R2Itf)Yxh~Z48 z*JEfMstU#5PBe}L=1YXKnFez|B z;P(x5EU*)lg>A9VF?a6^t(3)CxH~Mgf?KjlDd0J%p4PE}MkW)elkn``6$^g>-PwC94gf>bYhQJM`c< zN`E3QktlnCSe8bt6x$(4$~3Y?0R~+NrPndQt(7b)nzvSl&JBSfq@YjQ|AyfFA!0E<98xv$BxYi_F00x~ITE>e4yoUrty~T(55TM#%_D`hPmiF7w=A0HymUTEe%0njC5iSAJg)-jKn3w&KkYD9OxrZSc3sxdvT|iRFblJrVI^`NvELNx& zpM7`0^yAw!jAwPZgh3-|svUx#8*HQJ2`aS}ScPMlNNt=XDMg})ukxEJLH z`K_#z)QJ3iHIKHaMtw$2Tp^UU zfZe5ss*7nmF@E+q<2U(Pr7B=}LlS$VN%a&}H(chQyy}=)N#soiG(UH`ap%d@dWcTU zR>sd@-mirR=-?fB%OqqAu;-Y!y!^#UK~u96Ai=3i$=(Md+>}lF))j;>dxZ#ygu6^V zU*x6jIqQ0eY1WWn)SRIfjVr_7dLoVLe+(L-!s} ze$7z{Z2jAo)0YJNyj8cHoC^eAj1S5c1lF8z;L;o$>*Z~P1YA=AC*XD*Fd}0CYdkio zrXbz$xBG{Q@**Y9h2$Mf)DNUU=~IKMm!+bQ-Z>PX_4;dq5PuhS9v1G1x;fZJrVZbo zJ(j48fEBEiP%<+)nv#@~)pO@!ghQs_-gdn3EH;GvG{Xn?tK!(i zn-FgpnK@-DTsq7eVL+G$@xWVqFz2e%lk>x&K*TgZ_hivgDdgiPaE|F2P4HIg{%gu1 zp$Mka&+L}>V|me9y*|{JbB}q1?fwRICV$e>|6?JqVf{QIds|7Hs{6b>p8aR@o1i#! zdbN;1*6n81gM`!Pv>{y=3QT451tCuNVmnOie&@L8J2nI_+)P?COT1=R)>LqH&Y;KZ zl}mS6(*{`-`<% z124W3Cop2e6i-X9YgsD?wY2R$crvxF{%HRpJNxx3Bih|l$|#$$+T_?$V zj+UMOntTncW;+&S$x(=G;{{G0%jKHxtXAPr6%R8|?JN4v#OF$2v6h`u?B2zKYGmk> zWjs|Gb~{p!f77w`njd|=x!AjPZ`EO6(jtnH8&+`^Q3@w z>ck+;_5(ex$bM+tXU`R8ej_=VnC&8o{gPPTOW~ky?T9r-povO_=x@onI5n0W!}130 zq!bAQ=9?ttivYg;TBH6ry};`MRV)3WB$0E~!=Za({RC54k3gU=36YJo?NpF~=V(3_ zd1>5R^20d83cknE+PYOh$L)mOot6{yB(~J<6M94(k44DmwDd3hquw2}yY2 zofgYmVYG#Qt^9CIxS2>gW98S+nPxU=Gkbi)7T>E%}=O`&f|XD>aS zXovRy%n)OLZ=El|M@%?&5X(8XJlJx94BHA3P*Pm95z@{S)5c*E3-+vG@@nmm7QhA> zC(Ys9O^1s9LUao+joJ81oC8)?Keg`l{bZnGJL~quuR_uyCf#z6X)-2`Nh@O>!CBIk zVt9%rQLZ-f}vMS+U37fB)ni01K^{Q<3W3yEyT#uktz9_xy(+ z@D+mx*&Y(;7{!x(z(YU5$<=_`P%##9rLvEYEwD*_E*Y095z;)e;2wN)^c~$$3;^AI zTv2+T=&4@qB`(;QwMFy}IuzIHz2<$PtC36plQ|zBHy09ESK07_pIb1t)Y0M^T5Ic< z4Mi--U3tM+bB$6R*vulL_13PrsF<2LUvl>i0UAHzQQwaGLUTNwjVxi-w;DM5FP@mP zp(J;1jvyz(zTbPF@d7pyR8*CC%4AsR4wXU5gUVznHeKzfWwtNJ*F4HNsUNHstHH%i z?$X2Zq{6ONQ;OhmXS}?3e&3LI!dh;3OcMdKk_=BYPeh)a$gkl0(!OvHv#4fRq zrP|Gfg;FGw|B2v3VEesW72|)g+!)paVztcXo7M!&X*JY?1KUV-gsoqxDaH3^#^MAL zzO1Wd(L>3jG8N$Y)jD0RAD;ajl@oif2Ev6k(idO7nt8^v?nlslYZ~SEhplSWle#>S zK5AhM*ln;>i%dQIKx*Ev0_@f1eUJz^LyRYb+j3tu|^SbM*qn#|H6nBvEvi7{Rne~rb(TbI%9v< zJ{sX%(NT$JFu1xbJAccgl@l!(_iVE%+((87{ZQIaGS%JlAFzha0BabbPmvG}npUWS;>&R`-5llgKJeTAI)Gl~e}Khac*jZ$7d~oroa6 z<|w^+x7D+hcQAPWN0?wA2DBY7?K_eNo5o*OK$e7UYpW)9&ploZd%I^L4&Sj#O<=`K zJCbQ%C*9d#Bb-K7N*a5V5-`JZ)_YPxe9@UN4A~F;R3Ih@K5WWIPrA8rEe}dGROT?# zX=Z|(*Y0An^(tU_p=t;pUZ+BM0`>(_@_m^uJhm~l!SN*9pR)wF+Q-a;e4FO>>-%g9 z_OTSn($uzYFUbwalR4I7k(su=-D7w?4^r0o(??(Hk^jYeuD=e> z$=&4u<#(5@^K)=E4sTxc00oRq$}en^mbN&2&m=zD3#+iZFr$V%Olp?A9UAFQ1{&&3*;DR8e37*k? z{&?q|Jhxv>*ZBJq)~uIL?xL|k8aK&<_Z-9HPu)quF$WGQ_rwif*nd6L$V&S*C+Rbr#VA*yhgh^j1d6-&9c<7|ZI%wRTU3ryB|V1`Cq}Q~<2vH!KV= zn?aGYH`Nn~ySMq}5`bpw_w)7jS3N4H`MkS` zitIU{oz^vXX^+Gv^4~xZFQest1`e3oG-;;5k0p$H;m9I3HiUH&D|y?@l&a&Dhg2^# zK&n#p9m1tu&}<^5Ytt*>0ixj=nXGLvn$G@oPfO$Dv@*Lk*e#$uZ{CT1(t!H<{_P+{ zgEu_A-wk(~Kp>7^iV`B+(F60!dKwiN40Uuc{;s9!wweH?pkj@PXY@@3k#f#8;%;D#-sYNTZ)DjRJaW1D zl|a4FPy`^;n||An4_rX>jHI0L;g_C#5&l_R!{Z_(I-x2+&`_XBQ7{w-26pmmRAl15 zLP>;X>uW{UnT}39)hIQG${AhE!{on-r48-ZHuZ16{W>`WuZViTN)zFufx#~3dkZ;R z25*D6l%)1+?VMlTK8%L0{JpN+>H7NFl3qf;isi|qrkHnHt;s@i#_#n^Fvj5r!(Y%x ze2h1TZtSCe?&nNx#L}I!O&AXoP;}@ zDvDm){}vZl_Vhn~gWbJj{t9=6X^Y-yGBE%zB;iuo>TXP89Y@EA4o}EuCSwni?ujuu zdWnnHgl+7-)es$KAa2_*id6Bkv5HM19moRS_XAZuf~r@%Gg*88gG4GG;>HewuP6`| zS4bzwB2mBmX6d-!Q|u^4+Itgj1;MYyxY_fvs62;PlmBBzTIR{lnlU$v=Dq1lo*FTz zB2!M_dASjD|L@C>r@L={QlGl&f>4sug|bUXOt5L1wTWBBtRr2d)ja1GjaU=*O1C+< z`jrAL-k$|JU}B$DzFT->oS)%EWfKCqln}i^i8x%3i%O3DE51d(Fl7|~70%Mg%-fc% zU44vx5k~Sb_QFNuugMftNnc)5wF=BiZET{oGnnI7P~$qT;EWGohjM=)L`z0Wx#sXL z)P9)5ZjaSbZnjJhN+Kt__o+FqLi+g#ZJ$*I7B?0JB5X288~>D?c3JUO_4_p}^41KO z2h9N}u0kBn3okX$@ArC@nIJ}}1;g4mGwAe%*=8@elBLbyog*n^_H~6SkX%os^Y-<} zNP89013{R_2dUzSWmDjAUxVK2gfSC~%SD{H`%)kd`#0hn)b`<1XpWEAH(BX{XKu?1 z4?cU)zYDc5h>G{8-|po7vCA8@gNZBMMLEde_!1Sfu90hmAN&x|pvhm`GFqeVelYs$ z0}&!x8fyAn`mJ|qX9%aPdY~OQ;pU4U$@fu(W$r(5uWkMtLcC${0VWsFYf8k`I5Rmo z0Q9ua-LOPsvJgUrP(RfiY-B3d`Dj*SfqW}R&k!&F_IpLL$Rie4e4w5~WYG6puvP#)C1hEHi@-VFF_>wz!8 z&R7Vfav09B4tno4TWX+le zbxow5>ruaHlN2OGPy>{eI^33$Ec9j;$=p4^qS3iN3809&k?iocxnX zBzs_B?RS>d9lze(PEbZU`U(8{{?tS)7-%#!-cERTB7tB=TmdPZ(AQS4t}v35Aq4!* z=Q?s2av)Ormb-yN4;0Ep0%)qC8L_M`$di7~A*~rmIgvK0&>c#^?Ok#OJ}&JUIU@G) z4E3!KcDFCUPfd!BETwIVI$v$!^20Z!#z`W2!|7yeUzc3g0V@NVDZ3v6W`AJHHY~Mx z4^@GIW)e&Sa0*BGH7{ZrFd5D&yB0k9v&V_L1RwKK_jdsIBVhh2ff!DcyY^S0|fLu`2U>p!|+PV0+AARmxIwpQ9aH6idJ)+K;Lqe$7r=jAR7%k@qKWDGF*Q ztX+u_eOKPMHF@*lo1*;tmtDv&0iXSvx$hInRYje3Bw>3TL@mDCbsfVFJExcQJe$JT#2 zrQ(jiW~X?suK}pUJ|-EX*cfVmqX8ey#_j3@zUv@`V>vEwsS*F+ED0Zru)RmQr0BXI zr))5TKWv)gefZP0w0*4yDc}!)VwF?MTM+vAV=^c#XmJ@^9--GmLa;jtH(r+O@N7PF zlQ{A#gsk2x*S?s14E*Mr$*ys*RMSWgZM^X_i_?~FX)-icq+-8%1YAiVAky6ho2nBJ z#t3{K*_^ZwMxJ9+N2E!YQR;i6~89!x4NIdJfN{o zx`w|4Sz}8}MEEE9);nVNhW!tCkr)Dp#mMY@%vPc*>-FY(k;XB9P_MGoKY{zicXkh= zU*!P(&#RVLlD|f5nI3MlN(F3>*V(qPct8VlKh@s;>q-$xOr9el1>d}!&ztQo&%#Pk z>%zAr1Py>LAq9N*U&KPx)Y2oZpLzVvI7^m!MEw&a;m=)qG=UxZ!XJWvx7gdLmvA@i zlyF+K$Azy2#sCd|3E5+6MBjZJ!v_2!$nRz0i&1@`6-uELmj(Zvc3kE1JnhcbLtjxs z2@S;!1=3E1cGeD{5b`whGWvy^9)3AaZ$~-ELRHTqZKPFhPq$4mj#%_{(Z?=RF!=7> z<`Lve6iLyw%^#UmRa^P_vqvSb3^dmR!BHK<3|-iuXe?(oeoyh8vdVh@S9NLqi8q$1 z;^wU@PL35vqu_V?+*oJi1Yu)vgn+S2ak9n;QzT0JK7lJ1#Myu0L5)bDzrsbl`9KK8 zCH?T`Vk$gTDso|WmNH06MU;0_^1y1pLtc}XxYsOW&}ZoM@AVDHWB`-IE1vX5R_41|ibM6~gawE2xWX{<;RxdskP6th*q<`!Wxy_D>vB6qH+)T2*g z{5Y3IPM}+bz|&5W-Im~~e&TnHL2fSqwsgAux6KZ^! z9Gjk^@3WsQ_D}oX+tnm*Z+vs6(fQJJgl&BZV+-pnwtmU!fBChXR>FZ!@|^e)v9i_n z;PWbaq2ft{w=1<`(rxSCW}KIZ$j6MyN=`p5`0NfxYa8FNTy3HhqL;**#3xP$XOS%08(>|F{SRT0%D=&xw|S zto$bgF}Gs&>b2mwM_+X2M{0AXVzz&F;rBk&FryS2v{|av^Q~VP;tAO;H!+OjoqBg`P&}8)J(D?1o z@<%(O2WbSeZMyL8_Nr>#duJ`(U0d-9}Z znGcECAWO0=?3QB^ES3+G8=>^O_bD&F=4;G}o*Ih=y@dZ?O=lg}pAc%BGN=pe6(nvQ_Bcx-$y+7aUw?B7XyUxz;bI$Jj z^?E)Z&sWM=K3!U%WHjIAXB$pWc+MVWN|*@bvT>=o-jt(I(xmHQF(2f<)k z8!x_eBLB5mqs(2iHI~<*pDJ`KMNl_5tCljnYJ>dl))xFyg$LrLy+R3A=|625nV5N0 z#ZO@HM3d0)vfk@gaycW)J%x~*m-^CQx8L(bdP%mj-GUAF6v1BUI#;}Q2muW8eO}r` z|KYS`BV8Afw5EJQJa9Zvt7Td0W^aa4oK^N>9m zz2|?w5d71NO2}T0<=45y)MbUWyIIEa1Bad|>=%D2@_u8P)5h@X&x62?;l32YcKur& zBZ??f654Q(Meo`>XLx@c@#*-#d8ocblC4uX9y1me5^z5`V)^`t>^DvOc@=`5bMTsF z3WxZ0R@KL`!f|r@;N$Q1Twbk|IBPco5m)cZ*)6jvVJkTIS9nuz^QgMotq=-XfPx}A z3GgwujPJ>Y+Eq)>k%1wo-?go{QIq-!EVVPn^!>SFeEFl8vSQ!&5l>{7j=86Fyua${ zEU#NmiJJ1f0F5FS0(;8nXrMQ^b!Regf^NB4Y(C4^mD;DO@?|00QSHB&hk1@xMl;OA z9g*SUl|Djkb4x`h*N@os7S)l2%XoEp2u1HPTwn>*^Ng-{GM^3}yh@~{r! zqpq1(t!AnjtMMlujDEtExI!p?UJcQX9QIew1MwQU&?g1MgIKZoN(&iLHw3Yxo#Q5X zx0tIKq4Qh`5)9J1SuE!aw5`o19yq;K8OEcl;cPFb0sBmy2qj#NvpoEjvssYA?wEWG zM@&xr>jiV-4_e#qa6&G`4Flc@^8oCfy;FUpl#;l0z7KOW88$NJ(Yc_rxRI`s&D^{B$kXk{;@(si+0h)V|RTP z();weR#bL<$kx$nV^-MTA>KVN`1ver9vvX$l+wWM>}UP&J@27ooVw{0!$aCZlCqGo znzn%u-&(vUqeSxa@mq@06z9i9S5J-e8jdnE=|U9UqhRl@JA~;uPe=>rIf!NKSa0gc zyp4R2Dj`5CI_)tzoTlJ(#@lxm=Q@@+a`yftoq6w=Kc!j7b4Gn~K?UNAchP@eHoq^0 z>hIi#hWgS78_1d`)_q?y1YO7w$@3^lzJqiUwk(H$qn1=iEm-db-zYC4>!Q!!2Fmh% z+adRE{Y1t7^Rm<$*oAuBox`}|=8%m*5qD@4;X|@NW-hv8DwKt&g z8`<)msX^%`1PC=G zEo6==e!yhk$-H-B?s`xrLL*(;jiufiYFcHLeVQ!>j*gc8nqe<2nmYU;|6YEb*T-Fc0~K75dSCTIDY2Zg z;{f#>x&MgwHqC$&L#Tv*OV0zSZK6x0+dfro{PP&Bts<#JE6Oid@yN55$bKPOPj%gH zFueF6qX@4FkuA`DiazFYYE{+izypuoDud4~)!w(dWPX&gu7Lw@V@96P6ICvs+~P@t z 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 diff --git a/editor/icons/editor_icons.py b/editor/icons/editor_icons.py index a1756b34..3015a1a0 100644 --- a/editor/icons/editor_icons.py +++ b/editor/icons/editor_icons.py @@ -331,6 +331,15 @@ def parallel(d): arrowhead(d, 40, 32, 0, 10) +def station(d): + # two tracks of a station with a crossover between them, and the insulated joints of its track circuits + line(d, [(4, 22), (60, 22)], W - 6) + line(d, [(4, 46), (60, 46)], W - 6) + line(d, [(14, 46), (32, 22)], W - 6) + line(d, [(46, 12), (46, 32)], W - 10) + line(d, [(46, 36), (46, 56)], W - 10) + + def more(d): for x in (12, 32, 52): circle(d, x, 32, 6) @@ -381,7 +390,7 @@ def work_terrain(d): ICONS = [select, insert, brush, area_fill, copy_to_bank, translate, rotate, scale, local_space, ortho, perspective, sculpt, smooth, chunks, orthophoto, road, place, lanes, lay_track, switch, straight, curve, signal, objects, - vehicle, profile, speed, joints, infra, gauge, turntable, parallel, more, work_surroundings, + vehicle, profile, speed, joints, infra, gauge, turntable, parallel, station, more, work_surroundings, work_tracks, work_roads, work_terrain] diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 4a2b9083..58b5fc97 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -5981,8 +5981,8 @@ msgstr "Rozjazd krzyżowy pojedynczy (Rkp) albo podwójny (Rkpd) na skrzyżowani msgid "Crossover, double, scissors, ladder" msgstr "Przejście rozjazdowe, podwójne przejście rozjazdowe, nożyce, ulica zwrotnicowa" -msgid "Turntable, signals, objects, trainset, profile, speed, joints, gauge" -msgstr "Obrotnica, sygnalizatory, obiekty, skład, profil, prędkość, styki, skrajnia" +msgid "Turntable, signals, signalling, objects, trainset, profile, speed, joints, gauge" +msgstr "Obrotnica, sygnalizatory, SRK, obiekty, skład, profil, prędkość, styki, skrajnia" msgid "No switch drive models" msgstr "Brak modeli napędów" @@ -10270,3 +10270,98 @@ msgstr "Wyłączone: tor kładziony od innego toru albo dołączany do niego bie msgid "Off: a track laid on from another one, or joined to it, is designed for the speed of that track and takes it.\nOn: it's designed for the speed above and the new tracks get it.\nWhile laying: Ctrl+Shift+wheel" msgstr "Wyłączone: tor kładziony od innego toru albo dołączany do niego jest projektowany na prędkość tamtego toru i ją dostaje.\nWłączone: jest projektowany na prędkość z pola powyżej i nowe tory ją dostają.\nW trakcie kładzenia: Ctrl+Shift+kółko" + +# scenery editor: the signalling of a station (SRK) +msgid "Signalling" +msgstr "SRK" + +msgid "The signalling of a station: LMB on a switch lays its station out as a schematic" +msgstr "Sterowanie ruchem na stacji: LPM na rozjazd rozkłada jego stację w schemat" + +msgid "Click: the station of this switch" +msgstr "Klik: stacja tego rozjazdu" + +msgid "Click: the station of the switch nearest along this track" +msgstr "Klik: stacja najbliższego rozjazdu wzdłuż tego toru" + +msgid "No switch near this track: it isn't a part of a station" +msgstr "Przy tym torze nie ma rozjazdu: nie należy do żadnej stacji" + +msgid "Station of %s" +msgstr "Stacja rozjazdu %s" + +msgid "Find the stations of the scenery" +msgstr "Znajdź stacje w scenerii" + +msgid "Each group of switches joined by tracks shorter than 1.5 km is a station" +msgstr "Stacją jest każda grupa rozjazdów połączonych torami krótszymi niż 1.5 km" + +msgid "The signalling of a station: its schematic, the numbers of the switches and the tracks, the track circuits and the signals" +msgstr "SRK stacji: jej schemat, numery rozjazdów i torów, izolacje i semafory" + +msgid "LMB on a switch" +msgstr "LPM na rozjazd" + +msgid "opens its station" +msgstr "otwiera jego stację" + +msgid "switches" +msgstr "rozjazdy" + +msgid "Code" +msgstr "Skrót" + +msgid "The abbreviation of the station: the names of its switches, track circuits and signals begin with it" +msgstr "Skrót nazwy stacji: od niego zaczynają się nazwy jej rozjazdów, izolacji i semaforów" + +msgid "Line" +msgstr "Linia" + +msgid "The number of the railway line the station lies on, as the block sections between the stations are named after it" +msgstr "Numer linii kolejowej, na której leży stacja: od niego biorą nazwy odstępy blokady między stacjami" + +msgid "Type" +msgstr "Typ" + +msgid "km" +msgstr "km" + +msgid "Another station" +msgstr "Inna stacja" + +msgid "Leaves this station; LMB on a switch opens another one" +msgstr "Zamyka tę stację; LPM na rozjazd otwiera inną" + +msgid "Station schematic" +msgstr "Schemat stacji" + +msgid "Shows the whole station" +msgstr "Pokazuje całą stację" + +msgid "offsets x%.0f" +msgstr "rozstawy x%.0f" + +msgid "How much the offsets of the tracks are stretched against the lengths, also Ctrl+wheel over the plot" +msgstr "Ile razy rozstawy torów są rozciągnięte względem długości, także Ctrl+kółko nad schematem" + +msgid "Wheel: zoom, drag: move, LMB on a track: select it" +msgstr "Kółko: przybliżenie, przeciąganie: przesunięcie, LPM na tor: zaznacza go" + +msgid "line out of the station" +msgstr "szlak wychodzący ze stacji" + +msgid "LMB: select it" +msgstr "LPM: zaznacza go" + +msgid "%zu station" +msgid_plural "%zu stations" +msgstr[0] "%zu stacja" +msgstr[1] "%zu stacje" +msgstr[2] "%zu stacji" + +msgid "%s: %zu switch" +msgid_plural "%s: %zu switches" +msgstr[0] "%s: %zu rozjazd" +msgstr[1] "%s: %zu rozjazdy" +msgstr[2] "%s: %zu rozjazdów" + diff --git a/scene/scenelayers.cpp b/scene/scenelayers.cpp index 561ccdd0..c7163477 100644 --- a/scene/scenelayers.cpp +++ b/scene/scenelayers.cpp @@ -843,16 +843,17 @@ bool node_layers::pending() const namespace { // marks of the lines the editor keeps track of -std::array const editormarks{"//$p", "//$h", "//$r", "//$b", "//$c", "//$o"}; +std::array const editormarks{"//$p", "//$h", "//$r", "//$b", "//$c", "//$o", "//$s"}; // other tools put lines of their own after some of these marks: Rainsted its includes after //$r, the launcher the descriptions // of the trainsets after //$o. The editor's lines of them are told by their first word bool editor_line(char const *Mark, std::string const &Text) { - static std::array>, 3> const words{{ + static std::array>, 4> const words{{ {"//$h", {"#", "alignment", "source", "ends", "norms", "pieces", "vertex", "element", "end"}}, {"//$r", {"#", "switch", "slip", "crossing", "double", "connection"}}, {"//$o", {"on"}}, + {"//$s", {"#", "station"}}, }}; auto const lookup{std::find_if(words.begin(), words.end(), [&](auto const &Entry) { return Entry.first == Mark; })}; if (lookup == words.end())