17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 23:59:48 +01:00

feat(sleepers): the crossings at wide angles laid on timbers: where two tracks cross so wide they're laid as a grid, the one track carries timbers square to it through the crossing, two side by side under each rail of the other, which lays none there; the rail running along the two held on their seam by fastenings as far apart as the beams at most, clear of the fastenings of the rails across; the guard rails of the crossings at wide and middle angles held as the rails beside are, a running rail with its guard rail in the fastening of both, the rails crowding at the corners under one plate; the beams under the guard rails timbers as far as the guard rails reach; the fastenings selftest lists the rails on each timber with detail rails

This commit is contained in:
maj00r
2026-10-23 23:37:28 +02:00
parent 997a8da41d
commit 2e00619d7f
7 changed files with 290 additions and 50 deletions

View File

@@ -153,8 +153,8 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c
if (command == "fastenings")
{
// the fastenings on the timbers of the track: along the track, the part and where across it, which way its +x goes
std::string name;
words >> name;
std::string name, detail;
words >> name >> detail;
auto *track{simulation::Paths.find(name)};
auto const *model{track != nullptr ? track_sleepers::timber_model(*track) : nullptr};
if (model == nullptr || false == model->fastened())
@@ -170,11 +170,25 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c
continue;
auto const middle{0.5 * (beam.from + beam.to)};
std::string text{format("SELFTEST fastenings %s at %.2f %.2f:", track->name().c_str(), middle.x, middle.z)};
if (detail == "rails")
{
auto const span{beam.to - beam.from};
auto const length{glm::length(glm::dvec2{span.x, span.z})};
text += " rails";
for (auto const &rail : rails.lying(beam))
{
auto const at{beam.from + span * (rail.at / length)};
text += format(" %.3f %.3f k%d r%.3f;", at.x, at.z, static_cast<int>(rail.kind), rail.rise);
}
text += " parts";
}
for (auto const &fastening : rails.on(beam, *model))
{
text += format(" %s%s %.3f %.3f %+.0f", fastening.pad ? "pad:" : "", track_sleepers::part_names[fastening.part], fastening.point.x, fastening.point.z, glm::degrees(std::atan2(fastening.across.y, fastening.across.x)));
if (fastening.lift != 0.f)
text += format(" ^%.3f", fastening.lift);
if (detail == "rails" && (fastening.scale != 1.f || fastening.shift != 0.f))
text += format(" s%.3f/%.3f/%.3f", fastening.scale, fastening.pivot, fastening.shift);
if (false == fastening.pad)
++counts[fastening.part];
}

View File

@@ -40,6 +40,11 @@ fastened_rails::role fastened_rails::rail::at( double const Distance ) const {
return role::plain;
}
bool fastened_rails::rail::guarded( double const Distance ) const {
return std::any_of( guards.begin(), guards.end(), [&]( glm::dvec2 const &Stretch ) { return Distance >= Stretch.x && Distance <= Stretch.y; } );
}
fastened_rails::fastened_rails( TTrack &Track ) {
std::vector<TTrack *> tracks;
@@ -65,8 +70,35 @@ fastened_rails::fastened_rails( TTrack &Track ) {
for( auto const &check : track_rails::rail_checks( *track )[ Path ][ Side > 0 ? 1 : 0 ] ) {
Line.checks.emplace_back( check.from, check.to );
}
Line.guards = track->m_railguards[ Path ][ Side > 0 ? 1 : 0 ];
}
m_rails.emplace_back( std::move( Line ) ); };
// the rail of the side of the path from and to along it, by points the given way out from the middle of the path
auto const sampled = [&]( int const Path, int const Side, double const From, double const To, double const Middle, knots::line const &Offsets ) {
auto const *segment { path_segment( *track, Path ) };
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 );
}
return line; };
for( int path = 0; path < path_count( *track ); ++path ) {
auto const *segment { path_segment( *track, path ) };
if( segment == nullptr ) {
@@ -86,32 +118,21 @@ fastened_rails::fastened_rails( TTrack &Track ) {
if( to - from < fastening_merge ) {
continue;
}
auto const offsets { track_rails::part_offsets( before, after ) };
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;
auto line { sampled( path, side, from, to, middle, track_rails::part_offsets( before, after ) ) };
line.rises = track_rails::part_rises( rise > 0.0 ? track_rails::wing_shares( before, after, stock_stretch( *track, path, side ) ) : knots::line {}, rise, blade_heel( *track, path, 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 );
}
// the guard rails of the crossings at wide angles inside the rail, the flangeway from it
for( auto const &guard : cut ? track->m_railguards[ path ][ side > 0 ] : std::vector<glm::dvec2> {} ) {
auto const from { std::max( 0.0, guard.x ) };
auto const to { std::min( length, guard.y ) };
if( to - from < fastening_merge ) {
continue;
}
auto line { sampled( path, side, from, to, middle - ( track_rails::flangeway + size.head ), {} ) };
line.guard = true;
m_rails.emplace_back( std::move( line ) );
}
}
}
}
@@ -345,9 +366,152 @@ std::vector<fastening> fastened_rails::on( beam const &Beam, beam_model const &M
}
fit( result, Beam, Model );
lift( result, rails, Beam, Model );
along( result, Beam, Model );
return result;
}
void fastened_rails::along( std::vector<fastening> &Fastenings, beam const &Beam, beam_model const &Model ) const {
auto const span { Beam.to - Beam.from };
auto const length { glm::length( plan( span ) ) };
auto const start { plan( Beam.from ) };
auto const axis { plan( span ) / length };
auto const side { glm::normalize( plan( Beam.along ) ) };
auto const low { glm::min( start, plan( Beam.to ) ) - 2.0 * half_width };
auto const high { glm::max( start, plan( Beam.to ) ) + 2.0 * half_width };
// where the rail lies over the timber, along it: a stretch of a chord of the rail
struct stretch {
rail const *line;
std::size_t index; // of the end of the chord
double from; // along the timber
double to;
};
std::vector<stretch> stretches;
for( auto const &line : m_rails ) {
// a guard rail along the timber lies in the fastening of its running rail
if( line.guard || line.high.x < low.x || line.low.x > high.x || line.high.y < low.y || line.low.y > high.y ) {
continue;
}
for( std::size_t index = 1; index < line.points.size(); ++index ) {
auto const chord { line.points[ index ] - line.points[ index - 1 ] };
if( glm::length( chord ) < 1e-6 || std::abs( cross( axis, glm::normalize( chord ) ) ) >= crossing_sine_min ) {
continue;
}
if( std::abs( line.heights[ index ] - glm::mix( Beam.from.y, Beam.to.y, std::clamp( glm::dot( line.points[ index ] - start, axis ) / length, 0.0, 1.0 ) ) ) > level_tolerance ) {
continue;
}
// the share of the chord over the middle of the timber or its side toward the next one, and over its length
auto const u0 { glm::dot( line.points[ index - 1 ] - start, axis ) };
auto const u1 { glm::dot( line.points[ index ] - start, axis ) };
auto const w0 { glm::dot( line.points[ index - 1 ] - start, side ) };
auto const w1 { glm::dot( line.points[ index ] - start, side ) };
auto from { 0.0 };
auto to { 1.0 };
auto const clip = [&]( double const A, double const B, double const Low, double const High ) {
if( std::abs( B - A ) < 1e-9 ) {
if( A <= Low || A > High ) {
to = -1.0;
}
return;
}
auto const a { ( Low - A ) / ( B - A ) };
auto const b { ( High - A ) / ( B - A ) };
from = std::max( from, std::min( a, b ) );
to = std::min( to, std::max( a, b ) ); };
clip( w0, w1, -half_width + 2.0 * fastening_gap, half_width + 2.0 * fastening_gap );
clip( u0, u1, 0.0, length );
if( to <= from ) {
continue;
}
auto const a { glm::mix( u0, u1, from ) };
auto const b { glm::mix( u0, u1, to ) };
stretches.push_back( { &line, index, std::min( a, b ), std::max( a, b ) } );
}
}
if( stretches.empty() ) {
return;
}
// kept clear of the fastenings of the rails across the timber, and of its ends
std::vector<glm::dvec2> taken;
for( auto const &fastening : Fastenings ) {
auto const at { glm::dot( plan( fastening.point ) - start, axis ) };
auto const turn { glm::dot( fastening.across, axis ) };
auto const &reach { Model.spans[ fastening.part ] };
auto const a { at + turn * fastening.placed( reach.x, fastening.scale ) };
auto const b { at + turn * fastening.placed( reach.y, fastening.scale ) };
taken.emplace_back( std::min( a, b ) - 0.5 * fastening_length - fastening_gap, std::max( a, b ) + 0.5 * fastening_length + fastening_gap );
}
auto const end { 0.5 * fastening_length + fastening_gap };
std::sort( stretches.begin(), stretches.end(), []( stretch const &Left, stretch const &Right ) { return Left.line != Right.line ? Left.line < Right.line : Left.from < Right.from; } );
for( std::size_t first = 0; first < stretches.size(); ) {
// the stretches of one rail going on one from another
auto last { first };
auto to { stretches[ first ].to };
while( last + 1 < stretches.size() && stretches[ last + 1 ].line == stretches[ first ].line && stretches[ last + 1 ].from <= to + 0.01 ) {
to = std::max( to, stretches[ ++last ].to );
}
std::vector<glm::dvec2> free { glm::dvec2 { std::max( stretches[ first ].from, end ), std::min( to, length - end ) } };
for( auto const &blocked : taken ) {
std::vector<glm::dvec2> left;
for( auto const &room : free ) {
if( blocked.y <= room.x || blocked.x >= room.y ) {
left.push_back( room );
continue;
}
if( blocked.x > room.x ) {
left.emplace_back( room.x, blocked.x );
}
if( blocked.y < room.y ) {
left.emplace_back( blocked.y, room.y );
}
}
free = std::move( left );
}
auto const plate = [&]( double const At ) {
for( auto index { first }; index <= last; ++index ) {
auto const &piece { stretches[ index ] };
if( At < piece.from - 1e-6 || At > piece.to + 1e-6 ) {
continue;
}
auto const &line { *piece.line };
auto const u0 { glm::dot( line.points[ piece.index - 1 ] - start, axis ) };
auto const u1 { glm::dot( line.points[ piece.index ] - start, axis ) };
auto const share { std::abs( u1 - u0 ) > 1e-9 ? std::clamp( ( At - u0 ) / ( u1 - u0 ), 0.0, 1.0 ) : 0.0 };
auto const offset { glm::dot( glm::mix( line.points[ piece.index - 1 ], line.points[ piece.index ], share ) - start, side ) };
auto const inward { glm::normalize( glm::mix( line.inward[ piece.index - 1 ], line.inward[ piece.index ], share ) ) };
auto const point { Beam.from + span * ( At / length ) + glm::dvec3 { side.x, 0.0, side.y } * offset };
// with its check rail, or the guard rail of the crossing, in the fastening of both
auto const distance { glm::mix( line.distances[ piece.index - 1 ], line.distances[ piece.index ], share ) };
if( Model.parts[ part_check ] != nullptr && ( line.at( distance ) == role::checked || line.guarded( distance ) ) ) {
Fastenings.push_back( { part_check, point, inward, 1.f, static_cast<float>( 0.5 * check_seats ) } );
}
else {
Fastenings.push_back( { part_plate, point, inward } );
}
return;
} };
for( auto const &room : free ) {
if( room.y < room.x ) {
continue;
}
if( room.y - room.x < 0.5 * pitch ) {
plate( 0.5 * ( room.x + room.y ) );
continue;
}
auto const count { static_cast<int>( std::ceil( ( room.y - room.x ) / pitch ) ) + 1 };
for( int index = 0; index < count; ++index ) {
plate( glm::mix( room.x, room.y, static_cast<double>( index ) / ( count - 1 ) ) );
}
}
first = last + 1;
}
}
std::vector<fastened_rails::hit> fastened_rails::lying( beam const &Beam ) const {
return rails_on( Beam );
}
std::vector<glm::dvec2> fastened_rails::bare( beam const &Beam, beam_model const &Model, std::vector<fastening> const &Fastenings ) const {
auto const span { plan( Beam.to - Beam.from ) };
@@ -807,10 +971,15 @@ std::vector<check_rail_gap> check_rail_seats() {
}
auto const length { segment->GetLength() };
for( auto const side : { -1, 1 } ) {
// the check rails and the guard rails of the crossings at wide angles
auto spans { track->m_railguards[ path ][ side > 0 ? 1 : 0 ] };
for( auto const &check : checks[ path ][ side > 0 ? 1 : 0 ] ) {
check_line line { { track->name(), path, side, check.from, check.to } };
auto const from { std::max( 0.0, check.from ) };
auto const to { std::min( length, check.to ) };
spans.emplace_back( check.from, check.to );
}
for( auto const &check : spans ) {
check_line line { { track->name(), path, side, check.x, check.y } };
auto const from { std::max( 0.0, check.x ) };
auto const to { std::min( length, check.y ) };
auto const count { static_cast<int>( std::ceil( ( to - from ) / fastening_step ) ) };
for( int step = 0; step <= count && to > from; ++step ) {
glm::dvec3 point, across;

View File

@@ -45,13 +45,6 @@ struct fastening {
class fastened_rails {
public:
explicit fastened_rails( TTrack &Track );
// the fastenings of the rails on the timber, of the parts the model has
std::vector<fastening> on( beam const &Beam, beam_model const &Model ) const;
// where the rails on the timber lie over none of the fastenings
std::vector<glm::dvec2> bare( beam const &Beam, beam_model const &Model, std::vector<fastening> const &Fastenings ) const;
private:
// what a rail is where it lies on a timber
enum class role {
plain,
@@ -59,6 +52,24 @@ private:
stock, // the stock rail of a switch with the blade beside it
checked // a rail with a check rail beside it
};
// where a rail crosses a timber
struct hit {
double at { 0.0 }; // along the timber, from its from end
role kind { role::plain };
glm::dvec2 inward { 1.0, 0.0 };
double foot { 0.0 };
double rise { 0.0 };
};
explicit fastened_rails( TTrack &Track );
// the fastenings of the rails on the timber, of the parts the model has
std::vector<fastening> on( beam const &Beam, beam_model const &Model ) const;
// the rails lying on the timber, in the order along it
std::vector<hit> lying( beam const &Beam ) const;
// where the rails on the timber lie over none of the fastenings
std::vector<glm::dvec2> bare( beam const &Beam, beam_model const &Model, std::vector<fastening> const &Fastenings ) const;
private:
// a rail by points along its middle
struct rail {
TTrack *track { nullptr };
@@ -76,15 +87,10 @@ private:
glm::dvec2 blade { -1.0 };
glm::dvec2 stock { -1.0 };
std::vector<glm::dvec2> checks;
std::vector<glm::dvec2> guards; // with the guard rail of a crossing at a wide angle inside it
bool guard { false }; // the guard rail itself
role at( double const Distance ) const;
};
// where a rail crosses a timber
struct hit {
double at { 0.0 }; // along the timber, from its from end
role kind { role::plain };
glm::dvec2 inward { 1.0, 0.0 };
double foot { 0.0 };
double rise { 0.0 };
bool guarded( double const Distance ) const;
};
// the rails lying on the timber, in the order along it, one where several lie on one another, but the blades
@@ -95,6 +101,10 @@ private:
// the fastenings under the raised rails: one holding them moved up on a pad as far as the least raised of them, a rail not raised
// sunk in it, a rail raised more on a pad of its own on it
static void lift( std::vector<fastening> &Fastenings, std::vector<hit> const &Rails, beam const &Beam, beam_model const &Model );
// the plates of the rails lying along the timber rather than across it, as of a track crossing another square to it, on two
// timbers side by side under each of its rails: as far apart as the beams at most, in the room the fastenings of the rails
// across leave, on the timber with the rail over its middle or over its side toward the next one
void along( std::vector<fastening> &Fastenings, beam const &Beam, beam_model const &Model ) const;
std::vector<rail> m_rails;
};

View File

@@ -203,6 +203,12 @@ private:
return result;
}
// the sine of the angle the paths of a grid cross at, no less than of 30 degrees
static double grid_sine( feature const &Feature ) {
return std::max( 0.5, std::sin( 2.0 * std::asin( Feature.sine ) ) );
}
// the pins of the lines led along the path: the rails of a path crossing this one so wide it's laid on a grid of beams; elsewhere
// the lines are spread evenly
std::vector<double> pins( sampled_path const &Path ) const {
@@ -216,17 +222,46 @@ private:
if( feature.paths[ side ] != &Path ) {
continue;
}
// under the rails of the other path, which runs across the beams
// under the middles of the rails of the other path, which runs across the beams
auto const *other { feature.paths[ 1 - side ] };
auto const sine { std::max( 0.5, std::sin( 2.0 * std::asin( feature.sine ) ) ) };
result.push_back( feature.at[ side ] - 0.5 * other->gauge / sine );
result.push_back( feature.at[ side ] + 0.5 * other->gauge / sine );
auto const rail { ( 0.5 * other->gauge + rail_web ) / grid_sine( feature ) };
result.push_back( feature.at[ side ] - rail );
result.push_back( feature.at[ side ] + rail );
}
}
std::sort( result.begin(), result.end() );
return result;
}
// whether the beam of the path at the distance is a timber of a crossing at a wide angle: where it crosses another path so wide
// they're laid as a grid, the other path laying none there, or under the guard rails of the crossing, as far as they reach
bool crossing_timber( sampled_path const &Path, double const Distance ) const {
return std::any_of( features.begin(), features.end(), [&]( feature const &Feature ) {
if( Feature.frog ) {
return false;
}
auto const angle { 2.0 * std::asin( std::min( 1.0, Feature.sine ) ) };
auto const acute { std::min( angle, glm::pi<double>() - angle ) };
for( int side = 0; side < 2; ++side ) {
if( Feature.paths[ side ] != &Path ) {
continue;
}
auto const *other { Feature.paths[ 1 - side ] };
auto reach { Feature.grid ? ( other->half + half_width + end_gap ) / grid_sine( Feature ) : 0.0 };
if( acute > track_rails::wide_angle ) {
// past where the rails cross, as far as the guard rails go on along them, a beam reaching under their ends too
reach = std::max(
reach,
( 0.5 * other->gauge + 0.5 * Path.gauge * std::cos( acute ) + track_rails::flangeway + rail_head + track_rails::guard_reach ) / std::sin( acute ) + half_width );
}
if( std::abs( Distance - Feature.at[ side ] ) < reach ) {
return true;
}
}
return false; } );
}
// the line of the timbers carrying the path nearest to the distance on the side, between the distance and the reach from it
std::optional<record> record_near( std::size_t const Index, double const Distance, double const Side, double const Reach ) const {
@@ -343,8 +378,10 @@ private:
}
std::vector<anchor> anchors { start };
for( auto const pin : pins( path ) ) {
if( pin > start.at + 0.5 * pitch_min && pin < end.at - 0.5 * pitch_min && pin - anchors.back().at >= pitch_min ) {
anchors.push_back( { pin, 0.0 } );
// two timbers side by side under the rail running along them, its fastenings on both
if( pin - half_width > start.at + 0.5 * pitch_min && pin + half_width < end.at - 0.5 * pitch_min && pin - half_width - anchors.back().at >= pitch_min ) {
anchors.push_back( { pin - half_width, 0.0 } );
anchors.push_back( { pin + half_width, 0.0 } );
}
}
anchors.push_back( end );
@@ -515,7 +552,7 @@ private:
auto const top { point - rolledup * static_cast<double>( path.track->rail_height() ) };
// a sleeper of a plain track running alone and square to it, the rest are the timbers of the switches and crossings
auto const carried { std::count_if( bands.begin(), bands.end(), []( band const &Band ) { return Band.carried; } ) };
auto const plain { path.track->eType == tt_Normal && carried == 1 && pieces.size() == 1 && false == pieces.front().clipped && std::abs( glm::dot( forward, along ) ) > 0.9998 };
auto const plain { path.track->eType == tt_Normal && carried == 1 && pieces.size() == 1 && false == pieces.front().clipped && std::abs( glm::dot( forward, along ) ) > 0.9998 && false == crossing_timber( path, Distance ) };
for( std::size_t index = 0; index < pieces.size(); ++index ) {
auto const &piece { pieces[ index ] };
auto const &way { turned[ index ] };

View File

@@ -20,6 +20,9 @@ double constexpr pitch_min { 0.83 * pitch };
double constexpr pitch_max { 1.08 * pitch };
// how far out from the middle of a track its rail reaches past half the gauge: the foot of the rail outside the gauge line
double constexpr rail_reach { 0.1115 };
// how far out from the gauge line the middle of the rail is, and how wide its head is
double constexpr rail_web { 0.0365 };
double constexpr rail_head { 0.0735 };
// how far a beam reaches out past the foot of the rail, 2.6 m sleepers under the standard gauge, and how far at least
double constexpr overhang { 0.471 };
double constexpr overhang_min { 0.25 };
@@ -61,6 +64,8 @@ double constexpr fastening_nose { 0.05 };
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 };
// how long a fastening is along its rail, at most
double constexpr fastening_length { 0.19 };
// how far past the edge of the foot of a rail its fastening reaches, with the clip holding it
double constexpr fastening_clip { 0.09 };
// the room left between the fastenings of rails close by, and how narrow their plates get to keep it

View File

@@ -1598,6 +1598,10 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
for( int path = 0; path < 2; ++path ) {
for( int side = 0; side < 2; ++side ) {
m_railplans[ path ][ side ] = layout.plans[ path ][ side ];
m_railguards[ path ][ side ].clear();
for( auto const &guard : layout.guards[ path ][ side ] ) {
m_railguards[ path ][ side ].emplace_back( guard.from, guard.to );
}
}
}
}

View File

@@ -218,6 +218,7 @@ public:
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
std::vector<glm::dvec2> m_railguards[ 2 ][ 2 ]; // the guard rails inside its rails at the crossings at wide angles, from and to along the path, 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;