17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 23:59:48 +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 <algorithm>
#include <functional>
#include <numeric>
#include <optional>
@@ -57,50 +58,67 @@ fastened_rails::fastened_rails( TTrack &Track ) {
auto const bladestretch = [&]( int const Path ) {
auto const &shape { track->blade( 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 ) {
auto const *segment { path_segment( *track, path ) };
if( segment == nullptr ) {
continue;
}
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 } ) {
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 { length * 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 };
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 };
// 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
// gap at an obtuse crossing, where its check rail lies
auto cuts { cut ? track->m_railplans[ path ][ side > 0 ].cuts : std::vector<track_rails::rail_cut> {} };
std::stable_sort( cuts.begin(), cuts.end(), []( track_rails::rail_cut const &A, track_rails::rail_cut const &B ) { return A.at < B.at; } );
for( std::size_t part = 0; part <= cuts.size(); ++part ) {
auto const *before { part > 0 ? &cuts[ part - 1 ] : nullptr };
auto const *after { part < cuts.size() ? &cuts[ part ] : nullptr };
auto const from { std::max( 0.0, before != nullptr ? before->start : 0.0 ) };
auto const to { std::min( length, after != nullptr ? std::max( after->end, after->start ) : length ) };
if( to - from < fastening_merge ) {
continue;
}
if( side == bladeside( 1 - path ) ) {
auto const stretch { bladestretch( 1 - path ) };
line.stock = { std::max( 0.0, stretch.x - chair_lead ), stretch.y };
auto const offsets { knots::sum( before != nullptr ? before->startoffsets : knots::line {}, after != nullptr ? after->endoffsets : knots::line {} ) };
auto const count { std::max( 1, static_cast<int>( std::ceil( ( to - from ) / fastening_step ) ) ) };
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 ); };
auto const has = [&]( int const Part ) {
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
// and over as many of the rails after them as it reaches
auto const crowd = [&]( std::size_t First, std::size_t const Last ) {
// 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
// many of the rails around as it reaches: the one holding the most first, the rails before and after it in turn
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 ] };
while( First <= Last ) {
std::size_t covered { 0 };
auto origin { 0.0 };
auto turn { 1.0 };
for( auto seat { First }; seat < Last; ++seat ) {
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 from { at + std::min( direction * reach.x, direction * reach.y ) };
auto const to { at + std::max( direction * reach.x, direction * reach.y ) };
if( rails[ First ].at < from ) {
continue;
}
std::size_t count { 0 };
while( First + count <= Last && rails[ First + count ].at <= to ) {
++count;
}
if( count > covered ) {
covered = count;
origin = at;
turn = direction;
}
std::size_t covered { 0 };
std::size_t from { First };
auto origin { 0.0 };
auto turn { 1.0 };
for( auto seat { First }; seat < Last; ++seat ) {
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 low { at + std::min( direction * reach.x, direction * reach.y ) };
auto const high { at + std::max( direction * reach.x, direction * reach.y ) };
auto start { seat };
while( start > First && rails[ start - 1 ].at >= low ) {
--start;
}
auto end { seat + 1 };
while( end < Last && rails[ end + 1 ].at <= high ) {
++end;
}
if( end - start + 1 > covered ) {
covered = end - start + 1;
from = start;
origin = at;
turn = direction;
}
}
if( covered < 2 ) {
result.push_back( { part_plate, point( rails[ First ].at ), rails[ First ].inward } );
++First;
continue;
}
if( covered < 2 ) {
for( auto rail { First }; rail <= Last; ++rail ) {
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 } );
First += covered;
return;
}
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 ) {
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
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;
}
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 ) {
case part_plate: { return plate_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; }
} };
// 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;
continue;
}
auto const firstreach { extent( first ) };
auto const secondreach { extent( second ) };
auto firstreach { extent( first ) };
auto secondreach { extent( second ) };
auto const drop = [&]( std::pair<std::size_t, std::size_t> const &Rail ) {
for( auto member { Rail.first }; member < Rail.second; ++member ) {
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 toward { turn * Toward > 0.0 ? span.y : span.x };
auto const away { turn * Toward > 0.0 ? span.x : span.y };
auto const stays { fastening.placed( away, 1.0 ) };
auto const scale { ( Toward * turn * Room - stays ) / ( toward - away ) };
// the other end where the narrowing left it, or as the plate was if that leaves too little of it
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 ) {
return false;
}
@@ -489,15 +519,24 @@ void fastened_rails::fit( std::vector<fastening> &Fastenings, beam const &Beam,
}
return Rail; };
// 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 ) {
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;
return true;
}
}
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
// 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 ) ) {

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 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 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
double placed( double const X, double const Scale ) const {
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
double constexpr fastening_pair { 0.2 };
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
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

View File

@@ -1595,6 +1595,11 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
auto const layout { plan_rails( Bank != 0 ) };
if( Bank != 0 ) {
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 ) {
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
bool m_editorremoved { false };
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
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;