From 747c6817f0e78704dfc5cc328c2329413ed35ec7 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 21 Oct 2026 10:08:27 +0200 Subject: [PATCH] refactor(editor): the tools get the roles of the editor they need, not all of it editor_services was growing into one interface every tool depended on whole. It splits into the roles the editor plays for its tools: the selection, the view, the undo history, the templates by the track, the ground with the terrain model, the track design and the guide of the first steps. Each tool is handed only the roles it uses, so what it depends on can be read from its constructor; editor_services stays as the sum of the roles the editor implements. --- application/editormode.h | 14 ++--- application/editormodegauge.cpp | 20 +++---- application/editormodegauge.h | 7 ++- application/editormodejoints.cpp | 30 +++++----- application/editormodejoints.h | 7 ++- application/editormodelineside.cpp | 40 +++++++------- application/editormodelineside.h | 17 ++++-- application/editormodeparallel.cpp | 22 ++++---- application/editormodeparallel.h | 7 ++- application/editormodespeed.cpp | 16 +++--- application/editormodespeed.h | 7 ++- application/editormodevehicle.cpp | 6 +- application/editormodevehicle.h | 5 +- application/editortool.h | 88 ++++++++++++++++++++++++------ 14 files changed, 181 insertions(+), 105 deletions(-) diff --git a/application/editormode.h b/application/editormode.h index ea8a680a..86e8b72d 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -2236,13 +2236,13 @@ class editor_mode : public application_mode, private editor_track::observer, pri void infra_attach(EditorSnapshot &Snapshot); void render_infra_body(); - gauge_tool m_gauge{*this}; - speed_tool m_speed{*this}; - joints_tool m_joints{*this}; - vehicle_tool m_vehicle{*this}; - hekto_tool m_hekto{*this}; - fouling_tool m_fouling{*this}; - parallel_tool m_parallel{*this}; + gauge_tool m_gauge{*this, *this, *this, *this}; + speed_tool m_speed{*this, *this, *this, *this}; + joints_tool m_joints{*this, *this, *this, *this}; + vehicle_tool m_vehicle{*this, *this}; + 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}; void infra_rebase(); diff --git a/application/editormodegauge.cpp b/application/editormodegauge.cpp index 4c2f2187..4260033f 100644 --- a/application/editormodegauge.cpp +++ b/application/editormodegauge.cpp @@ -591,9 +591,9 @@ void gauge_tool::update() m_gauge.published = false; return; } - auto *track{m_gauge.enabled ? m_services.selected_track() : nullptr}; + auto *track{m_gauge.enabled ? m_selection.selected_track() : nullptr}; // the geometry is rescanned once the edit ends - if (ImGui::IsMouseDown(0) || m_services.track_editing()) + if (ImGui::IsMouseDown(0) || m_design.track_editing()) m_gauge.pending = true; else if (track == nullptr) { @@ -604,15 +604,15 @@ void gauge_tool::update() m_gauge.published = false; } } - else if (track != m_gauge.track || m_services.history_size() != m_gauge.history || m_gauge.pending) + else if (track != m_gauge.track || m_history.history_size() != m_gauge.history || m_gauge.pending) { m_gauge.track = track; - m_gauge.history = m_services.history_size(); + m_gauge.history = m_history.history_size(); m_gauge.pending = false; scan_gauge(*track); m_gauge.published = false; } - if (false == m_gauge.published || glm::distance(m_services.camera_position(), EditorOverlay.origin) > gauge_origin_range) + if (false == m_gauge.published || glm::distance(m_view.camera_position(), EditorOverlay.origin) > gauge_origin_range) gauge_publish(); } @@ -645,7 +645,7 @@ void gauge_tool::scan_gauge_map() scan->space.emplace(scan->lines); m_gauge.map = {}; m_gauge.map.scanned = true; - m_gauge.map.history = m_services.history_size(); + m_gauge.map.history = m_history.history_size(); auto const name{[](gauge::profile const *Profile) { return Profile != nullptr ? Profile->name : std::string{"-"}; }}; m_gauge.map.profile = name(chosen.railway) + ", " + name(chosen.road); m_gauge.current = -1; @@ -695,7 +695,7 @@ void gauge_tool::gauge_publish() { m_gauge.published = true; EditorOverlay.clear(); - EditorOverlay.origin = m_services.camera_position(); + EditorOverlay.origin = m_view.camera_position(); auto const batch{[](glm::vec4 const &Color, unsigned int const Type, std::vector const &Points) { gfx::editor_overlay::batch result{Color, Type}; result.points.reserve(Points.size()); @@ -730,9 +730,9 @@ void gauge_tool::gauge_focus(int const Index) // along the track, tangent to the gauge at the height of the place, from the end nearer to the camera glm::dvec3 look{hit.direction.x, 0.0, hit.direction.y}; look = glm::length(look) > 1e-6 ? glm::normalize(look) : glm::dvec3(0.0, 0.0, 1.0); - if (glm::dot(m_services.camera_position() - hit.point, look) > 0.0) + if (glm::dot(m_view.camera_position() - hit.point, look) > 0.0) look = -look; - m_services.fly_to(hit.point, look, gauge_view_distance); + m_view.fly_to(hit.point, look, gauge_view_distance); } void gauge_tool::gauge_spot(gauge_hit const &Hit) @@ -799,7 +799,7 @@ void gauge_tool::render_windows() m_gauge.current = -1; m_gauge.published = false; } - else if (m_gauge.map.history != m_services.history_size()) + else if (m_gauge.map.history != m_history.history_size()) ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.3f, 1.0f), STR_C("The scenery changed since the scan (%s)"), m_gauge.map.profile.c_str()); } render_gauge_hits(); diff --git a/application/editormodegauge.h b/application/editormodegauge.h index 7e39d8d1..0f0507bb 100644 --- a/application/editormodegauge.h +++ b/application/editormodegauge.h @@ -29,7 +29,7 @@ class gauge_tool final : public editor_tool int path{0}; glm::dvec2 direction{0.0, 1.0}; // of the track there, in the plan }; - explicit gauge_tool(editor_services &Services) : m_services{Services} {} + gauge_tool(editor_selection &Selection, editor_view &View, editor_history &History, editor_track_design &Design) : m_selection{Selection}, m_view{View}, m_history{History}, m_design{Design} {} void update() override; void render_windows() override; editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) override; @@ -37,7 +37,10 @@ class gauge_tool final : public editor_tool void toggle() { m_gauge.open = !m_gauge.open; } private: - editor_services &m_services; + editor_selection &m_selection; + editor_view &m_view; + editor_history &m_history; + editor_track_design &m_design; // structure gauge along the line of the selected path, shown in the 3d view struct gauge_state { diff --git a/application/editormodejoints.cpp b/application/editormodejoints.cpp index 5177de0e..a9afd279 100644 --- a/application/editormodejoints.cpp +++ b/application/editormodejoints.cpp @@ -164,7 +164,7 @@ void joints_tool::joints_start() continue; state.queue.push_back(track); } - state.history = m_services.history_size(); + state.history = m_history.history_size(); state.places.clear(); state.runs.clear(); state.run_next = 0; @@ -174,14 +174,14 @@ void joints_tool::joints_start() state.model_unplaced = false; if (false == state.model_check || state.queue.empty()) return; - m_services.nmt_locate(); - if (false == m_services.terrain_model().placed()) + m_ground.nmt_locate(); + if (false == m_ground.terrain_model().placed()) { state.model_unplaced = true; return; } std::set const queued(state.queue.begin(), state.queue.end()); - for (auto const &place : m_services.nmt_track_places(kModelStep)) + for (auto const &place : m_ground.nmt_track_places(kModelStep)) if (place.shapes && queued.count(place.track) > 0) { if (state.places.empty() || state.places.back().track != place.track || state.places.back().path != place.path || state.places.size() - state.runs.back().first >= kModelRun) @@ -216,27 +216,27 @@ bool joints_tool::joints_model() for (auto const &run : state.runs) { box(run, low, high); - m_services.terrain_model().request(low, high); + m_ground.terrain_model().request(low, high); } state.model_asked = true; } while (state.run_next < state.runs.size()) { box(state.runs[state.run_next], low, high); - if (false == m_services.terrain_model().request(low, high)) + if (false == m_ground.terrain_model().request(low, high)) return false; ++state.run_next; } std::vector differences(state.places.size(), std::numeric_limits::quiet_NaN()); std::vector known; for (std::size_t i = 0; i < state.places.size(); ++i) - if (double height; m_services.terrain_model().height(state.places[i].position.x, state.places[i].position.z, height)) + if (double height; m_ground.terrain_model().height(state.places[i].position.x, state.places[i].position.z, height)) { - differences[i] = state.places[i].crown - (height + m_services.terrain_model_offset()); + differences[i] = state.places[i].crown - (height + m_ground.terrain_model_offset()); known.push_back(differences[i]); } auto shift{0.0}; - if (false == m_services.terrain_model_fitted() && false == known.empty()) + if (false == m_ground.terrain_model_fitted() && false == known.empty()) { std::nth_element(known.begin(), known.begin() + known.size() / 2, known.end()); if (std::abs(known[known.size() / 2]) > kUnfitted) @@ -411,9 +411,9 @@ void joints_tool::joints_focus(int const Index) auto const &paths{simulation::Paths.sequence()}; if (std::find(paths.begin(), paths.end(), issue.track) == paths.end() || issue.track->m_editorremoved) return; - m_services.set_current_node(issue.track); - m_services.clear_track_point(); - m_services.focus_track(*issue.track, issue.point.path, issue.position); + m_selection.set_current_node(issue.track); + m_selection.clear_track_point(); + m_view.focus_track(*issue.track, issue.point.path, issue.position); } bool joints_tool::joint_fix(joint_issue &Issue, std::vector> &States, std::vector &Changed) @@ -498,11 +498,11 @@ void joints_tool::joints_fix(std::vector const &Indices) state.status = STR_C("Nothing could be fixed: the paths are switches or in the files which can't be changed"); return; } - m_services.push_track_snapshot(std::move(states)); + m_history.push_track_snapshot(std::move(states)); editor_track::commit(changed); for (auto *track : changed) track->mark_dirty(); - state.history = m_services.history_size(); + state.history = m_history.history_size(); state.status = format(STR_C("%zu joints fixed, Ctrl+Z takes them back"), count); WriteLog("Editor: joints check - " + state.status, logtype::generic); } @@ -580,7 +580,7 @@ void joints_tool::render_options() ImGui::TextWrapped("%s", state.status.c_str()); if (state.issues.empty()) return; - if (state.history != m_services.history_size()) + if (state.history != m_history.history_size()) ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.3f, 1.0f), "%s", STR_C("The scenery changed since the check")); int filter{state.filter + 1}; ImGui::SetNextItemWidth(150.0f); diff --git a/application/editormodejoints.h b/application/editormodejoints.h index 34dc5cbe..626846e7 100644 --- a/application/editormodejoints.h +++ b/application/editormodejoints.h @@ -31,7 +31,7 @@ class joints_tool final : public editor_tool double value{0.0}; // m, deg, mm, per mille; m of the track over the terrain model bool fixed{false}; }; - explicit joints_tool(editor_services &Services) : m_services{Services} {} + joints_tool(editor_selection &Selection, editor_view &View, editor_history &History, editor_ground &Ground) : m_selection{Selection}, m_view{View}, m_history{History}, m_ground{Ground} {} void update() override; void draw_overlay() const override; void render_options() override; @@ -40,7 +40,10 @@ class joints_tool final : public editor_tool double kink_angle() const { return m_joints.angle; } private: - editor_services &m_services; + editor_selection &m_selection; + editor_view &m_view; + editor_history &m_history; + editor_ground &m_ground; // 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/editormodelineside.cpp b/application/editormodelineside.cpp index f6de2967..56e93528 100644 --- a/application/editormodelineside.cpp +++ b/application/editormodelineside.cpp @@ -421,10 +421,10 @@ void hekto_tool::hekto_read_kilometrage() void hekto_tool::hekto_update() { auto &tool{m_hekto}; - if (false == tool.dirty && tool.history == m_services.history_size()) + if (false == tool.dirty && tool.history == m_history.history_size()) return; tool.dirty = false; - tool.history = m_services.history_size(); + tool.history = m_history.history_size(); tool.posts.clear(); tool.found.clear(); tool.error.clear(); @@ -449,7 +449,7 @@ void hekto_tool::hekto_update() double distance; tool.origin = project(tool.samples, tool.point, distance); auto const length{tool.samples.back().chainage}; - for (auto const &standing : m_services.standing_templates([](std::string const &File) { return File.find("hekto") != std::string::npos; })) + for (auto const &standing : m_templates.standing_templates([](std::string const &File) { return File.find("hekto") != std::string::npos; })) { hekto_found post; post.post = standing; @@ -488,7 +488,7 @@ void hekto_tool::hekto_update() // alternately: the even hectometres on the right, the odd ones on the left of the growing kilometrage auto const side{tool.side != 0 ? tool.side : (hectometres % 2 == 0 ? 1 : -1)}; auto const planar{plan_of(axis) + left * (side * tool.offset)}; - post.position = m_services.placement_on_ground({planar.x, axis.y, planar.y}); + post.position = m_ground.placement_on_ground({planar.x, axis.y, planar.y}); post.yaw = std::fmod(heading_of(growing) + tool.turn, 360.0); // of the posts with this number the one nearest to the place stands for it, the others are duplicates. it stands wrong // when it's farther from the place than allowed: along the line, across it, or on the other side @@ -526,7 +526,7 @@ void hekto_tool::hekto_remove_duplicates() for (auto const &found : tool.found) if (found.duplicate) instances.push_back(found.post.instance); - auto const removed{m_services.remove_templates(instances)}; + auto const removed{m_templates.remove_templates(instances)}; tool.status = format(STR_P("%zu duplicate hectometre post removed, Ctrl+Z brings it back", "%zu duplicate hectometre posts removed, Ctrl+Z brings them back", static_cast(removed)), removed); WriteLog("Editor: " + tool.status); tool.dirty = true; @@ -549,10 +549,10 @@ void hekto_tool::hekto_place() items.push_back(std::move(item)); } std::string error; - auto const placed{m_services.place_templates(items, error)}; + auto const placed{m_templates.place_templates(items, error)}; tool.status = placed > 0 ? format(STR_P("%zu hectometre post placed, Ctrl+Z takes it back", "%zu hectometre posts placed, Ctrl+Z takes them back", static_cast(placed)), placed) : error; if (placed > 0) - m_services.first_step_done(editor_first_step::posts); + m_guide.first_step_done(editor_first_step::posts); WriteLog("Editor: " + tool.status, logtype::generic); tool.dirty = true; } @@ -571,7 +571,7 @@ void hekto_tool::hekto_fix() item.yaw = post.yaw; moves.emplace_back(tool.found[post.standing].post, item); } - auto const moved{m_services.move_templates(moves)}; + auto const moved{m_templates.move_templates(moves)}; tool.status = format(STR_P("%zu hectometre post moved to its place, Ctrl+Z takes it back", "%zu hectometre posts moved to their places, Ctrl+Z takes it back", static_cast(moved)), moved); WriteLog("Editor: " + tool.status, logtype::generic); tool.dirty = true; @@ -580,7 +580,7 @@ void hekto_tool::hekto_fix() void hekto_tool::render_options() { auto &tool{m_hekto}; - auto *selected{m_services.selected_track()}; + auto *selected{m_selection.selected_track()}; if (tool.track == nullptr || (selected != nullptr && selected != tool.track && ImGui::SmallButton(STR_C("Along the line of the selected path")))) { if (selected == nullptr) @@ -679,7 +679,7 @@ void hekto_tool::render_options() auto const label{post.duplicate ? format(STR_C("km %s duplicate##%u"), km_text(post.hectometres).c_str(), post.post.instance) : format(STR_C("km %s stands %.1f m from its place##%u"), km_text(post.hectometres).c_str(), post.distance, post.post.instance)}; if (ImGui::Selectable(label.c_str())) { - m_services.fly_to(post.post.location, glm::normalize(glm::dvec3{-1.0, -0.8, -1.0}), 20.0); + m_view.fly_to(post.post.location, glm::normalize(glm::dvec3{-1.0, -0.8, -1.0}), 20.0); } } ImGui::EndChild(); @@ -735,13 +735,13 @@ void hekto_tool::draw_overlay() const void fouling_tool::fouling_update() { auto &tool{m_fouling}; - auto *selected{m_services.selected_track()}; + auto *selected{m_selection.selected_track()}; if (tool.scope == 0 && selected != tool.selected) tool.dirty = true; - if (false == tool.dirty && tool.history == m_services.history_size()) + if (false == tool.dirty && tool.history == m_history.history_size()) return; tool.dirty = false; - tool.history = m_services.history_size(); + tool.history = m_history.history_size(); tool.selected = selected; tool.points.clear(); tool.found.clear(); @@ -792,7 +792,7 @@ void fouling_tool::fouling_update() point.track = track; auto const middle{(onbranch + onmain) * 0.5}; // the points of the paths are at the rail foot, the heads of the rails are the height of the rail over them - point.position = tool.height_mode == 1 ? glm::dvec3{middle.x, 0.5 * (onbranch.y + onmain.y) + kRailHeight + tool.height, middle.z} : m_services.placement_on_ground({middle.x, std::max(onbranch.y, onmain.y), middle.z}); + point.position = tool.height_mode == 1 ? glm::dvec3{middle.x, 0.5 * (onbranch.y + onmain.y) + kRailHeight + tool.height, middle.z} : m_ground.placement_on_ground({middle.x, std::max(onbranch.y, onmain.y), middle.z}); auto const back{plan_of(previous - branch[i])}; point.yaw = std::fmod(heading_of(glm::length(back) > 1e-9 ? glm::normalize(back) : glm::dvec2{0.0, 1.0}) + tool.turn, 360.0); tool.points.push_back(point); @@ -800,7 +800,7 @@ void fouling_tool::fouling_update() } } // markers standing: each goes to the nearest fouling point, the others are left alone - auto const markers{m_services.standing_templates([](std::string const &File) { return File.find("w17") != std::string::npos || File.find("ukres") != std::string::npos; })}; + auto const markers{m_templates.standing_templates([](std::string const &File) { return File.find("w17") != std::string::npos || File.find("ukres") != std::string::npos; })}; for (auto const &marker : markers) { int nearest{-1}; @@ -857,7 +857,7 @@ void fouling_tool::place(int const Only) items.push_back(std::move(item)); } std::string error; - auto const placed{m_services.place_templates(items, error)}; + auto const placed{m_templates.place_templates(items, error)}; tool.status = placed > 0 ? format(STR_P("%zu fouling point marker placed, Ctrl+Z takes it back", "%zu fouling point markers placed, Ctrl+Z takes them back", static_cast(placed)), placed) : error; WriteLog("Editor: " + tool.status, logtype::generic); tool.dirty = true; @@ -888,7 +888,7 @@ void fouling_tool::fix(int const Only) } moves.emplace_back(found.marker, item); } - auto const moved{m_services.move_templates(moves)}; + auto const moved{m_templates.move_templates(moves)}; tool.status = format(STR_P("%zu fouling point marker moved to its place, Ctrl+Z takes it back", "%zu fouling point markers moved to their places, Ctrl+Z takes it back", static_cast(moved)), moved); WriteLog("Editor: " + tool.status, logtype::generic); tool.dirty = true; @@ -971,7 +971,7 @@ void fouling_tool::render_options() place(i); ImGui::SameLine(); if (ImGui::Selectable(format(STR_C("%s: no W17"), point.track->name().c_str()).c_str())) - m_services.fly_to(point.position, look, 25.0); + m_view.fly_to(point.position, look, 25.0); ImGui::PopID(); } for (int i = 0; i < static_cast(tool.found.size()); ++i) @@ -984,7 +984,7 @@ void fouling_tool::render_options() fix(i); ImGui::SameLine(); if (ImGui::Selectable(format(STR_C("%s: W17 %.1f m off, %+.2f m high"), tool.points[found.point].track->name().c_str(), found.error, found.height_error).c_str())) - m_services.fly_to(found.marker.location, look, 25.0); + m_view.fly_to(found.marker.location, look, 25.0); ImGui::PopID(); } ImGui::EndChild(); @@ -1146,7 +1146,7 @@ editor_selftest_result fouling_tool::selftest(std::string const &Command, std::i auto &tool{m_fouling}; if (values["show"] == "1") { - m_services.show_track_tab(editor_track_tab::lineside); + m_view.show_track_tab(editor_track_tab::lineside); m_tab.expand = true; } tool.scope = std::stoi(values.count("scope") ? values["scope"] : "2"); diff --git a/application/editormodelineside.h b/application/editormodelineside.h index d007cd54..d7b23606 100644 --- a/application/editormodelineside.h +++ b/application/editormodelineside.h @@ -20,7 +20,7 @@ http://mozilla.org/MPL/2.0/. class hekto_tool final : public editor_tool { public: - explicit hekto_tool(editor_services &Services) : m_services{Services} {} + hekto_tool(editor_selection &Selection, editor_view &View, editor_history &History, editor_templates &Templates, editor_ground &Ground, editor_guide &Guide) : m_selection{Selection}, m_view{View}, m_history{History}, m_templates{Templates}, m_ground{Ground}, m_guide{Guide} {} void draw_overlay() const override; void render_options() override; editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) override; @@ -28,7 +28,12 @@ class hekto_tool final : public editor_tool void start(TTrack &Track, glm::dvec3 const &Point); private: - editor_services &m_services; + editor_selection &m_selection; + editor_view &m_view; + editor_history &m_history; + editor_templates &m_templates; + editor_ground &m_ground; + editor_guide &m_guide; tool_tab m_tab; struct hekto_post { @@ -85,7 +90,7 @@ class hekto_tool final : public editor_tool class fouling_tool final : public editor_tool { public: - explicit fouling_tool(editor_services &Services) : m_services{Services} {} + fouling_tool(editor_selection &Selection, editor_view &View, editor_history &History, editor_templates &Templates, editor_ground &Ground) : m_selection{Selection}, m_view{View}, m_history{History}, m_templates{Templates}, m_ground{Ground} {} void draw_overlay() const override; void render_options() override; editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) override; @@ -101,7 +106,11 @@ class fouling_tool final : public editor_tool void check_selected_switch(); private: - editor_services &m_services; + editor_selection &m_selection; + editor_view &m_view; + editor_history &m_history; + editor_templates &m_templates; + editor_ground &m_ground; tool_tab m_tab; struct fouling_point { diff --git a/application/editormodeparallel.cpp b/application/editormodeparallel.cpp index 5180560c..f5ac7c5d 100644 --- a/application/editormodeparallel.cpp +++ b/application/editormodeparallel.cpp @@ -210,15 +210,15 @@ double lateral_of(TTrack const &Src, TTrack const &Dst) TTrack *parallel_tool::parallel_source() const { - auto const &set{m_services.track_set()}; + auto const &set{m_selection.track_set()}; if (set.size() >= 2) return set.front(); - return m_services.selected_track(); + return m_selection.selected_track(); } TTrack *parallel_tool::parallel_target() const { - auto const &set{m_services.track_set()}; + auto const &set{m_selection.track_set()}; if (set.size() != 2) return nullptr; return set.back() != set.front() ? set.back() : nullptr; @@ -245,13 +245,13 @@ void parallel_tool::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_services.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_history.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_services.history_size(); + tool.built_history = m_history.history_size(); tool.pieces.clear(); tool.styles.clear(); tool.length = 0.0; @@ -330,8 +330,8 @@ void parallel_tool::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); - m_services.digitize_drawn(created); - m_services.push_track_snapshot({}, created); + m_design.digitize_drawn(created); + m_history.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")}; @@ -376,11 +376,11 @@ void parallel_tool::parallel_apply() if (joint.other != nullptr) committed.push_back(joint.other); editor_track::commit(committed); - m_services.push_track_snapshot(std::move(states)); + m_history.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; - m_services.straight_refresh(); + m_design.straight_refresh(); } void parallel_tool::render_parallel_controls(bool const Scope) @@ -455,9 +455,9 @@ bool parallel_tool::parallel_preview_visible() const { if (m_parallel.pieces.empty() || parallel_source() != m_parallel.built_for) return false; - if (m_services.current_track_tab() == editor_track_tab::parallel) + if (m_view.current_track_tab() == editor_track_tab::parallel) return true; - return m_services.current_track_tab() == editor_track_tab::path && parallel_target() != nullptr; + return m_view.current_track_tab() == editor_track_tab::path && parallel_target() != nullptr; } void parallel_tool::draw_overlay() const diff --git a/application/editormodeparallel.h b/application/editormodeparallel.h index f221c6f4..d10ef41b 100644 --- a/application/editormodeparallel.h +++ b/application/editormodeparallel.h @@ -18,7 +18,7 @@ http://mozilla.org/MPL/2.0/. class parallel_tool final : public editor_tool { public: - explicit parallel_tool(editor_services &Services) : m_services{Services} {} + parallel_tool(editor_selection &Selection, editor_view &View, editor_history &History, editor_track_design &Design) : m_selection{Selection}, m_view{View}, m_history{History}, m_design{Design} {} void draw_overlay() const override; void render_options() override; editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) override; @@ -26,7 +26,10 @@ class parallel_tool final : public editor_tool void render_set_options(); private: - editor_services &m_services; + editor_selection &m_selection; + editor_view &m_view; + editor_history &m_history; + editor_track_design &m_design; tool_tab m_tab; struct parallel_state { diff --git a/application/editormodespeed.cpp b/application/editormodespeed.cpp index 583c489f..c033d9cd 100644 --- a/application/editormodespeed.cpp +++ b/application/editormodespeed.cpp @@ -51,14 +51,14 @@ void speed_tool::speed_start() state.results.clear(); state.selected = -1; state.status.clear(); - state.options.norms = m_services.route_norms(); + state.options.norms = m_design.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{m_services.selected_track()}; + auto *track{m_selection.selected_track()}; if (track == nullptr || false == rail(*track)) { state.status = STR_C("Select a track first"); @@ -87,7 +87,7 @@ void speed_tool::speed_start() if (track != nullptr && rail(*track)) add(track); } - state.history = m_services.history_size(); + state.history = m_history.history_size(); } void speed_tool::update() @@ -145,8 +145,8 @@ void speed_tool::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_services.set_current_node(verdict.track); - m_services.focus_track(*verdict.track, verdict.path, verdict.position); + m_selection.set_current_node(verdict.track); + m_view.focus_track(*verdict.track, verdict.path, verdict.position); } void speed_tool::speed_apply(std::vector const &Indices, bool const Branches) @@ -189,7 +189,7 @@ void speed_tool::speed_apply(std::vector const &Indices, bool const Branche state.status = STR_C("Nothing changed") + skipped; return; } - m_services.push_track_snapshot(std::move(states)); + m_history.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]}; @@ -198,7 +198,7 @@ void speed_tool::speed_apply(std::vector const &Indices, bool const Branche } for (auto &verdict : state.results) verdict.set = speed_check::path_velocity(*verdict.track, verdict.path); - state.history = m_services.history_size(); + state.history = m_history.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); } @@ -262,7 +262,7 @@ void speed_tool::render_options() ImGui::TextWrapped("%s", state.status.c_str()); if (state.results.empty()) return; - if (state.history != m_services.history_size()) + if (state.history != m_history.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}; diff --git a/application/editormodespeed.h b/application/editormodespeed.h index a477f59a..cafb8d21 100644 --- a/application/editormodespeed.h +++ b/application/editormodespeed.h @@ -19,7 +19,7 @@ http://mozilla.org/MPL/2.0/. class speed_tool final : public editor_tool { public: - explicit speed_tool(editor_services &Services) : m_services{Services} {} + speed_tool(editor_selection &Selection, editor_view &View, editor_history &History, editor_track_design &Design) : m_selection{Selection}, m_view{View}, m_history{History}, m_design{Design} {} void update() override; void draw_overlay() const override; void render_options() override; @@ -28,7 +28,10 @@ class speed_tool final : public editor_tool void check_line(); private: - editor_services &m_services; + editor_selection &m_selection; + editor_view &m_view; + editor_history &m_history; + editor_track_design &m_design; // speed limits of the paths checked against what their geometry allows struct speed_state { diff --git a/application/editormodevehicle.cpp b/application/editormodevehicle.cpp index daf22ceb..e83353f2 100644 --- a/application/editormodevehicle.cpp +++ b/application/editormodevehicle.cpp @@ -209,7 +209,7 @@ bool vehicle_tool::place() return false; } tool.placed = head; - m_services.first_step_done(editor_first_step::drive); + m_guide.first_step_done(editor_first_step::drive); auto const trackname{tool.track->name().empty() ? std::string{STR("(no name)")} : tool.track->name()}; auto const behind{static_cast(vehicles.size()) - 1}; tool.status = behind > 0 ? format(STR_P("%s and %d vehicle behind it stand on %s", "%s and %d vehicles behind it stand on %s", behind), head.c_str(), behind, trackname.c_str()) : format(STR_C("%s stands on %s"), head.c_str(), trackname.c_str()); @@ -240,8 +240,8 @@ bool vehicle_tool::place() void vehicle_tool::render_options() { auto &tool{m_vehicle}; - auto *selected{m_services.selected_track()}; - auto const &hover{m_services.hovered_track()}; + auto *selected{m_selection.selected_track()}; + auto const &hover{m_selection.hovered_track()}; if (selected != nullptr && selected == tool.track && hover.track == selected && ImGui::IsMouseClicked(0) && false == ImGui::GetIO().WantCaptureMouse) start(*selected, hover.point); else if (selected != nullptr && selected != tool.track) diff --git a/application/editormodevehicle.h b/application/editormodevehicle.h index 15b74c13..dabde1ce 100644 --- a/application/editormodevehicle.h +++ b/application/editormodevehicle.h @@ -27,7 +27,7 @@ struct skin_set; class vehicle_tool final : public editor_tool { public: - explicit vehicle_tool(editor_services &Services) : m_services{Services} {} + vehicle_tool(editor_selection &Selection, editor_guide &Guide) : m_selection{Selection}, m_guide{Guide} {} void draw_overlay() const override; void render_options() override; editor_selftest_result selftest(std::string const &Command, std::istringstream &Words) override; @@ -38,7 +38,8 @@ class vehicle_tool final : public editor_tool void trainsets_written(); private: - editor_services &m_services; + editor_selection &m_selection; + editor_guide &m_guide; tool_tab m_tab; struct vehicle_state { diff --git a/application/editortool.h b/application/editortool.h index fd110f91..19e301bf 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -84,37 +84,91 @@ struct nmt_place bool shapes{true}; // not a bridge nor a tunnel }; -class editor_services +class editor_selection { public: virtual TTrack *selected_track() const = 0; - virtual bool track_editing() const = 0; - 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; + virtual track_hover const &hovered_track() const = 0; + virtual void set_current_node(scene::basic_node *Node) = 0; + virtual void clear_track_point() = 0; + + protected: + ~editor_selection() = default; +}; + +class editor_view +{ + public: + 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 clear_track_point() = 0; + virtual editor_track_tab current_track_tab() const = 0; + virtual void show_track_tab(editor_track_tab const Wanted) = 0; + + protected: + ~editor_view() = default; +}; + +class editor_history +{ + public: + virtual std::size_t history_size() const = 0; + virtual void push_track_snapshot(std::vector> States, std::vector Created = {}, std::vector Removed = {}) = 0; + + protected: + ~editor_history() = default; +}; + +class editor_templates +{ + public: + virtual std::vector standing_templates(std::function const &Accept) const = 0; + 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; + + protected: + ~editor_templates() = default; +}; + +class editor_ground +{ + public: + virtual glm::dvec3 placement_on_ground(glm::dvec3 Location) = 0; virtual void nmt_locate() = 0; virtual std::vector nmt_track_places(double const Step) const = 0; virtual editor_nmt &terrain_model() = 0; virtual double terrain_model_offset() const = 0; virtual bool terrain_model_fitted() const = 0; - virtual std::vector standing_templates(std::function const &Accept) const = 0; - 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; + + protected: + ~editor_ground() = default; +}; + +class editor_track_design +{ + public: + virtual bool track_editing() const = 0; + virtual alignment::limits const &route_norms() const = 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: + ~editor_track_design() = default; +}; + +class editor_guide +{ + public: + virtual void first_step_done(editor_first_step const Step) = 0; + + protected: + ~editor_guide() = default; +}; + +class editor_services : public editor_selection, public editor_view, public editor_history, public editor_templates, public editor_ground, public editor_track_design, public editor_guide +{ protected: ~editor_services() = default; };