17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-18 18:29:40 +02:00

feat(track): at a crossing at a wide angle the rails and guard rails ending against the side of another rail or guard rail are glued to it, their ends fitted to its profile height by height instead of square ends with caps and a gap of 5 mm, so the corners of the crossing are closed; the ends at the flangeways stay open for the flanges; a loft span can shift its first and last cross-section along the track by a fit given per height of the profile, and the rail profile gives the outlines of its field and gauge sides

This commit is contained in:
maj00r
2026-10-14 03:09:08 +02:00
parent 2cb2ecfe0f
commit 23975654a4
5 changed files with 105 additions and 15 deletions

View File

@@ -580,7 +580,7 @@ void TSegment::plan_frame( double const Distance, glm::dvec3 &Point, glm::dvec3
Across = glm::normalize( Across );
}
bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, int const Side, float const Halfwidth, double const fTextureLength, double const From, double const To, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, float const Lift, std::vector<glm::dvec2> const &Inner, gfx::userdata_array *Userdata, std::vector<glm::dvec2> const &Ramp, double const Rampwidth, bool const Walls, float const Wallu, glm::dvec2 const &Bends ) const {
bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, int const Side, float const Halfwidth, double const fTextureLength, double const From, double const To, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, float const Lift, std::vector<glm::dvec2> const &Inner, gfx::userdata_array *Userdata, std::vector<glm::dvec2> const &Ramp, double const Rampwidth, bool const Walls, float const Wallu, glm::dvec2 const &Bends, std::vector<glm::dvec2> const &Fitfrom, std::vector<glm::dvec2> const &Fitto ) const {
if( fTsBuffer.empty() || To - From < 0.001 || ShapePoints.empty() )
return false;
@@ -628,14 +628,26 @@ 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( Bends.x != 0.0 || Bends.y != 0.0 ) {
if( Bends.x != 0.0 || Bends.y != 0.0 || false == Fitfrom.empty() || false == Fitto.empty() ) {
auto widest { 0.0 };
for( auto const &point : ShapePoints ) {
widest = std::max( widest, std::abs( static_cast<double>( Side * point.position.x - Halfwidth ) ) );
}
auto offsetmost { 0.0 };
for( auto const &knot : Offsets ) {
offsetmost = std::max( offsetmost, std::abs( knot.y ) );
}
auto const margin = [&]( double const Bend, std::vector<glm::dvec2> const &Fit ) {
auto fitmost { 0.0 };
for( auto const &knot : Fit ) {
fitmost = std::max( fitmost, std::abs( knot.y ) );
}
return std::abs( Bend ) * ( Fit.empty() ? widest : widest + offsetmost ) + fitmost + 0.001; };
auto const frommargin { margin( Bends.x, Fitfrom ) };
auto const tomargin { margin( Bends.y, Fitto ) };
stations.erase( std::remove_if( stations.begin(), stations.end(), [&]( double const Distance ) {
return ( Distance > From && Distance < From + std::abs( Bends.x ) * widest + 0.001 )
|| ( Distance < To && Distance > To - std::abs( Bends.y ) * widest - 0.001 ); } ), stations.end() );
return ( Distance > From && Distance < To )
&& ( Distance < From + frommargin || Distance > To - tomargin ); } ), stations.end() );
}
auto const ring = [&]( double const Distance, double const Middle, std::vector<gfx::basic_vertex> &Ring, std::vector<float> &Wear ) {
@@ -669,14 +681,18 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
}
auto const worn { sample( Ramp, Distance, -1e6 ) };
auto const bend { Distance <= From ? Bends.x : Distance >= To ? Bends.y : 0.0 };
static std::vector<glm::dvec2> const unfitted;
auto const &fit { Distance <= From ? Fitfrom : Distance >= To ? Fitto : unfitted };
auto along { glm::vec3 { 0.f } };
if( bend != 0.0 ) {
auto forward { glm::vec3 { 0.f } };
if( bend != 0.0 || false == fit.empty() ) {
glm::dvec3 ahead, behind, unused;
plan_frame( std::min( Distance + 0.001, static_cast<double>( 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 ) {
along = glm::vec3 { glm::normalize( tangent ) * bend };
forward = glm::vec3 { glm::normalize( tangent ) };
along = forward * static_cast<float>( bend );
}
}
Ring.clear();
@@ -758,7 +774,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
wall = ( side > 0 ? outerwall : innerwall );
}
}
auto pt { parallel * static_cast<float>( Side * ( Halfwidth + outside + offset ) ) + position + along * static_cast<float>( outside + offset ) };
auto pt { parallel * static_cast<float>( Side * ( Halfwidth + outside + offset ) ) + position + along * static_cast<float>( outside + offset ) + forward * static_cast<float>( sample( fit, shape.position.y, 0.0 ) ) };
pt.y += shape.position.y + Lift;
auto norm { shape.normal.x * parallel };
norm.y += shape.normal.y;

View File

@@ -132,7 +132,7 @@ public:
RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, bool const Transition, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, std::pair<float, float> fOffsetX = {0.f, 0.f}, glm::vec3 **p = nullptr, bool bRender = true );
// the profile lofted from distance From to To, moved outwards by Offsets and cut on its outer side to Extents beyond the gauge line, both as (distance, value) knots
bool
RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, int const Side, float const Halfwidth, double const fTextureLength, double const From, double const To, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents = {}, float const Lift = 0.f, std::vector<glm::dvec2> const &Inner = {}, gfx::userdata_array *Userdata = nullptr, std::vector<glm::dvec2> const &Ramp = {}, double const Rampwidth = 0.0, bool const Walls = false, float const Wallu = -1.f, glm::dvec2 const &Bends = glm::dvec2 { 0.0 } ) const;
RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, int const Side, float const Halfwidth, double const fTextureLength, double const From, double const To, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents = {}, float const Lift = 0.f, std::vector<glm::dvec2> const &Inner = {}, gfx::userdata_array *Userdata = nullptr, std::vector<glm::dvec2> const &Ramp = {}, double const Rampwidth = 0.0, bool const Walls = false, float const Wallu = -1.f, glm::dvec2 const &Bends = glm::dvec2 { 0.0 }, std::vector<glm::dvec2> const &Fitfrom = {}, std::vector<glm::dvec2> const &Fitto = {} ) const;
// point of the path and the unit vector across it towards the right rail profile, at a distance from Point1
void
plan_frame( double const Distance, glm::dvec3 &Point, glm::dvec3 &Across ) const;

