From 872e1b5e19bef1759b30db563e92fbfc1ab92e34 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 21 Oct 2026 09:54:39 +0200 Subject: [PATCH] refactor(editor): the parallel track is a tool of its own The seventh tool out of editor_mode. It reads the selected set of tracks and the tab shown through the services, digitizes the tracks it lays and refreshes the plan after it sets one parallel to another. The panel shown for two selected tracks is a public call of the tool, and the self-test command parallel moves with it and lays the same tracks as before. --- application/editormode.cpp | 2 +- application/editormode.h | 44 +++-------------- application/editormodeparallel.cpp | 65 +++++++++++++++---------- application/editormodeparallel.h | 58 ++++++++++++++++++++++ application/editormodeselftestsweep.cpp | 2 +- application/editormodeselftesttrack.cpp | 11 +---- application/editormodetrack.cpp | 14 +++++- application/editormodetrackux.cpp | 2 +- application/editortool.h | 4 ++ 9 files changed, 125 insertions(+), 77 deletions(-) create mode 100644 application/editormodeparallel.h diff --git a/application/editormode.cpp b/application/editormode.cpp index 389d3b58..952697fa 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -1450,7 +1450,7 @@ bool editor_mode::update() signal_move_update(); draw_signal_overlay(); } - draw_parallel_preview(); + m_parallel.draw_overlay(); if (m_track_tab == track_tab::lineside) { m_hekto.draw_overlay(); diff --git a/application/editormode.h b/application/editormode.h index c9881b6c..ea8a680a 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/. #include "application/editormodegauge.h" #include "application/editormodejoints.h" #include "application/editormodelineside.h" +#include "application/editormodeparallel.h" #include "application/editormodespeed.h" #include "application/editormodevehicle.h" #include "application/editortool.h" @@ -715,6 +716,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri std::size_t history_size() const override; glm::dvec3 camera_position() const override; track_hover const &hovered_track() const override; + std::vector const &track_set() const override; + editor_track_tab current_track_tab() const override; void render_path_ui(); void draw_track_overlay() const; editor_track::point_ref track_handle_hit() const; @@ -910,7 +913,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri void record_slip(std::vector const &Parts, std::string const &Kind, std::string const &Type, double const Radius, double const Angle, std::string const &Base, double const Middle = 0.0); void render_switch_record(TTrack const &Track) const; void route_store(); - bool digitize_drawn(std::vector const &Created, bool const Alone = true); + bool digitize_drawn(std::vector const &Created, bool const Alone = true) override; bool digitize_chain(editor_track::chain const &Chain, std::string *Reason = nullptr, TTrack **Culprit = nullptr); int digitize_parts(editor_track::chain const &Chain, std::string *Reason, int &Skipped); // the lines kept, the paths left out @@ -1016,7 +1019,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri void apply_straight_tool(); double straight_tool_radius(editor_track::straight const &Line) const; void straight_reshape(editor_track::straight const &Line, double const From, double const To, std::function const &Tail, double const Radius); - void straight_refresh(); + void straight_refresh() override; bool start_curve_fit(editor_track::straight const &Line, int const Handle); void update_curve_fit(glm::dvec3 const &Start, glm::dvec3 const &End); void add_detour_point(); @@ -1736,40 +1739,6 @@ class editor_mode : public application_mode, private editor_track::observer, pri std::vector> m_track_similar; TTrack const *m_track_list_hover{nullptr}; // in the list of the selected tracks int m_track_list_hover_frame{-1}; - // second track alongside the selected path or the line through it, at a spacing, curves included - struct parallel_tool - { - bool open{false}; - bool expand{false}; - double spacing{4.0}; - int side{1}; // 1: to the right, -1: to the left, of the direction of the line - int scope{1}; // 0: the selected path, 1: the line through it, up to the switches - TTrack *built_for{nullptr}; - double built_spacing{0.0}; - int built_side{0}; - int built_scope{-1}; - std::size_t built_history{0}; - TTrack *pair_src{nullptr}; - TTrack *pair_dst{nullptr}; - double measured{0.0}; - std::vector pieces; - std::vector styles; - double length{0.0}; - std::string error; - std::string status; - }; - parallel_tool m_parallel; - TTrack *parallel_source() const; - TTrack *parallel_target() const; - void parallel_update(); - void render_parallel_controls(bool const Scope = true); - void render_parallel_ui(); - void render_parallel_set_ui(); - void parallel_build(); - void parallel_apply(); - bool parallel_preview_visible() const; - void draw_parallel_preview() const; - using template_item = ::template_item; using standing_template = ::standing_template; // includes of the templates whose file names the test accepts, with the location in p2..p4 and the rotation in p5 @@ -2273,7 +2242,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri vehicle_tool m_vehicle{*this}; hekto_tool m_hekto{*this}; fouling_tool m_fouling{*this}; - std::vector m_tools{&m_gauge, &m_speed, &m_joints, &m_vehicle, &m_hekto, &m_fouling}; + parallel_tool m_parallel{*this}; + std::vector m_tools{&m_gauge, &m_speed, &m_joints, &m_vehicle, &m_hekto, &m_fouling, &m_parallel}; void infra_rebase(); void render_infra_search(); diff --git a/application/editormodeparallel.cpp b/application/editormodeparallel.cpp index 7fb5b893..5180560c 100644 --- a/application/editormodeparallel.cpp +++ b/application/editormodeparallel.cpp @@ -8,8 +8,7 @@ http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" -#include "application/editormode.h" -#include "application/editoruilayer.h" +#include "application/editormodeparallel.h" #include "application/editorprojection.h" #include "application/editormodeshared.h" #include "editor/editorFormat.hpp" @@ -209,21 +208,23 @@ double lateral_of(TTrack const &Src, TTrack const &Dst) } // namespace -TTrack *editor_mode::parallel_source() const +TTrack *parallel_tool::parallel_source() const { - if (m_track_set.size() >= 2) - return m_track_set.front(); - return selected_track(); + auto const &set{m_services.track_set()}; + if (set.size() >= 2) + return set.front(); + return m_services.selected_track(); } -TTrack *editor_mode::parallel_target() const +TTrack *parallel_tool::parallel_target() const { - if (m_track_set.size() != 2) + auto const &set{m_services.track_set()}; + if (set.size() != 2) return nullptr; - return m_track_set.back() != m_track_set.front() ? m_track_set.back() : nullptr; + return set.back() != set.front() ? set.back() : nullptr; } -void editor_mode::parallel_update() +void parallel_tool::parallel_update() { auto &tool{m_parallel}; auto *track{parallel_source()}; @@ -244,13 +245,13 @@ void editor_mode::parallel_update() else if (track != nullptr && target != nullptr) tool.measured = std::abs(lateral_of(*track, *target)); - if (track == tool.built_for && tool.spacing == tool.built_spacing && tool.side == tool.built_side && tool.scope == tool.built_scope && m_history.size() == tool.built_history) + if (track == tool.built_for && tool.spacing == tool.built_spacing && tool.side == tool.built_side && tool.scope == tool.built_scope && m_services.history_size() == tool.built_history) return; tool.built_for = track; tool.built_spacing = tool.spacing; tool.built_side = tool.side; tool.built_scope = tool.scope; - tool.built_history = m_history.size(); + tool.built_history = m_services.history_size(); tool.pieces.clear(); tool.styles.clear(); tool.length = 0.0; @@ -319,7 +320,7 @@ void editor_mode::parallel_update() } } -void editor_mode::parallel_build() +void parallel_tool::parallel_build() { auto &tool{m_parallel}; parallel_update(); @@ -329,8 +330,8 @@ void editor_mode::parallel_build() for (std::size_t i = 0; i < tool.pieces.size(); ++i) created.push_back(editor_track::create_path(*tool.styles[i], tool.pieces[i])); editor_track::commit(created); - digitize_drawn(created); - push_track_snapshot({}, created); + m_services.digitize_drawn(created); + m_services.push_track_snapshot({}, created); tool.status = format(STR_C("Parallel track of %zu paths, %.1f m, %.2f m away; Ctrl+Z takes it back"), created.size(), tool.length, tool.spacing); if (scene::Layers.active() == null_handle) tool.status += "\n" + std::string{STR_C("The scenery isn't opened for editing, the new track won't be saved")}; @@ -338,7 +339,7 @@ void editor_mode::parallel_build() tool.built_for = nullptr; } -void editor_mode::parallel_apply() +void parallel_tool::parallel_apply() { auto &tool{m_parallel}; auto *target{parallel_target()}; @@ -375,14 +376,14 @@ void editor_mode::parallel_apply() if (joint.other != nullptr) committed.push_back(joint.other); editor_track::commit(committed); - push_track_snapshot(std::move(states)); + m_services.push_track_snapshot(std::move(states)); tool.status = format(STR_C("%s set parallel to %s, %.2f m away; Ctrl+Z takes it back"), name_of(*target).c_str(), name_of(*parallel_source()).c_str(), tool.spacing); WriteLog("Editor: " + tool.status, logtype::generic); tool.built_for = nullptr; - straight_refresh(); + m_services.straight_refresh(); } -void editor_mode::render_parallel_controls(bool const Scope) +void parallel_tool::render_parallel_controls(bool const Scope) { auto &tool{m_parallel}; if (Scope) @@ -396,7 +397,7 @@ void editor_mode::render_parallel_controls(bool const Scope) value_chips("spacings", tool.spacing, {kSpacings[0], kSpacings[1], kSpacings[2], kSpacings[3], kSpacings[4], kSpacings[5]}, "%.2f"); } -void editor_mode::render_parallel_ui() +void parallel_tool::render_options() { auto &tool{m_parallel}; ImGui::PushTextWrapPos(0.0f); @@ -426,7 +427,7 @@ void editor_mode::render_parallel_ui() ImGui::TextWrapped("%s", tool.status.c_str()); } -void editor_mode::render_parallel_set_ui() +void parallel_tool::render_set_options() { auto &tool{m_parallel}; auto *source{parallel_source()}; @@ -450,16 +451,16 @@ void editor_mode::render_parallel_set_ui() ImGui::TextWrapped("%s", tool.status.c_str()); } -bool editor_mode::parallel_preview_visible() const +bool parallel_tool::parallel_preview_visible() const { if (m_parallel.pieces.empty() || parallel_source() != m_parallel.built_for) return false; - if (m_track_tab == track_tab::parallel) + if (m_services.current_track_tab() == editor_track_tab::parallel) return true; - return m_track_tab == track_tab::path && parallel_target() != nullptr; + return m_services.current_track_tab() == editor_track_tab::path && parallel_target() != nullptr; } -void editor_mode::draw_parallel_preview() const +void parallel_tool::draw_overlay() const { if (false == parallel_preview_visible()) return; @@ -488,3 +489,17 @@ void editor_mode::draw_parallel_preview() const drawlist->AddText(ImVec2(screen.x + 12.0f, screen.y - 8.0f), IM_COL32(90, 255, 120, 255), label.c_str()); } } + +editor_selftest_result parallel_tool::selftest(std::string const &Command, std::istringstream &Words) +{ + if (Command == "parallel") + { + Words >> m_parallel.spacing >> m_parallel.side; + m_parallel.scope = 1; + m_parallel.built_for = nullptr; + parallel_build(); + WriteLog("SELFTEST parallel: " + m_parallel.status + " " + m_parallel.error); + return editor_selftest_result::handled; + } + return editor_selftest_result::unhandled; +} diff --git a/application/editormodeparallel.h b/application/editormodeparallel.h new file mode 100644 index 00000000..f221c6f4 --- /dev/null +++ b/application/editormodeparallel.h @@ -0,0 +1,58 @@ +/* +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 +#include + +// second track alongside the selected path or the line through it, at a spacing, curves included +class parallel_tool final : public editor_tool +{ + public: + explicit parallel_tool(editor_services &Services) : m_services{Services} {} + void draw_overlay() const override; + void render_options() override; + editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) override; + tool_tab &tab() { return m_tab; } + void render_set_options(); + + private: + editor_services &m_services; + tool_tab m_tab; + struct parallel_state + { + double spacing{4.0}; + int side{1}; // 1: to the right, -1: to the left, of the direction of the line + int scope{1}; // 0: the selected path, 1: the line through it, up to the switches + TTrack *built_for{nullptr}; + double built_spacing{0.0}; + int built_side{0}; + int built_scope{-1}; + std::size_t built_history{0}; + TTrack *pair_src{nullptr}; + TTrack *pair_dst{nullptr}; + double measured{0.0}; + std::vector pieces; + std::vector styles; + double length{0.0}; + std::string error; + std::string status; + }; + parallel_state m_parallel; + TTrack *parallel_source() const; + TTrack *parallel_target() const; + void parallel_update(); + void render_parallel_controls(bool const Scope = true); + void parallel_build(); + void parallel_apply(); + bool parallel_preview_visible() const; +}; diff --git a/application/editormodeselftestsweep.cpp b/application/editormodeselftestsweep.cpp index fd37edb9..38289631 100644 --- a/application/editormodeselftestsweep.cpp +++ b/application/editormodeselftestsweep.cpp @@ -284,7 +284,7 @@ editor_mode::selftest_result editor_mode::selftest_sweep(std::string const &comm std::string tab; words >> tab; show_track_tab(tab == "parallel" ? track_tab::parallel : track_tab::lineside); - (tab == "vehicle" ? m_vehicle.tab().expand : tab == "hekto" ? m_hekto.tab().expand : tab == "fouling" ? m_fouling.tab().expand : m_parallel.expand) = true; + (tab == "vehicle" ? m_vehicle.tab().expand : tab == "hekto" ? m_hekto.tab().expand : tab == "fouling" ? m_fouling.tab().expand : m_parallel.tab().expand) = true; return selftest_result::handled; } if (command == "bendpanel") diff --git a/application/editormodeselftesttrack.cpp b/application/editormodeselftesttrack.cpp index 7c1c5eac..c1182d20 100644 --- a/application/editormodeselftesttrack.cpp +++ b/application/editormodeselftesttrack.cpp @@ -141,7 +141,7 @@ editor_mode::selftest_result editor_mode::selftest_track(std::string const &comm m_sweep.expand = tab == 0; m_hekto.tab().expand = tab == 1; m_fouling.tab().expand = tab == 2; - m_parallel.expand = tab == 3; + m_parallel.tab().expand = tab == 3; m_vehicle.tab().expand = tab == 4; return selftest_result::handled; } @@ -782,14 +782,5 @@ editor_mode::selftest_result editor_mode::selftest_track(std::string const &comm WriteLog(format("SELFTEST digitize %s: %d", nearest != nullptr ? nearest->name().c_str() : "-", kept ? 1 : 0)); return selftest_result::handled; } - if (command == "parallel") - { - words >> m_parallel.spacing >> m_parallel.side; - m_parallel.scope = 1; - m_parallel.built_for = nullptr; - parallel_build(); - WriteLog("SELFTEST parallel: " + m_parallel.status + " " + m_parallel.error); - return selftest_result::handled; - } return selftest_result::unhandled; } diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index c1a4fcf3..e9ada20f 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -72,6 +72,16 @@ editor_mode::track_hover const &editor_mode::hovered_track() const return m_hover; } +std::vector const &editor_mode::track_set() const +{ + return m_track_set; +} + +editor_track_tab editor_mode::current_track_tab() const +{ + return m_track_tab; +} + void editor_mode::set_current_node(scene::basic_node *Node) { m_node = Node; @@ -558,7 +568,7 @@ void editor_mode::render_track_inspector() m_sweep.open = false; m_hekto.tab().open = false; m_fouling.tab().open = false; - m_parallel.open = false; + m_parallel.tab().open = false; m_vehicle.tab().open = false; } ui()->set_track(m_track_window_open); @@ -611,7 +621,7 @@ void editor_mode::render_track_tool_options() render_lay_ui(); break; case track_tab::parallel: - render_parallel_ui(); + m_parallel.render_options(); break; case track_tab::turnout: if (track != nullptr && track->eType == tt_Switch) diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index 21629482..238b0cfe 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -1355,7 +1355,7 @@ void editor_mode::render_track_set_ui() ImGui::PushTextWrapPos(0.0f); ImGui::TextDisabled("%s", STR_C("Their properties are in the inspector, the gizmo moves them together")); ImGui::PopTextWrapPos(); - render_parallel_set_ui(); + m_parallel.render_set_options(); ImGui::Separator(); } diff --git a/application/editortool.h b/application/editortool.h index 791d7424..fd110f91 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -92,6 +92,8 @@ class editor_services virtual std::size_t history_size() const = 0; virtual glm::dvec3 camera_position() const = 0; virtual track_hover const &hovered_track() const = 0; + virtual std::vector const &track_set() const = 0; + virtual editor_track_tab current_track_tab() const = 0; virtual void first_step_done(editor_first_step const Step) = 0; virtual void show_track_tab(editor_track_tab const Wanted) = 0; virtual glm::dvec3 placement_on_ground(glm::dvec3 Location) = 0; @@ -109,6 +111,8 @@ class editor_services virtual std::size_t place_templates(std::vector const &Items, std::string &Error) = 0; virtual std::size_t move_templates(std::vector> const &Moves) = 0; virtual std::size_t remove_templates(std::vector const &Instances) = 0; + virtual bool digitize_drawn(std::vector const &Created, bool const Alone = true) = 0; + virtual void straight_refresh() = 0; virtual void push_track_snapshot(std::vector> States, std::vector Created = {}, std::vector Removed = {}) = 0; protected: