--User changes

-Give tabs a height of 4px in the qss files. Looks a little large on 4k screens, but just right on HD screens which are much more common.
 -Allow for styling of zero and non-zero variation tree nodes via qss.
 -Allow for toggling whether to interpolate between colors in the palette editor, or to do hard cuts between colors.
 -Allow for adjusting spinner values with the + = or up arrow keys to increase, and - _ or down arrow keys to decrease.
 -Allow for responding to global presses of + = and - _ to cycle up or down to specify which xform is set as the current one.
 -Allow for adding "layers" via xaos which will add a user-specified number of xforms, and set certain xaos values to 0 or 1.
 -Add a new menu item under the Edit menu to copy the OpenCL iteration kernel source to the clipboard.
 -Show text on the status bar which indicates that an OpenCL kernel compilation is taking place.
 -Show xform name on xform combo box when expanded. Adjust size to fit all names.
 -Draw post affine circles using dashed lines.
 -Prevent QSS dialog from styling its editor, which makes it easier to see text when creating styles which have custom colors for text boxes.

--Bug fixes
 -Fix up some table layouts which seemed to have regressed/decayed over time for reasons unknown.
 -Using undo/redo would create a new flame in the library every time.
 -Solo was not being preserved when using undo/redo.

--Code changes
 -Make the solo flag be a part of the flame data now.
 -Fix some tabification in the OpenCL code for EllipticVariation.
 -Fix tabification in the varState code for OpenCL.
 -Add an event called m_CompileBegun to RendererCL that is called right before an OpenCL compile is begun.
 --This required making RendererCLBase not a pure virtual base class. Member functions just return defaults.
 -Filter key presses on main window to only process the third one. This is due to Qt triggering three events for every key press.
 -The palette preview table was installing an event filter for seemingly no reason. Remove it.
 -Mark certain virtual functions as override in SpinBox and DoubleSpinBox.
This commit is contained in:
Person 2018-07-30 21:39:41 -07:00
parent 0deabd45b8
commit 26c558a2f5
37 changed files with 708 additions and 201 deletions

1
.gitignore vendored
View File

@ -254,3 +254,4 @@ Builds/include/GL
/Builds/MSVC/VS2017/zlib.props
*last.flame
/Source/Fractorium/Fractorium - Copy.ui
/.vs/slnx.sqlite

View File

@ -2,12 +2,19 @@
This is needed to deal with the large tabs in the fusion theme which is the default on Linux, and optional on Windows.
It's not needed for other themes.
You should keep this at the top of whatever custom style you make to ensure the tabs aren't unusually large.*/
QTabBar::tab { height: 3ex; }
QTabBar::tab { height: 4ex; }
/*This is needed to give the labels on the status bar some padding.*/
QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }
/*Specific styles below this line*/
Fractorium
{
qproperty-VariationTreeColorNonZero:black;
qproperty-VariationTreeColorZero:lightgray;
qproperty-VariationTreeBgColorNonZero:darkgray;
qproperty-VariationTreeBgColorZero:rgb(53, 53, 53);
}
QObject
{
@ -307,14 +314,12 @@ QTreeView
{
border: 1px solid gray;
background-color: rgb(53, 53, 53);
font: 9pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
font: 9pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
}
/*Setting this gives a more consistent look, but removes the ability to gray variations that are included in the xform*/
/*Setting this gives a more consistent look. Also, by omitting color and background color, it allows us to set it above with VariationTreeColorNonZero etc...*/
QTreeView::item
{
background-color: rgb(53, 53, 53);
color: darkgray;
outline: none;
margin-right: 1px;
}
@ -365,7 +370,7 @@ QTableView
color: darkgray;
selection-color: darkgray;
selection-background-color: rgb(53, 53, 53);
font: 9pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
font: 9pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
}
QTableView QTableCornerButton::section:enabled
@ -630,3 +635,8 @@ QTableView#FinalRenderParamsTable QPushButton
margin-bottom: 2px;
padding: 0px;
}
QssDialog QssTextEdit#QssEdit
{
background-color: #FFFCE1;
}

View File

@ -2,12 +2,19 @@
This is needed to deal with the large tabs in the fusion theme which is the default on Linux, and optional on Windows.
It's not needed for other themes.
You should keep this at the top of whatever custom style you make to ensure the tabs aren't unusually large.*/
QTabBar::tab { height: 3ex; }
QTabBar::tab { height: 4ex; }
/*This is needed to give the labels on the status bar some padding.*/
QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }
/*Specific styles below this line*/
Fractorium
{
qproperty-VariationTreeColorNonZero:black;
qproperty-VariationTreeColorZero:lightgray;
qproperty-VariationTreeBgColorNonZero:darkgray;
qproperty-VariationTreeBgColorZero:rgb(53, 53, 53);
}
QObject
{
@ -307,14 +314,13 @@ QTreeView
{
border: 1px solid gray;
background-color: rgb(53, 53, 53);
font: 9pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
font: 9pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
}
/*Setting this gives a more consistent look, but removes the ability to gray variations that are included in the xform*/
/*Setting this gives a more consistent look. Also, by omitting color and background color, it allows us to set it above with VariationTreeColorNonZero etc...*/
QTreeView::item
{
background-color: rgb(53, 53, 53);
color: darkgray;
outline: none;
margin-right: 1px;
}
@ -365,7 +371,7 @@ QTableView
color: darkgray;
selection-color: darkgray;
selection-background-color: rgb(53, 53, 53);
font: 9pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
font: 9pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
}
QTableView QTableCornerButton::section:enabled
@ -630,3 +636,8 @@ QTableView#FinalRenderParamsTable QPushButton
margin-bottom: 2px;
padding: 0px;
}
QssDialog QssTextEdit#QssEdit
{
background-color: #FFFCE1;
}

View File

@ -2,12 +2,19 @@
This is needed to deal with the large tabs in the fusion theme which is the default on Linux, and optional on Windows.
It's not needed for other themes.
You should keep this at the top of whatever custom style you make to ensure the tabs aren't unusually large.*/
QTabBar::tab { height: 3ex; }
QTabBar::tab { height: 4ex; }
/*This is needed to give the labels on the status bar some padding.*/
QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }
/*Specific styles below this line*/
Fractorium
{
qproperty-VariationTreeColorNonZero:black;
qproperty-VariationTreeColorZero:lightgray;
qproperty-VariationTreeBgColorNonZero:darkgray;
qproperty-VariationTreeBgColorZero:rgb(53, 53, 53);
}
QObject
{
@ -307,14 +314,12 @@ QTreeView
{
border: 1px solid gray;
background-color: rgb(53, 53, 53);
font: 8pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
font: 8pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
}
/*Setting this gives a more consistent look, but removes the ability to gray variations that are included in the xform*/
/*Setting this gives a more consistent look. Also, by omitting color and background color, it allows us to set it above with VariationTreeColorNonZero etc...*/
QTreeView::item
{
background-color: rgb(53, 53, 53);
color: darkgray;
outline: none;
margin-right: 1px;
}
@ -365,7 +370,7 @@ QTableView
color: darkgray;
selection-color: darkgray;
selection-background-color: rgb(53, 53, 53);
font: 8pt "MS Shell Dlg";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
font: 8pt "MS Shell Dlg 2";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/
}
QTableView QTableCornerButton::section:enabled
@ -630,3 +635,8 @@ QTableView#FinalRenderParamsTable QPushButton
margin-bottom: 2px;
padding: 0px;
}
QssDialog QssTextEdit#QssEdit
{
background-color: #FFFCE1;
}

View File

@ -178,6 +178,7 @@ public:
m_Edits = xmlCopyDoc(ember.m_Edits, 1);
CopyCont(m_EmberMotionElements, ember.m_EmberMotionElements);
m_Solo = ember.m_Solo;
return *this;
}
@ -1723,6 +1724,9 @@ public:
//The list of motion elements for the top-level flame params
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:
/// <summary>
/// The type of scaling used when resizing.

View File

@ -4009,18 +4009,18 @@ public:
string weight = WeightDefineString();
string weightDivPiDiv2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "double x2 = 2.0 * vIn.x;\n"
<< "double u = precalcSumSquares + x2;\n"
<< "double v = precalcSumSquares - x2;\n"
<< "double xmaxm1 = 0.5 * (Sqrt1pm1(u) + Sqrt1pm1(v));\n"
<< "double a = vIn.x / (1 + xmaxm1);\n"
<< "double ssx = xmaxm1 < 0 ? 0.0 : sqrt(xmaxm1);\n"
<< "vOut.x = (" << weightDivPiDiv2 << " * asin(clamp(a, (double)-1.0, (double)1.0)));\n"
<< "\t\tdouble x2 = 2.0 * vIn.x;\n"
<< "\t\tdouble u = precalcSumSquares + x2;\n"
<< "\t\tdouble v = precalcSumSquares - x2;\n"
<< "\t\tdouble xmaxm1 = 0.5 * (Sqrt1pm1(u) + Sqrt1pm1(v));\n"
<< "\t\tdouble a = vIn.x / (1 + xmaxm1);\n"
<< "\t\tdouble ssx = xmaxm1 < 0 ? 0.0 : sqrt(xmaxm1);\n"
<< "\t\tvOut.x = (" << weightDivPiDiv2 << " * asin(clamp(a, (double)-1.0, (double)1.0)));\n"
<< "\n"
<< "if (vIn.y > 0)\n"
<< " vOut.y = " << weightDivPiDiv2 << " * log1p(xmaxm1 + ssx);\n"
<< "else\n"
<< " vOut.y = -(" << weightDivPiDiv2 << " * log1p(xmaxm1 + ssx));\n"
<< "\t\tif (vIn.y > 0)\n"
<< "\t\t\tvOut.y = " << weightDivPiDiv2 << " * log1p(xmaxm1 + ssx);\n"
<< "\t\telse\n"
<< "\t\t\tvOut.y = -(" << weightDivPiDiv2 << " * log1p(xmaxm1 + ssx));\n"
<< "\n"
<< "\t\tvOut.z = " << DefaultZCl()
<< "\t}\n";

