Add option to display affines in polar coords.

This commit is contained in:
mfeemster 2015-02-03 17:11:16 -08:00
parent b1d5db261b
commit c01f444d52
6 changed files with 177 additions and 72 deletions

View File

@ -71,6 +71,14 @@ void DoubleSpinBox::DoubleClickNonZero(double val)
m_DoubleClickNonZero = val;
}
/// <summary>
/// Get the default step used when the user scrolls.
/// </summary>
double DoubleSpinBox::Step()
{
return m_Step;
}
/// <summary>
/// Set the default step to be used when the user scrolls.
/// </summary>
@ -80,6 +88,14 @@ void DoubleSpinBox::Step(double step)
m_Step = step;
}
/// <summary>
/// Get the small step to be used when the user holds down shift while scrolling.
/// </summary>
double DoubleSpinBox::SmallStep()
{
return m_SmallStep;
}
/// <summary>
/// Set the small step to be used when the user holds down shift while scrolling.
/// The default is step / 10, so use this if something else is needed.

View File

@ -22,7 +22,9 @@ public:
void DoubleClick(bool b);
void DoubleClickZero(double val);
void DoubleClickNonZero(double val);
double Step();
void Step(double step);
double SmallStep();
void SmallStep(double step);
QLineEdit* lineEdit();

View File

@ -211,6 +211,7 @@ public slots:
void OnAffineGroupBoxToggled(bool on);
void OnAffineDrawAllCurrentRadioButtonToggled(bool checked);
void OnPolarAffineCheckBoxStateChanged(int state);
//Xforms Color.
void OnXformColorIndexChanged(double d);
@ -385,6 +386,9 @@ private:
DoubleSpinBox* m_PostO1Spin;
DoubleSpinBox* m_PostO2Spin;
DoubleSpinBox* m_PreSpins[6];
DoubleSpinBox* m_PostSpins[6];
//Palette.
SpinBox* m_PaletteHueSpin;
SpinBox* m_PaletteSaturationSpin;

View File

@ -353,7 +353,7 @@
<enum>QTabWidget::Triangular</enum>
</property>
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<property name="usesScrollButtons">
<bool>true</bool>
@ -504,8 +504,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>259</width>
<height>852</height>
<width>73</width>
<height>837</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
@ -2285,7 +2285,7 @@ SpinBox
<enum>QTabWidget::Triangular</enum>
</property>
<property name="currentIndex">
<number>2</number>
<number>1</number>
</property>
<widget class="QWidget" name="ColorTab">
<property name="sizePolicy">
@ -2825,8 +2825,8 @@ SpinBox
<rect>
<x>0</x>
<y>0</y>
<width>118</width>
<height>597</height>
<width>245</width>
<height>747</height>
</rect>
</property>
<property name="autoFillBackground">
@ -4065,6 +4065,13 @@ SpinBox
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="PolarAffineCheckBox">
<property name="text">
<string>Polar</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="PivotGroupBox">
<property name="autoFillBackground">
@ -5041,8 +5048,8 @@ SpinBox
<rect>
<x>0</x>
<y>0</y>
<width>259</width>
<height>853</height>
<width>73</width>
<height>454</height>
</rect>
</property>
<property name="sizePolicy">

View File

@ -148,6 +148,7 @@ public:
virtual void MoveCurrentXform(double x, double y, bool pre) { }
virtual void ScaleCurrentXform(double scale, bool pre) { }
virtual void ResetCurrentXformAffine(bool pre) { }
virtual void FillBothAffines() { }
//Xforms Color.
virtual void XformColorIndexChanged(double d, bool updateRender) { }
@ -368,6 +369,7 @@ public:
virtual void MoveCurrentXform(double x, double y, bool pre) override;
virtual void ScaleCurrentXform(double scale, bool pre) override;
virtual void ResetCurrentXformAffine(bool pre) override;
virtual void FillBothAffines() override;
void FillAffineWithXform(Xform<T>* xform, bool pre);
//Xforms Color.

View File

