leocad/common/lc_model.cpp

4952 lines
116 KiB
C++
Raw Normal View History

2016-10-05 23:28:52 +02:00
#include "lc_global.h"
#include "lc_model.h"
#include <locale.h>
#include "piece.h"
#include "camera.h"
#include "light.h"
#include "group.h"
#include "lc_mainwindow.h"
#include "lc_profile.h"
#include "lc_library.h"
2020-12-24 20:32:56 +01:00
#include "lc_scene.h"
2016-10-05 23:28:52 +02:00
#include "lc_texture.h"
#include "lc_synth.h"
#include "lc_traintrack.h"
2016-10-05 23:28:52 +02:00
#include "lc_file.h"
#include "pieceinf.h"
2020-12-25 19:43:22 +01:00
#include "lc_view.h"
2016-10-05 23:28:52 +02:00
#include "minifig.h"
2023-05-16 06:12:08 +02:00
#include "lc_arraydialog.h"
2016-10-05 23:28:52 +02:00
#include "lc_qselectdialog.h"
2020-12-20 20:24:50 +01:00
#include "lc_minifigdialog.h"
2023-04-30 05:04:58 +02:00
#include "lc_groupdialog.h"
2016-10-05 23:28:52 +02:00
#include "lc_qeditgroupsdialog.h"
2017-12-11 03:12:31 +01:00
#include "lc_qpropertiesdialog.h"
2016-10-05 23:28:52 +02:00
#include "lc_qutils.h"
2017-06-26 03:20:34 +02:00
#include "lc_lxf.h"
#include "lc_previewwidget.h"
2021-01-31 21:05:15 +01:00
#include "lc_findreplacewidget.h"
2016-10-05 23:28:52 +02:00
void lcModelProperties::LoadDefaults()
{
mAuthor = lcGetProfileString(LC_PROFILE_DEFAULT_AUTHOR_NAME);
mAmbientColor = lcVector3FromColor(lcGetProfileInt(LC_PROFILE_DEFAULT_AMBIENT_COLOR));
}
void lcModelProperties::SaveDefaults()
{
lcSetProfileInt(LC_PROFILE_DEFAULT_AMBIENT_COLOR, lcColorFromVector3(mAmbientColor));
}
void lcModelProperties::SaveLDraw(QTextStream& Stream) const
{
2021-11-15 03:34:24 +01:00
const QLatin1String LineEnding("\r\n");
2016-10-05 23:28:52 +02:00
2020-05-03 21:11:51 +02:00
Stream << QLatin1String("0 ") << mDescription << LineEnding;
Stream << QLatin1String("0 Name: ") << mModelName << LineEnding;
Stream << QLatin1String("0 Author: ") << mAuthor << LineEnding;
2016-10-05 23:28:52 +02:00
if (!mComments.isEmpty())
{
QStringList Comments = mComments.split('\n');
2018-02-22 01:12:18 +01:00
for (const QString& Comment : Comments)
2016-10-05 23:28:52 +02:00
Stream << QLatin1String("0 !LEOCAD MODEL COMMENT ") << Comment << LineEnding;
}
// lcVector3 mAmbientColor;
}
2020-05-03 21:11:51 +02:00
bool lcModelProperties::ParseLDrawHeader(QString Line, bool FirstLine)
{
QTextStream LineStream(&Line, QIODevice::ReadOnly);
QString Token;
LineStream >> Token;
2021-11-15 03:34:24 +01:00
const int StartPos = LineStream.pos();
2020-05-03 21:11:51 +02:00
LineStream >> Token;
if (Token == QLatin1String("!LEOCAD"))
return false;
if (Token == QLatin1String("Name:"))
{
mModelName = LineStream.readLine().mid(1);
return true;
}
if (Token == QLatin1String("Author:"))
{
mAuthor = LineStream.readLine().mid(1);
return true;
}
if (FirstLine)
{
LineStream.seek(StartPos);
mDescription = LineStream.readLine().mid(1);
return true;
}
2020-05-03 21:11:51 +02:00
return false;
}
2016-10-05 23:28:52 +02:00
void lcModelProperties::ParseLDrawLine(QTextStream& Stream)
{
QString Token;
Stream >> Token;
if (Token == QLatin1String("AUTHOR"))
mAuthor = Stream.readLine().mid(1);
else if (Token == QLatin1String("DESCRIPTION"))
mDescription = Stream.readLine().mid(1);
else if (Token == QLatin1String("COMMENT"))
{
QString Comment = Stream.readLine().mid(1);
if (!mComments.isEmpty())
mComments += '\n';
mComments += Comment;
}
}
2023-08-09 12:35:07 +02:00
void lcPOVRayOptions::ParseLDrawLine(QTextStream& LineStream)
{
QString Token;
LineStream >> Token;
if (Token == QLatin1String("HEADER_INCLUDE_FILE"))
{
LineStream >> HeaderIncludeFile;
if (!QFileInfo(HeaderIncludeFile).isReadable())
HeaderIncludeFile.clear();
}
else if (Token == QLatin1String("FOOTER_INCLUDE_FILE"))
{
LineStream >> FooterIncludeFile;
if (!QFileInfo(FooterIncludeFile).isReadable())
FooterIncludeFile.clear();
}
else if (Token == QLatin1String("FLOOR_AXIS"))
{
LineStream >> FloorAxis;
if (FloorAxis < 0 || FloorAxis > 2)
FloorAxis = 1; // y
}
else if (Token == QLatin1String("FLOOR_COLOR_RGB"))
LineStream >> FloorColor[0] >> FloorColor[1] >> FloorColor[2];
else if (Token == QLatin1String("FLOOR_AMBIENT"))
LineStream >> FloorAmbient;
else if (Token == QLatin1String("FLOOR_DIFFUSE"))
LineStream >> FloorDiffuse;
else if (Token == QLatin1String("EXCLUDE_FLOOR"))
ExcludeFloor = true;
else if (Token == QLatin1String("EXCLUDE_BACKGROUND"))
ExcludeFloor = true;
else if (Token == QLatin1String("NO_REFLECTION"))
NoReflection = true;
else if (Token == QLatin1String("NO_SHADOWS"))
NoShadow = true;
else if (Token == QLatin1String("USE_LGEO"))
UseLGEO = true;
}
void lcPOVRayOptions::SaveLDraw(QTextStream& Stream) const
{
const QLatin1String LineEnding("\r\n");
if (!HeaderIncludeFile.isEmpty())
Stream << QLatin1String("0 !LEOCAD POV_RAY HEADER_INCLUDE_FILE ") << QDir::toNativeSeparators(HeaderIncludeFile) << LineEnding;
if (!FooterIncludeFile.isEmpty())
Stream << QLatin1String("0 !LEOCAD POV_RAY FOOTER_INCLUDE_FILE ") << QDir::toNativeSeparators(FooterIncludeFile) << LineEnding;
if (FloorAxis != 1)
Stream << QLatin1String("0 !LEOCAD POV_RAY FLOOR_AXIS ") << FloorAxis << LineEnding;
if (FloorColor != lcVector3(0.8f,0.8f,0.8f))
Stream << QLatin1String("0 !LEOCAD POV_RAY FLOOR_COLOR_RGB ") << FloorColor[0] << ' ' << FloorColor[1] << ' ' << FloorColor[2] << LineEnding;
if (FloorAmbient != 0.4f)
Stream << QLatin1String("0 !LEOCAD POV_RAY FLOOR_AMBIENT ") << FloorAmbient << LineEnding;
if (FloorDiffuse != 0.4f)
Stream << QLatin1String("0 !LEOCAD POV_RAY FLOOR_DIFFUSE ") << FloorDiffuse << LineEnding;
if (ExcludeFloor)
Stream << QLatin1String("0 !LEOCAD POV_RAY EXCLUDE_FLOOR") << LineEnding;
if (ExcludeBackground)
Stream << QLatin1String("0 !LEOCAD POV_RAY EXCLUDE_BACKGROUND") << LineEnding;
if (NoReflection)
Stream << QLatin1String("0 !LEOCAD POV_RAY NO_REFLECTION") << LineEnding;
if (NoShadow)
Stream << QLatin1String("0 !LEOCAD POV_RAY NO_SHADOWS") << LineEnding;
if (UseLGEO)
Stream << QLatin1String("0 !LEOCAD POV_RAY USE_LGEO") << LineEnding;
}
2020-12-16 02:19:32 +01:00
lcModel::lcModel(const QString& FileName, Project* Project, bool Preview)
: mProject(Project), mIsPreview(Preview)
2016-10-05 23:28:52 +02:00
{
2020-05-03 21:11:51 +02:00
mProperties.mModelName = FileName;
mProperties.mFileName = FileName;
2016-10-05 23:28:52 +02:00
mProperties.LoadDefaults();
mActive = false;
mCurrentStep = 1;
mPieceInfo = nullptr;
2016-10-05 23:28:52 +02:00
}
lcModel::~lcModel()
{
if (mPieceInfo)
{
if (!mIsPreview && gMainWindow && gMainWindow->GetCurrentPieceInfo() == mPieceInfo)
gMainWindow->SetCurrentPieceInfo(nullptr);
2016-10-05 23:28:52 +02:00
if (mPieceInfo->GetModel() == this)
mPieceInfo->SetPlaceholder();
2017-01-23 04:28:05 +01:00
lcPiecesLibrary* Library = lcGetPiecesLibrary();
Library->ReleasePieceInfo(mPieceInfo);
2016-10-05 23:28:52 +02:00
}
DeleteModel();
DeleteHistory();
}
bool lcModel::GetPieceWorldMatrix(lcPiece* Piece, lcMatrix44& ParentWorldMatrix) const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& ModelPiece : mPieces)
{
2024-06-20 04:22:18 +02:00
if (ModelPiece.get() == Piece)
{
ParentWorldMatrix = lcMul(ModelPiece->mModelWorld, ParentWorldMatrix);
return true;
}
2021-11-15 03:34:24 +01:00
const PieceInfo* Info = ModelPiece->mPieceInfo;
if (Info->IsModel())
{
lcMatrix44 WorldMatrix = lcMul(ModelPiece->mModelWorld, ParentWorldMatrix);
if (Info->GetPieceWorldMatrix(Piece, WorldMatrix))
{
ParentWorldMatrix = WorldMatrix;
return true;
}
}
}
return false;
}
2016-10-05 23:28:52 +02:00
bool lcModel::IncludesModel(const lcModel* Model) const
{
if (Model == this)
return true;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2019-03-09 18:50:34 +01:00
if (Piece->mPieceInfo->IncludesModel(Model))
2016-10-05 23:28:52 +02:00
return true;
return false;
}
void lcModel::DeleteHistory()
{
2019-06-24 03:28:14 +02:00
for (lcModelHistoryEntry* Entry : mUndoHistory)
delete Entry;
mUndoHistory.clear();
for (lcModelHistoryEntry* Entry : mRedoHistory)
delete Entry;
mRedoHistory.clear();
2016-10-05 23:28:52 +02:00
}
void lcModel::DeleteModel()
{
2020-10-17 23:41:59 +02:00
if (gMainWindow)
2016-10-05 23:28:52 +02:00
{
2021-02-27 20:15:04 +01:00
std::vector<lcView*> Views = lcView::GetModelViews(this);
2016-10-05 23:28:52 +02:00
// TODO: this is only needed to avoid a dangling pointer during undo/redo if a camera is set to a view but we should find a better solution instead
2021-02-27 20:15:04 +01:00
for (lcView* View : Views)
2016-10-05 23:28:52 +02:00
{
2024-06-04 18:34:38 +02:00
lcCamera* ViewCamera = View->GetCamera();
2016-10-05 23:28:52 +02:00
2024-06-04 18:34:38 +02:00
if (!ViewCamera->IsSimple())
{
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
{
if (Camera.get() == ViewCamera)
{
View->SetCamera(ViewCamera, true);
break;
}
}
}
2016-10-05 23:28:52 +02:00
}
}
2024-06-20 04:22:18 +02:00
mPieces.clear();
2024-06-04 18:34:38 +02:00
mCameras.clear();
2024-06-04 18:08:54 +02:00
mLights.clear();
mGroups.clear();
2016-10-05 23:28:52 +02:00
mFileLines.clear();
}
void lcModel::CreatePieceInfo(Project* Project)
{
2017-01-23 04:28:05 +01:00
lcPiecesLibrary* Library = lcGetPiecesLibrary();
2020-05-03 21:11:51 +02:00
mPieceInfo = Library->FindPiece(mProperties.mFileName.toLatin1().constData(), Project, true, false);
mPieceInfo->SetModel(this, true, Project, true);
2017-01-23 04:28:05 +01:00
Library->LoadPieceInfo(mPieceInfo, true, true);
2016-10-05 23:28:52 +02:00
}
void lcModel::UpdateMesh()
{
mPieceInfo->SetModel(this, true, nullptr, false);
}
2020-12-16 02:19:32 +01:00
void lcModel::UpdateAllViews() const
{
2020-12-25 19:54:33 +01:00
lcView::UpdateProjectViews(mProject);
2020-12-16 02:19:32 +01:00
}
2019-03-17 21:27:57 +01:00
void lcModel::UpdatePieceInfo(std::vector<lcModel*>& UpdatedModels)
2016-10-05 23:28:52 +02:00
{
2019-03-17 21:27:57 +01:00
if (std::find(UpdatedModels.begin(), UpdatedModels.end(), this) != UpdatedModels.end())
2016-10-05 23:28:52 +02:00
return;
mPieceInfo->SetModel(this, false, nullptr, false);
2019-03-17 21:27:57 +01:00
UpdatedModels.push_back(this);
2016-10-05 23:28:52 +02:00
2021-11-15 03:34:24 +01:00
const lcMesh* Mesh = mPieceInfo->GetMesh();
2016-10-05 23:28:52 +02:00
2024-05-26 22:01:34 +02:00
if (mPieces.empty() && !Mesh)
2016-10-05 23:28:52 +02:00
{
mPieceInfo->SetBoundingBox(lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 0.0f));
return;
}
lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
2019-03-13 03:51:04 +01:00
if (Piece->IsVisibleInSubModel())
2016-10-05 23:28:52 +02:00
{
Piece->mPieceInfo->UpdateBoundingBox(UpdatedModels);
Piece->CompareBoundingBox(Min, Max);
}
}
if (Mesh)
{
Min = lcMin(Min, Mesh->mBoundingBox.Min);
Max = lcMax(Max, Mesh->mBoundingBox.Max);
}
mPieceInfo->SetBoundingBox(Min, Max);
}
2023-06-19 03:37:59 +02:00
void lcModel::SaveLDraw(QTextStream& Stream, bool SelectedOnly, lcStep LastStep) const
2016-10-05 23:28:52 +02:00
{
2021-11-15 03:34:24 +01:00
const QLatin1String LineEnding("\r\n");
2016-10-05 23:28:52 +02:00
mProperties.SaveLDraw(Stream);
std::vector<lcGroup*> CurrentGroups;
2016-10-05 23:28:52 +02:00
lcStep Step = 1;
int CurrentLine = 0;
int AddedSteps = 0;
2023-06-19 03:37:59 +02:00
bool SavedStep = false;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (SelectedOnly && !Piece->IsSelected())
continue;
2023-12-28 20:55:11 +01:00
if ((SavedStep = (LastStep != 0 && Piece->GetStepShow() > LastStep)))
2023-05-23 13:05:56 +02:00
break;
2016-10-05 23:28:52 +02:00
while (Piece->GetFileLine() > CurrentLine && CurrentLine < mFileLines.size())
{
QString Line = mFileLines[CurrentLine];
QTextStream LineStream(&Line, QIODevice::ReadOnly);
QString Token;
LineStream >> Token;
bool Skip = false;
if (Token == QLatin1String("0"))
{
LineStream >> Token;
if (Token == QLatin1String("STEP"))
{
if (Piece->GetStepShow() > Step)
Step++;
else
Skip = true;
}
}
if (!Skip)
{
2016-10-05 23:28:52 +02:00
Stream << mFileLines[CurrentLine];
if (AddedSteps > 0)
AddedSteps--;
}
2016-10-05 23:28:52 +02:00
CurrentLine++;
}
while (Piece->GetStepShow() > Step)
{
Stream << QLatin1String("0 STEP\r\n");
AddedSteps++;
2016-10-05 23:28:52 +02:00
Step++;
}
lcGroup* PieceGroup = Piece->GetGroup();
if (PieceGroup)
{
2024-05-26 22:01:34 +02:00
if (CurrentGroups.empty() || (!CurrentGroups.empty() && PieceGroup != CurrentGroups[CurrentGroups.size() - 1]))
2016-10-05 23:28:52 +02:00
{
std::deque<lcGroup*> PieceParents;
2016-10-05 23:28:52 +02:00
for (lcGroup* Group = PieceGroup; Group; Group = Group->mGroup)
PieceParents.push_front(Group);
2016-10-05 23:28:52 +02:00
std::deque<lcGroup*>::iterator ParentsToAdd = PieceParents.begin();
2016-10-05 23:28:52 +02:00
2024-05-26 22:01:34 +02:00
while (!CurrentGroups.empty())
2016-10-05 23:28:52 +02:00
{
lcGroup* Group = CurrentGroups.back();
const std::deque<lcGroup*>::iterator ParentFound = std::find(PieceParents.begin(), PieceParents.end(), Group);
2016-10-05 23:28:52 +02:00
if (ParentFound == PieceParents.end())
2016-10-05 23:28:52 +02:00
{
CurrentGroups.pop_back();
2016-10-05 23:28:52 +02:00
Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
}
else
{
ParentsToAdd = ParentFound + 1;
2016-10-05 23:28:52 +02:00
break;
}
}
for (std::deque<lcGroup*>::iterator ParentIt = ParentsToAdd; ParentIt != PieceParents.end(); ParentIt++)
2016-10-05 23:28:52 +02:00
{
lcGroup* Group = *ParentIt;
2024-05-26 22:01:34 +02:00
CurrentGroups.emplace_back(Group);
2016-10-05 23:28:52 +02:00
Stream << QLatin1String("0 !LEOCAD GROUP BEGIN ") << Group->mName << LineEnding;
}
}
}
else
{
2024-05-26 22:01:34 +02:00
while (CurrentGroups.size())
2016-10-05 23:28:52 +02:00
{
CurrentGroups.pop_back();
2016-10-05 23:28:52 +02:00
Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
}
}
if (Piece->mPieceInfo->GetSynthInfo())
{
Stream << QLatin1String("0 !LEOCAD SYNTH BEGIN\r\n");
2024-05-12 21:45:15 +02:00
const std::vector<lcPieceControlPoint>& ControlPoints = Piece->GetControlPoints();
for (const lcPieceControlPoint& ControlPoint : ControlPoints)
2016-10-05 23:28:52 +02:00
{
Stream << QLatin1String("0 !LEOCAD SYNTH CONTROL_POINT");
const float* FloatMatrix = ControlPoint.Transform;
2021-11-15 03:34:24 +01:00
const float Numbers[13] = { FloatMatrix[12], -FloatMatrix[14], FloatMatrix[13], FloatMatrix[0], -FloatMatrix[8], FloatMatrix[4], -FloatMatrix[2], FloatMatrix[10], -FloatMatrix[6], FloatMatrix[1], -FloatMatrix[9], FloatMatrix[5], ControlPoint.Scale };
2016-10-05 23:28:52 +02:00
for (int NumberIdx = 0; NumberIdx < 13; NumberIdx++)
Stream << ' ' << lcFormatValue(Numbers[NumberIdx], NumberIdx < 3 ? 4 : 6);
2016-10-05 23:28:52 +02:00
Stream << LineEnding;
}
}
Piece->SaveLDraw(Stream);
if (Piece->mPieceInfo->GetSynthInfo())
Stream << QLatin1String("0 !LEOCAD SYNTH END\r\n");
}
2023-06-19 03:37:59 +02:00
while (!SavedStep && CurrentLine < mFileLines.size())
2016-10-05 23:28:52 +02:00
{
QString Line = mFileLines[CurrentLine];
QTextStream LineStream(&Line, QIODevice::ReadOnly);
QString Token;
LineStream >> Token;
bool Skip = false;
if (Token == QLatin1String("0"))
{
LineStream >> Token;
if (Token == QLatin1String("STEP") && AddedSteps-- > 0)
Skip = true;
}
if (!Skip)
Stream << mFileLines[CurrentLine];
2016-10-05 23:28:52 +02:00
CurrentLine++;
}
2024-05-26 22:01:34 +02:00
while (CurrentGroups.size())
2016-10-05 23:28:52 +02:00
{
CurrentGroups.pop_back();
2016-10-05 23:28:52 +02:00
Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
}
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
2016-10-05 23:28:52 +02:00
if (!SelectedOnly || Camera->IsSelected())
Camera->SaveLDraw(Stream);
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2016-10-05 23:28:52 +02:00
if (!SelectedOnly || Light->IsSelected())
Light->SaveLDraw(Stream);
Stream.flush();
}
int lcModel::SplitMPD(QIODevice& Device)
{
qint64 ModelPos = Device.pos();
while (!Device.atEnd())
{
2021-11-15 03:34:24 +01:00
const qint64 Pos = Device.pos();
QString OriginalLine = Device.readLine();
QString Line = OriginalLine.trimmed();
QTextStream LineStream(&Line, QIODevice::ReadOnly);
QString Token;
LineStream >> Token;
if (Token == QLatin1String("0"))
{
LineStream >> Token;
if (Token == QLatin1String("FILE"))
{
2020-05-03 21:11:51 +02:00
if (!mProperties.mFileName.isEmpty())
{
Device.seek(Pos);
break;
}
2020-05-03 21:11:51 +02:00
SetFileName(LineStream.readAll().trimmed());
ModelPos = Pos;
}
else if (Token == QLatin1String("NOFILE"))
{
break;
}
}
}
return ModelPos;
}
2016-10-05 23:28:52 +02:00
void lcModel::LoadLDraw(QIODevice& Device, Project* Project)
{
lcPiece* Piece = nullptr;
lcCamera* Camera = nullptr;
lcLight* Light = nullptr;
std::vector<lcGroup*> CurrentGroups;
2024-05-12 21:45:15 +02:00
std::vector<lcPieceControlPoint> ControlPoints;
2016-10-05 23:28:52 +02:00
int CurrentStep = 1;
2017-01-23 04:28:05 +01:00
lcPiecesLibrary* Library = lcGetPiecesLibrary();
2016-10-05 23:28:52 +02:00
mProperties.mAuthor.clear();
mProperties.mDescription.clear();
mProperties.mComments.clear();
2020-05-03 21:11:51 +02:00
bool ReadingHeader = true;
bool FirstLine = true;
2016-10-05 23:28:52 +02:00
while (!Device.atEnd())
{
2021-11-15 03:34:24 +01:00
const qint64 Pos = Device.pos();
2016-10-05 23:28:52 +02:00
QString OriginalLine = Device.readLine();
QString Line = OriginalLine.trimmed();
QTextStream LineStream(&Line, QIODevice::ReadOnly);
QString Token;
LineStream >> Token;
if (Token == QLatin1String("0"))
{
LineStream >> Token;
if (Token == QLatin1String("FILE"))
{
2016-12-05 03:12:39 +01:00
QString Name = LineStream.readAll().trimmed();
2020-05-03 21:11:51 +02:00
if (mProperties.mFileName != Name)
2016-10-05 23:28:52 +02:00
{
Device.seek(Pos);
break;
}
continue;
}
else if (Token == QLatin1String("NOFILE"))
{
break;
}
2020-05-03 21:11:51 +02:00
if (ReadingHeader)
{
ReadingHeader = mProperties.ParseLDrawHeader(Line, FirstLine);
FirstLine = false;
if (ReadingHeader)
continue;
}
if (Token == QLatin1String("STEP"))
2016-10-05 23:28:52 +02:00
{
delete Piece;
Piece = nullptr;
2016-10-05 23:28:52 +02:00
CurrentStep++;
mFileLines.append(OriginalLine);
continue;
}
if (Token != QLatin1String("!LEOCAD"))
{
2023-08-13 15:15:52 +02:00
mFileLines.append(OriginalLine);
2016-10-05 23:28:52 +02:00
continue;
}
LineStream >> Token;
if (Token == QLatin1String("MODEL"))
{
mProperties.ParseLDrawLine(LineStream);
}
else if (Token == QLatin1String("PIECE"))
{
if (!Piece)
Piece = new lcPiece(nullptr);
2016-10-05 23:28:52 +02:00
Piece->ParseLDrawLine(LineStream);
}
else if (Token == QLatin1String("CAMERA"))
{
if (!Camera)
Camera = new lcCamera(false);
if (Camera->ParseLDrawLine(LineStream))
{
Camera->CreateName(mCameras);
2024-05-26 22:01:34 +02:00
mCameras.emplace_back(Camera);
Camera = nullptr;
2016-10-05 23:28:52 +02:00
}
}
else if (Token == QLatin1String("LIGHT"))
{
2023-08-04 22:26:29 +02:00
if (!Light)
Light = new lcLight(lcVector3(0.0f, 0.0f, 0.0f), lcLightType::Point);
2023-08-04 22:26:29 +02:00
if (Light->ParseLDrawLine(LineStream))
{
Light->CreateName(mLights);
2024-05-26 22:01:34 +02:00
mLights.emplace_back(Light);
2023-08-04 22:26:29 +02:00
Light = nullptr;
}
2016-10-05 23:28:52 +02:00
}
2023-08-09 12:35:07 +02:00
else if (Token == QLatin1String("POV_RAY"))
{
mPOVRayOptions.ParseLDrawLine(LineStream);
2016-10-05 23:28:52 +02:00
}
else if (Token == QLatin1String("GROUP"))
{
LineStream >> Token;
if (Token == QLatin1String("BEGIN"))
{
QString Name = LineStream.readAll().trimmed();
lcGroup* Group = GetGroup(Name, true);
2024-05-26 22:01:34 +02:00
if (!CurrentGroups.empty())
Group->mGroup = CurrentGroups[CurrentGroups.size() - 1];
2016-10-05 23:28:52 +02:00
else
Group->mGroup = nullptr;
2024-05-26 22:01:34 +02:00
CurrentGroups.emplace_back(Group);
2016-10-05 23:28:52 +02:00
}
else if (Token == QLatin1String("END"))
{
2024-05-26 22:01:34 +02:00
if (!CurrentGroups.empty())
CurrentGroups.pop_back();
2016-10-05 23:28:52 +02:00
}
}
else if (Token == QLatin1String("SYNTH"))
{
LineStream >> Token;
if (Token == QLatin1String("BEGIN"))
{
2024-05-12 21:45:15 +02:00
ControlPoints.clear();
2016-10-05 23:28:52 +02:00
}
else if (Token == QLatin1String("END"))
{
2024-05-12 21:45:15 +02:00
ControlPoints.clear();
2016-10-05 23:28:52 +02:00
}
else if (Token == QLatin1String("CONTROL_POINT"))
{
float Numbers[13];
for (int TokenIdx = 0; TokenIdx < 13; TokenIdx++)
LineStream >> Numbers[TokenIdx];
2024-05-12 21:45:15 +02:00
lcPieceControlPoint& PieceControlPoint = ControlPoints.emplace_back();
2016-10-05 23:28:52 +02:00
PieceControlPoint.Transform = lcMatrix44(lcVector4(Numbers[3], Numbers[9], -Numbers[6], 0.0f), lcVector4(Numbers[5], Numbers[11], -Numbers[8], 0.0f),
lcVector4(-Numbers[4], -Numbers[10], Numbers[7], 0.0f), lcVector4(Numbers[0], Numbers[2], -Numbers[1], 1.0f));
PieceControlPoint.Scale = Numbers[12];
}
}
continue;
}
else if (Token == QLatin1String("1"))
{
2020-05-03 21:11:51 +02:00
ReadingHeader = false;
2016-10-05 23:28:52 +02:00
int ColorCode;
LineStream >> ColorCode;
float IncludeMatrix[12];
for (int TokenIdx = 0; TokenIdx < 12; TokenIdx++)
LineStream >> IncludeMatrix[TokenIdx];
lcMatrix44 IncludeTransform(lcVector4(IncludeMatrix[3], IncludeMatrix[6], IncludeMatrix[9], 0.0f), lcVector4(IncludeMatrix[4], IncludeMatrix[7], IncludeMatrix[10], 0.0f),
lcVector4(IncludeMatrix[5], IncludeMatrix[8], IncludeMatrix[11], 0.0f), lcVector4(IncludeMatrix[0], IncludeMatrix[1], IncludeMatrix[2], 1.0f));
QString PartId = LineStream.readAll().trimmed();
if (PartId.isEmpty())
continue;
QByteArray CleanId = PartId.toLatin1().toUpper().replace('\\', '/');
2016-10-05 23:28:52 +02:00
if (Library->IsPrimitive(CleanId.constData()))
2016-10-05 23:28:52 +02:00
{
2023-08-13 15:15:52 +02:00
mFileLines.append(OriginalLine);
2016-10-05 23:28:52 +02:00
}
else
{
if (!Piece)
Piece = new lcPiece(nullptr);
2016-10-05 23:28:52 +02:00
2024-05-26 22:01:34 +02:00
if (!CurrentGroups.empty())
Piece->SetGroup(CurrentGroups[CurrentGroups.size() - 1]);
2016-10-05 23:28:52 +02:00
PieceInfo* Info = Library->FindPiece(PartId.toLatin1().constData(), Project, true, true);
2016-10-05 23:28:52 +02:00
2021-11-15 03:34:24 +01:00
const float* Matrix = IncludeTransform;
const lcMatrix44 Transform(lcVector4(Matrix[0], Matrix[2], -Matrix[1], 0.0f), lcVector4(Matrix[8], Matrix[10], -Matrix[9], 0.0f),
lcVector4(-Matrix[4], -Matrix[6], Matrix[5], 0.0f), lcVector4(Matrix[12], Matrix[14], -Matrix[13], 1.0f));
2016-10-05 23:28:52 +02:00
Piece->SetFileLine(mFileLines.size());
Piece->SetPieceInfo(Info, PartId, false);
2016-10-05 23:28:52 +02:00
Piece->Initialize(Transform, CurrentStep);
Piece->SetColorCode(ColorCode);
Synthesis of Technic Universal Joints (#456) * Split synth info initialzation by type. We are going to remove the type enumeration and use a class hierarchy instead. This preparation will then be helpful. * Make Add...Parts() overrides of a virtual AddPart() function. Since we have a class hierarchy for the different synthesized pieces, we can now turn a case distinction into a virtual function call. * Move initialization based on type to derived class constructors. Move initialization of end transformations of flexible parts into class lcSynthInfoCurved. * Make GetDefaultControlPoints() virtual with overrides. * Remove obsolete enum lcSynthType. We have replaced its purpose by derived classes by now. * Initialize shock absorbers' spring part ID early. This removes the awkward early return that is needed in the if-else cascade. * Split lcSynthInfo into derived classes for curved and straight pieces. * Only curved parts have varying sections, start, middle, and end properties. Move the properties from the base class to the derived class that needs them. * Use derived classes to mark synthesized objects of different kinds. We will extend the derived classes in the upcoming commits. * PieceInfo is only needed to synthesize some hoses and shock absorbers. * Initialize edge part IDs of flexible hoses early. This removes another case distinction in AddParts(). * Verify the number of control points loaded from a model file. * Synthesize Technic universal joints. The direction of one end can be changed so that it points to the control point. * Technic universal joints need only the position of the control point. * Synthesize legacy universal joints.
2020-03-30 21:17:08 +02:00
Piece->VerifyControlPoints(ControlPoints);
2016-10-05 23:28:52 +02:00
Piece->SetControlPoints(ControlPoints);
2024-05-12 21:45:15 +02:00
ControlPoints.clear();
if (Piece->mPieceInfo->IsModel() && Piece->mPieceInfo->GetModel()->IncludesModel(this))
{
delete Piece;
Piece = nullptr;
continue;
}
2016-10-05 23:28:52 +02:00
AddPiece(Piece);
Piece = nullptr;
2016-10-05 23:28:52 +02:00
}
}
else
2020-05-03 21:11:51 +02:00
{
ReadingHeader = false;
mFileLines.append(OriginalLine);
}
FirstLine = false;
2016-10-05 23:28:52 +02:00
}
mCurrentStep = CurrentStep;
CalculateStep(mCurrentStep);
2017-01-23 04:28:05 +01:00
Library->WaitForLoadQueue();
Library->mBuffersDirty = true;
Library->UnloadUnusedParts();
2016-10-05 23:28:52 +02:00
delete Piece;
delete Camera;
delete Light;
}
bool lcModel::LoadBinary(lcFile* file)
{
2017-12-02 21:22:04 +01:00
qint32 i, count;
2016-10-05 23:28:52 +02:00
char id[32];
2017-12-02 21:22:04 +01:00
quint32 rgb;
2016-10-05 23:28:52 +02:00
float fv = 0.4f;
2017-12-02 21:22:04 +01:00
quint8 ch;
quint16 sh;
2016-10-05 23:28:52 +02:00
file->Seek(0, SEEK_SET);
file->ReadBuffer(id, 32);
sscanf(&id[7], "%f", &fv);
if (memcmp(id, "LeoCAD ", 7))
return false;
2016-10-05 23:28:52 +02:00
if (fv == 0.0f)
{
2021-11-15 03:34:24 +01:00
const lconv *loc = localeconv();
2016-10-05 23:28:52 +02:00
id[8] = loc->decimal_point[0];
sscanf(&id[7], "%f", &fv);
if (fv == 0.0f)
return false;
}
if (fv > 0.4f)
file->ReadFloats(&fv, 1);
file->ReadU32(&rgb, 1);
if (fv < 0.6f) // old view
{
double eye[3], target[3];
file->ReadDoubles(eye, 3);
file->ReadDoubles(target, 3);
}
file->Seek(28, SEEK_CUR);
file->ReadS32(&i, 1);
mCurrentStep = i;
if (fv > 0.8f)
file->ReadU32();//m_nScene
file->ReadS32(&count, 1);
lcPiecesLibrary* Library = lcGetPiecesLibrary();
2024-06-20 04:22:18 +02:00
const size_t FirstNewPiece = mPieces.size();
2016-10-05 23:28:52 +02:00
while (count--)
{
if (fv > 0.4f)
{
lcPiece* pPiece = new lcPiece(nullptr);
2016-10-05 23:28:52 +02:00
pPiece->FileLoad(*file);
AddPiece(pPiece);
}
else
{
char name[LC_PIECE_NAME_LEN];
lcVector3 pos, rot;
2017-12-02 21:22:04 +01:00
quint8 color, step, group;
2016-10-05 23:28:52 +02:00
file->ReadFloats(pos, 3);
file->ReadFloats(rot, 3);
file->ReadU8(&color, 1);
file->ReadBuffer(name, 9);
strcat(name, ".dat");
2016-10-05 23:28:52 +02:00
file->ReadU8(&step, 1);
file->ReadU8(&group, 1);
pos *= 25.0f;
lcMatrix44 WorldMatrix = lcMul(lcMatrix44RotationZ(rot[2] * LC_DTOR), lcMul(lcMatrix44RotationY(rot[1] * LC_DTOR), lcMatrix44RotationX(rot[0] * LC_DTOR)));
WorldMatrix.SetTranslation(pos);
PieceInfo* pInfo = Library->FindPiece(name, nullptr, true, false);
2016-10-05 23:28:52 +02:00
lcPiece* pPiece = new lcPiece(pInfo);
pPiece->Initialize(WorldMatrix, step);
pPiece->SetColorCode(lcGetColorCodeFromOriginalColor(color));
AddPiece(pPiece);
// pPiece->SetGroup((lcGroup*)group);
}
}
if (fv >= 0.4f)
{
file->ReadBuffer(&ch, 1);
if (ch == 0xFF) file->ReadU16(&sh, 1); else sh = ch;
if (sh > 100)
file->Seek(sh, SEEK_CUR);
else
{
2017-02-07 18:35:11 +01:00
QByteArray Author;
Author.resize(sh + 1);
file->ReadBuffer(Author.data(), sh);
Author[sh] = 0;
mProperties.mAuthor = QString::fromUtf8(Author);
2016-10-05 23:28:52 +02:00
}
file->ReadBuffer(&ch, 1);
if (ch == 0xFF) file->ReadU16(&sh, 1); else sh = ch;
if (sh > 100)
file->Seek(sh, SEEK_CUR);
else
{
2017-02-07 18:35:11 +01:00
QByteArray Description;
Description.resize(sh + 1);
file->ReadBuffer(Description.data(), sh);
Description[sh] = 0;
mProperties.mDescription = QString::fromUtf8(Description);
2016-10-05 23:28:52 +02:00
}
file->ReadBuffer(&ch, 1);
if (ch == 0xFF && fv < 1.3f) file->ReadU16(&sh, 1); else sh = ch;
if (sh > 255)
file->Seek(sh, SEEK_CUR);
else
{
2017-02-07 18:35:11 +01:00
QByteArray Comments;
Comments.resize(sh + 1);
file->ReadBuffer(Comments.data(), sh);
Comments[sh] = 0;
mProperties.mComments = QString::fromUtf8(Comments);
2016-10-05 23:28:52 +02:00
mProperties.mComments.replace(QLatin1String("\r\n"), QLatin1String("\n"));
}
}
if (fv >= 0.5f)
{
const size_t NumGroups = mGroups.size();
2016-10-05 23:28:52 +02:00
file->ReadS32(&count, 1);
for (i = 0; i < count; i++)
2024-05-26 22:01:34 +02:00
mGroups.emplace_back(new lcGroup());
2016-10-05 23:28:52 +02:00
for (size_t GroupIdx = NumGroups; GroupIdx < mGroups.size(); GroupIdx++)
2016-10-05 23:28:52 +02:00
{
lcGroup* Group = mGroups[GroupIdx].get();
2016-10-05 23:28:52 +02:00
if (fv < 1.0f)
{
char Name[LC_MAX_GROUP_NAME + 1];
file->ReadBuffer(Name, sizeof(Name));
Group->mName = QString::fromUtf8(Name);
file->ReadBuffer(&ch, 1);
Group->mGroup = (lcGroup*)-1;
}
else
Group->FileLoad(file);
}
for (size_t GroupIdx = NumGroups; GroupIdx < mGroups.size(); GroupIdx++)
2016-10-05 23:28:52 +02:00
{
lcGroup* Group = mGroups[GroupIdx].get();
2016-10-05 23:28:52 +02:00
2017-12-02 21:33:28 +01:00
i = (qint32)(quintptr)(Group->mGroup);
Group->mGroup = nullptr;
2016-10-05 23:28:52 +02:00
if (i > 0xFFFF || i == -1)
continue;
Group->mGroup = mGroups[NumGroups + i].get();
2016-10-05 23:28:52 +02:00
}
2024-06-20 04:22:18 +02:00
for (size_t PieceIndex = FirstNewPiece; PieceIndex < mPieces.size(); PieceIndex++)
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
lcPiece* Piece = mPieces[PieceIndex].get();
2016-10-05 23:28:52 +02:00
2017-12-02 21:33:28 +01:00
i = (qint32)(quintptr)(Piece->GetGroup());
Piece->SetGroup(nullptr);
2016-10-05 23:28:52 +02:00
if (i > 0xFFFF || i == -1)
continue;
Piece->SetGroup(mGroups[NumGroups + i].get());
2016-10-05 23:28:52 +02:00
}
RemoveEmptyGroups();
}
if (fv >= 0.6f)
{
if (fv < 1.0f)
file->Seek(4, SEEK_CUR);
else
file->Seek(2, SEEK_CUR);
file->ReadS32(&count, 1);
for (i = 0; i < count; i++)
lcCamera::FileLoad(*file);
2016-10-05 23:28:52 +02:00
}
if (fv >= 0.7f)
{
2017-04-21 23:17:03 +02:00
file->Seek(24, SEEK_CUR);
2016-10-05 23:28:52 +02:00
if (fv < 1.3f)
{
file->ReadU8(&ch, 1);
if (ch == 0xFF)
file->ReadU16(&sh, 1);
sh = ch;
}
else
file->ReadU16(&sh, 1);
2021-01-14 23:51:43 +01:00
file->Seek(sh, SEEK_CUR); // Background
2016-10-05 23:28:52 +02:00
}
if (fv >= 0.8f)
{
file->ReadBuffer(&ch, 1);
file->Seek(ch, SEEK_CUR);
file->ReadBuffer(&ch, 1);
file->Seek(ch, SEEK_CUR);
}
if (fv > 0.9f)
{
file->ReadU32(&rgb, 1);
mProperties.mAmbientColor[0] = (float)((unsigned char) (rgb))/255;
mProperties.mAmbientColor[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
mProperties.mAmbientColor[2] = (float)((unsigned char) ((rgb) >> 16))/255;
if (fv < 1.3f)
file->Seek(23, SEEK_CUR);
else
file->Seek(11, SEEK_CUR);
}
if (fv > 1.0f)
{
file->ReadU32(&rgb, 1);
file->ReadU32(&rgb, 1);
}
CalculateStep(mCurrentStep);
lcGetPiecesLibrary()->UnloadUnusedParts();
return true;
}
2017-06-26 03:20:34 +02:00
bool lcModel::LoadLDD(const QString& FileData)
{
2020-04-19 04:38:29 +02:00
std::vector<lcPiece*> Pieces;
std::vector<std::vector<lcPiece*>> Groups;
2023-08-13 15:15:52 +02:00
2017-07-08 18:29:35 +02:00
if (!lcImportLXFMLFile(FileData, Pieces, Groups))
2017-06-26 03:20:34 +02:00
return false;
for (lcPiece* Piece : Pieces)
AddPiece(Piece);
2020-04-19 04:38:29 +02:00
for (const std::vector<lcPiece*>& Group : Groups)
2017-06-26 03:20:34 +02:00
{
lcGroup* NewGroup = AddGroup(tr("Group #"), nullptr);
for (lcPiece* Piece : Group)
Piece->SetGroup(NewGroup);
}
lcPiecesLibrary* Library = lcGetPiecesLibrary();
CalculateStep(mCurrentStep);
Library->WaitForLoadQueue();
Library->mBuffersDirty = true;
Library->UnloadUnusedParts();
return true;
}
2017-08-20 22:47:53 +02:00
bool lcModel::LoadInventory(const QByteArray& Inventory)
{
QJsonDocument Document = QJsonDocument::fromJson(Inventory);
QJsonObject Root = Document.object();
QJsonArray Parts = Root["results"].toArray();
lcPiecesLibrary* Library = lcGetPiecesLibrary();
for (const QJsonValue& Part : Parts)
{
QJsonObject PartObject = Part.toObject();
QByteArray PartID = PartObject["part"].toObject()["part_num"].toString().toLatin1();
QJsonArray PartIDArray = PartObject["part"].toObject()["external_ids"].toObject()["LDraw"].toArray();
if (!PartIDArray.isEmpty())
PartID = PartIDArray.first().toString().toLatin1();
2017-08-20 22:47:53 +02:00
int Quantity = PartObject["quantity"].toInt();
int ColorCode = 16;
QJsonArray ColorArray = PartObject["color"].toObject()["external_ids"].toObject()["LDraw"].toObject()["ext_ids"].toArray();
if (!ColorArray.isEmpty())
ColorCode = ColorArray.first().toInt();
PieceInfo* Info = Library->FindPiece(PartID + ".dat", nullptr, true, false);
while (Quantity--)
{
lcPiece* Piece = new lcPiece(nullptr);
Piece->SetPieceInfo(Info, QString(), false);
Piece->Initialize(lcMatrix44Identity(), 1);
Piece->SetColorCode(ColorCode);
AddPiece(Piece);
}
}
2024-05-26 22:01:34 +02:00
if (mPieces.empty())
2017-08-27 00:49:46 +02:00
return false;
2017-08-20 22:47:53 +02:00
Library->WaitForLoadQueue();
Library->mBuffersDirty = true;
Library->UnloadUnusedParts();
2017-08-27 00:49:46 +02:00
auto RoundBounds = [](float& Value)
{
Value = ((Value < 0.0f) ? floor((Value - 5.0f) / 10.0f) : ceil((Value + 5.0f) / 10.0f)) * 10.0f;
};
2021-11-15 03:34:24 +01:00
constexpr float TargetHeight = 800.0f;
2017-08-27 00:49:46 +02:00
float CurrentX = 0.0f;
float CurrentY = 0.0f;
float ColumnWidth = 0.0f;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2017-08-27 00:49:46 +02:00
{
lcBoundingBox BoundingBox = Piece->mPieceInfo->GetBoundingBox();
RoundBounds(BoundingBox.Min.x);
RoundBounds(BoundingBox.Min.y);
RoundBounds(BoundingBox.Max.x);
RoundBounds(BoundingBox.Max.y);
2021-11-15 03:34:24 +01:00
const float PieceWidth = BoundingBox.Max.x - BoundingBox.Min.x;
const float PieceHeight = BoundingBox.Max.y - BoundingBox.Min.y;
2017-08-27 00:49:46 +02:00
if (CurrentY + PieceHeight > TargetHeight)
{
CurrentY = 0.0f;
CurrentX += ColumnWidth;
ColumnWidth = 0.0f;
}
Piece->SetPosition(lcVector3(CurrentX + PieceWidth / 2.0f, CurrentY + PieceHeight / 2.0f, 0.0f), 1, false);
CurrentY += PieceHeight;
ColumnWidth = qMax(ColumnWidth, PieceWidth);
}
CalculateStep(mCurrentStep);
2017-08-20 22:47:53 +02:00
return true;
}
2016-10-05 23:28:52 +02:00
void lcModel::Merge(lcModel* Other)
{
2024-06-20 04:22:18 +02:00
for (std::unique_ptr<lcPiece>& Piece : Other->mPieces)
2016-10-05 23:28:52 +02:00
{
Piece->SetFileLine(-1);
2024-06-20 04:22:18 +02:00
AddPiece(Piece.release());
2016-10-05 23:28:52 +02:00
}
2024-06-20 04:22:18 +02:00
Other->mPieces.clear();
2016-10-05 23:28:52 +02:00
2024-06-04 18:34:38 +02:00
for (std::unique_ptr<lcCamera>& Camera : Other->mCameras)
2016-10-05 23:28:52 +02:00
{
Camera->CreateName(mCameras);
2024-06-04 18:34:38 +02:00
mCameras.emplace_back(std::move(Camera));
2016-10-05 23:28:52 +02:00
}
2024-06-04 18:34:38 +02:00
Other->mCameras.clear();
2016-10-05 23:28:52 +02:00
2024-06-04 18:08:54 +02:00
for (std::unique_ptr<lcLight>& Light : Other->mLights)
2016-10-05 23:28:52 +02:00
{
Light->CreateName(mLights);
2024-06-04 18:08:54 +02:00
mLights.emplace_back(std::move(Light));
2016-10-05 23:28:52 +02:00
}
2024-06-04 18:08:54 +02:00
Other->mLights.clear();
2016-10-05 23:28:52 +02:00
for (std::vector<std::unique_ptr<lcGroup>>::iterator GroupIt = Other->mGroups.begin(); GroupIt != Other->mGroups.end(); GroupIt++)
2016-10-05 23:28:52 +02:00
{
std::unique_ptr<lcGroup>& Group = *GroupIt;
2016-10-05 23:28:52 +02:00
Group->CreateName(mGroups);
mGroups.emplace_back(std::move(Group));
2016-10-05 23:28:52 +02:00
}
Other->mGroups.clear();
2016-10-05 23:28:52 +02:00
delete Other;
gMainWindow->UpdateTimeline(false, false);
}
void lcModel::Cut()
{
Copy();
if (RemoveSelectedObjects())
{
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Cutting"));
2016-10-05 23:28:52 +02:00
}
}
void lcModel::Copy()
{
QByteArray File;
QTextStream Stream(&File, QIODevice::WriteOnly);
2023-06-19 03:37:59 +02:00
SaveLDraw(Stream, true, 0);
2016-10-05 23:28:52 +02:00
gApplication->ExportClipboard(File);
2016-10-05 23:28:52 +02:00
}
void lcModel::Paste(bool PasteToCurrentStep)
2016-10-05 23:28:52 +02:00
{
if (gApplication->mClipboard.isEmpty())
2016-10-05 23:28:52 +02:00
return;
2020-12-16 02:19:32 +01:00
lcModel* Model = new lcModel(QString(), nullptr, false);
2016-10-05 23:28:52 +02:00
QBuffer Buffer(&gApplication->mClipboard);
2016-10-05 23:28:52 +02:00
Buffer.open(QIODevice::ReadOnly);
Model->LoadLDraw(Buffer, lcGetActiveProject());
2024-06-20 04:22:18 +02:00
const std::vector<std::unique_ptr<lcPiece>>& PastedPieces = Model->mPieces;
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> SelectedObjects;
SelectedObjects.reserve(PastedPieces.size());
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : PastedPieces)
2016-10-05 23:28:52 +02:00
{
Piece->SetFileLine(-1);
2016-10-05 23:28:52 +02:00
if (PasteToCurrentStep)
{
Piece->SetStepShow(mCurrentStep);
2024-06-20 04:22:18 +02:00
SelectedObjects.emplace_back(Piece.get());
}
else
{
if (Piece->GetStepShow() <= mCurrentStep)
2024-06-20 04:22:18 +02:00
SelectedObjects.emplace_back(Piece.get());
}
2016-10-05 23:28:52 +02:00
}
Merge(Model);
SaveCheckpoint(tr("Pasting"));
2024-05-26 22:01:34 +02:00
if (SelectedObjects.size() == 1)
2017-11-22 02:58:36 +01:00
ClearSelectionAndSetFocus(SelectedObjects[0], LC_PIECE_SECTION_POSITION, false);
2016-10-05 23:28:52 +02:00
else
SetSelectionAndFocus(SelectedObjects, nullptr, 0, false);
2016-10-05 23:28:52 +02:00
CalculateStep(mCurrentStep);
gMainWindow->UpdateTimeline(false, false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
2017-03-09 00:49:57 +01:00
void lcModel::DuplicateSelectedPieces()
{
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> NewPieces;
lcPiece* Focus = nullptr;
std::map<lcGroup*, lcGroup*> GroupMap;
std::function<lcGroup*(lcGroup*)> GetNewGroup = [this, &GroupMap, &GetNewGroup](lcGroup* Group)
{
const auto GroupIt = GroupMap.find(Group);
if (GroupIt != GroupMap.end())
return GroupIt->second;
else
{
lcGroup* Parent = Group->mGroup ? GetNewGroup(Group->mGroup) : nullptr;
QString GroupName = Group->mName;
while (!GroupName.isEmpty())
{
2021-11-15 03:34:24 +01:00
const QChar Last = GroupName[GroupName.size() - 1];
if (Last.isDigit())
GroupName.chop(1);
else
break;
}
if (GroupName.isEmpty())
GroupName = Group->mName;
lcGroup* NewGroup = AddGroup(GroupName, Parent);
GroupMap[Group] = NewGroup;
return NewGroup;
}
};
2017-03-09 00:49:57 +01:00
2024-06-20 04:22:18 +02:00
for (size_t PieceIdx = 0; PieceIdx < mPieces.size(); PieceIdx++)
2017-03-09 00:49:57 +01:00
{
2024-06-20 04:22:18 +02:00
lcPiece* Piece = mPieces[PieceIdx].get();
2017-03-09 00:49:57 +01:00
if (!Piece->IsSelected())
continue;
lcPiece* NewPiece = new lcPiece(*Piece);
NewPiece->UpdatePosition(mCurrentStep);
2024-05-26 22:01:34 +02:00
NewPieces.emplace_back(NewPiece);
2017-03-09 00:49:57 +01:00
if (Piece->IsFocused())
Focus = NewPiece;
2017-03-09 00:49:57 +01:00
PieceIdx++;
InsertPiece(NewPiece, PieceIdx);
lcGroup* Group = Piece->GetGroup();
if (Group)
Piece->SetGroup(GetNewGroup(Group));
2017-03-09 00:49:57 +01:00
}
2024-05-26 22:01:34 +02:00
if (NewPieces.empty())
2017-03-09 00:49:57 +01:00
return;
gMainWindow->UpdateTimeline(false, false);
SetSelectionAndFocus(NewPieces, Focus, LC_PIECE_SECTION_POSITION, false);
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Duplicating Pieces"));
}
void lcModel::PaintSelectedPieces()
{
SetSelectedPiecesColorIndex(gMainWindow->mColorIndex);
}
2021-11-15 03:34:24 +01:00
void lcModel::GetScene(lcScene* Scene, const lcCamera* ViewCamera, bool AllowHighlight, bool AllowFade) const
2016-10-05 23:28:52 +02:00
{
if (mPieceInfo)
mPieceInfo->AddRenderMesh(*Scene);
2016-10-05 23:28:52 +02:00
lcPiece* FocusPiece = nullptr;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2020-01-02 02:06:17 +01:00
{
2016-10-05 23:28:52 +02:00
if (Piece->IsVisible(mCurrentStep))
2020-01-02 02:06:17 +01:00
{
if (Piece->IsFocused())
FocusPiece = Piece.get();
2021-11-15 03:34:24 +01:00
const lcStep StepShow = Piece->GetStepShow();
2020-01-02 02:06:17 +01:00
Piece->AddMainModelRenderMeshes(Scene, AllowHighlight && StepShow == mCurrentStep, AllowFade && StepShow < mCurrentStep);
}
}
2016-10-05 23:28:52 +02:00
2020-12-05 20:02:10 +01:00
if (Scene->GetDrawInterface() && !Scene->GetActiveSubmodelInstance())
2016-10-05 23:28:52 +02:00
{
if (FocusPiece)
UpdateTrainTrackConnections(FocusPiece);
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
if (Camera.get() != ViewCamera && Camera->IsVisible())
Scene->AddInterfaceObject(Camera.get());
2016-10-05 23:28:52 +02:00
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2016-10-05 23:28:52 +02:00
if (Light->IsVisible())
2024-06-04 18:08:54 +02:00
Scene->AddInterfaceObject(Light.get());
2016-10-05 23:28:52 +02:00
}
}
2020-12-05 20:02:10 +01:00
void lcModel::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
{
2019-03-13 03:51:04 +01:00
if (Piece->IsVisibleInSubModel())
{
if (Piece->IsFocused())
UpdateTrainTrackConnections(Piece.get());
Piece->AddSubModelRenderMeshes(Scene, WorldMatrix, DefaultColorIndex, RenderMeshState, ParentActive);
}
}
2016-10-05 23:28:52 +02:00
}
QImage lcModel::GetStepImage(bool Zoom, int Width, int Height, lcStep Step)
2016-10-05 23:28:52 +02:00
{
2021-11-15 03:34:24 +01:00
const lcView* ActiveView = gMainWindow->GetActiveView();
const lcStep CurrentStep = mCurrentStep;
2020-12-04 21:49:01 +01:00
lcCamera* Camera = ActiveView->GetCamera();
2020-12-25 19:54:33 +01:00
lcView View(lcViewType::View, this);
View.SetCamera(Camera, true);
View.SetOffscreenContext();
View.MakeCurrent();
2016-10-05 23:28:52 +02:00
if (!View.BeginRenderToImage(Width, Height))
{
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
2019-12-31 01:04:58 +01:00
return QImage();
}
2019-12-31 01:04:58 +01:00
SetTemporaryStep(Step);
if (Zoom)
ZoomExtents(Camera, (float)Width / (float)Height, lcMatrix44Identity());
2019-12-31 01:04:58 +01:00
View.OnDraw();
QImage Image = View.GetRenderImage();
View.EndRenderToImage();
2019-12-31 01:04:58 +01:00
SetTemporaryStep(CurrentStep);
if (!mActive)
CalculateStep(LC_STEP_MAX);
return Image;
}
2021-01-16 23:43:24 +01:00
QImage lcModel::GetPartsListImage(int MaxWidth, lcStep Step, quint32 BackgroundColor, QFont Font, QColor TextColor) const
2020-01-21 00:39:20 +01:00
{
lcPartsList PartsList;
if (Step == 0)
GetPartsList(gDefaultColor, true, false, PartsList);
else
2023-06-19 03:37:59 +02:00
GetPartsListForStep(Step, gDefaultColor, PartsList, false);
2020-01-21 00:39:20 +01:00
if (PartsList.empty())
return QImage();
struct lcPartsListImage
{
QImage Thumbnail;
const PieceInfo* Info;
int ColorIndex;
int Count;
QRect Bounds;
QPoint Position;
};
std::vector<lcPartsListImage> Images;
for (const auto& PartIt : PartsList)
{
for (const auto& ColorIt : PartIt.second)
{
Images.push_back(lcPartsListImage());
lcPartsListImage& Image = Images.back();
Image.Info = PartIt.first;
Image.ColorIndex = ColorIt.first;
Image.Count = ColorIt.second;
}
}
auto ImageCompare = [](const lcPartsListImage& Image1, const lcPartsListImage& Image2)
{
if (Image1.ColorIndex != Image2.ColorIndex)
return Image1.ColorIndex < Image2.ColorIndex;
return strcmp(Image1.Info->m_strDescription, Image2.Info->m_strDescription) < 0;
};
std::sort(Images.begin(), Images.end(), ImageCompare);
2021-01-10 02:57:24 +01:00
lcView View(lcViewType::PartsList, nullptr);
View.SetOffscreenContext();
View.MakeCurrent();
lcContext* Context = View.mContext;
2020-01-21 00:39:20 +01:00
const int ThumbnailSize = qMin(MaxWidth, 512);
2021-01-10 02:57:24 +01:00
View.SetSize(ThumbnailSize, ThumbnailSize);
2020-01-21 00:39:20 +01:00
2021-01-10 02:57:24 +01:00
if (!View.BeginRenderToImage(ThumbnailSize, ThumbnailSize))
2020-01-21 00:39:20 +01:00
{
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
return QImage();
}
float OrthoSize = 200.0f;
lcMatrix44 ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
2021-11-15 03:34:24 +01:00
const lcMatrix44 ViewMatrix = lcMatrix44LookAt(lcVector3(-100.0f, -100.0f, 75.0f), lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 1.0f));
2020-01-21 00:39:20 +01:00
const int Viewport[4] = { 0, 0, ThumbnailSize, ThumbnailSize };
float ExtraPixels = 0.0f;
2021-11-15 03:34:24 +01:00
for (const lcPartsListImage& Image : Images)
2020-01-21 00:39:20 +01:00
{
const PieceInfo* Info = Image.Info;
const lcBoundingBox& BoundingBox = Info->GetBoundingBox();
lcVector3 Points[8];
lcGetBoxCorners(BoundingBox.Min, BoundingBox.Max, Points);
for (lcVector3& Point : Points)
{
Point = lcProjectPoint(Point, ViewMatrix, ProjectionMatrix, Viewport);
ExtraPixels = qMax(ExtraPixels, -Point.x);
ExtraPixels = qMax(ExtraPixels, Point.x - ThumbnailSize);
ExtraPixels = qMax(ExtraPixels, -Point.y);
ExtraPixels = qMax(ExtraPixels, Point.y - ThumbnailSize);
}
}
if (ExtraPixels)
{
OrthoSize += ExtraPixels * (2.0f * OrthoSize / ThumbnailSize);
ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
}
Context->SetViewport(0, 0, ThumbnailSize, ThumbnailSize);
Context->SetDefaultState();
Context->SetProjectionMatrix(ProjectionMatrix);
for (lcPartsListImage& Image : Images)
{
2021-01-17 00:14:23 +01:00
View.BindRenderFramebuffer();
2021-01-16 23:43:24 +01:00
Context->ClearColorAndDepth(lcVector4(lcVector3FromColor(BackgroundColor), 0.0f));
2020-01-21 00:39:20 +01:00
lcScene Scene;
2021-01-10 01:02:23 +01:00
const lcPreferences& Preferences = lcGetPreferences();
lcShadingMode ShadingMode = Preferences.mShadingMode;
if (ShadingMode == lcShadingMode::Wireframe)
ShadingMode = lcShadingMode::Flat;
Scene.SetShadingMode(ShadingMode);
2020-01-21 00:39:20 +01:00
Scene.SetAllowLOD(false);
Scene.Begin(ViewMatrix);
2020-12-05 20:02:10 +01:00
Image.Info->AddRenderMeshes(&Scene, lcMatrix44Identity(), Image.ColorIndex, lcRenderMeshState::Default, true);
2020-01-21 00:39:20 +01:00
Scene.End();
Scene.Draw(Context);
2021-01-17 00:14:23 +01:00
View.UnbindRenderFramebuffer();
2021-01-10 02:57:24 +01:00
Image.Thumbnail = View.GetRenderFramebufferImage().convertToFormat(QImage::Format_ARGB32);
2020-01-21 00:39:20 +01:00
}
2021-01-10 02:57:24 +01:00
View.EndRenderToImage();
2020-01-21 00:39:20 +01:00
Context->ClearResources();
auto CalculateImageBounds = [](lcPartsListImage& Image)
{
2021-11-15 03:34:24 +01:00
const QImage& Thumbnail = Image.Thumbnail;
const int Width = Thumbnail.width();
const int Height = Thumbnail.height();
2020-01-21 00:39:20 +01:00
int MinX = Width;
int MinY = Height;
int MaxX = 0;
int MaxY = 0;
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
if (qAlpha(Thumbnail.pixel(x, y)))
{
MinX = qMin(x, MinX);
MinY = qMin(y, MinY);
MaxX = qMax(x, MaxX);
MaxY = qMax(y, MaxY);
}
}
}
Image.Bounds = QRect(QPoint(MinX, MinY), QPoint(MaxX, MaxY));
};
QtConcurrent::blockingMap(Images, CalculateImageBounds);
QImage DummyImage(16, 16, QImage::Format_ARGB32);
QPainter DummyPainter(&DummyImage);
DummyPainter.setFont(Font);
QFontMetrics FontMetrics = DummyPainter.fontMetrics();
2021-11-15 03:34:24 +01:00
const int Ascent = FontMetrics.ascent();
2020-01-21 00:39:20 +01:00
int CurrentHeight = 0;
int ImageWidth = MaxWidth;
2021-11-15 03:34:24 +01:00
for (const lcPartsListImage& Image : Images)
2020-01-21 00:39:20 +01:00
CurrentHeight = qMax(Image.Bounds.height() + Ascent, CurrentHeight);
for (;;)
{
int CurrentWidth = 0;
int CurrentX = 0;
int CurrentY = 0;
int ColumnWidth = 0;
2021-11-15 03:34:24 +01:00
constexpr int Spacing = 20;
2020-01-21 00:39:20 +01:00
int NextHeightIncrease = INT_MAX;
for (lcPartsListImage& Image : Images)
{
if (CurrentY + Image.Bounds.height() + Ascent > CurrentHeight)
{
2021-11-15 03:34:24 +01:00
const int NeededSpace = Image.Bounds.height() + Ascent - (CurrentHeight - CurrentY);
2020-01-21 00:39:20 +01:00
NextHeightIncrease = qMin(NeededSpace, NextHeightIncrease);
CurrentY = 0;
CurrentX += ColumnWidth + Spacing;
ColumnWidth = 0;
}
Image.Position = QPoint(CurrentX, CurrentY);
CurrentY += Image.Bounds.height() + Ascent + Spacing;
CurrentWidth = qMax(CurrentWidth, CurrentX + Image.Bounds.width());
ColumnWidth = qMax(ColumnWidth, Image.Bounds.width());
}
if (CurrentWidth <= MaxWidth)
2020-01-21 00:39:20 +01:00
{
ImageWidth = CurrentWidth;
break;
}
CurrentHeight += NextHeightIncrease;
}
QImage PainterImage(ImageWidth + 40, CurrentHeight + 40, QImage::Format_ARGB32);
2021-01-16 23:43:24 +01:00
PainterImage.fill(lcQColorFromRGBA(BackgroundColor));
2020-01-21 00:39:20 +01:00
QPainter Painter(&PainterImage);
Painter.setFont(Font);
2021-01-16 23:43:24 +01:00
Painter.setPen(TextColor);
2020-01-21 00:39:20 +01:00
2021-11-15 03:34:24 +01:00
for (const lcPartsListImage& Image : Images)
2020-01-21 00:39:20 +01:00
{
2021-11-15 03:34:24 +01:00
const QPoint Position = Image.Position + QPoint(20, 20);
2020-01-21 00:39:20 +01:00
Painter.drawImage(Position, Image.Thumbnail, Image.Bounds);
Painter.drawText(QPoint(Position.x(), Position.y() + Image.Bounds.height() + Ascent), QString::number(Image.Count) + 'x');
}
Painter.end();
return PainterImage;
}
void lcModel::SaveStepImages(const QString& BaseName, bool AddStepSuffix, bool Zoom, int Width, int Height, lcStep Start, lcStep End)
2019-12-31 01:04:58 +01:00
{
2016-10-05 23:28:52 +02:00
for (lcStep Step = Start; Step <= End; Step++)
{
QString FileName;
if (AddStepSuffix)
FileName = BaseName.arg(Step, 2, 10, QLatin1Char('0'));
else
FileName = BaseName;
2016-10-05 23:28:52 +02:00
QImageWriter Writer(FileName);
if (Writer.format().isEmpty())
Writer.setFormat("png");
QImage Image = GetStepImage(Zoom, Width, Height, Step);
2019-12-31 01:04:58 +01:00
if (!Writer.write(Image))
{
QMessageBox::information(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, Writer.errorString()));
2016-10-05 23:28:52 +02:00
break;
}
2016-10-05 23:28:52 +02:00
}
2019-12-31 01:04:58 +01:00
}
2016-10-05 23:28:52 +02:00
void lcModel::RayTest(lcObjectRayTest& ObjectRayTest) const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2017-11-13 04:38:07 +01:00
if (Piece->IsVisible(mCurrentStep) && (!ObjectRayTest.IgnoreSelected || !Piece->IsSelected()))
2016-10-05 23:28:52 +02:00
Piece->RayTest(ObjectRayTest);
if (ObjectRayTest.PiecesOnly)
return;
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
if (Camera.get() != ObjectRayTest.ViewCamera && Camera->IsVisible() && (!ObjectRayTest.IgnoreSelected || !Camera->IsSelected()))
2016-10-05 23:28:52 +02:00
Camera->RayTest(ObjectRayTest);
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2017-11-13 04:38:07 +01:00
if (Light->IsVisible() && (!ObjectRayTest.IgnoreSelected || !Light->IsSelected()))
Light->RayTest(ObjectRayTest);
2016-10-05 23:28:52 +02:00
}
void lcModel::BoxTest(lcObjectBoxTest& ObjectBoxTest) const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
if (Piece->IsVisible(mCurrentStep))
Piece->BoxTest(ObjectBoxTest);
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
if (Camera.get() != ObjectBoxTest.ViewCamera && Camera->IsVisible())
2016-10-05 23:28:52 +02:00
Camera->BoxTest(ObjectBoxTest);
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2019-03-09 18:50:34 +01:00
if (Light->IsVisible())
Light->BoxTest(ObjectBoxTest);
2016-10-05 23:28:52 +02:00
}
bool lcModel::SubModelMinIntersectDist(const lcVector3& WorldStart, const lcVector3& WorldEnd, float& MinDistance, lcPieceInfoRayTest& PieceInfoRayTest) const
2016-10-05 23:28:52 +02:00
{
bool MinIntersect = false;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
2021-11-15 03:34:24 +01:00
const lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(Piece->mModelWorld);
const lcVector3 Start = lcMul31(WorldStart, InverseWorldMatrix);
const lcVector3 End = lcMul31(WorldEnd, InverseWorldMatrix);
2016-10-05 23:28:52 +02:00
if (Piece->IsVisibleInSubModel())
{
if (Piece->mPieceInfo->MinIntersectDist(Start, End, MinDistance, PieceInfoRayTest)) // todo: this should check for piece->mMesh first
{
MinIntersect = true;
PieceInfoRayTest.Transform = lcMul(PieceInfoRayTest.Transform, Piece->mModelWorld);
}
}
2016-10-05 23:28:52 +02:00
}
return MinIntersect;
}
bool lcModel::SubModelBoxTest(const lcVector4 Planes[6]) const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2019-03-13 03:51:04 +01:00
if (Piece->IsVisibleInSubModel() && Piece->mPieceInfo->BoxTest(Piece->mModelWorld, Planes))
2016-10-05 23:28:52 +02:00
return true;
return false;
}
void lcModel::SubModelCompareBoundingBox(const lcMatrix44& WorldMatrix, lcVector3& Min, lcVector3& Max) const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
if (Piece->IsVisibleInSubModel())
Piece->SubModelCompareBoundingBox(WorldMatrix, Min, Max);
}
void lcModel::SubModelAddBoundingBoxPoints(const lcMatrix44& WorldMatrix, std::vector<lcVector3>& Points) const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
if (Piece->IsVisibleInSubModel())
Piece->SubModelAddBoundingBoxPoints(WorldMatrix, Points);
}
2016-10-05 23:28:52 +02:00
void lcModel::SaveCheckpoint(const QString& Description)
{
lcModelHistoryEntry* ModelHistoryEntry = new lcModelHistoryEntry();
ModelHistoryEntry->Description = Description;
QTextStream Stream(&ModelHistoryEntry->File);
2023-06-19 03:37:59 +02:00
SaveLDraw(Stream, false, 0);
2016-10-05 23:28:52 +02:00
2019-06-24 03:28:14 +02:00
mUndoHistory.insert(mUndoHistory.begin(), ModelHistoryEntry);
for (lcModelHistoryEntry* Entry : mRedoHistory)
delete Entry;
mRedoHistory.clear();
2016-10-05 23:28:52 +02:00
if (!Description.isEmpty())
{
gMainWindow->UpdateModified(IsModified());
2019-06-24 03:28:14 +02:00
gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : QString(), !mRedoHistory.empty() ? mRedoHistory[0]->Description : QString());
2016-10-05 23:28:52 +02:00
}
}
void lcModel::LoadCheckPoint(lcModelHistoryEntry* CheckPoint)
{
2017-01-23 04:28:05 +01:00
lcPiecesLibrary* Library = lcGetPiecesLibrary();
2019-07-05 02:06:26 +02:00
std::vector<PieceInfo*> LoadedInfos;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
{
2019-03-09 18:50:34 +01:00
PieceInfo* Info = Piece->mPieceInfo;
2017-01-23 04:28:05 +01:00
Library->LoadPieceInfo(Info, true, true);
2019-07-05 02:06:26 +02:00
LoadedInfos.push_back(Info);
}
// Remember the current step
const lcStep CurrentStep = mCurrentStep;
// Remember the camera names
2024-05-11 20:37:51 +02:00
std::vector<lcView*> Views = lcView::GetModelViews(this);
std::vector<QString> CameraNames(Views.size());
for (size_t ViewIndex = 0; ViewIndex < Views.size(); ViewIndex++)
{
2024-05-11 20:37:51 +02:00
lcCamera* Camera = Views[ViewIndex]->GetCamera();
if (!Camera->IsSimple())
CameraNames[ViewIndex] = Camera->GetName();
}
2016-10-05 23:28:52 +02:00
DeleteModel();
QBuffer Buffer(&CheckPoint->File);
Buffer.open(QIODevice::ReadOnly);
LoadLDraw(Buffer, lcGetActiveProject());
// Reset the current step
mCurrentStep = CurrentStep;
CalculateStep(CurrentStep);
// Reset the cameras
2024-05-11 20:37:51 +02:00
for (size_t ViewIndex = 0; ViewIndex < Views.size() && ViewIndex < CameraNames.size(); ViewIndex++)
if (!CameraNames[ViewIndex].isEmpty())
Views[ViewIndex]->SetCamera(CameraNames[ViewIndex]);
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(true, false);
gMainWindow->UpdateCurrentStep();
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2019-07-05 02:06:26 +02:00
for (PieceInfo* Info : LoadedInfos)
Library->ReleasePieceInfo(Info);
2016-10-05 23:28:52 +02:00
}
void lcModel::SetActive(bool Active)
{
CalculateStep(Active ? mCurrentStep : LC_STEP_MAX);
2016-10-05 23:28:52 +02:00
mActive = Active;
}
void lcModel::CalculateStep(lcStep Step)
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
Piece->UpdatePosition(Step);
if (Piece->IsSelected())
{
if (!Piece->IsVisible(Step))
Piece->SetSelected(false);
else
SelectGroup(Piece->GetTopGroup(), true);
}
}
2024-06-04 18:34:38 +02:00
for (std::unique_ptr<lcCamera>& Camera : mCameras)
2019-03-09 18:50:34 +01:00
Camera->UpdatePosition(Step);
2016-10-05 23:28:52 +02:00
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2019-03-09 18:50:34 +01:00
Light->UpdatePosition(Step);
2016-10-05 23:28:52 +02:00
}
void lcModel::SetCurrentStep(lcStep Step)
{
mCurrentStep = Step;
CalculateStep(Step);
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateCurrentStep();
}
void lcModel::ShowFirstStep()
{
if (mCurrentStep == 1)
return;
SetCurrentStep(1);
}
void lcModel::ShowLastStep()
{
2021-11-15 03:34:24 +01:00
const lcStep LastStep = GetLastStep();
2016-10-05 23:28:52 +02:00
if (mCurrentStep == LastStep)
return;
SetCurrentStep(LastStep);
}
void lcModel::ShowPreviousStep()
{
if (mCurrentStep == 1)
return;
SetCurrentStep(mCurrentStep - 1);
}
void lcModel::ShowNextStep()
{
if (mCurrentStep == LC_STEP_MAX)
return;
SetCurrentStep(mCurrentStep + 1);
}
lcStep lcModel::GetLastStep() const
{
lcStep Step = 1;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2019-03-09 18:50:34 +01:00
Step = lcMax(Step, Piece->GetStepShow());
2016-10-05 23:28:52 +02:00
return Step;
}
void lcModel::InsertStep(lcStep Step)
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
Piece->InsertTime(Step, 1);
if (Piece->IsSelected() && !Piece->IsVisible(mCurrentStep))
Piece->SetSelected(false);
}
2024-06-04 18:34:38 +02:00
for (std::unique_ptr<lcCamera>& Camera : mCameras)
2019-03-09 18:50:34 +01:00
Camera->InsertTime(Step, 1);
2016-10-05 23:28:52 +02:00
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2019-03-09 18:50:34 +01:00
Light->InsertTime(Step, 1);
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Inserting Step"));
SetCurrentStep(mCurrentStep);
}
void lcModel::RemoveStep(lcStep Step)
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
Piece->RemoveTime(Step, 1);
if (Piece->IsSelected() && !Piece->IsVisible(mCurrentStep))
Piece->SetSelected(false);
}
2024-06-04 18:34:38 +02:00
for (std::unique_ptr<lcCamera>& Camera : mCameras)
2019-03-09 18:50:34 +01:00
Camera->RemoveTime(Step, 1);
2016-10-05 23:28:52 +02:00
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2019-03-09 18:50:34 +01:00
Light->RemoveTime(Step, 1);
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Removing Step"));
SetCurrentStep(mCurrentStep);
}
lcGroup* lcModel::AddGroup(const QString& Prefix, lcGroup* Parent)
{
lcGroup* Group = new lcGroup();
2024-05-26 22:01:34 +02:00
mGroups.emplace_back(Group);
2016-10-05 23:28:52 +02:00
Group->mName = GetGroupName(Prefix);
Group->mGroup = Parent;
return Group;
}
lcGroup* lcModel::GetGroup(const QString& Name, bool CreateIfMissing)
{
for (const std::unique_ptr<lcGroup>& Group : mGroups)
2016-10-05 23:28:52 +02:00
if (Group->mName == Name)
return Group.get();
2016-10-05 23:28:52 +02:00
if (CreateIfMissing)
{
lcGroup* Group = new lcGroup();
Group->mName = Name;
2024-05-26 22:01:34 +02:00
mGroups.emplace_back(Group);
2016-10-05 23:28:52 +02:00
return Group;
}
return nullptr;
2016-10-05 23:28:52 +02:00
}
void lcModel::RemoveGroup(lcGroup* Group)
{
for (std::vector<std::unique_ptr<lcGroup>>::iterator GroupIt = mGroups.begin(); GroupIt != mGroups.end(); GroupIt++)
{
if (GroupIt->get() == Group)
{
mGroups.erase(GroupIt);
break;
}
}
2016-10-05 23:28:52 +02:00
}
void lcModel::GroupSelection()
{
if (!AnyPiecesSelected())
{
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No pieces selected."));
return;
}
2023-04-30 05:04:58 +02:00
lcGroupDialog Dialog(gMainWindow, GetGroupName(tr("Group #")));
2016-10-05 23:28:52 +02:00
if (Dialog.exec() != QDialog::Accepted)
return;
lcGroup* NewGroup = GetGroup(Dialog.mName, true);
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsSelected())
{
lcGroup* Group = Piece->GetTopGroup();
if (!Group)
Piece->SetGroup(NewGroup);
else if (Group != NewGroup)
Group->mGroup = NewGroup;
}
}
SaveCheckpoint(tr("Grouping"));
}
void lcModel::UngroupSelection()
{
std::set<lcGroup*> SelectedGroups;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsSelected())
{
lcGroup* Group = Piece->GetTopGroup();
if (SelectedGroups.insert(Group).second)
2016-10-05 23:28:52 +02:00
{
for (std::vector<std::unique_ptr<lcGroup>>::iterator GroupIt = mGroups.begin(); GroupIt != mGroups.end(); GroupIt++)
{
if (GroupIt->get() == Group)
{
GroupIt->release();
mGroups.erase(GroupIt);
break;
}
}
2016-10-05 23:28:52 +02:00
}
}
}
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
lcGroup* Group = Piece->GetGroup();
if (SelectedGroups.find(Group) != SelectedGroups.end())
Piece->SetGroup(nullptr);
2016-10-05 23:28:52 +02:00
}
for (const std::unique_ptr<lcGroup>& Group : mGroups)
if (SelectedGroups.find(Group->mGroup) != SelectedGroups.end())
Group->mGroup = nullptr;
2016-10-05 23:28:52 +02:00
for (lcGroup* Group : SelectedGroups)
delete Group;
2016-10-05 23:28:52 +02:00
RemoveEmptyGroups();
SaveCheckpoint(tr("Ungrouping"));
}
void lcModel::AddSelectedPiecesToGroup()
{
lcGroup* Group = nullptr;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsSelected())
{
Group = Piece->GetTopGroup();
if (Group)
break;
}
}
if (Group)
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsFocused())
{
Piece->SetGroup(Group);
break;
}
}
}
RemoveEmptyGroups();
SaveCheckpoint(tr("Grouping"));
}
void lcModel::RemoveFocusPieceFromGroup()
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsFocused())
{
Piece->SetGroup(nullptr);
2016-10-05 23:28:52 +02:00
break;
}
}
RemoveEmptyGroups();
SaveCheckpoint(tr("Ungrouping"));
}
void lcModel::ShowEditGroupsDialog()
{
QMap<lcPiece*, lcGroup*> PieceParents;
QMap<lcGroup*, lcGroup*> GroupParents;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
PieceParents[Piece.get()] = Piece->GetGroup();
2016-10-05 23:28:52 +02:00
for (const std::unique_ptr<lcGroup>& Group : mGroups)
GroupParents[Group.get()] = Group->mGroup;
2016-10-05 23:28:52 +02:00
lcQEditGroupsDialog Dialog(gMainWindow, PieceParents, GroupParents, this);
2016-10-05 23:28:52 +02:00
if (Dialog.exec() != QDialog::Accepted)
return;
bool Modified = Dialog.mNewGroups.isEmpty();
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
lcGroup* ParentGroup = Dialog.mPieceParents.value(Piece.get());
2016-10-05 23:28:52 +02:00
if (ParentGroup != Piece->GetGroup())
{
2019-03-09 18:50:34 +01:00
Piece->SetGroup(ParentGroup);
2016-10-05 23:28:52 +02:00
Modified = true;
}
}
for (const std::unique_ptr<lcGroup>& Group : mGroups)
2016-10-05 23:28:52 +02:00
{
lcGroup* ParentGroup = Dialog.mGroupParents.value(Group.get());
2016-10-05 23:28:52 +02:00
if (ParentGroup != Group->mGroup)
{
Group->mGroup = ParentGroup;
Modified = true;
}
}
if (Modified)
{
ClearSelection(true);
SaveCheckpoint(tr("Editing Groups"));
}
}
QString lcModel::GetGroupName(const QString& Prefix)
{
2021-11-15 03:34:24 +01:00
const int Length = Prefix.length();
2016-10-05 23:28:52 +02:00
int Max = 0;
for (const std::unique_ptr<lcGroup>& Group : mGroups)
2016-10-05 23:28:52 +02:00
{
2019-03-09 18:50:34 +01:00
const QString& Name = Group->mName;
2016-10-05 23:28:52 +02:00
if (Name.startsWith(Prefix))
{
bool Ok = false;
2017-04-21 03:56:35 +02:00
int GroupNumber = Name.mid(Length).toInt(&Ok);
2016-10-05 23:28:52 +02:00
if (Ok && GroupNumber > Max)
Max = GroupNumber;
}
}
return Prefix + QString::number(Max + 1);
}
void lcModel::RemoveEmptyGroups()
{
bool Removed;
do
{
Removed = false;
for (std::vector<std::unique_ptr<lcGroup>>::iterator GroupIt = mGroups.begin(); GroupIt != mGroups.end();)
2016-10-05 23:28:52 +02:00
{
lcGroup* Group = GroupIt->get();
2016-10-05 23:28:52 +02:00
int Ref = 0;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2019-03-09 18:50:34 +01:00
if (Piece->GetGroup() == Group)
2016-10-05 23:28:52 +02:00
Ref++;
for (size_t ParentIdx = 0; ParentIdx < mGroups.size(); ParentIdx++)
2016-10-05 23:28:52 +02:00
if (mGroups[ParentIdx]->mGroup == Group)
Ref++;
if (Ref > 1)
{
GroupIt++;
2016-10-05 23:28:52 +02:00
continue;
}
if (Ref != 0)
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->GetGroup() == Group)
{
Piece->SetGroup(Group->mGroup);
break;
}
}
for (size_t ParentIdx = 0; ParentIdx < mGroups.size(); ParentIdx++)
2016-10-05 23:28:52 +02:00
{
if (mGroups[ParentIdx]->mGroup == Group)
{
mGroups[ParentIdx]->mGroup = Group->mGroup;
break;
}
}
}
GroupIt = mGroups.erase(GroupIt);
2016-10-05 23:28:52 +02:00
Removed = true;
}
}
while (Removed);
}
lcVector3 lcModel::SnapPosition(const lcVector3& Distance) const
{
lcVector3 NewDistance(Distance);
float SnapXY = gMainWindow->GetMoveXYSnap();
if (SnapXY != 0.0f)
{
int i = (int)(NewDistance[0] / SnapXY);
float Leftover = NewDistance[0] - (SnapXY * i);
if (Leftover > SnapXY / 2)
i++;
else if (Leftover < -SnapXY / 2)
i--;
NewDistance[0] = SnapXY * i;
i = (int)(NewDistance[1] / SnapXY);
Leftover = NewDistance[1] - (SnapXY * i);
if (Leftover > SnapXY / 2)
i++;
else if (Leftover < -SnapXY / 2)
i--;
NewDistance[1] = SnapXY * i;
}
float SnapZ = gMainWindow->GetMoveZSnap();
if (SnapZ != 0.0f)
{
int i = (int)(NewDistance[2] / SnapZ);
2021-11-15 03:34:24 +01:00
const float Leftover = NewDistance[2] - (SnapZ * i);
2016-10-05 23:28:52 +02:00
if (Leftover > SnapZ / 2)
i++;
else if (Leftover < -SnapZ / 2)
i--;
NewDistance[2] = SnapZ * i;
}
return NewDistance;
}
lcVector3 lcModel::SnapRotation(const lcVector3& Angles) const
{
2021-11-15 03:34:24 +01:00
const float AngleSnap = gMainWindow->GetAngleSnap();
2016-10-05 23:28:52 +02:00
lcVector3 NewAngles(Angles);
if (AngleSnap != 0.0f)
2016-10-05 23:28:52 +02:00
{
int Snap[3];
for (int i = 0; i < 3; i++)
Snap[i] = (int)(Angles[i] / AngleSnap);
2016-10-05 23:28:52 +02:00
NewAngles = lcVector3((float)(AngleSnap * Snap[0]), (float)(AngleSnap * Snap[1]), (float)(AngleSnap * Snap[2]));
}
return NewAngles;
}
lcMatrix33 lcModel::GetRelativeRotation() const
{
if (gMainWindow->GetRelativeTransform())
{
2021-11-15 03:34:24 +01:00
const lcObject* Focus = GetFocusObject();
2016-10-05 23:28:52 +02:00
if (Focus)
{
if (Focus->IsPiece())
return ((lcPiece*)Focus)->GetRelativeRotation();
if (Focus->IsLight())
return ((lcLight*)Focus)->GetRelativeRotation();
}
2016-10-05 23:28:52 +02:00
}
return lcMatrix33Identity();
}
void lcModel::AddPiece(PieceInfo* PieceInfo, quint32 Section)
2016-10-05 23:28:52 +02:00
{
if (!PieceInfo)
PieceInfo = gMainWindow->GetCurrentPieceInfo();
2016-10-05 23:28:52 +02:00
if (!PieceInfo)
2016-10-05 23:28:52 +02:00
return;
2024-06-20 04:22:18 +02:00
lcPiece* Last = mPieces.empty() ? nullptr : mPieces.back().get();
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsFocused())
{
2024-06-20 04:22:18 +02:00
Last = Piece.get();
2016-10-05 23:28:52 +02:00
break;
}
}
lcMatrix44 WorldMatrix;
const lcBoundingBox& PieceInfoBoundingBox = PieceInfo->GetBoundingBox();
2016-10-05 23:28:52 +02:00
if (Last)
{
2024-11-27 00:53:50 +01:00
bool TransformValid = false;
if (PieceInfo->GetTrainTrackInfo() && Last->mPieceInfo->GetTrainTrackInfo())
{
std::optional<lcMatrix44> TrainTrackTransform = lcTrainTrackInfo::GetPieceInsertTransform(Last, PieceInfo, Section);
2024-11-27 00:53:50 +01:00
if (TrainTrackTransform)
{
TransformValid = true;
WorldMatrix = TrainTrackTransform.value();
}
}
if (!TransformValid)
{
const lcBoundingBox& LastBoundingBox = Last->GetBoundingBox();
lcVector3 Dist(0, 0, LastBoundingBox.Max.z - PieceInfoBoundingBox.Min.z);
Dist = SnapPosition(Dist);
WorldMatrix = Last->mModelWorld;
WorldMatrix.SetTranslation(lcMul31(Dist, Last->mModelWorld));
}
2016-10-05 23:28:52 +02:00
}
else
{
WorldMatrix = lcMatrix44Translation(lcVector3(0.0f, 0.0f, -PieceInfoBoundingBox.Min.z));
2016-10-05 23:28:52 +02:00
}
lcPiece* Piece = new lcPiece(PieceInfo);
2016-10-05 23:28:52 +02:00
Piece->Initialize(WorldMatrix, mCurrentStep);
Piece->SetColorIndex(gMainWindow->mColorIndex);
AddPiece(Piece);
gMainWindow->UpdateTimeline(false, false);
2017-11-22 02:58:36 +01:00
ClearSelectionAndSetFocus(Piece, LC_PIECE_SECTION_POSITION, false);
2016-10-05 23:28:52 +02:00
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Adding Piece"));
2016-10-05 23:28:52 +02:00
}
void lcModel::AddPiece(lcPiece* Piece)
{
2024-06-20 04:22:18 +02:00
for (size_t PieceIndex = 0; PieceIndex < mPieces.size(); PieceIndex++)
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
if (mPieces[PieceIndex]->GetStepShow() > Piece->GetStepShow())
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
InsertPiece(Piece, PieceIndex);
2016-10-05 23:28:52 +02:00
return;
}
}
2024-05-26 22:01:34 +02:00
InsertPiece(Piece, mPieces.size());
2016-10-05 23:28:52 +02:00
}
2024-06-20 04:22:18 +02:00
void lcModel::InsertPiece(lcPiece* Piece, size_t Index)
2016-10-05 23:28:52 +02:00
{
2021-11-15 03:34:24 +01:00
const PieceInfo* Info = Piece->mPieceInfo;
2016-10-05 23:28:52 +02:00
if (!Info->IsModel())
{
2021-11-15 03:34:24 +01:00
const lcMesh* Mesh = Info->GetMesh();
2016-10-05 23:28:52 +02:00
2017-01-23 04:28:05 +01:00
if (Mesh && Mesh->mVertexCacheOffset == -1)
2016-10-05 23:28:52 +02:00
lcGetPiecesLibrary()->mBuffersDirty = true;
}
2024-06-20 04:22:18 +02:00
mPieces.insert(mPieces.begin() + Index, std::unique_ptr<lcPiece>(Piece));
2016-10-05 23:28:52 +02:00
}
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();
}
2024-12-18 04:20:38 +01:00
void lcModel::RotateFocusedTrainTrack(int Direction)
{
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);
2024-12-18 04:20:38 +01:00
int NewConnectionIndex = (RotateConnectionIndex + Direction + static_cast<int>(TrainTrackInfo->GetConnections().size())) % 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())
return;
const lcTrainTrackInfo* TrainTrackInfo = FocusPiece->mPieceInfo->GetTrainTrackInfo();
if (!TrainTrackInfo)
return;
const int ConnectionCount = static_cast<int>(TrainTrackInfo->GetConnections().size());
std::vector<bool> Connections(ConnectionCount, false);
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
{
if (Piece.get() == FocusPiece || !Piece->mPieceInfo->GetTrainTrackInfo())
continue;
for (int ConnectionIndex = 0; ConnectionIndex < ConnectionCount; ConnectionIndex++)
2024-11-30 21:29:15 +01:00
if (!Connections[ConnectionIndex] && lcTrainTrackInfo::GetPieceConnectionIndex(FocusPiece, ConnectionIndex, Piece.get()) != -1)
Connections[ConnectionIndex] = true;
}
FocusPiece->SetTrainTrackConnections(std::move(Connections));
}
2016-10-05 23:28:52 +02:00
void lcModel::DeleteAllCameras()
{
2024-05-26 22:01:34 +02:00
if (mCameras.empty())
2016-10-05 23:28:52 +02:00
return;
2024-06-04 18:34:38 +02:00
mCameras.clear();
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
SaveCheckpoint(tr("Resetting Cameras"));
2016-10-05 23:28:52 +02:00
}
void lcModel::DeleteSelectedObjects()
{
if (RemoveSelectedObjects())
{
if (!mIsPreview)
{
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(true);
gMainWindow->UpdateInUseCategory();
2020-12-16 02:19:32 +01:00
UpdateAllViews();
SaveCheckpoint(tr("Deleting"));
}
2016-10-05 23:28:52 +02:00
}
}
void lcModel::ResetSelectedPiecesPivotPoint()
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
if (Piece->IsSelected())
Piece->ResetPivotPoint();
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::RemoveSelectedPiecesKeyFrames()
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
if (Piece->IsSelected())
Piece->RemoveKeyFrames();
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
if (Camera->IsSelected())
Camera->RemoveKeyFrames();
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
if (Light->IsSelected())
Light->RemoveKeyFrames();
2020-12-16 02:19:32 +01:00
UpdateAllViews();
SaveCheckpoint(tr("Removing Key Frames"));
}
2016-10-05 23:28:52 +02:00
void lcModel::InsertControlPoint()
{
lcObject* Focus = GetFocusObject();
if (!Focus || !Focus->IsPiece())
return;
lcPiece* Piece = (lcPiece*)Focus;
lcVector3 Start, End;
gMainWindow->GetActiveView()->GetRayUnderPointer(Start, End);
if (Piece->InsertControlPoint(Start, End))
{
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Modifying"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
}
void lcModel::RemoveFocusedControlPoint()
{
lcObject* Focus = GetFocusObject();
if (!Focus || !Focus->IsPiece())
return;
lcPiece* Piece = (lcPiece*)Focus;
if (Piece->RemoveFocusedControlPoint())
{
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Modifying"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
}
void lcModel::ShowSelectedPiecesEarlier()
{
2024-05-26 22:01:34 +02:00
std::vector<lcPiece*> MovedPieces;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (auto PieceIt = mPieces.begin(); PieceIt != mPieces.end(); )
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
lcPiece* Piece = PieceIt->get();
2016-10-05 23:28:52 +02:00
if (Piece->IsSelected())
{
lcStep Step = Piece->GetStepShow();
if (Step > 1)
{
Step--;
Piece->SetStepShow(Step);
2024-06-20 04:22:18 +02:00
MovedPieces.emplace_back(PieceIt->release());
PieceIt = mPieces.erase(PieceIt);
2016-10-05 23:28:52 +02:00
continue;
}
}
2024-06-20 04:22:18 +02:00
PieceIt++;
2016-10-05 23:28:52 +02:00
}
2024-05-26 22:01:34 +02:00
if (MovedPieces.empty())
2016-10-05 23:28:52 +02:00
return;
2024-05-26 22:01:34 +02:00
for (lcPiece* Piece : MovedPieces)
2016-10-05 23:28:52 +02:00
{
Piece->SetFileLine(-1);
AddPiece(Piece);
}
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Modifying"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::ShowSelectedPiecesLater()
{
2024-06-20 04:22:18 +02:00
std::vector<lcPiece*> MovedPieces;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (auto PieceIt = mPieces.begin(); PieceIt != mPieces.end(); )
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
lcPiece* Piece = PieceIt->get();
2016-10-05 23:28:52 +02:00
if (Piece->IsSelected())
{
lcStep Step = Piece->GetStepShow();
if (Step < LC_STEP_MAX)
{
Step++;
Piece->SetStepShow(Step);
if (!Piece->IsVisible(mCurrentStep))
Piece->SetSelected(false);
2024-06-20 04:22:18 +02:00
MovedPieces.emplace_back(PieceIt->release());
PieceIt = mPieces.erase(PieceIt);
2016-10-05 23:28:52 +02:00
continue;
}
}
2024-06-20 04:22:18 +02:00
PieceIt++;
2016-10-05 23:28:52 +02:00
}
2024-05-26 22:01:34 +02:00
if (MovedPieces.empty())
2016-10-05 23:28:52 +02:00
return;
2024-06-20 04:22:18 +02:00
for (lcPiece* Piece : MovedPieces)
2016-10-05 23:28:52 +02:00
{
Piece->SetFileLine(-1);
AddPiece(Piece);
}
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Modifying"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
2024-06-20 04:22:18 +02:00
void lcModel::SetPieceSteps(const std::vector<std::pair<lcPiece*, lcStep>>& PieceSteps)
2016-10-05 23:28:52 +02:00
{
2024-05-26 22:01:34 +02:00
if (PieceSteps.size() != mPieces.size())
2016-10-05 23:28:52 +02:00
return;
bool Modified = false;
2024-06-20 04:22:18 +02:00
for (size_t PieceIdx = 0; PieceIdx < PieceSteps.size(); PieceIdx++)
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
const std::pair<lcPiece*, lcStep>& PieceStep = PieceSteps[PieceIdx];
lcPiece* Piece = mPieces[PieceIdx].get();
2016-10-05 23:28:52 +02:00
if (Piece != PieceStep.first || Piece->GetStepShow() != PieceStep.second)
{
Piece = PieceStep.first;
2021-11-15 03:34:24 +01:00
const lcStep Step = PieceStep.second;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
mPieces[PieceIdx].release();
mPieces[PieceIdx] = std::unique_ptr<lcPiece>(Piece);
2016-10-05 23:28:52 +02:00
Piece->SetStepShow(Step);
if (!Piece->IsVisible(mCurrentStep))
Piece->SetSelected(false);
Modified = true;
}
}
if (Modified)
{
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Modifying"));
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(true);
}
}
2017-07-24 04:35:18 +02:00
void lcModel::RenamePiece(PieceInfo* Info)
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2017-07-24 04:35:18 +02:00
if (Piece->mPieceInfo == Info)
Piece->UpdateID();
}
2016-10-05 23:28:52 +02:00
void lcModel::MoveSelectionToModel(lcModel* Model)
{
if (!Model)
return;
2024-06-20 04:22:18 +02:00
std::vector<lcPiece*> Pieces;
lcPiece* ModelPiece = nullptr;
lcStep FirstStep = LC_STEP_MAX;
lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (size_t PieceIndex = 0; PieceIndex < mPieces.size(); )
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
lcPiece* Piece = mPieces[PieceIndex].get();
2016-10-05 23:28:52 +02:00
if (Piece->IsSelected())
{
Piece->CompareBoundingBox(Min, Max);
2024-06-20 04:22:18 +02:00
mPieces[PieceIndex].release();
mPieces.erase(mPieces.begin() + PieceIndex);
Piece->SetGroup(nullptr); // todo: copy groups
2024-05-26 22:01:34 +02:00
Pieces.emplace_back(Piece);
FirstStep = qMin(FirstStep, Piece->GetStepShow());
2016-10-05 23:28:52 +02:00
if (!ModelPiece)
{
ModelPiece = new lcPiece(Model->mPieceInfo);
ModelPiece->SetColorIndex(gDefaultColor);
2024-06-20 04:22:18 +02:00
InsertPiece(ModelPiece, PieceIndex);
PieceIndex++;
2016-10-05 23:28:52 +02:00
}
}
else
2024-06-20 04:22:18 +02:00
PieceIndex++;
2016-10-05 23:28:52 +02:00
}
lcVector3 ModelCenter = (Min + Max) / 2.0f;
ModelCenter.z += (Min.z - Max.z) / 2.0f;
2024-06-20 04:22:18 +02:00
for (lcPiece* Piece : Pieces)
{
Piece->SetFileLine(-1);
Piece->SetStepShow(Piece->GetStepShow() - FirstStep + 1);
Piece->MoveSelected(Piece->GetStepShow(), false, -ModelCenter);
Model->AddPiece(Piece);
}
2016-10-05 23:28:52 +02:00
2019-03-17 21:27:57 +01:00
std::vector<lcModel*> UpdatedModels;
2016-10-05 23:28:52 +02:00
Model->UpdatePieceInfo(UpdatedModels);
if (ModelPiece)
{
ModelPiece->Initialize(lcMatrix44Translation(ModelCenter), FirstStep);
2017-02-13 03:05:20 +01:00
ModelPiece->UpdatePosition(mCurrentStep);
}
2016-10-05 23:28:52 +02:00
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("New Model"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, false);
2017-11-22 02:58:36 +01:00
ClearSelectionAndSetFocus(ModelPiece, LC_PIECE_SECTION_POSITION, false);
2016-10-05 23:28:52 +02:00
}
void lcModel::InlineSelectedModels()
{
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> NewPieces;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (size_t PieceIndex = 0; PieceIndex < mPieces.size(); )
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
lcPiece* Piece = mPieces[PieceIndex].get();
2016-10-05 23:28:52 +02:00
if (!Piece->IsSelected() || !Piece->mPieceInfo->IsModel())
{
2024-06-20 04:22:18 +02:00
PieceIndex++;
2016-10-05 23:28:52 +02:00
continue;
}
2024-06-20 04:22:18 +02:00
mPieces[PieceIndex].release();
mPieces.erase(mPieces.begin() + PieceIndex);
2016-10-05 23:28:52 +02:00
2017-07-23 05:54:33 +02:00
lcModel* Model = Piece->mPieceInfo->GetModel();
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& ModelPiece : Model->mPieces)
2016-10-05 23:28:52 +02:00
{
2017-07-23 05:54:33 +02:00
lcPiece* NewPiece = new lcPiece(nullptr);
2016-10-05 23:28:52 +02:00
// todo: recreate in groups in the current model
2021-01-17 03:27:39 +01:00
int ColorIndex = ModelPiece->GetColorIndex();
2017-07-23 05:54:33 +02:00
if (ColorIndex == gDefaultColor)
2021-01-17 03:27:39 +01:00
ColorIndex = Piece->GetColorIndex();
2017-07-23 05:54:33 +02:00
NewPiece->SetPieceInfo(ModelPiece->mPieceInfo, ModelPiece->GetID(), true);
NewPiece->Initialize(lcMul(ModelPiece->mModelWorld, Piece->mModelWorld), Piece->GetStepShow());
NewPiece->SetColorIndex(ColorIndex);
2016-10-05 23:28:52 +02:00
NewPiece->UpdatePosition(mCurrentStep);
2024-05-26 22:01:34 +02:00
NewPieces.emplace_back(NewPiece);
2024-06-20 04:22:18 +02:00
InsertPiece(NewPiece, PieceIndex);
PieceIndex++;
2016-10-05 23:28:52 +02:00
}
delete Piece;
}
2024-05-26 22:01:34 +02:00
if (!NewPieces.size())
2016-10-05 23:28:52 +02:00
{
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No models selected."));
return;
}
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Inlining"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, false);
SetSelectionAndFocus(NewPieces, nullptr, 0, false);
2016-10-05 23:28:52 +02:00
}
bool lcModel::RemoveSelectedObjects()
{
bool RemovedPiece = false;
bool RemovedCamera = false;
bool RemovedLight = false;
2024-06-20 04:22:18 +02:00
for (auto PieceIt = mPieces.begin(); PieceIt != mPieces.end(); )
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
lcPiece* Piece = PieceIt->get();
2016-10-05 23:28:52 +02:00
if (Piece->IsSelected())
{
RemovedPiece = true;
2024-06-20 04:22:18 +02:00
PieceIt = mPieces.erase(PieceIt);
2016-10-05 23:28:52 +02:00
}
else
2024-06-20 04:22:18 +02:00
PieceIt++;
2016-10-05 23:28:52 +02:00
}
2024-06-04 18:34:38 +02:00
for (std::vector<std::unique_ptr<lcCamera>>::iterator CameraIt = mCameras.begin(); CameraIt != mCameras.end(); )
2016-10-05 23:28:52 +02:00
{
2024-06-04 18:34:38 +02:00
lcCamera* Camera = CameraIt->get();
2016-10-05 23:28:52 +02:00
if (Camera->IsSelected())
{
2021-02-27 20:15:04 +01:00
std::vector<lcView*> Views = lcView::GetModelViews(this);
2016-10-05 23:28:52 +02:00
2021-02-27 20:15:04 +01:00
for (lcView* View : Views)
2020-12-04 21:49:01 +01:00
if (Camera == View->GetCamera())
2016-10-05 23:28:52 +02:00
View->SetCamera(Camera, true);
RemovedCamera = true;
2024-06-04 18:34:38 +02:00
CameraIt = mCameras.erase(CameraIt);
2016-10-05 23:28:52 +02:00
}
else
2024-06-04 18:34:38 +02:00
CameraIt++;
2016-10-05 23:28:52 +02:00
}
2024-06-04 18:08:54 +02:00
for (std::vector<std::unique_ptr<lcLight>>::iterator LightIt = mLights.begin(); LightIt != mLights.end(); )
2016-10-05 23:28:52 +02:00
{
2024-06-04 18:34:38 +02:00
lcLight* Light = LightIt->get();
2016-10-05 23:28:52 +02:00
if (Light->IsSelected())
{
RemovedLight = true;
2024-06-04 18:08:54 +02:00
LightIt = mLights.erase(LightIt);
2016-10-05 23:28:52 +02:00
}
else
2024-06-04 18:08:54 +02:00
LightIt++;
2016-10-05 23:28:52 +02:00
}
RemoveEmptyGroups();
return RemovedPiece || RemovedCamera || RemovedLight;
}
void lcModel::MoveSelectedObjects(const lcVector3& PieceDistance, const lcVector3& ObjectDistance, bool AllowRelative, bool AlternateButtonDrag, bool Update, bool Checkpoint, bool FirstMove)
2016-10-05 23:28:52 +02:00
{
bool Moved = false;
lcMatrix33 RelativeRotation;
if (AllowRelative)
2016-10-05 23:28:52 +02:00
RelativeRotation = GetRelativeRotation();
else
RelativeRotation = lcMatrix33Identity();
if (PieceDistance.LengthSquared() >= 0.001f)
{
lcVector3 TransformedPieceDistance = lcMul(PieceDistance, RelativeRotation);
if (AlternateButtonDrag)
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsFocused())
{
Piece->MovePivotPoint(TransformedPieceDistance);
Moved = true;
break;
}
}
}
else
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsSelected())
{
if (gMainWindow->GetSeparateTransform())
TransformedPieceDistance = lcMul(PieceDistance, Piece->GetRelativeRotation());
Piece->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedPieceDistance);
2016-10-05 23:28:52 +02:00
Piece->UpdatePosition(mCurrentStep);
Moved = true;
}
}
}
}
if (ObjectDistance.LengthSquared() >= 0.001f && !AlternateButtonDrag)
{
2021-11-15 03:34:24 +01:00
const lcVector3 TransformedObjectDistance = lcMul(ObjectDistance, RelativeRotation);
2016-10-05 23:28:52 +02:00
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
2016-10-05 23:28:52 +02:00
{
if (Camera->IsSelected())
{
Camera->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedObjectDistance);
2016-10-05 23:28:52 +02:00
Camera->UpdatePosition(mCurrentStep);
Moved = true;
}
}
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2016-10-05 23:28:52 +02:00
{
if (Light->IsSelected())
{
Light->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedObjectDistance, FirstMove);
2016-10-05 23:28:52 +02:00
Light->UpdatePosition(mCurrentStep);
Moved = true;
}
}
}
if (Moved && Update)
{
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
if (Checkpoint)
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Moving"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(false);
}
}
2023-09-04 19:59:16 +02:00
void lcModel::RotateSelectedObjects(const lcVector3& Angles, bool Relative, bool RotatePivotPoint, bool Update, bool Checkpoint)
2016-10-05 23:28:52 +02:00
{
if (Angles.LengthSquared() < 0.001f)
return;
lcMatrix33 RotationMatrix = lcMatrix33Identity();
bool Rotated = false;
if (Angles[0] != 0.0f)
RotationMatrix = lcMul(lcMatrix33RotationX(Angles[0] * LC_DTOR), RotationMatrix);
if (Angles[1] != 0.0f)
RotationMatrix = lcMul(lcMatrix33RotationY(Angles[1] * LC_DTOR), RotationMatrix);
if (Angles[2] != 0.0f)
RotationMatrix = lcMul(lcMatrix33RotationZ(Angles[2] * LC_DTOR), RotationMatrix);
if (RotatePivotPoint)
2016-10-05 23:28:52 +02:00
{
lcObject* Focus = GetFocusObject();
if (Focus && Focus->IsPiece())
{
((lcPiece*)Focus)->RotatePivotPoint(RotationMatrix);
Rotated = true;
}
}
else
{
2023-09-04 19:59:16 +02:00
int Flags;
2024-05-15 05:45:09 +02:00
std::vector<lcObject*> Selection;
2023-09-04 19:59:16 +02:00
lcObject* Focus;
GetSelectionInformation(&Flags, Selection, &Focus);
if (!gMainWindow->GetSeparateTransform())
{
lcVector3 Center;
lcMatrix33 RelativeRotation;
2016-10-05 23:28:52 +02:00
GetMoveRotateTransform(Center, RelativeRotation);
2016-10-05 23:28:52 +02:00
lcMatrix33 WorldToFocusMatrix;
2016-10-05 23:28:52 +02:00
if (Relative)
{
WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation);
RotationMatrix = lcMul(RotationMatrix, RelativeRotation);
}
else
WorldToFocusMatrix = lcMatrix33Identity();
2023-09-04 19:59:16 +02:00
for (lcObject* Object : Selection)
{
2023-09-04 19:59:16 +02:00
if (Object->IsPiece())
{
lcPiece* Piece = (lcPiece*)Object;
2023-09-04 19:59:16 +02:00
Piece->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix);
Piece->UpdatePosition(mCurrentStep);
Rotated = true;
}
else if (Object->IsLight())
{
lcLight* Light = (lcLight*)Object;
Light->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix);
2023-09-04 19:59:16 +02:00
Light->UpdatePosition(mCurrentStep);
Rotated = true;
}
}
2016-10-05 23:28:52 +02:00
}
else
{
2023-09-04 19:59:16 +02:00
for (lcObject* Object : Selection)
{
2023-09-04 19:59:16 +02:00
if (Object->IsPiece())
{
lcPiece* Piece = (lcPiece*)Object;
2016-10-05 23:28:52 +02:00
2023-09-04 19:59:16 +02:00
const lcVector3 Center = Piece->GetRotationCenter();
lcMatrix33 WorldToFocusMatrix;
lcMatrix33 RelativeRotationMatrix;
2023-09-04 19:59:16 +02:00
if (Relative)
{
const lcMatrix33 RelativeRotation = Piece->GetRelativeRotation();
WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation);
RelativeRotationMatrix = lcMul(RotationMatrix, RelativeRotation);
}
else
{
WorldToFocusMatrix = lcMatrix33Identity();
RelativeRotationMatrix = RotationMatrix;
}
Piece->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RelativeRotationMatrix, Center, WorldToFocusMatrix);
Piece->UpdatePosition(mCurrentStep);
Rotated = true;
}
2023-09-04 19:59:16 +02:00
else if (Object->IsLight())
{
2023-09-04 19:59:16 +02:00
lcLight* Light = (lcLight*)Object;
const lcVector3 Center = Light->GetRotationCenter();
lcMatrix33 WorldToFocusMatrix;
lcMatrix33 RelativeRotationMatrix;
2023-09-04 19:59:16 +02:00
if (Relative)
{
const lcMatrix33 RelativeRotation = Light->GetRelativeRotation();
WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation);
RelativeRotationMatrix = lcMul(RotationMatrix, RelativeRotation);
}
else
{
WorldToFocusMatrix = lcMatrix33Identity();
RelativeRotationMatrix = RotationMatrix;
}
Light->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix);
2023-09-04 19:59:16 +02:00
Light->UpdatePosition(mCurrentStep);
Rotated = true;
}
}
2016-10-05 23:28:52 +02:00
}
}
if (Rotated && Update)
{
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
if (Checkpoint)
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Rotating"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(false);
}
}
void lcModel::ScaleSelectedPieces(const float Scale, bool Update, bool Checkpoint)
{
if (Scale < 0.001f)
return;
lcObject* Focus = GetFocusObject();
if (!Focus || !Focus->IsPiece())
return;
lcPiece* Piece = (lcPiece*)Focus;
2021-11-15 03:34:24 +01:00
const quint32 Section = Piece->GetFocusSection();
2016-10-05 23:28:52 +02:00
if (Section >= LC_PIECE_SECTION_CONTROL_POINT_FIRST && Section <= LC_PIECE_SECTION_CONTROL_POINT_LAST)
2016-10-05 23:28:52 +02:00
{
2021-11-15 03:34:24 +01:00
const int ControlPointIndex = Section - LC_PIECE_SECTION_CONTROL_POINT_FIRST;
2016-10-05 23:28:52 +02:00
Piece->SetControlPointScale(ControlPointIndex, Scale);
if (Update)
{
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
if (Checkpoint)
2017-03-09 00:49:57 +01:00
SaveCheckpoint(tr("Scaling"));
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(false);
}
}
}
void lcModel::TransformSelectedObjects(lcTransformType TransformType, const lcVector3& Transform)
{
switch (TransformType)
{
2020-05-03 22:04:40 +02:00
case lcTransformType::AbsoluteTranslation:
MoveSelectedObjects(Transform, false, false, true, true, true);
2016-10-05 23:28:52 +02:00
break;
2020-05-03 22:04:40 +02:00
case lcTransformType::RelativeTranslation:
MoveSelectedObjects(Transform, true, false, true, true, true);
2016-10-05 23:28:52 +02:00
break;
2020-05-03 22:04:40 +02:00
case lcTransformType::AbsoluteRotation:
2023-09-04 19:59:16 +02:00
RotateSelectedObjects(Transform, false, false, true, true);
2016-10-05 23:28:52 +02:00
break;
2020-05-03 22:04:40 +02:00
case lcTransformType::RelativeRotation:
2023-09-04 19:59:16 +02:00
RotateSelectedObjects(Transform, true, false, true, true);
2016-10-05 23:28:52 +02:00
break;
2020-06-01 21:29:30 +02:00
case lcTransformType::Count:
break;
2016-10-05 23:28:52 +02:00
}
}
2024-05-15 05:45:09 +02:00
void lcModel::SetObjectsKeyFrame(const std::vector<lcObject*>& Objects, lcObjectPropertyId PropertyId, bool KeyFrame)
2024-01-21 21:53:18 +01:00
{
bool Modified = false;
for (lcObject* Object : Objects)
{
Modified |= Object->SetKeyFrame(PropertyId, mCurrentStep, KeyFrame);
Object->UpdatePosition(mCurrentStep);
}
if (Modified)
{
SaveCheckpoint(tr("Changing Key Frame"));
gMainWindow->UpdateSelectedObjects(false);
UpdateAllViews();
}
}
2016-10-05 23:28:52 +02:00
void lcModel::SetSelectedPiecesColorIndex(int ColorIndex)
{
bool Modified = false;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
2021-01-17 03:27:39 +01:00
if (Piece->IsSelected() && Piece->GetColorIndex() != ColorIndex)
2016-10-05 23:28:52 +02:00
{
Piece->SetColorIndex(ColorIndex);
Modified = true;
}
}
if (Modified)
{
SaveCheckpoint(tr("Painting"));
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, true);
}
}
void lcModel::SetSelectedPiecesStepShow(lcStep Step)
{
2024-06-20 04:22:18 +02:00
std::vector<lcPiece*> MovedPieces;
2016-10-05 23:28:52 +02:00
bool SelectionChanged = false;
2024-06-20 04:22:18 +02:00
for (auto PieceIt = mPieces.begin(); PieceIt != mPieces.end(); )
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
lcPiece* Piece = PieceIt->get();
2016-10-05 23:28:52 +02:00
if (Piece->IsSelected() && Piece->GetStepShow() != Step)
{
Piece->SetStepShow(Step);
if (!Piece->IsVisible(mCurrentStep))
{
Piece->SetSelected(false);
SelectionChanged = true;
}
2024-06-20 04:22:18 +02:00
MovedPieces.emplace_back(PieceIt->release());
PieceIt = mPieces.erase(PieceIt);
continue;
2016-10-05 23:28:52 +02:00
}
2024-06-20 04:22:18 +02:00
PieceIt++;
2016-10-05 23:28:52 +02:00
}
2024-05-26 22:01:34 +02:00
if (MovedPieces.empty())
return;
2024-06-20 04:22:18 +02:00
for (lcPiece* Piece : MovedPieces)
2016-10-05 23:28:52 +02:00
{
Piece->SetFileLine(-1);
AddPiece(Piece);
2016-10-05 23:28:52 +02:00
}
SaveCheckpoint(tr("Showing Pieces"));
UpdateAllViews();
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(SelectionChanged);
2016-10-05 23:28:52 +02:00
}
void lcModel::SetSelectedPiecesStepHide(lcStep Step)
{
bool Modified = false;
bool SelectionChanged = false;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsSelected() && Piece->GetStepHide() != Step)
{
Piece->SetStepHide(Step);
if (!Piece->IsVisible(mCurrentStep))
{
Piece->SetSelected(false);
SelectionChanged = true;
}
Modified = true;
}
}
if (Modified)
{
SaveCheckpoint(tr("Hiding Pieces"));
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(SelectionChanged);
}
}
void lcModel::SetCameraOrthographic(lcCamera* Camera, bool Ortho)
{
if (Camera->IsOrtho() == Ortho)
return;
Camera->SetOrtho(Ortho);
Camera->UpdatePosition(mCurrentStep);
SaveCheckpoint(tr("Editing Camera"));
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2024-02-20 02:51:34 +01:00
gMainWindow->UpdateSelectedObjects(false);
2016-10-05 23:28:52 +02:00
}
void lcModel::SetCameraFOV(lcCamera* Camera, float FOV)
{
if (Camera->m_fovy == FOV)
return;
Camera->m_fovy = FOV;
Camera->UpdatePosition(mCurrentStep);
SaveCheckpoint(tr("Changing FOV"));
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::SetCameraZNear(lcCamera* Camera, float ZNear)
{
if (Camera->m_zNear == ZNear)
return;
Camera->m_zNear = ZNear;
Camera->UpdatePosition(mCurrentStep);
SaveCheckpoint(tr("Editing Camera"));
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::SetCameraZFar(lcCamera* Camera, float ZFar)
{
if (Camera->m_zFar == ZFar)
return;
Camera->m_zFar = ZFar;
Camera->UpdatePosition(mCurrentStep);
SaveCheckpoint(tr("Editing Camera"));
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
2024-05-15 05:45:09 +02:00
void lcModel::SetObjectsProperty(const std::vector<lcObject*>& Objects, lcObjectPropertyId PropertyId, QVariant Value)
2023-09-02 19:40:52 +02:00
{
bool Modified = false;
2023-09-02 19:40:52 +02:00
for (lcObject* Object : Objects)
{
2024-02-19 23:54:45 +01:00
bool ObjectModified = Object->SetPropertyValue(PropertyId, mCurrentStep, gMainWindow->GetAddKeys(), Value);
if (ObjectModified)
{
Object->UpdatePosition(mCurrentStep);
Modified = true;
}
}
if (!Modified)
return;
2023-09-02 19:40:52 +02:00
SaveCheckpoint(lcObject::GetCheckpointString(PropertyId));
2023-09-02 19:40:52 +02:00
gMainWindow->UpdateSelectedObjects(false);
UpdateAllViews();
// todo: fix hacky timeline update
if (PropertyId == lcObjectPropertyId::PieceId || PropertyId == lcObjectPropertyId::PieceColor)
{
gMainWindow->UpdateTimeline(false, true);
}
// todo: fix hacky category update
if (PropertyId == lcObjectPropertyId::PieceId)
{
gMainWindow->UpdateInUseCategory();
}
2023-08-04 22:26:29 +02:00
}
2016-10-05 23:28:52 +02:00
bool lcModel::AnyPiecesSelected() const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2019-03-09 18:50:34 +01:00
if (Piece->IsSelected())
2016-10-05 23:28:52 +02:00
return true;
return false;
}
bool lcModel::AnyObjectsSelected() const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2019-03-09 18:50:34 +01:00
if (Piece->IsSelected())
2016-10-05 23:28:52 +02:00
return true;
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
2019-03-09 18:50:34 +01:00
if (Camera->IsSelected())
2016-10-05 23:28:52 +02:00
return true;
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2019-03-09 18:50:34 +01:00
if (Light->IsSelected())
2016-10-05 23:28:52 +02:00
return true;
return false;
}
lcObject* lcModel::GetFocusObject() const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
if (Piece->IsFocused())
2024-06-20 04:22:18 +02:00
return Piece.get();
2016-10-05 23:28:52 +02:00
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
2016-10-05 23:28:52 +02:00
if (Camera->IsFocused())
2024-06-04 18:34:38 +02:00
return Camera.get();
2016-10-05 23:28:52 +02:00
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2016-10-05 23:28:52 +02:00
if (Light->IsFocused())
2024-06-04 18:08:54 +02:00
return Light.get();
2016-10-05 23:28:52 +02:00
return nullptr;
2016-10-05 23:28:52 +02:00
}
lcModel* lcModel::GetFirstSelectedSubmodel() const
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
if (Piece->IsSelected() && Piece->mPieceInfo->IsModel())
return Piece->mPieceInfo->GetModel();
return nullptr;
2016-10-05 23:28:52 +02:00
}
2024-06-17 02:43:02 +02:00
void lcModel::GetSubModels(std::set<lcModel*>& SubModels) const
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->mPieceInfo->IsModel())
{
lcModel* SubModel = Piece->mPieceInfo->GetModel();
2024-06-17 02:43:02 +02:00
SubModels.insert(SubModel);
2016-10-05 23:28:52 +02:00
}
}
}
bool lcModel::GetMoveRotateTransform(lcVector3& Center, lcMatrix33& RelativeRotation) const
{
2021-11-15 03:34:24 +01:00
const bool Relative = gMainWindow->GetRelativeTransform();
2016-10-05 23:28:52 +02:00
int NumSelected = 0;
Center = lcVector3(0.0f, 0.0f, 0.0f);
RelativeRotation = lcMatrix33Identity();
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (!Piece->IsSelected())
continue;
if (Piece->IsFocused() && Relative)
{
Center = Piece->GetRotationCenter();
RelativeRotation = Piece->GetRelativeRotation();
return true;
}
Center += Piece->mModelWorld.GetTranslation();
NumSelected++;
}
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
2016-10-05 23:28:52 +02:00
{
if (!Camera->IsSelected())
continue;
if (Camera->IsFocused() && Relative)
{
Center = Camera->GetSectionPosition(Camera->GetFocusSection());
// RelativeRotation = Piece->GetRelativeRotation();
return true;
}
Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_POSITION);
Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_TARGET);
Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_UPVECTOR);
NumSelected += 3;
}
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2016-10-05 23:28:52 +02:00
{
if (!Light->IsSelected())
continue;
2023-09-04 19:59:16 +02:00
if (Light->IsFocused())
2016-10-05 23:28:52 +02:00
{
2023-09-04 19:59:16 +02:00
Center = Light->GetRotationCenter();
if (Relative)
RelativeRotation = Light->GetRelativeRotation();
2016-10-05 23:28:52 +02:00
return true;
}
Center += Light->GetSectionPosition(LC_LIGHT_SECTION_POSITION);
NumSelected++;
}
if (NumSelected)
{
Center /= NumSelected;
return true;
}
return false;
}
2023-09-04 19:59:16 +02:00
bool lcModel::CanRotateSelection() const
{
int Flags;
2024-05-15 05:45:09 +02:00
std::vector<lcObject*> Selection;
2023-09-04 19:59:16 +02:00
lcObject* Focus;
GetSelectionInformation(&Flags, Selection, &Focus);
if (Flags & LC_SEL_PIECE)
{
if ((Flags & (LC_SEL_CAMERA | LC_SEL_LIGHT)) == 0)
return true;
}
if ((Flags & (LC_SEL_PIECE | LC_SEL_CAMERA)) == 0)
{
if (Focus && Focus->IsLight())
{
lcLight* Light = (lcLight*)Focus;
return (Light->GetAllowedTransforms() & LC_OBJECT_TRANSFORM_ROTATE_XYZ) != 0;
}
}
return false;
}
2016-10-05 23:28:52 +02:00
bool lcModel::GetPieceFocusOrSelectionCenter(lcVector3& Center) const
{
lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
lcPiece* Selected = nullptr;
2016-10-05 23:28:52 +02:00
int NumSelected = 0;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsFocused())
{
Center = Piece->mModelWorld.GetTranslation();
return true;
}
if (Piece->IsSelected())
{
Piece->CompareBoundingBox(Min, Max);
2024-06-20 04:22:18 +02:00
Selected = Piece.get();
2016-10-05 23:28:52 +02:00
NumSelected++;
}
}
if (NumSelected == 1)
Center = Selected->mModelWorld.GetTranslation();
else if (NumSelected)
Center = (Min + Max) / 2.0f;
else
Center = lcVector3(0.0f, 0.0f, 0.0f);
return NumSelected != 0;
}
lcVector3 lcModel::GetSelectionOrModelCenter() const
{
lcVector3 Center;
if (!GetSelectionCenter(Center))
{
lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2021-03-01 00:57:02 +01:00
if (GetVisiblePiecesBoundingBox(Min, Max))
2016-10-05 23:28:52 +02:00
Center = (Min + Max) / 2.0f;
else
Center = lcVector3(0.0f, 0.0f, 0.0f);
}
return Center;
}
bool lcModel::GetFocusPosition(lcVector3& Position) const
{
2021-11-15 03:34:24 +01:00
const lcObject* FocusObject = GetFocusObject();
2016-10-05 23:28:52 +02:00
if (FocusObject)
{
Position = FocusObject->GetSectionPosition(FocusObject->GetFocusSection());
return true;
}
else
{
Position = lcVector3(0.0f, 0.0f, 0.0f);
return false;
}
}
bool lcModel::GetSelectionCenter(lcVector3& Center) const
{
lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
lcPiece* SelectedPiece = nullptr;
2016-10-05 23:28:52 +02:00
bool SinglePieceSelected = true;
bool Selected = false;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsSelected())
{
Piece->CompareBoundingBox(Min, Max);
Selected = true;
if (!SelectedPiece)
2024-06-20 04:22:18 +02:00
SelectedPiece = Piece.get();
2016-10-05 23:28:52 +02:00
else
SinglePieceSelected = false;
}
}
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
2016-10-05 23:28:52 +02:00
{
if (Camera->IsSelected())
{
Camera->CompareBoundingBox(Min, Max);
Selected = true;
SinglePieceSelected = false;
}
}
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2016-10-05 23:28:52 +02:00
{
if (Light->IsSelected())
{
Light->CompareBoundingBox(Min, Max);
Selected = true;
SinglePieceSelected = false;
}
}
if (SelectedPiece && SinglePieceSelected)
Center = SelectedPiece->GetSectionPosition(LC_PIECE_SECTION_POSITION);
else if (Selected)
Center = (Min + Max) / 2.0f;
else
Center = lcVector3(0.0f, 0.0f, 0.0f);
return Selected;
}
2021-03-01 00:57:02 +01:00
lcBoundingBox lcModel::GetAllPiecesBoundingBox() const
{
lcBoundingBox Box;
2024-05-26 22:01:34 +02:00
if (!mPieces.empty())
2021-03-01 00:57:02 +01:00
{
Box.Min = lcVector3(FLT_MAX, FLT_MAX, FLT_MAX);
Box.Max = lcVector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2021-03-01 00:57:02 +01:00
Piece->CompareBoundingBox(Box.Min, Box.Max);
}
else
Box.Min = Box.Max = lcVector3(0.0f, 0.0f, 0.0f);
return Box;
}
bool lcModel::GetVisiblePiecesBoundingBox(lcVector3& Min, lcVector3& Max) const
2016-10-05 23:28:52 +02:00
{
bool Valid = false;
2016-10-05 23:28:52 +02:00
Min = lcVector3(FLT_MAX, FLT_MAX, FLT_MAX);
Max = lcVector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsVisible(mCurrentStep))
{
2016-10-05 23:28:52 +02:00
Piece->CompareBoundingBox(Min, Max);
Valid = true;
}
2016-10-05 23:28:52 +02:00
}
return Valid;
2016-10-05 23:28:52 +02:00
}
std::vector<lcVector3> lcModel::GetPiecesBoundingBoxPoints() const
{
std::vector<lcVector3> Points;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
if (Piece->IsVisible(mCurrentStep))
Piece->SubModelAddBoundingBoxPoints(lcMatrix44Identity(), Points);
return Points;
}
void lcModel::GetPartsList(int DefaultColorIndex, bool ScanSubModels, bool AddSubModels, lcPartsList& PartsList) const
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (!Piece->IsVisibleInSubModel())
continue;
2021-01-17 03:27:39 +01:00
int ColorIndex = Piece->GetColorIndex();
2016-10-05 23:28:52 +02:00
if (ColorIndex == gDefaultColor)
ColorIndex = DefaultColorIndex;
Piece->mPieceInfo->GetPartsList(ColorIndex, ScanSubModels, AddSubModels, PartsList);
2016-10-05 23:28:52 +02:00
}
}
2023-05-23 13:05:56 +02:00
void lcModel::GetPartsListForStep(lcStep Step, int DefaultColorIndex, lcPartsList& PartsList, bool Cumulative) const
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
2023-05-23 13:05:56 +02:00
if (Cumulative ? Piece->GetStepShow() > Step : Piece->GetStepShow() != Step || Piece->IsHidden())
2016-10-05 23:28:52 +02:00
continue;
2021-01-17 03:27:39 +01:00
int ColorIndex = Piece->GetColorIndex();
2016-10-05 23:28:52 +02:00
if (ColorIndex == gDefaultColor)
ColorIndex = DefaultColorIndex;
Piece->mPieceInfo->GetPartsList(ColorIndex, false, true, PartsList);
2016-10-05 23:28:52 +02:00
}
}
2019-05-28 01:39:51 +02:00
void lcModel::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex, std::vector<lcModelPartsEntry>& ModelParts) const
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
Piece->GetModelParts(WorldMatrix, DefaultColorIndex, ModelParts);
2016-10-05 23:28:52 +02:00
}
2024-05-15 05:45:09 +02:00
void lcModel::GetSelectionInformation(int* Flags, std::vector<lcObject*>& Selection, lcObject** Focus) const
2016-10-05 23:28:52 +02:00
{
*Flags = 0;
*Focus = nullptr;
2016-10-05 23:28:52 +02:00
2024-05-26 22:01:34 +02:00
if (mPieces.empty())
2016-10-05 23:28:52 +02:00
*Flags |= LC_SEL_NO_PIECES;
else
{
lcGroup* Group = nullptr;
2016-10-05 23:28:52 +02:00
bool First = true;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (Piece->IsSelected())
{
2024-06-20 04:22:18 +02:00
Selection.emplace_back(Piece.get());
2016-10-05 23:28:52 +02:00
if (Piece->IsFocused())
2024-06-20 04:22:18 +02:00
*Focus = Piece.get();
2016-10-05 23:28:52 +02:00
if (Piece->mPieceInfo->IsModel())
*Flags |= LC_SEL_MODEL_SELECTED;
if (Piece->IsHidden())
*Flags |= LC_SEL_HIDDEN | LC_SEL_HIDDEN_SELECTED;
else
*Flags |= LC_SEL_VISIBLE_SELECTED;
*Flags |= LC_SEL_PIECE | LC_SEL_SELECTED;
if (Piece->AreTrainTrackConnectionsVisible())
*Flags |= LC_SEL_TRAIN_TRACK_VISIBLE;
if (Piece->CanAddControlPoint())
2016-10-05 23:28:52 +02:00
*Flags |= LC_SEL_CAN_ADD_CONTROL_POINT;
if (Piece->CanRemoveControlPoint())
*Flags |= LC_SEL_CAN_REMOVE_CONTROL_POINT;
2016-10-05 23:28:52 +02:00
if (Piece->GetGroup() != nullptr)
2016-10-05 23:28:52 +02:00
{
*Flags |= LC_SEL_GROUPED;
if (Piece->IsFocused())
*Flags |= LC_SEL_FOCUS_GROUPED;
}
if (First)
{
Group = Piece->GetGroup();
First = false;
}
else
{
if (Group != Piece->GetGroup())
*Flags |= LC_SEL_CAN_GROUP;
else
if (Group == nullptr)
2016-10-05 23:28:52 +02:00
*Flags |= LC_SEL_CAN_GROUP;
}
}
else
{
*Flags |= LC_SEL_UNSELECTED;
if (Piece->IsHidden())
*Flags |= LC_SEL_HIDDEN;
}
}
}
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
2016-10-05 23:28:52 +02:00
{
if (Camera->IsSelected())
{
2024-06-04 18:34:38 +02:00
Selection.emplace_back(Camera.get());
2023-09-04 19:59:16 +02:00
*Flags |= LC_SEL_SELECTED | LC_SEL_CAMERA;
2016-10-05 23:28:52 +02:00
if (Camera->IsFocused())
2024-06-04 18:34:38 +02:00
*Focus = Camera.get();
2016-10-05 23:28:52 +02:00
}
}
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2016-10-05 23:28:52 +02:00
{
if (Light->IsSelected())
{
2024-06-04 18:08:54 +02:00
Selection.emplace_back(Light.get());
2023-09-04 19:59:16 +02:00
*Flags |= LC_SEL_SELECTED | LC_SEL_LIGHT;
2016-10-05 23:28:52 +02:00
if (Light->IsFocused())
2024-06-04 18:08:54 +02:00
*Focus = Light.get();
2016-10-05 23:28:52 +02:00
}
}
}
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> lcModel::GetSelectionModePieces(const lcPiece* SelectedPiece) const
2017-11-22 02:58:36 +01:00
{
2021-11-15 03:34:24 +01:00
const PieceInfo* Info = SelectedPiece->mPieceInfo;
const int ColorIndex = SelectedPiece->GetColorIndex();
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Pieces;
2017-11-22 02:58:36 +01:00
switch (gMainWindow->GetSelectionMode())
{
2020-05-03 22:04:40 +02:00
case lcSelectionMode::Single:
2017-11-22 02:58:36 +01:00
break;
2020-05-03 22:04:40 +02:00
case lcSelectionMode::Piece:
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
if (Piece->IsVisible(mCurrentStep) && Piece->mPieceInfo == Info && Piece.get() != SelectedPiece)
Pieces.emplace_back(Piece.get());
2017-11-22 02:58:36 +01:00
break;
2020-05-03 22:04:40 +02:00
case lcSelectionMode::Color:
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
if (Piece->IsVisible(mCurrentStep) && Piece->GetColorIndex() == ColorIndex && Piece.get() != SelectedPiece)
Pieces.emplace_back(Piece.get());
2017-11-22 02:58:36 +01:00
break;
2020-05-03 22:04:40 +02:00
case lcSelectionMode::PieceColor:
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
if (Piece->IsVisible(mCurrentStep) && Piece->mPieceInfo == Info && Piece->GetColorIndex() == ColorIndex && Piece.get() != SelectedPiece)
Pieces.emplace_back(Piece.get());
2017-11-22 02:58:36 +01:00
break;
}
return Pieces;
}
2016-10-05 23:28:52 +02:00
void lcModel::ClearSelection(bool UpdateInterface)
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2019-03-09 18:50:34 +01:00
Piece->SetSelected(false);
2016-10-05 23:28:52 +02:00
2024-06-04 18:34:38 +02:00
for (const std::unique_ptr<lcCamera>& Camera : mCameras)
2019-03-09 18:50:34 +01:00
Camera->SetSelected(false);
2016-10-05 23:28:52 +02:00
2024-06-04 18:08:54 +02:00
for (const std::unique_ptr<lcLight>& Light : mLights)
2019-03-09 18:50:34 +01:00
Light->SetSelected(false);
2016-10-05 23:28:52 +02:00
if (UpdateInterface)
{
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
}
void lcModel::SelectGroup(lcGroup* TopGroup, bool Select)
{
if (!TopGroup)
return;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
if (!Piece->IsSelected() && Piece->IsVisible(mCurrentStep) && (Piece->GetTopGroup() == TopGroup))
Piece->SetSelected(Select);
}
void lcModel::FocusOrDeselectObject(const lcObjectSection& ObjectSection)
{
lcObject* FocusObject = GetFocusObject();
lcObject* Object = ObjectSection.Object;
2021-11-15 03:34:24 +01:00
const quint32 Section = ObjectSection.Section;
2016-10-05 23:28:52 +02:00
if (Object)
{
2021-11-15 03:34:24 +01:00
const bool WasSelected = Object->IsSelected();
2016-10-05 23:28:52 +02:00
if (!Object->IsFocused(Section))
{
if (FocusObject)
FocusObject->SetFocused(FocusObject->GetFocusSection(), false);
Object->SetFocused(Section, true);
}
else
Object->SetFocused(Section, false);
2016-10-05 23:28:52 +02:00
2021-11-15 03:34:24 +01:00
const bool IsSelected = Object->IsSelected();
2016-10-05 23:28:52 +02:00
if (Object->IsPiece() && (WasSelected != IsSelected))
2017-11-22 02:58:36 +01:00
{
lcPiece* Piece = (lcPiece*)Object;
2020-05-03 22:04:40 +02:00
if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
2017-11-22 02:58:36 +01:00
SelectGroup(Piece->GetTopGroup(), IsSelected);
else
{
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Pieces = GetSelectionModePieces(Piece);
AddToSelection(Pieces, false, false);
2017-11-22 02:58:36 +01:00
}
}
2016-10-05 23:28:52 +02:00
}
else
{
if (FocusObject)
FocusObject->SetFocused(FocusObject->GetFocusSection(), false);
}
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
2017-12-02 21:22:04 +01:00
void lcModel::ClearSelectionAndSetFocus(lcObject* Object, quint32 Section, bool EnableSelectionMode)
2016-10-05 23:28:52 +02:00
{
ClearSelection(false);
if (Object)
{
Object->SetFocused(Section, true);
if (Object->IsPiece())
{
lcPiece* Piece = dynamic_cast<lcPiece*>(Object);
SelectGroup(Piece->GetTopGroup(), true);
2017-11-22 02:58:36 +01:00
if (EnableSelectionMode)
{
std::vector<lcObject*> Pieces = GetSelectionModePieces(Piece);
AddToSelection(Pieces, false, false);
}
2017-11-22 02:58:36 +01:00
}
2016-10-05 23:28:52 +02:00
}
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
2017-11-22 02:58:36 +01:00
void lcModel::ClearSelectionAndSetFocus(const lcObjectSection& ObjectSection, bool EnableSelectionMode)
2016-10-05 23:28:52 +02:00
{
2017-11-22 02:58:36 +01:00
ClearSelectionAndSetFocus(ObjectSection.Object, ObjectSection.Section, EnableSelectionMode);
2016-10-05 23:28:52 +02:00
}
2024-05-26 22:01:34 +02:00
void lcModel::SetSelectionAndFocus(const std::vector<lcObject*>& Selection, lcObject* Focus, quint32 Section, bool EnableSelectionMode)
2016-10-05 23:28:52 +02:00
{
ClearSelection(false);
if (Focus)
{
Focus->SetFocused(Section, true);
if (Focus->IsPiece())
{
2016-10-05 23:28:52 +02:00
SelectGroup(((lcPiece*)Focus)->GetTopGroup(), true);
if (EnableSelectionMode)
{
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Pieces = GetSelectionModePieces((lcPiece*)Focus);
AddToSelection(Pieces, false, false);
}
}
2016-10-05 23:28:52 +02:00
}
AddToSelection(Selection, EnableSelectionMode, true);
2016-10-05 23:28:52 +02:00
}
2024-05-26 22:01:34 +02:00
void lcModel::AddToSelection(const std::vector<lcObject*>& Objects, bool EnableSelectionMode, bool UpdateInterface)
2016-10-05 23:28:52 +02:00
{
for (lcObject* Object : Objects)
2016-10-05 23:28:52 +02:00
{
2021-11-15 03:34:24 +01:00
const bool WasSelected = Object->IsSelected();
Object->SetSelected(true);
2016-10-05 23:28:52 +02:00
if (Object->IsPiece())
{
if (!WasSelected)
SelectGroup(((lcPiece*)Object)->GetTopGroup(), true);
if (EnableSelectionMode)
{
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Pieces = GetSelectionModePieces((lcPiece*)Object);
AddToSelection(Pieces, false, false);
}
}
2016-10-05 23:28:52 +02:00
}
2017-11-22 02:58:36 +01:00
if (UpdateInterface)
{
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2017-11-22 02:58:36 +01:00
}
2016-10-05 23:28:52 +02:00
}
2024-05-26 22:01:34 +02:00
void lcModel::RemoveFromSelection(const std::vector<lcObject*>& Objects)
{
for (lcObject* SelectedObject : Objects)
{
2021-11-15 03:34:24 +01:00
const bool WasSelected = SelectedObject->IsSelected();
SelectedObject->SetSelected(false);
if (WasSelected && SelectedObject->IsPiece())
{
lcPiece* Piece = (lcPiece*)SelectedObject;
2020-05-03 22:04:40 +02:00
if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
SelectGroup(Piece->GetTopGroup(), false);
else
{
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Pieces = GetSelectionModePieces(Piece);
for (lcObject* Object : Pieces)
{
if (Object->IsSelected())
{
Object->SetSelected(false);
SelectGroup(((lcPiece*)Object)->GetTopGroup(), false);
}
}
}
}
}
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
}
void lcModel::RemoveFromSelection(const lcObjectSection& ObjectSection)
{
lcObject* SelectedObject = ObjectSection.Object;
if (!SelectedObject)
return;
2021-11-15 03:34:24 +01:00
const bool WasSelected = SelectedObject->IsSelected();
2017-11-22 02:58:36 +01:00
if (SelectedObject->IsFocused(ObjectSection.Section))
SelectedObject->SetSelected(ObjectSection.Section, false);
else
SelectedObject->SetSelected(false);
if (SelectedObject->IsPiece() && WasSelected)
{
lcPiece* Piece = (lcPiece*)SelectedObject;
2020-05-03 22:04:40 +02:00
if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
SelectGroup(Piece->GetTopGroup(), false);
else
{
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Pieces = GetSelectionModePieces(Piece);
for (lcObject* Object : Pieces)
{
if (Object->IsSelected())
{
Object->SetSelected(false);
SelectGroup(((lcPiece*)Object)->GetTopGroup(), false);
}
}
}
}
2017-11-22 02:58:36 +01:00
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
}
2016-10-05 23:28:52 +02:00
void lcModel::SelectAllPieces()
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
if (Piece->IsVisible(mCurrentStep))
Piece->SetSelected(true);
2020-12-16 02:19:32 +01:00
if (!mIsPreview)
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::InvertSelection()
{
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
if (Piece->IsVisible(mCurrentStep))
Piece->SetSelected(!Piece->IsSelected());
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::HideSelectedPieces()
{
bool Modified = false;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
{
2016-10-05 23:28:52 +02:00
if (Piece->IsSelected())
{
Piece->SetHidden(true);
Piece->SetSelected(false);
Modified = true;
2016-10-05 23:28:52 +02:00
}
}
if (!Modified)
return;
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, true);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
SaveCheckpoint(tr("Hide"));
2016-10-05 23:28:52 +02:00
}
void lcModel::HideUnselectedPieces()
{
bool Modified = false;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
{
2016-10-05 23:28:52 +02:00
if (!Piece->IsSelected())
{
2016-10-05 23:28:52 +02:00
Piece->SetHidden(true);
Modified = true;
}
2016-10-05 23:28:52 +02:00
}
if (!Modified)
return;
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, true);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
SaveCheckpoint(tr("Hide"));
2016-10-05 23:28:52 +02:00
}
void lcModel::UnhideSelectedPieces()
{
bool Modified = false;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
{
if (Piece->IsSelected() && Piece->IsHidden())
{
2016-10-05 23:28:52 +02:00
Piece->SetHidden(false);
Modified = true;
}
2016-10-05 23:28:52 +02:00
}
if (!Modified)
return;
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, true);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
SaveCheckpoint(tr("Unhide"));
2016-10-05 23:28:52 +02:00
}
void lcModel::UnhideAllPieces()
{
bool Modified = false;
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
{
if (Piece->IsHidden())
{
Piece->SetHidden(false);
Modified = true;
}
}
if (!Modified)
return;
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, true);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
SaveCheckpoint(tr("Unhide"));
2016-10-05 23:28:52 +02:00
}
void lcModel::FindReplacePiece(bool SearchForward, bool FindAll, bool Replace)
2016-10-05 23:28:52 +02:00
{
2024-05-26 22:01:34 +02:00
if (mPieces.empty())
2016-10-05 23:28:52 +02:00
return;
const lcFindReplaceParams& Params = lcView::GetFindReplaceParams();
const bool ReplacePieceInfo = Replace && Params.ReplacePieceInfo;
const bool ReplaceColor = Replace && lcGetColorCode(Params.ReplaceColorIndex) != LC_COLOR_NOCOLOR;
// Check if we are supposed to actually replace something
const bool Replacing = (ReplaceColor || ReplacePieceInfo);
2021-02-01 02:32:38 +01:00
auto PieceMatches = [&Params](const lcPiece* Piece)
{
if (Params.FindInfo && Params.FindInfo != Piece->mPieceInfo)
2021-01-31 21:05:15 +01:00
return false;
if (!Params.FindString.isEmpty() && !strcasestr(Piece->mPieceInfo->m_strDescription, Params.FindString.toLatin1()))
2021-01-31 21:05:15 +01:00
return false;
return (lcGetColorCode(Params.FindColorIndex) == LC_COLOR_NOCOLOR) || (Piece->GetColorIndex() == Params.FindColorIndex);
2021-01-31 00:37:17 +01:00
};
auto ReplacePiece = [&Params, ReplacePieceInfo, ReplaceColor](lcPiece* Piece)
{
if (ReplaceColor)
Piece->SetColorIndex(Params.ReplaceColorIndex);
if (ReplacePieceInfo)
Piece->SetPieceInfo(Params.ReplacePieceInfo, QString(), true);
};
2024-06-20 04:22:18 +02:00
size_t StartIndex = mPieces.size() - 1;
int ReplacedCount = 0;
if (!FindAll)
2016-10-05 23:28:52 +02:00
{
// We have to find the currently focused piece, in order to find next/prev match and (optionally) to replace it
lcPiece* const FocusedPiece = dynamic_cast<lcPiece*>(GetFocusObject());
2016-10-05 23:28:52 +02:00
if (FocusedPiece)
2021-01-31 00:37:17 +01:00
{
2024-06-20 04:22:18 +02:00
for (size_t PieceIndex = 0; PieceIndex < mPieces.size(); PieceIndex++)
2016-10-05 23:28:52 +02:00
{
2024-06-20 04:22:18 +02:00
if (FocusedPiece == mPieces[PieceIndex].get())
2021-02-01 02:49:22 +01:00
{
2024-06-20 04:22:18 +02:00
StartIndex = PieceIndex;
break;
2021-02-01 02:49:22 +01:00
}
2016-10-05 23:28:52 +02:00
}
2021-01-31 00:37:17 +01:00
if (Replacing && PieceMatches(FocusedPiece))
{
ReplacePiece(FocusedPiece);
ReplacedCount++;
}
2016-10-05 23:28:52 +02:00
}
}
2024-06-20 04:22:18 +02:00
size_t CurrentIndex = StartIndex;
2021-01-31 00:37:17 +01:00
lcPiece* Focus = nullptr;
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Selection;
2016-10-05 23:28:52 +02:00
for (;;)
{
if (SearchForward)
2024-06-20 04:22:18 +02:00
{
CurrentIndex++;
if (CurrentIndex >= mPieces.size())
CurrentIndex = 0;
}
2016-10-05 23:28:52 +02:00
else
2024-06-20 04:22:18 +02:00
{
if (CurrentIndex == 0)
CurrentIndex = mPieces.size();
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
CurrentIndex--;
}
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
lcPiece* Current = mPieces[CurrentIndex].get();
2016-10-05 23:28:52 +02:00
if (Current->IsVisible(mCurrentStep) && PieceMatches(Current))
2016-10-05 23:28:52 +02:00
{
2021-01-31 20:02:50 +01:00
if (FindAll)
{
2024-05-26 22:01:34 +02:00
Selection.emplace_back(Current);
if (Replacing)
{
ReplacePiece(Current);
ReplacedCount++;
}
}
2021-01-31 20:02:50 +01:00
else
{
Focus = Current;
break;
}
2016-10-05 23:28:52 +02:00
}
2021-01-31 00:37:17 +01:00
2024-06-20 04:22:18 +02:00
if (CurrentIndex == StartIndex)
2021-01-31 00:37:17 +01:00
break;
2016-10-05 23:28:52 +02:00
}
2021-01-31 20:02:50 +01:00
if (FindAll)
SetSelectionAndFocus(Selection, nullptr, 0, false);
else
ClearSelectionAndSetFocus(Focus, LC_PIECE_SECTION_POSITION, false);
if (ReplacedCount)
{
SaveCheckpoint(tr("Replacing Piece(s)", "", ReplacedCount));
gMainWindow->UpdateSelectedObjects(false);
UpdateAllViews();
gMainWindow->UpdateTimeline(false, true);
}
2016-10-05 23:28:52 +02:00
}
void lcModel::UndoAction()
{
2021-01-10 01:37:32 +01:00
if (mUndoHistory.size() < 2)
2016-10-05 23:28:52 +02:00
return;
2019-06-24 03:28:14 +02:00
lcModelHistoryEntry* Undo = mUndoHistory.front();
mUndoHistory.erase(mUndoHistory.begin());
mRedoHistory.insert(mRedoHistory.begin(), Undo);
2016-10-05 23:28:52 +02:00
LoadCheckPoint(mUndoHistory[0]);
gMainWindow->UpdateModified(IsModified());
2019-06-24 03:28:14 +02:00
gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
2016-10-05 23:28:52 +02:00
}
void lcModel::RedoAction()
{
2021-01-10 01:37:32 +01:00
if (mRedoHistory.empty())
2016-10-05 23:28:52 +02:00
return;
2019-06-24 03:28:14 +02:00
lcModelHistoryEntry* Redo = mRedoHistory.front();
mRedoHistory.erase(mRedoHistory.begin());
mUndoHistory.insert(mUndoHistory.begin(), Redo);
2016-10-05 23:28:52 +02:00
LoadCheckPoint(Redo);
gMainWindow->UpdateModified(IsModified());
2019-06-24 03:28:14 +02:00
gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
2016-10-05 23:28:52 +02:00
}
void lcModel::BeginMouseTool()
{
mMouseToolDistance = lcVector3(0.0f, 0.0f, 0.0f);
mMouseToolFirstMove = true;
2016-10-05 23:28:52 +02:00
}
void lcModel::EndMouseTool(lcTool Tool, bool Accept)
{
2021-01-10 01:37:32 +01:00
if (!Accept)
2016-10-05 23:28:52 +02:00
{
2020-12-24 03:30:01 +01:00
if (!mUndoHistory.empty())
LoadCheckPoint(mUndoHistory.front());
2016-10-05 23:28:52 +02:00
return;
}
switch (Tool)
{
2020-12-05 00:38:49 +01:00
case lcTool::Insert:
2023-08-13 15:15:52 +02:00
case lcTool::PointLight:
2020-12-05 00:38:49 +01:00
case lcTool::SpotLight:
2023-08-13 15:15:52 +02:00
case lcTool::DirectionalLight:
2023-08-04 22:26:29 +02:00
case lcTool::AreaLight:
2016-10-05 23:28:52 +02:00
break;
2020-12-05 00:38:49 +01:00
case lcTool::Camera:
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("New Camera"));
break;
2020-12-05 00:38:49 +01:00
case lcTool::Select:
2016-10-05 23:28:52 +02:00
break;
2020-12-05 00:38:49 +01:00
case lcTool::Move:
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Move"));
break;
2020-12-05 00:38:49 +01:00
case lcTool::Rotate:
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Rotate"));
break;
2020-12-05 00:38:49 +01:00
case lcTool::Eraser:
case lcTool::Paint:
case lcTool::ColorPicker:
2016-10-05 23:28:52 +02:00
break;
2020-12-05 00:38:49 +01:00
case lcTool::Zoom:
2021-01-10 01:37:32 +01:00
if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Zoom"));
break;
2020-12-05 00:38:49 +01:00
case lcTool::Pan:
2021-01-10 01:37:32 +01:00
if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Pan"));
break;
2020-12-05 00:38:49 +01:00
case lcTool::RotateView:
2021-01-10 01:37:32 +01:00
if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Orbit"));
break;
2020-12-05 00:38:49 +01:00
case lcTool::Roll:
2021-01-10 01:37:32 +01:00
if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Roll"));
break;
2020-12-05 00:38:49 +01:00
case lcTool::ZoomRegion:
2016-10-05 23:28:52 +02:00
break;
2020-12-05 00:38:49 +01:00
case lcTool::Count:
2016-10-05 23:28:52 +02:00
break;
}
}
2024-11-03 03:34:22 +01:00
void lcModel::InsertPieceToolClicked(PieceInfo* Info, const lcMatrix44& WorldMatrix)
2016-10-05 23:28:52 +02:00
{
2024-11-03 03:34:22 +01:00
lcPiece* Piece = new lcPiece(Info);
2016-10-05 23:28:52 +02:00
Piece->Initialize(WorldMatrix, mCurrentStep);
Piece->SetColorIndex(gMainWindow->mColorIndex);
Piece->UpdatePosition(mCurrentStep);
AddPiece(Piece);
gMainWindow->UpdateTimeline(false, false);
2017-11-22 02:58:36 +01:00
ClearSelectionAndSetFocus(Piece, LC_PIECE_SECTION_POSITION, false);
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Insert"));
}
void lcModel::InsertLightToolClicked(const lcVector3& Position, lcLightType LightType)
2016-10-05 23:28:52 +02:00
{
lcLight* Light = new lcLight(Position, LightType);
2016-10-05 23:28:52 +02:00
Light->CreateName(mLights);
2024-05-26 22:01:34 +02:00
mLights.emplace_back(Light);
2016-10-05 23:28:52 +02:00
2017-11-22 02:58:36 +01:00
ClearSelectionAndSetFocus(Light, LC_LIGHT_SECTION_POSITION, false);
2016-10-05 23:28:52 +02:00
2023-08-13 15:15:52 +02:00
switch (LightType)
{
case lcLightType::Point:
SaveCheckpoint(tr("New Point Light"));
2023-08-13 15:15:52 +02:00
break;
2016-10-05 23:28:52 +02:00
2023-08-13 15:15:52 +02:00
case lcLightType::Spot:
SaveCheckpoint(tr("New Spot Light"));
break;
2016-10-05 23:28:52 +02:00
2023-08-13 15:15:52 +02:00
case lcLightType::Directional:
SaveCheckpoint(tr("New Directional Light"));
break;
2016-10-05 23:28:52 +02:00
2023-08-13 15:15:52 +02:00
case lcLightType::Area:
SaveCheckpoint(tr("New Area Light"));
break;
2016-10-05 23:28:52 +02:00
2023-09-02 05:46:29 +02:00
case lcLightType::Count:
break;
2023-08-13 15:15:52 +02:00
}
2016-10-05 23:28:52 +02:00
}
void lcModel::BeginCameraTool(const lcVector3& Position, const lcVector3& Target)
{
lcCamera* Camera = new lcCamera(Position[0], Position[1], Position[2], Target[0], Target[1], Target[2]);
Camera->CreateName(mCameras);
2024-05-26 22:01:34 +02:00
mCameras.emplace_back(Camera);
2016-10-05 23:28:52 +02:00
mMouseToolDistance = Position;
mMouseToolFirstMove = false;
2016-10-05 23:28:52 +02:00
2017-11-22 02:58:36 +01:00
ClearSelectionAndSetFocus(Camera, LC_CAMERA_SECTION_TARGET, false);
2016-10-05 23:28:52 +02:00
}
void lcModel::UpdateCameraTool(const lcVector3& Position)
{
2024-06-04 18:34:38 +02:00
if (mCameras.empty())
return;
std::unique_ptr<lcCamera>& Camera = mCameras.back();
2016-10-05 23:28:52 +02:00
Camera->MoveSelected(1, false, Position - mMouseToolDistance);
2016-10-05 23:28:52 +02:00
Camera->UpdatePosition(1);
mMouseToolDistance = Position;
mMouseToolFirstMove = false;
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::UpdateMoveTool(const lcVector3& Distance, bool AllowRelative, bool AlternateButtonDrag)
2016-10-05 23:28:52 +02:00
{
2021-11-15 03:34:24 +01:00
const lcVector3 PieceDistance = SnapPosition(Distance) - SnapPosition(mMouseToolDistance);
const lcVector3 ObjectDistance = Distance - mMouseToolDistance;
2016-10-05 23:28:52 +02:00
MoveSelectedObjects(PieceDistance, ObjectDistance, AllowRelative, AlternateButtonDrag, true, false, mMouseToolFirstMove);
2016-10-05 23:28:52 +02:00
mMouseToolDistance = Distance;
mMouseToolFirstMove = false;
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag)
{
2021-11-15 03:34:24 +01:00
const lcVector3 Delta = SnapRotation(Angles) - SnapRotation(mMouseToolDistance);
2023-09-04 19:59:16 +02:00
RotateSelectedObjects(Delta, true, AlternateButtonDrag, false, false);
2016-10-05 23:28:52 +02:00
mMouseToolDistance = Angles;
mMouseToolFirstMove = false;
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::UpdateScaleTool(const float Scale)
{
ScaleSelectedPieces(Scale, true, false);
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::EraserToolClicked(lcObject* Object)
{
if (!Object)
return;
switch (Object->GetType())
{
2020-04-19 04:45:21 +02:00
case lcObjectType::Piece:
2024-06-20 04:22:18 +02:00
if (auto PieceIt = std::find_if(mPieces.begin(), mPieces.end(), [Object](const std::unique_ptr<lcPiece>& CheckPiece) { return CheckPiece.get() == Object; }); PieceIt != mPieces.end())
{
mPieces.erase(PieceIt);
RemoveEmptyGroups();
}
2016-10-05 23:28:52 +02:00
break;
2020-04-19 04:45:21 +02:00
case lcObjectType::Camera:
2016-10-05 23:28:52 +02:00
{
2021-02-27 20:15:04 +01:00
std::vector<lcView*> Views = lcView::GetModelViews(this);
for (lcView* View : Views)
2016-10-05 23:28:52 +02:00
{
2020-12-04 21:49:01 +01:00
lcCamera* Camera = View->GetCamera();
2016-10-05 23:28:52 +02:00
if (Camera == Object)
View->SetCamera(Camera, true);
}
2024-06-04 18:34:38 +02:00
for (std::vector<std::unique_ptr<lcCamera>>::iterator CameraIt = mCameras.begin(); CameraIt != mCameras.end(); CameraIt++)
{
if (CameraIt->get() == Object)
{
mCameras.erase(CameraIt);
break;
}
}
2016-10-05 23:28:52 +02:00
}
break;
2020-04-19 04:45:21 +02:00
case lcObjectType::Light:
2024-06-04 18:08:54 +02:00
for (std::vector<std::unique_ptr<lcLight>>::iterator LightIt = mLights.begin(); LightIt != mLights.end(); LightIt++)
{
if (LightIt->get() == Object)
{
mLights.erase(LightIt);
break;
}
}
2016-10-05 23:28:52 +02:00
break;
}
gMainWindow->UpdateTimeline(false, false);
gMainWindow->UpdateSelectedObjects(true);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Deleting"));
}
void lcModel::PaintToolClicked(lcObject* Object)
{
2020-04-19 04:45:21 +02:00
if (!Object || !Object->IsPiece())
2016-10-05 23:28:52 +02:00
return;
lcPiece* Piece = (lcPiece*)Object;
2021-01-17 03:27:39 +01:00
if (Piece->GetColorIndex() != gMainWindow->mColorIndex)
2016-10-05 23:28:52 +02:00
{
Piece->SetColorIndex(gMainWindow->mColorIndex);
SaveCheckpoint(tr("Painting"));
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, true);
}
}
2021-11-15 03:34:24 +01:00
void lcModel::ColorPickerToolClicked(const lcObject* Object)
{
2020-04-19 04:45:21 +02:00
if (!Object || !Object->IsPiece())
return;
2021-11-15 03:34:24 +01:00
const lcPiece* Piece = (lcPiece*)Object;
2021-01-17 03:27:39 +01:00
gMainWindow->SetColorIndex(Piece->GetColorIndex());
}
2016-10-05 23:28:52 +02:00
void lcModel::UpdateZoomTool(lcCamera* Camera, float Mouse)
{
Camera->Zoom(Mouse - mMouseToolDistance.x, mCurrentStep, gMainWindow->GetAddKeys());
mMouseToolDistance.x = Mouse;
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::UpdatePanTool(lcCamera* Camera, const lcVector3& Distance)
{
Camera->Pan(Distance, mCurrentStep, gMainWindow->GetAddKeys());
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::UpdateOrbitTool(lcCamera* Camera, float MouseX, float MouseY)
{
lcVector3 Center;
GetSelectionCenter(Center);
2020-12-16 02:19:32 +01:00
2016-10-05 23:28:52 +02:00
Camera->Orbit(MouseX - mMouseToolDistance.x, MouseY - mMouseToolDistance.y, Center, mCurrentStep, gMainWindow->GetAddKeys());
mMouseToolDistance.x = MouseX;
mMouseToolDistance.y = MouseY;
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::UpdateRollTool(lcCamera* Camera, float Mouse)
{
Camera->Roll(Mouse - mMouseToolDistance.x, mCurrentStep, gMainWindow->GetAddKeys());
mMouseToolDistance.x = Mouse;
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
}
void lcModel::ZoomRegionToolClicked(lcCamera* Camera, float AspectRatio, const lcVector3& Position, const lcVector3& TargetPosition, const lcVector3* Corners)
{
Camera->ZoomRegion(AspectRatio, Position, TargetPosition, Corners, mCurrentStep, gMainWindow->GetAddKeys());
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
if (!Camera->IsSimple())
SaveCheckpoint(tr("Zoom"));
}
void lcModel::LookAt(lcCamera* Camera)
{
lcVector3 Center;
if (!GetSelectionCenter(Center))
{
lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2021-03-01 00:57:02 +01:00
if (GetVisiblePiecesBoundingBox(Min, Max))
2016-10-05 23:28:52 +02:00
Center = (Min + Max) / 2.0f;
else
Center = lcVector3(0.0f, 0.0f, 0.0f);
}
Camera->Center(Center, mCurrentStep, gMainWindow->GetAddKeys());
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
if (!Camera->IsSimple())
SaveCheckpoint(tr("Look At"));
}
void lcModel::MoveCamera(lcCamera* Camera, const lcVector3& Direction)
{
Camera->MoveRelative(Direction, mCurrentStep, gMainWindow->GetAddKeys());
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
if (!Camera->IsSimple())
SaveCheckpoint(tr("Moving Camera"));
}
void lcModel::ZoomExtents(lcCamera* Camera, float Aspect, const lcMatrix44& WorldMatrix)
2016-10-05 23:28:52 +02:00
{
std::vector<lcVector3> Points = GetPiecesBoundingBoxPoints();
2016-10-05 23:28:52 +02:00
if (Points.empty())
2016-10-05 23:28:52 +02:00
return;
lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2016-10-05 23:28:52 +02:00
for (lcVector3& Point : Points)
{
Point = lcMul31(Point, WorldMatrix);
Min = lcMin(Point, Min);
Max = lcMax(Point, Max);
}
2021-11-15 03:34:24 +01:00
const lcVector3 Center = (Min + Max) / 2.0f;
2016-10-05 23:28:52 +02:00
Camera->ZoomExtents(Aspect, Center, Points, mCurrentStep, gMainWindow ? gMainWindow->GetAddKeys() : false);
2016-10-05 23:28:52 +02:00
if (!mIsPreview && gMainWindow)
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
if (!Camera->IsSimple())
SaveCheckpoint(tr("Zoom"));
}
void lcModel::Zoom(lcCamera* Camera, float Amount)
{
2021-01-10 01:37:32 +01:00
Camera->Zoom(Amount, mCurrentStep, gMainWindow->GetAddKeys());
2020-12-16 02:19:32 +01:00
if (!mIsPreview)
gMainWindow->UpdateSelectedObjects(false);
2020-12-16 02:19:32 +01:00
UpdateAllViews();
2016-10-05 23:28:52 +02:00
if (!Camera->IsSimple())
SaveCheckpoint(tr("Zoom"));
}
void lcModel::ShowPropertiesDialog()
{
lcPropertiesDialogOptions Options;
Options.Properties = mProperties;
2021-03-01 00:57:02 +01:00
Options.BoundingBox = GetAllPiecesBoundingBox();
2016-10-05 23:28:52 +02:00
GetPartsList(gDefaultColor, true, false, Options.PartsList);
2016-10-05 23:28:52 +02:00
2017-12-11 03:12:31 +01:00
lcQPropertiesDialog Dialog(gMainWindow, &Options);
if (Dialog.exec() != QDialog::Accepted)
2016-10-05 23:28:52 +02:00
return;
if (mProperties == Options.Properties)
return;
mProperties = Options.Properties;
gMainWindow->GetPreviewWidget()->UpdatePreview();
2016-10-05 23:28:52 +02:00
SaveCheckpoint(tr("Changing Properties"));
}
void lcModel::ShowSelectByNameDialog()
{
2024-05-26 22:01:34 +02:00
if (mPieces.empty() && mCameras.empty() && mLights.empty())
2016-10-05 23:28:52 +02:00
{
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to select."));
return;
}
lcQSelectDialog Dialog(gMainWindow, this);
2016-10-05 23:28:52 +02:00
if (Dialog.exec() != QDialog::Accepted)
return;
SetSelectionAndFocus(Dialog.mObjects, nullptr, 0, false);
2016-10-05 23:28:52 +02:00
}
void lcModel::ShowArrayDialog()
{
lcVector3 Center;
if (!GetPieceFocusOrSelectionCenter(Center))
{
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No pieces selected."));
return;
}
2023-08-13 15:15:52 +02:00
2023-05-16 06:02:17 +02:00
lcArrayDialog Dialog(gMainWindow);
2016-10-05 23:28:52 +02:00
if (Dialog.exec() != QDialog::Accepted)
return;
if (Dialog.mCounts[0] * Dialog.mCounts[1] * Dialog.mCounts[2] < 2)
{
QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Array only has 1 element or less, no pieces added."));
return;
}
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> NewPieces;
2016-10-05 23:28:52 +02:00
for (int Step1 = 0; Step1 < Dialog.mCounts[0]; Step1++)
{
for (int Step2 = 0; Step2 < Dialog.mCounts[1]; Step2++)
{
for (int Step3 = (Step1 == 0 && Step2 == 0) ? 1 : 0; Step3 < Dialog.mCounts[2]; Step3++)
{
lcMatrix44 ModelWorld;
lcVector3 Position;
lcVector3 RotationAngles = Dialog.mRotations[0] * Step1 + Dialog.mRotations[1] * Step2 + Dialog.mRotations[2] * Step3;
2021-11-15 03:34:24 +01:00
const lcVector3 Offset = Dialog.mOffsets[0] * Step1 + Dialog.mOffsets[1] * Step2 + Dialog.mOffsets[2] * Step3;
2016-10-05 23:28:52 +02:00
2024-06-20 04:22:18 +02:00
for (const std::unique_ptr<lcPiece>& Piece : mPieces)
2016-10-05 23:28:52 +02:00
{
if (!Piece->IsSelected())
continue;
ModelWorld = Piece->mModelWorld;
ModelWorld.r[3] -= lcVector4(Center, 0.0f);
ModelWorld = lcMul(ModelWorld, lcMatrix44RotationX(RotationAngles[0] * LC_DTOR));
ModelWorld = lcMul(ModelWorld, lcMatrix44RotationY(RotationAngles[1] * LC_DTOR));
ModelWorld = lcMul(ModelWorld, lcMatrix44RotationZ(RotationAngles[2] * LC_DTOR));
ModelWorld.r[3] += lcVector4(Center, 0.0f);
Position = lcVector3(ModelWorld.r[3].x, ModelWorld.r[3].y, ModelWorld.r[3].z);
ModelWorld.SetTranslation(Position + Offset);
2017-07-23 05:54:33 +02:00
lcPiece* NewPiece = new lcPiece(nullptr);
NewPiece->SetPieceInfo(Piece->mPieceInfo, Piece->GetID(), true);
2016-10-05 23:28:52 +02:00
NewPiece->Initialize(ModelWorld, mCurrentStep);
2021-01-17 03:27:39 +01:00
NewPiece->SetColorIndex(Piece->GetColorIndex());
2016-10-05 23:28:52 +02:00
2024-05-26 22:01:34 +02:00
NewPieces.emplace_back(NewPiece);
2016-10-05 23:28:52 +02:00
}
}
}
}
2024-05-26 22:01:34 +02:00
for (size_t PieceIdx = 0; PieceIdx < NewPieces.size(); PieceIdx++)
2016-10-05 23:28:52 +02:00
{
lcPiece* Piece = (lcPiece*)NewPieces[PieceIdx];
Piece->UpdatePosition(mCurrentStep);
AddPiece(Piece);
}
AddToSelection(NewPieces, false, true);
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, false);
SaveCheckpoint(tr("Array"));
}
void lcModel::ShowMinifigDialog()
{
2020-12-20 20:24:50 +01:00
lcMinifigDialog Dialog(gMainWindow);
2016-10-05 23:28:52 +02:00
if (Dialog.exec() != QDialog::Accepted)
return;
2016-12-28 22:30:31 +01:00
gMainWindow->GetActiveView()->MakeCurrent();
2016-10-05 23:28:52 +02:00
lcGroup* Group = AddGroup(tr("Minifig #"), nullptr);
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Pieces;
Pieces.reserve(LC_MFW_NUMITEMS);
lcMinifig& Minifig = Dialog.mMinifigWizard->mMinifig;
2016-10-05 23:28:52 +02:00
for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
{
if (Minifig.Parts[PartIdx] == nullptr)
2016-10-05 23:28:52 +02:00
continue;
lcPiece* Piece = new lcPiece(Minifig.Parts[PartIdx]);
Piece->Initialize(Minifig.Matrices[PartIdx], mCurrentStep);
Piece->SetColorIndex(Minifig.Colors[PartIdx]);
Piece->SetGroup(Group);
AddPiece(Piece);
Piece->UpdatePosition(mCurrentStep);
2024-05-26 22:01:34 +02:00
Pieces.emplace_back(Piece);
2016-10-05 23:28:52 +02:00
}
SetSelectionAndFocus(Pieces, nullptr, 0, false);
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(false, false);
SaveCheckpoint(tr("Minifig"));
}
void lcModel::SetMinifig(const lcMinifig& Minifig)
{
DeleteModel();
2024-05-26 22:01:34 +02:00
std::vector<lcObject*> Pieces;
Pieces.reserve(LC_MFW_NUMITEMS);
2020-12-24 03:45:34 +01:00
for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
{
if (!Minifig.Parts[PartIdx])
continue;
lcPiece* Piece = new lcPiece(Minifig.Parts[PartIdx]);
Piece->Initialize(Minifig.Matrices[PartIdx], 1);
Piece->SetColorIndex(Minifig.Colors[PartIdx]);
AddPiece(Piece);
Piece->UpdatePosition(1);
2020-12-24 03:45:34 +01:00
2024-05-26 22:01:34 +02:00
Pieces.emplace_back(Piece);
}
2020-12-24 03:45:34 +01:00
SetSelectionAndFocus(Pieces, nullptr, 0, false);
}
2021-01-09 00:11:02 +01:00
void lcModel::SetPreviewPieceInfo(PieceInfo* Info, int ColorIndex)
{
DeleteModel();
lcPiece* Piece = new lcPiece(Info);
Piece->Initialize(lcMatrix44Identity(), 1);
Piece->SetColorIndex(ColorIndex);
AddPiece(Piece);
Piece->UpdatePosition(1);
2021-01-10 01:37:32 +01:00
mCurrentStep = LC_STEP_MAX;
CalculateStep(LC_STEP_MAX);
2021-01-10 01:37:32 +01:00
SaveCheckpoint(QString());
2021-01-09 00:11:02 +01:00
}
2016-10-05 23:28:52 +02:00
void lcModel::UpdateInterface()
{
if (!gMainWindow)
return;
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateTimeline(true, false);
2019-06-24 03:28:14 +02:00
gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
gMainWindow->UpdatePaste(!gApplication->mClipboard.isEmpty());
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateCategories();
gMainWindow->UpdateTitle();
gMainWindow->SetTool(gMainWindow->GetTool());
gMainWindow->UpdateSelectedObjects(true);
gMainWindow->SetTransformType(gMainWindow->GetTransformType());
gMainWindow->UpdateLockSnap();
gMainWindow->UpdateSnap();
gMainWindow->UpdateModels();
2017-08-25 21:57:14 +02:00
gMainWindow->UpdateShadingMode();
2016-10-05 23:28:52 +02:00
gMainWindow->UpdateCurrentStep();
2017-11-22 02:58:36 +01:00
gMainWindow->UpdateSelectionMode();
2016-10-05 23:28:52 +02:00
}