View File

@ -3678,9 +3678,9 @@ public:
string prefix = Prefix();
//CPU sets fycle and bcycle to 0 at the beginning in Precalc().
//Set to random in OpenCL since a value can't be set once and kept between kernel launches without writing it back to an OpenCL buffer.
ss << "\n\t\tvarState." << prefix << "hexaplay3D_rswtch" << stateIndex << " = trunc(MwcNext01(&mwc) * 3.0);";
ss << "\n\t\tvarState." << prefix << "hexaplay3D_fcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 5.0);";
ss << "\n\t\tvarState." << prefix << "hexaplay3D_bcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 2.0);";
ss << "\n\tvarState." << prefix << "hexaplay3D_rswtch" << stateIndex << " = trunc(MwcNext01(&mwc) * 3.0);";
ss << "\n\tvarState." << prefix << "hexaplay3D_fcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 5.0);";
ss << "\n\tvarState." << prefix << "hexaplay3D_bcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 2.0);";
return ss.str();
}
@ -4068,9 +4068,9 @@ public:
string prefix = Prefix();
//CPU sets fycle and bcycle to 0 at the beginning in Precalc().
//Set to random in OpenCL since a value can't be set once and kept between kernel launches without writing it back to an OpenCL buffer.
ss << "\n\t\tvarState." << prefix << "hexnix3D_rswtch" << stateIndex << " = trunc(MwcNext01(&mwc) * 3.0);";
ss << "\n\t\tvarState." << prefix << "hexnix3D_fcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 5.0);";
ss << "\n\t\tvarState." << prefix << "hexnix3D_bcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 2.0);";
ss << "\n\tvarState." << prefix << "hexnix3D_rswtch" << stateIndex << " = trunc(MwcNext01(&mwc) * 3.0);";
ss << "\n\tvarState." << prefix << "hexnix3D_fcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 5.0);";
ss << "\n\tvarState." << prefix << "hexnix3D_bcycle" << stateIndex << " = trunc(MwcNext01(&mwc) * 2.0);";
return ss.str();
}

View File

@ -4353,10 +4353,10 @@ public:
ss2 << "_" << XformIndexInEmber();
string stateIndex = ss2.str();
string prefix = Prefix();
ss << "\n\t\tvarState." << prefix << "smartcrop_x" << stateIndex << " = 0;";
ss << "\n\t\tvarState." << prefix << "smartcrop_y" << stateIndex << " = 0;";
ss << "\n\t\tvarState." << prefix << "smartcrop_z" << stateIndex << " = 0;";
ss << "\n\t\tvarState." << prefix << "smartcrop_c" << stateIndex << " = 0;";
ss << "\n\tvarState." << prefix << "smartcrop_x" << stateIndex << " = 0;";
ss << "\n\tvarState." << prefix << "smartcrop_y" << stateIndex << " = 0;";
ss << "\n\tvarState." << prefix << "smartcrop_z" << stateIndex << " = 0;";
ss << "\n\tvarState." << prefix << "smartcrop_c" << stateIndex << " = 0;";
return ss.str();
}

View File

@ -33,6 +33,7 @@ RendererCL<T, bucketT>::RendererCL(const vector<pair<size_t, size_t>>& devices,
m_PaletteFormat.image_channel_data_type = CL_FLOAT;
m_FinalFormat.image_channel_order = CL_RGBA;
m_FinalFormat.image_channel_data_type = CL_FLOAT;
m_CompileBegun = [&]() { };
Init(devices, shared, outputTexID);
}
@ -903,6 +904,7 @@ bool RendererCL<T, bucketT>::BuildIterProgramForEmber(bool doAccum)
if (b)
{
m_CompileBegun();
m_IterKernel = m_IterOpenCLKernelCreator.CreateIterKernelString(m_Ember, m_Params.first, m_GlobalShared.first, m_LockAccum, doAccum);
//cout << "Building: " << "\n" << iterProgram << "\n";
vector<std::thread> threads;

View File

@ -19,9 +19,10 @@ class EMBERCL_API RendererCLBase
{
public:
virtual ~RendererCLBase() { }
virtual bool ReadFinal(v4F* pixels) = 0;
virtual bool ClearFinal() = 0;
virtual bool AnyNvidia() const = 0;
virtual bool ReadFinal(v4F* pixels) { return false; }
virtual bool ClearFinal() { return false; }
virtual bool AnyNvidia() const { return false; }
std::function<void(void)> m_CompileBegun;
};
/// <summary>

View File

@ -230,6 +230,47 @@ bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
return QDoubleSpinBox::eventFilter(o, e);
}
/// <summary>
/// Override which is for handling specific key presses while this control is focused.
/// In particular, + = and up arrow increase the value, equivalent to scrolling the mouse wheel up, while also observing shift/ctrl modifiers.
/// Values are decreased in the same way by pressing - _ or down arrow.
/// </summary>
/// <param name="ke">The key event</param>
void DoubleSpinBox::keyPressEvent(QKeyEvent* ke)
{
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal || ke->key() == Qt::Key_Up)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() + m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() + (ctrl ? m_Step * 10 : m_Step));
}
}
else if (ke->key() == Qt::Key_Minus || ke->key() == Qt::Key_Underscore || ke->key() == Qt::Key_Down)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() - m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() - (ctrl ? m_Step * 10 : m_Step));
}
}
else
QDoubleSpinBox::keyPressEvent(ke);
}
/// <summary>
/// Called when focus enters the spinner.
/// </summary>

View File

@ -38,10 +38,11 @@ public slots:
protected:
virtual bool eventFilter(QObject* o, QEvent* e) override;
virtual void focusInEvent(QFocusEvent* e);
virtual void focusOutEvent(QFocusEvent* e);
virtual void enterEvent(QEvent* e);
virtual void leaveEvent(QEvent* e);
virtual void keyPressEvent(QKeyEvent* event) override;
virtual void focusInEvent(QFocusEvent* e) override;
virtual void focusOutEvent(QFocusEvent* e) override;
virtual void enterEvent(QEvent* e) override;
virtual void leaveEvent(QEvent* e) override;
bool m_DoubleClick;
shared_ptr<FractoriumSettings> m_Settings;

View File

@ -89,10 +89,6 @@ Fractorium::Fractorium(QWidget* p)
pixmap.fill(m_XformComboColors[i]);
m_XformComboIcons[i] = QIcon(pixmap);
}
//Set Default VariationTreeBgColor
m_VariationTreeBgColorNoneZero=QColor(200,200,200);
m_VariationTreeBgColorZero=QColor(255,255,255);
QPixmap pixmap(iconSize_, iconSize_);
pixmap.fill(m_FinalXformComboColor);
@ -348,6 +344,11 @@ void Fractorium::dockLocationChanged(Qt::DockWidgetArea area)
/// <returns>false</returns>
bool Fractorium::eventFilter(QObject* o, QEvent* e)
{
static int fcount = 0;//Qt seems to deliver three events for every key press. So a count must be kept to only respond to the third event.
static int libdelcount = 0;//Note that if anything ever changes under the hood with Qt, this will likely stop working, so adjust as accordingly.
static int xfupcount = 0;
static int xfdncount = 0;
if (o == ui.GLParentScrollArea && e->type() == QEvent::Resize)
{
m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width() * ui.GLDisplay->devicePixelRatioF());
@ -355,24 +356,79 @@ bool Fractorium::eventFilter(QObject* o, QEvent* e)
}
else if (auto ke = dynamic_cast<QKeyEvent*>(e))
{
auto combo = ui.CurrentXformCombo;
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
if (ke->key() >= Qt::Key_F1 && ke->key() <= Qt::Key_F32)
{
int val = ke->key() - (int)Qt::Key_F1;
fcount++;
if (val < ui.CurrentXformCombo->count())
ui.CurrentXformCombo->setCurrentIndex(val);
if (fcount >= 3)
{
int val = ke->key() - (int)Qt::Key_F1;
if (val < combo->count())
combo->setCurrentIndex(val);
fcount = 0;
qDebug() << "global function key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
}
return true;
}
else if (o == ui.LibraryTree)
{
//Require shift for deleting to prevent it from triggering when the user enters delete in the edit box.
if (ke->key() == Qt::Key_Delete && e->type() == QEvent::KeyRelease && shift)
{
auto v = GetCurrentEmberIndex();
libdelcount++;
if (ui.LibraryTree->topLevelItem(0)->childCount() > 1)
OnDelete(v);
if (libdelcount >= 3)
{
auto v = GetCurrentEmberIndex();
if (ui.LibraryTree->topLevelItem(0)->childCount() > 1)
OnDelete(v);
libdelcount = 0;
}
return true;
}
}
else if (o == this)
{
unsigned int index = combo->currentIndex();
if (ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal)
{
xfupcount++;
if (xfupcount >= 3)
{
xfupcount = 0;
combo->setCurrentIndex((index + 1) % combo->count());
qDebug() << "global arrow plus key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
}
return true;
}
else if (ke->key() == Qt::Key_Minus || ke->key() == Qt::Key_Underscore)
{
xfdncount++;
if (xfdncount >= 3)
{
xfdncount = 0;
if (index == 0)
index = combo->count();
combo->setCurrentIndex((index - 1) % combo->count());
qDebug() << "global arrow minus key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
}
return true;
}
}
}
@ -866,6 +922,10 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, ui.WorldPivotRadio);
w = SetTabOrder(this, ui.VariationsFilterLineEdit, ui.VariationsFilterClearButton);//Xforms variation.
w = SetTabOrder(this, w, ui.VariationsTree);
w = SetTabOrder(this, w, ui.ClearXaosButton);
w = SetTabOrder(this, w, ui.RandomXaosButton);
w = SetTabOrder(this, w, ui.AddLayerButton);
w = SetTabOrder(this, w, ui.AddLayerSpinBox);
//Xforms xaos is done dynamically every time.
w = SetTabOrder(this, ui.PaletteFilenameCombo, m_PaletteHueSpin);//Palette.
w = SetTabOrder(this, w, m_PaletteContrastSpin);

