mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-29 13:57:28 +01:00
feat(editor): switches kept in the scenery as //$r lines: type (Rz, arc Rlj/Rld, slip Rkpd), rail, R, 1:n, side, a, b, R of the main track, the parts of a slip, km and number, catalogue or measured; recorded as they are placed on a straight, in a curve (one-sided when diverging to the side the curve turns to), as a double slip, and when their geometry is replaced or flipped; the switch panel shows the kept type and the R of the diverging track bent with the main one, with the number, rail and km to edit (km from the kept horizontal alignment); the speed check of a diverging track takes its R from the kept type; records follow renamed switches and go with removed ones on save; editor records stay out of the terrain files (.txtf), and those written there by hand go with them into .btf; the side of the diverging track shown as seen; self-test commands for placing switches, the kept switches and the .btf conversion; Polish translation
This commit is contained in:
@@ -179,6 +179,7 @@ set(SOURCES
|
|||||||
"editor/editorRoad.cpp"
|
"editor/editorRoad.cpp"
|
||||||
"editor/editorAlignment.cpp"
|
"editor/editorAlignment.cpp"
|
||||||
"editor/editorAlignmentStore.cpp"
|
"editor/editorAlignmentStore.cpp"
|
||||||
|
"editor/editorSwitchStore.cpp"
|
||||||
"editor/editorSpeed.cpp"
|
"editor/editorSpeed.cpp"
|
||||||
"editor/editorProfile.cpp"
|
"editor/editorProfile.cpp"
|
||||||
"editor/editorGauge.cpp"
|
"editor/editorGauge.cpp"
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ extern char **environ;
|
|||||||
#include "editor/editorGroundMesh.hpp"
|
#include "editor/editorGroundMesh.hpp"
|
||||||
#include "editor/editorFormat.hpp"
|
#include "editor/editorFormat.hpp"
|
||||||
#include "editor/editorAlignmentStore.hpp"
|
#include "editor/editorAlignmentStore.hpp"
|
||||||
|
#include "editor/editorSwitchStore.hpp"
|
||||||
#include "editor/editorGeometry.hpp"
|
#include "editor/editorGeometry.hpp"
|
||||||
#include "editor/editorIcons.hpp"
|
#include "editor/editorIcons.hpp"
|
||||||
#include "utilities/translation.h"
|
#include "utilities/translation.h"
|
||||||
@@ -497,6 +498,7 @@ void editor_mode::editormode_input::poll()
|
|||||||
|
|
||||||
editor_mode::editor_mode() {
|
editor_mode::editor_mode() {
|
||||||
alignment_store::reset();
|
alignment_store::reset();
|
||||||
|
switch_store::reset();
|
||||||
m_userinterface = std::make_shared<editor_ui>();
|
m_userinterface = std::make_shared<editor_ui>();
|
||||||
// the area fill settings live in the node bank window, in the tab of the fill mode
|
// the area fill settings live in the node bank window, in the tab of the fill mode
|
||||||
ui()->set_fill_options([this]() { render_area_fill(); });
|
ui()->set_fill_options([this]() { render_area_fill(); });
|
||||||
@@ -2904,6 +2906,7 @@ bool editor_mode::save()
|
|||||||
|
|
||||||
if (m_profile.changed)
|
if (m_profile.changed)
|
||||||
profile_store();
|
profile_store();
|
||||||
|
switch_store::tidy();
|
||||||
infra_store();
|
infra_store();
|
||||||
if (false == m_saved_camera.has_value() || glm::distance(m_saved_camera->first, glm::dvec3{Camera.Pos}) > 0.5 || glm::distance(m_saved_camera->second, glm::vec3{Camera.Angle}) > 0.01f)
|
if (false == m_saved_camera.has_value() || glm::distance(m_saved_camera->first, glm::dvec3{Camera.Pos}) > 0.5 || glm::distance(m_saved_camera->second, glm::vec3{Camera.Angle}) > 0.01f)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -787,6 +787,9 @@ class editor_mode : public application_mode, private editor_track::observer
|
|||||||
bool route_load(TTrack &Track);
|
bool route_load(TTrack &Track);
|
||||||
void route_read_stored();
|
void route_read_stored();
|
||||||
void route_take_stored(int const Index);
|
void route_take_stored(int const Index);
|
||||||
|
void record_switch(TTrack &Track, editor_track::switch_template const &Shape);
|
||||||
|
void record_slip(std::vector<TTrack *> const &Parts, std::string const &Kind, std::string const &Type, double const Radius, double const Angle, std::string const &Base);
|
||||||
|
void render_switch_record(TTrack &Track);
|
||||||
void route_store();
|
void route_store();
|
||||||
void render_route_stored();
|
void render_route_stored();
|
||||||
void route_update() { m_route.result = alignment::compute(m_route.design); }
|
void route_update() { m_route.result = alignment::compute(m_route.design); }
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "editor/editorFormat.hpp"
|
#include "editor/editorFormat.hpp"
|
||||||
#include "editor/editorAlignmentStore.hpp"
|
#include "editor/editorAlignmentStore.hpp"
|
||||||
|
#include "editor/editorSwitchStore.hpp"
|
||||||
|
#include "scene/sceneterrain.h"
|
||||||
|
#include "editor/editorSpeed.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -98,6 +101,10 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
// routeapply, routetake as the buttons of the route design do it
|
// routeapply, routetake as the buttons of the route design do it
|
||||||
// alignments logs the alignments kept in the scenery and the element under each path
|
// alignments logs the alignments kept in the scenery and the element under each path
|
||||||
// splitkind <kind>, nudgekind <kind> <dx> <dz> the first path over an element of the kind split in two, or moved
|
// splitkind <kind>, nudgekind <kind> <dx> <dz> the first path over an element of the kind split in two, or moved
|
||||||
|
// switchplace <template> <x> <z> <direction> <side> the catalogue template whose label starts so put into the straight
|
||||||
|
// nearest the point, or into the curve (switchcurve); slipplace <template> <x> <z>
|
||||||
|
// switches logs the switches kept in the scenery and the speed of each diverging track
|
||||||
|
// btfcheck <terrain file> the .txtf converted to .btf, the editor lines the binary file keeps logged
|
||||||
// screenshot
|
// screenshot
|
||||||
// save
|
// save
|
||||||
// log <text>
|
// log <text>
|
||||||
@@ -1127,6 +1134,111 @@ void editor_mode::selftest_step()
|
|||||||
text += format(" [R %.3f turn %.4f L %.3f]", arc.radius, arc.turn, arc.transition);
|
text += format(" [R %.3f turn %.4f L %.3f]", arc.radius, arc.turn, arc.transition);
|
||||||
WriteLog(text);
|
WriteLog(text);
|
||||||
}
|
}
|
||||||
|
else if (command == "switchplace" || command == "switchcurve" || command == "slipplace")
|
||||||
|
{
|
||||||
|
std::string label;
|
||||||
|
glm::dvec3 point{0.0};
|
||||||
|
int direction{1}, side{1};
|
||||||
|
words >> label >> point.x >> point.z >> direction >> side;
|
||||||
|
std::replace(label.begin(), label.end(), '_', ' ');
|
||||||
|
auto &tool{m_switch};
|
||||||
|
tool.templates = editor_track::standard_switch_templates();
|
||||||
|
tool.collected = true;
|
||||||
|
tool.armed = -1;
|
||||||
|
for (int i = 0; i < static_cast<int>(tool.templates.size()); ++i)
|
||||||
|
if (tool.templates[i].label.rfind(label, 0) == 0)
|
||||||
|
{
|
||||||
|
tool.armed = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TTrack *nearest{nullptr};
|
||||||
|
double distance{1e9};
|
||||||
|
for (auto *track : simulation::Paths.sequence())
|
||||||
|
{
|
||||||
|
if (track == nullptr || track->m_editorremoved || track->eType != tt_Normal || track->m_paths.empty())
|
||||||
|
continue;
|
||||||
|
auto const d{geometry::plan_distance(editor_track::point_at(*track, editor_track::nearest_parameter(*track, point)), point)};
|
||||||
|
if (d < distance)
|
||||||
|
{
|
||||||
|
distance = d;
|
||||||
|
nearest = track;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tool.armed < 0 || nearest == nullptr)
|
||||||
|
{
|
||||||
|
WriteLog("SELFTEST " + command + ": no template " + label + " or no path");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tool.status.clear();
|
||||||
|
if (command == "switchcurve")
|
||||||
|
{
|
||||||
|
editor_track::curve curve;
|
||||||
|
editor_track::chain chain;
|
||||||
|
std::string error;
|
||||||
|
if (false == editor_track::find_curve(*nearest, m_straights.tolerance, m_route.design.norms.gauge, curve) || false == editor_track::find_chain(curve.from, curve.to, chain, error, true))
|
||||||
|
{
|
||||||
|
WriteLog("SELFTEST switchcurve: no curve");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tool.frame = sample_chain(chain);
|
||||||
|
tool.frame_tracks = chain.tracks;
|
||||||
|
tool.curved = true;
|
||||||
|
tool.along = nearest_station(tool.frame, point);
|
||||||
|
insert_curved_switch(direction, side);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto const line{editor_track::find_straight(*nearest, m_straights.tolerance)};
|
||||||
|
if (command == "slipplace")
|
||||||
|
{
|
||||||
|
WriteLog(format("SELFTEST slipplace: straight from %s, %zu paths, start %.3f %.3f, %.3f m", nearest->name().c_str(), line.tracks.size(), line.start.x, line.start.z, line.length));
|
||||||
|
insert_double_slip(line, point, tool.templates[tool.armed]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
insert_switch(line, std::clamp(line.along(point), 0.0, line.length), direction, side);
|
||||||
|
}
|
||||||
|
WriteLog("SELFTEST " + command + ": " + tool.status + " " + m_straights.status);
|
||||||
|
}
|
||||||
|
else if (command == "btfcheck")
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
words >> name;
|
||||||
|
auto const textfile{Global.asCurrentSceneryPath + name + ".txtf"};
|
||||||
|
auto const binaryfile{Global.asCurrentSceneryPath + name + ".btf"};
|
||||||
|
auto const written{scene::terrain_file::prepare(textfile, binaryfile) == scene::terrain_file::state::binary};
|
||||||
|
auto const kept{scene::terrain_file::extra(binaryfile)};
|
||||||
|
WriteLog(format("SELFTEST btfcheck: binary %d, %zu bytes of the editor lines kept", written ? 1 : 0, kept.size()));
|
||||||
|
std::istringstream lines{kept};
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(lines, line))
|
||||||
|
WriteLog("SELFTEST btf kept: " + line);
|
||||||
|
}
|
||||||
|
else if (command == "switches")
|
||||||
|
{
|
||||||
|
for (auto const &entry : switch_store::records())
|
||||||
|
{
|
||||||
|
std::string parts;
|
||||||
|
for (auto const &part : entry.parts)
|
||||||
|
parts += part + " ";
|
||||||
|
WriteLog(format("SELFTEST switch record %s %s: %s, R %.3f 1:%.3f hand %c a %.3f b %.3f main R %.3f, branch R %.3f, km %s, number %s, %s, parts %s", entry.kind.c_str(), entry.name.c_str(), switch_store::label(entry).c_str(), entry.radius, entry.ratio, entry.hand, entry.a,
|
||||||
|
entry.b, entry.main_radius, switch_store::branch_radius(entry), entry.km.has_value() ? format("%.3f", *entry.km / 1000.0).c_str() : "-", entry.number.c_str(), entry.source.c_str(), parts.c_str()));
|
||||||
|
}
|
||||||
|
speed_check::options options;
|
||||||
|
options.norms = m_route.design.norms;
|
||||||
|
for (auto *track : simulation::Paths.sequence())
|
||||||
|
{
|
||||||
|
if (track == nullptr || track->m_editorremoved || track->eType != tt_Switch || track->m_paths.size() < 2)
|
||||||
|
continue;
|
||||||
|
auto const known{switch_store::of(*track)};
|
||||||
|
geometry::bezier const branch{track->m_paths[1]};
|
||||||
|
auto const first{branch.first(0.3)};
|
||||||
|
auto const second{branch.second(0.3)};
|
||||||
|
auto const planar{std::abs(first.x * second.z - first.z * second.x) / std::pow(first.x * first.x + first.z * first.z, 1.5)};
|
||||||
|
auto const verdict{speed_check::check(*track, 1, options)};
|
||||||
|
WriteLog(format("SELFTEST switch %s: %s, branch R at 0.3 %.3f, path R %.3f, branch speed %.1f km/h at R %.1f", track->name().c_str(), known.has_value() ? switch_store::label(*known).c_str() : "no record", planar > 1e-9 ? 1.0 / planar : 0.0,
|
||||||
|
track->m_paths[1].radius, verdict.safe, verdict.radius));
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (command == "splitkind" || command == "nudgekind")
|
else if (command == "splitkind" || command == "nudgekind")
|
||||||
{
|
{
|
||||||
std::string kind;
|
std::string kind;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ http:
|
|||||||
#include "application/editorprojection.h"
|
#include "application/editorprojection.h"
|
||||||
#include "editor/editorFormat.hpp"
|
#include "editor/editorFormat.hpp"
|
||||||
#include "editor/editorAlignmentStore.hpp"
|
#include "editor/editorAlignmentStore.hpp"
|
||||||
|
#include "editor/editorSwitchStore.hpp"
|
||||||
#include "editor/editorGeometry.hpp"
|
#include "editor/editorGeometry.hpp"
|
||||||
#include "editor/editorIncludeInfo.hpp"
|
#include "editor/editorIncludeInfo.hpp"
|
||||||
|
|
||||||
@@ -1751,8 +1752,9 @@ void editor_mode::render_turnout_ui()
|
|||||||
auto const offset{glm::dot(end, normal)};
|
auto const offset{glm::dot(end, normal)};
|
||||||
glm::dvec2 const tangent{glm::normalize(glm::dvec2{-control.x, -control.z})};
|
glm::dvec2 const tangent{glm::normalize(glm::dvec2{-control.x, -control.z})};
|
||||||
auto const angle{std::atan2(std::abs(tangent.x * axis.y - tangent.y * axis.x), glm::dot(tangent, axis))};
|
auto const angle{std::atan2(std::abs(tangent.x * axis.y - tangent.y * axis.x), glm::dot(tangent, axis))};
|
||||||
ImGui::Text(STR_C("Length %.3f m, diverging %s, end offset %.3f m"), length, offset > 0.0 ? STR_C("left") : STR_C("right"), std::abs(offset));
|
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_track_name(*track);
|
||||||
|
render_switch_record(*track);
|
||||||
if ((simulation::Events.FindEvent(track->name() + "+") != nullptr && simulation::Events.FindEvent(track->name() + "-") != nullptr) || (m_track_name.track == track && m_track_name.uses.includes > 0))
|
if ((simulation::Events.FindEvent(track->name() + "+") != nullptr && simulation::Events.FindEvent(track->name() + "-") != nullptr) || (m_track_name.track == track && m_track_name.uses.includes > 0))
|
||||||
ImGui::TextDisabled(STR_C("Drive: events %s+ and %s- switch it, also in the map"), track->name().c_str(), track->name().c_str());
|
ImGui::TextDisabled(STR_C("Drive: events %s+ and %s- switch it, also in the map"), track->name().c_str(), track->name().c_str());
|
||||||
else
|
else
|
||||||
@@ -1832,6 +1834,7 @@ void editor_mode::render_turnout_ui()
|
|||||||
push_track_snapshot(std::move(states));
|
push_track_snapshot(std::move(states));
|
||||||
editor_track::follow(*track, following);
|
editor_track::follow(*track, following);
|
||||||
editor_track::commit(changed);
|
editor_track::commit(changed);
|
||||||
|
record_switch(*track, Template);
|
||||||
if (false == loose.empty())
|
if (false == loose.empty())
|
||||||
ui()->set_status(STR_C("Too far from the new ends of the switch, left unjoined: ") + loose, true);
|
ui()->set_status(STR_C("Too far from the new ends of the switch, left unjoined: ") + loose, true);
|
||||||
};
|
};
|
||||||
@@ -2448,6 +2451,24 @@ alignment::vertex editor_mode::route_vertex_of(editor_track::curve const &Curve)
|
|||||||
return vertex;
|
return vertex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
// layer the editor keeps the records of the track in: its own, unless it's a terrain file (.txtf), whose binary version
|
||||||
|
// (.btf) the editor doesn't read the records from
|
||||||
|
scene::layer_handle record_layer(TTrack const &Track)
|
||||||
|
{
|
||||||
|
auto const usable = [](scene::layer_handle const Layer) { return scene::Layers.valid(Layer) && scene::Layers.accepts(Layer) && false == scene::Layers.layer(Layer).name.ends_with(".txtf"); };
|
||||||
|
auto layer{scene::Layers.resolve(Track.layer())};
|
||||||
|
if (false == usable(layer))
|
||||||
|
layer = scene::Layers.active();
|
||||||
|
if (false == usable(layer))
|
||||||
|
layer = 1;
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
bool editor_mode::route_load(TTrack &Track)
|
bool editor_mode::route_load(TTrack &Track)
|
||||||
{
|
{
|
||||||
m_route.designed = 0;
|
m_route.designed = 0;
|
||||||
@@ -2542,12 +2563,7 @@ void editor_mode::route_store()
|
|||||||
auto &route{m_route};
|
auto &route{m_route};
|
||||||
if (false == route.result.valid || route.chain.tracks.empty() || scene::Layers.empty())
|
if (false == route.result.valid || route.chain.tracks.empty() || scene::Layers.empty())
|
||||||
return;
|
return;
|
||||||
auto layer{scene::Layers.resolve(route.chain.tracks.front()->layer())};
|
alignment_store::store(route.design, route.result, route.origin, record_layer(*route.chain.tracks.front()));
|
||||||
if (false == scene::Layers.valid(layer) || false == scene::Layers.accepts(layer))
|
|
||||||
layer = scene::Layers.active();
|
|
||||||
if (false == scene::Layers.valid(layer))
|
|
||||||
layer = 1;
|
|
||||||
alignment_store::store(route.design, route.result, route.origin, layer);
|
|
||||||
route.departed = -1;
|
route.departed = -1;
|
||||||
route.departure = 0.0;
|
route.departure = 0.0;
|
||||||
}
|
}
|
||||||
@@ -6120,6 +6136,138 @@ void editor_mode::insert_switch(editor_track::straight const &Line, double const
|
|||||||
switch_placed(placed);
|
switch_placed(placed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void place_record(switch_store::record &Entry, TTrack const &First)
|
||||||
|
{
|
||||||
|
auto const &main{First.m_paths.front()};
|
||||||
|
bezier const curve{main};
|
||||||
|
Entry.start = main.points[segment_data::point::start];
|
||||||
|
auto const direction{plan_of(curve.first(0.0))};
|
||||||
|
Entry.direction = glm::length(direction) > 1e-12 ? glm::normalize(direction) : glm::dvec2{0.0, 1.0};
|
||||||
|
Entry.km = alignment_store::chainage_at(Entry.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// the switch is kept in the scenery with its type: of the catalogue template, of the switch of the scenery it was copied
|
||||||
|
// from, or measured; on a curve it is an arc switch, one-sided when it diverges to the side the curve turns to
|
||||||
|
void editor_mode::record_switch(TTrack &Track, editor_track::switch_template const &Shape)
|
||||||
|
{
|
||||||
|
if (Track.eType != tt_Switch || Track.m_paths.size() < 2 || Track.name().empty() || scene::Layers.empty())
|
||||||
|
return;
|
||||||
|
switch_store::record entry;
|
||||||
|
if (Shape.source != nullptr)
|
||||||
|
{
|
||||||
|
if (auto const known{switch_store::of(*Shape.source)}; known.has_value() && known->kind == "switch")
|
||||||
|
{
|
||||||
|
entry = *known;
|
||||||
|
entry.number = "-";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.type = "-";
|
||||||
|
entry.radius = Shape.radius;
|
||||||
|
entry.ratio = Shape.ratio;
|
||||||
|
entry.source = "measured";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.type = Shape.type;
|
||||||
|
entry.rail = Shape.rail;
|
||||||
|
entry.radius = Shape.radius;
|
||||||
|
entry.ratio = Shape.ratio;
|
||||||
|
entry.a = Shape.a;
|
||||||
|
entry.b = Shape.b;
|
||||||
|
entry.source = "designed";
|
||||||
|
}
|
||||||
|
entry.kind = "switch";
|
||||||
|
entry.name = Track.name();
|
||||||
|
entry.parts = {entry.name};
|
||||||
|
place_record(entry, Track);
|
||||||
|
bezier const main{Track.m_paths[0]};
|
||||||
|
auto const &diverging{Track.m_paths[1]};
|
||||||
|
entry.hand = cross(entry.direction, plan_of(diverging.points[segment_data::point::end] - entry.start)) > 0.0 ? 'R' : 'L';
|
||||||
|
auto const first{plan_of(main.first(0.0))};
|
||||||
|
auto const last{plan_of(main.first(1.0))};
|
||||||
|
auto const turn{glm::length(first) > 1e-12 && glm::length(last) > 1e-12 ? signed_angle(glm::normalize(first), glm::normalize(last)) : 0.0};
|
||||||
|
entry.main_radius = std::abs(turn) > 1e-6 ? main.plan_length() / turn : 0.0;
|
||||||
|
if (std::abs(entry.main_radius) > 20000.0)
|
||||||
|
entry.main_radius = 0.0;
|
||||||
|
if (entry.type == "Rz" || entry.type == "Rlj" || entry.type == "Rld")
|
||||||
|
entry.type = entry.main_radius == 0.0 ? "Rz" : (entry.main_radius > 0.0) == (entry.hand == 'R') ? "Rlj" : "Rld";
|
||||||
|
switch_store::put(entry, record_layer(Track));
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_mode::record_slip(std::vector<TTrack *> const &Parts, std::string const &Kind, std::string const &Type, double const Radius, double const Angle, std::string const &Base)
|
||||||
|
{
|
||||||
|
if (Parts.empty() || scene::Layers.empty())
|
||||||
|
return;
|
||||||
|
switch_store::record entry;
|
||||||
|
entry.kind = Kind;
|
||||||
|
entry.name = Base;
|
||||||
|
entry.type = Type;
|
||||||
|
entry.radius = Radius;
|
||||||
|
entry.ratio = Angle > 1e-9 ? 1.0 / std::tan(Angle) : 0.0;
|
||||||
|
for (auto const *part : Parts)
|
||||||
|
if (part != nullptr && false == part->name().empty() && (Kind == "crossing" || part->eType == tt_Switch))
|
||||||
|
entry.parts.push_back(part->name());
|
||||||
|
if (entry.parts.empty())
|
||||||
|
return;
|
||||||
|
auto *first{simulation::Paths.find(entry.parts.front())};
|
||||||
|
if (first == nullptr || first->m_paths.empty())
|
||||||
|
return;
|
||||||
|
place_record(entry, *first);
|
||||||
|
switch_store::put(entry, record_layer(*first));
|
||||||
|
}
|
||||||
|
|
||||||
|
void editor_mode::render_switch_record(TTrack &Track)
|
||||||
|
{
|
||||||
|
auto const known{switch_store::of(Track)};
|
||||||
|
if (false == known.has_value())
|
||||||
|
{
|
||||||
|
ImGui::TextDisabled("%s", STR_C("The scenery keeps no type for this switch, it's read from its shape"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto entry{*known};
|
||||||
|
ImGui::Text(STR_C("Type kept in the scenery: %s"), switch_store::label(entry).c_str());
|
||||||
|
if (entry.kind == "switch" && entry.main_radius != 0.0)
|
||||||
|
ImGui::TextDisabled(STR_C("Diverging track R %.0f m: the R %.0f m of the type bent with the main track"), switch_store::branch_radius(entry), entry.radius);
|
||||||
|
if (entry.source == "measured")
|
||||||
|
ImGui::TextDisabled("%s", STR_C("Copied from a switch of the scenery: the size is measured from its shape"));
|
||||||
|
bool changed{false};
|
||||||
|
char number[32];
|
||||||
|
std::snprintf(number, sizeof(number), "%s", entry.number == "-" ? "" : entry.number.c_str());
|
||||||
|
ImGui::PushItemWidth(100.0f);
|
||||||
|
if (ImGui::InputText(STR_C("Number##switchnumber"), number, sizeof(number), ImGuiInputTextFlags_EnterReturnsTrue) || (ImGui::IsItemDeactivatedAfterEdit()))
|
||||||
|
{
|
||||||
|
entry.number = number[0] != '\0' ? number : "-";
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
item_tooltip("Number of the switch in the station, as the plans of the station give it");
|
||||||
|
ImGui::SameLine();
|
||||||
|
char rail[16];
|
||||||
|
std::snprintf(rail, sizeof(rail), "%s", entry.rail == "-" ? "" : entry.rail.c_str());
|
||||||
|
if (ImGui::InputText(STR_C("Rail##switchrail"), rail, sizeof(rail), ImGuiInputTextFlags_EnterReturnsTrue) || (ImGui::IsItemDeactivatedAfterEdit()))
|
||||||
|
{
|
||||||
|
entry.rail = rail[0] != '\0' ? rail : "-";
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
item_tooltip("Rail of the switch: 60E1, 49E1...");
|
||||||
|
auto km{entry.km.has_value() ? *entry.km / 1000.0 : 0.0};
|
||||||
|
if (ImGui::InputDouble(STR_C("Km of the start##switchkm"), &km, 0.0, 0.0, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue))
|
||||||
|
{
|
||||||
|
entry.km = km * 1000.0;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
item_tooltip("Chainage of the start of the switch; taken from the horizontal alignment kept in the scenery when the switch lies on one");
|
||||||
|
ImGui::PopItemWidth();
|
||||||
|
if (changed)
|
||||||
|
switch_store::put(entry, entry.layer);
|
||||||
|
}
|
||||||
|
|
||||||
void editor_mode::switch_placed(std::vector<TTrack *> const &Created)
|
void editor_mode::switch_placed(std::vector<TTrack *> const &Created)
|
||||||
{
|
{
|
||||||
auto const found{std::find_if(Created.begin(), Created.end(), [](TTrack const *Track) { return Track != nullptr && Track->eType == tt_Switch; })};
|
auto const found{std::find_if(Created.begin(), Created.end(), [](TTrack const *Track) { return Track != nullptr && Track->eType == tt_Switch; })};
|
||||||
@@ -6410,6 +6558,7 @@ bool editor_mode::place_switch_on_straight(editor_track::straight const &Line, e
|
|||||||
if (track != nullptr)
|
if (track != nullptr)
|
||||||
{
|
{
|
||||||
editor_track::commit({track});
|
editor_track::commit({track});
|
||||||
|
record_switch(*track, shape);
|
||||||
Created.push_back(track);
|
Created.push_back(track);
|
||||||
m_switch.status = STR_C("Switch ") + track->name() + STR_C(" inserted");
|
m_switch.status = STR_C("Switch ") + track->name() + STR_C(" inserted");
|
||||||
}
|
}
|
||||||
@@ -6985,6 +7134,7 @@ std::vector<TTrack *> editor_mode::build_double_slip(glm::dvec3 const &PointA, g
|
|||||||
parts.push_back(editor_track::create_path(Style, arc(PointA, First, side, PointD, middle - split, middle + split)));
|
parts.push_back(editor_track::create_path(Style, arc(PointA, First, side, PointD, middle - split, middle + split)));
|
||||||
parts.push_back(editor_track::create_path(Style, arc(PointC, Second, -side, PointB, middle - split, middle + split)));
|
parts.push_back(editor_track::create_path(Style, arc(PointC, Second, -side, PointB, middle - split, middle + split)));
|
||||||
parts.erase(std::remove(parts.begin(), parts.end(), nullptr), parts.end());
|
parts.erase(std::remove(parts.begin(), parts.end(), nullptr), parts.end());
|
||||||
|
record_slip(parts, "slip", "Rkpd", Radius, Angle, base);
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7449,6 +7599,7 @@ void editor_mode::insert_curved_switch(int const Direction, int const Side)
|
|||||||
if (track != nullptr)
|
if (track != nullptr)
|
||||||
{
|
{
|
||||||
editor_track::commit({track});
|
editor_track::commit({track});
|
||||||
|
record_switch(*track, shape);
|
||||||
created.push_back(track);
|
created.push_back(track);
|
||||||
tool.status = STR_C("Switch ") + track->name() + STR_C(" inserted into the curve");
|
tool.status = STR_C("Switch ") + track->name() + STR_C(" inserted into the curve");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "editor/editorFormat.hpp"
|
#include "editor/editorFormat.hpp"
|
||||||
#include "editor/editorGeometry.hpp"
|
#include "editor/editorGeometry.hpp"
|
||||||
#include "editor/editorTrack.hpp"
|
#include "editor/editorTrack.hpp"
|
||||||
|
#include "editor/editorSwitchStore.hpp"
|
||||||
|
|
||||||
#include "world/Track.h"
|
#include "world/Track.h"
|
||||||
|
|
||||||
@@ -232,6 +233,10 @@ verdict check(TTrack &Track, int const Path, options const &Options)
|
|||||||
|
|
||||||
auto const gauge{Options.norms.gauge / 1000.0};
|
auto const gauge{Options.norms.gauge / 1000.0};
|
||||||
bool const branch{Track.eType == tt_Switch && Path == 1};
|
bool const branch{Track.eType == tt_Switch && Path == 1};
|
||||||
|
double kept{0.0};
|
||||||
|
if (branch)
|
||||||
|
if (auto const known{switch_store::of(Track)})
|
||||||
|
kept = switch_store::branch_radius(*known);
|
||||||
auto const &own{Track.m_paths[Path]};
|
auto const &own{Track.m_paths[Path]};
|
||||||
geometry::bezier const curve{own};
|
geometry::bezier const curve{own};
|
||||||
auto const before{continuations(Track, Path, false, curve.first(0.0), true)};
|
auto const before{continuations(Track, Path, false, curve.first(0.0), true)};
|
||||||
@@ -273,7 +278,7 @@ verdict check(TTrack &Track, int const Path, options const &Options)
|
|||||||
if (sampled[i].span != own_span)
|
if (sampled[i].span != own_span)
|
||||||
continue;
|
continue;
|
||||||
auto const &here{samples[i]};
|
auto const &here{samples[i]};
|
||||||
auto const curvature{std::abs(here.curvature)};
|
auto const curvature{kept > 0.0 ? std::min(std::abs(here.curvature), 1.0 / kept) : std::abs(here.curvature)};
|
||||||
auto const cant{std::abs(here.cant)};
|
auto const cant{std::abs(here.cant)};
|
||||||
double safe{Options.top};
|
double safe{Options.top};
|
||||||
cause why{cause::none};
|
cause why{cause::none};
|
||||||
|
|||||||
257
editor/editorSwitchStore.cpp
Normal file
257
editor/editorSwitchStore.cpp
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
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/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "editor/editorSwitchStore.hpp"
|
||||||
|
#include "editor/editorFormat.hpp"
|
||||||
|
#include "editor/editorGeometry.hpp"
|
||||||
|
|
||||||
|
#include "world/Track.h"
|
||||||
|
#include "simulation/simulation.h"
|
||||||
|
#include "utilities/Logs.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace switch_store
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
using geometry::plan_of;
|
||||||
|
|
||||||
|
std::string const kMark{"//$r"};
|
||||||
|
double const kTolerance{0.02};
|
||||||
|
|
||||||
|
std::vector<record> g_records;
|
||||||
|
bool g_loaded{false};
|
||||||
|
|
||||||
|
// format of the lines, coordinates in the space of the file:
|
||||||
|
// <switch|slip|crossing> <name> <type> <rail> <R> <n of 1:n> <hand L|R|-> <a> <b> <R of the main track, + right, - left, 0 straight>
|
||||||
|
// parts <count> <names>... start <x y z> dir <x z> km <km|-> number <label|-> source <designed|measured>
|
||||||
|
std::string word(std::string const &Text)
|
||||||
|
{
|
||||||
|
if (Text.empty())
|
||||||
|
return "-";
|
||||||
|
auto result{Text};
|
||||||
|
std::replace(result.begin(), result.end(), ' ', '_');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::dvec2 direction_of(TTrack const &Track)
|
||||||
|
{
|
||||||
|
geometry::bezier const curve{Track.m_paths.front()};
|
||||||
|
auto const direction{plan_of(curve.first(0.0))};
|
||||||
|
return glm::length(direction) > 1e-12 ? glm::normalize(direction) : glm::dvec2{0.0, 1.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
TTrack *usable(std::string const &Name)
|
||||||
|
{
|
||||||
|
if (Name.empty() || Name == "-")
|
||||||
|
return nullptr;
|
||||||
|
auto *track{simulation::Paths.find(Name)};
|
||||||
|
return track != nullptr && false == track->m_editorremoved && false == track->m_paths.empty() ? track : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_place(record const &Record, TTrack const &Track)
|
||||||
|
{
|
||||||
|
return geometry::plan_distance(Track.m_paths.front().points[segment_data::point::start], Record.start) <= kTolerance && glm::dot(direction_of(Track), Record.direction) > 0.9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark(scene::layer_handle const Layer)
|
||||||
|
{
|
||||||
|
if (false == scene::Layers.valid(Layer))
|
||||||
|
return;
|
||||||
|
auto const &context{scene::Layers.layer(Layer).context_insert()};
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
for (auto const &entry : g_records)
|
||||||
|
{
|
||||||
|
if (entry.layer != Layer)
|
||||||
|
continue;
|
||||||
|
if (lines.empty())
|
||||||
|
lines.emplace_back("# switches placed in the track editor, the simulation skips these lines");
|
||||||
|
auto const start{context.to_local(entry.start)};
|
||||||
|
auto const ahead{context.to_local(entry.start + glm::dvec3{entry.direction.x, 0.0, entry.direction.y})};
|
||||||
|
auto direction{plan_of(ahead - start)};
|
||||||
|
direction = glm::length(direction) > 1e-12 ? glm::normalize(direction) : entry.direction;
|
||||||
|
auto text{format("%s %s %s %s %.3f %.3f %c %.3f %.3f %.3f parts %zu", entry.kind.c_str(), word(entry.name).c_str(), word(entry.type).c_str(), word(entry.rail).c_str(), entry.radius, entry.ratio, entry.hand, entry.a, entry.b, entry.main_radius,
|
||||||
|
entry.parts.size())};
|
||||||
|
for (auto const &part : entry.parts)
|
||||||
|
text += ' ' + word(part);
|
||||||
|
text += format(" start %.4f %.4f %.4f dir %.9f %.9f km ", start.x, start.y, start.z, direction.x, direction.y);
|
||||||
|
text += entry.km.has_value() ? format("%.3f", *entry.km / 1000.0) : std::string{"-"};
|
||||||
|
text += " number " + word(entry.number) + " source " + word(entry.source);
|
||||||
|
lines.push_back(text);
|
||||||
|
}
|
||||||
|
scene::Layers.mark(Layer, kMark, std::move(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
g_records.clear();
|
||||||
|
g_loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load()
|
||||||
|
{
|
||||||
|
if (g_loaded || scene::Layers.empty())
|
||||||
|
return;
|
||||||
|
g_loaded = true;
|
||||||
|
g_records.clear();
|
||||||
|
for (auto const layer : scene::Layers.marked_layers(kMark))
|
||||||
|
{
|
||||||
|
auto const &context{scene::Layers.layer(layer).context_insert()};
|
||||||
|
for (auto const &text : scene::Layers.marked(layer, kMark))
|
||||||
|
{
|
||||||
|
std::istringstream line{text};
|
||||||
|
record entry;
|
||||||
|
entry.layer = layer;
|
||||||
|
std::string keyword, hand, km;
|
||||||
|
std::size_t count{0};
|
||||||
|
line >> entry.kind;
|
||||||
|
if (entry.kind != "switch" && entry.kind != "slip" && entry.kind != "crossing")
|
||||||
|
continue;
|
||||||
|
line >> entry.name >> entry.type >> entry.rail >> entry.radius >> entry.ratio >> hand >> entry.a >> entry.b >> entry.main_radius >> keyword >> count;
|
||||||
|
if (line.fail() || keyword != "parts" || count > 64)
|
||||||
|
continue;
|
||||||
|
entry.hand = hand.empty() ? '-' : hand.front();
|
||||||
|
entry.parts.resize(count);
|
||||||
|
for (auto &part : entry.parts)
|
||||||
|
line >> part;
|
||||||
|
glm::dvec3 start{0.0}, ahead{0.0};
|
||||||
|
glm::dvec2 direction{0.0, 1.0};
|
||||||
|
line >> keyword >> start.x >> start.y >> start.z >> keyword >> direction.x >> direction.y >> keyword >> km >> keyword >> entry.number >> keyword >> entry.source;
|
||||||
|
if (line.fail())
|
||||||
|
continue;
|
||||||
|
entry.start = context.to_world(start);
|
||||||
|
ahead = context.to_world(start + glm::dvec3{direction.x, 0.0, direction.y});
|
||||||
|
entry.direction = plan_of(ahead - entry.start);
|
||||||
|
entry.direction = glm::length(entry.direction) > 1e-12 ? glm::normalize(entry.direction) : glm::dvec2{0.0, 1.0};
|
||||||
|
if (km != "-")
|
||||||
|
entry.km = std::stod(km) * 1000.0;
|
||||||
|
g_records.push_back(std::move(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (false == g_records.empty())
|
||||||
|
WriteLog("Editor: " + std::to_string(g_records.size()) + " switch record(s) read from the scenery", logtype::generic);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<record> const &records()
|
||||||
|
{
|
||||||
|
load();
|
||||||
|
return g_records;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<record> of(TTrack const &Track)
|
||||||
|
{
|
||||||
|
load();
|
||||||
|
auto const &name{Track.name()};
|
||||||
|
if (name.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
for (auto const &entry : g_records)
|
||||||
|
{
|
||||||
|
if (std::find(entry.parts.begin(), entry.parts.end(), name) == entry.parts.end())
|
||||||
|
continue;
|
||||||
|
auto const *first{usable(entry.parts.front())};
|
||||||
|
if (first != nullptr && in_place(entry, *first))
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
double branch_radius(record const &Record)
|
||||||
|
{
|
||||||
|
if (Record.radius <= 0.0)
|
||||||
|
return 0.0;
|
||||||
|
auto const side{Record.hand == 'L' ? -1.0 : 1.0};
|
||||||
|
auto const curvature{side / Record.radius + (Record.main_radius != 0.0 ? 1.0 / Record.main_radius : 0.0)};
|
||||||
|
return std::abs(curvature) > 1e-9 ? 1.0 / std::abs(curvature) : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void put(record Entry, scene::layer_handle const Layer)
|
||||||
|
{
|
||||||
|
load();
|
||||||
|
auto const existing{std::find_if(g_records.begin(), g_records.end(), [&](record const &Other) { return Other.name == Entry.name && Other.kind == Entry.kind; })};
|
||||||
|
if (existing != g_records.end())
|
||||||
|
{
|
||||||
|
Entry.layer = existing->layer;
|
||||||
|
if (Entry.number == "-")
|
||||||
|
Entry.number = existing->number;
|
||||||
|
*existing = std::move(Entry);
|
||||||
|
mark(existing->layer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entry.layer = Layer;
|
||||||
|
g_records.push_back(std::move(Entry));
|
||||||
|
mark(g_records.back().layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forget(std::string const &Name)
|
||||||
|
{
|
||||||
|
load();
|
||||||
|
std::set<scene::layer_handle> layers;
|
||||||
|
for (auto const &entry : g_records)
|
||||||
|
if (entry.name == Name)
|
||||||
|
layers.insert(entry.layer);
|
||||||
|
g_records.erase(std::remove_if(g_records.begin(), g_records.end(), [&](record const &Entry) { return Entry.name == Name; }), g_records.end());
|
||||||
|
for (auto const layer : layers)
|
||||||
|
mark(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tidy()
|
||||||
|
{
|
||||||
|
load();
|
||||||
|
std::set<scene::layer_handle> layers;
|
||||||
|
for (auto &entry : g_records)
|
||||||
|
{
|
||||||
|
if (entry.kind != "switch" || usable(entry.parts.front()) != nullptr)
|
||||||
|
continue;
|
||||||
|
for (auto *track : simulation::Paths.sequence())
|
||||||
|
{
|
||||||
|
if (track == nullptr || track->m_editorremoved || track->eType != tt_Switch || track->m_paths.empty() || track->name().empty() || false == in_place(entry, *track))
|
||||||
|
continue;
|
||||||
|
entry.name = track->name();
|
||||||
|
entry.parts = {entry.name};
|
||||||
|
layers.insert(entry.layer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto const gone = [](record const &Entry) { return std::any_of(Entry.parts.begin(), Entry.parts.end(), [](std::string const &Part) { return usable(Part) == nullptr; }); };
|
||||||
|
for (auto const &entry : g_records)
|
||||||
|
if (gone(entry))
|
||||||
|
layers.insert(entry.layer);
|
||||||
|
g_records.erase(std::remove_if(g_records.begin(), g_records.end(), gone), g_records.end());
|
||||||
|
for (auto const layer : layers)
|
||||||
|
mark(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string label(record const &Record)
|
||||||
|
{
|
||||||
|
auto text{Record.type};
|
||||||
|
if (Record.rail != "-")
|
||||||
|
text += ' ' + Record.rail + '-';
|
||||||
|
else
|
||||||
|
text += ' ';
|
||||||
|
if (Record.radius > 0.0)
|
||||||
|
text += format("%.0f-", Record.radius);
|
||||||
|
if (Record.ratio > 0.0)
|
||||||
|
text += format(std::abs(Record.ratio - std::round(Record.ratio)) < 1e-3 ? "1:%.0f" : "1:%.1f", Record.ratio);
|
||||||
|
if (Record.hand == 'L' || Record.hand == 'R')
|
||||||
|
text += ' ' + std::string(1, Record.hand == 'L' ? 'L' : 'P');
|
||||||
|
if (Record.main_radius != 0.0)
|
||||||
|
text += format(", Rg %.0f", std::abs(Record.main_radius));
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace switch_store
|
||||||
61
editor/editorSwitchStore.hpp
Normal file
61
editor/editorSwitchStore.hpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
This Source Code Form is subject to the
|
||||||
|
terms of the Mozilla Public License, v.
|
||||||
|
2.0. If a copy of the MPL was not
|
||||||
|
distributed with this file, You can
|
||||||
|
obtain one at
|
||||||
|
http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include "scene/scenelayers.h"
|
||||||
|
|
||||||
|
class TTrack;
|
||||||
|
|
||||||
|
// switches placed in the track editor, kept in the scenery files as "//$r" comment lines: their type, size and side, the
|
||||||
|
// paths a slip or a crossing is made of, their km and number, so a switch is known again without guessing from its shape
|
||||||
|
namespace switch_store
|
||||||
|
{
|
||||||
|
|
||||||
|
struct record
|
||||||
|
{
|
||||||
|
scene::layer_handle layer{null_handle};
|
||||||
|
std::string kind{"switch"}; // switch, slip, crossing
|
||||||
|
std::string name; // the switch, or the base name of a slip or a crossing
|
||||||
|
std::string type{"Rz"}; // Rz, Rlj, Rld, Rkp, Rkpd, Skrz, - when unknown
|
||||||
|
std::string rail{"-"};
|
||||||
|
double radius{0.0}; // of the diverging track of the straight switch the type is made of
|
||||||
|
double ratio{0.0}; // n of 1:n
|
||||||
|
char hand{'-'}; // L, R as seen along the switch from its start
|
||||||
|
double a{0.0}, b{0.0};
|
||||||
|
double main_radius{0.0}; // of the main track, signed: + to the right, - to the left, 0 straight
|
||||||
|
std::vector<std::string> parts; // the switch, the switches of a slip, the paths of a crossing
|
||||||
|
glm::dvec3 start{0.0}; // of the first part
|
||||||
|
glm::dvec2 direction{0.0, 1.0};
|
||||||
|
std::optional<double> km;
|
||||||
|
std::string number{"-"};
|
||||||
|
std::string source{"designed"}; // designed: from the catalogue, measured: copied from a switch of the scenery
|
||||||
|
};
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void load();
|
||||||
|
std::vector<record> const &records();
|
||||||
|
// the record of the switch, slip or crossing the track is a part of, while its first part starts where the record says
|
||||||
|
std::optional<record> of(TTrack const &Track);
|
||||||
|
// radius of the diverging track: of the type, bent with the main track for an arc switch
|
||||||
|
double branch_radius(record const &Record);
|
||||||
|
// keeps the record in the file of the layer, in place of the one with the same name
|
||||||
|
void put(record Entry, scene::layer_handle const Layer);
|
||||||
|
void forget(std::string const &Name);
|
||||||
|
// on save: the records follow the renamed switches, the ones whose paths are gone are dropped
|
||||||
|
void tidy();
|
||||||
|
// "Rz 60E1-500-1:12 L", as the type is written on the lists of the switches
|
||||||
|
std::string label(record const &Record);
|
||||||
|
|
||||||
|
} // namespace switch_store
|
||||||
@@ -2556,7 +2556,9 @@ std::vector<editor_track::switch_template> editor_track::standard_switch_templat
|
|||||||
{
|
{
|
||||||
switch_template slip;
|
switch_template slip;
|
||||||
slip.double_slip = true;
|
slip.double_slip = true;
|
||||||
|
slip.type = "Rkpd";
|
||||||
slip.radius = slipping.first;
|
slip.radius = slipping.first;
|
||||||
|
slip.ratio = std::stod(std::string{slipping.second}.substr(2));
|
||||||
slip.label = std::string{"Rkpd "} + slipping.second + " R" + std::to_string(static_cast<int>(slipping.first)) + " double slip (click at a crossing of two straights)";
|
slip.label = std::string{"Rkpd "} + slipping.second + " R" + std::to_string(static_cast<int>(slipping.first)) + " double slip (click at a crossing of two straights)";
|
||||||
result.push_back(slip);
|
result.push_back(slip);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,6 +301,8 @@ class editor_track
|
|||||||
double a{0.0};
|
double a{0.0};
|
||||||
double b{0.0};
|
double b{0.0};
|
||||||
bool double_slip{false};
|
bool double_slip{false};
|
||||||
|
std::string type{"Rz"};
|
||||||
|
std::string rail{"-"};
|
||||||
};
|
};
|
||||||
static std::vector<switch_template> standard_switch_templates();
|
static std::vector<switch_template> standard_switch_templates();
|
||||||
static std::vector<switch_template> find_switch_templates();
|
static std::vector<switch_template> find_switch_templates();
|
||||||
|
|||||||
@@ -5346,3 +5346,33 @@ msgstr "Zapisany projekt zostanie usunięty z "
|
|||||||
|
|
||||||
msgid "The paths stay as they are, the scenery no longer keeps a design for them"
|
msgid "The paths stay as they are, the scenery no longer keeps a design for them"
|
||||||
msgstr "Odcinki zostają bez zmian, sceneria przestaje przechowywać dla nich projekt"
|
msgstr "Odcinki zostają bez zmian, sceneria przestaje przechowywać dla nich projekt"
|
||||||
|
|
||||||
|
msgid "The scenery keeps no type for this switch, it's read from its shape"
|
||||||
|
msgstr "Sceneria nie przechowuje typu tego rozjazdu, jest odczytywany z jego kształtu"
|
||||||
|
|
||||||
|
msgid "Type kept in the scenery: %s"
|
||||||
|
msgstr "Typ zapisany w scenerii: %s"
|
||||||
|
|
||||||
|
msgid "Diverging track R %.0f m: the R %.0f m of the type bent with the main track"
|
||||||
|
msgstr "Tor zwrotny R %.0f m: R %.0f m typu wygięte razem z torem zasadniczym"
|
||||||
|
|
||||||
|
msgid "Copied from a switch of the scenery: the size is measured from its shape"
|
||||||
|
msgstr "Skopiowany z rozjazdu scenerii: wymiary zmierzone z jego kształtu"
|
||||||
|
|
||||||
|
msgid "Number##switchnumber"
|
||||||
|
msgstr "Numer##switchnumber"
|
||||||
|
|
||||||
|
msgid "Number of the switch in the station, as the plans of the station give it"
|
||||||
|
msgstr "Numer rozjazdu na stacji, według planów stacji"
|
||||||
|
|
||||||
|
msgid "Rail##switchrail"
|
||||||
|
msgstr "Szyna##switchrail"
|
||||||
|
|
||||||
|
msgid "Rail of the switch: 60E1, 49E1..."
|
||||||
|
msgstr "Szyna rozjazdu: 60E1, 49E1..."
|
||||||
|
|
||||||
|
msgid "Km of the start##switchkm"
|
||||||
|
msgstr "Km początku##switchkm"
|
||||||
|
|
||||||
|
msgid "Chainage of the start of the switch; taken from the horizontal alignment kept in the scenery when the switch lies on one"
|
||||||
|
msgstr "Kilometraż początku rozjazdu; brany z planu sytuacyjnego zapisanego w scenerii, gdy rozjazd na nim leży"
|
||||||
|
|||||||
Reference in New Issue
Block a user