View File

@@ -2635,6 +2635,36 @@ TTrack::rail_dimensions() const {
}
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 );
}
}
std::vector<glm::dvec2> 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 ) {
std::sort( side.begin(), side.end(), []( glm::dvec2 const &A, glm::dvec2 const &B ) { return A.x < B.x; } );
}
auto const mean = []( std::vector<glm::dvec2> const &Side ) {
auto sum { 0.0 };
for( auto const &knot : Side ) {
sum += knot.y;
}
return sum / std::max<std::size_t>( 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 };

View File

@@ -342,6 +342,7 @@ public:
double ledge { 0.0 };
double web { 0.0365 };
double neck { -0.0467 };
std::vector<glm::dvec2> field, gauge;
};
rail_size rail_dimensions() const;
// a check rail beside a rail, how far its ends turn away from the running rail
@@ -367,6 +368,7 @@ public:
glm::dvec2 startmiter { 0.0 };
double endbend { 0.0 };
double startbend { 0.0 };
std::vector<glm::dvec2> endfit, startfit;
std::vector<glm::dvec2> endinner, startinner;
bool endcap { true };
bool startcap { true };

View File

@@ -1021,7 +1021,7 @@ void obtuse( TTrack::rail_plan &Plan, double const At, double const Into, TTrack
}
// crossing at a wide angle: running rails are broken for the flanges of the other track, the ones of the track not going through also where the head of the other rail lies; guard rails give way to the running rails and flangeways of the other track, the ones of the track not going through also to its guard rails
void wide( TTrack::rail_plan &Plan, double const At, double const Into, double const Facing, double const From, double const To, double const Inner, double const Outer ) {
void wide( TTrack::rail_plan &Plan, double const At, double const Into, double const Facing, double const From, double const To, double const Inner, double const Outer, std::vector<glm::dvec2> const &Fromline = {}, std::vector<glm::dvec2> const &Toline = {} ) {
auto const low { ( From + std::min( Inner * Facing, Outer * Facing ) - 0.005 ) / Into };
auto const high { ( To + std::max( Inner * Facing, Outer * Facing ) + 0.005 ) / Into };
@@ -1031,9 +1031,31 @@ void wide( TTrack::rail_plan &Plan, double const At, double const Into, double c
cut.start = At + std::max( low, high );
cut.endmiter = { At + ( Into > 0.0 ? From - 0.005 : To + 0.005 ) / Into, Facing / Into };
cut.startmiter = { At + ( Into > 0.0 ? To + 0.005 : From - 0.005 ) / Into, Facing / Into };
auto const glue = [&]( std::vector<glm::dvec2> const &Line, double const Edge, double &Position, glm::dvec2 &Miter, double &Bend, std::vector<glm::dvec2> &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 ? Fromline : Toline, Into > 0.0 ? From : To, cut.end, cut.endmiter, cut.endbend, cut.endfit, cut.endcap );
glue( Into > 0.0 ? Toline : Fromline, Into > 0.0 ? To : From, cut.start, cut.startmiter, cut.startbend, cut.startfit, cut.startcap );
Plan.cuts.push_back( cut );
}
std::vector<glm::dvec2> glue_line( std::vector<glm::dvec2> const &Outline, double const Base ) {
std::vector<glm::dvec2> line;
for( auto const &knot : Outline ) {
line.emplace_back( knot.x, Base - knot.y );
}
return line;
}
TTrack::rail_fill guard_fill( TTrack::guard_rail const &Guard, TTrack::rail_size const &Size ) {
auto const offset { Size.web - ( flangeway + Size.head ) };
@@ -1338,7 +1360,7 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
if( crossing.angle > wide_angle ) {
auto const other { crossing.track->rail_dimensions() };
auto const through { goes_through( *this, *crossing.track ) };
wide( plan, crossing.at, crossing.into, crossing.facing, through ? 0.0 : -other.head, flangeway, 0.0, railhead );
wide( plan, crossing.at, crossing.into, crossing.facing, through ? 0.0 : -other.head, flangeway, 0.0, railhead, through ? std::vector<glm::dvec2>{} : glue_line( other.field, 0.0 ) );
if( through ) {
tread( crossing.path, crossing.side, across( crossing, other ), *crossing.track, crossing.otherpath );
}
@@ -1346,7 +1368,7 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) {
auto &spans { guards[ crossing.path ][ crossing.side > 0 ] };
TTrack::guard_rail guard { crossing.at - reach, crossing.at + reach };
guard.through = through;
wide( guard.plan, crossing.at, crossing.into, crossing.facing, -other.head, through ? flangeway : flangeway + other.head, guardoffset, guardoffset + railhead );
wide( guard.plan, crossing.at, crossing.into, crossing.facing, -other.head, through ? flangeway : flangeway + other.head, guardoffset, guardoffset + railhead, glue_line( other.field, 0.0 ), through ? std::vector<glm::dvec2>{} : glue_line( other.gauge, flangeway + other.head ) );
auto merged { false };
for( auto &span : spans ) {
if( guard.from < span.to + 2.0 && guard.to > span.from - 2.0 ) {
@@ -1652,10 +1674,14 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
return glm::dvec2 {
std::abs( Start - bendfrom.x ) < 1e-6 ? bendfrom.y : 0.0,
std::abs( End - bendto.x ) < 1e-6 ? bendto.y : 0.0 }; };
std::pair<double, std::vector<glm::dvec2>> fitfrom, fitto;
std::vector<glm::dvec2> const unfitted;
auto const fitted = [&]( std::pair<double, std::vector<glm::dvec2>> const &Fit, double const Distance ) -> std::vector<glm::dvec2> const & {
return ( std::abs( Distance - Fit.first ) < 1e-6 ? Fit.second : unfitted ); };
auto const piece = [&]( double const Start, double const End, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, bool const Capstart, bool const Capend, gfx::vertex_array const &Shape, bool const Rolled, std::vector<glm::dvec2> const &Inner, std::vector<glm::dvec2> const &Ramp = {}, bool const Walls = false, float const Lift = 0.f, float const Wallu = -1.f, double const Fade = blend ) {
gfx::vertex_array vertices;
gfx::userdata_array userdata;
if( Shape.size() > 1 && segment.RenderLoftSpan( vertices, m_origin, Shape, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, Lift, Inner, Rolled ? nullptr : &userdata, Ramp, Ramp.empty() ? 0.0 : Fade, Walls, Wallu, bends( Start, End ) ) ) {
if( Shape.size() > 1 && segment.RenderLoftSpan( vertices, m_origin, Shape, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, Lift, Inner, Rolled ? nullptr : &userdata, Ramp, Ramp.empty() ? 0.0 : Fade, Walls, Wallu, bends( Start, End ), fitted( fitfrom, Start ), fitted( fitto, End ) ) ) {
cut_faces( vertices, Shape.size(), Capstart, Capend, Drawing.bank, sink( true ) );
gfx::vertex_array triangles;
gfx::userdata_array triangleuserdata;
@@ -1751,7 +1777,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
};
auto const running = [&]( double const Start, double const End, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, std::vector<glm::dvec2> const &Inner, std::vector<glm::dvec2> const &Rolled, bool const Capstart, bool const Capend ) {
gfx::vertex_array vertices;
if( segment.RenderLoftSpan( vertices, m_origin, Profile, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, 0.f, Inner, nullptr, {}, 0.0, false, -1.f, bends( Start, End ) ) ) {
if( segment.RenderLoftSpan( vertices, m_origin, Profile, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, 0.f, Inner, nullptr, {}, 0.0, false, -1.f, bends( Start, End ), fitted( fitfrom, Start ), fitted( fitto, End ) ) ) {
cut_faces( vertices, Profile.size(), Capstart, Capend, Drawing.bank, sink( true ) );
}
piece( Start, End, Offsets, Extents, false, false, strip( 0, topfirst ), true, Inner );
@@ -1801,7 +1827,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
auto const treaded = [&]( double const Start, double const End, std::vector<glm::dvec2> const &Offsets, std::vector<glm::dvec2> const &Extents, std::vector<glm::dvec2> const &Inner, bool const Capstart, bool const Capend, rail_tread const &Span ) {
current = null_handle;
gfx::vertex_array vertices;
if( segment.RenderLoftSpan( vertices, m_origin, Profile, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, 0.f, Inner, nullptr, {}, 0.0, false, -1.f, bends( Start, End ) ) ) {
if( segment.RenderLoftSpan( vertices, m_origin, Profile, Side, halfwidth, texturelength(), Start, End, Offsets, Extents, 0.f, Inner, nullptr, {}, 0.0, false, -1.f, bends( Start, End ), fitted( fitfrom, Start ), fitted( fitto, End ) ) ) {
cut_faces( vertices, Profile.size(), Capstart, Capend, Drawing.bank, sink( true ) );
}
piece( Start, End, Offsets, Extents, false, false, strip( 0, topfirst ), true, Inner );
@@ -1946,9 +1972,11 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
std::sort( cuts.begin(), cuts.end(), []( rail_cut const &A, rail_cut const &B ) { return A.at < B.at; } );
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 ), From, 0.0 } ) };
auto const b { std::min( { mitered( endmiter, end, true ), length, mitered( guard.tomiter, guard.to, true ) } ) };
if( b - a > 0.01 ) {
@@ -1959,11 +1987,20 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
( ending == ( miter.y < 0.0 ) ? uppers : lowers ).emplace_back( 1.0 / miter.y, -miter.x / miter.y - guardoffset );
}
}
piece( a, b, offsets, envelope( uppers, true, a, b ), true, true, Profile, false, envelope( lowers, false, a, b ) );
auto const glued { a == start && false == startcut.startfit.empty() };
auto const gluedend { b == end && false == endcut.endfit.empty() };
bendfrom = { a, glued ? startcut.startbend : 0.0 };
bendto = { b, gluedend ? endcut.endbend : 0.0 };
fitfrom = { a, glued ? startcut.startfit : unfitted };
fitto = { b, gluedend ? endcut.endfit : unfitted };
piece( a, b, offsets, envelope( uppers, true, a, b ), false == glued, false == gluedend, Profile, false, envelope( lowers, false, a, b ) );
bendfrom = bendto = glm::dvec2 { 0.0 };
fitfrom = fitto = {};
}
if( i < cuts.size() && cuts[ i ].start > start ) {
start = cuts[ i ].start;
startmiter = cuts[ i ].startmiter;
startcut = cuts[ i ];
}
}
}
@@ -2052,6 +2089,7 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
auto start { -1e9 };
auto startcap { true };
auto startbend { 0.0 };
std::vector<glm::dvec2> startfit;
std::vector<glm::dvec2> rolledfrom;
std::vector<glm::dvec2> offsets, extents, inner;
for( std::size_t i = 0; i <= cuts.size(); ++i ) {
@@ -2074,6 +2112,8 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
auto const capend { end < length ? cuts[ i ].endcap : Openend };
bendfrom = { a, start > From ? startbend : 0.0 };
bendto = { b, end < length ? cuts[ i ].endbend : 0.0 };
fitfrom = { a, start > From ? startfit : unfitted };
fitto = { b, end < length ? cuts[ i ].endfit : unfitted };
if( b - a > 0.01 ) {
std::vector<rail_tread> active;
for( auto const &span : Spans ) {
@@ -2112,11 +2152,13 @@ bool TTrack::lay_rail( rail_layout const &Layout, rail_drawing const &Drawing, i
current = null_handle;
}
bendfrom = bendto = glm::dvec2 { 0.0 };
fitfrom = fitto = {};
if( i < cuts.size() ) {
rolledfrom = cuts[ i ].startrolled;
start = cuts[ i ].start;
startcap = cuts[ i ].startcap;
startbend = cuts[ i ].startbend;
startfit = cuts[ i ].startfit;
offsets = cuts[ i ].startoffsets;
extents = cuts[ i ].startextents;
inner = cuts[ i ].startinner;