mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-02 15:09:13 +01:00
Merge remote-tracking branch 'origin/feat/track-uneven' into feat/renderers-cleanup
This commit is contained in:
@@ -447,13 +447,12 @@ void TSegment::RaPositionGet(double const fDistance, glm::dvec3 &position, glm::
|
||||
rotation.x += static_cast<float>( track_uneven::stray_per_mm * (
|
||||
amount.up * track_uneven::roll( position, across, gauge )
|
||||
+ amount.cross * track_uneven::level( position, across ) / gauge ) );
|
||||
position += stray( position, fDistance );
|
||||
position += track_uneven::across_only( stray( position, amount, fDistance ), across );
|
||||
}
|
||||
|
||||
glm::dvec3 TSegment::stray( glm::dvec3 const &Point, double const Distance ) const {
|
||||
auto const amount { pOwner->uneven_at( Distance, m_path ) };
|
||||
glm::dvec3 TSegment::stray( glm::dvec3 const &Point, track_uneven::amounts const &Amount, double const Distance ) const {
|
||||
auto const field { track_uneven::field( Point ) };
|
||||
glm::dvec3 result { field.x * amount.side, field.y * amount.up, field.z * amount.side };
|
||||
glm::dvec3 result { field.x * Amount.side, field.y * Amount.up, field.z * Amount.side };
|
||||
result *= track_uneven::stray_per_mm;
|
||||
result.y -= pOwner->joint_dip( Distance );
|
||||
return result;
|
||||
@@ -463,13 +462,25 @@ glm::dvec3 TSegment::uneven_bend( double const Distance ) const {
|
||||
if( false == m_uneven ) {
|
||||
return glm::dvec3 { 0.0 };
|
||||
}
|
||||
// over about a bogie; FastGetTFromS extrapolates past the ends of the path
|
||||
// over about a bogie
|
||||
auto const reach { 1.5 };
|
||||
auto const strayed = [&]( double const At ) {
|
||||
return stray( FastGetPoint( FastGetTFromS( At ) ), At ); };
|
||||
return stray( point_extended( At ), pOwner->uneven_at( At, m_path ), At ); };
|
||||
return ( strayed( Distance + reach ) - 2.0 * strayed( Distance ) + strayed( Distance - reach ) ) / ( reach * reach );
|
||||
}
|
||||
|
||||
glm::dvec3 TSegment::point_extended( double const Distance ) const {
|
||||
auto const on { std::clamp( Distance, 0.0, fLength ) };
|
||||
auto const t { FastGetTFromS( on ) };
|
||||
auto const point { FastGetPoint( t ) };
|
||||
if( on == Distance ) {
|
||||
return point;
|
||||
}
|
||||
auto const direction { FastGetDirection( t, 0.1 / fLength ) };
|
||||
auto const size { glm::length( direction ) };
|
||||
return size > 0.0 ? point + direction * ( ( Distance - on ) / size ) : point;
|
||||
}
|
||||
|
||||
void TSegment::settle_vertex( gfx::basic_vertex &Vertex, glm::dvec3 const &Origin, glm::vec3 const &Position, glm::vec3 const &Parallel, double const Distance ) const {
|
||||
if( false == m_uneven ) {
|
||||
return;
|
||||
@@ -478,8 +489,12 @@ void TSegment::settle_vertex( gfx::basic_vertex &Vertex, glm::dvec3 const &Origi
|
||||
auto const gauge { std::max( std::abs( pOwner->fTrackWidth ), 0.1f ) };
|
||||
auto const across { std::clamp( glm::dot( Vertex.position - Position, Parallel ), -0.5f * gauge, 0.5f * gauge ) };
|
||||
auto const share { pOwner->uneven_bed_share( Position.y - Vertex.position.y ) };
|
||||
Vertex.position.y -= static_cast<float>( share * track_uneven::stray_per_mm * pOwner->uneven_at( Distance, m_path ).cross * track_uneven::level( Origin + glm::dvec3 { Position }, glm::dvec3 { Parallel } ) * across / gauge );
|
||||
Vertex.position += glm::vec3 { share * stray( Origin + glm::dvec3 { Vertex.position }, Distance ) };
|
||||
if( share <= 0.0 ) {
|
||||
return;
|
||||
}
|
||||
auto const amount { pOwner->uneven_at( Distance, m_path ) };
|
||||
Vertex.position.y -= static_cast<float>( share * track_uneven::stray_per_mm * amount.cross * track_uneven::level( Origin + glm::dvec3 { Position }, glm::dvec3 { Parallel } ) * across / gauge );
|
||||
Vertex.position += glm::vec3 { share * stray( Origin + glm::dvec3 { Vertex.position }, amount, Distance ) };
|
||||
}
|
||||
|
||||
void TSegment::design_position(double const fDistance, glm::dvec3 &position, glm::vec3 &rotation) const {
|
||||
@@ -785,15 +800,15 @@ bool TSegment::RenderLoftSteps( gfx::vertex_array &Output, glm::dvec3 const &Ori
|
||||
}
|
||||
|
||||
void TSegment::plan_frame( double const Distance, glm::dvec3 &Point, glm::dvec3 &Across ) const {
|
||||
if( fTsBuffer.empty() ) {
|
||||
// never made into a path: tt_Cross and the like
|
||||
Point = Point1;
|
||||
Across = glm::dvec3 { 0.0 };
|
||||
return;
|
||||
}
|
||||
auto const t { FastGetTFromS( std::clamp( Distance, 0.0, fLength ) ) };
|
||||
Point = FastGetPoint( t );
|
||||
auto const dir { FastGetDirection( t, 0.1 / fLength ) };
|
||||
Across = glm::dvec3{ -dir.z, 0.0, dir.x };
|
||||
if( glm::length2( Across ) == 0.0 ) {
|
||||
auto const chord { FastGetPoint_1() - FastGetPoint_0() };
|
||||
Across = glm::dvec3{ -chord.z, 0.0, chord.x };
|
||||
}
|
||||
Across = glm::normalize( Across );
|
||||
Across = track_uneven::across_of( FastGetDirection( t, 0.1 / fLength ), FastGetPoint_1() - FastGetPoint_0() );
|
||||
}
|
||||
|
||||
bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Origin, gfx::vertex_array const &ShapePoints, loft_span const &Span ) const {
|
||||
|
||||
@@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "utilities/Classes.h"
|
||||
#include "rendering/geometrybank.h"
|
||||
#include "world/TrackUneven.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@@ -139,7 +140,9 @@ class TSegment
|
||||
// moves a lofted vertex with the unevenness of the track
|
||||
void settle_vertex( gfx::basic_vertex &Vertex, glm::dvec3 const &Origin, glm::vec3 const &Position, glm::vec3 const &Parallel, double const Distance ) const;
|
||||
// displacement of a point by the unevenness, at a distance along the path
|
||||
glm::dvec3 stray( glm::dvec3 const &Point, double const Distance ) const;
|
||||
glm::dvec3 stray( glm::dvec3 const &Point, track_uneven::amounts const &Amount, double const Distance ) const;
|
||||
// a point of the path at a distance, past its ends straight on: the polynomial would run off on a short curved path
|
||||
glm::dvec3 point_extended( double const Distance ) const;
|
||||
void design_position( double const fDistance, glm::dvec3 &position, glm::vec3 &rotation ) const;
|
||||
|
||||
public:
|
||||
@@ -195,6 +198,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; }
|
||||
float
|
||||
design_roll( int const End ) const {
|
||||
return m_designrolls[ End ]; }
|
||||
|
||||
320
world/Track.cpp
320
world/Track.cpp
@@ -87,9 +87,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
|
||||
@@ -740,10 +737,10 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
}
|
||||
}
|
||||
else if( str == "uneven" ) {
|
||||
// uneven <mm>[;<mm>...]: see read_uneven and TrackUneven.h. The semicolons stay in the token
|
||||
// uneven <mm>[;<mm>...]: see TrackUneven.h. The semicolons stay in the token
|
||||
auto const values { parser->getToken<std::string>( false, "\n\r\t " ) };
|
||||
if( ( eType == tt_Normal || eType == tt_Switch ) && iCategoryFlag == 1 ) {
|
||||
read_uneven( values );
|
||||
m_uneven = track_uneven::read_attribute( values, eType == tt_Switch );
|
||||
}
|
||||
}
|
||||
else if( str == "friction" ) {
|
||||
@@ -822,250 +819,6 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
// to a stale origin and the renderer would draw sleepers in the wrong place).
|
||||
}
|
||||
|
||||
void TTrack::read_uneven( std::string const &Text ) {
|
||||
|
||||
auto const fields { track_uneven::read_fields<6>( Text ) };
|
||||
m_uneven = std::max( fields[ 0 ], 0.f );
|
||||
if( eType == tt_Switch ) {
|
||||
m_unevenbranch = fields[ 1 ];
|
||||
m_unevenup = fields[ 2 ];
|
||||
m_unevenside = fields[ 3 ];
|
||||
m_unevenbranchup = fields[ 4 ];
|
||||
m_unevenbranchside = fields[ 5 ];
|
||||
}
|
||||
else {
|
||||
m_unevenup = fields[ 1 ];
|
||||
m_unevenside = fields[ 2 ];
|
||||
m_unevencross = fields[ 3 ];
|
||||
m_jointdip = fields[ 4 ];
|
||||
}
|
||||
}
|
||||
|
||||
std::string TTrack::uneven_text() const {
|
||||
|
||||
auto const fields { eType == tt_Switch ?
|
||||
std::array<float, 6> { m_uneven, m_unevenbranch, m_unevenup, m_unevenside, m_unevenbranchup, m_unevenbranchside } :
|
||||
std::array<float, 6> { m_uneven, m_unevenup, m_unevenside, m_unevencross, m_jointdip, -1.f } };
|
||||
if( m_uneven <= 0.f && std::all_of( fields.begin() + 1, fields.end(), []( float const Field ) { return Field < 0.f; } ) ) {
|
||||
return {};
|
||||
}
|
||||
return "uneven " + track_uneven::write_fields( fields ) + ' ';
|
||||
}
|
||||
|
||||
track_uneven::amounts TTrack::uneven_declared( int const Path ) const {
|
||||
|
||||
track_uneven::amounts result {
|
||||
m_unevenup >= 0.f ? m_unevenup : m_uneven,
|
||||
m_unevenside >= 0.f ? m_unevenside : m_uneven,
|
||||
eType != tt_Normal ? 0.f : m_unevencross >= 0.f ? m_unevencross : m_uneven };
|
||||
if( Path == 1 ) {
|
||||
auto const branch = [ this ]( float const Part, double const Straight ) -> double {
|
||||
return Part >= 0.f ? Part : m_unevenbranch >= 0.f ? m_unevenbranch : Straight; };
|
||||
result.up = branch( m_unevenbranchup, result.up );
|
||||
result.side = branch( m_unevenbranchside, result.side );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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_jointdip >= 0.f ? m_jointdip : 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;
|
||||
}
|
||||
}
|
||||
|
||||
void TTrack::update_location() {
|
||||
|
||||
location( (
|
||||
@@ -1813,73 +1566,6 @@ void TTrack::rebuild_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
create_geometry( Bank );
|
||||
}
|
||||
|
||||
void TTrack::share_joints() {
|
||||
|
||||
for( int path = 0; path < track_rails::path_count( *this ); ++path ) {
|
||||
auto *segment { eType == tt_Switch ? SwitchExtension->Segments[ path ].get() : Segment.get() };
|
||||
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 *neighbour { track_rails::path_neighbour( *this, path, end ) };
|
||||
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 < track_rails::path_count( *neighbour ); ++theirpath ) {
|
||||
auto const *theirs { neighbour->eType == tt_Switch ? neighbour->SwitchExtension->Segments[ theirpath ].get() : neighbour->Segment.get() };
|
||||
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 ) {
|
||||
// the cant of a plain track is what was designed, a switch takes it; alike ones meet halfway
|
||||
auto const own { segment->design_roll( end ) };
|
||||
auto const their { theirend == end ? -theirs->design_roll( theirend ) : theirs->design_roll( theirend ) };
|
||||
roll = (
|
||||
( eType == tt_Switch ) == ( neighbour->eType == tt_Switch ) ? 0.5f * ( own + their ) :
|
||||
eType == tt_Switch ? their :
|
||||
own );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if( eType == tt_Switch && SwitchExtension ) {
|
||||
// the profiles of the switch are made at the mean of the rolls of its paths as they meet the tracks joined, and turned from it
|
||||
// along each path (init_segments() bakes the mean of the rolls as designed)
|
||||
float mean1, mean2;
|
||||
profile_rolls( mean1, mean2 );
|
||||
for( int path = 0; path < 2; ++path ) {
|
||||
SwitchExtension->Segments[ path ]->set_baked_roll( mean1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
gfx::userdata_array empty_userdata;
|
||||
// the rails of a frog with a movable nose are laid again as it moves over, in the same chunks
|
||||
@@ -3136,7 +2822,7 @@ TTrack::export_as_text_( std::ostream &Output ) const {
|
||||
&& false == m_profile1.first.empty() ) {
|
||||
Output << "railprofile " << m_profile1.first << ' ';
|
||||
}
|
||||
Output << uneven_text();
|
||||
Output << track_uneven::write_attribute( m_uneven, eType == tt_Switch );
|
||||
if( false == m_friction.first.empty() ) {
|
||||
Output << "friction " << m_friction.first << ' ';
|
||||
}
|
||||
|
||||
@@ -287,16 +287,7 @@ public:
|
||||
TEnvironmentType eEnvironment = e_flat; // dźwięk i oświetlenie
|
||||
int iAction = 0; // czy modyfikowany eventami (specjalna obsługa przy skanowaniu)
|
||||
float fOverhead = -1.0; // można normalnie pobierać prąd (0 dla jazdy bezprądowej po danym odcinku, >0-z opuszczonym i ograniczeniem prędkości)
|
||||
float m_uneven { 0.f }; // mm, see TrackUneven.h; plain tracks and switches
|
||||
// the components set apart from m_uneven, mm; negative: m_uneven. Cross level on plain tracks only
|
||||
float m_unevenup { -1.f };
|
||||
float m_unevenside { -1.f };
|
||||
float m_unevencross { -1.f };
|
||||
// the diverging path of a switch, mm; negative: as the straight one
|
||||
float m_unevenbranch { -1.f };
|
||||
float m_unevenbranchup { -1.f };
|
||||
float m_unevenbranchside { -1.f };
|
||||
float m_jointdip { -1.f }; // depth of the dip at rail joints, mm; negative: from m_uneven; plain tracks only
|
||||
track_uneven::attribute m_uneven; // plain tracks and switches
|
||||
private:
|
||||
double fVelocity = -1.0; // ograniczenie prędkości // prędkość dla AI (powyżej rośnie prawdopowobieństwo wykolejenia)
|
||||
public:
|
||||
@@ -417,10 +408,10 @@ public:
|
||||
void VelocitySet(float v);
|
||||
double VelocityGet();
|
||||
float Friction() const;
|
||||
// the attribute of the entry. A plain track: the value;up and down;sideways;cross level;dip at the joints.
|
||||
// A switch: path p1-p2;path p3-p4;up and down of the first;sideways of the first;up and down of the second;sideways of the second
|
||||
void read_uneven( std::string const &Text );
|
||||
std::string uneven_text() 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;
|
||||
@@ -483,6 +474,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
|
||||
|
||||
345
world/TrackUneven.cpp
Normal file
345
world/TrackUneven.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
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;
|
||||
}
|
||||
}
|
||||
if( eType == tt_Switch && SwitchExtension ) {
|
||||
// the profiles of the switch are made at the mean of the rolls of its paths as they meet the tracks joined, and turned from it
|
||||
// along each path (init_segments() bakes the mean of the rolls as designed)
|
||||
float mean1, mean2;
|
||||
profile_rolls( mean1, mean2 );
|
||||
for( int path = 0; path < 2; ++path ) {
|
||||
SwitchExtension->Segments[ path ]->set_baked_roll( mean1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,68 @@ std::string write_fields( std::array<float, Count> const &Fields ) {
|
||||
return text.str();
|
||||
}
|
||||
|
||||
// the values of the attribute, mm, negative where not given. Cross level and the dip at joints for plain tracks, the path p3-p4 for switches
|
||||
struct attribute {
|
||||
float value { 0.f };
|
||||
float up { -1.f };
|
||||
float side { -1.f };
|
||||
float cross { -1.f };
|
||||
float dip { -1.f };
|
||||
float branch { -1.f };
|
||||
float branchup { -1.f };
|
||||
float branchside { -1.f };
|
||||
};
|
||||
|
||||
// a plain track: value;up;side;cross;dip. A switch: p1-p2;p3-p4;up of p1-p2;side of p1-p2;up of p3-p4;side of p3-p4
|
||||
inline attribute read_attribute( std::string const &Text, bool const Switch ) {
|
||||
|
||||
auto const fields { read_fields<6>( Text ) };
|
||||
attribute result;
|
||||
result.value = std::max( fields[ 0 ], 0.f );
|
||||
if( Switch ) {
|
||||
result.branch = fields[ 1 ];
|
||||
result.up = fields[ 2 ];
|
||||
result.side = fields[ 3 ];
|
||||
result.branchup = fields[ 4 ];
|
||||
result.branchside = fields[ 5 ];
|
||||
}
|
||||
else {
|
||||
result.up = fields[ 1 ];
|
||||
result.side = fields[ 2 ];
|
||||
result.cross = fields[ 3 ];
|
||||
result.dip = fields[ 4 ];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// in the layout of the entry it goes into, which can be of another type than the track the values come from
|
||||
inline std::string write_attribute( attribute const &Values, bool const Switch ) {
|
||||
|
||||
auto const fields { Switch ?
|
||||
std::array<float, 6> { Values.value, Values.branch, Values.up, Values.side, Values.branchup, Values.branchside } :
|
||||
std::array<float, 6> { Values.value, Values.up, Values.side, Values.cross, Values.dip, -1.f } };
|
||||
if( Values.value <= 0.f && std::all_of( fields.begin() + 1, fields.end(), []( float const Field ) { return Field < 0.f; } ) ) {
|
||||
return {};
|
||||
}
|
||||
return "uneven " + write_fields( fields ) + ' ';
|
||||
}
|
||||
|
||||
// what a path comes to from the values: what's not given takes the first value, p3-p4 those of p1-p2. Cross level on plain tracks only
|
||||
inline amounts declared( attribute const &Values, bool const Plain, int const Path ) {
|
||||
|
||||
amounts result {
|
||||
Values.up >= 0.f ? Values.up : Values.value,
|
||||
Values.side >= 0.f ? Values.side : Values.value,
|
||||
false == Plain ? 0.f : Values.cross >= 0.f ? Values.cross : Values.value };
|
||||
if( Path == 1 ) {
|
||||
auto const branch = [ &Values ]( float const Part, double const Straight ) -> double {
|
||||
return Part >= 0.f ? Part : Values.branch >= 0.f ? Values.branch : Straight; };
|
||||
result.up = branch( Values.branchup, result.up );
|
||||
result.side = branch( Values.branchside, result.side );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <std::size_t Count>
|
||||
using wave_set = std::array<wave, Count>;
|
||||
|
||||
@@ -162,6 +224,40 @@ inline double bed_share( double const Depth, double const Rail, double const Hei
|
||||
return Height > 0.0 ? std::clamp( 1.0 - ( Depth - Rail ) / Height, 0.0, 1.0 ) : 1.0;
|
||||
}
|
||||
|
||||
// a displacement with the part along the track taken out, Across being a unit vector in the plan. Along the track
|
||||
// it would only change the distance between coupled vehicles, and with it the forces between them
|
||||
inline glm::dvec3 across_only( glm::dvec3 const &Offset, glm::dvec3 const &Across ) {
|
||||
|
||||
return Across * glm::dot( Offset, Across ) + glm::dvec3 { 0.0, Offset.y, 0.0 };
|
||||
}
|
||||
|
||||
// the unit vector across a direction in the plan, or across the chord if it has none; zero for a path of no length, which has neither
|
||||
inline glm::dvec3 across_of( glm::dvec3 const &Direction, glm::dvec3 const &Chord ) {
|
||||
|
||||
for( auto const &along : { Direction, Chord } ) {
|
||||
glm::dvec3 const across { -along.z, 0.0, along.x };
|
||||
if( glm::dot( across, across ) > 0.0 ) {
|
||||
return glm::normalize( across );
|
||||
}
|
||||
}
|
||||
return glm::dvec3 { 0.0 };
|
||||
}
|
||||
|
||||
// the cant where two paths meet, theirs turned to the way of this one: a switch takes that of a plain track, alike ones meet halfway
|
||||
inline float joint_roll( float const Own, float const Their, bool const Switch, bool const Theirswitch ) {
|
||||
|
||||
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