diff --git a/application/editormodeselftestsleepers.cpp b/application/editormodeselftestsleepers.cpp index 3f795bb0..cf9d6c51 100644 --- a/application/editormodeselftestsleepers.cpp +++ b/application/editormodeselftestsleepers.cpp @@ -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(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]; } diff --git a/world/SleeperFastenings.cpp b/world/SleeperFastenings.cpp index b11dea4a..dfae41f8 100644 --- a/world/SleeperFastenings.cpp +++ b/world/SleeperFastenings.cpp @@ -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 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( 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::max() }; + line.high = glm::dvec2 { std::numeric_limits::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( -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( 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::max() }; - line.high = glm::dvec2 { std::numeric_limits::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( -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 {} ) { + 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 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 &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 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 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 free { glm::dvec2 { std::max( stretches[ first ].from, end ), std::min( to, length - end ) } }; + for( auto const &blocked : taken ) { + std::vector 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( 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( std::ceil( ( room.y - room.x ) / pitch ) ) + 1 }; + for( int index = 0; index < count; ++index ) { + plate( glm::mix( room.x, room.y, static_cast( index ) / ( count - 1 ) ) ); + } + } + first = last + 1; + } +} + +std::vector fastened_rails::lying( beam const &Beam ) const { + + return rails_on( Beam ); +} + std::vector fastened_rails::bare( beam const &Beam, beam_model const &Model, std::vector const &Fastenings ) const { auto const span { plan( Beam.to - Beam.from ) }; @@ -807,10 +971,15 @@ std::vector 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( std::ceil( ( to - from ) / fastening_step ) ) }; for( int step = 0; step <= count && to > from; ++step ) { glm::dvec3 point, across; diff --git a/world/SleeperFastenings.h b/world/SleeperFastenings.h index f6d22003..dc8d88da 100644 --- a/world/SleeperFastenings.h +++ b/world/SleeperFastenings.h @@ -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 on( beam const &Beam, beam_model const &Model ) const; - // where the rails on the timber lie over none of the fastenings - std::vector bare( beam const &Beam, beam_model const &Model, std::vector 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 on( beam const &Beam, beam_model const &Model ) const; + // the rails lying on the timber, in the order along it + std::vector lying( beam const &Beam ) const; + // where the rails on the timber lie over none of the fastenings + std::vector bare( beam const &Beam, beam_model const &Model, std::vector 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 checks; + std::vector 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 &Fastenings, std::vector 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 &Fastenings, beam const &Beam, beam_model const &Model ) const; std::vector m_rails; }; diff --git a/world/SleeperLayout.cpp b/world/SleeperLayout.cpp index 23bca2fa..f22623f8 100644 --- a/world/SleeperLayout.cpp +++ b/world/SleeperLayout.cpp @@ -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 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() - 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_near( std::size_t const Index, double const Distance, double const Side, double const Reach ) const { @@ -343,8 +378,10 @@ private: } std::vector 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( 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 ] }; diff --git a/world/SleeperRules.h b/world/SleeperRules.h index bba9b833..30267cfa 100644 --- a/world/SleeperRules.h +++ b/world/SleeperRules.h @@ -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 diff --git a/world/Track.cpp b/world/Track.cpp index 9ca80383..82e8a954 100644 --- a/world/Track.cpp +++ b/world/Track.cpp @@ -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 ); + } } } } diff --git a/world/Track.h b/world/Track.h index 9d18f06c..64426404 100644 --- a/world/Track.h +++ b/world/Track.h @@ -218,6 +218,7 @@ public: bool m_editorremoved { false }; std::vector 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 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 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;