From 572ee569dc659114e72e314962551fa6524b24c0 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 14 Oct 2026 11:08:02 +0200 Subject: [PATCH] feat(track): at a crossing at a wide angle every head of one track meets every head of the other in a mitre from the corner of their flangeways to the far corner of the heads, the way the corners of a cast diamond look, so the running rails and guard rails of both tracks form closed corners and only the flangeways stay open, instead of the rails of one track going through and the other ones glued to their sides; corner_cut gives the mitres, as bends of the running rails and as miters of the guard rails; the fit of a loft end to the profile of another rail and the field and gauge outlines of the rail profile are gone with the gluing --- world/RailLayer.cpp | 15 ++++--------- world/RailLayout.h | 2 -- world/RailPlan.cpp | 50 ++++++++++++++++++------------------------- world/RailProfile.cpp | 30 -------------------------- world/Segment.cpp | 25 ++++++---------------- world/Segment.h | 1 - 6 files changed, 31 insertions(+), 92 deletions(-) diff --git a/world/RailLayer.cpp b/world/RailLayer.cpp index 9c3f2bbb..4d6b07ed 100644 --- a/world/RailLayer.cpp +++ b/world/RailLayer.cpp @@ -568,11 +568,9 @@ void TTrack::rail_layer::lay_guards() { sort_cuts( cuts ); auto start { guard.from }; auto startmiter { guard.frommiter }; - rail_cut startcut; for( std::size_t i = 0; i <= cuts.size(); ++i ) { auto const end { i < cuts.size() ? cuts[ i ].end : guard.to }; auto const endmiter { i < cuts.size() ? cuts[ i ].endmiter : guard.tomiter }; - auto const endcut { i < cuts.size() ? cuts[ i ] : rail_cut {} }; auto const a { std::max( { mitered( startmiter, start, false ), m_from, 0.0 } ) }; auto const b { std::min( { mitered( endmiter, end, true ), m_length, mitered( guard.tomiter, guard.to, true ) } ) }; if( b - a > 0.01 ) { @@ -583,17 +581,14 @@ void TTrack::rail_layer::lay_guards() { ( ending == ( miter.y < 0.0 ) ? uppers : lowers ).emplace_back( 1.0 / miter.y, -miter.x / miter.y - m_guardoffset ); } } - auto const glued { a == start && false == startcut.startfit.empty() }; - auto const gluedend { b == end && false == endcut.endfit.empty() }; piece( m_profile, - { .from = a, .to = b, .offsets = offsets, .extents = envelope( uppers, true, a, b ), .inner = envelope( lowers, false, a, b ), .capfrom = false == glued, .capto = false == gluedend, .rolled = false }, - { a, b, { glued ? startcut.startbend : 0.0, glued ? startcut.startfit : knots::line {} }, { gluedend ? endcut.endbend : 0.0, gluedend ? endcut.endfit : knots::line {} } } ); + { .from = a, .to = b, .offsets = offsets, .extents = envelope( uppers, true, a, b ), .inner = envelope( lowers, false, a, b ), .capfrom = true, .capto = true, .rolled = false }, + { a, b, {}, {} } ); } if( i < cuts.size() && cuts[ i ].start > start ) { start = cuts[ i ].start; startmiter = cuts[ i ].startmiter; - startcut = cuts[ i ]; } } } @@ -639,7 +634,6 @@ void TTrack::rail_layer::lay_cuts( gfx::vertex_array const &Shape, std::vector m_from ? startbend : 0.0, start > m_from ? startfit : knots::line {} }, - { end < m_length ? Cuts[ i ].endbend : 0.0, end < m_length ? Cuts[ i ].endfit : knots::line {} } }; + { start > m_from ? startbend : 0.0 }, + { end < m_length ? Cuts[ i ].endbend : 0.0 } }; if( b - a > 0.01 ) { std::vector active; for( auto const &tread : Treads ) { @@ -714,7 +708,6 @@ void TTrack::rail_layer::lay_cuts( gfx::vertex_array const &Shape, std::vector 0.0 ? Cut.from - 0.005 : Cut.to + 0.005 ) / into, facing / into }; cut.startmiter = { at + ( into > 0.0 ? Cut.to + 0.005 : Cut.from - 0.005 ) / into, facing / into }; - auto const glue = [&]( knots::line const &Line, double const Edge, double &Position, glm::dvec2 &Miter, double &Bend, knots::line &Fit, bool &Cap ) { - if( Line.empty() ) { - return; - } - Position = at + Edge / into; - Miter = {}; - Bend = facing / into; - for( auto const &knot : Line ) { - Fit.emplace_back( knot.x, ( knot.y - Edge ) / into ); - } - Cap = false; }; - glue( into > 0.0 ? Cut.fromline : Cut.toline, into > 0.0 ? Cut.from : Cut.to, cut.end, cut.endmiter, cut.endbend, cut.endfit, cut.endcap ); - glue( into > 0.0 ? Cut.toline : Cut.fromline, into > 0.0 ? Cut.to : Cut.from, cut.start, cut.startmiter, cut.startbend, cut.startfit, cut.startcap ); Plan.cuts.push_back( cut ); } -std::vector glue_line( std::vector const &Outline, double const Base ) { +track_rails::rail_cut corner_cut( track_rails::rail_crossing const &Crossing, double const Face, double const Toward, double const Head, double const Other ) { - std::vector line; - for( auto const &knot : Outline ) { - line.emplace_back( knot.x, Base - knot.y ); - } - return line; + auto const mitre = [&]( double const Edge, double const Flange ) { + auto const slope { Flange * Other / ( Toward * Head ) }; + return glm::dvec2 { Crossing.at + ( Edge - Face * slope ) / Crossing.into, ( slope + Crossing.facing ) / Crossing.into }; }; + auto const rail { mitre( 0.0, 1.0 ) }; + auto const guard { mitre( flangeway, -1.0 ) }; + auto const rising { Crossing.into > 0.0 }; + track_rails::rail_cut cut; + cut.at = Crossing.at; + cut.endmiter = ( rising ? rail : guard ); + cut.startmiter = ( rising ? guard : rail ); + cut.end = cut.endmiter.x + cut.endmiter.y * Face; + cut.start = cut.startmiter.x + cut.startmiter.y * Face; + cut.endbend = cut.endmiter.y; + cut.startbend = cut.startmiter.y; + return cut; } track_rails::rail_fill guard_fill( track_rails::guard_rail const &Guard, track_rails::rail_size const &Size ) { @@ -443,15 +438,12 @@ void TTrack::rail_planner::cross_grooved( rail_crossing const &Crossing, rail_si void TTrack::rail_planner::cross_wide( rail_crossing const &Crossing, rail_size const &Other ) { - auto const through { goes_through( m_track, *Crossing.track ) }; - wide( plan_of( Crossing ), Crossing, { through ? 0.0 : -Other.head, flangeway, 0.0, m_size.head, through ? knots::line {} : glue_line( Other.field, 0.0 ) } ); - if( through ) { - add_tread( Crossing.path, Crossing.side, shared_span( Crossing, Other ), *Crossing.track, Crossing.otherpath ); - } + plan_of( Crossing ).cuts.push_back( corner_cut( Crossing, 0.0, -1.0, m_size.head, Other.head ) ); + add_tread( Crossing.path, Crossing.side, shared_span( Crossing, Other ), *Crossing.track, Crossing.otherpath ); auto const reach { ( flangeway + m_size.head + guard_reach ) / std::abs( Crossing.into ) }; track_rails::guard_rail guard { Crossing.at - reach, Crossing.at + reach }; - guard.through = through; - wide( guard.plan, Crossing, { -Other.head, through ? flangeway : flangeway + Other.head, m_guardoffset, m_guardoffset + m_size.head, glue_line( Other.field, 0.0 ), through ? knots::line {} : glue_line( Other.gauge, flangeway + Other.head ) } ); + guard.through = goes_through( m_track, *Crossing.track ); + guard.plan.cuts.push_back( corner_cut( Crossing, -flangeway, 1.0, m_size.head, Other.head ) ); merge_guard( m_layout.guards[ Crossing.path ][ Crossing.side > 0 ], guard ); } diff --git a/world/RailProfile.cpp b/world/RailProfile.cpp index c3a01cfd..e8d5b244 100644 --- a/world/RailProfile.cpp +++ b/world/RailProfile.cpp @@ -33,36 +33,6 @@ rail_size measure_rail( gfx::vertex_array const &Profile ) { } result.height = top - bottom; result.crest = top; - std::size_t firsttop { count }; - std::size_t lasttop { 0 }; - for( std::size_t i = 0; i < count; ++i ) { - if( Profile[ i ].position.y > top - 1e-4 ) { - firsttop = std::min( firsttop, i ); - lasttop = std::max( lasttop, i ); - } - } - knots::line sides[ 2 ]; - for( std::size_t i = 0; i < count; ++i ) { - auto const &point { Profile[ i ].position }; - if( i <= firsttop ) { - sides[ 0 ].emplace_back( point.y, point.x ); - } - if( i >= lasttop ) { - sides[ 1 ].emplace_back( point.y, point.x ); - } - } - for( auto &side : sides ) { - knots::sort( side ); - } - auto const mean = []( knots::line const &Side ) { - auto sum { 0.0 }; - for( auto const &knot : Side ) { - sum += knot.y; - } - return sum / std::max( Side.size(), 1 ); }; - auto const fieldfirst { mean( sides[ 0 ] ) > mean( sides[ 1 ] ) }; - result.field = sides[ fieldfirst ? 0 : 1 ]; - result.gauge = sides[ fieldfirst ? 1 : 0 ]; auto const middle { top - 0.5 * result.height }; auto webfrom { 1e9 }; auto webto { -1e9 }; diff --git a/world/Segment.cpp b/world/Segment.cpp index d52113c7..c5389f13 100644 --- a/world/Segment.cpp +++ b/world/Segment.cpp @@ -616,23 +616,13 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig } std::sort( stations.begin(), stations.end() ); stations.erase( std::unique( stations.begin(), stations.end(), []( double const A, double const B ) { return B - A < 0.001; } ), stations.end() ); - if( Span.fromend.bend != 0.0 || Span.toend.bend != 0.0 || false == Span.fromend.fit.empty() || false == Span.toend.fit.empty() ) { + if( Span.fromend.bend != 0.0 || Span.toend.bend != 0.0 ) { auto widest { 0.0 }; for( auto const &point : ShapePoints ) { widest = std::max( widest, std::abs( static_cast( Span.side * point.position.x - Span.halfwidth ) ) ); } - auto offsetmost { 0.0 }; - for( auto const &knot : Span.offsets ) { - offsetmost = std::max( offsetmost, std::abs( knot.y ) ); - } - auto const margin = [&]( loft_end const &End ) { - auto fitmost { 0.0 }; - for( auto const &knot : End.fit ) { - fitmost = std::max( fitmost, std::abs( knot.y ) ); - } - return std::abs( End.bend ) * ( End.fit.empty() ? widest : widest + offsetmost ) + fitmost + 0.001; }; - auto const frommargin { margin( Span.fromend ) }; - auto const tomargin { margin( Span.toend ) }; + auto const frommargin { std::abs( Span.fromend.bend ) * widest + 0.001 }; + auto const tomargin { std::abs( Span.toend.bend ) * widest + 0.001 }; stations.erase( std::remove_if( stations.begin(), stations.end(), [&]( double const Distance ) { return ( Distance > Span.from && Distance < Span.to ) && ( Distance < Span.from + frommargin || Distance > Span.to - tomargin ); } ), stations.end() ); @@ -671,17 +661,14 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig static loft_end const unshaped; auto const &shaped { Distance <= Span.from ? Span.fromend : Distance >= Span.to ? Span.toend : unshaped }; auto const bend { shaped.bend }; - auto const &fit { shaped.fit }; auto along { glm::vec3 { 0.f } }; - auto forward { glm::vec3 { 0.f } }; - if( bend != 0.0 || false == fit.empty() ) { + if( bend != 0.0 ) { glm::dvec3 ahead, behind, unused; plan_frame( std::min( Distance + 0.001, static_cast( fLength ) ), ahead, unused ); plan_frame( std::max( Distance - 0.001, 0.0 ), behind, unused ); auto const tangent { glm::dvec3 { ahead.x - behind.x, 0.0, ahead.z - behind.z } }; if( glm::length2( tangent ) > 0.0 ) { - forward = glm::vec3 { glm::normalize( tangent ) }; - along = forward * static_cast( bend ); + along = glm::vec3 { glm::normalize( tangent ) } * static_cast( bend ); } } Ring.clear(); @@ -763,7 +750,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig wall = ( side > 0 ? outerwall : innerwall ); } } - auto pt { parallel * static_cast( Span.side * ( Span.halfwidth + outside + offset ) ) + position + along * static_cast( outside + offset ) + forward * static_cast( knots::sample( fit, shape.position.y, 0.0 ) ) }; + auto pt { parallel * static_cast( Span.side * ( Span.halfwidth + outside + offset ) ) + position + along * static_cast( outside + offset ) }; pt.y += shape.position.y + Span.lift; auto norm { shape.normal.x * parallel }; norm.y += shape.normal.y; diff --git a/world/Segment.h b/world/Segment.h index 971794c3..afe0953c 100644 --- a/world/Segment.h +++ b/world/Segment.h @@ -24,7 +24,6 @@ struct map_colored_paths { struct loft_end { double bend { 0.0 }; - knots::line fit; }; struct loft_span {