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

refactor(editor): the helpers repeated over the editor files are shared

The name of a node or "(no name)" was written out in twenty places and
four local functions, a lower-case copy of a text in four files, the
test for a number, the file name of a template made comparable and the
heading of a direction in three and two, the green main button of a
panel in five and the key=value parameters of a self-test command in
five. Each of them is one helper now: name_of, tall_button_height and
main_button with the panel helpers, numeric with format, normalized
with the include helpers, heading_of with the plan geometry and
selftest_values with the tools; lower gives way to ToLower.
This commit is contained in:
maj00r
2026-10-21 10:28:42 +02:00
parent 747c6817f0
commit 87bacede74
27 changed files with 137 additions and 218 deletions

View File

@@ -527,7 +527,7 @@ editor_mode::editor_mode() {
std::string reason;
if (Node->from_template() || false == scene::Layers.editable(Node, &reason))
{
ui()->set_status(format(STR_C("\"%s\" can't be edited: %s"), (Node->name().empty() ? std::string{STR_C("(no name)")} : Node->name()).c_str(), reason.c_str()), true);
ui()->set_status(format(STR_C("\"%s\" can't be edited: %s"), name_of(*Node).c_str(), reason.c_str()), true);
return;
}
m_track_window_open = false;
@@ -6594,7 +6594,7 @@ void editor_mode::on_mouse_button(int const Button, int const Action, int const
}
else if (node->from_template() || false == scene::Layers.editable(node, &reason))
{
ui()->set_status(format(STR_C("\"%s\" can't be edited: %s"), (node->name().empty() ? std::string{STR_C("(no name)")} : node->name()).c_str(), reason.c_str()), true);
ui()->set_status(format(STR_C("\"%s\" can't be edited: %s"), name_of(*node).c_str(), reason.c_str()), true);
node = nullptr;
}
}

View File

@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "application/editormode.h"
#include "application/editormodeshared.h"
#include "application/editoruilayer.h"
#include "editor/editorAlignmentStore.hpp"
#include "editor/editorConnections.hpp"
@@ -328,7 +329,7 @@ void editor_mode::digitize_crossovers(digitize_report &Report, bool const Write)
if (spacing < kSpacingLeast || spacing > kSpacingMost)
continue;
++Report.crossovers;
auto const name = [](TTrack const *Track) { return Track->name().empty() ? STR("(no name)") : Track->name(); };
auto const name = [](TTrack const *Track) { return name_of(*Track); };
auto const note = [&](std::string const &Why) { Report.notes.push_back(name(first) + " + " + name(second) + ": " + Why); };
if (false == switch_store::connections(*first).empty())
{

View File

@@ -134,13 +134,6 @@ struct include_place
std::string category; // of the description of the template
};
bool numeric(std::string const &Text, double &Value)
{
char *end{nullptr};
Value = std::strtod(Text.c_str(), &end);
return false == Text.empty() && *end == '\0';
}
bool include_place_of(scene::instance_handle const Instance, include_place &Place)
{
if (false == scene::Layers.tracked(Instance))

View File

@@ -137,11 +137,9 @@ ImU32 kind_colour(kind const Kind)
}
}
std::string name_of(TTrack const *Track)
std::string label_of(TTrack const *Track)
{
if (Track == nullptr)
return "-";
return Track->name().empty() ? STR("(no name)") : Track->name();
return Track == nullptr ? std::string{"-"} : ::name_of(*Track);
}
} // namespace
@@ -538,7 +536,7 @@ void joints_tool::render_options()
{
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(family_colour(2, 0.55f)));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(family_colour(2, 0.75f)));
if (ImGui::Button(STR_C("Check"), ImVec2(-1.0f, std::round(ImGui::GetFrameHeight() * 1.3f))))
if (ImGui::Button(STR_C("Check"), ImVec2(-1.0f, tall_button_height())))
joints_start();
ImGui::PopStyleColor(2);
item_tooltip("Finds the gaps between the ends of the tracks, the steps, the kinks and the jumps of the cant and of the grade");
@@ -632,9 +630,9 @@ void joints_tool::render_options()
ImGui::NextColumn();
ImGui::TextUnformatted(issue.fixed ? STR_C("fixed") : value_text(issue).c_str());
ImGui::NextColumn();
ImGui::TextUnformatted(name_of(issue.track).c_str());
ImGui::TextUnformatted(label_of(issue.track).c_str());
ImGui::NextColumn();
ImGui::TextUnformatted(name_of(issue.other).c_str());
ImGui::TextUnformatted(label_of(issue.other).c_str());
ImGui::NextColumn();
}
ImGui::Columns(1);
@@ -642,7 +640,7 @@ void joints_tool::render_options()
if (chosen)
{
auto &issue{state.issues[state.selected]};
ImGui::Text("%s %s: %s / %s", STR_C(kind_name(issue.type)), value_text(issue).c_str(), name_of(issue.track).c_str(), name_of(issue.other).c_str());
ImGui::Text("%s %s: %s / %s", STR_C(kind_name(issue.type)), value_text(issue).c_str(), label_of(issue.track).c_str(), label_of(issue.other).c_str());
if (false == issue.fixed && issue.type != kind::open && issue.type != kind::model && ImGui::Button(STR_C("Fix this one")))
joints_fix({state.selected});
}

View File

@@ -1159,7 +1159,7 @@ glm::dvec3 editor_mode::lay_angle_snap(glm::dvec3 const &Mouse, std::string &Lab
ratio = n;
}
}
auto const name{axis->name().empty() ? std::string{STR_C("(no name)")} : axis->name()};
auto const name{name_of(*axis)};
auto const degrees{glm::degrees(std::abs(std::remainder(angle, glm::pi<double>())))};
Label = ratio > 0.0 ? format(STR_C("1:%g to %s"), ratio, name.c_str()) : degrees < 0.1 ? format(STR_C("parallel to %s"), name.c_str()) : format(STR_C("%.0f deg to %s"), degrees, name.c_str());
}

View File

