17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-10 16:03:16 +01:00

fix(track): what Sonar reports on the unevenness

The blockers are in the EP brake of a vehicle, older than this work: its
arrays made with new[] are vectors now, which also removes the deletes.

- The caches of the unevenness of a track are no longer mutable: what fills
  them isn't const any more.
- TSegment gets back to RaPositionGet in one piece, with what the path has
  to do with the others in one struct.
- Less nesting in the group of switches and in the fields of the editor,
  nested conditionals written out, and the attribute is read from a
  string_view. A test no longer counts in floating point.

No change in behaviour.
This commit is contained in:
maj00r
2026-10-31 20:17:47 +01:00
parent 56d389e2df
commit 9364b03e1a
9 changed files with 223 additions and 207 deletions

View File

@@ -1166,26 +1166,23 @@ void editor_mode::render_path_parameters(TTrack &Track)
track->m_uneven.value = std::clamp(track->m_uneven.value, 0.f, track_uneven::most);
item_tooltip("Peak deviation of a rail on a 10 m chord. Id-14 limits: 6 mm at 160 km/h, 10 at 120, 18 at 80, 35 at 40.\nWith a sound distance set, the rails also dip at the joints. Joined switches use the lowest of their values");
finish_edit(rebuild::parameters);
struct component
{
char const *label;
float *value;
bool shown;
};
auto const plain{track->eType == tt_Normal};
for (auto const &part : {component{"Up and down (mm)", &track->m_uneven.up, true},
component{"Sideways (mm)", &track->m_uneven.side, true},
component{"Cross level (mm)", &track->m_uneven.cross, plain},
component{"Diverging path (mm)", &track->m_uneven.branch, !plain},
component{"Diverging, up and down (mm)", &track->m_uneven.branchup, !plain},
component{"Diverging, sideways (mm)", &track->m_uneven.branchside, !plain}})
{
if (false == part.shown)
continue;
if (ImGui::InputFloat(STR_C(part.label), part.value, 0.0f, 0.0f, "%.1f"))
*part.value = *part.value < 0.f ? -1.f : std::min(*part.value, track_uneven::most);
auto const component = [&](char const *Label, float &Value) {
if (ImGui::InputFloat(STR_C(Label), &Value, 0.0f, 0.0f, "%.1f"))
Value = Value < 0.f ? -1.f : std::min(Value, track_uneven::most);
item_tooltip("Set apart from the unevenness, mm. Negative: the unevenness");
finish_edit(rebuild::parameters);
};
component("Up and down (mm)", track->m_uneven.up);
component("Sideways (mm)", track->m_uneven.side);
if (track->eType == tt_Normal)
{
component("Cross level (mm)", track->m_uneven.cross);
}
else
{
component("Diverging path (mm)", track->m_uneven.branch);
component("Diverging, up and down (mm)", track->m_uneven.branchup);
component("Diverging, sideways (mm)", track->m_uneven.branchside);
}
}
if (track->eType == tt_Normal && (track->iCategoryFlag & 15) == 1)

View File

