mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-27 11:47:29 +01:00
feat(sleepers): the fastenings fitted on the switches crossing and the double ones: the chair of the model split into its brace and its slide plate, the slide plate from the edge of the foot of the stock rail on, not under it; the plates and the slide plates narrowed where the rails lie close, the fastening of two rails centred between them, a rail lying on the fastening of the rail beside it left without one of its own, the brace dropped where the rail beside comes too close; selftest fastencheck
This commit is contained in:
@@ -162,7 +162,6 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c
|
||||
WriteLog("SELFTEST fastenings: no track " + name + " with the fastenings in the model of its timbers");
|
||||
return selftest_result::handled;
|
||||
}
|
||||
std::array<char const *, track_sleepers::part_count> const names{"begin", "main", "end", "plate", "chair", "check"};
|
||||
track_sleepers::fastened_rails const rails{*track};
|
||||
std::array<int, track_sleepers::part_count> counts{};
|
||||
for (auto const &beam : track_sleepers::place(*track))
|
||||
@@ -173,7 +172,7 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c
|
||||
std::string text{format("SELFTEST fastenings %s at %.2f %.2f:", track->name().c_str(), middle.x, middle.z)};
|
||||
for (auto const &fastening : rails.on(beam, *model))
|
||||
{
|
||||
text += format(" %s %.3f %.3f %+.0f", names[fastening.part], fastening.point.x, fastening.point.z, glm::degrees(std::atan2(fastening.across.y, fastening.across.x)));
|
||||
text += format(" %s %.3f %.3f %+.0f", track_sleepers::part_names[fastening.part], fastening.point.x, fastening.point.z, glm::degrees(std::atan2(fastening.across.y, fastening.across.x)));
|
||||
++counts[fastening.part];
|
||||
}
|
||||
WriteLog(text);
|
||||
@@ -181,6 +180,35 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c
|
||||
WriteLog(format("SELFTEST fastenings %s: %d plates, %d chairs, %d checks", track->name().c_str(), counts[track_sleepers::part_plate], counts[track_sleepers::part_chair], counts[track_sleepers::part_check]));
|
||||
return selftest_result::handled;
|
||||
}
|
||||
if (command == "fastencheck")
|
||||
{
|
||||
// the fastenings of all the tracks laying timbers with them: of each kind, and those over one another on a timber
|
||||
std::string mode;
|
||||
words >> mode;
|
||||
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::array<std::size_t, track_sleepers::part_count> parts{};
|
||||
for (auto const &row : rows)
|
||||
{
|
||||
beams += row.beams;
|
||||
overlaps += row.overlaps;
|
||||
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,
|
||||
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 &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,
|
||||
std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count()));
|
||||
return selftest_result::handled;
|
||||
}
|
||||
if (command == "sleepercheck")
|
||||
{
|
||||
auto const start{std::chrono::steady_clock::now()};
|
||||
|
||||
@@ -15,6 +15,11 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "world/SleeperRules.h"
|
||||
#include "world/TrackRails.h"
|
||||
#include "world/Track.h"
|
||||
#include "simulation/simulation.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
|
||||
namespace track_sleepers {
|
||||
|
||||
@@ -42,7 +47,8 @@ fastened_rails::fastened_rails( TTrack &Track ) {
|
||||
tracks.push_back( Other );
|
||||
} } );
|
||||
for( auto *track : tracks ) {
|
||||
auto const middle { 0.5 * std::abs( track->fTrackWidth ) + track->rail_dimensions().web };
|
||||
auto const size { track->rail_dimensions() };
|
||||
auto const middle { 0.5 * std::abs( track->fTrackWidth ) + size.web };
|
||||
auto const cut { track_rails::cut_rails( *track ) };
|
||||
auto const switched { track->eType == tt_Switch && track->SwitchExtension != nullptr };
|
||||
// the side of the blade of the path, and the stretch along it from the tip to the heel
|
||||
@@ -63,6 +69,7 @@ fastened_rails::fastened_rails( TTrack &Track ) {
|
||||
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 ) {
|
||||
@@ -138,7 +145,8 @@ std::vector<fastening> fastened_rails::on( beam const &Beam, beam_model const &M
|
||||
hits.push_back( {
|
||||
u * length,
|
||||
line.at( glm::mix( line.distances[ index - 1 ], line.distances[ index ], v ) ),
|
||||
glm::normalize( glm::mix( line.inward[ index - 1 ], line.inward[ index ], v ) ) } );
|
||||
glm::normalize( glm::mix( line.inward[ index - 1 ], line.inward[ index ], v ) ),
|
||||
line.foot } );
|
||||
}
|
||||
}
|
||||
std::sort( hits.begin(), hits.end(), []( hit const &Left, hit const &Right ) { return Left.at < Right.at; } );
|
||||
@@ -177,7 +185,7 @@ std::vector<fastening> 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( rail.at ), axis } );
|
||||
result.push_back( { part_check, point( 0.5 * ( rail.at + other.at - check_seats ) ), axis, 1.f, static_cast<float>( 0.5 * check_seats ) } );
|
||||
}
|
||||
else {
|
||||
result.push_back( { part_plate, point( rail.at ), rail.inward } );
|
||||
@@ -189,10 +197,16 @@ std::vector<fastening> 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_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 ] };
|
||||
auto const edge { std::min<double>( slide.y, -rail.foot ) };
|
||||
result.push_back( { part_slide, point( rail.at ), -rail.inward, static_cast<float>( ( edge - slide.x ) / ( slide.y - slide.x ) ), slide.y, static_cast<float>( edge - slide.y ) } );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case role::checked: {
|
||||
result.push_back( { has( part_check ) ? part_check : part_plate, point( rail.at ), rail.inward } );
|
||||
result.push_back( { has( part_check ) ? part_check : part_plate, point( rail.at ), rail.inward, 1.f, has( part_check ) ? static_cast<float>( 0.5 * check_seats ) : 0.f } );
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -201,6 +215,215 @@ std::vector<fastening> fastened_rails::on( beam const &Beam, beam_model const &M
|
||||
}
|
||||
}
|
||||
}
|
||||
fit( result, Beam, Model );
|
||||
return result;
|
||||
}
|
||||
|
||||
void fastened_rails::fit( std::vector<fastening> &Fastenings, beam const &Beam, beam_model const &Model ) {
|
||||
|
||||
auto const start { plan( Beam.from ) };
|
||||
auto const axis { glm::normalize( plan( Beam.to - Beam.from ) ) };
|
||||
auto const at = [&]( fastening const &Fastening ) {
|
||||
return glm::dot( plan( Fastening.point ) - start, axis ); };
|
||||
// how far it reaches along the timber before and after where it is, at the scale
|
||||
auto const reach = [&]( fastening const &Fastening, double const Scale ) {
|
||||
auto const &span { Model.spans[ Fastening.part ] };
|
||||
auto const turn { glm::dot( Fastening.across, axis ) };
|
||||
auto const a { turn * Fastening.placed( span.x, Scale ) };
|
||||
auto const b { turn * Fastening.placed( span.y, Scale ) };
|
||||
return glm::dvec2 { -std::min( a, b ), std::max( a, b ) }; };
|
||||
auto const least = [&]( fastening const &Fastening ) {
|
||||
switch( Fastening.part ) {
|
||||
case part_plate: { return plate_least; }
|
||||
case part_slide: { return slide_least; }
|
||||
case part_check: { return check_least; }
|
||||
default: { return 1.0; }
|
||||
} };
|
||||
// those of one rail together, the rails in the order along the timber
|
||||
std::vector<std::size_t> order( Fastenings.size() );
|
||||
std::iota( order.begin(), order.end(), std::size_t { 0 } );
|
||||
std::stable_sort( order.begin(), order.end(), [&]( std::size_t const Left, std::size_t const Right ) {
|
||||
return at( Fastenings[ Left ] ) < at( Fastenings[ Right ] ) - 1e-6; } );
|
||||
std::vector<std::pair<std::size_t, std::size_t>> rails;
|
||||
for( std::size_t index = 0; index < order.size(); ) {
|
||||
auto next { index + 1 };
|
||||
while( next < order.size() && at( Fastenings[ order[ next ] ] ) - at( Fastenings[ order[ index ] ] ) < 1e-6 ) {
|
||||
++next;
|
||||
}
|
||||
rails.emplace_back( index, next );
|
||||
index = next;
|
||||
}
|
||||
for( std::size_t index = 1; index < rails.size(); ++index ) {
|
||||
auto const [ firstfrom, firstto ] { rails[ index - 1 ] };
|
||||
auto const [ secondfrom, secondto ] { rails[ index ] };
|
||||
auto const room { at( Fastenings[ order[ secondfrom ] ] ) - at( Fastenings[ order[ firstfrom ] ] ) - fastening_gap };
|
||||
// the ones of both reaching less toward the other as they're narrowed; with them narrowed by the share, how far the two
|
||||
// lie over each other
|
||||
std::vector<std::size_t> narrowed;
|
||||
for( auto member { firstfrom }; member < secondto; ++member ) {
|
||||
auto const &fastening { Fastenings[ order[ member ] ] };
|
||||
auto const side { member < firstto ? 1 : 0 };
|
||||
if( reach( fastening, std::min<double>( fastening.scale, least( fastening ) ) )[ side ] < reach( fastening, fastening.scale )[ side ] - 1e-6 ) {
|
||||
narrowed.push_back( order[ member ] );
|
||||
}
|
||||
}
|
||||
auto const over = [&]( double const Share ) {
|
||||
auto after { 0.0 };
|
||||
auto before { 0.0 };
|
||||
for( auto member { firstfrom }; member < secondto; ++member ) {
|
||||
auto const &fastening { Fastenings[ order[ member ] ] };
|
||||
auto const shrinks { std::find( narrowed.begin(), narrowed.end(), order[ member ] ) != narrowed.end() };
|
||||
auto const scale { shrinks ? glm::mix<double>( fastening.scale, least( fastening ), Share ) : fastening.scale };
|
||||
if( member < firstto ) {
|
||||
after = std::max( after, reach( fastening, scale ).y );
|
||||
}
|
||||
else {
|
||||
before = std::max( before, reach( fastening, scale ).x );
|
||||
}
|
||||
}
|
||||
return after + before - room; };
|
||||
if( narrowed.empty() || over( 0.0 ) <= 0.0 ) {
|
||||
continue;
|
||||
}
|
||||
auto share { 1.0 };
|
||||
if( over( 1.0 ) < 0.0 ) {
|
||||
auto low { 0.0 };
|
||||
for( int step = 0; step < 20; ++step ) {
|
||||
auto const middle { 0.5 * ( low + share ) };
|
||||
( over( middle ) > 0.0 ? low : share ) = middle;
|
||||
}
|
||||
}
|
||||
for( auto const index : narrowed ) {
|
||||
auto &fastening { Fastenings[ index ] };
|
||||
fastening.scale = static_cast<float>( glm::mix<double>( fastening.scale, least( fastening ), share ) );
|
||||
}
|
||||
}
|
||||
// a rail still lying on the fastening of the rail beside it goes without one of its own, unless it's a chair
|
||||
std::vector<bool> dropped( Fastenings.size(), false );
|
||||
auto const chair = [&]( std::pair<std::size_t, std::size_t> const &Rail ) {
|
||||
for( auto member { Rail.first }; member < Rail.second; ++member ) {
|
||||
if( Fastenings[ order[ member ] ].part == part_chair || Fastenings[ order[ member ] ].part == part_slide ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; };
|
||||
auto const extent = [&]( std::pair<std::size_t, std::size_t> const &Rail ) {
|
||||
glm::dvec2 result { 0.0 };
|
||||
for( auto member { Rail.first }; member < Rail.second; ++member ) {
|
||||
if( false == dropped[ order[ member ] ] ) {
|
||||
result = glm::max( result, reach( Fastenings[ order[ member ] ], Fastenings[ order[ member ] ].scale ) );
|
||||
}
|
||||
}
|
||||
return result; };
|
||||
auto kept { std::size_t { 0 } };
|
||||
for( std::size_t index = 1; index < rails.size(); ++index ) {
|
||||
auto const &first { rails[ kept ] };
|
||||
auto const &second { rails[ index ] };
|
||||
auto const apart { at( Fastenings[ order[ second.first ] ] ) - at( Fastenings[ order[ first.first ] ] ) };
|
||||
auto const overlaps = [&]() {
|
||||
return extent( first ).y + extent( second ).x - apart > fastening_gap; };
|
||||
if( false == overlaps() ) {
|
||||
kept = index;
|
||||
continue;
|
||||
}
|
||||
auto const firstreach { extent( first ) };
|
||||
auto const 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;
|
||||
} };
|
||||
// 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 ) {
|
||||
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 ) {
|
||||
dropped[ order[ member ] ] = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false; };
|
||||
if( false == chair( second ) && apart <= firstreach.y ) {
|
||||
drop( second );
|
||||
continue;
|
||||
}
|
||||
if( false == chair( first ) && apart <= secondreach.x ) {
|
||||
drop( first );
|
||||
}
|
||||
else {
|
||||
brace( first, 1 );
|
||||
if( overlaps() ) {
|
||||
brace( second, 0 );
|
||||
}
|
||||
}
|
||||
kept = index;
|
||||
}
|
||||
std::vector<fastening> fitted;
|
||||
for( std::size_t index = 0; index < Fastenings.size(); ++index ) {
|
||||
if( false == dropped[ index ] ) {
|
||||
fitted.push_back( Fastenings[ index ] );
|
||||
}
|
||||
}
|
||||
Fastenings = std::move( fitted );
|
||||
}
|
||||
|
||||
std::vector<fastening_row> check_fastenings() {
|
||||
|
||||
std::vector<fastening_row> result;
|
||||
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;
|
||||
}
|
||||
fastening_row row;
|
||||
row.track = track->name();
|
||||
std::optional<fastened_rails> rails;
|
||||
for( auto const &beam : place( *track ) ) {
|
||||
if( beam.plain ) {
|
||||
continue;
|
||||
}
|
||||
if( false == rails.has_value() ) {
|
||||
rails.emplace( *track );
|
||||
}
|
||||
++row.beams;
|
||||
auto const start { plan( beam.from ) };
|
||||
auto const axis { glm::normalize( plan( beam.to - beam.from ) ) };
|
||||
struct footprint {
|
||||
double from;
|
||||
double to;
|
||||
int part;
|
||||
glm::dvec3 point;
|
||||
};
|
||||
std::vector<footprint> footprints;
|
||||
for( auto const &fastening : rails->on( beam, *model ) ) {
|
||||
++row.parts[ fastening.part ];
|
||||
auto const at { glm::dot( plan( fastening.point ) - start, axis ) };
|
||||
auto const turn { glm::dot( fastening.across, axis ) };
|
||||
auto const a { at + turn * fastening.placed( model->spans[ fastening.part ].x, fastening.scale ) };
|
||||
auto const b { at + turn * fastening.placed( model->spans[ fastening.part ].y, fastening.scale ) };
|
||||
footprints.push_back( { std::min( a, b ), std::max( a, b ), fastening.part, fastening.point } );
|
||||
}
|
||||
std::sort( footprints.begin(), footprints.end(), []( footprint const &Left, footprint const &Right ) { return Left.from < Right.from; } );
|
||||
for( std::size_t index = 1; index < footprints.size(); ++index ) {
|
||||
auto const over { footprints[ index - 1 ].to - footprints[ index ].from };
|
||||
if( over <= fastening_overlap ) {
|
||||
continue;
|
||||
}
|
||||
++row.overlaps;
|
||||
row.cases.push_back( { { footprints[ index - 1 ].part, footprints[ index ].part }, glm::length( plan( footprints[ index ].point - footprints[ index - 1 ].point ) ), over, footprints[ index ].point } );
|
||||
if( over > row.worst ) {
|
||||
row.worst = over;
|
||||
row.worstat = footprints[ index ].point;
|
||||
row.worstparts = std::string { part_names[ footprints[ index - 1 ].part ] } + "/" + part_names[ footprints[ index ].part ];
|
||||
}
|
||||
}
|
||||
}
|
||||
if( row.beams > 0 ) {
|
||||
result.push_back( std::move( row ) );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
@@ -25,6 +27,12 @@ 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 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
|
||||
// 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 ); }
|
||||
};
|
||||
|
||||
// the rails of the tracks around a track, which lie on its timbers
|
||||
@@ -54,6 +62,7 @@ private:
|
||||
std::vector<double> distances;
|
||||
glm::dvec2 low { 0.0 };
|
||||
glm::dvec2 high { 0.0 };
|
||||
double foot { 0.0 }; // from the middle of the rail to the edge of its foot toward the middle of the path
|
||||
// the stretches along the path the rail is a blade, a stock rail with a chair, checked along
|
||||
glm::dvec2 blade { -1.0 };
|
||||
glm::dvec2 stock { -1.0 };
|
||||
@@ -65,9 +74,34 @@ private:
|
||||
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 };
|
||||
};
|
||||
|
||||
// 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
|
||||
static void fit( std::vector<fastening> &Fastenings, beam const &Beam, beam_model const &Model );
|
||||
|
||||
std::vector<rail> m_rails;
|
||||
};
|
||||
|
||||
// the fastenings of a track with them in the model of its timbers, as laid on the timbers laid with it
|
||||
struct fastening_row {
|
||||
std::string track;
|
||||
std::size_t beams { 0 };
|
||||
std::array<std::size_t, part_count> parts {};
|
||||
std::size_t overlaps { 0 }; // fastenings lying over one another on a timber, along it
|
||||
double worst { 0.0 };
|
||||
glm::dvec3 worstat { 0.0 };
|
||||
std::string worstparts;
|
||||
struct overlap {
|
||||
int parts[ 2 ];
|
||||
double apart; // of the rails the two lie under
|
||||
double over;
|
||||
glm::dvec3 at;
|
||||
};
|
||||
std::vector<overlap> cases;
|
||||
};
|
||||
// those of all the tracks laying timbers with fastenings
|
||||
std::vector<fastening_row> check_fastenings();
|
||||
|
||||
} // namespace track_sleepers
|
||||
|
||||
@@ -109,22 +109,29 @@ beam_model const *find_model( std::string const &Name, std::string const &Skinna
|
||||
model.skin = GfxRenderer->Fetch_Material( Skin );
|
||||
}
|
||||
// a timber put together from its parts, with a part between the ends stretched and repeated to the length
|
||||
std::array<char const *, part_count> const names { "begin", "main", "end", "plate", "chair", "check" };
|
||||
model.stretchable = true;
|
||||
for( int part = 0; part < part_count; ++part ) {
|
||||
model.parts[ part ] = model.model->GetFromName( names[ part ] );
|
||||
model.parts[ part ] = model.model->GetFromName( part_names[ part ] );
|
||||
model.stretchable = model.stretchable && ( part >= timber_parts || model.parts[ part ] != nullptr );
|
||||
}
|
||||
auto top { std::numeric_limits<float>::lowest() };
|
||||
model.span = { std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest() };
|
||||
if( model.stretchable ) {
|
||||
for( int part = 0; part < timber_parts; ++part ) {
|
||||
for( int part = 0; part < part_count; ++part ) {
|
||||
if( model.parts[ part ] == nullptr ) {
|
||||
continue;
|
||||
}
|
||||
glm::vec2 span { std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest() };
|
||||
auto const timber { part < timber_parts };
|
||||
vertices_of( model.parts[ part ], [&]( glm::vec3 const &Position ) {
|
||||
span = { std::min( span.x, Position.x ), std::max( span.y, Position.x ) };
|
||||
top = std::max( top, Position.y ); } );
|
||||
if( timber ) {
|
||||
top = std::max( top, Position.y );
|
||||
} } );
|
||||
model.spans[ part ] = span;
|
||||
model.span = { std::min( model.span.x, span.x ), std::max( model.span.y, span.y ) };
|
||||
if( timber ) {
|
||||
model.span = { std::min( model.span.x, span.x ), std::max( model.span.y, span.y ) };
|
||||
}
|
||||
}
|
||||
model.stretchable = model.spans[ part_main ].y > model.spans[ part_main ].x;
|
||||
}
|
||||
@@ -152,8 +159,12 @@ beam_model const *find_model( std::string const &Name, std::string const &Skinna
|
||||
if( Seat < 0.f && model.fastened() ) {
|
||||
model.seat = std::max( top, surface_at( model.parts[ part_plate ], 0.f, 0.f ).value_or( top ) );
|
||||
}
|
||||
if( model.fastened() && model.parts[ part_chair ] != nullptr ) {
|
||||
model.slide = std::max( 0.f, surface_at( model.parts[ part_chair ], static_cast<float>( -slide_probe ), 0.f ).value_or( model.seat ) - model.seat );
|
||||
if( model.fastened() ) {
|
||||
for( auto const part : { part_chair, part_slide } ) {
|
||||
if( model.parts[ part ] != nullptr ) {
|
||||
model.slide = std::max( model.slide, surface_at( model.parts[ part ], static_cast<float>( -slide_probe ), 0.f ).value_or( model.seat ) - model.seat );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( model.length() < 0.5f ) {
|
||||
ErrorLog( "Bad sleepers3d: model \"" + Name + "\" has no beam along x" );
|
||||
@@ -167,7 +178,7 @@ beam_model const *find_model( std::string const &Name, std::string const &Skinna
|
||||
", main " + to_string( model.spans[ part_main ].x, 3 ) + ".." + to_string( model.spans[ part_main ].y, 3 ) :
|
||||
std::string {} )
|
||||
+ ( model.fastened() ?
|
||||
std::string { ", fastenings plate" } + ( model.parts[ part_chair ] != nullptr ? " chair" : "" ) + ( model.parts[ part_check ] != nullptr ? " check" : "" ) :
|
||||
std::string { ", fastenings plate" } + ( model.parts[ part_chair ] != nullptr ? " chair" : "" ) + ( model.parts[ part_check ] != nullptr ? " check" : "" ) + ( model.parts[ part_slide ] != nullptr ? " slide" : "" ) :
|
||||
std::string {} )
|
||||
+ ", seat " + to_string( model.seat, 3 )
|
||||
+ ( model.slide > 0.f ? ", slide " + to_string( model.slide, 3 ) : std::string {} ) );
|
||||
|
||||
@@ -55,12 +55,22 @@ double constexpr fastening_step { 0.25 };
|
||||
double constexpr fastening_merge { 0.03 };
|
||||
// two rails closer than this lie in one fastening of both, as a rail of the frog with the one beside it; closer than the second
|
||||
// one, as the legs of the nose where they're joined, in one plate between them
|
||||
double constexpr fastening_pair { 0.24 };
|
||||
double constexpr fastening_pair { 0.2 };
|
||||
double constexpr fastening_nose { 0.05 };
|
||||
// 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
|
||||
double constexpr fastening_gap { 0.01 };
|
||||
double constexpr plate_least { 0.6 };
|
||||
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 };
|
||||
// how far before the tip of the blade the slide chairs of its stock rail begin
|
||||
double constexpr chair_lead { 0.3 };
|
||||
// how far from the stock rail toward its blade the slide plate of a chair is measured
|
||||
double constexpr slide_probe { 0.12 };
|
||||
// how far two fastenings on a timber may reach over one another before the check counts them
|
||||
double constexpr fastening_overlap { 0.01 };
|
||||
// how far from the camera the fastenings are drawn at most
|
||||
double constexpr fastening_range { 120.0 };
|
||||
|
||||
|
||||
@@ -141,8 +141,8 @@ void lay( TTrack &Track ) {
|
||||
// the layout of the cluster isn't needed by this track any more, the instances below are what's drawn
|
||||
forget_layout( Track );
|
||||
|
||||
// the rails around, where they lie on the timbers, for the fastenings of the model
|
||||
std::optional<fastened_rails> const rails { parts.fastened() ? std::make_optional<fastened_rails>( Track ) : std::nullopt };
|
||||
// the rails around, where they lie on the timbers, for the fastenings of the model; found at the first timber, none for plain sleepers
|
||||
std::optional<fastened_rails> rails;
|
||||
|
||||
auto const partlength = [&]( int const Part ) {
|
||||
return static_cast<double>( parts.spans[ Part ].y - parts.spans[ Part ].x ); };
|
||||
@@ -242,7 +242,10 @@ void lay( TTrack &Track ) {
|
||||
put( part_end, scale, parts.spans[ part_end ].x, -half );
|
||||
put( part_begin, scale, parts.spans[ part_begin ].y, half );
|
||||
}
|
||||
if( rails && false == ( beam.plain && laid.plains != nullptr ) ) {
|
||||
if( parts.fastened() && false == ( beam.plain && laid.plains != nullptr ) ) {
|
||||
if( false == rails.has_value() ) {
|
||||
rails.emplace( Track );
|
||||
}
|
||||
// each square to its rail on the top of the timber, the plate under the foot of the rail
|
||||
for( auto const &fastening : rails->on( beam, parts ) ) {
|
||||
auto across { glm::dvec3 { fastening.across.x, 0.0, fastening.across.y } };
|
||||
@@ -252,7 +255,9 @@ void lay( TTrack &Track ) {
|
||||
place[ 1 ] = glm::vec4 { glm::vec3 { y }, 0.f };
|
||||
place[ 2 ] = glm::vec4 { glm::vec3 { glm::cross( across, y ) }, 0.f };
|
||||
place[ 3 ] = glm::vec4 { glm::vec3 { fastening.point - Track.m_origin }, 1.f };
|
||||
laid.instances[ fastening.part ].emplace_back( glm::translate( place, glm::vec3 { 0.f, -parts.seat, 0.f } ) );
|
||||
auto const pivot { glm::vec3 { fastening.pivot, 0.f, 0.f } };
|
||||
laid.instances[ fastening.part ].emplace_back(
|
||||
glm::translate( glm::scale( glm::translate( place, glm::vec3 { fastening.shift, -parts.seat, 0.f } + pivot ), glm::vec3 { fastening.scale, 1.f, 1.f } ), -pivot ) );
|
||||
}
|
||||
}
|
||||
runbeams.emplace_back( middle, half );
|
||||
|
||||
@@ -36,11 +36,15 @@ enum part : int {
|
||||
part_main, // the piece between the ends
|
||||
part_end, // the end on the -x side
|
||||
part_plate, // the fastening of a rail, its origin under the middle of the foot of the rail
|
||||
part_chair, // the slide chair of a stock rail with the blade beside it, its origin under the stock rail, the slide plate on its -x side
|
||||
part_chair, // the brace of the slide chair of a stock rail with the blade beside it, its origin under the stock rail, on its +x side
|
||||
part_check, // the fastening of a rail with another one close beside it, as a check rail or a rail of the frog, its origin under the
|
||||
// rail, the other one on its +x side
|
||||
part_slide, // the slide plate of the chair, apart from it on its -x side, from the edge of the foot of the stock rail on, shortened
|
||||
// toward it where another rail lies close
|
||||
part_count
|
||||
};
|
||||
// the names of the submodels of the parts
|
||||
inline constexpr std::array<char const *, part_count> part_names { "begin", "main", "end", "plate", "chair", "check", "slide" };
|
||||
// the parts a timber is put together from, the fastenings after them
|
||||
int constexpr timber_parts { part_end + 1 };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user