diff --git a/application/editormodeselftestsleepers.cpp b/application/editormodeselftestsleepers.cpp index 930bc134..63972320 100644 --- a/application/editormodeselftestsleepers.cpp +++ b/application/editormodeselftestsleepers.cpp @@ -188,27 +188,46 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c auto const verbose{mode == "all"}; auto const start{std::chrono::steady_clock::now()}; auto const rows{track_sleepers::check_fastenings()}; - std::size_t beams{0}, overlaps{0}, tracks{0}; + std::size_t beams{0}, overlaps{0}, tracks{0}, bare{0}; std::array parts{}; for (auto const &row : rows) { beams += row.beams; overlaps += row.overlaps; + bare += row.bares.size(); tracks += (row.overlaps > 0 ? 1 : 0); for (int part = 0; part < track_sleepers::part_count; ++part) parts[part] += row.parts[part]; - WriteLog(format("SELFTEST fastencheck %s: %zu timbers, %zu plates, %zu chairs, %zu checks, %zu overlaps%s", - row.track.c_str(), row.beams, row.parts[track_sleepers::part_plate], row.parts[track_sleepers::part_chair], row.parts[track_sleepers::part_check], row.overlaps, + WriteLog(format("SELFTEST fastencheck %s: %zu timbers, %zu plates, %zu chairs, %zu checks, %zu bare rails, %zu overlaps%s", + row.track.c_str(), row.beams, row.parts[track_sleepers::part_plate], row.parts[track_sleepers::part_chair], row.parts[track_sleepers::part_check], row.bares.size(), row.overlaps, row.overlaps > 0 ? format(", worst %.3f m %s at %.2f %.2f", row.worst, row.worstparts.c_str(), row.worstat.x, row.worstat.z).c_str() : "")); + if (verbose) + for (auto const &at : row.bares) + WriteLog(format("SELFTEST fastencheck bare %s at %.3f %.3f", row.track.c_str(), at.x, at.y)); if (verbose) for (auto const &overlap : row.cases) WriteLog(format("SELFTEST fastencheck overlap %s %d %d apart %.3f over %.3f at %.2f %.2f", row.track.c_str(), overlap.parts[0], overlap.parts[1], overlap.apart, overlap.over, overlap.at.x, overlap.at.z)); } - WriteLog(format("SELFTEST fastencheck: %zu tracks, %zu timbers, %zu plates, %zu chairs, %zu checks, %zu overlaps on %zu tracks; in %.1f ms", - rows.size(), beams, parts[track_sleepers::part_plate], parts[track_sleepers::part_chair], parts[track_sleepers::part_check], overlaps, tracks, + WriteLog(format("SELFTEST fastencheck: %zu tracks, %zu timbers, %zu plates, %zu chairs, %zu checks, %zu bare rails, %zu overlaps on %zu tracks; in %.1f ms", + rows.size(), beams, parts[track_sleepers::part_plate], parts[track_sleepers::part_chair], parts[track_sleepers::part_check], bare, overlaps, tracks, std::chrono::duration(std::chrono::steady_clock::now() - start).count())); return selftest_result::handled; } + if (command == "checkseats") + { + // the check rails with timbers under them and no fastening seating them, along how much of them + auto const gaps{track_sleepers::check_rail_seats()}; + double bare{0.0}; + std::size_t unsupported{0}; + for (auto const &gap : gaps) + { + bare += gap.bare; + unsupported += gap.unsupported; + WriteLog(format("SELFTEST checkseats %s path %d side %+d %.2f-%.2f: %.0f of %zu timbers bare, %zu with nothing under, first at %.2f %.2f", gap.track.c_str(), gap.path, gap.side, gap.from, gap.to, gap.bare, gap.timbers, gap.unsupported, gap.at.x, gap.at.y)); + } + WriteLog(format("SELFTEST checkseats: %zu check rails with %.0f timbers bare, %zu with nothing under", gaps.size(), bare, unsupported)); + return selftest_result::handled; + } if (command == "sleepercheck") { auto const start{std::chrono::steady_clock::now()}; diff --git a/world/SleeperFastenings.cpp b/world/SleeperFastenings.cpp index c9cd5945..b18ac69b 100644 --- a/world/SleeperFastenings.cpp +++ b/world/SleeperFastenings.cpp @@ -106,13 +106,12 @@ fastened_rails::fastened_rails( TTrack &Track ) { } } -std::vector fastened_rails::on( beam const &Beam, beam_model const &Model ) const { +std::vector fastened_rails::rails_on( beam const &Beam ) const { - std::vector result; auto const span { Beam.to - Beam.from }; auto const length { glm::length( plan( span ) ) }; if( length < 0.1 ) { - return result; + return {}; } auto const start { plan( Beam.from ) }; auto const axis { plan( span ) / length }; @@ -173,12 +172,71 @@ std::vector fastened_rails::on( beam const &Beam, beam_model const &M } index = next; } + return rails; +} + +std::vector fastened_rails::on( beam const &Beam, beam_model const &Model ) const { + + std::vector result; + auto const span { Beam.to - Beam.from }; + auto const length { glm::length( plan( span ) ) }; + if( length < 0.1 ) { + return result; + } + auto const axis { plan( span ) / length }; + auto const rails { rails_on( Beam ) }; auto const point = [&]( double const At ) { 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 ) { + 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; + } + } + } + if( covered < 2 ) { + result.push_back( { part_plate, point( rails[ First ].at ), rails[ First ].inward } ); + ++First; + continue; + } + result.push_back( { part_check, point( origin ), axis * turn, 1.f, static_cast( 0.5 * check_seats ), 0.f, true } ); + First += covered; + } + }; 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 ) { + ++last; + } + if( last >= index + 2 && has( part_check ) ) { + crowd( index, last ); + index = last; + continue; + } if( index + 1 < rails.size() && rails[ index + 1 ].at - rail.at < fastening_pair && rail.kind != role::stock && rails[ index + 1 ].kind != role::stock ) { auto const &other { rails[ index + 1 ] }; // a rail of the two with its check rail on the side away from the other one in the fastening with the check rail, @@ -202,7 +260,7 @@ std::vector fastened_rails::on( beam const &Beam, beam_model const &M result.push_back( { part_plate, point( 0.5 * ( rail.at + other.at ) ), rail.inward } ); } else if( has( part_check ) ) { - result.push_back( { part_check, point( 0.5 * ( rail.at + other.at - check_seats ) ), axis, 1.f, static_cast( 0.5 * check_seats ) } ); + result.push_back( { part_check, point( 0.5 * ( rail.at + other.at - check_seats ) ), axis, 1.f, static_cast( 0.5 * check_seats ), 0.f, true } ); } else { result.push_back( { part_plate, point( rail.at ), rail.inward } ); @@ -214,6 +272,14 @@ std::vector fastened_rails::on( beam const &Beam, beam_model const &M switch( rail.kind ) { case role::stock: { result.push_back( { has( part_chair ) ? part_chair : part_plate, point( rail.at ), -rail.inward } ); + if( has( part_chair ) ) { + // the rail on a plate as wide as its foot, between the brace and the slide plate + auto const &plate { Model.spans[ part_plate ] }; + auto const low { -rail.foot }; + auto const high { std::min( rail.foot, Model.spans[ part_chair ].x ) }; + auto const scale { ( high - low ) / ( plate.y - plate.x ) }; + result.push_back( { part_plate, point( rail.at ), -rail.inward, static_cast( scale ), 0.f, static_cast( low - scale * plate.x ) } ); + } if( has( part_slide ) ) { // the slide plate from the edge of the foot of the stock rail on, not under it auto const &slide { Model.spans[ part_slide ] }; @@ -236,6 +302,25 @@ std::vector fastened_rails::on( beam const &Beam, beam_model const &M return result; } +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 ) }; + auto const length { glm::length( span ) }; + std::vector result; + for( auto const &rail : rails_on( Beam ) ) { + auto const at { plan( Beam.from ) + span * ( rail.at / length ) }; + auto const under { std::any_of( Fastenings.begin(), Fastenings.end(), [&]( fastening const &Fastening ) { + auto const along { glm::dot( at - plan( Fastening.point ), Fastening.across ) }; + auto const a { Fastening.placed( Model.spans[ Fastening.part ].x, Fastening.scale ) }; + auto const b { Fastening.placed( Model.spans[ Fastening.part ].y, Fastening.scale ) }; + return along >= std::min( a, b ) - 0.01 && along <= std::max( a, b ) + 0.01; } ) }; + if( false == under ) { + result.push_back( at ); + } + } + return result; +} + void fastened_rails::fit( std::vector &Fastenings, beam const &Beam, beam_model const &Model ) { auto const start { plan( Beam.from ) }; @@ -324,6 +409,22 @@ void fastened_rails::fit( std::vector &Fastenings, beam const &Beam, } } return false; }; + auto const checks = [&]( std::pair const &Rail ) { + for( auto member { Rail.first }; member < Rail.second; ++member ) { + auto const &fastening { Fastenings[ order[ member ] ] }; + if( fastening.part == part_check && false == fastening.both ) { + return true; + } + } + return false; }; + auto const faces = [&]( std::pair const &Rail, double const Toward ) { + for( auto member { Rail.first }; member < Rail.second; ++member ) { + auto const &fastening { Fastenings[ order[ member ] ] }; + if( fastening.part == part_check && false == fastening.both && glm::dot( fastening.across, axis ) * Toward > 0.0 ) { + return true; + } + } + return false; }; auto const plates = [&]( std::pair const &Rail ) { for( auto member { Rail.first }; member < Rail.second; ++member ) { if( Fastenings[ order[ member ] ].part != part_plate ) { @@ -356,6 +457,37 @@ void fastened_rails::fit( std::vector &Fastenings, beam const &Beam, for( auto member { Rail.first }; member < Rail.second; ++member ) { dropped[ order[ member ] ] = true; } }; + // the plates of the rail with their end toward the rail beside Room from it, the other end kept where it is + auto const clip = [&]( std::pair const &Rail, double const Toward, double const Room ) { + if( Room < 0.0 ) { + return false; + } + for( auto member { Rail.first }; member < Rail.second; ++member ) { + auto &fastening { Fastenings[ order[ member ] ] }; + auto const &span { Model.spans[ fastening.part ] }; + 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 ) }; + if( scale < clip_least ) { + return false; + } + fastening.pivot = static_cast( away ); + fastening.shift = static_cast( stays - away ); + fastening.scale = static_cast( scale ); + } + return true; }; + // the fastenings of the rail made plates of their own + auto const replate = [&]( std::pair const &Rail ) -> std::pair const & { + for( auto member { Rail.first }; member < Rail.second; ++member ) { + auto &fastening { Fastenings[ order[ member ] ] }; + fastening.part = part_plate; + fastening.scale = 1.f; + fastening.pivot = 0.f; + fastening.shift = 0.f; + } + 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 const &Rail, int const Side ) { for( auto member { Rail.first }; member < Rail.second; ++member ) { @@ -366,11 +498,28 @@ void fastened_rails::fit( std::vector &Fastenings, beam const &Beam, } } return false; }; - if( false == chair( second ) && apart <= firstreach.y ) { + // 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 ) ) { + if( faces( first, 1.0 ) && apart - check_seats <= secondreach.x ) { + if( false == clip( replate( first ), 1.0, apart - secondreach.x - fastening_gap ) ) { + drop( first ); + } + kept = index; + continue; + } + if( faces( second, -1.0 ) && apart - check_seats <= firstreach.y ) { + if( false == clip( replate( second ), -1.0, apart - firstreach.y - fastening_gap ) ) { + drop( second ); + } + continue; + } + } + if( false == chair( second ) && apart <= firstreach.y && ( false == checks( second ) || checks( first ) ) ) { drop( second ); continue; } - if( false == chair( first ) && apart <= secondreach.x ) { + if( false == chair( first ) && apart <= secondreach.x && ( false == checks( first ) || checks( second ) ) ) { drop( first ); } else { @@ -379,14 +528,44 @@ void fastened_rails::fit( std::vector &Fastenings, beam const &Beam, brace( second, 0 ); } } - // a rail in a plate still lying over the fastening of a rail beside held with more than a plate goes without its own + // a rail in a plate still lying over the fastening of a rail beside held with more than a plate gets its plate cut short + // at the edge of that fastening, as on one plate with it, or goes without its own if the plate would no longer be under it if( overlaps() ) { if( plates( second ) && false == plates( first ) ) { - drop( second ); - continue; + if( false == clip( second, -1.0, apart - extent( first ).y - fastening_gap ) ) { + drop( second ); + continue; + } } - if( plates( first ) && false == plates( second ) ) { - drop( first ); + else if( plates( first ) && false == plates( second ) ) { + if( false == clip( first, 1.0, apart - extent( second ).x - fastening_gap ) ) { + drop( first ); + } + } + } + // the plates of two rails still lying over each other, as of the stock rails back to back, one plate under both + auto const plate = [&]( std::pair const &Rail ) { + for( auto member { Rail.first }; member < Rail.second; ++member ) { + if( Fastenings[ order[ member ] ].part == part_plate && false == dropped[ order[ member ] ] ) { + return order[ member ]; + } + } + return Fastenings.size(); }; + auto const firstplate { plate( first ) }; + auto const secondplate { plate( second ) }; + if( firstplate < Fastenings.size() && secondplate < Fastenings.size() ) { + auto &one { Fastenings[ firstplate ] }; + auto const &other { Fastenings[ secondplate ] }; + auto const from { -reach( one, one.scale ).x }; + auto const to { apart + reach( other, other.scale ).y }; + if( reach( one, one.scale ).y + reach( other, other.scale ).x > apart ) { + auto const &span { Model.spans[ part_plate ] }; + auto const turn { glm::dot( one.across, axis ) }; + auto const scale { ( to - from ) / ( span.y - span.x ) }; + one.pivot = 0.f; + one.scale = static_cast( scale ); + one.shift = static_cast( ( turn > 0.0 ? from : -to ) - scale * span.x ); + dropped[ secondplate ] = true; } } kept = index; @@ -431,7 +610,11 @@ std::vector check_fastenings() { glm::dvec3 point; }; std::vector footprints; - for( auto const &fastening : rails->on( beam, *model ) ) { + auto const fastenings { rails->on( beam, *model ) }; + for( auto const &at : rails->bare( beam, *model, fastenings ) ) { + row.bares.push_back( at ); + } + for( auto const &fastening : fastenings ) { ++row.parts[ fastening.part ]; auto const at { glm::dot( plan( fastening.point ) - start, axis ) }; auto const turn { glm::dot( fastening.across, axis ) }; @@ -461,4 +644,128 @@ std::vector check_fastenings() { return result; } +std::vector check_rail_seats() { + + // the check rails, by points along their middle + struct check_line { + check_rail_gap gap; + std::vector points; + glm::dvec2 low { std::numeric_limits::max() }; + glm::dvec2 high { std::numeric_limits::lowest() }; + }; + std::vector lines; + for( auto *track : simulation::Paths.sequence() ) { + if( track == nullptr || track->m_editorremoved || false == bedded( *track ) || false == track_rails::cut_rails( *track ) ) { + continue; + } + auto const size { track->rail_dimensions() }; + auto const middle { 0.5 * std::abs( track->fTrackWidth ) + size.web }; + auto const &checks { track_rails::rail_checks( *track ) }; + 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() }; + for( auto const side : { -1, 1 } ) { + 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 ) }; + auto const count { static_cast( std::ceil( ( to - from ) / fastening_step ) ) }; + for( int step = 0; step <= count && to > from; ++step ) { + glm::dvec3 point, across; + segment->plan_frame( from + ( to - from ) * step / std::max( 1, count ), point, across ); + auto const flat { plan( across ) }; + if( glm::length( flat ) < 1e-9 ) { + continue; + } + auto const inward { glm::normalize( flat ) * static_cast( -side ) }; + auto const position { plan( point ) - inward * ( middle - check_seats ) }; + line.points.push_back( position ); + line.low = glm::min( line.low, position ); + line.high = glm::max( line.high, position ); + } + if( line.points.size() > 1 ) { + lines.push_back( std::move( line ) ); + } + } + } + } + } + // where each timber with the fastenings crosses a check rail, a seat of a fastening of the timber there + std::vector timbers( lines.size(), 0 ); + for( auto *track : simulation::Paths.sequence() ) { + if( track == nullptr || track->m_editorremoved || false == track->m_sleepers3d.active ) { + continue; + } + auto const *model { timber_model( *track ) }; + if( model == nullptr || false == model->fastened() ) { + continue; + } + std::optional rails; + for( auto const &beam : place( *track ) ) { + if( beam.plain ) { + continue; + } + if( false == rails.has_value() ) { + rails.emplace( *track ); + } + auto const start { plan( beam.from ) }; + auto const span { plan( beam.to - beam.from ) }; + auto const low { glm::min( start, start + span ) }; + auto const high { glm::max( start, start + span ) }; + std::vector seats; + auto const fastenings { rails->on( beam, *model ) }; + auto const under = [&]( glm::dvec2 const &Point ) { + return std::any_of( fastenings.begin(), fastenings.end(), [&]( fastening const &Fastening ) { + auto const along { glm::dot( Point - plan( Fastening.point ), Fastening.across ) }; + auto const a { Fastening.placed( model->spans[ Fastening.part ].x, Fastening.scale ) }; + auto const b { Fastening.placed( model->spans[ Fastening.part ].y, Fastening.scale ) }; + return along >= std::min( a, b ) && along <= std::max( a, b ); } ); }; + for( auto const &fastening : fastenings ) { + seats.push_back( plan( fastening.point ) + fastening.across * fastening.placed( 0.0, fastening.scale ) ); + if( fastening.part == part_check ) { + seats.push_back( plan( fastening.point ) + fastening.across * fastening.placed( check_seats, fastening.scale ) ); + } + } + for( std::size_t index = 0; index < lines.size(); ++index ) { + auto &line { lines[ index ] }; + if( 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 point = 1; point < line.points.size(); ++point ) { + auto const chord { line.points[ point ] - line.points[ point - 1 ] }; + double u, v; + if( false == track_rails::meet( start, span, line.points[ point - 1 ], chord, 1e-12, u, v ) || u < 0.0 || u > 1.0 || v < 0.0 || v >= 1.0 ) { + continue; + } + auto const at { start + span * u }; + ++timbers[ index ]; + auto const seated { std::any_of( seats.begin(), seats.end(), [&]( glm::dvec2 const &Seat ) { + return glm::length( Seat - at ) < 0.03; } ) }; + if( false == seated ) { + if( line.gap.bare == 0.0 ) { + line.gap.at = at; + } + line.gap.bare += 1.0; + if( false == under( at ) ) { + ++line.gap.unsupported; + } + } + break; + } + } + } + } + std::vector result; + for( std::size_t index = 0; index < lines.size(); ++index ) { + if( lines[ index ].gap.bare > 0.0 ) { + lines[ index ].gap.timbers = timbers[ index ]; + result.push_back( lines[ index ].gap ); + } + } + return result; +} + } // namespace track_sleepers diff --git a/world/SleeperFastenings.h b/world/SleeperFastenings.h index c4f36a82..98a52fc4 100644 --- a/world/SleeperFastenings.h +++ b/world/SleeperFastenings.h @@ -27,9 +27,10 @@ struct fastening { int part { part_plate }; glm::dvec3 point { 0.0 }; // under the middle of the rail, on the top of the timber glm::dvec2 across { 1.0, 0.0 }; // unit vector in plan square to the rail, the way the +x of the part goes - float scale { 1.f }; // along its x, a plate or a slide plate narrowed where it'd lie over the fastenings of rails close beside + float scale { 1.f }; // along its x, a plate or a slide plate narrowed where it'd lie over the fastenings of rails close beside, a plate stretched under two 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 // 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 ); } @@ -42,6 +43,8 @@ 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 @@ -77,8 +80,10 @@ private: double foot { 0.0 }; }; - // the fastenings of rails close by fitted to the room between them: narrowed across, and a rail lying on the fastening of the - // rail beside it left without one of its own + // the rails lying on the timber, in the order along it, one where several lie on one another, but the blades + std::vector rails_on( beam const &Beam ) const; + // the fastenings of rails close by fitted to the room between them: narrowed across, a plate cut short at the fastening of the + // rail beside it or left out, the plates of two rails lying over each other made one static void fit( std::vector &Fastenings, beam const &Beam, beam_model const &Model ); std::vector m_rails; @@ -90,6 +95,7 @@ struct fastening_row { std::size_t beams { 0 }; std::array parts {}; std::size_t overlaps { 0 }; // fastenings lying over one another on a timber, along it + std::vector bares; // where rails lie on a timber over no fastening double worst { 0.0 }; glm::dvec3 worstat { 0.0 }; std::string worstparts; @@ -104,4 +110,19 @@ struct fastening_row { // those of all the tracks laying timbers with fastenings std::vector check_fastenings(); +// a check rail of a track with timbers under it and no fastening seating it there, along some of its length +struct check_rail_gap { + std::string track; + int path { 0 }; + int side { 1 }; + double from { 0.0 }; // the check rail, along the path + double to { 0.0 }; + std::size_t timbers { 0 }; // with the fastenings, under it + double bare { 0.0 }; // of those, with no seat under it + std::size_t unsupported { 0 }; // of those, with no fastening under it at all + glm::dvec2 at { 0.0 }; // where the first of those crosses it +}; +// those of all the tracks with fastenings in the model of their timbers +std::vector check_rail_seats(); + } // namespace track_sleepers diff --git a/world/SleeperRules.h b/world/SleeperRules.h index d4573e9d..b1e3062b 100644 --- a/world/SleeperRules.h +++ b/world/SleeperRules.h @@ -62,6 +62,8 @@ 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 double constexpr fastening_gap { 0.01 }; double constexpr plate_least { 0.6 }; +// a plate cut short at the fastening of the rail beside, as on one plate with it, no narrower than this +double constexpr clip_least { 0.4 }; double constexpr slide_least { 0.25 }; // a fastening of two rails narrowed no more than this, the rails lying a little off its seats then double constexpr check_least { 0.8 };