17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 09:49:19 +02:00

feat(track): at a crossing at a wide angle the tread of the other track is laid where its wheels roll over the mitred corners: the part of a guard rail under the running rail of the other track becomes a running surface in the shared tread material instead of matte, and running rails of different materials share the tread over the rolled band of the other track instead of over a square span; wide_tread draws the edges of the band from the crossing itself, since the gauge lines meet in one point at right angles, and a tread gives its own fade, widened for bands lying across the rail; treaded keeps the matte parts of a guard rail matte, lay_treads splits guard rails at their treads, and a piece of no triangles is not inserted

This commit is contained in:
maj00r
2026-10-14 11:25:34 +02:00
parent 572ee569dc
commit 2a5dda1509
3 changed files with 87 additions and 27 deletions

View File

@@ -465,6 +465,7 @@ private:
void lay_guards();
void lay_checks();
void lay_fills();
void lay_treads( rail_piece const &Whole, std::vector<rail_tread> const &Treads );
void lay_cuts( gfx::vertex_array const &Shape, std::vector<rail_cut> Cuts, bool const Wheels, std::vector<rail_tread> const &Treads, float const Wallu = -1.f );
void lay_grooved( gfx::vertex_array const &Outline );
grooved_profile split_grooved( gfx::vertex_array const &Outline ) const;
@@ -581,10 +582,7 @@ 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 );
}
}
piece(
m_profile,
{ .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, {}, {} } );
lay_treads( { .from = a, .to = b, .offsets = offsets, .extents = envelope( uppers, true, a, b ), .inner = envelope( lowers, false, a, b ), .capfrom = true, .capto = true, .rolled = false }, guard.treads );
}
if( i < cuts.size() && cuts[ i ].start > start ) {
start = cuts[ i ].start;
@@ -614,6 +612,34 @@ void TTrack::rail_layer::lay_checks() {
}
}
void TTrack::rail_layer::lay_treads( rail_piece const &Whole, std::vector<rail_tread> const &Treads ) {
std::vector<double> splits { Whole.from, Whole.to };
for( auto const &tread : Treads ) {
for( auto const edge : { tread.from, tread.to } ) {
if( edge > Whole.from + 0.01 && edge < Whole.to - 0.01 ) {
splits.push_back( edge );
}
}
}
std::sort( splits.begin(), splits.end() );
piece_ends const ends { Whole.from, Whole.to };
for( std::size_t k = 1; k < splits.size(); ++k ) {
auto part { Whole };
part.from = splits[ k - 1 ];
part.to = splits[ k ];
part.capfrom = ( k == 1 && Whole.capfrom );
part.capto = ( k + 1 == splits.size() && Whole.capto );
auto const *tread { tread_at( Treads, 0.5 * ( part.from + part.to ) ) };
if( tread != nullptr && m_topfirst < m_toplast ) {
treaded( part, *tread, ends );
}
else {
piece( m_profile, part, ends );
}
}
}
void TTrack::rail_layer::lay_fills() {
for( auto const &fill : ours( m_layout.fills ) ) {
@@ -923,33 +949,34 @@ void TTrack::rail_layer::treaded( rail_piece Whole, rail_tread const &Tread, pie
auto const colour { 0.5f * ( tread_colour( Tread.material, m_track.tread_band() ) + tread_colour( own, m_track.tread_band() ) ) };
auto const tint = [&]( material_handle const Material, glm::vec3 const &Mark ) {
return paint( Material, marked ? Mark : colour ); };
auto const owntint { tint( own, { 1.f, 0.f, 0.f } ) };
auto const owntint { Whole.rolled ? tint( own, { 1.f, 0.f, 0.f } ) : null_handle };
auto const sharedtint { tint( Tread.material, { 0.f, 1.f, 0.f } ) };
auto const shared { marked ? tint( Tread.material, { 0.f, 0.f, 1.f } ) : Tread.material };
auto const ramp = [&]( knots::line const &Knots ) {
return marked ? knots::line {} : Knots; };
auto const fade { Tread.fade };
auto const centre { knots::middle( lower, upper ) };
auto const beneath { knots::shifted( lower, -tread_fade ) };
auto const beyond { knots::shifted( upper, tread_fade ) };
auto const lowfade { knots::bound( knots::shifted( lower, tread_fade ), centre, true ) };
auto const highfade { knots::bound( knots::shifted( upper, -tread_fade ), centre, false ) };
auto const beneath { knots::shifted( lower, -fade ) };
auto const beyond { knots::shifted( upper, fade ) };
auto const lowfade { knots::bound( knots::shifted( lower, fade ), centre, true ) };
auto const highfade { knots::bound( knots::shifted( upper, -fade ), centre, false ) };
auto splits { cross_splits( Whole, reach, { &lower, &upper, &lowfade, &highfade, &beneath, &beyond }, 0.001 ) };
add_meetings( knots::shifted( lower, 2.0 * tread_fade ), upper, Whole.from, Whole.to, 0.001, splits );
add_meetings( knots::shifted( lower, 2.0 * fade ), upper, Whole.from, Whole.to, 0.001, splits );
std::sort( splits.begin(), splits.end() );
lay_top(
top, Whole, reach, splits,
{
{ null_handle, knots::bound( Whole.extents, beneath, true ), Whole.inner },
{ owntint, knots::bound( Whole.extents, lower, true ), knots::bound( Whole.inner, beneath, false ), false, ramp( lower ), tread_fade },
{ sharedtint, knots::bound( Whole.extents, lowfade, true ), knots::bound( Whole.inner, lower, false ), false, ramp( lower ), -tread_fade },
{ null_handle, knots::bound( Whole.extents, beneath, true ), Whole.inner, Whole.rolled },
{ owntint, knots::bound( Whole.extents, lower, true ), knots::bound( Whole.inner, beneath, false ), false, ramp( lower ), fade },
{ sharedtint, knots::bound( Whole.extents, lowfade, true ), knots::bound( Whole.inner, lower, false ), false, ramp( lower ), -fade },
{ shared, knots::bound( Whole.extents, highfade, true ), knots::bound( Whole.inner, lowfade, false ), false == marked },
{ sharedtint, knots::bound( Whole.extents, upper, true ), knots::bound( Whole.inner, highfade, false ), false, ramp( upper ), tread_fade },
{ owntint, knots::bound( Whole.extents, beyond, true ), knots::bound( Whole.inner, upper, false ), false, ramp( upper ), -tread_fade },
{ null_handle, Whole.extents, knots::bound( Whole.inner, beyond, false ) } },
{ sharedtint, knots::bound( Whole.extents, upper, true ), knots::bound( Whole.inner, highfade, false ), false, ramp( upper ), fade },
{ owntint, knots::bound( Whole.extents, beyond, true ), knots::bound( Whole.inner, upper, false ), false, ramp( upper ), -fade },
{ null_handle, Whole.extents, knots::bound( Whole.inner, beyond, false ), Whole.rolled } },
[&]( double const Middle ) {
auto const from { knots::sample( lower, Middle, 0.0 ) };
auto const to { knots::sample( upper, Middle, 0.0 ) };
return std::vector<double> { from - tread_fade, from, knots::sample( lowfade, Middle, 0.0 ), knots::sample( highfade, Middle, 0.0 ), to, to + tread_fade }; },
return std::vector<double> { from - fade, from, knots::sample( lowfade, Middle, 0.0 ), knots::sample( highfade, Middle, 0.0 ), to, to + fade }; },
Ends );
}
@@ -1012,6 +1039,9 @@ void TTrack::rail_layer::piece( gfx::vertex_array const &Shape, rail_piece const
}
}
}
if( triangles.empty() ) {
return;
}
sink( Piece.material, false == Piece.rolled ).emplace_back( GfxRenderer->Insert( triangles, triangleuserdata, m_drawing.bank, GL_TRIANGLES ) );
}

