From 9b75e64c9b22fc23b044a382f9af111f68fcfab7 Mon Sep 17 00:00:00 2001 From: maj00r Date: Mon, 12 Oct 2026 18:00:26 +0200 Subject: [PATCH] feat(editor): digitizing takes the good parts of a line: a run whose design can't be read whole is split at the track the reading fails at and each part is digitized on its own, only single tracks that make no line are left out and named in the log; Digitize all reports the lines digitized and the paths left out, opening a line digitized in parts says so with the reason for the paths left out; a stored radius agrees with the one its design makes within 0.1 % --- application/editormode.h | 5 ++- application/editormodetrack.cpp | 75 +++++++++++++++++++++++++-------- editor/editorAlignmentStore.cpp | 3 +- lang/editor_pl.po | 10 ++++- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/application/editormode.h b/application/editormode.h index 9cd0c3af..7f4f12b1 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -829,14 +829,15 @@ class editor_mode : public application_mode, private editor_track::observer bool route_from_line(TTrack &Track); alignment::vertex route_vertex_of(editor_track::curve const &Curve) const; bool route_load(TTrack &Track); - void route_read_stored(); + void route_read_stored(TTrack &Track); void route_take_stored(int const Index); void record_switch(TTrack &Track, editor_track::switch_template const &Shape); void record_slip(std::vector const &Parts, std::string const &Kind, std::string const &Type, double const Radius, double const Angle, std::string const &Base); void render_switch_record(TTrack const &Track) const; void route_store(); bool digitize_drawn(std::vector const &Created, bool const Alone = true); - bool digitize_chain(editor_track::chain const &Chain, std::string *Reason = nullptr); + bool digitize_chain(editor_track::chain const &Chain, std::string *Reason = nullptr, TTrack **Culprit = nullptr); + int digitize_parts(editor_track::chain const &Chain, std::string *Reason, int &Skipped); void digitize_all(); void render_route_stored(); void route_update() diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index efbd0ba5..d4b6ec2b 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -1649,12 +1649,12 @@ bool editor_mode::route_load(TTrack &Track) m_route.designed = 0; if (false == (m_route.single ? route_from_curve(Track) : route_from_line(Track))) return false; - route_read_stored(); + route_read_stored(Track); return true; } // the design kept in the scenery for the fragment takes the place of the one read from the paths; its chainage is carried on -void editor_mode::route_read_stored() +void editor_mode::route_read_stored(TTrack &Track) { auto &route{m_route}; route.departed = -1; @@ -1668,15 +1668,24 @@ void editor_mode::route_read_stored() route.departed = alignment_store::departed(chain.tracks, route.departure); auto const loose{std::any_of(chain.tracks.begin(), chain.tracks.end(), [](TTrack const *Track) { return false == alignment_store::on_track(*Track).has_value(); })}; bool digitized{false}; + int parts{0}; + int skipped{0}; std::string failure; if (index < 0 && route.departed < 0 && loose && false == route.single) { - if (digitize_chain(chain, &failure)) + parts = digitize_parts(chain, &failure, skipped); + if (parts == 1 && skipped == 0) { alignment_store::settle(); index = alignment_store::find(chain.start, chain.end, reversed); digitized = true; } + else if (parts > 0) + { + alignment_store::settle(); + route_from_line(Track); + route.departed = alignment_store::departed(route.chain.tracks, route.departure); + } } if (index >= 0 && false == reversed && false == route.single) { @@ -1693,6 +1702,10 @@ void editor_mode::route_read_stored() route.status += STR_C("\nThe line is digitized now, a straight"); else if (digitized) route.status += format(STR_C("\nThe line is digitized now, curves kept in the scenery: %d"), route.designed); + else if (parts > 0) + route.status += format(STR_C("\nThe line is digitized in parts: %d, paths left out: %d"), parts, skipped) + (failure.empty() ? std::string{} : " - " + failure); + else if (skipped > 0 && loose && skipped < static_cast(chain.tracks.size())) + route.status += format(STR_C("\nPaths left out of the digitized line: %d"), skipped) + (failure.empty() ? std::string{} : " - " + failure); else if (false == failure.empty()) route.status += STR_C("\nThe line can't be digitized: ") + failure; else if (route.designed > 0) @@ -1808,21 +1821,12 @@ void editor_mode::digitize_all() } visited.insert(chain.tracks.begin(), chain.tracks.end()); std::string reason; - if (digitize_chain(chain, &reason)) - { - ++kept; - continue; - } - ++failed; - if (false == reason.empty()) - { - WriteLog("Editor: digitizing - " + reason, logtype::generic); - if (first.empty()) - first = reason; - } + kept += digitize_parts(chain, &reason, failed); + if (first.empty()) + first = reason; } alignment_store::settle(); - auto text{format(STR_C("Lines digitized: %d, not digitized: %d"), kept, failed)}; + auto text{format(STR_C("Lines digitized: %d, paths left out: %d"), kept, failed)}; if (false == first.empty()) text += STR_C(", the first: ") + first; WriteLog("Editor: " + text, logtype::generic); @@ -1831,7 +1835,40 @@ void editor_mode::digitize_all() route_load(*m_route.chain.tracks[m_route.chain.tracks.size() / 2]); } -bool editor_mode::digitize_chain(editor_track::chain const &Chain, std::string *Reason) +int editor_mode::digitize_parts(editor_track::chain const &Chain, std::string *Reason, int &Skipped) +{ + auto const count{Chain.tracks.size()}; + if (count == 0 || std::all_of(Chain.tracks.begin(), Chain.tracks.end(), [](TTrack const *Track) { return alignment_store::on_track(*Track).has_value(); })) + return 0; + TTrack *culprit{nullptr}; + std::string reason; + if (digitize_chain(Chain, &reason, &culprit)) + return 1; + if (count == 1) + { + ++Skipped; + reason = (Chain.tracks.front()->name().empty() ? std::string{"(noname)"} : Chain.tracks.front()->name()) + ": " + reason; + WriteLog("Editor: digitizing - " + reason, logtype::generic); + if (Reason != nullptr && Reason->empty()) + *Reason = reason; + return 0; + } + auto const found{static_cast(std::find(Chain.tracks.begin(), Chain.tracks.end(), culprit) - Chain.tracks.begin())}; + auto const split{found < count ? std::max(1, found) : count / 2}; + int parts{0}; + for (auto const &[first, last] : {std::pair{0, split - 1}, std::pair{split, count - 1}}) + { + editor_track::chain part; + std::string error; + if (editor_track::find_chain(Chain.tracks[first], Chain.tracks[last], part, error) && part.tracks.size() == last - first + 1) + parts += digitize_parts(part, Reason, Skipped); + else + Skipped += static_cast(last - first + 1); + } + return parts; +} + +bool editor_mode::digitize_chain(editor_track::chain const &Chain, std::string *Reason, TTrack **Culprit) { if (Chain.tracks.empty()) return false; @@ -1887,6 +1924,8 @@ bool editor_mode::digitize_chain(editor_track::chain const &Chain, std::string * { if (Reason != nullptr) *Reason = format(STR_C("the straights meet with no curve at path %s"), name(nearest(entry.position)).c_str()); + if (Culprit != nullptr) + *Culprit = nearest(entry.position); return false; } auto vertex{route_vertex_of(entry.shape)}; @@ -1923,6 +1962,8 @@ bool editor_mode::digitize_chain(editor_track::chain const &Chain, std::string * } if (false == fits) { + if (Culprit != nullptr) + *Culprit = farthest; if (Reason != nullptr) *Reason = closest < std::numeric_limits::max() ? format(STR_C("the curves read from the paths leave path %s by %.2f m"), name(farthest).c_str(), closest) : std::string{STR_C("the paths don't make a line of straights, arcs and transition curves")}; return false; diff --git a/editor/editorAlignmentStore.cpp b/editor/editorAlignmentStore.cpp index 506fa566..e57258d2 100644 --- a/editor/editorAlignmentStore.cpp +++ b/editor/editorAlignmentStore.cpp @@ -96,9 +96,10 @@ bool agrees(std::vector const &Stored, std::vector 0.01 || std::abs(stored[i].length - made[i].length) > 0.01 || - std::abs(stored[i].radius_start - made[i].radius_start) > 0.01 || std::abs(stored[i].radius_end - made[i].radius_end) > 0.01) + false == close(stored[i].radius_start, made[i].radius_start) || false == close(stored[i].radius_end, made[i].radius_end)) return false; return true; } diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 47e2083c..2f896bf2 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -5185,8 +5185,8 @@ msgstr "Zdigitalizuj wszystkie tory" msgid "Every line of the scenery not digitized yet is kept in it with its straights, arcs and transition curves, the way the drawn tracks are kept; the paths stay as they are" msgstr "Każda jeszcze niezdigitalizowana linia scenerii zostaje w niej zapisana z prostymi, łukami i krzywymi przejściowymi, tak jak rysowane tory; tory zostają tak, jak leżą" -msgid "Lines digitized: %d, not digitized: %d" -msgstr "Zdigitalizowane linie: %d, niezdigitalizowane: %d" +msgid "Lines digitized: %d, paths left out: %d" +msgstr "Zdigitalizowane linie: %d, pominięte tory: %d" msgid ", the first: " msgstr ", pierwsza: " @@ -5197,6 +5197,12 @@ msgstr "\nLinia zdigitalizowana: prosta" msgid "\nThe line is digitized now, curves kept in the scenery: %d" msgstr "\nLinia zdigitalizowana, łuki zapisane w scenerii: %d" +msgid "\nThe line is digitized in parts: %d, paths left out: %d" +msgstr "\nLinia zdigitalizowana w częściach: %d, pominięte tory: %d" + +msgid "\nPaths left out of the digitized line: %d" +msgstr "\nTory pominięte w zdigitalizowanej linii: %d" + msgid "\nThe line can't be digitized: " msgstr "\nNie da się zdigitalizować linii: "