From 21eca4d9a64b8188f22b1d364064dabd9780662e Mon Sep 17 00:00:00 2001 From: maj00r Date: Sat, 17 Oct 2026 09:59:06 +0200 Subject: [PATCH] fix(track): the tip of a blade is where the diverging track of the switch is broken into pieces near the start, as a template lays it from the scheme of the switch, so blades of 60E1 and 49E1 switches keep the cut tips of their tables at 1.248, 1.500, 2.165, 2.633 and 3.335 m and blades of S42 switches start at the end of their pre-blade stretch; without such a break a blade is told angled or tangent by its angle to the stock rail taken back to where it touches it, read from the nearest point of the other rail instead of its nearest sample, which took the blade of the main track of an S42-190-1:6,6 for a tangent one --- world/RailCrossings.cpp | 57 ++++++++++++++++++++++++++++++----------- world/RailLayout.h | 1 + world/Segment.h | 4 +++ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/world/RailCrossings.cpp b/world/RailCrossings.cpp index 412a1ddc..d80be2d1 100644 --- a/world/RailCrossings.cpp +++ b/world/RailCrossings.cpp @@ -710,9 +710,6 @@ track_rails::switch_blade const &TTrack::blade( int const Path ) const { glm::dvec3 point, tangent; ours.at( 0.0, point, tangent ); auto otherat { nearest_distance( theirs, point ) }; - glm::dvec3 start, along; - theirs.at( otherat, start, along ); - auto const tangential { std::abs( glm::cross( glm::normalize( glm::dvec3 { tangent.x, 0.0, tangent.z } ), glm::normalize( glm::dvec3 { along.x, 0.0, along.z } ) ).y ) < blade_tangent }; for( int i = 0; i <= count; ++i ) { auto const distance { length * i / count }; ours.at( distance, point, tangent ); @@ -736,9 +733,48 @@ track_rails::switch_blade const &TTrack::blade( int const Path ) const { } } return -1.0; }; - result.tip = reach( blade_point ); - if( auto const nose { reach( blade_nose ) }; tangential && nose >= 0.0 ) { - result.tip = std::max( result.tip, nose - blade_planed ); + auto const level = [&]( double const Distance ) { + for( std::size_t i = 1; i < gauge.size(); ++i ) { + if( gauge[ i ].x >= Distance ) { + auto const &a { gauge[ i - 1 ] }; + auto const &b { gauge[ i ] }; + return glm::mix( a.y, b.y, std::clamp( ( Distance - a.x ) / std::max( b.x - a.x, 1e-9 ), 0.0, 1.0 ) ); + } + } + return gauge.back().y; }; + auto const turn = [&]( double const Distance ) { + glm::dvec3 here, heading, line, along; + ours.at( Distance, here, heading ); + auto there { nearest_distance( theirs, here ) }; + lateral( theirs, side, here, there ); + theirs.at( there, line, along ); + return std::abs( glm::cross( glm::normalize( glm::dvec3 { heading.x, 0.0, heading.z } ), glm::normalize( glm::dvec3 { along.x, 0.0, along.z } ) ).y ); }; + auto const thin { reach( blade_point ) }; + auto const thick { reach( blade_nose ) }; + auto angled { false }; + if( thin >= 0.0 && thick > thin ) { + auto const at { turn( thin ) }; + auto const bending { ( turn( thick ) - at ) / ( thick - thin ) }; + angled = std::sqrt( std::max( 0.0, at * at - 2.0 * bending * blade_point ) ) >= blade_tangent; + } + result.tip = ( angled || thick < 0.0 ? thin : std::max( thin, thick - blade_planed ) ); + for( auto const path : { Path, 1 - Path } ) { + auto const &segment { *SwitchExtension->Segments[ path ] }; + auto mark { -1.0 }; + for( std::size_t piece = 1; piece < segment.PieceCount(); ++piece ) { + auto const at { segment.PieceStart( piece ) }; + auto const apart { level( at ) }; + if( apart > blade_mark ) { + break; + } + if( angled || apart >= blade_gap ) { + mark = at; + } + } + if( mark >= 0.0 ) { + result.tip = std::max( mark, reach( blade_gap ) ); + break; + } } result.full = reach( size.head + blade_gap ); auto const clear { reach( size.head + blade_clearance ) }; @@ -790,15 +826,6 @@ track_rails::switch_blade const &TTrack::blade( int const Path ) const { bladetop = std::max( bladetop, static_cast( profile[ i ].position.y ) ); } result.drop = ( half > 0 ? std::max( 0.0, size.crest - bladetop ) : 0.0 ); - auto const level = [&]( double const Distance ) { - for( std::size_t i = 1; i < gauge.size(); ++i ) { - if( gauge[ i ].x >= Distance ) { - auto const &a { gauge[ i - 1 ] }; - auto const &b { gauge[ i ] }; - return glm::mix( a.y, b.y, std::clamp( ( Distance - a.x ) / std::max( b.x - a.x, 1e-9 ), 0.0, 1.0 ) ); - } - } - return gauge.back().y; }; auto const lowered { reach( blade_lowered + blade_gap ) }; auto const cutto { std::min( result.heel, foot >= 0.0 ? foot + blade_spacing : result.heel ) }; for( auto distance { result.tip }; distance < cutto + 0.5 * blade_spacing; distance += blade_spacing ) { diff --git a/world/RailLayout.h b/world/RailLayout.h index 0cc4f740..489ec3c9 100644 --- a/world/RailLayout.h +++ b/world/RailLayout.h @@ -41,6 +41,7 @@ double constexpr blade_point { 0.003 }; double constexpr blade_nose { 0.005 }; double constexpr blade_planed { 0.125 }; double constexpr blade_tangent { 0.00175 }; +double constexpr blade_mark { 0.010 }; double constexpr blade_gap { 0.0005 }; double constexpr blade_lowered { 0.050 }; double constexpr blade_spacing { 0.5 }; diff --git a/world/Segment.h b/world/Segment.h index 8df81d4a..1de9b005 100644 --- a/world/Segment.h +++ b/world/Segment.h @@ -123,6 +123,10 @@ public: std::size_t PieceCount() const { return m_pieces.size(); }; + inline + double + PieceStart( std::size_t const Piece ) const { + return m_starts[ Piece ]; }; double ComputeLength() const; // McZapkie-150503 // finds point on segment closest to specified point in 3d space. returns: point on segment as value in range 0-1