@@ -35,6 +35,7 @@ namespace
using geometry::bezier;
using geometry::plan_distance;
using geometry::heading_of;
using geometry::plan_of;
double const kSampleStep{2.0}; // m between the samples of the line
@@ -47,19 +48,6 @@ int const kRailCategory{1};
char const *const kHektoStyles[] = {"nowy", "stary", "zgnity"};
char const *const kFoulingModels[] = {"ip/pkp/w17.inc", "ip/pkp/w17_new.inc", "ip/pkp/w17_old.inc", "ip/pkp/w17_dwuteo.inc"};
std::string normalized(std::string Text)
{
std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast<char>(Character == '\\' ? '/' : std::tolower(Character)); });
return Text;
}
bool numeric(std::string const &Text, double &Value)
{
char *end{nullptr};
Value = std::strtod(Text.c_str(), &end);
return false == Text.empty() && *end == '\0';
}
std::string number(double const Value, int const Decimals)
{
auto text{format("%.*f", Decimals, Value)};
@@ -68,12 +56,6 @@ std::string number(double const Value, int const Decimals)
return text;
}
double heading_of(glm::dvec2 const &Direction)
{
auto const degrees{glm::degrees(std::atan2(Direction.x, Direction.y))};
return degrees < 0.0 ? degrees + 360.0 : degrees;
}
// index of the sample at or before the chainage
std::size_t sample_index(std::vector<editor_track::route_sample> const &Samples, double const Chainage)
{
@@ -238,7 +220,7 @@ std::vector<editor_mode::standing_template> editor_mode::standing_templates(std:
auto const &included{scene::Layers.instance(handle)};
if (included.removed || included.dead || included.file == nullptr)
continue;
auto const file{normalized(*included.file)};
auto const file{editor_includes::normalized(*included.file)};
if (false == Accept(file) || included.context.rotation != glm::vec3{0.f} || included.context.scale != glm::vec3{1.f})
continue;
std::string target;
@@ -649,11 +631,8 @@ void hekto_tool::render_options()
tool.posts.empty() ? "-" : km_text(std::max(tool.posts.front().hectometres, tool.posts.back().hectometres)).c_str(), tool.found.size(), static_cast<int>(wrong));
if (missing > 0)
{
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(format(STR_P("Place %d post", "Place %d posts", static_cast<long long>(missing)), static_cast<int>(missing)).c_str(), ImVec2(-1.0f, std::round(ImGui::GetFrameHeight() * 1.3f))))
if (main_button(format(STR_P("Place %d post", "Place %d posts", static_cast<long long>(missing)), static_cast<int>(missing)).c_str(), ImVec2(-1.0f, tall_button_height())))
hekto_place();
ImGui::PopStyleColor(2);
}
if (duplicates > 0)
{
@@ -938,14 +917,11 @@ void fouling_tool::render_options()
ImGui::TextWrapped("%s", tool.status.c_str());
auto const missing{std::count_if(tool.points.begin(), tool.points.end(), [](fouling_point const &Point) { return Point.standing < 0; })};
auto const wrong{std::count_if(tool.found.begin(), tool.found.end(), [](fouling_found const &Found) { return Found.wrong; })};
auto const tall{std::round(ImGui::GetFrameHeight() * 1.3f)};
auto const tall{tall_button_height()};
if (missing > 0)
{
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(format(STR_P("Place %d marker", "Place %d markers", static_cast<long long>(missing)), static_cast<int>(missing)).c_str(), ImVec2(-1.0f, tall)))
if (main_button(format(STR_P("Place %d marker", "Place %d markers", static_cast<long long>(missing)), static_cast<int>(missing)).c_str(), ImVec2(-1.0f, tall)))
place();
ImGui::PopStyleColor(2);
}
if (wrong > 0)
{
@@ -1069,14 +1045,7 @@ editor_selftest_result hekto_tool::selftest(std::string const &Command, std::ist
{
if (Command == "hekto")
{
std::map<std::string, std::string> values;
std::string pair;
while (Words >> pair)
{
auto const equals{pair.find('=')};
if (equals != std::string::npos)
values[pair.substr(0, equals)] = pair.substr(equals + 1);
}
auto values{selftest_values(Words)};
auto *track{simulation::Paths.find(values["track"])};
if (track == nullptr)
{
@@ -1135,14 +1104,7 @@ editor_selftest_result fouling_tool::selftest(std::string const &Command, std::i
{
if (Command == "fouling")
{
std::map<std::string, std::string> values;
std::string pair;
while (Words >> pair)
{
auto const equals{pair.find('=')};
if (equals != std::string::npos)
values[pair.substr(0, equals)] = pair.substr(equals + 1);
}
auto values{selftest_values(Words)};
auto &tool{m_fouling};
if (values["show"] == "1")
{

View File

@@ -122,11 +122,6 @@ bool side_picker(int &Side, double const Spacing)
return changed;
}
std::string name_of(TTrack const &Track)
{
return Track.name().empty() ? STR("(no name)") : Track.name();
}
segment_data reversed(segment_data const &Path)
{
segment_data result{Path};
@@ -416,11 +411,8 @@ void parallel_tool::render_options()
}
else if (false == tool.pieces.empty())
{
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(format(STR_P("Lay %zu track, %.1f m", "Lay %zu tracks, %.1f m", static_cast<long long>(tool.pieces.size())), tool.pieces.size(), tool.length).c_str(), ImVec2(-1.0f, std::round(ImGui::GetFrameHeight() * 1.3f))))
if (main_button(format(STR_P("Lay %zu track, %.1f m", "Lay %zu tracks, %.1f m", static_cast<long long>(tool.pieces.size())), tool.pieces.size(), tool.length).c_str(), ImVec2(-1.0f, tall_button_height())))
parallel_build();
ImGui::PopStyleColor(2);
item_tooltip("A new track beside the one selected, as in the preview; the selected stays as it is. Ctrl+Z takes it back");
}
if (false == tool.status.empty())

View File

@@ -1493,7 +1493,7 @@ void editor_mode::profile_apply(bool const Terrain)
state.status = format(STR_P("Grade line applied: %zu path changed, %zu added, largest departure %.3f m", "Grade line applied: %zu paths changed, %zu added, largest departure %.3f m", changed), changed, added, state.departure);
if (false == scene::Layers.empty())
state.status += STR_C("\nThe paths and the design (//$p lines) go to the scenery files on save");
auto const name = [](TTrack const *Track) { return Track->name().empty() ? std::string{STR_C("(no name)")} : Track->name(); };
auto const name = [](TTrack const *Track) { return name_of(*Track); };
auto const closed{std::count_if(gaps.begin(), gaps.end(), [](auto const &Gap) { return Gap.closed; })};
if (closed > 0)
state.status += STR_C("\nPaths off the route brought to its height:");

View File

@@ -381,7 +381,7 @@ int editor_mode::digitize_parts(editor_track::chain const &Chain, std::string *R
if (count == 1)
{
++Skipped;
reason = (Chain.tracks.front()->name().empty() ? std::string{STR_C("(no name)")} : Chain.tracks.front()->name()) + ": " + reason;
reason = name_of(*Chain.tracks.front()) + ": " + reason;
WriteLog("Editor: digitizing - " + reason, logtype::generic);
if (Reason != nullptr && Reason->empty())
*Reason = reason;
@@ -805,7 +805,7 @@ bool editor_mode::route_relay_through_switches(editor_track::chain const &Chain,
{
auto &route{m_route};
auto const &result{route.result};
auto const name = [](TTrack const *Track) { return Track->name().empty() ? std::string{STR_C("(no name)")} : Track->name(); };
auto const name = [](TTrack const *Track) { return name_of(*Track); };
auto const plan = [](glm::dvec3 const &Vector) { return glm::normalize(plan_of(Vector)); };
struct placement
{
@@ -1657,14 +1657,11 @@ void editor_mode::render_route_result()
if (result.valid)
{
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("Apply to the scenery"), ImVec2(-1.0f, 28.0f)))
if (main_button(STR_C("Apply to the scenery"), ImVec2(-1.0f, 28.0f)))
{
route_apply();
first_step_done(first_step::plan_apply);
}
ImGui::PopStyleColor(2);
}
else
{

View File

@@ -11,6 +11,7 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "application/editormode.h"
#include "application/editormodeshared.h"
#include "application/editoruilayer.h"
#include "application/editorprojection.h"
#include "editor/editorFormat.hpp"
@@ -271,7 +272,7 @@ void editor_mode::group_selection()
{
if (reason.empty())
reason = STR("it's a part of a scenery template");
ui()->set_status(format(STR_C("\"%s\" can't be grouped: %s"), node->name().empty() ? STR_C("(no name)") : node->name().c_str(), reason.c_str()), true);
ui()->set_status(format(STR_C("\"%s\" can't be grouped: %s"), name_of(*node).c_str(), reason.c_str()), true);
return;
}
}
@@ -334,7 +335,7 @@ void editor_mode::render_context_menu()
if (count > 1)
ImGui::TextDisabled(STR_C("%zu models selected"), count);
else if (selected)
ImGui::TextDisabled("%s", m_node->name().empty() ? STR_C("(no name)") : m_node->name().c_str());
ImGui::TextDisabled("%s", name_of(*m_node).c_str());
else
ImGui::TextDisabled("%s", STR_C("Nothing selected"));
ImGui::Separator();

View File

@@ -52,14 +52,7 @@ editor_mode::selftest_result editor_mode::selftest_signal(std::string const &com
{
if (command == "signal" || command == "signalmove" || command == "signaldelete" || command == "signalrename" || command == "signalprobe")
{
std::map<std::string, std::string> values;
std::string pair;
while (words >> pair)
{
auto const equals{pair.find('=')};
if (equals != std::string::npos)
values[pair.substr(0, equals)] = pair.substr(equals + 1);
}
auto values{selftest_values(words)};
show_track_tab(track_tab::signals);
signal_scan();
signal_refresh(true);

View File

@@ -52,14 +52,7 @@ editor_mode::selftest_result editor_mode::selftest_sweep(std::string const &comm
{
if (command == "sweep")
{
std::map<std::string, std::string> values;
std::string pair;
while (words >> pair)
{
auto const equals{pair.find('=')};
if (equals != std::string::npos)
values[pair.substr(0, equals)] = pair.substr(equals + 1);
}
auto values{selftest_values(words)};
auto &tool{m_sweep};
auto const get = [&](char const *Key, std::string const &Default) { auto const found{values.find(Key)}; return found != values.end() ? found->second : Default; };
tool.edited = nullptr;
@@ -158,14 +151,7 @@ editor_mode::selftest_result editor_mode::selftest_sweep(std::string const &comm
}
if (command == "bend" || command == "bendset")
{
std::map<std::string, std::string> values;
std::string pair;
while (words >> pair)
{
auto const equals{pair.find('=')};
if (equals != std::string::npos)
values[pair.substr(0, equals)] = pair.substr(equals + 1);
}
auto values{selftest_values(words)};
auto const vector = [](std::string Text) {
std::replace(Text.begin(), Text.end(), ',', ' ');
std::istringstream numbers(Text);

View File

@@ -24,6 +24,25 @@ http://mozilla.org/MPL/2.0/.
float constexpr kDragPixels{5.0f};
template <typename Node_> inline std::string name_of(Node_ const &Node)
{
return Node.name().empty() ? std::string{STR_C("(no name)")} : Node.name();
}
inline float tall_button_height()
{
return std::round(ImGui::GetFrameHeight() * 1.3f);
}
inline bool main_button(char const *Label, ImVec2 const &Size)
{
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));
auto const pressed{ImGui::Button(Label, Size)};
ImGui::PopStyleColor(2);
return pressed;
}
inline void item_tooltip(char const *Text)
{
if (ImGui::IsItemHovered())

View File

@@ -36,6 +36,7 @@ namespace
{
using geometry::bezier;
using geometry::heading_of;
using geometry::plan_of;
double const kRailTop{0.18};
@@ -58,12 +59,6 @@ int kind_index(std::string const &Kind)
return -1;
}
std::string lower(std::string Text)
{
std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast<char>(std::tolower(Character)); });
return Text;
}
double nearest_on(TTrack const &Track, int const Path, glm::dvec3 const &Point)
{
bezier const curve{Track.m_paths[Path]};
@@ -93,12 +88,6 @@ glm::dvec2 right_of(glm::dvec2 const &Direction)
return {-Direction.y, Direction.x};
}
double heading_of(glm::dvec2 const &Direction)
{
auto const degrees{glm::degrees(std::atan2(Direction.x, Direction.y))};
return degrees < 0.0 ? degrees + 360.0 : degrees;
}
int lean_wanted(int const Side)
{
return Side > 0 ? 1 : 2;
@@ -159,7 +148,7 @@ std::vector<std::string> const &plate_textures()
std::string stem;
try
{
stem = lower(file->path().stem().string());
stem = ToLower(file->path().stem().string());
}
catch (std::exception const &)
{
@@ -177,7 +166,7 @@ std::string plate_for(std::string const &Plate, std::string const &Name)
if (false == Plate.empty())
return Plate;
auto const &plates{plate_textures()};
auto const name{lower(Name)};
auto const name{ToLower(Name)};
return std::binary_search(plates.begin(), plates.end(), name) ? name : std::string{"0_nothing"};
}
@@ -304,7 +293,7 @@ editor_mode::signal_template const *editor_mode::signal_armed() const
editor_mode::signal_template const *editor_mode::signal_template_of(std::string const &File) const
{
auto const file{lower(File)};
auto const file{ToLower(File)};
for (auto const &item : m_signal.templates)
if (item.file == file)
return &item;
@@ -350,7 +339,7 @@ std::string editor_mode::signal_intent_label(signal_template const &Template, si
if (Template.read.empty())
return format(STR_C("Click: %s %s (%s)%s, the train doesn't read it"), STR(kKindLabels[kind_index(Template.kind)]).c_str(), name.c_str(), variant.c_str(), where.c_str()) + "\n" + STR("Drag along: the trains it faces run the way of the drag");
auto const event{editor_includes::substitute(Template.read, {name})};
auto const path{Spot.track->name().empty() ? std::string{STR("(no name)")} : Spot.track->name()};
auto const path{name_of(*Spot.track)};
return format(STR_C("Click: %s %s (%s)%s, read by %s %s of %s"), STR(kKindLabels[kind_index(Template.kind)]).c_str(), name.c_str(), variant.c_str(), where.c_str(), editor_track::event_keyword(Spot.list), event.c_str(), path.c_str()) + "\n" + STR("Drag along: the trains it faces run the way of the drag");
}
@@ -376,7 +365,7 @@ void editor_mode::signal_refresh(bool const Force)
for (int list = 0; list < 6; ++list)
for (auto const &event : editor_track::events(*track, list))
if (false == event.first.empty())
readers.emplace(lower(event.first), std::make_pair(track, list));
readers.emplace(ToLower(event.first), std::make_pair(track, list));
}
for (auto const &include : found)
{
@@ -388,7 +377,7 @@ void editor_mode::signal_refresh(bool const Force)
if (false == item->read.empty())
{
standing.read = editor_includes::substitute(item->read, include.values);
if (auto const reader{readers.find(lower(standing.read))}; reader != readers.end())
if (auto const reader{readers.find(ToLower(standing.read))}; reader != readers.end())
{
standing.track = reader->second.first;
standing.list = reader->second.second;
@@ -564,7 +553,7 @@ void editor_mode::signal_release()
signal_detach_events(found->read, states);
if (std::none_of(states.begin(), states.end(), [&](auto const &State) { return State.first == spot.track; }))
states.emplace_back(spot.track, editor_track::capture(*spot.track));
editor_track::events(*spot.track, spot.list).emplace_back(lower(found->read), nullptr);
editor_track::events(*spot.track, spot.list).emplace_back(ToLower(found->read), nullptr);
std::string missing;
editor_track::bind_events(*spot.track, missing);
spot.track->mark_dirty();
@@ -585,7 +574,7 @@ void editor_mode::signal_release()
m_history.back().directives.emplace_back(found->include.instance, std::move(before));
}
tool.status = found->read.empty() ? format(STR_C("Signal %s moved, Ctrl+Z takes it back"), found->name.c_str())
: format(STR_C("Signal %s moved, read by %s %s of %s, Ctrl+Z takes it back"), found->name.c_str(), editor_track::event_keyword(spot.list), found->read.c_str(), spot.track->name().empty() ? STR_C("(no name)") : spot.track->name().c_str());
: format(STR_C("Signal %s moved, read by %s %s of %s, Ctrl+Z takes it back"), found->name.c_str(), editor_track::event_keyword(spot.list), found->read.c_str(), name_of(*spot.track).c_str());
auto const instance{found->include.instance};
signal_refresh(true);
signal_select(instance);
@@ -639,7 +628,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T
}
signal_refresh(true);
auto const taken = [&](std::string const &Name) {
return std::any_of(tool.standing.begin(), tool.standing.end(), [&](signal_standing const &Standing) { return lower(Standing.name) == lower(Name); });
return std::any_of(tool.standing.begin(), tool.standing.end(), [&](signal_standing const &Standing) { return ToLower(Standing.name) == ToLower(Name); });
};
for (int guard = 0; taken(name) && guard < 1000; ++guard)
name = signal_next_name(name);
@@ -663,7 +652,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T
auto const instance{m_history.back().instances.front()};
auto const *placed{signal_template_of(file)};
auto const read{placed != nullptr && false == placed->read.empty() ? editor_includes::substitute(placed->read, {name}) : std::string{}};
auto const path{Spot.track->name().empty() ? std::string{STR("(no name)")} : Spot.track->name()};
auto const path{name_of(*Spot.track)};
std::string reason;
if (read.empty())
tool.status = format(STR_C("Signal %s placed (%s), the train doesn't read it, Ctrl+Z takes it back"), name.c_str(), file.c_str());
@@ -676,7 +665,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T
auto snap{std::move(m_history.back())};
m_history.pop_back();
auto const before{editor_track::capture(*Spot.track)};
editor_track::events(*Spot.track, Spot.list).emplace_back(lower(read), nullptr);
editor_track::events(*Spot.track, Spot.list).emplace_back(ToLower(read), nullptr);
std::string missing;
editor_track::bind_events(*Spot.track, missing);
Spot.track->mark_dirty();
@@ -693,7 +682,7 @@ bool editor_mode::signal_place(signal_spot const &Spot, signal_template const &T
void editor_mode::signal_detach_events(std::string const &Read, std::vector<std::pair<TTrack *, editor_track::state>> &States)
{
auto const name{lower(Read)};
auto const name{ToLower(Read)};
for (auto *track : simulation::Paths.sequence())
{
if (track == nullptr || track->m_editorremoved)
@@ -701,7 +690,7 @@ void editor_mode::signal_detach_events(std::string const &Read, std::vector<std:
bool carries{false};
for (int list = 0; list < 6 && false == carries; ++list)
for (auto const &event : editor_track::events(*track, list))
carries = carries || lower(event.first) == name;
carries = carries || ToLower(event.first) == name;
if (false == carries)
continue;
if (std::none_of(States.begin(), States.end(), [&](auto const &State) { return State.first == track; }))
@@ -709,7 +698,7 @@ void editor_mode::signal_detach_events(std::string const &Read, std::vector<std:
for (int list = 0; list < 6; ++list)
{
auto &events{editor_track::events(*track, list)};
events.erase(std::remove_if(events.begin(), events.end(), [&](auto const &Event) { return lower(Event.first) == name; }), events.end());
events.erase(std::remove_if(events.begin(), events.end(), [&](auto const &Event) { return ToLower(Event.first) == name; }), events.end());
}
std::string missing;
editor_track::bind_events(*track, missing);
@@ -747,7 +736,7 @@ bool editor_mode::signal_delete(scene::instance_handle const Instance)
void editor_mode::signal_renamed(std::string const &Before, std::string const &After, EditorSnapshot &Snapshot)
{
auto const name{lower(Before)};
auto const name{ToLower(Before)};
for (auto *track : simulation::Paths.sequence())
{
if (track == nullptr || track->m_editorremoved)
@@ -755,15 +744,15 @@ void editor_mode::signal_renamed(std::string const &Before, std::string const &A
bool carries{false};
for (int list = 0; list < 6 && false == carries; ++list)
for (auto const &event : editor_track::events(*track, list))
carries = carries || lower(event.first) == name;
carries = carries || ToLower(event.first) == name;
if (false == carries)
continue;
if (std::none_of(Snapshot.tracks.begin(), Snapshot.tracks.end(), [&](auto const &State) { return State.first == track; }))
Snapshot.tracks.emplace_back(track, editor_track::capture(*track));
for (int list = 0; list < 6; ++list)
for (auto &event : editor_track::events(*track, list))
if (lower(event.first) == name)
event = {lower(After), nullptr};
if (ToLower(event.first) == name)
event = {ToLower(After), nullptr};
std::string missing;
editor_track::bind_events(*track, missing);
track->mark_dirty();
@@ -790,7 +779,7 @@ void editor_mode::render_signal_palette()
auto &tool{m_signal};
ImGui::SetNextItemWidth(-1.0f);
ImGui::InputTextWithHint("##signalfilter", STR_C("filter: name, w24, sbl..."), tool.filter, sizeof(tool.filter));
auto const filter{lower(tool.filter)};
auto const filter{ToLower(tool.filter)};
auto const *armed{signal_armed()};
std::vector<std::vector<int>> groups;
std::vector<std::string> keys;
@@ -799,7 +788,7 @@ void editor_mode::render_signal_palette()
auto const &item{tool.templates[i]};
if (item.kind != kKinds[tool.kind])
continue;
if (false == filter.empty() && item.name.find(filter) == std::string::npos && lower(item.description).find(filter) == std::string::npos)
if (false == filter.empty() && item.name.find(filter) == std::string::npos && ToLower(item.description).find(filter) == std::string::npos)
continue;
auto const key{group_of(item.mount, item.lamps)};
auto const found{std::find(keys.begin(), keys.end(), key)};
@@ -897,7 +886,7 @@ void editor_mode::render_signal_selected()
if (found->read.empty())
ImGui::TextDisabled("%s", STR_C("The train doesn't read it"));
else if (found->track != nullptr)
ImGui::TextDisabled(STR_C("Read by %s %s of %s"), editor_track::event_keyword(found->list), found->read.c_str(), found->track->name().empty() ? STR_C("(no name)") : found->track->name().c_str());
ImGui::TextDisabled(STR_C("Read by %s %s of %s"), editor_track::event_keyword(found->list), found->read.c_str(), name_of(*found->track).c_str());
else
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.3f, 1.0f), STR_C("No path has %s among its events: the train doesn't see it"), found->read.c_str());
}
@@ -954,7 +943,7 @@ void editor_mode::render_signal_ui()
if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", STR_C("Texture of the plate from textures/tabl, e.g. a-12 or tob. Left empty: the one named like the signal, if there is one"));
auto const &plates{plate_textures()};
auto const typed{lower(tool.plate)};
auto const typed{ToLower(tool.plate)};
auto const exists{typed.empty() || std::binary_search(plates.begin(), plates.end(), typed)};
if (false == exists)
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.3f, 1.0f), "%s", STR_C("no such texture"));
@@ -987,7 +976,7 @@ void editor_mode::render_signal_ui()
if (missing)
{
auto const &plates{plate_textures()};
auto const typed{lower(tool.plate)};
auto const typed{ToLower(tool.plate)};
int shown{0};
for (auto const &plate : plates)
{

View File

@@ -223,7 +223,7 @@ void speed_tool::render_options()
{
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::ColorConvertU32ToFloat4(family_colour(2, 0.55f)));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(family_colour(2, 0.75f)));
if (ImGui::Button(STR_C("Check"), ImVec2(-1.0f, std::round(ImGui::GetFrameHeight() * 1.3f))))
if (ImGui::Button(STR_C("Check"), ImVec2(-1.0f, tall_button_height())))
speed_start();
ImGui::PopStyleColor(2);
item_tooltip("Looks at the radius and the cant, the cant ramps, the curves without the transitions and the switch branches");
@@ -312,7 +312,7 @@ void speed_tool::render_options()
bool const violated{verdict.violated(state.options)};
bool const bad{violated || verdict.warned(state.options)};
auto const colour{violated ? ImVec4(1.0f, 0.45f, 0.4f, 1.0f) : ImVec4(1.0f, 0.75f, 0.3f, 1.0f)};
auto name{verdict.track->name().empty() ? STR("(no name)") : verdict.track->name()};
auto name{name_of(*verdict.track)};
if (verdict.track->eType == tt_Switch)
name += verdict.path == 0 ? STR_C(" (main)") : STR_C(" (branch)");
if (bad)

View File

@@ -22,6 +22,7 @@ http://mozilla.org/MPL/2.0/.
#include "simulation/simulation.h"
#include "utilities/Globals.h"
#include "utilities/Logs.h"
#include "utilities/utilities.h"
#include "world/Sweep.h"
#include "model/AnimModel.h"
#include "world/Track.h"
@@ -126,12 +127,6 @@ bool same_piece(segment_data const &A, segment_data const &B)
return std::abs(A.rolls[0] - B.rolls[0]) < 1e-3f && std::abs(A.rolls[1] - B.rolls[1]) < 1e-3f;
}
std::string lower(std::string Text)
{
std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast<char>(std::tolower(Character)); });
return Text;
}
} // namespace
void editor_mode::sweep_scan_models()
@@ -153,7 +148,7 @@ void editor_mode::sweep_scan_models()
std::string name;
try
{
extension = lower(entry->path().extension().string());
extension = ToLower(entry->path().extension().string());
if (extension != ".e3d" && extension != ".t3d")
continue;
name = std::filesystem::relative(entry->path(), "models", status).generic_string();
@@ -754,14 +749,14 @@ void editor_mode::render_sweep_ui()
changed = true;
ImGui::SetNextItemWidth(-1.0f);
ImGui::InputTextWithHint("##sweepsearch", STR_C("search the models"), tool.search, sizeof(tool.search));
std::string const text{lower(tool.search)};
std::string const text{ToLower(tool.search)};
if (false == text.empty())
{
ImGui::BeginChild("##sweepmodels", ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 7.0f + 6.0f), true);
std::size_t listed{0};
for (auto const &model : tool.models)
{
if (lower(model).find(text) == std::string::npos)
if (ToLower(model).find(text) == std::string::npos)
continue;
if (++listed > kSweepModelList)
break;
@@ -775,7 +770,7 @@ void editor_mode::render_sweep_ui()
}
// a template takes its parameters, the ones which place it are zeros: it's laid out in its own space
std::string const model{tool.model};
bool const istemplate{model.size() > 4 && lower(model.substr(model.size() - 4)) == ".inc"};
bool const istemplate{model.size() > 4 && ToLower(model.substr(model.size() - 4)) == ".inc"};
if (istemplate && model != tool.parameters_for)
{
tool.parameters_for = model;
@@ -803,7 +798,7 @@ void editor_mode::render_sweep_ui()
auto const id{std::atoi(word.substr(open + 2, close - open - 2).c_str())};
if (id < 1 || id > count || false == tool.parameter_choices[id - 1].empty())
continue;
auto const prefix{lower(word.substr(0, open))};
auto const prefix{ToLower(word.substr(0, open))};
auto suffix{word.substr(close + 1)};
suffix = suffix.substr(0, suffix.find(':'));
auto const slash{prefix.rfind('/')};
@@ -816,13 +811,13 @@ void editor_mode::render_sweep_ui()
std::string stem;
try
{
stem = lower(entry->path().stem().string());
stem = ToLower(entry->path().stem().string());
}
catch (std::exception const &)
{
continue;
}
if (stem.size() > start.size() + suffix.size() && stem.compare(0, start.size(), start) == 0 && (suffix.empty() || stem.compare(stem.size() - suffix.size(), suffix.size(), lower(suffix)) == 0))
if (stem.size() > start.size() + suffix.size() && stem.compare(0, start.size(), start) == 0 && (suffix.empty() || stem.compare(stem.size() - suffix.size(), suffix.size(), ToLower(suffix)) == 0))
variants.insert(stem.substr(start.size(), stem.size() - start.size() - suffix.size()));
}
tool.parameter_choices[id - 1].assign(variants.begin(), variants.end());
@@ -1836,7 +1831,7 @@ void editor_mode::render_bend()
else
{
tool.track = std::clamp(tool.track, 0, static_cast<int>(tool.tracks.size()) - 1);
auto const label = [](std::pair<TTrack *, double> const &Entry) { return format("%s (%.1f m)", Entry.first->name().empty() ? STR_C("(no name)") : Entry.first->name().c_str(), Entry.second); };
auto const label = [](std::pair<TTrack *, double> const &Entry) { return format("%s (%.1f m)", name_of(*Entry.first).c_str(), Entry.second); };
if (begin_fields("##bendsource", 0.35f))
{
field_row("Track");

View File

@@ -43,11 +43,6 @@ struct switch_end_move
double shift{0.0};
};
std::string track_label(TTrack const &Track)
{
return Track.name().empty() ? std::string{STR_C("(no name)")} : Track.name();
}
std::optional<std::vector<switch_end_move>> switch_end_moves(TTrack const &Track, segment_data const &Main, bool const Online, std::pair<double, double> const &Room, double const Offset, std::string &Status)
{
auto const axis{editor_track::main_axis(Track)};
@@ -83,7 +78,7 @@ std::string switch_rejoin(TTrack &Track, std::vector<editor_track::joint> const
auto const shift{glm::distance(editor_track::point_position(Track, joint.own), editor_track::point_position(*joint.other, joint.theirs))};
if (shift > 0.3 * bezier{theirs}.plan_length())
{
loose += (loose.empty() ? "" : ", ") + track_label(*joint.other);
loose += (loose.empty() ? "" : ", ") + name_of(*joint.other);
continue;
}
following.push_back(joint);
@@ -579,7 +574,7 @@ std::string editor_mode::plan_switch_branches(TTrack const &Track, std::vector<e
std::string reason;
if (false == plan_switch_branch(Track.m_paths[joint.own.path], joint, *branch) || false == tracks_editable(branch->part.tracks, reason))
{
notes += (notes.empty() ? "" : "\n") + format(STR_C("The curve past the end of the switch can't keep to it, %s follows the switch on its own"), track_label(*joint.other).c_str());
notes += (notes.empty() ? "" : "\n") + format(STR_C("The curve past the end of the switch can't keep to it, %s follows the switch on its own"), name_of(*joint.other).c_str());
branch = Branches.erase(branch);
continue;
}

View File

@@ -302,7 +302,7 @@ bool editor_mode::tracks_editable(std::vector<TTrack *> const &Tracks, std::stri
std::string reason;
if (track != nullptr && false == editor_track::can_edit_geometry(*track, reason))
{
Reason = (track->name().empty() ? std::string{STR_C("(no name)")} : track->name()) + ": " + reason;
Reason = name_of(*track) + ": " + reason;
return false;
}
}
@@ -698,7 +698,7 @@ void editor_mode::render_track_selection()
auto *track{tracks.front()};
if (false == sheet)
{
ImGui::TextUnformatted(track->name().empty() ? STR_C("(no name)") : track->name().c_str());
ImGui::TextUnformatted(name_of(*track).c_str());
ImGui::TextDisabled("%s, %.2f m", STR_C(type_name(*track)), track->Length());
return;
}
@@ -1743,7 +1743,7 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
// values of the change in progress, shown next to the cursor
std::string editor_mode::track_readout() const
{
auto const name = [](TTrack const *Track) { return Track->name().empty() ? std::string{STR_C("(no name)")} : Track->name(); };
auto const name = [](TTrack const *Track) { return name_of(*Track); };
auto const azimuth = [](glm::dvec3 const &From, glm::dvec3 const &To) {
auto const degrees{glm::degrees(std::atan2(To.x - From.x, To.z - From.z))};
return degrees < 0.0 ? degrees + 360.0 : degrees;

View File

@@ -57,11 +57,6 @@ bool rail(TTrack const &Track)
return (Track.iCategoryFlag & 15) == kRailCategory && false == Track.m_editorremoved && Track.visible();
}
std::string name_of(TTrack const &Track)
{
return Track.name().empty() ? std::string{STR_C("(no name)")} : Track.name();
}
char const *type_of(TTrack const &Track)
{
switch (Track.eType)

View File

@@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/.
#include "utilities/Globals.h"
#include "rendering/renderer.h"
#include "world/Track.h"
#include "utilities/utilities.h"
#include "scene/scene.h"
#include "scene/scenelayers.h"
#include "simulation/simulation.h"
@@ -61,17 +62,6 @@ bool rail(TTrack const &Track)
return (Track.iCategoryFlag & 15) == kRailCategory && false == Track.m_editorremoved && Track.visible();
}
std::string name_of(TTrack const &Track)
{
return Track.name().empty() ? std::string{STR_C("(no name)")} : Track.name();
}
std::string lower(std::string Text)
{
std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast<char>(std::tolower(Character)); });
return Text;
}
char const *type_of(TTrack const &Track)
{
switch (Track.eType)
@@ -843,7 +833,7 @@ void editor_mode::render_track_search()
}
ImGui::SetNextItemWidth(-1.0f);
ImGui::InputTextWithHint("##findtrack", STR_C("Find a track by its name (Ctrl+F)"), search.text, sizeof(search.text));
std::string const text{lower(search.text)};
std::string const text{ToLower(search.text)};
if (text.empty())
{
search.searched.clear();
@@ -857,7 +847,7 @@ void editor_mode::render_track_search()
search.total = 0;
for (auto *track : simulation::Paths.sequence())
{
if (track == nullptr || false == rail(*track) || lower(track->name()).find(text) == std::string::npos)
if (track == nullptr || false == rail(*track) || ToLower(track->name()).find(text) == std::string::npos)
continue;
++search.total;
if (search.found.size() < kSearchLimit)

View File

@@ -77,19 +77,6 @@ double line_angle(double const Angle)
return result;
}
bool numeric(std::string const &Text, double &Value)
{
char *end{nullptr};
Value = std::strtod(Text.c_str(), &end);
return false == Text.empty() && *end == '\0';
}
std::string normalized(std::string File)
{
std::replace(File.begin(), File.end(), '\\', '/');
return ToLower(File);
}
std::vector<double> fixed_angles(std::string const &File)
{
std::vector<double> result;
@@ -280,8 +267,8 @@ bool editor_mode::turntable_read(TTrack *Table)
auto const &included{scene::Layers.instance(handle)};
if (included.removed || included.dead || included.file == nullptr)
continue;
auto const file{normalized(*included.file)};
auto const kind{std::find_if(tool.templates.begin(), tool.templates.end(), [&](turntable_template const &Template) { return normalized(Template.file) == file; })};
auto const file{editor_includes::normalized(*included.file)};
auto const kind{std::find_if(tool.templates.begin(), tool.templates.end(), [&](turntable_template const &Template) { return editor_includes::normalized(Template.file) == file; })};
if (kind == tool.templates.end())
continue;
std::string target;
@@ -559,7 +546,7 @@ std::vector<scene::instance_handle> editor_mode::turntable_includes(TTrack const
auto const &included{scene::Layers.instance(handle)};
if (included.removed || included.dead || included.file == nullptr)
continue;
auto const kind{std::find_if(m_turntable.templates.begin(), m_turntable.templates.end(), [&](turntable_template const &Template) { return normalized(Template.file) == normalized(*included.file); })};
auto const kind{std::find_if(m_turntable.templates.begin(), m_turntable.templates.end(), [&](turntable_template const &Template) { return editor_includes::normalized(Template.file) == editor_includes::normalized(*included.file); })};
std::string target;
std::vector<std::string> values;
if (kind != m_turntable.templates.end() && editor_includes::parse_directive(scene::Layers.directive(handle), target, values) && kind->track <= static_cast<int>(values.size()) && ToLower(values[kind->track - 1]) == Table.name())
@@ -959,7 +946,7 @@ void editor_mode::render_turntable_ui()
else
ImGui::TextDisabled(" - ");
ImGui::SameLine();
auto const name{fit.end.track->name().empty() ? std::string{STR_C("(no name)")} : fit.end.track->name()};
auto const name{name_of(*fit.end.track)};
if (ImGui::Selectable(name.c_str(), false, ImGuiSelectableFlags_None, ImVec2(ImGui::CalcTextSize(name.c_str()).x, 0.f)))
focus_track(*fit.end.track, fit.end.point.path, fit.end.position);
if (ImGui::IsItemHovered())
@@ -1158,7 +1145,7 @@ bool editor_mode::turntable_controls(std::vector<std::string> &Rootstatements)
auto const &included{scene::Layers.instance(handle)};
if (included.removed || included.dead || included.file == nullptr)
continue;
auto const kind{std::find_if(m_turntable.templates.begin(), m_turntable.templates.end(), [&](turntable_template const &Template) { return normalized(Template.file) == normalized(*included.file); })};
auto const kind{std::find_if(m_turntable.templates.begin(), m_turntable.templates.end(), [&](turntable_template const &Template) { return editor_includes::normalized(Template.file) == editor_includes::normalized(*included.file); })};
if (kind == m_turntable.templates.end() || kind->angles == 0)
continue;
std::string target;

View File

@@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/.
#include "simulation/simulation.h"
#include "utilities/Globals.h"
#include "utilities/Logs.h"
#include "utilities/utilities.h"
#include "vehicle/DynObj.h"
#include "world/Track.h"
@@ -37,12 +38,6 @@ using geometry::bezier;
std::size_t const kVehicleList{300};
double const kHalfVehicle{8.0}; // m, the vehicle is placed about centred on the point
std::string lower(std::string Text)
{
std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char const Character) { return static_cast<char>(std::tolower(Character)); });
return Text;
}
std::string skin_name(ui::skin_set const &Skin)
{
return Skin.meta != nullptr && false == Skin.meta->name.empty() ? Skin.meta->name : Skin.skin;
@@ -191,7 +186,7 @@ bool vehicle_tool::place()
std::string name;
for (int index = 1; index < 10000; ++index)
{
name = lower("editor_" + stem + "_" + std::to_string(index));
name = ToLower("editor_" + stem + "_" + std::to_string(index));
if (simulation::Vehicles.find(name) == nullptr && std::find(names.begin(), names.end(), name) == names.end())
break;
}
@@ -210,7 +205,7 @@ bool vehicle_tool::place()
}
tool.placed = head;
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 trackname{name_of(*tool.track)};
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());
if (tool.write && tool.track->name().empty())
@@ -259,7 +254,7 @@ void vehicle_tool::render_options()
ImGui::SetNextItemWidth(-1.0f);
ImGui::InputTextWithHint("##vehiclesearch", STR_C("search: name, series, skin"), tool.search, sizeof(tool.search));
ImGui::Checkbox(STR_C("Only the ones with a cab"), &tool.controllable_only);
std::string const text{lower(tool.search)};
std::string const text{ToLower(tool.search)};
std::vector<std::pair<std::shared_ptr<ui::vehicle_desc>, std::shared_ptr<ui::skin_set>>> listed;
std::size_t total{0};
for (auto const &[path, vehicle] : tool.bank->vehicles)
@@ -270,7 +265,7 @@ void vehicle_tool::render_options()
{
if (skin == nullptr)
continue;
if (false == text.empty() && lower(skin_label(*vehicle, *skin)).find(text) == std::string::npos && (skin->meta == nullptr || skin->meta->search_lowered.find(text) == std::string::npos))
if (false == text.empty() && ToLower(skin_label(*vehicle, *skin)).find(text) == std::string::npos && (skin->meta == nullptr || skin->meta->search_lowered.find(text) == std::string::npos))
continue;
++total;
if (listed.size() < kVehicleList)
@@ -426,13 +421,10 @@ void vehicle_tool::render_options()
if (false == tool.consist.empty() || (tool.vehicle != nullptr && tool.skin != nullptr))
{
auto const half{std::floor((ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f)};
auto const tall{std::round(ImGui::GetFrameHeight() * 1.3f)};
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)))
auto const tall{tall_button_height()};
if (main_button(STR_C("Place"), ImVec2(half, tall)))
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)) && place())
{

View File

@@ -84,6 +84,19 @@ struct nmt_place
bool shapes{true}; // not a bridge nor a tunnel
};
inline std::map<std::string, std::string> selftest_values(std::istringstream &Words)
{
std::map<std::string, std::string> values;
std::string pair;
while (Words >> pair)
{
auto const equals{pair.find('=')};
if (equals != std::string::npos)
values[pair.substr(0, equals)] = pair.substr(equals + 1);
}
return values;
}
class editor_selection
{
public:

View File

@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <cstdio>
#include <cstdlib>
#include <string>
// printf-style formatting into a string, for the messages and the data lines of the editor
@@ -24,3 +25,10 @@ std::string format(char const *Format, Args... Arguments)
text.resize(static_cast<std::size_t>(length));
return text;
}
inline bool numeric(std::string const &Text, double &Value)
{
char *end{nullptr};
Value = std::strtod(Text.c_str(), &end);
return false == Text.empty() && *end == '\0';
}

View File

@@ -38,6 +38,12 @@ inline double plan_distance(glm::dvec3 const &A, glm::dvec3 const &B)
}
// z component of the 3d cross product; for a unit Direction it's the offset of the Point to its left
inline double heading_of(glm::dvec2 const &Direction)
{
auto const degrees{glm::degrees(std::atan2(Direction.x, Direction.y))};
return degrees < 0.0 ? degrees + 360.0 : degrees;
}
inline double cross(glm::dvec2 const &Direction, glm::dvec2 const &Point)
{
return Direction.x * Point.y - Direction.y * Point.x;

View File

@@ -590,6 +590,12 @@ bool complete(include_info const &Info, int const Parameters, std::string *Issue
return issue.empty();
}
std::string normalized(std::string File)
{
std::replace(File.begin(), File.end(), '\\', '/');
return ToLower(File);
}
std::string number(double const Value)
{
std::ostringstream converter;

View File

@@ -89,6 +89,7 @@ bool parse_directive(std::string const &Directive, std::string &File, std::vecto
std::string compose_directive(std::string const &File, std::vector<std::string> const &Values);
// text of a number passed as a parameter value
std::string number(double Value);
std::string normalized(std::string File);
// number of the parameter with specified role, 0 if the description has none
int parameter_with_role(include_info const &Info, std::string const &Role);