View File

@ -63,9 +63,11 @@ template <typename T> class FinalRenderEmberController;
class Fractorium : public QMainWindow
{
Q_OBJECT
Q_PROPERTY(QColor VariationTreeBgColorNoneZero MEMBER m_VariationTreeBgColorNoneZero)
Q_PROPERTY(QColor VariationTreeBgColorZero MEMBER m_VariationTreeBgColorZero)
Q_PROPERTY(QColor VariationTreeColorNonZero MEMBER m_VariationTreeColorNonZero)
Q_PROPERTY(QColor VariationTreeColorZero MEMBER m_VariationTreeColorZero)
Q_PROPERTY(QColor VariationTreeBgColorNonZero MEMBER m_VariationTreeBgColorNonZero)
Q_PROPERTY(QColor VariationTreeBgColorZero MEMBER m_VariationTreeBgColorZero)
friend GLWidget;
friend QssDialog;
friend LibraryTreeWidget;
@ -140,6 +142,7 @@ public slots:
void OnActionPasteXmlOver(bool checked);
void OnActionCopySelectedXforms(bool checked);
void OnActionPasteSelectedXforms(bool checked);
void OnActionCopyKernel(bool checked);
void OnActionResetWorkspace(bool checked);//View
void OnActionAlternateEditorImage(bool checked);
@ -319,6 +322,7 @@ public slots:
void OnXaosChanged(double d);
void OnClearXaosButtonClicked(bool checked);
void OnRandomXaosButtonClicked(bool checked);
void OnAddLayerButtonClicked(bool checked);
void OnXaosRowDoubleClicked(int logicalIndex);
void OnXaosColDoubleClicked(int logicalIndex);
void OnXaosTableModelDataChanged(const QModelIndex& indexA, const QModelIndex& indexB);
@ -561,7 +565,8 @@ private:
char m_CoordinateString[128];
QColor m_XformComboColors[XFORM_COLOR_COUNT], m_FinalXformComboColor;
QIcon m_XformComboIcons[XFORM_COLOR_COUNT], m_FinalXformComboIcon;
QColor m_VariationTreeBgColorNoneZero, m_VariationTreeBgColorZero;
QColor m_VariationTreeColorNonZero, m_VariationTreeColorZero;
QColor m_VariationTreeBgColorNonZero, m_VariationTreeBgColorZero;
vector<QDockWidget*> m_Docks;
int m_FontSize;
@ -572,7 +577,7 @@ private:
shared_ptr<OpenCLInfo> m_Info;
unique_ptr<FractoriumEmberControllerBase> m_Controller;
Ui::FractoriumClass ui;
};

View File

@ -41,6 +41,9 @@
<height>16</height>
</size>
</property>
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<property name="dockOptions">
<set>QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks|QMainWindow::GroupedDragging</set>
</property>
@ -432,7 +435,7 @@
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>15</number>
@ -513,7 +516,7 @@
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>15</number>
@ -575,6 +578,9 @@
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
@ -600,7 +606,7 @@
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>15</number>
@ -697,7 +703,7 @@
<bool>false</bool>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>15</number>
@ -1587,7 +1593,7 @@
<number>1</number>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>15</number>
@ -1891,6 +1897,48 @@
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_4" rowminimumheight="0" columnminimumwidth="0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="AddLayerButton">
<property name="toolTip">
<string>Add N new xforms with all to/from xaos values to each other set to 1, and the remainder set to 0</string>
</property>
<property name="text">
<string>Add Layer</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SpinBox" name="AddLayerSpinBox">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="suffix">
<string> Xforms</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999999</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="XaosTableView">
<property name="sizePolicy">
@ -2805,6 +2853,9 @@
<property name="maxVisibleItems">
<number>12</number>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<property name="iconSize">
<size>
<width>9</width>
@ -3096,7 +3147,7 @@
<number>2</number>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>110</number>
@ -3179,7 +3230,7 @@
<enum>QTabWidget::Triangular</enum>
</property>
<property name="currentIndex">
<number>2</number>
<number>3</number>
</property>
<widget class="QWidget" name="XformColorTab">
<property name="sizePolicy">
@ -6924,6 +6975,8 @@
<addaction name="separator"/>
<addaction name="ActionCopySelectedXforms"/>
<addaction name="ActionPasteSelectedXforms"/>
<addaction name="separator"/>
<addaction name="ActionCopyKernel"/>
</widget>
<widget class="QMenu" name="MenuView">
<property name="title">
@ -8286,6 +8339,17 @@
<string>Reset Scale</string>
</property>
</action>
<action name="ActionCopyKernel">
<property name="text">
<string>Copy &amp;Kernel</string>
</property>
<property name="statusTip">
<string>Copy the OpenCL program to the clipboard for debugging.</string>
</property>
<property name="shortcut">
<string>Ctrl+K</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
@ -8310,6 +8374,11 @@
<extends>QTreeWidget</extends>
<header>LibraryTreeWidget.h</header>
</customwidget>
<customwidget>
<class>SpinBox</class>
<extends>QSpinBox</extends>
<header>spinbox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>LibraryDockWidget</tabstop>

View File

@ -495,12 +495,61 @@ static QString BaseStyle()
"It's not needed for other themes."
"You should keep this at the top of whatever custom style you make to ensure the tabs aren't unusually large.*/\n"
#ifndef _WIN32
"QTabBar::tab { height: 3ex; }\n\n"
"QTabBar::tab { height: 4ex; }\n\n"
#else
"QTabBar::tab { height: 3ex; }\n\n"
"QTabBar::tab { height: 4ex; }\n\n"
#endif
"/*This is needed to give the labels on the status bar some padding.*/\n"
"QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }\n\n"
"QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }\n"
"\n"
"Fractorium\n"
"{\n"
"\tqproperty-VariationTreeColorNonZero:black;\n"
"\tqproperty-VariationTreeColorZero:black;\n"
"\tqproperty-VariationTreeBgColorNonZero:lightgray;\n"
"\tqproperty-VariationTreeBgColorZero:white;\n"
"}\n"
"\n"
"/*For some reason, Qt does not draw table headers correctly, so the style must always be manually specified.*/\n"
"QHeaderView::section::vertical:enabled\n"
"{\n"
"\tcolor: black;\n"
"\tbackground-color: lightgray;\n"
"\tborder: none;\n"
"\tborder-bottom: 1px solid gray;\n"
"\tpadding: 4px;\n"
"\tfont: 8pt \"MS Shell Dlg 2\";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/\n"
"}\n"
"\n"
"QHeaderView::section::horizontal:enabled\n"
"{\n"
"\tcolor: black;\n"
"\tbackground-color: lightgray;\n"
"\tborder: 0px solid darkgray;\n"
"\tborder-right: 1px solid gray;\n"
"\tpadding: 4px;\n"
"\tfont: 8pt \"MS Shell Dlg 2\";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/\n"
"}\n"
"\n"
"QHeaderView::section::vertical:disabled\n"
"{\n"
"\tcolor: rgb(35, 35, 35);\n"
"\tbackground-color: rgb(53, 53, 53);\n"
"\tborder: 0px none darkgray;\n"
"\tborder-bottom: 1px solid rgb(53, 53, 53);\n"
"\tpadding: 4px;\n"
"\tfont: 8pt \"MS Shell Dlg 2\";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/\n"
"}\n"
"\n"
"QHeaderView::section::horizontal:disabled\n"
"{\n"
"\tcolor:rgb(35, 35, 35);\n"
"\tbackground-color: rgb(53, 53, 53);\n"
"\tborder: 0px none darkgray;\n"
"\tborder-right: 0px solid rgb(53, 53, 53);\n"
"\tpadding: 4px;\n"
"\tfont: 8pt \"MS Shell Dlg 2\";/*For some reason the font changes if you set any style. Set this to whatever font is the default on your system*/\n"
"}\n"
;
}

View File

