mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-06-30 21:36:33 -04:00
--User changes
-Add buttons to copy and paste affine transforms. -Show xform names on the column headers of the xaos table. -Add a color-coded third column to the variations tree which shows any properties of each variation which are non-standard. -Draw a transparent circle over hovered xforms. -Change how xforms respond to dragging. Rotate only is now the default, and scale will only happen with shift. --Optionally do scale and rotate when holding shift, via a setting in the options dialog. --Bug fixes -Snapping when dragging was wrong sometimes. -The program would very rarely crash on startup due to some values being in an uninitialized state. --Code changes -Change almost every variation to use fma() in OpenCL when doing computations of the form a * b + c. This provides a slight speedup, mostly in double precision mode. -Also apply fma() to affine calcs. -Cleanup of OpenGL affine drawing code. -Separate the concept of hovering and selecting xforms.
This commit is contained in:
@ -873,7 +873,9 @@ void Fractorium::SetTabOrders()
|
||||
w = SetTabOrder(this, w, m_PreO1Spin);
|
||||
w = SetTabOrder(this, w, m_PreO2Spin);
|
||||
w = SetTabOrder(this, w, ui.PreFlipVerticalButton);
|
||||
w = SetTabOrder(this, w, ui.PreCopyButton);
|
||||
w = SetTabOrder(this, w, ui.PreResetButton);
|
||||
w = SetTabOrder(this, w, ui.PrePasteButton);
|
||||
w = SetTabOrder(this, w, ui.PreFlipHorizontalButton);
|
||||
w = SetTabOrder(this, w, ui.PreRotate90CcButton);
|
||||
w = SetTabOrder(this, w, ui.PreRotateCcButton);
|
||||
@ -899,7 +901,9 @@ void Fractorium::SetTabOrders()
|
||||
w = SetTabOrder(this, w, m_PostO1Spin);
|
||||
w = SetTabOrder(this, w, m_PostO2Spin);
|
||||
w = SetTabOrder(this, w, ui.PostFlipVerticalButton);
|
||||
w = SetTabOrder(this, w, ui.PostCopyButton);
|
||||
w = SetTabOrder(this, w, ui.PostResetButton);
|
||||
w = SetTabOrder(this, w, ui.PostPasteButton);
|
||||
w = SetTabOrder(this, w, ui.PostFlipHorizontalButton);
|
||||
w = SetTabOrder(this, w, ui.PostRotate90CcButton);
|
||||
w = SetTabOrder(this, w, ui.PostRotateCcButton);
|
||||
|
@ -114,7 +114,11 @@ public:
|
||||
void CurrentXform(uint i);
|
||||
|
||||
//Xforms Affine.
|
||||
bool DrawCurrentPre();
|
||||
bool DrawSelectedPre();
|
||||
bool DrawAllPre();
|
||||
bool DrawCurrentPost();
|
||||
bool DrawSelectedPost();
|
||||
bool DrawAllPost();
|
||||
bool LocalPivot();
|
||||
|
||||
@ -279,6 +283,8 @@ public slots:
|
||||
void OnScaleDownButtonClicked(bool checked);
|
||||
void OnScaleUpButtonClicked(bool checked);
|
||||
void OnResetAffineButtonClicked(bool checked);
|
||||
void OnCopyAffineButtonClicked(bool checked);
|
||||
void OnPasteAffineButtonClicked(bool checked);
|
||||
void OnRandomAffineButtonClicked(bool checked);
|
||||
|
||||
void OnAffineGroupBoxToggled(bool on);
|
||||
|
@ -3233,7 +3233,7 @@
|
||||
<enum>QTabWidget::Triangular</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>3</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="XformColorTab">
|
||||
<property name="sizePolicy">
|
||||
@ -4572,6 +4572,50 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="PreCopyButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>41</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Copy the pre affine values, which can then be pasted into other pre/post affines</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="PrePasteButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>41</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Paste the pre/post affine values which were previously copied</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Paste</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@ -4617,6 +4661,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="ShowPreAffineSelectedRadio">
|
||||
<property name="text">
|
||||
<string>Selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="ShowPreAffineAllRadio">
|
||||
<property name="text">
|
||||
@ -5402,6 +5453,50 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="PostCopyButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>41</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Copy the post affine values, which can then be pasted into other pre/post affines</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QPushButton" name="PostPasteButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>41</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Paste the pre/post affine values which were previously copied</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Paste</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@ -5444,6 +5539,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="ShowPostAffineSelectedRadio">
|
||||
<property name="text">
|
||||
<string>Selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="ShowPostAffineAllRadio">
|
||||
<property name="enabled">
|
||||
@ -5675,7 +5777,7 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
<number>3</number>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>70</number>
|
||||
@ -5702,6 +5804,14 @@
|
||||
<string>Weight</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Red: Uses non-standard assignment which means direct assignment for regular variations, sum for pre/post.</p><p>Green: Uses direct color.</p><p>Blue: Uses an internal variation state.</p></body></html></string>
|
||||
</property>
|
||||
</column>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Spherical</string>
|
||||
|
@ -194,6 +194,8 @@ public:
|
||||
virtual void MoveXforms(double x, double y, bool pre) { }
|
||||
virtual void ScaleXforms(double scale, bool pre) { }
|
||||
virtual void ResetXformsAffine(bool pre) { }
|
||||
virtual void CopyXformsAffine(bool pre) { }
|
||||
virtual void PasteXformsAffine(bool pre) { }
|
||||
virtual void RandomXformsAffine(bool pre) { }
|
||||
virtual void FillBothAffines() { }
|
||||
double LockedScale() { return m_LockedScale; }
|
||||
@ -223,6 +225,7 @@ public:
|
||||
virtual void FillVariationTreeWithCurrentXform() { }
|
||||
|
||||
//Xforms Selection.
|
||||
virtual QString MakeXformCaption(size_t i) { return ""; }
|
||||
|
||||
//Xaos.
|
||||
virtual void FillXaos() { }
|
||||
@ -265,12 +268,12 @@ public:
|
||||
vector<v4F>* FinalImage() { return &(m_FinalImage); }
|
||||
vector<v4F>* PreviewFinalImage() { return &m_PreviewFinalImage; }
|
||||
EmberStats Stats() { return m_Stats; }
|
||||
eProcessState ProcessState() { return m_Renderer.get() ? m_Renderer->ProcessState() : eProcessState::NONE; }
|
||||
|
||||
protected:
|
||||
//Rendering/progress.
|
||||
void AddProcessAction(eProcessAction action);
|
||||
eProcessAction CondenseAndClearProcessActions();
|
||||
eProcessState ProcessState() { return m_Renderer.get() ? m_Renderer->ProcessState() : eProcessState::NONE; }
|
||||
|
||||
//Non-templated members.
|
||||
bool m_Rendering = false;
|
||||
@ -468,6 +471,8 @@ public:
|
||||
virtual void MoveXforms(double x, double y, bool pre) override;
|
||||
virtual void ScaleXforms(double scale, bool pre) override;
|
||||
virtual void ResetXformsAffine(bool pre) override;
|
||||
virtual void CopyXformsAffine(bool pre) override;
|
||||
virtual void PasteXformsAffine(bool pre) override;
|
||||
virtual void RandomXformsAffine(bool pre) override;
|
||||
virtual void FillBothAffines() override;
|
||||
virtual void InitLockedScale() override;
|
||||
@ -506,6 +511,7 @@ public:
|
||||
virtual void AddLayer(int xforms) override;
|
||||
|
||||
//Xforms Selection.
|
||||
virtual QString MakeXformCaption(size_t i) override;
|
||||
bool XformCheckboxAt(int i, std::function<void(QCheckBox*)> func);
|
||||
bool XformCheckboxAt(Xform<T>* xform, std::function<void(QCheckBox*)> func);
|
||||
|
||||
@ -545,9 +551,6 @@ private:
|
||||
//Xforms Color.
|
||||
void FillCurvesControl();
|
||||
|
||||
//Xforms Selection.
|
||||
QString MakeXformCaption(size_t i);
|
||||
|
||||
//Palette.
|
||||
void UpdateAdjustedPaletteGUI(Palette<float>& palette);
|
||||
|
||||
@ -567,6 +570,7 @@ private:
|
||||
deque<Ember<T>> m_UndoList;
|
||||
vector<Xform<T>> m_CopiedXforms;
|
||||
Xform<T> m_CopiedFinalXform;
|
||||
Affine2D<T> m_CopiedAffine;
|
||||
shared_ptr<VariationList<T>> m_VariationList;
|
||||
unique_ptr<SheepTools<T, float>> m_SheepTools;
|
||||
unique_ptr<GLEmberController<T>> m_GLController;
|
||||
|
@ -645,7 +645,7 @@ void FractoriumEmberController<T>::InterpTypeChanged(int i)
|
||||
{
|
||||
eInterp interp;
|
||||
|
||||
if (i == 0)//Need to make this work like animate flag where it sets the value but doesn't trigger and update.//TODO
|
||||
if (i == 0)
|
||||
interp = eInterp::EMBER_INTERP_LINEAR;
|
||||
else if (i == 1)
|
||||
interp = eInterp::EMBER_INTERP_SMOOTH;
|
||||
|
@ -159,13 +159,13 @@ void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, c
|
||||
{
|
||||
vector<byte> rgba8Image(size * 4);
|
||||
Rgba32ToRgba8(data, rgba8Image.data(), width, height, transparency);
|
||||
b = WritePng(s.c_str(), rgba8Image.data(), width, height, 1, true, comments, id, url, nick);//Put an opt here for 1 or 2 bytes.//TODO
|
||||
b = WritePng(s.c_str(), rgba8Image.data(), width, height, 1, true, comments, id, url, nick);
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<glm::uint16> rgba16Image(size * 4);
|
||||
Rgba32ToRgba16(data, rgba16Image.data(), width, height, transparency);
|
||||
b = WritePng(s.c_str(), (byte*)rgba16Image.data(), width, height, 2, true, comments, id, url, nick);//Put an opt here for 1 or 2 bytes.//TODO
|
||||
b = WritePng(s.c_str(), (byte*)rgba16Image.data(), width, height, 2, true, comments, id, url, nick);
|
||||
}
|
||||
}
|
||||
else if (suffix.endsWith("exr", Qt::CaseInsensitive))
|
||||
|
@ -173,6 +173,9 @@ void FractoriumSettings::OpenClQuality(uint i) { setValue(OPEN
|
||||
bool FractoriumSettings::LoadLast() { return value(LOADLAST).toBool(); }
|
||||
void FractoriumSettings::LoadLast(bool b) { setValue(LOADLAST, b); }
|
||||
|
||||
bool FractoriumSettings::RotateAndScale() { return value(ROTSCALE).toBool(); }
|
||||
void FractoriumSettings::RotateAndScale(bool b) { setValue(ROTSCALE, b); }
|
||||
|
||||
/// <summary>
|
||||
/// Sequence generation settings.
|
||||
/// </summary>
|
||||
|
@ -28,6 +28,7 @@
|
||||
#define CPUQUALITY "render/cpuquality"
|
||||
#define OPENCLQUALITY "render/openclquality"
|
||||
#define LOADLAST "render/loadlastonstart"
|
||||
#define ROTSCALE "render/rotateandscale"
|
||||
|
||||
#define STAGGER "sequence/stagger"
|
||||
#define STAGGERMAX "sequence/staggermax"
|
||||
@ -171,6 +172,9 @@ public:
|
||||
bool LoadLast();
|
||||
void LoadLast(bool b);
|
||||
|
||||
bool RotateAndScale();
|
||||
void RotateAndScale(bool b);
|
||||
|
||||
double Stagger();
|
||||
void Stagger(double i);
|
||||
|
||||
|
@ -92,7 +92,7 @@ void Fractorium::FillXaosTable()
|
||||
{
|
||||
int count = int(m_Controller->XformCount());
|
||||
QStringList hl, vl;
|
||||
auto oldModel = m_XaosTableModel;
|
||||
auto oldModel = std::make_unique<QStandardItemModel>(m_XaosTableModel);
|
||||
hl.reserve(count);
|
||||
vl.reserve(count);
|
||||
m_XaosTableModel = new QStandardItemModel(count, count, this);
|
||||
@ -101,7 +101,7 @@ void Fractorium::FillXaosTable()
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
auto s = QString::number(i + 1);
|
||||
auto s = m_Controller->MakeXformCaption(i);
|
||||
hl.push_back("F" + s);
|
||||
vl.push_back("T" + s);
|
||||
}
|
||||
@ -113,10 +113,6 @@ void Fractorium::FillXaosTable()
|
||||
SetTabOrder(this, ui.ClearXaosButton, ui.RandomXaosButton);
|
||||
m_Controller->FillXaos();
|
||||
ui.XaosTableView->blockSignals(false);
|
||||
|
||||
if (oldModel)
|
||||
delete oldModel;
|
||||
|
||||
//Needed to get the dark stylesheet to correctly color the top left corner button.
|
||||
auto widgetList = ui.XaosTableView->findChildren<QAbstractButton*>();
|
||||
|
||||
|
@ -412,6 +412,7 @@ void FractoriumEmberController<T>::XformNameChanged(int row, int col)
|
||||
XformCheckboxAt(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(int row, int col)
|
||||
@ -570,6 +571,7 @@ void FractoriumEmberController<T>::FillXforms(int index)
|
||||
if (UseFinalXform())
|
||||
{
|
||||
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");
|
||||
@ -600,7 +602,6 @@ void FractoriumEmberController<T>::FillXforms(int index)
|
||||
/// 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)
|
||||
{
|
||||
|
@ -61,41 +61,47 @@ void Fractorium::InitXformsAffineUI()
|
||||
moveList.append(ToString(0.01));
|
||||
ui.PreMoveCombo->addItems(moveList);
|
||||
ui.PostMoveCombo->addItems(moveList);
|
||||
connect(ui.PreFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRandomButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRandomButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ShowPreAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ShowPreAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
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);
|
||||
connect(ui.PreFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreCopyButton, SIGNAL(clicked(bool)), this, SLOT(OnCopyAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PrePasteButton, SIGNAL(clicked(bool)), this, SLOT(OnPasteAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreRandomButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostCopyButton, SIGNAL(clicked(bool)), this, SLOT(OnCopyAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostPasteButton, SIGNAL(clicked(bool)), this, SLOT(OnPasteAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostRandomButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomAffineButtonClicked(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PreAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.PostAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ShowPreAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ShowPreAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
connect(ui.ShowPreAffineSelectedRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
|
||||
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.ShowPostAffineSelectedRadio, 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.
|
||||
@ -564,6 +570,7 @@ void Fractorium::OnScaleUpButtonClicked(bool checked)
|
||||
/// Called when reset pre/post affine buttons are clicked.
|
||||
/// Resets the rendering process.
|
||||
/// </summary>
|
||||
/// <param name="pre">True for pre affine, false for post.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::ResetXformsAffine(bool pre)
|
||||
{
|
||||
@ -577,6 +584,42 @@ void FractoriumEmberController<T>::ResetXformsAffine(bool pre)
|
||||
|
||||
void Fractorium::OnResetAffineButtonClicked(bool checked) { m_Controller->ResetXformsAffine(sender() == ui.PreResetButton); }
|
||||
|
||||
/// <summary>
|
||||
/// Copy the pre or post affine transform values from the current xform.
|
||||
/// </summary>
|
||||
/// <param name="pre">True for pre affine, false for post.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::CopyXformsAffine(bool pre)
|
||||
{
|
||||
if (auto xform = CurrentXform())
|
||||
m_CopiedAffine = pre ? xform->m_Affine : xform->m_Post;
|
||||
}
|
||||
|
||||
void Fractorium::OnCopyAffineButtonClicked(bool checked)
|
||||
{
|
||||
m_Controller->CopyXformsAffine(sender() == ui.PreCopyButton);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Paste the last copied affine transform values to the pre or post affine values in the current xform.
|
||||
/// </summary>
|
||||
/// <param name="pre">True for pre affine, false for post.</param>
|
||||
template <typename T>
|
||||
void FractoriumEmberController<T>::PasteXformsAffine(bool pre)
|
||||
{
|
||||
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
affine = m_CopiedAffine;
|
||||
}, eXformUpdate::UPDATE_CURRENT);
|
||||
FillAffineWithXform(CurrentXform(), pre);
|
||||
}
|
||||
|
||||
void Fractorium::OnPasteAffineButtonClicked(bool checked)
|
||||
{
|
||||
m_Controller->PasteXformsAffine(sender() == ui.PrePasteButton);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Randomize all values in selected pre/post affines to a range of -1 to 1.
|
||||
/// Called when random pre/post affine buttons are clicked.
|
||||
@ -731,7 +774,11 @@ void Fractorium::SetupAffineSpinner(QTableWidget* table, const QObject* receiver
|
||||
/// GUI wrapper functions, getters only.
|
||||
/// </summary>
|
||||
|
||||
bool Fractorium::DrawCurrentPre() { return !DrawAllPre() && !DrawSelectedPre(); }
|
||||
bool Fractorium::DrawSelectedPre() { return ui.ShowPreAffineSelectedRadio->isChecked(); }
|
||||
bool Fractorium::DrawAllPre() { return ui.ShowPreAffineAllRadio->isChecked(); }
|
||||
bool Fractorium::DrawCurrentPost() { return !DrawAllPost() && !DrawSelectedPost(); }
|
||||
bool Fractorium::DrawSelectedPost() { return ui.ShowPostAffineSelectedRadio->isChecked(); }
|
||||
bool Fractorium::DrawAllPost() { return ui.ShowPostAffineAllRadio->isChecked(); }
|
||||
bool Fractorium::LocalPivot() { return ui.LocalPivotRadio->isChecked(); }
|
||||
|
||||
|
@ -14,8 +14,9 @@ void Fractorium::InitXformsVariationsUI()
|
||||
connect(ui.VariationsFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnVariationsFilterClearButtonClicked(bool)));
|
||||
connect(ui.ActionVariationsDialog, SIGNAL(triggered(bool)), this, SLOT(OnActionVariationsDialog(bool)), Qt::QueuedConnection);
|
||||
//Setting dimensions in the designer with a layout is futile, so must hard code here.
|
||||
tree->setColumnWidth(0, 160);
|
||||
tree->setColumnWidth(1, 23);
|
||||
tree->setColumnWidth(0, 170);
|
||||
tree->setColumnWidth(1, 80);
|
||||
tree->setColumnWidth(2, 20);
|
||||
//Set Default variation tree text and background colors for zero and non zero cases.
|
||||
m_VariationTreeColorNonZero = Qt::black;
|
||||
m_VariationTreeColorZero = Qt::black;
|
||||
@ -106,11 +107,16 @@ void FractoriumEmberController<T>::SetupVariationsTree()
|
||||
{
|
||||
T fMin = TLOW;
|
||||
T fMax = TMAX;
|
||||
QSize hint0(75, 16);
|
||||
QSize hint1(30, 16);
|
||||
QSize hint0(170, 16);
|
||||
QSize hint1(80, 16);
|
||||
QSize hint2(20, 16);
|
||||
static vector<string> dc{ "m_ColorX" };
|
||||
static vector<string> assign{ "outPoint->m_X =", "outPoint->m_Y =", "outPoint->m_Z =",
|
||||
"outPoint->m_X=", "outPoint->m_Y=", "outPoint->m_Z=" };
|
||||
auto tree = m_Fractorium->ui.VariationsTree;
|
||||
tree->clear();
|
||||
tree->blockSignals(true);
|
||||
int iconSize_ = 20;
|
||||
|
||||
for (size_t i = 0; i < m_VariationList->Size(); i++)
|
||||
{
|
||||
@ -122,6 +128,34 @@ void FractoriumEmberController<T>::SetupVariationsTree()
|
||||
item->setText(0, QString::fromStdString(var->Name()));
|
||||
item->setSizeHint(0, hint0);
|
||||
item->setSizeHint(1, hint1);
|
||||
item->setSizeHint(2, hint2);
|
||||
QPixmap pixmap(iconSize_ * 3, iconSize_);
|
||||
auto mask = pixmap.createMaskFromColor(QColor("transparent"), Qt::MaskOutColor);
|
||||
pixmap.setMask(mask);
|
||||
QPainter paint(&pixmap);
|
||||
paint.fillRect(QRect(0, 0, iconSize_ * 3, iconSize_), QColor(0, 0, 0, 0));
|
||||
|
||||
if (var->VarType() == eVariationType::VARTYPE_REG)
|
||||
{
|
||||
if (SearchVar(var, assign, false))
|
||||
paint.fillRect(QRect(0, 0, iconSize_, iconSize_), QColor(255, 0, 0));
|
||||
}
|
||||
else if (var->VarType() == eVariationType::VARTYPE_PRE || var->VarType() == eVariationType::VARTYPE_POST)
|
||||
{
|
||||
if (var->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
|
||||
paint.fillRect(QRect(0, 0, iconSize_, iconSize_), QColor(255, 0, 0));
|
||||
}
|
||||
|
||||
bool isDc = SearchVar(var, dc, false);
|
||||
|
||||
if (isDc)
|
||||
paint.fillRect(QRect(iconSize_, 0, iconSize_, iconSize_), QColor(0, 255, 0));
|
||||
|
||||
if (!var->StateOpenCLString().empty())
|
||||
paint.fillRect(QRect(iconSize_ * 2, 0, iconSize_, iconSize_), QColor(0, 0, 255));
|
||||
|
||||
QIcon qi(pixmap);
|
||||
item->setIcon(2, qi);
|
||||
spinBox->setRange(fMin, fMax);
|
||||
spinBox->DoubleClick(true);
|
||||
spinBox->DoubleClickZero(1);
|
||||
@ -358,11 +392,14 @@ void FractoriumEmberController<T>::FillVariationTreeWithXform(Xform<T>* xform)
|
||||
/// <param name="logicalIndex">Column index of the header clicked. Sort by name if 0, sort by weight if 1.</param>
|
||||
void Fractorium::OnTreeHeaderSectionClicked(int logicalIndex)
|
||||
{
|
||||
m_VarSortMode = logicalIndex;
|
||||
ui.VariationsTree->sortItems(m_VarSortMode, m_VarSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);
|
||||
if (logicalIndex <= 1)
|
||||
{
|
||||
m_VarSortMode = logicalIndex;
|
||||
ui.VariationsTree->sortItems(m_VarSortMode, m_VarSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);
|
||||
|
||||
if (m_VarSortMode == 1)
|
||||
ui.VariationsTree->scrollToTop();
|
||||
if (m_VarSortMode == 1)
|
||||
ui.VariationsTree->scrollToTop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -175,7 +175,7 @@ typename v3T GLEmberController<T>::SnapToNormalizedAngle(v3T& vec, uint division
|
||||
{
|
||||
T rsq, theta;
|
||||
T bestRsq = numeric_limits<T>::max();
|
||||
v3T c, best;
|
||||
v3T c(0, 0, 0), best;
|
||||
best.x = 1;
|
||||
best.y = 0;
|
||||
|
||||
|
@ -118,7 +118,7 @@ public:
|
||||
void CalcDragTranslation();
|
||||
void SetSelectedXform(Xform<T>* xform);
|
||||
void DrawGrid();
|
||||
void DrawAffine(Xform<T>* xform, bool pre, bool selected);
|
||||
void DrawAffine(Xform<T>* xform, bool pre, bool selected, bool hovered);
|
||||
int UpdateHover(v3T& glCoords);
|
||||
bool CheckXformHover(Xform<T>* xform, v3T& glCoords, T& bestDist, bool pre, bool post);
|
||||
|
||||
|
@ -390,10 +390,9 @@ void GLEmberController<T>::SetSelectedXform(Xform<T>* xform)
|
||||
{
|
||||
//By doing this check, it prevents triggering unnecessary events when selecting an xform on this window with the mouse,
|
||||
//which will set the combo box on the main window, which will trigger this call. However, if the xform has been selected
|
||||
//here with the mouse, the window has already repainted, so there's no need to do it again.
|
||||
if (m_SelectedXform != xform || m_HoverXform != xform)
|
||||
//here with the mouse, the window has already been repainted, so there's no need to do it again.
|
||||
if (m_SelectedXform != xform)
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
m_SelectedXform = xform;
|
||||
|
||||
if (m_GL->m_Init)
|
||||
@ -513,7 +512,7 @@ void GLWidget::initializeGL()
|
||||
this->glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
this->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_MaxTexSize);
|
||||
this->glDisable(GL_TEXTURE_2D);
|
||||
m_Fractorium->m_WidthSpin->setMaximum(m_MaxTexSize);//This should also apply to the final render dialog.//TODO
|
||||
m_Fractorium->m_WidthSpin->setMaximum(m_MaxTexSize);
|
||||
m_Fractorium->m_HeightSpin->setMaximum(m_MaxTexSize);
|
||||
}
|
||||
}
|
||||
@ -535,9 +534,18 @@ void GLWidget::paintGL()
|
||||
auto controller = m_Fractorium->m_Controller.get();
|
||||
|
||||
//Ensure there is a renderer and that it's supposed to be drawing, signified by the running timer.
|
||||
if (controller && controller->Renderer())
|
||||
if (controller && controller->Renderer()/* && controller->ProcessState() != eProcessState::NONE*/)//Need a way to determine if at leat one successful render has happened.
|
||||
{
|
||||
auto renderer = controller->Renderer();
|
||||
float unitX = std::abs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f;
|
||||
float unitY = std::abs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f;
|
||||
|
||||
if (unitX > 100000 || unitY > 100000)//Need a better way to do this.//TODO
|
||||
{
|
||||
qDebug() << unitX << " " << unitY;
|
||||
//return;
|
||||
}
|
||||
|
||||
m_Drawing = true;
|
||||
|
||||
if (m_Fractorium->DrawImage())
|
||||
@ -553,8 +561,6 @@ void GLWidget::paintGL()
|
||||
//Affine drawing.
|
||||
bool pre = m_Fractorium->ui.PreAffineGroupBox->isChecked();
|
||||
bool post = m_Fractorium->ui.PostAffineGroupBox->isChecked();
|
||||
float unitX = std::abs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f;
|
||||
float unitY = std::abs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f;
|
||||
this->glEnable(GL_BLEND);
|
||||
this->glEnable(GL_LINE_SMOOTH);
|
||||
this->glEnable(GL_POINT_SMOOTH);
|
||||
@ -635,7 +641,6 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
|
||||
{
|
||||
auto dprf = m_GL->devicePixelRatioF();
|
||||
auto world = ScrolledCenter(true);
|
||||
|
||||
m_GL->glLineWidth(1.0f * dprf);
|
||||
GLfloat vertices[] =
|
||||
{
|
||||
@ -654,7 +659,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
|
||||
if (!m_Fractorium->m_Settings->ShowAllXforms() && dragging)
|
||||
{
|
||||
if (m_SelectedXform)
|
||||
DrawAffine(m_SelectedXform, m_AffineType == eAffineType::AffinePre, true);
|
||||
DrawAffine(m_SelectedXform, m_AffineType == eAffineType::AffinePre, true, false);
|
||||
}
|
||||
else//Show all while dragging, or not dragging just hovering/mouse move.
|
||||
{
|
||||
@ -664,13 +669,30 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
|
||||
|
||||
while (auto xform = ember->GetTotalXform(i, forceFinal))
|
||||
{
|
||||
bool selected = m_Fractorium->IsXformSelected(i++) || (dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform));
|
||||
DrawAffine(xform, true, selected);
|
||||
bool selected = m_Fractorium->IsXformSelected(i++) || m_SelectedXform == xform;
|
||||
DrawAffine(xform, true, selected, !dragging && (m_HoverXform == xform));
|
||||
}
|
||||
}
|
||||
else if (pre && m_HoverXform)//Only draw current pre affine.
|
||||
else if (pre && m_Fractorium->DrawSelectedPre())//Only draw selected pre affine, and if none are selected, draw current. All are considered "selected", so circles are drawn around them.
|
||||
{
|
||||
DrawAffine(m_HoverXform, true, true);
|
||||
size_t i = 0;
|
||||
bool any = false;
|
||||
|
||||
while (auto xform = ember->GetTotalXform(i, forceFinal))
|
||||
{
|
||||
if (m_Fractorium->IsXformSelected(i++))
|
||||
{
|
||||
DrawAffine(xform, true, true, !dragging && (m_HoverXform == xform));
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any)
|
||||
DrawAffine(m_FractoriumEmberController->CurrentXform(), true, true, !dragging && (m_HoverXform == m_FractoriumEmberController->CurrentXform()));
|
||||
}
|
||||
else if (pre)//Only draw current pre affine.
|
||||
{
|
||||
DrawAffine(m_SelectedXform, true, true, !dragging && (m_HoverXform == m_FractoriumEmberController->CurrentXform()));
|
||||
}
|
||||
|
||||
if (post && m_Fractorium->DrawAllPost())//Draw all post affine if specified.
|
||||
@ -679,13 +701,30 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
|
||||
|
||||
while (auto xform = ember->GetTotalXform(i, forceFinal))
|
||||
{
|
||||
bool selected = m_Fractorium->IsXformSelected(i++) || (dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform));
|
||||
DrawAffine(xform, false, selected);
|
||||
bool selected = m_Fractorium->IsXformSelected(i++) || m_SelectedXform == xform;
|
||||
DrawAffine(xform, false, selected, !dragging && (m_HoverXform == xform));
|
||||
}
|
||||
}
|
||||
else if (post && m_HoverXform)//Only draw current post affine.
|
||||
else if (post && m_Fractorium->DrawSelectedPost())//Only draw selected post, and if none are selected, draw current. All are considered "selected", so circles are drawn around them.
|
||||
{
|
||||
DrawAffine(m_HoverXform, false, true);
|
||||
size_t i = 0;
|
||||
bool any = false;
|
||||
|
||||
while (auto xform = ember->GetTotalXform(i, forceFinal))
|
||||
{
|
||||
if (m_Fractorium->IsXformSelected(i++))
|
||||
{
|
||||
DrawAffine(xform, false, true, !dragging && (m_HoverXform == xform));
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any)
|
||||
DrawAffine(m_FractoriumEmberController->CurrentXform(), false, true, !dragging && (m_HoverXform == m_FractoriumEmberController->CurrentXform()));
|
||||
}
|
||||
else if (post)//Only draw current post affine.
|
||||
{
|
||||
DrawAffine(m_SelectedXform, false, true, !dragging && (m_HoverXform == m_FractoriumEmberController->CurrentXform()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1032,7 +1071,9 @@ void GLEmberController<T>::MouseMove(QMouseEvent* e)
|
||||
//Check if they weren't dragging and weren't hovering over any affine.
|
||||
//In that case, nothing needs to be done.
|
||||
if (UpdateHover(mouseFlipped) == -1)
|
||||
draw = false;
|
||||
{
|
||||
m_HoverXform = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//Only update if the user was dragging or hovered over a point.
|
||||
@ -1128,7 +1169,7 @@ void GLWidget::DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, in
|
||||
{
|
||||
#ifdef USE_GLSL
|
||||
|
||||
if (dashed && drawType == GL_LINES)
|
||||
if (dashed && (drawType == GL_LINES || drawType == GL_LINE_LOOP))
|
||||
{
|
||||
glLineStipple(1, 0XFF00);
|
||||
glEnable(GL_LINE_STIPPLE);
|
||||
@ -1143,7 +1184,7 @@ void GLWidget::DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, in
|
||||
this->glDrawArrays(drawType, 0, size);
|
||||
this->glDisableVertexAttribArray(0);
|
||||
|
||||
if (dashed && drawType == GL_LINES)
|
||||
if (dashed && (drawType == GL_LINES || drawType == GL_LINE_LOOP))
|
||||
glDisable(GL_LINE_STIPPLE);
|
||||
|
||||
#endif
|
||||
@ -1370,7 +1411,13 @@ void GLEmberController<T>::DrawGrid()
|
||||
//qDebug() << renderer->UpperRightX(false) << " " << renderer->LowerLeftX(false) << " " << renderer->UpperRightY(false) << " " << renderer->LowerLeftY(false);
|
||||
float unitX = (std::abs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f) / scale;
|
||||
float unitY = (std::abs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f) / scale;
|
||||
//qDebug() << unitX << " " << unitY;
|
||||
|
||||
if (unitX > 100000 || unitY > 100000)//Need a better way to do this.//TODO
|
||||
{
|
||||
qDebug() << unitX << " " << unitY;
|
||||
//return;
|
||||
}
|
||||
|
||||
float xLow = std::floor(-unitX);
|
||||
float xHigh = std::ceil(unitX);
|
||||
float yLow = std::floor(-unitY);
|
||||
@ -1479,8 +1526,9 @@ void GLEmberController<T>::DrawGrid()
|
||||
/// <param name="xform">A pointer to the xform whose affine will be drawn</param>
|
||||
/// <param name="pre">True for pre affine, else false for post.</param>
|
||||
/// <param name="selected">True if selected (draw enclosing circle), else false (only draw axes).</param>
|
||||
/// <param name="hovered">True if the xform is being hovered over (draw tansparent disc), else false (no disc).</param>
|
||||
template <typename T>
|
||||
void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
|
||||
void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected, bool hovered)
|
||||
{
|
||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||
auto final = ember->IsFinalXform(xform);
|
||||
@ -1498,9 +1546,9 @@ void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
|
||||
MultMatrix(mat);
|
||||
//QueryMatrices(true);
|
||||
m_GL->glLineWidth(3.0f * m_GL->devicePixelRatioF());//One 3px wide, colored black, except green on x axis for post affine.
|
||||
m_GL->DrawAffineHelper(index, selected, pre, final, true);
|
||||
m_GL->DrawAffineHelper(index, selected, hovered, pre, final, true);
|
||||
m_GL->glLineWidth(1.0f * m_GL->devicePixelRatioF());//Again 1px wide, colored white, to give a white middle with black outline effect.
|
||||
m_GL->DrawAffineHelper(index, selected, pre, final, false);
|
||||
m_GL->DrawAffineHelper(index, selected, hovered, pre, final, false);
|
||||
m_GL->glPointSize(5.0f * m_GL->devicePixelRatioF());//Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline.
|
||||
m_GL->glBegin(GL_POINTS);
|
||||
m_GL->glColor4f(0.0f, 0.0f, 0.0f, selected ? 1.0f : 0.5f);
|
||||
@ -1528,9 +1576,9 @@ void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
|
||||
glm::tmat4x4<float, glm::defaultp> tempmat4 = mat;
|
||||
m_GL->m_ModelViewMatrix = QMatrix4x4(glm::value_ptr(tempmat4));
|
||||
m_GL->glLineWidth(3.0f * m_GL->devicePixelRatioF());//One 3px wide, colored black, except green on x axis for post affine.
|
||||
m_GL->DrawAffineHelper(index, selected, pre, final, true);
|
||||
m_GL->DrawAffineHelper(index, selected, hovered, pre, final, true);
|
||||
m_GL->glLineWidth(1.0f * m_GL->devicePixelRatioF());//Again 1px wide, colored white, to give a white middle with black outline effect.
|
||||
m_GL->DrawAffineHelper(index, selected, pre, final, false);
|
||||
m_GL->DrawAffineHelper(index, selected, hovered, pre, final, false);
|
||||
QVector4D col(0.0f, 0.0f, 0.0f, selected ? 1.0f : 0.5f);
|
||||
m_Verts.clear();
|
||||
m_Verts.push_back(0.0f);
|
||||
@ -1577,10 +1625,11 @@ void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="selected">True if selected (draw enclosing circle), else false (only draw axes).</param>
|
||||
/// <param name="hovered">True if the xform is being hovered over (draw tansparent disc), else false (no disc).</param>
|
||||
/// <param name="pre"></param>
|
||||
/// <param name="final"></param>
|
||||
/// <param name="background"></param>
|
||||
void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final, bool background)
|
||||
void GLWidget::DrawAffineHelper(int index, bool selected, bool hovered, bool pre, bool final, bool background)
|
||||
{
|
||||
float px = 1.0f;
|
||||
float py = 0.0f;
|
||||
@ -1641,32 +1690,28 @@ void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final,
|
||||
//Circle part.
|
||||
if (!background)
|
||||
{
|
||||
color = QVector4D(col.redF(), col.greenF(), col.blueF(), 1.0f);//Draw pre affine transform with white.
|
||||
color = QVector4D(col.redF(), col.greenF(), col.blueF(), hovered ? 0.25f : 1.0f);//Draw pre affine transform with white.
|
||||
}
|
||||
else
|
||||
{
|
||||
color = QVector4D(0.0f, 0.0f, 0.0f, 1.0f);//Draw pre affine transform outline with white.
|
||||
color = QVector4D(0.0f, 0.0f, 0.0f, hovered ? 0.25f : 1.0f);//Draw pre affine transform outline with white.
|
||||
}
|
||||
|
||||
m_Verts.clear();
|
||||
|
||||
if (selected)
|
||||
if (selected || hovered)
|
||||
{
|
||||
for (size_t i = 1; i <= 64; i++)//The circle.
|
||||
{
|
||||
float theta = float(M_PI) * 2.0f * float(i % 64) / 64.0f;
|
||||
float fx = std::cos(theta);
|
||||
float fy = std::sin(theta);
|
||||
m_Verts.push_back(px);
|
||||
m_Verts.push_back(py);
|
||||
m_Verts.push_back(fx);
|
||||
m_Verts.push_back(fy);
|
||||
px = fx;
|
||||
py = fy;
|
||||
}
|
||||
}
|
||||
|
||||
DrawPointOrLine(color, m_Verts, GL_LINES, !pre);
|
||||
DrawPointOrLine(color, m_Verts, hovered ? GL_TRIANGLE_FAN : GL_LINE_LOOP, !pre);
|
||||
}
|
||||
|
||||
//Lines from center to circle.
|
||||
if (!background)
|
||||
@ -1712,8 +1757,6 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
|
||||
{
|
||||
bool pre = m_Fractorium->ui.PreAffineGroupBox->isChecked();
|
||||
bool post = m_Fractorium->ui.PostAffineGroupBox->isChecked();
|
||||
bool preAll = pre && m_Fractorium->DrawAllPre();
|
||||
bool postAll = post && m_Fractorium->DrawAllPost();
|
||||
int i = 0, bestIndex = -1;
|
||||
T bestDist = 10;
|
||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||
@ -1729,10 +1772,11 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
|
||||
//These checks prevent highlighting the pre/post selected xform circle, when one is set to show all, and the other
|
||||
//is set to show current, and the user hovers over another xform, but doesn't select it, then moves the mouse
|
||||
//back over the hidden circle for the pre/post that was set to only show current.
|
||||
bool checkSelPre = preAll || (pre && m_HoverXform == m_SelectedXform);
|
||||
bool checkSelPost = postAll || (post && m_HoverXform == m_SelectedXform);
|
||||
bool isSel = m_Fractorium->IsXformSelected(ember->GetTotalXformIndex(m_SelectedXform));
|
||||
bool checkPre = pre && (m_Fractorium->DrawAllPre() || (m_Fractorium->DrawSelectedPre() && isSel) || m_Fractorium->DrawCurrentPre());
|
||||
bool checkPost = post && (m_Fractorium->DrawAllPost() || (m_Fractorium->DrawSelectedPost() && isSel) || m_Fractorium->DrawCurrentPost());
|
||||
|
||||
if (CheckXformHover(m_SelectedXform, glCoords, bestDist, checkSelPre, checkSelPost))
|
||||
if (CheckXformHover(m_SelectedXform, glCoords, bestDist, checkPre, checkPost))
|
||||
{
|
||||
m_HoverXform = m_SelectedXform;
|
||||
bestIndex = int(ember->GetTotalXformIndex(m_SelectedXform, forceFinal));
|
||||
@ -1740,24 +1784,35 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
|
||||
}
|
||||
|
||||
//Check all xforms.
|
||||
|
||||
while (auto xform = ember->GetTotalXform(i, forceFinal))
|
||||
{
|
||||
if (preAll || (pre && m_HoverXform == xform))//Only check pre affine if they are shown.
|
||||
bool isSel = m_Fractorium->IsXformSelected(i);
|
||||
|
||||
if (pre)
|
||||
{
|
||||
if (CheckXformHover(xform, glCoords, bestDist, true, false))
|
||||
bool checkPre = m_Fractorium->DrawAllPre() || (m_Fractorium->DrawSelectedPre() && isSel) || (m_SelectedXform == xform);
|
||||
|
||||
if (checkPre)//Only check pre affine if they are shown.
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
bestIndex = i;
|
||||
if (CheckXformHover(xform, glCoords, bestDist, true, false))
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (postAll || (post && m_HoverXform == xform))//Only check post affine if they are shown.
|
||||
if (post)
|
||||
{
|
||||
if (CheckXformHover(xform, glCoords, bestDist, false, true))
|
||||
bool checkPost = m_Fractorium->DrawAllPost() || (m_Fractorium->DrawSelectedPost() && isSel) || (m_SelectedXform == xform);
|
||||
|
||||
if (checkPost)
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
bestIndex = i;
|
||||
if (CheckXformHover(xform, glCoords, bestDist, false, true))
|
||||
{
|
||||
m_HoverXform = xform;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1885,15 +1940,15 @@ bool GLEmberController<T>::CheckXformHover(Xform<T>* xform, v3T& glCoords, T& be
|
||||
/// <summary>
|
||||
/// Calculate the new affine transform when dragging with the x axis with the left mouse button.
|
||||
/// The value returned will depend on whether any modifier keys were held down.
|
||||
/// None: Rotate and scale only.
|
||||
/// None: Rotate only.
|
||||
/// Local Pivot:
|
||||
/// Shift: Rotate only about affine center.
|
||||
/// Alt: Free transform.
|
||||
/// Shift + Alt: Rotate single axis about affine center.
|
||||
/// Control: Rotate and scale, snapping to grid.
|
||||
/// Control + Shift: Rotate only, snapping to grid.
|
||||
/// Control + Alt: Free transform, snapping to grid.
|
||||
/// Control + Shift + Alt: Rotate single axis about affine center, snapping to grid.
|
||||
/// Shift: Scale and optionally Rotate about affine center.
|
||||
/// Alt: Rotate single axis about affine center.
|
||||
/// Shift + Alt: Free transform.
|
||||
/// Control: Rotate, snapping to grid.
|
||||
/// Control + Shift: Scale and optionally Rotate, snapping to grid.
|
||||
/// Control + Alt: Rotate single axis about affine center, snapping to grid.
|
||||
/// Control + Shift + Alt: Free transform, snapping to grid.
|
||||
/// World Pivot:
|
||||
/// Shift + Alt: Rotate single axis about world center.
|
||||
/// Control + Shift + Alt: Rotate single axis about world center, snapping to grid.
|
||||
@ -1907,61 +1962,18 @@ void GLEmberController<T>::CalcDragXAxis()
|
||||
auto worldToAffineScale = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
bool pre = m_AffineType == eAffineType::AffinePre;
|
||||
bool worldPivotShiftAlt = !m_Fractorium->LocalPivot() && GetShift() && GetAlt();
|
||||
auto startDiff = (v2T(m_HoverHandlePos) * affineToWorldScale) - m_DragSrcTransform.O();
|
||||
T startAngle = std::atan2(startDiff.y, startDiff.x);
|
||||
auto worldRelAxisStartScaled = (v2T(m_HoverHandlePos) * affineToWorldScale) - m_DragSrcTransform.O();//World axis start position, relative, scaled by the zoom.
|
||||
T startAngle = std::atan2(worldRelAxisStartScaled.y, worldRelAxisStartScaled.x);
|
||||
v3T relScaled = (m_MouseWorldPos * affineToWorldScale) - v3T(m_DragSrcTransform.O(), 0);
|
||||
|
||||
if (GetShift())
|
||||
if (!GetShift())
|
||||
{
|
||||
v3T snapped = GetControl() ? SnapToNormalizedAngle(m_MouseWorldPos, 24u) : m_MouseWorldPos;
|
||||
auto endDiff = (v2T(snapped) * affineToWorldScale) - m_DragSrcTransform.O();
|
||||
T endAngle = std::atan2(endDiff.y, endDiff.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
auto srcRotated = m_DragSrcTransforms[selIndex];
|
||||
|
||||
if (worldPivotShiftAlt)
|
||||
{
|
||||
srcRotated.X(srcRotated.O() + srcRotated.X());
|
||||
srcRotated.O(v2T(0));
|
||||
srcRotated.Rotate(angle);
|
||||
affine.X(srcRotated.X() - affine.O());
|
||||
}
|
||||
else if (GetAlt())
|
||||
{
|
||||
srcRotated.Rotate(angle);
|
||||
affine.X(srcRotated.X());
|
||||
}
|
||||
else
|
||||
{
|
||||
srcRotated.Rotate(angle);
|
||||
affine = srcRotated;
|
||||
}
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T((affine.O() + affine.X()) * worldToAffineScale, 0);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Calling code will update renderer.
|
||||
}
|
||||
else
|
||||
{
|
||||
v3T diff = m_MouseWorldPos - m_MouseDownWorldPos;
|
||||
auto diffscale = diff * affineToWorldScale;
|
||||
auto origmag = Zeps(glm::length(m_DragSrcTransform.X()));
|
||||
auto origXPlusOff = v3T(m_DragSrcTransform.X(), 0) + diffscale;
|
||||
|
||||
if (GetControl())
|
||||
{
|
||||
auto o3 = v3T(m_DragSrcTransform.O(), 0);
|
||||
auto o3x = origXPlusOff + o3;
|
||||
origXPlusOff = SnapToGrid(o3x);
|
||||
origXPlusOff -= o3;
|
||||
relScaled = SnapToNormalizedAngle(relScaled, 24u);//relScaled is using the relative scaled position of the axis.
|
||||
}
|
||||
|
||||
auto newmag = glm::length(origXPlusOff);
|
||||
auto newprc = newmag / origmag;
|
||||
auto endDiff = (v2T(origXPlusOff) * affineToWorldScale);
|
||||
T endAngle = std::atan2(endDiff.y, endDiff.x);
|
||||
T endAngle = std::atan2(relScaled.y, relScaled.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
|
||||
{
|
||||
@ -1970,12 +1982,55 @@ void GLEmberController<T>::CalcDragXAxis()
|
||||
|
||||
if (GetAlt())
|
||||
{
|
||||
affine.X(v2T(origXPlusOff));//Absolute, not ratio.
|
||||
src.Rotate(angle);
|
||||
affine.X(src.X());
|
||||
}
|
||||
else
|
||||
{
|
||||
src.Rotate(angle);
|
||||
affine = src;
|
||||
}
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T((affine.O() + affine.X()) * worldToAffineScale, 0);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Calling code will update renderer.
|
||||
}
|
||||
else
|
||||
{
|
||||
auto origmag = Zeps(glm::length(m_DragSrcTransform.X()));//Magnitude of original dragged axis before it was dragged.
|
||||
|
||||
if (GetControl())
|
||||
{
|
||||
relScaled = SnapToGrid(relScaled);
|
||||
}
|
||||
|
||||
auto newmag = glm::length(relScaled);
|
||||
auto newprc = newmag / origmag;
|
||||
T endAngle = std::atan2(relScaled.y, relScaled.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
auto src = m_DragSrcTransforms[selIndex];
|
||||
|
||||
if (worldPivotShiftAlt)
|
||||
{
|
||||
src.X(src.O() + src.X());
|
||||
src.O(v2T(0));
|
||||
src.Rotate(angle);
|
||||
affine.X(src.X() - affine.O());
|
||||
}
|
||||
else if (GetAlt())
|
||||
{
|
||||
affine.X(v2T(relScaled));//Absolute, not ratio.
|
||||
}
|
||||
else
|
||||
{
|
||||
src.ScaleXY(newprc);
|
||||
src.Rotate(angle);
|
||||
|
||||
if (m_Fractorium->m_Settings->RotateAndScale())
|
||||
src.Rotate(angle);
|
||||
|
||||
affine = src;
|
||||
}
|
||||
|
||||
@ -1988,15 +2043,15 @@ void GLEmberController<T>::CalcDragXAxis()
|
||||
/// <summary>
|
||||
/// Calculate the new affine transform when dragging with the y axis with the left mouse button.
|
||||
/// The value returned will depend on whether any modifier keys were held down.
|
||||
/// None: Rotate and scale only.
|
||||
/// None: Rotate only.
|
||||
/// Local Pivot:
|
||||
/// Shift: Rotate only about affine center.
|
||||
/// Alt: Free transform.
|
||||
/// Shift + Alt: Rotate single axis about affine center.
|
||||
/// Control: Rotate and scale, snapping to grid.
|
||||
/// Control + Shift: Rotate only, snapping to grid.
|
||||
/// Control + Alt: Free transform, snapping to grid.
|
||||
/// Control + Shift + Alt: Rotate single axis about affine center, snapping to grid.
|
||||
/// Shift: Scale and optionally Rotate about affine center.
|
||||
/// Alt: Rotate single axis about affine center.
|
||||
/// Shift + Alt: Free transform.
|
||||
/// Control: Rotate, snapping to grid.
|
||||
/// Control + Shift: Scale and optionally Rotate, snapping to grid.
|
||||
/// Control + Alt: Rotate single axis about affine center, snapping to grid.
|
||||
/// Control + Shift + Alt: Free transform, snapping to grid.
|
||||
/// World Pivot:
|
||||
/// Shift + Alt: Rotate single axis about world center.
|
||||
/// Control + Shift + Alt: Rotate single axis about world center, snapping to grid.
|
||||
@ -2010,61 +2065,18 @@ void GLEmberController<T>::CalcDragYAxis()
|
||||
auto worldToAffineScale = m_FractoriumEmberController->AffineScaleCurrentToLocked();
|
||||
bool pre = m_AffineType == eAffineType::AffinePre;
|
||||
bool worldPivotShiftAlt = !m_Fractorium->LocalPivot() && GetShift() && GetAlt();
|
||||
auto startDiff = (v2T(m_HoverHandlePos) * affineToWorldScale) - m_DragSrcTransform.O();
|
||||
T startAngle = std::atan2(startDiff.y, startDiff.x);
|
||||
auto worldRelAxisStartScaled = (v2T(m_HoverHandlePos) * affineToWorldScale) - m_DragSrcTransform.O();//World axis start position, relative, scaled by the zoom.
|
||||
T startAngle = std::atan2(worldRelAxisStartScaled.y, worldRelAxisStartScaled.x);
|
||||
v3T relScaled = (m_MouseWorldPos * affineToWorldScale) - v3T(m_DragSrcTransform.O(), 0);
|
||||
|
||||
if (GetShift())
|
||||
if (!GetShift())
|
||||
{
|
||||
v3T snapped = GetControl() ? SnapToNormalizedAngle(m_MouseWorldPos, 24u) : m_MouseWorldPos;
|
||||
auto endDiff = (v2T(snapped) * affineToWorldScale) - m_DragSrcTransform.O();
|
||||
T endAngle = std::atan2(endDiff.y, endDiff.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
auto srcRotated = m_DragSrcTransforms[selIndex];
|
||||
|
||||
if (worldPivotShiftAlt)
|
||||
{
|
||||
srcRotated.Y(srcRotated.O() + srcRotated.Y());
|
||||
srcRotated.O(v2T(0));
|
||||
srcRotated.Rotate(angle);
|
||||
affine.Y(srcRotated.Y() - affine.O());
|
||||
}
|
||||
else if (GetAlt())
|
||||
{
|
||||
srcRotated.Rotate(angle);
|
||||
affine.Y(srcRotated.Y());
|
||||
}
|
||||
else
|
||||
{
|
||||
srcRotated.Rotate(angle);
|
||||
affine = srcRotated;
|
||||
}
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T((affine.O() + affine.Y()) * worldToAffineScale, 0);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Calling code will update renderer.
|
||||
}
|
||||
else
|
||||
{
|
||||
v3T diff = m_MouseWorldPos - m_MouseDownWorldPos;
|
||||
auto diffscale = diff * affineToWorldScale;
|
||||
auto origmag = Zeps(glm::length(m_DragSrcTransform.Y()));
|
||||
auto origYPlusOff = v3T(m_DragSrcTransform.Y(), 0) + diffscale;
|
||||
|
||||
if (GetControl())
|
||||
{
|
||||
auto o3 = v3T(m_DragSrcTransform.O(), 0);
|
||||
auto o3y = origYPlusOff + o3;
|
||||
origYPlusOff = SnapToGrid(o3y);
|
||||
origYPlusOff -= o3;
|
||||
relScaled = SnapToNormalizedAngle(relScaled, 24u);//relScaled is using the relative scaled position of the axis.
|
||||
}
|
||||
|
||||
auto newmag = glm::length(origYPlusOff);
|
||||
auto newprc = newmag / origmag;
|
||||
auto endDiff = (v2T(origYPlusOff) * affineToWorldScale);
|
||||
T endAngle = std::atan2(endDiff.y, endDiff.x);
|
||||
T endAngle = std::atan2(relScaled.y, relScaled.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
|
||||
{
|
||||
@ -2073,12 +2085,55 @@ void GLEmberController<T>::CalcDragYAxis()
|
||||
|
||||
if (GetAlt())
|
||||
{
|
||||
affine.Y(v2T(origYPlusOff));//Absolute, not ratio.
|
||||
src.Rotate(angle);
|
||||
affine.Y(src.Y());
|
||||
}
|
||||
else
|
||||
{
|
||||
src.Rotate(angle);
|
||||
affine = src;
|
||||
}
|
||||
|
||||
if (xform == m_FractoriumEmberController->CurrentXform())
|
||||
m_DragHandlePos = v3T((affine.O() + affine.Y()) * worldToAffineScale, 0);
|
||||
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Calling code will update renderer.
|
||||
}
|
||||
else
|
||||
{
|
||||
auto origmag = Zeps(glm::length(m_DragSrcTransform.Y()));//Magnitude of original dragged axis before it was dragged.
|
||||
|
||||
if (GetControl())
|
||||
{
|
||||
relScaled = SnapToGrid(relScaled);
|
||||
}
|
||||
|
||||
auto newmag = glm::length(relScaled);
|
||||
auto newprc = newmag / origmag;
|
||||
T endAngle = std::atan2(relScaled.y, relScaled.x);
|
||||
T angle = startAngle - endAngle;
|
||||
m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
|
||||
{
|
||||
auto& affine = pre ? xform->m_Affine : xform->m_Post;
|
||||
auto src = m_DragSrcTransforms[selIndex];
|
||||
|
||||
if (worldPivotShiftAlt)
|
||||
{
|
||||
src.Y(src.O() + src.Y());
|
||||
src.O(v2T(0));
|
||||
src.Rotate(angle);
|
||||
affine.Y(src.Y() - affine.O());
|
||||
}
|
||||
else if (GetAlt())
|
||||
{
|
||||
affine.Y(v2T(relScaled));//Absolute, not ratio.
|
||||
}
|
||||
else
|
||||
{
|
||||
src.ScaleXY(newprc);
|
||||
src.Rotate(angle);
|
||||
|
||||
if (m_Fractorium->m_Settings->RotateAndScale())
|
||||
src.Rotate(angle);
|
||||
|
||||
affine = src;
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ private:
|
||||
bool Deallocate();
|
||||
void SetViewport();
|
||||
void DrawUnitSquare();
|
||||
void DrawAffineHelper(int index, bool selected, bool pre, bool final, bool background);
|
||||
void DrawAffineHelper(int index, bool selected, bool hovered, bool pre, bool final, bool background);
|
||||
GLEmberControllerBase* GLController();
|
||||
|
||||
bool m_Init = false;
|
||||
|
@ -76,6 +76,7 @@ bool FractoriumOptionsDialog::ToggleType() { return ui.ToggleTypeCheckBox->isChe
|
||||
bool FractoriumOptionsDialog::Png16Bit() { return ui.Png16BitCheckBox->isChecked(); }
|
||||
bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); }
|
||||
bool FractoriumOptionsDialog::LoadLast() { return ui.LoadLastOnStartCheckBox->isChecked(); }
|
||||
bool FractoriumOptionsDialog::RotateAndScale() { return ui.RotateAndScaleCheckBox->isChecked(); }
|
||||
uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
|
||||
uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); }
|
||||
uint FractoriumOptionsDialog::CpuQuality() { return ui.CpuQualitySpin->value(); }
|
||||
@ -192,6 +193,7 @@ void FractoriumOptionsDialog::GuiToData()
|
||||
m_Settings->ThreadCount(ThreadCount());
|
||||
m_Settings->RandomCount(RandomCount());
|
||||
m_Settings->LoadLast(LoadLast());
|
||||
m_Settings->RotateAndScale(RotateAndScale());
|
||||
m_Settings->CpuQuality(CpuQuality());
|
||||
m_Settings->OpenClQuality(OpenClQuality());
|
||||
m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value());
|
||||
@ -230,6 +232,7 @@ void FractoriumOptionsDialog::DataToGui()
|
||||
ui.ThreadCountSpin->setValue(m_Settings->ThreadCount());
|
||||
ui.RandomCountSpin->setValue(m_Settings->RandomCount());
|
||||
ui.LoadLastOnStartCheckBox->setChecked(m_Settings->LoadLast());
|
||||
ui.RotateAndScaleCheckBox->setChecked(m_Settings->RotateAndScale());
|
||||
ui.CpuQualitySpin->setValue(m_Settings->CpuQuality());
|
||||
ui.OpenCLQualitySpin->setValue(m_Settings->OpenClQuality());
|
||||
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
bool Png16Bit();
|
||||
bool AutoUnique();
|
||||
bool LoadLast();
|
||||
bool RotateAndScale();
|
||||
uint ThreadCount();
|
||||
uint RandomCount();
|
||||
uint CpuQuality();
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>541</width>
|
||||
<height>475</height>
|
||||
<width>546</width>
|
||||
<height>490</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -496,6 +496,16 @@ in interactive mode for each mouse movement</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="RotateAndScaleCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Checked: scale and rotate when dragging affines while holding shift.</p><p>Unchecked: only scale when dragging affines while holding shift.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Scale and Rotate With Shift</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="OptionsXmlSavingTab">
|
||||
@ -913,9 +923,15 @@ in interactive mode for each mouse movement</string>
|
||||
<tabstops>
|
||||
<tabstop>EarlyClipCheckBox</tabstop>
|
||||
<tabstop>OpenCLCheckBox</tabstop>
|
||||
<tabstop>YAxisUpCheckBox</tabstop>
|
||||
<tabstop>SharedTextureCheckBox</tabstop>
|
||||
<tabstop>TransparencyCheckBox</tabstop>
|
||||
<tabstop>DoublePrecisionCheckBox</tabstop>
|
||||
<tabstop>ContinuousUpdateCheckBox</tabstop>
|
||||
<tabstop>ShowAllXformsCheckBox</tabstop>
|
||||
<tabstop>Png16BitCheckBox</tabstop>
|
||||
<tabstop>ToggleTypeCheckBox</tabstop>
|
||||
<tabstop>RotateAndScaleCheckBox</tabstop>
|
||||
<tabstop>LoadLastOnStartCheckBox</tabstop>
|
||||
<tabstop>ThreadCountSpin</tabstop>
|
||||
<tabstop>CpuSubBatchSpin</tabstop>
|
||||
|
Reference in New Issue
Block a user