diff --git a/common/lc_commands.cpp b/common/lc_commands.cpp index 00d5620b..130f6ca2 100644 --- a/common/lc_commands.cpp +++ b/common/lc_commands.cpp @@ -1278,6 +1278,27 @@ const lcCommand gCommands[] = QT_TRANSLATE_NOOP("Status", "Remove the selected control point"), "" }, + // LC_PIECE_TRAIN_TRACK_FOCUS_NEXT + { + QT_TRANSLATE_NOOP("Action", "Piece.TrainTrack.FocusNext"), + QT_TRANSLATE_NOOP("Menu", "Focus Next Connection"), + QT_TRANSLATE_NOOP("Status", "Focus the next connection of the currently selected track"), + "" + }, + // LC_PIECE_TRAIN_TRACK_FOCUS_PREVIOUS + { + QT_TRANSLATE_NOOP("Action", "Piece.TrainTrack.FocusPrevious"), + QT_TRANSLATE_NOOP("Menu", "Focus Previous Connection"), + QT_TRANSLATE_NOOP("Status", "Focus the previous connection of the currently selected track"), + "" + }, + // LC_PIECE_TRAIN_TRACK_ROTATE + { + QT_TRANSLATE_NOOP("Action", "Piece.TrainTrack.Rotate"), + QT_TRANSLATE_NOOP("Menu", "Rotate Train Track"), + QT_TRANSLATE_NOOP("Status", "Rotate the focused train track arount its last connection"), + "" + }, // LC_PIECE_MOVE_PLUSX { QT_TRANSLATE_NOOP("Action", "Piece.Move.PlusX"), diff --git a/common/lc_commands.h b/common/lc_commands.h index f88091ca..c024bc20 100644 --- a/common/lc_commands.h +++ b/common/lc_commands.h @@ -198,6 +198,9 @@ enum lcCommandId LC_PIECE_REMOVE_KEY_FRAMES, LC_PIECE_CONTROL_POINT_INSERT, LC_PIECE_CONTROL_POINT_REMOVE, + LC_PIECE_TRAIN_TRACK_FOCUS_NEXT, + LC_PIECE_TRAIN_TRACK_FOCUS_PREVIOUS, + LC_PIECE_TRAIN_TRACK_ROTATE, LC_PIECE_MOVE_PLUSX, LC_PIECE_MOVE_MINUSX, LC_PIECE_MOVE_PLUSY, diff --git a/common/lc_mainwindow.cpp b/common/lc_mainwindow.cpp index 6d41bdd7..36cef237 100644 --- a/common/lc_mainwindow.cpp +++ b/common/lc_mainwindow.cpp @@ -2005,6 +2005,9 @@ void lcMainWindow::UpdateSelectedObjects(bool SelectionChanged) mActions[LC_PIECE_ARRAY]->setEnabled(Flags & LC_SEL_PIECE); mActions[LC_PIECE_CONTROL_POINT_INSERT]->setEnabled(Flags & LC_SEL_CAN_ADD_CONTROL_POINT); mActions[LC_PIECE_CONTROL_POINT_REMOVE]->setEnabled(Flags & LC_SEL_CAN_REMOVE_CONTROL_POINT); + mActions[LC_PIECE_TRAIN_TRACK_FOCUS_NEXT]->setEnabled(Flags & LC_SEL_TRAIN_TRACK_VISIBLE); + mActions[LC_PIECE_TRAIN_TRACK_FOCUS_PREVIOUS]->setEnabled(Flags & LC_SEL_TRAIN_TRACK_VISIBLE); + mActions[LC_PIECE_TRAIN_TRACK_ROTATE]->setEnabled(Flags & LC_SEL_TRAIN_TRACK_VISIBLE); mActions[LC_PIECE_HIDE_SELECTED]->setEnabled(Flags & LC_SEL_VISIBLE_SELECTED); mActions[LC_PIECE_HIDE_UNSELECTED]->setEnabled(Flags & LC_SEL_UNSELECTED); mActions[LC_PIECE_UNHIDE_SELECTED]->setEnabled(Flags & LC_SEL_HIDDEN_SELECTED); @@ -2926,6 +2929,21 @@ void lcMainWindow::HandleCommand(lcCommandId CommandId) ActiveModel->RemoveFocusedControlPoint(); break; + case LC_PIECE_TRAIN_TRACK_FOCUS_NEXT: + if (ActiveModel) + ActiveModel->FocusNextTrainTrack(); + break; + + case LC_PIECE_TRAIN_TRACK_FOCUS_PREVIOUS: + if (ActiveModel) + ActiveModel->FocusPreviousTrainTrack(); + break; + + case LC_PIECE_TRAIN_TRACK_ROTATE: + if (ActiveModel) + ActiveModel->RotateFocusedTrainTrack(); + break; + case LC_PIECE_MOVE_PLUSX: if (ActiveModel) ActiveModel->MoveSelectedObjects(ActiveView->GetMoveDirection(lcVector3(lcMax(GetMoveXYSnap(), 0.1f), 0.0f, 0.0f)), true, false, true, true, true); diff --git a/common/lc_model.cpp b/common/lc_model.cpp index 9a1a7456..b44d83b8 100644 --- a/common/lc_model.cpp +++ b/common/lc_model.cpp @@ -2371,6 +2371,126 @@ void lcModel::InsertPiece(lcPiece* Piece, size_t Index) mPieces.insert(mPieces.begin() + Index, std::unique_ptr(Piece)); } +void lcModel::FocusNextTrainTrack() +{ + const lcObject* Focus = GetFocusObject(); + + if (!Focus || !Focus->IsPiece()) + return; + + lcPiece* FocusPiece = (lcPiece*)Focus; + const lcTrainTrackInfo* TrainTrackInfo = FocusPiece->mPieceInfo->GetTrainTrackInfo(); + + if (!TrainTrackInfo) + return; + + quint32 FocusSection = FocusPiece->GetFocusSection(); + std::optional Transform; + int ConnectionIndex = 0; + + if (FocusSection != LC_PIECE_SECTION_INVALID && FocusSection != LC_PIECE_SECTION_POSITION) + { + ConnectionIndex = FocusSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST; + ConnectionIndex = (ConnectionIndex + 1) % TrainTrackInfo->GetConnections().size(); + } + + FocusPiece->SetFocused(LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST + ConnectionIndex, true); + + gMainWindow->UpdateSelectedObjects(true); + UpdateAllViews(); +} + +void lcModel::FocusPreviousTrainTrack() +{ + const lcObject* Focus = GetFocusObject(); + + if (!Focus || !Focus->IsPiece()) + return; + + lcPiece* FocusPiece = (lcPiece*)Focus; + const lcTrainTrackInfo* TrainTrackInfo = FocusPiece->mPieceInfo->GetTrainTrackInfo(); + + if (!TrainTrackInfo) + return; + + quint32 FocusSection = FocusPiece->GetFocusSection(); + std::optional Transform; + int ConnectionIndex = 0; + + if (FocusSection != LC_PIECE_SECTION_INVALID && FocusSection != LC_PIECE_SECTION_POSITION) + { + ConnectionIndex = FocusSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST; + ConnectionIndex = (ConnectionIndex + static_cast(TrainTrackInfo->GetConnections().size()) - 1) % TrainTrackInfo->GetConnections().size(); + } + + FocusPiece->SetFocused(LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST + ConnectionIndex, true); + + gMainWindow->UpdateSelectedObjects(true); + UpdateAllViews(); +} + +void lcModel::RotateFocusedTrainTrack() +{ + const lcObject* Focus = GetFocusObject(); + + if (!Focus || !Focus->IsPiece()) + return; + + lcPiece* FocusPiece = (lcPiece*)Focus; + const lcTrainTrackInfo* TrainTrackInfo = FocusPiece->mPieceInfo->GetTrainTrackInfo(); + + if (!TrainTrackInfo) + return; + + quint32 FocusSection = FocusPiece->GetFocusSection(); + std::optional Transform; + int FirstConnectionIndex = 0, RotateConnectionIndex = 0, TracksConnected = 0; + + for (int ConnectionIndex = 0; ConnectionIndex < static_cast(TrainTrackInfo->GetConnections().size()); ConnectionIndex++) + { + if (FocusPiece->IsTrainTrackConnected(ConnectionIndex)) + { + if (!TracksConnected) + FirstConnectionIndex = ConnectionIndex; + + TracksConnected++; + } + } + + if (FocusSection != LC_PIECE_SECTION_INVALID && FocusSection != LC_PIECE_SECTION_POSITION) + { + RotateConnectionIndex = FocusSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST; + } + else + { + RotateConnectionIndex = FirstConnectionIndex; + + if (TracksConnected > 1) + { + // todo: find most recent connection + } + } + + lcMatrix44 ConnectionTransform = lcMul(TrainTrackInfo->GetConnections()[RotateConnectionIndex].Transform, FocusPiece->mModelWorld); + int NewConnectionIndex = (RotateConnectionIndex + 1) % TrainTrackInfo->GetConnections().size(); + + Transform = lcTrainTrackInfo::CalculateTransformToConnection(ConnectionTransform, FocusPiece->mPieceInfo, NewConnectionIndex); + + if (!Transform) + return; + + if ((FocusSection != LC_PIECE_SECTION_INVALID && FocusSection != LC_PIECE_SECTION_POSITION) || !TracksConnected) + FocusPiece->SetFocused(LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST + NewConnectionIndex, true); + + FocusPiece->SetPosition(Transform.value().GetTranslation(), mCurrentStep, gMainWindow->GetAddKeys()); + FocusPiece->SetRotation(lcMatrix33(Transform.value()), mCurrentStep, gMainWindow->GetAddKeys()); + FocusPiece->UpdatePosition(mCurrentStep); + + gMainWindow->UpdateSelectedObjects(true); + UpdateAllViews(); + SaveCheckpoint(tr("Rotating")); +} + void lcModel::UpdateTrainTrackConnections(lcPiece* FocusPiece) const { if (!FocusPiece || !FocusPiece->IsFocused()) @@ -3645,6 +3765,9 @@ void lcModel::GetSelectionInformation(int* Flags, std::vector& Select *Flags |= LC_SEL_PIECE | LC_SEL_SELECTED; + if (Piece->AreTrainTrackConnectionsVisible()) + *Flags |= LC_SEL_TRAIN_TRACK_VISIBLE; + if (Piece->CanAddControlPoint()) *Flags |= LC_SEL_CAN_ADD_CONTROL_POINT; diff --git a/common/lc_model.h b/common/lc_model.h index 3a1dc64e..ad9e61a8 100644 --- a/common/lc_model.h +++ b/common/lc_model.h @@ -20,6 +20,7 @@ enum class lcObjectPropertyId; #define LC_SEL_MODEL_SELECTED 0x1000 // At least one model reference is selected #define LC_SEL_CAN_ADD_CONTROL_POINT 0x2000 // Can add control points to focused piece #define LC_SEL_CAN_REMOVE_CONTROL_POINT 0x4000 // Can remove control points from focused piece +#define LC_SEL_TRAIN_TRACK_VISIBLE 0x8000 // Focused piece has train track connections enum class lcSelectionMode { @@ -215,6 +216,9 @@ public: void RemoveSelectedPiecesKeyFrames(); void InsertControlPoint(); void RemoveFocusedControlPoint(); + void FocusNextTrainTrack(); + void FocusPreviousTrainTrack(); + void RotateFocusedTrainTrack(); void ShowSelectedPiecesEarlier(); void ShowSelectedPiecesLater(); void SetPieceSteps(const std::vector>& PieceSteps); diff --git a/common/lc_traintrack.cpp b/common/lc_traintrack.cpp index 060f63be..218b67cc 100644 --- a/common/lc_traintrack.cpp +++ b/common/lc_traintrack.cpp @@ -55,9 +55,9 @@ void lcTrainTrackInit(lcPiecesLibrary* Library) { lcTrainTrackInfo* TrainTrackInfo = new lcTrainTrackInfo(); + TrainTrackInfo->AddConnection({lcMatrix44(lcMatrix33RotationZ(LC_PI), lcVector3(-320.0f, 0.0f, 0.0f))}); TrainTrackInfo->AddConnection({lcMatrix44Translation(lcVector3(320.0f, 0.0f, 0.0f))}); TrainTrackInfo->AddConnection({lcMatrix44(lcMatrix33RotationZ(22.5f * LC_DTOR), lcVector3(BranchX, BranchY, 0.0f))}); - TrainTrackInfo->AddConnection({lcMatrix44(lcMatrix33RotationZ(LC_PI), lcVector3(-320.0f, 0.0f, 0.0f))}); Info->SetTrainTrackInfo(TrainTrackInfo); } @@ -68,9 +68,9 @@ void lcTrainTrackInit(lcPiecesLibrary* Library) { lcTrainTrackInfo* TrainTrackInfo = new lcTrainTrackInfo(); + TrainTrackInfo->AddConnection({lcMatrix44(lcMatrix33RotationZ(LC_PI), lcVector3(-320.0f, 0.0f, 0.0f))}); TrainTrackInfo->AddConnection({lcMatrix44Translation(lcVector3(320.0f, 0.0f, 0.0f))}); TrainTrackInfo->AddConnection({lcMatrix44(lcMatrix33RotationZ(-22.5f * LC_DTOR), lcVector3(BranchX, -BranchY, 0.0f))}); - TrainTrackInfo->AddConnection({lcMatrix44(lcMatrix33RotationZ(LC_PI), lcVector3(-320.0f, 0.0f, 0.0f))}); Info->SetTrainTrackInfo(TrainTrackInfo); } @@ -170,6 +170,9 @@ std::optional lcTrainTrackInfo::GetPieceInsertTransform(lcPiece* Cur for (ConnectionIndex = 0; ConnectionIndex < CurrentTrackInfo->GetConnections().size(); ConnectionIndex++) if (!CurrentPiece->IsTrainTrackConnected(ConnectionIndex)) break; + + if (ConnectionIndex == CurrentTrackInfo->GetConnections().size()) + return std::nullopt; } else if (FocusSection != LC_PIECE_SECTION_INVALID) { @@ -179,7 +182,7 @@ std::optional lcTrainTrackInfo::GetPieceInsertTransform(lcPiece* Cur ConnectionIndex = FocusSection - LC_PIECE_SECTION_TRAIN_TRACK_CONNECTION_FIRST; } - return GetConnectionTransform(CurrentPiece, ConnectionIndex, Info, 0); + return GetConnectionTransform(CurrentPiece, ConnectionIndex, Info, ConnectionIndex ? 0 : 1); } std::optional lcTrainTrackInfo::GetConnectionTransform(lcPiece* CurrentPiece, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex) @@ -215,3 +218,28 @@ std::optional lcTrainTrackInfo::GetConnectionTransform(lcPiece* Curr return Transform; } + +std::optional 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; +} diff --git a/common/lc_traintrack.h b/common/lc_traintrack.h index 2885415b..359f20b9 100644 --- a/common/lc_traintrack.h +++ b/common/lc_traintrack.h @@ -28,6 +28,7 @@ public: std::pair GetPieceInsertTransform(lcPiece* Piece, quint32 ConnectionIndex, lcTrainTrackType TrainTrackType) const; static std::optional GetPieceInsertTransform(lcPiece* CurrentPiece, PieceInfo* Info); static std::optional GetConnectionTransform(lcPiece* CurrentPiece, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex); + static std::optional CalculateTransformToConnection(const lcMatrix44& ConnectionTransform, PieceInfo* Info, quint32 ConnectionIndex); static int GetPieceConnectionIndex(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2); void AddConnection(const lcTrainTrackConnection& TrainTrackConnection) diff --git a/common/piece.h b/common/piece.h index 29d5ffe1..85019a3a 100644 --- a/common/piece.h +++ b/common/piece.h @@ -171,6 +171,12 @@ public: QString GetName() const override; bool IsVisible(lcStep Step) const; bool IsVisibleInSubModel() const; + + bool AreTrainTrackConnectionsVisible() const + { + return IsFocused(); + } + void GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex, std::vector& ModelParts) const; void Initialize(const lcMatrix44& WorldMatrix, lcStep Step); const lcBoundingBox& GetBoundingBox() const; @@ -352,11 +358,6 @@ protected: void DrawSynthInterface(lcContext* Context, const lcMatrix44& WorldMatrix) const; - bool AreTrainTrackConnectionsVisible() const - { - return IsFocused(); - } - void DrawTrainTrackInterface(lcContext* Context, const lcMatrix44& WorldMatrix) const; lcObjectProperty mPosition = lcObjectProperty(lcVector3(0.0f, 0.0f, 0.0f));