17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-31 05:20:52 +01:00

fix(editor): the signalling mode places the joints and the signals as Ie-4 gives them, a branch joint 4.2 m beyond the fouling point, the joint a main signal drops at 15 m beyond it, the block sections no shorter than the braking distance of the line and the warning discs that far out

The block signals are numbered after the nearest hectometre of their parity ahead of them, and two signals no longer take one spot between the tracks.
This commit is contained in:
maj00r
2026-10-22 12:34:53 +02:00
parent 2b40a36173
commit f287ecf840
4 changed files with 259 additions and 90 deletions

View File

@@ -232,16 +232,19 @@ glm::dvec2 map_of(glm::dvec3 const &Point)
}
double constexpr kStart{1.5}; // m, a joint before the start of a switch
double constexpr kClear{4.0}; // m, between the axes at a joint beyond a fouling point
double constexpr kFouling{3.5}; // m, between the axes at the fouling point of two tracks
double constexpr kBeyond{4.2}; // m, from a fouling point on to the joint beyond it, Ie-4 § 33
double constexpr kBranches{10.0}; // m, the ends of a switch closer together are the ends of its paths at the frog
double constexpr kReach{120.0}; // m, along a branch looked for its fouling point
double constexpr kSection{30.0}; // m, the shortest stretch between two joints made a track circuit of its own
double constexpr kSnap{0.3}; // m, a joint this close to the end of a track lies at the end
double constexpr kThrough{400.0}; // m, of the switches a station track is followed through for its circuits beyond
double constexpr kEntry{300.0}; // m, from the joint of the station out to its entry signal
double constexpr kJoint{3.5}; // m, from a signal to the joint of the circuit it guards
double constexpr kJoint{3.5}; // m, from a shunting signal to the joint of the circuit it guards
double constexpr kStopping{200.0}; // m, the shortest circuit of a station track a train is sent out of by an exit signal
double constexpr kSignalSide{2.25}; // m, from the axis to a signal
double constexpr kSignalClear{2.5}; // m, the least from a signal to the axis of another track
double constexpr kSignalApart{3.0}; // m, the least between two signals
// the points of the way out of an end of the track, as a train runs: along the main path of a switch run into from its start,
// back along the path of one run into from the end of it
@@ -326,12 +329,12 @@ double plan_gap(glm::dvec3 const &Point, std::vector<glm::dvec3> const &Line)
return best;
}
// how far from the end of the switch its joint lies: beyond the fouling point at a branch, before the start elsewhere
// how far from the end of the switch its joint lies: 4.2 m beyond the fouling point at a branch, before the start elsewhere
double clearance(TTrack const &Switch, int const Path, bool const Atend)
{
auto const point{end_of(Switch, Path, Atend)};
std::optional<std::pair<int, bool>> other;
auto nearest{kClear};
auto nearest{kBranches};
for (auto const &[path, atend] : ends_of(Switch))
if (auto const distance{plan_distance(end_of(Switch, path, atend), point)}; distance > same_point && distance < nearest)
{
@@ -340,16 +343,29 @@ double clearance(TTrack const &Switch, int const Path, bool const Atend)
}
if (false == other.has_value())
return kStart;
auto const mine{ahead(Switch, Path, Atend, kReach)};
auto const theirs{ahead(Switch, other->first, other->second, kReach + 50.0)};
if (plan_gap(point, theirs) >= kFouling)
{
// the fouling point within the switch: back along its paths to it
double length{0.0};
auto mine{dense_points(Switch, Path, length)};
auto const own{dense_points(Switch, other->first, length)};
if (Atend)
std::reverse(mine.begin(), mine.end());
double back{0.0};
for (std::size_t i = 1; i < mine.size() && plan_gap(mine[i], own) >= kFouling; ++i)
back += plan_distance(mine[i - 1], mine[i]);
return std::max(0.0, kBeyond - back);
}
auto const mine{ahead(Switch, Path, Atend, kReach)};
double along{0.0};
auto gap{plan_gap(mine.front(), theirs)};
for (std::size_t i = 1; i < mine.size(); ++i)
{
auto const step{plan_distance(mine[i - 1], mine[i])};
auto const next{plan_gap(mine[i], theirs)};
if (next >= kClear)
return along + (next > gap ? step * std::clamp((kClear - gap) / (next - gap), 0.0, 1.0) : step);
if (next >= kFouling)
return along + (next > gap ? step * std::clamp((kFouling - gap) / (next - gap), 0.0, 1.0) : step) + kBeyond;
along += step;
gap = next;
}
@@ -1007,12 +1023,46 @@ std::pair<glm::dvec3, glm::dvec3> in_from(TTrack const &Track, bool const Atend,
return {points.front(), glm::dvec3{0.0, 0.0, 1.0}};
}
// how far a signal on the side of the track at the point keeps from the other tracks; Way: of the path there
double side_gap(layout const &Layout, glm::dvec3 const &Point, glm::dvec3 const &Way, int const Side)
// the point the distance in from the end of the track, on into the tracks before it while they're shorter, short of a switch: the
// track, the end of it the trains running out reach, the point and the way to that end
std::tuple<TTrack *, bool, glm::dvec3, glm::dvec3> back_from(TTrack *Track, bool Atend, double Distance)
{
std::unordered_set<TTrack const *> seen{Track};
while (true)
{
auto const length{plan_length(*Track, 0)};
if (Distance < length - 1.0)
break;
auto const inner{end_of(*Track, 0, false == Atend)};
auto *before{neighbour(*Track, 0, false == Atend)};
int path{0};
bool atend{false};
if (before == nullptr || node(*before) || false == rail(*before) || false == seen.insert(before).second || false == end_at(*before, inner, path, atend))
{
Distance = std::min(Distance, length * 0.5);
break;
}
Distance -= length;
Track = before;
Atend = atend;
}
auto const [at, way] = in_from(*Track, Atend, Distance);
return {Track, Atend, at, way};
}
// where a signal on the side of the track at the point stands, in the plan; Way: of the path there
glm::dvec2 side_spot(glm::dvec3 const &Point, glm::dvec3 const &Way, int const Side)
{
glm::dvec2 const way{glm::normalize(glm::dvec2{Way.x, Way.z} + glm::dvec2{1e-12, 0.0})};
glm::dvec2 const right{-way.y, way.x};
auto const spot{glm::dvec2{Point.x, Point.z} + right * (Side * kSignalSide)};
return glm::dvec2{Point.x, Point.z} + glm::dvec2{-way.y, way.x} * (Side * kSignalSide);
}
// how far a signal on the side of the track at the point keeps from the other tracks, none where another signal stands already
double side_gap(layout const &Layout, glm::dvec3 const &Point, glm::dvec3 const &Way, int const Side, std::vector<glm::dvec2> const &Taken)
{
auto const spot{side_spot(Point, Way, Side)};
if (std::any_of(Taken.begin(), Taken.end(), [&](glm::dvec2 const &Other) { return glm::distance(Other, spot) < kSignalApart; }))
return 0.0;
glm::dvec3 const at{spot.x, Point.y, spot.y};
auto gap{std::numeric_limits<double>::max()};
for (auto const &path : Layout.paths)
@@ -1021,14 +1071,20 @@ double side_gap(layout const &Layout, glm::dvec3 const &Point, glm::dvec3 const
return gap;
}
// on the right of the trains, unless another track runs too close by there and less so on the left
int side_for(layout const &Layout, glm::dvec3 const &Point, glm::dvec3 const &Way, int const Facing)
// on the right of the trains, unless another track runs too close by there or another signal stands there, and less so on the left;
// the spot taken
int side_for(layout const &Layout, glm::dvec3 const &Point, glm::dvec3 const &Way, int const Facing, std::vector<glm::dvec2> &Taken)
{
auto const right{side_gap(Layout, Point, Way, Facing)};
if (right >= kSignalClear)
return Facing;
auto const left{side_gap(Layout, Point, Way, -Facing)};
return left >= kSignalClear || left > right ? -Facing : Facing;
auto const right{side_gap(Layout, Point, Way, Facing, Taken)};
auto side{Facing};
if (right < kSignalClear)
{
auto const left{side_gap(Layout, Point, Way, -Facing, Taken)};
if (left >= kSignalClear || left > right)
side = -Facing;
}
Taken.push_back(side_spot(Point, Way, side));
return side;
}
// the ends of the tracks out of the set, Free where no track goes on
@@ -1073,6 +1129,7 @@ std::vector<signal> signals(layout const &Layout, std::vector<numbered> const &T
{
std::vector<signal> result;
auto const middle{(Layout.from + Layout.to) * 0.5};
std::vector<glm::dvec2> taken; // the spots of the signals placed
// the entry signals: out along each line from where it leaves the circuits of the station
struct entry
{
@@ -1126,7 +1183,7 @@ std::vector<signal> signals(layout const &Layout, std::vector<numbered> const &T
found.placed.track = next;
found.placed.point = at;
found.placed.facing = outward ? -1 : 1;
found.placed.side = side_for(Layout, at, outward ? -way : way, found.placed.facing);
found.placed.side = side_for(Layout, at, outward ? -way : way, found.placed.facing, taken);
found.placed.travel = way;
break;
}
@@ -1143,7 +1200,7 @@ std::vector<signal> signals(layout const &Layout, std::vector<numbered> const &T
found.placed.track = last;
found.placed.point = at;
found.placed.facing = outward ? -1 : 1;
found.placed.side = side_for(Layout, at, outward ? way : -way, found.placed.facing);
found.placed.side = side_for(Layout, at, outward ? way : -way, found.placed.facing, taken);
found.placed.travel = -way;
}
if (found.placed.track != nullptr)
@@ -1226,13 +1283,14 @@ std::vector<signal> signals(layout const &Layout, std::vector<numbered> const &T
if (end.track == nullptr || end.free)
continue;
signal placed;
auto const [at, way] = in_from(*end.track, end.atend, std::min(kJoint, plan_length(*end.track, 0) * 0.5));
placed.track = end.track;
auto const exit{stopping && (start ? starts : ends)};
auto const [track, atend, at, way] = back_from(end.track, end.atend, exit ? signal_drop : kJoint);
placed.track = track;
placed.point = at;
placed.facing = end.atend ? 1 : -1;
placed.side = side_for(Layout, at, end.atend ? way : -way, placed.facing);
placed.facing = atend ? 1 : -1;
placed.side = side_for(Layout, at, atend ? way : -way, placed.facing, taken);
placed.travel = way;
if (stopping && (start ? starts : ends))
if (exit)
{
placed.kind = signal::role::exit;
placed.name = std::string(1, start ? (odd ? odd_start : even_start) : (odd ? odd_end : even_end)) + upper;
@@ -1264,36 +1322,61 @@ std::vector<line> lines_out(layout const &Layout, std::vector<signal> const &Sig
continue;
line found;
found.entry = index;
std::unordered_set<TTrack const *> seen;
auto const add = [&](TTrack *Track, bool const Outward, std::vector<glm::dvec3> Points) {
if (false == found.points.empty())
Points.erase(Points.begin());
seen.insert(Track);
found.tracks.push_back(Track);
found.outward.push_back(Outward);
for (auto const &point : Points)
{
found.distances.push_back(found.points.empty() ? 0.0 : found.distances.back() + plan_distance(found.points.back(), point));
found.points.push_back(point);
found.owners.push_back(found.tracks.size() - 1);
}
};
// in from the signal to the joint of the station first: its trains run on there beyond it
for (auto const &[part, out] : entry.run)
{
if (part == entry.track)
break;
double length{0.0};
auto points{dense_points(*part, 0, length)};
if (false == out)
std::reverse(points.begin(), points.end());
add(part, out, std::move(points));
}
// out where the trains it faces come from
auto *track{entry.track};
auto outward{entry.facing < 0};
auto first{true};
std::unordered_set<TTrack const *> seen;
auto origin{0.0};
while (true)
{
double length{0.0};
auto points{dense_points(*track, 0, length)};
if (false == outward)
std::reverse(points.begin(), points.end());
std::size_t at{0};
if (first)
{
auto const nearest{std::min_element(points.begin(), points.end(), [&](glm::dvec3 const &A, glm::dvec3 const &B) { return plan_distance(A, entry.point) < plan_distance(B, entry.point); })};
points.erase(points.begin(), nearest);
points.front() = entry.point;
}
else
points.erase(points.begin());
seen.insert(track);
found.tracks.push_back(track);
found.outward.push_back(outward);
for (auto const &point : points)
{
found.distances.push_back(found.points.empty() ? 0.0 : found.distances.back() + plan_distance(found.points.back(), point));
found.points.push_back(point);
found.owners.push_back(found.tracks.size() - 1);
*nearest = entry.point;
at = static_cast<std::size_t>(nearest - points.begin());
if (found.points.empty())
{
points.erase(points.begin(), nearest);
at = 0;
}
else
at += found.points.size() - 1;
}
add(track, outward, std::move(points));
if (first)
origin = found.distances[at];
first = false;
if (found.length() > Limit)
if (found.length() - origin > Limit)
break;
auto const end{end_of(*track, 0, outward)};
auto *next{neighbour(*track, 0, outward)};
@@ -1311,6 +1394,8 @@ std::vector<line> lines_out(layout const &Layout, std::vector<signal> const &Sig
track = next;
outward = false == atend;
}
for (auto &distance : found.distances)
distance -= origin;
if (found.length() > 1.0)
result.push_back(std::move(found));
}
@@ -1359,6 +1444,29 @@ std::vector<line> lines_out(layout const &Layout, std::vector<signal> const &Sig
return result;
}
line run_line(signal const &Entry)
{
line result;
for (auto const &[track, outward] : Entry.run)
{
double length{0.0};
auto points{dense_points(*track, 0, length)};
if (false == outward)
std::reverse(points.begin(), points.end());
if (false == result.points.empty())
points.erase(points.begin());
result.tracks.push_back(track);
result.outward.push_back(outward);
for (auto const &point : points)
{
result.distances.push_back(result.points.empty() ? 0.0 : result.distances.back() + plan_distance(result.points.back(), point));
result.points.push_back(point);
result.owners.push_back(result.tracks.size() - 1);
}
}
return result;
}
on_line point_on(line const &Line, double const Distance)
{
on_line result;
@@ -1407,12 +1515,12 @@ double distance_on(line const &Line, glm::dvec3 const &Point, double &Gap)
return result;
}
std::string block_number(double const Km, int const Track)
std::string block_number(double const Km, int const Track, bool const Rising)
{
auto const hectometres{std::max(Km, 0.0) * 10.0};
auto number{static_cast<long long>(std::llround(hectometres))};
auto number{static_cast<long long>(Rising ? std::ceil(hectometres - 1e-6) : std::floor(hectometres + 1e-6))};
if ((number % 2 == 1) != (Track % 2 == 1))
number += hectometres > static_cast<double>(number) ? 1 : -1;
number += Rising ? 1 : -1;
if (number < 1)
number = Track % 2 == 1 ? 1 : 2;
return std::to_string(number);

View File

@@ -116,6 +116,9 @@ struct numbered
};
// a signal of the station, as PKP PLK places and names them
// m, from a main signal on to the joint its trains turn it to stop at: Ie-4 § 9, at least 15 m, at most 30 for an entry or a block signal
double constexpr signal_drop{15.0};
struct signal
{
enum class role
@@ -158,7 +161,7 @@ struct line
std::vector<TTrack *> tracks; // from the entry signal out
std::vector<bool> outward; // of each track: the line runs out towards the end of its path
std::vector<glm::dvec3> points; // along the tracks from the entry signal on, a metre or so apart
std::vector<double> distances; // of the points, from the entry signal
std::vector<double> distances; // of the points, from the entry signal: below 0 in from it to the joint of the station
std::vector<std::size_t> owners; // of the points, the track each lies on
double length() const { return distances.empty() ? 0.0 : distances.back(); }
};
@@ -174,11 +177,13 @@ struct on_line
// the lines beyond the entry signals among the signals given, no longer than the limit; two lines out of the same end side by side
// are the tracks of a double-track line
std::vector<line> lines_out(layout const &Layout, std::vector<signal> const &Signals, double const Limit);
// the run of an entry signal as a line, from the joint of the station out, the distances from the joint
line run_line(signal const &Entry);
on_line point_on(line const &Line, double const Distance);
// how far out along the line the point lies, Gap: how far from its axis in the plan
double distance_on(line const &Line, glm::dvec3 const &Point, double &Gap);
// the number of a block signal at the km, as PKP PLK gives them: the km and the hectometre, the last digit odd on track 1, even on
// track 2: 241, 242
std::string block_number(double const Km, int const Track);
// the number of a block signal at the km, as PKP PLK gives them (Ie-4 § 10): the km and the hectometre, the nearest one ahead of the
// signal odd on track 1, even on track 2: 241, 242; Rising: its trains run towards the higher km
std::string block_number(double const Km, int const Track, bool const Rising);
} // namespace station