17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-28 02:57:28 +01:00

feat(editor): the top view labels each curve once and each row of straights in line once, not each of their paths: a curve, from its first transition to its last, with the radius of its arc, of each arc of a compound one, its transitions and its cant (each arc's where they differ), as the plan reads it and as the laying shows it, in the middle of it; a straight row with its whole length. A piece of a transition curve no longer shows the radius of the arc as its own. The tutorial clips recorded again with them, in the top view: laying, the curves with a compound one traced (R 300/150) after the bend too sharp for the speed, a track selected with no gizmo, its control vector dragged and taken back, and the switches: one put into a track, a crossover between two tracks and a switch ladder laying its fan; the lessons of the curves and of the switches tell of the compound curve, the crossovers and the ladder

This commit is contained in:
maj00r
2026-10-25 11:35:25 +01:00
parent 29b9b93353
commit cb766dabb4
7 changed files with 127 additions and 18 deletions

View File

@@ -5231,7 +5231,7 @@ void editor_mode::render_first_steps()
{"Curves",
"curves",
{{"LMB", "Click the bends of the line: each gets a curve as large as fits; one too sharp for the speed set in the panel has its label in orange"},
{"Tab", "A curve seen on the photo: click points along its middle, the last two on the straight after it"},
{"Tab", "A curve seen on the photo: click points along its middle, the last two on the straight after it; one of two or three radii comes out a compound curve"},
{"Alt", "Held: the direction turns in steps of 15 degrees, or to 1:9, 1:6.6... to a straight track it starts by or crosses"},
{"0 1 2 3", "Type the length of the next straight, Enter puts the point"},
{"wheel", "Alone it zooms, as everywhere"}},
@@ -5258,9 +5258,10 @@ void editor_mode::render_first_steps()
{"Switches",
"switch",
{{"T", "The switch tool; at the top: what to place"},
{"LMB", "Click a track: the switch goes into it"},
{"LMB + move", "Press on it and drag: along the direction, sideways the side"},
{"LMB + move", "Connection: drag from a straight onto the other track"},
{"LMB", "Click a track: the one the switch goes into"},
{"LMB + move", "Press on it and drag: along the direction, sideways the side; let go: the switch is laid"},
{"LMB + move", "Connection: drag from a straight onto the other track; a crossover, a double one or scissors, as chosen at the top"},
{"LMB + move", "Switch ladder: drag from the straight to the side the tracks go off to; the ones missing are laid with it"},
{"Esc", "Back to selecting"}},
{{first_step::turnout, "Put a switch into a track"}},
trial::turnout},

View File

@@ -33,6 +33,7 @@ http://mozilla.org/MPL/2.0/.
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <deque>
#include <array>
#include <functional>
#include <cctype>
@@ -298,6 +299,67 @@ void editor_mode::draw_track_labels() const
Row = static_cast<int>((Y - low.y) / cell);
return Column >= 0 && Row >= 0 && Column < columns && Row < rows;
};
// one label for a curve, from its first transition curve to its last, and one for a row of straights in line, not one for each
// of their paths: the paths of a curve are pieces of it, a piece of a transition curve has no radius of its own to name
std::unordered_set<TTrack const *> labelled;
auto const plain = [&](TTrack const *Other) { return Other != nullptr && Other->eType == tt_Normal && rail(*Other) && false == Other->m_paths.empty(); };
auto const heading = [](TTrack const &Other) { return plan_of(Other.m_paths.front().points[segment_data::point::end] - Other.m_paths.front().points[segment_data::point::start]); };
auto const own_turn = [](TTrack const &Other, bool const Forward) {
bezier const curve{Other.m_paths.front()};
auto const begin{plan_of(curve.first(0.0))};
auto const finish{plan_of(curve.first(1.0))};
if (glm::length(begin) < 1e-9 || glm::length(finish) < 1e-9)
return 0.0;
auto const turn{geometry::signed_angle(glm::normalize(begin), glm::normalize(finish))};
return Forward ? turn : -turn;
};
// the paths joined in a row with the one given, both ways, as long as each next one belongs with it; each with the flag of being
// gone along from its start
auto const row = [&](TTrack &Track, std::function<bool(TTrack const &, bool)> const &Belongs) {
std::deque<std::pair<TTrack *, bool>> run{{&Track, true}};
auto const known = [&](TTrack const *Other) { return std::any_of(run.begin(), run.end(), [&](auto const &Member) { return Member.first == Other; }); };
for (auto const throughend : {true, false})
{
TTrack *previous{&Track};
TTrack *current{throughend ? Track.trNext : Track.trPrev};
while (plain(current) && false == known(current) && false == labelled.contains(current))
{
auto const entersatstart{current->trPrev == previous};
if (false == entersatstart && current->trNext != previous)
break;
if (false == Belongs(*current, throughend == entersatstart))
break;
if (throughend)
run.emplace_back(current, entersatstart);
else
run.emplace_front(current, false == entersatstart);
previous = current;
current = entersatstart ? current->trNext : current->trPrev;
}
}
return std::vector<std::pair<TTrack *, bool>>{run.begin(), run.end()};
};
// the curve as the plan reads it: the radius of the arc, of each arc of a compound one, the transition curves and the cant
auto const curve_text = [](editor_track::curve const &Curve) {
if (Curve.designed.has_value() && Curve.designed->spirals)
return format("2xL %.0f R %.0f", Curve.transition_in, Curve.radius);
std::string text{"R "};
if (Curve.compound && Curve.arcs.size() >= 2)
for (std::size_t i = 0; i < Curve.arcs.size(); ++i)
text += format(i == 0 ? "%.0f" : "/%.0f", Curve.arcs[i].radius);
else
text += format("%.0f", Curve.radius);
if (Curve.transition_in >= 0.5 || Curve.transition_out >= 0.5)
text += std::abs(Curve.transition_in - Curve.transition_out) < 0.5 ? format(" L %.0f", Curve.transition_in) : format(" L %.0f/%.0f", Curve.transition_in, Curve.transition_out);
// the arcs of a compound curve kept with its design have each a cant of their own: all shown where they aren't the same
auto const cants{Curve.designed.has_value() && Curve.compound ? alignment::compound_cants(*Curve.designed) : std::vector<double>{}};
if (std::any_of(cants.begin(), cants.end(), [&](double const Cant) { return std::abs(Cant - cants.front()) >= 0.5; }))
for (std::size_t i = 0; i < cants.size(); ++i)
text += format(i == 0 ? " h %.0f" : "/%.0f", cants[i]);
else if (Curve.cant >= 0.5)
text += format(" h %.0f", Curve.cant);
return text;
};
for (auto *track : simulation::Paths.sequence())
{
if (track == nullptr || false == rail(*track) || track->m_paths.empty())
@@ -342,14 +404,57 @@ void editor_mode::draw_track_labels() const
labels.push_back({0, 0.0, first.point(0.0), text, IM_COL32(255, 220, 120, 255)});
continue;
}
auto const length{first.plan_length()};
if (labelled.contains(track) || false == plain(track))
continue;
auto const straight{editor_track::is_straight(*track, m_straights.tolerance)};
std::vector<std::pair<TTrack *, bool>> run;
if (straight)
{
// the straights in line: the next one parallel to this one, a bend between them is the end of the row
auto const along{heading(*track)};
run = row(*track, [&](TTrack const &Other, bool) {
auto const other{heading(Other)};
return editor_track::is_straight(Other, m_straights.tolerance) && glm::length(along) > 1e-6 && glm::length(other) > 1e-6 &&
std::abs(geometry::cross(glm::normalize(along), glm::normalize(other))) < 0.002;
});
}
else
{
// the pieces of a curve turning the same way: one turning the other way starts a curve of its own
auto const side{own_turn(*track, true)};
run = row(*track, [&](TTrack const &Other, bool const Forward) { return false == editor_track::is_straight(Other, m_straights.tolerance) && own_turn(Other, Forward) * side >= 0.0; });
}
double length{0.0};
for (auto const &member : run)
{
labelled.insert(member.first);
length += bezier{member.first->m_paths.front()}.plan_length();
}
if (length < 0.5)
continue;
auto const radius{::track_mode::piece_plan_radius(track->m_paths.front())};
if (radius >= 0.5)
labels.push_back({1, radius, first.point(0.5), format("R %.0f L %.1f", radius, length), IM_COL32(235, 235, 235, 240)});
else
labels.push_back({2, -length, first.point(0.5), format("L %.1f", length), IM_COL32(200, 215, 230, 225)});
// the label in the middle of the row
glm::dvec3 middle{first.point(0.5)};
auto left{length * 0.5};
for (auto const &[member, forward] : run)
{
bezier const piece{member->m_paths.front()};
auto const span{piece.plan_length()};
if (left <= span || &member == &run.back().first)
{
auto const share{std::clamp(span > 1e-6 ? left / span : 0.5, 0.0, 1.0)};
middle = piece.point(forward ? share : 1.0 - share);
break;
}
left -= span;
}
if (straight)
{
labels.push_back({2, -length, middle, format(STR_C("straight %.0f m"), length), IM_COL32(200, 215, 230, 225)});
continue;
}
editor_track::curve shape;
if (editor_track::analyse_curve(run, m_route.design.norms.gauge, shape) && shape.radius >= 0.5)
labels.push_back({1, shape.radius, middle, curve_text(shape), IM_COL32(235, 235, 235, 240)});
}
std::sort(labels.begin(), labels.end(), [](track_label const &A, track_label const &B) { return A.rank != B.rank ? A.rank < B.rank : A.order < B.order; });
// each label where it reads best: beside its track if it's free there, else moved out to the nearest room, a line leading back

