From 55bb48e8bd29eaac773ec08037025c270ae49616 Mon Sep 17 00:00:00 2001 From: maj00r Date: Sun, 11 Oct 2026 11:39:57 +0200 Subject: [PATCH] feat(editor): laying from the ground goes on like laying from a free end - the second click of the lay tool lays a straight from the first point and goes on from its end, each next click lays the shape chosen (arc then straight, straight then arc, arc through the cursor, straight) with the radius on Ctrl+wheel, as from a free end; no polyline of vertices cut into pieces of the longest path any more - a track started on the ground and ended at a free end is the shape pulled out of that end towards the start point, one path per up to 90 degrees of arc - hints and readout the same for both; self-test command layclicks --- application/editormodeselftest.cpp | 38 ++++++++++++++++++++++++++++++ application/editormodetrack.cpp | 35 +++++++++++++++++---------- lang/editor_pl.po | 15 +++--------- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/application/editormodeselftest.cpp b/application/editormodeselftest.cpp index 593ade8a..ae224e4a 100644 --- a/application/editormodeselftest.cpp +++ b/application/editormodeselftest.cpp @@ -90,6 +90,7 @@ http://mozilla.org/MPL/2.0/. // orthophoto [in scene 0|1] the imagery layer on, placed and fitted as given // join [radius] [variant] [transitions 0|1] the free ends at the points joined, as a drag from one to the other // layto from the free end at the first point towards the second, as a drag in the layout +// layclicks ... clicks of the lay tool at the points, a free end within 3 m joined // joints ... the joints check over the scenery, the issues around the points logged // screenshot // save @@ -1042,6 +1043,43 @@ void editor_mode::selftest_step() if (false == created.empty()) WriteLog(format("SELFTEST %s end: %.3f m from the target", command.c_str(), geometry::plan_distance(previous, b))); } + else if (command == "layclicks") + { + int layout{0}; + words >> layout >> m_lay.radius; + m_track_layout = static_cast(std::clamp(layout, 0, 3)); + auto const active{m_lay.active}; + m_lay.active = true; + lay_cancel(); + auto const before{m_history.size()}; + for (glm::dvec3 point{0.0}; words >> point.x >> point.z;) + { + point.y = m_lay.points.empty() ? 0.0 : m_lay.points.back().y; + m_lay.mouse = point; + m_lay.mouse_snap = editor_track::find_free_end(nullptr, 1, point, 3.0, {}); + lay_click(); + WriteLog(format("SELFTEST layclicks %.1f %.1f: %s", point.x, point.z, m_lay.status.c_str())); + } + lay_cancel(); + m_lay.active = active; + glm::dvec2 heading{0.0}; + glm::dvec3 previous{0.0}; + bool first{true}; + for (auto step{before}; step < m_history.size(); ++step) + for (auto const *track : m_history[step].created) + { + auto const &path{track->m_paths.front()}; + geometry::bezier const curve{path}; + auto const begin{glm::normalize(geometry::plan_of(curve.first(0.0)))}; + auto const &a{path.points[segment_data::point::start]}; + auto const &b{path.points[segment_data::point::end]}; + WriteLog(format("SELFTEST layclicks path %s: %.2f %.2f -> %.2f %.2f, L %.2f m, R %.1f m, kink %.4f deg, step %.4f m", track->name().c_str(), a.x, a.z, b.x, b.z, curve.plan_length(), static_cast(path.radius), + first ? 0.0 : glm::degrees(std::abs(geometry::signed_angle(heading, begin))), first ? 0.0 : glm::distance(previous, a))); + heading = glm::normalize(geometry::plan_of(curve.first(1.0))); + previous = b; + first = false; + } + } else if (command == "joints") { std::vector points; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 7c021098..cdfd354a 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -3495,13 +3495,13 @@ std::vector editor_mode::track_key_hints(bool const All) if (m_lay.active) { if (m_lay.points.empty()) - hints = {{"LMB", "start of the new track, on the ground or at a free end"}, {"Drag from a free end", "straight along it, to another free end: a curve joining them"}, {"Parallel", "first select a straight, then a second track beside it in the panel"}, {"Esc", "stop laying"}}; + hints = {{"LMB", "start of the new track, on the ground or at a free end"}, {"Drag from a free end", "the shape chosen towards the cursor, to a free end: joins it"}, {"Parallel", "first select a straight, then a second track beside it in the panel"}, {"Esc", "stop laying"}}; else { - bool const chained{m_lay.points.size() == 1 && m_lay.start.track != nullptr}; - hints = {{"LMB", chained ? "lay the shape up to here and go on from its end" : m_lay.points.size() == 1 ? "end of the straight, or a vertex of a curve" : "next vertex of the curve"}, {"LMB on a free end", "join and lay"}, {"Ctrl+wheel", "radius"}}; + bool const chained{m_lay.start.track != nullptr}; + hints = {{"LMB", chained ? "lay the shape up to here and go on from its end" : "lay the straight up to here and go on from its end"}, {"LMB on a free end", "join and lay"}}; if (chained || m_lay.mouse_snap.track != nullptr) - hints.push_back({"Shift+wheel", "arc / straight, at a free end: another shape"}); + hints.insert(hints.end(), {{"Ctrl+wheel", "radius"}, {"Shift+wheel", "arc / straight, at a free end: another shape"}}); hints.insert(hints.end(), {{"Enter", "lay up to the cursor"}, {"Backspace", "take back the point"}, {"Esc", "cancel"}}); if (glm::dvec2 heading; lay_heading(heading)) hints.insert(hints.begin() + 1, {"Shift", "straight on in the direction of the track"}); @@ -3669,9 +3669,7 @@ std::string editor_mode::track_readout() const if (false == lay.preview_error.empty()) text += "\n" + lay.preview_error; if (lay.points.size() == 1 && (lay.start.track != nullptr || lay.mouse_snap.track != nullptr)) - text += "\n" + track_shaping(lay.mouse_snap.track != nullptr); - else if (false == lay.points.empty()) - text += "\n" + format(STR_C("R %.0f m Ctrl+wheel: radius"), lay.radius); + text += "\n" + track_shaping(lay.mouse_snap.track != nullptr && lay.start.track != nullptr); return text; } auto const &state{m_straights}; @@ -4509,7 +4507,7 @@ bool editor_mode::track_wheel(double const Offset) return false; int const step{Offset > 0.0 ? 1 : -1}; editor_track::snap_target target; - bool const joined{m_extend.active ? extend_snap(target) : m_lay.mouse_snap.track != nullptr}; + bool const joined{m_extend.active ? extend_snap(target) : m_lay.mouse_snap.track != nullptr && m_lay.start.track != nullptr}; static double const radii[]{100.0, 150.0, 190.0, 250.0, 300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1200.0, 1500.0, 2000.0, 3000.0, 4000.0, 6000.0}; auto const next_radius = [&](double const Current) { if (step > 0) @@ -4550,7 +4548,7 @@ std::vector editor_mode::lay_pieces(std::vector const Length = 0.0; if (Points.size() < 2) return {}; - if (Points.size() == 2 && (End.track != nullptr || tool.start.track != nullptr)) + if (Points.size() == 2) { auto const leaving{tool.start.track != nullptr ? -plan_of(tool.start.direction) : plan_of(Points[1] - Points[0])}; if (glm::length(leaving) < 1e-6) @@ -4558,10 +4556,23 @@ std::vector editor_mode::lay_pieces(std::vector const Error = STR_C("Two points lie on one another"); return {}; } - if (End.track != nullptr) + if (End.track != nullptr && tool.start.track != nullptr) return join_pieces(tool.start.track, tool.start.point, Points[0], glm::normalize(leaving), End, Length, Error); m_join_last = {}; - auto const pieces{layout_pieces(Points[0], glm::normalize(leaving), -grade_of(tool.start.direction), Points[1], tool.radius)}; + std::vector pieces; + if (tool.start.track != nullptr) + pieces = layout_pieces(Points[0], glm::normalize(leaving), -grade_of(tool.start.direction), Points[1], tool.radius); + else if (End.track == nullptr) + { + auto const length{glm::length(leaving)}; + pieces = straight_piece(Points[0], glm::normalize(leaving), (Points[1].y - Points[0].y) / length, length); + } + else if (glm::length(plan_of(End.direction)) > 1e-6) + { + auto const back{layout_pieces(End.position, glm::normalize(-plan_of(End.direction)), -grade_of(End.direction), Points[0], tool.radius)}; + for (auto piece{back.rbegin()}; piece != back.rend(); ++piece) + pieces.push_back(turned_around(*piece)); + } for (auto const &piece : pieces) Length += bezier{piece}.plan_length(); if (pieces.empty()) @@ -4707,7 +4718,7 @@ void editor_mode::lay_click() if (plan_distance(point, tool.points.back()) < 1.0) return; m_typed.clear(); - if (tool.points.size() == 1 && tool.start.track != nullptr) + if (tool.points.size() == 1) { tool.points.push_back(tool.mouse); lay_finish({}); diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 54e4f947..21cd94df 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -29,12 +29,6 @@ msgstr "Pokaż inspektor" msgid "The curve can't be read from this path, edit it as a single path" msgstr "Z tego odcinka nie da się odczytać łuku, edytuj go jako pojedynczy odcinek" -msgid "end of the straight, or a vertex of a curve" -msgstr "koniec prostej albo wierzchołek łuku" - -msgid "next vertex of the curve" -msgstr "kolejny wierzchołek łuku" - msgid "Laying track: LMB adds the points" msgstr "Układanie toru: LPM dodaje punkty" @@ -2934,9 +2928,6 @@ msgstr "Rozjazd: wybierz typ z listy" msgid "Drag from a free end" msgstr "Przeciągnij z wolnego końca" -msgid "straight along it, to another free end: a curve joining them" -msgstr "prosta wzdłuż niego, do innego wolnego końca: łuk łączący" - msgid "moves the point, level" msgstr "przesuwa punkt w poziomie" @@ -5236,9 +5227,6 @@ msgstr "Co przeciągnięcie lub kliknięcie z wolnego końca kładzie w stronę msgid "Speed limit of the new paths, and the design speed of the transition curves and of the cant; the speed of the track joined comes first, and none higher than the radius allows" msgstr "Prędkość nowych torów i prędkość projektowa krzywych przejściowych oraz przechyłki; pierwszeństwo ma prędkość toru, do którego się dołącza, i nigdy nie wyższa, niż pozwala promień" -msgid "R %.0f m Ctrl+wheel: radius" -msgstr "R %.0f m Ctrl+kółko: promień" - msgid "Ctrl+wheel: radius, Shift+wheel: arc / straight" msgstr "Ctrl+kółko: promień, Shift+kółko: łuk / prosta" @@ -5281,5 +5269,8 @@ msgstr "łuk / prosta, na wolnym końcu: inny kształt" msgid "lay the shape up to here and go on from its end" msgstr "kładzie kształt dotąd i idzie dalej od jego końca" +msgid "lay the straight up to here and go on from its end" +msgstr "kładzie prostą dotąd i idzie dalej od jej końca" + msgid "type the radius" msgstr "wpisz promień"