@ -336,8 +336,6 @@ void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool v
if (updatePointer && (typeid(T) == typeid(U)))
m_EmberFilePointer = (Ember<T>*)&ember;
else
m_EmberFilePointer = nullptr;
if (!verbatim)
{

View File

@ -109,6 +109,7 @@ public:
virtual void PasteXmlOver() { }
virtual void CopySelectedXforms() { }
virtual void PasteSelectedXforms() { }
virtual void CopyKernel() { }
virtual void AddReflectiveSymmetry() { }//Tools.
virtual void AddRotationalSymmetry() { }
virtual void AddBothSymmetry() { }
@ -184,7 +185,7 @@ public:
virtual void XformNameChanged(int row, int col) { }
virtual void XformAnimateChanged(int state) { }
virtual void FillXforms(int index = 0) { }
virtual void UpdateXformName(int index) { }
virtual void UpdateXformName(int index) { }
//Xforms Affine.
virtual void AffineSetHelper(double d, int index, bool pre) { }
@ -210,6 +211,7 @@ public:
virtual void XformColorSpeedChanged(double d) { }
virtual void XformOpacityChanged(double d) { }
virtual void XformDirectColorChanged(double d) { }
virtual void SoloXformCheckBoxStateChanged(int state, int index) { }
virtual QColor ColorIndexToQColor(double d) { return QColor(); }
//Xforms Variations.
@ -218,6 +220,7 @@ public:
virtual void ClearVariationsTree() { }
virtual void VariationSpinBoxValueChanged(double d) { }
virtual void FilteredVariations() { }
virtual void FillVariationTreeWithCurrentXform() { }
//Xforms Selection.
@ -226,6 +229,7 @@ public:
virtual void XaosChanged(int x, int y, double val) { }
virtual void ClearXaos() { }
virtual void RandomXaos() { }
virtual void AddLayer(int xforms) { }
//Palette.
virtual size_t InitPaletteList(const QString& s) { return 0; }
@ -375,6 +379,7 @@ public:
virtual void PasteXmlOver() override;
virtual void CopySelectedXforms() override;
virtual void PasteSelectedXforms() override;
virtual void CopyKernel() override;
virtual void AddReflectiveSymmetry() override;
virtual void AddRotationalSymmetry() override;
virtual void AddBothSymmetry() override;
@ -451,7 +456,7 @@ public:
virtual void XformNameChanged(int row, int col) override;
virtual void XformAnimateChanged(int state) override;
virtual void FillXforms(int index = 0) override;
virtual void UpdateXformName(int index) override;
virtual void UpdateXformName(int index) override;
void FillWithXform(Xform<T>* xform);
Xform<T>* CurrentXform();
void UpdateXform(std::function<void(Xform<T>*, size_t, size_t)> func, eXformUpdate updateType = eXformUpdate::UPDATE_CURRENT, bool updateRender = true, eProcessAction action = eProcessAction::FULL_RENDER, size_t index = 0);
@ -480,6 +485,7 @@ public:
virtual void XformColorSpeedChanged(double d) override;
virtual void XformOpacityChanged(double d) override;
virtual void XformDirectColorChanged(double d) override;
virtual void SoloXformCheckBoxStateChanged(int state, int index) override;
virtual QColor ColorIndexToQColor(double d) override;
void FillColorWithXform(Xform<T>* xform);
@ -489,6 +495,7 @@ public:
virtual void ClearVariationsTree() override;
virtual void VariationSpinBoxValueChanged(double d) override;
virtual void FilteredVariations() override;
virtual void FillVariationTreeWithCurrentXform() override;
void FillVariationTreeWithXform(Xform<T>* xform);
//Xforms Xaos.
@ -496,6 +503,7 @@ public:
virtual void XaosChanged(int x, int y, double val) override;
virtual void ClearXaos() override;
virtual void RandomXaos() override;
virtual void AddLayer(int xforms) override;
//Xforms Selection.
bool XformCheckboxAt(int i, std::function<void(QCheckBox*)> func);

View File

@ -25,6 +25,7 @@ void Fractorium::InitMenusUI()
connect(ui.ActionPasteXmlOver, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlOver(bool)), Qt::QueuedConnection);
connect(ui.ActionCopySelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionCopySelectedXforms(bool)), Qt::QueuedConnection);
connect(ui.ActionPasteSelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteSelectedXforms(bool)), Qt::QueuedConnection);
connect(ui.ActionCopyKernel, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyKernel(bool)), Qt::QueuedConnection);
ui.ActionPasteSelectedXforms->setEnabled(false);
//View menu.
connect(ui.ActionResetWorkspace, SIGNAL(triggered(bool)), this, SLOT(OnActionResetWorkspace(bool)), Qt::QueuedConnection);
@ -471,10 +472,10 @@ void FractoriumEmberController<T>::Undo()
int index = m_Ember.GetTotalXformIndex(current, forceFinal);
m_LastEditWasUndoRedo = true;
m_UndoIndex = std::max<size_t>(0u, m_UndoIndex - 1u);
SetEmber(m_UndoList[m_UndoIndex], true, false);//Don't update pointer because it's coming from the undo list...
SetEmber(m_UndoList[m_UndoIndex], true, false);//Don't update pointer because it's coming from the undo list.
m_EditState = eEditUndoState::UNDO_REDO;
if (index >= 0)
if (index >= 0 && index < m_Fractorium->ui.CurrentXformCombo->count())
m_Fractorium->CurrentXform(index);
m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0));
@ -497,10 +498,10 @@ void FractoriumEmberController<T>::Redo()
int index = m_Ember.GetTotalXformIndex(current, forceFinal);
m_LastEditWasUndoRedo = true;
m_UndoIndex = std::min<size_t>(m_UndoIndex + 1, m_UndoList.size() - 1);
SetEmber(m_UndoList[m_UndoIndex], true, false);
SetEmber(m_UndoList[m_UndoIndex], true, false);//Don't update pointer because it's coming from the undo list.
m_EditState = eEditUndoState::UNDO_REDO;
if (index >= 0)
if (index >= 0 && index < m_Fractorium->ui.CurrentXformCombo->count())
m_Fractorium->CurrentXform(index);
m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0));
@ -713,6 +714,19 @@ void Fractorium::OnActionPasteSelectedXforms(bool checked)
m_Controller->PasteSelectedXforms();
}
/// <summary>
/// Copy the text of the OpenCL iteration kernel to the clipboard.
/// This performs no action if the renderer is not of type RendererCL.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::CopyKernel()
{
if (auto rendererCL = dynamic_cast<RendererCL<T, float>*>(m_Renderer.get()))
QApplication::clipboard()->setText(QString::fromStdString(rendererCL->IterKernel()));
}
void Fractorium::OnActionCopyKernel(bool checked) { m_Controller->CopyKernel(); }
/// <summary>
/// Reset dock widgets and tabs to their default position.
/// Note that there is a bug in Qt, where it will only move them all to the left side if at least
@ -837,7 +851,7 @@ void FractoriumEmberController<T>::Flatten()
{
ember.Flatten(XmlToEmber<T>::m_FlattenNames);
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
FillVariationTreeWithXform(CurrentXform());
FillVariationTreeWithCurrentXform();
}
void Fractorium::OnActionFlatten(bool checked) { m_Controller->Flatten(); }
@ -852,7 +866,7 @@ void FractoriumEmberController<T>::Unflatten()
{
ember.Unflatten();
}, true, eProcessAction::FULL_RENDER, m_Fractorium->ApplyAll());
FillVariationTreeWithXform(CurrentXform());
FillVariationTreeWithCurrentXform();
}
void Fractorium::OnActionUnflatten(bool checked) { m_Controller->Unflatten(); }

View File

@ -38,7 +38,6 @@ void Fractorium::InitPaletteUI()
palettePreviewTable->setItem(0, 0, previewNameCol);
auto previewPaletteItem = new QTableWidgetItem();
palettePreviewTable->setItem(0, 1, previewPaletteItem);
palettePreviewTable->installEventFilter(this);
connect(ui.PaletteFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnPaletteFilterLineEditTextChanged(const QString&)));
connect(ui.PaletteFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteFilterClearButtonClicked(bool)));
paletteTable->setColumnWidth(1, 260);//256 plus small margin on each side.
@ -392,6 +391,7 @@ void FractoriumEmberController<T>::PaletteEditorButtonClicked()
ed->SetColorIndices(colorIndices);
ed->SetPaletteFile(m_CurrentPaletteFilePath);
//ed->setpal
if (ed->exec() == QDialog::Accepted)
{
//Copy all just to be safe, because they may or may not have synced.

View File

@ -346,7 +346,7 @@ bool FractoriumEmberController<T>::Render()
if (action != eProcessAction::NOTHING)
{
size_t i = 0;
int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt();
int solo = m_Ember.m_Solo;
bool forceFinal = m_Fractorium->HaveFinal();
if (solo != -1)
@ -667,9 +667,11 @@ bool Fractorium::CreateRendererFromOptions(bool updatePreviews)
bool ok = true;
bool useOpenCL = m_Info->Ok() && m_Settings->OpenCL();
auto v = Devices(m_Settings->Devices());
bool doOpenCL = useOpenCL && !v.empty();
ui.ActionCopyKernel->setEnabled(doOpenCL);
//The most important option to process is what kind of renderer is desired, so do it first.
if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v, updatePreviews, useOpenCL && m_Settings->SharedTexture()))
if (!m_Controller->CreateRenderer(doOpenCL ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v, updatePreviews, doOpenCL && m_Settings->SharedTexture()))
{
//If using OpenCL, will only get here if creating RendererCL failed, but creating a backup CPU Renderer succeeded.
ShowCritical("Renderer Creation Error", "Error creating renderer, most likely a GPU problem. Using CPU instead.");
@ -677,12 +679,21 @@ bool Fractorium::CreateRendererFromOptions(bool updatePreviews)
m_Settings->SharedTexture(false);
ui.ActionCpu->setChecked(true);
ui.ActionCL->setChecked(false);
ui.ActionCopyKernel->setEnabled(false);
m_OptionsDialog->ui.OpenCLCheckBox->setChecked(false);
m_OptionsDialog->ui.SharedTextureCheckBox->setChecked(false);
m_FinalRenderDialog->ui.FinalRenderOpenCLCheckBox->setChecked(false);
ok = false;
}
if (auto rendererCL = dynamic_cast<RendererCLBase*>(m_Controller->Renderer()))
rendererCL->m_CompileBegun = [&]()
{
m_RenderStatusLabel->setText("Compiling OpenCL kernel...");
m_RenderStatusLabel->repaint();
QApplication::processEvents();
};
return ok;
}

