Rotate dragged tracks when making new connections.

This commit is contained in:
Leonardo Zide
2025-05-15 17:13:34 -07:00
parent 6059442c6a
commit 026a25c8eb
9 changed files with 183 additions and 31 deletions
+1 -1
View File
@@ -1816,7 +1816,7 @@ std::vector<PieceInfo*> lcPiecesLibrary::GetVisibleTrainTrackParts(const lcTrain
{
lcTrainTrackInfo* TrainTrackInfo = Info->GetTrainTrackInfo();
if (TrainTrackInfo && TrainTrackInfo->IsVisible() && TrainTrackInfo->CanConnectTo(ConnectionType))
if (TrainTrackInfo && TrainTrackInfo->IsVisible() && TrainTrackInfo->CanConnectTo(ConnectionType, true))
Parts.emplace_back(Info);
}
+13
View File
@@ -970,6 +970,19 @@ inline lcVector3 lcMatrix33ToEulerAngles(const lcMatrix33& RotMat)
return Rot;
}
inline bool lcMatrix33Similar(const lcMatrix33& a, const lcMatrix33& b)
{
for (int Row = 0; Row < 3; Row++)
{
float Dot = lcDot(a.r[Row], b.r[Row]);
if (Dot < 0.99f || Dot > 1.01f)
return false;
}
return true;
}
inline float lcMatrix44::Determinant() const
{
return r[0][0] * r[1][1] * r[2][2] + r[0][1] * r[1][2] * r[2][0] +
+95 -1
View File
@@ -2325,7 +2325,7 @@ lcPiece* lcModel::AddPiece(PieceInfo* Info, quint32 Section)
if (!Last->IsFocused())
UpdateTrainTrackConnections(Last, false);
TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(Last, Info, gMainWindow->mColorIndex, Section, std::nullopt);
TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(Last, Info, nullptr, gMainWindow->mColorIndex, Section, true, std::nullopt);
for (const lcInsertPieceInfo& TrainTrack : TrainTracks)
CreatePiece(TrainTrack.Info, TrainTrack.Transform, TrainTrack.ColorIndex);
@@ -2533,6 +2533,34 @@ void lcModel::UpdateTrainTrackConnections(lcPiece* TrackPiece, bool IgnoreSelect
TrackPiece->SetTrainTrackConnections(std::move(Connections));
}
void lcModel::UpdateSelectedPiecesTrainTrackConnections()
{
std::vector<lcPiece *> TrackPieces;
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
if (Piece->mPieceInfo->GetTrainTrackInfo() && Piece->IsSelected())
TrackPieces.push_back(Piece.get());
for (lcPiece* TrackPiece : TrackPieces)
{
const lcTrainTrackInfo* TrainTrackInfo = TrackPiece->mPieceInfo->GetTrainTrackInfo();
const int ConnectionCount = static_cast<int>(TrainTrackInfo->GetConnections().size());
std::vector<bool> Connections(ConnectionCount, false);
for (const lcPiece* CheckPiece : TrackPieces)
{
if (CheckPiece == TrackPiece)
continue;
for (int ConnectionIndex = 0; ConnectionIndex < ConnectionCount; ConnectionIndex++)
if (!Connections[ConnectionIndex] && lcTrainTrackInfo::GetPieceConnectionIndex(TrackPiece, ConnectionIndex, CheckPiece) != -1)
Connections[ConnectionIndex] = true;
}
TrackPiece->SetTrainTrackConnections(std::move(Connections));
}
}
void lcModel::DeleteAllCameras()
{
if (mCameras.empty())
@@ -4539,6 +4567,72 @@ void lcModel::UpdateMoveTool(const lcVector3& Distance, bool AllowRelative, bool
UpdateAllViews();
}
void lcModel::UpdateFreeMoveTool(lcPiece* MousePiece, const lcMatrix44& StartTransform, const lcMatrix44& NewTransform, bool IsConnection, bool AlternateButtonDrag)
{
lcMatrix33 NewRotation = IsConnection ? lcMatrix33(NewTransform) : lcMatrix33(StartTransform);
lcMatrix33 CurrentRotation = lcMatrix33(MousePiece->mModelWorld);
if (!lcMatrix33Similar(CurrentRotation, NewRotation))
{
const lcVector3 PieceDistance = NewTransform.GetTranslation() - MousePiece->mModelWorld.GetTranslation();
const lcVector3 ObjectDistance = PieceDistance;
lcMatrix33 RotationMatrix = lcMul(lcMatrix33AffineInverse(CurrentRotation), NewRotation);
const lcVector3& Center = NewTransform.GetTranslation();
lcMatrix33 RelativeRotation = MousePiece->GetRelativeRotation();
lcMatrix33 WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation);
RotationMatrix = lcMul(RotationMatrix, RelativeRotation);
int Flags;
std::vector<lcObject*> Selection;
lcObject* Focus;
GetSelectionInformation(&Flags, Selection, &Focus);
for (lcObject* Object : Selection)
{
if (Object->IsPiece())
{
lcPiece* Piece = (lcPiece*)Object;
Piece->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), PieceDistance);
Piece->UpdatePosition(mCurrentStep);
Piece->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix);
Piece->UpdatePosition(mCurrentStep);
}
else if (Object->IsCamera())
{
//move
//rotate
}
else if (Object->IsLight())
{
lcLight* Light = (lcLight*)Object;
//move
// Light->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix);
Light->UpdatePosition(mCurrentStep);
}
}
mMouseToolDistance = NewTransform.GetTranslation() - StartTransform.GetTranslation();
mMouseToolFirstMove = false;
gMainWindow->UpdateSelectedObjects(false);
UpdateAllViews();
}
else
{
lcVector3 Distance = NewTransform.GetTranslation() - StartTransform.GetTranslation();
UpdateMoveTool(Distance, false, AlternateButtonDrag);
}
}
void lcModel::UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag)
{
const lcVector3 Delta = SnapRotation(Angles) - SnapRotation(mMouseToolDistance);
+2
View File
@@ -269,6 +269,7 @@ public:
void DuplicateSelectedPieces();
void PaintSelectedPieces();
void UpdateTrainTrackConnections(lcPiece* TrackPiece, bool IgnoreSelected) const;
void UpdateSelectedPiecesTrainTrackConnections();
void GetScene(lcScene* Scene, const lcCamera* ViewCamera, bool AllowHighlight, bool AllowFade) const;
void AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const;
@@ -345,6 +346,7 @@ public:
void BeginCameraTool(const lcVector3& Position, const lcVector3& Target);
void UpdateCameraTool(const lcVector3& Position);
void UpdateMoveTool(const lcVector3& Distance, bool AllowRelative, bool AlternateButtonDrag);
void UpdateFreeMoveTool(lcPiece* MousePiece, const lcMatrix44& StartTransform, const lcMatrix44& NewTransform, bool IsConnection, bool AlternateButtonDrag);
void UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag);
void UpdateScaleTool(const float Scale);
void EraserToolClicked(lcObject* Object);
+46 -5
View File
@@ -7,7 +7,10 @@
#include "lc_application.h"
// todo:
// when moving existing pieces, lcView::OnMouseMove calls UpdateMoveTool which only takes a position and can't rotate pieces
// when moving existing pieces:
// need to check for parts intersecting instead of mouse intersection because it's hard to connect tracks
// look into the snapping that happens when there are no available connections
// fix comments in UpdateFreeMoveTool
// hide some of the 12v tracks to avoid bloat
// auto replace cross when going over a straight section
// set focus connection after adding
@@ -131,12 +134,12 @@ int lcTrainTrackInfo::GetPieceConnectionIndex(const lcPiece* Piece1, int Connect
return -1;
}
std::vector<lcInsertPieceInfo> lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* CurrentPiece, PieceInfo* Info, int ColorIndex, quint32 PreferredSection, std::optional<lcVector3> ClosestPoint)
std::vector<lcInsertPieceInfo> lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* CurrentPiece, PieceInfo* Info, lcPiece* MovingPiece, int ColorIndex, quint32 PreferredSection, bool AllowNewPieces, std::optional<lcVector3> ClosestPoint)
{
std::vector<lcInsertPieceInfo> Pieces;
const lcTrainTrackInfo* CurrentTrackInfo = CurrentPiece->mPieceInfo->GetTrainTrackInfo();
if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty())
if (!CurrentTrackInfo || CurrentTrackInfo->GetConnections().empty() || (MovingPiece && Info != MovingPiece->mPieceInfo))
return Pieces;
quint32 ConnectionIndex = 0;
@@ -152,6 +155,29 @@ std::vector<lcInsertPieceInfo> lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* Cur
if (CurrentPiece->IsTrainTrackConnected(CheckIndex))
continue;
if (MovingPiece)
{
const std::vector<lcTrainTrackConnection>& MovingConnections = Info->GetTrainTrackInfo()->GetConnections();
bool CanConnect = false;
for (quint32 MovingConnectionIndex = 0; MovingConnectionIndex < MovingConnections.size(); MovingConnectionIndex++)
{
if (!MovingPiece->IsTrainTrackConnected(MovingConnectionIndex) && AreConnectionsCompatible(CurrentTrackInfo->GetConnections()[CheckIndex].Type, MovingConnections[MovingConnectionIndex].Type, AllowNewPieces))
{
CanConnect = true;
break;
}
}
if (!CanConnect)
continue;
}
else
{
if (!Info->GetTrainTrackInfo()->CanConnectTo(CurrentTrackInfo->GetConnections()[CheckIndex].Type, AllowNewPieces))
continue;
}
float Distance = (CurrentTrackInfo->GetConnections()[CheckIndex].Transform.GetTranslation() - LocalClosestPoint).LengthSquared();
if (Distance < BestDistance)
@@ -186,8 +212,23 @@ std::vector<lcInsertPieceInfo> lcTrainTrackInfo::GetInsertPieceInfo(lcPiece* Cur
quint32 NewConnectionIndex;// = ConnectionIndex ? 0 : 1;
for (NewConnectionIndex = 0; NewConnectionIndex < NewConnections.size(); NewConnectionIndex++)
if (AreConnectionsCompatible(CurrentConnectionType, NewConnections[NewConnectionIndex].Type))
break;
{
if (MovingPiece)
{
if (MovingPiece->IsTrainTrackConnected(NewConnectionIndex))
continue;
const std::vector<lcTrainTrackConnection>& MovingConnections = Info->GetTrainTrackInfo()->GetConnections();
if (AreConnectionsCompatible(CurrentTrackInfo->GetConnections()[ConnectionIndex].Type, MovingConnections[NewConnectionIndex].Type, AllowNewPieces))
break;
}
else
{
if (AreConnectionsCompatible(CurrentConnectionType, NewConnections[NewConnectionIndex].Type, AllowNewPieces))
break;
}
}
if (NewConnectionIndex == NewConnections.size())
return Pieces;
+5 -5
View File
@@ -45,7 +45,7 @@ public:
}
static void Initialize(lcPiecesLibrary* Library);
static std::vector<lcInsertPieceInfo> GetInsertPieceInfo(lcPiece* CurrentPiece, PieceInfo* Info, int ColorIndex, quint32 PreferredSection, std::optional<lcVector3> ClosestPoint);
static std::vector<lcInsertPieceInfo> GetInsertPieceInfo(lcPiece* CurrentPiece, PieceInfo* Info, lcPiece* MovingPiece, int ColorIndex, quint32 PreferredSection, bool AllowNewPieces, std::optional<lcVector3> ClosestPoint);
static std::optional<lcMatrix44> GetConnectionTransform(PieceInfo* CurrentInfo, const lcMatrix44& CurrentTransform, 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);
@@ -55,17 +55,17 @@ public:
return mConnections;
}
bool CanConnectTo(const lcTrainTrackConnectionType& ConnectionType) const
bool CanConnectTo(const lcTrainTrackConnectionType& ConnectionType, bool AllowNewPieces) const
{
for (const lcTrainTrackConnection& Connection : mConnections)
if (AreConnectionsCompatible(Connection.Type, ConnectionType))
if (AreConnectionsCompatible(Connection.Type, ConnectionType, AllowNewPieces))
return true;
return false;
}
protected:
static bool AreConnectionsCompatible(const lcTrainTrackConnectionType& a, const lcTrainTrackConnectionType& b)
static bool AreConnectionsCompatible(const lcTrainTrackConnectionType& a, const lcTrainTrackConnectionType& b, bool AllowNewPieces)
{
if (a.Group != b.Group)
return false;
@@ -79,7 +79,7 @@ protected:
return b.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper;
case lcTrainTrackConnectionSleeper::NeedsSleeper:
return b.Sleeper == lcTrainTrackConnectionSleeper::HasSleeper || b.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper;
return b.Sleeper == lcTrainTrackConnectionSleeper::HasSleeper || (AllowNewPieces && b.Sleeper == lcTrainTrackConnectionSleeper::NeedsSleeper);
}
return false;
+17 -15
View File
@@ -463,10 +463,11 @@ void lcView::UpdatePiecePreview()
{
PieceInfo* PreviewInfo = gMainWindow->GetCurrentPieceInfo();
std::vector<lcInsertPieceInfo> InsertPieceInfo;
bool IsConnection;
if (PreviewInfo)
{
InsertPieceInfo = GetMouseInsertPieceInfo(false, PreviewInfo);
std::tie(InsertPieceInfo, IsConnection) = GetMouseInsertPieceInfo(false, true, PreviewInfo, nullptr);
if (GetActiveModel() != mModel)
{
@@ -486,7 +487,7 @@ void lcView::UpdatePiecePreview()
mPreviewInsertPieceInfo = std::move(InsertPieceInfo);
}
std::vector<lcInsertPieceInfo> lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, PieceInfo* Info) const
std::pair<std::vector<lcInsertPieceInfo>, bool> lcView::GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, PieceInfo* Info, lcPiece* MovingPiece) const
{
lcModel* ActiveModel = GetActiveModel();
@@ -505,10 +506,10 @@ std::vector<lcInsertPieceInfo> lcView::GetMouseInsertPieceInfo(bool IgnoreSelect
lcVector3 ClosestPoint = ObjectRayTest.Start + lcNormalize(ObjectRayTest.End - ObjectRayTest.Start) * ObjectRayTest.Distance;
quint32 FocusSection = MousePiece->GetFocusSection();
std::vector<lcInsertPieceInfo> TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, Info, gMainWindow->mColorIndex, FocusSection, ClosestPoint);
std::vector<lcInsertPieceInfo> TrainTracks = lcTrainTrackInfo::GetInsertPieceInfo(MousePiece, Info, MovingPiece, gMainWindow->mColorIndex, FocusSection, AllowNewPieces, ClosestPoint);
if (!TrainTracks.empty())
return TrainTracks;
return { TrainTracks, true };
}
lcVector3 Position = ObjectRayTest.PieceInfoRayTest.Plane;
@@ -534,7 +535,7 @@ std::vector<lcInsertPieceInfo> lcView::GetMouseInsertPieceInfo(bool IgnoreSelect
lcMatrix44 WorldMatrix = ObjectRayTest.PieceInfoRayTest.Transform;
WorldMatrix.SetTranslation(Position);
return { lcInsertPieceInfo{ Info, WorldMatrix, gMainWindow->mColorIndex } };
return { { lcInsertPieceInfo{ Info, WorldMatrix, gMainWindow->mColorIndex } }, false };
}
std::array<lcVector3, 2> ClickPoints = {{ lcVector3((float)mMouseX, (float)mMouseY, 0.0f), lcVector3((float)mMouseX, (float)mMouseY, 1.0f) }};
@@ -554,7 +555,7 @@ std::vector<lcInsertPieceInfo> lcView::GetMouseInsertPieceInfo(bool IgnoreSelect
if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(0, 0, 1, BoundingBox.Min.z)))
{
Intersection = ActiveModel->SnapPosition(Intersection);
return { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } };
return { { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false };
}
lcVector3 Position;
@@ -567,10 +568,10 @@ std::vector<lcInsertPieceInfo> lcView::GetMouseInsertPieceInfo(bool IgnoreSelect
if (lcLineSegmentPlaneIntersection(&Intersection, ClickPoints[0], ClickPoints[1], lcVector4(FrontVector, -lcDot(FrontVector, Position))))
{
Intersection = ActiveModel->SnapPosition(Intersection);
return { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } };
return { { lcInsertPieceInfo{ Info, lcMatrix44Translation(Intersection), gMainWindow->mColorIndex } }, false };
}
return { lcInsertPieceInfo{ Info, lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } };
return { { lcInsertPieceInfo{ Info, lcMatrix44Translation(UnprojectPoint(lcVector3((float)mMouseX, (float)mMouseY, 0.9f))), gMainWindow->mColorIndex } }, false };
}
lcVector3 lcView::GetCameraLightInsertPosition() const
@@ -2082,6 +2083,7 @@ void lcView::UpdateTrackTool()
int x = mMouseX;
int y = mMouseY;
bool Redraw = false;
mMouseDownPiece = nullptr;
mTrackToolFromOverlay = false;
lcModel* ActiveModel = GetActiveModel();
@@ -2114,7 +2116,6 @@ void lcView::UpdateTrackTool()
case lcTool::Select:
case lcTool::Move:
{
mMouseDownPiece = nullptr;
std::tie(NewTrackTool, NewTrackSection) = mViewManipulator->UpdateSelectMove(mTrackButton);
mTrackToolFromOverlay = NewTrackTool != lcTrackTool::MoveXYZ && NewTrackTool != lcTrackTool::Select;
Redraw = NewTrackTool != mTrackTool || NewTrackSection != mTrackToolSection;
@@ -2127,8 +2128,8 @@ void lcView::UpdateTrackTool()
if (Object && Object->IsPiece() && ObjectSection.Section == LC_PIECE_SECTION_POSITION && Object->IsSelected())
{
lcPiece* Piece = (lcPiece*)Object;
mMouseDownPosition = Piece->mModelWorld.GetTranslation();
mMouseDownPiece = Piece->mPieceInfo;
mMouseDownTransform = Piece->mModelWorld;
mMouseDownPiece = Piece;
NewTrackTool = lcTrackTool::MoveXYZ;
}
}
@@ -2926,13 +2927,14 @@ void lcView::OnMouseMove()
}
else if (mTrackTool == lcTrackTool::MoveXYZ && mMouseDownPiece)
{
std::vector<lcInsertPieceInfo> PieceInfoTransforms = GetMouseInsertPieceInfo(true, mMouseDownPiece);
if (mMouseDownPiece->mPieceInfo->GetTrainTrackInfo())
ActiveModel->UpdateSelectedPiecesTrainTrackConnections();
auto [PieceInfoTransforms, IsConnection] = GetMouseInsertPieceInfo(true, false, mMouseDownPiece->mPieceInfo, mMouseDownPiece);
if (!PieceInfoTransforms.empty())
{
lcMatrix44 NewPosition = PieceInfoTransforms.back().Transform;
lcVector3 Distance = NewPosition.GetTranslation() - mMouseDownPosition;
ActiveModel->UpdateMoveTool(Distance, false, mTrackButton != lcTrackButton::Left);
ActiveModel->UpdateFreeMoveTool(mMouseDownPiece, mMouseDownTransform, PieceInfoTransforms.back().Transform, IsConnection, mTrackButton != lcTrackButton::Left);
}
}
else if (mTrackTool == lcTrackTool::ScalePlus || mTrackTool == lcTrackTool::ScaleMinus)
+3 -3
View File
@@ -266,7 +266,7 @@ public:
float GetOverlayScale() const;
lcVector3 GetMoveDirection(const lcVector3& Direction) const;
void UpdatePiecePreview();
std::vector<lcInsertPieceInfo> GetMouseInsertPieceInfo(bool IgnoreSelected, PieceInfo* Info) const;
std::pair<std::vector<lcInsertPieceInfo>, bool> GetMouseInsertPieceInfo(bool IgnoreSelected, bool AllowNewPieces, PieceInfo* Info, lcPiece* MovingPiece) const;
lcVector3 GetCameraLightInsertPosition() const;
void GetRayUnderPointer(lcVector3& Start, lcVector3& End) const;
lcObjectRayTest RayTest(bool PiecesOnly, bool IgnoreSelected) const;
@@ -337,8 +337,8 @@ protected:
lcDragState mDragState;
bool mTrackToolFromOverlay;
quint32 mTrackToolSection = ~0U;
lcVector3 mMouseDownPosition;
PieceInfo* mMouseDownPiece;
lcMatrix44 mMouseDownTransform;
lcPiece* mMouseDownPiece = nullptr;
int mPanX = 0;
int mPanY = 0;
+1 -1
View File
@@ -400,7 +400,7 @@
{
"Position": [ -160.0, 0.0, 0.0 ],
"Rotation": -180.0,
"Type": "+4.5V"
"Type": "-4.5V"
}
]
},