mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 05:09:20 +02: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:
@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "editor/editorFormat.hpp"
|
||||
#include "editor/editorGeometry.hpp"
|
||||
#include "editor/editorTrack.hpp"
|
||||
#include "editor/editorSwitchStore.hpp"
|
||||
|
||||
#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};
|
||||
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]};
|
||||
geometry::bezier const curve{own};
|
||||
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)
|
||||
continue;
|
||||
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)};
|
||||
double safe{Options.top};
|
||||
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;
|
||||
slip.double_slip = true;
|
||||
slip.type = "Rkpd";
|
||||
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)";
|
||||
result.push_back(slip);
|
||||
}
|
||||
|
||||
@@ -301,6 +301,8 @@ class editor_track
|
||||
double a{0.0};
|
||||
double b{0.0};
|
||||
bool double_slip{false};
|
||||
std::string type{"Rz"};
|
||||
std::string rail{"-"};
|
||||
};
|
||||
static std::vector<switch_template> standard_switch_templates();
|
||||
static std::vector<switch_template> find_switch_templates();
|
||||
|
||||
Reference in New Issue
Block a user