View File

@ -22,6 +22,7 @@ void Fractorium::InitXaosUI()
connect(m_XaosSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnXaosChanged(double)), Qt::QueuedConnection);
connect(ui.ClearXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnClearXaosButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.RandomXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomXaosButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.AddLayerButton, SIGNAL(clicked(bool)), this, SLOT(OnAddLayerButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.XaosTableView->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnXaosRowDoubleClicked(int)), Qt::QueuedConnection);
connect(ui.XaosTableView->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnXaosColDoubleClicked(int)), Qt::QueuedConnection);
}
@ -165,6 +166,49 @@ void FractoriumEmberController<T>::RandomXaos()
void Fractorium::OnRandomXaosButtonClicked(bool checked) { m_Controller->RandomXaos(); }
/// <summary>
/// Add a layer using the specified number of xforms.
/// A layer is defined as a new set of xforms whose xaos values are the following:
/// From existing to existing: unchanged.
/// From existing to new: 0.
/// From new to existing: 0.
/// From new to new: 1.
/// </summary>
/// <param name="xforms">The number of new xforms to add to create the layer</param>
template <typename T>
void FractoriumEmberController<T>::AddLayer(int xforms)
{
Update([&]
{
auto origXformCount = m_Ember.XformCount();
m_Ember.AddXforms(xforms);
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();
FillSummary();
}
void Fractorium::OnAddLayerButtonClicked(bool checked) { m_Controller->AddLayer(ui.AddLayerSpinBox->value()); }
/// <summary>
/// Toggle all xaos values in one row.
/// Resets the rendering process.

View File

@ -15,25 +15,28 @@ void Fractorium::InitXformsUI()
connect(ui.AddFinalXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddFinalXformButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.CurrentXformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentXformComboChanged(int)), Qt::QueuedConnection);
connect(ui.AnimateXformCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnXformAnimateCheckBoxStateChanged(int)), Qt::QueuedConnection);
SetFixedTableHeader(ui.XformWeightNameTable->horizontalHeader(),QHeaderView::ResizeToContents);
SetFixedTableHeader(ui.XformWeightNameTable->horizontalHeader(), QHeaderView::ResizeToContents);
//Use SetupSpinner() just to create the spinner, but use col of -1 to prevent it from being added to the table.
SetupSpinner<DoubleSpinBox, double>(ui.XformWeightNameTable, this, row, -1, m_XformWeightSpin, spinHeight, 0, 1000, 0.05, SIGNAL(valueChanged(double)), SLOT(OnXformWeightChanged(double)), false, 0, 1, 0);
m_XformWeightSpin->setDecimals(3);
m_XformWeightSpin->SmallStep(0.001);
m_XformWeightSpin->setMinimumWidth(40);
m_XformWeightSpin->setMinimumWidth(40);
m_XformWeightSpinnerButtonWidget = new SpinnerLabelButtonWidget(m_XformWeightSpin, "=", 20, 19, ui.XformWeightNameTable);
m_XformWeightSpinnerButtonWidget->m_SpinBox->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);
m_XformWeightSpinnerButtonWidget->m_Label->setStyleSheet("border: 0px;");
m_XformWeightSpinnerButtonWidget->m_Label->setAlignment(Qt::AlignRight| Qt::AlignVCenter);
m_XformWeightSpinnerButtonWidget->m_Label->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
m_XformWeightSpinnerButtonWidget->m_Button->setToolTip("Equalize weights");
m_XformWeightSpinnerButtonWidget->m_SpinBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_XformWeightSpinnerButtonWidget->m_Label->setStyleSheet("border: 0px;");
m_XformWeightSpinnerButtonWidget->m_Label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_XformWeightSpinnerButtonWidget->m_Label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_XformWeightSpinnerButtonWidget->m_Button->setToolTip("Equalize weights");
m_XformWeightSpinnerButtonWidget->m_Button->setStyleSheet("text-align: center center");
m_XformWeightSpinnerButtonWidget->setMaximumWidth(130);
m_XformWeightSpinnerButtonWidget->setMaximumWidth(130);
connect(m_XformWeightSpinnerButtonWidget->m_Button, SIGNAL(clicked(bool)), this, SLOT(OnEqualWeightButtonClicked(bool)), Qt::QueuedConnection);
ui.XformWeightNameTable->setCellWidget(0, 0, m_XformWeightSpinnerButtonWidget);
ui.XformWeightNameTable->setItem(0, 1, new QTableWidgetItem());
connect(ui.XformWeightNameTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnXformNameChanged(int, int)), Qt::QueuedConnection);
ui.CurrentXformCombo->setProperty("soloxform", -1);
ui.CurrentXformCombo->view()->setMinimumWidth(100);
ui.CurrentXformCombo->view()->setMaximumWidth(500);
//ui.CurrentXformCombo->view()->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
ui.CurrentXformCombo->view()->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow);
#ifndef _WIN32
//For some reason linux makes these 24x24, even though the designer explicitly says 16x16.
ui.AddXformButton->setIconSize(QSize(16, 16));
@ -63,7 +66,7 @@ Xform<T>* FractoriumEmberController<T>::CurrentXform()
void Fractorium::CurrentXform(uint i)
{
if (i < uint(ui.CurrentXformCombo->count()))
ui.CurrentXformCombo->setCurrentIndex(i);
ui.CurrentXformCombo->setCurrentIndex(i);
}
/// <summary>
/// Set the current xform and populate all GUI widgets.
@ -79,7 +82,7 @@ void FractoriumEmberController<T>::CurrentXformComboChanged(int index)
{
FillWithXform(xform);
m_GLController->SetSelectedXform(xform);
int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt();
int solo = m_Ember.m_Solo;
m_Fractorium->ui.SoloXformCheckBox->blockSignals(true);
m_Fractorium->ui.SoloXformCheckBox->setChecked(solo == index);
m_Fractorium->ui.SoloXformCheckBox->blockSignals(false);
@ -244,7 +247,7 @@ void FractoriumEmberController<T>::ClearXform()
{
xform->ClearAndDeleteVariations();//Note xaos is left alone.
}, eXformUpdate::UPDATE_SELECTED);
FillVariationTreeWithXform(CurrentXform());
FillVariationTreeWithCurrentXform();
}
void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearXform(); }
@ -393,11 +396,13 @@ void FractoriumEmberController<T>::XformNameChanged(int row, int col)
}, eXformUpdate::UPDATE_CURRENT, false);
FillSummary();//Manually update because this does not trigger a render, which is where this would normally be called.
}
void Fractorium::OnXformNameChanged(int row, int col)
{
m_Controller->XformNameChanged(row, col);
m_Controller->UpdateXformName(ui.CurrentXformCombo->currentIndex());
m_Controller->XformNameChanged(row, col);
m_Controller->UpdateXformName(ui.CurrentXformCombo->currentIndex());
}
/// <summary>
/// Set the animate field of the selected xforms, this allows excluding current if it's not checked, but applies only to it if none are checked.
/// This has no effect on interactive rendering, it only sets a value
@ -480,9 +485,8 @@ void FractoriumEmberController<T>::SetNormalizedWeightText(Xform<T>* xform)
m_Ember.CalcNormalizedWeights(m_NormalizedWeights);
if (index != -1 && index < m_NormalizedWeights.size())
//m_Fractorium->m_XformWeightSpin->setSuffix(QString(" (") + QLocale::system().toString(double(m_NormalizedWeights[index]), 'g', 3) + ")");
m_Fractorium->m_XformWeightSpinnerButtonWidget->m_Label->setText(QString(" (") + QLocale::system().toString(double(m_NormalizedWeights[index]), 'g', 3) + ")");
}
m_Fractorium->m_XformWeightSpinnerButtonWidget->m_Label->setText(QString(" (") + QLocale::system().toString(double(m_NormalizedWeights[index]), 'g', 3) + ")");
}
}
/// <summary>
/// Determine whether the specified xform is the final xform in the ember.
@ -517,7 +521,7 @@ void FractoriumEmberController<T>::FillXforms(int index)
{
combo->addItem(ToString(i + 1));
combo->setItemIcon(i, m_Fractorium->m_XformComboIcons[i % XFORM_COLOR_COUNT]);
UpdateXformName(i);
UpdateXformName(i);
}
i = 0;
@ -553,7 +557,7 @@ void FractoriumEmberController<T>::FillXforms(int index)
m_Fractorium->m_XformsSelectionLayout->addRow(cb, new QWidget(m_Fractorium));
combo->addItem("Final");
combo->setItemIcon(i, m_Fractorium->m_FinalXformComboIcon);
UpdateXformName(i);
UpdateXformName(i);
}
m_Fractorium->m_XformsSelectionLayout->blockSignals(false);
@ -562,31 +566,50 @@ void FractoriumEmberController<T>::FillXforms(int index)
if (index < combo->count())
combo->setCurrentIndex(index);
m_Fractorium->ui.SoloXformCheckBox->blockSignals(true);
if (m_Ember.m_Solo == combo->currentIndex())
m_Fractorium->ui.SoloXformCheckBox->setChecked(true);
else
m_Fractorium->ui.SoloXformCheckBox->setChecked(false);
SoloXformCheckBoxStateChanged(m_Ember.m_Solo > -1 ? Qt::Checked : Qt::Unchecked, m_Ember.m_Solo);
m_Fractorium->ui.SoloXformCheckBox->blockSignals(false);
m_Fractorium->FillXaosTable();
m_Fractorium->OnSoloXformCheckBoxStateChanged(Qt::Unchecked);
m_Fractorium->OnCurrentXformComboChanged(index);//Make sure the event gets called, because it won't if the zero index is already selected.
m_Fractorium->OnCurrentXformComboChanged(index);//Make sure the event gets called, because it won't if the zero index is already selected.
}
/// <summary>
/// Update the text in xforms combo box to show the name of Xform.
/// </summary>
/// <param name="index">The index of the Xform to update.</param>
///
///
template<typename T>
void FractoriumEmberController<T>::UpdateXformName(int index)
{
bool forceFinal = m_Fractorium->HaveFinal();
bool isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(index, forceFinal);
QString name = isFinal ? "Final" : QString::number(index + 1);
if (auto xform = m_Ember.GetTotalXform(index, forceFinal))
{
if (!xform->m_Name.empty())
{
name += " " + QString::fromStdString(xform->m_Name);
}
m_Fractorium->ui.CurrentXformCombo->setItemText(index,name);
}
bool forceFinal = m_Fractorium->HaveFinal();
bool isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(index, forceFinal);
QString name = isFinal ? "Final" : QString::number(index + 1);
if (auto xform = m_Ember.GetTotalXform(index, forceFinal))
{
if (!xform->m_Name.empty())
name += " " + QString::fromStdString(xform->m_Name);
m_Fractorium->ui.CurrentXformCombo->setItemText(index, name);
auto view = m_Fractorium->ui.CurrentXformCombo->view();
auto fontMetrics1 = view->fontMetrics();
auto textWidth = m_Fractorium->ui.CurrentXformCombo->width();
auto ww = fontMetrics1.width("WW");
for (int i = 0; i < m_Fractorium->ui.CurrentXformCombo->count(); ++i)
textWidth = std::max(fontMetrics1.width(m_Fractorium->ui.CurrentXformCombo->itemText(i)) + ww, textWidth);
view->setMinimumWidth(textWidth);
view->setMaximumWidth(textWidth);
}
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;

View File

@ -181,28 +181,32 @@ void Fractorium::OnXformDirectColorChanged(double d) { m_Controller->XformDirect
/// If checked, current is solo, if unchecked, none are solo.
/// Solo means that all other xforms will have their opacity temporarily
/// set to zero while rendering so that only the effect of current xform is visible.
/// This will not permanently alter the ember, as the temporary opacity values will be applied
/// This will not permanently alter the opacities of ember, as the temporary opacity values will be applied
/// right before rendering and reset right after.
/// Called when solo xform check box is checked.
/// Resets the rendering process.
/// </summary>
/// <param name="state">The state of the checkbox</param>
void Fractorium::OnSoloXformCheckBoxStateChanged(int state)
/// <param name="index">The index which has been specified as the solo xform, -1 to specify none.</param>
template <typename T>
void FractoriumEmberController<T>::SoloXformCheckBoxStateChanged(int state, int index)
{
if (state == Qt::Checked)
{
ui.CurrentXformCombo->setProperty("soloxform", ui.CurrentXformCombo->currentIndex());
ui.SoloXformCheckBox->setText("Solo (" + ToString(ui.CurrentXformCombo->currentIndex() + 1) + ")");
m_Ember.m_Solo = index;
m_Fractorium->ui.SoloXformCheckBox->setText("Solo (" + ToString(index + 1) + ")");
}
else if (state == Qt::Unchecked)
{
ui.CurrentXformCombo->setProperty("soloxform", -1);
ui.SoloXformCheckBox->setText("Solo");
m_Ember.m_Solo = -1;
m_Fractorium->ui.SoloXformCheckBox->setText("Solo");
}
m_Controller->UpdateRender();
UpdateRender();
}
void Fractorium::OnSoloXformCheckBoxStateChanged(int state) { m_Controller->SoloXformCheckBoxStateChanged(state, ui.CurrentXformCombo->currentIndex()); }
/// <summary>
/// Redraw the palette ref table.
/// Called on resize.

View File

@ -16,6 +16,11 @@ void Fractorium::InitXformsVariationsUI()
//Setting dimensions in the designer with a layout is futile, so must hard code here.
tree->setColumnWidth(0, 160);
tree->setColumnWidth(1, 23);
//Set Default variation tree text and background colors for zero and non zero cases.
m_VariationTreeColorNonZero = Qt::black;
m_VariationTreeColorZero = Qt::black;
m_VariationTreeBgColorNonZero = Qt::lightGray;
m_VariationTreeBgColorZero = Qt::white;
}
/// <summary>
@ -226,8 +231,9 @@ void FractoriumEmberController<T>::VariationSpinBoxValueChanged(double d)//Would
if (xformVar)
xform->DeleteVariationById(var->VariationId());
// widgetItem->setBackgroundColor(0, QColor(255, 255, 255));//Ensure background is always white if weight goes to zero.
widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorZero); }
widgetItem->setTextColor(0, m_Fractorium->m_VariationTreeColorZero);
widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorZero);
}
else
{
if (xformVar)//The xform already contained this variation, which means they just went from a non-zero weight to another non-zero weight (the simple case).
@ -241,8 +247,9 @@ void FractoriumEmberController<T>::VariationSpinBoxValueChanged(double d)//Would
auto newVar = var->Copy();//Create a new one with default values.
newVar->m_Weight = d;
xform->AddVariation(newVar);
// widgetItem->setBackgroundColor(0, QColor(200, 200, 200));//Set background to gray when a variation has non-zero weight in this xform.
widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorNoneZero);
widgetItem->setTextColor(0, m_Fractorium->m_VariationTreeColorNonZero);
widgetItem->setBackgroundColor(0, m_Fractorium->m_VariationTreeBgColorNonZero);
//If they've added a new parametric variation, then grab the values currently in the spinners
//for the child parameters and assign them to the newly added variation.
if (parVar)
@ -275,6 +282,15 @@ void FractoriumEmberController<T>::VariationSpinBoxValueChanged(double d)//Would
void Fractorium::OnVariationSpinBoxValueChanged(double d) { m_Controller->VariationSpinBoxValueChanged(d); }
/// <summary>
/// Fill in the variations tree with the values from the current xform.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillVariationTreeWithCurrentXform()
{
FillVariationTreeWithXform(CurrentXform());
}
/// <summary>
/// Fill the variation tree values from passed in xform and apply the current sorting mode.
/// Called when the currently selected xform changes.
@ -300,9 +316,9 @@ void FractoriumEmberController<T>::FillVariationTreeWithXform(Xform<T>* xform)
item->setHidden(false);
spinBox->SetValueStealth(var ? var->m_Weight : 0);//If the variation was present, set the spin box to its weight, else zero.
// item->setBackgroundColor(0, var ? Qt::darkGray : Qt::lightGray);//Ensure background is always white if the value goes to zero, else gray if var present.
// item->setBackgroundColor(0, var ? QColor(200, 200, 200) : QColor(255, 255, 255));//Ensure background is always white if the value goes to zero, else gray if var present.
item->setBackgroundColor(0, var ? m_Fractorium->m_VariationTreeBgColorNoneZero : m_Fractorium->m_VariationTreeBgColorZero);
item->setTextColor(0, var ? m_Fractorium->m_VariationTreeColorNonZero : m_Fractorium->m_VariationTreeColorZero);
item->setBackgroundColor(0, var ? m_Fractorium->m_VariationTreeBgColorNonZero : m_Fractorium->m_VariationTreeBgColorZero);
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params if it was a parametric variation.
{
T* param = nullptr;

View File

@ -685,7 +685,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
GLfloat(m_DragHandlePos.x), GLfloat(m_DragHandlePos.y)
};
QVector4D col(1.0f, 1.0f, 0.5f, 1.0f);
m_GL->DrawPointOrLine(col, vertices, 1, GL_POINTS, 6.0f);
m_GL->DrawPointOrLine(col, vertices, 1, GL_POINTS, false, 6.0f);
#endif
}
else if (m_DragState == eDragState::DragSelect)
@ -733,7 +733,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
GLfloat(m_HoverHandlePos.x), GLfloat(m_HoverHandlePos.y)
};
QVector4D col(0.5f, 1.0f, 1.0f, 1.0f);
m_GL->DrawPointOrLine(col, vertices, 1, GL_POINTS, 6.0f);
m_GL->DrawPointOrLine(col, vertices, 1, GL_POINTS, false, 6.0f);
#endif
}
}
@ -1091,10 +1091,11 @@ void GLWidget::wheelEvent(QWheelEvent* e)
/// <param name="col">The color to draw with</param>
/// <param name="vertices">The vertices to use</param>
/// <param name="drawType">The type of primitive to draw, such as GL_POINT or GL_LINES</param>
/// <param name="dashed">True to draw dashed lines, else solid</param>
/// <param name="pointSize">The size in pixels of points, which is internally scaled by the device pixel ratio.</param>
void GLWidget::DrawPointOrLine(const QVector4D& col, const std::vector<float>& vertices, int drawType, GLfloat pointSize)
void GLWidget::DrawPointOrLine(const QVector4D& col, const std::vector<float>& vertices, int drawType, bool dashed, GLfloat pointSize)
{
DrawPointOrLine(col, vertices.data(), int(vertices.size() / 2), drawType, pointSize);
DrawPointOrLine(col, vertices.data(), int(vertices.size() / 2), drawType, dashed, pointSize);
}
/// <summary>
@ -1104,10 +1105,18 @@ void GLWidget::DrawPointOrLine(const QVector4D& col, const std::vector<float>& v
/// <param name="vertices">The vertices to use</param>
/// <param name="size">The number of verticies. This is usually the size of vertices / 2.</param>
/// <param name="drawType">The type of primitive to draw, such as GL_POINT or GL_LINES</param>
/// <param name="dashed">True to draw dashed lines, else solid</param>
/// <param name="pointSize">The size in pixels of points, which is internally scaled by the device pixel ratio.</param>
void GLWidget::DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, GLfloat pointSize)
void GLWidget::DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, bool dashed, GLfloat pointSize)
{
#ifdef USE_GLSL
if (dashed && drawType == GL_LINES)
{
glLineStipple(1, 0XFF00);
glEnable(GL_LINE_STIPPLE);
}
m_ModelViewProjectionMatrix = m_ProjMatrix * m_ModelViewMatrix;
m_Program->setUniformValue(m_ColAttr, col);
m_Program->setUniformValue(m_PointSizeUniform, pointSize * GLfloat(devicePixelRatioF()));
@ -1116,6 +1125,10 @@ void GLWidget::DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, in
this->glEnableVertexAttribArray(0);
this->glDrawArrays(drawType, 0, size);
this->glDisableVertexAttribArray(0);
if (dashed && drawType == GL_LINES)
glDisable(GL_LINE_STIPPLE);
#endif
}
@ -1509,7 +1522,7 @@ void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
m_Verts.push_back(0.0f);
m_Verts.push_back(0.0f);
m_Verts.push_back(1.0f);
m_GL->DrawPointOrLine(col, m_Verts, GL_POINTS, 5.0f);//Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline.
m_GL->DrawPointOrLine(col, m_Verts, GL_POINTS, !pre, 5.0f);//Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline.
//
m_GL->glLineWidth(2.0f * m_GL->devicePixelRatioF());//Draw lines again for y axis only, without drawing the circle, using the color of the selected xform.
m_Verts.clear();
@ -1518,7 +1531,7 @@ void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
m_Verts.push_back(0.0f);
m_Verts.push_back(1.0f);
col = QVector4D(color.r, color.g, color.b, 1.0f);
m_GL->DrawPointOrLine(col, m_Verts, GL_LINES);
m_GL->DrawPointOrLine(col, m_Verts, GL_LINES, !pre);
//
m_Verts.clear();
m_Verts.push_back(0.0f);
@ -1528,7 +1541,7 @@ void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
m_Verts.push_back(0.0f);
m_Verts.push_back(1.0f);
col = QVector4D(1.0f, 1.0f, 1.0f, selected ? 1.0f : 0.5f);
m_GL->DrawPointOrLine(col, m_Verts, GL_POINTS, 3.0f);//Draw smaller white points, to give a black outline effect.
m_GL->DrawPointOrLine(col, m_Verts, GL_POINTS, false, 3.0f);//Draw smaller white points, to give a black outline effect.
m_GL->m_ModelViewMatrix.setToIdentity();
#endif
}
@ -1628,7 +1641,7 @@ void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final,
}
}
DrawPointOrLine(color, m_Verts, GL_LINES);
DrawPointOrLine(color, m_Verts, GL_LINES, !pre);
//Lines from center to circle.
if (!background)
@ -1649,7 +1662,7 @@ void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final,
m_Verts.push_back(0);
m_Verts.push_back(1);
m_Verts.push_back(0);
DrawPointOrLine(color, m_Verts, GL_LINES);
DrawPointOrLine(color, m_Verts, GL_LINES, !pre);
if (background)
color = QVector4D(0.0f, 0.0f, 0.0f, 1.0f);
@ -1659,7 +1672,7 @@ void GLWidget::DrawAffineHelper(int index, bool selected, bool pre, bool final,
m_Verts.push_back(0);
m_Verts.push_back(0);
m_Verts.push_back(1);
DrawPointOrLine(color, m_Verts, GL_LINES);
DrawPointOrLine(color, m_Verts, GL_LINES, !pre);
#endif
}

