2023-04-25 19:59:54 -04:00
# include " FractoriumPch.h "
# include "Fractorium.h"
/// <summary>
/// Initialize the xforms UI.
/// </summary>
void Fractorium : : InitXformsUI ( )
{
const int spinHeight = 20 ;
auto row = 0 ;
connect ( ui . AddXformButton , SIGNAL ( clicked ( bool ) ) , this , SLOT ( OnAddXformButtonClicked ( bool ) ) , Qt : : QueuedConnection ) ;
connect ( ui . AddLinkedXformButton , SIGNAL ( clicked ( bool ) ) , this , SLOT ( OnAddLinkedXformButtonClicked ( bool ) ) , Qt : : QueuedConnection ) ;
connect ( ui . DuplicateXformButton , SIGNAL ( clicked ( bool ) ) , this , SLOT ( OnDuplicateXformButtonClicked ( bool ) ) , Qt : : QueuedConnection ) ;
connect ( ui . ClearXformButton , SIGNAL ( clicked ( bool ) ) , this , SLOT ( OnClearXformButtonClicked ( bool ) ) , Qt : : QueuedConnection ) ;
connect ( ui . DeleteXformButton , SIGNAL ( clicked ( bool ) ) , this , SLOT ( OnDeleteXformButtonClicked ( bool ) ) , Qt : : QueuedConnection ) ;
connect ( ui . AddFinalXformButton , SIGNAL ( clicked ( bool ) ) , this , SLOT ( OnAddFinalXformButtonClicked ( bool ) ) , Qt : : QueuedConnection ) ;
connect ( ui . CurrentXformCombo , SIGNAL ( currentIndexChanged ( int ) ) , this , SLOT ( OnCurrentXformComboChanged ( int ) ) , Qt : : QueuedConnection ) ;
connect ( ui . AnimateXformCheckBox , SIGNAL ( stateChanged ( int ) ) , this , SLOT ( OnXformAnimateCheckBoxStateChanged ( int ) ) , Qt : : QueuedConnection ) ;
SetFixedTableHeader ( ui . XformWeightNameTable - > horizontalHeader ( ) , QHeaderView : : ResizeToContents ) ;
//Use SetupSpinner() just to create the spinner, but use col of -1 to prevent it from being added to the table.
SetupSpinner < DoubleSpinBox , double > ( ui . XformWeightNameTable , this , row , - 1 , m_XformWeightSpin , spinHeight , 0 , 1000 , 0.05 , SIGNAL ( valueChanged ( double ) ) , SLOT ( OnXformWeightChanged ( double ) ) , false , 0 , 1 , 0 ) ;
m_XformWeightSpin - > setDecimals ( 3 ) ;
m_XformWeightSpin - > SmallStep ( 0.001 ) ;
m_XformWeightSpin - > setMinimumWidth ( 40 ) ;
m_XformWeightSpinnerButtonWidget = new SpinnerLabelButtonWidget ( m_XformWeightSpin , " = " , 20 , 19 , ui . XformWeightNameTable ) ;
m_XformWeightSpinnerButtonWidget - > m_SpinBox - > setSizePolicy ( QSizePolicy : : Expanding , QSizePolicy : : Fixed ) ;
m_XformWeightSpinnerButtonWidget - > m_Label - > setStyleSheet ( " border: 0px; " ) ;
m_XformWeightSpinnerButtonWidget - > m_Label - > setSizePolicy ( QSizePolicy : : Preferred , QSizePolicy : : Fixed ) ;
m_XformWeightSpinnerButtonWidget - > m_Button - > setToolTip ( " Equalize weights " ) ;
m_XformWeightSpinnerButtonWidget - > m_Button - > setStyleSheet ( " text-align: center center " ) ;
m_XformWeightSpinnerButtonWidget - > setMaximumWidth ( 130 ) ;
connect ( m_XformWeightSpinnerButtonWidget - > m_Button , SIGNAL ( clicked ( bool ) ) , this , SLOT ( OnEqualWeightButtonClicked ( bool ) ) , Qt : : QueuedConnection ) ;
ui . XformWeightNameTable - > setCellWidget ( 0 , 0 , m_XformWeightSpinnerButtonWidget ) ;
m_XformNameEdit = new QLineEdit ( ui . XformWeightNameTable ) ;
ui . XformWeightNameTable - > setCellWidget ( 0 , 1 , m_XformNameEdit ) ;
connect ( m_XformNameEdit , SIGNAL ( textChanged ( const QString & ) ) , this , SLOT ( OnXformNameChanged ( const QString & ) ) , Qt : : QueuedConnection ) ;
ui . CurrentXformCombo - > view ( ) - > setMinimumWidth ( 100 ) ;
ui . CurrentXformCombo - > view ( ) - > setMaximumWidth ( 500 ) ;
//ui.CurrentXformCombo->view()->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
ui . CurrentXformCombo - > view ( ) - > setSizeAdjustPolicy ( QAbstractScrollArea : : SizeAdjustPolicy : : AdjustToContents ) ;
# ifndef _WIN32
//For some reason linux makes these 24x24, even though the designer explicitly says 16x16.
ui . AddXformButton - > setIconSize ( QSize ( 16 , 16 ) ) ;
ui . DuplicateXformButton - > setIconSize ( QSize ( 16 , 16 ) ) ;
ui . ClearXformButton - > setIconSize ( QSize ( 16 , 16 ) ) ;
ui . DeleteXformButton - > setIconSize ( QSize ( 16 , 16 ) ) ;
ui . AddFinalXformButton - > setIconSize ( QSize ( 16 , 16 ) ) ;
ui . CurrentXformCombo - > setIconSize ( QSize ( 16 , 16 ) ) ;
# endif
}
/// <summary>
/// Get the current xform.
/// </summary>
/// <returns>The current xform as specified by the current xform combo box index. nullptr if out of range (should never happen).</returns>
template < typename T >
Xform < T > * FractoriumEmberController < T > : : CurrentXform ( )
{
return m_Ember . GetTotalXform ( m_Fractorium - > ui . CurrentXformCombo - > currentIndex ( ) , m_Fractorium - > HaveFinal ( ) ) ; //Need to force final for the special case they created a final, then cleared it, but did not delete it.
}
/// <summary>
/// Set the current xform to the index passed in.
/// </summary>
/// <param name="i">The index to set the current xform to</param>
void Fractorium : : CurrentXform ( uint i )
{
if ( i < static_cast < uint > ( ui . CurrentXformCombo - > count ( ) ) )
ui . CurrentXformCombo - > setCurrentIndex ( i ) ;
}
/// <summary>
/// Set the current xform and populate all GUI widgets.
/// Called when the current xform combo box index changes.
/// </summary>
/// <param name="index">The selected combo box index</param>
template < typename T >
void FractoriumEmberController < T > : : CurrentXformComboChanged ( int index )
{
bool forceFinal = m_Fractorium - > HaveFinal ( ) ;
if ( auto xform = m_Ember . GetTotalXform ( index , forceFinal ) )
{
FillWithXform ( xform ) ;
m_GLController - > SetSelectedXform ( xform ) ;
m_Fractorium - > ui . SoloXformCheckBox - > blockSignals ( true ) ;
m_Fractorium - > ui . SoloXformCheckBox - > setChecked ( m_Ember . m_Solo = = index ) ;
m_Fractorium - > ui . SoloXformCheckBox - > blockSignals ( false ) ;
const bool enable = ! IsFinal ( CurrentXform ( ) ) ;
m_Fractorium - > ui . DuplicateXformButton - > setEnabled ( enable ) ;
m_Fractorium - > m_XformWeightSpin - > setEnabled ( enable ) ;
m_Fractorium - > ui . SoloXformCheckBox - > setEnabled ( enable ) ;
m_Fractorium - > ui . AddLinkedXformButton - > setEnabled ( enable ) ;
m_Fractorium - > ui . AddFinalXformButton - > setEnabled ( ! m_Ember . UseFinalXform ( ) ) ;
}
}
void Fractorium : : OnCurrentXformComboChanged ( int index ) { m_Controller - > CurrentXformComboChanged ( index ) ; }
/// <summary>
/// Add an empty xform in the current ember and set it as the current xform.
/// Called when the add xform button is clicked.
/// Resets the rendering process.
/// </summary>
/// <param name="checked">Ignored</param>
template < typename T >
void FractoriumEmberController < T > : : AddXform ( )
{
bool forceFinal = m_Fractorium - > HaveFinal ( ) ;
Update ( [ & ] ( )
{
Xform < T > newXform ;
newXform . m_Weight = 0.25 ;
newXform . m_ColorX = m_Rand . Frand01 < T > ( ) ;
newXform . AddVariation ( m_VariationList - > GetVariationCopy ( eVariationId : : VAR_LINEAR ) ) ;
m_Ember . AddXform ( newXform ) ;
const int index = static_cast < int > ( m_Ember . TotalXformCount ( forceFinal ) - ( forceFinal ? 2 : 1 ) ) ; //Set index to the last item before final.
FillXforms ( index ) ;
} ) ;
}
void Fractorium : : OnAddXformButtonClicked ( bool checked ) { m_Controller - > AddXform ( ) ; }
/// <summary>
/// Add a new linked xform in the current ember and set it as the current xform.
/// Linked means:
/// Add an xform whose xaos values are:
/// From: All xaos values from the current xform are zero when going to any xform but the new one added, which is 1.
/// To: The xaos value coming from the current xform is 1 and the xaos values from all other xforms are 0, when going to the newly added xform.
/// Take different action when a single xform is selected vs. multiple.
/// Single: Copy the current xform's xaos values to the new one.
/// Multiple: Set the new xform's xaos values to 1, and except the last entry which is 0.
/// Called when the add xform button is clicked.
/// Resets the rendering process.
/// </summary>
/// <param name="checked">Ignored</param>
template < typename T >
void FractoriumEmberController < T > : : AddLinkedXform ( )
{
bool hasAdded = false ;
const auto forceFinal = m_Fractorium - > HaveFinal ( ) ;
auto selCount = m_Fractorium - > SelectedXformCount ( false ) ;
if ( ! selCount ) //If none explicitly selected, use current.
selCount = 1 ;
Ember < T > ember = m_Ember ;
m_Ember . Reserve ( m_Ember . XformCount ( ) + 1 ) ; //Doing this ahead of time ensures pointers remain valid.
auto iterCount = 0 ;
UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
//Covers very strange case where final is selected, but initially not considered because final is excluded,
//but after adding the new xform, it thinks its selected index is non-final.
if ( iterCount < selCount )
{
size_t i , count = m_Ember . XformCount ( ) ;
if ( ! hasAdded )
{
Xform < T > newXform ;
newXform . m_Weight = 0.5 ;
newXform . m_Opacity = xform - > m_Opacity ;
newXform . m_ColorSpeed = 0 ;
newXform . m_ColorX = 0 ;
//newXform.m_ColorY = xform->m_ColorY;
newXform . AddVariation ( m_VariationList - > GetVariationCopy ( eVariationId : : VAR_LINEAR ) ) ;
//Set all of the new xform's xaos values to the selected xform's xaos values,
//then set the selected xform's xaos values to 0.
for ( i = 0 ; i < count ; i + + )
{
if ( selCount = = 1 )
newXform . SetXaos ( i , xform - > Xaos ( i ) ) ;
else
newXform . SetXaos ( i , 1 ) ;
}
//Add the new xform and update the total count.
m_Ember . AddXform ( newXform ) ;
count = m_Ember . XformCount ( ) ;
//Set the xaos for all xforms pointing to the new one to zero.
//Will set the last element of all linking and non-linking xforms, including the one we just added.
//Linking xforms will have their last xaos element set to 1 below.
for ( i = 0 ; i < count ; i + + )
if ( auto xf = m_Ember . GetXform ( i ) )
xf - > SetXaos ( count - 1 , 0 ) ;
hasAdded = true ;
}
//Linking xform, so set all xaos elements to 0, except the last.
for ( i = 0 ; i < count - 1 ; i + + )
xform - > SetXaos ( i , 0 ) ;
xform - > SetXaos ( count - 1 , 1 ) ; //Set the xaos value for the linking xform pointing to the new one to one.
xform - > m_Opacity = 0 ; //Clear the opacity of the all linking xform.
iterCount + + ;
}
} , eXformUpdate : : UPDATE_SELECTED_EXCEPT_FINAL ) ;
//Now update the GUI.
const auto index = static_cast < int > ( m_Ember . TotalXformCount ( forceFinal ) - ( forceFinal ? 2 : 1 ) ) ; //Set index to the last item before final.
FillXforms ( index ) ;
FillXaos ( ) ;
}
void Fractorium : : OnAddLinkedXformButtonClicked ( bool checked ) { m_Controller - > AddLinkedXform ( ) ; }
/// <summary>
/// Add a vector of xforms to the passed in ember, and optionally preserve the xaos based on position.
/// </summary>
/// <param name="ember">The ember to add xforms to</param>
/// <param name="xforms">The vector of xforms to add</param>
/// <param name="eXaosPasteStyle">The method which governs how the copying of xaos values is handles</param>
template < typename T >
void FractoriumEmberController < T > : : AddXformsWithXaos ( Ember < T > & ember , std : : vector < std : : pair < Xform < T > , size_t > > & xforms , eXaosPasteStyle pastestyle )
{
const auto oldxfcount = ember . XformCount ( ) ;
for ( auto & it : xforms )
{
ember . AddXform ( it . first ) ;
const auto newxfcount = ember . XformCount ( ) - 1 ;
auto * newxform = ember . GetXform ( newxfcount ) ;
for ( size_t i = 0 ; i < oldxfcount ; i + + )
{
if ( auto xform = ember . GetXform ( i ) )
{
switch ( pastestyle )
{
case EmberCommon : : eXaosPasteStyle : : NONE :
newxform - > SetXaos ( i , 1 ) ;
xform - > SetXaos ( newxfcount , 1 ) ;
break ;
case EmberCommon : : eXaosPasteStyle : : ZERO_TO_ONE :
case EmberCommon : : eXaosPasteStyle : : ZERO_TO_VALS :
newxform - > SetXaos ( i , 0 ) ;
xform - > SetXaos ( newxfcount , 0 ) ;
break ;
case EmberCommon : : eXaosPasteStyle : : ONE_TO_VALS :
newxform - > SetXaos ( i , 1 ) ;
xform - > SetXaos ( newxfcount , 1 ) ;
break ;
case EmberCommon : : eXaosPasteStyle : : VALS_TO_ONE :
newxform - > SetXaos ( i , it . first . Xaos ( i ) ) ;
xform - > SetXaos ( newxfcount , xform - > Xaos ( it . second ) ) ;
break ;
default :
break ;
}
}
}
}
for ( size_t i = oldxfcount ; i < ember . XformCount ( ) ; i + + )
{
if ( auto xform = ember . GetXform ( i ) )
{
for ( size_t j = oldxfcount ; j < ember . XformCount ( ) ; j + + )
{
switch ( pastestyle )
{
case EmberCommon : : eXaosPasteStyle : : NONE :
case EmberCommon : : eXaosPasteStyle : : ZERO_TO_ONE :
xform - > SetXaos ( j , 1 ) ;
break ;
case EmberCommon : : eXaosPasteStyle : : ZERO_TO_VALS :
case EmberCommon : : eXaosPasteStyle : : ONE_TO_VALS :
xform - > SetXaos ( j , xforms [ i - oldxfcount ] . first . Xaos ( j - oldxfcount ) ) ;
break ;
case EmberCommon : : eXaosPasteStyle : : VALS_TO_ONE :
xform - > SetXaos ( j , 1 ) ;
break ;
default :
break ;
}
}
}
}
}
/// <summary>
/// Duplicate the specified xforms in the current ember, and set the last one as the current xform.
/// If xaos is present in the ember, the duplicated xforms will be added with xaos preserved, else they'll just be added normally.
/// The manner in which xaos is preserved is altered when ctrl is pressed.
/// Called when the duplicate xform button is clicked.
/// Resets the rendering process.
/// </summary>
/// <param name="checked">Ignored</param>
template < typename T >
void FractoriumEmberController < T > : : DuplicateXform ( )
{
bool forceFinal = m_Fractorium - > HaveFinal ( ) ;
const bool ctrl = QGuiApplication : : keyboardModifiers ( ) . testFlag ( Qt : : ControlModifier ) ;
vector < std : : pair < Xform < T > , size_t > > vec ;
vec . reserve ( m_Ember . XformCount ( ) ) ;
UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
vec . emplace_back ( * xform , xfindex ) ;
} , eXformUpdate : : UPDATE_SELECTED_EXCEPT_FINAL , false ) ;
Update ( [ & ] ( )
{
AddXformsWithXaos ( m_Ember , vec , m_Fractorium - > GetXaosPasteStyleType ( ) ) ;
int index = int ( m_Ember . TotalXformCount ( forceFinal ) - ( forceFinal ? 2 : 1 ) ) ; //Set index to the last item before final.
FillXforms ( index ) ; //Handles xaos.
} ) ;
}
void Fractorium : : OnDuplicateXformButtonClicked ( bool checked ) { m_Controller - > DuplicateXform ( ) ; }
/// <summary>
/// Clear all variations from the selected xforms. Affine, palette and xaos are left untouched.
/// Called when the clear xform button is clicked.
/// Resets the rendering process.
/// </summary>
/// <param name="checked">Ignored</param>
template < typename T >
void FractoriumEmberController < T > : : ClearXform ( )
{
UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
xform - > ClearAndDeleteVariations ( ) ; //Note xaos is left alone.
} , eXformUpdate : : UPDATE_SELECTED ) ;
FillVariationTreeWithCurrentXform ( ) ;
}
void Fractorium : : OnClearXformButtonClicked ( bool checked ) { m_Controller - > ClearXform ( ) ; }
/// <summary>
/// Delete the selected xforms.
/// Cache a copy of the final xform if it's been selected for removal.
/// Will not delete the last remaining non-final xform.
/// Called when the delete xform button is clicked.
/// Resets the rendering process.
/// </summary>
/// <param name="checked">Ignored</param>
template < typename T >
void FractoriumEmberController < T > : : DeleteXforms ( )
{
bool removed = false ;
bool anyChecked = false ;
const bool haveFinal = m_Fractorium - > HaveFinal ( ) ;
const auto combo = m_Fractorium - > ui . CurrentXformCombo ;
Xform < T > * finalXform = nullptr ;
vector < Xform < T > > xformsToKeep ;
xformsToKeep . reserve ( m_Ember . TotalXformCount ( ) ) ;
//Iterating over the checkboxes must be done instead of using UpdateXform() to iterate over xforms
//because xforms are being deleted inside the loop.
//Also manually calling UpdateRender() rather than using the usual Update() call because
//it should only be called if an xform has actually been deleted.
//Rather than go through and delete, it's easier to just make a list of what we want to keep.
m_Fractorium - > ForEachXformCheckbox ( [ & ] ( int i , QCheckBox * w , bool isFinal )
{
if ( ! w - > isChecked ( ) ) //Keep if not checked.
{
if ( isFinal )
finalXform = m_Ember . NonConstFinalXform ( ) ;
else if ( const auto xform = m_Ember . GetXform ( i ) )
xformsToKeep . push_back ( * xform ) ;
}
else
anyChecked = true ; //At least one was selected for removal.
} ) ;
//They might not have selected any checkboxes, in which case just delete the current.
const auto current = combo - > currentIndex ( ) ;
const auto totalCount = m_Ember . TotalXformCount ( ) ;
const auto keepFinal = finalXform & & haveFinal ;
//Nothing was selected, so just delete current.
if ( ! anyChecked )
{
//Disallow deleting the only remaining non-final xform.
if ( ! ( haveFinal & & totalCount < = 2 & & current = = 0 ) & & //One non-final, one final, disallow deleting non-final.
! ( ! haveFinal & & totalCount = = 1 ) ) //One non-final, no final, disallow deleting.
{
if ( haveFinal & & m_Ember . IsFinalXform ( CurrentXform ( ) ) ) //Is final the current?
m_Ember . m_CachedFinal = * m_Ember . FinalXform ( ) ; //Keep a copy in case the user wants to re-add the final.
m_Ember . DeleteTotalXform ( current , haveFinal ) ; //Will cover the case of current either being final or non-final.
removed = true ;
}
}
else
{
if ( ! xformsToKeep . empty ( ) ) //Remove if they requested to do so, but ensure it's not removing all.
{
removed = true ;
m_Ember . ReplaceXforms ( xformsToKeep ) ; //Replace with only those they chose to keep (the inverse of what was checked).
}
else //They selected all to delete, which is not allowed, so just keep the first xform.
{
removed = true ;
while ( m_Ember . XformCount ( ) > 1 )
m_Ember . DeleteXform ( m_Ember . XformCount ( ) - 1 ) ;
}
if ( ! keepFinal ) //They selected final to delete.
{
removed = true ;
m_Ember . m_CachedFinal = * m_Ember . FinalXform ( ) ; //Keep a copy in case the user wants to re-add the final.
m_Ember . NonConstFinalXform ( ) - > Clear ( ) ;
}
}
if ( removed )
{
const auto index = static_cast < int > ( m_Ember . TotalXformCount ( ) - ( m_Ember . UseFinalXform ( ) ? 2 : 1 ) ) ; //Set index to the last item before final. Note final is requeried one last time.
FillXforms ( index ) ;
UpdateRender ( ) ;
m_Fractorium - > ui . GLDisplay - > repaint ( ) ; //Force update because for some reason it doesn't always happen.
}
}
void Fractorium : : OnDeleteXformButtonClicked ( bool checked ) { m_Controller - > DeleteXforms ( ) ; }
/// <summary>
/// Add a final xform to the ember and set it as the current xform.
/// Will only take action if a final xform is not already present.
/// Will re-add a copy of the last used final xform for the current ember if one had already been added then removed.
/// Called when the add final xform button is clicked.
/// Resets the rendering process.
/// </summary>
/// <param name="checked">Ignored</param>
template < typename T >
void FractoriumEmberController < T > : : AddFinalXform ( )
{
//Check to see if a final xform is already present.
if ( ! m_Fractorium - > HaveFinal ( ) )
{
Update ( [ & ] ( )
{
auto & final = m_Ember . m_CachedFinal ;
final . m_Animate = 0 ;
if ( final . Empty ( ) )
final . AddVariation ( m_VariationList - > GetVariationCopy ( eVariationId : : VAR_LINEAR ) ) ; //Just a placeholder so other parts of the code don't see it as being empty.
m_Ember . SetFinalXform ( final ) ;
const auto index = static_cast < int > ( m_Ember . TotalXformCount ( ) - 1 ) ; //Set index to the last item.
FillXforms ( index ) ;
} ) ;
}
}
void Fractorium : : OnAddFinalXformButtonClicked ( bool checked ) { m_Controller - > AddFinalXform ( ) ; }
/// <summary>
/// Set the weight of the selected xforms.
/// Called when weight spinner changes.
/// Resets the rendering process.
/// </summary>
/// <param name="d">The weight</param>
template < typename T >
void FractoriumEmberController < T > : : XformWeightChanged ( double d )
{
UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
xform - > m_Weight = d ;
} , eXformUpdate : : UPDATE_SELECTED_EXCEPT_FINAL ) ;
SetNormalizedWeightText ( CurrentXform ( ) ) ;
FillAppliedXaos ( ) ;
}
void Fractorium : : OnXformWeightChanged ( double d ) { m_Controller - > XformWeightChanged ( d ) ; }
/// <summary>
/// Equalize the weights of all xforms in the ember.
/// </summary>
template < typename T >
void FractoriumEmberController < T > : : EqualizeWeights ( )
{
UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
m_Ember . EqualizeWeights ( ) ;
m_Fractorium - > m_XformWeightSpin - > setValue ( xform - > m_Weight ) ; //Will trigger an update, so pass false to updateRender below.
} , eXformUpdate : : UPDATE_CURRENT , false ) ;
FillAppliedXaos ( ) ;
}
void Fractorium : : OnEqualWeightButtonClicked ( bool checked ) { m_Controller - > EqualizeWeights ( ) ; }
/// <summary>
/// Set the name of the current xform.
/// Update the corresponding xform checkbox text with the name.
/// Called when the user types in the name cell of the table.
/// </summary>
/// <param name="s">The text of the cell</param>
template < typename T >
void FractoriumEmberController < T > : : XformNameChanged ( const QString & s )
{
const auto forceFinal = m_Fractorium - > HaveFinal ( ) ;
UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
xform - > m_Name = s . toStdString ( ) ;
XformCheckboxAt ( static_cast < int > ( xfindex ) , [ & ] ( QCheckBox * checkbox ) { checkbox - > setText ( MakeXformCaption ( xfindex ) ) ; } ) ;
} , eXformUpdate : : UPDATE_CURRENT , false ) ;
FillSummary ( ) ; //Manually update because this does not trigger a render, which is where this would normally be called.
m_Fractorium - > FillXaosTable ( ) ;
}
void Fractorium : : OnXformNameChanged ( const QString & s )
{
m_Controller - > XformNameChanged ( s ) ;
m_Controller - > UpdateXformName ( ui . CurrentXformCombo - > currentIndex ( ) ) ;
}
/// <summary>
/// Set the animate field of the selected xforms, this allows excluding current if it's not checked, but applies only to it if none are checked.
/// This has no effect on interactive rendering, it only sets a value
/// that will later be saved to Xml when the user saves.
/// This value is observed when creating sequences for animation.
/// Applies to all embers if "Apply All" is checked.
/// Called when the user toggles the animate xform checkbox.
/// </summary>
/// <param name="state">1 for checked, else false</param>
template < typename T >
void FractoriumEmberController < T > : : XformAnimateChanged ( int state )
{
T animate = state > 0 ? 1 : 0 ;
UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
bool final = IsFinal ( xform ) ;
UpdateAll ( [ & ] ( Ember < T > & ember , bool isMain )
{
if ( final ) //If the current xform was final, only apply to other embers which also have a final xform.
{
if ( ember . UseFinalXform ( ) )
{
auto xform = ember . NonConstFinalXform ( ) ;
xform - > m_Animate = animate ;
}
if ( ! m_Fractorium - > ApplyAll ( ) )
if ( m_EmberFilePointer & & m_EmberFilePointer - > UseFinalXform ( ) )
m_EmberFilePointer - > NonConstFinalXform ( ) - > m_Animate = animate ;
}
else //Current was not final, so apply to other embers which have a non-final xform at this index.
{
if ( auto xform = ember . GetXform ( xfindex ) )
xform - > m_Animate = animate ;
if ( ! m_Fractorium - > ApplyAll ( ) & & m_EmberFilePointer )
if ( auto xform = m_EmberFilePointer - > GetXform ( xfindex ) )
xform - > m_Animate = animate ;
}
} , false , eProcessAction : : NOTHING , m_Fractorium - > ApplyAll ( ) ) ;
} , eXformUpdate : : UPDATE_SELECTED , false ) ;
}
void Fractorium : : OnXformAnimateCheckBoxStateChanged ( int state ) { m_Controller - > XformAnimateChanged ( state ) ; }
/// <summary>
/// Fill all GUI widgets with values from the passed in xform.
/// </summary>
/// <param name="xform">The xform whose values will be used to populate the widgets</param>
template < typename T >
void FractoriumEmberController < T > : : FillWithXform ( Xform < T > * xform )
{
m_Fractorium - > m_XformWeightSpin - > SetValueStealth ( xform - > m_Weight ) ;
SetNormalizedWeightText ( xform ) ;
m_Fractorium - > ui . AnimateXformCheckBox - > blockSignals ( true ) ;
m_Fractorium - > ui . AnimateXformCheckBox - > setChecked ( xform - > m_Animate > 0 ? true : false ) ;
m_Fractorium - > ui . AnimateXformCheckBox - > blockSignals ( false ) ;
if ( const auto item = m_Fractorium - > ui . XformWeightNameTable - > item ( 0 , 1 ) )
{
m_Fractorium - > m_XformNameEdit - > blockSignals ( true ) ;
m_Fractorium - > m_XformNameEdit - > setText ( QString : : fromStdString ( xform - > m_Name ) ) ;
m_Fractorium - > m_XformNameEdit - > blockSignals ( false ) ;
}
FillVariationTreeWithXform ( xform ) ;
FillColorWithXform ( xform ) ;
FillAffineWithXform ( xform , true ) ;
FillAffineWithXform ( xform , false ) ;
}
/// <summary>
/// Set the normalized weight of the current xform as the suffix text of the weight spinner.
/// </summary>
/// <param name="xform">The current xform whose normalized weight will be shown</param>
template < typename T >
void FractoriumEmberController < T > : : SetNormalizedWeightText ( Xform < T > * xform )
{
if ( xform )
{
const auto index = m_Ember . GetXformIndex ( xform ) ;
m_Ember . CalcNormalizedWeights ( m_NormalizedWeights ) ;
if ( index ! = - 1 & & index < static_cast < intmax_t > ( m_NormalizedWeights . size ( ) ) )
m_Fractorium - > m_XformWeightSpinnerButtonWidget - > m_Label - > setText ( QString ( " ( " ) + QLocale : : system ( ) . toString ( static_cast < double > ( m_NormalizedWeights [ index ] ) , ' g ' , 3 ) + " ) " ) ;
}
}
/// <summary>
/// Determine whether the specified xform is the final xform in the ember.
/// </summary>
/// <param name="xform">The xform to examine</param>
/// <returns>True if final, else false.</returns>
template < typename T >
bool FractoriumEmberController < T > : : IsFinal ( Xform < T > * xform )
{
return m_Fractorium - > HaveFinal ( ) & & ( xform = = m_Ember . FinalXform ( ) ) ;
}
/// <summary>
/// Fill the xforms combo box with the xforms in the current ember.
/// Select the index passed in and fill all widgets with its values.
/// Also dynamically generate a checkbox for each xform which will allow the user
/// to select which xforms to apply operations to.
/// </summary>
/// <param name="index">The index to select after populating, default 0.</param>
template < typename T >
void FractoriumEmberController < T > : : FillXforms ( int index )
{
int i = 0 ;
const auto count = static_cast < int > ( XformCount ( ) ) ;
auto combo = m_Fractorium - > ui . CurrentXformCombo ;
combo - > blockSignals ( true ) ;
combo - > clear ( ) ;
//First clear all dynamically created checkboxes.
m_Fractorium - > ClearXformsSelections ( ) ;
m_Fractorium - > m_XformsSelectionLayout - > blockSignals ( true ) ;
//Fill combo box and create new checkboxes.
for ( i = 0 ; i < count ; i + + )
{
combo - > addItem ( ToString ( i + 1 ) ) ;
combo - > setItemIcon ( i , m_Fractorium - > m_XformComboIcons [ i % XFORM_COLOR_COUNT ] ) ;
UpdateXformName ( i ) ;
}
i = 0 ;
while ( i < count )
{
if ( i < count - 1 )
{
const auto cb1 = new QCheckBox ( MakeXformCaption ( i ) , m_Fractorium ) ;
const auto cb2 = new QCheckBox ( MakeXformCaption ( i + 1 ) , m_Fractorium ) ;
QObject : : connect ( cb1 , & QCheckBox : : stateChanged , [ & ] ( int state ) { m_Fractorium - > ui . GLDisplay - > update ( ) ; } ) ; //Ensure circles are drawn immediately after toggle.
QObject : : connect ( cb2 , & QCheckBox : : stateChanged , [ & ] ( int state ) { m_Fractorium - > ui . GLDisplay - > update ( ) ; } ) ;
m_Fractorium - > m_XformSelections . push_back ( cb1 ) ;
m_Fractorium - > m_XformSelections . push_back ( cb2 ) ;
m_Fractorium - > m_XformsSelectionLayout - > addRow ( cb1 , cb2 ) ;
i + = 2 ;
}
else if ( i < count )
{
const auto cb = new QCheckBox ( MakeXformCaption ( i ) , m_Fractorium ) ;
QObject : : connect ( cb , & QCheckBox : : stateChanged , [ & ] ( int state ) { m_Fractorium - > ui . GLDisplay - > update ( ) ; } ) ;
m_Fractorium - > m_XformSelections . push_back ( cb ) ;
m_Fractorium - > m_XformsSelectionLayout - > addRow ( cb , new QWidget ( m_Fractorium ) ) ;
i + + ;
}
}
//Special case for final xform.
if ( UseFinalXform ( ) )
{
const auto cb = new QCheckBox ( MakeXformCaption ( i ) , m_Fractorium ) ;
QObject : : connect ( cb , & QCheckBox : : stateChanged , [ & ] ( int state ) { m_Fractorium - > ui . GLDisplay - > update ( ) ; } ) ;
m_Fractorium - > m_XformSelections . push_back ( cb ) ;
m_Fractorium - > m_XformsSelectionLayout - > addRow ( cb , new QWidget ( m_Fractorium ) ) ;
combo - > addItem ( " Final " ) ;
combo - > setItemIcon ( i , m_Fractorium - > m_FinalXformComboIcon ) ;
UpdateXformName ( i ) ;
}
m_Fractorium - > m_XformsSelectionLayout - > blockSignals ( false ) ;
combo - > blockSignals ( false ) ;
if ( index < combo - > count ( ) )
combo - > setCurrentIndex ( index ) ;
m_Fractorium - > ui . SoloXformCheckBox - > blockSignals ( true ) ;
if ( m_Ember . m_Solo = = combo - > currentIndex ( ) )
m_Fractorium - > ui . SoloXformCheckBox - > setChecked ( true ) ;
else
m_Fractorium - > ui . SoloXformCheckBox - > setChecked ( false ) ;
SoloXformCheckBoxStateChanged ( m_Ember . m_Solo > - 1 ? Qt : : Checked : Qt : : Unchecked , m_Ember . m_Solo ) ;
m_Fractorium - > ui . SoloXformCheckBox - > blockSignals ( false ) ;
m_Fractorium - > FillXaosTable ( ) ;
m_Fractorium - > OnCurrentXformComboChanged ( index ) ; //Make sure the event gets called, because it won't if the zero index is already selected.
}
/// <summary>
/// Update the text in xforms combo box to show the name of Xform.
/// </summary>
/// <param name="index">The index of the Xform to update.</param>
template < typename T >
void FractoriumEmberController < T > : : UpdateXformName ( int index )
{
const auto forceFinal = m_Fractorium - > HaveFinal ( ) ;
const auto isFinal = m_Ember . FinalXform ( ) = = m_Ember . GetTotalXform ( index , forceFinal ) ;
QString name = isFinal ? " Final " : QString : : number ( index + 1 ) ;
if ( const auto xform = m_Ember . GetTotalXform ( index , forceFinal ) )
{
if ( ! xform - > m_Name . empty ( ) )
name + = " " + QString : : fromStdString ( xform - > m_Name ) ;
m_Fractorium - > ui . CurrentXformCombo - > setItemText ( index , name ) ;
const auto view = m_Fractorium - > ui . CurrentXformCombo - > view ( ) ;
const auto fontMetrics1 = view - > fontMetrics ( ) ;
const auto ww = fontMetrics1 . horizontalAdvance ( " WW " ) * 3 ;
auto textWidth = m_Fractorium - > ui . CurrentXformCombo - > width ( ) ;
for ( int i = 0 ; i < m_Fractorium - > ui . CurrentXformCombo - > count ( ) ; + + i )
textWidth = std : : max ( fontMetrics1 . horizontalAdvance ( m_Fractorium - > ui . CurrentXformCombo - > itemText ( i ) ) + ww , textWidth ) ;
view - > setMinimumWidth ( textWidth ) ;
view - > setMaximumWidth ( textWidth ) ;
}
}
template class FractoriumEmberController < float > ;
# ifdef DO_DOUBLE
template class FractoriumEmberController < double > ;
2017-07-27 00:25:44 -04:00
# endif