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

feat(editor): the norms of a slip switch bent into a curve, as ST-T1-A6 sees it: switch_curve::check_slip tells that a slip is bent in a side track only, never in the running nor the main tracks (11.4 pkt 4b), that a track through it is to keep its curvature of one sign (9.7 pkt 12), that a slip curve bent the way the curve turns gets sharper and no sharper than the 180 m of a side track (table 9.2), and the cant as over a bent switch, with none recommended in a side track (9.3 pkt 5). The cant over a switch is checked by one function for both. Unit tests: a Rkpd of R 190 m bent into R 800 m has its slip curves of R 154 m, below the side track, and 249 m; into R 4000 m of R 181 m, within it; on a straight it's no bent one

This commit is contained in:
maj00r
2026-10-27 19:23:24 +01:00
parent c353435cc1
commit 89b6ca07af
3 changed files with 175 additions and 21 deletions

View File

@@ -28,6 +28,8 @@ constexpr double kStraight{2e-5};
constexpr double kAtEnd{0.3};
// m: how far off the switch a ramp keeps, 9.6 pkt 16
constexpr double kRampClear{6.0};
// m: the least radius of a side track, table 9.2
constexpr double kSideRadius{180.0};
// the curved switches bent from the basic ones, table 11.9: the radius of the basic switch, the limiting curvature of the
// diverging track and the least radius of the main track; the values in its brackets, allowed only by way of exception, are past
@@ -64,6 +66,34 @@ double cant_at(std::vector<track_point> const &Track, double const Station)
return previous->cant + (next->cant - previous->cant) * (Station - previous->station) / span;
}
// the cant of the line over a switch From to To along it: the same all along it, a ramp off it by 6 m at least or ending at its ends
// (11.4 pkt 7, 9.6 pkt 16-17), as much as table 9.6 lets a switch have, Curved or straight
void check_cant(std::vector<track_point> const &Line, double const From, double const To, bool const Curved, double &Start, double &End, std::vector<std::string> &Notes, std::vector<std::string> &Warnings)
{
Start = std::abs(cant_at(Line, From));
End = std::abs(cant_at(Line, To));
if (std::abs(End - Start) > 1.0)
Warnings.emplace_back(format(STR_C("The cant changes within the switch, %.0f to %.0f mm: it's to stay the same over it (11.4 pkt 7)"), Start, End));
auto const low{std::min(From, To)};
auto const high{std::max(From, To)};
for (auto const end : ramp_ends(Line))
{
if (end > low + kAtEnd && end < high - kAtEnd)
Warnings.emplace_back(format(STR_C("A cant ramp starts or ends within the switch, %.1f m from its start: at the start or the end of the switch, or 6 m off it at least (9.6 pkt 16-17)"), std::abs(end - From)));
else if (std::abs(end - low) <= kAtEnd || std::abs(end - high) <= kAtEnd)
Notes.emplace_back(STR_C("A cant ramp starts or ends at an end of the switch"));
else if (auto const off{end < low ? low - end : end - high}; off < kRampClear)
Warnings.emplace_back(format(STR_C("A cant ramp ends %.1f m off the switch, 6 m at least (9.6 pkt 16)"), off));
}
auto const cant{std::max(Start, End)};
if (Curved && cant > 100.0 + 1e-6)
Warnings.emplace_back(format(STR_C("Cant %.0f mm in a bent switch, over 100 mm (table 9.6)"), cant));
else if (Curved && cant > 60.0 + 1e-6)
Notes.emplace_back(format(STR_C("Cant %.0f mm: over the 60 mm recommended for a bent switch, up to 100 mm (table 9.6)"), cant));
else if (false == Curved && cant > 1.0)
Notes.emplace_back(format(STR_C("Cant %.0f mm in a straight switch: none recommended, up to 100 mm (table 9.6)"), cant));
}
} // namespace
double curvature_at(std::vector<track_point> const &Track, double const Station)
@@ -140,8 +170,6 @@ switch_report check(std::vector<track_point> const &Main, std::vector<track_poin
report.main_end = main.last;
report.branch_start = branch.first;
report.branch_end = branch.last;
report.cant_start = std::abs(cant_at(Line, From));
report.cant_end = std::abs(cant_at(Line, To));
report.curved = main.sharpest > kStraight;
report.transition = report.curved && std::abs(main.last - main.first) > std::max(2e-6, 0.05 * std::max(std::abs(main.first), std::abs(main.last)));
@@ -166,27 +194,63 @@ switch_report check(std::vector<track_point> const &Main, std::vector<track_poin
report.warnings.emplace_back(format(STR_C("A switch of 1:%.3f isn't bent in the running and the main tracks: none of 1:9.403 nor steeper than 1:9 (11.4 pkt 4)"), Ratio));
}
// the cant: the same all along the switch, a ramp off it by 6 m at least or ending at its ends (11.4 pkt 7, 9.6 pkt 16-17)
if (std::abs(report.cant_end - report.cant_start) > 1.0)
report.warnings.emplace_back(format(STR_C("The cant changes within the switch, %.0f to %.0f mm: it's to stay the same over it (11.4 pkt 7)"), report.cant_start, report.cant_end));
auto const low{std::min(From, To)};
auto const high{std::max(From, To)};
for (auto const end : ramp_ends(Line))
check_cant(Line, From, To, report.curved, report.cant_start, report.cant_end, report.notes, report.warnings);
return report;
}
slip_report check_slip(std::vector<std::vector<track_point>> const &Tracks, std::vector<std::vector<track_point>> const &Curves, std::vector<track_point> const &Line, double const From, double const To)
{
slip_report report;
if (Tracks.size() < 2 || Line.size() < 2 || std::any_of(Tracks.begin(), Tracks.end(), [](std::vector<track_point> const &Track) { return Track.size() < 2; }))
return report;
// the curvatures counted positive the way the line turns, as the first track goes along it
auto const sign{(Line.back().heading - Line.front().heading < 0.0 ? -1.0 : 1.0) * (To >= From ? 1.0 : -1.0)};
struct span
{
if (end > low + kAtEnd && end < high - kAtEnd)
report.warnings.emplace_back(format(STR_C("A cant ramp starts or ends within the switch, %.1f m from its start: at the start or the end of the switch, or 6 m off it at least (9.6 pkt 16-17)"), std::abs(end - From)));
else if (std::abs(end - low) <= kAtEnd || std::abs(end - high) <= kAtEnd)
report.notes.emplace_back(STR_C("A cant ramp starts or ends at an end of the switch"));
else if (auto const off{end < low ? low - end : end - high}; off < kRampClear)
report.warnings.emplace_back(format(STR_C("A cant ramp ends %.1f m off the switch, 6 m at least (9.6 pkt 16)"), off));
double low{std::numeric_limits<double>::max()};
double high{std::numeric_limits<double>::lowest()};
double sharpest{0.0};
};
auto const along = [&](std::vector<track_point> const &Track) {
span result;
auto const margin{std::min(1.0, (Track.back().station - Track.front().station) * 0.25)};
for (auto s{Track.front().station + margin}; s <= Track.back().station - margin + 1e-9; s += 0.5)
{
auto const curvature{sign * curvature_at(Track, s)};
result.low = std::min(result.low, curvature);
result.high = std::max(result.high, curvature);
result.sharpest = std::max(result.sharpest, std::abs(curvature));
}
return result;
};
auto const middle = [&](std::vector<track_point> const &Track) { return sign * curvature_at(Track, 0.5 * (Track.front().station + Track.back().station)); };
report.first = middle(Tracks[0]);
report.second = middle(Tracks[1]);
// the curvature of each track through it of one sign all along it, as in the main track of a switch (9.7 pkt 12); the slip curves
// begin with the leads along the stock rails, which bend with the line even where the curve turns against it
auto turning{false};
for (auto const &track : Tracks)
{
auto const curvatures{along(track)};
report.curved = report.curved || curvatures.sharpest > kStraight;
turning = turning || (curvatures.low < -kStraight && curvatures.high > kStraight);
}
auto const cant{std::max(report.cant_start, report.cant_end)};
if (report.curved && cant > 100.0 + 1e-6)
report.warnings.emplace_back(format(STR_C("Cant %.0f mm in a bent switch, over 100 mm (table 9.6)"), cant));
else if (report.curved && cant > 60.0 + 1e-6)
report.notes.emplace_back(format(STR_C("Cant %.0f mm: over the 60 mm recommended for a bent switch, up to 100 mm (table 9.6)"), cant));
else if (false == report.curved && cant > 1.0)
report.notes.emplace_back(format(STR_C("Cant %.0f mm in a straight switch: none recommended, up to 100 mm (table 9.6)"), cant));
if (turning)
report.warnings.emplace_back(STR_C("The curvature of a track through the slip turns from one side to the other within it, which isn't allowed (9.7 pkt 12)"));
for (auto const &curve : Curves)
if (curve.size() >= 2)
report.curves.push_back(along(curve).sharpest);
if (report.curved)
{
report.warnings.emplace_back(STR_C("A slip isn't bent in the running nor in the main tracks, a bent one goes into a side track only (11.4 pkt 4b)"));
// bent the way the curve of the slip turns it gets sharper: no sharper than the side tracks take
for (auto const curvature : report.curves)
if (curvature > 1.0 / kSideRadius + 1e-9)
report.warnings.emplace_back(format(STR_C("A slip curve bent to R %.0f m, below the least %.0f m of a side track (table 9.2)"), 1.0 / curvature, kSideRadius));
}
check_cant(Line, From, To, report.curved, report.cant_start, report.cant_end, report.notes, report.warnings);
if (report.curved && std::max(report.cant_start, report.cant_end) > 1.0)
report.notes.emplace_back(STR_C("In a side track the layouts needing no cant are recommended (9.3 pkt 5)"));
return report;
}

