17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 17:04:53 +01:00

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.
This commit is contained in:
maj00r
2026-10-21 10:08:27 +02:00
parent 872e1b5e19
commit 747c6817f0
14 changed files with 181 additions and 105 deletions

View File

@@ -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<editor_tool *> m_tools{&m_gauge, &m_speed, &m_joints, &m_vehicle, &m_hekto, &m_fouling, &m_parallel};
void infra_rebase();

View File

@@ -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<glm::dvec3> 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();

View File

@@ -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
{

View File

@@ -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<TTrack const *> 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<double> differences(state.places.size(), std::numeric_limits<double>::quiet_NaN());
std::vector<double> 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<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Changed)
@@ -498,11 +498,11 @@ void joints_tool::joints_fix(std::vector<int> 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);

View File

@@ -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
{

View File

@@ -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<long long>(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<long long>(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<long long>(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<long long>(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<long long>(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<int>(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");

View File

@@ -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
{

View File

@@ -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

View File

@@ -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
{

View File

@@ -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<int>(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<int> const &Indices, bool const Branches)
@@ -189,7 +189,7 @@ void speed_tool::speed_apply(std::vector<int> 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<int> 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};

View File

@@ -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
{

View File

@@ -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<int>(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)

View File

@@ -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
{

View File

@@ -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<TTrack *> 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<std::pair<TTrack *, editor_track::state>> States, std::vector<TTrack *> Created = {}, std::vector<TTrack *> Removed = {}) = 0;
protected:
~editor_history() = default;
};
class editor_templates
{
public:
virtual std::vector<standing_template> standing_templates(std::function<bool(std::string const &)> const &Accept) const = 0;
virtual std::size_t place_templates(std::vector<template_item> const &Items, std::string &Error) = 0;
virtual std::size_t move_templates(std::vector<std::pair<standing_template, template_item>> const &Moves) = 0;
virtual std::size_t remove_templates(std::vector<scene::instance_handle> 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_place> 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_template> standing_templates(std::function<bool(std::string const &)> const &Accept) const = 0;
virtual std::size_t place_templates(std::vector<template_item> const &Items, std::string &Error) = 0;
virtual std::size_t move_templates(std::vector<std::pair<standing_template, template_item>> const &Moves) = 0;
virtual std::size_t remove_templates(std::vector<scene::instance_handle> 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<TTrack *> const &Created, bool const Alone = true) = 0;
virtual void straight_refresh() = 0;
virtual void push_track_snapshot(std::vector<std::pair<TTrack *, editor_track::state>> States, std::vector<TTrack *> Created = {}, std::vector<TTrack *> 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;
};