View File

@ -66,8 +66,8 @@ protected:
virtual void mouseMoveEvent(QMouseEvent* e) override;
virtual void wheelEvent(QWheelEvent* e) override;
void DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, GLfloat pointSize = 1.0f);
void DrawPointOrLine(const QVector4D& col, const std::vector<float>& vertices, int drawType, GLfloat pointSize = 1.0f);
void DrawPointOrLine(const QVector4D& col, const GLfloat* vertices, int size, int drawType, bool dashed = false, GLfloat pointSize = 1.0f);
void DrawPointOrLine(const QVector4D& col, const std::vector<float>& vertices, int drawType, bool dashed = false, GLfloat pointSize = 1.0f);
private:
void SetDimensions(int w, int h);

View File

@ -70,11 +70,11 @@
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<kerning>true</kerning>
</font>
</property>
<property name="font">
<font>
<kerning>true</kerning>
</font>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
@ -213,17 +213,17 @@
</property>
<item>
<layout class="QGridLayout" name="AdjustmentLeftGridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="SyncCheckBox">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<item row="1" column="0">
<widget class="QCheckBox" name="AutoDistributeCheckBox">
<property name="text">
<string>Sync</string>
<string>Auto Distribute</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QSpinBox" name="ArrowsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -251,10 +251,20 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="AutoDistributeCheckBox">
<item row="0" column="0">
<widget class="QCheckBox" name="SyncCheckBox">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Auto Distribute</string>
<string>Sync</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="BlendCheckBox">
<property name="text">
<string>Blend</string>
</property>
<property name="checked">
<bool>true</bool>

