#include "FractoriumPch.h"
#include "Fractorium.h"
///
/// Initialize the parameters UI.
///
void Fractorium::InitParamsUI()
{
int row = 0;
int spinHeight = 20;
double dmax = numeric_limits::max();
vector comboVals;
QTableWidget* table = ui.ColorTable;
//Because QTableWidget does not allow for a single title bar/header
//at the top of a multi-column table, the workaround hack is to just
//make another single column table with no rows, and use the single
//column header as the title bar. Then positioning it right above the table
//that holds the data. Disallow selecting and resizing of the title bar.
SetFixedTableHeader(ui.ColorTableHeader->horizontalHeader());
SetFixedTableHeader(ui.GeometryTableHeader->horizontalHeader());
SetFixedTableHeader(ui.FilterTableHeader->horizontalHeader());
SetFixedTableHeader(ui.IterationTableHeader->horizontalHeader());
SetFixedTableHeader(ui.AnimationTableHeader->horizontalHeader());
//Color.
SetupSpinner(table, this, row, 1, m_BrightnessSpin, spinHeight, 0.01, dmax, 1, SIGNAL(valueChanged(double)), SLOT(OnBrightnessChanged(double)), true, 4.0, 4.0, 4.0);
SetupSpinner(table, this, row, 1, m_GammaSpin, spinHeight, 1, dmax, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(double)), true, 4.0, 4.0, 4.0);
SetupSpinner(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, dmax, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0);
SetupSpinner(table, this, row, 1, m_VibrancySpin, spinHeight, 0, dmax, 0.01, SIGNAL(valueChanged(double)), SLOT(OnVibrancyChanged(double)), true, 1.0, 1.0, 0.0);
SetupSpinner(table, this, row, 1, m_HighlightSpin, spinHeight, -1.0, 10, 0.1, SIGNAL(valueChanged(double)), SLOT(OnHighlightPowerChanged(double)), true, 1.0, 1.0, -1.0);
SetupSpinner(table, this, row, 1, m_K2Spin, spinHeight, 0, 99.0, 0.0001, SIGNAL(valueChanged(double)), SLOT(OnK2Changed(double)), true, 0, 0.0001, 0);
m_HighlightSpin->DoubleClickLowVal(-1.0);
int dec = 6;
m_BrightnessSpin->setDecimals(dec);
m_GammaSpin->setDecimals(dec);
m_GammaThresholdSpin->setDecimals(dec);
m_VibrancySpin->setDecimals(dec);
m_HighlightSpin->setDecimals(dec);
m_K2Spin->setDecimals(dec);
m_BgRow = row;
m_BackgroundColorButton = new QPushButton("...", table);
m_BackgroundColorButton->setMinimumWidth(21);
m_BackgroundColorButton->setMaximumWidth(21);
table->setCellWidget(row, 1, m_BackgroundColorButton);
table->item(row, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
connect(m_BackgroundColorButton, SIGNAL(clicked(bool)), this, SLOT(OnBackgroundColorButtonClicked(bool)), Qt::QueuedConnection);
row++;
comboVals.push_back("Step");
comboVals.push_back("Linear");
SetupCombo(table, this, row, 1, m_PaletteModeCombo, comboVals, SIGNAL(currentIndexChanged(int)), SLOT(OnPaletteModeComboCurrentIndexChanged(int)));
m_PaletteModeCombo->SetCurrentIndexStealth(int(ePaletteMode::PALETTE_LINEAR));
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
//Geometry.
row = 0;
table = ui.GeometryTable;
SetupSpinner (table, this, row, 1, m_WidthSpin, spinHeight, 10, 2048, 50, SIGNAL(valueChanged(int)), SLOT(OnWidthChanged(int)), true, width(), width(), width());
SetupSpinner (table, this, row, 1, m_HeightSpin, spinHeight, 10, 2048, 50, SIGNAL(valueChanged(int)), SLOT(OnHeightChanged(int)), true, height(), height(), height());
SetupSpinner(table, this, row, 1, m_CenterXSpin, spinHeight, -dmax, dmax, 0.05, SIGNAL(valueChanged(double)), SLOT(OnCenterXChanged(double)), true, 0, 0, 0);
SetupSpinner(table, this, row, 1, m_CenterYSpin, spinHeight, -dmax, dmax, 0.05, SIGNAL(valueChanged(double)), SLOT(OnCenterYChanged(double)), true, 0, 0, 0);
SetupSpinner(table, this, row, 1, m_ScaleSpin, spinHeight, 10, dmax, 20, SIGNAL(valueChanged(double)), SLOT(OnScaleChanged(double)), true, 240, 240, 240);
SetupSpinner(table, this, row, 1, m_ZoomSpin, spinHeight, 0, 25, 0.2, SIGNAL(valueChanged(double)), SLOT(OnZoomChanged(double)), true, 0, 0, 0);
SetupSpinner(table, this, row, 1, m_RotateSpin, spinHeight, -180, 180, 10, SIGNAL(valueChanged(double)), SLOT(OnRotateChanged(double)), true, 0, 0, 0);
SetupSpinner(table, this, row, 1, m_ZPosSpin, spinHeight, -1000, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnZPosChanged(double)), true, 0, 1, 0);
SetupSpinner(table, this, row, 1, m_PerspectiveSpin, spinHeight, -500, 500, 0.01, SIGNAL(valueChanged(double)), SLOT(OnPerspectiveChanged(double)), true, 0, 1, 0);
SetupSpinner(table, this, row, 1, m_PitchSpin, spinHeight, -dmax, dmax, 1, SIGNAL(valueChanged(double)), SLOT(OnPitchChanged(double)), true, 0, 45, 0);
SetupSpinner(table, this, row, 1, m_YawSpin, spinHeight, -dmax, dmax, 1, SIGNAL(valueChanged(double)), SLOT(OnYawChanged(double)), true, 0, 45, 0);
SetupSpinner(table, this, row, 1, m_DepthBlurSpin, spinHeight, -dmax, dmax, 0.01, SIGNAL(valueChanged(double)), SLOT(OnDepthBlurChanged(double)), true, 0, 1, 0);
m_WidthSpin->m_DoubleClickNonZeroEvent = [&](SpinBox * sb, int val)
{
m_Controller->ResizeAndScale(val, m_HeightSpin->DoubleClickNonZero(), eScaleType::SCALE_WIDTH);
m_HeightSpin->SetValueStealth(m_HeightSpin->DoubleClickNonZero());
};
m_HeightSpin->m_DoubleClickNonZeroEvent = [&](SpinBox * sb, int val)
{
m_Controller->ResizeAndScale(m_WidthSpin->DoubleClickNonZero(), val, eScaleType::SCALE_HEIGHT);
m_WidthSpin->SetValueStealth(m_WidthSpin->DoubleClickNonZero());
};
dec = 4;
m_CenterXSpin->setDecimals(dec);
m_CenterYSpin->setDecimals(dec);
m_ZPosSpin->setDecimals(dec);
m_PerspectiveSpin->setDecimals(dec);
m_DepthBlurSpin->setDecimals(dec);
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
//Filter.
row = 0;
table = ui.FilterTable;
SetupSpinner(table, this, row, 1, m_SpatialFilterWidthSpin, spinHeight, 0, 2, 0.1, SIGNAL(valueChanged(double)), SLOT(OnSpatialFilterWidthChanged(double)), true, 1.0, 1.0, 0);
comboVals = SpatialFilterCreator::FilterTypes();
SetupCombo(table, this, row, 1, m_SpatialFilterTypeCombo, comboVals, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnSpatialFilterTypeComboCurrentIndexChanged(const QString&)));
m_SpatialFilterTypeCombo->SetCurrentIndexStealth(0);
SetupSpinner(table, this, row, 1, m_DEFilterMinRadiusSpin, spinHeight, 0, 25, 1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterMinRadiusWidthChanged(double)), true, 0, 0, 0);
SetupSpinner(table, this, row, 1, m_DEFilterMaxRadiusSpin, spinHeight, 0, 25, 1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterMaxRadiusWidthChanged(double)), true, 0.0, 9.0, 0);
SetupSpinner(table, this, row, 1, m_DECurveSpin, spinHeight, 0.15, 5, 0.1, SIGNAL(valueChanged(double)), SLOT(OnDEFilterCurveWidthChanged(double)), true, 0.4, 0.4, 0.4);
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
//Iteration.
row = 0;
table = ui.IterationTable;
auto quality = m_Settings->OpenCL() ? m_Settings->OpenClQuality() : m_Settings->CpuQuality();
SetupSpinner( table, this, row, 1, m_SbsSpin, spinHeight, 1000, 100000, 100, SIGNAL(valueChanged(int)), SLOT(OnSbsChanged(int)), true, DEFAULT_SBS, DEFAULT_SBS, DEFAULT_SBS);
SetupSpinner( table, this, row, 1, m_FuseSpin, spinHeight, 1, 1000, 5, SIGNAL(valueChanged(int)), SLOT(OnFuseChanged(int)), true, 15, 100, 15);
SetupSpinner(table, this, row, 1, m_RandRangeSpin, spinHeight, 0.01, 1000, 0.1, SIGNAL(valueChanged(double)), SLOT(OnRandRangeChanged(double)), true, 1, 10, 1);
SetupSpinner(table, this, row, 1, m_QualitySpin, spinHeight, 1, dmax, 50, SIGNAL(valueChanged(double)), SLOT(OnQualityChanged(double)), true, quality, 10, 10);
SetupSpinner( table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 1, 2, 1);
m_RandRangeSpin->DoubleClickLowVal(1);
m_RandRangeSpin->setDecimals(4);
m_FuseSpin->DoubleClickLowVal(15);
m_SupersampleSpin->DoubleClickLowVal(1);
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
//Animation.
row = 0;
table = ui.AnimationTable;
comboVals.clear();
comboVals.push_back("Linear");
comboVals.push_back("Smooth");
SetupCombo(table, this, row, 1, m_InterpTypeCombo, comboVals, SIGNAL(currentIndexChanged(int)), SLOT(OnInterpTypeComboCurrentIndexChanged(int)));
m_InterpTypeCombo->SetCurrentIndexStealth(int(eInterp::EMBER_INTERP_SMOOTH));
comboVals.clear();
comboVals.push_back("Linear");
comboVals.push_back("Log");
SetupCombo( table, this, row, 1, m_AffineInterpTypeCombo, comboVals, SIGNAL(currentIndexChanged(int)), SLOT(OnAffineInterpTypeComboCurrentIndexChanged(int)));
m_AffineInterpTypeCombo->SetCurrentIndexStealth(int(eAffineInterp::AFFINE_INTERP_LOG));
SetupSpinner(table, this, row, 1, m_TemporalFilterWidthSpin, spinHeight, 1, 10, 1, SIGNAL(valueChanged(double)), SLOT(OnTemporalFilterWidthChanged(double)), true, 1);
comboVals = TemporalFilterCreator::FilterTypes();
SetupCombo( table, this, row, 1, m_TemporalFilterTypeCombo, comboVals, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnTemporalFilterTypeComboCurrentIndexChanged(const QString&)));
m_TemporalFilterTypeCombo->SetCurrentIndexStealth(int(eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER));
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
}
///
/// Return whether the apply all checkbox is checked.
///
/// True if checked, else false.
bool Fractorium::ApplyAll()
{
return ui.ApplyAllParamsCheckBox->isChecked();
}
///
/// Color.
///
///
/// Set the brightness to be used for calculating K1 and K2 for filtering and final accum.
/// Called when brightness spinner is changed.
/// Resets the rendering process to the filtering stage.
///
/// The brightness
template
void FractoriumEmberController::BrightnessChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_Brightness = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
}
void Fractorium::OnBrightnessChanged(double d) { m_Controller->BrightnessChanged(d); }
///
/// Set the gamma to be used for final accum.
/// Called when gamma spinner is changed.
/// Resets the rendering process if temporal samples is greater than 1,
/// else if early clip is true, filter and accum, else final accum only.
///
/// The gamma value
template void FractoriumEmberController::GammaChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_Gamma = d;
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY, m_Fractorium->ApplyAll());
}
void Fractorium::OnGammaChanged(double d) { m_Controller->GammaChanged(d); }
///
/// Set the gamma threshold to be used for final accum.
/// Called when gamma threshold spinner is changed.
/// Resets the rendering process to the final accumulation stage.
///
/// The gamma threshold
template void FractoriumEmberController::GammaThresholdChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_GammaThresh = d;
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY, m_Fractorium->ApplyAll());
}
void Fractorium::OnGammaThresholdChanged(double d) { m_Controller->GammaThresholdChanged(d); }
///
/// Set the vibrancy to be used for final accum.
/// Called when vibrancy spinner is changed.
/// Resets the rendering process to the final accumulation stage if temporal samples is 1, else full reset.
///
/// The vibrancy
template void FractoriumEmberController::VibrancyChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_Vibrancy = d;
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY, m_Fractorium->ApplyAll());
}
void Fractorium::OnVibrancyChanged(double d) { m_Controller->VibrancyChanged(d); }
///
/// Set the highlight power to be used for final accum.
/// Called when highlight power spinner is changed.
/// Resets the rendering process to the final accumulation stage.
///
/// The highlight power
template void FractoriumEmberController::HighlightPowerChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_HighlightPower = d;
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY, m_Fractorium->ApplyAll());
}
void Fractorium::OnHighlightPowerChanged(double d) { m_Controller->HighlightPowerChanged(d); }
///
/// Set the k2 brightness value to be used for final accum.
/// Called when k2 is changed.
/// Resets the rendering process to the final accumulation stage.
///
/// The k2 value
template void FractoriumEmberController::K2Changed(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_K2 = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
}
void Fractorium::OnK2Changed(double d) { m_Controller->K2Changed(d); }
///
/// Show the color selection dialog.
/// Called when background color button is clicked.
///
/// Ignored
void Fractorium::OnBackgroundColorButtonClicked(bool checked)
{
m_ColorDialog->exec();
}
///
/// Set a new ember background color when the user accepts the color dialog.
/// Also change the background and foreground colors of the color cell in the
/// color params table.
/// Resets the rendering process.
///
/// The color to set, RGB in the 0-255 range
template
void FractoriumEmberController::BackgroundChanged(const QColor& color)
{
auto itemRow = m_Fractorium->m_BgRow;
auto colorTable = m_Fractorium->ui.ColorTable;
colorTable->item(itemRow, 1)->setBackgroundColor(color);
auto r = ToString(color.red());
auto g = ToString(color.green());
auto b = ToString(color.blue());
colorTable->item(itemRow, 1)->setTextColor(VisibleColor(color));
colorTable->item(itemRow, 1)->setText("rgb(" + r + ", " + g + ", " + b + ")");
UpdateAll([&](Ember& ember, bool isMain)
{
//Color is 0-255, normalize to 0-1.
ember.m_Background.r = color.red() / 255.0;
ember.m_Background.g = color.green() / 255.0;
ember.m_Background.b = color.blue() / 255.0;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnColorSelected(const QColor& color) { m_Controller->BackgroundChanged(color); }
///
/// Set the palette index interpolation mode.
/// Called when palette mode combo box index is changed.
/// Resets the rendering process.
///
/// The index of the palette mode combo box
template void FractoriumEmberController::PaletteModeChanged(uint i)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_PaletteMode = i == 0 ? ePaletteMode::PALETTE_STEP : ePaletteMode::PALETTE_LINEAR;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnPaletteModeComboCurrentIndexChanged(int index) { m_Controller->PaletteModeChanged(index); }
///
/// Geometry.
///
///
/// Set the width of the ember in pixels to the passed in value.
/// Called when the width spinner is changed in a manner other than double clicking.
/// Resets the rendering process.
///
/// The width value in pixels to set
template void FractoriumEmberController::WidthChanged(uint i)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_FinalRasW = i;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnWidthChanged(int i) { m_Controller->WidthChanged(i); }
///
/// Set the height of the ember in pixels to the passed in value.
/// Called when the height spinner is changed in a manner other than double clicking.
/// Resets the rendering process.
///
/// The height value in pixels to set
template void FractoriumEmberController::HeightChanged(uint i)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_FinalRasH = i;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnHeightChanged(int i) { m_Controller->HeightChanged(i); }
///
/// Set the width and height of the ember in pixels to the passed in values.
/// Called when either the width or height spinners are double clicked.
/// Because this will change the scale value, the scale spinner gets a stealth update.
/// For this reason, the affine scales are reset, even though they are not when doing a manual
/// height or width adjustment.
/// Resets the rendering process.
///
/// The width value in pixels to set
/// The height value in pixels to set
/// The height value in pixels to set
template
void FractoriumEmberController::ResizeAndScale(int width, int height, eScaleType scaleType)
{
UpdateAll([&](Ember& ember, bool isMain)
{
m_Ember.SetSizeAndAdjustScale(width, height, false, scaleType);
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
m_Fractorium->m_ScaleSpin->SetValueStealth(m_Ember.m_PixelsPerUnit);
m_Fractorium->OnActionResetScale(true);
}
///
/// Set the x offset applied to the center of the image.
/// Resets the rendering process.
///
/// The x offset value
template void FractoriumEmberController::CenterXChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CenterX = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnCenterXChanged(double d) { m_Controller->CenterXChanged(d); }
///
/// Set the y offset applied to the center of the image.
/// Resets the rendering process.
///
/// The y offset value
template void FractoriumEmberController::CenterYChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CenterY = ember.m_RotCenterY = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnCenterYChanged(double d) { m_Controller->CenterYChanged(d); }
///
/// Set the scale (pixels per unit) value of the image.
/// Note this will not increase the number of iters ran, but will degrade quality.
/// To preserve quality, but exponentially increase iters, use zoom.
/// Called when scale spinner is changed.
/// Resets the rendering process.
///
/// The scale value
template void FractoriumEmberController::ScaleChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_PixelsPerUnit = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnScaleChanged(double d) { m_Controller->ScaleChanged(d); }
///
/// Set the zoom value of the image.
/// Note this will increase the number of iters ran exponentially.
/// To zoom in without increasing iters, but sacrifice quality, use scale.
/// Called when zoom spinner is changed.
/// Resets the rendering process.
///
/// The zoom value
template void FractoriumEmberController::ZoomChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_Zoom = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnZoomChanged(double d) { m_Controller->ZoomChanged(d); }
///
/// Set the angular rotation of the image.
/// Called when rotate spinner is changed.
/// Resets the rendering process.
///
/// The rotation in angles
template void FractoriumEmberController::RotateChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_Rotate = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnRotateChanged(double d) { m_Controller->RotateChanged(d); }
template void FractoriumEmberController::ZPosChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CamZPos = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnZPosChanged(double d) { m_Controller->ZPosChanged(d); }
template void FractoriumEmberController::PerspectiveChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CamPerspective = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnPerspectiveChanged(double d) { m_Controller->PerspectiveChanged(d); }
template void FractoriumEmberController::PitchChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CamPitch = d * DEG_2_RAD;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnPitchChanged(double d) { m_Controller->PitchChanged(d); }
template void FractoriumEmberController::YawChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CamYaw = d * DEG_2_RAD;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnYawChanged(double d) { m_Controller->YawChanged(d); }
template void FractoriumEmberController::DepthBlurChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CamDepthBlur = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnDepthBlurChanged(double d) { m_Controller->DepthBlurChanged(d); }
///
/// Filter.
///
///
/// Set the spatial filter width.
/// Called when the spatial filter width spinner is changed.
/// Resets the rendering process to density filtering if early clip is used, else to final accumulation.
///
/// The spatial filter width
template void FractoriumEmberController::SpatialFilterWidthChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_SpatialFilterRadius = d;
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY, m_Fractorium->ApplyAll());
}
void Fractorium::OnSpatialFilterWidthChanged(double d) { m_Controller->SpatialFilterWidthChanged(d); }
///
/// Set the spatial filter type.
/// Called when the spatial filter type combo box index is changed.
/// Resets the rendering process to density filtering if early clip is used, else to final accumulation.
///
/// The spatial filter type
template void FractoriumEmberController::SpatialFilterTypeChanged(const QString& text)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_SpatialFilterType = SpatialFilterCreator::FromString(text.toStdString());
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY, m_Fractorium->ApplyAll());
}
void Fractorium::OnSpatialFilterTypeComboCurrentIndexChanged(const QString& text) { m_Controller->SpatialFilterTypeChanged(text); }
///
/// Set the density estimation filter min radius value.
/// Resets the rendering process to density filtering.
///
/// The min radius value
template
void FractoriumEmberController::DEFilterMinRadiusWidthChanged(double d)
{
if (m_Ember.m_MinRadDE != d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_MinRadDE = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
}
}
void Fractorium::OnDEFilterMinRadiusWidthChanged(double d)
{
ConstrainLow(m_DEFilterMinRadiusSpin, m_DEFilterMaxRadiusSpin);
m_Controller->DEFilterMinRadiusWidthChanged(m_DEFilterMinRadiusSpin->value());
}
///
/// Set the density estimation filter max radius value.
/// Resets the rendering process to density filtering.
///
/// The max radius value
template
void FractoriumEmberController::DEFilterMaxRadiusWidthChanged(double d)
{
if (m_Ember.m_MaxRadDE != d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_MaxRadDE = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
}
}
void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d)
{
ConstrainHigh(m_DEFilterMinRadiusSpin, m_DEFilterMaxRadiusSpin);
m_Controller->DEFilterMaxRadiusWidthChanged(m_DEFilterMaxRadiusSpin->value());
}
///
/// Set the density estimation filter curve value.
/// Resets the rendering process to density filtering.
///
/// The curve value
template void FractoriumEmberController::DEFilterCurveWidthChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CurveDE = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
}
void Fractorium::OnDEFilterCurveWidthChanged(double d) { m_Controller->DEFilterCurveWidthChanged(d); }
///
/// Iteration.
///
///
/// Set the iteration depth.
/// Called when the sub batch size spinner is changed.
/// Resets the rendering process.
///
/// The sub batch size value to set
template void FractoriumEmberController::SbsChanged(int d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_SubBatchSize = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnSbsChanged(int d) { m_Controller->SbsChanged(d); }
///
/// Set the range from which to chose the starting random points, as well as point resets due to bad points.
/// Called when the rand range spinner is changed.
/// Resets the rendering process.
///
/// The sub batch size value to set
template void FractoriumEmberController::RandRangeChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_RandPointRange = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnRandRangeChanged(double d) { m_Controller->RandRangeChanged(d); }
///
/// Set the number of samples to disregard for each sub batch.
/// Called when the fuse count spinner is changed.
/// Resets the rendering process.
///
/// The fuse count value to set
template void FractoriumEmberController::FuseChanged(int d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_FuseCount = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnFuseChanged(int d) { m_Controller->FuseChanged(d); }
///
/// Set the quality.
/// 10 is good for interactive rendering on the CPU.
/// 20-50 is good for OpenCL.
/// Above 500 seems to offer little additional value for final renders.
/// Called when the quality spinner is changed.
/// If rendering is done, and the value is greater than the last value,
/// the rendering process is continued, else it's reset.
///
/// The quality in terms of iterations per pixel
template void FractoriumEmberController::QualityChanged(double d) { }
void Fractorium::OnQualityChanged(double d) { m_Controller->QualityChanged(d); }
///
/// Set the supersample.
/// Note this will dramatically degrade performance, especially in
/// OpenCL, while only giving a minor improvement in visual quality.
/// Values above 2 add no noticeable difference.
/// The user should only use this for a final render, or a quick preview, and then
/// reset it back to 1 when done.
/// Called when the supersample spinner is changed.
/// Resets the rendering process.
///
/// The supersample value to set
template void FractoriumEmberController::SupersampleChanged(int d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_Supersample = d;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
}
void Fractorium::OnSupersampleChanged(int d) { m_Controller->SupersampleChanged(d); }
///
/// Set the affine interpolation type.
/// Does not reset anything because this is only used for animation.
/// In the future, when animation is implemented, this will have an effect.
/// Called when the affine interp type combo box index is changed.
///
/// The index
template
void FractoriumEmberController::AffineInterpTypeChanged(int i)
{
UpdateAll([&](Ember& ember, bool isMain)
{
eAffineInterp interp;
if (i == 0)
interp = eAffineInterp::AFFINE_INTERP_LINEAR;
else if (i == 1)
interp = eAffineInterp::AFFINE_INTERP_LOG;
else
interp = eAffineInterp::AFFINE_INTERP_LINEAR;
ember.m_AffineInterp = interp;
if (!m_Fractorium->ApplyAll())
if (m_EmberFilePointer)
m_EmberFilePointer->m_AffineInterp = interp;
}, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll());
}
void Fractorium::OnAffineInterpTypeComboCurrentIndexChanged(int index) { m_Controller->AffineInterpTypeChanged(index); }
///
/// Set the interpolation type.
/// Does not reset anything because this is only used for animation.
/// Called when the interp type combo box index is changed.
///
/// The index
template
void FractoriumEmberController::InterpTypeChanged(int i)
{
UpdateAll([&](Ember& ember, bool isMain)
{
eInterp interp;
if (i == 0)
interp = eInterp::EMBER_INTERP_LINEAR;
else if (i == 1)
interp = eInterp::EMBER_INTERP_SMOOTH;
else
interp = eInterp::EMBER_INTERP_LINEAR;
ember.m_Interp = interp;
if (!m_Fractorium->ApplyAll())
if (m_EmberFilePointer)
m_EmberFilePointer->m_Interp = interp;
}, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll());
}
void Fractorium::OnInterpTypeComboCurrentIndexChanged(int index) { m_Controller->InterpTypeChanged(index); }
///
/// Set the temporal filter width to be used with animation.
/// Called when the temporal filter width spinner is changed.
/// Does not reset anything because this is only used for animation.
///
/// The temporal filter width
template
void FractoriumEmberController::TemporalFilterWidthChanged(double d)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_TemporalFilterWidth = d;
if (!m_Fractorium->ApplyAll())
if (m_EmberFilePointer)
m_EmberFilePointer->m_TemporalFilterWidth = d;
}, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented.
}
void Fractorium::OnTemporalFilterWidthChanged(double d) { m_Controller->TemporalFilterWidthChanged(d); }
///
/// Set the temporal filter type to be used with animation.
/// Called when the temporal filter combo box index is changed.
/// Does not reset anything because this is only used for animation.
///
/// The name of the temporal filter
template
void FractoriumEmberController::TemporalFilterTypeChanged(const QString& text)
{
UpdateAll([&](Ember& ember, bool isMain)
{
auto filter = TemporalFilterCreator::FromString(text.toStdString());
ember.m_TemporalFilterType = filter;
if (!m_Fractorium->ApplyAll())
if (m_EmberFilePointer)
m_EmberFilePointer->m_TemporalFilterType = filter;
}, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented.
}
void Fractorium::OnTemporalFilterTypeComboCurrentIndexChanged(const QString& text) { m_Controller->TemporalFilterTypeChanged(text); }
///
/// Set the center.
/// This updates the spinners as well as the current ember center.
/// Resets the renering process.
///
/// The x offset
/// The y offset
template
void FractoriumEmberController::SetCenter(double x, double y)
{
UpdateAll([&](Ember& ember, bool isMain)
{
ember.m_CenterX = x;
ember.m_CenterY = ember.m_RotCenterY = y;
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
m_Fractorium->m_CenterXSpin->SetValueStealth(x);//Don't trigger a redraw twice.
m_Fractorium->m_CenterYSpin->SetValueStealth(y);
}
///
/// Fill the parameter tables and palette widgets with values from the current ember.
/// This takes ~1-2ms.
///
template
void FractoriumEmberController::FillParamTablesAndPalette()
{
m_Fractorium->m_BrightnessSpin->SetValueStealth(m_Ember.m_Brightness);//Color.
m_Fractorium->m_GammaSpin->SetValueStealth(m_Ember.m_Gamma);
m_Fractorium->m_GammaThresholdSpin->SetValueStealth(m_Ember.m_GammaThresh);
m_Fractorium->m_VibrancySpin->SetValueStealth(m_Ember.m_Vibrancy);
m_Fractorium->m_HighlightSpin->SetValueStealth(m_Ember.m_HighlightPower);
m_Fractorium->m_K2Spin->SetValueStealth(m_Ember.m_K2);
m_Fractorium->m_ColorDialog->setCurrentColor(QColor(m_Ember.m_Background.r * 255, m_Ember.m_Background.g * 255, m_Ember.m_Background.b * 255));
m_Fractorium->ui.ColorTable->item(m_Fractorium->m_BgRow, 1)->setBackgroundColor(m_Fractorium->m_ColorDialog->currentColor());
BackgroundChanged(m_Fractorium->m_ColorDialog->currentColor());
m_Fractorium->m_PaletteModeCombo->SetCurrentIndexStealth(int(m_Ember.m_PaletteMode));
m_Fractorium->m_WidthSpin->SetValueStealth(m_Ember.m_FinalRasW);//Geometry.
m_Fractorium->m_HeightSpin->SetValueStealth(m_Ember.m_FinalRasH);
m_Fractorium->m_CenterXSpin->SetValueStealth(m_Ember.m_CenterX);
m_Fractorium->m_CenterYSpin->SetValueStealth(m_Ember.m_CenterY);
m_Fractorium->m_ScaleSpin->SetValueStealth(m_Ember.m_PixelsPerUnit);
m_Fractorium->m_ZoomSpin->SetValueStealth(m_Ember.m_Zoom);
m_Fractorium->m_RotateSpin->SetValueStealth(m_Ember.m_Rotate);
m_Fractorium->m_ZPosSpin->SetValueStealth(m_Ember.m_CamZPos);
m_Fractorium->m_PerspectiveSpin->SetValueStealth(m_Ember.m_CamPerspective);
m_Fractorium->m_PitchSpin->SetValueStealth(m_Ember.m_CamPitch * RAD_2_DEG_T);
m_Fractorium->m_YawSpin->SetValueStealth(m_Ember.m_CamYaw * RAD_2_DEG_T);
m_Fractorium->m_DepthBlurSpin->SetValueStealth(m_Ember.m_CamDepthBlur);
m_Fractorium->m_SpatialFilterWidthSpin->SetValueStealth(m_Ember.m_SpatialFilterRadius);//Filter.
m_Fractorium->m_SpatialFilterTypeCombo->SetCurrentIndexStealth(int(m_Ember.m_SpatialFilterType));
m_Fractorium->m_TemporalFilterWidthSpin->SetValueStealth(m_Ember.m_TemporalFilterWidth);
m_Fractorium->m_TemporalFilterTypeCombo->SetCurrentIndexStealth(int(m_Ember.m_TemporalFilterType));
m_Fractorium->m_DEFilterMinRadiusSpin->SetValueStealth(m_Ember.m_MinRadDE);
m_Fractorium->m_DEFilterMaxRadiusSpin->SetValueStealth(m_Ember.m_MaxRadDE);
m_Fractorium->m_DECurveSpin->SetValueStealth(m_Ember.m_CurveDE);
m_Fractorium->m_SbsSpin->SetValueStealth(m_Ember.m_SubBatchSize);//Iteration.
m_Fractorium->m_FuseSpin->SetValueStealth(m_Ember.m_FuseCount);
m_Fractorium->m_RandRangeSpin->SetValueStealth(m_Ember.m_RandPointRange);
m_Fractorium->m_QualitySpin->SetValueStealth(m_Ember.m_Quality);
m_Fractorium->m_SupersampleSpin->SetValueStealth(m_Ember.m_Supersample);
m_Fractorium->m_AffineInterpTypeCombo->SetCurrentIndexStealth(int(m_Ember.m_AffineInterp));
m_Fractorium->m_InterpTypeCombo->SetCurrentIndexStealth(int(m_Ember.m_Interp));
auto temp = m_Ember.m_Palette.m_Filename;
if (temp.get())
m_Fractorium->SetPaletteFileComboIndex(*temp.get());
//Update the palette preview widget.
m_Fractorium->ResetPaletteControls();
//Since the controls were cleared above, the adjusted palette will be identical to the base palette.
//Callers can set, apply and display palette adjustments after this function exits if needed.
SetBasePaletteAndAdjust(m_Ember.m_Palette);//Updating the palette GUI will trigger a full render.
InitLockedScale();
}
///
/// Copy all GUI widget values on the parameters tab to the passed in ember.
///
/// The ember to copy values to.
template
void FractoriumEmberController::ParamsToEmber(Ember& ember)
{
auto color = m_Fractorium->ui.ColorTable->item(5, 1)->backgroundColor();
ember.m_Brightness = m_Fractorium->m_BrightnessSpin->value();//Color.
ember.m_Gamma = m_Fractorium->m_GammaSpin->value();
ember.m_GammaThresh = m_Fractorium->m_GammaThresholdSpin->value();
ember.m_Vibrancy = m_Fractorium->m_VibrancySpin->value();
ember.m_HighlightPower = m_Fractorium->m_HighlightSpin->value();
ember.m_K2 = m_Fractorium->m_K2Spin->value();
ember.m_Background.r = color.red() / 255.0;
ember.m_Background.g = color.green() / 255.0;
ember.m_Background.b = color.blue() / 255.0;
ember.m_PaletteMode = ePaletteMode(m_Fractorium->m_PaletteModeCombo->currentIndex());
ember.m_FinalRasW = m_Fractorium->m_WidthSpin->value();//Geometry.
ember.m_FinalRasH = m_Fractorium->m_HeightSpin->value();
ember.m_CenterX = m_Fractorium->m_CenterXSpin->value();
ember.m_CenterY = ember.m_RotCenterY = m_Fractorium->m_CenterYSpin->value();
ember.m_PixelsPerUnit = m_Fractorium->m_ScaleSpin->value();
ember.m_Zoom = m_Fractorium->m_ZoomSpin->value();
ember.m_Rotate = m_Fractorium->m_RotateSpin->value();
ember.m_CamZPos = m_Fractorium->m_ZPosSpin->value();
ember.m_CamPerspective = m_Fractorium->m_PerspectiveSpin->value();
ember.m_CamPitch = m_Fractorium->m_PitchSpin->value() * DEG_2_RAD_T;
ember.m_CamYaw = m_Fractorium->m_YawSpin->value() * DEG_2_RAD_T;
ember.m_CamDepthBlur = m_Fractorium->m_DepthBlurSpin->value();
ember.m_SpatialFilterRadius = m_Fractorium->m_SpatialFilterWidthSpin->value();//Filter.
ember.m_SpatialFilterType = eSpatialFilterType(m_Fractorium->m_SpatialFilterTypeCombo->currentIndex());
ember.m_TemporalFilterWidth = m_Fractorium->m_TemporalFilterWidthSpin->value();
ember.m_TemporalFilterType = eTemporalFilterType(m_Fractorium->m_TemporalFilterTypeCombo->currentIndex());
ember.m_MinRadDE = m_Fractorium->m_DEFilterMinRadiusSpin->value();
ember.m_MaxRadDE = m_Fractorium->m_DEFilterMaxRadiusSpin->value();
ember.m_CurveDE = m_Fractorium->m_DECurveSpin->value();
ember.m_SubBatchSize = m_Fractorium->m_SbsSpin->value();
ember.m_FuseCount = m_Fractorium->m_FuseSpin->value();
ember.m_RandPointRange = m_Fractorium->m_RandRangeSpin->value();
ember.m_Quality = m_Fractorium->m_QualitySpin->value();
ember.m_Supersample = m_Fractorium->m_SupersampleSpin->value();
ember.m_AffineInterp = eAffineInterp(m_Fractorium->m_AffineInterpTypeCombo->currentIndex());
ember.m_Interp = eInterp(m_Fractorium->m_InterpTypeCombo->currentIndex());
ember.SyncSize();
}
///
/// Set the rotation.
/// This updates the spinner, optionally stealth.
///
/// The rotation value in angles to set
/// True if stealth to skip re-rendering, else false to trigger a new render
void Fractorium::SetRotation(double rot, bool stealth)
{
if (stealth)
m_RotateSpin->SetValueStealth(rot);
else
m_RotateSpin->setValue(rot);
}
///
/// Set the scale.
/// This is the number of raster pixels that correspond to the distance
/// between 0-1 in the cartesian plane. The higher the number, the more
/// zoomed in the image is.
/// Resets the rendering process.
///
/// The scale value
void Fractorium::SetScale(double scale)
{
m_ScaleSpin->setValue(scale);
}
template class FractoriumEmberController;
#ifdef DO_DOUBLE
template class FractoriumEmberController;
#endif