#include "FractoriumPch.h" #include "Fractorium.h" /// /// Initialize the info UI. /// void Fractorium::InitInfoUI() { auto treeHeader = ui.SummaryTree->header(); auto tableHeader = ui.SummaryTable->horizontalHeader(); treeHeader->setVisible(true); treeHeader->setSectionsClickable(true); treeHeader->setSectionResizeMode(QHeaderView::ResizeToContents); connect(treeHeader, SIGNAL(sectionClicked(int)), this, SLOT(OnSummaryTreeHeaderSectionClicked(int)), Qt::QueuedConnection); connect(tableHeader, SIGNAL(sectionResized(int, int, int)), this, SLOT(OnSummaryTableHeaderResized(int, int, int)), Qt::QueuedConnection); SetFixedTableHeader(ui.SummaryTable->verticalHeader()); ui.SummaryTable->setItem(0, 0, m_InfoNameItem = new QTableWidgetItem("")); ui.SummaryTable->setItem(1, 0, m_InfoPaletteItem = new QTableWidgetItem("")); ui.SummaryTable->setItem(2, 0, m_Info3dItem = new QTableWidgetItem("")); ui.SummaryTable->setItem(3, 0, m_InfoXaosItem = new QTableWidgetItem("")); ui.SummaryTable->setItem(4, 0, m_InfoXformCountItem = new QTableWidgetItem("")); ui.SummaryTable->setItem(5, 0, m_InfoFinalXformItem = new QTableWidgetItem("")); ui.InfoTabWidget->setCurrentIndex(0);//Make summary tab focused by default. } /// /// Called when the palette cell of the summary table is resized in response /// to a resizing of the Info dock. /// /// Ignored /// Ignored /// Ignored void Fractorium::OnSummaryTableHeaderResized(int logicalIndex, int oldSize, int newSize) { QPixmap pixmap(QPixmap::fromImage(m_Controller->FinalPaletteImage()));//Create a QPixmap out of the QImage, will be empty on startup. SetPaletteTableItem(&pixmap, ui.SummaryTable, m_InfoPaletteItem, 1, 0); } /// /// Expand or collapse the summary tree depending on the column index clicked. /// 0: collapse, 1: expand. /// /// The column which was clicked void Fractorium::OnSummaryTreeHeaderSectionClicked(int logicalIndex) { auto tree = ui.SummaryTree; if (logicalIndex) tree->expandAll(); else tree->collapseAll(); } /// /// Fill the summary tree with values from the current ember. /// This is meant to be a rough summary by containing only the most relevant /// values from the ember. /// It's also meant to be used in a fire-and-forget way. Once the tree is filled /// individual nodes are never referenced again. /// The entire tree is cleared and refilled whenever a render is completed. /// This would seem inefficient, but it appears to update with no flicker. /// If this ever presents a problem in the future, revisit with a more /// intelligent design. /// template void FractoriumEmberController::FillSummary() { int p = 3; int vp = 4; int vlen = 7; char pc = 'f'; bool forceFinal = m_Fractorium->HaveFinal(); size_t x = 0, total = m_Ember.TotalXformCount(forceFinal); Xform* xform = nullptr; QColor color; auto table = m_Fractorium->ui.SummaryTable; auto tree = m_Fractorium->ui.SummaryTree; tree->blockSignals(true); tree->clear(); m_Fractorium->m_InfoNameItem->setText(m_Ember.m_Name.c_str()); m_Fractorium->m_Info3dItem->setText(m_Ember.ProjBits() ? "Yes" : "No"); m_Fractorium->m_InfoXaosItem->setText(m_Ember.XaosPresent() ? "Yes" : "No"); m_Fractorium->m_InfoXformCountItem->setText(QString::number(m_Ember.XformCount())); m_Fractorium->m_InfoFinalXformItem->setText(m_Ember.UseFinalXform() ? "Yes" : "No"); QPixmap pixmap(QPixmap::fromImage(m_FinalPaletteImage));//Create a QPixmap out of the QImage. QSize size(table->columnWidth(0), table->rowHeight(1) + 1); m_Fractorium->m_InfoPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); for (x = 0; x < total && (xform = m_Ember.GetTotalXform(x, forceFinal)); x++) { size_t i = 0; QString as = "Pre"; auto item1 = new QTreeWidgetItem(tree); intmax_t linkedIndex = IsXformLinked(m_Ember, xform); QString linked = (linkedIndex != -1) ? (" Linked to " + QString::number(linkedIndex + 1)) : ""; auto index = m_Ember.GetXformIndex(xform); m_Ember.CalcNormalizedWeights(m_NormalizedWeights); xform->SetPrecalcFlags();//Needed for HasPost() below. if (!m_Ember.IsFinalXform(xform) && index != -1) { item1->setText(0, "Xform " + QString::number(x + 1) + " (" + QLocale::system().toString(xform->m_Weight, pc, p) + ") (" + QLocale::system().toString(double(m_NormalizedWeights[index]), pc, p) + ")" + linked); } else item1->setText(0, "Final xform"); item1->setText(1, xform->m_Name.c_str()); auto affineItem = new QTreeWidgetItem(item1); affineItem->setText(0, "Affine"); if (xform->m_Affine.IsZero()) as += " Empty"; else if (xform->m_Affine.IsID()) as += " ID"; if (xform->HasPost()) { as += ", Post"; if (xform->m_Post.IsZero()) as += " Empty";//Don't need to check further for IsID() because post is not included if it's ID. } affineItem->setText(1, as); auto colorIndexItem = new QTreeWidgetItem(item1); colorIndexItem->setText(0, "Color index"); colorIndexItem->setText(1, QLocale::system().toString(xform->m_ColorX, pc, p)); color = ColorIndexToQColor(xform->m_ColorX); color.setAlphaF(xform->m_Opacity); colorIndexItem->setBackgroundColor(1, color); colorIndexItem->setTextColor(1, VisibleColor(color)); auto colorSpeedItem = new QTreeWidgetItem(item1); colorSpeedItem->setText(0, "Color speed"); colorSpeedItem->setText(1, QLocale::system().toString(xform->m_ColorSpeed, pc, p)); auto opacityItem = new QTreeWidgetItem(item1); opacityItem->setText(0, "Opacity"); opacityItem->setText(1, QLocale::system().toString(xform->m_Opacity, pc, p)); auto dcItem = new QTreeWidgetItem(item1); dcItem->setText(0, "Direct color"); dcItem->setText(1, QLocale::system().toString(xform->m_DirectColor, pc, p)); while (auto var = xform->GetVariation(i++)) { auto vitem = new QTreeWidgetItem(item1); vitem->setText(0, QString::fromStdString(var->Name())); vitem->setText(1, QLocale::system().toString(var->m_Weight, pc, vp).rightJustified(vlen, ' ')); if (auto parVar = dynamic_cast*>(var)) { auto params = parVar->Params(); for (auto j = 0; j < parVar->ParamCount(); j++) { if (!params[j].IsPrecalc()) { auto pitem = new QTreeWidgetItem(vitem); pitem->setText(0, params[j].Name().c_str()); pitem->setText(1, QLocale::system().toString(params[j].ParamVal(), pc, vp).rightJustified(vlen, ' ')); } } } } auto item2 = new QTreeWidgetItem(tree);//Empty item in between xforms. } tree->expandAll(); tree->blockSignals(false); } void Fractorium::FillSummary() { m_Controller->FillSummary(); } /// /// Update the histogram bounds display labels. /// This shows the user the actual bounds of what's /// being rendered. Mostly of engineering interest. /// void Fractorium::UpdateHistogramBounds() { static QString ul, ur, lr, ll, wh, g, de; if (auto r = m_Controller->Renderer()) { ul.sprintf("UL: %3.3f, %3.3f", r->LowerLeftX(), r->UpperRightY());//These bounds include gutter padding. ur.sprintf("UR: %3.3f, %3.3f", r->UpperRightX(), r->UpperRightY()); lr.sprintf("LR: %3.3f, %3.3f", r->UpperRightX(), r->LowerLeftY()); ll.sprintf("LL: %3.3f, %3.3f", r->LowerLeftX(), r->LowerLeftY()); wh.sprintf("W x H: %4u x %4u", r->SuperRasW(), r->SuperRasH()); g.sprintf("%u", (uint)r->GutterWidth()); ui.InfoBoundsLabelUL->setText(ul); ui.InfoBoundsLabelUR->setText(ur); ui.InfoBoundsLabelLR->setText(lr); ui.InfoBoundsLabelLL->setText(ll); ui.InfoBoundsLabelWH->setText(wh); ui.InfoBoundsTable->item(0, 1)->setText(g); if (r->GetDensityFilter()) { auto deWidth = (r->GetDensityFilter()->FilterWidth() * 2) + 1; de.sprintf("%d x %d", deWidth, deWidth); ui.InfoBoundsTable->item(1, 1)->setText(de); } else ui.InfoBoundsTable->item(1, 1)->setText("N/A"); } } /// /// Fill the passed in QTextEdit with the vector of strings. /// Optionally clear first. /// Serves as a convenience function because the error reports coming /// from Ember and EmberCL use vector. /// Use invokeMethod() in case this is called from a thread. /// /// The vector of error strings /// The QTextEdit to fill /// Clear if true, else don't. void Fractorium::ErrorReportToQTextEdit(const vector& errors, QTextEdit* textEdit, bool clear) { if (clear) QMetaObject::invokeMethod(textEdit, "clear", Qt::QueuedConnection); for (auto& error : errors) QMetaObject::invokeMethod(textEdit, "append", Qt::QueuedConnection, Q_ARG(const QString&, QString::fromStdString(error) + "\n")); } template class FractoriumEmberController; #ifdef DO_DOUBLE template class FractoriumEmberController; #endif