View File

@ -36,6 +36,24 @@ GradientColorsView::GradientColorsView(QWidget* p)
ResetToDefault();
}
/// <summary>
/// Get whether to interpolate color keys.
/// </summary>
/// <returns>True to interpolate (blend), false to do hard cuts.</returns>
bool GradientColorsView::Blend()
{
return m_Blend;
}
/// <summary>
/// Set whether to interpolate color keys.
/// </summary>
/// <param name="blend">rue to interpolate (blend), false to do hard cuts.</param>
void GradientColorsView::Blend(bool blend)
{
m_Blend = blend;
}
/// <summary>
/// Set the focus to the arrow at the given normalized position.
/// </summary>
@ -374,23 +392,37 @@ Palette<float>& GradientColorsView::GetPalette(int size)
{
if (!m_Arrows.empty())
{
QPainter p;
QSize imageSize(size, 1);
QImage image(imageSize, QImage::Format_ARGB32_Premultiplied);
QLinearGradient grad(QPoint(0, 0), QPoint(imageSize.width(), imageSize.height()));
m_Palette.m_SourceColors.clear();
QPainter painter(&image);
float start = 0;
for (auto& it : m_Arrows)
if (Blend())
{
auto pos = it.first;
auto col = it.second.Color();
m_Palette.m_SourceColors[pos] = v4F(col.red() / 255.0f, col.green() / 255.0f, col.blue() / 255.0f, 1.0f);
grad.setColorAt(pos, col);
QLinearGradient grad(QPoint(0, 0), QPoint(imageSize.width(), imageSize.height()));
for (auto& it : m_Arrows)
{
auto col = it.second.Color();
m_Palette.m_SourceColors[it.first] = v4F(col.red() / 255.0f, col.green() / 255.0f, col.blue() / 255.0f, 1.0f);
grad.setColorAt(it.first, col);
}
painter.fillRect(image.rect(), grad);
}
else
{
for (auto& it : m_Arrows)
{
auto col = it.second.Color();
m_Palette.m_SourceColors[it.first] = v4F(col.red() / 255.0f, col.green() / 255.0f, col.blue() / 255.0f, 1.0f);
painter.setBrush(col);
painter.fillRect(start, 0, imageSize.width(), imageSize.height(), col);
start = std::ceil(it.first * imageSize.width());
}
}
p.begin(&image);
p.fillRect(image.rect(), grad);
p.end();
m_Palette.m_Entries.reserve(image.width());
for (int i = 0; i < image.width(); i++)
@ -493,13 +525,25 @@ void GradientColorsView::paintEvent(QPaintEvent*)
QPoint gradStart = QPoint(m_ViewRect.topLeft().x(), m_ViewRect.bottomLeft().y() / 2);
QPoint gradStop = QPoint(m_ViewRect.topRight().x(), m_ViewRect.bottomRight().y() / 2);
QLinearGradient grad(gradStart, gradStop);
float start = m_ViewRect.x();
for (auto& it : m_Arrows)
{
GradientArrow& arrow = it.second;
grad.setColorAt(it.first, arrow.Color());
auto offset = std::ceil(it.first * RectWidth());
if (Blend())
{
grad.setColorAt(it.first, arrow.Color());
}
else
{
painter.fillRect(start, m_ViewRect.y(), m_ViewRect.right() - start, m_ViewRect.height(), arrow.Color());
start = m_ViewRect.x() + offset;
}
QPolygon arrowPolygon = arrow.Area();
int iPosX = it.first * RectWidth(),
int iPosX = offset,
iPosY = m_ViewRect.height() + m_ViewRect.top() + 3;
arrowPolygon.translate(iPosX, iPosY);
QPainterPath paintPath;
@ -513,8 +557,11 @@ void GradientColorsView::paintEvent(QPaintEvent*)
painter.setBrush(QBrush(Qt::NoBrush));
}
QBrush brush(grad);
painter.fillRect(m_ViewRect, brush);
if (Blend())
{
painter.fillRect(m_ViewRect, grad);
}
painter.drawRect(m_ViewRect);
}
else
@ -534,8 +581,6 @@ void GradientColorsView::paintEvent(QPaintEvent*)
//Draw text inside of the arrow.
painter.drawText(topArrowRect.x() + (topArrowRect.width() - (topArrow.Width() - 5)) / 2.0, topArrowRect.y() + (topArrowRect.height() - 5), topArrow.Text());
}
painter.end();
}
/// <summary>

