1 #include "lc_global.h"
2 #include "lc_model.h"
3 #include <locale.h>
4 #include "piece.h"
5 #include "camera.h"
6 #include "light.h"
7 #include "group.h"
8 #include "lc_mainwindow.h"
9 #include "lc_profile.h"
10 #include "lc_library.h"
11 #include "lc_scene.h"
12 #include "lc_texture.h"
13 #include "lc_synth.h"
14 #include "lc_file.h"
15 #include "pieceinf.h"
16 #include "lc_view.h"
17 #include "minifig.h"
18 #include "lc_qarraydialog.h"
19 #include "lc_qselectdialog.h"
20 #include "lc_minifigdialog.h"
21 #include "lc_qgroupdialog.h"
22 #include "lc_qeditgroupsdialog.h"
23 #include "lc_qpropertiesdialog.h"
24 #include "lc_qutils.h"
25 #include "lc_lxf.h"
26 #include "lc_previewwidget.h"
27 #include "lc_findreplacewidget.h"
28 
LoadDefaults()29 void lcModelProperties::LoadDefaults()
30 {
31 	mAuthor = lcGetProfileString(LC_PROFILE_DEFAULT_AUTHOR_NAME);
32 	mAmbientColor = lcVector3FromColor(lcGetProfileInt(LC_PROFILE_DEFAULT_AMBIENT_COLOR));
33 }
34 
SaveDefaults()35 void lcModelProperties::SaveDefaults()
36 {
37 	lcSetProfileInt(LC_PROFILE_DEFAULT_AMBIENT_COLOR, lcColorFromVector3(mAmbientColor));
38 }
39 
SaveLDraw(QTextStream & Stream) const40 void lcModelProperties::SaveLDraw(QTextStream& Stream) const
41 {
42 	QLatin1String LineEnding("\r\n");
43 
44 	Stream << QLatin1String("0 ") << mDescription << LineEnding;
45 	Stream << QLatin1String("0 Name: ") << mModelName << LineEnding;
46 	Stream << QLatin1String("0 Author: ") << mAuthor << LineEnding;
47 
48 	if (!mComments.isEmpty())
49 	{
50 		QStringList Comments = mComments.split('\n');
51 		for (const QString& Comment : Comments)
52 			Stream << QLatin1String("0 !LEOCAD MODEL COMMENT ") << Comment << LineEnding;
53 	}
54 
55 //	lcVector3 mAmbientColor;
56 }
57 
ParseLDrawHeader(QString Line,bool FirstLine)58 bool lcModelProperties::ParseLDrawHeader(QString Line, bool FirstLine)
59 {
60 	QTextStream LineStream(&Line, QIODevice::ReadOnly);
61 
62 	QString Token;
63 	LineStream >> Token;
64 	int StartPos = LineStream.pos();
65 	LineStream >> Token;
66 
67 	if (Token == QLatin1String("!LEOCAD"))
68 		return false;
69 
70 	if (Token == QLatin1String("Name:"))
71 	{
72 		mModelName = LineStream.readLine().mid(1);
73 		return true;
74 	}
75 
76 	if (Token == QLatin1String("Author:"))
77 	{
78 		mAuthor = LineStream.readLine().mid(1);
79 		return true;
80 	}
81 
82 	if (FirstLine)
83 	{
84 		LineStream.seek(StartPos);
85 		mDescription = LineStream.readLine().mid(1);
86 		return true;
87 	}
88 
89 	return false;
90 }
91 
ParseLDrawLine(QTextStream & Stream)92 void lcModelProperties::ParseLDrawLine(QTextStream& Stream)
93 {
94 	QString Token;
95 	Stream >> Token;
96 
97 	if (Token == QLatin1String("AUTHOR"))
98 		mAuthor = Stream.readLine().mid(1);
99 	else if (Token == QLatin1String("DESCRIPTION"))
100 		mDescription = Stream.readLine().mid(1);
101 	else if (Token == QLatin1String("COMMENT"))
102 	{
103 		QString Comment = Stream.readLine().mid(1);
104 		if (!mComments.isEmpty())
105 			mComments += '\n';
106 		mComments += Comment;
107 	}
108 }
109 
lcModel(const QString & FileName,Project * Project,bool Preview)110 lcModel::lcModel(const QString& FileName, Project* Project, bool Preview)
111 	: mProject(Project), mIsPreview(Preview)
112 {
113 	mProperties.mModelName = FileName;
114 	mProperties.mFileName = FileName;
115 	mProperties.LoadDefaults();
116 
117 	mActive = false;
118 	mCurrentStep = 1;
119 	mPieceInfo = nullptr;
120 }
121 
~lcModel()122 lcModel::~lcModel()
123 {
124 	if (mPieceInfo)
125 	{
126 		if (!mIsPreview && gMainWindow && gMainWindow->GetCurrentPieceInfo() == mPieceInfo)
127 			gMainWindow->SetCurrentPieceInfo(nullptr);
128 
129 		if (mPieceInfo->GetModel() == this)
130 			mPieceInfo->SetPlaceholder();
131 
132 		lcPiecesLibrary* Library = lcGetPiecesLibrary();
133 		Library->ReleasePieceInfo(mPieceInfo);
134 	}
135 
136 	DeleteModel();
137 	DeleteHistory();
138 }
139 
GetPieceWorldMatrix(lcPiece * Piece,lcMatrix44 & ParentWorldMatrix) const140 bool lcModel::GetPieceWorldMatrix(lcPiece* Piece, lcMatrix44& ParentWorldMatrix) const
141 {
142 	for (lcPiece* ModelPiece : mPieces)
143 	{
144 		if (ModelPiece == Piece)
145 		{
146 			ParentWorldMatrix = lcMul(ModelPiece->mModelWorld, ParentWorldMatrix);
147 			return true;
148 		}
149 
150 		PieceInfo* Info = ModelPiece->mPieceInfo;
151 
152 		if (Info->IsModel())
153 		{
154 			lcMatrix44 WorldMatrix = lcMul(ModelPiece->mModelWorld, ParentWorldMatrix);
155 
156 			if (Info->GetPieceWorldMatrix(Piece, WorldMatrix))
157 			{
158 				ParentWorldMatrix = WorldMatrix;
159 				return true;
160 			}
161 		}
162 	}
163 
164 	return false;
165 }
166 
IncludesModel(const lcModel * Model) const167 bool lcModel::IncludesModel(const lcModel* Model) const
168 {
169 	if (Model == this)
170 		return true;
171 
172 	for (lcPiece* Piece : mPieces)
173 		if (Piece->mPieceInfo->IncludesModel(Model))
174 			return true;
175 
176 	return false;
177 }
178 
DeleteHistory()179 void lcModel::DeleteHistory()
180 {
181 	for (lcModelHistoryEntry* Entry : mUndoHistory)
182 		delete Entry;
183 	mUndoHistory.clear();
184 	for (lcModelHistoryEntry* Entry : mRedoHistory)
185 		delete Entry;
186 	mRedoHistory.clear();
187 }
188 
DeleteModel()189 void lcModel::DeleteModel()
190 {
191 	if (gMainWindow)
192 	{
193 		std::vector<lcView*> Views = lcView::GetModelViews(this);
194 
195 		// 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
196 		for (lcView* View : Views)
197 		{
198 			lcCamera* Camera = View->GetCamera();
199 
200 			if (!Camera->IsSimple() && mCameras.FindIndex(Camera) != -1)
201 				View->SetCamera(Camera, true);
202 		}
203 	}
204 
205 	mPieces.DeleteAll();
206 	mCameras.DeleteAll();
207 	mLights.DeleteAll();
208 	mGroups.DeleteAll();
209 	mFileLines.clear();
210 }
211 
CreatePieceInfo(Project * Project)212 void lcModel::CreatePieceInfo(Project* Project)
213 {
214 	lcPiecesLibrary* Library = lcGetPiecesLibrary();
215 	mPieceInfo = Library->FindPiece(mProperties.mFileName.toLatin1().constData(), Project, true, false);
216 	mPieceInfo->SetModel(this, true, Project, true);
217 	Library->LoadPieceInfo(mPieceInfo, true, true);
218 }
219 
UpdateMesh()220 void lcModel::UpdateMesh()
221 {
222 	mPieceInfo->SetModel(this, true, nullptr, false);
223 }
224 
UpdateAllViews() const225 void lcModel::UpdateAllViews() const
226 {
227 	lcView::UpdateProjectViews(mProject);
228 }
229 
UpdatePieceInfo(std::vector<lcModel * > & UpdatedModels)230 void lcModel::UpdatePieceInfo(std::vector<lcModel*>& UpdatedModels)
231 {
232 	if (std::find(UpdatedModels.begin(), UpdatedModels.end(), this) != UpdatedModels.end())
233 		return;
234 
235 	mPieceInfo->SetModel(this, false, nullptr, false);
236 	UpdatedModels.push_back(this);
237 
238 	lcMesh* Mesh = mPieceInfo->GetMesh();
239 
240 	if (mPieces.IsEmpty() && !Mesh)
241 	{
242 		mPieceInfo->SetBoundingBox(lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 0.0f));
243 		return;
244 	}
245 
246 	lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
247 
248 	for (lcPiece* Piece : mPieces)
249 	{
250 		if (Piece->IsVisibleInSubModel())
251 		{
252 			Piece->mPieceInfo->UpdateBoundingBox(UpdatedModels);
253 			Piece->CompareBoundingBox(Min, Max);
254 		}
255 	}
256 
257 	if (Mesh)
258 	{
259 		Min = lcMin(Min, Mesh->mBoundingBox.Min);
260 		Max = lcMax(Max, Mesh->mBoundingBox.Max);
261 	}
262 
263 	mPieceInfo->SetBoundingBox(Min, Max);
264 }
265 
SaveLDraw(QTextStream & Stream,bool SelectedOnly) const266 void lcModel::SaveLDraw(QTextStream& Stream, bool SelectedOnly) const
267 {
268 	QLatin1String LineEnding("\r\n");
269 
270 	mProperties.SaveLDraw(Stream);
271 
272 	lcArray<lcGroup*> CurrentGroups;
273 	lcStep Step = 1;
274 	int CurrentLine = 0;
275 	int AddedSteps = 0;
276 
277 	for (lcPiece* Piece : mPieces)
278 	{
279 		if (SelectedOnly && !Piece->IsSelected())
280 			continue;
281 
282 		while (Piece->GetFileLine() > CurrentLine && CurrentLine < mFileLines.size())
283 		{
284 			QString Line = mFileLines[CurrentLine];
285 			QTextStream LineStream(&Line, QIODevice::ReadOnly);
286 
287 			QString Token;
288 			LineStream >> Token;
289 			bool Skip = false;
290 
291 			if (Token == QLatin1String("0"))
292 			{
293 				LineStream >> Token;
294 
295 				if (Token == QLatin1String("STEP"))
296 				{
297 					if (Piece->GetStepShow() > Step)
298 						Step++;
299 					else
300 						Skip = true;
301 				}
302 			}
303 
304 			if (!Skip)
305 			{
306 				Stream << mFileLines[CurrentLine];
307 				if (AddedSteps > 0)
308 					AddedSteps--;
309 			}
310 			CurrentLine++;
311 		}
312 
313 		while (Piece->GetStepShow() > Step)
314 		{
315 			Stream << QLatin1String("0 STEP\r\n");
316 			AddedSteps++;
317 			Step++;
318 		}
319 
320 		lcGroup* PieceGroup = Piece->GetGroup();
321 
322 		if (PieceGroup)
323 		{
324 			if (CurrentGroups.IsEmpty() || (!CurrentGroups.IsEmpty() && PieceGroup != CurrentGroups[CurrentGroups.GetSize() - 1]))
325 			{
326 				lcArray<lcGroup*> PieceParents;
327 
328 				for (lcGroup* Group = PieceGroup; Group; Group = Group->mGroup)
329 					PieceParents.InsertAt(0, Group);
330 
331 				int FoundParent = -1;
332 
333 				while (!CurrentGroups.IsEmpty())
334 				{
335 					lcGroup* Group = CurrentGroups[CurrentGroups.GetSize() - 1];
336 					int Index = PieceParents.FindIndex(Group);
337 
338 					if (Index == -1)
339 					{
340 						CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
341 						Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
342 					}
343 					else
344 					{
345 						FoundParent = Index;
346 						break;
347 					}
348 				}
349 
350 				for (int ParentIdx = FoundParent + 1; ParentIdx < PieceParents.GetSize(); ParentIdx++)
351 				{
352 					lcGroup* Group = PieceParents[ParentIdx];
353 					CurrentGroups.Add(Group);
354 					Stream << QLatin1String("0 !LEOCAD GROUP BEGIN ") << Group->mName << LineEnding;
355 				}
356 			}
357 		}
358 		else
359 		{
360 			while (CurrentGroups.GetSize())
361 			{
362 				CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
363 				Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
364 			}
365 		}
366 
367 		if (Piece->mPieceInfo->GetSynthInfo())
368 		{
369 			Stream << QLatin1String("0 !LEOCAD SYNTH BEGIN\r\n");
370 
371 			const lcArray<lcPieceControlPoint>& ControlPoints = Piece->GetControlPoints();
372 			for (int ControlPointIdx = 0; ControlPointIdx < ControlPoints.GetSize(); ControlPointIdx++)
373 			{
374 				const lcPieceControlPoint& ControlPoint = ControlPoints[ControlPointIdx];
375 
376 				Stream << QLatin1String("0 !LEOCAD SYNTH CONTROL_POINT");
377 
378 				const float* FloatMatrix = ControlPoint.Transform;
379 				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 };
380 
381 				for (int NumberIdx = 0; NumberIdx < 13; NumberIdx++)
382 					Stream << ' ' << lcFormatValue(Numbers[NumberIdx], NumberIdx < 3 ? 4 : 6);
383 
384 				Stream << LineEnding;
385 			}
386 		}
387 
388 		Piece->SaveLDraw(Stream);
389 
390 		if (Piece->mPieceInfo->GetSynthInfo())
391 			Stream << QLatin1String("0 !LEOCAD SYNTH END\r\n");
392 	}
393 
394 	while (CurrentLine < mFileLines.size())
395 	{
396 		QString Line = mFileLines[CurrentLine];
397 		QTextStream LineStream(&Line, QIODevice::ReadOnly);
398 
399 		QString Token;
400 		LineStream >> Token;
401 		bool Skip = false;
402 
403 		if (Token == QLatin1String("0"))
404 		{
405 			LineStream >> Token;
406 
407 			if (Token == QLatin1String("STEP") && AddedSteps-- > 0)
408 				Skip = true;
409 		}
410 
411 		if (!Skip)
412 			Stream << mFileLines[CurrentLine];
413 		CurrentLine++;
414 	}
415 
416 	while (CurrentGroups.GetSize())
417 	{
418 		CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
419 		Stream << QLatin1String("0 !LEOCAD GROUP END\r\n");
420 	}
421 
422 	for (lcCamera* Camera : mCameras)
423 		if (!SelectedOnly || Camera->IsSelected())
424 			Camera->SaveLDraw(Stream);
425 
426 	for (lcLight* Light : mLights)
427 		if (!SelectedOnly || Light->IsSelected())
428 			Light->SaveLDraw(Stream);
429 
430 	Stream.flush();
431 }
432 
SplitMPD(QIODevice & Device)433 int lcModel::SplitMPD(QIODevice& Device)
434 {
435 	qint64 ModelPos = Device.pos();
436 
437 	while (!Device.atEnd())
438 	{
439 		qint64 Pos = Device.pos();
440 		QString OriginalLine = Device.readLine();
441 		QString Line = OriginalLine.trimmed();
442 		QTextStream LineStream(&Line, QIODevice::ReadOnly);
443 
444 		QString Token;
445 		LineStream >> Token;
446 
447 		if (Token == QLatin1String("0"))
448 		{
449 			LineStream >> Token;
450 
451 			if (Token == QLatin1String("FILE"))
452 			{
453 				if (!mProperties.mFileName.isEmpty())
454 				{
455 					Device.seek(Pos);
456 					break;
457 				}
458 
459 				SetFileName(LineStream.readAll().trimmed());
460 				ModelPos = Pos;
461 			}
462 			else if (Token == QLatin1String("NOFILE"))
463 			{
464 				break;
465 			}
466 		}
467 	}
468 
469 	return ModelPos;
470 }
471 
LoadLDraw(QIODevice & Device,Project * Project)472 void lcModel::LoadLDraw(QIODevice& Device, Project* Project)
473 {
474 	lcPiece* Piece = nullptr;
475 	lcCamera* Camera = nullptr;
476 	lcLight* Light = nullptr;
477 	lcArray<lcGroup*> CurrentGroups;
478 	lcArray<lcPieceControlPoint> ControlPoints;
479 	int CurrentStep = 1;
480 	lcPiecesLibrary* Library = lcGetPiecesLibrary();
481 
482 	mProperties.mAuthor.clear();
483 	mProperties.mDescription.clear();
484 	mProperties.mComments.clear();
485 	bool ReadingHeader = true;
486 	bool FirstLine = true;
487 
488 	while (!Device.atEnd())
489 	{
490 		qint64 Pos = Device.pos();
491 		QString OriginalLine = Device.readLine();
492 		QString Line = OriginalLine.trimmed();
493 		QTextStream LineStream(&Line, QIODevice::ReadOnly);
494 
495 		QString Token;
496 		LineStream >> Token;
497 
498 		if (Token == QLatin1String("0"))
499 		{
500 			LineStream >> Token;
501 
502 			if (Token == QLatin1String("FILE"))
503 			{
504 				QString Name = LineStream.readAll().trimmed();
505 
506 				if (mProperties.mFileName != Name)
507 				{
508 					Device.seek(Pos);
509 					break;
510 				}
511 
512 				continue;
513 			}
514 			else if (Token == QLatin1String("NOFILE"))
515 			{
516 				break;
517 			}
518 
519 			if (ReadingHeader)
520 			{
521 				ReadingHeader = mProperties.ParseLDrawHeader(Line, FirstLine);
522 				FirstLine = false;
523 
524 				if (ReadingHeader)
525 					continue;
526 			}
527 
528 			if (Token == QLatin1String("STEP"))
529 			{
530 				delete Piece;
531 				Piece = nullptr;
532 				CurrentStep++;
533 				mFileLines.append(OriginalLine);
534 				continue;
535 			}
536 
537 			if (Token != QLatin1String("!LEOCAD"))
538 			{
539 				mFileLines.append(OriginalLine);
540 				continue;
541 			}
542 
543 			LineStream >> Token;
544 
545 			if (Token == QLatin1String("MODEL"))
546 			{
547 				mProperties.ParseLDrawLine(LineStream);
548 			}
549 			else if (Token == QLatin1String("PIECE"))
550 			{
551 				if (!Piece)
552 					Piece = new lcPiece(nullptr);
553 
554 				Piece->ParseLDrawLine(LineStream);
555 			}
556 			else if (Token == QLatin1String("CAMERA"))
557 			{
558 				if (!Camera)
559 					Camera = new lcCamera(false);
560 
561 				if (Camera->ParseLDrawLine(LineStream))
562 				{
563 					Camera->CreateName(mCameras);
564 					mCameras.Add(Camera);
565 					Camera = nullptr;
566 				}
567 			}
568 			else if (Token == QLatin1String("LIGHT"))
569 			{
570 			}
571 			else if (Token == QLatin1String("GROUP"))
572 			{
573 				LineStream >> Token;
574 
575 				if (Token == QLatin1String("BEGIN"))
576 				{
577 					QString Name = LineStream.readAll().trimmed();
578 					lcGroup* Group = GetGroup(Name, true);
579 					if (!CurrentGroups.IsEmpty())
580 						Group->mGroup = CurrentGroups[CurrentGroups.GetSize() - 1];
581 					else
582 						Group->mGroup = nullptr;
583 					CurrentGroups.Add(Group);
584 				}
585 				else if (Token == QLatin1String("END"))
586 				{
587 					if (!CurrentGroups.IsEmpty())
588 						CurrentGroups.RemoveIndex(CurrentGroups.GetSize() - 1);
589 				}
590 			}
591 			else if (Token == QLatin1String("SYNTH"))
592 			{
593 				LineStream >> Token;
594 
595 				if (Token == QLatin1String("BEGIN"))
596 				{
597 					ControlPoints.RemoveAll();
598 				}
599 				else if (Token == QLatin1String("END"))
600 				{
601 					ControlPoints.RemoveAll();
602 				}
603 				else if (Token == QLatin1String("CONTROL_POINT"))
604 				{
605 					float Numbers[13];
606 					for (int TokenIdx = 0; TokenIdx < 13; TokenIdx++)
607 						LineStream >> Numbers[TokenIdx];
608 
609 					lcPieceControlPoint& PieceControlPoint = ControlPoints.Add();
610 					PieceControlPoint.Transform = lcMatrix44(lcVector4(Numbers[3], Numbers[9], -Numbers[6], 0.0f), lcVector4(Numbers[5], Numbers[11], -Numbers[8], 0.0f),
611 					                                         lcVector4(-Numbers[4], -Numbers[10], Numbers[7], 0.0f), lcVector4(Numbers[0], Numbers[2], -Numbers[1], 1.0f));
612 					PieceControlPoint.Scale = Numbers[12];
613 				}
614 			}
615 
616 			continue;
617 		}
618 		else if (Token == QLatin1String("1"))
619 		{
620 			ReadingHeader = false;
621 			int ColorCode;
622 			LineStream >> ColorCode;
623 
624 			float IncludeMatrix[12];
625 			for (int TokenIdx = 0; TokenIdx < 12; TokenIdx++)
626 				LineStream >> IncludeMatrix[TokenIdx];
627 
628 			lcMatrix44 IncludeTransform(lcVector4(IncludeMatrix[3], IncludeMatrix[6], IncludeMatrix[9], 0.0f), lcVector4(IncludeMatrix[4], IncludeMatrix[7], IncludeMatrix[10], 0.0f),
629 										lcVector4(IncludeMatrix[5], IncludeMatrix[8], IncludeMatrix[11], 0.0f), lcVector4(IncludeMatrix[0], IncludeMatrix[1], IncludeMatrix[2], 1.0f));
630 
631 			QString PartId = LineStream.readAll().trimmed();
632 
633 			if (PartId.isEmpty())
634 				continue;
635 
636 			QByteArray CleanId = PartId.toLatin1().toUpper().replace('\\', '/');
637 
638 			if (Library->IsPrimitive(CleanId.constData()))
639 			{
640 				mFileLines.append(OriginalLine);
641 			}
642 			else
643 			{
644 				if (!Piece)
645 					Piece = new lcPiece(nullptr);
646 
647 				if (!CurrentGroups.IsEmpty())
648 					Piece->SetGroup(CurrentGroups[CurrentGroups.GetSize() - 1]);
649 
650 				PieceInfo* Info = Library->FindPiece(PartId.toLatin1().constData(), Project, true, true);
651 
652 				float* Matrix = IncludeTransform;
653 				lcMatrix44 Transform(lcVector4(Matrix[0], Matrix[2], -Matrix[1], 0.0f), lcVector4(Matrix[8], Matrix[10], -Matrix[9], 0.0f),
654 									 lcVector4(-Matrix[4], -Matrix[6], Matrix[5], 0.0f), lcVector4(Matrix[12], Matrix[14], -Matrix[13], 1.0f));
655 
656 				Piece->SetFileLine(mFileLines.size());
657 				Piece->SetPieceInfo(Info, PartId, false);
658 				Piece->Initialize(Transform, CurrentStep);
659 				Piece->SetColorCode(ColorCode);
660 				Piece->VerifyControlPoints(ControlPoints);
661 				Piece->SetControlPoints(ControlPoints);
662 				ControlPoints.RemoveAll();
663 
664 				if (Piece->mPieceInfo->IsModel() && Piece->mPieceInfo->GetModel()->IncludesModel(this))
665 				{
666 					delete Piece;
667 					Piece = nullptr;
668 					continue;
669 				}
670 
671 				AddPiece(Piece);
672 				Piece = nullptr;
673 			}
674 		}
675 		else
676 		{
677 			ReadingHeader = false;
678 			mFileLines.append(OriginalLine);
679 		}
680 
681 		FirstLine = false;
682 	}
683 
684 	mCurrentStep = CurrentStep;
685 	CalculateStep(mCurrentStep);
686 	Library->WaitForLoadQueue();
687 	Library->mBuffersDirty = true;
688 	Library->UnloadUnusedParts();
689 
690 	delete Piece;
691 	delete Camera;
692 	delete Light;
693 }
694 
LoadBinary(lcFile * file)695 bool lcModel::LoadBinary(lcFile* file)
696 {
697 	qint32 i, count;
698 	char id[32];
699 	quint32 rgb;
700 	float fv = 0.4f;
701 	quint8 ch;
702 	quint16 sh;
703 
704 	file->Seek(0, SEEK_SET);
705 	file->ReadBuffer(id, 32);
706 	sscanf(&id[7], "%f", &fv);
707 
708 	if (memcmp(id, "LeoCAD ", 7))
709 		return false;
710 
711 	if (fv == 0.0f)
712 	{
713 		lconv *loc = localeconv();
714 		id[8] = loc->decimal_point[0];
715 		sscanf(&id[7], "%f", &fv);
716 
717 		if (fv == 0.0f)
718 			return false;
719 	}
720 
721 	if (fv > 0.4f)
722 		file->ReadFloats(&fv, 1);
723 
724 	file->ReadU32(&rgb, 1);
725 
726 	if (fv < 0.6f) // old view
727 	{
728 		double eye[3], target[3];
729 		file->ReadDoubles(eye, 3);
730 		file->ReadDoubles(target, 3);
731 	}
732 
733 	file->Seek(28, SEEK_CUR);
734 	file->ReadS32(&i, 1);
735 	mCurrentStep = i;
736 
737 	if (fv > 0.8f)
738 		file->ReadU32();//m_nScene
739 
740 	file->ReadS32(&count, 1);
741 	lcPiecesLibrary* Library = lcGetPiecesLibrary();
742 
743 	int FirstNewPiece = mPieces.GetSize();
744 
745 	while (count--)
746 	{
747 		if (fv > 0.4f)
748 		{
749 			lcPiece* pPiece = new lcPiece(nullptr);
750 			pPiece->FileLoad(*file);
751 			AddPiece(pPiece);
752 		}
753 		else
754 		{
755 			char name[LC_PIECE_NAME_LEN];
756 			lcVector3 pos, rot;
757 			quint8 color, step, group;
758 
759 			file->ReadFloats(pos, 3);
760 			file->ReadFloats(rot, 3);
761 			file->ReadU8(&color, 1);
762 			file->ReadBuffer(name, 9);
763 			strcat(name, ".dat");
764 			file->ReadU8(&step, 1);
765 			file->ReadU8(&group, 1);
766 
767 			pos *= 25.0f;
768 			lcMatrix44 WorldMatrix = lcMul(lcMatrix44RotationZ(rot[2] * LC_DTOR), lcMul(lcMatrix44RotationY(rot[1] * LC_DTOR), lcMatrix44RotationX(rot[0] * LC_DTOR)));
769 			WorldMatrix.SetTranslation(pos);
770 
771 			PieceInfo* pInfo = Library->FindPiece(name, nullptr, true, false);
772 			lcPiece* pPiece = new lcPiece(pInfo);
773 
774 			pPiece->Initialize(WorldMatrix, step);
775 			pPiece->SetColorCode(lcGetColorCodeFromOriginalColor(color));
776 			AddPiece(pPiece);
777 
778 //			pPiece->SetGroup((lcGroup*)group);
779 		}
780 	}
781 
782 	if (fv >= 0.4f)
783 	{
784 		file->ReadBuffer(&ch, 1);
785 		if (ch == 0xFF) file->ReadU16(&sh, 1); else sh = ch;
786 		if (sh > 100)
787 			file->Seek(sh, SEEK_CUR);
788 		else
789 		{
790 			QByteArray Author;
791 			Author.resize(sh + 1);
792 			file->ReadBuffer(Author.data(), sh);
793 			Author[sh] = 0;
794 			mProperties.mAuthor = QString::fromUtf8(Author);
795 		}
796 
797 		file->ReadBuffer(&ch, 1);
798 		if (ch == 0xFF) file->ReadU16(&sh, 1); else sh = ch;
799 		if (sh > 100)
800 			file->Seek(sh, SEEK_CUR);
801 		else
802 		{
803 			QByteArray Description;
804 			Description.resize(sh + 1);
805 			file->ReadBuffer(Description.data(), sh);
806 			Description[sh] = 0;
807 			mProperties.mDescription = QString::fromUtf8(Description);
808 		}
809 
810 		file->ReadBuffer(&ch, 1);
811 		if (ch == 0xFF && fv < 1.3f) file->ReadU16(&sh, 1); else sh = ch;
812 		if (sh > 255)
813 			file->Seek(sh, SEEK_CUR);
814 		else
815 		{
816 			QByteArray Comments;
817 			Comments.resize(sh + 1);
818 			file->ReadBuffer(Comments.data(), sh);
819 			Comments[sh] = 0;
820 			mProperties.mComments = QString::fromUtf8(Comments);
821 			mProperties.mComments.replace(QLatin1String("\r\n"), QLatin1String("\n"));
822 		}
823 	}
824 
825 	if (fv >= 0.5f)
826 	{
827 		int NumGroups = mGroups.GetSize();
828 
829 		file->ReadS32(&count, 1);
830 		for (i = 0; i < count; i++)
831 			mGroups.Add(new lcGroup());
832 
833 		for (int GroupIdx = NumGroups; GroupIdx < mGroups.GetSize(); GroupIdx++)
834 		{
835 			lcGroup* Group = mGroups[GroupIdx];
836 
837 			if (fv < 1.0f)
838 			{
839 				char Name[LC_MAX_GROUP_NAME + 1];
840 				file->ReadBuffer(Name, sizeof(Name));
841 				Group->mName = QString::fromUtf8(Name);
842 				file->ReadBuffer(&ch, 1);
843 				Group->mGroup = (lcGroup*)-1;
844 			}
845 			else
846 				Group->FileLoad(file);
847 		}
848 
849 		for (int GroupIdx = NumGroups; GroupIdx < mGroups.GetSize(); GroupIdx++)
850 		{
851 			lcGroup* Group = mGroups[GroupIdx];
852 
853 			i = (qint32)(quintptr)(Group->mGroup);
854 			Group->mGroup = nullptr;
855 
856 			if (i > 0xFFFF || i == -1)
857 				continue;
858 
859 			Group->mGroup = mGroups[NumGroups + i];
860 		}
861 
862 		for (int PieceIdx = FirstNewPiece; PieceIdx < mPieces.GetSize(); PieceIdx++)
863 		{
864 			lcPiece* Piece = mPieces[PieceIdx];
865 
866 			i = (qint32)(quintptr)(Piece->GetGroup());
867 			Piece->SetGroup(nullptr);
868 
869 			if (i > 0xFFFF || i == -1)
870 				continue;
871 
872 			Piece->SetGroup(mGroups[NumGroups + i]);
873 		}
874 
875 		RemoveEmptyGroups();
876 	}
877 
878 	if (fv >= 0.6f)
879 	{
880 		if (fv < 1.0f)
881 			file->Seek(4, SEEK_CUR);
882 		else
883 			file->Seek(2, SEEK_CUR);
884 
885 		file->ReadS32(&count, 1);
886 
887 		for (i = 0; i < count; i++)
888 			lcCamera::FileLoad(*file);
889 	}
890 
891 	if (fv >= 0.7f)
892 	{
893 		file->Seek(24, SEEK_CUR);
894 
895 		if (fv < 1.3f)
896 		{
897 			file->ReadU8(&ch, 1);
898 			if (ch == 0xFF)
899 				file->ReadU16(&sh, 1);
900 			sh = ch;
901 		}
902 		else
903 			file->ReadU16(&sh, 1);
904 
905 		file->Seek(sh, SEEK_CUR); // Background
906 	}
907 
908 	if (fv >= 0.8f)
909 	{
910 		file->ReadBuffer(&ch, 1);
911 		file->Seek(ch, SEEK_CUR);
912 		file->ReadBuffer(&ch, 1);
913 		file->Seek(ch, SEEK_CUR);
914 	}
915 
916 	if (fv > 0.9f)
917 	{
918 		file->ReadU32(&rgb, 1);
919 		mProperties.mAmbientColor[0] = (float)((unsigned char) (rgb))/255;
920 		mProperties.mAmbientColor[1] = (float)((unsigned char) (((unsigned short) (rgb)) >> 8))/255;
921 		mProperties.mAmbientColor[2] = (float)((unsigned char) ((rgb) >> 16))/255;
922 
923 		if (fv < 1.3f)
924 			file->Seek(23, SEEK_CUR);
925 		else
926 			file->Seek(11, SEEK_CUR);
927 	}
928 
929 	if (fv > 1.0f)
930 	{
931 		file->ReadU32(&rgb, 1);
932 		file->ReadU32(&rgb, 1);
933 	}
934 
935 	CalculateStep(mCurrentStep);
936 	lcGetPiecesLibrary()->UnloadUnusedParts();
937 
938 	return true;
939 }
940 
LoadLDD(const QString & FileData)941 bool lcModel::LoadLDD(const QString& FileData)
942 {
943 	std::vector<lcPiece*> Pieces;
944 	std::vector<std::vector<lcPiece*>> Groups;
945 
946 	if (!lcImportLXFMLFile(FileData, Pieces, Groups))
947 		return false;
948 
949 	for (lcPiece* Piece : Pieces)
950 		AddPiece(Piece);
951 
952 	for (const std::vector<lcPiece*>& Group : Groups)
953 	{
954 		lcGroup* NewGroup = AddGroup(tr("Group #"), nullptr);
955 		for (lcPiece* Piece : Group)
956 			Piece->SetGroup(NewGroup);
957 	}
958 
959 	lcPiecesLibrary* Library = lcGetPiecesLibrary();
960 	CalculateStep(mCurrentStep);
961 	Library->WaitForLoadQueue();
962 	Library->mBuffersDirty = true;
963 	Library->UnloadUnusedParts();
964 
965 	return true;
966 }
967 
LoadInventory(const QByteArray & Inventory)968 bool lcModel::LoadInventory(const QByteArray& Inventory)
969 {
970 	QJsonDocument Document = QJsonDocument::fromJson(Inventory);
971 	QJsonObject Root = Document.object();
972 	QJsonArray Parts = Root["results"].toArray();
973 	lcPiecesLibrary* Library = lcGetPiecesLibrary();
974 
975 	for (const QJsonValue& Part : Parts)
976 	{
977 		QJsonObject PartObject = Part.toObject();
978 		QByteArray PartID = PartObject["part"].toObject()["part_num"].toString().toLatin1();
979 		QJsonArray PartIDArray = PartObject["part"].toObject()["external_ids"].toObject()["LDraw"].toArray();
980 		if (!PartIDArray.isEmpty())
981 			PartID = PartIDArray.first().toString().toLatin1();
982 		int Quantity = PartObject["quantity"].toInt();
983 		int ColorCode = 16;
984 		QJsonArray ColorArray = PartObject["color"].toObject()["external_ids"].toObject()["LDraw"].toObject()["ext_ids"].toArray();
985 		if (!ColorArray.isEmpty())
986 			ColorCode = ColorArray.first().toInt();
987 
988 		PieceInfo* Info = Library->FindPiece(PartID + ".dat", nullptr, true, false);
989 
990 		while (Quantity--)
991 		{
992 			lcPiece* Piece = new lcPiece(nullptr);
993 			Piece->SetPieceInfo(Info, QString(), false);
994 			Piece->Initialize(lcMatrix44Identity(), 1);
995 			Piece->SetColorCode(ColorCode);
996 			AddPiece(Piece);
997 		}
998 	}
999 
1000 	if (mPieces.IsEmpty())
1001 		return false;
1002 
1003 	Library->WaitForLoadQueue();
1004 	Library->mBuffersDirty = true;
1005 	Library->UnloadUnusedParts();
1006 
1007 	auto RoundBounds = [](float& Value)
1008 	{
1009 		Value = ((Value < 0.0f) ? floor((Value - 5.0f) / 10.0f) : ceil((Value + 5.0f) / 10.0f)) * 10.0f;
1010 	};
1011 
1012 	const float TargetHeight = 800.0f;
1013 	float CurrentX = 0.0f;
1014 	float CurrentY = 0.0f;
1015 	float ColumnWidth = 0.0f;
1016 
1017 	for (lcPiece* Piece : mPieces)
1018 	{
1019 		lcBoundingBox BoundingBox = Piece->mPieceInfo->GetBoundingBox();
1020 		RoundBounds(BoundingBox.Min.x);
1021 		RoundBounds(BoundingBox.Min.y);
1022 		RoundBounds(BoundingBox.Max.x);
1023 		RoundBounds(BoundingBox.Max.y);
1024 
1025 		float PieceWidth = BoundingBox.Max.x - BoundingBox.Min.x;
1026 		float PieceHeight = BoundingBox.Max.y - BoundingBox.Min.y;
1027 
1028 		if (CurrentY + PieceHeight > TargetHeight)
1029 		{
1030 			CurrentY = 0.0f;
1031 			CurrentX += ColumnWidth;
1032 			ColumnWidth = 0.0f;
1033 		}
1034 
1035 		Piece->SetPosition(lcVector3(CurrentX + PieceWidth / 2.0f, CurrentY + PieceHeight / 2.0f, 0.0f), 1, false);
1036 		CurrentY += PieceHeight;
1037 		ColumnWidth = qMax(ColumnWidth, PieceWidth);
1038 	}
1039 
1040 	CalculateStep(mCurrentStep);
1041 
1042 	return true;
1043 }
1044 
Merge(lcModel * Other)1045 void lcModel::Merge(lcModel* Other)
1046 {
1047 	for (int PieceIdx = 0; PieceIdx < Other->mPieces.GetSize(); PieceIdx++)
1048 	{
1049 		lcPiece* Piece = Other->mPieces[PieceIdx];
1050 		Piece->SetFileLine(-1);
1051 		AddPiece(Piece);
1052 	}
1053 
1054 	Other->mPieces.RemoveAll();
1055 
1056 	for (int CameraIdx = 0; CameraIdx < Other->mCameras.GetSize(); CameraIdx++)
1057 	{
1058 		lcCamera* Camera = Other->mCameras[CameraIdx];
1059 		Camera->CreateName(mCameras);
1060 		mCameras.Add(Camera);
1061 	}
1062 
1063 	Other->mCameras.RemoveAll();
1064 
1065 	for (int LightIdx = 0; LightIdx < Other->mLights.GetSize(); LightIdx++)
1066 	{
1067 		lcLight* Light = Other->mLights[LightIdx];
1068 		Light->CreateName(mLights);
1069 		mLights.Add(Light);
1070 	}
1071 
1072 	Other->mLights.RemoveAll();
1073 
1074 	for (int GroupIdx = 0; GroupIdx < Other->mGroups.GetSize(); GroupIdx++)
1075 	{
1076 		lcGroup* Group = Other->mGroups[GroupIdx];
1077 		Group->CreateName(mGroups);
1078 		mGroups.Add(Group);
1079 	}
1080 
1081 	Other->mGroups.RemoveAll();
1082 
1083 	delete Other;
1084 
1085 	gMainWindow->UpdateTimeline(false, false);
1086 }
1087 
Cut()1088 void lcModel::Cut()
1089 {
1090 	Copy();
1091 
1092 	if (RemoveSelectedObjects())
1093 	{
1094 		gMainWindow->UpdateTimeline(false, false);
1095 		gMainWindow->UpdateSelectedObjects(true);
1096 		UpdateAllViews();
1097 		SaveCheckpoint(tr("Cutting"));
1098 	}
1099 }
1100 
Copy()1101 void lcModel::Copy()
1102 {
1103 	QByteArray File;
1104 	QTextStream Stream(&File, QIODevice::WriteOnly);
1105 
1106 	SaveLDraw(Stream, true);
1107 
1108 	gApplication->ExportClipboard(File);
1109 }
1110 
Paste(bool PasteToCurrentStep)1111 void lcModel::Paste(bool PasteToCurrentStep)
1112 {
1113 	if (gApplication->mClipboard.isEmpty())
1114 		return;
1115 
1116 	lcModel* Model = new lcModel(QString(), nullptr, false);
1117 
1118 	QBuffer Buffer(&gApplication->mClipboard);
1119 	Buffer.open(QIODevice::ReadOnly);
1120 	Model->LoadLDraw(Buffer, lcGetActiveProject());
1121 
1122 	const lcArray<lcPiece*>& PastedPieces = Model->mPieces;
1123 	lcArray<lcObject*> SelectedObjects;
1124 	SelectedObjects.AllocGrow(PastedPieces.GetSize());
1125 
1126 	for (lcPiece* Piece : PastedPieces)
1127 	{
1128 		Piece->SetFileLine(-1);
1129 
1130 		if (PasteToCurrentStep)
1131 		{
1132 			Piece->SetStepShow(mCurrentStep);
1133 			SelectedObjects.Add(Piece);
1134 		}
1135 		else
1136 		{
1137 			if (Piece->GetStepShow() <= mCurrentStep)
1138 				SelectedObjects.Add(Piece);
1139 		}
1140 	}
1141 
1142 	Merge(Model);
1143 	SaveCheckpoint(tr("Pasting"));
1144 
1145 	if (SelectedObjects.GetSize() == 1)
1146 		ClearSelectionAndSetFocus(SelectedObjects[0], LC_PIECE_SECTION_POSITION, false);
1147 	else
1148 		SetSelectionAndFocus(SelectedObjects, nullptr, 0, false);
1149 
1150 	CalculateStep(mCurrentStep);
1151 	gMainWindow->UpdateTimeline(false, false);
1152 	UpdateAllViews();
1153 }
1154 
DuplicateSelectedPieces()1155 void lcModel::DuplicateSelectedPieces()
1156 {
1157 	lcArray<lcObject*> NewPieces;
1158 	lcPiece* Focus = nullptr;
1159 	std::map<lcGroup*, lcGroup*> GroupMap;
1160 
1161 	std::function<lcGroup*(lcGroup*)> GetNewGroup = [this, &GroupMap, &GetNewGroup](lcGroup* Group)
1162 	{
1163 		const auto GroupIt = GroupMap.find(Group);
1164 		if (GroupIt != GroupMap.end())
1165 			return GroupIt->second;
1166 		else
1167 		{
1168 			lcGroup* Parent = Group->mGroup ? GetNewGroup(Group->mGroup) : nullptr;
1169 			QString GroupName = Group->mName;
1170 
1171 			while (!GroupName.isEmpty())
1172 			{
1173 				QCharRef Last = GroupName[GroupName.size() - 1];
1174 				if (Last.isDigit())
1175 					GroupName.chop(1);
1176 				else
1177 					break;
1178 			}
1179 
1180 			if (GroupName.isEmpty())
1181 				GroupName = Group->mName;
1182 
1183 			lcGroup* NewGroup = AddGroup(GroupName, Parent);
1184 			GroupMap[Group] = NewGroup;
1185 			return NewGroup;
1186 		}
1187 	};
1188 
1189 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
1190 	{
1191 		lcPiece* Piece = mPieces[PieceIdx];
1192 
1193 		if (!Piece->IsSelected())
1194 			continue;
1195 
1196 		lcPiece* NewPiece = new lcPiece(*Piece);
1197 		NewPiece->UpdatePosition(mCurrentStep);
1198 		NewPieces.Add(NewPiece);
1199 
1200 		if (Piece->IsFocused())
1201 			Focus = NewPiece;
1202 
1203 		PieceIdx++;
1204 		InsertPiece(NewPiece, PieceIdx);
1205 
1206 		lcGroup* Group = Piece->GetGroup();
1207 		if (Group)
1208 			Piece->SetGroup(GetNewGroup(Group));
1209 	}
1210 
1211 	if (NewPieces.IsEmpty())
1212 		return;
1213 
1214 	gMainWindow->UpdateTimeline(false, false);
1215 	SetSelectionAndFocus(NewPieces, Focus, LC_PIECE_SECTION_POSITION, false);
1216 	SaveCheckpoint(tr("Duplicating Pieces"));
1217 }
1218 
PaintSelectedPieces()1219 void lcModel::PaintSelectedPieces()
1220 {
1221 	SetSelectedPiecesColorIndex(gMainWindow->mColorIndex);
1222 }
1223 
GetScene(lcScene * Scene,lcCamera * ViewCamera,bool AllowHighlight,bool AllowFade) const1224 void lcModel::GetScene(lcScene* Scene, lcCamera* ViewCamera, bool AllowHighlight, bool AllowFade) const
1225 {
1226 	if (mPieceInfo)
1227 		mPieceInfo->AddRenderMesh(*Scene);
1228 
1229 	for (const lcPiece* Piece : mPieces)
1230 	{
1231 		if (Piece->IsVisible(mCurrentStep))
1232 		{
1233 			lcStep StepShow = Piece->GetStepShow();
1234 			Piece->AddMainModelRenderMeshes(Scene, AllowHighlight && StepShow == mCurrentStep, AllowFade && StepShow < mCurrentStep);
1235 		}
1236 	}
1237 
1238 	if (Scene->GetDrawInterface() && !Scene->GetActiveSubmodelInstance())
1239 	{
1240 		for (lcCamera* Camera : mCameras)
1241 			if (Camera != ViewCamera && Camera->IsVisible())
1242 				Scene->AddInterfaceObject(Camera);
1243 
1244 		for (lcLight* Light : mLights)
1245 			if (Light->IsVisible())
1246 				Scene->AddInterfaceObject(Light);
1247 	}
1248 }
1249 
AddSubModelRenderMeshes(lcScene * Scene,const lcMatrix44 & WorldMatrix,int DefaultColorIndex,lcRenderMeshState RenderMeshState,bool ParentActive) const1250 void lcModel::AddSubModelRenderMeshes(lcScene* Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const
1251 {
1252 	for (lcPiece* Piece : mPieces)
1253 		if (Piece->IsVisibleInSubModel())
1254 			Piece->AddSubModelRenderMeshes(Scene, WorldMatrix, DefaultColorIndex, RenderMeshState, ParentActive);
1255 }
1256 
GetStepImage(bool Zoom,int Width,int Height,lcStep Step)1257 QImage lcModel::GetStepImage(bool Zoom, int Width, int Height, lcStep Step)
1258 {
1259 	lcView* ActiveView = gMainWindow->GetActiveView();
1260 	const lcStep CurrentStep = mCurrentStep;
1261 	lcCamera* Camera = ActiveView->GetCamera();
1262 
1263 	lcView View(lcViewType::View, this);
1264 	View.SetCamera(Camera, true);
1265 	View.SetOffscreenContext();
1266 	View.MakeCurrent();
1267 
1268 	if (!View.BeginRenderToImage(Width, Height))
1269 	{
1270 		QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
1271 		return QImage();
1272 	}
1273 
1274 	SetTemporaryStep(Step);
1275 
1276 	if (Zoom)
1277 		ZoomExtents(Camera, (float)Width / (float)Height);
1278 
1279 	View.OnDraw();
1280 
1281 	QImage Image = View.GetRenderImage();
1282 
1283 	View.EndRenderToImage();
1284 
1285 	SetTemporaryStep(CurrentStep);
1286 
1287 	if (!mActive)
1288 		CalculateStep(LC_STEP_MAX);
1289 
1290 	return Image;
1291 }
1292 
GetPartsListImage(int MaxWidth,lcStep Step,quint32 BackgroundColor,QFont Font,QColor TextColor) const1293 QImage lcModel::GetPartsListImage(int MaxWidth, lcStep Step, quint32 BackgroundColor, QFont Font, QColor TextColor) const
1294 {
1295 	lcPartsList PartsList;
1296 
1297 	if (Step == 0)
1298 		GetPartsList(gDefaultColor, true, false, PartsList);
1299 	else
1300 		GetPartsListForStep(Step, gDefaultColor, PartsList);
1301 
1302 	if (PartsList.empty())
1303 		return QImage();
1304 
1305 	struct lcPartsListImage
1306 	{
1307 		QImage Thumbnail;
1308 		const PieceInfo* Info;
1309 		int ColorIndex;
1310 		int Count;
1311 		QRect Bounds;
1312 		QPoint Position;
1313 	};
1314 
1315 	std::vector<lcPartsListImage> Images;
1316 
1317 	for (const auto& PartIt : PartsList)
1318 	{
1319 		for (const auto& ColorIt : PartIt.second)
1320 		{
1321 			Images.push_back(lcPartsListImage());
1322 			lcPartsListImage& Image = Images.back();
1323 			Image.Info = PartIt.first;
1324 			Image.ColorIndex = ColorIt.first;
1325 			Image.Count = ColorIt.second;
1326 		}
1327 	}
1328 
1329 	auto ImageCompare = [](const lcPartsListImage& Image1, const lcPartsListImage& Image2)
1330 	{
1331 		if (Image1.ColorIndex != Image2.ColorIndex)
1332 			return Image1.ColorIndex < Image2.ColorIndex;
1333 
1334 		return strcmp(Image1.Info->m_strDescription, Image2.Info->m_strDescription) < 0;
1335 	};
1336 
1337 	std::sort(Images.begin(), Images.end(), ImageCompare);
1338 
1339 	lcView View(lcViewType::PartsList, nullptr);
1340 	View.SetOffscreenContext();
1341 	View.MakeCurrent();
1342 	lcContext* Context = View.mContext;
1343 	const int ThumbnailSize = qMin(MaxWidth, 512);
1344 
1345 	View.SetSize(ThumbnailSize, ThumbnailSize);
1346 
1347 	if (!View.BeginRenderToImage(ThumbnailSize, ThumbnailSize))
1348 	{
1349 		QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
1350 		return QImage();
1351 	}
1352 
1353 	float OrthoSize = 200.0f;
1354 
1355 	lcMatrix44 ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
1356 	lcMatrix44 ViewMatrix = lcMatrix44LookAt(lcVector3(-100.0f, -100.0f, 75.0f), lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 1.0f));
1357 	const int Viewport[4] = { 0, 0, ThumbnailSize, ThumbnailSize };
1358 
1359 	float ExtraPixels = 0.0f;
1360 
1361 	for (lcPartsListImage& Image : Images)
1362 	{
1363 		const PieceInfo* Info = Image.Info;
1364 		const lcBoundingBox& BoundingBox = Info->GetBoundingBox();
1365 
1366 		lcVector3 Points[8];
1367 		lcGetBoxCorners(BoundingBox.Min, BoundingBox.Max, Points);
1368 
1369 		for (lcVector3& Point : Points)
1370 		{
1371 			Point = lcProjectPoint(Point, ViewMatrix, ProjectionMatrix, Viewport);
1372 
1373 			ExtraPixels = qMax(ExtraPixels, -Point.x);
1374 			ExtraPixels = qMax(ExtraPixels, Point.x - ThumbnailSize);
1375 			ExtraPixels = qMax(ExtraPixels, -Point.y);
1376 			ExtraPixels = qMax(ExtraPixels, Point.y - ThumbnailSize);
1377 		}
1378 	}
1379 
1380 	if (ExtraPixels)
1381 	{
1382 		OrthoSize += ExtraPixels * (2.0f * OrthoSize / ThumbnailSize);
1383 		ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
1384 	}
1385 
1386 	Context->SetViewport(0, 0, ThumbnailSize, ThumbnailSize);
1387 	Context->SetDefaultState();
1388 	Context->SetProjectionMatrix(ProjectionMatrix);
1389 
1390 	for (lcPartsListImage& Image : Images)
1391 	{
1392 		View.BindRenderFramebuffer();
1393 		Context->ClearColorAndDepth(lcVector4(lcVector3FromColor(BackgroundColor), 0.0f));
1394 
1395 		lcScene Scene;
1396 
1397 		const lcPreferences& Preferences = lcGetPreferences();
1398 		lcShadingMode ShadingMode = Preferences.mShadingMode;
1399 		if (ShadingMode == lcShadingMode::Wireframe)
1400 			ShadingMode = lcShadingMode::Flat;
1401 
1402 		Scene.SetShadingMode(ShadingMode);
1403 		Scene.SetAllowLOD(false);
1404 		Scene.Begin(ViewMatrix);
1405 
1406 		Image.Info->AddRenderMeshes(&Scene, lcMatrix44Identity(), Image.ColorIndex, lcRenderMeshState::Default, true);
1407 
1408 		Scene.End();
1409 
1410 		Scene.Draw(Context);
1411 
1412 		View.UnbindRenderFramebuffer();
1413 		Image.Thumbnail = View.GetRenderFramebufferImage().convertToFormat(QImage::Format_ARGB32);
1414 	}
1415 
1416 	View.EndRenderToImage();
1417 	Context->ClearResources();
1418 
1419 	auto CalculateImageBounds = [](lcPartsListImage& Image)
1420 	{
1421 		QImage& Thumbnail = Image.Thumbnail;
1422 		int Width = Thumbnail.width();
1423 		int Height = Thumbnail.height();
1424 
1425 		int MinX = Width;
1426 		int MinY = Height;
1427 		int MaxX = 0;
1428 		int MaxY = 0;
1429 
1430 		for (int x = 0; x < Width; x++)
1431 		{
1432 			for (int y = 0; y < Height; y++)
1433 			{
1434 				if (qAlpha(Thumbnail.pixel(x, y)))
1435 				{
1436 					MinX = qMin(x, MinX);
1437 					MinY = qMin(y, MinY);
1438 					MaxX = qMax(x, MaxX);
1439 					MaxY = qMax(y, MaxY);
1440 				}
1441 			}
1442 		}
1443 
1444 		Image.Bounds = QRect(QPoint(MinX, MinY), QPoint(MaxX, MaxY));
1445 	};
1446 
1447 	QtConcurrent::blockingMap(Images, CalculateImageBounds);
1448 
1449 	QImage DummyImage(16, 16, QImage::Format_ARGB32);
1450 	QPainter DummyPainter(&DummyImage);
1451 
1452 	DummyPainter.setFont(Font);
1453 	QFontMetrics FontMetrics = DummyPainter.fontMetrics();
1454 	int Ascent = FontMetrics.ascent();
1455 
1456 	int CurrentHeight = 0;
1457 	int ImageWidth = MaxWidth;
1458 
1459 	for (lcPartsListImage& Image : Images)
1460 		CurrentHeight = qMax(Image.Bounds.height() + Ascent, CurrentHeight);
1461 
1462 	for (;;)
1463 	{
1464 		int CurrentWidth = 0;
1465 		int CurrentX = 0;
1466 		int CurrentY = 0;
1467 		int ColumnWidth = 0;
1468 		int Spacing = 20;
1469 		int NextHeightIncrease = INT_MAX;
1470 
1471 		for (lcPartsListImage& Image : Images)
1472 		{
1473 			if (CurrentY + Image.Bounds.height() + Ascent > CurrentHeight)
1474 			{
1475 				int NeededSpace = Image.Bounds.height() + Ascent - (CurrentHeight - CurrentY);
1476 				NextHeightIncrease = qMin(NeededSpace, NextHeightIncrease);
1477 
1478 				CurrentY = 0;
1479 				CurrentX += ColumnWidth + Spacing;
1480 				ColumnWidth = 0;
1481 			}
1482 
1483 			Image.Position = QPoint(CurrentX, CurrentY);
1484 			CurrentY += Image.Bounds.height() + Ascent + Spacing;
1485 			CurrentWidth = qMax(CurrentWidth, CurrentX + Image.Bounds.width());
1486 			ColumnWidth = qMax(ColumnWidth, Image.Bounds.width());
1487 		}
1488 
1489 		if (CurrentWidth <= MaxWidth)
1490 		{
1491 			ImageWidth = CurrentWidth;
1492 			break;
1493 		}
1494 
1495 		CurrentHeight += NextHeightIncrease;
1496 	}
1497 
1498 	QImage PainterImage(ImageWidth + 40, CurrentHeight + 40, QImage::Format_ARGB32);
1499 	PainterImage.fill(lcQColorFromRGBA(BackgroundColor));
1500 
1501 	QPainter Painter(&PainterImage);
1502 	Painter.setFont(Font);
1503 	Painter.setPen(TextColor);
1504 
1505 	for (lcPartsListImage& Image : Images)
1506 	{
1507 		QPoint Position = Image.Position + QPoint(20, 20);
1508 		Painter.drawImage(Position, Image.Thumbnail, Image.Bounds);
1509 		Painter.drawText(QPoint(Position.x(), Position.y() + Image.Bounds.height() + Ascent), QString::number(Image.Count) + 'x');
1510 	}
1511 
1512 	Painter.end();
1513 
1514 	return PainterImage;
1515 }
1516 
SaveStepImages(const QString & BaseName,bool AddStepSuffix,bool Zoom,int Width,int Height,lcStep Start,lcStep End)1517 void lcModel::SaveStepImages(const QString& BaseName, bool AddStepSuffix, bool Zoom, int Width, int Height, lcStep Start, lcStep End)
1518 {
1519 	for (lcStep Step = Start; Step <= End; Step++)
1520 	{
1521 		QString FileName;
1522 
1523 		if (AddStepSuffix)
1524 			FileName = BaseName.arg(Step, 2, 10, QLatin1Char('0'));
1525 		else
1526 			FileName = BaseName;
1527 
1528 		QImageWriter Writer(FileName);
1529 
1530 		if (Writer.format().isEmpty())
1531 			Writer.setFormat("png");
1532 
1533 		QImage Image = GetStepImage(Zoom, Width, Height, Step);
1534 		if (!Writer.write(Image))
1535 		{
1536 			QMessageBox::information(gMainWindow, tr("Error"), tr("Error writing to file '%1':\n%2").arg(FileName, Writer.errorString()));
1537 			break;
1538 		}
1539 	}
1540 }
1541 
RayTest(lcObjectRayTest & ObjectRayTest) const1542 void lcModel::RayTest(lcObjectRayTest& ObjectRayTest) const
1543 {
1544 	for (lcPiece* Piece : mPieces)
1545 		if (Piece->IsVisible(mCurrentStep) && (!ObjectRayTest.IgnoreSelected || !Piece->IsSelected()))
1546 			Piece->RayTest(ObjectRayTest);
1547 
1548 	if (ObjectRayTest.PiecesOnly)
1549 		return;
1550 
1551 	for (lcCamera* Camera : mCameras)
1552 		if (Camera != ObjectRayTest.ViewCamera && Camera->IsVisible() && (!ObjectRayTest.IgnoreSelected || !Camera->IsSelected()))
1553 			Camera->RayTest(ObjectRayTest);
1554 
1555 	for (lcLight* Light : mLights)
1556 		if (Light->IsVisible() && (!ObjectRayTest.IgnoreSelected || !Light->IsSelected()))
1557 			Light->RayTest(ObjectRayTest);
1558 }
1559 
BoxTest(lcObjectBoxTest & ObjectBoxTest) const1560 void lcModel::BoxTest(lcObjectBoxTest& ObjectBoxTest) const
1561 {
1562 	for (lcPiece* Piece : mPieces)
1563 		if (Piece->IsVisible(mCurrentStep))
1564 			Piece->BoxTest(ObjectBoxTest);
1565 
1566 	for (lcCamera* Camera : mCameras)
1567 		if (Camera != ObjectBoxTest.ViewCamera && Camera->IsVisible())
1568 			Camera->BoxTest(ObjectBoxTest);
1569 
1570 	for (lcLight* Light : mLights)
1571 		if (Light->IsVisible())
1572 			Light->BoxTest(ObjectBoxTest);
1573 }
1574 
SubModelMinIntersectDist(const lcVector3 & WorldStart,const lcVector3 & WorldEnd,float & MinDistance) const1575 bool lcModel::SubModelMinIntersectDist(const lcVector3& WorldStart, const lcVector3& WorldEnd, float& MinDistance) const
1576 {
1577 	bool MinIntersect = false;
1578 
1579 	for (lcPiece* Piece : mPieces)
1580 	{
1581 		lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(Piece->mModelWorld);
1582 		lcVector3 Start = lcMul31(WorldStart, InverseWorldMatrix);
1583 		lcVector3 End = lcMul31(WorldEnd, InverseWorldMatrix);
1584 
1585 		if (Piece->IsVisibleInSubModel() && Piece->mPieceInfo->MinIntersectDist(Start, End, MinDistance)) // todo: this should check for piece->mMesh first
1586 			MinIntersect = true;
1587 	}
1588 
1589 	return MinIntersect;
1590 }
1591 
SubModelBoxTest(const lcVector4 Planes[6]) const1592 bool lcModel::SubModelBoxTest(const lcVector4 Planes[6]) const
1593 {
1594 	for (lcPiece* Piece : mPieces)
1595 		if (Piece->IsVisibleInSubModel() && Piece->mPieceInfo->BoxTest(Piece->mModelWorld, Planes))
1596 			return true;
1597 
1598 	return false;
1599 }
1600 
SubModelCompareBoundingBox(const lcMatrix44 & WorldMatrix,lcVector3 & Min,lcVector3 & Max) const1601 void lcModel::SubModelCompareBoundingBox(const lcMatrix44& WorldMatrix, lcVector3& Min, lcVector3& Max) const
1602 {
1603 	for (lcPiece* Piece : mPieces)
1604 		if (Piece->IsVisibleInSubModel())
1605 			Piece->SubModelCompareBoundingBox(WorldMatrix, Min, Max);
1606 }
1607 
SubModelAddBoundingBoxPoints(const lcMatrix44 & WorldMatrix,std::vector<lcVector3> & Points) const1608 void lcModel::SubModelAddBoundingBoxPoints(const lcMatrix44& WorldMatrix, std::vector<lcVector3>& Points) const
1609 {
1610 	for (lcPiece* Piece : mPieces)
1611 		if (Piece->IsVisibleInSubModel())
1612 			Piece->SubModelAddBoundingBoxPoints(WorldMatrix, Points);
1613 }
1614 
SaveCheckpoint(const QString & Description)1615 void lcModel::SaveCheckpoint(const QString& Description)
1616 {
1617 	lcModelHistoryEntry* ModelHistoryEntry = new lcModelHistoryEntry();
1618 
1619 	ModelHistoryEntry->Description = Description;
1620 
1621 	QTextStream Stream(&ModelHistoryEntry->File);
1622 	SaveLDraw(Stream, false);
1623 
1624 	mUndoHistory.insert(mUndoHistory.begin(), ModelHistoryEntry);
1625 	for (lcModelHistoryEntry* Entry : mRedoHistory)
1626 		delete Entry;
1627 	mRedoHistory.clear();
1628 
1629 	if (!Description.isEmpty())
1630 	{
1631 		gMainWindow->UpdateModified(IsModified());
1632 		gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : QString(), !mRedoHistory.empty() ? mRedoHistory[0]->Description : QString());
1633 	}
1634 }
1635 
LoadCheckPoint(lcModelHistoryEntry * CheckPoint)1636 void lcModel::LoadCheckPoint(lcModelHistoryEntry* CheckPoint)
1637 {
1638 	lcPiecesLibrary* Library = lcGetPiecesLibrary();
1639 	std::vector<PieceInfo*> LoadedInfos;
1640 
1641 	for (lcPiece* Piece : mPieces)
1642 	{
1643 		PieceInfo* Info = Piece->mPieceInfo;
1644 		Library->LoadPieceInfo(Info, true, true);
1645 		LoadedInfos.push_back(Info);
1646 	}
1647 
1648 	DeleteModel();
1649 
1650 	QBuffer Buffer(&CheckPoint->File);
1651 	Buffer.open(QIODevice::ReadOnly);
1652 	LoadLDraw(Buffer, lcGetActiveProject());
1653 
1654 	gMainWindow->UpdateTimeline(true, false);
1655 	gMainWindow->UpdateCameraMenu();
1656 	gMainWindow->UpdateCurrentStep();
1657 	gMainWindow->UpdateSelectedObjects(true);
1658 	UpdateAllViews();
1659 
1660 	for (PieceInfo* Info : LoadedInfos)
1661 		Library->ReleasePieceInfo(Info);
1662 }
1663 
SetActive(bool Active)1664 void lcModel::SetActive(bool Active)
1665 {
1666 	CalculateStep(Active ? mCurrentStep : LC_STEP_MAX);
1667 	mActive = Active;
1668 }
1669 
CalculateStep(lcStep Step)1670 void lcModel::CalculateStep(lcStep Step)
1671 {
1672 	for (lcPiece* Piece : mPieces)
1673 	{
1674 		Piece->UpdatePosition(Step);
1675 
1676 		if (Piece->IsSelected())
1677 		{
1678 			if (!Piece->IsVisible(Step))
1679 				Piece->SetSelected(false);
1680 			else
1681 				SelectGroup(Piece->GetTopGroup(), true);
1682 		}
1683 	}
1684 
1685 	for (lcCamera* Camera : mCameras)
1686 		Camera->UpdatePosition(Step);
1687 
1688 	for (lcLight* Light : mLights)
1689 		Light->UpdatePosition(Step);
1690 }
1691 
SetCurrentStep(lcStep Step)1692 void lcModel::SetCurrentStep(lcStep Step)
1693 {
1694 	mCurrentStep = Step;
1695 	CalculateStep(Step);
1696 
1697 	gMainWindow->UpdateTimeline(false, false);
1698 	gMainWindow->UpdateSelectedObjects(true);
1699 	UpdateAllViews();
1700 	gMainWindow->UpdateCurrentStep();
1701 }
1702 
ShowFirstStep()1703 void lcModel::ShowFirstStep()
1704 {
1705 	if (mCurrentStep == 1)
1706 		return;
1707 
1708 	SetCurrentStep(1);
1709 }
1710 
ShowLastStep()1711 void lcModel::ShowLastStep()
1712 {
1713 	lcStep LastStep = GetLastStep();
1714 
1715 	if (mCurrentStep == LastStep)
1716 		return;
1717 
1718 	SetCurrentStep(LastStep);
1719 }
1720 
ShowPreviousStep()1721 void lcModel::ShowPreviousStep()
1722 {
1723 	if (mCurrentStep == 1)
1724 		return;
1725 
1726 	SetCurrentStep(mCurrentStep - 1);
1727 }
1728 
ShowNextStep()1729 void lcModel::ShowNextStep()
1730 {
1731 	if (mCurrentStep == LC_STEP_MAX)
1732 		return;
1733 
1734 	SetCurrentStep(mCurrentStep + 1);
1735 }
1736 
GetLastStep() const1737 lcStep lcModel::GetLastStep() const
1738 {
1739 	lcStep Step = 1;
1740 
1741 	for (lcPiece* Piece : mPieces)
1742 		Step = lcMax(Step, Piece->GetStepShow());
1743 
1744 	return Step;
1745 }
1746 
InsertStep(lcStep Step)1747 void lcModel::InsertStep(lcStep Step)
1748 {
1749 	for (lcPiece* Piece : mPieces)
1750 	{
1751 		Piece->InsertTime(Step, 1);
1752 		if (Piece->IsSelected() && !Piece->IsVisible(mCurrentStep))
1753 			Piece->SetSelected(false);
1754 	}
1755 
1756 	for (lcCamera* Camera : mCameras)
1757 		Camera->InsertTime(Step, 1);
1758 
1759 	for (lcLight* Light : mLights)
1760 		Light->InsertTime(Step, 1);
1761 
1762 	SaveCheckpoint(tr("Inserting Step"));
1763 	SetCurrentStep(mCurrentStep);
1764 }
1765 
RemoveStep(lcStep Step)1766 void lcModel::RemoveStep(lcStep Step)
1767 {
1768 	for (lcPiece* Piece : mPieces)
1769 	{
1770 		Piece->RemoveTime(Step, 1);
1771 		if (Piece->IsSelected() && !Piece->IsVisible(mCurrentStep))
1772 			Piece->SetSelected(false);
1773 	}
1774 
1775 	for (lcCamera* Camera : mCameras)
1776 		Camera->RemoveTime(Step, 1);
1777 
1778 	for (lcLight* Light : mLights)
1779 		Light->RemoveTime(Step, 1);
1780 
1781 	SaveCheckpoint(tr("Removing Step"));
1782 	SetCurrentStep(mCurrentStep);
1783 }
1784 
AddGroup(const QString & Prefix,lcGroup * Parent)1785 lcGroup* lcModel::AddGroup(const QString& Prefix, lcGroup* Parent)
1786 {
1787 	lcGroup* Group = new lcGroup();
1788 	mGroups.Add(Group);
1789 
1790 	Group->mName = GetGroupName(Prefix);
1791 	Group->mGroup = Parent;
1792 
1793 	return Group;
1794 }
1795 
GetGroup(const QString & Name,bool CreateIfMissing)1796 lcGroup* lcModel::GetGroup(const QString& Name, bool CreateIfMissing)
1797 {
1798 	for (lcGroup* Group : mGroups)
1799 		if (Group->mName == Name)
1800 			return Group;
1801 
1802 	if (CreateIfMissing)
1803 	{
1804 		lcGroup* Group = new lcGroup();
1805 		Group->mName = Name;
1806 		mGroups.Add(Group);
1807 
1808 		return Group;
1809 	}
1810 
1811 	return nullptr;
1812 }
1813 
RemoveGroup(lcGroup * Group)1814 void lcModel::RemoveGroup(lcGroup* Group)
1815 {
1816 	mGroups.Remove(Group);
1817 	delete Group;
1818 }
1819 
GroupSelection()1820 void lcModel::GroupSelection()
1821 {
1822 	if (!AnyPiecesSelected())
1823 	{
1824 		QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No pieces selected."));
1825 		return;
1826 	}
1827 
1828 	lcQGroupDialog Dialog(gMainWindow, GetGroupName(tr("Group #")));
1829 	if (Dialog.exec() != QDialog::Accepted)
1830 		return;
1831 
1832 	lcGroup* NewGroup = GetGroup(Dialog.mName, true);
1833 
1834 	for (lcPiece* Piece : mPieces)
1835 	{
1836 		if (Piece->IsSelected())
1837 		{
1838 			lcGroup* Group = Piece->GetTopGroup();
1839 
1840 			if (!Group)
1841 				Piece->SetGroup(NewGroup);
1842 			else if (Group != NewGroup)
1843 				Group->mGroup = NewGroup;
1844 		}
1845 	}
1846 
1847 	SaveCheckpoint(tr("Grouping"));
1848 }
1849 
UngroupSelection()1850 void lcModel::UngroupSelection()
1851 {
1852 	lcArray<lcGroup*> SelectedGroups;
1853 
1854 	for (lcPiece* Piece : mPieces)
1855 	{
1856 		if (Piece->IsSelected())
1857 		{
1858 			lcGroup* Group = Piece->GetTopGroup();
1859 
1860 			if (SelectedGroups.FindIndex(Group) == -1)
1861 			{
1862 				mGroups.Remove(Group);
1863 				SelectedGroups.Add(Group);
1864 			}
1865 		}
1866 	}
1867 
1868 	for (lcPiece* Piece : mPieces)
1869 	{
1870 		lcGroup* Group = Piece->GetGroup();
1871 
1872 		if (SelectedGroups.FindIndex(Group) != -1)
1873 			Piece->SetGroup(nullptr);
1874 	}
1875 
1876 	for (lcGroup* Group : mGroups)
1877 		if (SelectedGroups.FindIndex(Group->mGroup) != -1)
1878 			Group->mGroup = nullptr;
1879 
1880 	SelectedGroups.DeleteAll();
1881 
1882 	RemoveEmptyGroups();
1883 	SaveCheckpoint(tr("Ungrouping"));
1884 }
1885 
AddSelectedPiecesToGroup()1886 void lcModel::AddSelectedPiecesToGroup()
1887 {
1888 	lcGroup* Group = nullptr;
1889 
1890 	for (lcPiece* Piece : mPieces)
1891 	{
1892 		if (Piece->IsSelected())
1893 		{
1894 			Group = Piece->GetTopGroup();
1895 			if (Group)
1896 				break;
1897 		}
1898 	}
1899 
1900 	if (Group)
1901 	{
1902 		for (lcPiece* Piece : mPieces)
1903 		{
1904 			if (Piece->IsFocused())
1905 			{
1906 				Piece->SetGroup(Group);
1907 				break;
1908 			}
1909 		}
1910 	}
1911 
1912 	RemoveEmptyGroups();
1913 	SaveCheckpoint(tr("Grouping"));
1914 }
1915 
RemoveFocusPieceFromGroup()1916 void lcModel::RemoveFocusPieceFromGroup()
1917 {
1918 	for (lcPiece* Piece : mPieces)
1919 	{
1920 		if (Piece->IsFocused())
1921 		{
1922 			Piece->SetGroup(nullptr);
1923 			break;
1924 		}
1925 	}
1926 
1927 	RemoveEmptyGroups();
1928 	SaveCheckpoint(tr("Ungrouping"));
1929 }
1930 
ShowEditGroupsDialog()1931 void lcModel::ShowEditGroupsDialog()
1932 {
1933 	QMap<lcPiece*, lcGroup*> PieceParents;
1934 	QMap<lcGroup*, lcGroup*> GroupParents;
1935 
1936 	for (lcPiece* Piece : mPieces)
1937 		PieceParents[Piece] = Piece->GetGroup();
1938 
1939 	for (lcGroup* Group : mGroups)
1940 		GroupParents[Group] = Group->mGroup;
1941 
1942 	lcQEditGroupsDialog Dialog(gMainWindow, PieceParents, GroupParents, this);
1943 
1944 	if (Dialog.exec() != QDialog::Accepted)
1945 		return;
1946 
1947 	bool Modified = Dialog.mNewGroups.isEmpty();
1948 
1949 	for (lcPiece* Piece : mPieces)
1950 	{
1951 		lcGroup* ParentGroup = Dialog.mPieceParents.value(Piece);
1952 
1953 		if (ParentGroup != Piece->GetGroup())
1954 		{
1955 			Piece->SetGroup(ParentGroup);
1956 			Modified = true;
1957 		}
1958 	}
1959 
1960 	for (lcGroup* Group : mGroups)
1961 	{
1962 		lcGroup* ParentGroup = Dialog.mGroupParents.value(Group);
1963 
1964 		if (ParentGroup != Group->mGroup)
1965 		{
1966 			Group->mGroup = ParentGroup;
1967 			Modified = true;
1968 		}
1969 	}
1970 
1971 	if (Modified)
1972 	{
1973 		ClearSelection(true);
1974 		SaveCheckpoint(tr("Editing Groups"));
1975 	}
1976 }
1977 
GetGroupName(const QString & Prefix)1978 QString lcModel::GetGroupName(const QString& Prefix)
1979 {
1980 	int Length = Prefix.length();
1981 	int Max = 0;
1982 
1983 	for (lcGroup* Group : mGroups)
1984 	{
1985 		const QString& Name = Group->mName;
1986 
1987 		if (Name.startsWith(Prefix))
1988 		{
1989 			bool Ok = false;
1990 			int GroupNumber = Name.mid(Length).toInt(&Ok);
1991 			if (Ok && GroupNumber > Max)
1992 				Max = GroupNumber;
1993 		}
1994 	}
1995 
1996 	return Prefix + QString::number(Max + 1);
1997 }
1998 
RemoveEmptyGroups()1999 void lcModel::RemoveEmptyGroups()
2000 {
2001 	bool Removed;
2002 
2003 	do
2004 	{
2005 		Removed = false;
2006 
2007 		for (int GroupIdx = 0; GroupIdx < mGroups.GetSize();)
2008 		{
2009 			lcGroup* Group = mGroups[GroupIdx];
2010 			int Ref = 0;
2011 
2012 			for (lcPiece* Piece : mPieces)
2013 				if (Piece->GetGroup() == Group)
2014 					Ref++;
2015 
2016 			for (int ParentIdx = 0; ParentIdx < mGroups.GetSize(); ParentIdx++)
2017 				if (mGroups[ParentIdx]->mGroup == Group)
2018 					Ref++;
2019 
2020 			if (Ref > 1)
2021 			{
2022 				GroupIdx++;
2023 				continue;
2024 			}
2025 
2026 			if (Ref != 0)
2027 			{
2028 				for (lcPiece* Piece : mPieces)
2029 				{
2030 					if (Piece->GetGroup() == Group)
2031 					{
2032 						Piece->SetGroup(Group->mGroup);
2033 						break;
2034 					}
2035 				}
2036 
2037 				for (int ParentIdx = 0; ParentIdx < mGroups.GetSize(); ParentIdx++)
2038 				{
2039 					if (mGroups[ParentIdx]->mGroup == Group)
2040 					{
2041 						mGroups[ParentIdx]->mGroup = Group->mGroup;
2042 						break;
2043 					}
2044 				}
2045 			}
2046 
2047 			mGroups.RemoveIndex(GroupIdx);
2048 			delete Group;
2049 			Removed = true;
2050 		}
2051 	}
2052 	while (Removed);
2053 }
2054 
SnapPosition(const lcVector3 & Distance) const2055 lcVector3 lcModel::SnapPosition(const lcVector3& Distance) const
2056 {
2057 	lcVector3 NewDistance(Distance);
2058 
2059 	float SnapXY = gMainWindow->GetMoveXYSnap();
2060 	if (SnapXY != 0.0f)
2061 	{
2062 		int i = (int)(NewDistance[0] / SnapXY);
2063 		float Leftover = NewDistance[0] - (SnapXY * i);
2064 
2065 		if (Leftover > SnapXY / 2)
2066 			i++;
2067 		else if (Leftover < -SnapXY / 2)
2068 			i--;
2069 
2070 		NewDistance[0] = SnapXY * i;
2071 
2072 		i = (int)(NewDistance[1] / SnapXY);
2073 		Leftover = NewDistance[1] - (SnapXY * i);
2074 
2075 		if (Leftover > SnapXY / 2)
2076 			i++;
2077 		else if (Leftover < -SnapXY / 2)
2078 			i--;
2079 
2080 		NewDistance[1] = SnapXY * i;
2081 	}
2082 
2083 	float SnapZ = gMainWindow->GetMoveZSnap();
2084 	if (SnapZ != 0.0f)
2085 	{
2086 		int i = (int)(NewDistance[2] / SnapZ);
2087 		float Leftover = NewDistance[2] - (SnapZ * i);
2088 
2089 		if (Leftover > SnapZ / 2)
2090 			i++;
2091 		else if (Leftover < -SnapZ / 2)
2092 			i--;
2093 
2094 		NewDistance[2] = SnapZ * i;
2095 	}
2096 
2097 	return NewDistance;
2098 }
2099 
SnapRotation(const lcVector3 & Angles) const2100 lcVector3 lcModel::SnapRotation(const lcVector3& Angles) const
2101 {
2102 	float AngleSnap = gMainWindow->GetAngleSnap();
2103 	lcVector3 NewAngles(Angles);
2104 
2105 	if (AngleSnap != 0.0f)
2106 	{
2107 		int Snap[3];
2108 
2109 		for (int i = 0; i < 3; i++)
2110 			Snap[i] = (int)(Angles[i] / AngleSnap);
2111 
2112 		NewAngles = lcVector3((float)(AngleSnap * Snap[0]), (float)(AngleSnap * Snap[1]), (float)(AngleSnap * Snap[2]));
2113 	}
2114 
2115 	return NewAngles;
2116 }
2117 
GetRelativeRotation() const2118 lcMatrix33 lcModel::GetRelativeRotation() const
2119 {
2120 	if (gMainWindow->GetRelativeTransform())
2121 	{
2122 		lcObject* Focus = GetFocusObject();
2123 
2124 		if (Focus && Focus->IsPiece())
2125 			return ((lcPiece*)Focus)->GetRelativeRotation();
2126 	}
2127 
2128 	return lcMatrix33Identity();
2129 }
2130 
AddPiece()2131 void lcModel::AddPiece()
2132 {
2133 	PieceInfo* CurPiece = gMainWindow->GetCurrentPieceInfo();
2134 
2135 	if (!CurPiece)
2136 		return;
2137 
2138 	lcPiece* Last = mPieces.IsEmpty() ? nullptr : mPieces[mPieces.GetSize() - 1];
2139 
2140 	for (lcPiece* Piece : mPieces)
2141 	{
2142 		if (Piece->IsFocused())
2143 		{
2144 			Last = Piece;
2145 			break;
2146 		}
2147 	}
2148 
2149 	lcMatrix44 WorldMatrix;
2150 
2151 	if (Last)
2152 	{
2153 		const lcBoundingBox& BoundingBox = Last->GetBoundingBox();
2154 		lcVector3 Dist(0, 0, BoundingBox.Max.z - BoundingBox.Min.z);
2155 		Dist = SnapPosition(Dist);
2156 
2157 		WorldMatrix = Last->mModelWorld;
2158 		WorldMatrix.SetTranslation(lcMul31(Dist, Last->mModelWorld));
2159 	}
2160 	else
2161 	{
2162 		const lcBoundingBox& BoundingBox = CurPiece->GetBoundingBox();
2163 		WorldMatrix = lcMatrix44Translation(lcVector3(0.0f, 0.0f, -BoundingBox.Min.z));
2164 	}
2165 
2166 	lcPiece* Piece = new lcPiece(CurPiece);
2167 	Piece->Initialize(WorldMatrix, mCurrentStep);
2168 	Piece->SetColorIndex(gMainWindow->mColorIndex);
2169 	AddPiece(Piece);
2170 	gMainWindow->UpdateTimeline(false, false);
2171 	ClearSelectionAndSetFocus(Piece, LC_PIECE_SECTION_POSITION, false);
2172 
2173 	SaveCheckpoint(tr("Adding Piece"));
2174 }
2175 
AddPiece(lcPiece * Piece)2176 void lcModel::AddPiece(lcPiece* Piece)
2177 {
2178 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
2179 	{
2180 		if (mPieces[PieceIdx]->GetStepShow() > Piece->GetStepShow())
2181 		{
2182 			InsertPiece(Piece, PieceIdx);
2183 			return;
2184 		}
2185 	}
2186 
2187 	InsertPiece(Piece, mPieces.GetSize());
2188 }
2189 
InsertPiece(lcPiece * Piece,int Index)2190 void lcModel::InsertPiece(lcPiece* Piece, int Index)
2191 {
2192 	PieceInfo* Info = Piece->mPieceInfo;
2193 
2194 	if (!Info->IsModel())
2195 	{
2196 		lcMesh* Mesh = Info->GetMesh();
2197 
2198 		if (Mesh && Mesh->mVertexCacheOffset == -1)
2199 			lcGetPiecesLibrary()->mBuffersDirty = true;
2200 	}
2201 
2202 	mPieces.InsertAt(Index, Piece);
2203 }
2204 
DeleteAllCameras()2205 void lcModel::DeleteAllCameras()
2206 {
2207 	if (mCameras.IsEmpty())
2208 		return;
2209 
2210 	mCameras.DeleteAll();
2211 
2212 	gMainWindow->UpdateCameraMenu();
2213 	gMainWindow->UpdateSelectedObjects(true);
2214 	UpdateAllViews();
2215 	SaveCheckpoint(tr("Resetting Cameras"));
2216 }
2217 
DeleteSelectedObjects()2218 void lcModel::DeleteSelectedObjects()
2219 {
2220 	if (RemoveSelectedObjects())
2221 	{
2222 		if (!mIsPreview) {
2223 			gMainWindow->UpdateTimeline(false, false);
2224 			gMainWindow->UpdateSelectedObjects(true);
2225 			UpdateAllViews();
2226 			SaveCheckpoint(tr("Deleting"));
2227 		}
2228 	}
2229 }
2230 
ResetSelectedPiecesPivotPoint()2231 void lcModel::ResetSelectedPiecesPivotPoint()
2232 {
2233 	for (lcPiece* Piece : mPieces)
2234 		if (Piece->IsSelected())
2235 			Piece->ResetPivotPoint();
2236 
2237 	UpdateAllViews();
2238 }
2239 
RemoveSelectedPiecesKeyFrames()2240 void lcModel::RemoveSelectedPiecesKeyFrames()
2241 {
2242 	for (lcPiece* Piece : mPieces)
2243 		if (Piece->IsSelected())
2244 			Piece->RemoveKeyFrames();
2245 
2246 	for (lcCamera* Camera : mCameras)
2247 		if (Camera->IsSelected())
2248 			Camera->RemoveKeyFrames();
2249 
2250 	for (lcLight* Light : mLights)
2251 		if (Light->IsSelected())
2252 			Light->RemoveKeyFrames();
2253 
2254 	UpdateAllViews();
2255 	SaveCheckpoint(tr("Removing Key Frames"));
2256 }
2257 
InsertControlPoint()2258 void lcModel::InsertControlPoint()
2259 {
2260 	lcObject* Focus = GetFocusObject();
2261 
2262 	if (!Focus || !Focus->IsPiece())
2263 		return;
2264 
2265 	lcPiece* Piece = (lcPiece*)Focus;
2266 
2267 	lcVector3 Start, End;
2268 	gMainWindow->GetActiveView()->GetRayUnderPointer(Start, End);
2269 
2270 	if (Piece->InsertControlPoint(Start, End))
2271 	{
2272 		SaveCheckpoint(tr("Modifying"));
2273 		gMainWindow->UpdateSelectedObjects(true);
2274 		UpdateAllViews();
2275 	}
2276 }
2277 
RemoveFocusedControlPoint()2278 void lcModel::RemoveFocusedControlPoint()
2279 {
2280 	lcObject* Focus = GetFocusObject();
2281 
2282 	if (!Focus || !Focus->IsPiece())
2283 		return;
2284 
2285 	lcPiece* Piece = (lcPiece*)Focus;
2286 
2287 	if (Piece->RemoveFocusedControlPoint())
2288 	{
2289 		SaveCheckpoint(tr("Modifying"));
2290 		gMainWindow->UpdateSelectedObjects(true);
2291 		UpdateAllViews();
2292 	}
2293 }
2294 
ShowSelectedPiecesEarlier()2295 void lcModel::ShowSelectedPiecesEarlier()
2296 {
2297 	lcArray<lcPiece*> MovedPieces;
2298 
2299 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2300 	{
2301 		lcPiece* Piece = mPieces[PieceIdx];
2302 
2303 		if (Piece->IsSelected())
2304 		{
2305 			lcStep Step = Piece->GetStepShow();
2306 
2307 			if (Step > 1)
2308 			{
2309 				Step--;
2310 				Piece->SetStepShow(Step);
2311 
2312 				MovedPieces.Add(Piece);
2313 				mPieces.RemoveIndex(PieceIdx);
2314 				continue;
2315 			}
2316 		}
2317 
2318 		PieceIdx++;
2319 	}
2320 
2321 	if (MovedPieces.IsEmpty())
2322 		return;
2323 
2324 	for (int PieceIdx = 0; PieceIdx < MovedPieces.GetSize(); PieceIdx++)
2325 	{
2326 		lcPiece* Piece = MovedPieces[PieceIdx];
2327 		Piece->SetFileLine(-1);
2328 		AddPiece(Piece);
2329 	}
2330 
2331 	SaveCheckpoint(tr("Modifying"));
2332 	gMainWindow->UpdateTimeline(false, false);
2333 	gMainWindow->UpdateSelectedObjects(true);
2334 	UpdateAllViews();
2335 }
2336 
ShowSelectedPiecesLater()2337 void lcModel::ShowSelectedPiecesLater()
2338 {
2339 	lcArray<lcPiece*> MovedPieces;
2340 
2341 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2342 	{
2343 		lcPiece* Piece = mPieces[PieceIdx];
2344 
2345 		if (Piece->IsSelected())
2346 		{
2347 			lcStep Step = Piece->GetStepShow();
2348 
2349 			if (Step < LC_STEP_MAX)
2350 			{
2351 				Step++;
2352 				Piece->SetStepShow(Step);
2353 
2354 				if (!Piece->IsVisible(mCurrentStep))
2355 					Piece->SetSelected(false);
2356 
2357 				MovedPieces.Add(Piece);
2358 				mPieces.RemoveIndex(PieceIdx);
2359 				continue;
2360 			}
2361 		}
2362 
2363 		PieceIdx++;
2364 	}
2365 
2366 	if (MovedPieces.IsEmpty())
2367 		return;
2368 
2369 	for (int PieceIdx = 0; PieceIdx < MovedPieces.GetSize(); PieceIdx++)
2370 	{
2371 		lcPiece* Piece = MovedPieces[PieceIdx];
2372 		Piece->SetFileLine(-1);
2373 		AddPiece(Piece);
2374 	}
2375 
2376 	SaveCheckpoint(tr("Modifying"));
2377 	gMainWindow->UpdateTimeline(false, false);
2378 	gMainWindow->UpdateSelectedObjects(true);
2379 	UpdateAllViews();
2380 }
2381 
SetPieceSteps(const QList<QPair<lcPiece *,lcStep>> & PieceSteps)2382 void lcModel::SetPieceSteps(const QList<QPair<lcPiece*, lcStep>>& PieceSteps)
2383 {
2384 	if (PieceSteps.size() != mPieces.GetSize())
2385 		return;
2386 
2387 	bool Modified = false;
2388 
2389 	for (int PieceIdx = 0; PieceIdx < PieceSteps.size(); PieceIdx++)
2390 	{
2391 		const QPair<lcPiece*, lcStep>& PieceStep = PieceSteps[PieceIdx];
2392 		lcPiece* Piece = mPieces[PieceIdx];
2393 
2394 		if (Piece != PieceStep.first || Piece->GetStepShow() != PieceStep.second)
2395 		{
2396 			Piece = PieceStep.first;
2397 			lcStep Step = PieceStep.second;
2398 
2399 			mPieces[PieceIdx] = Piece;
2400 			Piece->SetStepShow(Step);
2401 
2402 			if (!Piece->IsVisible(mCurrentStep))
2403 				Piece->SetSelected(false);
2404 
2405 			Modified = true;
2406 		}
2407 	}
2408 
2409 	if (Modified)
2410 	{
2411 		SaveCheckpoint(tr("Modifying"));
2412 		UpdateAllViews();
2413 		gMainWindow->UpdateTimeline(false, false);
2414 		gMainWindow->UpdateSelectedObjects(true);
2415 	}
2416 }
2417 
RenamePiece(PieceInfo * Info)2418 void lcModel::RenamePiece(PieceInfo* Info)
2419 {
2420 	for (lcPiece* Piece : mPieces)
2421 		if (Piece->mPieceInfo == Info)
2422 			Piece->UpdateID();
2423 }
2424 
MoveSelectionToModel(lcModel * Model)2425 void lcModel::MoveSelectionToModel(lcModel* Model)
2426 {
2427 	if (!Model)
2428 		return;
2429 
2430 	lcArray<lcPiece*> Pieces;
2431 	lcPiece* ModelPiece = nullptr;
2432 	lcStep FirstStep = LC_STEP_MAX;
2433 	lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
2434 
2435 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2436 	{
2437 		lcPiece* Piece = mPieces[PieceIdx];
2438 
2439 		if (Piece->IsSelected())
2440 		{
2441 			Piece->CompareBoundingBox(Min, Max);
2442 			mPieces.RemoveIndex(PieceIdx);
2443 			Piece->SetGroup(nullptr); // todo: copy groups
2444 			Pieces.Add(Piece);
2445 			FirstStep = qMin(FirstStep, Piece->GetStepShow());
2446 
2447 			if (!ModelPiece)
2448 			{
2449 				ModelPiece = new lcPiece(Model->mPieceInfo);
2450 				ModelPiece->SetColorIndex(gDefaultColor);
2451 				InsertPiece(ModelPiece, PieceIdx);
2452 				PieceIdx++;
2453 			}
2454 		}
2455 		else
2456 			PieceIdx++;
2457 	}
2458 
2459 	lcVector3 ModelCenter = (Min + Max) / 2.0f;
2460 	ModelCenter.z += (Min.z - Max.z) / 2.0f;
2461 
2462 	for (int PieceIdx = 0; PieceIdx < Pieces.GetSize(); PieceIdx++)
2463 	{
2464 		lcPiece* Piece = Pieces[PieceIdx];
2465 		Piece->SetFileLine(-1);
2466 		Piece->SetStepShow(Piece->GetStepShow() - FirstStep + 1);
2467 		Piece->MoveSelected(Piece->GetStepShow(), false, -ModelCenter);
2468 		Model->AddPiece(Piece);
2469 	}
2470 
2471 	std::vector<lcModel*> UpdatedModels;
2472 	Model->UpdatePieceInfo(UpdatedModels);
2473 	if (ModelPiece)
2474 	{
2475 		ModelPiece->Initialize(lcMatrix44Translation(ModelCenter), FirstStep);
2476 		ModelPiece->UpdatePosition(mCurrentStep);
2477 	}
2478 
2479 	SaveCheckpoint(tr("New Model"));
2480 	gMainWindow->UpdateTimeline(false, false);
2481 	ClearSelectionAndSetFocus(ModelPiece, LC_PIECE_SECTION_POSITION, false);
2482 }
2483 
InlineSelectedModels()2484 void lcModel::InlineSelectedModels()
2485 {
2486 	lcArray<lcObject*> NewPieces;
2487 
2488 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2489 	{
2490 		lcPiece* Piece = mPieces[PieceIdx];
2491 
2492 		if (!Piece->IsSelected() || !Piece->mPieceInfo->IsModel())
2493 		{
2494 			PieceIdx++;
2495 			continue;
2496 		}
2497 
2498 		mPieces.RemoveIndex(PieceIdx);
2499 
2500 		lcModel* Model = Piece->mPieceInfo->GetModel();
2501 
2502 		for (const lcPiece* ModelPiece : Model->mPieces)
2503 		{
2504 			lcPiece* NewPiece = new lcPiece(nullptr);
2505 
2506 			// todo: recreate in groups in the current model
2507 
2508 			int ColorIndex = ModelPiece->GetColorIndex();
2509 
2510 			if (ColorIndex == gDefaultColor)
2511 				ColorIndex = Piece->GetColorIndex();
2512 
2513 			NewPiece->SetPieceInfo(ModelPiece->mPieceInfo, ModelPiece->GetID(), true);
2514 			NewPiece->Initialize(lcMul(ModelPiece->mModelWorld, Piece->mModelWorld), Piece->GetStepShow());
2515 			NewPiece->SetColorIndex(ColorIndex);
2516 			NewPiece->UpdatePosition(mCurrentStep);
2517 
2518 			NewPieces.Add(NewPiece);
2519 			InsertPiece(NewPiece, PieceIdx);
2520 			PieceIdx++;
2521 		}
2522 
2523 		delete Piece;
2524 	}
2525 
2526 	if (!NewPieces.GetSize())
2527 	{
2528 		QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No models selected."));
2529 		return;
2530 	}
2531 
2532 	SaveCheckpoint(tr("Inlining"));
2533 	gMainWindow->UpdateTimeline(false, false);
2534 	SetSelectionAndFocus(NewPieces, nullptr, 0, false);
2535 }
2536 
RemoveSelectedObjects()2537 bool lcModel::RemoveSelectedObjects()
2538 {
2539 	bool RemovedPiece = false;
2540 	bool RemovedCamera = false;
2541 	bool RemovedLight = false;
2542 
2543 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2544 	{
2545 		lcPiece* Piece = mPieces[PieceIdx];
2546 
2547 		if (Piece->IsSelected())
2548 		{
2549 			RemovedPiece = true;
2550 			mPieces.Remove(Piece);
2551 			delete Piece;
2552 		}
2553 		else
2554 			PieceIdx++;
2555 	}
2556 
2557 	for (int CameraIdx = 0; CameraIdx < mCameras.GetSize(); )
2558 	{
2559 		lcCamera* Camera = mCameras[CameraIdx];
2560 
2561 		if (Camera->IsSelected())
2562 		{
2563 			std::vector<lcView*> Views = lcView::GetModelViews(this);
2564 
2565 			for (lcView* View : Views)
2566 				if (Camera == View->GetCamera())
2567 					View->SetCamera(Camera, true);
2568 
2569 			RemovedCamera = true;
2570 			mCameras.RemoveIndex(CameraIdx);
2571 			delete Camera;
2572 		}
2573 		else
2574 			CameraIdx++;
2575 	}
2576 
2577 	if (RemovedCamera)
2578 		gMainWindow->UpdateCameraMenu();
2579 
2580 	for (int LightIdx = 0; LightIdx < mLights.GetSize(); )
2581 	{
2582 		lcLight* Light = mLights[LightIdx];
2583 
2584 		if (Light->IsSelected())
2585 		{
2586 			RemovedLight = true;
2587 			mLights.RemoveIndex(LightIdx);
2588 			delete Light;
2589 		}
2590 		else
2591 			LightIdx++;
2592 	}
2593 
2594 	RemoveEmptyGroups();
2595 
2596 	return RemovedPiece || RemovedCamera || RemovedLight;
2597 }
2598 
MoveSelectedObjects(const lcVector3 & PieceDistance,const lcVector3 & ObjectDistance,bool AllowRelative,bool AlternateButtonDrag,bool Update,bool Checkpoint)2599 void lcModel::MoveSelectedObjects(const lcVector3& PieceDistance, const lcVector3& ObjectDistance, bool AllowRelative, bool AlternateButtonDrag, bool Update, bool Checkpoint)
2600 {
2601 	bool Moved = false;
2602 	lcMatrix33 RelativeRotation;
2603 
2604 	if (AllowRelative)
2605 		RelativeRotation = GetRelativeRotation();
2606 	else
2607 		RelativeRotation = lcMatrix33Identity();
2608 
2609 	if (PieceDistance.LengthSquared() >= 0.001f)
2610 	{
2611 		lcVector3 TransformedPieceDistance = lcMul(PieceDistance, RelativeRotation);
2612 
2613 		if (AlternateButtonDrag)
2614 		{
2615 			for (lcPiece* Piece : mPieces)
2616 			{
2617 				if (Piece->IsFocused())
2618 				{
2619 					Piece->MovePivotPoint(TransformedPieceDistance);
2620 					Moved = true;
2621 					break;
2622 				}
2623 			}
2624 		}
2625 		else
2626 		{
2627 			for (lcPiece* Piece : mPieces)
2628 			{
2629 				if (Piece->IsSelected())
2630 				{
2631 					if (gMainWindow->GetSeparateTransform())
2632 						TransformedPieceDistance = lcMul(PieceDistance, Piece->GetRelativeRotation());
2633 
2634 					Piece->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedPieceDistance);
2635 					Piece->UpdatePosition(mCurrentStep);
2636 					Moved = true;
2637 				}
2638 			}
2639 		}
2640 	}
2641 
2642 	if (ObjectDistance.LengthSquared() >= 0.001f && !AlternateButtonDrag)
2643 	{
2644 		lcVector3 TransformedObjectDistance = lcMul(ObjectDistance, RelativeRotation);
2645 
2646 		for (lcCamera* Camera : mCameras)
2647 		{
2648 			if (Camera->IsSelected())
2649 			{
2650 				Camera->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedObjectDistance);
2651 				Camera->UpdatePosition(mCurrentStep);
2652 				Moved = true;
2653 			}
2654 		}
2655 
2656 		for (lcLight* Light : mLights)
2657 		{
2658 			if (Light->IsSelected())
2659 			{
2660 				Light->MoveSelected(mCurrentStep, gMainWindow->GetAddKeys(), TransformedObjectDistance);
2661 				Light->UpdatePosition(mCurrentStep);
2662 				Moved = true;
2663 			}
2664 		}
2665 	}
2666 
2667 	if (Moved && Update)
2668 	{
2669 		UpdateAllViews();
2670 		if (Checkpoint)
2671 			SaveCheckpoint(tr("Moving"));
2672 		gMainWindow->UpdateSelectedObjects(false);
2673 	}
2674 }
2675 
RotateSelectedPieces(const lcVector3 & Angles,bool Relative,bool RotatePivotPoint,bool Update,bool Checkpoint)2676 void lcModel::RotateSelectedPieces(const lcVector3& Angles, bool Relative, bool RotatePivotPoint, bool Update, bool Checkpoint)
2677 {
2678 	if (Angles.LengthSquared() < 0.001f)
2679 		return;
2680 
2681 	lcMatrix33 RotationMatrix = lcMatrix33Identity();
2682 	bool Rotated = false;
2683 
2684 	if (Angles[0] != 0.0f)
2685 		RotationMatrix = lcMul(lcMatrix33RotationX(Angles[0] * LC_DTOR), RotationMatrix);
2686 
2687 	if (Angles[1] != 0.0f)
2688 		RotationMatrix = lcMul(lcMatrix33RotationY(Angles[1] * LC_DTOR), RotationMatrix);
2689 
2690 	if (Angles[2] != 0.0f)
2691 		RotationMatrix = lcMul(lcMatrix33RotationZ(Angles[2] * LC_DTOR), RotationMatrix);
2692 
2693 	if (RotatePivotPoint)
2694 	{
2695 		lcObject* Focus = GetFocusObject();
2696 
2697 		if (Focus && Focus->IsPiece())
2698 		{
2699 			((lcPiece*)Focus)->RotatePivotPoint(RotationMatrix);
2700 			Rotated = true;
2701 		}
2702 	}
2703 	else
2704 	{
2705 		if (!gMainWindow->GetSeparateTransform())
2706 		{
2707 			lcVector3 Center;
2708 			lcMatrix33 RelativeRotation;
2709 
2710 			GetMoveRotateTransform(Center, RelativeRotation);
2711 
2712 			lcMatrix33 WorldToFocusMatrix;
2713 
2714 			if (Relative)
2715 			{
2716 				WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation);
2717 				RotationMatrix = lcMul(RotationMatrix, RelativeRotation);
2718 			}
2719 			else
2720 				WorldToFocusMatrix = lcMatrix33Identity();
2721 
2722 			for (lcPiece* Piece : mPieces)
2723 			{
2724 				if (!Piece->IsSelected())
2725 					continue;
2726 
2727 				Piece->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RotationMatrix, Center, WorldToFocusMatrix);
2728 				Piece->UpdatePosition(mCurrentStep);
2729 				Rotated = true;
2730 			}
2731 		}
2732 		else
2733 		{
2734 			for (lcPiece* Piece : mPieces)
2735 			{
2736 				if (!Piece->IsSelected())
2737 					continue;
2738 
2739 				const lcVector3 Center = Piece->GetRotationCenter();
2740 				lcMatrix33 WorldToFocusMatrix;
2741 				lcMatrix33 RelativeRotationMatrix;
2742 
2743 				if (Relative)
2744 				{
2745 					lcMatrix33 RelativeRotation = Piece->GetRelativeRotation();
2746 					WorldToFocusMatrix = lcMatrix33AffineInverse(RelativeRotation);
2747 					RelativeRotationMatrix = lcMul(RotationMatrix, RelativeRotation);
2748 				}
2749 				else
2750 				{
2751 					WorldToFocusMatrix = lcMatrix33Identity();
2752 					RelativeRotationMatrix = RotationMatrix;
2753 				}
2754 
2755 				Piece->Rotate(mCurrentStep, gMainWindow->GetAddKeys(), RelativeRotationMatrix, Center, WorldToFocusMatrix);
2756 				Piece->UpdatePosition(mCurrentStep);
2757 				Rotated = true;
2758 			}
2759 		}
2760 	}
2761 
2762 	if (Rotated && Update)
2763 	{
2764 		UpdateAllViews();
2765 		if (Checkpoint)
2766 			SaveCheckpoint(tr("Rotating"));
2767 		gMainWindow->UpdateSelectedObjects(false);
2768 	}
2769 }
2770 
ScaleSelectedPieces(const float Scale,bool Update,bool Checkpoint)2771 void lcModel::ScaleSelectedPieces(const float Scale, bool Update, bool Checkpoint)
2772 {
2773 	if (Scale < 0.001f)
2774 		return;
2775 
2776 	lcObject* Focus = GetFocusObject();
2777 	if (!Focus || !Focus->IsPiece())
2778 		return;
2779 
2780 	lcPiece* Piece = (lcPiece*)Focus;
2781 	quint32 Section = Piece->GetFocusSection();
2782 
2783 	if (Section >= LC_PIECE_SECTION_CONTROL_POINT_FIRST && Section <= LC_PIECE_SECTION_CONTROL_POINT_LAST)
2784 	{
2785 		int ControlPointIndex = Section - LC_PIECE_SECTION_CONTROL_POINT_FIRST;
2786 		Piece->SetControlPointScale(ControlPointIndex, Scale);
2787 
2788 		if (Update)
2789 		{
2790 			UpdateAllViews();
2791 			if (Checkpoint)
2792 				SaveCheckpoint(tr("Scaling"));
2793 			gMainWindow->UpdateSelectedObjects(false);
2794 		}
2795 	}
2796 }
2797 
TransformSelectedObjects(lcTransformType TransformType,const lcVector3 & Transform)2798 void lcModel::TransformSelectedObjects(lcTransformType TransformType, const lcVector3& Transform)
2799 {
2800 	switch (TransformType)
2801 	{
2802 	case lcTransformType::AbsoluteTranslation:
2803 		MoveSelectedObjects(Transform, false, false, true, true);
2804 		break;
2805 
2806 	case lcTransformType::RelativeTranslation:
2807 		MoveSelectedObjects(Transform, true, false, true, true);
2808 		break;
2809 
2810 	case lcTransformType::AbsoluteRotation:
2811 		RotateSelectedPieces(Transform, false, false, true, true);
2812 		break;
2813 
2814 	case lcTransformType::RelativeRotation:
2815 		RotateSelectedPieces(Transform, true, false, true, true);
2816 		break;
2817 
2818 	case lcTransformType::Count:
2819 		break;
2820 	}
2821 }
2822 
SetSelectedPiecesColorIndex(int ColorIndex)2823 void lcModel::SetSelectedPiecesColorIndex(int ColorIndex)
2824 {
2825 	bool Modified = false;
2826 
2827 	for (lcPiece* Piece : mPieces)
2828 	{
2829 		if (Piece->IsSelected() && Piece->GetColorIndex() != ColorIndex)
2830 		{
2831 			Piece->SetColorIndex(ColorIndex);
2832 			Modified = true;
2833 		}
2834 	}
2835 
2836 	if (Modified)
2837 	{
2838 		SaveCheckpoint(tr("Painting"));
2839 		gMainWindow->UpdateSelectedObjects(false);
2840 		UpdateAllViews();
2841 		gMainWindow->UpdateTimeline(false, true);
2842 	}
2843 }
2844 
SetSelectedPiecesPieceInfo(PieceInfo * Info)2845 void lcModel::SetSelectedPiecesPieceInfo(PieceInfo* Info)
2846 {
2847 	bool Modified = false;
2848 
2849 	for (lcPiece* Piece : mPieces)
2850 	{
2851 		if (Piece->IsSelected() && Piece->mPieceInfo != Info)
2852 		{
2853 			lcPiecesLibrary* Library = lcGetPiecesLibrary();
2854 			Library->ReleasePieceInfo(Piece->mPieceInfo);
2855 			Piece->SetPieceInfo(Info, QString(), true);
2856 			Modified = true;
2857 		}
2858 	}
2859 
2860 	if (Modified)
2861 	{
2862 		SaveCheckpoint(tr("Setting Part"));
2863 		gMainWindow->UpdateSelectedObjects(false);
2864 		UpdateAllViews();
2865 		gMainWindow->UpdateTimeline(false, true);
2866 	}
2867 }
2868 
SetSelectedPiecesStepShow(lcStep Step)2869 void lcModel::SetSelectedPiecesStepShow(lcStep Step)
2870 {
2871 	lcArray<lcPiece*> MovedPieces;
2872 	bool SelectionChanged = false;
2873 
2874 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); )
2875 	{
2876 		lcPiece* Piece = mPieces[PieceIdx];
2877 
2878 		if (Piece->IsSelected() && Piece->GetStepShow() != Step)
2879 		{
2880 			Piece->SetStepShow(Step);
2881 
2882 			if (!Piece->IsVisible(mCurrentStep))
2883 			{
2884 				Piece->SetSelected(false);
2885 				SelectionChanged = true;
2886 			}
2887 
2888 			MovedPieces.Add(Piece);
2889 			mPieces.RemoveIndex(PieceIdx);
2890 			continue;
2891 		}
2892 
2893 		PieceIdx++;
2894 	}
2895 
2896 	if (MovedPieces.IsEmpty())
2897 		return;
2898 
2899 	for (int PieceIdx = 0; PieceIdx < MovedPieces.GetSize(); PieceIdx++)
2900 	{
2901 		lcPiece* Piece = MovedPieces[PieceIdx];
2902 		Piece->SetFileLine(-1);
2903 		AddPiece(Piece);
2904 	}
2905 
2906 	SaveCheckpoint(tr("Showing Pieces"));
2907 	UpdateAllViews();
2908 	gMainWindow->UpdateTimeline(false, false);
2909 	gMainWindow->UpdateSelectedObjects(SelectionChanged);
2910 }
2911 
SetSelectedPiecesStepHide(lcStep Step)2912 void lcModel::SetSelectedPiecesStepHide(lcStep Step)
2913 {
2914 	bool Modified = false;
2915 	bool SelectionChanged = false;
2916 
2917 	for (lcPiece* Piece : mPieces)
2918 	{
2919 		if (Piece->IsSelected() && Piece->GetStepHide() != Step)
2920 		{
2921 			Piece->SetStepHide(Step);
2922 
2923 			if (!Piece->IsVisible(mCurrentStep))
2924 			{
2925 				Piece->SetSelected(false);
2926 				SelectionChanged = true;
2927 			}
2928 
2929 			Modified = true;
2930 		}
2931 	}
2932 
2933 	if (Modified)
2934 	{
2935 		SaveCheckpoint(tr("Hiding Pieces"));
2936 		UpdateAllViews();
2937 		gMainWindow->UpdateTimeline(false, false);
2938 		gMainWindow->UpdateSelectedObjects(SelectionChanged);
2939 	}
2940 }
2941 
SetCameraOrthographic(lcCamera * Camera,bool Ortho)2942 void lcModel::SetCameraOrthographic(lcCamera* Camera, bool Ortho)
2943 {
2944 	if (Camera->IsOrtho() == Ortho)
2945 		return;
2946 
2947 	Camera->SetOrtho(Ortho);
2948 	Camera->UpdatePosition(mCurrentStep);
2949 
2950 	SaveCheckpoint(tr("Editing Camera"));
2951 	UpdateAllViews();
2952 	gMainWindow->UpdatePerspective();
2953 }
2954 
SetCameraFOV(lcCamera * Camera,float FOV)2955 void lcModel::SetCameraFOV(lcCamera* Camera, float FOV)
2956 {
2957 	if (Camera->m_fovy == FOV)
2958 		return;
2959 
2960 	Camera->m_fovy = FOV;
2961 	Camera->UpdatePosition(mCurrentStep);
2962 
2963 	SaveCheckpoint(tr("Changing FOV"));
2964 	UpdateAllViews();
2965 }
2966 
SetCameraZNear(lcCamera * Camera,float ZNear)2967 void lcModel::SetCameraZNear(lcCamera* Camera, float ZNear)
2968 {
2969 	if (Camera->m_zNear == ZNear)
2970 		return;
2971 
2972 	Camera->m_zNear = ZNear;
2973 	Camera->UpdatePosition(mCurrentStep);
2974 
2975 	SaveCheckpoint(tr("Editing Camera"));
2976 	UpdateAllViews();
2977 }
2978 
SetCameraZFar(lcCamera * Camera,float ZFar)2979 void lcModel::SetCameraZFar(lcCamera* Camera, float ZFar)
2980 {
2981 	if (Camera->m_zFar == ZFar)
2982 		return;
2983 
2984 	Camera->m_zFar = ZFar;
2985 	Camera->UpdatePosition(mCurrentStep);
2986 
2987 	SaveCheckpoint(tr("Editing Camera"));
2988 	UpdateAllViews();
2989 }
2990 
SetCameraName(lcCamera * Camera,const QString & Name)2991 void lcModel::SetCameraName(lcCamera* Camera, const QString& Name)
2992 {
2993 	if (Camera->GetName() == Name)
2994 		return;
2995 
2996 	Camera->SetName(Name);
2997 
2998 	SaveCheckpoint(tr("Renaming Camera"));
2999 	gMainWindow->UpdateSelectedObjects(false);
3000 	UpdateAllViews();
3001 	gMainWindow->UpdateCameraMenu();
3002 }
3003 
AnyPiecesSelected() const3004 bool lcModel::AnyPiecesSelected() const
3005 {
3006 	for (lcPiece* Piece : mPieces)
3007 		if (Piece->IsSelected())
3008 			return true;
3009 
3010 	return false;
3011 }
3012 
AnyObjectsSelected() const3013 bool lcModel::AnyObjectsSelected() const
3014 {
3015 	for (lcPiece* Piece : mPieces)
3016 		if (Piece->IsSelected())
3017 			return true;
3018 
3019 	for (lcCamera* Camera : mCameras)
3020 		if (Camera->IsSelected())
3021 			return true;
3022 
3023 	for (lcLight* Light : mLights)
3024 		if (Light->IsSelected())
3025 			return true;
3026 
3027 	return false;
3028 }
3029 
GetFocusObject() const3030 lcObject* lcModel::GetFocusObject() const
3031 {
3032 	for (lcPiece* Piece : mPieces)
3033 		if (Piece->IsFocused())
3034 			return Piece;
3035 
3036 	for (lcCamera* Camera : mCameras)
3037 		if (Camera->IsFocused())
3038 			return Camera;
3039 
3040 	for (lcLight* Light : mLights)
3041 		if (Light->IsFocused())
3042 			return Light;
3043 
3044 	return nullptr;
3045 }
3046 
GetFirstSelectedSubmodel() const3047 lcModel* lcModel::GetFirstSelectedSubmodel() const
3048 {
3049 	for (lcPiece* Piece : mPieces)
3050 		if (Piece->IsSelected() && Piece->mPieceInfo->IsModel())
3051 			return Piece->mPieceInfo->GetModel();
3052 
3053 	return nullptr;
3054 }
3055 
GetSubModels(lcArray<lcModel * > & SubModels) const3056 void lcModel::GetSubModels(lcArray<lcModel*>& SubModels) const
3057 {
3058 	for (lcPiece* Piece : mPieces)
3059 	{
3060 		if (Piece->mPieceInfo->IsModel())
3061 		{
3062 			lcModel* SubModel = Piece->mPieceInfo->GetModel();
3063 			if (SubModels.FindIndex(SubModel) == -1)
3064 				SubModels.Add(SubModel);
3065 		}
3066 	}
3067 }
3068 
GetMoveRotateTransform(lcVector3 & Center,lcMatrix33 & RelativeRotation) const3069 bool lcModel::GetMoveRotateTransform(lcVector3& Center, lcMatrix33& RelativeRotation) const
3070 {
3071 	bool Relative = gMainWindow->GetRelativeTransform();
3072 	int NumSelected = 0;
3073 
3074 	Center = lcVector3(0.0f, 0.0f, 0.0f);
3075 	RelativeRotation = lcMatrix33Identity();
3076 
3077 	for (lcPiece* Piece : mPieces)
3078 	{
3079 		if (!Piece->IsSelected())
3080 			continue;
3081 
3082 		if (Piece->IsFocused() && Relative)
3083 		{
3084 			Center = Piece->GetRotationCenter();
3085 			RelativeRotation = Piece->GetRelativeRotation();
3086 			return true;
3087 		}
3088 
3089 		Center += Piece->mModelWorld.GetTranslation();
3090 		NumSelected++;
3091 	}
3092 
3093 	for (lcCamera* Camera : mCameras)
3094 	{
3095 		if (!Camera->IsSelected())
3096 			continue;
3097 
3098 		if (Camera->IsFocused() && Relative)
3099 		{
3100 			Center = Camera->GetSectionPosition(Camera->GetFocusSection());
3101 //			RelativeRotation = Piece->GetRelativeRotation();
3102 			return true;
3103 		}
3104 
3105 		Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_POSITION);
3106 		Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_TARGET);
3107 		Center += Camera->GetSectionPosition(LC_CAMERA_SECTION_UPVECTOR);
3108 		NumSelected += 3;
3109 	}
3110 
3111 	for (lcLight* Light : mLights)
3112 	{
3113 		if (!Light->IsSelected())
3114 			continue;
3115 
3116 		if (Light->IsFocused() && Relative)
3117 		{
3118 			Center = Light->GetSectionPosition(Light->GetFocusSection());
3119 //			RelativeRotation = Piece->GetRelativeRotation();
3120 			return true;
3121 		}
3122 
3123 		Center += Light->GetSectionPosition(LC_LIGHT_SECTION_POSITION);
3124 		NumSelected++;
3125 		if (Light->IsSpotLight() || Light->IsDirectionalLight())
3126 		{
3127 			Center += Light->GetSectionPosition(LC_LIGHT_SECTION_TARGET);
3128 			NumSelected++;
3129 		}
3130 	}
3131 
3132 	if (NumSelected)
3133 	{
3134 		Center /= NumSelected;
3135 		return true;
3136 	}
3137 
3138 	return false;
3139 }
3140 
GetPieceFocusOrSelectionCenter(lcVector3 & Center) const3141 bool lcModel::GetPieceFocusOrSelectionCenter(lcVector3& Center) const
3142 {
3143 	lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3144 	lcPiece* Selected = nullptr;
3145 	int NumSelected = 0;
3146 
3147 	for (lcPiece* Piece : mPieces)
3148 	{
3149 		if (Piece->IsFocused())
3150 		{
3151 			Center = Piece->mModelWorld.GetTranslation();
3152 			return true;
3153 		}
3154 
3155 		if (Piece->IsSelected())
3156 		{
3157 			Piece->CompareBoundingBox(Min, Max);
3158 			Selected = Piece;
3159 			NumSelected++;
3160 		}
3161 	}
3162 
3163 	if (NumSelected == 1)
3164 		Center = Selected->mModelWorld.GetTranslation();
3165 	else if (NumSelected)
3166 		Center = (Min + Max) / 2.0f;
3167 	else
3168 		Center = lcVector3(0.0f, 0.0f, 0.0f);
3169 
3170 	return NumSelected != 0;
3171 }
3172 
GetSelectionOrModelCenter() const3173 lcVector3 lcModel::GetSelectionOrModelCenter() const
3174 {
3175 	lcVector3 Center;
3176 
3177 	if (!GetSelectionCenter(Center))
3178 	{
3179 		lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3180 
3181 		if (GetVisiblePiecesBoundingBox(Min, Max))
3182 			Center = (Min + Max) / 2.0f;
3183 		else
3184 			Center = lcVector3(0.0f, 0.0f, 0.0f);
3185 	}
3186 
3187 	return Center;
3188 }
3189 
GetFocusPosition(lcVector3 & Position) const3190 bool lcModel::GetFocusPosition(lcVector3& Position) const
3191 {
3192 	lcObject* FocusObject = GetFocusObject();
3193 
3194 	if (FocusObject)
3195 	{
3196 		Position = FocusObject->GetSectionPosition(FocusObject->GetFocusSection());
3197 		return true;
3198 	}
3199 	else
3200 	{
3201 		Position = lcVector3(0.0f, 0.0f, 0.0f);
3202 		return false;
3203 	}
3204 }
3205 
GetSelectionCenter(lcVector3 & Center) const3206 bool lcModel::GetSelectionCenter(lcVector3& Center) const
3207 {
3208 	lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3209 	lcPiece* SelectedPiece = nullptr;
3210 	bool SinglePieceSelected = true;
3211 	bool Selected = false;
3212 
3213 	for (lcPiece* Piece : mPieces)
3214 	{
3215 		if (Piece->IsSelected())
3216 		{
3217 			Piece->CompareBoundingBox(Min, Max);
3218 			Selected = true;
3219 
3220 			if (!SelectedPiece)
3221 				SelectedPiece = Piece;
3222 			else
3223 				SinglePieceSelected = false;
3224 		}
3225 	}
3226 
3227 	for (lcCamera* Camera : mCameras)
3228 	{
3229 		if (Camera->IsSelected())
3230 		{
3231 			Camera->CompareBoundingBox(Min, Max);
3232 			Selected = true;
3233 			SinglePieceSelected = false;
3234 		}
3235 	}
3236 
3237 	for (lcLight* Light : mLights)
3238 	{
3239 		if (Light->IsSelected())
3240 		{
3241 			Light->CompareBoundingBox(Min, Max);
3242 			Selected = true;
3243 			SinglePieceSelected = false;
3244 		}
3245 	}
3246 
3247 	if (SelectedPiece && SinglePieceSelected)
3248 		Center = SelectedPiece->GetSectionPosition(LC_PIECE_SECTION_POSITION);
3249 	else if (Selected)
3250 		Center = (Min + Max) / 2.0f;
3251 	else
3252 		Center = lcVector3(0.0f, 0.0f, 0.0f);
3253 
3254 	return Selected;
3255 }
3256 
GetAllPiecesBoundingBox() const3257 lcBoundingBox lcModel::GetAllPiecesBoundingBox() const
3258 {
3259 	lcBoundingBox Box;
3260 
3261 	if (!mPieces.IsEmpty())
3262 	{
3263 		Box.Min = lcVector3(FLT_MAX, FLT_MAX, FLT_MAX);
3264 		Box.Max = lcVector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3265 
3266 		for (lcPiece* Piece : mPieces)
3267 			Piece->CompareBoundingBox(Box.Min, Box.Max);
3268 	}
3269 	else
3270 		Box.Min = Box.Max = lcVector3(0.0f, 0.0f, 0.0f);
3271 
3272 	return Box;
3273 }
3274 
GetVisiblePiecesBoundingBox(lcVector3 & Min,lcVector3 & Max) const3275 bool lcModel::GetVisiblePiecesBoundingBox(lcVector3& Min, lcVector3& Max) const
3276 {
3277 	bool Valid = false;
3278 	Min = lcVector3(FLT_MAX, FLT_MAX, FLT_MAX);
3279 	Max = lcVector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
3280 
3281 	for (lcPiece* Piece : mPieces)
3282 	{
3283 		if (Piece->IsVisible(mCurrentStep))
3284 		{
3285 			Piece->CompareBoundingBox(Min, Max);
3286 			Valid = true;
3287 		}
3288 	}
3289 
3290 	return Valid;
3291 }
3292 
GetPiecesBoundingBoxPoints() const3293 std::vector<lcVector3> lcModel::GetPiecesBoundingBoxPoints() const
3294 {
3295 	std::vector<lcVector3> Points;
3296 
3297 	for (lcPiece* Piece : mPieces)
3298 		if (Piece->IsVisible(mCurrentStep))
3299 			Piece->SubModelAddBoundingBoxPoints(lcMatrix44Identity(), Points);
3300 
3301 	return Points;
3302 }
3303 
GetPartsList(int DefaultColorIndex,bool ScanSubModels,bool AddSubModels,lcPartsList & PartsList) const3304 void lcModel::GetPartsList(int DefaultColorIndex, bool ScanSubModels, bool AddSubModels, lcPartsList& PartsList) const
3305 {
3306 	for (lcPiece* Piece : mPieces)
3307 	{
3308 		if (!Piece->IsVisibleInSubModel())
3309 			continue;
3310 
3311 		int ColorIndex = Piece->GetColorIndex();
3312 
3313 		if (ColorIndex == gDefaultColor)
3314 			ColorIndex = DefaultColorIndex;
3315 
3316 		Piece->mPieceInfo->GetPartsList(ColorIndex, ScanSubModels, AddSubModels, PartsList);
3317 	}
3318 }
3319 
GetPartsListForStep(lcStep Step,int DefaultColorIndex,lcPartsList & PartsList) const3320 void lcModel::GetPartsListForStep(lcStep Step, int DefaultColorIndex, lcPartsList& PartsList) const
3321 {
3322 	for (lcPiece* Piece : mPieces)
3323 	{
3324 		if (Piece->GetStepShow() != Step || Piece->IsHidden())
3325 			continue;
3326 
3327 		int ColorIndex = Piece->GetColorIndex();
3328 
3329 		if (ColorIndex == gDefaultColor)
3330 			ColorIndex = DefaultColorIndex;
3331 
3332 		Piece->mPieceInfo->GetPartsList(ColorIndex, false, true, PartsList);
3333 	}
3334 }
3335 
GetModelParts(const lcMatrix44 & WorldMatrix,int DefaultColorIndex,std::vector<lcModelPartsEntry> & ModelParts) const3336 void lcModel::GetModelParts(const lcMatrix44& WorldMatrix, int DefaultColorIndex, std::vector<lcModelPartsEntry>& ModelParts) const
3337 {
3338 	for (lcPiece* Piece : mPieces)
3339 		Piece->GetModelParts(WorldMatrix, DefaultColorIndex, ModelParts);
3340 }
3341 
GetSelectionInformation(int * Flags,lcArray<lcObject * > & Selection,lcObject ** Focus) const3342 void lcModel::GetSelectionInformation(int* Flags, lcArray<lcObject*>& Selection, lcObject** Focus) const
3343 {
3344 	*Flags = 0;
3345 	*Focus = nullptr;
3346 
3347 	if (mPieces.IsEmpty())
3348 		*Flags |= LC_SEL_NO_PIECES;
3349 	else
3350 	{
3351 		lcGroup* Group = nullptr;
3352 		bool First = true;
3353 
3354 		for (lcPiece* Piece : mPieces)
3355 		{
3356 			if (Piece->IsSelected())
3357 			{
3358 				Selection.Add(Piece);
3359 
3360 				if (Piece->IsFocused())
3361 					*Focus = Piece;
3362 
3363 				if (Piece->mPieceInfo->IsModel())
3364 					*Flags |= LC_SEL_MODEL_SELECTED;
3365 
3366 				if (Piece->IsHidden())
3367 					*Flags |= LC_SEL_HIDDEN | LC_SEL_HIDDEN_SELECTED;
3368 				else
3369 					*Flags |= LC_SEL_VISIBLE_SELECTED;
3370 
3371 				*Flags |= LC_SEL_PIECE | LC_SEL_SELECTED;
3372 
3373 				if (Piece->CanAddControlPoint())
3374 					*Flags |= LC_SEL_CAN_ADD_CONTROL_POINT;
3375 
3376 				if (Piece->CanRemoveControlPoint())
3377 					*Flags |= LC_SEL_CAN_REMOVE_CONTROL_POINT;
3378 
3379 				if (Piece->GetGroup() != nullptr)
3380 				{
3381 					*Flags |= LC_SEL_GROUPED;
3382 					if (Piece->IsFocused())
3383 						*Flags |= LC_SEL_FOCUS_GROUPED;
3384 				}
3385 
3386 				if (First)
3387 				{
3388 					Group = Piece->GetGroup();
3389 					First = false;
3390 				}
3391 				else
3392 				{
3393 					if (Group != Piece->GetGroup())
3394 						*Flags |= LC_SEL_CAN_GROUP;
3395 					else
3396 						if (Group == nullptr)
3397 							*Flags |= LC_SEL_CAN_GROUP;
3398 				}
3399 			}
3400 			else
3401 			{
3402 				*Flags |= LC_SEL_UNSELECTED;
3403 
3404 				if (Piece->IsHidden())
3405 					*Flags |= LC_SEL_HIDDEN;
3406 			}
3407 		}
3408 	}
3409 
3410 	for (lcCamera* Camera : mCameras)
3411 	{
3412 		if (Camera->IsSelected())
3413 		{
3414 			Selection.Add(Camera);
3415 			*Flags |= LC_SEL_SELECTED;
3416 
3417 			if (Camera->IsFocused())
3418 				*Focus = Camera;
3419 		}
3420 	}
3421 
3422 	for (lcLight* Light : mLights)
3423 	{
3424 		if (Light->IsSelected())
3425 		{
3426 			Selection.Add(Light);
3427 			*Flags |= LC_SEL_SELECTED;
3428 
3429 			if (Light->IsFocused())
3430 				*Focus = Light;
3431 		}
3432 	}
3433 }
3434 
GetSelectionModePieces(lcPiece * SelectedPiece) const3435 lcArray<lcObject*> lcModel::GetSelectionModePieces(lcPiece* SelectedPiece) const
3436 {
3437 	PieceInfo* Info = SelectedPiece->mPieceInfo;
3438 	int ColorIndex = SelectedPiece->GetColorIndex();
3439 	lcArray<lcObject*> Pieces;
3440 
3441 	switch (gMainWindow->GetSelectionMode())
3442 	{
3443 	case lcSelectionMode::Single:
3444 		break;
3445 
3446 	case lcSelectionMode::Piece:
3447 		for (lcPiece* Piece : mPieces)
3448 			if (Piece->IsVisible(mCurrentStep) && Piece->mPieceInfo == Info && Piece != SelectedPiece)
3449 				Pieces.Add(Piece);
3450 		break;
3451 
3452 	case lcSelectionMode::Color:
3453 		for (lcPiece* Piece : mPieces)
3454 			if (Piece->IsVisible(mCurrentStep) && Piece->GetColorIndex() == ColorIndex && Piece != SelectedPiece)
3455 				Pieces.Add(Piece);
3456 		break;
3457 
3458 	case lcSelectionMode::PieceColor:
3459 		for (lcPiece* Piece : mPieces)
3460 			if (Piece->IsVisible(mCurrentStep) && Piece->mPieceInfo == Info && Piece->GetColorIndex() == ColorIndex && Piece != SelectedPiece)
3461 				Pieces.Add(Piece);
3462 		break;
3463 	}
3464 
3465 	return Pieces;
3466 }
3467 
ClearSelection(bool UpdateInterface)3468 void lcModel::ClearSelection(bool UpdateInterface)
3469 {
3470 	for (lcPiece* Piece : mPieces)
3471 		Piece->SetSelected(false);
3472 
3473 	for (lcCamera* Camera : mCameras)
3474 		Camera->SetSelected(false);
3475 
3476 	for (lcLight* Light : mLights)
3477 		Light->SetSelected(false);
3478 
3479 	if (UpdateInterface)
3480 	{
3481 		gMainWindow->UpdateSelectedObjects(true);
3482 		UpdateAllViews();
3483 	}
3484 }
3485 
SelectGroup(lcGroup * TopGroup,bool Select)3486 void lcModel::SelectGroup(lcGroup* TopGroup, bool Select)
3487 {
3488 	if (!TopGroup)
3489 		return;
3490 
3491 	for (lcPiece* Piece : mPieces)
3492 		if (!Piece->IsSelected() && Piece->IsVisible(mCurrentStep) && (Piece->GetTopGroup() == TopGroup))
3493 			Piece->SetSelected(Select);
3494 }
3495 
FocusOrDeselectObject(const lcObjectSection & ObjectSection)3496 void lcModel::FocusOrDeselectObject(const lcObjectSection& ObjectSection)
3497 {
3498 	lcObject* FocusObject = GetFocusObject();
3499 	lcObject* Object = ObjectSection.Object;
3500 	quint32 Section = ObjectSection.Section;
3501 
3502 	if (Object)
3503 	{
3504 		bool WasSelected = Object->IsSelected();
3505 
3506 		if (!Object->IsFocused(Section))
3507 		{
3508 			if (FocusObject)
3509 				FocusObject->SetFocused(FocusObject->GetFocusSection(), false);
3510 
3511 			Object->SetFocused(Section, true);
3512 		}
3513 		else
3514 			Object->SetFocused(Section, false);
3515 
3516 		bool IsSelected = Object->IsSelected();
3517 
3518 		if (Object->IsPiece() && (WasSelected != IsSelected))
3519 		{
3520 			lcPiece* Piece = (lcPiece*)Object;
3521 
3522 			if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
3523 				SelectGroup(Piece->GetTopGroup(), IsSelected);
3524 			else
3525 			{
3526 				lcArray<lcObject*> Pieces = GetSelectionModePieces(Piece);
3527 				AddToSelection(Pieces, false, false);
3528 			}
3529 		}
3530 	}
3531 	else
3532 	{
3533 		if (FocusObject)
3534 			FocusObject->SetFocused(FocusObject->GetFocusSection(), false);
3535 	}
3536 
3537 	gMainWindow->UpdateSelectedObjects(true);
3538 	UpdateAllViews();
3539 }
3540 
ClearSelectionAndSetFocus(lcObject * Object,quint32 Section,bool EnableSelectionMode)3541 void lcModel::ClearSelectionAndSetFocus(lcObject* Object, quint32 Section, bool EnableSelectionMode)
3542 {
3543 	ClearSelection(false);
3544 
3545 	if (Object)
3546 	{
3547 		Object->SetFocused(Section, true);
3548 
3549 		if (Object->IsPiece())
3550 		{
3551 			SelectGroup(((lcPiece*)Object)->GetTopGroup(), true);
3552 
3553 			if (EnableSelectionMode)
3554 			{
3555 				lcArray<lcObject*> Pieces = GetSelectionModePieces((lcPiece*)Object);
3556 				AddToSelection(Pieces, false, false);
3557 			}
3558 		}
3559 	}
3560 
3561 	gMainWindow->UpdateSelectedObjects(true);
3562 	UpdateAllViews();
3563 }
3564 
ClearSelectionAndSetFocus(const lcObjectSection & ObjectSection,bool EnableSelectionMode)3565 void lcModel::ClearSelectionAndSetFocus(const lcObjectSection& ObjectSection, bool EnableSelectionMode)
3566 {
3567 	ClearSelectionAndSetFocus(ObjectSection.Object, ObjectSection.Section, EnableSelectionMode);
3568 }
3569 
SetSelectionAndFocus(const lcArray<lcObject * > & Selection,lcObject * Focus,quint32 Section,bool EnableSelectionMode)3570 void lcModel::SetSelectionAndFocus(const lcArray<lcObject*>& Selection, lcObject* Focus, quint32 Section, bool EnableSelectionMode)
3571 {
3572 	ClearSelection(false);
3573 
3574 	if (Focus)
3575 	{
3576 		Focus->SetFocused(Section, true);
3577 
3578 		if (Focus->IsPiece())
3579 		{
3580 			SelectGroup(((lcPiece*)Focus)->GetTopGroup(), true);
3581 
3582 			if (EnableSelectionMode)
3583 			{
3584 				lcArray<lcObject*> Pieces = GetSelectionModePieces((lcPiece*)Focus);
3585 				AddToSelection(Pieces, false, false);
3586 			}
3587 		}
3588 	}
3589 
3590 	AddToSelection(Selection, EnableSelectionMode, true);
3591 }
3592 
AddToSelection(const lcArray<lcObject * > & Objects,bool EnableSelectionMode,bool UpdateInterface)3593 void lcModel::AddToSelection(const lcArray<lcObject*>& Objects, bool EnableSelectionMode, bool UpdateInterface)
3594 {
3595 	for (lcObject* Object : Objects)
3596 	{
3597 		bool WasSelected = Object->IsSelected();
3598 		Object->SetSelected(true);
3599 
3600 		if (Object->IsPiece())
3601 		{
3602 			if (!WasSelected)
3603 				SelectGroup(((lcPiece*)Object)->GetTopGroup(), true);
3604 
3605 			if (EnableSelectionMode)
3606 			{
3607 				lcArray<lcObject*> Pieces = GetSelectionModePieces((lcPiece*)Object);
3608 				AddToSelection(Pieces, false, false);
3609 			}
3610 		}
3611 	}
3612 
3613 	if (UpdateInterface)
3614 	{
3615 		gMainWindow->UpdateSelectedObjects(true);
3616 		UpdateAllViews();
3617 	}
3618 }
3619 
RemoveFromSelection(const lcArray<lcObject * > & Objects)3620 void lcModel::RemoveFromSelection(const lcArray<lcObject*>& Objects)
3621 {
3622 	for (lcObject* SelectedObject : Objects)
3623 	{
3624 		bool WasSelected = SelectedObject->IsSelected();
3625 		SelectedObject->SetSelected(false);
3626 
3627 		if (WasSelected && SelectedObject->IsPiece())
3628 		{
3629 			lcPiece* Piece = (lcPiece*)SelectedObject;
3630 
3631 			if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
3632 				SelectGroup(Piece->GetTopGroup(), false);
3633 			else
3634 			{
3635 				lcArray<lcObject*> Pieces = GetSelectionModePieces(Piece);
3636 
3637 				for (lcObject* Object : Pieces)
3638 				{
3639 					if (Object->IsSelected())
3640 					{
3641 						Object->SetSelected(false);
3642 						SelectGroup(((lcPiece*)Object)->GetTopGroup(), false);
3643 					}
3644 				}
3645 			}
3646 		}
3647 	}
3648 
3649 	gMainWindow->UpdateSelectedObjects(true);
3650 	UpdateAllViews();
3651 }
3652 
RemoveFromSelection(const lcObjectSection & ObjectSection)3653 void lcModel::RemoveFromSelection(const lcObjectSection& ObjectSection)
3654 {
3655 	lcObject* SelectedObject = ObjectSection.Object;
3656 
3657 	if (!SelectedObject)
3658 		return;
3659 
3660 	bool WasSelected = SelectedObject->IsSelected();
3661 
3662 	if (SelectedObject->IsFocused(ObjectSection.Section))
3663 		SelectedObject->SetSelected(ObjectSection.Section, false);
3664 	else
3665 		SelectedObject->SetSelected(false);
3666 
3667 
3668 	if (SelectedObject->IsPiece() && WasSelected)
3669 	{
3670 		lcPiece* Piece = (lcPiece*)SelectedObject;
3671 
3672 		if (gMainWindow->GetSelectionMode() == lcSelectionMode::Single)
3673 			SelectGroup(Piece->GetTopGroup(), false);
3674 		else
3675 		{
3676 			lcArray<lcObject*> Pieces = GetSelectionModePieces(Piece);
3677 
3678 			for (lcObject* Object : Pieces)
3679 			{
3680 				if (Object->IsSelected())
3681 				{
3682 					Object->SetSelected(false);
3683 					SelectGroup(((lcPiece*)Object)->GetTopGroup(), false);
3684 				}
3685 			}
3686 		}
3687 	}
3688 
3689 	gMainWindow->UpdateSelectedObjects(true);
3690 	UpdateAllViews();
3691 }
3692 
SelectAllPieces()3693 void lcModel::SelectAllPieces()
3694 {
3695 	for (lcPiece* Piece : mPieces)
3696 		if (Piece->IsVisible(mCurrentStep))
3697 			Piece->SetSelected(true);
3698 
3699 	if (!mIsPreview)
3700 		gMainWindow->UpdateSelectedObjects(true);
3701 	UpdateAllViews();
3702 }
3703 
InvertSelection()3704 void lcModel::InvertSelection()
3705 {
3706 	for (lcPiece* Piece : mPieces)
3707 		if (Piece->IsVisible(mCurrentStep))
3708 			Piece->SetSelected(!Piece->IsSelected());
3709 
3710 	gMainWindow->UpdateSelectedObjects(true);
3711 	UpdateAllViews();
3712 }
3713 
HideSelectedPieces()3714 void lcModel::HideSelectedPieces()
3715 {
3716 	bool Modified = false;
3717 
3718 	for (lcPiece* Piece : mPieces)
3719 	{
3720 		if (Piece->IsSelected())
3721 		{
3722 			Piece->SetHidden(true);
3723 			Piece->SetSelected(false);
3724 			Modified = true;
3725 		}
3726 	}
3727 
3728 	if (!Modified)
3729 		return;
3730 
3731 	gMainWindow->UpdateTimeline(false, true);
3732 	gMainWindow->UpdateSelectedObjects(true);
3733 	UpdateAllViews();
3734 
3735 	SaveCheckpoint(tr("Hide"));
3736 }
3737 
HideUnselectedPieces()3738 void lcModel::HideUnselectedPieces()
3739 {
3740 	bool Modified = false;
3741 
3742 	for (lcPiece* Piece : mPieces)
3743 	{
3744 		if (!Piece->IsSelected())
3745 		{
3746 			Piece->SetHidden(true);
3747 			Modified = true;
3748 		}
3749 	}
3750 
3751 	if (!Modified)
3752 		return;
3753 
3754 	gMainWindow->UpdateTimeline(false, true);
3755 	gMainWindow->UpdateSelectedObjects(true);
3756 	UpdateAllViews();
3757 
3758 	SaveCheckpoint(tr("Hide"));
3759 }
3760 
UnhideSelectedPieces()3761 void lcModel::UnhideSelectedPieces()
3762 {
3763 	bool Modified = false;
3764 
3765 	for (lcPiece* Piece : mPieces)
3766 	{
3767 		if (Piece->IsSelected() && Piece->IsHidden())
3768 		{
3769 			Piece->SetHidden(false);
3770 			Modified = true;
3771 		}
3772 	}
3773 
3774 	if (!Modified)
3775 		return;
3776 
3777 	gMainWindow->UpdateTimeline(false, true);
3778 	gMainWindow->UpdateSelectedObjects(true);
3779 	UpdateAllViews();
3780 
3781 	SaveCheckpoint(tr("Unhide"));
3782 }
3783 
UnhideAllPieces()3784 void lcModel::UnhideAllPieces()
3785 {
3786 	bool Modified = false;
3787 
3788 	for (lcPiece* Piece : mPieces)
3789 	{
3790 		if (Piece->IsHidden())
3791 		{
3792 			Piece->SetHidden(false);
3793 			Modified = true;
3794 		}
3795 	}
3796 
3797 	if (!Modified)
3798 		return;
3799 
3800 	gMainWindow->UpdateTimeline(false, true);
3801 	gMainWindow->UpdateSelectedObjects(true);
3802 	UpdateAllViews();
3803 
3804 	SaveCheckpoint(tr("Unhide"));
3805 }
3806 
FindReplacePiece(bool SearchForward,bool FindAll)3807 void lcModel::FindReplacePiece(bool SearchForward, bool FindAll)
3808 {
3809 	if (mPieces.IsEmpty())
3810 		return;
3811 
3812 	auto PieceMatches = [](const lcPiece* Piece)
3813 	{
3814 		const lcFindReplaceParams& Params = lcView::GetFindReplaceParams();
3815 
3816 		if (Params.FindInfo && Params.FindInfo != Piece->mPieceInfo)
3817 			return false;
3818 
3819 		if (!Params.FindString.isEmpty() && !strcasestr(Piece->mPieceInfo->m_strDescription, Params.FindString.toLatin1()))
3820 			return false;
3821 
3822 		return (lcGetColorCode(Params.FindColorIndex) == LC_COLOR_NOCOLOR) || (Piece->GetColorIndex() == Params.FindColorIndex);
3823 	};
3824 
3825 	const lcFindReplaceParams& Params = lcView::GetFindReplaceParams();
3826 	int StartIdx = mPieces.GetSize() - 1;
3827 
3828 	for (int PieceIdx = 0; PieceIdx < mPieces.GetSize(); PieceIdx++)
3829 	{
3830 		lcPiece* Piece = mPieces[PieceIdx];
3831 
3832 		if (Piece->IsFocused() && Piece->IsVisible(mCurrentStep))
3833 		{
3834 			if (PieceMatches(Piece))
3835 			{
3836 				const bool ReplaceColor = lcGetColorCode(Params.FindColorIndex) != LC_COLOR_NOCOLOR;
3837 
3838 				if (ReplaceColor)
3839 					Piece->SetColorIndex(Params.ReplaceColorIndex);
3840 
3841 				if (Params.ReplacePieceInfo)
3842 					Piece->SetPieceInfo(Params.ReplacePieceInfo, QString(), true);
3843 
3844 				if (ReplaceColor || Params.ReplacePieceInfo)
3845 				{
3846 					SaveCheckpoint(tr("Replacing Part"));
3847 					gMainWindow->UpdateSelectedObjects(false);
3848 					UpdateAllViews();
3849 					gMainWindow->UpdateTimeline(false, true);
3850 				}
3851 			}
3852 
3853 			StartIdx = PieceIdx;
3854 			break;
3855 		}
3856 	}
3857 
3858 	int CurrentIdx = StartIdx;
3859 	lcPiece* Focus = nullptr;
3860 	lcArray<lcObject*> Selection;
3861 
3862 	for (;;)
3863 	{
3864 		if (SearchForward)
3865 			CurrentIdx++;
3866 		else
3867 			CurrentIdx--;
3868 
3869 		if (CurrentIdx < 0)
3870 			CurrentIdx = mPieces.GetSize() - 1;
3871 		else if (CurrentIdx >= mPieces.GetSize())
3872 			CurrentIdx = 0;
3873 
3874 		lcPiece* Current = mPieces[CurrentIdx];
3875 
3876 		if (!Current->IsVisible(mCurrentStep))
3877 			continue;
3878 
3879 		if (PieceMatches(Current))
3880 		{
3881 			if (FindAll)
3882 				Selection.Add(Current);
3883 			else
3884 			{
3885 				Focus = Current;
3886 				break;
3887 			}
3888 		}
3889 
3890 		if (CurrentIdx == StartIdx)
3891 			break;
3892 	}
3893 
3894 	if (FindAll)
3895 		SetSelectionAndFocus(Selection, nullptr, 0, false);
3896 	else
3897 		ClearSelectionAndSetFocus(Focus, LC_PIECE_SECTION_POSITION, false);
3898 }
3899 
UndoAction()3900 void lcModel::UndoAction()
3901 {
3902 	if (mUndoHistory.size() < 2)
3903 		return;
3904 
3905 	lcModelHistoryEntry* Undo = mUndoHistory.front();
3906 	mUndoHistory.erase(mUndoHistory.begin());
3907 	mRedoHistory.insert(mRedoHistory.begin(), Undo);
3908 
3909 	LoadCheckPoint(mUndoHistory[0]);
3910 
3911 	gMainWindow->UpdateModified(IsModified());
3912 	gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
3913 }
3914 
RedoAction()3915 void lcModel::RedoAction()
3916 {
3917 	if (mRedoHistory.empty())
3918 		return;
3919 
3920 	lcModelHistoryEntry* Redo = mRedoHistory.front();
3921 	mRedoHistory.erase(mRedoHistory.begin());
3922 	mUndoHistory.insert(mUndoHistory.begin(), Redo);
3923 
3924 	LoadCheckPoint(Redo);
3925 
3926 	gMainWindow->UpdateModified(IsModified());
3927 	gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
3928 }
3929 
BeginMouseTool()3930 void lcModel::BeginMouseTool()
3931 {
3932 	mMouseToolDistance = lcVector3(0.0f, 0.0f, 0.0f);
3933 }
3934 
EndMouseTool(lcTool Tool,bool Accept)3935 void lcModel::EndMouseTool(lcTool Tool, bool Accept)
3936 {
3937 	if (!Accept)
3938 	{
3939 		if (!mUndoHistory.empty())
3940 			LoadCheckPoint(mUndoHistory.front());
3941 		return;
3942 	}
3943 
3944 	switch (Tool)
3945 	{
3946 	case lcTool::Insert:
3947 	case lcTool::Light:
3948 		break;
3949 
3950 	case lcTool::SpotLight:
3951 		SaveCheckpoint(tr("New SpotLight"));
3952 		break;
3953 
3954 	case lcTool::Camera:
3955 		gMainWindow->UpdateCameraMenu();
3956 		SaveCheckpoint(tr("New Camera"));
3957 		break;
3958 
3959 	case lcTool::Select:
3960 		break;
3961 
3962 	case lcTool::Move:
3963 		SaveCheckpoint(tr("Move"));
3964 		break;
3965 
3966 	case lcTool::Rotate:
3967 		SaveCheckpoint(tr("Rotate"));
3968 		break;
3969 
3970 	case lcTool::Eraser:
3971 	case lcTool::Paint:
3972 	case lcTool::ColorPicker:
3973 		break;
3974 
3975 	case lcTool::Zoom:
3976 		if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
3977 			SaveCheckpoint(tr("Zoom"));
3978 		break;
3979 
3980 	case lcTool::Pan:
3981 		if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
3982 			SaveCheckpoint(tr("Pan"));
3983 		break;
3984 
3985 	case lcTool::RotateView:
3986 		if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
3987 			SaveCheckpoint(tr("Orbit"));
3988 		break;
3989 
3990 	case lcTool::Roll:
3991 		if (!gMainWindow->GetActiveView()->GetCamera()->IsSimple())
3992 			SaveCheckpoint(tr("Roll"));
3993 		break;
3994 
3995 	case lcTool::ZoomRegion:
3996 		break;
3997 
3998 	case lcTool::Count:
3999 		break;
4000 	}
4001 }
4002 
InsertPieceToolClicked(const lcMatrix44 & WorldMatrix)4003 void lcModel::InsertPieceToolClicked(const lcMatrix44& WorldMatrix)
4004 {
4005 	lcPiece* Piece = new lcPiece(gMainWindow->GetCurrentPieceInfo());
4006 	Piece->Initialize(WorldMatrix, mCurrentStep);
4007 	Piece->SetColorIndex(gMainWindow->mColorIndex);
4008 	Piece->UpdatePosition(mCurrentStep);
4009 	AddPiece(Piece);
4010 
4011 	gMainWindow->UpdateTimeline(false, false);
4012 	ClearSelectionAndSetFocus(Piece, LC_PIECE_SECTION_POSITION, false);
4013 
4014 	SaveCheckpoint(tr("Insert"));
4015 }
4016 
PointLightToolClicked(const lcVector3 & Position)4017 void lcModel::PointLightToolClicked(const lcVector3& Position)
4018 {
4019 	lcLight* Light = new lcLight(Position[0], Position[1], Position[2]);
4020 	Light->CreateName(mLights);
4021 	mLights.Add(Light);
4022 
4023 	ClearSelectionAndSetFocus(Light, LC_LIGHT_SECTION_POSITION, false);
4024 	SaveCheckpoint(tr("New Light"));
4025 }
4026 
BeginSpotLightTool(const lcVector3 & Position,const lcVector3 & Target)4027 void lcModel::BeginSpotLightTool(const lcVector3& Position, const lcVector3& Target)
4028 {
4029 	lcLight* Light = new lcLight(Position[0], Position[1], Position[2], Target[0], Target[1], Target[2]);
4030 	mLights.Add(Light);
4031 
4032 	mMouseToolDistance = Target;
4033 
4034 	ClearSelectionAndSetFocus(Light, LC_LIGHT_SECTION_TARGET, false);
4035 }
4036 
UpdateSpotLightTool(const lcVector3 & Position)4037 void lcModel::UpdateSpotLightTool(const lcVector3& Position)
4038 {
4039 	lcLight* Light = mLights[mLights.GetSize() - 1];
4040 
4041 	Light->MoveSelected(1, false, Position - mMouseToolDistance);
4042 	Light->UpdatePosition(1);
4043 
4044 	mMouseToolDistance = Position;
4045 
4046 	gMainWindow->UpdateSelectedObjects(false);
4047 	UpdateAllViews();
4048 }
4049 
BeginCameraTool(const lcVector3 & Position,const lcVector3 & Target)4050 void lcModel::BeginCameraTool(const lcVector3& Position, const lcVector3& Target)
4051 {
4052 	lcCamera* Camera = new lcCamera(Position[0], Position[1], Position[2], Target[0], Target[1], Target[2]);
4053 	Camera->CreateName(mCameras);
4054 	mCameras.Add(Camera);
4055 
4056 	mMouseToolDistance = Position;
4057 
4058 	ClearSelectionAndSetFocus(Camera, LC_CAMERA_SECTION_TARGET, false);
4059 }
4060 
UpdateCameraTool(const lcVector3 & Position)4061 void lcModel::UpdateCameraTool(const lcVector3& Position)
4062 {
4063 	lcCamera* Camera = mCameras[mCameras.GetSize() - 1];
4064 
4065 	Camera->MoveSelected(1, false, Position - mMouseToolDistance);
4066 	Camera->UpdatePosition(1);
4067 
4068 	mMouseToolDistance = Position;
4069 
4070 	gMainWindow->UpdateSelectedObjects(false);
4071 	UpdateAllViews();
4072 }
4073 
UpdateMoveTool(const lcVector3 & Distance,bool AllowRelative,bool AlternateButtonDrag)4074 void lcModel::UpdateMoveTool(const lcVector3& Distance, bool AllowRelative, bool AlternateButtonDrag)
4075 {
4076 	lcVector3 PieceDistance = SnapPosition(Distance) - SnapPosition(mMouseToolDistance);
4077 	lcVector3 ObjectDistance = Distance - mMouseToolDistance;
4078 
4079 	MoveSelectedObjects(PieceDistance, ObjectDistance, AllowRelative, AlternateButtonDrag, true, false);
4080 	mMouseToolDistance = Distance;
4081 
4082 	gMainWindow->UpdateSelectedObjects(false);
4083 	UpdateAllViews();
4084 }
4085 
UpdateRotateTool(const lcVector3 & Angles,bool AlternateButtonDrag)4086 void lcModel::UpdateRotateTool(const lcVector3& Angles, bool AlternateButtonDrag)
4087 {
4088 	lcVector3 Delta = SnapRotation(Angles) - SnapRotation(mMouseToolDistance);
4089 	RotateSelectedPieces(Delta, true, AlternateButtonDrag, false, false);
4090 	mMouseToolDistance = Angles;
4091 
4092 	gMainWindow->UpdateSelectedObjects(false);
4093 	UpdateAllViews();
4094 }
4095 
UpdateScaleTool(const float Scale)4096 void lcModel::UpdateScaleTool(const float Scale)
4097 {
4098 	ScaleSelectedPieces(Scale, true, false);
4099 
4100 	gMainWindow->UpdateSelectedObjects(false);
4101 	UpdateAllViews();
4102 }
4103 
EraserToolClicked(lcObject * Object)4104 void lcModel::EraserToolClicked(lcObject* Object)
4105 {
4106 	if (!Object)
4107 		return;
4108 
4109 	switch (Object->GetType())
4110 	{
4111 	case lcObjectType::Piece:
4112 		mPieces.Remove((lcPiece*)Object);
4113 		RemoveEmptyGroups();
4114 		break;
4115 
4116 	case lcObjectType::Camera:
4117 		{
4118 			std::vector<lcView*> Views = lcView::GetModelViews(this);
4119 
4120 			for (lcView* View : Views)
4121 			{
4122 				lcCamera* Camera = View->GetCamera();
4123 
4124 				if (Camera == Object)
4125 					View->SetCamera(Camera, true);
4126 			}
4127 
4128 			mCameras.Remove((lcCamera*)Object);
4129 
4130 			gMainWindow->UpdateCameraMenu();
4131 		}
4132 		break;
4133 
4134 	case lcObjectType::Light:
4135 		mLights.Remove((lcLight*)Object);
4136 		break;
4137 	}
4138 
4139 	delete Object;
4140 	gMainWindow->UpdateTimeline(false, false);
4141 	gMainWindow->UpdateSelectedObjects(true);
4142 	UpdateAllViews();
4143 	SaveCheckpoint(tr("Deleting"));
4144 }
4145 
PaintToolClicked(lcObject * Object)4146 void lcModel::PaintToolClicked(lcObject* Object)
4147 {
4148 	if (!Object || !Object->IsPiece())
4149 		return;
4150 
4151 	lcPiece* Piece = (lcPiece*)Object;
4152 
4153 	if (Piece->GetColorIndex() != gMainWindow->mColorIndex)
4154 	{
4155 		Piece->SetColorIndex(gMainWindow->mColorIndex);
4156 
4157 		SaveCheckpoint(tr("Painting"));
4158 		gMainWindow->UpdateSelectedObjects(false);
4159 		UpdateAllViews();
4160 		gMainWindow->UpdateTimeline(false, true);
4161 	}
4162 }
4163 
ColorPickerToolClicked(lcObject * Object)4164 void lcModel::ColorPickerToolClicked(lcObject* Object)
4165 {
4166 	if (!Object || !Object->IsPiece())
4167 		return;
4168 
4169 	lcPiece* Piece = (lcPiece*)Object;
4170 
4171 	gMainWindow->SetColorIndex(Piece->GetColorIndex());
4172 }
4173 
UpdateZoomTool(lcCamera * Camera,float Mouse)4174 void lcModel::UpdateZoomTool(lcCamera* Camera, float Mouse)
4175 {
4176 	Camera->Zoom(Mouse - mMouseToolDistance.x, mCurrentStep, gMainWindow->GetAddKeys());
4177 	mMouseToolDistance.x = Mouse;
4178 
4179 	UpdateAllViews();
4180 }
4181 
UpdatePanTool(lcCamera * Camera,const lcVector3 & Distance)4182 void lcModel::UpdatePanTool(lcCamera* Camera, const lcVector3& Distance)
4183 {
4184 	Camera->Pan(Distance - mMouseToolDistance, mCurrentStep, gMainWindow->GetAddKeys());
4185 	mMouseToolDistance = Distance;
4186 
4187 	UpdateAllViews();
4188 }
4189 
UpdateOrbitTool(lcCamera * Camera,float MouseX,float MouseY)4190 void lcModel::UpdateOrbitTool(lcCamera* Camera, float MouseX, float MouseY)
4191 {
4192 	lcVector3 Center;
4193 	GetSelectionCenter(Center);
4194 
4195 	Camera->Orbit(MouseX - mMouseToolDistance.x, MouseY - mMouseToolDistance.y, Center, mCurrentStep, gMainWindow->GetAddKeys());
4196 	mMouseToolDistance.x = MouseX;
4197 	mMouseToolDistance.y = MouseY;
4198 
4199 	UpdateAllViews();
4200 }
4201 
UpdateRollTool(lcCamera * Camera,float Mouse)4202 void lcModel::UpdateRollTool(lcCamera* Camera, float Mouse)
4203 {
4204 	Camera->Roll(Mouse - mMouseToolDistance.x, mCurrentStep, gMainWindow->GetAddKeys());
4205 	mMouseToolDistance.x = Mouse;
4206 
4207 	UpdateAllViews();
4208 }
4209 
ZoomRegionToolClicked(lcCamera * Camera,float AspectRatio,const lcVector3 & Position,const lcVector3 & TargetPosition,const lcVector3 * Corners)4210 void lcModel::ZoomRegionToolClicked(lcCamera* Camera, float AspectRatio, const lcVector3& Position, const lcVector3& TargetPosition, const lcVector3* Corners)
4211 {
4212 	Camera->ZoomRegion(AspectRatio, Position, TargetPosition, Corners, mCurrentStep, gMainWindow->GetAddKeys());
4213 
4214 	gMainWindow->UpdateSelectedObjects(false);
4215 	UpdateAllViews();
4216 
4217 	if (!Camera->IsSimple())
4218 		SaveCheckpoint(tr("Zoom"));
4219 }
4220 
LookAt(lcCamera * Camera)4221 void lcModel::LookAt(lcCamera* Camera)
4222 {
4223 	lcVector3 Center;
4224 
4225 	if (!GetSelectionCenter(Center))
4226 	{
4227 		lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
4228 
4229 		if (GetVisiblePiecesBoundingBox(Min, Max))
4230 			Center = (Min + Max) / 2.0f;
4231 		else
4232 			Center = lcVector3(0.0f, 0.0f, 0.0f);
4233 	}
4234 
4235 	Camera->Center(Center, mCurrentStep, gMainWindow->GetAddKeys());
4236 
4237 	gMainWindow->UpdateSelectedObjects(false);
4238 	UpdateAllViews();
4239 
4240 	if (!Camera->IsSimple())
4241 		SaveCheckpoint(tr("Look At"));
4242 }
4243 
MoveCamera(lcCamera * Camera,const lcVector3 & Direction)4244 void lcModel::MoveCamera(lcCamera* Camera, const lcVector3& Direction)
4245 {
4246 	Camera->MoveRelative(Direction, mCurrentStep, gMainWindow->GetAddKeys());
4247 	gMainWindow->UpdateSelectedObjects(false);
4248 	UpdateAllViews();
4249 
4250 	if (!Camera->IsSimple())
4251 		SaveCheckpoint(tr("Moving Camera"));
4252 }
4253 
ZoomExtents(lcCamera * Camera,float Aspect)4254 void lcModel::ZoomExtents(lcCamera* Camera, float Aspect)
4255 {
4256 	std::vector<lcVector3> Points = GetPiecesBoundingBoxPoints();
4257 
4258 	if (Points.empty())
4259 		return;
4260 
4261 	lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX), Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
4262 
4263 	for (const lcVector3& Point : Points)
4264 	{
4265 		Min = lcMin(Point, Min);
4266 		Max = lcMax(Point, Max);
4267 	}
4268 
4269 	lcVector3 Center = (Min + Max) / 2.0f;
4270 
4271 	Camera->ZoomExtents(Aspect, Center, Points, mCurrentStep, gMainWindow ? gMainWindow->GetAddKeys() : false);
4272 
4273 	if (!mIsPreview && gMainWindow)
4274 		gMainWindow->UpdateSelectedObjects(false);
4275 	UpdateAllViews();
4276 
4277 	if (!Camera->IsSimple())
4278 		SaveCheckpoint(tr("Zoom"));
4279 }
4280 
Zoom(lcCamera * Camera,float Amount)4281 void lcModel::Zoom(lcCamera* Camera, float Amount)
4282 {
4283 	Camera->Zoom(Amount, mCurrentStep, gMainWindow->GetAddKeys());
4284 
4285 	if (!mIsPreview)
4286 		gMainWindow->UpdateSelectedObjects(false);
4287 	UpdateAllViews();
4288 
4289 	if (!Camera->IsSimple())
4290 		SaveCheckpoint(tr("Zoom"));
4291 }
4292 
ShowPropertiesDialog()4293 void lcModel::ShowPropertiesDialog()
4294 {
4295 	lcPropertiesDialogOptions Options;
4296 
4297 	Options.Properties = mProperties;
4298 	Options.BoundingBox = GetAllPiecesBoundingBox();
4299 
4300 	GetPartsList(gDefaultColor, true, false, Options.PartsList);
4301 
4302 	lcQPropertiesDialog Dialog(gMainWindow, &Options);
4303 	if (Dialog.exec() != QDialog::Accepted)
4304 		return;
4305 
4306 	if (mProperties == Options.Properties)
4307 		return;
4308 
4309 	mProperties = Options.Properties;
4310 
4311 	gMainWindow->GetPreviewWidget()->UpdatePreview();
4312 
4313 	SaveCheckpoint(tr("Changing Properties"));
4314 }
4315 
ShowSelectByNameDialog()4316 void lcModel::ShowSelectByNameDialog()
4317 {
4318 	if (mPieces.IsEmpty() && mCameras.IsEmpty() && mLights.IsEmpty())
4319 	{
4320 		QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Nothing to select."));
4321 		return;
4322 	}
4323 
4324 	lcQSelectDialog Dialog(gMainWindow, this);
4325 
4326 	if (Dialog.exec() != QDialog::Accepted)
4327 		return;
4328 
4329 	SetSelectionAndFocus(Dialog.mObjects, nullptr, 0, false);
4330 }
4331 
ShowArrayDialog()4332 void lcModel::ShowArrayDialog()
4333 {
4334 	lcVector3 Center;
4335 
4336 	if (!GetPieceFocusOrSelectionCenter(Center))
4337 	{
4338 		QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("No pieces selected."));
4339 		return;
4340 	}
4341 
4342 	lcQArrayDialog Dialog(gMainWindow);
4343 
4344 	if (Dialog.exec() != QDialog::Accepted)
4345 		return;
4346 
4347 	if (Dialog.mCounts[0] * Dialog.mCounts[1] * Dialog.mCounts[2] < 2)
4348 	{
4349 		QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Array only has 1 element or less, no pieces added."));
4350 		return;
4351 	}
4352 
4353 	lcArray<lcObject*> NewPieces;
4354 
4355 	for (int Step1 = 0; Step1 < Dialog.mCounts[0]; Step1++)
4356 	{
4357 		for (int Step2 = 0; Step2 < Dialog.mCounts[1]; Step2++)
4358 		{
4359 			for (int Step3 = (Step1 == 0 && Step2 == 0) ? 1 : 0; Step3 < Dialog.mCounts[2]; Step3++)
4360 			{
4361 				lcMatrix44 ModelWorld;
4362 				lcVector3 Position;
4363 
4364 				lcVector3 RotationAngles = Dialog.mRotations[0] * Step1 + Dialog.mRotations[1] * Step2 + Dialog.mRotations[2] * Step3;
4365 				lcVector3 Offset = Dialog.mOffsets[0] * Step1 + Dialog.mOffsets[1] * Step2 + Dialog.mOffsets[2] * Step3;
4366 
4367 				for (lcPiece* Piece : mPieces)
4368 				{
4369 					if (!Piece->IsSelected())
4370 						continue;
4371 
4372 					ModelWorld = Piece->mModelWorld;
4373 
4374 					ModelWorld.r[3] -= lcVector4(Center, 0.0f);
4375 					ModelWorld = lcMul(ModelWorld, lcMatrix44RotationX(RotationAngles[0] * LC_DTOR));
4376 					ModelWorld = lcMul(ModelWorld, lcMatrix44RotationY(RotationAngles[1] * LC_DTOR));
4377 					ModelWorld = lcMul(ModelWorld, lcMatrix44RotationZ(RotationAngles[2] * LC_DTOR));
4378 					ModelWorld.r[3] += lcVector4(Center, 0.0f);
4379 
4380 					Position = lcVector3(ModelWorld.r[3].x, ModelWorld.r[3].y, ModelWorld.r[3].z);
4381 					ModelWorld.SetTranslation(Position + Offset);
4382 
4383 					lcPiece* NewPiece = new lcPiece(nullptr);
4384 					NewPiece->SetPieceInfo(Piece->mPieceInfo, Piece->GetID(), true);
4385 					NewPiece->Initialize(ModelWorld, mCurrentStep);
4386 					NewPiece->SetColorIndex(Piece->GetColorIndex());
4387 
4388 					NewPieces.Add(NewPiece);
4389 				}
4390 			}
4391 		}
4392 	}
4393 
4394 	for (int PieceIdx = 0; PieceIdx < NewPieces.GetSize(); PieceIdx++)
4395 	{
4396 		lcPiece* Piece = (lcPiece*)NewPieces[PieceIdx];
4397 		Piece->UpdatePosition(mCurrentStep);
4398 		AddPiece(Piece);
4399 	}
4400 
4401 	AddToSelection(NewPieces, false, true);
4402 	gMainWindow->UpdateTimeline(false, false);
4403 	SaveCheckpoint(tr("Array"));
4404 }
4405 
ShowMinifigDialog()4406 void lcModel::ShowMinifigDialog()
4407 {
4408 	lcMinifigDialog Dialog(gMainWindow);
4409 
4410 	if (Dialog.exec() != QDialog::Accepted)
4411 		return;
4412 
4413 	gMainWindow->GetActiveView()->MakeCurrent();
4414 
4415 	lcGroup* Group = AddGroup(tr("Minifig #"), nullptr);
4416 	lcArray<lcObject*> Pieces(LC_MFW_NUMITEMS);
4417 	lcMinifig& Minifig = Dialog.mMinifigWizard->mMinifig;
4418 
4419 	for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
4420 	{
4421 		if (Minifig.Parts[PartIdx] == nullptr)
4422 			continue;
4423 
4424 		lcPiece* Piece = new lcPiece(Minifig.Parts[PartIdx]);
4425 
4426 		Piece->Initialize(Minifig.Matrices[PartIdx], mCurrentStep);
4427 		Piece->SetColorIndex(Minifig.Colors[PartIdx]);
4428 		Piece->SetGroup(Group);
4429 		AddPiece(Piece);
4430 		Piece->UpdatePosition(mCurrentStep);
4431 
4432 		Pieces.Add(Piece);
4433 	}
4434 
4435 	SetSelectionAndFocus(Pieces, nullptr, 0, false);
4436 	gMainWindow->UpdateTimeline(false, false);
4437 	SaveCheckpoint(tr("Minifig"));
4438 }
4439 
SetMinifig(const lcMinifig & Minifig)4440 void lcModel::SetMinifig(const lcMinifig& Minifig)
4441 {
4442 	DeleteModel();
4443 
4444 	lcArray<lcObject*> Pieces(LC_MFW_NUMITEMS);
4445 
4446 	for (int PartIdx = 0; PartIdx < LC_MFW_NUMITEMS; PartIdx++)
4447 	{
4448 		if (!Minifig.Parts[PartIdx])
4449 			continue;
4450 
4451 		lcPiece* Piece = new lcPiece(Minifig.Parts[PartIdx]);
4452 
4453 		Piece->Initialize(Minifig.Matrices[PartIdx], 1);
4454 		Piece->SetColorIndex(Minifig.Colors[PartIdx]);
4455 		AddPiece(Piece);
4456 		Piece->UpdatePosition(1);
4457 
4458 		Pieces.Add(Piece);
4459 	}
4460 
4461 	SetSelectionAndFocus(Pieces, nullptr, 0, false);
4462 }
4463 
SetPreviewPieceInfo(PieceInfo * Info,int ColorIndex)4464 void lcModel::SetPreviewPieceInfo(PieceInfo* Info, int ColorIndex)
4465 {
4466 	DeleteModel();
4467 
4468 	lcPiece* Piece = new lcPiece(Info);
4469 
4470 	Piece->Initialize(lcMatrix44Identity(), 1);
4471 	Piece->SetColorIndex(ColorIndex);
4472 	AddPiece(Piece);
4473 	Piece->UpdatePosition(1);
4474 
4475 	SaveCheckpoint(QString());
4476 }
4477 
UpdateInterface()4478 void lcModel::UpdateInterface()
4479 {
4480 	if (!gMainWindow)
4481 		return;
4482 
4483 	gMainWindow->UpdateTimeline(true, false);
4484 	gMainWindow->UpdateUndoRedo(mUndoHistory.size() > 1 ? mUndoHistory[0]->Description : nullptr, !mRedoHistory.empty() ? mRedoHistory[0]->Description : nullptr);
4485 	gMainWindow->UpdatePaste(!gApplication->mClipboard.isEmpty());
4486 	gMainWindow->UpdateCategories();
4487 	gMainWindow->UpdateTitle();
4488 	gMainWindow->SetTool(gMainWindow->GetTool());
4489 
4490 	gMainWindow->UpdateSelectedObjects(true);
4491 	gMainWindow->SetTransformType(gMainWindow->GetTransformType());
4492 	gMainWindow->UpdateLockSnap();
4493 	gMainWindow->UpdateSnap();
4494 	gMainWindow->UpdateCameraMenu();
4495 	gMainWindow->UpdateModels();
4496 	gMainWindow->UpdatePerspective();
4497 	gMainWindow->UpdateShadingMode();
4498 	gMainWindow->UpdateCurrentStep();
4499 	gMainWindow->UpdateSelectionMode();
4500 }
4501