17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-05 21:49:10 +01:00

fix(sleepers): the wing rails on the timbers as they're bent out: the rails of a track taken in their parts between the cuts at the crossings, as they're laid, a part going on over the gap at an obtuse crossing where its check rail lies; three rails or more close beside held by the fastening of two rails covering the most of them first, then those before and after it, one holding more rails than its seats not narrowed; a rail lying on the brace of a chair beside takes the brace away; a plate cut short at the fastening beside from where its narrowing left it

This commit is contained in:
maj00r
2026-10-23 19:24:47 +02:00
parent fa62a61389
commit f7821eea99
5 changed files with 120 additions and 72 deletions

View File

@@ -18,6 +18,7 @@ http://mozilla.org/MPL/2.0/.
#include "simulation/simulation.h" #include "simulation/simulation.h"
#include <algorithm> #include <algorithm>
#include <functional>
#include <numeric> #include <numeric>
#include <optional> #include <optional>
@@ -57,50 +58,67 @@ fastened_rails::fastened_rails( TTrack &Track ) {
auto const bladestretch = [&]( int const Path ) { auto const bladestretch = [&]( int const Path ) {
auto const &shape { track->blade( Path ) }; auto const &shape { track->blade( Path ) };
return glm::dvec2 { shape.valid ? shape.tip : 0.0, track_rails::blade_length( *track, Path ) }; }; return glm::dvec2 { shape.valid ? shape.tip : 0.0, track_rails::blade_length( *track, Path ) }; };
// what the rail is along the path: a blade, the stock rail of a blade, checked
auto const add = [&]( rail &&Line, int const Path, int const Side ) {
if( switched ) {
if( Side == bladeside( Path ) ) {
Line.blade = { 0.0, bladestretch( Path ).y };
}
if( Side == bladeside( 1 - Path ) ) {
auto const stretch { bladestretch( 1 - Path ) };
Line.stock = { std::max( 0.0, stretch.x - chair_lead ), stretch.y };
}
}
if( cut ) {
for( auto const &check : track_rails::rail_checks( *track )[ Path ][ Side > 0 ? 1 : 0 ] ) {
Line.checks.emplace_back( check.from, check.to );
}
}
m_rails.emplace_back( std::move( Line ) ); };
for( int path = 0; path < path_count( *track ); ++path ) { for( int path = 0; path < path_count( *track ); ++path ) {
auto const *segment { path_segment( *track, path ) }; auto const *segment { path_segment( *track, path ) };
if( segment == nullptr ) { if( segment == nullptr ) {
continue; continue;
} }
auto const length { segment->GetLength() }; auto const length { segment->GetLength() };
auto const count { std::max( 1, static_cast<int>( std::ceil( length / fastening_step ) ) ) };
for( auto const side : { -1, 1 } ) { for( auto const side : { -1, 1 } ) {
rail line; // the rail in its parts between the cuts at the crossings, as they're laid: a wing rail bent out from the path; on over the
line.track = track; // gap at an obtuse crossing, where its check rail lies
line.path = path; auto cuts { cut ? track->m_railplans[ path ][ side > 0 ].cuts : std::vector<track_rails::rail_cut> {} };
line.side = side; std::stable_sort( cuts.begin(), cuts.end(), []( track_rails::rail_cut const &A, track_rails::rail_cut const &B ) { return A.at < B.at; } );
line.foot = size.web - size.low; for( std::size_t part = 0; part <= cuts.size(); ++part ) {
line.low = glm::dvec2 { std::numeric_limits<double>::max() }; auto const *before { part > 0 ? &cuts[ part - 1 ] : nullptr };
line.high = glm::dvec2 { std::numeric_limits<double>::lowest() }; auto const *after { part < cuts.size() ? &cuts[ part ] : nullptr };
for( int step = 0; step <= count; ++step ) { auto const from { std::max( 0.0, before != nullptr ? before->start : 0.0 ) };
auto const distance { length * step / count }; auto const to { std::min( length, after != nullptr ? std::max( after->end, after->start ) : length ) };
glm::dvec3 point, across; if( to - from < fastening_merge ) {
segment->plan_frame( distance, point, across ); continue;
auto const flat { plan( across ) };
auto const inward { glm::length( flat ) > 1e-9 ? glm::normalize( flat ) * static_cast<double>( -side ) : glm::dvec2 { 1.0, 0.0 } };
auto const position { plan( point ) - inward * middle };
line.points.push_back( position );
line.heights.push_back( point.y );
line.inward.push_back( inward );
line.distances.push_back( distance );
line.low = glm::min( line.low, position );
line.high = glm::max( line.high, position );
}
if( switched ) {
if( side == bladeside( path ) ) {
line.blade = { 0.0, bladestretch( path ).y };
} }
if( side == bladeside( 1 - path ) ) { auto const offsets { knots::sum( before != nullptr ? before->startoffsets : knots::line {}, after != nullptr ? after->endoffsets : knots::line {} ) };
auto const stretch { bladestretch( 1 - path ) }; auto const count { std::max( 1, static_cast<int>( std::ceil( ( to - from ) / fastening_step ) ) ) };
line.stock = { std::max( 0.0, stretch.x - chair_lead ), stretch.y }; rail line;
line.track = track;
line.path = path;
line.side = side;
line.foot = size.web - size.low;
line.low = glm::dvec2 { std::numeric_limits<double>::max() };
line.high = glm::dvec2 { std::numeric_limits<double>::lowest() };
for( int step = 0; step <= count; ++step ) {
auto const distance { from + ( to - from ) * step / count };
glm::dvec3 point, across;
segment->plan_frame( distance, point, across );
auto const flat { plan( across ) };
auto const inward { glm::length( flat ) > 1e-9 ? glm::normalize( flat ) * static_cast<double>( -side ) : glm::dvec2 { 1.0, 0.0 } };
auto const position { plan( point ) - inward * ( middle + knots::sample( offsets, distance, 0.0 ) ) };
line.points.push_back( position );
line.heights.push_back( point.y );
line.inward.push_back( inward );
line.distances.push_back( distance );
line.low = glm::min( line.low, position );
line.high = glm::max( line.high, position );
} }
add( std::move( line ), path, side );
} }
if( cut ) {
for( auto const &check : track_rails::rail_checks( *track )[ path ][ side > 0 ? 1 : 0 ] ) {
line.checks.emplace_back( check.from, check.to );
}
}
m_rails.emplace_back( std::move( line ) );
} }
} }
} }
@@ -189,47 +207,54 @@ std::vector<fastening> fastened_rails::on( beam const &Beam, beam_model const &M
return Beam.from + span * ( At / length ); }; return Beam.from + span * ( At / length ); };
auto const has = [&]( int const Part ) { auto const has = [&]( int const Part ) {
return Model.parts[ Part ] != nullptr; }; return Model.parts[ Part ] != nullptr; };
// the rails from the first to the last under fastenings of two rails in turn, each with its seats under two rails side by side // the rails from the first to the last under fastenings of two rails, each with its seats under two rails side by side and over as
// and over as many of the rails after them as it reaches // many of the rails around as it reaches: the one holding the most first, the rails before and after it in turn
auto const crowd = [&]( std::size_t First, std::size_t const Last ) { std::function<void( std::size_t, std::size_t )> crowd = [&]( std::size_t const First, std::size_t const Last ) {
auto const &reach { Model.spans[ part_check ] }; auto const &reach { Model.spans[ part_check ] };
while( First <= Last ) { std::size_t covered { 0 };
std::size_t covered { 0 }; std::size_t from { First };
auto origin { 0.0 }; auto origin { 0.0 };
auto turn { 1.0 }; auto turn { 1.0 };
for( auto seat { First }; seat < Last; ++seat ) { for( auto seat { First }; seat < Last; ++seat ) {
for( auto const direction : { 1.0, -1.0 } ) { for( auto const direction : { 1.0, -1.0 } ) {
auto const at { 0.5 * ( rails[ seat ].at + rails[ seat + 1 ].at - direction * check_seats ) }; auto const at { 0.5 * ( rails[ seat ].at + rails[ seat + 1 ].at - direction * check_seats ) };
auto const from { at + std::min( direction * reach.x, direction * reach.y ) }; auto const low { at + std::min( direction * reach.x, direction * reach.y ) };
auto const to { at + std::max( direction * reach.x, direction * reach.y ) }; auto const high { at + std::max( direction * reach.x, direction * reach.y ) };
if( rails[ First ].at < from ) { auto start { seat };
continue; while( start > First && rails[ start - 1 ].at >= low ) {
} --start;
std::size_t count { 0 }; }
while( First + count <= Last && rails[ First + count ].at <= to ) { auto end { seat + 1 };
++count; while( end < Last && rails[ end + 1 ].at <= high ) {
} ++end;
if( count > covered ) { }
covered = count; if( end - start + 1 > covered ) {
origin = at; covered = end - start + 1;
turn = direction; from = start;
} origin = at;
turn = direction;
} }
} }
if( covered < 2 ) { }
result.push_back( { part_plate, point( rails[ First ].at ), rails[ First ].inward } ); if( covered < 2 ) {
++First; for( auto rail { First }; rail <= Last; ++rail ) {
continue; result.push_back( { part_plate, point( rails[ rail ].at ), rails[ rail ].inward } );
} }
result.push_back( { part_check, point( origin ), axis * turn, 1.f, static_cast<float>( 0.5 * check_seats ), 0.f, true } ); return;
First += covered; }
result.push_back( { part_check, point( origin ), axis * turn, 1.f, static_cast<float>( 0.5 * check_seats ), 0.f, true, covered > 2 } );
if( from > First ) {
crowd( First, from - 1 );
}
if( from + covered <= Last ) {
crowd( from + covered, Last );
} }
}; };
for( std::size_t index = 0; index < rails.size(); ++index ) { for( std::size_t index = 0; index < rails.size(); ++index ) {
auto const &rail { rails[ index ] }; auto const &rail { rails[ index ] };
// three rails or more close beside one another, as at the crossings of the slips, none a stock rail or with a check rail // three rails or more close beside one another, as at the crossings of the slips, none a stock rail or with a check rail
auto last { index }; auto last { index };
while( last + 1 < rails.size() && rails[ last + 1 ].at - rails[ last ].at < fastening_pair && rails[ last ].kind == role::plain && rails[ last + 1 ].kind == role::plain ) { while( last + 1 < rails.size() && rails[ last + 1 ].at - rails[ last ].at < fastening_crowd && rails[ last ].kind == role::plain && rails[ last + 1 ].kind == role::plain ) {
++last; ++last;
} }
if( last >= index + 2 && has( part_check ) ) { if( last >= index + 2 && has( part_check ) ) {
@@ -338,7 +363,7 @@ void fastened_rails::fit( std::vector<fastening> &Fastenings, beam const &Beam,
switch( Fastening.part ) { switch( Fastening.part ) {
case part_plate: { return plate_least; } case part_plate: { return plate_least; }
case part_slide: { return slide_least; } case part_slide: { return slide_least; }
case part_check: { return check_least; } case part_check: { return Fastening.whole ? 1.0 : check_least; }
default: { return 1.0; } default: { return 1.0; }
} }; } };
// those of one rail together, the rails in the order along the timber // those of one rail together, the rails in the order along the timber
@@ -451,8 +476,8 @@ void fastened_rails::fit( std::vector<fastening> &Fastenings, beam const &Beam,
kept = index; kept = index;
continue; continue;
} }
auto const firstreach { extent( first ) }; auto firstreach { extent( first ) };
auto const secondreach { extent( second ) }; auto secondreach { extent( second ) };
auto const drop = [&]( std::pair<std::size_t, std::size_t> const &Rail ) { auto const drop = [&]( std::pair<std::size_t, std::size_t> const &Rail ) {
for( auto member { Rail.first }; member < Rail.second; ++member ) { for( auto member { Rail.first }; member < Rail.second; ++member ) {
dropped[ order[ member ] ] = true; dropped[ order[ member ] ] = true;
@@ -468,8 +493,13 @@ void fastened_rails::fit( std::vector<fastening> &Fastenings, beam const &Beam,
auto const turn { glm::dot( fastening.across, axis ) }; auto const turn { glm::dot( fastening.across, axis ) };
auto const toward { turn * Toward > 0.0 ? span.y : span.x }; auto const toward { turn * Toward > 0.0 ? span.y : span.x };
auto const away { turn * Toward > 0.0 ? span.x : span.y }; auto const away { turn * Toward > 0.0 ? span.x : span.y };
auto const stays { fastening.placed( away, 1.0 ) }; // the other end where the narrowing left it, or as the plate was if that leaves too little of it
auto const scale { ( Toward * turn * Room - stays ) / ( toward - away ) }; auto stays { fastening.placed( away, fastening.scale ) };
auto scale { ( Toward * turn * Room - stays ) / ( toward - away ) };
if( scale < clip_least ) {
stays = fastening.placed( away, 1.0 );
scale = ( Toward * turn * Room - stays ) / ( toward - away );
}
if( scale < clip_least ) { if( scale < clip_least ) {
return false; return false;
} }
@@ -489,15 +519,24 @@ void fastened_rails::fit( std::vector<fastening> &Fastenings, beam const &Beam,
} }
return Rail; }; return Rail; };
// the brace of a chair where the rail beside comes too close for it, its slide plate kept // the brace of a chair where the rail beside comes too close for it, its slide plate kept
auto const brace = [&]( std::pair<std::size_t, std::size_t> const &Rail, int const Side ) { auto const brace = [&]( std::pair<std::size_t, std::size_t> const &Rail, int const Side, double const Reaching = fastening_gap ) {
for( auto member { Rail.first }; member < Rail.second; ++member ) { for( auto member { Rail.first }; member < Rail.second; ++member ) {
auto const &fastening { Fastenings[ order[ member ] ] }; auto const &fastening { Fastenings[ order[ member ] ] };
if( fastening.part == part_chair && reach( fastening, fastening.scale )[ Side ] > fastening_gap ) { if( fastening.part == part_chair && false == dropped[ order[ member ] ] && reach( fastening, fastening.scale )[ Side ] > Reaching ) {
dropped[ order[ member ] ] = true; dropped[ order[ member ] ] = true;
return true; return true;
} }
} }
return false; }; return false; };
// a rail lying on the brace of a chair beside takes the brace away
if( brace( first, 1, apart ) || brace( second, 0, apart ) ) {
if( false == overlaps() ) {
kept = index;
continue;
}
firstreach = extent( first );
secondreach = extent( second );
}
// of two rails with check rails, the one with its check rail toward the other one, lying on the fastening of the other // of two rails with check rails, the one with its check rail toward the other one, lying on the fastening of the other
// one then, goes in a plate cut short at that fastening, or without its own, the other one keeping its check rail seated // one then, goes in a plate cut short at that fastening, or without its own, the other one keeping its check rail seated
if( checks( first ) && checks( second ) && faces( first, 1.0 ) != faces( second, -1.0 ) ) { if( checks( first ) && checks( second ) && faces( first, 1.0 ) != faces( second, -1.0 ) ) {

View File

@@ -31,6 +31,7 @@ struct fastening {
float pivot { 0.f }; // the x of the part kept in place as it's narrowed float pivot { 0.f }; // the x of the part kept in place as it's narrowed
float shift { 0.f }; // along its x, how far the pivot is moved float shift { 0.f }; // along its x, how far the pivot is moved
bool both { false }; // the fastening of two rails close beside each other, neither with a check rail of its own seated bool both { false }; // the fastening of two rails close beside each other, neither with a check rail of its own seated
bool whole { false }; // holding more rails than its seats, out to its ends: not narrowed
// where an x of the part lands along its across, at the scale // where an x of the part lands along its across, at the scale
double placed( double const X, double const Scale ) const { double placed( double const X, double const Scale ) const {
return pivot + shift + Scale * ( X - pivot ); } return pivot + shift + Scale * ( X - pivot ); }

View File

@@ -57,6 +57,8 @@ double constexpr fastening_merge { 0.03 };
// one, as the legs of the nose where they're joined, in one plate between them // one, as the legs of the nose where they're joined, in one plate between them
double constexpr fastening_pair { 0.2 }; double constexpr fastening_pair { 0.2 };
double constexpr fastening_nose { 0.05 }; double constexpr fastening_nose { 0.05 };
// three rails or more with less than this between each and the next, as the legs of a nose with the wing rails beside, are held together
double constexpr fastening_crowd { 0.3 };
// how far apart the rails lie in the fastening of both, the check part of the model // how far apart the rails lie in the fastening of both, the check part of the model
double constexpr check_seats { 0.113 }; double constexpr check_seats { 0.113 };
// the room left between the fastenings of rails close by, and how narrow their plates get to keep it // the room left between the fastenings of rails close by, and how narrow their plates get to keep it

View File

@@ -1595,6 +1595,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
auto const layout { plan_rails( Bank != 0 ) }; auto const layout { plan_rails( Bank != 0 ) };
if( Bank != 0 ) { if( Bank != 0 ) {
m_crossings = layout.crossed; m_crossings = layout.crossed;
for( int path = 0; path < 2; ++path ) {
for( int side = 0; side < 2; ++side ) {
m_railplans[ path ][ side ] = layout.plans[ path ][ side ];
}
}
} }
auto const lay_rail = [&]( int const Path, gfx::vertex_array const &Profile, int const Side, float const Texturelength, double const From, bool const Openstart, bool const Openend, geometryhandle_sequence &Geometry, geometryhandle_sequence &Cuts ) { auto const lay_rail = [&]( int const Path, gfx::vertex_array const &Profile, int const Side, float const Texturelength, double const From, bool const Openstart, bool const Openend, geometryhandle_sequence &Geometry, geometryhandle_sequence &Cuts ) {
return this->lay_rail( layout, drawing, Path, Profile, Side, Texturelength, From, Openstart, Openend, Geometry, Cuts ); }; return this->lay_rail( layout, drawing, Path, Profile, Side, Texturelength, From, Openstart, Openend, Geometry, Cuts ); };

View File

@@ -217,6 +217,7 @@ public:
std::vector<segment_data> m_paths; // source data for owned paths std::vector<segment_data> m_paths; // source data for owned paths
bool m_editorremoved { false }; bool m_editorremoved { false };
std::vector<TTrack *> m_crossings; // tracks whose rails cross the rails of this one, found when its geometry was made std::vector<TTrack *> m_crossings; // tracks whose rails cross the rails of this one, found when its geometry was made
track_rails::rail_plan m_railplans[ 2 ][ 2 ]; // the cuts of its rails, by path and right side, as its geometry was made
scene::basic_node *m_road { nullptr }; // road or road junction which generated this path. such path is one-way, and isn't saved nor edited on its own scene::basic_node *m_road { nullptr }; // road or road junction which generated this path. such path is one-way, and isn't saved nor edited on its own
std::array<float, 3> m_routeweights { 0.f, 0.f, 0.f }; // odds of each route through a crossroads being picked by a driver, by the number of the route. all zero: even odds std::array<float, 3> m_routeweights { 0.f, 0.f, 0.f }; // odds of each route through a crossroads being picked by a driver, by the number of the route. all zero: even odds
int iterate_stamp = 0; int iterate_stamp = 0;