From 3dbe8a75f80fd109ccd0dab5bccf96b23faf33d8 Mon Sep 17 00:00:00 2001 From: maj00r Date: Sat, 31 Oct 2026 18:27:19 +0100 Subject: [PATCH] feat(track): the foot of the ballast stays on the ground as designed Rails, sleepers and the top of the ballast go with the unevenness, the slope of the ballast takes up the difference. --- tests/unit/test_track_uneven.cpp | 12 ++++++++++++ track_uneven_doc.md | 2 +- world/Segment.cpp | 7 ++++--- world/Track.cpp | 5 +++++ world/Track.h | 2 ++ world/TrackUneven.h | 7 +++++++ 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_track_uneven.cpp b/tests/unit/test_track_uneven.cpp index afea0b67..46bd1e5f 100644 --- a/tests/unit/test_track_uneven.cpp +++ b/tests/unit/test_track_uneven.cpp @@ -165,6 +165,18 @@ TEST_CASE("uneven track: the value along a track meets its neighbours exactly") } } +TEST_CASE("uneven track: the foot of the ballast stays where it was") +{ + double const rail{0.18}; + double const height{0.38}; + CHECK(track_uneven::bed_share(0.0, rail, height) == doctest::Approx(1.0)); // rail top + CHECK(track_uneven::bed_share(rail, rail, height) == doctest::Approx(1.0)); // top of the ballast + CHECK(track_uneven::bed_share(rail + 0.5 * height, rail, height) == doctest::Approx(0.5)); + CHECK(track_uneven::bed_share(rail + height, rail, height) == doctest::Approx(0.0)); // its foot + CHECK(track_uneven::bed_share(rail + 2.0 * height, rail, height) == doctest::Approx(0.0)); + CHECK(track_uneven::bed_share(rail + 1.0, rail, 0.0) == doctest::Approx(1.0)); // no ballast +} + TEST_CASE("uneven track: the rails dip around a joint") { CHECK(track_uneven::dip(0.0) == doctest::Approx(1.0)); diff --git a/track_uneven_doc.md b/track_uneven_doc.md index aca9ca32..8570fd94 100644 --- a/track_uneven_doc.md +++ b/track_uneven_doc.md @@ -60,7 +60,7 @@ Każda składowa łączy się osobno: - **Styki:** zapadnięcia są co `fSoundDistance`, na długości 6 m, i biegną dalej przez kolejne tory o tym samym odstępie. Stukot osi wypada w miejscu zapadnięcia. Przy odstępie 6 m lub mniejszym oraz na rozjazdach zapadnięć nie ma. - **Przechyłka:** dolicza się do projektowej. Jest pochyleniem w planie mapy, więc tory wpisane ku sobie spotykają się na tej samej przechyłce. Na rozjazdach tylko ta wynikająca z fal, bez osobnego siadania toków. -Za nierównością idą szyny, podsypka, `sleepermodel`, osie, przechył pudła i bujanie kabiny. +Za nierównością idą szyny, wierzch podsypki, `sleepermodel`, osie, przechył pudła i bujanie kabiny. Skarpa podsypki przejmuje różnicę, a jej spód zostaje w projekcie, na terenie. Nie idą za nią modele z includów (np. napędy i podkłady rozjazdów), teren ani sieć trakcyjna, więc przy dużych wartościach szyna rozjeżdża się z takim modelem o kilka centymetrów. diff --git a/world/Segment.cpp b/world/Segment.cpp index 84a0c22b..3668ed13 100644 --- a/world/Segment.cpp +++ b/world/Segment.cpp @@ -408,11 +408,12 @@ void TSegment::settle_vertex( gfx::basic_vertex &Vertex, glm::dvec3 const &Origi if( false == m_uneven ) { return; } - // the rails settling apart turn about the middle of the track, what's outside them follows the rail + // the rails settling apart turn about the middle of the track, what's outside them follows the rail; the foot of the ballast stays auto const gauge { std::max( std::abs( pOwner->fTrackWidth ), 0.1f ) }; auto const across { std::clamp( glm::dot( Vertex.position - Position, Parallel ), -0.5f * gauge, 0.5f * gauge ) }; - Vertex.position.y -= static_cast( track_uneven::stray_per_mm * pOwner->uneven_at( Distance ).cross * track_uneven::level( Origin + glm::dvec3 { Position }, glm::dvec3 { Parallel } ) * across / gauge ); - Vertex.position += glm::vec3 { stray( Origin + glm::dvec3 { Vertex.position }, Distance ) }; + auto const share { pOwner->uneven_bed_share( Position.y - Vertex.position.y ) }; + Vertex.position.y -= static_cast( share * track_uneven::stray_per_mm * pOwner->uneven_at( Distance ).cross * track_uneven::level( Origin + glm::dvec3 { Position }, glm::dvec3 { Parallel } ) * across / gauge ); + Vertex.position += glm::vec3 { share * stray( Origin + glm::dvec3 { Vertex.position }, Distance ) }; } void TSegment::design_position(double const fDistance, glm::dvec3 &position, glm::vec3 &rotation) const { diff --git a/world/Track.cpp b/world/Track.cpp index ff5a378a..b5589eed 100644 --- a/world/Track.cpp +++ b/world/Track.cpp @@ -731,6 +731,11 @@ bool TTrack::is_uneven() const { return declared.up > 0.0 || declared.side > 0.0 || declared.cross > 0.0 || joint_depth() > 0.0; } +double TTrack::uneven_bed_share( double const Depth ) const { + + return track_uneven::bed_share( Depth, std::abs( track_rail_profile( m_profile1.second ).front().position.y ), fTexHeight1 ); +} + track_uneven::amounts TTrack::uneven_at( double const Distance ) const { if( eType == tt_Switch ) { diff --git a/world/Track.h b/world/Track.h index ac6d1425..92516ee6 100644 --- a/world/Track.h +++ b/world/Track.h @@ -351,6 +351,8 @@ public: bool is_uneven() const; // the unevenness at a distance along the track, meeting the tracks joined at its ends track_uneven::amounts uneven_at( double const Distance ) const; + // how much of the displacement a point of the profile at the depth takes + double uneven_bed_share( double const Depth ) const; double joint_depth() const; // mm double joint_dip( double const Distance ) const; // m // distance from the rail joint behind; negative: the track has no joints that dip diff --git a/world/TrackUneven.h b/world/TrackUneven.h index 9031c6ca..182f3799 100644 --- a/world/TrackUneven.h +++ b/world/TrackUneven.h @@ -109,6 +109,13 @@ inline double along( double const Full, double const Atstart, double const Atend return ends + ( Full - ends ) * std::min( smooth( Distance / fade ), smooth( ( Length - Distance ) / fade ) ); } +// how much of the displacement a point of the profile takes, by its depth under the rail top: all of it down to the foot of the rail, +// none at the foot of the ballast, which stays on the ground as designed +inline double bed_share( double const Depth, double const Rail, double const Height ) { + + return Height > 0.0 ? std::clamp( 1.0 - ( Depth - Rail ) / Height, 0.0, 1.0 ) : 1.0; +} + // distance from the joint behind the point inline double rail_phase( double const Along, double const Rail ) {