From 0c2880f48f84393f6ff0315b5a4e5a50d2289115 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 21 Oct 2026 09:10:27 +0200 Subject: [PATCH] refactor(editor): the speed check is a tool of its own The second tool out of editor_mode. Tools get two more hooks, the overlay drawn over the view and the options in the tool window, and the editor gives them the track it focuses on, the node it makes current, the norms of the route design and the snapshot of the tracks for the undo. The check gets a self-test command, speedcheck , which runs it through and logs the paths found too fast for their geometry. --- application/editormode.cpp | 4 +- application/editormode.h | 36 ++++-------------- application/editormodespeed.cpp | 61 ++++++++++++++++++++++--------- application/editormodespeed.h | 53 +++++++++++++++++++++++++++ application/editormodetrack.cpp | 15 +++++++- application/editormodetrackux.cpp | 3 +- application/editortool.h | 10 +++++ 7 files changed, 130 insertions(+), 52 deletions(-) create mode 100644 application/editormodespeed.h diff --git a/application/editormode.cpp b/application/editormode.cpp index 9cd2df06..dccc7b20 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -1466,13 +1466,13 @@ bool editor_mode::update() draw_track_spread(); draw_build_overlay(); draw_track_intent(); - speed_step(); + m_speed.update(); joints_step(); draw_track_hints(); draw_empty_scenery_hint(); draw_profile_overlay(); draw_infra_overlay(); - draw_speed_overlay(); + m_speed.draw_overlay(); draw_joints_overlay(); } else diff --git a/application/editormode.h b/application/editormode.h index 7b0b0ade..2651ab36 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/. #include "input/editorkeyboardinput.h" #include "vehicle/Camera.h" #include "application/editormodegauge.h" +#include "application/editormodespeed.h" #include "application/editortool.h" #include "scene/sceneeditor.h" #include "scene/scenenode.h" @@ -745,7 +746,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri void render_track_gizmo(); void commit_track_drag(bool const Force); void cancel_track_tools(); - void push_track_snapshot(std::vector> States, std::vector Created = {}, std::vector Removed = {}); + void push_track_snapshot(std::vector> States, std::vector Created = {}, std::vector Removed = {}) override; + void set_current_node(scene::basic_node *Node) override; + alignment::limits const &route_norms() const override; // false, with the reason, when one of the paths can't be changed, its file not taking the changes back static bool tracks_editable(std::vector const &Tracks, std::string &Reason); void trim_history(); @@ -857,8 +860,6 @@ class editor_mode : public application_mode, private editor_track::observer, pri std::string m_track_events_missing; void render_path_events(TTrack &Track); std::string m_track_mode_notice; - gauge_tool m_gauge{*this}; - std::vector m_tools{&m_gauge}; struct route_design { TTrack *from{nullptr}; @@ -1619,7 +1620,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri void track_context_release(); void render_track_context(); void split_track_at(TTrack &Track, glm::dvec3 const &Point); - void focus_track(TTrack &Track, int const Path, glm::dvec3 const &Point); + void focus_track(TTrack &Track, int const Path, glm::dvec3 const &Point) override; // the camera flies over Duration seconds to the place Distance back from the target along Look, looking along it void fly_to(glm::dvec3 const &Target, glm::dvec3 const &Look, double const Distance, double const Duration = 0.6) override; // paths found by their names @@ -2458,30 +2459,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri void infra_attach(EditorSnapshot &Snapshot); void render_infra_body(); - // speed limits of the paths checked against what their geometry allows - struct speed_state - { - bool open{false}; - int scope{0}; // 0: the line through the selected path, 1: the whole scenery - speed_check::options options; - std::vector> queue; - std::size_t next{0}; - std::vector results; - bool show_all{false}; - int limits{0}; // 0: all the paths, 1: only those with a speed limit set, 2: only those without - int selected{-1}; - std::size_t history{0}; // size of the undo history at the check - std::string status; - }; - speed_state m_speed; - void render_speed_body(); - void speed_start(); - void speed_step(); - void speed_focus(int const Index); - // with Branches the limits of the switch branches count too, otherwise they're only warnings - void speed_apply(std::vector const &Indices, bool const Branches); - std::vector speed_listed() const; - void draw_speed_overlay() const; + gauge_tool m_gauge{*this}; + speed_tool m_speed{*this}; + std::vector m_tools{&m_gauge, &m_speed}; // joints of the paths: ends which almost meet, steps, kinks, jumps of the cant and of the grade struct joints_state diff --git a/application/editormodespeed.cpp b/application/editormodespeed.cpp index 5eb8ea44..583c489f 100644 --- a/application/editormodespeed.cpp +++ b/application/editormodespeed.cpp @@ -8,8 +8,7 @@ http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" -#include "application/editormode.h" -#include "application/editoruilayer.h" +#include "application/editormodespeed.h" #include "application/editorprojection.h" #include "application/editormodeshared.h" #include "editor/editorFormat.hpp" @@ -44,7 +43,7 @@ std::string speed_text(double const Speed) } // namespace -void editor_mode::speed_start() +void speed_tool::speed_start() { auto &state{m_speed}; state.queue.clear(); @@ -52,14 +51,14 @@ void editor_mode::speed_start() state.results.clear(); state.selected = -1; state.status.clear(); - state.options.norms = m_route.design.norms; + state.options.norms = m_services.route_norms(); auto const add = [&](TTrack *Track) { for (int i = 0; i < static_cast(Track->m_paths.size()); ++i) state.queue.emplace_back(Track, i); }; if (state.scope == 0) { - auto *track{selected_track()}; + auto *track{m_services.selected_track()}; if (track == nullptr || false == rail(*track)) { state.status = STR_C("Select a track first"); @@ -88,10 +87,10 @@ void editor_mode::speed_start() if (track != nullptr && rail(*track)) add(track); } - state.history = m_history.size(); + state.history = m_services.history_size(); } -void editor_mode::speed_step() +void speed_tool::update() { auto &state{m_speed}; if (state.next >= state.queue.size()) @@ -122,7 +121,7 @@ void editor_mode::speed_step() WriteLog("Editor: speed check - " + state.status, logtype::generic); } -std::vector editor_mode::speed_listed() const +std::vector speed_tool::speed_listed() const { std::vector listed; for (int i = 0; i < static_cast(m_speed.results.size()); ++i) @@ -136,7 +135,7 @@ std::vector editor_mode::speed_listed() const return listed; } -void editor_mode::speed_focus(int const Index) +void speed_tool::speed_focus(int const Index) { auto &state{m_speed}; if (Index < 0 || Index >= static_cast(state.results.size())) @@ -146,12 +145,11 @@ void editor_mode::speed_focus(int const Index) auto const &paths{simulation::Paths.sequence()}; if (std::find(paths.begin(), paths.end(), verdict.track) == paths.end() || verdict.track->m_editorremoved) return; - m_node = verdict.track; - ui()->set_node(m_node); - focus_track(*verdict.track, verdict.path, verdict.position); + m_services.set_current_node(verdict.track); + m_services.focus_track(*verdict.track, verdict.path, verdict.position); } -void editor_mode::speed_apply(std::vector const &Indices, bool const Branches) +void speed_tool::speed_apply(std::vector const &Indices, bool const Branches) { auto &state{m_speed}; struct change @@ -191,7 +189,7 @@ void editor_mode::speed_apply(std::vector const &Indices, bool const Branch state.status = STR_C("Nothing changed") + skipped; return; } - push_track_snapshot(std::move(states)); + m_services.push_track_snapshot(std::move(states)); for (auto const &entry : changes) { auto const velocity{entry.track->eType == tt_Switch ? speed_check::switch_velocity(entry.limits[0], entry.limits[1]) : entry.limits[0]}; @@ -200,12 +198,12 @@ void editor_mode::speed_apply(std::vector const &Indices, bool const Branch } for (auto &verdict : state.results) verdict.set = speed_check::path_velocity(*verdict.track, verdict.path); - state.history = m_history.size(); + state.history = m_services.history_size(); state.status = format(STR_C("Speed limits of %zu paths lowered to the safe ones, Ctrl+Z brings them back"), changes.size()) + skipped; WriteLog("Editor: speed check - " + state.status, logtype::generic); } -void editor_mode::render_speed_body() +void speed_tool::render_options() { auto &state{m_speed}; ImGui::PushTextWrapPos(0.0f); @@ -264,7 +262,7 @@ void editor_mode::render_speed_body() ImGui::TextWrapped("%s", state.status.c_str()); if (state.results.empty()) return; - if (state.history != m_history.size()) + if (state.history != m_services.history_size()) ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.3f, 1.0f), STR_C("The scenery changed since the check")); auto const *const within{STR_C("Within their limits too")}; auto const checkbox{ImGui::GetFrameHeight() + ImGui::GetStyle().ItemInnerSpacing.x + ImGui::CalcTextSize(within).x}; @@ -357,7 +355,7 @@ void editor_mode::render_speed_body() } } -void editor_mode::draw_speed_overlay() const +void speed_tool::draw_overlay() const { auto const &state{m_speed}; if (false == state.open || state.results.empty()) @@ -380,3 +378,30 @@ void editor_mode::draw_speed_overlay() const drawlist->AddText(ImVec2(screen.x + 14.0f, screen.y - 7.0f), colour, text.c_str()); } } + +void speed_tool::check_line() +{ + m_speed.scope = 0; + speed_start(); +} + +editor_selftest_result speed_tool::selftest(std::string const &Command, std::istringstream &Words) +{ + if (Command == "speedcheck") + { + int scope{0}; + Words >> scope; + m_speed.scope = scope; + speed_start(); + while (m_speed.next < m_speed.queue.size()) + update(); + WriteLog("SELFTEST speedcheck: " + m_speed.status); + for (auto const index : speed_listed()) + { + auto const &verdict{m_speed.results[index]}; + WriteLog(format("SELFTEST speedcheck %s path %d: set %.0f safe %.0f", verdict.track->name().c_str(), verdict.path, verdict.set, verdict.safe)); + } + return editor_selftest_result::handled; + } + return editor_selftest_result::unhandled; +} diff --git a/application/editormodespeed.h b/application/editormodespeed.h new file mode 100644 index 00000000..a477f59a --- /dev/null +++ b/application/editormodespeed.h @@ -0,0 +1,53 @@ +/* +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/editorSpeed.hpp" + +#include +#include +#include + +class speed_tool final : public editor_tool +{ + public: + explicit speed_tool(editor_services &Services) : m_services{Services} {} + void update() override; + void draw_overlay() const override; + void render_options() override; + editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) override; + void set_open(bool const Open) { m_speed.open = Open; } + void check_line(); + + private: + editor_services &m_services; + // speed limits of the paths checked against what their geometry allows + struct speed_state + { + bool open{false}; + int scope{0}; // 0: the line through the selected path, 1: the whole scenery + speed_check::options options; + std::vector> queue; + std::size_t next{0}; + std::vector results; + bool show_all{false}; + int limits{0}; // 0: all the paths, 1: only those with a speed limit set, 2: only those without + int selected{-1}; + std::size_t history{0}; // size of the undo history at the check + std::string status; + }; + speed_state m_speed; + void speed_start(); + void speed_focus(int const Index); + // with Branches the limits of the switch branches count too, otherwise they're only warnings + void speed_apply(std::vector const &Indices, bool const Branches); + std::vector speed_listed() const; +}; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index b84d6bf8..f00f060f 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -67,6 +67,17 @@ glm::dvec3 editor_mode::camera_position() const return Camera.Pos; } +void editor_mode::set_current_node(scene::basic_node *Node) +{ + m_node = Node; + ui()->set_node(m_node); +} + +alignment::limits const &editor_mode::route_norms() const +{ + return m_route.design.norms; +} + void editor_mode::select_track(scene::basic_node *Node) { auto *track = dynamic_cast(Node); @@ -514,7 +525,7 @@ void editor_mode::render_track_inspector() { auto const &io{ImGui::GetIO()}; m_profile.open = m_track_window_open && m_track_tab == track_tab::profile; - m_speed.open = m_track_window_open && m_track_tab == track_tab::speed; + 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.open = m_track_window_open && m_track_tab == track_tab::joints; if (false == m_track_window_open || m_track_tab != track_tab::lineside) @@ -547,7 +558,7 @@ void editor_mode::render_track_inspector() { switch (m_track_tab) { - case track_tab::speed: render_speed_body(); break; + case track_tab::speed: m_speed.render_options(); break; case track_tab::joints: render_joints_body(); break; default: render_infra_body(); break; } diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index 9518c5ca..9c551577 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -814,9 +814,8 @@ void editor_mode::render_track_context() } if (ImGui::MenuItem(STR_C("Speed check of the line"), "V")) { - m_speed.scope = 0; show_track_tab(track_tab::speed); - speed_start(); + m_speed.check_line(); } ImGui::Separator(); if (ImGui::MenuItem(track_selected(track) ? STR_C("Take out of the selection") : STR_C("Add to the selection"), STR_C("Shift+LMB"))) diff --git a/application/editortool.h b/application/editortool.h index 1ddfc50f..2e3eb275 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -12,9 +12,13 @@ http://mozilla.org/MPL/2.0/. #include #include #include +#include +#include #include +#include "editor/editorTrack.hpp" + class TTrack; namespace scene { @@ -36,6 +40,10 @@ class editor_services virtual std::size_t history_size() const = 0; virtual glm::dvec3 camera_position() const = 0; virtual void fly_to(glm::dvec3 const &Target, glm::dvec3 const &Look, double const Distance, double const Duration = 0.6) = 0; + virtual void focus_track(TTrack &Track, int const Path, glm::dvec3 const &Point) = 0; + virtual void set_current_node(scene::basic_node *Node) = 0; + virtual alignment::limits const &route_norms() const = 0; + virtual void push_track_snapshot(std::vector> States, std::vector Created = {}, std::vector Removed = {}) = 0; protected: ~editor_services() = default; @@ -46,6 +54,8 @@ class editor_tool public: virtual ~editor_tool() = default; virtual void update() {} + virtual void draw_overlay() const {} + virtual void render_options() {} virtual void render_windows() {} virtual void forget(scene::basic_node const *Node) {} virtual editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) { return editor_selftest_result::unhandled; }