View File

@@ -54,6 +54,28 @@ struct switch_report
switch_report check(std::vector<track_point> const &Main, std::vector<track_point> const &Branch, std::vector<track_point> const &Line, double const From, double const To, double const Radius,
double const Ratio);
// a slip switch bent into a curve, as ST-T1-A6 sees it: in a side track only (11.4 pkt 4b), the curvature of each track through it of
// one sign all along it (9.7 pkt 12), its slip curves no sharper than a side track may be (table 9.2), the cant as over a switch bent,
// none recommended in a side track (9.3 pkt 5)
struct slip_report
{
// of the two tracks through the slip in its middle, 1/m, positive turning the way the line does
double first{0.0};
double second{0.0};
// of the slip curves at their sharpest, 1/m
std::vector<double> curves;
// of the line, mm
double cant_start{0.0};
double cant_end{0.0};
bool curved{false}; // the tracks through it bent
std::vector<std::string> notes;
std::vector<std::string> warnings;
};
// Tracks: the two tracks through the slip, Curves: its slip curves, each from its start, the stations from 0 there; Line as for a switch,
// From and To the stations of the ends of the first track through the slip along it
slip_report check_slip(std::vector<std::vector<track_point>> const &Tracks, std::vector<std::vector<track_point>> const &Curves, std::vector<track_point> const &Line, double const From, double const To);
// the start of a switch Length long, going Direction along the line, drawn to where its start or its end meets an end of a cant ramp,
// or to where it keeps the 6 m off it, when the station pressed is within Reach of that; What receives which of them, empty for none
double snapped_start(std::vector<double> const &Ramps, double const Pressed, double const Length, int const Direction, double const Reach, std::string *What = nullptr);

