Reorganized PLI function.

This commit is contained in:
Leonardo Zide 2020-01-20 15:39:20 -08:00
parent ddf6f9dbba
commit 3b8b22493a
4 changed files with 226 additions and 228 deletions

View file

@ -23,6 +23,9 @@
#include "lc_qpropertiesdialog.h"
#include "lc_qutils.h"
#include "lc_lxf.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtConcurrent>
#endif
void lcModelProperties::LoadDefaults()
{
@ -1401,6 +1404,227 @@ QImage lcModel::GetStepImage(bool Zoom, bool Highlight, int Width, int Height, l
return Image;
}
QImage lcModel::GetPartsListImage(int MaxWidth, lcStep Step) const
{
lcPartsList PartsList;
if (Step == 0)
GetPartsList(gDefaultColor, true, false, PartsList);
else
GetPartsListForStep(Step, gDefaultColor, PartsList);
if (PartsList.empty())
return QImage();
struct lcPartsListImage
{
QImage Thumbnail;
const PieceInfo* Info;
int ColorIndex;
int Count;
QRect Bounds;
QPoint Position;
};
std::vector<lcPartsListImage> Images;
for (const auto& PartIt : PartsList)
{
for (const auto& ColorIt : PartIt.second)
{
Images.push_back(lcPartsListImage());
lcPartsListImage& Image = Images.back();
Image.Info = PartIt.first;
Image.ColorIndex = ColorIt.first;
Image.Count = ColorIt.second;
}
}
auto ImageCompare = [](const lcPartsListImage& Image1, const lcPartsListImage& Image2)
{
if (Image1.ColorIndex != Image2.ColorIndex)
return Image1.ColorIndex < Image2.ColorIndex;
return strcmp(Image1.Info->m_strDescription, Image2.Info->m_strDescription) < 0;
};
std::sort(Images.begin(), Images.end(), ImageCompare);
View* View = gMainWindow->GetActiveView();
View->MakeCurrent();
lcContext* Context = View->mContext;
const int ThumbnailSize = qMin(MaxWidth, 512);
std::pair<lcFramebuffer, lcFramebuffer> RenderFramebuffer = Context->CreateRenderFramebuffer(ThumbnailSize, ThumbnailSize);
if (!RenderFramebuffer.first.IsValid())
{
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
return QImage();
}
Context->BindFramebuffer(RenderFramebuffer.first);
float OrthoSize = 200.0f;
lcMatrix44 ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
lcMatrix44 ViewMatrix = lcMatrix44LookAt(lcVector3(-100.0f, -100.0f, 75.0f), lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 1.0f));
const int Viewport[4] = { 0, 0, ThumbnailSize, ThumbnailSize };
float ExtraPixels = 0.0f;
for (lcPartsListImage& Image : Images)
{
const PieceInfo* Info = Image.Info;
const lcBoundingBox& BoundingBox = Info->GetBoundingBox();
lcVector3 Center = (BoundingBox.Min + BoundingBox.Max) / 2.0f;
lcVector3 Points[8];
lcGetBoxCorners(BoundingBox.Min, BoundingBox.Max, Points);
for (lcVector3& Point : Points)
{
Point = lcProjectPoint(Point, ViewMatrix, ProjectionMatrix, Viewport);
ExtraPixels = qMax(ExtraPixels, -Point.x);
ExtraPixels = qMax(ExtraPixels, Point.x - ThumbnailSize);
ExtraPixels = qMax(ExtraPixels, -Point.y);
ExtraPixels = qMax(ExtraPixels, Point.y - ThumbnailSize);
}
}
if (ExtraPixels)
{
OrthoSize += ExtraPixels * (2.0f * OrthoSize / ThumbnailSize);
ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
}
Context->SetViewport(0, 0, ThumbnailSize, ThumbnailSize);
Context->SetDefaultState();
Context->SetProjectionMatrix(ProjectionMatrix);
for (lcPartsListImage& Image : Images)
{
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
lcScene Scene;
Scene.SetAllowWireframe(false);
Scene.SetAllowLOD(false);
Scene.Begin(ViewMatrix);
Image.Info->AddRenderMeshes(Scene, lcMatrix44Identity(), Image.ColorIndex, lcRenderMeshState::Default, true);
Scene.End();
Scene.Draw(Context);
Image.Thumbnail = Context->GetRenderFramebufferImage(RenderFramebuffer);
}
Context->ClearFramebuffer();
Context->DestroyRenderFramebuffer(RenderFramebuffer);
Context->ClearResources();
auto CalculateImageBounds = [](lcPartsListImage& Image)
{
QImage& Thumbnail = Image.Thumbnail;
int Width = Thumbnail.width();
int Height = Thumbnail.height();
int MinX = Width;
int MinY = Height;
int MaxX = 0;
int MaxY = 0;
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
if (qAlpha(Thumbnail.pixel(x, y)))
{
MinX = qMin(x, MinX);
MinY = qMin(y, MinY);
MaxX = qMax(x, MaxX);
MaxY = qMax(y, MaxY);
}
}
}
Image.Bounds = QRect(QPoint(MinX, MinY), QPoint(MaxX, MaxY));
};
QtConcurrent::blockingMap(Images, CalculateImageBounds);
QImage DummyImage(16, 16, QImage::Format_ARGB32);
QPainter DummyPainter(&DummyImage);
QFont Font("helvetica", 20, QFont::Bold);
DummyPainter.setFont(Font);
QFontMetrics FontMetrics = DummyPainter.fontMetrics();
int Ascent = FontMetrics.ascent();
int CurrentHeight = 0;
int ImageWidth = MaxWidth;
for (lcPartsListImage& Image : Images)
CurrentHeight = qMax(Image.Bounds.height() + Ascent, CurrentHeight);
for (;;)
{
int CurrentWidth = 0;
int CurrentX = 0;
int CurrentY = 0;
int ColumnWidth = 0;
int Spacing = 20;
int NextHeightIncrease = INT_MAX;
for (lcPartsListImage& Image : Images)
{
if (CurrentY + Image.Bounds.height() + Ascent > CurrentHeight)
{
int NeededSpace = Image.Bounds.height() + Ascent - (CurrentHeight - CurrentY);
NextHeightIncrease = qMin(NeededSpace, NextHeightIncrease);
CurrentY = 0;
CurrentX += ColumnWidth + Spacing;
ColumnWidth = 0;
}
Image.Position = QPoint(CurrentX, CurrentY);
CurrentY += Image.Bounds.height() + Ascent + Spacing;
CurrentWidth = qMax(CurrentWidth, CurrentX + Image.Bounds.width());
ColumnWidth = qMax(ColumnWidth, Image.Bounds.width());
}
if (CurrentWidth < MaxWidth)
{
ImageWidth = CurrentWidth;
break;
}
CurrentHeight += NextHeightIncrease;
}
QImage PainterImage(ImageWidth + 40, CurrentHeight + 40, QImage::Format_ARGB32);
PainterImage.fill(QColor(255, 255, 255, 0));
QPainter Painter(&PainterImage);
Painter.setFont(Font);
for (lcPartsListImage& Image : Images)
{
QPoint Position = Image.Position + QPoint(20, 20);
Painter.drawImage(Position, Image.Thumbnail, Image.Bounds);
Painter.drawText(QPoint(Position.x(), Position.y() + Image.Bounds.height() + Ascent), QString::number(Image.Count) + 'x');
}
Painter.end();
return PainterImage;
}
void lcModel::SaveStepImages(const QString& BaseName, bool AddStepSuffix, bool Zoom, bool Highlight, int Width, int Height, lcStep Start, lcStep End)
{
for (lcStep Step = Start; Step <= End; Step++)

View file

@ -229,6 +229,7 @@ public:
void AddSubModelRenderMeshes(lcScene& Scene, const lcMatrix44& WorldMatrix, int DefaultColorIndex, lcRenderMeshState RenderMeshState, bool ParentActive) const;
void DrawBackground(lcGLWidget* Widget);
QImage GetStepImage(bool Zoom, bool Highlight, int Width, int Height, lcStep Step);
QImage GetPartsListImage(int MaxWidth, lcStep Step) const;
void SaveStepImages(const QString& BaseName, bool AddStepSuffix, bool Zoom, bool Highlight, int Width, int Height, lcStep Start, lcStep End);
std::vector<std::pair<lcModel*, lcStep>> GetPageLayouts(std::vector<const lcModel*>& AddedModels);

View file

@ -15,9 +15,6 @@
#include "lc_zipfile.h"
#include "lc_qimagedialog.h"
#include "lc_qmodellistdialog.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtConcurrent>
#endif
lcHTMLExportOptions::lcHTMLExportOptions(const Project* Project)
{
@ -1492,229 +1489,6 @@ std::vector<std::pair<lcModel*, lcStep>> Project::GetPageLayouts() const
return std::vector<std::pair<lcModel*, lcStep>>();
}
QImage Project::CreatePartsListImage(lcModel* Model, lcStep Step)
{
lcPartsList PartsList;
if (Step == 0)
Model->GetPartsList(gDefaultColor, true, false, PartsList);
else
Model->GetPartsListForStep(Step, gDefaultColor, PartsList);
if (PartsList.empty())
return QImage();
struct lcPartsListImage
{
QImage Thumbnail;
const PieceInfo* Info;
int ColorIndex;
int Count;
QRect Bounds;
QPoint Position;
};
std::vector<lcPartsListImage> Images;
for (const auto& PartIt : PartsList)
{
for (const auto& ColorIt : PartIt.second)
{
Images.push_back(lcPartsListImage());
lcPartsListImage& Image = Images.back();
Image.Info = PartIt.first;
Image.ColorIndex = ColorIt.first;
Image.Count = ColorIt.second;
}
}
auto ImageCompare = [](const lcPartsListImage& Image1, const lcPartsListImage& Image2)
{
if (Image1.ColorIndex != Image2.ColorIndex)
return Image1.ColorIndex < Image2.ColorIndex;
return strcmp(Image1.Info->m_strDescription, Image2.Info->m_strDescription) < 0;
};
std::sort(Images.begin(), Images.end(), ImageCompare);
View* View = gMainWindow->GetActiveView();
View->MakeCurrent();
lcContext* Context = View->mContext;
const int ThumbnailSize = 512;
std::pair<lcFramebuffer, lcFramebuffer> RenderFramebuffer = Context->CreateRenderFramebuffer(ThumbnailSize, ThumbnailSize);
if (!RenderFramebuffer.first.IsValid())
{
QMessageBox::warning(gMainWindow, tr("LeoCAD"), tr("Error creating images."));
return QImage();
}
Context->BindFramebuffer(RenderFramebuffer.first);
float OrthoSize = 200.0f;
lcMatrix44 ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
lcMatrix44 ViewMatrix = lcMatrix44LookAt(lcVector3(-100.0f, -100.0f, 75.0f), lcVector3(0.0f, 0.0f, 0.0f), lcVector3(0.0f, 0.0f, 1.0f));
const int Viewport[4] = { 0, 0, ThumbnailSize, ThumbnailSize };
float ExtraPixels = 0.0f;
for (lcPartsListImage& Image : Images)
{
const PieceInfo* Info = Image.Info;
const lcBoundingBox& BoundingBox = Info->GetBoundingBox();
lcVector3 Center = (BoundingBox.Min + BoundingBox.Max) / 2.0f;
lcVector3 Points[8];
lcGetBoxCorners(BoundingBox.Min, BoundingBox.Max, Points);
for (lcVector3& Point : Points)
{
Point = lcProjectPoint(Point, ViewMatrix, ProjectionMatrix, Viewport);
ExtraPixels = qMax(ExtraPixels, -Point.x);
ExtraPixels = qMax(ExtraPixels, Point.x - ThumbnailSize);
ExtraPixels = qMax(ExtraPixels, -Point.y);
ExtraPixels = qMax(ExtraPixels, Point.y - ThumbnailSize);
}
}
if (ExtraPixels)
{
OrthoSize += ExtraPixels * (2.0f * OrthoSize / ThumbnailSize);
ProjectionMatrix = lcMatrix44Ortho(-OrthoSize, OrthoSize, -OrthoSize, OrthoSize, -5000.0f, 5000.0f);
}
Context->SetViewport(0, 0, ThumbnailSize, ThumbnailSize);
Context->SetDefaultState();
Context->SetProjectionMatrix(ProjectionMatrix);
for (lcPartsListImage& Image : Images)
{
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
lcScene Scene;
Scene.SetAllowWireframe(false);
Scene.SetAllowLOD(false);
Scene.Begin(ViewMatrix);
Image.Info->AddRenderMeshes(Scene, lcMatrix44Identity(), Image.ColorIndex, lcRenderMeshState::Default, true);
Scene.End();
Scene.Draw(Context);
Image.Thumbnail = Context->GetRenderFramebufferImage(RenderFramebuffer);
}
Context->ClearFramebuffer();
Context->DestroyRenderFramebuffer(RenderFramebuffer);
Context->ClearResources();
auto CalculateImageBounds = [](lcPartsListImage& Image)
{
QImage& Thumbnail = Image.Thumbnail;
int Width = Thumbnail.width();
int Height = Thumbnail.height();
int MinX = Width;
int MinY = Height;
int MaxX = 0;
int MaxY = 0;
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
if (qAlpha(Thumbnail.pixel(x, y)))
{
MinX = qMin(x, MinX);
MinY = qMin(y, MinY);
MaxX = qMax(x, MaxX);
MaxY = qMax(y, MaxY);
}
}
}
Image.Bounds = QRect(QPoint(MinX, MinY), QPoint(MaxX, MaxY));
};
QtConcurrent::blockingMap(Images, CalculateImageBounds);
QImage DummyImage(16, 16, QImage::Format_ARGB32);
QPainter DummyPainter(&DummyImage);
QFont Font("helvetica", 20, QFont::Bold);
DummyPainter.setFont(Font);
QFontMetrics FontMetrics = DummyPainter.fontMetrics();
int Ascent = FontMetrics.ascent();
int CurrentHeight = 0;
int MaxWidth = 1024;
for (lcPartsListImage& Image : Images)
{
CurrentHeight = qMax(Image.Bounds.height() + Ascent, CurrentHeight);
MaxWidth = qMax(MaxWidth, Image.Bounds.width());
}
for (;;)
{
int CurrentWidth = 0;
int CurrentX = 0;
int CurrentY = 0;
int ColumnWidth = 0;
int Spacing = 20;
int NextHeightIncrease = INT_MAX;
for (lcPartsListImage& Image : Images)
{
if (CurrentY + Image.Bounds.height() + Ascent > CurrentHeight)
{
int NeededSpace = Image.Bounds.height() + Ascent - (CurrentHeight - CurrentY);
NextHeightIncrease = qMin(NeededSpace, NextHeightIncrease);
CurrentY = 0;
CurrentX += ColumnWidth + Spacing;
ColumnWidth = 0;
}
Image.Position = QPoint(CurrentX, CurrentY);
CurrentY += Image.Bounds.height() + Ascent + Spacing;
CurrentWidth = qMax(CurrentWidth, CurrentX + Image.Bounds.width());
ColumnWidth = qMax(ColumnWidth, Image.Bounds.width());
}
if (CurrentWidth < MaxWidth)
{
MaxWidth = CurrentWidth;
break;
}
CurrentHeight += NextHeightIncrease;
}
QImage PainterImage(MaxWidth + 40, CurrentHeight + 40, QImage::Format_ARGB32);
PainterImage.fill(QColor(255, 255, 255, 0));
QPainter Painter(&PainterImage);
Painter.setFont(Font);
for (lcPartsListImage& Image : Images)
{
QPoint Position = Image.Position + QPoint(20, 20);
Painter.drawImage(Position, Image.Thumbnail, Image.Bounds);
Painter.drawText(QPoint(Position.x(), Position.y() + Image.Bounds.height() + Ascent), QString::number(Image.Count) + 'x');
}
Painter.end();
return PainterImage;
}
void Project::ExportHTML(const lcHTMLExportOptions& Options)
{
QDir Dir(Options.PathName);
@ -1736,7 +1510,7 @@ void Project::ExportHTML(const lcHTMLExportOptions& Options)
auto AddPartsListImage = [this, &Dir](QTextStream& Stream, lcModel* Model, lcStep Step, const QString& BaseName)
{
QImage Image = CreatePartsListImage(Model, Step);
QImage Image = Model->GetPartsListImage(1024, Step);
if (!Image.isNull())
{

View file

@ -108,7 +108,6 @@ public:
protected:
QString GetExportFileName(const QString& FileName, const QString& DefaultExtension, const QString& DialogTitle, const QString& DialogFilter) const;
std::vector<lcModelPartsEntry> GetModelParts();
QImage CreatePartsListImage(lcModel* Model, lcStep Step);
void SetFileName(const QString& FileName);
bool mModified;