From 244646803812b334546f601bd4206d85b3aeecf1 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 4 Sep 2017 19:08:59 +0200 Subject: [PATCH 1/3] build 170903. scan table fixes, some const correctness improvements --- Driver.cpp | 95 ++++++++++----------------------- Driver.h | 2 +- DynObj.h | 36 ++++++------- Event.cpp | 2 +- Event.h | 2 +- Model3d.cpp | 2 + PyInt.cpp | 4 +- Segment.cpp | 108 ++++++++++++++++++++++++++------------ Segment.h | 148 ++++++++++++++++++++++++++++++---------------------- Track.cpp | 26 +++++---- Track.h | 10 ++-- TrkFoll.cpp | 14 ++--- TrkFoll.h | 4 +- usefull.h | 19 +++++++ version.h | 2 +- 15 files changed, 254 insertions(+), 220 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 85c35407..6c25c34a 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -32,6 +32,20 @@ http://mozilla.org/MPL/2.0/. #define LOGSTOPS 1 #define LOGBACKSCAN 0 #define LOGPRESS 0 + +// finds point of specified track nearest to specified event. returns: distance to that point from the specified end of the track +// TODO: move this to file with all generic routines, too easy to forget it's here and it may come useful +double +ProjectEventOnTrack( TEvent const *Event, TTrack const *Track, double const Direction ) { + + auto const segment = Track->CurrentSegment(); + auto const nearestpoint = segment->find_nearest_point( Event->PositionGet() ); + return ( + Direction > 0 ? + nearestpoint * segment->GetLength() : // measure from point1 + ( 1.0 - nearestpoint ) * segment->GetLength() ); // measure from point2 +}; + /* Moduł obsługujący sterowanie pojazdami (składami pociągów, samochodami). @@ -106,43 +120,6 @@ std::string StopReasonTable[] = { "Error", // stopError //z powodu błędu w obliczeniu drogi hamowania }; -double GetDistanceToEvent(TTrack* track, TEvent* event, double scan_dir, double start_dist, int iter = 0, bool back = false) -{ - std::shared_ptr segment = track->CurrentSegment(); - vector3 pos_event = event->PositionGet(); - double len1, len2; - double sd = scan_dir; - double seg_len = scan_dir > 0 ? 0.0 : 1.0; - double dzielnik = 1.0 / segment->GetLength();// rozdzielczosc mniej wiecej 1m - int krok = 0; // krok obliczeniowy do sprawdzania czy odwracamy - len2 = (pos_event - segment->FastGetPoint(seg_len)).Length(); - do - { - len1 = len2; - seg_len += scan_dir > 0 ? dzielnik : -dzielnik; - len2 = (pos_event - segment->FastGetPoint(seg_len)).Length(); - krok++; - } while ((len1 > len2) && (seg_len >= dzielnik && (seg_len <= (1 - dzielnik)))); - //trzeba sprawdzić czy seg_len nie osiągnął skrajnych wartości, bo wtedy - // trzeba sprawdzić tor obok - if (1 == krok) - sd = -sd; // jeśli tylko jeden krok tzn, że event przy poprzednim sprawdzaym torze - if (((seg_len <= dzielnik) || (seg_len > (1 - dzielnik))) && (iter < 3)) - { // przejście na inny tor - track = track->Neightbour(int(sd), sd); - start_dist += (1 == krok) ? 0 : back ? -segment->GetLength() : segment->GetLength(); - return GetDistanceToEvent(track, event, sd, start_dist, ++iter, 1 == krok ? true : false); - } - else - { // obliczenie mojego toru - seg_len -= scan_dir > 0 ? dzielnik : -dzielnik; //trzeba wrócić do pozycji len1 - seg_len = scan_dir < 0 ? 1 - seg_len : seg_len; - seg_len = back ? 1 - seg_len : seg_len; // odwracamy jeśli idzie do tyłu - start_dist -= back ? segment->GetLength() : 0; - return start_dist + (segment->GetLength() * seg_len); - } -}; - //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -384,7 +361,7 @@ void TController::TableClear() eSignSkip = nullptr; // nic nie pomijamy }; -TEvent * TController::CheckTrackEvent(double fDirection, TTrack *Track) +TEvent * TController::CheckTrackEvent( TTrack *Track, double const fDirection ) const { // sprawdzanie eventów na podanym torze do podstawowego skanowania TEvent *e = (fDirection > 0) ? Track->evEvent2 : Track->evEvent1; if (!e) @@ -444,7 +421,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) } fTrackLength -= odl_czola_od_wozka; fCurrentDistance = -fLength - fTrackLength; // aktualna odległość ma być ujemna gdyż jesteśmy na końcu składu - fLastVel = pTrack->VelocityGet(); // aktualna prędkość + fLastVel = -1.0; // pTrack->VelocityGet(); // aktualna prędkość // changed to -1 to recognize speed limit, if any sSpeedTable.clear(); iLast = -1; tLast = nullptr; //żaden nie sprawdzony @@ -504,7 +481,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) WriteLog( "Speed table for " + OwnerName() + " tracing through track " + pTrack->NameGet() ); } - if( ( pEvent = CheckTrackEvent( fLastDir, pTrack ) ) != nullptr ) // jeśli jest semafor na tym torze + if( ( pEvent = CheckTrackEvent( pTrack, fLastDir ) ) != nullptr ) // jeśli jest semafor na tym torze { // trzeba sprawdzić tabelkę, bo dodawanie drugi raz tego samego przystanku nie jest korzystne if (TableNotFound(pEvent)) // jeśli nie ma { @@ -514,7 +491,10 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) WriteLog("Speed table for " + OwnerName() + " found new event, " + pEvent->asName); } auto &newspeedpoint = sSpeedTable[iLast]; - if (newspeedpoint.Set(pEvent, GetDistanceToEvent(pTrack, pEvent, fLastDir, fCurrentDistance), OrderCurrentGet())) { + if( newspeedpoint.Set( + pEvent, + fCurrentDistance + ProjectEventOnTrack( pEvent, pTrack, fLastDir ), + OrderCurrentGet() ) ) { fDistance = newspeedpoint.fDist; // jeśli sygnał stop, to nie ma potrzeby dalej skanować SemNextStopIndex = iLast; @@ -604,29 +584,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) if( ( tLast->VelocityGet() > 0 ) && ( ( tLast->VelocityGet() < pTrack->VelocityGet() ) || ( pTrack->VelocityGet() < 0 ) ) ) { -/* - if( ( pTrack->VelocityGet() < 0 ? - tLast->VelocityGet() > 0 : - pTrack->VelocityGet() > tLast->VelocityGet() ) ) { - // jeśli kolejny ma większą prędkość niż poprzedni, to zapamiętać poprzedni (do czasu wyjechania) - if( ( ( ( iLast != -1 ) - && ( TestFlag( sSpeedTable[ iLast ].iFlags, spEnabled | spTrack ) ) ) ? - ( sSpeedTable[ iLast ].trTrack != tLast ) : - true ) ) { - // jeśli nie był dodany do tabelki - if( TableAddNew() ) { - // zapisanie toru z ograniczeniem prędkości - sSpeedTable[ iLast ].Set( - tLast, fCurrentDistance, - ( fLastDir > 0 ? - pTrack->iPrevDirection : - pTrack->iNextDirection ) ? - spEnabled : - spEnabled | spReverse ); - } - } -*/ if( ( iLast != -1 ) && ( sSpeedTable[ iLast ].trTrack == tLast ) ) { // if the track is already in the table we only need to mark it as relevant @@ -658,7 +616,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) if( TableAddNew() ) { // zapisanie ostatniego sprawdzonego toru sSpeedTable[iLast].Set( - tLast, fCurrentDistance, + tLast, fCurrentDistance - fTrackLength, // by now the current distance points to beginning of next track, ( fLastDir < 0 ? spEnabled | spEnd | spReverse : spEnabled | spEnd )); @@ -692,9 +650,10 @@ void TController::TableCheck(double fDistance) } else if (iTableDirection) { // trzeba sprawdzić, czy coś się zmieniło - for (auto &sp : sSpeedTable) - { - sp.UpdateDistance(MoveDistanceGet()); // aktualizacja odległości dla wszystkich pozycji tabeli + auto const distance = MoveDistanceGet(); + for (auto &sp : sSpeedTable) { + // aktualizacja odległości dla wszystkich pozycji tabeli + sp.UpdateDistance(distance); } MoveDistanceReset(); // kasowanie odległości po aktualizacji tabelki for( int i = 0; i < iLast; ++i ) @@ -4245,7 +4204,7 @@ TController::UpdateSituation(double dt) { 0 : 1 ); // sprzęg z przodu składu if( ( coupler->Connected ) - && ( coupler->CouplingFlag == 0 ) ) { + && ( coupler->CouplingFlag == 0 ) ) { // mamy coś z przodu podłączone sprzęgiem wirtualnym // wyliczanie optymalnego przyspieszenia do jazdy na widoczność ActualProximityDist = std::min( diff --git a/Driver.h b/Driver.h index dfb24ec8..1d6e410e 100644 --- a/Driver.h +++ b/Driver.h @@ -369,7 +369,7 @@ private: // parametry aktualnego składu // double Distance(vector3 &p1, vector3 &n, vector3 &p2); private: // Ra: metody obsługujące skanowanie toru - TEvent *CheckTrackEvent(double fDirection, TTrack *Track); + TEvent *CheckTrackEvent(TTrack *Track, double const fDirection ) const; // bool TableCheckEvent(TEvent *e); bool TableAddNew(); bool TableNotFound(TEvent const *Event) const; diff --git a/DynObj.h b/DynObj.h index 88c9e0ea..04f4bc97 100644 --- a/DynObj.h +++ b/DynObj.h @@ -78,7 +78,7 @@ class TAnimValveGear class TAnimPant { // współczynniki do animacji pantografu public: - vector3 vPos; // Ra: współrzędne punktu zerowego pantografu (X dodatnie dla przedniego) + Math3D::vector3 vPos; // Ra: współrzędne punktu zerowego pantografu (X dodatnie dla przedniego) double fLenL1; // długość dolnego ramienia 1, odczytana z modelu double fLenU1; // długość górnego ramienia 1, odczytana z modelu double fLenL2; // długość dolnego ramienia 2, odczytana z modelu @@ -152,9 +152,9 @@ class TDynamicObject { // klasa pojazdu friend class opengl_renderer; private: // położenie pojazdu w świecie oraz parametry ruchu - vector3 vPosition; // Ra: pozycja pojazdu liczona zaraz po przesunięciu - vector3 vCoulpler[ 2 ]; // współrzędne sprzęgów do liczenia zderzeń czołowych - vector3 vUp, vFront, vLeft; // wektory jednostkowe ustawienia pojazdu + Math3D::vector3 vPosition; // Ra: pozycja pojazdu liczona zaraz po przesunięciu + Math3D::vector3 vCoulpler[ 2 ]; // współrzędne sprzęgów do liczenia zderzeń czołowych + Math3D::vector3 vUp, vFront, vLeft; // wektory jednostkowe ustawienia pojazdu int iDirection; // kierunek pojazdu względem czoła składu (1=zgodny,0=przeciwny) TTrackShape ts; // parametry toru przekazywane do fizyki TTrackParam tp; // parametry toru przekazywane do fizyki @@ -163,14 +163,14 @@ private: // położenie pojazdu w świecie oraz parametry ruchu int iAxleFirst; // numer pierwszej osi w kierunku ruchu (oś wiążąca pojazd z torem i wyzwalająca // eventy) float fAxleDist; // rozstaw wózków albo osi do liczenia proporcji zacienienia - vector3 modelRot; // obrot pudła względem świata - do przeanalizowania, czy potrzebne!!! + Math3D::vector3 modelRot; // obrot pudła względem świata - do przeanalizowania, czy potrzebne!!! // bool bCameraNear; //blisko kamer są potrzebne dodatkowe obliczenia szczegółów TDynamicObject * ABuFindNearestObject( TTrack *Track, TDynamicObject *MyPointer, int &CouplNr ); public: // parametry położenia pojazdu dostępne publicznie std::string asTrack; // nazwa toru początkowego; wywalić? std::string asDestination; // dokąd pojazd ma być kierowany "(stacja):(tor)" - matrix4x4 mMatrix; // macierz przekształcenia do renderowania modeli + Math3D::matrix4x4 mMatrix; // macierz przekształcenia do renderowania modeli TMoverParameters *MoverParameters; // parametry fizyki ruchu oraz przeliczanie // TMoverParameters *pControlled; //wskaźnik do sterowanego członu silnikowego TDynamicObject *NextConnected; // pojazd podłączony od strony sprzęgu 1 (kabina -1) @@ -237,7 +237,7 @@ public: // modele składowe pojazdu void toggle_lights(); // switch light levels for registered interior sections private: // Ra: ciąg dalszy animacji, dopiero do ogarnięcia // ABuWozki 060504 - vector3 bogieRot[2]; // Obroty wozkow w/m korpusu + Math3D::vector3 bogieRot[2]; // Obroty wozkow w/m korpusu TSubModel *smBogie[2]; // Wyszukiwanie max 2 wozkow TSubModel *smWahacze[4]; // wahacze (np. nogi, dźwignia w drezynie) TSubModel *smBrakeMode; // Ra 15-01: nastawa hamulca też @@ -249,7 +249,7 @@ public: // modele składowe pojazdu TSubModel *smBuforLewy[2]; TSubModel *smBuforPrawy[2]; TAnimValveGear *pValveGear; - vector3 vFloor; // podłoga dla ładunku + Math3D::vector3 vFloor; // podłoga dla ładunku public: TAnim *pants; // indeks obiektu animującego dla pantografu 0 double NoVoltTime; // czas od utraty zasilania @@ -264,7 +264,7 @@ public: // modele składowe pojazdu void ABuLittleUpdate(double ObjSqrDist); bool btnOn; // ABu: czy byly uzywane buttony, jesli tak, to po renderingu wylacz // bo ten sam model moze byc jeszcze wykorzystany przez inny obiekt! - double ComputeRadius(vector3 p1, vector3 p2, vector3 p3, vector3 p4); + double ComputeRadius( Math3D::vector3 p1, Math3D::vector3 p2, Math3D::vector3 p3, Math3D::vector3 p4); TButton btCoupler1; // sprzegi TButton btCoupler2; @@ -337,7 +337,7 @@ public: // modele składowe pojazdu double eng_turbo; void ABuBogies(); void ABuModelRoll(); - vector3 modelShake; + Math3D::vector3 modelShake; bool renderme; // yB - czy renderowac // TRealSound sBrakeAcc; //dźwięk przyspieszacza @@ -406,7 +406,7 @@ public: // modele składowe pojazdu int &CouplNr); TDynamicObject * GetFirstDynamic(int cpl_type, int cf = 1); // TDynamicObject* GetFirstCabDynamic(int cpl_type); - void ABuSetModelShake(vector3 mShake); + void ABuSetModelShake( Math3D::vector3 mShake); // McZapkie-010302 TController *Mechanik; @@ -437,31 +437,31 @@ public: // modele składowe pojazdu void Move(double fDistance); void FastMove(double fDistance); void RenderSounds(); - inline vector3 GetPosition() const + inline Math3D::vector3 GetPosition() const { return vPosition; }; - inline vector3 HeadPosition() + inline Math3D::vector3 HeadPosition() { return vCoulpler[iDirection ^ 1]; }; // pobranie współrzędnych czoła - inline vector3 RearPosition() + inline Math3D::vector3 RearPosition() { return vCoulpler[iDirection]; }; // pobranie współrzędnych tyłu - inline vector3 AxlePositionGet() + inline Math3D::vector3 AxlePositionGet() { return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; }; - inline vector3 VectorFront() const + inline Math3D::vector3 VectorFront() const { return vFront; }; - inline vector3 VectorUp() + inline Math3D::vector3 VectorUp() { return vUp; }; - inline vector3 VectorLeft() const + inline Math3D::vector3 VectorLeft() const { return vLeft; }; diff --git a/Event.cpp b/Event.cpp index 1038d1be..42ee01c3 100644 --- a/Event.cpp +++ b/Event.cpp @@ -673,7 +673,7 @@ double TEvent::ValueGet(int n) return 0.0; // inne eventy się nie liczą }; -vector3 TEvent::PositionGet() +vector3 TEvent::PositionGet() const { // pobranie współrzędnych eventu switch (Type) { // diff --git a/Event.h b/Event.h index 4eaebfe1..663efef3 100644 --- a/Event.h +++ b/Event.h @@ -108,7 +108,7 @@ class TEvent // zmienne: ev* std::string CommandGet(); TCommandType Command(); double ValueGet(int n); - vector3 PositionGet(); + vector3 PositionGet() const; bool StopCommand(); void StopCommandSent(); void Append(TEvent *e); diff --git a/Model3d.cpp b/Model3d.cpp index 41834753..2b74dd34 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1489,6 +1489,8 @@ void TModel3d::deserialize(std::istream &s, size_t size, bool dynamic) */ // we rely on the SUB chunk coming before the vertex data, and on the overall vertex count matching the size of data in the chunk. // geometry associated with chunks isn't stored in the same order as the chunks themselves, so we need to sort that out first + if( Root == nullptr ) + throw std::runtime_error( "e3d: VNT chunk encountered before SUB chunk" ); std::vector< std::pair > submodeloffsets; // vertex data offset, submodel index submodeloffsets.reserve( iSubModelsCount ); for( int submodelindex = 0; submodelindex < iSubModelsCount; ++submodelindex ) { diff --git a/PyInt.cpp b/PyInt.cpp index 6d91a24b..3bfbade0 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -64,10 +64,10 @@ bool TPythonInterpreter::loadClassFile( std::string const &lookupPath, std::stri if (sourceFile != nullptr) { fseek(sourceFile, 0, SEEK_END); - long fsize = ftell(sourceFile); + auto const fsize = ftell(sourceFile); char *buffer = (char *)calloc(fsize + 1, sizeof(char)); fseek(sourceFile, 0, SEEK_SET); - size_t freaded = fread(buffer, sizeof(char), fsize, sourceFile); + auto const freaded = fread(buffer, sizeof(char), fsize, sourceFile); buffer[freaded] = 0; // z jakiegos powodu czytamy troche mniej i trzczeba dodac konczace // zero do bufora (mimo ze calloc teoretycznie powiniene zwrocic // wyzerowana pamiec) diff --git a/Segment.cpp b/Segment.cpp index 19a0fa15..41527efd 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -21,20 +21,17 @@ http://mozilla.org/MPL/2.0/. // 110806 Ra: odwrócone mapowanie wzdłuż - Point1 == 1.0 TSegment::TSegment(TTrack *owner) : - pOwner( owner ) -{ - fAngle[ 0 ] = 0.0; - fAngle[ 1 ] = 0.0; -}; + pOwner( owner ) +{} TSegment::~TSegment() { SafeDeleteArray(fTsBuffer); -}; +} -bool TSegment::Init(vector3 NewPoint1, vector3 NewPoint2, double fNewStep, double fNewRoll1, double fNewRoll2) +bool TSegment::Init(Math3D::vector3 NewPoint1, Math3D::vector3 NewPoint2, double fNewStep, double fNewRoll1, double fNewRoll2) { // wersja dla prostego - wyliczanie punktów kontrolnych - vector3 dir; + Math3D::vector3 dir; // NOTE: we're enforcing division also for straight track, to ensure dense enough mesh for per-vertex lighting /* @@ -55,9 +52,7 @@ bool TSegment::Init(vector3 NewPoint1, vector3 NewPoint2, double fNewStep, doubl } }; -bool TSegment::Init(vector3 &NewPoint1, vector3 NewCPointOut, vector3 NewCPointIn, - vector3 &NewPoint2, double fNewStep, double fNewRoll1, double fNewRoll2, - bool bIsCurve) +bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, Math3D::vector3 NewCPointIn, Math3D::vector3 &NewPoint2, double fNewStep, double fNewRoll1, double fNewRoll2, bool bIsCurve) { // wersja uniwersalna (dla krzywej i prostego) Point1 = NewPoint1; CPointOut = NewCPointOut; @@ -77,7 +72,7 @@ bool TSegment::Init(vector3 &NewPoint1, vector3 NewCPointOut, vector3 NewCPointI // mieć moment wypoziomowania, ale musi on być również podniesiony. if (fRoll1 != 0.0) { // tylko jeśli jest przechyłka - double w1 = fabs(sin(fRoll1) * 0.75); // 0.5*w2+0.0325; //0.75m dla 1.435 + double w1 = std::abs(std::sin(fRoll1) * 0.75); // 0.5*w2+0.0325; //0.75m dla 1.435 Point1.y += w1; // modyfikacja musi być przed policzeniem dalszych parametrów if (bCurve) CPointOut.y += w1; // prosty ma wektory jednostkowe @@ -85,7 +80,7 @@ bool TSegment::Init(vector3 &NewPoint1, vector3 NewCPointOut, vector3 NewCPointI } if (fRoll2 != 0.0) { - double w2 = fabs(sin(fRoll2) * 0.75); // 0.5*w2+0.0325; //0.75m dla 1.435 + double w2 = std::abs(std::sin(fRoll2) * 0.75); // 0.5*w2+0.0325; //0.75m dla 1.435 Point2.y += w2; // modyfikacja musi być przed policzeniem dalszych parametrów if (bCurve) CPointIn.y += w2; // prosty ma wektory jednostkowe @@ -137,12 +132,12 @@ bool TSegment::Init(vector3 &NewPoint1, vector3 NewCPointOut, vector3 NewCPointI return true; } -vector3 TSegment::GetFirstDerivative(double fTime) +Math3D::vector3 TSegment::GetFirstDerivative(double const fTime) const { double fOmTime = 1.0 - fTime; double fPowTime = fTime; - vector3 kResult = fOmTime * (CPointOut - Point1); + Math3D::vector3 kResult = fOmTime * (CPointOut - Point1); // int iDegreeM1 = 3 - 1; @@ -156,7 +151,7 @@ vector3 TSegment::GetFirstDerivative(double fTime) return kResult; } -double TSegment::RombergIntegral(double fA, double fB) +double TSegment::RombergIntegral(double const fA, double const fB) const { double fH = fB - fA; @@ -188,7 +183,7 @@ double TSegment::RombergIntegral(double fA, double fB) return ms_apfRom[0][ms_iOrder - 1]; } -double TSegment::GetTFromS(double s) +double TSegment::GetTFromS(double const s) const { // initial guess for Newton's method double fTolerance = 0.001; @@ -216,41 +211,83 @@ double TSegment::GetTFromS(double s) return fTime; }; -vector3 TSegment::RaInterpolate(double t) +Math3D::vector3 TSegment::RaInterpolate(double const t) const { // wyliczenie XYZ na krzywej Beziera z użyciem współczynników return t * (t * (t * vA + vB) + vC) + Point1; // 9 mnożeń, 9 dodawań }; -vector3 TSegment::RaInterpolate0(double t) +Math3D::vector3 TSegment::RaInterpolate0(double const t) const { // wyliczenie XYZ na krzywej Beziera, na użytek liczenia długości nie jest dodawane Point1 return t * (t * (t * vA + vB) + vC); // 9 mnożeń, 6 dodawań }; -double TSegment::ComputeLength() // McZapkie-150503: dlugosc miedzy punktami krzywej +double TSegment::ComputeLength() const // McZapkie-150503: dlugosc miedzy punktami krzywej { // obliczenie długości krzywej Beziera za pomocą interpolacji odcinkami // Ra: zamienić na liczenie rekurencyjne średniej z cięciwy i łamanej po kontrolnych // Ra: koniec rekurencji jeśli po podziale suma długości nie różni się więcej niż 0.5mm od // poprzedniej // Ra: ewentualnie rozpoznać łuk okręgu płaskiego i liczyć ze wzoru na długość łuku double t, l = 0; - vector3 last = vector3(0, 0, 0); // długość liczona po przesunięciu odcinka do początku układu - vector3 tmp = Point2 - Point1; + Math3D::vector3 last = Math3D::vector3(0, 0, 0); // długość liczona po przesunięciu odcinka do początku układu + Math3D::vector3 tmp = Point2 - Point1; int m = 20.0 * tmp.Length(); // było zawsze do 10000, teraz jest liczone odcinkami po około 5cm for (int i = 1; i <= m; i++) { t = double(i) / double(m); // wyznaczenie parametru na krzywej z przedziału (0,1> // tmp=Interpolate(t,p1,cp1,cp2,p2); tmp = RaInterpolate0(t); // obliczenie punktu dla tego parametru - t = vector3(tmp - last).Length(); // obliczenie długości wektora + t = Math3D::vector3(tmp - last).Length(); // obliczenie długości wektora l += t; // zwiększenie wyliczanej długości last = tmp; } return (l); } +// finds point on segment closest to specified point in 3d space. returns: point on segment as value in range 0-1 +double +TSegment::find_nearest_point( glm::dvec3 const &Point ) const { + + if( ( false == bCurve ) || ( iSegCount == 1 ) ) { + // for straight track just treat it as a single segment + return + nearest_segment_point( + glm::dvec3{ FastGetPoint_0() }, + glm::dvec3{ FastGetPoint_1() }, + Point ); + } + else { + // for curves iterate through segment chunks, and find the one which gives us the least distance to the specified point + double distance = std::numeric_limits::max(); + double nearest; + // NOTE: we're reusing already created segment chunks, which are created based on splinefidelity setting + // this means depending on splinefidelity the results can be potentially slightly different + for( int segmentidx = 0; segmentidx < iSegCount; ++segmentidx ) { + + auto const segmentpoint = + clamp( + nearest_segment_point( + glm::dvec3{ FastGetPoint( fTsBuffer[ segmentidx ] ) }, + glm::dvec3{ FastGetPoint( fTsBuffer[ segmentidx + 1 ] ) }, + Point ) // point in range 0-1 on current segment + * ( fTsBuffer[ segmentidx + 1 ] - fTsBuffer[ segmentidx ] ) // segment length + + fTsBuffer[ segmentidx ], // segment offset + 0.0, 1.0 ); // we clamp the range in case there's some floating point math inaccuracies + + auto const segmentdistance = glm::length2( Point - glm::dvec3{ FastGetPoint( segmentpoint ) } ); + if( segmentdistance < distance ) { + + nearest = segmentpoint; + distance = segmentdistance; + } + } + // + return nearest; + } +} + const double fDirectionOffset = 0.1; // długość wektora do wyliczenia kierunku -vector3 TSegment::GetDirection(double fDistance) +Math3D::vector3 TSegment::GetDirection(double const fDistance) const { // takie toporne liczenie pochodnej dla podanego dystansu od Point1 double t1 = GetTFromS(fDistance - fDirectionOffset); if (t1 <= 0.0) @@ -261,7 +298,7 @@ vector3 TSegment::GetDirection(double fDistance) return (FastGetPoint(t2) - FastGetPoint(t1)); } -vector3 TSegment::FastGetDirection(double fDistance, double fOffset) +Math3D::vector3 TSegment::FastGetDirection(double fDistance, double fOffset) { // takie toporne liczenie pochodnej dla parametru 0.0÷1.0 double t1 = fDistance - fOffset; if (t1 <= 0.0) @@ -272,7 +309,7 @@ vector3 TSegment::FastGetDirection(double fDistance, double fOffset) return (FastGetPoint(t2) - FastGetPoint(t1)); } -vector3 TSegment::GetPoint(double fDistance) +Math3D::vector3 TSegment::GetPoint(double const fDistance) const { // wyliczenie współrzędnych XYZ na torze w odległości (fDistance) od Point1 if (bCurve) { // można by wprowadzić uproszczony wzór dla okręgów płaskich @@ -287,7 +324,7 @@ vector3 TSegment::GetPoint(double fDistance) } }; -void TSegment::RaPositionGet(double fDistance, vector3 &p, vector3 &a) +void TSegment::RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const { // ustalenie pozycji osi na torze, przechyłki, pochylenia i kierunku jazdy if (bCurve) { // można by wprowadzić uproszczony wzór dla okręgów płaskich @@ -309,13 +346,16 @@ void TSegment::RaPositionGet(double fDistance, vector3 &p, vector3 &a) } }; -vector3 TSegment::FastGetPoint(double t) +Math3D::vector3 TSegment::FastGetPoint(double const t) const { // return (bCurve?Interpolate(t,Point1,CPointOut,CPointIn,Point2):((1.0-t)*Point1+(t)*Point2)); - return (bCurve ? RaInterpolate(t) : ((1.0 - t) * Point1 + (t)*Point2)); + return ( + ( ( true == bCurve ) || ( iSegCount == 1 ) ) ? + RaInterpolate( t ) : + interpolate( Point1, Point2, t ) ); } -bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, const vector6 *ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale, int iSkip, int iEnd, double fOffsetX, vector3 **p, bool bRender) +bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, const vector6 *ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale, int iSkip, int iEnd, double fOffsetX, Math3D::vector3 **p, bool bRender) { // generowanie trójkątów dla odcinka trajektorii ruchu // standardowo tworzy triangle_strip dla prostego albo ich zestaw dla łuku // po modyfikacji - dla ujemnego (iNumShapePoints) w dodatkowych polach tabeli @@ -325,7 +365,7 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, if( !fTsBuffer ) return false; // prowizoryczne zabezpieczenie przed wysypem - ustalić faktyczną przyczynę - vector3 pos1, pos2, dir, parallel1, parallel2, pt, norm; + Math3D::vector3 pos1, pos2, dir, parallel1, parallel2, pt, norm; double s, step, fOffset, tv1, tv2, t, fEnd; bool const trapez = iNumShapePoints < 0; // sygnalizacja trapezowatości iNumShapePoints = std::abs( iNumShapePoints ); @@ -341,7 +381,7 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, fOffset = 0.1 / fLength; // pierwsze 10cm pos1 = FastGetPoint( t ); // wektor początku segmentu dir = FastGetDirection( t, fOffset ); // wektor kierunku - parallel1 = Normalize( vector3( -dir.z, 0.0, dir.x ) ); // wektor poprzeczny + parallel1 = Normalize( Math3D::vector3( -dir.z, 0.0, dir.x ) ); // wektor poprzeczny if( iEnd == 0 ) iEnd = iSegCount; fEnd = fLength * double( iEnd ) / double( iSegCount ); @@ -371,7 +411,7 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, t = fTsBuffer[ i ]; // szybsze od GetTFromS(s); pos2 = FastGetPoint( t ); dir = FastGetDirection( t, fOffset ); // nowy wektor kierunku - parallel2 = Normalize( vector3( -dir.z, 0.0, dir.x ) ); // wektor poprzeczny + parallel2 = Normalize( Math3D::vector3( -dir.z, 0.0, dir.x ) ); // wektor poprzeczny if( trapez ) { for( int j = 0; j < iNumShapePoints; ++j ) { @@ -453,7 +493,7 @@ bool TSegment::RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, void TSegment::Render() { - vector3 pt; + Math3D::vector3 pt; GfxRenderer.Bind_Material( null_handle ); if (bCurve) diff --git a/Segment.h b/Segment.h index 90d0a6a8..618132da 100644 --- a/Segment.h +++ b/Segment.h @@ -7,24 +7,19 @@ obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef SegmentH -#define SegmentH -/* -#include "VBO.h" -*/ +#pragma once + #include "openglgeometrybank.h" #include "dumb3d.h" #include "Classes.h" #include "usefull.h" -using namespace Math3D; - // 110405 Ra: klasa punktów przekroju z normalnymi -class vector6 : public vector3 +class vector6 : public Math3D::vector3 { // punkt przekroju wraz z wektorem normalnym public: - vector3 n; + Math3D::vector3 n; vector6() { x = y = z = n.x = n.z = 0.0; @@ -54,7 +49,7 @@ class vector6 : public vector3 class TSegment { // aproksymacja toru (zwrotnica ma dwa takie, jeden z nich jest aktywny) private: - vector3 Point1, CPointOut, CPointIn, Point2; + Math3D::vector3 Point1, CPointOut, CPointIn, Point2; double fRoll1 = 0.0, fRoll2 = 0.0; // przechyłka na końcach double fLength = 0.0; // długość policzona @@ -63,63 +58,92 @@ class TSegment int iSegCount = 0; // ilość odcinków do rysowania krzywej double fDirection = 0.0; // Ra: kąt prostego w planie; dla łuku kąt od Point1 double fStoop = 0.0; // Ra: kąt wzniesienia; dla łuku od Point1 - vector3 vA, vB, vC; // współczynniki wielomianów trzeciego stopnia vD==Point1 + Math3D::vector3 vA, vB, vC; // współczynniki wielomianów trzeciego stopnia vD==Point1 TTrack *pOwner = nullptr; // wskaźnik na właściciela - double fAngle[2]; // kąty zakończenia drogi na przejazdach + double fAngle[ 2 ] = { 0.0, 0.0 }; // kąty zakończenia drogi na przejazdach - vector3 GetFirstDerivative(double fTime); - double RombergIntegral(double fA, double fB); - double GetTFromS(double s); - vector3 RaInterpolate(double t); - vector3 RaInterpolate0(double t); - public: + Math3D::vector3 + GetFirstDerivative(double const fTime) const; + double + RombergIntegral(double const fA, double const fB) const; + double + GetTFromS(double const s) const; + Math3D::vector3 + RaInterpolate(double const t) const; + Math3D::vector3 + RaInterpolate0(double const t) const; + +public: bool bCurve = false; + TSegment(TTrack *owner); ~TSegment(); - bool Init(vector3 NewPoint1, vector3 NewPoint2, double fNewStep, double fNewRoll1 = 0, double fNewRoll2 = 0); - bool Init(vector3 &NewPoint1, vector3 NewCPointOut, vector3 NewCPointIn, vector3 &NewPoint2, - double fNewStep, double fNewRoll1 = 0, double fNewRoll2 = 0, bool bIsCurve = true); - inline double ComputeLength(); // McZapkie-150503 - inline vector3 GetDirection1() { - return bCurve ? CPointOut - Point1 : CPointOut; }; - inline vector3 GetDirection2() { - return bCurve ? CPointIn - Point2 : CPointIn; }; - vector3 GetDirection(double fDistance); - vector3 GetDirection() { - return CPointOut; }; - vector3 FastGetDirection(double fDistance, double fOffset); - vector3 GetPoint(double fDistance); - void RaPositionGet(double fDistance, vector3 &p, vector3 &a); - vector3 FastGetPoint(double t); - inline vector3 FastGetPoint_0() { - return Point1; }; - inline vector3 FastGetPoint_1() { - return Point2; }; - inline double GetRoll(double const Distance) { - return interpolate( fRoll1, fRoll2, Distance / fLength ); } - void GetRolls(double &r1, double &r2) { - // pobranie przechyłek (do generowania trójkątów) - r1 = fRoll1; - r2 = fRoll2; } - bool RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, vector6 const *ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, double fOffsetX = 0.0, vector3 **p = nullptr, bool bRender = true); - void Render(); - inline double GetLength() { - return fLength; }; - void MoveMe(vector3 pPosition) - { - Point1 += pPosition; - Point2 += pPosition; - if (bCurve) - { - CPointIn += pPosition; - CPointOut += pPosition; - } - } - int RaSegCount() { - return fTsBuffer ? iSegCount : 1; }; - void AngleSet(int i, double a) { - fAngle[i] = a; }; + bool + Init( Math3D::vector3 NewPoint1, Math3D::vector3 NewPoint2, double fNewStep, double fNewRoll1 = 0, double fNewRoll2 = 0); + bool + Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, Math3D::vector3 NewCPointIn, Math3D::vector3 &NewPoint2, double fNewStep, double fNewRoll1 = 0, double fNewRoll2 = 0, bool bIsCurve = true); + double + ComputeLength() const; // McZapkie-150503 + // finds point on segment closest to specified point in 3d space. returns: point on segment as value in range 0-1 + double + find_nearest_point( glm::dvec3 const &Point ) const; + inline + Math3D::vector3 + GetDirection1() const { + return bCurve ? CPointOut - Point1 : CPointOut; }; + inline + Math3D::vector3 + GetDirection2() const { + return bCurve ? CPointIn - Point2 : CPointIn; }; + Math3D::vector3 + GetDirection(double const fDistance) const; + inline + Math3D::vector3 + GetDirection() const { + return CPointOut; }; + Math3D::vector3 + FastGetDirection(double const fDistance, double const fOffset); + Math3D::vector3 + GetPoint(double const fDistance) const; + void + RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const; + Math3D::vector3 + FastGetPoint(double const t) const; + inline + Math3D::vector3 + FastGetPoint_0() const { + return Point1; }; + inline + Math3D::vector3 + FastGetPoint_1() const { + return Point2; }; + inline + double + GetRoll(double const Distance) const { + return interpolate( fRoll1, fRoll2, Distance / fLength ); } + inline + void + GetRolls(double &r1, double &r2) const { + // pobranie przechyłek (do generowania trójkątów) + r1 = fRoll1; + r2 = fRoll2; } + + bool + RenderLoft( vertex_array &Output, Math3D::vector3 const &Origin, vector6 const *ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, double fOffsetX = 0.0, Math3D::vector3 **p = nullptr, bool bRender = true); + void + Render(); + inline + double + GetLength() const { + return fLength; }; + inline + int + RaSegCount() const { + return fTsBuffer ? iSegCount : 1; }; + inline + void + AngleSet(int const i, double const a) { + fAngle[i] = a; }; }; //--------------------------------------------------------------------------- -#endif diff --git a/Track.cpp b/Track.cpp index bee712aa..6814d710 100644 --- a/Track.cpp +++ b/Track.cpp @@ -268,32 +268,30 @@ TTrack * TTrack::NullCreate(int dir) trk2->fRadius = 20.0; // promień, aby się dodawało do tabelki prędkości i liczyło // narastająco trk2->Init(); // utworzenie segmentu + trk->pMyNode->asName = pMyNode->asName + ":loopstart"; + trk2->pMyNode->asName = pMyNode->asName + ":loopfinish"; switch (dir) { //łączenie z nowym torem case 0: p1 = Segment->FastGetPoint_0(); cv1 = -20.0 * Normalize(Segment->GetDirection1()); // pierwszy wektor kontrolny p2 = p1 + cv1 + cv1; // 40m - trk->Segment->Init(p1, p1 + cv1, p2 + vector3(-cv1.z, cv1.y, cv1.x), p2, 2, - -RadToDeg(r1), - 0.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk->Segment->Init(p1, p1 + cv1, p2 + vector3(-cv1.z, cv1.y, cv1.x), p2, 2, -RadToDeg(r1), 0.0); ConnectPrevPrev(trk, 0); - trk2->Segment->Init(p1, p1 + cv1, p2 + vector3(cv1.z, cv1.y, -cv1.x), p2, 2, - -RadToDeg(r1), - 0.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk2->Segment->Init(p1, p1 + cv1, p2 + vector3(cv1.z, cv1.y, -cv1.x), p2, 2, -RadToDeg(r1), 0.0); trk2->iPrevDirection = 0; // zwrotnie do tego samego odcinka break; case 1: p1 = Segment->FastGetPoint_1(); cv1 = -20.0 * Normalize(Segment->GetDirection2()); // pierwszy wektor kontrolny p2 = p1 + cv1 + cv1; - trk->Segment->Init(p1, p1 + cv1, p2 + vector3(-cv1.z, cv1.y, cv1.x), p2, 2, - RadToDeg(r2), - 0.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk->Segment->Init(p1, p1 + cv1, p2 + vector3(-cv1.z, cv1.y, cv1.x), p2, 2, RadToDeg(r2), 0.0); ConnectNextPrev(trk, 0); - trk2->Segment->Init(p1, p1 + cv1, p2 + vector3(cv1.z, cv1.y, -cv1.x), p2, 2, - RadToDeg(r2), - 0.0); // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + // bo prosty, kontrolne wyliczane przy zmiennej przechyłce + trk2->Segment->Init(p1, p1 + cv1, p2 + vector3(cv1.z, cv1.y, -cv1.x), p2, 2, RadToDeg(r2), 0.0); trk2->iPrevDirection = 1; // zwrotnie do tego samego odcinka break; } @@ -870,7 +868,7 @@ bool TTrack::AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEv } else { if( evEventall1 == nullptr ) { - evEventall1 = NewEvent0; + evEventall1 = NewEvent1; asEventall1Name = ""; iEvents |= 16; // sumaryczna informacja o eventach } @@ -888,7 +886,7 @@ bool TTrack::AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEv } else { if( evEventall2 == nullptr ) { - evEventall2 = NewEvent0; + evEventall2 = NewEvent2; asEventall2Name = ""; iEvents |= 32; // sumaryczna informacja o eventach } diff --git a/Track.h b/Track.h index af635859..6398b5d0 100644 --- a/Track.h +++ b/Track.h @@ -77,7 +77,7 @@ class TSwitchExtension struct { // zmienne dla skrzyżowania int iRoads; // ile dróg się spotyka? - vector3 *vPoints; // tablica wierzchołków nawierzchni, generowana przez pobocze + Math3D::vector3 *vPoints; // tablica wierzchołków nawierzchni, generowana przez pobocze // int iPoints; // liczba faktycznie użytych wierzchołków nawierzchni bool bPoints; // czy utworzone? }; @@ -90,7 +90,7 @@ class TSwitchExtension TEvent *evPlus = nullptr, *evMinus = nullptr; // zdarzenia sygnalizacji rozprucia float fVelocity = -1.0; // maksymalne ograniczenie prędkości (ustawianej eventem) - vector3 vTrans; // docelowa translacja przesuwnicy + Math3D::vector3 vTrans; // docelowa translacja przesuwnicy private: }; @@ -199,7 +199,7 @@ public: void ConnectNextNext(TTrack *pNewNext, int typ); inline double Length() { return Segment->GetLength(); }; - inline std::shared_ptr CurrentSegment() { + inline std::shared_ptr CurrentSegment() const { return Segment; }; inline TTrack * CurrentNext() { return (trNext); }; @@ -215,7 +215,7 @@ public: SwitchExtension ? SwitchExtension->CurrentIndex : -1); }; - void Load(cParser *parser, vector3 pOrigin, std::string name); + void Load(cParser *parser, Math3D::vector3 pOrigin, std::string name); bool AssignEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2); bool AssignallEvents(TEvent *NewEvent0, TEvent *NewEvent1, TEvent *NewEvent2); bool AssignForcedEvents(TEvent *NewEventPlus, TEvent *NewEventMinus); @@ -247,7 +247,7 @@ public: m_material1 : m_material2 ); }; bool IsGroupable(); - int TestPoint(vector3 *Point); + int TestPoint( Math3D::vector3 *Point); void MovedUp1(float const dh); std::string NameGet(); void VelocitySet(float v); diff --git a/TrkFoll.cpp b/TrkFoll.cpp index 7e9007d3..c48ecac6 100644 --- a/TrkFoll.cpp +++ b/TrkFoll.cpp @@ -71,19 +71,11 @@ TTrack * TTrackFollower::SetCurrentTrack(TTrack *pTrack, int end) } break; } - if (!pTrack) - { // gdy nie ma toru w kierunku jazdy - pTrack = pCurrentTrack->NullCreate( - end); // tworzenie toru wykolejącego na przedłużeniu pCurrentTrack + if (!pTrack) { + // gdy nie ma toru w kierunku jazdy tworzenie toru wykolejącego na przedłużeniu pCurrentTrack + pTrack = pCurrentTrack->NullCreate(end); if (!end) // jeśli dodana od strony zero, to zmiana kierunku fDirection = -fDirection; // wtórna zmiana - // if (pTrack->iCategoryFlag&2) - //{//jeśli samochód, zepsuć na miejscu - // Owner->MoverParameters->V=0; //zatrzymać - // Owner->MoverParameters->Power=0; //ukraść silnik - // Owner->MoverParameters->AccS=0; //wchłonąć moc - // Global::iPause|=1; //zapauzowanie symulacji - //} } else { // najpierw +1, później -1, aby odcinek izolowany wspólny dla tych torów nie wykrył zera diff --git a/TrkFoll.h b/TrkFoll.h index 5723ccb1..87bc8312 100644 --- a/TrkFoll.h +++ b/TrkFoll.h @@ -28,8 +28,8 @@ class TTrackFollower // zwrotnicy pod taborem) public: double fOffsetH = 0.0; // Ra: odległość środka osi od osi toru (dla samochodów) - użyć do wężykowania - vector3 pPosition; // współrzędne XYZ w układzie scenerii - vector3 vAngles; // x:przechyłka, y:pochylenie, z:kierunek w planie (w radianach) + Math3D::vector3 pPosition; // współrzędne XYZ w układzie scenerii + Math3D::vector3 vAngles; // x:przechyłka, y:pochylenie, z:kierunek w planie (w radianach) TTrackFollower() = default; ~TTrackFollower(); TTrack * SetCurrentTrack(TTrack *pTrack, int end); diff --git a/usefull.h b/usefull.h index 0c4c398d..8c5359ca 100644 --- a/usefull.h +++ b/usefull.h @@ -95,4 +95,23 @@ bounding_box( VecType_ &Mincorner, VecType_ &Maxcorner, Iterator_ First, Iterato Maxcorner = glm::max( Maxcorner, VecType_{ point } ); } ); } +// finds point on specified segment closest to specified point in 3d space. returns: point on segment as value in range 0-1 where 0 = start and 1 = end of the segment +template +typename VecType_::value_type +nearest_segment_point( VecType_ const &Segmentstart, VecType_ const &Segmentend, VecType_ const &Point ) { + + auto const v = Segmentend - Segmentstart; + auto const w = Point - Segmentstart; + + auto const c1 = glm::dot( w, v ); + if( c1 <= 0.0 ) { + return 0.0; + } + auto const c2 = glm::dot( v, v ); + if( c2 <= c1 ) { + return 1.0; + } + return c1 / c2; +} + //--------------------------------------------------------------------------- diff --git a/version.h b/version.h index 7043a400..dadb643a 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 902 +#define VERSION_MINOR 903 #define VERSION_REVISION 0 From 2324ae5eda2902ac111e39ab202f47da16c90851 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Tue, 5 Sep 2017 22:19:46 +0200 Subject: [PATCH 2/3] improved consistency of simulation updates, reduction of random factor in acceleration calculations --- Driver.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++--- DynObj.cpp | 41 +++++++++++++++++++++++++------- DynObj.h | 2 +- McZapkie/Mover.cpp | 20 +++++++++------- Segment.cpp | 2 +- Track.h | 10 ++++---- TrkFoll.h | 2 +- World.cpp | 12 ++++++---- version.h | 2 +- 9 files changed, 115 insertions(+), 34 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 6c25c34a..b4209bb7 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -46,6 +46,45 @@ ProjectEventOnTrack( TEvent const *Event, TTrack const *Track, double const Dire ( 1.0 - nearestpoint ) * segment->GetLength() ); // measure from point2 }; +double GetDistanceToEvent(TTrack* track, TEvent* event, double scan_dir, double start_dist, int iter = 0, bool back = false) +{ + if( track == nullptr ) { return start_dist; } + + auto const segment = track->CurrentSegment(); + vector3 const pos_event = event->PositionGet(); + double len1, len2; + double sd = scan_dir; + double seg_len = scan_dir > 0 ? 0.0 : 1.0; + double const dzielnik = 1.0 / segment->GetLength();// rozdzielczosc mniej wiecej 1m + int krok = 0; // krok obliczeniowy do sprawdzania czy odwracamy + len2 = (pos_event - segment->FastGetPoint(seg_len)).LengthSquared(); + do + { + len1 = len2; + seg_len += scan_dir > 0 ? dzielnik : -dzielnik; + len2 = (pos_event - segment->FastGetPoint(seg_len)).LengthSquared(); + krok++; + } while ((len1 > len2) && (seg_len >= dzielnik && (seg_len <= (1 - dzielnik)))); + //trzeba sprawdzić czy seg_len nie osiągnął skrajnych wartości, bo wtedy + // trzeba sprawdzić tor obok + if (1 == krok) + sd = -sd; // jeśli tylko jeden krok tzn, że event przy poprzednim sprawdzaym torze + if (((seg_len <= dzielnik) || (seg_len > (1 - dzielnik))) && (iter < 3)) + { // przejście na inny tor + track = track->Neightbour(int(sd), sd); + start_dist += (1 == krok) ? 0 : back ? -segment->GetLength() : segment->GetLength(); + return GetDistanceToEvent(track, event, sd, start_dist, ++iter, 1 == krok ? true : false); + } + else + { // obliczenie mojego toru + seg_len -= scan_dir > 0 ? dzielnik : -dzielnik; //trzeba wrócić do pozycji len1 + seg_len = scan_dir < 0 ? 1 - seg_len : seg_len; + seg_len = back ? 1 - seg_len : seg_len; // odwracamy jeśli idzie do tyłu + start_dist -= back ? segment->GetLength() : 0; + return start_dist + (segment->GetLength() * seg_len); + } +}; + /* Moduł obsługujący sterowanie pojazdami (składami pociągów, samochodami). @@ -491,10 +530,16 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) WriteLog("Speed table for " + OwnerName() + " found new event, " + pEvent->asName); } auto &newspeedpoint = sSpeedTable[iLast]; +/* if( newspeedpoint.Set( pEvent, fCurrentDistance + ProjectEventOnTrack( pEvent, pTrack, fLastDir ), OrderCurrentGet() ) ) { +*/ + if( newspeedpoint.Set( + pEvent, + GetDistanceToEvent( pTrack, pEvent, fLastDir, fCurrentDistance ), + OrderCurrentGet() ) ) { fDistance = newspeedpoint.fDist; // jeśli sygnał stop, to nie ma potrzeby dalej skanować SemNextStopIndex = iLast; @@ -3619,7 +3664,7 @@ TController::UpdateSituation(double dt) { // towards coupler 0 if( ( mvOccupied->V * iDirection < 0.0 ) || ( ( rearvehicle->NextConnected != nullptr ) - && ( rearvehicle->MoverParameters->Couplers[ ( rearvehicle->DirectionGet() > 0 ? 1 : 0 ) ].CouplingFlag == ctrain_virtual ) ) ) { + && ( rearvehicle->MoverParameters->Couplers[ ( rearvehicle->DirectionGet() > 0 ? 1 : 0 ) ].CouplingFlag == coupling::faux ) ) ) { // scan behind if we're moving backward, or if we had something connected there and are moving away rearvehicle->ABuScanObjects( ( pVehicle->DirectionGet() == rearvehicle->DirectionGet() ? @@ -3637,7 +3682,7 @@ TController::UpdateSituation(double dt) { // towards coupler 1 if( ( mvOccupied->V * iDirection < 0.0 ) || ( ( rearvehicle->PrevConnected != nullptr ) - && ( rearvehicle->MoverParameters->Couplers[ ( rearvehicle->DirectionGet() > 0 ? 0 : 1 ) ].CouplingFlag == ctrain_virtual ) ) ) { + && ( rearvehicle->MoverParameters->Couplers[ ( rearvehicle->DirectionGet() > 0 ? 0 : 1 ) ].CouplingFlag == coupling::faux ) ) ) { // scan behind if we're moving backward, or if we had something connected there and are moving away rearvehicle->ABuScanObjects( ( pVehicle->DirectionGet() == rearvehicle->DirectionGet() ? @@ -3653,6 +3698,8 @@ TController::UpdateSituation(double dt) { } } + + // tu bedzie logika sterowania if (AIControllFlag) { @@ -3900,6 +3947,11 @@ TController::UpdateSituation(double dt) { // NOTE: this will affect also multi-unit vehicles TBD: is this what we want? fMinProximityDist *= 2.0; fMaxProximityDist *= 2.0; + if( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 ) { + // additional safety margin for cargo consists + fMinProximityDist *= 2.0; + fMaxProximityDist *= 2.0; + } } fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania // margines prędkości powodujący załączenie napędu @@ -4204,7 +4256,7 @@ TController::UpdateSituation(double dt) { 0 : 1 ); // sprzęg z przodu składu if( ( coupler->Connected ) - && ( coupler->CouplingFlag == 0 ) ) { + && ( coupler->CouplingFlag == coupling::faux ) ) { // mamy coś z przodu podłączone sprzęgiem wirtualnym // wyliczanie optymalnego przyspieszenia do jazdy na widoczność ActualProximityDist = std::min( diff --git a/DynObj.cpp b/DynObj.cpp index f2926f88..f3c24d7f 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1127,7 +1127,7 @@ void TDynamicObject::ABuCheckMyTrack() // Ra: w poniższej funkcji jest problem ze sprzęgami TDynamicObject * -TDynamicObject::ABuFindObject( int &Foundcoupler, double &Distance, TTrack *Track, int const Direction, int const Mycoupler ) +TDynamicObject::ABuFindObject( int &Foundcoupler, double &Distance, TTrack const *Track, int const Direction, int const Mycoupler ) { // Zwraca wskaźnik najbliższego obiektu znajdującego się // na torze w określonym kierunku, ale tylko wtedy, kiedy // obiekty mogą się zderzyć, tzn. nie mijają się. @@ -1169,7 +1169,15 @@ TDynamicObject::ABuFindObject( int &Foundcoupler, double &Distance, TTrack *Trac for( auto dynamic : Track->Dynamics ) { if( dynamic == this ) { continue; } // szukający się nie liczy - +/* + if( ( ( dynamic == PrevConnected ) && ( MoverParameters->Couplers[ TMoverParameters::side::front ].CouplingFlag != coupling::faux ) ) + || ( ( dynamic == NextConnected ) && ( MoverParameters->Couplers[ TMoverParameters::side::rear ].CouplingFlag != coupling::faux ) ) ){ + // stop-gap check to prevent 'detection' of attached vehicles + // which seems to be a side-effect of inaccurate location calculation in 'simple' mode? + // TODO: investigate actual cause + continue; + } +*/ if( Direction > 0 ) { // jeśli szukanie w kierunku Point2 objectposition = ( dynamic->RaTranslationGet() ) - myposition; // odległogłość tamtego od szukającego @@ -1328,16 +1336,23 @@ void TDynamicObject::ABuScanObjects( int Direction, double Distance ) // ScanDir - określa kierunek poszukiwania zależnie od zwrotu prędkości // pojazdu // ScanDir=1 - od strony Coupler0, ScanDir=-1 - od strony Coupler1 - int initialdirection = Direction; // zapamiętanie kierunku poszukiwań na torze początkowym, względem sprzęgów - TTrackFollower *firstaxle = (initialdirection > 0 ? &Axle0 : &Axle1); // można by to trzymać w trainset - TTrack *track = firstaxle->GetTrack(); // tor na którym "stoi" skrajny wózek + auto const initialdirection = Direction; // zapamiętanie kierunku poszukiwań na torze początkowym, względem sprzęgów +/* + TTrackFollower const *firstaxle = (initialdirection > 0 ? &Axle0 : &Axle1); // można by to trzymać w trainset + TTrack const *track = firstaxle->GetTrack(); // tor na którym "stoi" skrajny wózek // (może być inny niż tor pojazdu) if( firstaxle->GetDirection() < 0 ) { // czy oś jest ustawiona w stronę Point1? Direction = -Direction; // jeśli tak, to kierunek szukania będzie przeciwny } - // (teraz względem - // toru) +*/ + TTrack const *track = RaTrackGet(); + if( RaDirectionGet() < 0 ) { + // czy oś jest ustawiona w stronę Point1? + Direction = -Direction; + } + + // (teraz względem toru) int const mycoupler = ( initialdirection < 0 ? 1 : 0 ); // numer sprzęgu do podłączenia w obiekcie szukajacym int foundcoupler { -1 }; // numer sprzęgu w znalezionym obiekcie (znaleziony wypełni) double distance = 0; // przeskanowana odleglość; odległość do zawalidrogi @@ -1347,9 +1362,9 @@ void TDynamicObject::ABuScanObjects( int Direction, double Distance ) // jeśli nie ma na tym samym, szukamy po okolicy szukanie najblizszego toru z jakims obiektem // praktycznie przeklejone z TraceRoute()... if (Direction >= 0) // uwzględniamy kawalek przeanalizowanego wcześniej toru - distance = track->Length() - firstaxle->GetTranslation(); // odległość osi od Point2 toru + distance = track->Length() - RaTranslationGet(); // odległość osi od Point2 toru else - distance = firstaxle->GetTranslation(); // odległość osi od Point1 toru + distance = RaTranslationGet(); // odległość osi od Point1 toru while (distance < Distance) { if (Direction > 0) { @@ -1455,6 +1470,14 @@ void TDynamicObject::ABuScanObjects( int Direction, double Distance ) // nic nie znalezione, to nie ma przeszkód fTrackBlock = 10000.0; } + +#ifdef EU07_DEBUG_COLLISIONS + // debug collider scans + if( ( PrevConnected != nullptr ) + && ( PrevConnected == NextConnected ) ) { + ErrorLog( "Bad coupling: " + asName + " has the same vehicle detected attached to both couplers" ); + } +#endif } //----------ABu: koniec skanowania pojazdow diff --git a/DynObj.h b/DynObj.h index 04f4bc97..623b6ac4 100644 --- a/DynObj.h +++ b/DynObj.h @@ -361,7 +361,7 @@ public: // modele składowe pojazdu void ABuScanObjects(int ScanDir, double ScanDist); protected: - TDynamicObject *ABuFindObject( int &Foundcoupler, double &Distance, TTrack *Track, int const Direction, int const Mycoupler ); + TDynamicObject *ABuFindObject( int &Foundcoupler, double &Distance, TTrack const *Track, int const Direction, int const Mycoupler ); void ABuCheckMyTrack(); public: diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 53453b95..95011356 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1094,6 +1094,7 @@ void TMoverParameters::CollisionDetect(int CouplerN, double dt) coupler.Connected->Couplers[0].CouplingFlag = 0; break; } + WriteLog( "Bad driving: " + Name + " broke a coupler" ); } } } @@ -1268,11 +1269,13 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap AccN = g * Shape.dHrail / TrackW; // szarpanie - if (FuzzyLogic((10.0 + Track.DamageFlag) * Mass * Vel / Vmax, 500000.0, - p_accn)) // Ra: czemu tu masa bez ładunku? - AccV = sqrt((1.0 + Track.DamageFlag) * Random(floor(50.0 * Mass / 1000000.0)) * Vel / - (Vmax * (10.0 + (Track.QualityFlag & 31)))); +#ifdef EU07_USE_FUZZYLOGIC + if( FuzzyLogic( ( 10.0 + Track.DamageFlag ) * Mass * Vel / Vmax, 500000.0, p_accn ) ) { + // Ra: czemu tu masa bez ładunku? + AccV /= ( 2.0 * 0.95 + 2.0 * Random() * 0.1 ); // 95-105% of base modifier (2.0) + } else +#endif AccV = AccV / 2.0; if (AccV > 1.0) @@ -1438,12 +1441,13 @@ double TMoverParameters::FastComputeMovement(double dt, const TTrackShape &Shape // else AccN:=g*Shape.dHrail/TrackW; // szarpanie} - if (FuzzyLogic((10.0 + Track.DamageFlag) * Mass * Vel / Vmax, 500000.0, p_accn)) - { - AccV = sqrt((1.0 + Track.DamageFlag) * Random(floor(50.0 * Mass / 1000000.0)) * Vel / - (Vmax * (10.0 + (Track.QualityFlag & 31)))); // Trunc na floor, czy dobrze? +#ifdef EU07_USE_FUZZYLOGIC + if( FuzzyLogic( ( 10.0 + Track.DamageFlag ) * Mass * Vel / Vmax, 500000.0, p_accn ) ) { + // Ra: czemu tu masa bez ładunku? + AccV /= ( 2.0 * 0.95 + 2.0 * Random() * 0.1 ); // 95-105% of base modifier (2.0) } else +#endif AccV = AccV / 2.0; if (AccV > 1.0) diff --git a/Segment.cpp b/Segment.cpp index 41527efd..f002cc3c 100644 --- a/Segment.cpp +++ b/Segment.cpp @@ -350,7 +350,7 @@ Math3D::vector3 TSegment::FastGetPoint(double const t) const { // return (bCurve?Interpolate(t,Point1,CPointOut,CPointIn,Point2):((1.0-t)*Point1+(t)*Point2)); return ( - ( ( true == bCurve ) || ( iSegCount == 1 ) ) ? + ( ( true == bCurve ) || ( iSegCount != 1 ) ) ? RaInterpolate( t ) : interpolate( Point1, Point2, t ) ); } diff --git a/Track.h b/Track.h index 6398b5d0..323bfb69 100644 --- a/Track.h +++ b/Track.h @@ -197,14 +197,14 @@ public: void ConnectPrevNext(TTrack *pNewPrev, int typ); void ConnectNextPrev(TTrack *pNewNext, int typ); void ConnectNextNext(TTrack *pNewNext, int typ); - inline double Length() { + inline double Length() const { return Segment->GetLength(); }; inline std::shared_ptr CurrentSegment() const { return Segment; }; - inline TTrack * CurrentNext() { - return (trNext); }; - inline TTrack * CurrentPrev() { - return (trPrev); }; + inline TTrack *CurrentNext() const { + return trNext; }; + inline TTrack *CurrentPrev() const { + return trPrev; }; TTrack * Neightbour(int s, double &d); bool SetConnections(int i); bool Switch(int i, double t = -1.0, double d = -1.0); diff --git a/TrkFoll.h b/TrkFoll.h index 87bc8312..2545ba58 100644 --- a/TrkFoll.h +++ b/TrkFoll.h @@ -47,7 +47,7 @@ class TTrackFollower { return fDirection; }; // zwrot na torze - inline double GetTranslation() + inline double GetTranslation() const { return fCurrentDistance; }; // ABu-030403 diff --git a/World.cpp b/World.cpp index 6612b6b0..e573e275 100644 --- a/World.cpp +++ b/World.cpp @@ -1048,17 +1048,19 @@ bool TWorld::Update() // this means at count > 20 simulation and render are going to desync. is that right? // NOTE: experimentally changing this to prevent the desync. // TODO: test what happens if we hit more than 20 * 0.01 sec slices, i.e. less than 5 fps - Ground.Update(dt / updatecount, updatecount); // tu zrobić tylko coklatkową aktualizację przesunięć + for( int updateidx = 0; updateidx < updatecount; ++updateidx ) { + Ground.Update( dt / updatecount, 1 ); // tu zrobić tylko coklatkową aktualizację przesunięć + } // yB dodał przyspieszacz fizyki if( (true == DebugModeFlag) && (true == Global::bActive) // nie przyspieszać, gdy jedzie w tle :) && ( glfwGetKey( window, GLFW_KEY_PAUSE ) == GLFW_PRESS ) ) { - Ground.Update( dt, updatecount ); - Ground.Update( dt, updatecount ); - Ground.Update( dt, updatecount ); - Ground.Update( dt, updatecount ); // 5 razy + Ground.Update( dt, 1 ); + Ground.Update( dt, 1 ); + Ground.Update( dt, 1 ); + Ground.Update( dt, 1 ); // 5 razy } // secondary fixed step simulation time routines diff --git a/version.h b/version.h index dadb643a..998e22bf 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 17 -#define VERSION_MINOR 903 +#define VERSION_MINOR 905 #define VERSION_REVISION 0 From 53ad41afa0a072b85d139ade69de0d08f6ca5baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3lik=20Uszasty?= Date: Tue, 5 Sep 2017 20:14:58 +0200 Subject: [PATCH 3/3] cherry-pick from refs/remotes/youby/mover_in_c++ "new formula for adhesion, fixed some bugs in calculation of wheels' rotation" --- Driver.cpp | 22 +++++++------------- McZapkie/Mover.cpp | 52 ++++++++++++++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index b4209bb7..3c9b9780 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -4561,24 +4561,16 @@ TController::UpdateSituation(double dt) { // zapobieganie poslizgowi u nas if (mvControlling->SlippingWheels) { - if (!mvControlling->DecScndCtrl(2)) // bocznik na zero - mvControlling->DecMainCtrl(1); -/* - if (mvOccupied->BrakeCtrlPos == - mvOccupied->BrakeCtrlPosNo) // jeśli ostatnia pozycja hamowania - //yB: ten warunek wyżej nie ma sensu - mvOccupied->DecBrakeLevel(); // to cofnij hamulec - DecBrake(); // to cofnij hamulec - else - mvControlling->AntiSlippingButton(); -*/ - DecBrake(); // to cofnij hamulec + if( false == mvControlling->DecScndCtrl( 2 ) ) { + // bocznik na zero + mvControlling->DecMainCtrl( 1 ); + } + DecBrake(); // cofnij hamulec mvControlling->AntiSlippingButton(); ++iDriverFailCount; - mvControlling->SlippingWheels = false; // flaga już wykorzystana } - if (iDriverFailCount > maxdriverfails) - { + if (iDriverFailCount > maxdriverfails) { + Psyche = Easyman; if (iDriverFailCount > maxdriverfails * 2) SetDriverPsyche(); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 95011356..088e0074 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -3674,6 +3674,7 @@ double TMoverParameters::ComputeMass(void) void TMoverParameters::ComputeTotalForce(double dt, double dt1, bool FullVer) { int b; + double Fwheels = 0.0; if (PhysicActivation) { @@ -3733,19 +3734,35 @@ void TMoverParameters::ComputeTotalForce(double dt, double dt1, bool FullVer) else FTrain = 0; Fb = BrakeForce(RunningTrack); + Fwheels = FTrain - Fb * Sign(V); if( ( Vel > 0.001 ) // crude trap, to prevent braked stationary vehicles from passing fb > mass * adhesive test - && ( std::max( std::abs( FTrain ), Fb ) > TotalMassxg * Adhesive( RunningTrack.friction ) ) ) // poslizg + && ( std::abs(Fwheels) > TotalMassxg * Adhesive( RunningTrack.friction ) ) ) // poslizg { SlippingWheels = true; } if( true == SlippingWheels ) { // TrainForce:=TrainForce-Fb; - nrot = ComputeRotatingWheel((FTrain - Fb * Sign(V) - FStand) / NAxles - + double temp_nrot = ComputeRotatingWheel(Fwheels - Sign(nrot * M_PI * WheelDiameter - V) * - Adhesive(RunningTrack.friction) * TotalMass, + Adhesive(RunningTrack.friction) * TotalMassxg, dt, nrot); - FTrain = Sign(FTrain) * TotalMassxg * Adhesive(RunningTrack.friction); - Fb = std::min(Fb, TotalMassxg * Adhesive(RunningTrack.friction)); + Fwheels = Sign(nrot * M_PI * WheelDiameter - V) * TotalMassxg * Adhesive(RunningTrack.friction); + if (Fwheels*Sign(V)>0) + { + FTrain = Fwheels - Fb; + } + else + { + Fb = FTrain - Fwheels; + } + if (Sign(nrot * M_PI * WheelDiameter - V)*Sign(temp_nrot * M_PI * WheelDiameter - V) < 0) + { + SlippingWheels = false; + temp_nrot = V / M_PI / WheelDiameter; + } + + + nrot = temp_nrot; } // else SlippingWheels:=false; // FStand:=0; @@ -3754,7 +3771,7 @@ void TMoverParameters::ComputeTotalForce(double dt, double dt1, bool FullVer) (Couplers[b].CouplerType<>Articulated)*/ { // doliczenie sił z innych pojazdów Couplers[b].CForce = CouplerForce(b, dt); - FTrain += Couplers[b].CForce; + FTrain += Couplers[b].CForce; } else Couplers[b].CForce = 0; @@ -3888,16 +3905,6 @@ double TMoverParameters::BrakeForce(const TTrackParam &Track) } else UnitBrakeForce = K * 1000.0; - if (((double)NBpA * UnitBrakeForce > TotalMassxg * Adhesive(RunningTrack.friction) / NAxles) && - (abs(V) > 0.001)) - // poslizg - { - // Fb = Adhesive(Track.friction) * Mass * g; - SlippingWheels = true; - } - //{ else - // begin - //{ SlippingWheels:=false;} // if (LocalBrake=ManualBrake)or(MBrake=true)) and (BrakePress<0.3) then // Fb:=UnitBrakeForce*NBpA {ham. reczny dziala na jedna os} // else //yB: to nie do konca ma sens, ponieważ ręczny w wagonie działa na jeden cylinder @@ -3933,7 +3940,10 @@ double TMoverParameters::FrictionForce(double R, int TDamage) // ************************************************************************************************* double TMoverParameters::Adhesive(double staticfriction) { - double adhesion; + double adhesion = 0.0; + const double adh_factor = 0.25; //współczynnik określający, jak bardzo spada tarcie przy poślizgu + const double slipfactor = 0.33; //współczynnik określający, jak szybko spada tarcie przy poślizgu + const double sandfactor = 1.25; //współczynnik określający, jak mocno pomaga piasek /* // ABu: male przerobki, tylko czy to da jakikolwiek skutek w FPS? // w kazdym razie zaciemni kod na pewno :) @@ -3956,6 +3966,7 @@ double TMoverParameters::Adhesive(double staticfriction) // WriteLog(FloatToStr(adhesive)); // tutaj jest na poziomie 0.2 - 0.3 return adhesion; */ + /* //wersja druga if( true == SlippingWheels ) { if( true == SandDose ) { adhesion = 0.48; } @@ -3966,7 +3977,12 @@ double TMoverParameters::Adhesive(double staticfriction) if( true == SandDose ) { adhesion = std::max( staticfriction * ( 100.0 + Vel ) / ( 50.0 + Vel ) * 1.1, 0.48 ); } else { adhesion = staticfriction * ( 100.0 + Vel ) / ( 50.0 + Vel ); } } - adhesion *= ( 11.0 - 2 * Random() ) / 10.0; + adhesion *= ( 11.0 - 2 * Random() ) / 10.0; */ + //wersja3 by youBy - uwzględnia naturalne mikropoślizgi i wpływ piasecznicy, usuwa losowość z pojazdu + double Vwheels = nrot * M_PI * WheelDiameter; // predkosc liniowa koła wynikająca z obrotowej + double deltaV = V - Vwheels; //poślizg - różnica prędkości w punkcie styku koła i szyny + deltaV = std::max(0.0, std::abs(deltaV) - 0.25); //mikropoślizgi do ok. 0,25 m/s nie zrywają przyczepności + adhesion = staticfriction * (28 + Vwheels) / (14 + Vwheels) * ((SandDose? sandfactor : 1) - (1 - adh_factor)*(deltaV / (deltaV + slipfactor))); return adhesion; }