17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 17:27:30 +01:00

feat(editor): a switch path can be a chain of pieces: the attributes pieces1 and pieces2 keep the parts of each path after the classic bezier, which stays as the approximation older versions read and log the attributes as unknown; the simulation drives, draws and maps a path along its pieces by distance, pieces that don't join the ends of the path are reported and the bezier used; the catalogue Rz switches get the diverging path as the arc of their radius and the straight after it, moving, rotating and mirroring a switch carry the pieces, reshaping a path by hand drops them; self-test commands switchpieces and switchbreak

This commit is contained in:
maj00r
2026-10-12 18:32:23 +02:00
parent 46d41549c9
commit ea341af27a
8 changed files with 329 additions and 60 deletions

View File

@@ -33,6 +33,16 @@ segment_data::deserialize( cParser &Input, glm::dvec3 const &Offset ) {
}
void
segment_data::translate( glm::dvec3 const &Offset ) {
points[ segment_data::point::start ] += Offset;
points[ segment_data::point::end ] += Offset;
for( auto &piece : pieces ) {
piece.translate( Offset );
}
}
TSegment::TSegment(TTrack *owner) :
pOwner( owner )
{}
@@ -62,6 +72,8 @@ bool TSegment::Init(glm::dvec3 NewPoint1, glm::dvec3 NewPoint2, double fNewStep,
bool TSegment::Init(glm::dvec3 &NewPoint1, glm::dvec3 NewCPointOut, glm::dvec3 NewCPointIn, glm::dvec3 &NewPoint2, double fNewStep, double fNewRoll1, double fNewRoll2, bool bIsCurve)
{ // wersja uniwersalna (dla krzywej i prostego)
m_pieces.clear();
m_starts.clear();
Point1 = NewPoint1;
CPointOut = NewCPointOut;
CPointIn = NewCPointIn;
@@ -133,6 +145,45 @@ bool TSegment::Init(glm::dvec3 &NewPoint1, glm::dvec3 NewCPointOut, glm::dvec3 N
return true;
}
bool TSegment::InitChain(std::vector<std::shared_ptr<TSegment>> Pieces)
{
if( Pieces.empty() )
return false;
m_pieces = std::move( Pieces );
m_starts.clear();
fLength = 0.0;
iSegCount = 0;
for( auto const &piece : m_pieces ) {
m_starts.push_back( fLength );
fLength += piece->fLength;
iSegCount += piece->iSegCount;
}
auto const &first { *m_pieces.front() };
auto const &last { *m_pieces.back() };
Point1 = first.Point1;
CPointOut = first.CPointOut;
CPointIn = last.CPointIn;
Point2 = last.Point2;
fRoll1 = first.fRoll1;
fRoll2 = last.fRoll2;
bCurve = true;
fDirection = -std::atan2(Point2.x - Point1.x, Point2.z - Point1.z);
fStoop = std::atan2(Point2.y - Point1.y, fLength);
fStep = fLength / iSegCount;
fTsBuffer.resize( iSegCount + 1 );
for( int i = 0; i <= iSegCount; ++i ) {
fTsBuffer[ i ] = static_cast<double>( i ) / iSegCount;
}
fTsSlope.assign( iSegCount + 1, fStep / fLength );
return true;
}
std::size_t TSegment::piece_at(double const s) const
{
auto const next { std::upper_bound( std::begin( m_starts ), std::end( m_starts ), s ) };
return next == std::begin( m_starts ) ? 0 : static_cast<std::size_t>( std::distance( std::begin( m_starts ), next ) - 1 );
}
void TSegment::InitTFromSInterpolateData()
{
// pre-compute the curve parameter t at equal distance intervals
@@ -235,6 +286,8 @@ double TSegment::FastGetTFromS(double const s) const
// NOTE: for s outside <0, fLength> the result is extrapolated
// outside <0, 1>, which is the expected behavior (see e.g. usage
// in GetDirection())
if( false == m_pieces.empty() )
return s / fLength;
int const i = std::clamp(static_cast<int>(s / fStep), 0, iSegCount - 1); // interval index
double const u = (s - i * fStep) / fStep; // position within interval (possibly outside)
double const u2 = u * u;
@@ -263,6 +316,8 @@ double TSegment::ComputeLength() const // McZapkie-150503: dlugosc miedzy punkta
// 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
if( false == m_pieces.empty() )
return fLength;
double t;
double l = 0;
glm::dvec3 last{0, 0, 0}; // długość liczona po przesunięciu odcinka do początku układu
@@ -366,7 +421,11 @@ Math3D::vector3 TSegment::GetPoint(double const fDistance) const
*/
void TSegment::RaPositionGet(double const fDistance, glm::dvec3 &position, glm::vec3 &rotation) const {
if (bCurve) {
if( false == m_pieces.empty() ) {
auto const piece { piece_at( fDistance ) };
m_pieces[ piece ]->RaPositionGet( fDistance - m_starts[ piece ], position, rotation );
}
else if (bCurve) {
// można by wprowadzić uproszczony wzór dla okręgów płaskich
auto const t = FastGetTFromS(fDistance); // aproksymacja dystansu na krzywej Beziera na parametr (t)
position = FastGetPoint( t );
@@ -393,6 +452,12 @@ void TSegment::RaPositionGet(double const fDistance, glm::dvec3 &position, glm::
glm::dvec3 TSegment::FastGetPoint(double const t) const
{
// return (bCurve?std::lerp(t,Point1,CPointOut,CPointIn,Point2):((1.0-t)*Point1+(t)*Point2));
if( false == m_pieces.empty() ) {
auto const distance { t * fLength };
auto const index { piece_at( distance ) };
auto const &piece { *m_pieces[ index ] };
return piece.FastGetPoint( piece.FastGetTFromS( distance - m_starts[ index ] ) );
}
return true == bCurve || iSegCount != 1 ? RaInterpolate(t) : glm::mix(Point1, Point2, t);
}