From c4772823b3b89d59bc422db80f53ed260dc242c1 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 14 Oct 2026 14:01:08 +0200 Subject: [PATCH] feat(track): a check rail is held to its running rail by spacer blocks every 0.5 m along its straight part, laid in the same way as the spacers of a guard rail at a wide crossing, below the head from the web of the running rail to the face of the check rail, so the flangeway stays open above them; a check rail replaced by a wing rail gets none; the spacing of spacers and castings is shared by guard rails and check rails --- world/RailCrossings.cpp | 6 +++++ world/RailLayer.cpp | 8 ++---- world/RailLayout.h | 5 ++++ world/RailPlan.cpp | 60 +++++++++++++++++++++++++++++------------ world/TrackRails.h | 1 + 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/world/RailCrossings.cpp b/world/RailCrossings.cpp index 5f749fd1..8ffbde5b 100644 --- a/world/RailCrossings.cpp +++ b/world/RailCrossings.cpp @@ -126,6 +126,12 @@ bool replaced_check( std::vector const &Checks, double const Wingend return false; } +bool replaced_by_wing( check_rail const &Check, knots::line const &Wingends ) { + + auto farend { 0.0 }; + return std::any_of( Wingends.begin(), Wingends.end(), [&]( glm::dvec2 const &Wing ) { return replaced_check( { Check }, Wing.x, Wing.y, farend ); } ); +} + // gauge line of a rail of a track, along a path of a switch from past its blades when Blades gauge_line const &rail_line( TTrack const &Track, int const Path, int const Side, bool const Blades ) { diff --git a/world/RailLayer.cpp b/world/RailLayer.cpp index cb9dd0fe..0df4538f 100644 --- a/world/RailLayer.cpp +++ b/world/RailLayer.cpp @@ -607,19 +607,15 @@ void TTrack::rail_layer::lay_guards() { void TTrack::rail_layer::lay_checks() { auto const &shape { m_drawing.checkshapes[ m_side > 0 ? 0 : 1 ] }; - auto const &wingends { ours( m_layout.wingends ) }; for( auto const &check : m_layout.checks[ m_path ][ m_side > 0 ] ) { - auto const replaced { std::any_of( wingends.begin(), wingends.end(), [&]( glm::dvec2 const &Wing ) { - auto farend { 0.0 }; - return replaced_check( { check }, Wing.x, Wing.y, farend ); } ) }; auto const a { std::max( check.from, m_from ) }; auto const b { std::min( check.to, m_length ) }; - if( replaced || b - a <= 0.01 ) { + if( replaced_by_wing( check, ours( m_layout.wingends ) ) || b - a <= 0.01 ) { continue; } piece( shape, - { .from = a, .to = b, .offsets = flared( check.from, check.to, std::min( 0.6, 0.4 * ( check.to - check.from ) ), 0.0, check.flarefrom, check.flareto ), .capfrom = a > 0.0 || check.from >= 0.0, .capto = check.to <= m_length, .rolled = false }, + { .from = a, .to = b, .offsets = flared( check.from, check.to, check_flare( check ), 0.0, check.flarefrom, check.flareto ), .capfrom = a > 0.0 || check.from >= 0.0, .capto = check.to <= m_length, .rolled = false }, { a, b, { a == check.from ? check.frombend : 0.0 }, { b == check.to ? check.tobend : 0.0 } } ); } } diff --git a/world/RailLayout.h b/world/RailLayout.h index fae6d5ad..f596844a 100644 --- a/world/RailLayout.h +++ b/world/RailLayout.h @@ -34,6 +34,7 @@ double constexpr casting_reach { 0.3 }; double constexpr square_slope { 3.0 }; double constexpr spacer_length { 0.12 }; double constexpr spacer_pitch { 0.5 }; +double constexpr check_flangeway { 0.041 }; // where the inner rails of the two paths of a switch cross, found from their gauge lines behind the blades struct switch_frog { @@ -87,6 +88,10 @@ struct check_rail { double tobend { 0.0 }; }; using check_rail_set = std::array, 2>, 2>; // path, right side +inline double check_flare( check_rail const &Check ) { + + return std::min( guard_flare, 0.4 * ( Check.to - Check.from ) ); +} // a rail broken where another one crosses it: the part coming from below ends at end, the part going on starts at start struct rail_cut { double at { 0.0 }; diff --git a/world/RailPlan.cpp b/world/RailPlan.cpp index f9ebac10..d3869015 100644 --- a/world/RailPlan.cpp +++ b/world/RailPlan.cpp @@ -142,17 +142,10 @@ track_rails::rail_cut corner_cut( track_rails::rail_crossing const &Crossing, do return cut; } -std::vector guard_fills( track_rails::guard_rail const &Guard, track_rails::rail_size const &Size ) { +std::vector spacer_fills( track_rails::rail_fill const &Whole, double const From, double const To, knots::line const &Castings ) { - auto const offset { Size.web - ( flangeway + Size.head ) }; - auto const flare { std::min( guard_flare, 0.25 * ( Guard.to - Guard.from ) ) }; - track_rails::rail_fill whole; - whole.web = true; - whole.lift = ( Guard.through ? 0.0 : -0.001 ); - whole.inner = { { Guard.from, offset - ( Guard.flarefrom ? 0.02 : 0.0 ) }, { Guard.from + flare, offset }, { Guard.to - flare, offset }, { Guard.to, offset - ( Guard.flareto ? 0.02 : 0.0 ) } }; - whole.outer = { { Guard.from, Size.web }, { Guard.to, Size.web } }; knots::line pieces; - for( auto const &casting : Guard.castings ) { + for( auto const &casting : Castings ) { if( false == pieces.empty() && casting.x <= pieces.back().y ) { pieces.back().y = std::max( pieces.back().y, casting.y ); } @@ -161,14 +154,14 @@ std::vector guard_fills( track_rails::guard_rail const & } } knots::line spacers; - auto const place = [&]( double const From, double const To, double const Step ) { - auto const count { static_cast( std::floor( ( To - From - 0.5 * spacer_length ) / spacer_pitch ) ) }; + auto const place = [&]( double const Start, double const End, double const Step ) { + auto const count { static_cast( std::floor( ( End - Start - 0.5 * spacer_length ) / spacer_pitch ) ) }; for( int i = 1; i <= count; ++i ) { - auto const at { ( Step > 0.0 ? From : To ) + Step * i * spacer_pitch }; + auto const at { ( Step > 0.0 ? Start : End ) + Step * i * spacer_pitch }; spacers.emplace_back( at - 0.5 * spacer_length, at + 0.5 * spacer_length ); } }; - auto const first { Guard.from + spacer_length }; - auto const last { Guard.to - spacer_length }; + auto const first { From + spacer_length }; + auto const last { To - spacer_length }; if( pieces.empty() ) { place( first - spacer_pitch, last, 1.0 ); } @@ -184,9 +177,9 @@ std::vector guard_fills( track_rails::guard_rail const & pieces.insert( pieces.end(), spacers.begin(), spacers.end() ); std::vector fills; for( auto const &piece : pieces ) { - auto fill { whole }; - fill.from = std::max( piece.x, Guard.from ); - fill.to = std::min( piece.y, Guard.to ); + auto fill { Whole }; + fill.from = std::max( piece.x, From ); + fill.to = std::min( piece.y, To ); fill.capfrom = true; fill.capto = true; if( fill.to - fill.from > 0.01 ) { @@ -196,6 +189,29 @@ std::vector guard_fills( track_rails::guard_rail const & return fills; } +std::vector guard_fills( track_rails::guard_rail const &Guard, track_rails::rail_size const &Size ) { + + auto const offset { Size.web - ( flangeway + Size.head ) }; + auto const flare { std::min( guard_flare, 0.25 * ( Guard.to - Guard.from ) ) }; + track_rails::rail_fill whole; + whole.web = true; + whole.lift = ( Guard.through ? 0.0 : -0.001 ); + whole.inner = { { Guard.from, offset - ( Guard.flarefrom ? 0.02 : 0.0 ) }, { Guard.from + flare, offset }, { Guard.to - flare, offset }, { Guard.to, offset - ( Guard.flareto ? 0.02 : 0.0 ) } }; + whole.outer = { { Guard.from, Size.web }, { Guard.to, Size.web } }; + return spacer_fills( whole, Guard.from, Guard.to, Guard.castings ); +} + +std::vector check_fills( track_rails::check_rail const &Check, track_rails::rail_size const &Size ) { + + auto const from { Check.from + check_flare( Check ) }; + auto const to { Check.to - check_flare( Check ) }; + track_rails::rail_fill whole; + whole.web = true; + whole.inner = { { from, -check_flangeway }, { to, -check_flangeway } }; + whole.outer = { { from, Size.web }, { to, Size.web } }; + return spacer_fills( whole, from, to, {} ); +} + bool goes_through( TTrack const &Track, TTrack const &Other ) { if( Track.name() != Other.name() ) { @@ -468,6 +484,16 @@ rail_layout TTrack::rail_planner::lay_out() { if( m_track.eType == tt_Switch ) { switch_frog(); } + for( int path = 0; path < 2; ++path ) { + for( int side = 0; side < 2; ++side ) { + for( auto const &check : m_layout.checks[ path ][ side ] ) { + if( false == replaced_by_wing( check, m_layout.wingends[ path ][ side ] ) ) { + auto const fills { check_fills( check, m_size ) }; + m_layout.fills[ path ][ side ].insert( m_layout.fills[ path ][ side ].end(), fills.begin(), fills.end() ); + } + } + } + } return m_layout; } diff --git a/world/TrackRails.h b/world/TrackRails.h index b7404661..8a7e7edf 100644 --- a/world/TrackRails.h +++ b/world/TrackRails.h @@ -38,6 +38,7 @@ int path_count( TTrack const &Track ); TTrack *path_neighbour( TTrack const &Track, int const Path, int const End ); double knuckle_bend( double const Into ); bool replaced_check( std::vector const &Checks, double const Wingend, double const Direction, double &Far ); +bool replaced_by_wing( check_rail const &Check, knots::line const &Wingends ); gauge_line const &rail_line( TTrack const &Track, int const Path, int const Side, bool const Blades ); check_rail_set const &rail_checks( TTrack &Track ); glm::dvec3 rail_point( TTrack const &Track, int const Path, int const Side, double const Distance, double const Lateral );