From a312fd99353a1dac7bacab13763a3bdfd5f65de3 Mon Sep 17 00:00:00 2001 From: maj00r Date: Sun, 25 Oct 2026 12:33:19 +0100 Subject: [PATCH] feat(editor): with the gizmo hidden a track is changed by its ends only: its control vectors are neither drawn nor caught until Q, W, E or the buttons above the view bring the gizmo out, so a track selected won't bend by chance; the hints say what dragging an end does (a joint: the neighbours follow, a free end: a new track out of it, with Ctrl it moves). The tutorial has a lesson of its own for editing with the gizmo, after the one of selecting and fixing, which no longer speaks of it: the arrows, the rings, the handles, the control vectors, a point clicked, Ctrl and Esc, ticked off when the gizmo is out and a track bent or moved with it; thirteen lessons. Also puts back in place two lines of the band of the curvature which dd64b821 committed out of their place (the arc declared inside the loop, so that commit didn't compile) --- application/editormode.cpp | 25 +++++++++--- application/editormoderoute.cpp | 4 +- application/editormodetrack.cpp | 15 ++++++-- application/editormodeviewport.cpp | 2 +- application/editortool.h | 2 +- lang/editor_pl.po | 62 +++++++++++++++++++++++------- 6 files changed, 85 insertions(+), 25 deletions(-) diff --git a/application/editormode.cpp b/application/editormode.cpp index 66c8581f..d471cf83 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -4759,7 +4759,7 @@ void editor_mode::render_help_menu() ImGui::SetTooltip("%s", STR_C("The keys of the editor, for each field of work")); ImGui::MenuItem(STR_C("Tutorial"), nullptr, &m_first_steps_open); if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C("Twelve short lessons with clips and drawings: the camera, laying track, curves, correcting a curve, joining, switches, signals and posts, fixing, the grade line, terrain, older sceneries, saving and driving")); + ImGui::SetTooltip("%s", STR_C("Thirteen short lessons with clips and drawings: the camera, laying track, curves, correcting a curve, joining, switches, signals and posts, fixing, the gizmo, the grade line, terrain, older sceneries, saving and driving")); ImGui::EndMenu(); } @@ -5107,6 +5107,10 @@ void editor_mode::render_first_steps() first_step_done(first_step::terrain); if (ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::route && false == m_route.chain.tracks.empty()) first_step_done(first_step::plan); + if (ui()->mode() == nodebank_panel::TRACK && m_track_gizmo_on) + first_step_done(first_step::gizmo); + if (m_track_gizmo_using || (m_point_drag.moved && m_track_point.valid() && false == editor_track::is_end(m_track_point.kind))) + first_step_done(first_step::vector); enum class trial { none, top, lay, turnout, select, profile, terrain, plan, trainset, signals }; struct input { @@ -5198,14 +5202,25 @@ void editor_mode::render_first_steps() {"Selecting and fixing", "edit", {{"Esc", "The select tool"}, - {"LMB", "Click a track: it is selected, with its points"}, - {"LMB + move", "Drag a point: the end or the curve follows"}, - {"Q W E", "The gizmo on the selected track: move, turn, lengthen. Hidden until then, so the track won't move by chance; the arrow above the view hides it again"}, + {"LMB", "Click a track: it is selected, with its ends"}, + {"LMB + move", "Drag an end: at a joint the neighbours follow, from a free end a new track goes out, with Ctrl the end moves"}, {"RMB", "A click on a track: what can be done with it"}, {"Del", "Deletes the selected"}, - {"Ctrl + Z", "Takes the last change back; Ctrl+Y brings it back again"}}, + {"Ctrl + Z", "Takes the last change back; Ctrl+Y brings it back again"}, + {"Q W E", "The gizmo, to move, turn or bend the track: the next lesson"}}, {{first_step::select, "Select a track"}, {first_step::undo, "Take a change back"}}, trial::select}, + {"Editing with the gizmo", + "gizmo", + {{"Q", "The gizmo on the selected track, hidden until then: arrows which move it"}, + {"W", "Rings which turn it: its direction, its grade and its cant; the tracks joined to it follow"}, + {"E", "Handles which lengthen it at its free end, the curve keeping its radius"}, + {"LMB + move", "With the gizmo out the control vectors are there too: drag one, the curve follows it"}, + {"LMB", "A click on a point of the track: the gizmo goes to it, that point alone is moved or turned"}, + {"Ctrl", "Held while dragging: the gizmo goes by its step"}, + {"Esc", "Releases the point; the arrow above the view hides the gizmo again"}}, + {{first_step::gizmo, "Bring out the gizmo with Q, W or E"}, {first_step::vector, "Bend a track by a control vector, or move it with the gizmo"}}, + trial::select}, {"The grade line", nullptr, {{"P", "The profile tool: a click on a track opens the grade line of its whole line in the strip below"}, diff --git a/application/editormoderoute.cpp b/application/editormoderoute.cpp index ed3331a5..f8ac7a8f 100644 --- a/application/editormoderoute.cpp +++ b/application/editormoderoute.cpp @@ -1741,12 +1741,12 @@ bool editor_mode::render_route_band() // ones of no length yet int under{-1}; int part{0}; + int arc{0}; if (hovered && false == dragging) { for (int i = 0; i < count; ++i) { auto const &element{result.elements[i]}; - int arc{0}; if (mouse.x < x_of(element.chainage) || mouse.x >= x_of(element.chainage + element.length)) continue; under = i; @@ -1810,12 +1810,12 @@ bool editor_mode::render_route_band() } else { + // the transition in grows to the left, from where it starts; the others to the right auto const shift{(mouse.x - band.press.x) / std::max(1e-6f, metres)}; wanted = std::max(0.0, std::round(band.value + (band.part == 2 ? -shift : shift))); } if (wanted != value) { - // the transition in grows to the left, from where it starts; the others to the right value = wanted; changed = true; } diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 227f4fc0..fed97ccd 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -177,6 +177,9 @@ editor_track::point_ref editor_mode::track_handle_hit() const { for (auto const kind : kPointKinds) { + // the control vectors only with the gizmo out: hidden, a track is changed by its ends and won't bend by chance + if (false == m_track_gizmo_on && false == editor_track::is_end(kind)) + continue; editor_track::point_ref const point{i, kind}; float const distance = projection.mouse_distance2(editor_track::point_position(*track, point)) * (editor_track::is_end(kind) ? 0.5f : 1.0f); if (distance < best) @@ -217,8 +220,9 @@ void editor_mode::draw_track_overlay() const if (false == trackmode || m_route_tab || false == editor_track::is_supported(*track)) return; + // the control vectors are drawn, and dragged, only with the gizmo out ImU32 const vectorcolor = IM_COL32(200, 200, 200, 160); - for (int i = 0; i < static_cast(track->m_paths.size()); ++i) + for (int i = 0; m_track_gizmo_on && i < static_cast(track->m_paths.size()); ++i) { projection.line(drawlist, editor_track::point_position(*track, {i, editor_track::point_kind::start}), editor_track::point_position(*track, {i, editor_track::point_kind::control1}), vectorcolor, 1.0f); projection.line(drawlist, editor_track::point_position(*track, {i, editor_track::point_kind::end}), editor_track::point_position(*track, {i, editor_track::point_kind::control2}), vectorcolor, 1.0f); @@ -227,6 +231,8 @@ void editor_mode::draw_track_overlay() const { for (auto const kind : kPointKinds) { + if (false == m_track_gizmo_on && false == editor_track::is_end(kind)) + continue; editor_track::point_ref const point{i, kind}; ImVec2 screen; if (false == projection.project(editor_track::point_position(*track, point), screen)) @@ -1809,12 +1815,15 @@ std::vector editor_mode::track_key_hints(bool const All) auto const handles{std::find_if(hints.begin(), hints.end(), named("Gizmo"))}; if (auto const keys{std::find_if(hints.begin(), hints.end(), named("Q W E"))}; keys != hints.end()) { - keys->action = "bring out the gizmo: move, turn, lengthen"; + keys->action = "bring out the gizmo: move, turn, lengthen, bend by the control vectors"; if (handles != hints.end()) hints.erase(handles); } else if (handles != hints.end()) - *handles = {"Q W E", "bring out the gizmo: move, turn, lengthen"}; + *handles = {"Q W E", "bring out the gizmo: move, turn, lengthen, bend by the control vectors"}; + // with it hidden only the ends of the track are there to drag, the control vectors come out with it + if (auto const points{std::find_if(hints.begin(), hints.end(), named("Drag a point"))}; points != hints.end()) + *points = {"Drag an end", "a joint: the neighbours follow; a free end: a new track out of it, with Ctrl it moves"}; gizmo = false; } if (auto const *meaning{typed_meaning()}; meaning != nullptr) diff --git a/application/editormodeviewport.cpp b/application/editormodeviewport.cpp index 14012a7e..d06ace70 100644 --- a/application/editormodeviewport.cpp +++ b/application/editormodeviewport.cpp @@ -425,7 +425,7 @@ void editor_mode::render_viewport_gizmo() }; std::vector buttons; if (tracks) - buttons.push_back({icon::select, STR_C("No gizmo"), nullptr, STR_C("The gizmo hidden: a track is changed by dragging its points, and won't move by chance"), false == on, [this]() { m_track_gizmo_on = false; }}); + buttons.push_back({icon::select, STR_C("No gizmo"), nullptr, STR_C("The gizmo hidden: a track is changed by dragging its ends, and won't move nor bend by chance; its control vectors come out with the gizmo"), false == on, [this]() { m_track_gizmo_on = false; }}); buttons.push_back({icon::translate, STR_C("Translate"), "Q", tooltip, on && m_gizmo_op == gizmo_operation::translate, [choose]() { choose(gizmo_operation::translate); }, tracks}); buttons.push_back({icon::rotate, STR_C("Rotate"), "W", tooltip, on && m_gizmo_op == gizmo_operation::rotate, [choose]() { choose(gizmo_operation::rotate); }}); buttons.push_back({icon::scale, STR_C("Scale"), "E", tooltip, on && m_gizmo_op == gizmo_operation::scale, [choose]() { choose(gizmo_operation::scale); }}); diff --git a/application/editortool.h b/application/editortool.h index 8a3601da..ca375604 100644 --- a/application/editortool.h +++ b/application/editortool.h @@ -34,7 +34,7 @@ enum class editor_selftest_result again }; -enum class editor_first_step { tracks, lay, laid, wheel, join, turnout, undo, save, drive, look, top, select, shape, profile, profile_point, profile_apply, terrain, nmt, plan, plan_band, plan_apply, signal, posts, bend, trace, count }; +enum class editor_first_step { tracks, lay, laid, wheel, join, turnout, undo, save, drive, look, top, select, shape, profile, profile_point, profile_apply, terrain, nmt, plan, plan_band, plan_apply, signal, posts, bend, trace, gizmo, vector, count }; enum class editor_track_tab { straights, route, path, turnout, profile, speed, joints, infra, lay, lineside, turntable, parallel, srk, kilometrage }; diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 9d0adcda..e632832a 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -5361,11 +5361,44 @@ msgstr "Zaznaczanie i poprawki" msgid "The select tool" msgstr "Narzędzie Zaznaczanie" -msgid "Click a track: it is selected, with its points" -msgstr "Kliknij tor: zostaje zaznaczony, widać jego punkty" +msgid "Click a track: it is selected, with its ends" +msgstr "Kliknij tor: zostaje zaznaczony, widać jego końce" -msgid "Drag a point: the end or the curve follows" -msgstr "Przeciągnij punkt: koniec albo łuk idzie za nim" +msgid "The gizmo, to move, turn or bend the track: the next lesson" +msgstr "Gizmo do przesuwania, obracania i wyginania toru: następna lekcja" + +msgid "Editing with the gizmo" +msgstr "Edycja z gizmo" + +msgid "The gizmo on the selected track, hidden until then: arrows which move it" +msgstr "Gizmo na zaznaczonym torze, do tej pory ukryte: strzałki, które go przesuwają" + +msgid "Rings which turn it: its direction, its grade and its cant; the tracks joined to it follow" +msgstr "Pierścienie, które go obracają: kierunek, pochylenie i przechyłka; połączone tory idą za nim" + +msgid "Handles which lengthen it at its free end, the curve keeping its radius" +msgstr "Uchwyty, które wydłużają go na wolnym końcu, łuk zachowuje promień" + +msgid "With the gizmo out the control vectors are there too: drag one, the curve follows it" +msgstr "Z gizmo widać też wektory sterujące: przeciągnij jeden, łuk idzie za nim" + +msgid "A click on a point of the track: the gizmo goes to it, that point alone is moved or turned" +msgstr "Klik na punkcie toru: gizmo przechodzi do niego, przesuwa albo obraca tylko ten punkt" + +msgid "Held while dragging: the gizmo goes by its step" +msgstr "Trzymany przy przeciąganiu: gizmo przeskakuje o swój krok" + +msgid "Releases the point; the arrow above the view hides the gizmo again" +msgstr "Puszcza punkt; strzałka nad widokiem chowa gizmo z powrotem" + +msgid "Bring out the gizmo with Q, W or E" +msgstr "Pokaż gizmo klawiszem Q, W albo E" + +msgid "Bend a track by a control vector, or move it with the gizmo" +msgstr "Wygnij tor wektorem sterującym albo przesuń go gizmem" + +msgid "Drag an end: at a joint the neighbours follow, from a free end a new track goes out, with Ctrl the end moves" +msgstr "Przeciągnij koniec: na styku sąsiednie tory idą za nim, z wolnego końca wychodzi nowy tor, z Ctrl koniec się przesuwa" msgid "A click on a track: what can be done with it" msgstr "Klik na torze: co można z nim zrobić" @@ -7478,8 +7511,8 @@ msgstr "Postaw i jedź: pociąg staje na torze, a kabina jest twoja" msgid "The trainset tool" msgstr "Narzędzie Skład" -msgid "Twelve short lessons with clips and drawings: the camera, laying track, curves, correcting a curve, joining, switches, signals and posts, fixing, the grade line, terrain, older sceneries, saving and driving" -msgstr "Dwanaście krótkich lekcji z filmikami i rysunkami: kamera, układanie toru, łuki, poprawianie łuku, łączenie, rozjazdy, sygnalizatory i słupki, poprawki, niweleta, teren, starsze scenerie, zapis i jazda" +msgid "Thirteen short lessons with clips and drawings: the camera, laying track, curves, correcting a curve, joining, switches, signals and posts, fixing, the gizmo, the grade line, terrain, older sceneries, saving and driving" +msgstr "Trzynaście krótkich lekcji z filmikami i rysunkami: kamera, układanie toru, łuki, poprawianie łuku, łączenie, rozjazdy, sygnalizatory i słupki, poprawki, gizmo, niweleta, teren, starsze scenerie, zapis i jazda" msgid "B: the hectometre posts of the whole line at once" msgstr "B: słupki hektometrowe całej linii naraz" @@ -12021,11 +12054,17 @@ msgstr "rampa %.0f m, %.0f mm" msgid "No gizmo" msgstr "Bez gizma" -msgid "The gizmo hidden: a track is changed by dragging its points, and won't move by chance" -msgstr "Gizmo ukryte: tor zmienia się przeciąganiem jego punktów i nie przesunie się przypadkiem" +msgid "The gizmo hidden: a track is changed by dragging its ends, and won't move nor bend by chance; its control vectors come out with the gizmo" +msgstr "Gizmo ukryte: tor zmienia się przeciąganiem jego końców i nie przesunie się ani nie wygnie przypadkiem; wektory sterujące pojawiają się razem z gizmo" -msgid "bring out the gizmo: move, turn, lengthen" -msgstr "pokazuje gizmo: przesuń, obróć, wydłuż" +msgid "bring out the gizmo: move, turn, lengthen, bend by the control vectors" +msgstr "pokazuje gizmo: przesuń, obróć, wydłuż, wygnij wektorami sterującymi" + +msgid "Drag an end" +msgstr "Przeciągnij koniec" + +msgid "a joint: the neighbours follow; a free end: a new track out of it, with Ctrl it moves" +msgstr "styk: sąsiednie tory idą za nim; wolny koniec: wychodzi z niego nowy tor, z Ctrl się przesuwa" msgid "gizmo snaps by %.2f m" msgstr "gizmo przeskakuje co %.2f m" @@ -12114,9 +12153,6 @@ msgstr "Przełącz klawiszem Tab na odrysowywanie łuku" msgid "Change the radius of the joining curve with Ctrl+wheel" msgstr "Zmień promień łuku łączącego Ctrl+kółkiem" -msgid "The gizmo on the selected track: move, turn, lengthen. Hidden until then, so the track won't move by chance; the arrow above the view hides it again" -msgstr "Gizmo na zaznaczonym torze: przesuń, obróć, wydłuż. Do tego czasu ukryte, więc tor nie przesunie się przypadkiem; strzałka nad widokiem chowa je z powrotem" - msgid "In a scenery begun from nothing the first L asks: the real terrain of the place, or the flat plane" msgstr "W scenerii zaczętej od zera pierwsze L pyta: prawdziwy teren tego miejsca czy płaska płaszczyzna"