mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-02-01 18:40:12 -05:00
--User changes
-Attempt to preserve xaos when adding xforms. Note this is not an exact copy, but just a preservation of some values based on position. -Add some acceleration to the changing of spinner values when dragging the right mouse button to adjust. -Make the pivot be the center of the viewable area when doing drag/rotate/scale with the right mouse button. --Clamp minimum scale to 10 --Draw a line from the mouse position to the pivot. -Keep a cache of the last added final xform with each flame so that it can be quickly added, removed, then added back for testing its effect. --This is not saved with the xml file and is solely for interactive editing. --Bug fixes -File filtering in open and save dialogs were broken. -Right clicking on integer spin boxes was causing the context menu to pop up, when it should be supressed just like double spin boxes. -Deleting xforms was still broken. --Code changes -Refactor the code for adding and pasting xforms into a single global static function called AddXformsWithXaos().
This commit is contained in:
parent
26c558a2f5
commit
dee4304bf2
@ -179,6 +179,7 @@ public:
|
|||||||
|
|
||||||
CopyCont(m_EmberMotionElements, ember.m_EmberMotionElements);
|
CopyCont(m_EmberMotionElements, ember.m_EmberMotionElements);
|
||||||
m_Solo = ember.m_Solo;
|
m_Solo = ember.m_Solo;
|
||||||
|
m_CachedFinal = ember.m_CachedFinal;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1724,9 +1725,6 @@ public:
|
|||||||
//The list of motion elements for the top-level flame params
|
//The list of motion elements for the top-level flame params
|
||||||
vector<EmberMotion<T>> m_EmberMotionElements;
|
vector<EmberMotion<T>> m_EmberMotionElements;
|
||||||
|
|
||||||
//Index of xform to have non-zero opacity, while all others have zero. This is an interactive rendering parameter and is not saved to Xml. -1 means solo is not used.
|
|
||||||
intmax_t m_Solo = -1;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of scaling used when resizing.
|
/// The type of scaling used when resizing.
|
||||||
@ -1810,6 +1808,13 @@ private:
|
|||||||
for (size_t k = 0; k < size; k++)
|
for (size_t k = 0; k < size; k++)
|
||||||
xform->*m += coefs[k] * embers[k].GetTotalXform(i)->*m;
|
xform->*m += coefs[k] * embers[k].GetTotalXform(i)->*m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
//Index of xform to have non-zero opacity, while all others have zero. This is an interactive rendering parameter and is not saved to Xml. -1 means solo is not used.
|
||||||
|
intmax_t m_Solo = -1;
|
||||||
|
|
||||||
|
//Cached copy of the final xform which makes it easy to add and remove the final repeatedly during editing without losing information. Used only with interactive rendering.
|
||||||
|
Xform<T> m_CachedFinal;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -793,6 +793,43 @@ static vector<const Variation<T>*> FindVarsWithout(const vector<const Variation<
|
|||||||
|
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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="preserveXaos">True to preserve xaos else false.</param>
|
||||||
|
template <typename T>
|
||||||
|
static void AddXformsWithXaos(Ember<T>& ember, std::vector<Xform<T>>& xforms, bool preserveXaos)
|
||||||
|
{
|
||||||
|
auto origXformCount = ember.XformCount();
|
||||||
|
|
||||||
|
for (auto& it : xforms)
|
||||||
|
ember.AddXform(it);
|
||||||
|
|
||||||
|
for (auto i = 0; i < ember.XformCount(); i++)
|
||||||
|
{
|
||||||
|
auto xf = ember.GetXform(i);
|
||||||
|
|
||||||
|
if (i < origXformCount)
|
||||||
|
{
|
||||||
|
for (auto j = 0; j < ember.XformCount(); j++)
|
||||||
|
if (j >= origXformCount)
|
||||||
|
xf->SetXaos(j, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto j = 0; j < ember.XformCount(); j++)
|
||||||
|
if (j < origXformCount)
|
||||||
|
xf->SetXaos(j, 0);
|
||||||
|
else if (!preserveXaos)
|
||||||
|
xf->SetXaos(j, 1);
|
||||||
|
else if (i - origXformCount < xforms.size())//Should never be out of bounds, but just to be safe.
|
||||||
|
xf->SetXaos(j, xforms[i - origXformCount].Xaos(j - origXformCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -146,6 +146,7 @@ void DoubleSpinBox::OnTimeout()
|
|||||||
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
|
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
|
||||||
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
|
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
|
||||||
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
|
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
|
||||||
|
distance = Sqr(distance) * (distance < 0 ? -1 : 1);
|
||||||
double scale, val;
|
double scale, val;
|
||||||
double d = value();
|
double d = value();
|
||||||
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
||||||
|
@ -601,7 +601,7 @@ QStringList Fractorium::SetupOpenXmlDialog()
|
|||||||
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->OpenXmlExt(filter); });
|
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->OpenXmlExt(filter); });
|
||||||
m_FileDialog->setFileMode(QFileDialog::ExistingFiles);
|
m_FileDialog->setFileMode(QFileDialog::ExistingFiles);
|
||||||
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
|
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
|
||||||
m_FileDialog->setNameFilter("*.flam3;;*.flame;;*.xml");
|
m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)");
|
||||||
m_FileDialog->setWindowTitle("Open Flame");
|
m_FileDialog->setWindowTitle("Open Flame");
|
||||||
m_FileDialog->setDirectory(m_Settings->OpenFolder());
|
m_FileDialog->setDirectory(m_Settings->OpenFolder());
|
||||||
m_FileDialog->selectNameFilter(m_Settings->OpenXmlExt());
|
m_FileDialog->selectNameFilter(m_Settings->OpenXmlExt());
|
||||||
@ -616,7 +616,7 @@ QStringList Fractorium::SetupOpenXmlDialog()
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
auto defaultFilter(m_Settings->OpenXmlExt());
|
auto defaultFilter(m_Settings->OpenXmlExt());
|
||||||
auto filenames = QFileDialog::getOpenFileNames(this, tr("Open Flame"), m_Settings->OpenFolder(), tr("Flame Files (*.flam3 *.flame *.xml)"), &defaultFilter);
|
auto filenames = QFileDialog::getOpenFileNames(this, tr("Open Flame"), m_Settings->OpenFolder(), tr("Flam3(*.flam3);; Flame(*.flame);; Xml(*.xml)"), &defaultFilter);
|
||||||
m_Settings->OpenXmlExt(defaultFilter);
|
m_Settings->OpenXmlExt(defaultFilter);
|
||||||
|
|
||||||
if (!filenames.empty())
|
if (!filenames.empty())
|
||||||
@ -655,7 +655,7 @@ QString Fractorium::SetupSaveXmlDialog(const QString& defaultFilename)
|
|||||||
//This is most likely a bug in QFileDialog.
|
//This is most likely a bug in QFileDialog.
|
||||||
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
|
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
|
||||||
m_FileDialog->selectFile(defaultFilename);
|
m_FileDialog->selectFile(defaultFilename);
|
||||||
m_FileDialog->setNameFilter(".flam3;;.flame;;.xml");
|
m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)");
|
||||||
m_FileDialog->setWindowTitle("Save flame as xml");
|
m_FileDialog->setWindowTitle("Save flame as xml");
|
||||||
m_FileDialog->setDirectory(m_Settings->SaveFolder());
|
m_FileDialog->setDirectory(m_Settings->SaveFolder());
|
||||||
m_FileDialog->selectNameFilter(m_Settings->SaveXmlExt());
|
m_FileDialog->selectNameFilter(m_Settings->SaveXmlExt());
|
||||||
|
@ -1922,10 +1922,13 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="SpinBox" name="AddLayerSpinBox">
|
<widget class="QSpinBox" name="AddLayerSpinBox">
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::StrongFocus</enum>
|
<enum>Qt::StrongFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string> Xforms</string>
|
<string> Xforms</string>
|
||||||
</property>
|
</property>
|
||||||
@ -8374,11 +8377,6 @@
|
|||||||
<extends>QTreeWidget</extends>
|
<extends>QTreeWidget</extends>
|
||||||
<header>LibraryTreeWidget.h</header>
|
<header>LibraryTreeWidget.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
|
||||||
<class>SpinBox</class>
|
|
||||||
<extends>QSpinBox</extends>
|
|
||||||
<header>spinbox.h</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>LibraryDockWidget</tabstop>
|
<tabstop>LibraryDockWidget</tabstop>
|
||||||
|
@ -699,8 +699,7 @@ void FractoriumEmberController<T>::PasteSelectedXforms()
|
|||||||
{
|
{
|
||||||
Update([&]()
|
Update([&]()
|
||||||
{
|
{
|
||||||
for (auto& it : m_CopiedXforms)
|
AddXformsWithXaos(m_Ember, m_CopiedXforms, true);
|
||||||
m_Ember.AddXform(it);
|
|
||||||
|
|
||||||
if (!m_CopiedFinalXform.Empty())
|
if (!m_CopiedFinalXform.Empty())
|
||||||
m_Ember.SetFinalXform(m_CopiedFinalXform);
|
m_Ember.SetFinalXform(m_CopiedFinalXform);
|
||||||
|
@ -180,28 +180,9 @@ void FractoriumEmberController<T>::AddLayer(int xforms)
|
|||||||
{
|
{
|
||||||
Update([&]
|
Update([&]
|
||||||
{
|
{
|
||||||
auto origXformCount = m_Ember.XformCount();
|
std::vector<Xform<T>> vec(xforms);
|
||||||
m_Ember.AddXforms(xforms);
|
AddXformsWithXaos(m_Ember, vec, false);
|
||||||
|
|
||||||
for (auto i = 0; i < m_Ember.XformCount(); i++)
|
|
||||||
{
|
|
||||||
auto xf = m_Ember.GetXform(i);
|
|
||||||
|
|
||||||
if (i < origXformCount)
|
|
||||||
{
|
|
||||||
for (auto j = 0; j < m_Ember.XformCount(); j++)
|
|
||||||
if (j >= origXformCount)
|
|
||||||
xf->SetXaos(j, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (auto j = 0; j < m_Ember.XformCount(); j++)
|
|
||||||
if (j < origXformCount)
|
|
||||||
xf->SetXaos(j, 0);
|
|
||||||
else
|
|
||||||
xf->SetXaos(j, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
FillXforms();
|
FillXforms();
|
||||||
FillSummary();
|
FillSummary();
|
||||||
@ -210,25 +191,35 @@ void FractoriumEmberController<T>::AddLayer(int xforms)
|
|||||||
void Fractorium::OnAddLayerButtonClicked(bool checked) { m_Controller->AddLayer(ui.AddLayerSpinBox->value()); }
|
void Fractorium::OnAddLayerButtonClicked(bool checked) { m_Controller->AddLayer(ui.AddLayerSpinBox->value()); }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Toggle all xaos values in one row.
|
/// Toggle all xaos values in one row on left mouse button double click and resize all cells to fit their data.
|
||||||
|
/// Skip toggling and only refit on right mouse button double click.
|
||||||
/// Resets the rendering process.
|
/// Resets the rendering process.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logicalIndex">The index of the row that was double clicked</param>
|
/// <param name="logicalIndex">The index of the row that was double clicked</param>
|
||||||
void Fractorium::OnXaosRowDoubleClicked(int logicalIndex)
|
void Fractorium::OnXaosRowDoubleClicked(int logicalIndex)
|
||||||
{
|
{
|
||||||
ToggleTableRow(ui.XaosTableView, logicalIndex);
|
auto btn = QApplication::mouseButtons();
|
||||||
|
|
||||||
|
if (!btn.testFlag(Qt::RightButton))
|
||||||
|
ToggleTableRow(ui.XaosTableView, logicalIndex);
|
||||||
|
|
||||||
ui.XaosTableView->resizeRowsToContents();
|
ui.XaosTableView->resizeRowsToContents();
|
||||||
ui.XaosTableView->resizeColumnsToContents();
|
ui.XaosTableView->resizeColumnsToContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Toggle all xaos values in one column.
|
/// Toggle all xaos values in one column on left mouse button double click and resize all cells to fit their data.
|
||||||
|
/// Skip toggling and only refit on right mouse button double click.
|
||||||
/// Resets the rendering process.
|
/// Resets the rendering process.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logicalIndex">The index of the column that was double clicked</param>
|
/// <param name="logicalIndex">The index of the column that was double clicked</param>
|
||||||
void Fractorium::OnXaosColDoubleClicked(int logicalIndex)
|
void Fractorium::OnXaosColDoubleClicked(int logicalIndex)
|
||||||
{
|
{
|
||||||
ToggleTableCol(ui.XaosTableView, logicalIndex);
|
auto btn = QApplication::mouseButtons();
|
||||||
|
|
||||||
|
if (!btn.testFlag(Qt::RightButton))
|
||||||
|
ToggleTableCol(ui.XaosTableView, logicalIndex);
|
||||||
|
|
||||||
ui.XaosTableView->resizeRowsToContents();
|
ui.XaosTableView->resizeRowsToContents();
|
||||||
ui.XaosTableView->resizeColumnsToContents();
|
ui.XaosTableView->resizeColumnsToContents();
|
||||||
}
|
}
|
||||||
|
@ -224,9 +224,7 @@ void FractoriumEmberController<T>::DuplicateXform()
|
|||||||
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL, false);
|
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL, false);
|
||||||
Update([&]()
|
Update([&]()
|
||||||
{
|
{
|
||||||
for (auto& it : vec)
|
AddXformsWithXaos(m_Ember, vec, true);
|
||||||
m_Ember.AddXform(it);
|
|
||||||
|
|
||||||
int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
|
int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
|
||||||
FillXforms(index);//Handles xaos.
|
FillXforms(index);//Handles xaos.
|
||||||
});
|
});
|
||||||
@ -254,6 +252,7 @@ void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearXf
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete the selected xforms.
|
/// 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.
|
/// Will not delete the last remaining non-final xform.
|
||||||
/// Called when the delete xform button is clicked.
|
/// Called when the delete xform button is clicked.
|
||||||
/// Resets the rendering process.
|
/// Resets the rendering process.
|
||||||
@ -263,11 +262,12 @@ template <typename T>
|
|||||||
void FractoriumEmberController<T>::DeleteXforms()
|
void FractoriumEmberController<T>::DeleteXforms()
|
||||||
{
|
{
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
|
bool anyChecked = false;
|
||||||
bool haveFinal = m_Fractorium->HaveFinal();
|
bool haveFinal = m_Fractorium->HaveFinal();
|
||||||
auto combo = m_Fractorium->ui.CurrentXformCombo;
|
auto combo = m_Fractorium->ui.CurrentXformCombo;
|
||||||
Xform<T>* finalXform = nullptr;
|
Xform<T>* finalXform = nullptr;
|
||||||
vector<Xform<T>> xforms;
|
vector<Xform<T>> xformsToKeep;
|
||||||
xforms.reserve(m_Ember.TotalXformCount());
|
xformsToKeep.reserve(m_Ember.TotalXformCount());
|
||||||
//Iterating over the checkboxes must be done instead of using UpdateXform() to iterate over xforms
|
//Iterating over the checkboxes must be done instead of using UpdateXform() to iterate over xforms
|
||||||
//because xforms are being deleted inside the loop.
|
//because xforms are being deleted inside the loop.
|
||||||
//Also manually calling UpdateRender() rather than using the usual Update() call because
|
//Also manually calling UpdateRender() rather than using the usual Update() call because
|
||||||
@ -280,37 +280,49 @@ void FractoriumEmberController<T>::DeleteXforms()
|
|||||||
if (isFinal)
|
if (isFinal)
|
||||||
finalXform = m_Ember.NonConstFinalXform();
|
finalXform = m_Ember.NonConstFinalXform();
|
||||||
else if (auto xform = m_Ember.GetXform(i))
|
else if (auto xform = m_Ember.GetXform(i))
|
||||||
xforms.push_back(*xform);
|
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.
|
//They might not have selected any checkboxes, in which case just delete the current.
|
||||||
auto current = combo->currentIndex();
|
auto current = combo->currentIndex();
|
||||||
auto count = m_Ember.TotalXformCount();
|
auto totalCount = m_Ember.TotalXformCount();
|
||||||
bool anySelected = xforms.size() < m_Ember.XformCount();
|
bool keepFinal = finalXform && haveFinal;
|
||||||
bool finalSelected = !finalXform && haveFinal;
|
|
||||||
|
|
||||||
//Nothing was selected, so just delete current.
|
//Nothing was selected, so just delete current.
|
||||||
if (!anySelected && !finalSelected)
|
if (!anyChecked)
|
||||||
{
|
{
|
||||||
//Disallow deleting the only remaining non-final xform.
|
//Disallow deleting the only remaining non-final xform.
|
||||||
if (!(haveFinal && count <= 2 && current == 0) &&//One non-final, one final, disallow deleting non-final.
|
if (!(haveFinal && totalCount <= 2 && current == 0) &&//One non-final, one final, disallow deleting non-final.
|
||||||
!(!haveFinal && count == 1))//One non-final, no final, disallow deleting.
|
!(!haveFinal && totalCount == 1))//One non-final, no final, disallow deleting.
|
||||||
{
|
{
|
||||||
m_Ember.DeleteTotalXform(current, haveFinal);
|
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;
|
removed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!xforms.empty() && (xforms.size() != m_Ember.XformCount()))//Remove if they requested to do so, but ensure it's not removing all.
|
if (!xformsToKeep.empty())//Remove if they requested to do so, but ensure it's not removing all.
|
||||||
{
|
{
|
||||||
removed = true;
|
removed = true;
|
||||||
m_Ember.ReplaceXforms(xforms);
|
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 (finalSelected)
|
if (!keepFinal)//They selected final to delete.
|
||||||
{
|
{
|
||||||
removed = true;
|
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();
|
m_Ember.NonConstFinalXform()->Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,6 +332,7 @@ void FractoriumEmberController<T>::DeleteXforms()
|
|||||||
int index = int(m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1));//Set index to the last item before final. Note final is requeried one last time.
|
int index = 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);
|
FillXforms(index);
|
||||||
UpdateRender();
|
UpdateRender();
|
||||||
|
m_Fractorium->ui.GLDisplay->repaint();//Force update because for some reason it doesn't always happen.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,6 +340,7 @@ void Fractorium::OnDeleteXformButtonClicked(bool checked) { m_Controller->Delete
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a final xform to the ember and set it as the current xform.
|
/// 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 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.
|
/// Called when the add final xform button is clicked.
|
||||||
/// Resets the rendering process.
|
/// Resets the rendering process.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -339,9 +353,12 @@ void FractoriumEmberController<T>::AddFinalXform()
|
|||||||
{
|
{
|
||||||
Update([&]()
|
Update([&]()
|
||||||
{
|
{
|
||||||
Xform<T> final;
|
auto& final = m_Ember.m_CachedFinal;
|
||||||
final.m_Animate = 0;
|
final.m_Animate = 0;
|
||||||
final.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));//Just a placeholder so other parts of the code don't see it as being empty.
|
|
||||||
|
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);
|
m_Ember.SetFinalXform(final);
|
||||||
int index = int(m_Ember.TotalXformCount() - 1);//Set index to the last item.
|
int index = int(m_Ember.TotalXformCount() - 1);//Set index to the last item.
|
||||||
FillXforms(index);
|
FillXforms(index);
|
||||||
|
@ -82,7 +82,7 @@ T GLEmberController<T>::CalcScale()
|
|||||||
{
|
{
|
||||||
//Can't operate using world coords here because every time scale changes, the world bounds change.
|
//Can't operate using world coords here because every time scale changes, the world bounds change.
|
||||||
//So must instead calculate distance traveled based on window coords, which do not change outside of resize events.
|
//So must instead calculate distance traveled based on window coords, which do not change outside of resize events.
|
||||||
v2T windowCenter(T(m_GL->width()) / T(2), T(m_GL->height()) / T(2));
|
auto windowCenter = ScrolledCenter(false);
|
||||||
v2T windowMousePosDistanceFromCenter(m_MousePos.x - windowCenter.x, m_MousePos.y - windowCenter.y);
|
v2T windowMousePosDistanceFromCenter(m_MousePos.x - windowCenter.x, m_MousePos.y - windowCenter.y);
|
||||||
v2T windowMouseDownDistanceFromCenter(m_MouseDownPos.x - windowCenter.x, m_MouseDownPos.y - windowCenter.y);
|
v2T windowMouseDownDistanceFromCenter(m_MouseDownPos.x - windowCenter.x, m_MouseDownPos.y - windowCenter.y);
|
||||||
T lengthMousePosFromCenterInPixels = glm::length(windowMousePosDistanceFromCenter);
|
T lengthMousePosFromCenterInPixels = glm::length(windowMousePosDistanceFromCenter);
|
||||||
@ -98,11 +98,43 @@ T GLEmberController<T>::CalcScale()
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T GLEmberController<T>::CalcRotation()
|
T GLEmberController<T>::CalcRotation()
|
||||||
{
|
{
|
||||||
T rotStart = NormalizeDeg180<T>(T(90) - (atan2(-m_MouseDownWorldPos.y, m_MouseDownWorldPos.x) * RAD_2_DEG_T));
|
auto scrolledWorldCenter = ScrolledCenter(true);
|
||||||
T rot = NormalizeDeg180<T>(T(90) - (atan2(-m_MouseWorldPos.y, m_MouseWorldPos.x) * RAD_2_DEG_T));
|
T rotStart = NormalizeDeg180<T>((std::atan2(m_MouseDownWorldPos.y - scrolledWorldCenter.y, m_MouseDownWorldPos.x - scrolledWorldCenter.x) * RAD_2_DEG_T));
|
||||||
|
T rot = NormalizeDeg180<T>((std::atan2(m_MouseWorldPos.y - scrolledWorldCenter.y, m_MouseWorldPos.x - scrolledWorldCenter.x) * RAD_2_DEG_T));
|
||||||
return rotStart - rot;
|
return rotStart - rot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the window coordinates of the center of the viewable area.
|
||||||
|
/// This is the middle of the parent scroll area plus the scroll bar offset, all scaled by the device pixel ratio.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toWorld">True to return world coordinates, else return window coordinates.</param>
|
||||||
|
/// <returns>The coordinates of the center of the viewable area in either window space or world space.</returns>
|
||||||
|
template <typename T>
|
||||||
|
v3T GLEmberController<T>::ScrolledCenter(bool toWorld)
|
||||||
|
{
|
||||||
|
auto dprf = m_GL->devicePixelRatioF();
|
||||||
|
auto wpsa = m_Fractorium->ui.GLParentScrollArea->width();
|
||||||
|
auto hpsa = m_Fractorium->ui.GLParentScrollArea->height();
|
||||||
|
auto hpos = m_Fractorium->ui.GLParentScrollArea->horizontalScrollBar()->value();
|
||||||
|
auto vpos = m_Fractorium->ui.GLParentScrollArea->verticalScrollBar()->value();
|
||||||
|
v3T v;
|
||||||
|
|
||||||
|
if (!m_Fractorium->ui.GLParentScrollArea->horizontalScrollBar()->isVisible() && !m_Fractorium->ui.GLParentScrollArea->verticalScrollBar()->isVisible())
|
||||||
|
v = v3T(((m_GL->width() / 2)) * dprf,
|
||||||
|
((m_GL->height() / 2)) * dprf,
|
||||||
|
0);
|
||||||
|
else
|
||||||
|
v = v3T((hpos + (wpsa / 2)) * dprf,
|
||||||
|
(vpos + (hpsa / 2)) * dprf,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (toWorld)
|
||||||
|
return WindowToWorld(v, true);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Snap the passed in world cartesian coordinate to the grid for rotation, scale or translation.
|
/// Snap the passed in world cartesian coordinate to the grid for rotation, scale or translation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -61,6 +61,7 @@ public:
|
|||||||
void ClearAlt();
|
void ClearAlt();
|
||||||
void ClearShift();
|
void ClearShift();
|
||||||
void ClearControl();
|
void ClearControl();
|
||||||
|
eDragState DragState() { return m_DragState; }
|
||||||
virtual void DrawImage() { }
|
virtual void DrawImage() { }
|
||||||
virtual void DrawAffines(bool pre, bool post) { }
|
virtual void DrawAffines(bool pre, bool post) { }
|
||||||
virtual void ClearWindow() { }
|
virtual void ClearWindow() { }
|
||||||
@ -111,6 +112,7 @@ public:
|
|||||||
|
|
||||||
T CalcScale();
|
T CalcScale();
|
||||||
T CalcRotation();
|
T CalcRotation();
|
||||||
|
v3T ScrolledCenter(bool toWorld = false);
|
||||||
void CalcDragXAxis();
|
void CalcDragXAxis();
|
||||||
void CalcDragYAxis();
|
void CalcDragYAxis();
|
||||||
void CalcDragTranslation();
|
void CalcDragTranslation();
|
||||||
|
@ -416,6 +416,8 @@ bool GLWidget::Init() { return m_Init; }
|
|||||||
bool GLWidget::Drawing() { return m_Drawing; }
|
bool GLWidget::Drawing() { return m_Drawing; }
|
||||||
GLint GLWidget::MaxTexSize() { return m_MaxTexSize; }
|
GLint GLWidget::MaxTexSize() { return m_MaxTexSize; }
|
||||||
GLuint GLWidget::OutputTexID() { return m_OutputTexID; }
|
GLuint GLWidget::OutputTexID() { return m_OutputTexID; }
|
||||||
|
GLint GLWidget::TexWidth() { return m_TexWidth; }
|
||||||
|
GLint GLWidget::TexHeight() { return m_TexHeight; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize OpenGL, called once at startup after the main window constructor finishes.
|
/// Initialize OpenGL, called once at startup after the main window constructor finishes.
|
||||||
@ -622,13 +624,28 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
|
|||||||
{
|
{
|
||||||
QueryVMP();//Resolves to float or double specialization function depending on T.
|
QueryVMP();//Resolves to float or double specialization function depending on T.
|
||||||
|
|
||||||
if (!m_Fractorium->DrawXforms() || (m_DragState == eDragState::DragRotateScale))
|
if (!m_Fractorium->DrawXforms())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto ember = m_FractoriumEmberController->CurrentEmber();
|
auto ember = m_FractoriumEmberController->CurrentEmber();
|
||||||
bool dragging = m_DragState == eDragState::DragDragging;
|
bool dragging = m_DragState == eDragState::DragDragging;
|
||||||
bool forceFinal = m_Fractorium->HaveFinal();
|
bool forceFinal = m_Fractorium->HaveFinal();
|
||||||
|
|
||||||
|
if (m_DragState == eDragState::DragRotateScale)
|
||||||
|
{
|
||||||
|
auto dprf = m_GL->devicePixelRatioF();
|
||||||
|
auto world = ScrolledCenter(true);
|
||||||
|
|
||||||
|
m_GL->glLineWidth(1.0f * dprf);
|
||||||
|
GLfloat vertices[] =
|
||||||
|
{
|
||||||
|
GLfloat(m_MouseWorldPos.x), GLfloat(m_MouseWorldPos.y),//Mouse position while dragging with right button down...
|
||||||
|
GLfloat(world.x), GLfloat(world.y)//...to center.
|
||||||
|
};
|
||||||
|
QVector4D col(0.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
m_GL->DrawPointOrLine(col, vertices, 2, GL_LINES);
|
||||||
|
}
|
||||||
|
|
||||||
//Draw grid if control key is pressed.
|
//Draw grid if control key is pressed.
|
||||||
if ((m_GL->hasFocus() && GetControl()) || m_Fractorium->DrawGrid())
|
if ((m_GL->hasFocus() && GetControl()) || m_Fractorium->DrawGrid())
|
||||||
DrawGrid();
|
DrawGrid();
|
||||||
@ -1003,7 +1020,7 @@ void GLEmberController<T>::MouseMove(QMouseEvent* e)
|
|||||||
T scale = CalcScale();
|
T scale = CalcScale();
|
||||||
ember->m_Rotate = NormalizeDeg180<T>(m_RotationDown + rot);
|
ember->m_Rotate = NormalizeDeg180<T>(m_RotationDown + rot);
|
||||||
m_Fractorium->SetRotation(ember->m_Rotate, true);
|
m_Fractorium->SetRotation(ember->m_Rotate, true);
|
||||||
m_Fractorium->SetScale(m_ScaleDown + scale);//Will restart the rendering process.
|
m_Fractorium->SetScale(std::max(T(10), m_ScaleDown + scale));//Will restart the rendering process.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1532,6 +1549,14 @@ void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
|
|||||||
m_Verts.push_back(1.0f);
|
m_Verts.push_back(1.0f);
|
||||||
col = QVector4D(color.r, color.g, color.b, 1.0f);
|
col = QVector4D(color.r, color.g, color.b, 1.0f);
|
||||||
m_GL->DrawPointOrLine(col, m_Verts, GL_LINES, !pre);
|
m_GL->DrawPointOrLine(col, m_Verts, GL_LINES, !pre);
|
||||||
|
//Line from x to y in the color of the xform combo, solid with no background to somewhat distinguish it.
|
||||||
|
m_Verts.clear();
|
||||||
|
m_Verts.push_back(0);
|
||||||
|
m_Verts.push_back(1);
|
||||||
|
m_Verts.push_back(1);
|
||||||
|
m_Verts.push_back(0);
|
||||||
|
auto qcol = final ? m_Fractorium->m_FinalXformComboColor : m_Fractorium->m_XformComboColors[index % XFORM_COLOR_COUNT];
|
||||||
|
m_GL->DrawPointOrLine(QVector4D(qcol.redF(), qcol.greenF(), qcol.blueF(), 1.0f), m_Verts, GL_LINES, !pre);
|
||||||
//
|
//
|
||||||
m_Verts.clear();
|
m_Verts.clear();
|
||||||
m_Verts.push_back(0.0f);
|
m_Verts.push_back(0.0f);
|
||||||
|
@ -55,6 +55,8 @@ public:
|
|||||||
bool Drawing();
|
bool Drawing();
|
||||||
GLint MaxTexSize();
|
GLint MaxTexSize();
|
||||||
GLuint OutputTexID();
|
GLuint OutputTexID();
|
||||||
|
GLint TexWidth();
|
||||||
|
GLint TexHeight();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void initializeGL() override;
|
virtual void initializeGL() override;
|
||||||
|
@ -29,6 +29,7 @@ SpinBox::SpinBox(QWidget* p, int h, int step)
|
|||||||
setFocusPolicy(Qt::StrongFocus);
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
setMinimumHeight(h);//setGeometry() has no effect, so set both of these instead.
|
setMinimumHeight(h);//setGeometry() has no effect, so set both of these instead.
|
||||||
setMaximumHeight(h);
|
setMaximumHeight(h);
|
||||||
|
setContextMenuPolicy(Qt::PreventContextMenu);
|
||||||
lineEdit()->installEventFilter(this);
|
lineEdit()->installEventFilter(this);
|
||||||
lineEdit()->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
lineEdit()->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
connect(this, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxValueChanged(int)), Qt::QueuedConnection);
|
connect(this, SIGNAL(valueChanged(int)), this, SLOT(onSpinBoxValueChanged(int)), Qt::QueuedConnection);
|
||||||
@ -136,6 +137,7 @@ void SpinBox::OnTimeout()
|
|||||||
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
|
int xdistance = m_MouseMovePoint.x() - m_MouseDownPoint.x();
|
||||||
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
|
int ydistance = m_MouseMovePoint.y() - m_MouseDownPoint.y();
|
||||||
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
|
int distance = abs(xdistance) > abs(ydistance) ? xdistance : ydistance;
|
||||||
|
distance = Sqr(distance) * (distance < 0 ? -1 : 1);
|
||||||
double scale, val;
|
double scale, val;
|
||||||
int d = value();
|
int d = value();
|
||||||
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
||||||
|
Loading…
Reference in New Issue
Block a user