View File

@ -35,6 +35,8 @@ class GradientColorsView : public QWidget
public:
explicit GradientColorsView(QWidget* p = nullptr);
bool Blend();
void Blend(bool blend);
void SetFocus(float position);
void SetFocus(size_t position);
void SetFocusColor(const QColor& col);
@ -74,6 +76,7 @@ private:
int RectHeight();
bool m_ArrowMoving = false;
bool m_ColorIndexArrowMoving = false;
bool m_Blend = true;
QPoint m_ViewRectSize;
QPoint m_ViewRectOffset = QPoint(5, 15);
QPoint m_ViewRectTranslate = QPoint(5, 5);

View File

@ -34,9 +34,10 @@ PaletteEditor::PaletteEditor(QWidget* p) :
connect(ui->RemoveColorButton, SIGNAL(clicked()), this, SLOT(OnRemoveColorButtonClicked()));
connect(ui->InvertColorsButton, SIGNAL(clicked()), this, SLOT(OnInvertColorsButtonClicked()));
connect(ui->ResetColorsButton, SIGNAL(clicked()), this, SLOT(OnResetToDefaultButtonClicked()));
connect(ui->DistributeColorsButton, SIGNAL(clicked()), this, SLOT(OnDistributeColorsButtonClicked()));
connect(ui->RandomColorsButton, SIGNAL(clicked()), this, SLOT(OnRandomColorsButtonClicked()));
connect(ui->DistributeColorsButton, SIGNAL(clicked()), this, SLOT(OnDistributeColorsButtonClicked()));
connect(ui->SyncCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSyncCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui->BlendCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnBlendCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui->PaletteFilenameCombo, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnPaletteFilenameComboChanged(const QString&)), Qt::QueuedConnection);
connect(ui->PaletteListTable, SIGNAL(cellClicked(int, int)), this, SLOT(OnPaletteCellClicked(int, int)), Qt::QueuedConnection);
connect(ui->PaletteListTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnPaletteCellChanged(int, int)), Qt::QueuedConnection);
@ -290,6 +291,18 @@ void PaletteEditor::OnSyncCheckBoxStateChanged(int state)
EmitColorIndexChanged(std::numeric_limits<size_t>::max(), 0);//Pass special value to update all.
}
/// <summary>
/// Change whether palette colors are blended between points, or instead do hard cuts.
/// Called when the Blend checkbox is checked/unchecked.
/// </summary>
/// <param name="state">Ignored</param>
void PaletteEditor::OnBlendCheckBoxStateChanged(int state)
{
m_GradientColorView->Blend((bool)state);
m_GradientColorView->update();
EmitPaletteChanged();
}
/// <summary>
/// Load the palette file based on the currently selected index in the combo box.
/// Called when the index of the palette filename combo box changes.
@ -607,6 +620,7 @@ void PaletteEditor::EnablePaletteControls()
ui->AddColorButton->setEnabled(b);
ui->DistributeColorsButton->setEnabled(b);
ui->AutoDistributeCheckBox->setEnabled(b);
ui->BlendCheckBox->setEnabled(b);
ui->RandomColorsButton->setEnabled(b);
ui->RemoveColorButton->setEnabled(b);
ui->ResetColorsButton->setEnabled(b);

View File

@ -54,6 +54,7 @@ private Q_SLOTS:
void OnColorPickerColorChanged(const QColor& col);
void OnArrowDoubleClicked(const GradientArrow& arrow);
void OnSyncCheckBoxStateChanged(int state);
void OnBlendCheckBoxStateChanged(int state);
void OnArrowMoved(qreal lastPos, const GradientArrow& arrow);
void OnColorIndexMove(size_t index, float value);
void OnPaletteFilenameComboChanged(const QString& text);

View File

@ -315,6 +315,7 @@ void QssDialog::reject()
m_Parent->m_Settings->Theme(m_LastTheme->objectName());
}
m_Parent->m_Controller->FillVariationTreeWithCurrentXform();
QDialog::reject();
}
@ -466,6 +467,7 @@ void QssDialog::SlotApplyCss()
label->setText(tr("Valid Style Sheet"));
label->setStyleSheet(QStringLiteral("color: green"));
m_Parent->setStyleSheet(style);
m_Parent->m_Controller->FillVariationTreeWithCurrentXform();
}
else
{

View File

@ -161,9 +161,12 @@ void SpinBox::OnTimeout()
/// <returns>false</returns>
bool SpinBox::eventFilter(QObject* o, QEvent* e)
{
if (!isEnabled())
return QSpinBox::eventFilter(o, e);
auto me = dynamic_cast<QMouseEvent*>(e);
if (isEnabled() && me)
if (me)
{
if (!m_Settings->ToggleType() &&//Ensure double click toggles, not right click.
me->type() == QMouseEvent::MouseButtonPress &&
@ -222,6 +225,47 @@ bool SpinBox::eventFilter(QObject* o, QEvent* e)
return QSpinBox::eventFilter(o, e);
}
/// <summary>
/// Override which is for handling specific key presses while this control is focused.
/// In particular, + = and up arrow increase the value, equivalent to scrolling the mouse wheel up, while also observing shift/ctrl modifiers.
/// Values are decreased in the same way by pressing - _ or down arrow.
/// </summary>
/// <param name="ke">The key event</param>
void SpinBox::keyPressEvent(QKeyEvent* ke)
{
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool ctrl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
if (ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal || ke->key() == Qt::Key_Up)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() + m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() + (ctrl ? m_Step * 10 : m_Step));
}
}
else if (ke->key() == Qt::Key_Minus || ke->key() == Qt::Key_Underscore || ke->key() == Qt::Key_Down)
{
if (shift)
{
setSingleStep(m_SmallStep);
setValue(value() - m_SmallStep);
}
else
{
setSingleStep(m_Step);
setValue(value() - (ctrl ? m_Step * 10 : m_Step));
}
}
else
QSpinBox::keyPressEvent(ke);
}
/// <summary>
/// Called when focus enters the spinner.
/// </summary>

View File

@ -38,11 +38,12 @@ public slots:
void OnTimeout();
protected:
bool eventFilter(QObject* o, QEvent* e);
virtual void focusInEvent(QFocusEvent* e);
virtual void focusOutEvent(QFocusEvent* e);
virtual void enterEvent(QEvent* e);
virtual void leaveEvent(QEvent* e);
virtual bool eventFilter(QObject* o, QEvent* e) override;
virtual void keyPressEvent(QKeyEvent* event) override;
virtual void focusInEvent(QFocusEvent* e) override;
virtual void focusOutEvent(QFocusEvent* e) override;
virtual void enterEvent(QEvent* e) override;
virtual void leaveEvent(QEvent* e) override;
private:
void StartTimer();

View File

@ -34,7 +34,6 @@ public:
m_Button1 = new QPushButton(caption1, p);
m_Button2 = new QPushButton(caption2, p);
m_Combo = new QComboBox(p);
m_Combo->addItems(comboStrings);
if (w1 != -1)
@ -55,14 +54,12 @@ public:
m_Button2->setMaximumHeight(h);
m_Combo->setMinimumHeight(h - 3);
m_Combo->setMaximumHeight(h - 3);
l->addWidget(m_Combo);
l->addWidget(m_Button1);
l->addWidget(m_Button2);
l->setAlignment(Qt::AlignLeft);
l->setMargin(0);
l->setSpacing(2);
setLayout(l);
}
@ -105,13 +102,11 @@ public:
m_Button->setMinimumHeight(h);
m_Button->setMaximumHeight(h);
l->addWidget(spinBox);
l->addWidget(m_Button);
l->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
l->setMargin(0);
l->setSpacing(0);
setLayout(l);
}
@ -125,8 +120,8 @@ class SpinnerLabelButtonWidget : public QWidget
public:
/// <summary>
/// Constructor that passes the parent to the base, then creates a QLabel,
/// then creates a QPushButton and sets up its caption and dimensions, then
/// Constructor that passes the parent to the base, then creates a QLabel,
/// then creates a QPushButton and sets up its caption and dimensions, then
/// assigns the DoubleSpinBox.
/// </summary>
/// <param name="spinBox">The pre-created DoubleSpinBox</param>
@ -139,12 +134,12 @@ public:
{
QHBoxLayout* l = new QHBoxLayout(this);
m_Button = new QPushButton(buttonCaption, p);
m_SpinBox = spinBox;
m_SpinBox = spinBox;
m_Label = new QLabel(p);
m_Label->setMinimumHeight(h);
m_Label->setMaximumHeight(h);
if (w != -1)
m_Label->setMinimumHeight(h);
m_Label->setMaximumHeight(h);
if (w != -1)
{
m_Button->setMinimumWidth(w);
m_Button->setMaximumWidth(w);
@ -152,9 +147,8 @@ public:
m_Button->setMinimumHeight(h);
m_Button->setMaximumHeight(h);
l->addWidget(spinBox);
l->addWidget(m_Label);
l->addWidget(m_Label);
l->addWidget(m_Button);
l->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
l->setMargin(0);
@ -164,5 +158,5 @@ public:
DoubleSpinBox* m_SpinBox;
QPushButton* m_Button;
QLabel* m_Label;
QLabel* m_Label;
};

View File

@ -194,10 +194,8 @@ void FractoriumVariationsDialog::OnSelectionCheckBoxStateChanged(int i)
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=" };
//static vector<const Variation<T>*> excluded;
static std::set<eVariationId> excluded;
excluded.clear();
//excluded.reserve(size_t(eVariationId::LAST_VAR));
if (auto s = dynamic_cast<QCheckBox*>(sender()))
{