Added track rotation and focus shortcuts.
Some checks are pending
LeoCAD CI / build-ubuntu (push) Waiting to run
LeoCAD CI / build-macos (push) Waiting to run

This commit is contained in:
Leonardo Zide 2024-12-07 20:02:47 -08:00
parent fee870d33d
commit ff23653709
8 changed files with 207 additions and 8 deletions

View file

@ -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"),

View file

@ -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,

View file

@ -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);

View file

@ -2371,6 +2371,126 @@ void lcModel::InsertPiece(lcPiece* Piece, size_t Index)
mPieces.insert(mPieces.begin() + Index, std::unique_ptr<lcPiece>(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<lcMatrix44> 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<lcMatrix44> 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<int>(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<lcMatrix44> Transform;
int FirstConnectionIndex = 0, RotateConnectionIndex = 0, TracksConnected = 0;
for (int ConnectionIndex = 0; ConnectionIndex < static_cast<int>(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<lcObject*>& 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;

View file

@ -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<std::pair<lcPiece*, lcStep>>& PieceSteps);

View file

@ -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<lcMatrix44> 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<lcMatrix44> 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<lcMatrix44> lcTrainTrackInfo::GetConnectionTransform(lcPiece* CurrentPiece, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex)
@ -215,3 +218,28 @@ std::optional<lcMatrix44> lcTrainTrackInfo::GetConnectionTransform(lcPiece* Curr
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;
}

View file

@ -28,6 +28,7 @@ public:
std::pair<PieceInfo*, lcMatrix44> GetPieceInsertTransform(lcPiece* Piece, quint32 ConnectionIndex, lcTrainTrackType TrainTrackType) const;
static std::optional<lcMatrix44> GetPieceInsertTransform(lcPiece* CurrentPiece, PieceInfo* Info);
static std::optional<lcMatrix44> GetConnectionTransform(lcPiece* CurrentPiece, quint32 CurrentConnectionIndex, PieceInfo* Info, quint32 NewConnectionIndex);
static std::optional<lcMatrix44> CalculateTransformToConnection(const lcMatrix44& ConnectionTransform, PieceInfo* Info, quint32 ConnectionIndex);
static int GetPieceConnectionIndex(const lcPiece* Piece1, int ConnectionIndex1, const lcPiece* Piece2);
void AddConnection(const lcTrainTrackConnection& TrainTrackConnection)

View file

@ -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<lcModelPartsEntry>& 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<lcVector3> mPosition = lcObjectProperty<lcVector3>(lcVector3(0.0f, 0.0f, 0.0f));