From 0d9fa6337d8700b561580c5161f68af4e234dd35 Mon Sep 17 00:00:00 2001 From: maj00r Date: Thu, 22 Oct 2026 02:45:19 +0200 Subject: [PATCH] feat(editor): the whole-scenery grade fit sets the tracks over a bridge or a viaduct to the bridge environment Where the terrain model falls away under a fitted line by the abutments, the paths are cut off at the ends of the stretch as the line is applied and set to the bridge, in the same step of the history: they sound as on a bridge, get no formation and the terrain under them stays as the model has it, also after the scenery is saved and opened again. Tunnels and tracks set to the bridge already are left as they are. The status says how many paths went on a bridge. --- application/editormode.h | 5 +++-- application/editormodenmt.cpp | 19 ++++++++++--------- application/editormodeprofile.cpp | 20 ++++++++++++++++++-- lang/editor_pl.po | 6 ++++++ 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/application/editormode.h b/application/editormode.h index dfcb1324..3fb5bf59 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -2288,8 +2288,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri void profile_restore_ground(); void render_profile_earthworks(); void profile_check(); - // Terrain: the heightmap terrain follows the tracks changed - void profile_apply(bool const Terrain = false); + // Terrain: the heightmap terrain follows the tracks changed. Bridges: stretches of the route over a bridge or a viaduct, the + // paths over them cut off at their ends and set to the bridge environment + void profile_apply(bool const Terrain = false, std::vector const &Bridges = {}); // the terrain under the tracks changed by the last Edits steps of the history which changed tracks void profile_follow_terrain(std::size_t const Edits); void profile_fit_view(); diff --git a/application/editormodenmt.cpp b/application/editormodenmt.cpp index e0adf534..02992d4b 100644 --- a/application/editormodenmt.cpp +++ b/application/editormodenmt.cpp @@ -2069,7 +2069,15 @@ bool editor_mode::nmt_fit_line(TTrack &Track) profile_check(); auto const fitted_line{profile.line}; auto const authored{profile.samples}; - profile_apply(); + // where the model falls away under the line by the abutments, the line goes over on a bridge or a viaduct: its paths there + // are cut off and set to the bridge, the formation is laid up to it, the ground under it stays as the model has it + double constexpr kBridged{1.0}; // m + double constexpr kAbutment{100.0}; // per mille + std::vector heights(count); + for (std::size_t i = 0; i < count; ++i) + heights[i] = profile::elevation(fitted_line, sampled[i]); + auto const bridges{profile::bridged(sampled, heights, model, kBridged, kAbutment)}; + profile_apply(false, bridges); auto const applied{profile.error.empty()}; // where the line parts from the model: under it a gap, a bridge or a viaduct, over it a wall or a structure above the track auto const parted{profile::departures(fitted_line, sampled, model, 2.0 * kModel)}; @@ -2125,14 +2133,7 @@ bool editor_mode::nmt_fit_line(TTrack &Track) m_nmt_state.laid[{static_cast(std::floor(sample.position.x / kPlanCell)), static_cast(std::floor(sample.position.z / kPlanCell))}].push_back( {sample.position, sample.direction, sample.grade, editor_track::sampled_elevation(authored, sample.chainage), track != nullptr ? static_cast(track->rail_height()) : 0.0, track}); } - // where the model falls away under the line by the abutments, the line goes over on a bridge or a viaduct: the formation - // is laid up to it, the ground under it stays as the model has it - double constexpr kBridged{1.0}; // m - double constexpr kAbutment{100.0}; // per mille - std::vector heights(count); - for (std::size_t i = 0; i < count; ++i) - heights[i] = profile::elevation(fitted_line, sampled[i]); - auto const spans{applied ? profile::bridged(sampled, heights, model, kBridged, kAbutment) : std::vector{}}; + auto const spans{applied ? bridges : std::vector{}}; for (auto const &stretch : spans) for (std::size_t i = 0; i < count; ++i) if (sampled[i] >= stretch.from && sampled[i] <= stretch.to) diff --git a/application/editormodeprofile.cpp b/application/editormodeprofile.cpp index d7bbab48..d14dcf70 100644 --- a/application/editormodeprofile.cpp +++ b/application/editormodeprofile.cpp @@ -1450,7 +1450,7 @@ void editor_mode::profile_frame_view() fly_to(centre - right * (shift.x * pixel) + above * (shift.y * pixel), look, distance, 0.35); } -void editor_mode::profile_apply(bool const Terrain) +void editor_mode::profile_apply(bool const Terrain, std::vector const &Bridges) { auto &state{m_profile}; state.status.clear(); @@ -1475,8 +1475,22 @@ void editor_mode::profile_apply(bool const Terrain) auto const before{state.samples}; std::vector> states; std::vector created; + auto cuts{profile::breaks(line)}; + for (auto const &bridge : Bridges) + cuts.insert(cuts.end(), {bridge.from, bridge.to}); + std::sort(cuts.begin(), cuts.end()); auto const gaps{editor_track::apply_profile( - state.route, [&](double const Chainage) { return profile::elevation(line, Chainage); }, [&](double const Chainage) { return profile::grade(line, Chainage); }, profile::breaks(line), states, created)}; + state.route, [&](double const Chainage) { return profile::elevation(line, Chainage); }, [&](double const Chainage) { return profile::grade(line, Chainage); }, cuts, states, created)}; + // the paths over a bridge sound and light as on one, and get no formation; within what a cut too near an end leaves on + double constexpr kBridgeEnd{0.5}; // m + std::size_t bridged{0}; + for (auto const &span : state.route.spans) + if (span.track->eEnvironment != e_bridge && span.track->eEnvironment != e_tunnel + && std::any_of(Bridges.begin(), Bridges.end(), [&](profile::departure const &Bridge) { return span.from >= Bridge.from - kBridgeEnd && span.to <= Bridge.to + kBridgeEnd; })) + { + span.track->eEnvironment = e_bridge; + ++bridged; + } auto const changed{states.size()}; auto const added{created.size()}; state.created = created; @@ -1499,6 +1513,8 @@ void editor_mode::profile_apply(bool const Terrain) state.status = format(STR_P("Grade line applied: %zu path changed, %zu added, largest departure %.3f m", "Grade line applied: %zu paths changed, %zu added, largest departure %.3f m", changed), changed, added, state.departure); if (false == scene::Layers.empty()) state.status += STR_C("\nThe paths and the design (//$p lines) go to the scenery files on save"); + if (bridged > 0) + state.status += format(STR_P("\nOver a bridge or a viaduct: %zu path", "\nOver a bridge or a viaduct: %zu paths", bridged), bridged); auto const name = [](TTrack const *Track) { return name_of(*Track); }; auto const closed{std::count_if(gaps.begin(), gaps.end(), [](auto const &Gap) { return Gap.closed; })}; if (closed > 0) diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 54968b17..4a2b9083 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -8960,6 +8960,12 @@ msgstr "Serwis nie zwrócił modelu terenu" msgid "\nThe paths and the design (//$p lines) go to the scenery files on save" msgstr "\nTory i projekt (wiersze //$p) trafią do plików scenerii przy zapisie" +msgid "\nOver a bridge or a viaduct: %zu path" +msgid_plural "\nOver a bridge or a viaduct: %zu paths" +msgstr[0] "\nNa moście lub wiadukcie: %zu tor" +msgstr[1] "\nNa moście lub wiadukcie: %zu tory" +msgstr[2] "\nNa moście lub wiadukcie: %zu torów" + msgid "\nPaths off the route brought to the height of the switches:" msgstr "\nTory spoza trasy dociągnięte do wysokości rozjazdów:"