@ -97,47 +97,63 @@ void Fractorium::InitXformsAffineUI()
connect(ui.ShowPostAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPostAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.PolarAffineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnPolarAffineCheckBoxStateChanged(int)), Qt::QueuedConnection);
#ifndef WIN32
//For some reason linux makes these 24x24, even though the designer explicitly says 16x16.
//Also, in order to get 4 pixels of spacing between elements in the grid layout, 0 must be specified.
ui.PreFlipHorizontalButton->setIconSize(QSize(16, 16));
ui.PreFlipVerticalButton->setIconSize(QSize(16, 16));
ui.PreRotate90CButton->setIconSize(QSize(16, 16));
ui.PreRotate90CcButton->setIconSize(QSize(16, 16));
ui.PreRotateCButton->setIconSize(QSize(16, 16));
ui.PreRotateCcButton->setIconSize(QSize(16, 16));
ui.PreMoveUpButton->setIconSize(QSize(16, 16));
ui.PreMoveDownButton->setIconSize(QSize(16, 16));
ui.PreMoveLeftButton->setIconSize(QSize(16, 16));
ui.PreMoveRightButton->setIconSize(QSize(16, 16));
ui.PreScaleDownButton->setIconSize(QSize(16, 16));
ui.PreScaleUpButton->setIconSize(QSize(16, 16));
ui.PreResetButton->setIconSize(QSize(16, 16));
ui.PreAffineGridLayout->setHorizontalSpacing(0);
ui.PreAffineGridLayout->setVerticalSpacing(0);
//For some reason linux makes these 24x24, even though the designer explicitly says 16x16.
//Also, in order to get 4 pixels of spacing between elements in the grid layout, 0 must be specified.
ui.PreFlipHorizontalButton->setIconSize(QSize(16, 16));
ui.PreFlipVerticalButton->setIconSize(QSize(16, 16));
ui.PreRotate90CButton->setIconSize(QSize(16, 16));
ui.PreRotate90CcButton->setIconSize(QSize(16, 16));
ui.PreRotateCButton->setIconSize(QSize(16, 16));
ui.PreRotateCcButton->setIconSize(QSize(16, 16));
ui.PreMoveUpButton->setIconSize(QSize(16, 16));
ui.PreMoveDownButton->setIconSize(QSize(16, 16));
ui.PreMoveLeftButton->setIconSize(QSize(16, 16));
ui.PreMoveRightButton->setIconSize(QSize(16, 16));
ui.PreScaleDownButton->setIconSize(QSize(16, 16));
ui.PreScaleUpButton->setIconSize(QSize(16, 16));
ui.PreResetButton->setIconSize(QSize(16, 16));
ui.PreAffineGridLayout->setHorizontalSpacing(0);
ui.PreAffineGridLayout->setVerticalSpacing(0);
ui.PostFlipHorizontalButton->setIconSize(QSize(16, 16));
ui.PostFlipVerticalButton->setIconSize(QSize(16, 16));
ui.PostRotate90CButton->setIconSize(QSize(16, 16));
ui.PostRotate90CcButton->setIconSize(QSize(16, 16));
ui.PostRotateCButton->setIconSize(QSize(16, 16));
ui.PostRotateCcButton->setIconSize(QSize(16, 16));
ui.PostMoveUpButton->setIconSize(QSize(16, 16));
ui.PostMoveDownButton->setIconSize(QSize(16, 16));
ui.PostMoveLeftButton->setIconSize(QSize(16, 16));
ui.PostMoveRightButton->setIconSize(QSize(16, 16));
ui.PostScaleDownButton->setIconSize(QSize(16, 16));
ui.PostScaleUpButton->setIconSize(QSize(16, 16));
ui.PostResetButton->setIconSize(QSize(16, 16));
ui.PostAffineGridLayout->setHorizontalSpacing(0);
ui.PostAffineGridLayout->setVerticalSpacing(0);
ui.PostFlipHorizontalButton->setIconSize(QSize(16, 16));
ui.PostFlipVerticalButton->setIconSize(QSize(16, 16));
ui.PostRotate90CButton->setIconSize(QSize(16, 16));
ui.PostRotate90CcButton->setIconSize(QSize(16, 16));
ui.PostRotateCButton->setIconSize(QSize(16, 16));
ui.PostRotateCcButton->setIconSize(QSize(16, 16));
ui.PostMoveUpButton->setIconSize(QSize(16, 16));
ui.PostMoveDownButton->setIconSize(QSize(16, 16));
ui.PostMoveLeftButton->setIconSize(QSize(16, 16));
ui.PostMoveRightButton->setIconSize(QSize(16, 16));
ui.PostScaleDownButton->setIconSize(QSize(16, 16));
ui.PostScaleUpButton->setIconSize(QSize(16, 16));
ui.PostResetButton->setIconSize(QSize(16, 16));
ui.PostAffineGridLayout->setHorizontalSpacing(0);
ui.PostAffineGridLayout->setVerticalSpacing(0);
//Further, the size of the dock widget won't be properly adjusted until the xforms tab is shown.
//So show it here and it will be switched back in Fractorium's constructor.
ui.ParamsTabWidget->setCurrentIndex(2);
ui.DockWidget->update();
//Further, the size of the dock widget won't be properly adjusted until the xforms tab is shown.
//So show it here and it will be switched back in Fractorium's constructor.
ui.ParamsTabWidget->setCurrentIndex(2);
ui.DockWidget->update();
#endif
//Placing pointers to the spin boxes in arrays makes them easier to access in various places.
m_PreSpins[0] = m_PreX1Spin;//A
m_PreSpins[1] = m_PreY1Spin;//B
m_PreSpins[2] = m_PreO1Spin;//C
m_PreSpins[3] = m_PreX2Spin;//D
m_PreSpins[4] = m_PreY2Spin;//E
m_PreSpins[5] = m_PreO2Spin;//F
m_PostSpins[0] = m_PostX1Spin;
m_PostSpins[1] = m_PostY1Spin;
m_PostSpins[2] = m_PostO1Spin;
m_PostSpins[3] = m_PostX2Spin;
m_PostSpins[4] = m_PostY2Spin;
m_PostSpins[5] = m_PostO2Spin;
ui.PostAffineGroupBox->setChecked(true);//Flip it once to force the disabling of the group box.
ui.PostAffineGroupBox->setChecked(false);
@ -155,32 +171,52 @@ void FractoriumEmberController<T>::AffineSetHelper(double d, int index, bool pre
{
UpdateCurrentXform([&] (Xform<T>* xform)
{
QObject* objSender = m_Fractorium->sender();
DoubleSpinBox* spinBox = dynamic_cast<DoubleSpinBox*>(objSender);
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins;
if (spinBox)
if (m_Fractorium->ui.PolarAffineCheckBox->isChecked())
{
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
switch (index)
{
case 0:
affine->A(spinBox->value());
case 3:
affine->A(cos(spinners[0]->value() * DEG_2_RAD) * spinners[3]->value());
affine->D(sin(spinners[0]->value() * DEG_2_RAD) * spinners[3]->value());
break;
case 1:
affine->B(spinBox->value());
case 4:
affine->B(cos(spinners[1]->value() * DEG_2_RAD) * spinners[4]->value());
affine->E(sin(spinners[1]->value() * DEG_2_RAD) * spinners[4]->value());
break;
case 2:
affine->C(spinBox->value());
case 5:
default:
affine->C(cos(spinners[2]->value() * DEG_2_RAD) * spinners[5]->value());
affine->F(sin(spinners[2]->value() * DEG_2_RAD) * spinners[5]->value());
break;
}
}
else
{
switch (index)
{
case 0:
affine->A(d);
break;
case 1:
affine->B(d);
break;
case 2:
affine->C(d);
break;
case 3:
affine->D(spinBox->value());
affine->D(d);
break;
case 4:
affine->E(spinBox->value());
affine->E(d);
break;
case 5:
affine->F(spinBox->value());
affine->F(d);
break;
}
}
@ -465,6 +501,16 @@ void FractoriumEmberController<T>::ResetCurrentXformAffine(bool pre)
/// </summary>
void Fractorium::OnResetAffineButtonClicked(bool checked) { m_Controller->ResetCurrentXformAffine(sender() == ui.PreResetButton); }
/// <summary>
/// Fill the GUI with the pre and post affine xform values.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillBothAffines()
{
FillAffineWithXform(CurrentXform(), true);
FillAffineWithXform(CurrentXform(), false);
}
/// <summary>
/// Fill the GUI with the pre/post affine xform values.
/// </summary>
@ -473,23 +519,26 @@ void Fractorium::OnResetAffineButtonClicked(bool checked) { m_Controller->ResetC
template <typename T>
void FractoriumEmberController<T>::FillAffineWithXform(Xform<T>* xform, bool pre)
{
if (pre)
DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins;
const Affine2D<T>& affine = pre ? xform->m_Affine : xform->m_Post;
if (m_Fractorium->ui.PolarAffineCheckBox->isChecked())
{
m_Fractorium->m_PreX1Spin->SetValueStealth(xform->m_Affine.A());
m_Fractorium->m_PreY1Spin->SetValueStealth(xform->m_Affine.B());
m_Fractorium->m_PreO1Spin->SetValueStealth(xform->m_Affine.C());
m_Fractorium->m_PreX2Spin->SetValueStealth(xform->m_Affine.D());
m_Fractorium->m_PreY2Spin->SetValueStealth(xform->m_Affine.E());
m_Fractorium->m_PreO2Spin->SetValueStealth(xform->m_Affine.F());
spinners[0]->SetValueStealth(RAD_2_DEG * atan2(affine.D(), affine.A()));
spinners[1]->SetValueStealth(RAD_2_DEG * atan2(affine.E(), affine.B()));
spinners[2]->SetValueStealth(RAD_2_DEG * atan2(affine.F(), affine.C()));
spinners[3]->SetValueStealth(Hypot(affine.D(), affine.A()));
spinners[4]->SetValueStealth(Hypot(affine.E(), affine.B()));
spinners[5]->SetValueStealth(Hypot(affine.F(), affine.C()));
}
else
{
m_Fractorium->m_PostX1Spin->SetValueStealth(xform->m_Post.A());
m_Fractorium->m_PostY1Spin->SetValueStealth(xform->m_Post.B());
m_Fractorium->m_PostO1Spin->SetValueStealth(xform->m_Post.C());
m_Fractorium->m_PostX2Spin->SetValueStealth(xform->m_Post.D());
m_Fractorium->m_PostY2Spin->SetValueStealth(xform->m_Post.E());
m_Fractorium->m_PostO2Spin->SetValueStealth(xform->m_Post.F());
spinners[0]->SetValueStealth(affine.A());
spinners[1]->SetValueStealth(affine.B());
spinners[2]->SetValueStealth(affine.C());
spinners[3]->SetValueStealth(affine.D());
spinners[4]->SetValueStealth(affine.E());
spinners[5]->SetValueStealth(affine.F());
}
}
@ -516,6 +565,31 @@ void Fractorium::OnAffineDrawAllCurrentRadioButtonToggled(bool checked)
ui.GLDisplay->update();
}
/// <summary>
/// Set whether to display affine values in polar vs. rectangular coordinates.
/// Updates the current affine display.
/// </summary>
/// <param name="state">The state of the checkbox</param>
void Fractorium::OnPolarAffineCheckBoxStateChanged(int state)
{
double mult = state ? 100 : 0.01;
double step = m_PreX1Spin->Step() * mult;
double small = m_PreX1Spin->SmallStep() * mult;
double click = state ? 90 : 1;
for (int i = 0; i < 3; i++)
{
m_PreSpins[i]->Step(step);
m_PreSpins[i]->SmallStep(small);
m_PostSpins[i]->Step(step);
m_PostSpins[i]->SmallStep(small);
m_PreSpins[i]->DoubleClickZero(click);
m_PostSpins[i]->DoubleClickZero(click);
}
m_Controller->FillBothAffines();
}
/// <summary>
/// Setup a spinner to be placed in a table cell.
/// Special setup function for affine spinners which differs slightly from the regular