From c45c4713d653db8c45622ee699a0e989934495d5 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 21 Oct 2026 09:44:17 +0200 Subject: [PATCH] refactor(editor): the trainset is a tool of its own The fourth tool out of editor_mode. The track under the cursor and the first steps of the tutorial become types of their own, which the editor gives to the tools, and a tool shown as a tab of the lineside panel keeps its open and expanded flags in a tool_tab. The trainsets waiting for the save are handed to the editor by the tool, which hears back when they were written. The self-test command trainset moves with it. --- application/editormode.cpp | 22 ++----- application/editormode.h | 66 +++----------------- application/editormodeselftestscene.cpp | 23 ------- application/editormodeselftestsweep.cpp | 2 +- application/editormodeselftesttrack.cpp | 2 +- application/editormodetrack.cpp | 7 ++- application/editormodetrackux.cpp | 4 +- application/editormodevehicle.cpp | 82 ++++++++++++++++++++----- application/editormodevehicle.h | 79 ++++++++++++++++++++++++ application/editormodeviewport.cpp | 4 +- application/editortool.h | 18 ++++++ 11 files changed, 188 insertions(+), 121 deletions(-) create mode 100644 application/editormodevehicle.h diff --git a/application/editormode.cpp b/application/editormode.cpp index 19559df6..29f6347f 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -1286,9 +1286,8 @@ bool editor_mode::update() { Global.editor_tracks = (current_work_area() == work_area::tracks); selftest_step(); - if (m_vehicle.leave) + if (m_vehicle.take_leave()) { - m_vehicle.leave = false; Application.pop_mode(); return true; } @@ -1456,7 +1455,7 @@ bool editor_mode::update() { draw_hekto_overlay(); draw_fouling_overlay(); - draw_vehicle_marker(); + m_vehicle.draw_overlay(); } render_track_context(); update_build_tools(); @@ -2937,13 +2936,7 @@ bool editor_mode::save() auto const controls{turntable_controls(rootstatements)}; auto const noses{nose_controls(rootstatements)}; std::vector trailing; - std::erase_if(m_vehicle.trainsets, [](auto const &Trainset) { - auto const *track{simulation::Paths.find(Trainset.track)}; - return track == nullptr || track->m_editorremoved; - }); - auto const placed{m_vehicle.trainsets.size()}; - for (auto const &trainset : m_vehicle.trainsets) - trailing.insert(trailing.end(), trainset.lines.begin(), trainset.lines.end()); + auto const placed{m_vehicle.trainset_lines(trailing)}; if (placed == 0) starter_trainset(trailing); auto const result = scene::Layers.save(rootstatements, trailing); @@ -2952,10 +2945,7 @@ bool editor_mode::save() if (result.success && noses) m_nose_included = true; if (result.success && placed > 0) - { - m_vehicle.trainsets.clear(); - m_vehicle.status = format(STR_C("%d trainset(s) written to the scenery"), static_cast(placed)); - } + m_vehicle.trainsets_written(); if (result.success && placed == 0 && trailing.size() == 3) { std::istringstream header{trailing.front()}; @@ -5301,12 +5291,12 @@ void editor_mode::render_first_steps() } case trial::trainset: { - ImGui::BeginDisabled(ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::lineside && m_vehicle.open); + ImGui::BeginDisabled(ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::lineside && m_vehicle.tab().open); if (ImGui::Button(STR_C("The trainset tool"))) { choose_work_area(work_area::tracks); show_track_tab(track_tab::lineside); - m_vehicle.expand = true; + m_vehicle.tab().expand = true; } ImGui::EndDisabled(); break; diff --git a/application/editormode.h b/application/editormode.h index 3d5c3140..3f6708b7 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/editormodespeed.h" +#include "application/editormodevehicle.h" #include "application/editortool.h" #include "scene/sceneeditor.h" #include "scene/scenenode.h" @@ -55,12 +56,6 @@ class TAnimModel; // a tool of a field of work of the editor, a button in the toolbar and over the 3d view struct editor_tool_button; class TTrack; -namespace ui -{ -class vehicles_bank; -struct vehicle_desc; -struct skin_set; -} class editor_mode : public application_mode, private editor_track::observer, private editor_services { @@ -240,9 +235,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri void render_help_menu(); void render_shortcuts_window(); bool m_shortcuts_open{false}; - enum class 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 }; + using first_step = editor_first_step; void render_first_steps(); - void first_step_done(first_step const Step); + void first_step_done(first_step const Step) override; bool m_first_steps_open{false}; std::array(first_step::count)> m_first_steps_done{}; int m_tutorial_page{0}; @@ -718,6 +713,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri bool track_editing() const override; std::size_t history_size() const override; glm::dvec3 camera_position() const override; + track_hover const &hovered_track() const override; void render_path_ui(); void draw_track_overlay() const; editor_track::point_ref track_handle_hit() const; @@ -1516,13 +1512,7 @@ class editor_mode : public application_mode, private editor_track::observer, pri // a scenery with no tracks yet: in the middle of the view, how the first one is laid void draw_empty_scenery_hint(); // path under the cursor, outlined before a click so it's known which one the click takes - struct track_hover - { - TTrack *track{nullptr}; - int path{0}; - glm::dvec3 point{0.0}; - double since{0.0}; - }; + using track_hover = ::track_hover; track_hover m_hover; track_hover track_under_cursor(TTrack const *Only = nullptr, float const Pixels = 0.0f) const; track_hover split_spot() const; @@ -1925,49 +1915,6 @@ class editor_mode : public application_mode, private editor_track::observer, pri void render_fouling_ui(); void draw_fouling_overlay() const; - // a trainset put on the selected path, written to the scenery on save, or only to be driven - struct vehicle_tool - { - bool open{false}; - bool expand{false}; - std::shared_ptr bank; - char search[64]{}; - bool controllable_only{true}; - std::shared_ptr vehicle; - std::shared_ptr skin; - int driver{0}; // 0: a driver, to be driven; 1: nobody, parked - TTrack *track{nullptr}; - glm::dvec3 point{0.0}; - std::string placed; - std::string status; - bool leave{false}; // the editor gives way to the driving at the start of the next frame - struct consist_vehicle - { - std::shared_ptr vehicle; - std::shared_ptr skin; - bool turned{false}; - }; - std::vector consist; - int picked{-1}; // vehicle of the train whose buttons are shown - std::set minis; // skins whose side view was asked for - int facing{1}; - bool write{true}; - char timetable[64]{"none"}; - double velocity{0.0}; - struct trainset - { - std::string head; - std::string track; - std::vector lines; - }; - std::vector trainsets; - }; - vehicle_tool m_vehicle; - void vehicle_start(TTrack &Track, glm::dvec3 const &Point); - bool vehicle_place(); - void render_vehicle_ui(); - void draw_vehicle_marker() const; - struct signal_template { std::string file; @@ -2445,7 +2392,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri gauge_tool m_gauge{*this}; speed_tool m_speed{*this}; joints_tool m_joints{*this}; - std::vector m_tools{&m_gauge, &m_speed, &m_joints}; + vehicle_tool m_vehicle{*this}; + std::vector m_tools{&m_gauge, &m_speed, &m_joints, &m_vehicle}; void infra_rebase(); void render_infra_search(); diff --git a/application/editormodeselftestscene.cpp b/application/editormodeselftestscene.cpp index b74dc2de..46c8e2e0 100644 --- a/application/editormodeselftestscene.cpp +++ b/application/editormodeselftestscene.cpp @@ -293,29 +293,6 @@ editor_mode::selftest_result editor_mode::selftest_scene(std::string const &comm WriteLog(format("SELFTEST mapstate orthophoto %d top %d extent %.0f mark %s camera %.1f %.1f %.1f", m_orthophoto.enabled() ? 1 : 0, Global.EditorOrtho ? 1 : 0, Global.EditorOrthoExtent, marked.empty() ? "-" : marked.front().c_str(), Camera.Pos.x, Camera.Pos.y, Camera.Pos.z)); return selftest_result::handled; } - if (command == "trainset") - { - std::string name; - std::string vehicle; - std::string skin; - int facing{1}; - words >> name >> vehicle >> skin >> facing >> m_vehicle.velocity; - auto *track{simulation::Paths.find(name)}; - if (track == nullptr) - return selftest_result::handled; - auto desc{std::make_shared()}; - desc->path = vehicle; - auto set{std::make_shared()}; - set->skin = skin; - m_vehicle.consist.clear(); - m_vehicle.vehicle = desc; - m_vehicle.skin = set; - vehicle_start(*track, geometry::bezier{track->m_paths.front()}.point(0.5)); - m_vehicle.facing = facing; - auto const placed{vehicle_place()}; - WriteLog(format("SELFTEST trainset %s placed %d pending %d: %s", name.c_str(), placed ? 1 : 0, static_cast(m_vehicle.trainsets.size()), m_vehicle.status.c_str())); - return selftest_result::handled; - } if (command == "selectmodel") { glm::dvec3 point{0.0}; diff --git a/application/editormodeselftestsweep.cpp b/application/editormodeselftestsweep.cpp index 814562f3..084994a2 100644 --- a/application/editormodeselftestsweep.cpp +++ b/application/editormodeselftestsweep.cpp @@ -388,7 +388,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.expand : tab == "hekto" ? m_hekto.expand : tab == "fouling" ? m_fouling.expand : m_parallel.expand) = true; + (tab == "vehicle" ? m_vehicle.tab().expand : tab == "hekto" ? m_hekto.expand : tab == "fouling" ? m_fouling.expand : m_parallel.expand) = true; return selftest_result::handled; } if (command == "bendpanel") diff --git a/application/editormodeselftesttrack.cpp b/application/editormodeselftesttrack.cpp index 0c8ca5b5..dad7a6a5 100644 --- a/application/editormodeselftesttrack.cpp +++ b/application/editormodeselftesttrack.cpp @@ -142,7 +142,7 @@ editor_mode::selftest_result editor_mode::selftest_track(std::string const &comm m_hekto.expand = tab == 1; m_fouling.expand = tab == 2; m_parallel.expand = tab == 3; - m_vehicle.expand = tab == 4; + m_vehicle.tab().expand = tab == 4; return selftest_result::handled; } if (command == "curve") diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 3aa605f1..4866c4fd 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -67,6 +67,11 @@ glm::dvec3 editor_mode::camera_position() const return Camera.Pos; } +editor_mode::track_hover const &editor_mode::hovered_track() const +{ + return m_hover; +} + void editor_mode::set_current_node(scene::basic_node *Node) { m_node = Node; @@ -554,7 +559,7 @@ void editor_mode::render_track_inspector() m_hekto.open = false; m_fouling.open = false; m_parallel.open = false; - m_vehicle.open = false; + m_vehicle.tab().open = false; } ui()->set_track(m_track_window_open); if (false == m_track_window_open) diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index 9c551577..cf67bba7 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -793,7 +793,7 @@ void editor_mode::render_track_context() if (ImGui::MenuItem(STR_C("Place a vehicle here"))) { show_track_tab(track_tab::lineside); - vehicle_start(*track, point); + m_vehicle.start(*track, point); } if (ImGui::MenuItem(STR_C("Hectometre posts from here"))) { @@ -1479,7 +1479,7 @@ void editor_mode::render_lineside_ui() std::array const tabs{{ {"Hectometre posts", &m_hekto.open, &m_hekto.expand, [this]() { render_hekto_ui(); }}, {"Fouling points", &m_fouling.open, &m_fouling.expand, [this]() { render_fouling_ui(); }}, - {"Trainset", &m_vehicle.open, &m_vehicle.expand, [this]() { render_vehicle_ui(); }}, + {"Trainset", &m_vehicle.tab().open, &m_vehicle.tab().expand, [this]() { m_vehicle.render_options(); }}, }}; if (selected_track() == nullptr) ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.3f, 1.0f), "%s", STR_C("LMB on a track: the line the objects go along")); diff --git a/application/editormodevehicle.cpp b/application/editormodevehicle.cpp index 719d5a01..daf22ceb 100644 --- a/application/editormodevehicle.cpp +++ b/application/editormodevehicle.cpp @@ -8,8 +8,7 @@ http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" -#include "application/editormode.h" -#include "application/editoruilayer.h" +#include "application/editormodevehicle.h" #include "application/editorprojection.h" #include "application/editormodeshared.h" #include "application/application.h" @@ -154,18 +153,18 @@ double distance_along(TTrack const &Track, int const Path, glm::dvec3 const &Poi } // namespace -void editor_mode::vehicle_start(TTrack &Track, glm::dvec3 const &Point) +void vehicle_tool::start(TTrack &Track, glm::dvec3 const &Point) { m_vehicle.track = &Track; m_vehicle.point = Point; - m_vehicle.expand = true; + m_tab.expand = true; m_vehicle.status.clear(); auto const heading{heading_at(Track, Track.eType == tt_Switch ? path_under(Track, Point) : 0, Point)}; glm::dvec2 const away{Point.x - Global.pCamera.Pos.x, Point.z - Global.pCamera.Pos.z}; m_vehicle.facing = glm::dot(heading, away) >= 0.0 ? 1 : -1; } -bool editor_mode::vehicle_place() +bool vehicle_tool::place() { auto &tool{m_vehicle}; if (tool.track == nullptr || tool.track->m_editorremoved) @@ -210,7 +209,7 @@ bool editor_mode::vehicle_place() return false; } tool.placed = head; - first_step_done(first_step::drive); + m_services.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()); @@ -220,7 +219,7 @@ bool editor_mode::vehicle_place() { std::string timetable{tool.timetable}; timetable.erase(std::remove_if(timetable.begin(), timetable.end(), [](unsigned char const Character) { return std::isspace(Character) != 0; }), timetable.end()); - vehicle_tool::trainset entry{head, tool.track->name(), {}}; + vehicle_state::trainset entry{head, tool.track->name(), {}}; entry.lines.push_back("trainset " + (timetable.empty() ? std::string{"none"} : timetable) + " " + tool.track->name() + " " + to_string(offset, 2) + " " + to_string(tool.velocity, 0)); if (against) entry.lines.push_back("reversed"); @@ -238,14 +237,15 @@ bool editor_mode::vehicle_place() return true; } -void editor_mode::render_vehicle_ui() +void vehicle_tool::render_options() { auto &tool{m_vehicle}; - auto *selected{selected_track()}; - if (selected != nullptr && selected == tool.track && m_hover.track == selected && ImGui::IsMouseClicked(0) && false == ImGui::GetIO().WantCaptureMouse) - vehicle_start(*selected, m_hover.point); + auto *selected{m_services.selected_track()}; + auto const &hover{m_services.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) - vehicle_start(*selected, m_hover.track == selected ? m_hover.point : bezier{selected->m_paths.front()}.point(0.5)); + start(*selected, hover.track == selected ? hover.point : bezier{selected->m_paths.front()}.point(0.5)); if (tool.track == nullptr) { ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.3f, 1.0f), "%s", STR_C("LMB on a track: where the vehicle stands")); @@ -430,11 +430,11 @@ void editor_mode::render_vehicle_ui() ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.55f, 0.25f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.25f, 0.65f, 0.3f, 1.0f)); if (ImGui::Button(STR_C("Place"), ImVec2(half, tall))) - vehicle_place(); + place(); item_tooltip("A trainset on the track, placed where the track was clicked, or in the middle of the path"); ImGui::PopStyleColor(2); ImGui::SameLine(); - if (ImGui::Button(STR_C("Place and drive"), ImVec2(-1.0f, tall)) && vehicle_place()) + if (ImGui::Button(STR_C("Place and drive"), ImVec2(-1.0f, tall)) && place()) { Global.editor_enter_vehicle = tool.placed; tool.leave = true; @@ -461,10 +461,10 @@ void editor_mode::render_vehicle_ui() tool.trainsets.erase(tool.trainsets.begin() + static_cast(*drop)); } -void editor_mode::draw_vehicle_marker() const +void vehicle_tool::draw_overlay() const { auto const &tool{m_vehicle}; - if (false == tool.open || tool.track == nullptr) + if (false == m_tab.open || tool.track == nullptr) return; screen_projection const projection; ImVec2 screen; @@ -490,3 +490,53 @@ void editor_mode::draw_vehicle_marker() const ImVec2 const side{-unit.y, unit.x}; drawlist->AddTriangleFilled(ImVec2(end.x + unit.x * 10.0f, end.y + unit.y * 10.0f), ImVec2(end.x + side.x * 7.0f, end.y + side.y * 7.0f), ImVec2(end.x - side.x * 7.0f, end.y - side.y * 7.0f), colour); } + +bool vehicle_tool::take_leave() +{ + return std::exchange(m_vehicle.leave, false); +} + +std::size_t vehicle_tool::trainset_lines(std::vector &Lines) +{ + std::erase_if(m_vehicle.trainsets, [](auto const &Trainset) { + auto const *track{simulation::Paths.find(Trainset.track)}; + return track == nullptr || track->m_editorremoved; + }); + for (auto const &trainset : m_vehicle.trainsets) + Lines.insert(Lines.end(), trainset.lines.begin(), trainset.lines.end()); + return m_vehicle.trainsets.size(); +} + +void vehicle_tool::trainsets_written() +{ + m_vehicle.status = format(STR_C("%d trainset(s) written to the scenery"), static_cast(m_vehicle.trainsets.size())); + m_vehicle.trainsets.clear(); +} + +editor_selftest_result vehicle_tool::selftest(std::string const &Command, std::istringstream &Words) +{ + if (Command == "trainset") + { + std::string name; + std::string vehicle; + std::string skin; + int facing{1}; + Words >> name >> vehicle >> skin >> facing >> m_vehicle.velocity; + auto *track{simulation::Paths.find(name)}; + if (track == nullptr) + return editor_selftest_result::handled; + auto desc{std::make_shared()}; + desc->path = vehicle; + auto set{std::make_shared()}; + set->skin = skin; + m_vehicle.consist.clear(); + m_vehicle.vehicle = desc; + m_vehicle.skin = set; + start(*track, geometry::bezier{track->m_paths.front()}.point(0.5)); + m_vehicle.facing = facing; + auto const placed{place()}; + WriteLog(format("SELFTEST trainset %s placed %d pending %d: %s", name.c_str(), placed ? 1 : 0, static_cast(m_vehicle.trainsets.size()), m_vehicle.status.c_str())); + return editor_selftest_result::handled; + } + return editor_selftest_result::unhandled; +} diff --git a/application/editormodevehicle.h b/application/editormodevehicle.h new file mode 100644 index 00000000..15b74c13 --- /dev/null +++ b/application/editormodevehicle.h @@ -0,0 +1,79 @@ +/* +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 +#include +#include + +namespace ui +{ +class vehicles_bank; +struct vehicle_desc; +struct skin_set; +} + +// a trainset put on the selected path, written to the scenery on save, or only to be driven +class vehicle_tool final : public editor_tool +{ + public: + explicit vehicle_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 start(TTrack &Track, glm::dvec3 const &Point); + bool take_leave(); + std::size_t trainset_lines(std::vector &Lines); + void trainsets_written(); + + private: + editor_services &m_services; + tool_tab m_tab; + struct vehicle_state + { + std::shared_ptr bank; + char search[64]{}; + bool controllable_only{true}; + std::shared_ptr vehicle; + std::shared_ptr skin; + int driver{0}; // 0: a driver, to be driven; 1: nobody, parked + TTrack *track{nullptr}; + glm::dvec3 point{0.0}; + std::string placed; + std::string status; + bool leave{false}; // the editor gives way to the driving at the start of the next frame + struct consist_vehicle + { + std::shared_ptr vehicle; + std::shared_ptr skin; + bool turned{false}; + }; + std::vector consist; + int picked{-1}; // vehicle of the train whose buttons are shown + std::set minis; // skins whose side view was asked for + int facing{1}; + bool write{true}; + char timetable[64]{"none"}; + double velocity{0.0}; + struct trainset + { + std::string head; + std::string track; + std::vector lines; + }; + std::vector trainsets; + }; + vehicle_state m_vehicle; + bool place(); +}; diff --git a/application/editormodeviewport.cpp b/application/editormodeviewport.cpp index 7ba759f9..1bbed3fa 100644 --- a/application/editormodeviewport.cpp +++ b/application/editormodeviewport.cpp @@ -160,7 +160,7 @@ void editor_mode::work_area_tools(std::vector &Buttons) } case work_area::tracks: { - auto const vehicle{m_track_tab == track_tab::lineside && m_vehicle.open}; + auto const vehicle{m_track_tab == track_tab::lineside && m_vehicle.tab().open}; auto const add = [&](track_tab const Tab, bool const Group) { auto const &modes{track_modes()}; auto const found{std::find_if(modes.begin(), modes.end(), [&](track_mode const &Mode) { return Mode.tab == Tab; })}; @@ -191,7 +191,7 @@ void editor_mode::work_area_tools(std::vector &Buttons) 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); - m_vehicle.expand = false == vehicle; + m_vehicle.tab().expand = false == vehicle; }}); Buttons.back().accent = family_colour(1); add(track_tab::profile, true); diff --git a/application/editortool.h b/application/editortool.h index 2c746dc8..c4bbaa43 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -34,6 +34,22 @@ enum class editor_selftest_result again }; +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 }; + +struct track_hover +{ + TTrack *track{nullptr}; + int path{0}; + glm::dvec3 point{0.0}; + double since{0.0}; +}; + +struct tool_tab +{ + bool open{false}; + bool expand{false}; +}; + struct nmt_place { glm::dvec3 position{0.0}; // on the axis of the path @@ -52,6 +68,8 @@ class editor_services 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 void first_step_done(editor_first_step const Step) = 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;