View File

@@ -101,6 +101,16 @@ struct rail_cut {
struct rail_plan {
std::vector<rail_cut> cuts;
};
struct rail_tread {
double from { 0.0 };
double to { 0.0 };
material_handle material { 0 };
int anchor { 0 };
knots::line edge;
knots::line gauge;
bool below { false };
double fade { tread_fade };
};
// guard rails of a crossing at a wide angle: the rail profile laid inside the running rail across the flangeway, broken like the rails
struct guard_rail {
double from { 0.0 };
@@ -111,6 +121,7 @@ struct guard_rail {
glm::dvec2 frommiter { 0.0 };
glm::dvec2 tomiter { 0.0 };
bool through { true };
std::vector<rail_tread> treads;
};
struct rail_band {
double at { 0.0 };
@@ -129,15 +140,6 @@ struct rail_fill {
double lift { 0.0 };
bool web { false };
};
struct rail_tread {
double from { 0.0 };
double to { 0.0 };
material_handle material { 0 };
int anchor { 0 };
knots::line edge;
knots::line gauge;
bool below { false };
};
struct rail_layout {
rail_plan plans[ 2 ][ 2 ]; // path, right side
std::vector<rail_band> bands[ 2 ][ 2 ];

View File

@@ -212,6 +212,29 @@ void vee_fill( std::vector<track_rails::rail_fill> &Fills, double const At, doub
Fills.push_back( fill );
}
track_rails::rail_tread wide_tread( track_rails::rail_crossing const &Crossing, material_handle const Material, double const Base, double const Head, double const Reach ) {
auto const facing { std::abs( Crossing.facing ) > 0.02 ? Crossing.facing : std::copysign( 0.02, Crossing.facing ) };
auto const slope { Crossing.into / facing };
track_rails::rail_tread tread { 1e9, -1e9, Material };
for( auto const across : { Base, Base + Head } ) {
for( auto const edge : { tread_fade, -Reach - tread_fade } ) {
auto const distance { Crossing.at + ( edge + Crossing.facing * across ) / Crossing.into };
tread.from = std::min( tread.from, distance - 0.01 );
tread.to = std::max( tread.to, distance + 0.01 );
}
}
auto const line = [&]( double const Edge ) {
auto const across = [&]( double const Distance ) {
return ( ( Distance - Crossing.at ) * Crossing.into - Edge ) / facing - Base; };
return knots::line { { tread.from, across( tread.from ) }, { tread.to, across( tread.to ) } }; };
tread.gauge = line( 0.0 );
tread.edge = line( -Reach );
tread.below = ( facing > 0.0 );
tread.fade = tread_fade * std::sqrt( 1.0 + slope * slope );
return tread;
}
std::vector<glm::dvec2> tread_edge( TTrack const &Track, int const Path, int const Side, TTrack const &Other, int const Otherpath, int const Otherside, double const From, double const To, bool &Below, std::vector<glm::dvec2> &Gauge ) {
auto const &ours { rail_line( Track, Path, Side > 0, false ) };
@@ -293,6 +316,7 @@ void merge_guard( std::vector<track_rails::guard_rail> &Guards, track_rails::gua
span.to = std::max( span.to, Guard.to );
span.plan.cuts.insert( span.plan.cuts.end(), Guard.plan.cuts.begin(), Guard.plan.cuts.end() );
span.through = ( span.through && Guard.through );
span.treads.insert( span.treads.end(), Guard.treads.begin(), Guard.treads.end() );
return;
}
}
@@ -439,11 +463,15 @@ 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 ) {
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 material { m_track.shared_tread( Crossing.path, *Crossing.track, Crossing.otherpath ) };
if( material != m_track.rail_material( Crossing.path ) ) {
m_layout.treads[ Crossing.path ][ Crossing.side > 0 ].push_back( wide_tread( Crossing, material, 0.0, m_size.head, rolled_band ) );
}
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 = goes_through( m_track, *Crossing.track );
guard.plan.cuts.push_back( corner_cut( Crossing, -flangeway, 1.0, m_size.head, Other.head ) );
guard.treads.push_back( wide_tread( Crossing, material, m_guardoffset, m_size.head, Other.head ) );
merge_guard( m_layout.guards[ Crossing.path ][ Crossing.side > 0 ], guard );
}