View File

@@ -5969,14 +5969,17 @@ msgstr "Tylko szablony na szynę zaznaczonego toru"
msgid "The switch tool; at the top: what to place"
msgstr "Narzędzie Rozjazd; na górze: co stawiasz"
msgid "Click a track: the switch goes into it"
msgstr "Kliknij tor: rozjazd wejdzie w niego"
msgid "Click a track: the one the switch goes into"
msgstr "Kliknij tor: ten, w który wejdzie rozjazd"
msgid "Press on it and drag: along the direction, sideways the side"
msgstr "Wciśnij na nim i przeciągnij: wzdłuż toru zmienia kierunek, w bok stronę"
msgid "Press on it and drag: along the direction, sideways the side; let go: the switch is laid"
msgstr "Wciśnij na nim i przeciągnij: wzdłuż toru zmienia kierunek, w bok stronę; puszczenie kładzie rozjazd"
msgid "Connection: drag from a straight onto the other track"
msgstr "Połączenie: przeciągnij z prostej na drugi tor"
msgid "Connection: drag from a straight onto the other track; a crossover, a double one or scissors, as chosen at the top"
msgstr "Połączenie: przeciągnij z prostej na drugi tor; przejście rozjazdowe, podwójne albo nożyce, jak wybrano na górze"
msgid "Switch ladder: drag from the straight to the side the tracks go off to; the ones missing are laid with it"
msgstr "Ulica zwrotnicowa: przeciągnij z prostej w stronę, w którą odchodzą tory; brakujące tory układają się razem z nią"
msgid "Click the green or red point in the view: that one only"
msgstr "Kliknij zielony albo czerwony punkt w widoku: postawi albo poprawi tylko ten wskaźnik"
@@ -12093,8 +12096,8 @@ msgstr "Wyklikaj kilka punktów i ułóż je Enterem"
msgid "Click the bends of the line: each gets a curve as large as fits; one too sharp for the speed set in the panel has its label in orange"
msgstr "Klikaj załamania linii: każde dostaje łuk tak duży, jak się zmieści; łuk za ostry dla prędkości ustawionej w panelu ma pomarańczowy opis"
msgid "A curve seen on the photo: click points along its middle, the last two on the straight after it"
msgstr "Łuk widoczny na zdjęciu: klikaj punkty wzdłuż jego środka, dwa ostatnie na prostej za nim"
msgid "A curve seen on the photo: click points along its middle, the last two on the straight after it; one of two or three radii comes out a compound curve"
msgstr "Łuk widoczny na zdjęciu: klikaj punkty wzdłuż jego środka, dwa ostatnie na prostej za nim; łuk o dwóch albo trzech promieniach wychodzi jako koszowy"
msgid "Lay a track with a bend in it"
msgstr "Ułóż tor z załamaniem"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 KiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 565 KiB

After

Width:  |  Height:  |  Size: 557 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 755 KiB