Files
leocad/common/lc_traintrack.cpp
T

265 lines
9.3 KiB
C++

#include "lc_global.h"
#include "lc_traintrack.h"
#include "lc_library.h"
#include "pieceinf.h"
#include "piece.h"
#include "lc_model.h"
#include "lc_application.h"
// todo:
// when moving existing pieces, lcView::OnMouseMove calls UpdateMoveTool which only takes a position and can't rotate pieces
// detect the closest connection when dragging over pieces
// hide some of the 12v tracks to avoid bloat
// better insert gizmo mouse detection
// auto replace cross when going over a straight section
// set focus connection after adding
std::map<quint32, PieceInfo*> lcTrainTrackInfo::mSleepers;
void lcTrainTrackInfo::Initialize(lcPiecesLibrary* Library)
{
QFile ConfigFile(QLatin1String(":/resources/traintrack.json"));
if (!ConfigFile.open(QIODevice::ReadOnly))
return;
QJsonParseError Error;
QJsonDocument Document = QJsonDocument::fromJson(ConfigFile.readAll(), &Error);
if (Error.error != QJsonParseError::NoError)
{
qDebug() << Error.errorString();
}
QJsonObject Root = Document.object();
if (Root["Version"].toInt() != 1)
return;
QJsonArray JsonPieces = Root["Pieces"].toArray();
for (QJsonArray::const_iterator PiecesIt = JsonPieces.constBegin(); PiecesIt != JsonPieces.constEnd(); ++PiecesIt)
{
QJsonObject JsonPiece = PiecesIt->toObject();
QJsonArray JsonConnections = JsonPiece["Connections"].toArray();
std::vector<lcTrainTrackConnection> Connections;
for (QJsonArray::const_iterator ConnectionIt = JsonConnections.constBegin(); ConnectionIt != JsonConnections.constEnd(); ++ConnectionIt)
{
QJsonObject JsonConnection = ConnectionIt->toObject();
QJsonArray JsonPosition = JsonConnection["Position"].toArray();
lcVector3 Position(JsonPosition[0].toDouble(), JsonPosition[1].toDouble(), JsonPosition[2].toDouble());
float Rotation = JsonConnection["Rotation"].toDouble() * LC_DTOR;
QString ConnectionGroup = JsonConnection["Type"].toString();
lcTrainTrackConnectionSleeper ConnectionSleeper = lcTrainTrackConnectionSleeper::None;
if (ConnectionGroup.startsWith('+'))
{
ConnectionSleeper = lcTrainTrackConnectionSleeper::NeedsSleeper;
ConnectionGroup = ConnectionGroup.mid(1);
}
else if (ConnectionGroup.startsWith('-'))
{
ConnectionSleeper = lcTrainTrackConnectionSleeper::HasSleeper;
ConnectionGroup = ConnectionGroup.mid(1);
}
Connections.emplace_back(lcTrainTrackConnection{ lcMatrix44(lcMatrix33RotationZ(Rotation), Position), { qHash(ConnectionGroup), ConnectionSleeper } });
}
int Color = JsonPiece["Color"].toInt(16);
QJsonArray JsonIDs = JsonPiece["IDs"].toArray();
for (QJsonArray::const_iterator IDIt = JsonIDs.constBegin(); IDIt != JsonIDs.constEnd(); ++IDIt)
{
PieceInfo* Info = Library->FindPiece(IDIt->toString().toLatin1(), nullptr, false, false);
if (Info)
Info->SetTrainTrackInfo(new lcTrainTrackInfo(Connections, Color, true));
}
JsonIDs = JsonPiece["HiddenIDs"].toArray();
for (QJsonArray::const_iterator IDIt = JsonIDs.constBegin(); IDIt != JsonIDs.constEnd(); ++IDIt)
{
PieceInfo* Info = Library->FindPiece(IDIt->toString().toLatin1(), nullptr, false, false);
if (Info)
Info->SetTrainTrackInfo(new lcTrainTrackInfo(Connections, Color, false));
}
}
QJsonObject JsonSleepers = Root["Sleepers"].toObject();
mSleepers.clear();
for (QJsonObject::const_iterator SleepersIt = JsonSleepers.constBegin(); SleepersIt != JsonSleepers.constEnd(); ++SleepersIt)
{
quint32 Group = qHash(SleepersIt.key());
QString PartId = SleepersIt->toString();
PieceInfo* SleeperInfo = Library->FindPiece(PartId.toLatin1(), nullptr, false, false);
if (SleeperInfo)
mSleepers[Group] = SleeperInfo;
}
}
int lcTrainTrackInfo::GetPieceConnectionIndex(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2)
{
const lcTrainTrackInfo* TrainTrackInfo1 = Piece1->mPieceInfo->GetTrainTrackInfo();
const lcTrainTrackInfo* TrainTrackInfo2 = Piece2->mPieceInfo->GetTrainTrackInfo();
const std::vector<lcTrainTrackConnection>& Connections2 = TrainTrackInfo2->GetConnections();
lcMatrix44 Transform1 = lcMul(TrainTrackInfo1->GetConnections()[ConnectionIndex1].Transform, Piece1->mModelWorld);
for (int ConnectionIndex2 = 0; ConnectionIndex2 < static_cast<int>(Connections2.size()); ConnectionIndex2++)
{
const lcTrainTrackConnection& Connection2 = Connections2[ConnectionIndex2];
const lcMatrix44 Transform2 = lcMul(Connection2.Transform, Piece2->mModelWorld);
if (lcLengthSquared(Transform1.GetTranslation() - Transform2.GetTranslation()) > 0.1f)
continue;
float Dot = lcDot3(Transform1[0], Transform2[0]);
if (Dot < -0.99f && Dot > -1.01f)
return ConnectionIndex2;
}
return -1;
}
std::vector<lcPieceInfoTransform> lcTrainTrackInfo::GetPieceInsertTransforms(lcPiece* CurrentPiece, PieceInfo* Info, quint32 PreferredSection)
{
std::vector<lcPieceInfoTransform> Pieces;
const lcTrainTrackInfo* CurrentTrackInfo = CurrentPiece->mPieceInfo->GetTrainTrackInfo();
if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty())
return Pieces;
quint32 ConnectionIndex = 0;
if (PreferredSection != LC_PIECE_SECTION_INVALID && PreferredSection >= LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST)
ConnectionIndex = PreferredSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST;
quint32 CheckIndex;
for (CheckIndex = 0; CheckIndex < CurrentTrackInfo->GetConnections().size(); CheckIndex++)
if (!CurrentPiece->IsTrainTrackConnected((ConnectionIndex + CheckIndex) % CurrentTrackInfo->GetConnections().size()))
break;
if (CheckIndex == CurrentTrackInfo->GetConnections().size())
return Pieces;
ConnectionIndex = (ConnectionIndex + CheckIndex) % CurrentTrackInfo->GetConnections().size();
const lcTrainTrackConnectionType& CurrentConnectionType = CurrentTrackInfo->GetConnections()[ConnectionIndex].Type;
const std::vector<lcTrainTrackConnection>& NewConnections = Info->GetTrainTrackInfo()->GetConnections();
quint32 NewConnectionIndex;// = ConnectionIndex ? 0 : 1;
for (NewConnectionIndex = 0; NewConnectionIndex < NewConnections.size(); NewConnectionIndex++)
if (AreConnectionsCompatible(CurrentConnectionType, NewConnections[NewConnectionIndex].Type))
break;
if (NewConnectionIndex == NewConnections.size())
return Pieces;
if (CurrentConnectionType.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper && NewConnections[NewConnectionIndex].Type.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper)
{
std::map<quint32, PieceInfo*>::const_iterator SleeperIt = mSleepers.find(CurrentConnectionType.Group);
if (SleeperIt == mSleepers.end())
return Pieces;
PieceInfo* SleeperInfo = SleeperIt->second;
if (!SleeperInfo)
return Pieces;
std::optional<lcMatrix44> SleeperTransform = GetConnectionTransform(CurrentPiece->mPieceInfo, CurrentPiece->mModelWorld, ConnectionIndex, SleeperInfo, 0);
if (!SleeperTransform)
return Pieces;
std::optional<lcMatrix44> Transform = GetConnectionTransform(SleeperInfo, SleeperTransform.value(), 1, Info, NewConnectionIndex);
if (Transform)
{
Pieces.emplace_back(lcPieceInfoTransform{ SleeperInfo, SleeperTransform.value() });
Pieces.emplace_back(lcPieceInfoTransform{ Info, Transform.value() });
}
}
else
{
std::optional<lcMatrix44> Transform = GetConnectionTransform(CurrentPiece->mPieceInfo, CurrentPiece->mModelWorld, ConnectionIndex, Info, NewConnectionIndex);
if (Transform)
Pieces.emplace_back(lcPieceInfoTransform{ Info, Transform.value() });
}
return Pieces;
}
std::optional<lcMatrix44> lcTrainTrackInfo::GetConnectionTransform(PieceInfo* CurrentInfo, const lcMatrix44& CurrentTransform, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex)
{
if (!CurrentInfo || !Info)
return std::nullopt;
const lcTrainTrackInfo* CurrentTrackInfo = CurrentInfo->GetTrainTrackInfo();
if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty())
return std::nullopt;
if (CurrentConnectionIndex >= CurrentTrackInfo->GetConnections().size())
return std::nullopt;
lcTrainTrackInfo* NewTrackInfo = Info->GetTrainTrackInfo();
if (!NewTrackInfo || NewConnectionIndex >= NewTrackInfo->mConnections.size())
return std::nullopt;
lcMatrix44 Transform;
// if (TrainTrackType != lcTrainTrackType::Left)
// Transform = NewTrackInfo->mConnections[NewConnectionIndex].Transform;
// else
// {
Transform = lcMatrix44AffineInverse(NewTrackInfo->mConnections[NewConnectionIndex].Transform);
Transform = lcMul(Transform, lcMatrix44RotationZ(LC_PI));
// }
Transform = lcMul(Transform, CurrentTrackInfo->GetConnections()[CurrentConnectionIndex].Transform);
Transform = lcMul(Transform, CurrentTransform);
return Transform;
}
std::optional<lcMatrix44> lcTrainTrackInfo::CalculateTransformToConnection(const lcMatrix44& ConnectionTransform, PieceInfo* Info, quint32 ConnectionIndex)
{
lcTrainTrackInfo* TrackInfo = Info->GetTrainTrackInfo();
if (!TrackInfo || ConnectionIndex >= TrackInfo->mConnections.size())
return std::nullopt;
lcMatrix44 Transform;
// if (TrainTrackType != lcTrainTrackType::Left)
// Transform = NewTrackInfo->mConnections[NewConnectionIndex].Transform;
// else
// {
Transform = lcMatrix44AffineInverse(TrackInfo->mConnections[ConnectionIndex].Transform);
// Transform = lcMul(Transform, lcMatrix44RotationZ(LC_PI));
// }
Transform = lcMul(Transform, ConnectionTransform);
// Transform = lcMul(Transform, CurrentTrackInfo->GetConnections()[CurrentConnectionIndex].Transform);
// Transform = lcMul(Transform, CurrentPiece->mModelWorld);
return Transform;
}