#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. /// True to get just spatial and density filters plus coloring params. template void FractoriumEmberController::ParamsToEmber(Ember& ember, bool imageParamsOnly) { ParamsToEmberPrivate(ember, imageParamsOnly); } #ifdef DO_DOUBLE template void FractoriumEmberController::ParamsToEmber(Ember& ember, bool imageParamsOnly) { ParamsToEmberPrivate(ember, imageParamsOnly); } #endif template template void FractoriumEmberController::ParamsToEmberPrivate(Ember& ember, bool imageParamsOnly) { 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_SpatialFilterRadius = m_Fractorium->m_SpatialFilterWidthSpin->value();//Filter. ember.m_SpatialFilterType = eSpatialFilterType(m_Fractorium->m_SpatialFilterTypeCombo->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(); if (imageParamsOnly) return; ember.m_TemporalFilterWidth = m_Fractorium->m_TemporalFilterWidthSpin->value(); ember.m_TemporalFilterType = eTemporalFilterType(m_Fractorium->m_TemporalFilterTypeCombo->currentIndex()); auto color = m_Fractorium->ui.ColorTable->item(5, 1)->backgroundColor(); 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_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