@@ -2,6 +2,8 @@
#include "doctest.h"
#include "world/TrackUneven.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <vector>
@@ -12,19 +14,20 @@ double deviation(std::vector<double> const &Values)
auto mean{0.0};
for (auto const value : Values)
mean += value;
mean /= Values.size();
auto const count{static_cast<double>(Values.size())};
mean /= count;
auto sum{0.0};
for (auto const value : Values)
sum += (value - mean) * (value - mean);
return std::sqrt(sum / Values.size());
return std::sqrt(sum / count);
}
double peak(std::vector<double> Values)
{
for (auto &value : Values)
value = std::abs(value);
std::sort(Values.begin(), Values.end());
return Values[static_cast<std::size_t>(0.999 * Values.size())];
std::ranges::sort(Values);
return Values[Values.size() * 999 / 1000];
}
void add_chord(std::vector<double> &Output, std::vector<double> const &Line, int const Half)
@@ -44,14 +47,22 @@ TEST_CASE("uneven track: measures as the Id-14 limits for its value")
int const steps{4000};
int const base{10}; // 5 m
int const lines{36};
double vertical{0.0}, horizontal{0.0}, twist{0.0}, wander{0.0}, level{0.0}, j{0.0}, away{0.0};
double vertical{0.0};
double horizontal{0.0};
double twist{0.0};
double wander{0.0};
double level{0.0};
double j{0.0};
double away{0.0};
for (int line = 0; line < lines; ++line)
{
auto const heading{glm::pi<double>() * (line + 0.37) / lines};
glm::dvec3 const start{7919.0 * std::sin(line * 1.7), 0.0, 6577.0 * std::cos(line * 2.3)};
glm::dvec3 const along{std::cos(heading), 0.0, std::sin(heading)};
glm::dvec3 const across{-along.z, 0.0, along.x};
std::vector<double> up[2], side[2], middle;
std::array<std::vector<double>, 2> up;
std::array<std::vector<double>, 2> side;
std::vector<double> middle;
for (int i = 0; i < steps; ++i)
{
auto const point{start + along * (i * step)};
@@ -64,7 +75,11 @@ TEST_CASE("uneven track: measures as the Id-14 limits for its value")
}
middle.push_back(track_uneven::field(point).y * amplitude);
}
std::vector<double> verticals, horizontals, twists, gauges, levels;
std::vector<double> verticals;
std::vector<double> horizontals;
std::vector<double> twists;
std::vector<double> gauges;
std::vector<double> levels;
for (int rail = 0; rail < 2; ++rail)
{
add_chord(verticals, up[rail], base);
@@ -269,9 +284,9 @@ TEST_CASE("uneven track: the value along a track meets its neighbours exactly")
CHECK(track_uneven::along(18.0, 35.0, 35.0, 0.25, 0.5) == doctest::Approx(35.0).epsilon(0.01));
// no jump anywhere
auto last{track_uneven::along(18.0, 35.0, 0.0, 0.0, length)};
for (auto distance{0.25}; distance <= length; distance += 0.25)
for (int step = 1; step <= 2000; ++step)
{
auto const value{track_uneven::along(18.0, 35.0, 0.0, distance, length)};
auto const value{track_uneven::along(18.0, 35.0, 0.0, step * 0.25, length)};
REQUIRE(std::abs(value - last) < 0.5);
last = value;
}

View File

@@ -3382,14 +3382,12 @@ bool TDynamicObject::Update(double dt, double dt1)
FzadED = 0; //pętla bezpieczeństwa - bez ED
auto const FzadPN = Fzad - FrED * MoverParameters->MED_FrED_factor;
//np = 0;
// BUG: likely memory leak, allocation per inner loop, deleted only once outside
// TODO: sort this shit out
bool* PrzekrF = new bool[np];
std::vector<bool> PrzekrF( np );
float nPrzekrF = 0;
bool test = true;
float* FzED = new float[np];
float* FzEP = new float[np];
float* FmaxEP = new float[np];
std::vector<float> FzED( np );
std::vector<float> FzEP( np );
std::vector<float> FmaxEP( np );
// 3. ustaw pojazdom sile hamowania ED
// - proporcjonalnie do mozliwosci
@@ -3548,11 +3546,6 @@ bool TDynamicObject::Update(double dt, double dt1)
}
delete[] PrzekrF;
delete[] FzED;
delete[] FzEP;
delete[] FmaxEP;
MED_oldFED = FzadED;
}
@@ -3661,33 +3654,32 @@ bool TDynamicObject::Update(double dt, double dt1)
1 :
ctOwner->Vehicle()->DirectionGet() == DirectionGet() ? 1 : -1 ) };
for( auto &axle : m_axlesounds ) {
if( axle_passed_joint( axle, dDOMoveLen, directioninconsist ) ) {
if( MoverParameters->Vel > 0.1 ) {
// NOTE: for combined clatter sound we supply 1/100th of actual value, as the sound module converts does the opposite, converting received (typically) 0-1 values to 0-100 range
auto const frequency = true == axle.clatter.is_combined() ? MoverParameters->Vel * 0.01 : 1.0;
axle.clatter
.pitch( frequency )
.gain( volume )
.play();
// crude bump simulation, drop down on even axles, move back up on
// the odd ones
// MoverParameters->AccVert += (MoverParameters->Vel*0.1f) *
double const headroom = std::clamp(
MoverParameters->Vmax - ( MoverParameters->Vel + MoverParameters->Vmax * 0.32f ),
0.0, MoverParameters->Vmax );
double const jolt = headroom * .05f * ( MyTrack->iDamageFlag * 0.25f );
if (MyTrack->eType == tt_Normal)
{
MoverParameters->AccVert += std::clamp( jolt, 0.0, 4.0 );
}
else if (MyTrack->eType == tt_Switch)
{
double const accHorizontal = std::clamp( jolt, 0.0, 1.0 ) * ((axleindex % 2) != 0 ? 1 : -1);
MoverParameters->AccS += accHorizontal;
MoverParameters->AccN += accHorizontal;
MoverParameters->AccVert += std::clamp( jolt, 0.0, 2.0 );
}
}
if( axle_passed_joint( axle, dDOMoveLen, directioninconsist )
&& MoverParameters->Vel > 0.1 ) {
// NOTE: for combined clatter sound we supply 1/100th of actual value, as the sound module converts does the opposite, converting received (typically) 0-1 values to 0-100 range
auto const frequency = true == axle.clatter.is_combined() ? MoverParameters->Vel * 0.01 : 1.0;
axle.clatter
.pitch( frequency )
.gain( volume )
.play();
// crude bump simulation, drop down on even axles, move back up on
// the odd ones
// MoverParameters->AccVert += (MoverParameters->Vel*0.1f) *
double const headroom = std::clamp(
MoverParameters->Vmax - ( MoverParameters->Vel + MoverParameters->Vmax * 0.32f ),
0.0, MoverParameters->Vmax );
double const jolt = headroom * .05f * ( MyTrack->iDamageFlag * 0.25f );
if (MyTrack->eType == tt_Normal)
{
MoverParameters->AccVert += std::clamp( jolt, 0.0, 4.0 );
}
else if (MyTrack->eType == tt_Switch)
{
double const accHorizontal = std::clamp( jolt, 0.0, 1.0 ) * ((axleindex % 2) != 0 ? 1 : -1);
MoverParameters->AccS += accHorizontal;
MoverParameters->AccN += accHorizontal;
MoverParameters->AccVert += std::clamp( jolt, 0.0, 2.0 );
}
}
++axleindex;
}
@@ -8072,12 +8064,11 @@ TDynamicObject::update_shake( double const Timedelta ) {
}
bool
TDynamicObject::axle_passed_joint( axle_sounds &Axle, double const Move, int const Direction ) {
TDynamicObject::axle_passed_joint( axle_sounds &Axle, double const Move, int const Direction ) const {
// by the outer axle on its side of the vehicle: the two can be on different tracks
auto const &outer { Axle.offset >= 0.0 ? Axle0 : Axle1 };
auto const railphase { outer.RailPhase( Axle.offset - std::copysign( 0.5 * fAxleDist, Axle.offset ) ) };
if( railphase >= 0.0 ) {
if( auto const railphase { outer.RailPhase( Axle.offset - std::copysign( 0.5 * fAxleDist, Axle.offset ) ) }; railphase >= 0.0 ) {
// the rails under the axle, which aren't always those under the track the vehicle is on
auto const passed { Axle.atjoints && std::abs( railphase - Axle.distance ) > 0.5 * outer.GetTrack()->fSoundDistance };
Axle.distance = railphase;

View File

@@ -405,7 +405,7 @@ private:
bool atjoints { false }; // distance comes from TTrack::rail_phase
};
// whether the axle has just passed a rail joint. On a track with dipping joints it goes by them, elsewhere the axle counts a rail off itself
bool axle_passed_joint( axle_sounds &Axle, double const Move, int const Direction );
bool axle_passed_joint( axle_sounds &Axle, double const Move, int const Direction ) const;
struct powertrain_sounds {
sound_source inverter { sound_placement::engine };

View File

@@ -69,10 +69,8 @@ bool TSegment::Init(glm::dvec3 &NewPoint1, glm::dvec3 NewCPointOut, glm::dvec3 N
// poprawienie przechyłki
fRoll1 = static_cast<float>(glm::radians(fNewRoll1)); // Ra: przeliczone jest bardziej przydatne do obliczeń
fRoll2 = static_cast<float>(glm::radians(fNewRoll2));
m_designrolls[ 0 ] = fRoll1;
m_designrolls[ 1 ] = fRoll2;
m_joints[ 0 ].reset();
m_joints[ 1 ].reset();
m_setting.designrolls = { fRoll1, fRoll2 };
m_setting.joints = {};
bCurve = bIsCurve;
if (Global.bRollFix)
{ // Ra: poprawianie przechyłki
@@ -122,7 +120,7 @@ bool TSegment::Init(glm::dvec3 &NewPoint1, glm::dvec3 NewCPointOut, glm::dvec3 N
fStoop = std::atan2(Point2.y - Point1.y, fLength); // pochylenie toru prostego, żeby nie liczyć wielokrotnie
fStep = fNewStep;
if( m_uneven && pOwner->eType != tt_Switch ) {
if( m_setting.uneven && pOwner->eType != tt_Switch ) {
// dense enough for the waves; a switch keeps its division, the blades depend on it
fStep = std::min( fStep, 2.0 );
}
@@ -373,21 +371,6 @@ Math3D::vector3 TSegment::GetPoint(double const fDistance) const
};
*/
void TSegment::RaPositionGet(double const fDistance, glm::dvec3 &position, glm::vec3 &rotation) const {
design_position( fDistance, position, rotation );
if( false == m_uneven ) {
return;
}
// direction and slope stay as designed: the physics takes the curve radius and the grade from them
auto const gauge { std::max( std::abs( pOwner->fTrackWidth ), 0.1f ) };
glm::dvec3 const across { -std::cos( rotation.z ), 0.0, -std::sin( rotation.z ) };
auto const amount { pOwner->uneven_at( fDistance, m_path ) };
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 += track_uneven::across_only( stray( position, amount, fDistance ), across );
}
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 };
@@ -397,30 +380,25 @@ glm::dvec3 TSegment::stray( glm::dvec3 const &Point, track_uneven::amounts const
}
glm::dvec3 TSegment::uneven_bend( double const Distance ) const {
if( false == m_uneven ) {
if( false == m_setting.uneven ) {
return glm::dvec3 { 0.0 };
}
// over about a bogie
auto const reach { 1.5 };
auto const strayed = [&]( double const At ) {
return stray( point_extended( At ), pOwner->uneven_at( At, m_path ), At ); };
auto const strayed = [ this ]( double const At ) {
// past the ends of the path straight on: the polynomial would run off on a short curved one
auto const on { std::clamp( At, 0.0, fLength ) };
auto const t { FastGetTFromS( on ) };
auto point { FastGetPoint( t ) };
if( auto const direction { FastGetDirection( t, 0.1 / fLength ) }; on != At && glm::length( direction ) > 0.0 ) {
point += direction * ( ( At - on ) / glm::length( direction ) );
}
return stray( point, pOwner->uneven_at( At, m_setting.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 ) {
if( false == m_setting.uneven ) {
return;
}
// the rails settling apart turn about the middle of the track, what's outside them follows the rail; the foot of the ballast stays
@@ -430,12 +408,12 @@ void TSegment::settle_vertex( gfx::basic_vertex &Vertex, glm::dvec3 const &Origi
if( share <= 0.0 ) {
return;
}
auto const amount { pOwner->uneven_at( Distance, m_path ) };
auto const amount { pOwner->uneven_at( Distance, m_setting.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 {
void TSegment::RaPositionGet(double const fDistance, glm::dvec3 &position, glm::vec3 &rotation) const {
if (bCurve) {
// można by wprowadzić uproszczony wzór dla okręgów płaskich
auto const t = FastGetTFromS(fDistance); // aproksymacja dystansu na krzywej Beziera na parametr (t)
@@ -458,6 +436,17 @@ void TSegment::design_position(double const fDistance, glm::dvec3 &position, glm
rotation.y = static_cast<float>(fStoop); // pochylenie toru prostego
rotation.z = static_cast<float>(fDirection); // kierunek toru w planie
}
if( false == m_setting.uneven ) {
return;
}
// direction and slope stay as designed: the physics takes the curve radius and the grade from them
auto const gauge { std::max( std::abs( pOwner->fTrackWidth ), 0.1f ) };
glm::dvec3 const across { -std::cos( rotation.z ), 0.0, -std::sin( rotation.z ) };
auto const amount { pOwner->uneven_at( fDistance, m_setting.path ) };
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 += track_uneven::across_only( stray( position, amount, fDistance ), across );
};
void TSegment::plan_frame( double const Distance, glm::dvec3 &Point, glm::dvec3 &Across ) const {
@@ -523,9 +512,9 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
parallel1 = glm::vec3{ glm::dvec3{ FastGetPoint_1() - FastGetPoint_0() } };
}
parallel1 = glm::normalize( parallel1 );
if( iSkip == 0 && m_joints[ 0 ] ) {
pos1 = glm::vec3{ m_joints[ 0 ]->first - Origin };
parallel1 = glm::vec3{ m_joints[ 0 ]->second };
if( iSkip == 0 && m_setting.joints[ 0 ] ) {
pos1 = glm::vec3{ m_setting.joints[ 0 ]->first - Origin };
parallel1 = glm::vec3{ m_setting.joints[ 0 ]->second };
}
if( iEnd == 0 )
iEnd = iSegCount;
@@ -572,9 +561,9 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin,
parallel2 = glm::vec3{ glm::dvec3{ FastGetPoint_1() - FastGetPoint_0() } };
}
parallel2 = glm::normalize( parallel2 );
if( i == iSegCount && m_joints[ 1 ] ) {
pos2 = glm::vec3{ m_joints[ 1 ]->first - Origin };
parallel2 = glm::vec3{ m_joints[ 1 ]->second };
if( i == iSegCount && m_setting.joints[ 1 ] ) {
pos2 = glm::vec3{ m_setting.joints[ 1 ]->first - Origin };
parallel2 = glm::vec3{ m_setting.joints[ 1 ]->second };
}
// TODO: refactor the loop, there's no need to calculate starting points for each segment when we can copy the end points of the previous one

View File

@@ -9,7 +9,9 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <array>
#include <optional>
#include <utility>
#include "utilities/Classes.h"
#include "rendering/geometrybank.h"
@@ -50,11 +52,14 @@ class TSegment
glm::dvec3 Point2;
float fRoll1 { 0.f };
float fRoll2 { 0.f }; // przechyłka na końcach
float m_designrolls[ 2 ] { 0.f, 0.f }; // as entered; the two above can differ at a joint
bool m_uneven { false }; // see TrackUneven.h
int m_path { 0 }; // which path of a switch
// where the path meets the next one: the middle of their ends and their common vector across
std::optional<std::pair<glm::dvec3, glm::dvec3>> m_joints[ 2 ];
// how the path sits among the others
struct setting {
std::array<float, 2> designrolls { 0.f, 0.f }; // as entered; fRoll1 and fRoll2 can differ at a joint
bool uneven { false }; // see TrackUneven.h
int path { 0 }; // which path of a switch
// where the path meets the next one: the middle of their ends and their common vector across
std::array<std::optional<std::pair<glm::dvec3, glm::dvec3>>, 2> joints;
} m_setting;
double fLength { -1.0 }; // długość policzona
std::vector<double> fTsBuffer; // wartości parametru krzywej t dla równych odcinków s
std::vector<double> fTsSlope; // nachylenie dt/du (= dt/ds * fStep) w tych samych punktach
@@ -79,9 +84,6 @@ class TSegment
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, 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:
bool bCurve = false;
@@ -125,20 +127,17 @@ public:
// call before Init, it sets the density of the geometry
void
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; }
m_setting.uneven = Uneven;
m_setting.path = Path; }
// 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;
float
design_roll( int const End ) const {
return m_designrolls[ End ]; }
return m_setting.designrolls[ End ]; }
void
set_joint( int const End, std::optional<std::pair<glm::dvec3, glm::dvec3>> const &Joint, float const Roll ) {
m_joints[ End ] = Joint;
m_setting.joints[ End ] = Joint;
( End == 0 ? fRoll1 : fRoll2 ) = Roll; }
// second derivative of the displacement along the path; times speed squared: acceleration of an axle
glm::dvec3

View File

@@ -349,15 +349,15 @@ public:
track_uneven::amounts uneven_declared( int const Path = 0 ) const;
bool is_uneven() const;
// the unevenness at a distance along a path, meeting the tracks joined at its ends
track_uneven::amounts uneven_at( double const Distance, int const Path = 0 ) const;
track_uneven::amounts uneven_at( double const Distance, int const Path = 0 );
// how much of the displacement a point of the profile at the depth takes
double uneven_bed_share( double const Depth ) const;
// closes the gaps and steps of cant between the ends of joined paths
void share_joints();
double joint_depth() const; // mm
double joint_dip( double const Distance ) const; // m
double joint_dip( double const Distance ); // m
// distance from the rail joint behind; negative: the track has no joints that dip
double rail_phase( double const Distance ) const;
double rail_phase( double const Distance );
void ConnectionsLog();
bool DoubleSlip() const;
static void fetch_default_profiles();
@@ -395,8 +395,8 @@ private:
double way { 1.0 }; // direction of the path along the run
int stamp { -1 };
};
void settle_rail_run() const;
mutable rail_run m_railrun;
void settle_rail_run();
rail_run m_railrun;
// the value along each path of a switch: the lowest of that path in the switches joined, 0 next to a track with none.
// And at the ends of the paths, by what meets there: the other path at the common end, the path of a switch joined
struct switch_stray {
@@ -404,15 +404,16 @@ private:
std::array<std::array<track_uneven::amounts, 2>, 2> ends;
int stamp { -1 };
};
mutable switch_stray m_switchstray;
switch_stray const &switch_uneven() const;
void settle_switch_stray() const;
switch_stray m_switchstray;
switch_stray const &switch_uneven();
void settle_switch_stray();
void settle_switch_ends();
// 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;
track_uneven::amounts uneven_met( TTrack *Other, glm::dvec3 const &Point );
// the value of a switch where a track joined to it meets it
track_uneven::amounts switch_uneven_at( glm::dvec3 const &Point ) const;
track_uneven::amounts switch_uneven_at( glm::dvec3 const &Point );
static int s_layoutstamp; // bumped when tracks are joined or changed, invalidates the two above
void create_road_profile( gfx::vertex_array &Output, bool const Forcetransition = false );
void create_road_side_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition = false );

View File

@@ -24,42 +24,55 @@ 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 ) {
glm::dvec3 end_point( TSegment const &Segment, int const End ) {
return End == 0 ? Segment.FastGetPoint_0() : Segment.FastGetPoint_1();
}
std::array<TTrack *, 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 *> switch_group( TTrack &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() ) {
std::vector group { &Switch };
std::vector pending { &Switch };
while( false == pending.empty() ) {
auto const *track { pending.back() };
pending.pop_back();
for( auto *other : joined_to_switch( *track ) ) {
if( is_switch( other ) && std::ranges::find( group, other ) == group.end() ) {
group.push_back( other );
pending.push_back( other );
}
}
}
return group;
}
// a plain track with none of a component keeps the switch to its design in it, any other comes to the value of the switch
void keep_to_plain_track( std::array<track_uneven::amounts, 2> &Values, TTrack const &Plain ) {
auto const theirs { Plain.uneven_declared() };
for( auto &path : Values ) {
if( theirs.up <= 0.0 ) { path.up = 0.0; }
if( theirs.side <= 0.0 ) { path.side = 0.0; }
}
}
// 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> group_path_values( std::vector<TTrack *> 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; }
if( other != nullptr && false == is_switch( other ) ) {
keep_to_plain_track( values, *other );
}
}
}
@@ -74,7 +87,7 @@ track_uneven::amounts least_at_ends( TTrack const &Switch, glm::dvec3 const &Poi
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 ) {
if( glm::length( end_point( *Switch.path_segment( path ), end ) - Point ) < switch_end_reach ) {
result = track_uneven::least( result, Value( path, end ) );
found = true;
}
@@ -165,16 +178,16 @@ 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 {
track_uneven::amounts TTrack::uneven_at( double const Distance, int const Path ) {
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() );
return track_uneven::along( uneven_declared(), uneven_met( trPrev, end_point( *Segment, 0 ) ), uneven_met( trNext, end_point( *Segment, 1 ) ), Distance, Length() );
}
track_uneven::amounts TTrack::uneven_met( TTrack const *Other, glm::dvec3 const &Point ) const {
track_uneven::amounts TTrack::uneven_met( TTrack *Other, glm::dvec3 const &Point ) {
if( Other == nullptr ) {
return {};
@@ -182,7 +195,7 @@ track_uneven::amounts TTrack::uneven_met( TTrack const *Other, glm::dvec3 const
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 {
TTrack::switch_stray const &TTrack::switch_uneven() {
if( m_switchstray.stamp != s_layoutstamp ) {
settle_switch_stray();
@@ -190,26 +203,30 @@ TTrack::switch_stray const &TTrack::switch_uneven() const {
return m_switchstray;
}
void TTrack::settle_switch_stray() const {
void TTrack::settle_switch_stray() {
auto const group { switch_group( *this ) };
auto const values { group_path_values( group ) };
for( auto const *track : group ) {
for( auto *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;
// with the paths known in all of the group
for( auto *track : group ) {
track->settle_switch_ends();
}
}
void TTrack::settle_switch_ends() {
for( int path = 0; path < 2; ++path ) {
for( int end = 0; end < 2; ++end ) {
auto const point { end_point( *path_segment( path ), end ) };
auto value { switch_paths_at( point ) };
if( auto const *other { path_neighbour( path, end ) }; is_switch( other ) ) {
value = track_uneven::least( value, other->switch_paths_at( point ) );
}
m_switchstray.ends[ path ][ end ] = value;
}
}
}
@@ -217,13 +234,13 @@ void TTrack::settle_switch_stray() const {
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 ]; } );
return least_at_ends( *this, Point, track_uneven::least( paths[ 0 ], paths[ 1 ] ), [ &paths ]( int const Path, int ) { return paths[ Path ]; } );
}
track_uneven::amounts TTrack::switch_uneven_at( glm::dvec3 const &Point ) const {
track_uneven::amounts TTrack::switch_uneven_at( glm::dvec3 const &Point ) {
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 ]; } );
return least_at_ends( *this, Point, track_uneven::least( stray.paths[ 0 ], stray.paths[ 1 ] ), [ &stray ]( int const Path, int const End ) { return stray.ends[ Path ][ End ]; } );
}
double TTrack::joint_depth() const {
@@ -234,7 +251,7 @@ double TTrack::joint_depth() const {
return ( m_uneven.dip >= 0.f ? m_uneven.dip : track_uneven::dip_share * uneven_declared().up );
}
double TTrack::joint_dip( double const Distance ) const {
double TTrack::joint_dip( double const Distance ) {
auto const phase { rail_phase( Distance ) };
if( phase < 0.0 ) {
@@ -245,12 +262,12 @@ double TTrack::joint_dip( double const Distance ) const {
return 0.0;
}
auto const depth { joint_depth() };
auto const atend = [&]( TTrack const *Other ) {
auto const atend = [ this, depth ]( 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 {
double TTrack::rail_phase( double const Distance ) {
if( joint_depth() <= 0.0 || fSoundDistance <= 2.0 * track_uneven::dip_reach ) {
return -1.0;
@@ -261,22 +278,22 @@ double TTrack::rail_phase( double const Distance ) const {
return track_uneven::rail_phase( m_railrun.origin + m_railrun.way * Distance, fSoundDistance );
}
void TTrack::settle_rail_run() const {
void TTrack::settle_rail_run() {
// 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 };
auto const onward = []( TTrack const &Track, int const End, int &Joined ) -> TTrack * {
auto *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 };
auto const walk = [ this, &onward ]( int &End ) {
auto *track { this };
int joined { 0 };
for( int step = 0; step < 1000000; ++step ) {
auto const *next { onward( *track, End, joined ) };
auto *next { onward( *track, End, joined ) };
if( next == nullptr || next == this ) {
break;
}
@@ -285,15 +302,14 @@ void TTrack::settle_rail_run() const {
}
return track; };
auto const place = []( TTrack const &Track, int const End ) {
auto const point { Track.Segment->end_point( End ) };
auto const point { end_point( *Track.Segment, 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 ) ) {
auto *track { walk( end ) };
if( auto *other { walk( otherend ) }; place( *other, otherend ) < place( *track, end ) ) {
track = other;
end = otherend;
}
@@ -327,7 +343,8 @@ void TTrack::share_joints() {
}
}
if( eType != tt_Switch && Segment && ( iCategoryFlag & 1 ) ) {
float roll1, roll2;
float roll1 { 0.f };
float roll2 { 0.f };
Segment->GetRolls( roll1, roll2 );
if( roll1 != 0.f || roll2 != 0.f ) {
iTrapezoid |= 1;

View File

@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
#include <cmath>
#include <sstream>
#include <string>
#include <string_view>
#include <glm/glm.hpp>
#include <glm/gtc/constants.hpp>
@@ -51,18 +52,20 @@ struct wave {
// the numbers of an entry like "uneven 14;35", negative where left out
template <std::size_t Count>
std::array<float, Count> read_fields( std::string const &Text ) {
std::array<float, Count> read_fields( std::string_view const Text ) {
std::array<float, Count> result;
result.fill( -1.f );
std::size_t start { 0 };
for( std::size_t index = 0; index < Count && start < Text.size(); ++index ) {
auto const end { std::min( Text.find( ';', start ), Text.size() ) };
auto value { 0.f };
if( end > start && std::from_chars( Text.data() + start, Text.data() + end, value ).ec == std::errc {} ) {
result[ index ] = std::clamp( value, 0.f, most );
auto rest { Text };
for( auto &field : result ) {
auto const end { std::min( rest.find( ';' ), rest.size() ) };
if( auto value { 0.f }; end > 0 && std::from_chars( rest.data(), rest.data() + end, value ).ec == std::errc {} ) {
field = std::clamp( value, 0.f, most );
}
start = end + 1;
if( end == rest.size() ) {
break;
}
rest.remove_prefix( end + 1 );
}
return result;
}
@@ -100,7 +103,7 @@ struct attribute {
};
// 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 ) {
inline attribute read_attribute( std::string_view const Text, bool const Switch ) {
auto const fields { read_fields<6>( Text ) };
attribute result;
@@ -136,15 +139,15 @@ inline std::string write_attribute( attribute const &Values, bool const Switch )
// 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 ) {
auto const given = []( float const Part, double const Otherwise ) {
return Part >= 0.f ? static_cast<double>( Part ) : Otherwise; };
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 };
given( Values.up, Values.value ),
given( Values.side, Values.value ),
Plain ? given( Values.cross, Values.value ) : 0.0 };
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 );
result.up = given( Values.branchup, given( Values.branch, result.up ) );
result.side = given( Values.branchside, given( Values.branch, result.side ) );
}
return result;
}
@@ -160,8 +163,9 @@ wave_set<Count> make_waves( double const Turn, std::array<double, Count> const &
wave_set<Count> result {};
for( std::size_t i = 0; i < Count; ++i ) {
auto const number { glm::two_pi<double>() / Lengths[ i ] };
auto const heading { Turn + golden * i };
result[ i ] = { number * std::cos( heading ), number * std::sin( heading ), golden * ( i + 1 ) * ( 1.0 + 3.0 * Turn ), Shares[ i ] };
auto const place { static_cast<double>( i ) };
auto const heading { Turn + golden * place };
result[ i ] = { number * std::cos( heading ), number * std::sin( heading ), golden * ( place + 1.0 ) * ( 1.0 + 3.0 * Turn ), Shares[ i ] };
}
return result;
}
@@ -246,7 +250,10 @@ inline glm::dvec3 across_of( glm::dvec3 const &Direction, glm::dvec3 const &Chor
// 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;
if( Switch == Theirswitch ) {
return 0.5f * ( Own + Their );
}
return Switch ? Their : Own;
}
// each component the same way