mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-02 15:09:13 +01:00
refactor(track): what goes with unevenness in one place, readable
The rules stay in TrackUneven.h with no dependence on the engine, as they can be tested. Their use on the tracks moves out of Track.cpp to TrackUneven.cpp, split into steps: the group of switches, the values of its paths, the meeting of two ends. One set of helpers for the paths of a track takes the place of the same conditions written out in several places, and the value at the ends of a switch is worked out in one place. The clatter of an axle leaves the long Update of a vehicle. The fields in the editor say themselves when they show. No change in behaviour.
This commit is contained in:
@@ -127,6 +127,9 @@ public:
|
||||
set_uneven( bool const Uneven, int const Path = 0 ) {
|
||||
m_uneven = Uneven;
|
||||
m_path = Path; }
|
||||
glm::dvec3
|
||||
end_point( int const End ) const {
|
||||
return End == 0 ? Point1 : Point2; }
|
||||
// a point of the path and the unit vector across it in the plan
|
||||
void
|
||||
plan_frame( double const Distance, glm::dvec3 &Point, glm::dvec3 &Across ) const;
|
||||
|
||||
272
world/Track.cpp
272
world/Track.cpp
@@ -49,9 +49,6 @@ TTrack::profiles_array TTrack::m_profiles;
|
||||
TTrack::profiles_map TTrack::m_profilesmap;
|
||||
int TTrack::s_layoutstamp { 0 };
|
||||
|
||||
// how near an end of a path counts as being at a point, m
|
||||
static double const switch_end_reach { 0.3 };
|
||||
|
||||
TSwitchExtension::TSwitchExtension(TTrack *owner, int const what)
|
||||
{ // na początku wszystko puste
|
||||
pNexts[0] = nullptr; // wskaźniki do kolejnych odcinków ruchu
|
||||
@@ -706,214 +703,6 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
// to a stale origin and the renderer would draw sleepers in the wrong place).
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::uneven_declared( int const Path ) const {
|
||||
|
||||
return track_uneven::declared( m_uneven, eType == tt_Normal, Path );
|
||||
}
|
||||
|
||||
bool TTrack::is_uneven() const {
|
||||
|
||||
auto const any = []( track_uneven::amounts const &Amount ) {
|
||||
return Amount.up > 0.0 || Amount.side > 0.0 || Amount.cross > 0.0; };
|
||||
return any( uneven_declared( 0 ) ) || any( uneven_declared( 1 ) ) || joint_depth() > 0.0;
|
||||
}
|
||||
|
||||
double TTrack::uneven_bed_share( double const Depth ) const {
|
||||
|
||||
return track_uneven::bed_share( Depth, std::abs( track_rail_profile( m_profile1.second ).front().position.y ), fTexHeight1 );
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::uneven_at( double const Distance, int const Path ) const {
|
||||
|
||||
auto const along = [ Distance ]( track_uneven::amounts const &Own, track_uneven::amounts const &Start, track_uneven::amounts const &End, double const Length ) -> track_uneven::amounts {
|
||||
return {
|
||||
track_uneven::along( Own.up, Start.up, End.up, Distance, Length ),
|
||||
track_uneven::along( Own.side, Start.side, End.side, Distance, Length ),
|
||||
track_uneven::along( Own.cross, Start.cross, End.cross, Distance, Length ) }; };
|
||||
if( eType == tt_Switch ) {
|
||||
auto const &stray { switch_uneven() };
|
||||
return along( stray.paths[ Path ], stray.ends[ Path ][ 0 ], stray.ends[ Path ][ 1 ], SwitchExtension->Segments[ Path ]->GetLength() );
|
||||
}
|
||||
auto const own { uneven_declared() };
|
||||
auto const atend = [&]( TTrack const *Other, glm::dvec3 const &Point ) -> track_uneven::amounts {
|
||||
if( Other == nullptr ) {
|
||||
return {};
|
||||
}
|
||||
if( Other->eType == tt_Switch ) {
|
||||
return Other->switch_uneven_at( Point );
|
||||
}
|
||||
return track_uneven::least( own, Other->uneven_declared() ); };
|
||||
return along( own, atend( trPrev, Segment->FastGetPoint_0() ), atend( trNext, Segment->FastGetPoint_1() ), Length() );
|
||||
}
|
||||
|
||||
TTrack::switch_stray const &TTrack::switch_uneven() const {
|
||||
|
||||
if( m_switchstray.stamp == s_layoutstamp ) {
|
||||
return m_switchstray;
|
||||
}
|
||||
std::vector<TTrack const *> group { this };
|
||||
std::array<track_uneven::amounts, 2> least { uneven_declared( 0 ), uneven_declared( 1 ) };
|
||||
for( std::size_t index = 0; index < group.size(); ++index ) {
|
||||
auto const &extension { *group[ index ]->SwitchExtension };
|
||||
for( auto const *other : { extension.pPrevs[ 0 ], extension.pPrevs[ 1 ], extension.pNexts[ 0 ], extension.pNexts[ 1 ] } ) {
|
||||
if( other == nullptr ) {
|
||||
continue;
|
||||
}
|
||||
if( other->eType == tt_Switch && other->SwitchExtension ) {
|
||||
least[ 0 ] = track_uneven::least( least[ 0 ], other->uneven_declared( 0 ) );
|
||||
least[ 1 ] = track_uneven::least( least[ 1 ], other->uneven_declared( 1 ) );
|
||||
if( std::find( group.begin(), group.end(), other ) == group.end() ) {
|
||||
group.push_back( other );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// a track with none of a component keeps to its design in it, any other comes to the value of the switch
|
||||
auto const theirs { other->uneven_declared() };
|
||||
for( auto &path : least ) {
|
||||
if( theirs.up <= 0.0 ) { path.up = 0.0; }
|
||||
if( theirs.side <= 0.0 ) { path.side = 0.0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for( auto const *track : group ) {
|
||||
track->m_switchstray.paths = least;
|
||||
track->m_switchstray.stamp = s_layoutstamp;
|
||||
}
|
||||
// the lowest of the paths of a switch ending at a point
|
||||
auto const paths_at = []( TTrack const &Track, glm::dvec3 const &Point ) {
|
||||
auto result { track_uneven::least( Track.m_switchstray.paths[ 0 ], Track.m_switchstray.paths[ 1 ] ) };
|
||||
auto found { false };
|
||||
track_uneven::amounts ending { track_uneven::most, track_uneven::most, track_uneven::most };
|
||||
for( int path = 0; path < 2; ++path ) {
|
||||
auto const &segment { *Track.SwitchExtension->Segments[ path ] };
|
||||
if( glm::length( segment.FastGetPoint_0() - Point ) < switch_end_reach || glm::length( segment.FastGetPoint_1() - Point ) < switch_end_reach ) {
|
||||
ending = track_uneven::least( ending, Track.m_switchstray.paths[ path ] );
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return found ? ending : result; };
|
||||
for( auto const *track : group ) {
|
||||
auto const &extension { *track->SwitchExtension };
|
||||
for( int path = 0; path < 2; ++path ) {
|
||||
auto const &segment { *extension.Segments[ path ] };
|
||||
for( int end = 0; end < 2; ++end ) {
|
||||
auto const point { end == 0 ? segment.FastGetPoint_0() : segment.FastGetPoint_1() };
|
||||
auto value { paths_at( *track, point ) };
|
||||
auto const *other { end == 0 ? extension.pPrevs[ path ] : extension.pNexts[ path ] };
|
||||
if( other != nullptr && other->eType == tt_Switch && other->SwitchExtension ) {
|
||||
value = track_uneven::least( value, paths_at( *other, point ) );
|
||||
}
|
||||
track->m_switchstray.ends[ path ][ end ] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_switchstray;
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::switch_uneven_at( glm::dvec3 const &Point ) const {
|
||||
|
||||
auto const &stray { switch_uneven() };
|
||||
auto found { false };
|
||||
track_uneven::amounts result { track_uneven::most, track_uneven::most, track_uneven::most };
|
||||
for( int path = 0; path < 2; ++path ) {
|
||||
auto const &segment { *SwitchExtension->Segments[ path ] };
|
||||
for( int end = 0; end < 2; ++end ) {
|
||||
if( glm::length( ( end == 0 ? segment.FastGetPoint_0() : segment.FastGetPoint_1() ) - Point ) < switch_end_reach ) {
|
||||
result = track_uneven::least( result, stray.ends[ path ][ end ] );
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found ? result : track_uneven::least( stray.paths[ 0 ], stray.paths[ 1 ] );
|
||||
}
|
||||
|
||||
double TTrack::joint_depth() const {
|
||||
|
||||
if( eType != tt_Normal ) {
|
||||
return 0.0;
|
||||
}
|
||||
return ( m_uneven.dip >= 0.f ? m_uneven.dip : track_uneven::dip_share * uneven_declared().up );
|
||||
}
|
||||
|
||||
double TTrack::joint_dip( double const Distance ) const {
|
||||
|
||||
auto const phase { rail_phase( Distance ) };
|
||||
if( phase < 0.0 ) {
|
||||
return 0.0;
|
||||
}
|
||||
auto const fromjoint { std::min( phase, fSoundDistance - phase ) };
|
||||
if( fromjoint >= track_uneven::dip_reach ) {
|
||||
return 0.0;
|
||||
}
|
||||
auto const depth { joint_depth() };
|
||||
auto const atend = [&]( TTrack const *Other ) {
|
||||
return ( Other == nullptr || Other->fSoundDistance != fSoundDistance ? 0.0 : std::min( depth, Other->joint_depth() ) ); };
|
||||
return 0.001 * track_uneven::dip( fromjoint ) * track_uneven::along( depth, atend( trPrev ), atend( trNext ), Distance, Length() );
|
||||
}
|
||||
|
||||
double TTrack::rail_phase( double const Distance ) const {
|
||||
|
||||
if( joint_depth() <= 0.0 || fSoundDistance <= 2.0 * track_uneven::dip_reach ) {
|
||||
return -1.0;
|
||||
}
|
||||
if( m_railrun.stamp != s_layoutstamp ) {
|
||||
settle_rail_run();
|
||||
}
|
||||
return track_uneven::rail_phase( m_railrun.origin + m_railrun.way * Distance, fSoundDistance );
|
||||
}
|
||||
|
||||
void TTrack::settle_rail_run() const {
|
||||
|
||||
// the next track of the run past an end of a track, and which of its ends is joined there
|
||||
auto const onward = []( TTrack const &Track, int const End, int &Joined ) -> TTrack const * {
|
||||
auto const *other { End == 0 ? Track.trPrev : Track.trNext };
|
||||
if( other == nullptr || other == &Track || other->joint_depth() <= 0.0 || other->fSoundDistance != Track.fSoundDistance ) {
|
||||
return nullptr;
|
||||
}
|
||||
Joined = ( End == 0 ? Track.iPrevDirection : Track.iNextDirection ) & 1;
|
||||
return other; };
|
||||
// walks from this track to an end of the run
|
||||
auto const walk = [&]( int &End ) {
|
||||
auto const *track { this };
|
||||
int joined { 0 };
|
||||
for( int step = 0; step < 1000000; ++step ) {
|
||||
auto const *next { onward( *track, End, joined ) };
|
||||
if( next == nullptr || next == this ) {
|
||||
break;
|
||||
}
|
||||
track = next;
|
||||
End = 1 - joined;
|
||||
}
|
||||
return track; };
|
||||
auto const place = []( TTrack const &Track, int const End ) {
|
||||
auto const point { End == 0 ? Track.Segment->FastGetPoint_0() : Track.Segment->FastGetPoint_1() };
|
||||
return std::make_pair( point.x, point.z ); };
|
||||
|
||||
// start at the end with the lesser coordinates, to get the same joints from any track of the run
|
||||
int end { 0 };
|
||||
int otherend { 1 };
|
||||
auto const *track { walk( end ) };
|
||||
auto const *other { walk( otherend ) };
|
||||
if( place( *other, otherend ) < place( *track, end ) ) {
|
||||
track = other;
|
||||
end = otherend;
|
||||
}
|
||||
auto along { 0.0 };
|
||||
while( track != nullptr && track->m_railrun.stamp != s_layoutstamp ) {
|
||||
auto const length { track->Length() };
|
||||
track->m_railrun = { end == 0 ? along : along + length, end == 0 ? 1.0 : -1.0, s_layoutstamp };
|
||||
along += length;
|
||||
int joined { 0 };
|
||||
track = onward( *track, 1 - end, joined );
|
||||
end = joined;
|
||||
}
|
||||
// tracks joined one way only can leave this one out of the run: it stands on its own then, not to walk all of it again on every call
|
||||
if( m_railrun.stamp != s_layoutstamp ) {
|
||||
m_railrun = { 0.0, 1.0, s_layoutstamp };
|
||||
}
|
||||
}
|
||||
|
||||
void TTrack::update_location() {
|
||||
|
||||
location( (
|
||||
@@ -1600,67 +1389,6 @@ void TTrack::rebuild_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
create_geometry( Bank );
|
||||
}
|
||||
|
||||
void TTrack::share_joints() {
|
||||
|
||||
int const paths { eType == tt_Switch ? 2 : 1 };
|
||||
auto const segment_of = []( TTrack const &Track, int const Path ) -> TSegment * {
|
||||
return ( Track.eType == tt_Switch ? Track.SwitchExtension->Segments[ Path ].get() : Track.Segment.get() ); };
|
||||
for( int path = 0; path < paths; ++path ) {
|
||||
auto *segment { segment_of( *this, path ) };
|
||||
if( segment == nullptr ) {
|
||||
continue;
|
||||
}
|
||||
for( int end = 0; end < 2; ++end ) {
|
||||
std::optional<std::pair<glm::dvec3, glm::dvec3>> joint;
|
||||
auto roll { segment->design_roll( end ) };
|
||||
auto const *neighbour { eType == tt_Switch ?
|
||||
( end ? SwitchExtension->pNexts[ path ] : SwitchExtension->pPrevs[ path ] ) :
|
||||
( end ? trNext : trPrev ) };
|
||||
if( neighbour != nullptr && neighbour != this && ( neighbour->iCategoryFlag & 15 ) == ( iCategoryFlag & 15 ) ) {
|
||||
glm::dvec3 point;
|
||||
glm::dvec3 across;
|
||||
segment->plan_frame( end ? segment->GetLength() : 0.0, point, across );
|
||||
auto closest { 0.2 };
|
||||
for( int theirpath = 0; theirpath < ( neighbour->eType == tt_Switch ? 2 : 1 ); ++theirpath ) {
|
||||
auto const *theirs { segment_of( *neighbour, theirpath ) };
|
||||
if( theirs == nullptr ) {
|
||||
continue;
|
||||
}
|
||||
for( int theirend = 0; theirend < 2; ++theirend ) {
|
||||
glm::dvec3 theirpoint;
|
||||
glm::dvec3 theiracross;
|
||||
theirs->plan_frame( theirend ? theirs->GetLength() : 0.0, theirpoint, theiracross );
|
||||
if( theirend == end ) {
|
||||
theiracross = -theiracross;
|
||||
}
|
||||
auto const gap { glm::length( theirpoint - point ) };
|
||||
if( gap >= closest || glm::dot( across, theiracross ) < 0.9 ) {
|
||||
continue;
|
||||
}
|
||||
closest = gap;
|
||||
joint.emplace( 0.5 * ( point + theirpoint ), glm::normalize( across + theiracross ) );
|
||||
if( iCategoryFlag & 1 ) {
|
||||
roll = track_uneven::joint_roll(
|
||||
segment->design_roll( end ),
|
||||
theirend == end ? -theirs->design_roll( theirend ) : theirs->design_roll( theirend ),
|
||||
eType == tt_Switch,
|
||||
neighbour->eType == tt_Switch );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
segment->set_joint( end, joint, roll );
|
||||
}
|
||||
}
|
||||
if( eType != tt_Switch && Segment && ( iCategoryFlag & 1 ) ) {
|
||||
float roll1, roll2;
|
||||
Segment->GetRolls( roll1, roll2 );
|
||||
if( roll1 != 0.f || roll2 != 0.f ) {
|
||||
iTrapezoid |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
gfx::userdata_array empty_userdata;
|
||||
share_joints();
|
||||
|
||||
@@ -341,6 +341,10 @@ public:
|
||||
void VelocitySet(float v);
|
||||
double VelocityGet();
|
||||
float Friction() const;
|
||||
// a switch has two paths, any other track the one
|
||||
int path_count() const;
|
||||
TSegment *path_segment( int const Path ) const;
|
||||
TTrack *path_neighbour( int const Path, int const End ) const;
|
||||
// the unevenness the track declares, on a path of a switch
|
||||
track_uneven::amounts uneven_declared( int const Path = 0 ) const;
|
||||
bool is_uneven() const;
|
||||
@@ -402,6 +406,11 @@ private:
|
||||
};
|
||||
mutable switch_stray m_switchstray;
|
||||
switch_stray const &switch_uneven() const;
|
||||
void settle_switch_stray() const;
|
||||
// the lowest value of the paths ending at the point
|
||||
track_uneven::amounts switch_paths_at( glm::dvec3 const &Point ) const;
|
||||
// what a plain track comes to at its end, where it meets the other one
|
||||
track_uneven::amounts uneven_met( TTrack const *Other, glm::dvec3 const &Point ) const;
|
||||
// the value of a switch where a track joined to it meets it
|
||||
track_uneven::amounts switch_uneven_at( glm::dvec3 const &Point ) const;
|
||||
static int s_layoutstamp; // bumped when tracks are joined or changed, invalidates the two above
|
||||
|
||||
336
world/TrackUneven.cpp
Normal file
336
world/TrackUneven.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "world/Track.h"
|
||||
|
||||
// The part of TTrack that goes with the unevenness of a track and with what it meets at its ends: joined tracks and switches.
|
||||
// The rules themselves are in TrackUneven.h, here they get applied to the tracks.
|
||||
|
||||
namespace {
|
||||
|
||||
double const switch_end_reach { 0.3 }; // how near an end of a path counts as being at a point, m
|
||||
double const joint_reach { 0.2 }; // ends of joined paths further apart than this stay as they are, m
|
||||
double const joint_alike { 0.9 }; // ends of joined paths less alike in direction than this stay as they are, cosine
|
||||
|
||||
bool is_switch( TTrack const *Track ) {
|
||||
|
||||
return Track != nullptr && Track->eType == tt_Switch && Track->SwitchExtension;
|
||||
}
|
||||
|
||||
std::array<TTrack const *, 4> joined_to_switch( TTrack const &Switch ) {
|
||||
|
||||
auto const &extension { *Switch.SwitchExtension };
|
||||
return { extension.pPrevs[ 0 ], extension.pPrevs[ 1 ], extension.pNexts[ 0 ], extension.pNexts[ 1 ] };
|
||||
}
|
||||
|
||||
// the switch and the switches joined directly to it, and to those
|
||||
std::vector<TTrack const *> switch_group( TTrack const &Switch ) {
|
||||
|
||||
std::vector<TTrack const *> group { &Switch };
|
||||
for( std::size_t index = 0; index < group.size(); ++index ) {
|
||||
for( auto const *other : joined_to_switch( *group[ index ] ) ) {
|
||||
if( is_switch( other ) && std::find( group.begin(), group.end(), other ) == group.end() ) {
|
||||
group.push_back( other );
|
||||
}
|
||||
}
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
// the value along each path: the lowest of that path in the group, 0 in a component that a plain track joined has none of
|
||||
std::array<track_uneven::amounts, 2> group_path_values( std::vector<TTrack const *> const &Group ) {
|
||||
|
||||
std::array<track_uneven::amounts, 2> values { Group.front()->uneven_declared( 0 ), Group.front()->uneven_declared( 1 ) };
|
||||
for( auto const *track : Group ) {
|
||||
values[ 0 ] = track_uneven::least( values[ 0 ], track->uneven_declared( 0 ) );
|
||||
values[ 1 ] = track_uneven::least( values[ 1 ], track->uneven_declared( 1 ) );
|
||||
for( auto const *other : joined_to_switch( *track ) ) {
|
||||
if( other == nullptr || is_switch( other ) ) {
|
||||
continue;
|
||||
}
|
||||
// any other comes to the value of the switch
|
||||
auto const theirs { other->uneven_declared() };
|
||||
for( auto &path : values ) {
|
||||
if( theirs.up <= 0.0 ) { path.up = 0.0; }
|
||||
if( theirs.side <= 0.0 ) { path.side = 0.0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
// the lowest Value( path, end ) of the ends of the paths at the point; Otherwise when none of them is there
|
||||
template <typename Value_>
|
||||
track_uneven::amounts least_at_ends( TTrack const &Switch, glm::dvec3 const &Point, track_uneven::amounts const &Otherwise, Value_ const &Value ) {
|
||||
|
||||
auto found { false };
|
||||
track_uneven::amounts result { track_uneven::most, track_uneven::most, track_uneven::most };
|
||||
for( int path = 0; path < 2; ++path ) {
|
||||
for( int end = 0; end < 2; ++end ) {
|
||||
if( glm::length( Switch.path_segment( path )->end_point( end ) - Point ) < switch_end_reach ) {
|
||||
result = track_uneven::least( result, Value( path, end ) );
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found ? result : Otherwise;
|
||||
}
|
||||
|
||||
// how the end of a path meets the track joined to it
|
||||
struct path_joint {
|
||||
std::optional<std::pair<glm::dvec3, glm::dvec3>> place; // none: too far apart, or going different ways
|
||||
float roll;
|
||||
};
|
||||
|
||||
path_joint meet( TTrack const &Track, TSegment const &Segment, int const End, TTrack const &Neighbour ) {
|
||||
|
||||
path_joint result { {}, Segment.design_roll( End ) };
|
||||
glm::dvec3 point;
|
||||
glm::dvec3 across;
|
||||
Segment.plan_frame( End ? Segment.GetLength() : 0.0, point, across );
|
||||
auto closest { joint_reach };
|
||||
for( int path = 0; path < Neighbour.path_count(); ++path ) {
|
||||
auto const *theirs { Neighbour.path_segment( path ) };
|
||||
if( theirs == nullptr ) {
|
||||
continue;
|
||||
}
|
||||
for( int end = 0; end < 2; ++end ) {
|
||||
// laid towards each other, they look across and lean to opposite sides
|
||||
auto const opposite { end == End };
|
||||
glm::dvec3 theirpoint;
|
||||
glm::dvec3 theiracross;
|
||||
theirs->plan_frame( end ? theirs->GetLength() : 0.0, theirpoint, theiracross );
|
||||
if( opposite ) {
|
||||
theiracross = -theiracross;
|
||||
}
|
||||
auto const gap { glm::length( theirpoint - point ) };
|
||||
if( gap >= closest || glm::dot( across, theiracross ) < joint_alike ) {
|
||||
continue;
|
||||
}
|
||||
closest = gap;
|
||||
result.place.emplace( 0.5 * ( point + theirpoint ), glm::normalize( across + theiracross ) );
|
||||
if( Track.iCategoryFlag & 1 ) {
|
||||
result.roll = track_uneven::joint_roll(
|
||||
Segment.design_roll( End ),
|
||||
opposite ? -theirs->design_roll( end ) : theirs->design_roll( end ),
|
||||
Track.eType == tt_Switch,
|
||||
Neighbour.eType == tt_Switch );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int TTrack::path_count() const {
|
||||
|
||||
return eType == tt_Switch ? 2 : 1;
|
||||
}
|
||||
|
||||
TSegment *TTrack::path_segment( int const Path ) const {
|
||||
|
||||
return eType == tt_Switch ? SwitchExtension->Segments[ Path ].get() : Segment.get();
|
||||
}
|
||||
|
||||
TTrack *TTrack::path_neighbour( int const Path, int const End ) const {
|
||||
|
||||
if( eType == tt_Switch ) {
|
||||
return End == 0 ? SwitchExtension->pPrevs[ Path ] : SwitchExtension->pNexts[ Path ];
|
||||
}
|
||||
return End == 0 ? trPrev : trNext;
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::uneven_declared( int const Path ) const {
|
||||
|
||||
return track_uneven::declared( m_uneven, eType == tt_Normal, Path );
|
||||
}
|
||||
|
||||
bool TTrack::is_uneven() const {
|
||||
|
||||
auto const any = []( track_uneven::amounts const &Amount ) {
|
||||
return Amount.up > 0.0 || Amount.side > 0.0 || Amount.cross > 0.0; };
|
||||
return any( uneven_declared( 0 ) ) || any( uneven_declared( 1 ) ) || joint_depth() > 0.0;
|
||||
}
|
||||
|
||||
double TTrack::uneven_bed_share( double const Depth ) const {
|
||||
|
||||
return track_uneven::bed_share( Depth, std::abs( track_rail_profile( m_profile1.second ).front().position.y ), fTexHeight1 );
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::uneven_at( double const Distance, int const Path ) const {
|
||||
|
||||
if( eType == tt_Switch ) {
|
||||
auto const &stray { switch_uneven() };
|
||||
return track_uneven::along( stray.paths[ Path ], stray.ends[ Path ][ 0 ], stray.ends[ Path ][ 1 ], Distance, path_segment( Path )->GetLength() );
|
||||
}
|
||||
return track_uneven::along( uneven_declared(), uneven_met( trPrev, Segment->end_point( 0 ) ), uneven_met( trNext, Segment->end_point( 1 ) ), Distance, Length() );
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::uneven_met( TTrack const *Other, glm::dvec3 const &Point ) const {
|
||||
|
||||
if( Other == nullptr ) {
|
||||
return {};
|
||||
}
|
||||
return Other->eType == tt_Switch ? Other->switch_uneven_at( Point ) : track_uneven::least( uneven_declared(), Other->uneven_declared() );
|
||||
}
|
||||
|
||||
TTrack::switch_stray const &TTrack::switch_uneven() const {
|
||||
|
||||
if( m_switchstray.stamp != s_layoutstamp ) {
|
||||
settle_switch_stray();
|
||||
}
|
||||
return m_switchstray;
|
||||
}
|
||||
|
||||
void TTrack::settle_switch_stray() const {
|
||||
|
||||
auto const group { switch_group( *this ) };
|
||||
auto const values { group_path_values( group ) };
|
||||
for( auto const *track : group ) {
|
||||
track->m_switchstray.paths = values;
|
||||
track->m_switchstray.stamp = s_layoutstamp;
|
||||
}
|
||||
// at the ends of the paths, with the paths known in all of the group
|
||||
for( auto const *track : group ) {
|
||||
for( int path = 0; path < 2; ++path ) {
|
||||
for( int end = 0; end < 2; ++end ) {
|
||||
auto const point { track->path_segment( path )->end_point( end ) };
|
||||
auto value { track->switch_paths_at( point ) };
|
||||
auto const *other { track->path_neighbour( path, end ) };
|
||||
if( is_switch( other ) ) {
|
||||
value = track_uneven::least( value, other->switch_paths_at( point ) );
|
||||
}
|
||||
track->m_switchstray.ends[ path ][ end ] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::switch_paths_at( glm::dvec3 const &Point ) const {
|
||||
|
||||
auto const &paths { m_switchstray.paths };
|
||||
return least_at_ends( *this, Point, track_uneven::least( paths[ 0 ], paths[ 1 ] ), [ & ]( int const Path, int ) { return paths[ Path ]; } );
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::switch_uneven_at( glm::dvec3 const &Point ) const {
|
||||
|
||||
auto const &stray { switch_uneven() };
|
||||
return least_at_ends( *this, Point, track_uneven::least( stray.paths[ 0 ], stray.paths[ 1 ] ), [ & ]( int const Path, int const End ) { return stray.ends[ Path ][ End ]; } );
|
||||
}
|
||||
|
||||
double TTrack::joint_depth() const {
|
||||
|
||||
if( eType != tt_Normal ) {
|
||||
return 0.0;
|
||||
}
|
||||
return ( m_uneven.dip >= 0.f ? m_uneven.dip : track_uneven::dip_share * uneven_declared().up );
|
||||
}
|
||||
|
||||
double TTrack::joint_dip( double const Distance ) const {
|
||||
|
||||
auto const phase { rail_phase( Distance ) };
|
||||
if( phase < 0.0 ) {
|
||||
return 0.0;
|
||||
}
|
||||
auto const fromjoint { std::min( phase, fSoundDistance - phase ) };
|
||||
if( fromjoint >= track_uneven::dip_reach ) {
|
||||
return 0.0;
|
||||
}
|
||||
auto const depth { joint_depth() };
|
||||
auto const atend = [&]( TTrack const *Other ) {
|
||||
return ( Other == nullptr || Other->fSoundDistance != fSoundDistance ? 0.0 : std::min( depth, Other->joint_depth() ) ); };
|
||||
return 0.001 * track_uneven::dip( fromjoint ) * track_uneven::along( depth, atend( trPrev ), atend( trNext ), Distance, Length() );
|
||||
}
|
||||
|
||||
double TTrack::rail_phase( double const Distance ) const {
|
||||
|
||||
if( joint_depth() <= 0.0 || fSoundDistance <= 2.0 * track_uneven::dip_reach ) {
|
||||
return -1.0;
|
||||
}
|
||||
if( m_railrun.stamp != s_layoutstamp ) {
|
||||
settle_rail_run();
|
||||
}
|
||||
return track_uneven::rail_phase( m_railrun.origin + m_railrun.way * Distance, fSoundDistance );
|
||||
}
|
||||
|
||||
void TTrack::settle_rail_run() const {
|
||||
|
||||
// the next track of the run past an end of a track, and which of its ends is joined there
|
||||
auto const onward = []( TTrack const &Track, int const End, int &Joined ) -> TTrack const * {
|
||||
auto const *other { End == 0 ? Track.trPrev : Track.trNext };
|
||||
if( other == nullptr || other == &Track || other->joint_depth() <= 0.0 || other->fSoundDistance != Track.fSoundDistance ) {
|
||||
return nullptr;
|
||||
}
|
||||
Joined = ( End == 0 ? Track.iPrevDirection : Track.iNextDirection ) & 1;
|
||||
return other; };
|
||||
// walks from this track to an end of the run
|
||||
auto const walk = [&]( int &End ) {
|
||||
auto const *track { this };
|
||||
int joined { 0 };
|
||||
for( int step = 0; step < 1000000; ++step ) {
|
||||
auto const *next { onward( *track, End, joined ) };
|
||||
if( next == nullptr || next == this ) {
|
||||
break;
|
||||
}
|
||||
track = next;
|
||||
End = 1 - joined;
|
||||
}
|
||||
return track; };
|
||||
auto const place = []( TTrack const &Track, int const End ) {
|
||||
auto const point { Track.Segment->end_point( End ) };
|
||||
return std::make_pair( point.x, point.z ); };
|
||||
|
||||
// start at the end with the lesser coordinates, to get the same joints from any track of the run
|
||||
int end { 0 };
|
||||
int otherend { 1 };
|
||||
auto const *track { walk( end ) };
|
||||
auto const *other { walk( otherend ) };
|
||||
if( place( *other, otherend ) < place( *track, end ) ) {
|
||||
track = other;
|
||||
end = otherend;
|
||||
}
|
||||
auto along { 0.0 };
|
||||
while( track != nullptr && track->m_railrun.stamp != s_layoutstamp ) {
|
||||
auto const length { track->Length() };
|
||||
track->m_railrun = { end == 0 ? along : along + length, end == 0 ? 1.0 : -1.0, s_layoutstamp };
|
||||
along += length;
|
||||
int joined { 0 };
|
||||
track = onward( *track, 1 - end, joined );
|
||||
end = joined;
|
||||
}
|
||||
// tracks joined one way only can leave this one out of the run: it stands on its own then, not to walk all of it again on every call
|
||||
if( m_railrun.stamp != s_layoutstamp ) {
|
||||
m_railrun = { 0.0, 1.0, s_layoutstamp };
|
||||
}
|
||||
}
|
||||
|
||||
void TTrack::share_joints() {
|
||||
|
||||
for( int path = 0; path < path_count(); ++path ) {
|
||||
auto *segment { path_segment( path ) };
|
||||
if( segment == nullptr ) {
|
||||
continue;
|
||||
}
|
||||
for( int end = 0; end < 2; ++end ) {
|
||||
auto const *neighbour { path_neighbour( path, end ) };
|
||||
auto const joined { neighbour != nullptr && neighbour != this && ( neighbour->iCategoryFlag & 15 ) == ( iCategoryFlag & 15 ) };
|
||||
auto const joint { joined ? meet( *this, *segment, end, *neighbour ) : path_joint { {}, segment->design_roll( end ) } };
|
||||
segment->set_joint( end, joint.place, joint.roll );
|
||||
}
|
||||
}
|
||||
if( eType != tt_Switch && Segment && ( iCategoryFlag & 1 ) ) {
|
||||
float roll1, roll2;
|
||||
Segment->GetRolls( roll1, roll2 );
|
||||
if( roll1 != 0.f || roll2 != 0.f ) {
|
||||
iTrapezoid |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,6 +249,15 @@ inline float joint_roll( float const Own, float const Their, bool const Switch,
|
||||
return Switch == Theirswitch ? 0.5f * ( Own + Their ) : Switch ? Their : Own;
|
||||
}
|
||||
|
||||
// each component the same way
|
||||
inline amounts along( amounts const &Full, amounts const &Atstart, amounts const &Atend, double const Distance, double const Length ) {
|
||||
|
||||
return {
|
||||
along( Full.up, Atstart.up, Atend.up, Distance, Length ),
|
||||
along( Full.side, Atstart.side, Atend.side, Distance, Length ),
|
||||
along( Full.cross, Atstart.cross, Atend.cross, Distance, Length ) };
|
||||
}
|
||||
|
||||
// distance from the joint behind the point
|
||||
inline double rail_phase( double const Along, double const Rail ) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user