mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-31 09:59:06 +01:00
feat(editor): a less crowded track editor and one choice of what the switch tool places
- the switch tool asks what to place: a switch, a slip switch, a crossing or a connection of tracks (crossover, double, scissors, ladder), with the templates and the options of that kind only and one line of how to place it with the mouse; the bar of hints says the same, the wheel keys of a connection included; panel in its own editormodeswitchtool.cpp - templates are listed by their names, the lengths a and b and the rest of the label in the tooltip - the panel of a selected switch shows its length, its type and its drive; the name, number, rail, kilometre and angle are under Details - one toolbar: the tools are no longer drawn a second time down the edge of the view; the track tools show select, lay, plan, switch and parallel, the others behind More, which starts closed again (setting track_tools_all) - the infrastructure tool has no button nor key - the gizmo options start closed, the node bank isn't drawn in the track mode - no drive models: said in plain words - self-test switchkind
This commit is contained in:
@@ -505,7 +505,6 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
// over the 3d view: the tools of the field of work along its left edge, the modes of the gizmo at its top, the axes of the
|
||||
// world and the top view in its top right corner
|
||||
void render_viewport_overlays();
|
||||
void render_viewport_tools();
|
||||
// the tools of the field of work, in the order of the toolbar
|
||||
void work_area_tools(std::vector<editor_tool_button> &Buttons);
|
||||
// the tools of the field of work in the toolbar, as icons with their names
|
||||
@@ -1026,6 +1025,7 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
slide_state slide;
|
||||
double slide_typed{0.0};
|
||||
int connection{-1};
|
||||
int connection_kind{0}; // the kind of connection chosen last, taken again when the connections are chosen
|
||||
int connection_second{-1};
|
||||
double connection_radius{0.0};
|
||||
double connection_gap{6.0};
|
||||
@@ -1034,9 +1034,25 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
double connection_stub{25.0};
|
||||
};
|
||||
switch_tool m_switch;
|
||||
// what the switch tool places: a switch into a straight or a curve, a slip switch or a crossing at a crossing of two
|
||||
// straights, or a connection of tracks laid whole
|
||||
enum class switch_kind
|
||||
{
|
||||
single,
|
||||
slip,
|
||||
crossing,
|
||||
connection
|
||||
};
|
||||
switch_kind current_switch_kind() const;
|
||||
void choose_switch_kind(switch_kind const Kind);
|
||||
bool switch_kind_offered(switch_kind const Kind) const;
|
||||
bool plan_connection(connection::request &Request, connection::result &Result) const;
|
||||
void insert_connection();
|
||||
void render_connection_ui();
|
||||
void render_connection_options();
|
||||
// the name of a kind of connection of tracks, translated
|
||||
static char const *connection_name(int const Kind);
|
||||
// the name of a switch template for the lists: where it comes from and its name without the lengths
|
||||
static std::string switch_template_label(editor_track::switch_template const &Template);
|
||||
std::string connection_readout(connection::result const &Plan) const;
|
||||
editor_track::straight straight_near(glm::dvec3 const &Point, std::vector<TTrack const *> const &Exclude, double const Reach) const;
|
||||
editor_track::straight straight_through(glm::dvec3 const &Point, glm::dvec2 const &Direction) const;
|
||||
@@ -1274,7 +1290,7 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
track_tab tab;
|
||||
char const *tooltip;
|
||||
};
|
||||
static std::array<track_mode, 12> const &track_modes();
|
||||
static std::array<track_mode, 11> const &track_modes();
|
||||
bool track_plan_tab() const { return m_track_tab == track_tab::straights || m_track_tab == track_tab::route; }
|
||||
track_tab plan_tab_for(TTrack const *Track) const;
|
||||
// the objects along the track: a tab for each tool
|
||||
|
||||
@@ -32,17 +32,6 @@ namespace
|
||||
using geometry::cross;
|
||||
using geometry::plan_of;
|
||||
|
||||
char const *connection_name(int const Kind)
|
||||
{
|
||||
switch (static_cast<connection::kind>(Kind))
|
||||
{
|
||||
case connection::kind::double_crossover: return STR_C("Double crossover");
|
||||
case connection::kind::scissors: return STR_C("Scissors crossover");
|
||||
case connection::kind::ladder: return STR_C("Switch ladder");
|
||||
default: return STR_C("Crossover");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> spacings_of(char const *Text)
|
||||
{
|
||||
std::string text{Text};
|
||||
@@ -59,13 +48,6 @@ std::vector<double> spacings_of(char const *Text)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string switch_label(editor_track::switch_template const &Template)
|
||||
{
|
||||
if (Template.source != nullptr)
|
||||
return STR("Scenery: ") + Template.label;
|
||||
return (Template.own ? STR("Own: ") : std::string{"PLK: "}) + Template.label;
|
||||
}
|
||||
|
||||
bool usable_in_connection(editor_track::switch_template const &Template)
|
||||
{
|
||||
return false == Template.double_slip && false == Template.twofold();
|
||||
@@ -73,6 +55,17 @@ bool usable_in_connection(editor_track::switch_template const &Template)
|
||||
|
||||
} // namespace
|
||||
|
||||
char const *editor_mode::connection_name(int const Kind)
|
||||
{
|
||||
switch (static_cast<connection::kind>(Kind))
|
||||
{
|
||||
case connection::kind::double_crossover: return STR_C("Double crossover");
|
||||
case connection::kind::scissors: return STR_C("Scissors crossover");
|
||||
case connection::kind::ladder: return STR_C("Switch ladder");
|
||||
default: return STR_C("Crossover");
|
||||
}
|
||||
}
|
||||
|
||||
editor_track::straight editor_mode::straight_near(glm::dvec3 const &Point, std::vector<TTrack const *> const &Exclude, double const Reach) const
|
||||
{
|
||||
TTrack *found{nullptr};
|
||||
@@ -335,32 +328,19 @@ void editor_mode::insert_connection()
|
||||
ui()->set_status(tool.status, false);
|
||||
}
|
||||
|
||||
void editor_mode::render_connection_ui()
|
||||
void editor_mode::render_connection_options()
|
||||
{
|
||||
auto &tool{m_switch};
|
||||
ImGui::PushItemWidth(220.0f);
|
||||
if (ImGui::BeginCombo(STR_C("Connection##switchconnection"), tool.connection < 0 ? STR_C("Single switch") : connection_name(tool.connection)))
|
||||
{
|
||||
if (ImGui::Selectable(STR_C("Single switch"), tool.connection < 0))
|
||||
tool.connection = -1;
|
||||
for (int i = 0; i < connection::kind_count; ++i)
|
||||
if (ImGui::Selectable(connection_name(i), tool.connection == i))
|
||||
tool.connection = i;
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
item_tooltip("A connection of tracks laid whole: press on a straight and drag onto the other track. Its switches are cut into the tracks with the inserts between them and get their drives; Shift+wheel while dragging: another kind, Ctrl+wheel: R of the aligning curve");
|
||||
if (tool.connection < 0)
|
||||
{
|
||||
ImGui::PopItemWidth();
|
||||
return;
|
||||
}
|
||||
auto const label = [&](int const Index) { return Index < 0 || Index >= static_cast<int>(tool.templates.size()) ? std::string{STR_C("The same as the first one")} : switch_label(tool.templates[Index]); };
|
||||
ImGui::PushItemWidth(220.0f);
|
||||
auto const label = [&](int const Index) { return Index < 0 || Index >= static_cast<int>(tool.templates.size()) ? std::string{STR_C("The same as the first one")} : switch_template_label(tool.templates[Index]); };
|
||||
if (ImGui::BeginCombo(STR_C("Other switches##connectionsecond"), label(tool.connection_second).c_str()))
|
||||
{
|
||||
if (ImGui::Selectable(STR_C("The same as the first one"), tool.connection_second < 0))
|
||||
tool.connection_second = -1;
|
||||
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
|
||||
if (usable_in_connection(tool.templates[i]) && ImGui::Selectable((switch_label(tool.templates[i]) + "##connectionsecond" + std::to_string(i)).c_str(), tool.connection_second == i))
|
||||
if (usable_in_connection(tool.templates[i]) && ImGui::Selectable((switch_template_label(tool.templates[i]) + "##connectionsecond" + std::to_string(i)).c_str(), tool.connection_second == i))
|
||||
tool.connection_second = i;
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
@@ -1652,6 +1652,18 @@ editor_mode::selftest_result editor_mode::selftest_switch(std::string const &com
|
||||
WriteLog("SELFTEST layhold: " + m_lay.status);
|
||||
return selftest_result::handled;
|
||||
}
|
||||
if (command == "switchkind")
|
||||
{
|
||||
// what the switch tool places, as chosen in its panel: single, slip, crossing or connection with the kind of it
|
||||
std::string word;
|
||||
int sub{0};
|
||||
words >> word >> sub;
|
||||
auto const kind{word == "slip" ? switch_kind::slip : word == "crossing" ? switch_kind::crossing : word == "connection" ? switch_kind::connection : switch_kind::single};
|
||||
m_switch.connection_kind = sub;
|
||||
choose_switch_kind(kind);
|
||||
WriteLog(format("SELFTEST switchkind %s: kind %d, connection %d, armed %d", word.c_str(), static_cast<int>(current_switch_kind()), m_switch.connection, m_switch.armed));
|
||||
return selftest_result::handled;
|
||||
}
|
||||
if (command == "connection" || command == "connectionplan" || command == "connectionhold")
|
||||
{
|
||||
std::string kindword;
|
||||
|
||||
@@ -289,7 +289,7 @@ bool editor_mode::render_switch_drive_choice()
|
||||
scan_switch_drives();
|
||||
if (tool.drives.empty())
|
||||
{
|
||||
ImGui::TextDisabled("%s", STR_C("No switch drives: no template is described with category: switch drive"));
|
||||
ImGui::TextDisabled("%s", STR_C("No switch drive models in the scenery data"));
|
||||
return false;
|
||||
}
|
||||
auto const label = [&](int const Index) { return Index < 0 ? std::string{STR_C("no drive")} : tool.drives[Index].name; };
|
||||
|
||||
201
application/editormodeswitchtool.cpp
Normal file
201
application/editormodeswitchtool.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
// the panel of the switch tool: what it places, the templates and the options of that only, and how to place it
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "application/editormode.h"
|
||||
#include "application/editormodeshared.h"
|
||||
#include "editor/editorConnections.hpp"
|
||||
#include "utilities/translation.h"
|
||||
#include "world/Track.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// the next radio button on the same line as the last item, if it fits there
|
||||
void same_line_if_fits(char const *Label)
|
||||
{
|
||||
auto const &style{ImGui::GetStyle()};
|
||||
auto const width{ImGui::GetFrameHeight() + style.ItemInnerSpacing.x + ImGui::CalcTextSize(Label, nullptr, true).x};
|
||||
auto const end{ImGui::GetItemRectMax().x + style.ItemSpacing.x + width};
|
||||
if (end <= ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x)
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
// whether the template is one of those the kind places
|
||||
bool of_kind(editor_track::switch_template const &Template, int const Kind)
|
||||
{
|
||||
switch (Kind)
|
||||
{
|
||||
case 1: return Template.double_slip && Template.type != "Skrz";
|
||||
case 2: return Template.double_slip && Template.type == "Skrz";
|
||||
case 3: return false == Template.double_slip && false == Template.twofold();
|
||||
default: return false == Template.double_slip;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string editor_mode::switch_template_label(editor_track::switch_template const &Template)
|
||||
{
|
||||
if (Template.source != nullptr)
|
||||
return STR("Scenery: ") + Template.title() + " (" + std::to_string(Template.count) + "x)";
|
||||
return (Template.own ? STR("Own: ") : std::string{"PLK: "}) + Template.title();
|
||||
}
|
||||
|
||||
editor_mode::switch_kind editor_mode::current_switch_kind() const
|
||||
{
|
||||
auto const &tool{m_switch};
|
||||
if (tool.connection >= 0)
|
||||
return switch_kind::connection;
|
||||
if (tool.armed >= 0 && tool.armed < static_cast<int>(tool.templates.size()) && tool.templates[tool.armed].double_slip)
|
||||
return tool.templates[tool.armed].type == "Skrz" ? switch_kind::crossing : switch_kind::slip;
|
||||
return switch_kind::single;
|
||||
}
|
||||
|
||||
bool editor_mode::switch_kind_offered(switch_kind const Kind) const
|
||||
{
|
||||
auto const &templates{m_switch.templates};
|
||||
return std::any_of(templates.begin(), templates.end(), [&](editor_track::switch_template const &Template) { return of_kind(Template, static_cast<int>(Kind)); });
|
||||
}
|
||||
|
||||
void editor_mode::choose_switch_kind(switch_kind const Kind)
|
||||
{
|
||||
auto &tool{m_switch};
|
||||
tool.connection = (Kind == switch_kind::connection ? std::clamp(tool.connection_kind, 0, connection::kind_count - 1) : -1);
|
||||
// the template chosen stays if it's of the kind, else the first one of the kind
|
||||
auto const fits = [&](int const Index) { return Index >= 0 && Index < static_cast<int>(tool.templates.size()) && of_kind(tool.templates[Index], static_cast<int>(Kind)); };
|
||||
if (fits(tool.armed))
|
||||
return;
|
||||
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
|
||||
if (fits(i))
|
||||
{
|
||||
tool.last = i;
|
||||
tool.armed = i;
|
||||
tool.status.clear();
|
||||
return;
|
||||
}
|
||||
tool.armed = -1;
|
||||
}
|
||||
|
||||
void editor_mode::render_switch_ui()
|
||||
{
|
||||
auto &tool{m_switch};
|
||||
auto const kind{current_switch_kind()};
|
||||
|
||||
// what to place, each with the templates and the options of its own only
|
||||
struct choice
|
||||
{
|
||||
switch_kind kind;
|
||||
char const *label;
|
||||
char const *tooltip;
|
||||
};
|
||||
choice const choices[] = {{switch_kind::single, "Switch", "A switch put into a straight or a curve"},
|
||||
{switch_kind::slip, "Slip switch", "A single or double slip switch at a crossing of two straights"},
|
||||
{switch_kind::crossing, "Crossing", "A crossing of two straights"},
|
||||
{switch_kind::connection, "Connection", "Switches laid whole between two tracks: a crossover, a double or a scissors crossover, a switch ladder"}};
|
||||
ImGui::TextDisabled("%s", STR_C("What to place"));
|
||||
auto first{true};
|
||||
for (auto const &entry : choices)
|
||||
{
|
||||
if (entry.kind != switch_kind::single && false == switch_kind_offered(entry.kind))
|
||||
continue;
|
||||
if (false == first)
|
||||
same_line_if_fits(STR_C(entry.label));
|
||||
first = false;
|
||||
if (ImGui::RadioButton(STR_C(entry.label), kind == entry.kind) && kind != entry.kind)
|
||||
choose_switch_kind(entry.kind);
|
||||
item_tooltip(entry.tooltip);
|
||||
}
|
||||
if (kind == switch_kind::connection)
|
||||
{
|
||||
for (int i = 0; i < connection::kind_count; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
same_line_if_fits(connection_name(i));
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::RadioButton(connection_name(i), tool.connection == i))
|
||||
{
|
||||
tool.connection = i;
|
||||
tool.connection_kind = i;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
// how to place it with the mouse
|
||||
char const *how{nullptr};
|
||||
switch (kind)
|
||||
{
|
||||
case switch_kind::slip: how = "Click at a crossing of two straights; a single slip on the side of the joined tracks"; break;
|
||||
case switch_kind::crossing: how = "Click at a crossing of two straights"; break;
|
||||
case switch_kind::connection:
|
||||
how = tool.connection == static_cast<int>(connection::kind::double_crossover) ? "Press on a straight and drag onto the other track. While dragging: Shift+wheel another kind, Ctrl+wheel R of the aligning curve, Alt+wheel where they meet" :
|
||||
"Press on a straight and drag onto the other track. While dragging: Shift+wheel another kind, Ctrl+wheel R of the aligning curve";
|
||||
break;
|
||||
default: how = "Click a straight or a curve, then press on it and drag: along for the direction, sideways for the side"; break;
|
||||
}
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.3f, 1.0f));
|
||||
ImGui::TextWrapped("%s", STR_C(how));
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// the templates of the kind, by their names; the lengths and the rest of the label in the tooltip
|
||||
ImGui::TextDisabled("%s", STR_C(kind == switch_kind::connection ? "The first switch" : "Template"));
|
||||
auto const listed{static_cast<int>(std::count_if(tool.templates.begin(), tool.templates.end(), [&](editor_track::switch_template const &Template) { return of_kind(Template, static_cast<int>(kind)); }))};
|
||||
ImGui::BeginChild("##switchtemplates", ImVec2(0.0f, std::min(240.0f, (listed + 0.5f) * ImGui::GetTextLineHeightWithSpacing() + 8.0f)), true);
|
||||
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
|
||||
{
|
||||
auto const &entry{tool.templates[i]};
|
||||
if (false == of_kind(entry, static_cast<int>(kind)))
|
||||
continue;
|
||||
if (ImGui::Selectable((switch_template_label(entry) + "##switch" + std::to_string(i)).c_str(), tool.armed == i))
|
||||
{
|
||||
tool.last = i;
|
||||
tool.armed = i;
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", entry.label.c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
if (kind == switch_kind::connection)
|
||||
render_connection_options();
|
||||
|
||||
// the name and the drive of the next switch; a crossing has no moving parts
|
||||
if (kind != switch_kind::crossing)
|
||||
{
|
||||
ImGui::PushItemWidth(120.0f);
|
||||
ImGui::InputTextWithHint(STR_C("Name##switchname"), switch_name_for_new().c_str(), tool.name, sizeof(tool.name));
|
||||
ImGui::PopItemWidth();
|
||||
item_tooltip("Name of the next switch, its drive events are named after it; empty: the next free z1, z2...; a number at the end goes up with each switch");
|
||||
if (render_switch_drive_choice())
|
||||
if (auto *track{selected_track()}; track != nullptr && track->eType == tt_Switch && false == track->m_editorremoved)
|
||||
apply_switch_drive_choice(*track);
|
||||
}
|
||||
if (false == tool.status.empty())
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.2f, 1.0f), "%s", tool.status.c_str());
|
||||
|
||||
if (ImGui::SmallButton(STR_C("Collect from the scenery")))
|
||||
{
|
||||
tool.templates = editor_track::standard_switch_templates();
|
||||
auto const found{editor_track::find_switch_templates()};
|
||||
tool.templates.insert(tool.templates.end(), found.begin(), found.end());
|
||||
tool.collected = true;
|
||||
arm_switch();
|
||||
}
|
||||
item_tooltip("Looks for the switches of the scenery, to use their shapes as the templates");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(STR_C("Template editor...")))
|
||||
m_switch_designer.open = true;
|
||||
item_tooltip("Own switch templates: diverging tracks of straights, arcs and transition curves, double switches");
|
||||
}
|
||||
@@ -58,13 +58,6 @@ using geometry::level;
|
||||
using geometry::reversed;
|
||||
using geometry::straight_piece;
|
||||
|
||||
std::string template_label(editor_track::switch_template const &Template)
|
||||
{
|
||||
if (Template.source != nullptr)
|
||||
return STR("Scenery: ") + Template.label + " (" + std::to_string(Template.count) + "x)";
|
||||
return (Template.own ? STR("Own: ") : std::string{"PLK: "}) + Template.label;
|
||||
}
|
||||
|
||||
double constexpr kParallelFindAngle{15.0}; // deg, neighbours counted as beside the straight
|
||||
double constexpr kParallelSnapAngle{5.0}; // deg, close enough to snap parallel
|
||||
double constexpr kParallelSnapWindow{0.45}; // m from 4.00 / 4.50 / 4.75 m
|
||||
@@ -556,9 +549,9 @@ void editor_mode::restore_track_snapshot(EditorSnapshot const &Snapshot, std::ve
|
||||
profile_restore();
|
||||
}
|
||||
|
||||
std::array<editor_mode::track_mode, 12> const &editor_mode::track_modes()
|
||||
std::array<editor_mode::track_mode, 11> const &editor_mode::track_modes()
|
||||
{
|
||||
static std::array<track_mode, 12> const modes{{
|
||||
static std::array<track_mode, 11> const modes{{
|
||||
{"Select", "Esc", track_tab::path, "LMB on a path selects it: its points, control vectors and parameters"},
|
||||
{"Lay track", "L", track_tab::lay, "Lays new track through the clicked points, on the ground or from a free end of a track"},
|
||||
{"Plan", "G", track_tab::straights, "The plan of the line laid: LMB on a straight drags its ends or the middle, on a curve its vertex and radius. C works too"},
|
||||
@@ -570,7 +563,7 @@ std::array<editor_mode::track_mode, 12> const &editor_mode::track_modes()
|
||||
{"Profile", "P", track_tab::profile, "Vertical profile (grade line) along the line"},
|
||||
{"Speed", "V", track_tab::speed, "Speed limits of the paths against the speed their geometry allows"},
|
||||
{"Joints", "J", track_tab::joints, "Ends of the paths which almost meet, steps, kinks, jumps of the cant and of the grade at the joints"},
|
||||
{"Infra", "I", track_tab::infra, "Infrastructure along the track, bound to follow its changes"},
|
||||
// the infrastructure tool (track_tab::infra) is left out: no button nor key
|
||||
}};
|
||||
return modes;
|
||||
}
|
||||
@@ -994,8 +987,16 @@ void editor_mode::render_turnout_ui()
|
||||
}
|
||||
else
|
||||
ImGui::Text(STR_C("Length %.3f m, diverging %s, end offset %.3f m"), length, offset > 0.0 ? STR_C("right") : STR_C("left"), std::abs(offset));
|
||||
render_track_name(*track);
|
||||
render_switch_record(*track);
|
||||
if (auto const known{switch_store::of(*track)}; known.has_value())
|
||||
ImGui::TextUnformatted(switch_store::label(*known).c_str());
|
||||
// the name, the number, the rail and the kilometre of the switch, with what the scenery keeps of it
|
||||
if (ImGui::CollapsingHeader(STR_C("Details##switchdetails")))
|
||||
{
|
||||
render_track_name(*track);
|
||||
render_switch_record(*track);
|
||||
if (angle > 1e-6)
|
||||
ImGui::Text(STR_C("Angle 1:%.2f (%.4f deg), R in the entry %.0f m"), 1.0 / std::tan(angle), glm::degrees(angle), diverging.radius);
|
||||
}
|
||||
auto const drive_ui = [&](TTrack &Switch) {
|
||||
ImGui::PushID(&Switch);
|
||||
if (set.has_value())
|
||||
@@ -1064,8 +1065,6 @@ void editor_mode::render_turnout_ui()
|
||||
}
|
||||
if (false == m_switch.status.empty())
|
||||
ImGui::TextWrapped("%s", m_switch.status.c_str());
|
||||
if (angle > 1e-6)
|
||||
ImGui::Text(STR_C("Angle 1:%.2f (%.4f deg), R in the entry %.0f m"), 1.0 / std::tan(angle), glm::degrees(angle), diverging.radius);
|
||||
|
||||
auto &tool{m_switch};
|
||||
if (false == tool.collected)
|
||||
@@ -1086,13 +1085,13 @@ void editor_mode::render_turnout_ui()
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::PushItemWidth(-1.0f);
|
||||
if (ImGui::BeginCombo("##turnouttemplate", tool.templates[m_turnout_template].label.c_str()))
|
||||
if (ImGui::BeginCombo("##turnouttemplate", switch_template_label(tool.templates[m_turnout_template]).c_str()))
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
|
||||
{
|
||||
if (false == fits(tool.templates[i]))
|
||||
continue;
|
||||
auto const label{template_label(tool.templates[i])};
|
||||
auto const label{switch_template_label(tool.templates[i])};
|
||||
if (ImGui::Selectable(label.c_str(), i == m_turnout_template))
|
||||
m_turnout_template = i;
|
||||
}
|
||||
@@ -3709,7 +3708,13 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
|
||||
hints = {{"LMB", "start, on the ground or at a free end"}};
|
||||
else if (m_track_tab == track_tab::turnout && (selected_track() == nullptr || selected_track()->eType == tt_Switch))
|
||||
{
|
||||
hints = {{"LMB on a track", "the straight or the curve for the switch"}};
|
||||
switch (current_switch_kind())
|
||||
{
|
||||
case switch_kind::connection: hints = {{"Press on a straight", "and drag onto the other track"}, {"Shift+wheel", "another kind"}, {"Ctrl+wheel", "R of the aligning curve"}}; break;
|
||||
case switch_kind::slip:
|
||||
case switch_kind::crossing: hints = {{"LMB", "at a crossing of two straights"}}; break;
|
||||
default: hints = {{"LMB on a track", "the straight or the curve for the switch"}}; break;
|
||||
}
|
||||
if (selected != nullptr)
|
||||
{
|
||||
hints.push_back({"Gizmo", "move the selected switch"});
|
||||
@@ -5784,63 +5789,6 @@ void editor_mode::draw_build_overlay() const
|
||||
draw_free_ends(track, track->iCategoryFlag & 15, std::vector<TTrack const *>(m_track_drag.begin(), m_track_drag.end()));
|
||||
}
|
||||
|
||||
void editor_mode::render_switch_ui()
|
||||
{
|
||||
auto &tool{m_switch};
|
||||
auto const list = [&](bool const Slips) {
|
||||
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
|
||||
{
|
||||
auto const &entry{tool.templates[i]};
|
||||
if (entry.double_slip != Slips)
|
||||
continue;
|
||||
auto label{template_label(entry)};
|
||||
label += "##switch" + std::to_string(i);
|
||||
if (ImGui::Selectable(label.c_str(), tool.armed == i))
|
||||
{
|
||||
tool.last = i;
|
||||
tool.armed = tool.last;
|
||||
}
|
||||
}
|
||||
};
|
||||
ImGui::TextDisabled("%s", STR_C("Switch template"));
|
||||
ImGui::BeginChild("##switchtemplates", ImVec2(0.0f, 240.0f), true);
|
||||
list(false);
|
||||
ImGui::EndChild();
|
||||
if (std::any_of(tool.templates.begin(), tool.templates.end(), [](editor_track::switch_template const &Template) { return Template.double_slip; }))
|
||||
{
|
||||
if (tool.armed >= 0 && tool.armed < static_cast<int>(tool.templates.size()) && tool.templates[tool.armed].double_slip)
|
||||
ImGui::SetNextItemOpen(true);
|
||||
if (ImGui::CollapsingHeader(STR_C("Slip switches and crossings")))
|
||||
{
|
||||
ImGui::TextDisabled("%s", STR_C("Only at a crossing of two straights"));
|
||||
list(true);
|
||||
}
|
||||
}
|
||||
render_connection_ui();
|
||||
ImGui::PushItemWidth(120.0f);
|
||||
ImGui::InputTextWithHint(STR_C("Name##switchname"), switch_name_for_new().c_str(), tool.name, sizeof(tool.name));
|
||||
ImGui::PopItemWidth();
|
||||
item_tooltip("Name of the next switch, its drive events are named after it; empty: the next free z1, z2...; a number at the end goes up with each switch");
|
||||
if (render_switch_drive_choice())
|
||||
if (auto *track{selected_track()}; track != nullptr && track->eType == tt_Switch && false == track->m_editorremoved)
|
||||
apply_switch_drive_choice(*track);
|
||||
if (false == tool.status.empty())
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.2f, 1.0f), "%s", tool.status.c_str());
|
||||
if (ImGui::SmallButton(STR_C("Collect from the scenery")))
|
||||
{
|
||||
tool.templates = editor_track::standard_switch_templates();
|
||||
auto const found{editor_track::find_switch_templates()};
|
||||
tool.templates.insert(tool.templates.end(), found.begin(), found.end());
|
||||
tool.collected = true;
|
||||
arm_switch();
|
||||
}
|
||||
item_tooltip("Looks for the switches of the scenery, to use their shapes as the templates");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(STR_C("Template editor...")))
|
||||
m_switch_designer.open = true;
|
||||
item_tooltip("Own switch templates: diverging tracks of straights, arcs and transition curves, double switches");
|
||||
}
|
||||
|
||||
std::vector<glm::dvec3> editor_mode::detour_outline() const
|
||||
{
|
||||
auto const &state{m_straights};
|
||||
|
||||
@@ -127,7 +127,6 @@ void editor_mode::render_viewport_overlays()
|
||||
// a view squeezed between the docks has no room for them
|
||||
if (width < 8.0f * button_side() || height < 6.0f * button_side())
|
||||
return;
|
||||
render_viewport_tools();
|
||||
render_viewport_gizmo();
|
||||
render_view_axes();
|
||||
}
|
||||
@@ -166,30 +165,31 @@ void editor_mode::work_area_tools(std::vector<tool_button> &Buttons)
|
||||
auto const chosen{(m_track_tab == Tab || (Tab == track_tab::straights && track_plan_tab())) && false == (Tab == track_tab::lineside && vehicle)};
|
||||
Buttons.push_back({Picture, STR_C(found->label), found->key, STR_C(found->tooltip), chosen, [this, Tab, chosen]() { show_track_tab(chosen ? track_tab::path : Tab); }, Group});
|
||||
};
|
||||
// the tools of laying track first, the others behind More, shown while one of them is in use
|
||||
add(track_tab::path, icon::select, false);
|
||||
add(track_tab::lay, icon::lay_track, false);
|
||||
add(track_tab::straights, icon::curve, false);
|
||||
add(track_tab::turnout, icon::turnout, false);
|
||||
add(track_tab::parallel, icon::parallel, false);
|
||||
add(track_tab::turntable, icon::turntable, false);
|
||||
add(track_tab::signals, icon::signal, true);
|
||||
add(track_tab::lineside, icon::objects, 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;
|
||||
}});
|
||||
auto const advanced{m_track_tab == track_tab::profile || m_track_tab == track_tab::speed || m_track_tab == track_tab::joints || m_track_tab == track_tab::infra || m_gauge.open};
|
||||
auto const advanced{m_track_tab == track_tab::turntable || m_track_tab == track_tab::signals || m_track_tab == track_tab::lineside || m_track_tab == track_tab::profile || m_track_tab == track_tab::speed ||
|
||||
m_track_tab == track_tab::joints || m_gauge.open};
|
||||
auto const more{EditorSettings.track_tools_more()};
|
||||
Buttons.push_back({icon::more, more ? STR_C("Less") : STR_C("More"), nullptr, STR_C("The checks of a line laid: profile, speed, joints, infrastructure, structure gauge"), more, [more]() {
|
||||
Buttons.push_back({icon::more, more ? STR_C("Less") : STR_C("More"), nullptr, STR_C("Turntables, signals, objects along the track, trainsets and the checks of a line laid: profile, speed, joints, structure gauge"), more, [more]() {
|
||||
EditorSettings.track_tools_more(false == more);
|
||||
EditorSettings.save();
|
||||
}, true});
|
||||
if (false == more && false == advanced)
|
||||
break;
|
||||
add(track_tab::turntable, icon::turntable, true);
|
||||
add(track_tab::signals, icon::signal, false);
|
||||
add(track_tab::lineside, icon::objects, 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;
|
||||
}});
|
||||
add(track_tab::profile, icon::profile, true);
|
||||
add(track_tab::speed, icon::speed, false);
|
||||
add(track_tab::joints, icon::joints, false);
|
||||
add(track_tab::infra, icon::infra, false);
|
||||
Buttons.push_back({icon::gauge, STR_C("Structure gauge"), nullptr, STR_C("Checks which models enter the structure gauge of the tracks and the clearance over the roads (skrajnia budowli)"), m_gauge.open,
|
||||
[this]() { m_gauge.open = !m_gauge.open; }});
|
||||
break;
|
||||
@@ -238,48 +238,6 @@ void editor_mode::work_area_tools(std::vector<tool_button> &Buttons)
|
||||
}
|
||||
}
|
||||
|
||||
void editor_mode::render_viewport_tools()
|
||||
{
|
||||
// the tools of the field of work which have icons, as in the toolbar
|
||||
std::vector<tool_button> buttons;
|
||||
buttons.reserve(24);
|
||||
work_area_tools(buttons);
|
||||
std::erase_if(buttons, [](tool_button const &Button) { return Button.picture == icon::count; });
|
||||
if (buttons.empty())
|
||||
return;
|
||||
|
||||
// down the left edge, in more columns if the view is too low for them; the bottom is left to the hints of the tools
|
||||
auto const side{button_side()};
|
||||
auto const gap{2.0f * scale()};
|
||||
auto const groupgap{8.0f * scale()};
|
||||
auto const margin{8.0f * scale()};
|
||||
ImVec2 const origin{editor_ui::view_min().x + margin, editor_ui::view_min().y + margin};
|
||||
auto const bottom{std::max(origin.y + 4.0f * side, editor_ui::view_max().y - 130.0f * scale())};
|
||||
std::vector<ImVec2> places;
|
||||
places.reserve(buttons.size());
|
||||
ImVec2 place{origin};
|
||||
for (auto const &button : buttons)
|
||||
{
|
||||
if (button.group && place.y > origin.y)
|
||||
place.y += groupgap - gap;
|
||||
if (place.y > origin.y && place.y + side > bottom)
|
||||
place = ImVec2(place.x + side + gap, origin.y);
|
||||
places.push_back(place);
|
||||
place.y += side + gap;
|
||||
}
|
||||
ImVec2 extent{0.0f, 0.0f};
|
||||
for (auto const &at : places)
|
||||
extent = ImVec2(std::max(extent.x, at.x + side - origin.x), std::max(extent.y, at.y + side - origin.y));
|
||||
auto const pad{3.0f * scale()};
|
||||
if (begin_overlay("##viewporttools", ImVec2(origin.x - pad, origin.y - pad), ImVec2(extent.x + 2.0f * pad, extent.y + 2.0f * pad)))
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(origin.x - pad, origin.y - pad), ImVec2(origin.x + extent.x + pad, origin.y + extent.y + pad), IM_COL32(16, 18, 21, 140), 6.0f * scale());
|
||||
for (std::size_t index = 0; index < buttons.size(); ++index)
|
||||
icon_button(buttons[index], places[index], static_cast<int>(index));
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void editor_mode::render_toolbar_caption(char const *Text)
|
||||
{
|
||||
// as wide in both rows, so the buttons of the rows start one under the other. the rows are drawn with no spacing of items
|
||||
|
||||
@@ -762,7 +762,7 @@ void editor_ui::render_tool_options()
|
||||
}
|
||||
else
|
||||
render_mode_options(current);
|
||||
if (ImGui::CollapsingHeader(STR_C("Gizmo"), ImGuiTreeNodeFlags_DefaultOpen))
|
||||
if (ImGui::CollapsingHeader(STR_C("Gizmo")))
|
||||
{
|
||||
if (m_gizmooptions)
|
||||
m_gizmooptions();
|
||||
|
||||
@@ -58,6 +58,8 @@ class editor_ui : public ui_layer
|
||||
void set_track(bool const Track)
|
||||
{
|
||||
m_track = Track;
|
||||
// the models of the node bank aren't laid with the track tools
|
||||
m_nodebankpanel.suppressed = Track;
|
||||
}
|
||||
// draws menus of the editor mode in the menu bar, past the ones of the user interface
|
||||
void set_menu_options(std::function<void()> Renderer)
|
||||
|
||||
@@ -915,7 +915,7 @@ void nodebank_panel::render_set_cards(int const Setid)
|
||||
|
||||
void nodebank_panel::render()
|
||||
{
|
||||
if (false == is_open)
|
||||
if (false == is_open || suppressed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ class nodebank_panel : public ui_panel
|
||||
TRACK
|
||||
};
|
||||
edit_mode mode = MODIFY;
|
||||
bool suppressed{false}; // the field of work has no use for it: not drawn, though it stays open for the others
|
||||
|
||||
nodebank_panel(std::string const &Name, bool const Isopen);
|
||||
void nodebank_reload();
|
||||
|
||||
Reference in New Issue
Block a user