View File

@@ -99,3 +99,71 @@ TEST_CASE("switch on a curve: the ends of a cant ramp at the ends of the switch
CHECK(switch_curve::snapped_start(ramps, 160.0, 30.0, 1, 3.0, &what) == doctest::Approx(160.0));
CHECK(what.empty());
}
namespace {
// a track of one curvature from its start, heading as given there, a point every half metre
std::vector<switch_curve::track_point> arc(double const Length, double const Heading, double const Curvature) {
std::vector<switch_curve::track_point> points;
for (double s{0.0}; s <= Length + 1e-9; s += 0.5)
points.push_back({s, Heading + Curvature * s, 0.0});
return points;
}
// a slip of 1:9 with the slip curves of R 190 m bent onto the line from the station on: the tracks through it with the curvature of the
// line, the slip curves with theirs plus the one of the line, one turning as the line does, the other against it
switch_curve::slip_report bent_slip(std::vector<switch_curve::track_point> const &Line, double const From, double const Curvature) {
auto const angle{std::atan(1.0 / 9.0)};
auto const half{16.6};
auto const tracks{std::vector{arc(2.0 * half, 0.0, Curvature), arc(2.0 * half, angle, Curvature * std::cos(angle))}};
auto const curves{std::vector{arc(190.0 * angle, 0.0, 1.0 / 190.0 + Curvature), arc(190.0 * angle, angle, -1.0 / 190.0 + Curvature)}};
return switch_curve::check_slip(tracks, curves, Line, From, From + 2.0 * half);
}
} // namespace
TEST_CASE("slip bent into a curve: in a side track only, its slip curve bent the way of the curve no sharper than a side track takes") {
auto const sharp{bent_slip(line(0.0, 0.0, 400.0, 800.0, 0.0), 150.0, 1.0 / 800.0)};
CHECK(sharp.curved);
CHECK(says(sharp.warnings, "11.4 pkt 4b"));
CHECK(sharp.first == doctest::Approx(1.0 / 800.0).epsilon(0.02));
CHECK(sharp.second == doctest::Approx(std::cos(std::atan(1.0 / 9.0)) / 800.0).epsilon(0.02));
REQUIRE(sharp.curves.size() == 2);
// 1/190 + 1/800: R 153.5 m, 1/190 - 1/800: R 249.2 m
CHECK(1.0 / sharp.curves[0] == doctest::Approx(153.5).epsilon(0.01));
CHECK(1.0 / sharp.curves[1] == doctest::Approx(249.2).epsilon(0.01));
CHECK(says(sharp.warnings, "R 154 m, below the least 180 m"));
CHECK(false == says(sharp.warnings, "R 249 m"));
CHECK(false == says(sharp.warnings, "9.7 pkt 12"));
// R 4000 m: the slip curve of 1/190 + 1/4000, R 181.4 m, still within the side tracks
auto const gentle{bent_slip(line(0.0, 0.0, 400.0, 4000.0, 0.0), 150.0, 1.0 / 4000.0)};
CHECK(gentle.curved);
CHECK(says(gentle.warnings, "11.4 pkt 4b"));
CHECK(false == says(gentle.warnings, "table 9.2"));
CHECK(gentle.notes.empty());
// one on a straight is no bent one
auto const straight{bent_slip(line(400.0, 0.0, 0.0, 1000.0, 0.0), 150.0, 0.0)};
CHECK_FALSE(straight.curved);
CHECK(straight.warnings.empty());
}
TEST_CASE("slip bent into a curve: the cant of the side track, a track through it turning from one side to the other") {
// the cant the same over it, none recommended in a side track though
auto const canted{bent_slip(line(0.0, 0.0, 400.0, 1000.0, 30.0), 150.0, 1.0 / 1000.0)};
CHECK(canted.cant_start == doctest::Approx(30.0));
CHECK(canted.cant_end == doctest::Approx(30.0));
CHECK(false == says(canted.warnings, "The cant changes"));
CHECK(says(canted.notes, "9.3 pkt 5"));
// a reverse curve under it
auto const curve{line(0.0, 0.0, 400.0, 1000.0, 0.0)};
auto first{arc(33.2, 0.0, 1.0 / 1000.0)};
for (auto &point : first)
if (point.station > 16.6)
point.heading = 16.6 / 1000.0 - (point.station - 16.6) / 1000.0;
auto const angle{std::atan(1.0 / 9.0)};
auto const report{switch_curve::check_slip({first, arc(33.2, angle, 1.0 / 1000.0)}, {arc(21.0, 0.0, 1.0 / 190.0)}, curve, 150.0, 183.2)};
CHECK(says(report.warnings, "9.7 pkt 12"));
}