From 540dae420f4e1a9fd06965fcd2f14370203fee82 Mon Sep 17 00:00:00 2001 From: maj00r Date: Sat, 24 Oct 2026 02:18:58 +0200 Subject: [PATCH] fix(sleepers): the timbers a switch shares with a track with no model of timbers of its own laid of the model of the switch, not of the one of eu07.ini without the fastenings: the model of a track with none of its own that of the tracks most of its timbers carry with one, and a timber carrying none of them, as under the short tracks between the halves of a slip, of the model of the nearest timber with one up to 1.5 m away; the fastenings and checkseats selftests take the model the same way --- application/editormodeselftestsleepers.cpp | 5 ++-- world/SleeperFastenings.cpp | 10 ++++--- world/SleeperLayout.cpp | 35 ++++++++++++++++++++-- world/SleeperModels.cpp | 21 +++++++++++++ world/SleeperRules.h | 3 ++ world/TrackSleepers.cpp | 1 + world/TrackSleepers.h | 5 ++++ 7 files changed, 72 insertions(+), 8 deletions(-) diff --git a/application/editormodeselftestsleepers.cpp b/application/editormodeselftestsleepers.cpp index cf9d6c51..2188df66 100644 --- a/application/editormodeselftestsleepers.cpp +++ b/application/editormodeselftestsleepers.cpp @@ -156,7 +156,8 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c std::string name, detail; words >> name >> detail; auto *track{simulation::Paths.find(name)}; - auto const *model{track != nullptr ? track_sleepers::timber_model(*track) : nullptr}; + auto const beams{track != nullptr ? track_sleepers::place(*track) : std::vector{}}; + auto const *model{track != nullptr ? track_sleepers::timber_model(*track, beams) : nullptr}; if (model == nullptr || false == model->fastened()) { WriteLog("SELFTEST fastenings: no track " + name + " with the fastenings in the model of its timbers"); @@ -164,7 +165,7 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c } track_sleepers::fastened_rails const rails{*track}; std::array counts{}; - for (auto const &beam : track_sleepers::place(*track)) + for (auto const &beam : beams) { if (beam.plain) continue; diff --git a/world/SleeperFastenings.cpp b/world/SleeperFastenings.cpp index e047230d..3a616712 100644 --- a/world/SleeperFastenings.cpp +++ b/world/SleeperFastenings.cpp @@ -915,14 +915,15 @@ std::vector check_fastenings() { if( track == nullptr || track->m_editorremoved || false == track->m_sleepers3d.active ) { continue; } - auto const *model { timber_model( *track ) }; + auto const beams { place( *track ) }; + auto const *model { timber_model( *track, beams ) }; if( model == nullptr || false == model->fastened() ) { continue; } fastening_row row; row.track = track->name(); std::optional rails; - for( auto const &beam : place( *track ) ) { + for( auto const &beam : beams ) { if( beam.plain ) { continue; } @@ -1036,12 +1037,13 @@ std::vector check_rail_seats() { if( track == nullptr || track->m_editorremoved || false == track->m_sleepers3d.active ) { continue; } - auto const *model { timber_model( *track ) }; + auto const beams { place( *track ) }; + auto const *model { timber_model( *track, beams ) }; if( model == nullptr || false == model->fastened() ) { continue; } std::optional rails; - for( auto const &beam : place( *track ) ) { + for( auto const &beam : beams ) { if( beam.plain ) { continue; } diff --git a/world/SleeperLayout.cpp b/world/SleeperLayout.cpp index f22623f8..5fc40183 100644 --- a/world/SleeperLayout.cpp +++ b/world/SleeperLayout.cpp @@ -152,6 +152,7 @@ public: keep_apart(); fill_gaps(); keep_apart(); + share_panels(); for( auto const &laid : beams ) { result->beams[ laid.owner ].push_back( laid.value ); } @@ -553,17 +554,47 @@ private: // a sleeper of a plain track running alone and square to it, the rest are the timbers of the switches and crossings auto const carried { std::count_if( bands.begin(), bands.end(), []( band const &Band ) { return Band.carried; } ) }; auto const plain { path.track->eType == tt_Normal && carried == 1 && pieces.size() == 1 && false == pieces.front().clipped && std::abs( glm::dot( forward, along ) ) > 0.9998 && false == crossing_timber( path, Distance ) }; + auto const owner { std::find_if( bands.begin(), bands.end(), []( band const &Band ) { + return Band.carried && false == Band.path->track->m_timber_model_name.empty(); } ) }; + TTrack const *panel { owner != bands.end() ? owner->path->track : nullptr }; for( std::size_t index = 0; index < pieces.size(); ++index ) { auto const &piece { pieces[ index ] }; auto const &way { turned[ index ] }; if( way == along ) { - beams.push_back( { path.track, { top + rolledacross * piece.from, top + rolledacross * piece.to, glm::dvec3 { along.x, 0.0, along.y }, plain, piece.corefrom - piece.from, piece.coreto - piece.from } } ); + beams.push_back( { path.track, { top + rolledacross * piece.from, top + rolledacross * piece.to, glm::dvec3 { along.x, 0.0, along.y }, plain, piece.corefrom - piece.from, piece.coreto - piece.from, panel } } ); continue; } // turned about where the middle of its track crosses the line auto const centre { top + rolledacross * piece.single->centre }; glm::dvec3 const turnedacross { glm::dvec3 { -way.y, 0.0, way.x } * std::cos( roll ) - up * std::sin( roll ) }; - beams.push_back( { path.track, { centre + turnedacross * ( piece.from - piece.single->centre ), centre + turnedacross * ( piece.to - piece.single->centre ), glm::dvec3 { way.x, 0.0, way.y }, false, piece.corefrom - piece.from, piece.coreto - piece.from } } ); + beams.push_back( { path.track, { centre + turnedacross * ( piece.from - piece.single->centre ), centre + turnedacross * ( piece.to - piece.single->centre ), glm::dvec3 { way.x, 0.0, way.y }, false, piece.corefrom - piece.from, piece.coreto - piece.from, panel } } ); + } + } + + // the timbers whose lines carry no track with a model of timbers of its own of the model of the nearest timber with one + void share_panels() { + + std::vector> panels; + for( auto const &laid : beams ) { + if( false == laid.value.plain && laid.value.panel != nullptr ) { + panels.emplace_back( plan( 0.5 * ( laid.value.from + laid.value.to ) ), laid.value.panel ); + } + } + if( panels.empty() ) { + return; + } + for( auto &laid : beams ) { + if( laid.value.plain || laid.value.panel != nullptr ) { + continue; + } + auto const middle { plan( 0.5 * ( laid.value.from + laid.value.to ) ) }; + auto nearest { panel_reach }; + for( auto const &[ at, panel ] : panels ) { + if( auto const distance { glm::length( at - middle ) }; distance < nearest ) { + nearest = distance; + laid.value.panel = panel; + } + } } } diff --git a/world/SleeperModels.cpp b/world/SleeperModels.cpp index 003dab2c..9138450d 100644 --- a/world/SleeperModels.cpp +++ b/world/SleeperModels.cpp @@ -216,6 +216,27 @@ beam_model const *timber_model( TTrack const &Track ) { return timber_model(); } +beam_model const *timber_model( TTrack const &Track, std::vector const &Beams ) { + + if( false == Track.m_timber_model_name.empty() ) { + return timber_model( Track ); + } + std::vector> panels; + for( auto const &beam : Beams ) { + if( beam.plain || beam.panel == nullptr ) { + continue; + } + auto found { std::find_if( panels.begin(), panels.end(), [&]( auto const &Panel ) { return Panel.first == beam.panel; } ) }; + if( found == panels.end() ) { + panels.emplace_back( beam.panel, 0 ); + found = std::prev( panels.end() ); + } + ++found->second; + } + auto const most { std::max_element( panels.begin(), panels.end(), []( auto const &Left, auto const &Right ) { return Left.second < Right.second; } ) }; + return ( most != panels.end() ? timber_model( *most->first ) : timber_model( Track ) ); +} + beam_model const *plain_model( TTrack const &Track ) { if( Track.m_sleeper_enabled && false == Track.m_sleeper_model_name.empty() ) { diff --git a/world/SleeperRules.h b/world/SleeperRules.h index 34e85b47..b6fa0b8d 100644 --- a/world/SleeperRules.h +++ b/world/SleeperRules.h @@ -50,6 +50,9 @@ double constexpr level_tolerance { 1.0 }; // how far around a track the tracks are taken in to lay its beams: those whose beams it carries, or which carry its own, and // those which set where the beams of these go double constexpr around_margin { 20.0 }; +// a timber whose line carries no track with a model of timbers of its own, as under the short tracks between the halves of a slip, +// is of the model of the nearest timber with one so far from it +double constexpr panel_reach { 1.5 }; // how many beams make a run drawn or left out together std::size_t constexpr chunk_beams { 16 }; // how far apart the points of the rails are, as the places they lie on the timbers are found diff --git a/world/TrackSleepers.cpp b/world/TrackSleepers.cpp index 56843763..34709d4b 100644 --- a/world/TrackSleepers.cpp +++ b/world/TrackSleepers.cpp @@ -319,6 +319,7 @@ void lay( TTrack &Track, bool const Fastened ) { laid.laying = true; auto beams { in_background() ? std::move( placed ) : place( Track ) }; laid.laying = false; + laid.timbers = timber_model( Track, beams ); // the layout of the cluster isn't needed by this track any more, the instances below are what's drawn forget_layout( Track ); instance( Track, std::move( beams ), Fastened ); diff --git a/world/TrackSleepers.h b/world/TrackSleepers.h index f6c3730b..596544d6 100644 --- a/world/TrackSleepers.h +++ b/world/TrackSleepers.h @@ -56,6 +56,8 @@ struct beam { bool plain { false }; // a sleeper of a track running alone, square to it, not a timber of a switch or a crossing double corefrom { 0.0 }; // the stretch the feet of the rails it carries take, along it from its from end double coreto { 0.0 }; + TTrack const *panel { nullptr }; // of the tracks its line carries, the one with a model of timbers of its own: its own track first; else + // that of the nearest timber with one }; // a model sleepers or timbers are made of, as found in it @@ -119,6 +121,9 @@ beam_model const *plain_model(); // the models the track lays its beams of: those set for it with timbermodel and sleepermodel, else the ones of eu07.ini beam_model const *timber_model( TTrack const &Track ); beam_model const *plain_model( TTrack const &Track ); +// the model the track lays the timbers of the beams of: its own, else that of the tracks most of its timbers carry with one of their +// own, as a switch it shares its timbers with, else the one of eu07.ini +beam_model const *timber_model( TTrack const &Track, std::vector const &Beams ); // how long the plain sleepers of the track are, of the model it lays them of; 2.6 m at the standard gauge where there's none double plain_length( TTrack const &Track ); // whether the track has the ballast the simulator makes, which the beams are laid on when there's a model of them