17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 18:14:55 +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

@@ -48,11 +48,16 @@ double constexpr kLabelRange{700.0}; // m, of the names shown there, farther one
double constexpr kRailHead{0.18}; // m, above the axis of a path
double constexpr kLineLimit{30000.0}; // m, of a line followed out to the next station
double constexpr kBlock{1400.0}; // m, of a block section of the automatic block
double constexpr kBlockShare{1.1}; // of the braking distance, the block sections towards the end of the tracks
double constexpr kPredicted{305.0}; // m, out from the next station to its entry signal, while it has none
double constexpr kWarning{1000.0}; // m, from a warning disc to its entry signal, the braking distance
double constexpr kWarningFast{1300.0}; // m, the same on lines faster than 120 km/h
char const *const kControl{"sbl_odstep.inc"}; // the events of a block section, switching the block signal guarding it
char const *const kControl{"sbl_odstep.inc"};
// m, of a train at the speed, as Ie-4 § 8 takes it: the least block section of the automatic block, a warning disc that far before
// its signal
double braking_distance(double const Speed)
{
return Speed <= 60.0 ? 400.0 : Speed <= 100.0 ? 700.0 : Speed <= 140.0 ? 1000.0 : 1300.0;
} // the events of a block section, switching the block signal guarding it
ImU32 constexpr kPlain{IM_COL32(170, 176, 188, 255)};
ImU32 constexpr kSwitch{IM_COL32(240, 200, 90, 255)};
@@ -546,27 +551,29 @@ void srk_tool::render_options()
step_heading(2, STR_C("Track circuits"));
if (main_button(STR_C("Set the track circuits"), ImVec2(-1.0f, tall_button_height())))
set_circuits();
item_tooltip("Cuts the tracks of the station at the joints of the track circuits and names the circuits, as on the lines of PKP PLK: "
"a joint 1.5 m before the start of a switch, beyond the fouling point of each branch where the axes are 4 m apart, in the middle "
"of a link too short for both. Each switch gets bpd_iz<number>, each station track bpd_it<number>: 1 and 2 the main tracks, "
"track 1 on the right along the km, the odd ones beyond it, the even ones on the other side. Run again after changes, it keeps "
"the numbers the tracks have. One step of undo");
item_tooltip("Cuts the tracks of the station at the joints of the track circuits and names the circuits, as on the lines of PKP PLK: a "
"joint 1.5 m before the start of a switch, 4.2 m beyond the fouling point of each branch, where the axes are 3.5 m apart, as "
"Ie-4 gives it, in the middle of a link too short for both. Each switch gets bpd_iz<number>, each station track "
"bpd_it<number>: 1 and 2 the main tracks, track 1 on the right along the km, the odd ones beyond it, the even ones on the "
"other side. Run again after changes, it keeps the numbers the tracks have. One step of undo");
step_heading(3, STR_C("Signals"));
if (ImGui::Button(STR_C("Set the signals"), ImVec2(-1.0f, 0.0f)))
set_signals();
item_tooltip("Places the signals of the station, as on the lines of PKP PLK, and names them after its code: an entry signal 300 m out on "
"each line, A, B... from the start of the km, Z, Y... from the end, the circuit from it to the station bpd_ita; an exit "
"signal at the end of each station track a train is sent out of, F1, G2 towards the start, R1, P2 towards the end; a "
"shunting signal Tm1, Tm2... at the end of a shorter track. A signal standing by the place of one gets its name, none is "
"doubled. The kinds of signals chosen in the Signals tool. One step of undo");
"each line, A, B... from the start of the km, Z, Y... from the end, the circuit from 15 m beyond it to the station bpd_ita; "
"an exit signal 15 m before the joint at the end of each station track a train is sent out of, F1, G2 towards the start, R1, "
"P2 towards the end; a shunting signal Tm1, Tm2... at the end of a shorter track. A signal standing by the place of one gets "
"its name, none is doubled. The kinds of signals chosen in the Signals tool. One step of undo");
step_heading(4, STR_C("Lines"));
if (ImGui::Button(STR_C("Set the line signals"), ImVec2(-1.0f, 0.0f)))
set_lines();
item_tooltip("Places the signals of the lines out of the station, from its entry signals on. A double-track line gets the automatic "
"block: block signals evenly to the entry signals of the next station, about 1.4 km apart, numbered after the km, 241 on "
"track 1, 242 on track 2, and the block sections between them named as on the lines of PKP PLK, 2411-2571@LK2.1. A "
"single-track line gets a warning disc ToA 1000 m before the entry signal, 1300 m on a line faster than 120 km/h. Needs "
"the line and the km of the station. Run it at the next station too, it keeps what stands. One step of undo");
item_tooltip("Places the signals of the lines out of the station, from its entry signals on. A double-track line gets the automatic block: "
"block signals evenly to the entry signals of the next station, a section no shorter than the braking distance, numbered "
"after the hectometre ahead of them, odd on track 1, even on track 2, and the block sections from 15 m beyond them named as "
"on the lines of PKP PLK, 2411-2571@LK2.1. A single-track line gets a warning disc ToA the braking distance before the entry "
"signal. The braking distance after the speed of the line, as Ie-4 gives it: 400 m up to 60 km/h, 700 m up to 100, 1000 m up "
"to 140, 1300 m above. Needs the line and the km of the station. Run it at the next station too, it keeps what stands. One "
"step of undo");
if (auto const *selected{m_selection.selected_track()}; selected != nullptr && false == selected->Isolated.empty())
if (auto const number{track_of(selected->Isolated.front()->asName, stem_of_open())}; false == number.empty() && begin_fields("##srktrack"))
{
@@ -1007,6 +1014,7 @@ void srk_tool::set_signals()
glm::dvec3 point{0.0};
bool outward{false};
std::vector<TTrack *> named; // between the station and the cut
std::vector<TTrack *> beyond; // out from the cut to the signal and on
};
std::vector<cut> cuts;
for (std::size_t i = 0; i < plan.size(); ++i)
@@ -1014,7 +1022,11 @@ void srk_tool::set_signals()
auto const &planned{plan[i]};
if (planned.kind != station::signal::role::entry || planned.run.empty())
continue;
auto const at{match[i] >= 0 ? standing[match[i]].position : planned.point};
// the joint the trains pass the signal to, in from it towards the station
auto const run{station::run_line(planned)};
double gap{0.0};
auto const signal_at{station::distance_on(run, match[i] >= 0 ? standing[match[i]].position : planned.point, gap)};
auto const at{station::point_on(run, std::max(0.0, signal_at - station::signal_drop)).point};
cut entry;
entry.signal = i;
auto best{std::numeric_limits<double>::max()};
@@ -1033,6 +1045,8 @@ void srk_tool::set_signals()
}
for (std::size_t k = 0; k < nearest; ++k)
entry.named.push_back(planned.run[k].first);
for (std::size_t k = nearest + 1; k < planned.run.size(); ++k)
entry.beyond.push_back(planned.run[k].first);
entry.track = planned.run[nearest].first;
entry.outward = planned.run[nearest].second;
auto const &ends{entry.track->m_paths.front().points};
@@ -1042,8 +1056,7 @@ void srk_tool::set_signals()
if (std::min(to_start, to_end) <= 0.3)
{
// at an end already
if ((to_end < to_start) == entry.outward)
entry.named.push_back(entry.track);
((to_end < to_start) == entry.outward ? entry.named : entry.beyond).push_back(entry.track);
entry.track = nullptr;
}
else if (false == editor_track::can_edit_geometry(*entry.track, reason))
@@ -1074,8 +1087,9 @@ void srk_tool::set_signals()
created.push_back(piece);
captured.insert(piece);
entry.named.push_back(entry.outward ? entry.track : piece);
planned.track = entry.outward ? piece : entry.track;
planned.point = entry.point;
entry.beyond.push_back(entry.outward ? piece : entry.track);
if (planned.track == entry.track)
planned.track = entry.outward ? piece : entry.track;
}
}
auto const name{stem + "it" + ToLower(planned.name)};
@@ -1087,6 +1101,15 @@ void srk_tool::set_signals()
before.emplace_back(part, editor_track::capture(*part));
editor_track::isolated(*part, {name});
}
// out of the circuit beyond its joint, where an earlier one ended at the signal
for (auto *part : entry.beyond)
{
if (part->Isolated.size() != 1 || ToLower(part->Isolated.front()->asName) != name)
continue;
if (captured.insert(part).second)
before.emplace_back(part, editor_track::capture(*part));
editor_track::isolated(*part, {});
}
}
std::vector<signal_order> placed;
std::vector<signal_rename> renamed;
@@ -1165,12 +1188,21 @@ void srk_tool::set_lines()
m_status = STR_C("Set the signals first: the lines begin at the entry signals");
return;
}
auto const lines{station::lines_out(layout, entries, kLineLimit)};
auto lines{station::lines_out(layout, entries, kLineLimit)};
if (lines.empty())
{
m_status = STR_C("No line leads out of the station beyond its entry signals");
return;
}
// the distances from the entry signals as they stand
for (auto &line : lines)
{
double gap{0.0};
auto const at{station::distance_on(line, standing[entry_standing[line.entry]].position, gap)};
if (gap < 4.5)
for (auto &distance : line.distances)
distance -= at;
}
auto const &described{m_records[m_record]};
double known{0.0};
if (std::any_of(lines.begin(), lines.end(), [](station::line const &Line) { return Line.number > 0; }) && (false == km_of_open(0.0, known) || described.line.empty()))
@@ -1201,11 +1233,22 @@ void srk_tool::set_lines()
return glm::dot(travel_of(Signal.yaw), glm::dvec2{way.x, way.z});
};
auto const km_on = [&](std::size_t const Index, double const Distance) {
auto const chainage{station::project(layout, lines[Index].points.front()).x};
auto const chainage{station::project(layout, station::point_on(lines[Index], 0.0).point).x};
double km{0.0};
km_of_open(chainage < middle ? chainage - Distance : chainage + Distance, km);
return km;
};
// the trains of the line run towards the higher km
auto const rising = [&](std::size_t const Index) { return (km_on(Index, 1000.0) > km_on(Index, 0.0)) != lines[Index].inbound; };
// the braking distance at the highest speed out along the line, at 160 km/h while its tracks have none
auto const braking = [&](std::size_t const Index) {
auto speed{0.0};
auto const &line{lines[Index]};
for (std::size_t k = 0; k < line.points.size(); ++k)
if (line.distances[k] >= 0.0)
speed = std::max(speed, line.tracks[line.owners[k]]->VelocityGet());
return braking_distance(speed > 0.0 ? speed : 160.0);
};
for (std::size_t index = 0; index < lines.size(); ++index)
{
auto const &line{lines[index]};
@@ -1233,26 +1276,28 @@ void srk_tool::set_lines()
auto const &line{lines[index]};
auto const &end{ends[index]};
auto const &entry{entries[line.entry]};
auto const least{braking(index)};
if (line.number > 0)
{
// the block signals: the line to the next station parted evenly, towards the end of the tracks every 1.4 km
// the block signals, Ie-4 § 51: the sections no shorter than the braking distance and no longer than twice it, the line to the
// next station parted evenly, towards the end of the tracks a little over the braking distance each
std::vector<double> places;
if (line.station || end.signal >= 0)
{
// as many on both tracks of the line
auto const reach{line.partner >= 0 ? (end.distance + ends[line.partner].distance) * 0.5 : end.distance};
auto const sections{std::max(1L, std::lround(reach / kBlock))};
for (long k = 1; k < sections; ++k)
places.push_back(end.distance * static_cast<double>(k) / static_cast<double>(sections));
auto const sections{std::max(1.0, std::floor(reach / (line.partner >= 0 ? std::max(least, braking(line.partner)) : least)))};
for (auto k{1.0}; k < sections; k += 1.0)
places.push_back(end.distance * k / sections);
}
else
for (auto distance{kBlock}; distance <= end.distance - 100.0; distance += kBlock)
for (auto distance{least * kBlockShare}; distance <= end.distance - 100.0; distance += least * kBlockShare)
places.push_back(distance);
for (auto const distance : places)
{
planned_signal signal;
signal.kind = station::signal::role::block;
signal.name = station::block_number(km_on(index, distance), line.number);
signal.name = station::block_number(km_on(index, distance), line.number, rising(index));
signal.plate = signal.name;
signal.line = index;
signal.distance = distance;
@@ -1262,8 +1307,7 @@ void srk_tool::set_lines()
else
{
// the warning disc of the entry signal, the braking distance before it
auto const fast{entry.track != nullptr && entry.track->VelocityGet() > 120.0};
auto const distance{std::min(fast ? kWarningFast : kWarning, end.distance - 100.0)};
auto const distance{std::min(least, end.distance - 100.0)};
if (distance >= 400.0)
{
planned_signal signal;
@@ -1366,6 +1410,12 @@ void srk_tool::set_lines()
std::sort(order.begin(), order.end(), [](stop const &A, stop const &B) { return A.distance < B.distance; });
if (line.inbound)
std::reverse(order.begin(), order.end());
// the joints the trains pass the signals to: on beyond a block signal, in towards the station from an entry one
auto const joint_of = [&](stop const &Stop) {
if (Stop.signal >= 0)
return Stop.distance + (line.inbound ? -station::signal_drop : station::signal_drop);
return Stop.distance + (Stop.distance > 0.0 ? station::signal_drop : -station::signal_drop);
};
for (std::size_t i = 0; i < order.size(); ++i)
{
auto const &here{order[i]};
@@ -1385,8 +1435,8 @@ void srk_tool::set_lines()
section found;
found.name = ToLower(section_tag(low.name, line.number) + "-" + section_tag(high.name, line.number) + "@lk" + described.line + "." + std::to_string(line.number));
found.line = index;
found.from = std::min(before.distance, here.distance);
found.to = std::max(before.distance, here.distance);
found.from = std::min(joint_of(before), joint_of(here));
found.to = std::max(joint_of(before), joint_of(here));
found.guard = before.signal >= 0 ? before.name : std::string{};
found.start = station::point_on(line, before.distance).point;
sections.push_back(std::move(found));
@@ -2047,7 +2097,13 @@ editor_selftest_result srk_tool::selftest(std::string const &Command, std::istri
refresh();
std::string list;
for (auto const &mark : m_signal_marks)
list += format(" %s@%.0f/%.1f%s", mark.text.c_str(), mark.at.x, mark.at.y, mark.ahead ? ">" : "<");
{
// and the joint nearest to it, in the plan
auto nearest{std::numeric_limits<double>::max()};
for (auto const &joint : m_joints)
nearest = std::min(nearest, glm::distance(glm::dvec2{joint.at.x, joint.at.z}, glm::dvec2{mark.head.x, mark.head.z}));
list += format(" %s@%.0f/%.1f%s~%.1f", mark.text.c_str(), mark.at.x, mark.at.y, mark.ahead ? ">" : "<", nearest);
}
WriteLog(format("SELFTEST srksignals %zu:", m_signal_marks.size()) + list);
return editor_selftest_result::handled;
}
@@ -2080,7 +2136,7 @@ editor_selftest_result srk_tool::selftest(std::string const &Command, std::istri
along.emplace_back(distance, format("%s(%s,%s)", signal.name.c_str(), signal.kind.c_str(), signal.linked.c_str()));
}
std::string last;
for (std::size_t i = 1; i < line.points.size(); i += 20)
for (std::size_t i = 1; i < line.points.size(); ++i)
{
auto const *track{line.tracks[line.owners[i]]};
auto const name{track->Isolated.empty() ? std::string{"-"} : track->Isolated.front()->asName};

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

View File

@@ -10413,8 +10413,8 @@ msgstr[2] "Ponumerowano %zu rozjazdów"
msgid "Set the track circuits"
msgstr "Ustaw izolacje"
msgid "Cuts the tracks of the station at the joints of the track circuits and names the circuits, as on the lines of PKP PLK: a joint 1.5 m before the start of a switch, beyond the fouling point of each branch where the axes are 4 m apart, in the middle of a link too short for both. Each switch gets bpd_iz<number>, each station track bpd_it<number>: 1 and 2 the main tracks, track 1 on the right along the km, the odd ones beyond it, the even ones on the other side. Run again after changes, it keeps the numbers the tracks have. One step of undo"
msgstr "Tnie tory stacji na stykach izolowanych i nazywa odcinki jak na liniach PKP PLK: styk 1,5 m przed początkiem rozjazdu, za ukresem każdej gałęzi tam, gdzie osie są 4 m od siebie, w połowie łącznika za krótkiego na oba. Każdy rozjazd dostaje bpd_iz<numer>, każdy tor stacyjny bpd_it<numer>: 1 i 2 to tory główne, tor 1 po prawej w stronę rosnącego km, nieparzyste za nim, parzyste po drugiej stronie. Kliknięte ponownie po zmianach zostawia numery, które tory już mają. Jeden krok cofania"
msgid "Cuts the tracks of the station at the joints of the track circuits and names the circuits, as on the lines of PKP PLK: a joint 1.5 m before the start of a switch, 4.2 m beyond the fouling point of each branch, where the axes are 3.5 m apart, as Ie-4 gives it, in the middle of a link too short for both. Each switch gets bpd_iz<number>, each station track bpd_it<number>: 1 and 2 the main tracks, track 1 on the right along the km, the odd ones beyond it, the even ones on the other side. Run again after changes, it keeps the numbers the tracks have. One step of undo"
msgstr "Tnie tory stacji na stykach izolowanych i nazywa odcinki jak na liniach PKP PLK: styk 1,5 m przed początkiem rozjazdu, 4,2 m za ukresem każdej gałęzi, tam gdzie osie są 3,5 m od siebie, jak w Ie-4, w połowie łącznika za krótkiego na oba. Każdy rozjazd dostaje bpd_iz<numer>, każdy tor stacyjny bpd_it<numer>: 1 i 2 to tory główne, tor 1 po prawej w stronę rosnącego km, nieparzyste za nim, parzyste po drugiej stronie. Kliknięte ponownie po zmianach zostawia numery, które tory już mają. Jeden krok cofania"
msgid "Give the station its code first: the track circuits are named after it"
msgstr "Najpierw wpisz skrót stacji: od niego biorą się nazwy odcinków izolowanych"
@@ -10458,8 +10458,8 @@ msgstr "Tor %s ma teraz numer %s"
msgid "Set the signals"
msgstr "Ustaw semafory"
msgid "Places the signals of the station, as on the lines of PKP PLK, and names them after its code: an entry signal 300 m out on each line, A, B... from the start of the km, Z, Y... from the end, the circuit from it to the station bpd_ita; an exit signal at the end of each station track a train is sent out of, F1, G2 towards the start, R1, P2 towards the end; a shunting signal Tm1, Tm2... at the end of a shorter track. A signal standing by the place of one gets its name, none is doubled. The kinds of signals chosen in the Signals tool. One step of undo"
msgstr "Stawia semafory stacji jak na liniach PKP PLK i nazywa je od jej skrótu: wjazdowy 300 m przed stacją na każdej linii, A, B... od początku km, Z, Y... od końca, a odcinek od niego do stacji to bpd_ita; wyjazdowy na końcu każdego toru, z którego wyprawia się pociągi, F1, G2 w stronę początku, R1, P2 w stronę końca; tarcza manewrowa Tm1, Tm2... na końcu krótszego toru. Semafor, który już stoi w miejscu któregoś z nich, dostaje tylko jego nazwę, nic się nie dubluje. Modele semaforów te wybrane w narzędziu Sygnalizatory. Jeden krok cofania"
msgid "Places the signals of the station, as on the lines of PKP PLK, and names them after its code: an entry signal 300 m out on each line, A, B... from the start of the km, Z, Y... from the end, the circuit from 15 m beyond it to the station bpd_ita; an exit signal 15 m before the joint at the end of each station track a train is sent out of, F1, G2 towards the start, R1, P2 towards the end; a shunting signal Tm1, Tm2... at the end of a shorter track. A signal standing by the place of one gets its name, none is doubled. The kinds of signals chosen in the Signals tool. One step of undo"
msgstr "Stawia semafory stacji jak na liniach PKP PLK i nazywa je od jej skrótu: wjazdowy 300 m przed stacją na każdej linii, A, B... od początku km, Z, Y... od końca, a odcinek od 15 m za nim do stacji to bpd_ita; wyjazdowy 15 m przed stykiem na końcu każdego toru, z którego wyprawia się pociągi, F1, G2 w stronę początku, R1, P2 w stronę końca; tarcza manewrowa Tm1, Tm2... na końcu krótszego toru. Semafor, który już stoi w miejscu któregoś z nich, dostaje tylko jego nazwę, nic się nie dubluje. Modele semaforów te wybrane w narzędziu Sygnalizatory. Jeden krok cofania"
msgid "Give the station its code first: the signals are named after it"
msgstr "Najpierw wpisz skrót stacji: od niego biorą się nazwy semaforów"
@@ -10494,8 +10494,8 @@ msgstr "Szlaki"
msgid "Set the line signals"
msgstr "Ustaw sygnalizatory szlakowe"
msgid "Places the signals of the lines out of the station, from its entry signals on. A double-track line gets the automatic block: block signals evenly to the entry signals of the next station, about 1.4 km apart, numbered after the km, 241 on track 1, 242 on track 2, and the block sections between them named as on the lines of PKP PLK, 2411-2571@LK2.1. A single-track line gets a warning disc ToA 1000 m before the entry signal, 1300 m on a line faster than 120 km/h. Needs the line and the km of the station. Run it at the next station too, it keeps what stands. One step of undo"
msgstr "Stawia sygnalizatory szlaków wychodzących ze stacji, od jej semaforów wjazdowych. Szlak dwutorowy dostaje samoczynną blokadę liniową: semafory SBL rozstawione równo do semaforów wjazdowych następnej stacji, co około 1,4 km, numerowane według kilometra, 241 na torze 1, 242 na torze 2, a odstępy między nimi nazwane jak na liniach PKP PLK, 2411-2571@LK2.1. Szlak jednotorowy dostaje tarczę ostrzegawczą ToA 1000 m przed semaforem wjazdowym, 1300 m na linii szybszej niż 120 km/h. Potrzebna linia i kilometr stacji. Uruchom to też na następnej stacji, zostawia to, co już stoi. Jeden krok cofania"
msgid "Places the signals of the lines out of the station, from its entry signals on. A double-track line gets the automatic block: block signals evenly to the entry signals of the next station, a section no shorter than the braking distance, numbered after the hectometre ahead of them, odd on track 1, even on track 2, and the block sections from 15 m beyond them named as on the lines of PKP PLK, 2411-2571@LK2.1. A single-track line gets a warning disc ToA the braking distance before the entry signal. The braking distance after the speed of the line, as Ie-4 gives it: 400 m up to 60 km/h, 700 m up to 100, 1000 m up to 140, 1300 m above. Needs the line and the km of the station. Run it at the next station too, it keeps what stands. One step of undo"
msgstr "Stawia sygnalizatory szlaków wychodzących ze stacji, od jej semaforów wjazdowych. Szlak dwutorowy dostaje samoczynną blokadę liniową: semafory SBL rozstawione równo do semaforów wjazdowych następnej stacji, odstęp nie krótszy od drogi hamowania, numerowane według najbliższego hektometra przed nimi, nieparzyste na torze 1, parzyste na torze 2, a odstępy od 15 m za nimi nazwane jak na liniach PKP PLK, 2411-2571@LK2.1. Szlak jednotorowy dostaje tarczę ostrzegawczą ToA w odległości drogi hamowania przed semaforem wjazdowym. Droga hamowania według prędkości szlaku, jak w Ie-4: 400 m do 60 km/h, 700 m do 100, 1000 m do 140, 1300 m powyżej. Potrzebna linia i kilometr stacji. Uruchom to też na następnej stacji, zostawia to, co już stoi. Jeden krok cofania"
msgid "Km"
msgstr "Kilometr"