#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.05, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnBrightnessChanged(double)), true, 4.0, 4.0, 4.0); SetupSpinner(table, this, row, 1, m_GammaSpin, spinHeight, 1, 9999, 0.5, SIGNAL(valueChanged(double)), SLOT(OnGammaChanged(double)), true, 4.0, 4.0, 4.0); SetupSpinner(table, this, row, 1, m_GammaThresholdSpin, spinHeight, 0, 10, 0.01, SIGNAL(valueChanged(double)), SLOT(OnGammaThresholdChanged(double)), true, 0.1, 0.1, 0.0); SetupSpinner(table, this, row, 1, m_VibrancySpin, spinHeight, 0, 30, 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); m_GammaThresholdSpin->setDecimals(4); 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)); //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, -180, 180, 1, SIGNAL(valueChanged(double)), SLOT(OnPitchChanged(double)), true, 0, 45, 0); SetupSpinner(table, this, row, 1, m_YawSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(double)), SLOT(OnYawChanged(double)), true, 0, 45, 0); SetupSpinner(table, this, row, 1, m_DepthBlurSpin, spinHeight, -100, 100, 0.01, SIGNAL(valueChanged(double)), SLOT(OnDepthBlurChanged(double)), true, 0, 1, 0); //Set w/h max values. m_CenterXSpin->setDecimals(3); m_CenterYSpin->setDecimals(3); m_ZPosSpin->setDecimals(3); m_PerspectiveSpin->setDecimals(4); m_DepthBlurSpin->setDecimals(3); //Filter. row = 0; table = ui.FilterTable; SetupSpinner(table, this, row, 1, m_SpatialFilterWidthSpin, spinHeight, 0.1, 2, 0.1, SIGNAL(valueChanged(double)), SLOT(OnSpatialFilterWidthChanged(double)), true, 1.0, 1.0, 1.0); comboVals = SpatialFilterCreator::FilterTypes(); SetupCombo(table, this, row, 1, m_SpatialFilterTypeCombo, comboVals, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnSpatialFilterTypeComboCurrentIndexChanged(const QString&))); 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); //Iteration. row = 0; table = ui.IterationTable; 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, 15, 15); SetupSpinner(table, this, row, 1, m_QualitySpin, spinHeight, 1, dmax, 50, SIGNAL(valueChanged(double)), SLOT(OnQualityChanged(double)), true, 10, 10, 10); SetupSpinner( table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 1, 1, 1); //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_TemporalSamplesSpin, spinHeight, 1, 5000, 1, SIGNAL(valueChanged(int)), SLOT(OnTemporalSamplesChanged(int)), true, 1000); 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&))); } /// /// 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) { 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) { 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) { 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) { 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) { 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); } /// /// Show the color selection dialog. /// Called when background color button is clicked. /// /// Ignored void Fractorium::OnBackgroundColorButtonClicked(bool checked) { m_ColorDialog->show(); } /// /// 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) { int itemRow = 5; 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) { //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) { 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. /// /// /// Placeholder, do nothing. /// Dimensions are set automatically to match the dimensions of GLWidget. /// /// Ignored template void FractoriumEmberController::WidthChanged(uint i) { UpdateAll([&](Ember& ember) { ember.m_FinalRasW = i; }, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll()); } void Fractorium::OnWidthChanged(int i) { m_Controller->WidthChanged(i); } /// /// Placeholder, do nothing. /// Dimensions are set automatically to match the dimensions of GLWidget. /// /// Ignored template void FractoriumEmberController::HeightChanged(uint i) { UpdateAll([&](Ember& ember) { ember.m_FinalRasH = i; }, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll()); } void Fractorium::OnHeightChanged(int i) { m_Controller->HeightChanged(i); } /// /// 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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 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. /// In the future, when animation is implemented, this will have an effect. /// /// The temporal filter width template void FractoriumEmberController::TemporalFilterWidthChanged(double d) { UpdateAll([&](Ember& ember) { ember.m_TemporalFilterWidth = d; }, true, 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. /// In the future, when animation is implemented, this will have an effect. /// /// The name of the temporal filter template void FractoriumEmberController::TemporalFilterTypeChanged(const QString& text) { UpdateAll([&](Ember& ember) { ember.m_TemporalFilterType = TemporalFilterCreator::FromString(text.toStdString()); }, true, 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 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_Fractorium->m_DEFilterMinRadiusSpin->value() > m_Fractorium->m_DEFilterMaxRadiusSpin->value()) { m_Fractorium->m_DEFilterMinRadiusSpin->SetValueStealth(m_Fractorium->m_DEFilterMaxRadiusSpin->value()); return; } UpdateAll([&](Ember& ember) { ember.m_MinRadDE = d; }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); } void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) { m_Controller->DEFilterMinRadiusWidthChanged(d); } /// /// 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_Fractorium->m_DEFilterMaxRadiusSpin->value() < m_Fractorium->m_DEFilterMinRadiusSpin->value()) { m_Fractorium->m_DEFilterMaxRadiusSpin->SetValueStealth(m_Fractorium->m_DEFilterMinRadiusSpin->value()); return; } UpdateAll([&](Ember& ember) { ember.m_MaxRadDE = d; }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); } void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d) { m_Controller->DEFilterMaxRadiusWidthChanged(d); } /// /// 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) { 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) { ember.m_SubBatchSize = d; }, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll()); } void Fractorium::OnSbsChanged(int d) { m_Controller->SbsChanged(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) { 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) { ember.m_Supersample = d; }, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll()); } void Fractorium::OnSupersampleChanged(int d) { m_Controller->SupersampleChanged(d); } /// /// Set the temporal samples to be used with animation. /// Called when the temporal samples spinner is changed. /// Does not reset anything because this is only used for animation. /// In the future, when animation is implemented, this will have an effect. /// /// The temporal samples value template void FractoriumEmberController::TemporalSamplesChanged(int i) { UpdateAll([&](Ember& ember) { ember.m_TemporalSamples = i; }, true, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented. } void Fractorium::OnTemporalSamplesChanged(int d) { m_Controller->TemporalSamplesChanged(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) { if (i == 0) ember.m_AffineInterp = eAffineInterp::AFFINE_INTERP_LINEAR; else if (i == 1) ember.m_AffineInterp = eAffineInterp::AFFINE_INTERP_LOG; else ember.m_AffineInterp = eAffineInterp::AFFINE_INTERP_LINEAR; }, true, 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. /// In the future, when animation is implemented, this will have an effect. /// Called when the interp type combo box index is changed. /// /// The index template void FractoriumEmberController::InterpTypeChanged(int i) { UpdateAll([&](Ember& ember) { if (i == 0) ember.m_Interp = eInterp::EMBER_INTERP_LINEAR; else if (i == 1) ember.m_Interp = eInterp::EMBER_INTERP_SMOOTH; else ember.m_Interp = eInterp::EMBER_INTERP_LINEAR; }, true, eProcessAction::NOTHING, m_Fractorium->ApplyAll()); } void Fractorium::OnInterpTypeComboCurrentIndexChanged(int index) { m_Controller->InterpTypeChanged(index); } /// /// 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) { 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_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(5, 1)->setBackgroundColor(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_QualitySpin->SetValueStealth(m_Ember.m_Quality); m_Fractorium->m_SupersampleSpin->SetValueStealth(m_Ember.m_Supersample); m_Fractorium->m_TemporalSamplesSpin->SetValueStealth(m_Ember.m_TemporalSamples); m_Fractorium->m_AffineInterpTypeCombo->SetCurrentIndexStealth(int(m_Ember.m_AffineInterp)); m_Fractorium->m_InterpTypeCombo->SetCurrentIndexStealth(int(m_Ember.m_Interp)); //Palette related items: //The temp palette is assigned the palette read when the file was parsed/saved. The user can apply adjustments on the GUI later. //These adjustments will be applied to the temp palette, then assigned back to m_Ember.m_Palette. //Normally, the temp palette is assigned whenever the user clicks on a palette cell. But since this is not //called in response to that event, it is skipped here so must do it manually. m_TempPalette = m_Ember.m_Palette; //Palette controls are reset on each ember load. This means that if the palette was adjusted, saved, the selected ember //changed to another, then back, the previously adjusted palette will now be considered the base, and all adjustments set to 0. //To fix this, the caller must preserve the temp palette and the adjustment values and reassign. See Fractorium::CreateControllerFromOptions() //for an example. m_Fractorium->ResetPaletteControls(); auto temp = m_Ember.m_Palette.m_Filename; if (temp.get()) m_Fractorium->SetPaletteFileComboIndex(*temp.get()); //Update the palette preview widget. //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. UpdateAdjustedPaletteGUI(m_Ember.m_Palette);//Updating the palette GUI will trigger a full render. } /// /// 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_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_Quality = m_Fractorium->m_QualitySpin->value(); ember.m_Supersample = m_Fractorium->m_SupersampleSpin->value(); ember.m_TemporalSamples = m_Fractorium->m_TemporalSamplesSpin->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