diff --git a/Driver.cpp b/Driver.cpp index 26aa87e7..d0f6758c 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 ) @@ -4248,7 +4207,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 ba1b640d..802addef 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 b37c87c8..bbf7b2d5 100644 --- a/DynObj.h +++ b/DynObj.h @@ -76,7 +76,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 @@ -150,9 +150,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 @@ -161,14 +161,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) @@ -235,7 +235,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ż @@ -247,7 +247,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 @@ -262,7 +262,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; @@ -335,7 +335,7 @@ public: // modele składowe pojazdu double eng_turbo; void ABuBogies(); void ABuModelRoll(); - vector3 modelShake; + Math3D::vector3 modelShake; bool renderme; // yB - czy renderowac // sound* sBrakeAcc; //dźwięk przyspieszacza @@ -404,7 +404,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; @@ -435,31 +435,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 a9ce036b..c50a52d7 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 d729729f..abc9dd4e 100644 --- a/Event.h +++ b/Event.h @@ -109,7 +109,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 bae5465a..60d5b6f0 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 711626e6..c73fdeab 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -74,10 +74,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 d6b48054..393c4911 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 4e32b963..5e5d666a 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,19 +58,27 @@ 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(); +<<<<<<< HEAD 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); @@ -115,15 +118,77 @@ class TSegment CPointOut += pPosition; } } + 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() { if (!fTsBuffer || !bCurve) return 1; return iSegCount; }; - void AngleSet(int i, double a) { - fAngle[i] = a; }; + inline + void + AngleSet(int const i, double const a) { + fAngle[i] = a; }; }; //--------------------------------------------------------------------------- -#endif diff --git a/Track.cpp b/Track.cpp index cbbf04a1..703f5963 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 ac76e50a..88b18e48 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 15d943ca..4643019a 100644 --- a/usefull.h +++ b/usefull.h @@ -96,4 +96,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